main.rs (8475B)
1 #![cfg_attr(coverage_nightly, feature(coverage_attribute))] 2 #![forbid(unsafe_code)] 3 4 #[cfg_attr(coverage_nightly, coverage(off))] 5 mod contract; 6 mod coverage; 7 #[cfg_attr(coverage_nightly, coverage(off))] 8 mod hygiene; 9 10 use std::env; 11 use std::path::{Path, PathBuf}; 12 use std::process::ExitCode; 13 14 fn usage() { 15 eprintln!("usage:"); 16 eprintln!(" cargo xtask contract validate"); 17 eprintln!(" cargo xtask release preflight"); 18 eprintln!(" cargo xtask coverage run-crate --crate <crate> [--out <dir>]"); 19 eprintln!(" cargo xtask coverage required-crates"); 20 eprintln!(" cargo xtask coverage workspace-crates"); 21 eprintln!( 22 " cargo xtask coverage report --scope <scope> --summary <file> --lcov <file> --out <file> [--policy-gate | (--fail-under-exec-lines <pct> --fail-under-functions <pct> --fail-under-regions <pct> --fail-under-branches <pct> [--require-branches])]" 23 ); 24 eprintln!( 25 " cargo xtask coverage report-missing --scope <scope> --out <file> --reason <reason>" 26 ); 27 eprintln!( 28 " cargo xtask coverage refresh-summary [--reports-root <dir>] [--out <file>] [--status-out <file>]" 29 ); 30 eprintln!(" cargo xtask hygiene forbidden-identifiers"); 31 } 32 33 fn workspace_root_with_override(override_root: Option<&str>) -> PathBuf { 34 if let Some(raw) = override_root { 35 let trimmed = raw.trim(); 36 if !trimmed.is_empty() { 37 return PathBuf::from(trimmed); 38 } 39 } 40 let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR")); 41 let crates_dir = manifest_dir.parent().unwrap_or(manifest_dir); 42 let root = crates_dir.parent().unwrap_or(crates_dir); 43 root.to_path_buf() 44 } 45 46 fn workspace_root() -> PathBuf { 47 let override_root = env::var("RADROOTS_WORKSPACE_ROOT").ok(); 48 workspace_root_with_override(override_root.as_deref()) 49 } 50 51 fn validate_contract() -> Result<(), String> { 52 let root = workspace_root(); 53 contract::load_contract_bundle(&root) 54 .and_then(|bundle| contract::validate_contract_bundle(&bundle)) 55 .and_then(|_| contract::validate_canonical_event_boundary(&root)) 56 } 57 58 #[cfg_attr(coverage_nightly, coverage(off))] 59 fn release_preflight() -> Result<(), String> { 60 contract::validate_release_preflight(&workspace_root()) 61 } 62 63 fn run_release(args: &[String]) -> Result<(), String> { 64 match args.first().map(String::as_str) { 65 Some("preflight") => release_preflight(), 66 _ => Err("unknown release subcommand".to_string()), 67 } 68 } 69 70 fn run_contract(args: &[String]) -> Result<(), String> { 71 match args.first().map(String::as_str) { 72 Some("validate") => validate_contract(), 73 _ => Err("unknown contract subcommand".to_string()), 74 } 75 } 76 77 fn run(args: &[String]) -> Result<(), String> { 78 match args.first().map(String::as_str) { 79 Some("contract") => run_contract(&args[1..]), 80 Some("coverage") => coverage::run(&args[1..]), 81 Some("hygiene") => hygiene::run(&args[1..], &workspace_root()), 82 Some("release") => run_release(&args[1..]), 83 _ => Err("unknown command".to_string()), 84 } 85 } 86 87 fn main_with_args(args: Vec<String>) -> ExitCode { 88 if args.is_empty() { 89 usage(); 90 return ExitCode::from(2); 91 } 92 match run(&args) { 93 Ok(()) => ExitCode::SUCCESS, 94 Err(err) => { 95 eprintln!("{err}"); 96 usage(); 97 ExitCode::from(2) 98 } 99 } 100 } 101 102 #[cfg_attr(coverage_nightly, coverage(off))] 103 fn main() -> ExitCode { 104 main_with_args(env::args().skip(1).collect()) 105 } 106 107 #[cfg(test)] 108 mod tests { 109 use super::*; 110 use std::fs; 111 use std::sync::{Mutex, MutexGuard, OnceLock}; 112 use std::time::{SystemTime, UNIX_EPOCH}; 113 114 fn workspace_lock() -> &'static Mutex<()> { 115 static LOCK: OnceLock<Mutex<()>> = OnceLock::new(); 116 LOCK.get_or_init(|| Mutex::new(())) 117 } 118 119 fn lock_workspace() -> MutexGuard<'static, ()> { 120 match workspace_lock().lock() { 121 Ok(guard) => guard, 122 Err(poison) => poison.into_inner(), 123 } 124 } 125 126 fn unique_temp_dir(prefix: &str) -> PathBuf { 127 let ns = SystemTime::now() 128 .duration_since(UNIX_EPOCH) 129 .expect("system time") 130 .as_nanos(); 131 std::env::temp_dir().join(format!("radroots_xtask_main_{prefix}_{ns}")) 132 } 133 134 #[test] 135 fn workspace_root_resolves() { 136 let root = workspace_root(); 137 assert!(root.join("Cargo.toml").exists()); 138 } 139 140 #[test] 141 fn workspace_root_override_takes_precedence() { 142 let root = workspace_root_with_override(Some("/tmp/radroots-test-root")); 143 assert_eq!(root, PathBuf::from("/tmp/radroots-test-root")); 144 145 let fallback = workspace_root_with_override(Some(" ")); 146 assert!(fallback.join("Cargo.toml").exists()); 147 148 let default_root = workspace_root_with_override(None); 149 assert!(default_root.join("Cargo.toml").exists()); 150 } 151 152 #[test] 153 fn run_release_and_dispatchers_cover_error_paths() { 154 let unknown_release = 155 run_release(&["unknown".to_string()]).expect_err("unknown release subcommand"); 156 assert!(unknown_release.contains("unknown release subcommand")); 157 158 let unknown_contract = 159 run_contract(&["unknown".to_string()]).expect_err("unknown contract subcommand"); 160 assert!(unknown_contract.contains("unknown contract subcommand")); 161 162 let unknown_root = run(&["unknown".to_string()]).expect_err("unknown command"); 163 assert!(unknown_root.contains("unknown command")); 164 165 let removed_sdk = run(&["sdk".to_string(), "validate".to_string()]) 166 .expect_err("removed sdk command namespace"); 167 assert!(removed_sdk.contains("unknown command")); 168 } 169 170 #[test] 171 fn lock_workspace_recovers_from_poisoned_mutex() { 172 let handle = std::thread::spawn(|| { 173 let _guard = workspace_lock().lock().expect("lock workspace"); 174 panic!("poison workspace lock"); 175 }); 176 assert!(handle.join().is_err()); 177 178 let _guard = lock_workspace(); 179 } 180 181 #[test] 182 fn contract_and_coverage_dispatchers_execute() { 183 let _guard = lock_workspace(); 184 let out_dir = unique_temp_dir("coverage_dispatch"); 185 fs::create_dir_all(&out_dir).expect("create out dir"); 186 187 run_contract(&["validate".to_string()]).expect("validate contract"); 188 coverage::run(&["help".to_string()]).expect("coverage help"); 189 coverage::run(&["required-crates".to_string()]).expect("coverage required crates"); 190 coverage::run(&["workspace-crates".to_string()]).expect("coverage workspace crates"); 191 192 let summary_path = out_dir.join("summary.json"); 193 let lcov_path = out_dir.join("coverage.info"); 194 let gate_out = out_dir.join("gate-report.json"); 195 fs::write( 196 &summary_path, 197 r#"{"data":[{"totals":{"functions":{"percent":100.0},"lines":{"percent":100.0},"regions":{"percent":100.0}}}]}"#, 198 ) 199 .expect("write summary"); 200 fs::write(&lcov_path, "DA:1,1\nBRDA:1,0,0,1\n").expect("write lcov"); 201 coverage::run(&[ 202 "report".to_string(), 203 "--scope".to_string(), 204 "main-test".to_string(), 205 "--summary".to_string(), 206 summary_path.display().to_string(), 207 "--lcov".to_string(), 208 lcov_path.display().to_string(), 209 "--out".to_string(), 210 gate_out.display().to_string(), 211 "--policy-gate".to_string(), 212 ]) 213 .expect("coverage report"); 214 215 run(&["coverage".to_string(), "help".to_string()]).expect("root run coverage"); 216 run(&["hygiene".to_string(), "forbidden-identifiers".to_string()]) 217 .expect("hygiene forbidden identifiers"); 218 219 let _ = fs::remove_dir_all(out_dir); 220 } 221 222 #[test] 223 fn usage_and_main_entrypoints_execute() { 224 usage(); 225 let empty_code = main_with_args(Vec::new()); 226 assert_eq!(empty_code, ExitCode::from(2)); 227 let success_code = main_with_args(vec!["coverage".to_string(), "help".to_string()]); 228 assert_eq!(success_code, ExitCode::SUCCESS); 229 let failure_code = main_with_args(vec!["unknown".to_string()]); 230 assert_eq!(failure_code, ExitCode::from(2)); 231 let _ = main(); 232 } 233 234 #[test] 235 fn run_contract_dispatches_validate_command() { 236 let _guard = lock_workspace(); 237 run_contract(&["validate".to_string()]).expect("contract validate"); 238 } 239 }