ccsync_core/config/
validation.rs1use super::types::Config;
4use crate::error::Result;
5
6pub struct ConfigValidator;
8
9impl Default for ConfigValidator {
10 fn default() -> Self {
11 Self::new()
12 }
13}
14
15impl ConfigValidator {
16 #[must_use]
18 pub const fn new() -> Self {
19 Self
20 }
21
22 pub fn validate(config: &Config) -> Result<()> {
28 if config.follow_symlinks == Some(true) && config.preserve_symlinks == Some(true) {
30 anyhow::bail!(
31 "Conflicting configuration: both follow_symlinks and preserve_symlinks are enabled"
32 );
33 }
34
35 for pattern in &config.ignore {
37 if pattern.trim().is_empty() {
38 anyhow::bail!("Ignore pattern cannot be empty");
39 }
40 }
41
42 for pattern in &config.include {
43 if pattern.trim().is_empty() {
44 anyhow::bail!("Include pattern cannot be empty");
45 }
46 }
47
48 for (idx, rule) in config.rules.iter().enumerate() {
50 if rule.patterns.is_empty() {
51 anyhow::bail!("Rule #{} has no patterns", idx + 1);
52 }
53
54 for pattern in &rule.patterns {
55 if pattern.trim().is_empty() {
56 anyhow::bail!("Rule #{} has empty pattern", idx + 1);
57 }
58 }
59 }
60
61 Ok(())
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::config::types::{FileType, SyncDirection, SyncRule};
69
70 #[test]
71 fn test_validate_empty_config() {
72 let config = Config::default();
73 let _validator = ConfigValidator::new();
74
75 assert!(ConfigValidator::validate(&config).is_ok());
76 }
77
78 #[test]
79 fn test_validate_conflicting_symlink_settings() {
80 let mut config = Config::default();
81 config.follow_symlinks = Some(true);
82 config.preserve_symlinks = Some(true);
83
84 let _validator = ConfigValidator::new();
85 let result = ConfigValidator::validate(&config);
86
87 assert!(result.is_err());
88 assert!(
89 result
90 .unwrap_err()
91 .to_string()
92 .contains("follow_symlinks and preserve_symlinks")
93 );
94 }
95
96 #[test]
97 fn test_validate_empty_pattern() {
98 let mut config = Config::default();
99 config.ignore.push(" ".to_string());
100
101 let _validator = ConfigValidator::new();
102 let result = ConfigValidator::validate(&config);
103
104 assert!(result.is_err());
105 assert!(result.unwrap_err().to_string().contains("cannot be empty"));
106 }
107
108 #[test]
109 fn test_validate_rule_with_no_patterns() {
110 let mut config = Config::default();
111 config.rules.push(SyncRule {
112 patterns: vec![],
113 direction: Some(SyncDirection::ToLocal),
114 file_type: Some(FileType::Text),
115 include: true,
116 });
117
118 let _validator = ConfigValidator::new();
119 let result = ConfigValidator::validate(&config);
120
121 assert!(result.is_err());
122 assert!(result.unwrap_err().to_string().contains("has no patterns"));
123 }
124
125 #[test]
126 fn test_validate_valid_config() {
127 let mut config = Config::default();
128 config.ignore.push("*.tmp".to_string());
129 config.include.push("important.tmp".to_string());
130 config.follow_symlinks = Some(false);
131 config.preserve_symlinks = Some(false);
132
133 let _validator = ConfigValidator::new();
134 assert!(ConfigValidator::validate(&config).is_ok());
135 }
136}