medic_lib/check_result/
mod.rs1use 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}