1use crate::equivalence::EquivalenceMapper;
4use crate::normalizer::NormalizationConfig;
5
6use lnmp_core::StructuralLimits;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
10pub enum ParsingMode {
11 Strict,
13 #[default]
15 Loose,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum TextInputMode {
23 Strict,
25 Lenient,
27}
28
29#[derive(Debug, Clone)]
31pub struct ParserConfig {
32 pub mode: ParsingMode,
34 pub validate_checksums: bool,
36 pub normalize_values: bool,
38 pub require_checksums: bool,
40 pub max_nesting_depth: Option<usize>,
42 pub text_input_mode: TextInputMode,
44 pub structural_limits: Option<StructuralLimits>,
46 pub semantic_dictionary: Option<lnmp_sfe::SemanticDictionary>,
48}
49
50impl Default for ParserConfig {
51 fn default() -> Self {
52 Self {
53 mode: ParsingMode::Loose,
54 validate_checksums: false,
55 normalize_values: true,
56 require_checksums: false,
57 max_nesting_depth: None,
58 text_input_mode: TextInputMode::Strict,
59 structural_limits: None,
60 semantic_dictionary: None,
61 }
62 }
63}
64
65impl ParserConfig {
66 pub fn with_structural_limits(mut self, limits: StructuralLimits) -> Self {
68 self.structural_limits = Some(limits);
69 self
70 }
71
72 pub fn with_semantic_dictionary(mut self, dict: lnmp_sfe::SemanticDictionary) -> Self {
74 self.semantic_dictionary = Some(dict);
75 self
76 }
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
81pub struct PromptOptimizationConfig {
82 pub minimize_symbols: bool,
84 pub align_token_boundaries: bool,
86 pub optimize_arrays: bool,
88}
89
90#[derive(Debug, Clone)]
94pub struct EncoderConfig {
95 pub include_type_hints: bool,
97 pub canonical: bool,
99 pub enable_checksums: bool,
101 pub enable_explain_mode: bool,
103 pub prompt_optimization: PromptOptimizationConfig,
105 pub normalization_config: NormalizationConfig,
107 pub equivalence_mapper: Option<EquivalenceMapper>,
109 pub semantic_dictionary: Option<lnmp_sfe::SemanticDictionary>,
111}
112
113impl Default for EncoderConfig {
114 fn default() -> Self {
115 Self {
116 include_type_hints: false,
117 canonical: true,
118 enable_checksums: false,
119 enable_explain_mode: false,
120 prompt_optimization: PromptOptimizationConfig::default(),
121 normalization_config: NormalizationConfig::default(),
122 equivalence_mapper: None,
123 semantic_dictionary: None,
124 }
125 }
126}
127
128impl EncoderConfig {
129 pub fn new() -> Self {
131 Self::default()
132 }
133
134 pub fn with_checksums(mut self, enable: bool) -> Self {
136 self.enable_checksums = enable;
137 self
138 }
139
140 pub fn with_explain_mode(mut self, enable: bool) -> Self {
142 self.enable_explain_mode = enable;
143 self
144 }
145
146 pub fn with_prompt_optimization(mut self, config: PromptOptimizationConfig) -> Self {
148 self.prompt_optimization = config;
149 self
150 }
151
152 pub fn with_normalization(mut self, config: NormalizationConfig) -> Self {
154 self.normalization_config = config;
155 self
156 }
157
158 pub fn with_equivalence_mapper(mut self, mapper: EquivalenceMapper) -> Self {
160 self.equivalence_mapper = Some(mapper);
161 self
162 }
163
164 pub fn with_semantic_dictionary(mut self, dict: lnmp_sfe::SemanticDictionary) -> Self {
166 self.semantic_dictionary = Some(dict);
167 self
168 }
169
170 pub fn with_type_hints(mut self, enable: bool) -> Self {
172 self.include_type_hints = enable;
173 self
174 }
175
176 pub fn with_canonical(mut self, enable: bool) -> Self {
178 self.canonical = enable;
179 self
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
188 fn test_parsing_mode_default() {
189 assert_eq!(ParsingMode::default(), ParsingMode::Loose);
190 }
191
192 #[test]
193 fn test_encoder_config_default() {
194 let config = EncoderConfig::default();
195 assert!(!config.include_type_hints);
196 assert!(config.canonical);
197 assert!(!config.enable_checksums);
198 assert!(!config.enable_explain_mode);
199 assert!(!config.prompt_optimization.minimize_symbols);
200 assert!(!config.prompt_optimization.align_token_boundaries);
201 assert!(!config.prompt_optimization.optimize_arrays);
202 assert!(config.equivalence_mapper.is_none());
203 }
204
205 #[test]
206 fn test_parsing_mode_equality() {
207 assert_eq!(ParsingMode::Strict, ParsingMode::Strict);
208 assert_eq!(ParsingMode::Loose, ParsingMode::Loose);
209 assert_ne!(ParsingMode::Strict, ParsingMode::Loose);
210 }
211
212 #[test]
213 fn test_encoder_config_with_checksums() {
214 let config = EncoderConfig::new().with_checksums(true);
215 assert!(config.enable_checksums);
216 }
217
218 #[test]
219 fn test_encoder_config_with_explain_mode() {
220 let config = EncoderConfig::new().with_explain_mode(true);
221 assert!(config.enable_explain_mode);
222 }
223
224 #[test]
225 fn test_encoder_config_with_prompt_optimization() {
226 let prompt_opt = PromptOptimizationConfig {
227 minimize_symbols: true,
228 align_token_boundaries: true,
229 optimize_arrays: true,
230 };
231 let config = EncoderConfig::new().with_prompt_optimization(prompt_opt);
232 assert!(config.prompt_optimization.minimize_symbols);
233 assert!(config.prompt_optimization.align_token_boundaries);
234 assert!(config.prompt_optimization.optimize_arrays);
235 }
236
237 #[test]
238 fn test_encoder_config_with_normalization() {
239 use crate::normalizer::StringCaseRule;
240
241 let norm_config = NormalizationConfig {
242 string_case: StringCaseRule::Lower,
243 float_precision: Some(2),
244 remove_trailing_zeros: true,
245 semantic_dictionary: None,
246 };
247 let config = EncoderConfig::new().with_normalization(norm_config.clone());
248 assert_eq!(
249 config.normalization_config.string_case,
250 StringCaseRule::Lower
251 );
252 assert_eq!(config.normalization_config.float_precision, Some(2));
253 assert!(config.normalization_config.remove_trailing_zeros);
254 }
255
256 #[test]
257 fn test_encoder_config_with_equivalence_mapper() {
258 let mut mapper = EquivalenceMapper::new();
259 mapper.add_mapping(7, "yes".to_string(), "1".to_string());
260
261 let config = EncoderConfig::new().with_equivalence_mapper(mapper);
262 assert!(config.equivalence_mapper.is_some());
263
264 let mapper_ref = config.equivalence_mapper.as_ref().unwrap();
265 assert_eq!(mapper_ref.map(7, "yes"), Some("1".to_string()));
266 }
267
268 #[test]
269 fn test_encoder_config_with_type_hints() {
270 let config = EncoderConfig::new().with_type_hints(true);
271 assert!(config.include_type_hints);
272 }
273
274 #[test]
275 fn test_encoder_config_with_canonical() {
276 let config = EncoderConfig::new().with_canonical(false);
277 assert!(!config.canonical);
278 }
279
280 #[test]
281 fn test_encoder_config_builder_chain() {
282 let mut mapper = EquivalenceMapper::new();
283 mapper.add_mapping(7, "yes".to_string(), "1".to_string());
284
285 let config = EncoderConfig::new()
286 .with_checksums(true)
287 .with_explain_mode(true)
288 .with_type_hints(true)
289 .with_equivalence_mapper(mapper);
290
291 assert!(config.enable_checksums);
292 assert!(config.enable_explain_mode);
293 assert!(config.include_type_hints);
294 assert!(config.equivalence_mapper.is_some());
295 }
296
297 #[test]
298 fn test_prompt_optimization_config_default() {
299 let config = PromptOptimizationConfig::default();
300 assert!(!config.minimize_symbols);
301 assert!(!config.align_token_boundaries);
302 assert!(!config.optimize_arrays);
303 }
304
305 #[test]
306 fn test_parser_config_default() {
307 let config = ParserConfig::default();
308 assert_eq!(config.mode, ParsingMode::Loose);
309 assert!(!config.validate_checksums);
310 assert!(!config.require_checksums);
311 assert!(config.max_nesting_depth.is_none());
312 assert_eq!(config.text_input_mode, TextInputMode::Strict);
313 assert!(config.structural_limits.is_none());
314 }
315
316 #[test]
317 fn test_parser_config_with_checksum_validation() {
318 let config = ParserConfig {
319 mode: ParsingMode::Strict,
320 validate_checksums: true,
321 normalize_values: false,
322 require_checksums: false,
323 max_nesting_depth: None,
324 text_input_mode: TextInputMode::Strict,
325 structural_limits: None,
326 semantic_dictionary: None,
327 };
328 assert_eq!(config.mode, ParsingMode::Strict);
329 assert!(config.validate_checksums);
330 assert!(!config.require_checksums);
331 }
332
333 #[test]
334 fn test_parser_config_with_required_checksums() {
335 let config = ParserConfig {
336 mode: ParsingMode::Strict,
337 validate_checksums: true,
338 normalize_values: false,
339 require_checksums: true,
340 max_nesting_depth: None,
341 text_input_mode: TextInputMode::Strict,
342 structural_limits: None,
343 semantic_dictionary: None,
344 };
345 assert!(config.validate_checksums);
346 assert!(config.require_checksums);
347 }
348
349 #[test]
350 fn test_parser_config_with_structural_limits() {
351 let limits = StructuralLimits {
352 max_fields: 2,
353 ..Default::default()
354 };
355 let config = ParserConfig::default().with_structural_limits(limits.clone());
356 assert_eq!(
357 config.structural_limits.unwrap().max_fields,
358 limits.max_fields
359 );
360 }
361}