medic_lib/check_result/
mod.rs

1use serde::Serialize;
2use std::io::{self, Write};
3use std::ops::{ControlFlow, FromResidual, Try};
4
5#[derive(Serialize)]
6struct CheckJson {
7    output: String,
8    error: Option<String>,
9    remedy: Option<String>,
10}
11
12enum CheckResultFormat {
13    Json,
14    Stdio,
15}
16
17impl CheckResultFormat {
18    fn from_env() -> Self {
19        let format = std::env::var("MEDIC_OUTPUT_FORMAT").unwrap_or("json".to_owned());
20        match format.as_str() {
21            "stdio" => Self::Stdio,
22            _ => Self::Json,
23        }
24    }
25
26    fn print(
27        self,
28        msg: String,
29        stdout: Option<String>,
30        stderr: Option<String>,
31        remedy: Option<String>,
32    ) {
33        match self {
34            CheckResultFormat::Stdio => {
35                eprintln!("\x1b[31;1mError:\x1b[0m {msg}\r\n");
36                if let Some(stdout) = stdout {
37                    if !stdout.is_empty() {
38                        eprintln!("\x1b[31;1mstdout:\x1b[0m\r\n{stdout}");
39                    }
40                }
41                if let Some(stderr) = stderr {
42                    if !stderr.is_empty() {
43                        eprintln!("\x1b[31;1mstderr:\x1b[0m\r\n{stderr}");
44                    }
45                }
46                io::stderr().flush().unwrap();
47                if let Some(remedy) = remedy {
48                    println!("{remedy}");
49                }
50            }
51            CheckResultFormat::Json => {
52                let mut output = format!("\x1b[31;1mError: \x1b[0m {msg}");
53                let mut error = None;
54
55                if let Some(stdout) = stdout {
56                    if !stdout.is_empty() {
57                        output.push_str(&format!("\r\n\r\n\x1b[31;1mstdout:\x1b[0m\r\n{stdout}"));
58                    }
59                }
60                if let Some(stderr) = stderr {
61                    if !stderr.is_empty() {
62                        error = Some(format!("\x1b[31;1mstdout:\x1b[0m\r\n{stderr}"));
63                    }
64                }
65
66                let json = CheckJson {
67                    output,
68                    error,
69                    remedy,
70                };
71
72                println!("{}", serde_json::to_string(&json).unwrap());
73            }
74        }
75    }
76}
77
78#[derive(Debug, Default, Clone, Eq, PartialEq)]
79pub enum CheckResult {
80    #[default]
81    CheckOk,
82    CheckError(String, Option<String>, Option<String>, Option<String>),
83}
84
85impl std::process::Termination for CheckResult {
86    fn report(self) -> std::process::ExitCode {
87        match self {
88            CheckResult::CheckOk => std::process::ExitCode::from(0),
89            CheckResult::CheckError(msg, stdout, stderr, remedy) => {
90                CheckResultFormat::from_env().print(msg, stdout, stderr, remedy);
91                std::process::ExitCode::from(1)
92            }
93        }
94    }
95}
96
97pub struct ResultCodeResidual(String, Option<String>, Option<String>, Option<String>);
98
99impl Try for CheckResult {
100    type Output = ();
101    type Residual = ResultCodeResidual;
102
103    fn branch(self) -> ControlFlow<Self::Residual> {
104        match self {
105            CheckResult::CheckError(msg, stdout, stderr, remedy) => {
106                ControlFlow::Break(ResultCodeResidual(msg, stdout, stderr, remedy))
107            }
108            CheckResult::CheckOk => ControlFlow::Continue(()),
109        }
110    }
111    fn from_output((): ()) -> Self {
112        CheckResult::CheckOk
113    }
114}
115
116impl FromResidual for CheckResult {
117    fn from_residual(r: ResultCodeResidual) -> Self {
118        Self::CheckError(r.0, r.1, r.2, r.3)
119    }
120}