1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
use crate::models::core::ModelMetadata;

use crate::models::v1alpha::V1AlphaApiVersion;
use crate::models::{HelpMetadata, InternalScopeModel, ScopeModel};
use derive_builder::Builder;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

/// What needs to be checked before the action will run. All `paths` will be checked first, then
/// `commands`. If a `path` has changed, the `command` will not run.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct DoctorCheckSpec {
    #[serde(default)]
    /// A list of globs to check for changes. When the glob matches a new file, or the contents
    /// of the file change, the check will require a fix.
    pub paths: Option<Vec<String>>,

    #[serde(default)]
    /// A list of commands to execute to check the environment.
    pub commands: Option<Vec<String>>,
}

/// Definition for fixing the environment.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct DoctorFixSpec {
    #[serde(default)]
    /// List of commands to run to fix the env.
    pub commands: Vec<String>,

    #[serde(default)]
    /// Text to display when no command is provided / fails to fix the env.
    pub help_text: Option<String>,

    #[serde(default)]
    /// Link to documentation to fix the issue.
    pub help_url: Option<String>,
}

/// An action is a single step used to check in a group. This is most commonly used to build a
/// series of tasks for a system, like `ruby`, `python`, and databases.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct DoctorGroupActionSpec {
    /// Name of the "action". When not provided, it will be the index of the action within the group.
    /// This is used when reporting status to the users.
    pub name: Option<String>,

    /// A description of this specific action, used for information to the users.
    pub description: Option<String>,

    /// The `check` run before `fix` (if provided). A check is used to determine if the fix needs
    /// to be executed, or fail the action if no fix is provided. If a fix is specified, the check
    /// will re-execute to ensure that the fix applied correctly.
    pub check: DoctorCheckSpec,

    /// A fix defines how to fix the issue that a `check` is validating. When provided, will only
    /// run when the `check` "fails".
    pub fix: Option<DoctorFixSpec>,

    #[serde(default = "doctor_group_action_required_default")]
    /// If false, the action is allowed to fail and let other actions in the group execute. Defaults
    /// to `true`.
    pub required: bool,
}

fn doctor_group_action_required_default() -> bool {
    true
}

/// Often used to describe how to fix a "system", like `ruby`, `python`, or databases. Able to
/// depend on other "system".
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema)]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct DoctorGroupSpec {
    #[serde(default)]
    /// A list of `ScopeDoctorGroup` that are required for this group to execute. If not all finish
    /// successfully, this group will not execute.
    pub needs: Vec<String>,

    /// A series of steps to check and fix for the group.
    pub actions: Vec<DoctorGroupActionSpec>,
}

#[derive(Serialize, Deserialize, Debug, strum::Display, Clone, PartialEq, JsonSchema)]
pub enum DoctorGroupKind {
    #[strum(serialize = "ScopeDoctorGroup")]
    ScopeDoctorGroup,
}

/// Resource used to define a `ScopeDoctorGroup`.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Builder, JsonSchema)]
#[builder(setter(into))]
#[serde(rename_all = "camelCase")]
#[schemars(deny_unknown_fields)]
pub struct V1AlphaDoctorGroup {
    /// API version of the resource
    pub api_version: V1AlphaApiVersion,
    /// The type of resource.
    pub kind: DoctorGroupKind,
    /// Standard set of options including name, description for the resource.
    /// Together `kind` and `metadata.name` are required to be unique. If there are duplicate, the
    /// resources "closest" to the execution dir will take precedence.
    pub metadata: ModelMetadata,
    /// Options for the resource.
    pub spec: DoctorGroupSpec,
}

impl HelpMetadata for V1AlphaDoctorGroup {
    fn metadata(&self) -> &ModelMetadata {
        &self.metadata
    }

    fn full_name(&self) -> String {
        format!("{}/{}", self.kind(), self.name())
    }
}

impl ScopeModel<DoctorGroupSpec> for V1AlphaDoctorGroup {
    fn api_version(&self) -> String {
        Self::int_api_version()
    }

    fn kind(&self) -> String {
        Self::int_kind()
    }

    fn spec(&self) -> &DoctorGroupSpec {
        &self.spec
    }
}

impl InternalScopeModel<DoctorGroupSpec, V1AlphaDoctorGroup> for V1AlphaDoctorGroup {
    fn int_api_version() -> String {
        V1AlphaApiVersion::ScopeV1Alpha.to_string()
    }

    fn int_kind() -> String {
        DoctorGroupKind::ScopeDoctorGroup.to_string()
    }

    #[cfg(test)]
    fn examples() -> Vec<String> {
        vec!["v1alpha/DoctorGroup.yaml".to_string()]
    }
}