1#[derive(Debug, Clone)]
7pub struct RepoInfo {
8 pub owner: &'static str,
10 pub name: &'static str,
12}
13
14impl RepoInfo {
15 #[must_use]
17 pub const fn new(owner: &'static str, name: &'static str) -> Self {
18 Self { owner, name }
19 }
20}
21
22#[derive(Debug, Clone)]
24pub struct DoctorCheck {
25 pub name: String,
27 pub passed: bool,
29 pub message: Option<String>,
31}
32
33impl DoctorCheck {
34 #[must_use]
36 pub fn pass(name: impl Into<String>) -> Self {
37 Self {
38 name: name.into(),
39 passed: true,
40 message: None,
41 }
42 }
43
44 #[must_use]
46 pub fn fail(name: impl Into<String>, message: impl Into<String>) -> Self {
47 Self {
48 name: name.into(),
49 passed: false,
50 message: Some(message.into()),
51 }
52 }
53
54 pub fn file_exists(path: impl AsRef<std::path::Path>) -> Self {
59 let path_ref = path.as_ref();
60 if path_ref.exists() && path_ref.is_file() {
61 Self::pass(format!("File exists: {}", path_ref.display()))
62 } else {
63 Self::fail(
64 format!("File check: {}", path_ref.display()),
65 format!("File not found: {}", path_ref.display()),
66 )
67 }
68 }
69
70 pub fn dir_exists(path: impl AsRef<std::path::Path>) -> Self {
75 let path_ref = path.as_ref();
76 if path_ref.exists() && path_ref.is_dir() {
77 Self::pass(format!("Directory exists: {}", path_ref.display()))
78 } else {
79 Self::fail(
80 format!("Directory check: {}", path_ref.display()),
81 format!("Directory not found: {}", path_ref.display()),
82 )
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn test_repo_info_creation() {
93 let repo = RepoInfo::new("workhelix", "prompter");
94 assert_eq!(repo.owner, "workhelix");
95 assert_eq!(repo.name, "prompter");
96 }
97
98 #[test]
99 fn test_doctor_check_pass() {
100 let check = DoctorCheck::pass("test check");
101 assert!(check.passed);
102 assert_eq!(check.name, "test check");
103 assert!(check.message.is_none());
104 }
105
106 #[test]
107 fn test_doctor_check_fail() {
108 let check = DoctorCheck::fail("test check", "error message");
109 assert!(!check.passed);
110 assert_eq!(check.name, "test check");
111 assert_eq!(check.message, Some("error message".to_string()));
112 }
113
114 fn unique_temp_path(label: &str) -> std::path::PathBuf {
115 use std::sync::atomic::{AtomicU32, Ordering};
116 static COUNTER: AtomicU32 = AtomicU32::new(0);
117 let n = COUNTER.fetch_add(1, Ordering::Relaxed);
118 std::env::temp_dir().join(format!(
119 "tftio-lib-types-{label}-{}-{n}",
120 std::process::id()
121 ))
122 }
123
124 #[test]
125 fn file_exists_passes_for_a_real_file() {
126 let path = unique_temp_path("file-pass");
127 std::fs::write(&path, b"data").unwrap();
128 let check = DoctorCheck::file_exists(&path);
129 assert!(check.passed);
130 assert!(check.name.starts_with("File exists:"));
131 assert!(check.message.is_none());
132 std::fs::remove_file(&path).unwrap();
133 }
134
135 #[test]
136 fn file_exists_fails_when_the_path_is_missing() {
137 let path = unique_temp_path("file-missing");
138 let check = DoctorCheck::file_exists(&path);
139 assert!(!check.passed);
140 assert_eq!(
141 check.message,
142 Some(format!("File not found: {}", path.display()))
143 );
144 }
145
146 #[test]
147 fn file_exists_fails_when_the_path_is_a_directory() {
148 let path = unique_temp_path("file-is-dir");
149 std::fs::create_dir(&path).unwrap();
150 let check = DoctorCheck::file_exists(&path);
151 assert!(!check.passed);
152 assert!(check.name.starts_with("File check:"));
153 std::fs::remove_dir(&path).unwrap();
154 }
155
156 #[test]
157 fn dir_exists_passes_for_a_real_directory() {
158 let path = unique_temp_path("dir-pass");
159 std::fs::create_dir(&path).unwrap();
160 let check = DoctorCheck::dir_exists(&path);
161 assert!(check.passed);
162 assert!(check.name.starts_with("Directory exists:"));
163 assert!(check.message.is_none());
164 std::fs::remove_dir(&path).unwrap();
165 }
166
167 #[test]
168 fn dir_exists_fails_when_the_path_is_missing() {
169 let path = unique_temp_path("dir-missing");
170 let check = DoctorCheck::dir_exists(&path);
171 assert!(!check.passed);
172 assert_eq!(
173 check.message,
174 Some(format!("Directory not found: {}", path.display()))
175 );
176 }
177
178 #[test]
179 fn dir_exists_fails_when_the_path_is_a_file() {
180 let path = unique_temp_path("dir-is-file");
181 std::fs::write(&path, b"data").unwrap();
182 let check = DoctorCheck::dir_exists(&path);
183 assert!(!check.passed);
184 assert!(check.name.starts_with("Directory check:"));
185 std::fs::remove_file(&path).unwrap();
186 }
187}