1use crate::error::ParseError;
13use crate::parser::parse_text_magic_file;
14
15#[cfg(test)]
17use crate::parser::codegen::{
18 format_byte_vec, format_number, generate_builtin_rules, serialize_children,
19 serialize_endianness, serialize_offset_spec, serialize_operator, serialize_type_kind,
20 serialize_value,
21};
22
23pub fn parse_and_generate_builtin_rules(magic_content: &str) -> Result<String, ParseError> {
32 let parsed = parse_text_magic_file(magic_content)?;
33 Ok(crate::parser::codegen::generate_builtin_rules(
34 &parsed.rules,
35 ))
36}
37
38#[must_use]
43pub fn format_parse_error(error: &ParseError) -> String {
44 match error {
45 ParseError::InvalidSyntax { line, message } => {
46 format!("Error parsing builtin_rules.magic at line {line}: {message}")
47 }
48 ParseError::UnsupportedFeature { line, feature } => {
49 format!("Error parsing builtin_rules.magic at line {line}: {feature}")
50 }
51 ParseError::InvalidOffset { line, offset } => {
52 format!("Error parsing builtin_rules.magic at line {line}: {offset}")
53 }
54 ParseError::InvalidType { line, type_spec } => {
55 format!("Error parsing builtin_rules.magic at line {line}: {type_spec}")
56 }
57 ParseError::InvalidOperator { line, operator } => {
58 format!("Error parsing builtin_rules.magic at line {line}: {operator}")
59 }
60 ParseError::InvalidValue { line, value } => {
61 format!("Error parsing builtin_rules.magic at line {line}: {value}")
62 }
63 ParseError::UnsupportedFormat {
64 line,
65 format_type,
66 message,
67 } => format!("Error parsing builtin_rules.magic at line {line}: {format_type} {message}"),
68 ParseError::IoError(err) => {
69 format!("Error parsing builtin_rules.magic: I/O error: {err}")
70 }
71 }
72}
73
74#[cfg(test)]
75mod tests {
76 use super::*;
77 use crate::parser::ast::{
78 Endianness, IndirectAdjustmentOp, MagicRule, OffsetSpec, Operator, SearchFlags,
79 StringFlags, TypeKind, Value,
80 };
81 use crate::parser::codegen::format_string_literal;
82
83 #[test]
84 fn test_format_parse_error_invalid_syntax() {
85 let error = ParseError::InvalidSyntax {
86 line: 42,
87 message: "expected offset".to_string(),
88 };
89 let formatted = format_parse_error(&error);
90 assert!(formatted.contains("line 42"));
91 assert!(formatted.contains("expected offset"));
92 assert!(formatted.contains("builtin_rules.magic"));
93 }
94
95 #[test]
96 fn test_format_parse_error_unsupported_feature() {
97 let error = ParseError::UnsupportedFeature {
98 line: 10,
99 feature: "regex patterns".to_string(),
100 };
101 let formatted = format_parse_error(&error);
102 assert!(formatted.contains("line 10"));
103 assert!(formatted.contains("regex patterns"));
104 }
105
106 #[test]
107 fn test_format_parse_error_invalid_offset() {
108 let error = ParseError::InvalidOffset {
109 line: 5,
110 offset: "invalid offset spec".to_string(),
111 };
112 let formatted = format_parse_error(&error);
113 assert!(formatted.contains("line 5"));
114 assert!(formatted.contains("invalid offset spec"));
115 }
116
117 #[test]
118 fn test_format_parse_error_invalid_type() {
119 let error = ParseError::InvalidType {
120 line: 7,
121 type_spec: "unknown type".to_string(),
122 };
123 let formatted = format_parse_error(&error);
124 assert!(formatted.contains("line 7"));
125 assert!(formatted.contains("unknown type"));
126 }
127
128 #[test]
129 fn test_format_parse_error_invalid_operator() {
130 let error = ParseError::InvalidOperator {
131 line: 12,
132 operator: "bad operator".to_string(),
133 };
134 let formatted = format_parse_error(&error);
135 assert!(formatted.contains("line 12"));
136 assert!(formatted.contains("bad operator"));
137 }
138
139 #[test]
140 fn test_format_parse_error_invalid_value() {
141 let error = ParseError::InvalidValue {
142 line: 15,
143 value: "malformed value".to_string(),
144 };
145 let formatted = format_parse_error(&error);
146 assert!(formatted.contains("line 15"));
147 assert!(formatted.contains("malformed value"));
148 }
149
150 #[test]
151 fn test_serialize_offset_spec_absolute() {
152 let offset = OffsetSpec::Absolute(42);
153 let serialized = serialize_offset_spec(&offset);
154 assert_eq!(serialized, "OffsetSpec::Absolute(42)");
155 }
156
157 #[test]
158 fn test_serialize_offset_spec_relative() {
159 let offset = OffsetSpec::Relative(-10);
160 let serialized = serialize_offset_spec(&offset);
161 assert_eq!(serialized, "OffsetSpec::Relative(-10)");
162 }
163
164 #[test]
165 fn test_serialize_offset_spec_from_end() {
166 let offset = OffsetSpec::FromEnd(-16);
167 let serialized = serialize_offset_spec(&offset);
168 assert_eq!(serialized, "OffsetSpec::FromEnd(-16)");
169 }
170
171 #[test]
172 fn test_serialize_type_kind_byte() {
173 let signed = TypeKind::Byte { signed: true };
174 assert_eq!(
175 serialize_type_kind(&signed),
176 "TypeKind::Byte { signed: true }"
177 );
178 let unsigned = TypeKind::Byte { signed: false };
179 assert_eq!(
180 serialize_type_kind(&unsigned),
181 "TypeKind::Byte { signed: false }"
182 );
183 }
184
185 #[test]
186 fn test_serialize_type_kind_short() {
187 let typ = TypeKind::Short {
188 endian: Endianness::Little,
189 signed: false,
190 };
191 let serialized = serialize_type_kind(&typ);
192 assert!(serialized.contains("TypeKind::Short"));
193 assert!(serialized.contains("Endianness::Little"));
194 assert!(serialized.contains("signed: false"));
195 }
196
197 #[test]
198 fn test_serialize_type_kind_long() {
199 let typ = TypeKind::Long {
200 endian: Endianness::Big,
201 signed: true,
202 };
203 let serialized = serialize_type_kind(&typ);
204 assert!(serialized.contains("TypeKind::Long"));
205 assert!(serialized.contains("Endianness::Big"));
206 assert!(serialized.contains("signed: true"));
207 }
208
209 #[test]
210 fn test_serialize_type_kind_quad() {
211 let typ = TypeKind::Quad {
212 endian: Endianness::Little,
213 signed: true,
214 };
215 let serialized = serialize_type_kind(&typ);
216 assert!(serialized.contains("TypeKind::Quad"));
217 assert!(serialized.contains("Endianness::Little"));
218 assert!(serialized.contains("signed: true"));
219
220 let typ2 = TypeKind::Quad {
221 endian: Endianness::Big,
222 signed: false,
223 };
224 let serialized2 = serialize_type_kind(&typ2);
225 assert!(serialized2.contains("TypeKind::Quad"));
226 assert!(serialized2.contains("Endianness::Big"));
227 assert!(serialized2.contains("signed: false"));
228 }
229
230 #[test]
231 fn test_serialize_type_kind_id3() {
232 let cases = [
233 (
234 TypeKind::Id3 {
235 endian: Endianness::Little,
236 },
237 "TypeKind::Id3 { endian: Endianness::Little }",
238 ),
239 (
240 TypeKind::Id3 {
241 endian: Endianness::Big,
242 },
243 "TypeKind::Id3 { endian: Endianness::Big }",
244 ),
245 ];
246 for (typ, expected) in &cases {
247 assert_eq!(serialize_type_kind(typ), *expected);
248 }
249
250 let spec = OffsetSpec::Indirect {
252 base_offset: 6,
253 base_relative: false,
254 pointer_type: TypeKind::Id3 {
255 endian: Endianness::Big,
256 },
257 adjustment: 10,
258 adjustment_op: IndirectAdjustmentOp::Add,
259 result_relative: false,
260 endian: Endianness::Big,
261 };
262 assert_eq!(
263 serialize_offset_spec(&spec),
264 "OffsetSpec::Indirect { base_offset: 6, base_relative: false, \
265 pointer_type: TypeKind::Id3 { endian: Endianness::Big }, adjustment: 10, \
266 adjustment_op: IndirectAdjustmentOp::Add, result_relative: false, \
267 endian: Endianness::Big }"
268 );
269 }
270
271 #[test]
272 fn test_serialize_type_kind_float() {
273 let cases = [
274 (
275 TypeKind::Float {
276 endian: Endianness::Native,
277 },
278 "TypeKind::Float { endian: Endianness::Native }",
279 ),
280 (
281 TypeKind::Float {
282 endian: Endianness::Little,
283 },
284 "TypeKind::Float { endian: Endianness::Little }",
285 ),
286 (
287 TypeKind::Float {
288 endian: Endianness::Big,
289 },
290 "TypeKind::Float { endian: Endianness::Big }",
291 ),
292 ];
293 for (typ, expected) in &cases {
294 assert_eq!(serialize_type_kind(typ), *expected);
295 }
296 }
297
298 #[test]
299 fn test_serialize_type_kind_double() {
300 let cases = [
301 (
302 TypeKind::Double {
303 endian: Endianness::Native,
304 },
305 "TypeKind::Double { endian: Endianness::Native }",
306 ),
307 (
308 TypeKind::Double {
309 endian: Endianness::Little,
310 },
311 "TypeKind::Double { endian: Endianness::Little }",
312 ),
313 (
314 TypeKind::Double {
315 endian: Endianness::Big,
316 },
317 "TypeKind::Double { endian: Endianness::Big }",
318 ),
319 ];
320 for (typ, expected) in &cases {
321 assert_eq!(serialize_type_kind(typ), *expected);
322 }
323 }
324
325 #[test]
326 fn test_serialize_value_float() {
327 let serialized = serialize_value(&Value::Float(3.125));
329 assert_eq!(serialized, "Value::Float(3.125)");
330
331 let serialized = serialize_value(&Value::Float(-1.0));
333 assert_eq!(serialized, "Value::Float(-1.0)");
334
335 assert_eq!(
337 serialize_value(&Value::Float(f64::NAN)),
338 "Value::Float(f64::NAN)"
339 );
340 assert_eq!(
341 serialize_value(&Value::Float(f64::INFINITY)),
342 "Value::Float(f64::INFINITY)"
343 );
344 assert_eq!(
345 serialize_value(&Value::Float(f64::NEG_INFINITY)),
346 "Value::Float(f64::NEG_INFINITY)"
347 );
348 }
349
350 #[test]
351 fn test_serialize_type_kind_string() {
352 let typ1 = TypeKind::String {
353 max_length: None,
354 flags: StringFlags::default(),
355 };
356 let serialized1 = serialize_type_kind(&typ1);
357 assert_eq!(
358 serialized1,
359 "TypeKind::String { max_length: None, flags: crate::parser::ast::StringFlags::default() }"
360 );
361
362 let typ2 = TypeKind::String {
363 max_length: Some(256),
364 flags: StringFlags::default(),
365 };
366 let serialized2 = serialize_type_kind(&typ2);
367 assert_eq!(
368 serialized2,
369 "TypeKind::String { max_length: Some(256), flags: crate::parser::ast::StringFlags::default() }"
370 );
371
372 let typ3 = TypeKind::String {
374 max_length: None,
375 flags: StringFlags::default().with_ignore_lowercase(true),
376 };
377 let serialized3 = serialize_type_kind(&typ3);
378 assert_eq!(
379 serialized3,
380 "TypeKind::String { max_length: None, flags: crate::parser::ast::StringFlags::default().with_ignore_lowercase(true) }"
381 );
382 }
383
384 #[test]
385 fn test_serialize_type_kind_pstring() {
386 use crate::parser::ast::PStringLengthWidth;
387 let typ1 = TypeKind::PString {
388 max_length: None,
389 length_width: PStringLengthWidth::OneByte,
390 length_includes_itself: false,
391 };
392 let serialized1 = serialize_type_kind(&typ1);
393 assert_eq!(
394 serialized1,
395 "TypeKind::PString { max_length: None, length_width: PStringLengthWidth::OneByte, length_includes_itself: false }"
396 );
397
398 let typ2 = TypeKind::PString {
399 max_length: Some(128),
400 length_width: PStringLengthWidth::FourByteLE,
401 length_includes_itself: false,
402 };
403 let serialized2 = serialize_type_kind(&typ2);
404 assert_eq!(
405 serialized2,
406 "TypeKind::PString { max_length: Some(128), length_width: PStringLengthWidth::FourByteLE, length_includes_itself: false }"
407 );
408 }
409
410 #[test]
411 fn test_serialize_operator() {
412 assert_eq!(serialize_operator(&Operator::Equal), "Operator::Equal");
413 assert_eq!(
414 serialize_operator(&Operator::NotEqual),
415 "Operator::NotEqual"
416 );
417 assert_eq!(
418 serialize_operator(&Operator::LessThan),
419 "Operator::LessThan"
420 );
421 assert_eq!(
422 serialize_operator(&Operator::GreaterThan),
423 "Operator::GreaterThan"
424 );
425 assert_eq!(
426 serialize_operator(&Operator::LessEqual),
427 "Operator::LessEqual"
428 );
429 assert_eq!(
430 serialize_operator(&Operator::GreaterEqual),
431 "Operator::GreaterEqual"
432 );
433 assert_eq!(
434 serialize_operator(&Operator::BitwiseAnd),
435 "Operator::BitwiseAnd"
436 );
437 assert_eq!(
438 serialize_operator(&Operator::BitwiseAndMask(0xFF)),
439 "Operator::BitwiseAndMask(255)"
440 );
441 }
442
443 #[test]
444 fn test_serialize_value_uint() {
445 let value = Value::Uint(12345);
446 let serialized = serialize_value(&value);
447 assert_eq!(serialized, "Value::Uint(12_345)");
448 }
449
450 #[test]
451 fn test_serialize_value_int() {
452 let value = Value::Int(-100);
453 let serialized = serialize_value(&value);
454 assert!(serialized.contains("Value::Int"));
455 }
456
457 #[test]
458 fn test_serialize_value_bytes() {
459 let value = Value::Bytes(vec![0x7F, 0x45, 0x4C, 0x46]);
460 let serialized = serialize_value(&value);
461 assert_eq!(serialized, "Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46])");
462 }
463
464 #[test]
465 fn test_serialize_value_string() {
466 let value = Value::String("test".to_string());
467 let serialized = serialize_value(&value);
468 assert!(serialized.contains("Value::String"));
469 assert!(serialized.contains("test"));
470 }
471
472 #[test]
473 fn test_format_number_small() {
474 assert_eq!(format_number(42), "42");
475 assert_eq!(format_number(999), "999");
476 assert_eq!(format_number(9999), "9999");
477 }
478
479 #[test]
480 fn test_format_number_large() {
481 assert_eq!(format_number(10000), "10_000");
482 assert_eq!(format_number(123_456), "123_456");
483 assert_eq!(format_number(1_234_567_890), "1_234_567_890");
484 }
485
486 #[test]
487 fn test_serialize_endianness() {
488 assert_eq!(
489 serialize_endianness(Endianness::Little),
490 "Endianness::Little"
491 );
492 assert_eq!(serialize_endianness(Endianness::Big), "Endianness::Big");
493 assert_eq!(
494 serialize_endianness(Endianness::Native),
495 "Endianness::Native"
496 );
497 }
498
499 #[test]
500 fn test_format_byte_vec_empty() {
501 let result = format_byte_vec(&[]);
502 assert_eq!(result, "vec![]");
503 }
504
505 #[test]
506 fn test_format_byte_vec_single() {
507 let result = format_byte_vec(&[0x42]);
508 assert_eq!(result, "vec![0x42]");
509 }
510
511 #[test]
512 fn test_format_byte_vec_multiple() {
513 let result = format_byte_vec(&[0x12, 0x34, 0x56]);
514 assert_eq!(result, "vec![0x12, 0x34, 0x56]");
515 }
516
517 #[test]
518 fn test_format_string_literal() {
519 assert_eq!(format_string_literal("hello"), "\"hello\"");
520 assert_eq!(format_string_literal("test\n"), "\"test\\n\"");
521 assert_eq!(format_string_literal("quote\"here"), "\"quote\\\"here\"");
522 }
523
524 #[test]
525 fn test_generate_builtin_rules_empty() {
526 let rules: Vec<MagicRule> = vec![];
527 let generated = generate_builtin_rules(&rules);
528
529 assert!(generated.contains("LazyLock<Vec<MagicRule>>"));
530 assert!(generated.contains("vec![]") || generated.contains("vec!["));
531 assert!(generated.contains("use crate::parser::ast"));
532 assert!(generated.contains("use std::sync::LazyLock"));
533 }
534
535 #[test]
536 fn test_generate_builtin_rules_single_rule() {
537 let rule = MagicRule {
538 offset: OffsetSpec::Absolute(0),
539 typ: TypeKind::Byte { signed: true },
540 op: Operator::Equal,
541 value: Value::Uint(0x7F),
542 message: "test".to_string(),
543 children: vec![],
544 level: 0,
545 strength_modifier: None,
546 value_transform: None,
547 };
548
549 let generated = generate_builtin_rules(&[rule]);
550
551 assert!(generated.contains("OffsetSpec::Absolute(0)"));
552 assert!(generated.contains("TypeKind::Byte { signed: true }"));
553 assert!(generated.contains("Operator::Equal"));
554 assert!(generated.contains("Value::Uint(127)"));
555 assert!(generated.contains("test"));
556 assert!(generated.contains("level: 0"));
557 }
558
559 #[test]
560 fn test_serialize_children_empty() {
561 let result = serialize_children(&[], 4);
562 assert_eq!(result, "Vec::new()");
563 }
564
565 #[test]
566 fn test_serialize_children_with_nested_rule() {
567 let child = MagicRule {
568 offset: OffsetSpec::Absolute(4),
569 typ: TypeKind::Byte { signed: true },
570 op: Operator::Equal,
571 value: Value::Uint(1),
572 message: "child".to_string(),
573 children: vec![],
574 level: 1,
575 strength_modifier: None,
576 value_transform: None,
577 };
578
579 let result = serialize_children(&[child], 4);
580
581 assert!(result.contains("vec!["));
582 assert!(result.contains("OffsetSpec::Absolute(4)"));
583 assert!(result.contains("level: 1"));
584 assert!(result.contains("child"));
585 }
586
587 #[test]
589 fn test_parse_and_generate_invalid_syntax() {
590 let invalid_magic = "this is not valid magic syntax";
591 let result = parse_and_generate_builtin_rules(invalid_magic);
592
593 assert!(result.is_err());
594 let error = result.unwrap_err();
595 let formatted = format_parse_error(&error);
596 assert!(formatted.contains("builtin_rules.magic"));
597 }
598
599 #[test]
600 fn test_parse_and_generate_invalid_offset() {
601 let invalid_magic = "999999999999999999999 byte =0x7F ELF";
602 let result = parse_and_generate_builtin_rules(invalid_magic);
603
604 assert!(result.is_err());
605 let error = result.unwrap_err();
606 let formatted = format_parse_error(&error);
607 assert!(formatted.contains("builtin_rules.magic"));
608 }
609
610 #[test]
611 fn test_parse_and_generate_invalid_type() {
612 let invalid_magic = "0 invalidtype =0x7F test";
613 let result = parse_and_generate_builtin_rules(invalid_magic);
614
615 assert!(result.is_err());
616 let error = result.unwrap_err();
617 let formatted = format_parse_error(&error);
618 assert!(formatted.contains("builtin_rules.magic"));
619 }
620
621 #[test]
622 fn test_parse_and_generate_empty_input() {
623 let empty_magic = "";
624 let result = parse_and_generate_builtin_rules(empty_magic);
625
626 assert!(result.is_ok());
628 let generated = result.unwrap();
629 assert!(generated.contains("vec![]") || generated.contains("vec!["));
630 }
631
632 #[test]
633 fn test_parse_and_generate_valid_magic() {
634 let valid_magic = "0 byte =0x7F ELF executable";
635 let result = parse_and_generate_builtin_rules(valid_magic);
636
637 assert!(result.is_ok());
638 let generated = result.unwrap();
639 assert!(generated.contains("OffsetSpec::Absolute(0)"));
640 assert!(generated.contains("TypeKind::Byte { signed: true }"));
641 assert!(generated.contains("Value::Uint(127)"));
642 assert!(generated.contains("ELF executable"));
643 }
644
645 #[test]
646 fn test_parse_and_generate_malformed_value() {
647 let invalid_magic = "0 byte =notahexvalue test";
648 let result = parse_and_generate_builtin_rules(invalid_magic);
649
650 assert!(result.is_err());
651 let error = result.unwrap_err();
652 let formatted = format_parse_error(&error);
653 assert!(formatted.contains("builtin_rules.magic"));
654 }
655
656 #[test]
664 fn test_serialize_regex_codegen_has_no_expect_panic_marker() {
665 use crate::parser::ast::{RegexCount, RegexFlags};
666 use std::num::NonZeroU32;
667
668 let cases = [
669 TypeKind::Regex {
670 flags: RegexFlags::default(),
671 count: RegexCount::Bytes(NonZeroU32::new(256).unwrap()),
672 },
673 TypeKind::Regex {
674 flags: RegexFlags::default(),
675 count: RegexCount::Lines(Some(NonZeroU32::new(3).unwrap())),
676 },
677 ];
678 for typ in &cases {
679 let generated = serialize_type_kind(typ);
680 assert!(
681 !generated.contains(".expect("),
682 "serialize_type_kind must not emit .expect() (AGENTS.md panic-marker ban); got:\n{generated}"
683 );
684 assert!(
685 generated.contains(".unwrap_or(::std::num::NonZeroU32::MIN)"),
686 "serialize_type_kind must emit .unwrap_or(NonZeroU32::MIN); got:\n{generated}"
687 );
688 }
689 }
690
691 #[test]
692 fn test_serialize_search_codegen_has_no_expect_panic_marker() {
693 use std::num::NonZeroUsize;
694
695 let typ = TypeKind::Search {
701 range: NonZeroUsize::new(512),
702 flags: SearchFlags::default(),
703 };
704 let generated = serialize_type_kind(&typ);
705 assert!(
706 !generated.contains(".expect(") && !generated.contains(".unwrap()"),
707 "serialize_type_kind must not emit a panic marker (AGENTS.md ban); got:\n{generated}"
708 );
709 assert!(
710 generated.contains("range: ::std::num::NonZeroUsize::new(512)"),
711 "bounded search must serialize range as NonZeroUsize::new(N) (an Option); got:\n{generated}"
712 );
713
714 let bare = TypeKind::Search {
716 range: None,
717 flags: SearchFlags::default(),
718 };
719 let bare_gen = serialize_type_kind(&bare);
720 assert!(
721 bare_gen.contains("range: None"),
722 "bare search must serialize range as None (scan-to-EOF); got:\n{bare_gen}"
723 );
724 }
725
726 #[test]
732 fn test_serialize_type_kind_search_default_flags() {
733 use std::num::NonZeroUsize;
734
735 let typ = TypeKind::Search {
736 range: NonZeroUsize::new(256),
737 flags: SearchFlags::default(),
738 };
739 let generated = serialize_type_kind(&typ);
740
741 for field in [
743 "compact_whitespace: false",
744 "compact_optional_whitespace: false",
745 "ignore_lowercase: false",
746 "ignore_uppercase: false",
747 "text_test: false",
748 "trim: false",
749 "bin_test: false",
750 "full_word: false",
751 "start_anchor: false",
752 ] {
753 assert!(
754 generated.contains(field),
755 "generated codegen missing `{field}` field; got:\n{generated}"
756 );
757 }
758
759 assert!(
761 !generated.contains("..Default::default()"),
762 "generated codegen must not use `..Default::default()`; got:\n{generated}"
763 );
764
765 assert!(
767 generated.contains("crate::parser::ast::SearchFlags"),
768 "generated codegen must use fully-qualified path; got:\n{generated}"
769 );
770 }
771
772 #[test]
777 fn test_serialize_type_kind_search_with_flags() {
778 use std::num::NonZeroUsize;
779
780 let typ = TypeKind::Search {
781 range: NonZeroUsize::new(1024),
782 flags: SearchFlags::default()
783 .with_start_anchor(true)
784 .with_ignore_lowercase(true),
785 };
786 let generated = serialize_type_kind(&typ);
787
788 assert!(
789 generated.contains("start_anchor: true"),
790 "expected start_anchor: true; got:\n{generated}"
791 );
792 assert!(
793 generated.contains("ignore_lowercase: true"),
794 "expected ignore_lowercase: true; got:\n{generated}"
795 );
796
797 for field in [
799 "compact_whitespace: false",
800 "compact_optional_whitespace: false",
801 "ignore_uppercase: false",
802 "text_test: false",
803 "trim: false",
804 "bin_test: false",
805 "full_word: false",
806 ] {
807 assert!(
808 generated.contains(field),
809 "expected `{field}` in codegen; got:\n{generated}"
810 );
811 }
812
813 assert!(
815 generated.contains("1024"),
816 "expected range literal 1024; got:\n{generated}"
817 );
818 }
819
820 #[test]
825 fn test_search_codegen_has_no_panic_markers() {
826 use std::num::NonZeroUsize;
827
828 let cases = [
829 SearchFlags::default(),
830 SearchFlags::default().with_start_anchor(true),
831 SearchFlags::default().with_ignore_lowercase(true),
832 SearchFlags::default()
833 .with_compact_whitespace(true)
834 .with_full_word(true),
835 SearchFlags::default()
836 .with_start_anchor(true)
837 .with_ignore_lowercase(true)
838 .with_ignore_uppercase(true)
839 .with_compact_whitespace(true)
840 .with_compact_optional_whitespace(true)
841 .with_text_test(true)
842 .with_trim(true)
843 .with_bin_test(true)
844 .with_full_word(true),
845 ];
846
847 for flags in cases {
848 let typ = TypeKind::Search {
849 range: NonZeroUsize::new(64),
850 flags,
851 };
852 let generated = serialize_type_kind(&typ);
853
854 for forbidden in [".unwrap()", ".expect(", "panic!(", "unreachable!", "todo!"] {
855 assert!(
856 !generated.contains(forbidden),
857 "emitted codegen contains forbidden panic marker `{forbidden}`; got:\n{generated}"
858 );
859 }
860
861 assert!(
866 generated.contains("range: ::std::num::NonZeroUsize::new(64)"),
867 "bounded search must serialize range as an Option-yielding NonZeroUsize::new(N); got:\n{generated}"
868 );
869 }
870 }
871}