whiteout/transform/
smudge.rs1use 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 let parser = Parser::new();
17 let mut decorations = parser.parse(content)?;
18
19 if decorations.is_empty() {
20 return Ok(content.to_string());
21 }
22
23 for decoration in &mut decorations {
24 match decoration {
25 Decoration::Inline { line, local_value, .. } => {
26 if let Ok(stored_value) = storage.get_value(
27 file_path,
28 &format!("inline_{}", line),
29 ) {
30 *local_value = stored_value;
31 }
32 }
33 Decoration::Block { start_line, local_content, .. } => {
34 if let Ok(stored_value) = storage.get_value(
35 file_path,
36 &format!("block_{}", start_line),
37 ) {
38 *local_content = stored_value;
39 }
40 }
41 Decoration::Partial { line, replacements } => {
42 for (idx, replacement) in replacements.iter_mut().enumerate() {
43 let mut found = false;
46 for line_offset in 0..=5 {
47 for try_line in [*line + line_offset, line.saturating_sub(line_offset)] {
48 if let Ok(stored_value) = storage.get_value(
49 file_path,
50 &format!("partial_{}_{}", try_line, idx),
51 ) {
52 replacement.local_value = stored_value;
53 found = true;
54 break;
55 }
56 }
57 if found { break; }
58 }
59 }
60 }
61 }
62 }
63
64 let smudged = parser.apply_decorations(content, &decorations, true);
65
66 Ok(smudged)
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use tempfile::TempDir;
73
74 #[test]
75 fn test_smudge_inline() -> Result<()> {
76 let temp_dir = TempDir::new()?;
77 let storage = LocalStorage::new(temp_dir.path())?;
78 let config = Config::default();
79 let file_path = Path::new("test.rs");
80
81 storage.store_value(file_path, "inline_1", "let api_key = \"sk-12345\";")?;
82
83 let content = r#"let api_key = "ENV_VAR"; // @whiteout: "ENV_VAR""#;
84
85 let smudged = apply(content, file_path, &storage, &config)?;
86 assert!(smudged.contains("sk-12345"));
87 assert!(smudged.contains("@whiteout:"));
89
90 Ok(())
91 }
92
93 #[test]
94 fn test_smudge_block() -> Result<()> {
95 let temp_dir = TempDir::new()?;
96 let storage = LocalStorage::new(temp_dir.path())?;
97 let config = Config::default();
98 let file_path = Path::new("test.rs");
99
100 storage.store_value(file_path, "block_2", "const DEBUG = true;")?;
101
102 let content = r#"
103// @whiteout-start
104const DEBUG = false;
105// @whiteout-end
106const DEBUG = false;
107"#;
108
109 let smudged = apply(content, file_path, &storage, &config)?;
110 assert!(smudged.contains("true"));
111
112 Ok(())
113 }
114}