1use serde::{Deserialize, Serialize};
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct GitRef(String);
12
13impl GitRef {
14 pub fn new(s: impl Into<String>) -> Self {
16 Self(s.into())
17 }
18
19 pub fn as_str(&self) -> &str {
21 &self.0
22 }
23
24 pub fn into_inner(self) -> String {
26 self.0
27 }
28}
29
30impl Default for GitRef {
31 fn default() -> Self {
32 Self("HEAD".to_string())
33 }
34}
35
36impl From<&str> for GitRef {
37 fn from(s: &str) -> Self {
38 Self(s.to_string())
39 }
40}
41
42impl From<String> for GitRef {
43 fn from(s: String) -> Self {
44 Self(s)
45 }
46}
47
48impl AsRef<str> for GitRef {
49 fn as_ref(&self) -> &str {
50 &self.0
51 }
52}
53
54impl fmt::Display for GitRef {
55 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56 write!(f, "{}", self.0)
57 }
58}
59
60#[derive(Clone, Serialize, Deserialize)]
64#[serde(transparent)]
65pub struct AuthToken(String);
66
67impl AuthToken {
68 pub fn new(s: impl Into<String>) -> Self {
70 Self(s.into())
71 }
72
73 pub fn as_str(&self) -> &str {
75 &self.0
76 }
77
78 pub fn into_inner(self) -> String {
80 self.0
81 }
82
83 pub fn is_empty(&self) -> bool {
85 self.0.is_empty()
86 }
87}
88
89impl fmt::Debug for AuthToken {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 if self.0.is_empty() {
92 write!(f, "AuthToken(empty)")
93 } else {
94 write!(f, "AuthToken(***)")
95 }
96 }
97}
98
99impl From<&str> for AuthToken {
100 fn from(s: &str) -> Self {
101 Self(s.to_string())
102 }
103}
104
105impl From<String> for AuthToken {
106 fn from(s: String) -> Self {
107 Self(s)
108 }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
115#[serde(transparent)]
116pub struct RuleId(String);
117
118impl RuleId {
119 pub fn new(s: impl Into<String>) -> Self {
121 Self(s.into())
122 }
123
124 pub fn as_str(&self) -> &str {
126 &self.0
127 }
128
129 pub fn into_inner(self) -> String {
131 self.0
132 }
133}
134
135impl From<&str> for RuleId {
136 fn from(s: &str) -> Self {
137 Self(s.to_string())
138 }
139}
140
141impl From<String> for RuleId {
142 fn from(s: String) -> Self {
143 Self(s)
144 }
145}
146
147impl AsRef<str> for RuleId {
148 fn as_ref(&self) -> &str {
149 &self.0
150 }
151}
152
153impl fmt::Display for RuleId {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 write!(f, "{}", self.0)
156 }
157}
158
159#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
161#[serde(transparent)]
162pub struct FileHash(String);
163
164impl FileHash {
165 pub fn new(s: impl Into<String>) -> Self {
167 Self(s.into())
168 }
169
170 pub fn as_str(&self) -> &str {
172 &self.0
173 }
174
175 pub fn into_inner(self) -> String {
177 self.0
178 }
179}
180
181impl From<&str> for FileHash {
182 fn from(s: &str) -> Self {
183 Self(s.to_string())
184 }
185}
186
187impl From<String> for FileHash {
188 fn from(s: String) -> Self {
189 Self(s)
190 }
191}
192
193impl AsRef<str> for FileHash {
194 fn as_ref(&self) -> &str {
195 &self.0
196 }
197}
198
199impl fmt::Display for FileHash {
200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201 write!(f, "{}", self.0)
202 }
203}
204
205#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
207#[serde(transparent)]
208pub struct ServerName(String);
209
210impl ServerName {
211 pub fn new(s: impl Into<String>) -> Self {
213 Self(s.into())
214 }
215
216 pub fn as_str(&self) -> &str {
218 &self.0
219 }
220}
221
222impl From<&str> for ServerName {
223 fn from(s: &str) -> Self {
224 Self(s.to_string())
225 }
226}
227
228impl From<String> for ServerName {
229 fn from(s: String) -> Self {
230 Self(s)
231 }
232}
233
234impl fmt::Display for ServerName {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 write!(f, "{}", self.0)
237 }
238}
239
240#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
242#[serde(transparent)]
243pub struct CommandArgs(Vec<String>);
244
245impl CommandArgs {
246 pub fn new(args: impl IntoIterator<Item = impl Into<String>>) -> Self {
248 Self(args.into_iter().map(Into::into).collect())
249 }
250
251 pub fn as_slice(&self) -> &[String] {
253 &self.0
254 }
255
256 pub fn join(&self, sep: &str) -> String {
258 self.0.join(sep)
259 }
260
261 pub fn is_empty(&self) -> bool {
263 self.0.is_empty()
264 }
265
266 pub fn len(&self) -> usize {
268 self.0.len()
269 }
270}
271
272impl From<Vec<String>> for CommandArgs {
273 fn from(args: Vec<String>) -> Self {
274 Self(args)
275 }
276}
277
278impl<'a> From<&'a [&'a str]> for CommandArgs {
279 fn from(args: &'a [&'a str]) -> Self {
280 Self(args.iter().map(|s| s.to_string()).collect())
281 }
282}
283
284#[derive(Debug, Clone)]
286pub struct CompiledPattern {
287 pattern: regex::Regex,
288 source: String,
289}
290
291impl CompiledPattern {
292 pub fn new(pattern: &str) -> Result<Self, regex::Error> {
294 let regex = regex::Regex::new(pattern)?;
295 Ok(Self {
296 pattern: regex,
297 source: pattern.to_string(),
298 })
299 }
300
301 pub fn is_match(&self, text: &str) -> bool {
303 self.pattern.is_match(text)
304 }
305
306 pub fn as_str(&self) -> &str {
308 &self.source
309 }
310
311 pub fn regex(&self) -> ®ex::Regex {
313 &self.pattern
314 }
315}
316
317#[cfg(test)]
318mod tests {
319 use super::*;
320
321 #[test]
322 fn test_git_ref_default() {
323 assert_eq!(GitRef::default().as_str(), "HEAD");
324 }
325
326 #[test]
327 fn test_git_ref_from_str() {
328 let ref1: GitRef = "main".into();
329 assert_eq!(ref1.as_str(), "main");
330
331 let ref2 = GitRef::from("develop");
332 assert_eq!(ref2.as_str(), "develop");
333 }
334
335 #[test]
336 fn test_git_ref_display() {
337 let git_ref = GitRef::new("v1.0.0");
338 assert_eq!(format!("{}", git_ref), "v1.0.0");
339 }
340
341 #[test]
342 fn test_auth_token_debug_hides_value() {
343 let token = AuthToken::new("secret123");
344 let debug = format!("{:?}", token);
345 assert!(!debug.contains("secret123"));
346 assert!(debug.contains("***"));
347 }
348
349 #[test]
350 fn test_auth_token_empty_debug() {
351 let token = AuthToken::new("");
352 let debug = format!("{:?}", token);
353 assert!(debug.contains("empty"));
354 }
355
356 #[test]
357 fn test_rule_id_display() {
358 let id = RuleId::new("PE-001");
359 assert_eq!(format!("{}", id), "PE-001");
360 }
361
362 #[test]
363 fn test_rule_id_equality() {
364 let id1 = RuleId::new("EX-001");
365 let id2 = RuleId::new("EX-001");
366 let id3 = RuleId::new("EX-002");
367 assert_eq!(id1, id2);
368 assert_ne!(id1, id3);
369 }
370
371 #[test]
372 fn test_file_hash_from_string() {
373 let hash = FileHash::new("abc123def456");
374 assert_eq!(hash.as_str(), "abc123def456");
375 }
376
377 #[test]
378 fn test_into_inner() {
379 let git_ref = GitRef::new("main");
380 assert_eq!(git_ref.into_inner(), "main".to_string());
381
382 let rule_id = RuleId::new("PE-001");
383 assert_eq!(rule_id.into_inner(), "PE-001".to_string());
384 }
385
386 #[test]
387 fn test_server_name() {
388 let name = ServerName::new("my-server");
389 assert_eq!(name.as_str(), "my-server");
390 assert_eq!(format!("{}", name), "my-server");
391 }
392
393 #[test]
394 fn test_command_args() {
395 let args = CommandArgs::new(["arg1", "arg2", "arg3"]);
396 assert_eq!(args.len(), 3);
397 assert_eq!(args.join(" "), "arg1 arg2 arg3");
398 assert!(!args.is_empty());
399 }
400
401 #[test]
402 fn test_command_args_empty() {
403 let args = CommandArgs::default();
404 assert!(args.is_empty());
405 assert_eq!(args.len(), 0);
406 }
407
408 #[test]
409 fn test_compiled_pattern() {
410 let pattern = CompiledPattern::new(r"hello\s+world").unwrap();
411 assert!(pattern.is_match("hello world"));
412 assert!(!pattern.is_match("helloworld"));
413 assert_eq!(pattern.as_str(), r"hello\s+world");
414 }
415
416 #[test]
417 fn test_compiled_pattern_invalid() {
418 let result = CompiledPattern::new(r"[invalid");
419 assert!(result.is_err());
420 }
421
422 #[test]
423 fn test_auth_token_is_empty() {
424 let empty = AuthToken::new("");
425 assert!(empty.is_empty());
426
427 let non_empty = AuthToken::new("token");
428 assert!(!non_empty.is_empty());
429 }
430
431 #[test]
432 fn test_auth_token_into_inner() {
433 let token = AuthToken::new("secret");
434 assert_eq!(token.into_inner(), "secret".to_string());
435 }
436
437 #[test]
438 fn test_auth_token_from_string() {
439 let token: AuthToken = String::from("token123").into();
440 assert_eq!(token.as_str(), "token123");
441
442 let token2: AuthToken = "token456".into();
443 assert_eq!(token2.as_str(), "token456");
444 }
445
446 #[test]
447 fn test_file_hash_into_inner() {
448 let hash = FileHash::new("abc123");
449 assert_eq!(hash.into_inner(), "abc123".to_string());
450 }
451
452 #[test]
453 fn test_file_hash_display() {
454 let hash = FileHash::new("sha256:abc123");
455 assert_eq!(format!("{}", hash), "sha256:abc123");
456 }
457
458 #[test]
459 fn test_file_hash_as_ref() {
460 let hash = FileHash::new("abc123");
461 let s: &str = hash.as_ref();
462 assert_eq!(s, "abc123");
463 }
464
465 #[test]
466 fn test_server_name_from_implementations() {
467 let name1: ServerName = "server1".into();
468 assert_eq!(name1.as_str(), "server1");
469
470 let name2: ServerName = String::from("server2").into();
471 assert_eq!(name2.as_str(), "server2");
472 }
473
474 #[test]
475 fn test_command_args_as_slice() {
476 let args = CommandArgs::new(["a", "b", "c"]);
477 assert_eq!(
478 args.as_slice(),
479 &["a".to_string(), "b".to_string(), "c".to_string()]
480 );
481 }
482
483 #[test]
484 fn test_command_args_from_vec() {
485 let vec = vec!["x".to_string(), "y".to_string()];
486 let args: CommandArgs = vec.into();
487 assert_eq!(args.len(), 2);
488 }
489
490 #[test]
491 fn test_command_args_from_slice() {
492 let slice: &[&str] = &["p", "q", "r"];
493 let args: CommandArgs = slice.into();
494 assert_eq!(args.len(), 3);
495 }
496
497 #[test]
498 fn test_compiled_pattern_regex() {
499 let pattern = CompiledPattern::new(r"\d+").unwrap();
500 let regex = pattern.regex();
501 assert!(regex.is_match("123"));
502 }
503
504 #[test]
505 fn test_git_ref_as_ref() {
506 let git_ref = GitRef::new("main");
507 let s: &str = git_ref.as_ref();
508 assert_eq!(s, "main");
509 }
510
511 #[test]
512 fn test_git_ref_from_string() {
513 let git_ref: GitRef = String::from("develop").into();
514 assert_eq!(git_ref.as_str(), "develop");
515 }
516
517 #[test]
518 fn test_rule_id_as_ref() {
519 let rule_id = RuleId::new("PE-001");
520 let s: &str = rule_id.as_ref();
521 assert_eq!(s, "PE-001");
522 }
523
524 #[test]
525 fn test_rule_id_from_string() {
526 let rule_id: RuleId = String::from("EX-001").into();
527 assert_eq!(rule_id.as_str(), "EX-001");
528 }
529
530 #[test]
531 fn test_file_hash_from_owned_string() {
532 let hash: FileHash = String::from("hash123").into();
533 assert_eq!(hash.as_str(), "hash123");
534 }
535}