1use crate::parser::FafFile;
4use crate::types::*;
5
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum CompressionLevel {
9 Minimal = 1,
11 Standard = 2,
13 Full = 3,
15}
16
17pub fn compress(faf: &FafFile, level: CompressionLevel) -> FafData {
45 match level {
46 CompressionLevel::Minimal => compress_minimal(faf),
47 CompressionLevel::Standard => compress_standard(faf),
48 CompressionLevel::Full => faf.data.clone(),
49 }
50}
51
52fn compress_minimal(faf: &FafFile) -> FafData {
53 FafData {
54 faf_version: faf.data.faf_version.clone(),
55 project: Project {
56 name: faf.data.project.name.clone(),
57 goal: faf.data.project.goal.clone(),
58 main_language: None,
59 approach: None,
60 version: None,
61 license: None,
62 },
63 ai_score: None,
64 ai_confidence: None,
65 ai_tldr: None,
66 instant_context: faf.data.instant_context.as_ref().map(|ic| InstantContext {
67 what_building: None,
68 tech_stack: ic.tech_stack.clone(),
69 deployment: None,
70 key_files: Vec::new(),
71 commands: Default::default(),
72 }),
73 context_quality: None,
74 stack: None,
75 human_context: None,
76 preferences: None,
77 state: None,
78 tags: Vec::new(),
79 commands: Default::default(),
80 security: None,
81 ai_instructions: None,
82 conventions: Vec::new(),
83 }
84}
85
86fn compress_standard(faf: &FafFile) -> FafData {
87 FafData {
88 faf_version: faf.data.faf_version.clone(),
89 project: faf.data.project.clone(),
90 ai_score: faf.data.ai_score.clone(),
91 ai_confidence: None,
92 ai_tldr: None,
93 instant_context: faf.data.instant_context.as_ref().map(|ic| InstantContext {
94 what_building: ic.what_building.clone(),
95 tech_stack: ic.tech_stack.clone(),
96 deployment: None,
97 key_files: ic.key_files.iter().take(5).cloned().collect(),
98 commands: Default::default(),
99 }),
100 context_quality: None,
101 stack: faf.data.stack.clone(),
102 human_context: None,
103 preferences: None,
104 state: None,
105 tags: Vec::new(),
106 commands: Default::default(),
107 security: None,
108 ai_instructions: None,
109 conventions: Vec::new(),
110 }
111}
112
113pub fn estimate_tokens(level: CompressionLevel) -> usize {
115 match level {
116 CompressionLevel::Minimal => 150,
117 CompressionLevel::Standard => 400,
118 CompressionLevel::Full => 800,
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::parse;
126
127 #[test]
128 fn test_compress_minimal() {
129 let content = r#"
130faf_version: 2.5.0
131project:
132 name: test
133 goal: Testing
134instant_context:
135 what_building: App
136 tech_stack: Rust
137 key_files:
138 - a.rs
139 - b.rs
140stack:
141 backend: Rust
142human_context:
143 who: Devs
144"#;
145 let faf = parse(content).unwrap();
146 let compressed = compress(&faf, CompressionLevel::Minimal);
147
148 assert_eq!(compressed.project.name, "test");
149 assert!(
150 compressed
151 .instant_context
152 .as_ref()
153 .unwrap()
154 .tech_stack
155 .is_some()
156 );
157 assert!(compressed.stack.is_none());
158 assert!(compressed.human_context.is_none());
159 }
160
161 #[test]
162 fn test_compress_standard() {
163 let content = r#"
164faf_version: 2.5.0
165project:
166 name: test
167 goal: Testing
168instant_context:
169 what_building: App
170 tech_stack: Rust
171 key_files:
172 - a.rs
173 - b.rs
174 - c.rs
175 - d.rs
176 - e.rs
177 - f.rs
178 - g.rs
179stack:
180 backend: Rust
181human_context:
182 who: Devs
183"#;
184 let faf = parse(content).unwrap();
185 let compressed = compress(&faf, CompressionLevel::Standard);
186
187 assert!(compressed.stack.is_some());
188 assert_eq!(
190 compressed.instant_context.as_ref().unwrap().key_files.len(),
191 5
192 );
193 assert!(compressed.human_context.is_none());
195 }
196}