1use std::path::Path;
4use std::process::Command;
5use thiserror::Error;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum VerifyStep {
10 Format,
11 Clippy,
12 Test,
13}
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17pub enum StepResult {
18 Pass,
19 Fail(String),
20 Skip(String),
21}
22
23#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct VerifyReport {
26 pub format: StepResult,
27 pub clippy: StepResult,
28 pub test: StepResult,
29}
30
31#[derive(Error, Debug)]
33pub enum VerifyError {
34 #[error("Failed to execute cargo command: {0}")]
35 CommandExecution(String),
36
37 #[error("I/O error during verification: {0}")]
38 Io(#[from] std::io::Error),
39}
40
41impl VerifyReport {
42 pub fn is_success(&self) -> bool {
44 matches!(self.format, StepResult::Pass | StepResult::Skip(_))
45 && matches!(self.clippy, StepResult::Pass | StepResult::Skip(_))
46 && matches!(self.test, StepResult::Pass | StepResult::Skip(_))
47 }
48}
49
50pub fn run_all(target_dir: &Path) -> Result<VerifyReport, VerifyError> {
52 let format = run_format_check(target_dir)?;
53 let clippy = run_clippy(target_dir)?;
54 let test = run_tests(target_dir)?;
55
56 Ok(VerifyReport {
57 format,
58 clippy,
59 test,
60 })
61}
62
63fn run_format_check(target_dir: &Path) -> Result<StepResult, VerifyError> {
65 let output = Command::new("cargo")
66 .arg("fmt")
67 .arg("--check")
68 .current_dir(target_dir)
69 .output()?;
70
71 if output.status.success() {
72 Ok(StepResult::Pass)
73 } else {
74 let stderr = String::from_utf8_lossy(&output.stderr);
75 let stdout = String::from_utf8_lossy(&output.stdout);
76 let message = format!("{}\n{}", stdout, stderr).trim().to_string();
77 Ok(StepResult::Fail(message))
78 }
79}
80
81fn run_clippy(target_dir: &Path) -> Result<StepResult, VerifyError> {
83 let output = Command::new("cargo")
84 .arg("clippy")
85 .arg("--all-targets")
86 .arg("--all-features")
87 .current_dir(target_dir)
88 .output()?;
89
90 if output.status.success() {
91 Ok(StepResult::Pass)
92 } else {
93 let stderr = String::from_utf8_lossy(&output.stderr);
94 let stdout = String::from_utf8_lossy(&output.stdout);
95 let message = format!("{}\n{}", stdout, stderr).trim().to_string();
96 Ok(StepResult::Fail(message))
97 }
98}
99
100fn run_tests(target_dir: &Path) -> Result<StepResult, VerifyError> {
102 let nextest_check = Command::new("cargo")
104 .arg("nextest")
105 .arg("--version")
106 .output();
107
108 match nextest_check {
109 Ok(output) if output.status.success() => {
110 let test_output = Command::new("cargo")
112 .arg("nextest")
113 .arg("run")
114 .current_dir(target_dir)
115 .output()?;
116
117 if test_output.status.success() {
118 Ok(StepResult::Pass)
119 } else {
120 let stderr = String::from_utf8_lossy(&test_output.stderr);
121 let stdout = String::from_utf8_lossy(&test_output.stdout);
122 let message = format!("{}\n{}", stdout, stderr).trim().to_string();
123 Ok(StepResult::Fail(message))
124 }
125 }
126 Ok(_) | Err(_) => {
127 Ok(StepResult::Skip("cargo-nextest not installed".to_string()))
129 }
130 }
131}