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
use crate::models::prelude::{
    ModelRoot, V1AlphaDoctorGroup, V1AlphaKnownError, V1AlphaReportDefinition,
    V1AlphaReportLocation,
};
use crate::models::InternalScopeModel;
use crate::shared::prelude::*;
use anyhow::anyhow;
use path_clean::PathClean;
use serde_yaml::Value;
use std::collections::VecDeque;
use std::path::Path;

mod doctor_group;
mod known_error;
mod report_definition;
mod upload_location;

use self::known_error::KnownError;
use self::report_definition::ReportDefinition;
use self::upload_location::ReportUploadLocation;

pub mod prelude {
    pub use super::ParsedConfig;
    pub use super::{doctor_group::*, known_error::*, report_definition::*, upload_location::*};
}

#[derive(Debug, PartialEq)]
pub enum ParsedConfig {
    KnownError(KnownError),
    ReportUpload(ReportUploadLocation),
    ReportDefinition(ReportDefinition),
    DoctorGroup(DoctorGroup),
}

#[cfg(test)]
impl ParsedConfig {
    pub fn get_report_upload_spec(&self) -> Option<ReportUploadLocation> {
        match self {
            ParsedConfig::ReportUpload(root) => Some(root.clone()),
            _ => None,
        }
    }

    pub fn get_report_def_spec(&self) -> Option<ReportDefinition> {
        match self {
            ParsedConfig::ReportDefinition(root) => Some(root.clone()),
            _ => None,
        }
    }

    pub fn get_known_error_spec(&self) -> Option<KnownError> {
        match self {
            ParsedConfig::KnownError(root) => Some(root.clone()),
            _ => None,
        }
    }

    pub fn get_doctor_group(&self) -> Option<DoctorGroup> {
        match self {
            ParsedConfig::DoctorGroup(root) => Some(root.clone()),
            _ => None,
        }
    }
}

impl TryFrom<ModelRoot<Value>> for ParsedConfig {
    type Error = anyhow::Error;

    fn try_from(value: ModelRoot<Value>) -> Result<Self, Self::Error> {
        if let Ok(Some(known)) = V1AlphaDoctorGroup::known_type(&value) {
            return Ok(ParsedConfig::DoctorGroup(DoctorGroup::try_from(known)?));
        }
        if let Ok(Some(known)) = V1AlphaKnownError::known_type(&value) {
            return Ok(ParsedConfig::KnownError(KnownError::try_from(known)?));
        }
        if let Ok(Some(known)) = V1AlphaReportLocation::known_type(&value) {
            return Ok(ParsedConfig::ReportUpload(ReportUploadLocation::try_from(
                known,
            )?));
        }
        if let Ok(Some(known)) = V1AlphaReportDefinition::known_type(&value) {
            return Ok(ParsedConfig::ReportDefinition(ReportDefinition::try_from(
                known,
            )?));
        }
        Err(anyhow!("Error was know a known type"))
    }
}

pub(crate) fn extract_command_path(parent_dir: &Path, exec: &str) -> String {
    let mut parts: VecDeque<_> = exec.split(' ').map(|x| x.to_string()).collect();
    let mut command = parts.pop_front().unwrap();

    if command.starts_with('.') {
        let full_command = parent_dir.join(command).clean().display().to_string();
        command = full_command;
    }

    parts.push_front(command);

    parts
        .iter()
        .map(|x| x.to_string())
        .collect::<Vec<_>>()
        .join(" ")
}

#[test]
fn test_extract_command_path() {
    let base_path = Path::new("/foo/bar");
    assert_eq!(
        "/foo/bar/scripts/foo.sh",
        extract_command_path(base_path, "./scripts/foo.sh")
    );
    assert_eq!(
        "/scripts/foo.sh",
        extract_command_path(base_path, "/scripts/foo.sh")
    );
    assert_eq!("foo", extract_command_path(base_path, "foo"));
    assert_eq!("foo bar", extract_command_path(base_path, "foo bar"));
}