whiteout/transform/
clean.rs

1use anyhow::Result;
2use std::path::Path;
3
4use crate::{
5    config::Config,
6    parser::{Decoration, Parser},
7    storage::LocalStorage,
8};
9
10pub fn apply(
11    content: &str,
12    file_path: &Path,
13    storage: &LocalStorage,
14    _config: &Config,
15) -> Result<String> {
16    // Clean filter stores local values and returns content with committed values
17    // but preserves decoration markers so smudge can work later
18    
19    let parser = Parser::new();
20    let decorations = parser.parse(content)?;
21    
22    if decorations.is_empty() {
23        return Ok(content.to_string());
24    }
25    
26    // Store all local values
27    for decoration in &decorations {
28        match decoration {
29            Decoration::Inline { line, local_value, .. } => {
30                storage.store_value(
31                    file_path,
32                    &format!("inline_{}", line),
33                    local_value,
34                )?;
35            }
36            Decoration::Block { start_line, local_content, .. } => {
37                storage.store_value(
38                    file_path,
39                    &format!("block_{}", start_line),
40                    local_content,
41                )?;
42            }
43            Decoration::Partial { line, replacements } => {
44                for (idx, replacement) in replacements.iter().enumerate() {
45                    storage.store_value(
46                        file_path,
47                        &format!("partial_{}_{}", line, idx),
48                        &replacement.local_value,
49                    )?;
50                }
51            }
52        }
53    }
54    
55    // Apply transformations to remove local values and keep only committed values
56    // This is what gets stored in Git
57    let cleaned = parser.apply_decorations(content, &decorations, false);
58    Ok(cleaned)
59}
60
61#[cfg(test)]
62mod tests {
63    use super::*;
64    use tempfile::TempDir;
65
66    #[test]
67    fn test_clean_inline() -> Result<()> {
68        let temp_dir = TempDir::new()?;
69        let storage = LocalStorage::new(temp_dir.path())?;
70        let config = Config::default();
71        
72        let content = r#"let api_key = "sk-12345"; // @whiteout: "ENV_VAR""#;
73        let file_path = Path::new("test.rs");
74        
75        let cleaned = apply(content, file_path, &storage, &config)?;
76        assert!(cleaned.contains("ENV_VAR"));
77        assert!(!cleaned.contains("sk-12345"));
78        
79        Ok(())
80    }
81
82    #[test]
83    fn test_clean_block() -> Result<()> {
84        let temp_dir = TempDir::new()?;
85        let storage = LocalStorage::new(temp_dir.path())?;
86        let config = Config::default();
87        
88        let content = r#"
89// @whiteout-start
90const DEBUG = true;
91// @whiteout-end
92const DEBUG = false;
93"#;
94        let file_path = Path::new("test.rs");
95        
96        let cleaned = apply(content, file_path, &storage, &config)?;
97        assert!(cleaned.contains("false"));
98        assert!(!cleaned.contains("true"));
99        
100        Ok(())
101    }
102}