1use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::time::Duration;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct FidelityConfig {
14 pub preservation_level: PreservationLevel,
16 pub comment_preservation: CommentPreservationConfig,
18 pub processing_instruction_preservation: bool,
20 pub extension_preservation: ExtensionPreservationConfig,
22 pub attribute_preservation: AttributePreservationConfig,
24 pub namespace_preservation: NamespacePreservationConfig,
26 pub whitespace_preservation: WhitespacePreservationConfig,
28}
29
30impl Default for FidelityConfig {
31 fn default() -> Self {
32 Self {
33 preservation_level: PreservationLevel::Standard,
34 comment_preservation: CommentPreservationConfig::default(),
35 processing_instruction_preservation: false,
36 extension_preservation: ExtensionPreservationConfig::default(),
37 attribute_preservation: AttributePreservationConfig::default(),
38 namespace_preservation: NamespacePreservationConfig::default(),
39 whitespace_preservation: WhitespacePreservationConfig::default(),
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46pub enum PreservationLevel {
47 Basic,
49 Standard,
51 Perfect,
53 Custom,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct CommentPreservationConfig {
60 pub enabled: bool,
62 pub preserve_document_comments: bool,
64 pub preserve_element_comments: bool,
66 pub preserve_inline_comments: bool,
68 pub preserve_comment_positioning: bool,
70 pub max_comment_length: usize,
72}
73
74impl Default for CommentPreservationConfig {
75 fn default() -> Self {
76 Self {
77 enabled: false,
78 preserve_document_comments: true,
79 preserve_element_comments: true,
80 preserve_inline_comments: false,
81 preserve_comment_positioning: true,
82 max_comment_length: 0,
83 }
84 }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct ExtensionPreservationConfig {
90 pub enabled: bool,
92 pub known_extensions: Vec<String>,
94 pub unknown_extension_handling: UnknownExtensionHandling,
96 pub preserve_extension_attributes: bool,
98 pub extension_validation: ExtensionValidationConfig,
100}
101
102impl Default for ExtensionPreservationConfig {
103 fn default() -> Self {
104 Self {
105 enabled: true,
106 known_extensions: vec![
107 "http://spotify.com/ddex".to_string(),
108 "http://apple.com/ddex".to_string(),
109 "http://youtube.com/ddex".to_string(),
110 "http://amazon.com/ddex".to_string(),
111 ],
112 unknown_extension_handling: UnknownExtensionHandling::Preserve,
113 preserve_extension_attributes: true,
114 extension_validation: ExtensionValidationConfig::default(),
115 }
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
121pub enum UnknownExtensionHandling {
122 Preserve,
124 Drop,
126 ValidateAndPreserve,
128 Generalize,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct ExtensionValidationConfig {
135 pub validate_uris: bool,
137 pub validate_schema: bool,
139 pub max_nesting_depth: usize,
141 pub max_extension_count: usize,
143}
144
145impl Default for ExtensionValidationConfig {
146 fn default() -> Self {
147 Self {
148 validate_uris: true,
149 validate_schema: false,
150 max_nesting_depth: 10,
151 max_extension_count: 1000,
152 }
153 }
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct AttributePreservationConfig {
159 pub preserve_ordering: bool,
161 pub preserve_unknown_attributes: bool,
163 pub preserve_empty_attributes: bool,
165 pub normalization: AttributeNormalizationConfig,
167 pub validation: AttributeValidationConfig,
169}
170
171impl Default for AttributePreservationConfig {
172 fn default() -> Self {
173 Self {
174 preserve_ordering: false,
175 preserve_unknown_attributes: true,
176 preserve_empty_attributes: false,
177 normalization: AttributeNormalizationConfig::default(),
178 validation: AttributeValidationConfig::default(),
179 }
180 }
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct AttributeNormalizationConfig {
186 pub normalize_booleans: bool,
188 pub normalize_numbers: bool,
190 pub normalize_datetime: bool,
192 pub normalize_whitespace: bool,
194}
195
196impl Default for AttributeNormalizationConfig {
197 fn default() -> Self {
198 Self {
199 normalize_booleans: true,
200 normalize_numbers: true,
201 normalize_datetime: true,
202 normalize_whitespace: true,
203 }
204 }
205}
206
207#[derive(Debug, Clone, Serialize, Deserialize)]
209pub struct AttributeValidationConfig {
210 pub validate_formats: bool,
212 pub validate_constraints: bool,
214 pub custom_rules: HashMap<String, AttributeValidationRule>,
216}
217
218impl Default for AttributeValidationConfig {
219 fn default() -> Self {
220 Self {
221 validate_formats: true,
222 validate_constraints: true,
223 custom_rules: HashMap::new(),
224 }
225 }
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct AttributeValidationRule {
231 pub pattern: Option<String>,
233 pub min_value: Option<f64>,
235 pub max_value: Option<f64>,
237 pub allowed_values: Option<Vec<String>>,
239 pub validation_message: Option<String>,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct NamespacePreservationConfig {
246 pub preserve_prefixes: bool,
248 pub preserve_unused_declarations: bool,
250 pub minimization_strategy: NamespaceMinimizationStrategy,
252 pub default_namespace_handling: DefaultNamespaceHandling,
254}
255
256impl Default for NamespacePreservationConfig {
257 fn default() -> Self {
258 Self {
259 preserve_prefixes: false,
260 preserve_unused_declarations: false,
261 minimization_strategy: NamespaceMinimizationStrategy::Aggressive,
262 default_namespace_handling: DefaultNamespaceHandling::Preserve,
263 }
264 }
265}
266
267#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
269pub enum NamespaceMinimizationStrategy {
270 None,
272 Conservative,
274 Aggressive,
276 Custom(NamespaceMinimizationRules),
278}
279
280#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
282pub struct NamespaceMinimizationRules {
283 pub always_preserve: Vec<String>,
285 pub can_remove: Vec<String>,
287 pub preferred_prefixes: HashMap<String, String>,
289}
290
291#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
293pub enum DefaultNamespaceHandling {
294 Preserve,
296 Remove,
298 ConvertToPrefix,
300}
301
302#[derive(Debug, Clone, Serialize, Deserialize)]
304pub struct WhitespacePreservationConfig {
305 pub preserve_significant: bool,
307 pub preserve_formatting: bool,
309 pub normalize_line_endings: bool,
311 pub line_ending_style: LineEndingStyle,
313 pub indentation_style: IndentationStyle,
315}
316
317impl Default for WhitespacePreservationConfig {
318 fn default() -> Self {
319 Self {
320 preserve_significant: true,
321 preserve_formatting: false,
322 normalize_line_endings: true,
323 line_ending_style: LineEndingStyle::Unix,
324 indentation_style: IndentationStyle::Spaces(2),
325 }
326 }
327}
328
329#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
331pub enum LineEndingStyle {
332 Unix,
334 Windows,
336 Mac,
338 Preserve,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
344pub enum IndentationStyle {
345 Spaces(usize),
347 Tabs,
349 None,
351}
352
353#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct FidelityStatistics {
356 pub elements_preserved: HashMap<String, usize>,
358 pub attributes_preserved: HashMap<String, usize>,
360 pub comments_preserved: usize,
362 pub processing_instructions_preserved: usize,
364 pub extensions_preserved: HashMap<String, usize>,
366 pub namespaces_processed: usize,
368 pub processing_time: Duration,
370 pub memory_usage: usize,
372}
373
374impl Default for FidelityStatistics {
375 fn default() -> Self {
376 Self {
377 elements_preserved: HashMap::new(),
378 attributes_preserved: HashMap::new(),
379 comments_preserved: 0,
380 processing_instructions_preserved: 0,
381 extensions_preserved: HashMap::new(),
382 namespaces_processed: 0,
383 processing_time: Duration::ZERO,
384 memory_usage: 0,
385 }
386 }
387}
388
389#[derive(Debug, Clone, Serialize, Deserialize)]
391pub struct FidelityResult {
392 pub success: bool,
394 pub achieved_level: PreservationLevel,
396 pub statistics: FidelityStatistics,
398 pub issues: Vec<FidelityIssue>,
400 pub recommendations: Vec<String>,
402}
403
404#[derive(Debug, Clone, Serialize, Deserialize)]
406pub struct FidelityIssue {
407 pub severity: FidelitySeverity,
409 pub category: String,
411 pub message: String,
413 pub path: Option<String>,
415 pub suggestion: Option<String>,
417}
418
419#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
421pub enum FidelitySeverity {
422 Critical,
424 Major,
426 Minor,
428 Info,
430}
431
432#[cfg(test)]
433mod tests {
434 use super::*;
435
436 #[test]
437 fn test_fidelity_config_default() {
438 let config = FidelityConfig::default();
439 assert_eq!(config.preservation_level, PreservationLevel::Standard);
440 assert!(!config.comment_preservation.enabled);
441 assert!(!config.processing_instruction_preservation);
442 assert!(config.extension_preservation.enabled);
443 }
444
445 #[test]
446 fn test_preservation_levels() {
447 let basic = PreservationLevel::Basic;
448 let standard = PreservationLevel::Standard;
449 let perfect = PreservationLevel::Perfect;
450 let custom = PreservationLevel::Custom;
451
452 assert_ne!(basic, standard);
453 assert_ne!(standard, perfect);
454 assert_ne!(perfect, custom);
455 }
456
457 #[test]
458 fn test_comment_preservation_config() {
459 let mut config = CommentPreservationConfig::default();
460 assert!(!config.enabled);
461
462 config.enabled = true;
463 assert!(config.enabled);
464 assert!(config.preserve_document_comments);
465 assert!(config.preserve_element_comments);
466 assert!(!config.preserve_inline_comments);
467 }
468
469 #[test]
470 fn test_extension_preservation() {
471 let config = ExtensionPreservationConfig::default();
472 assert!(config.enabled);
473 assert!(!config.known_extensions.is_empty());
474 assert_eq!(
475 config.unknown_extension_handling,
476 UnknownExtensionHandling::Preserve
477 );
478 }
479
480 #[test]
481 fn test_namespace_minimization() {
482 let strategy = NamespaceMinimizationStrategy::Aggressive;
483 match strategy {
484 NamespaceMinimizationStrategy::Aggressive => {}
485 _ => panic!("Expected Aggressive strategy"),
486 }
487 }
488}