mdbook_validator/
dependency.rs1use std::process::Command;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct DependencyStatus {
11 pub jq_available: bool,
13 pub docker_available: bool,
15}
16
17pub trait DependencyChecker {
21 fn check_command(&self, cmd: &str, args: &[&str]) -> bool;
30}
31
32#[derive(Debug, Default, Clone, Copy)]
34pub struct RealChecker;
35
36impl DependencyChecker for RealChecker {
37 fn check_command(&self, cmd: &str, args: &[&str]) -> bool {
38 Command::new(cmd)
39 .args(args)
40 .output()
41 .map(|o| o.status.success())
42 .unwrap_or(false)
43 }
44}
45
46pub fn check_jq<C: DependencyChecker>(checker: &C) -> bool {
48 checker.check_command("jq", &["--version"])
49}
50
51pub fn check_docker<C: DependencyChecker>(checker: &C) -> bool {
53 checker.check_command("docker", &["info"])
54}
55
56pub fn check_all<C: DependencyChecker>(checker: &C) -> DependencyStatus {
60 DependencyStatus {
61 jq_available: check_jq(checker),
62 docker_available: check_docker(checker),
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 #![allow(clippy::panic, clippy::expect_used, clippy::unwrap_used)]
69
70 use super::*;
71
72 struct MockChecker {
74 jq_available: bool,
75 docker_available: bool,
76 }
77
78 impl MockChecker {
79 fn new(jq_available: bool, docker_available: bool) -> Self {
80 Self {
81 jq_available,
82 docker_available,
83 }
84 }
85 }
86
87 impl DependencyChecker for MockChecker {
88 fn check_command(&self, cmd: &str, _args: &[&str]) -> bool {
89 match cmd {
90 "jq" => self.jq_available,
91 "docker" => self.docker_available,
92 _ => false,
93 }
94 }
95 }
96
97 #[test]
98 fn test_check_jq_available() {
99 let checker = MockChecker::new(true, false);
100 assert!(check_jq(&checker));
101 }
102
103 #[test]
104 fn test_check_jq_unavailable() {
105 let checker = MockChecker::new(false, false);
106 assert!(!check_jq(&checker));
107 }
108
109 #[test]
110 fn test_check_docker_running() {
111 let checker = MockChecker::new(false, true);
112 assert!(check_docker(&checker));
113 }
114
115 #[test]
116 fn test_check_docker_not_running() {
117 let checker = MockChecker::new(false, false);
118 assert!(!check_docker(&checker));
119 }
120
121 #[test]
122 fn test_check_all_both_present() {
123 let checker = MockChecker::new(true, true);
124 let status = check_all(&checker);
125 assert!(status.jq_available);
126 assert!(status.docker_available);
127 }
128
129 #[test]
130 fn test_check_all_jq_missing() {
131 let checker = MockChecker::new(false, true);
132 let status = check_all(&checker);
133 assert!(!status.jq_available);
134 assert!(status.docker_available);
135 }
136
137 #[test]
138 fn test_check_all_docker_missing() {
139 let checker = MockChecker::new(true, false);
140 let status = check_all(&checker);
141 assert!(status.jq_available);
142 assert!(!status.docker_available);
143 }
144
145 #[test]
146 fn test_check_all_both_missing() {
147 let checker = MockChecker::new(false, false);
148 let status = check_all(&checker);
149 assert!(!status.jq_available);
150 assert!(!status.docker_available);
151 }
152
153 #[test]
154 fn test_real_checker_jq() {
155 let checker = RealChecker;
157 let result = check_jq(&checker);
159 let _ = result;
161 }
162
163 #[test]
164 fn test_real_checker_docker() {
165 let checker = RealChecker;
167 let result = check_docker(&checker);
169 let _ = result;
171 }
172
173 #[test]
174 fn test_dependency_status_equality() {
175 let status1 = DependencyStatus {
176 jq_available: true,
177 docker_available: false,
178 };
179 let status2 = DependencyStatus {
180 jq_available: true,
181 docker_available: false,
182 };
183 let status3 = DependencyStatus {
184 jq_available: false,
185 docker_available: false,
186 };
187 assert_eq!(status1, status2);
188 assert_ne!(status1, status3);
189 }
190
191 #[test]
192 fn test_dependency_status_clone() {
193 let status = DependencyStatus {
194 jq_available: true,
195 docker_available: true,
196 };
197 let cloned = status.clone();
198 assert_eq!(status, cloned);
199 }
200}