1use crate::ast::*;
7use crate::naming::{module_file_stem, to_pascal_case, to_screaming_snake_case, to_snake_case};
8use std::fmt::Write;
9
10#[derive(Debug, Clone, Default)]
12pub struct CCodeGenConfig {
13 pub synta_header_path: Option<String>,
15 pub generate_helpers: bool,
17 pub arena_mode: bool,
19}
20
21impl CCodeGenConfig {
22 pub fn with_header_path(path: impl Into<String>) -> Self {
24 Self {
25 synta_header_path: Some(path.into()),
26 generate_helpers: false,
27 arena_mode: false,
28 }
29 }
30
31 pub fn with_helpers(mut self) -> Self {
33 self.generate_helpers = true;
34 self
35 }
36
37 pub fn with_arena(mut self) -> Self {
39 self.arena_mode = true;
40 self
41 }
42}
43
44fn anonymous_inner_c(ty: &Type) -> Option<&Type> {
60 let candidate = match ty {
61 Type::Tagged { inner, .. } => inner.as_ref(),
62 Type::Constrained { base_type, .. } => base_type.as_ref(),
63 other => other,
64 };
65 matches!(
66 candidate,
67 Type::Sequence(_) | Type::Set(_) | Type::Choice(_)
68 )
69 .then_some(candidate)
70}
71
72fn rewrap_with_tag(original: &Type, inner: Type) -> Type {
75 match original {
76 Type::Tagged { tag, .. } => Type::Tagged {
77 tag: tag.clone(),
78 inner: Box::new(inner),
79 },
80 Type::Constrained { constraint, .. } => Type::Constrained {
81 base_type: Box::new(inner),
82 constraint: constraint.clone(),
83 },
84 _ => inner,
85 }
86}
87
88fn expand_fields(
91 fields: &[SequenceField],
92 parent_name: &str,
93 synthetics: &mut Vec<Definition>,
94) -> Vec<SequenceField> {
95 fields
96 .iter()
97 .map(|field| {
98 if let Some(body) = anonymous_inner_c(&field.ty) {
99 let syn_name = format!(
100 "{}{}",
101 to_pascal_case(parent_name),
102 to_pascal_case(&field.name)
103 );
104 let expanded_body = expand_type(body, &syn_name, synthetics);
105 synthetics.push(Definition {
106 name: syn_name.clone(),
107 ty: expanded_body,
108 });
109 SequenceField {
110 name: field.name.clone(),
111 ty: rewrap_with_tag(&field.ty, Type::TypeRef(syn_name)),
112 optional: field.optional,
113 default: field.default.clone(),
114 }
115 } else {
116 field.clone()
117 }
118 })
119 .collect()
120}
121
122fn expand_variants(
125 variants: &[ChoiceVariant],
126 parent_name: &str,
127 synthetics: &mut Vec<Definition>,
128) -> Vec<ChoiceVariant> {
129 variants
130 .iter()
131 .map(|variant| {
132 if let Some(body) = anonymous_inner_c(&variant.ty) {
133 let syn_name = format!(
134 "{}{}",
135 to_pascal_case(parent_name),
136 to_pascal_case(&variant.name)
137 );
138 let expanded_body = expand_type(body, &syn_name, synthetics);
139 synthetics.push(Definition {
140 name: syn_name.clone(),
141 ty: expanded_body,
142 });
143 ChoiceVariant {
144 name: variant.name.clone(),
145 ty: rewrap_with_tag(&variant.ty, Type::TypeRef(syn_name)),
146 }
147 } else {
148 variant.clone()
149 }
150 })
151 .collect()
152}
153
154fn expand_type(ty: &Type, parent_name: &str, synthetics: &mut Vec<Definition>) -> Type {
156 match ty {
157 Type::Sequence(fields) => Type::Sequence(expand_fields(fields, parent_name, synthetics)),
158 Type::Set(fields) => Type::Set(expand_fields(fields, parent_name, synthetics)),
159 Type::Choice(variants) => Type::Choice(expand_variants(variants, parent_name, synthetics)),
160 other => other.clone(),
161 }
162}
163
164fn expand_anonymous_types(defs: &[Definition]) -> Vec<Definition> {
172 let mut result: Vec<Definition> = Vec::with_capacity(defs.len() * 2);
173 for def in defs {
174 let mut synthetics: Vec<Definition> = Vec::new();
175 let expanded_ty = expand_type(&def.ty, &def.name, &mut synthetics);
176 result.extend(synthetics);
177 result.push(Definition {
178 name: def.name.clone(),
179 ty: expanded_ty,
180 });
181 }
182 result
183}
184
185pub fn generate_c(module: &Module) -> Result<String, Box<dyn std::error::Error>> {
187 generate_c_with_config(module, CCodeGenConfig::default())
188}
189
190pub fn generate_c_with_config(
192 module: &Module,
193 config: CCodeGenConfig,
194) -> Result<String, Box<dyn std::error::Error>> {
195 let expanded_defs = expand_anonymous_types(&module.definitions);
199
200 let mut output = String::new();
201
202 writeln!(
204 &mut output,
205 "/* Generated from ASN.1 module {} */",
206 module.name
207 )?;
208 writeln!(&mut output, "/* DO NOT EDIT - auto-generated code */")?;
209 writeln!(&mut output)?;
210
211 let guard_name = format!("{}_H", to_screaming_snake_case(&module.name));
213 writeln!(&mut output, "#ifndef {}", guard_name)?;
214 writeln!(&mut output, "#define {}", guard_name)?;
215 writeln!(&mut output)?;
216
217 writeln!(&mut output, "#include <stdint.h>")?;
219 writeln!(&mut output, "#include <stdbool.h>")?;
220 writeln!(&mut output, "#include <stdlib.h>")?;
221 writeln!(&mut output, "#include <string.h>")?;
222 if config.generate_helpers {
223 writeln!(&mut output, "#include <stdio.h>")?;
224 }
225 let header_path = config.synta_header_path.as_deref().unwrap_or("synta.h");
226 writeln!(&mut output, "#include \"{}\"", header_path)?;
227 writeln!(&mut output)?;
228
229 if !module.imports.is_empty() {
231 writeln!(&mut output, "/* Imported module headers */")?;
232 for import in &module.imports {
233 let stem = module_file_stem(&import.module_name);
234 writeln!(&mut output, "#include \"{}.h\"", stem)?;
235 }
236 writeln!(&mut output)?;
237 }
238
239 if config.arena_mode {
241 writeln!(&mut output, "/* Arena/bump allocator support */")?;
242 writeln!(&mut output)?;
243 writeln!(&mut output, "#ifndef SYNTA_ARENA_MAX_HANDLES")?;
244 writeln!(&mut output, "#define SYNTA_ARENA_MAX_HANDLES 256")?;
245 writeln!(&mut output, "#endif")?;
246 writeln!(&mut output)?;
247 writeln!(&mut output, "typedef struct {{")?;
248 writeln!(&mut output, " void *_ptrs[SYNTA_ARENA_MAX_HANDLES];")?;
249 writeln!(
250 &mut output,
251 " void (*_fns[SYNTA_ARENA_MAX_HANDLES])(void*);"
252 )?;
253 writeln!(&mut output, " size_t _n;")?;
254 writeln!(&mut output, "}} SyntaArena;")?;
255 writeln!(&mut output)?;
256 writeln!(
257 &mut output,
258 "static inline void synta_arena_init(SyntaArena *a) {{ a->_n = 0; }}"
259 )?;
260 writeln!(&mut output)?;
261 writeln!(
262 &mut output,
263 "static inline int _synta_arena_track(SyntaArena *a, void *p, void (*fn)(void*)) {{"
264 )?;
265 writeln!(
266 &mut output,
267 " if (!p || a->_n >= SYNTA_ARENA_MAX_HANDLES) return 0;"
268 )?;
269 writeln!(
270 &mut output,
271 " a->_ptrs[a->_n] = p; a->_fns[a->_n] = fn; a->_n++; return 1;"
272 )?;
273 writeln!(&mut output, "}}")?;
274 writeln!(&mut output)?;
275 writeln!(
276 &mut output,
277 "static inline void synta_arena_free_all(SyntaArena *a) {{"
278 )?;
279 writeln!(
280 &mut output,
281 " for (size_t i = 0; i < a->_n; i++) a->_fns[i](a->_ptrs[i]);"
282 )?;
283 writeln!(&mut output, " a->_n = 0;")?;
284 writeln!(&mut output, "}}")?;
285 writeln!(&mut output)?;
286 }
287
288 writeln!(&mut output, "#ifdef __cplusplus")?;
289 writeln!(&mut output, "extern \"C\" {{")?;
290 writeln!(&mut output, "#endif")?;
291 writeln!(&mut output)?;
292
293 writeln!(&mut output, "/* BitString support */")?;
295 writeln!(&mut output)?;
296 writeln!(&mut output, "typedef struct {{")?;
297 writeln!(&mut output, " SyntaByteArray data;")?;
298 writeln!(&mut output, " uint8_t unused_bits;")?;
299 writeln!(&mut output, "}} SyntaBitString;")?;
300 writeln!(&mut output)?;
301
302 generate_value_constants(&mut output, module)?;
304
305 writeln!(&mut output, "/* Forward declarations */")?;
307 writeln!(&mut output)?;
308 for def in &expanded_defs {
309 match &def.ty {
310 Type::Sequence(_)
311 | Type::Set(_)
312 | Type::Choice(_)
313 | Type::SequenceOf(_, _)
314 | Type::SetOf(_, _) => {
315 let c_name = to_pascal_case(&def.name);
316 writeln!(&mut output, "typedef struct {} {};", c_name, c_name)?;
317 }
318 Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
319 let c_name = to_pascal_case(&def.name);
320 writeln!(&mut output, "typedef int64_t {};", c_name)?;
321 }
322 Type::Enumerated(_) => {
323 let c_name = to_pascal_case(&def.name);
324 writeln!(&mut output, "typedef enum {} {};", c_name, c_name)?;
325 }
326 _ => {}
327 }
328 }
329 writeln!(&mut output)?;
330
331 writeln!(&mut output, "/* Type definitions */")?;
334 writeln!(&mut output)?;
335 for idx in topo_order(&expanded_defs) {
336 generate_type_definition(&mut output, &expanded_defs[idx], config.generate_helpers)?;
337 writeln!(&mut output)?;
338 }
339
340 writeln!(&mut output, "/* Encoder/Decoder functions */")?;
342 writeln!(&mut output)?;
343 for def in &expanded_defs {
344 generate_encoder_decoder_prototypes(&mut output, def, config.arena_mode)?;
345 writeln!(&mut output)?;
346 }
347
348 if config.generate_helpers {
350 writeln!(&mut output, "/* Helper functions */")?;
351 writeln!(&mut output)?;
352 for def in &expanded_defs {
353 generate_helper_functions(&mut output, def)?;
354 writeln!(&mut output)?;
355 }
356 }
357
358 writeln!(&mut output, "#ifdef __cplusplus")?;
359 writeln!(&mut output, "}}")?;
360 writeln!(&mut output, "#endif")?;
361 writeln!(&mut output)?;
362
363 writeln!(&mut output, "#endif /* {} */", guard_name)?;
364
365 Ok(output)
366}
367
368fn value_deps_of_type(ty: &Type, deps: &mut Vec<String>) {
371 match ty {
372 Type::Sequence(fields) | Type::Set(fields) => {
373 for field in fields {
374 value_deps_of_field_type(&field.ty, deps);
375 }
376 }
377 Type::Choice(variants) => {
378 for variant in variants {
381 value_deps_of_field_type(&variant.ty, deps);
382 }
383 }
384 _ => {}
386 }
387}
388
389fn value_deps_of_field_type(ty: &Type, deps: &mut Vec<String>) {
399 match ty {
400 Type::TypeRef(name) => deps.push(name.clone()),
401 Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
403 for f in inner_fields {
404 value_deps_of_field_type(&f.ty, deps);
405 }
406 }
407 Type::Tagged { inner, .. }
409 | Type::Constrained {
410 base_type: inner, ..
411 } => {
412 value_deps_of_field_type(inner, deps);
413 }
414 _ => {}
416 }
417}
418
419fn topo_order(defs: &[Definition]) -> Vec<usize> {
422 use std::collections::{HashMap, VecDeque};
423
424 let index: HashMap<&str, usize> = defs
425 .iter()
426 .enumerate()
427 .map(|(i, d)| (d.name.as_str(), i))
428 .collect();
429
430 let mut in_degree = vec![0usize; defs.len()];
431 let mut adj: Vec<Vec<usize>> = vec![Vec::new(); defs.len()];
433
434 for (i, def) in defs.iter().enumerate() {
435 let mut raw_deps = Vec::new();
436 value_deps_of_type(&def.ty, &mut raw_deps);
437
438 let mut seen = std::collections::HashSet::new();
440 for dep in raw_deps {
441 if !seen.insert(dep.clone()) {
442 continue;
443 }
444 if let Some(&j) = index.get(dep.as_str()) {
445 if j != i && !adj[j].contains(&i) {
446 adj[j].push(i);
447 in_degree[i] += 1;
448 }
449 }
450 }
451 }
452
453 let mut queue: VecDeque<usize> = (0..defs.len()).filter(|&i| in_degree[i] == 0).collect();
455 let mut result = Vec::with_capacity(defs.len());
456
457 while let Some(i) = queue.pop_front() {
458 result.push(i);
459 for &j in &adj[i] {
460 in_degree[j] -= 1;
461 if in_degree[j] == 0 {
462 queue.push_back(j);
463 }
464 }
465 }
466
467 for i in 0..defs.len() {
469 if !result.contains(&i) {
470 result.push(i);
471 }
472 }
473
474 result
475}
476
477fn format_c_constraint_display(constraint: &SubtypeConstraint) -> String {
479 match constraint {
480 SubtypeConstraint::SingleValue(val) => match val {
481 ConstraintValue::Integer(n) => n.to_string(),
482 ConstraintValue::Min => "MIN".to_string(),
483 ConstraintValue::Max => "MAX".to_string(),
484 ConstraintValue::NamedValue(name) => name.clone(),
485 },
486 SubtypeConstraint::ValueRange { min, max } => {
487 let min_str = match min {
488 ConstraintValue::Integer(n) => n.to_string(),
489 ConstraintValue::Min => "MIN".to_string(),
490 ConstraintValue::Max => "MAX".to_string(),
491 ConstraintValue::NamedValue(n) => n.clone(),
492 };
493 let max_str = match max {
494 ConstraintValue::Integer(n) => n.to_string(),
495 ConstraintValue::Max => "MAX".to_string(),
496 ConstraintValue::Min => "MIN".to_string(),
497 ConstraintValue::NamedValue(n) => n.clone(),
498 };
499 format!("{}..{}", min_str, max_str)
500 }
501 SubtypeConstraint::Union(elements) => {
502 let parts: Vec<String> = elements.iter().map(format_c_constraint_display).collect();
503 parts.join(" | ")
504 }
505 SubtypeConstraint::Intersection(elements) => {
506 let parts: Vec<String> = elements
507 .iter()
508 .map(|e| format!("({})", format_c_constraint_display(e)))
509 .collect();
510 parts.join(" ^ ")
511 }
512 SubtypeConstraint::Complement(inner) => {
513 format!("ALL EXCEPT {}", format_c_constraint_display(inner))
514 }
515 SubtypeConstraint::SizeConstraint(inner) => {
516 format!("SIZE ({})", format_c_constraint_display(inner))
517 }
518 SubtypeConstraint::Pattern(p) => format!("PATTERN \"{}\"", p),
519 SubtypeConstraint::PermittedAlphabet(ranges) => {
520 let parts: Vec<String> = ranges
521 .iter()
522 .map(|r| {
523 if r.min == r.max {
524 format!("\"{}\"", r.min)
525 } else {
526 format!("\"{}\"..\"{}\"", r.min, r.max)
527 }
528 })
529 .collect();
530 format!("FROM ({})", parts.join(" | "))
531 }
532 _ => "constraint".to_string(),
533 }
534}
535
536fn constrained_integer_c_type(constraint: &SubtypeConstraint) -> &'static str {
549 let (lo, hi) = match constraint {
550 SubtypeConstraint::SingleValue(ConstraintValue::Integer(n)) => (*n, *n),
551 SubtypeConstraint::ValueRange {
552 min: ConstraintValue::Integer(lo),
553 max: ConstraintValue::Integer(hi),
554 } => (*lo, *hi),
555 _ => return "int64_t",
556 };
557 if lo >= 0 {
558 if hi <= u8::MAX as i64 {
559 "uint8_t"
560 } else if hi <= u16::MAX as i64 {
561 "uint16_t"
562 } else if hi <= u32::MAX as i64 {
563 "uint32_t"
564 } else {
565 "uint64_t"
566 }
567 } else if lo >= i8::MIN as i64 && hi <= i8::MAX as i64 {
568 "int8_t"
569 } else if lo >= i16::MIN as i64 && hi <= i16::MAX as i64 {
570 "int16_t"
571 } else if lo >= i32::MIN as i64 && hi <= i32::MAX as i64 {
572 "int32_t"
573 } else {
574 "int64_t"
575 }
576}
577
578fn generate_c_constraint_check(var: &str, constraint: &SubtypeConstraint, c_type: &str) -> String {
589 let is_unsigned = c_type.starts_with("uint");
590 match constraint {
591 SubtypeConstraint::SingleValue(val) => match val {
592 ConstraintValue::Integer(n) => format!("{} == {}LL", var, n),
593 ConstraintValue::Min => format!("{} == INT64_MIN", var),
594 ConstraintValue::Max => format!("{} == INT64_MAX", var),
595 ConstraintValue::NamedValue(name) => format!("{} == {}", var, name),
596 },
597 SubtypeConstraint::ValueRange { min, max } => {
598 let mut parts: Vec<String> = Vec::new();
599 match min {
600 ConstraintValue::Integer(n) if is_unsigned && *n <= 0 => {}
602 ConstraintValue::Integer(n) => parts.push(format!("{} >= {}LL", var, n)),
603 ConstraintValue::Min => {}
604 ConstraintValue::Max => parts.push(format!("{} >= INT64_MAX", var)),
605 ConstraintValue::NamedValue(name) => parts.push(format!("{} >= {}", var, name)),
606 }
607 match max {
608 ConstraintValue::Integer(n) => parts.push(format!("{} <= {}LL", var, n)),
609 ConstraintValue::Max => {}
610 ConstraintValue::Min => parts.push(format!("{} <= INT64_MIN", var)),
611 ConstraintValue::NamedValue(name) => parts.push(format!("{} <= {}", var, name)),
612 }
613 if parts.is_empty() {
614 "1".to_string()
615 } else {
616 format!("({})", parts.join(" && "))
617 }
618 }
619 SubtypeConstraint::Union(elements) => {
620 let checks: Vec<String> = elements
621 .iter()
622 .map(|e| generate_c_constraint_check(var, e, c_type))
623 .collect();
624 format!("({})", checks.join(" || "))
625 }
626 SubtypeConstraint::Intersection(elements) => {
627 let checks: Vec<String> = elements
628 .iter()
629 .map(|e| generate_c_constraint_check(var, e, c_type))
630 .collect();
631 format!("({})", checks.join(" && "))
632 }
633 SubtypeConstraint::Complement(inner) => {
634 let inner_check = generate_c_constraint_check(var, inner, c_type);
635 format!("!({})", inner_check)
636 }
637 _ => "1 /* unsupported constraint */".to_string(),
638 }
639}
640
641fn generate_constrained_integer_c(
662 output: &mut String,
663 name: &str,
664 constraint: &SubtypeConstraint,
665 named_numbers: &[NamedNumber],
666) -> Result<(), Box<dyn std::error::Error>> {
667 let c_name = to_pascal_case(name);
668 let fn_prefix = to_snake_case(name);
669 let c_type = constrained_integer_c_type(constraint);
670 let display = format_c_constraint_display(constraint);
671 let check = generate_c_constraint_check("v", constraint, c_type);
672
673 writeln!(output, "/* INTEGER ({}) */", display)?;
675 writeln!(output, "typedef struct {{ {} value; }} {};", c_type, c_name)?;
676 writeln!(output)?;
677
678 if !named_numbers.is_empty() {
680 writeln!(output, "/* Named values for {} */", c_name)?;
681 for nn in named_numbers {
682 let const_name = to_screaming_snake_case(&nn.name);
683 writeln!(
684 output,
685 "#define {}_{} (({}){})",
686 c_name, const_name, c_type, nn.value
687 )?;
688 }
689 writeln!(output)?;
690 }
691
692 writeln!(
694 output,
695 "/** Create {}: validates INTEGER ({}). Returns false if out of range. */",
696 c_name, display
697 )?;
698 writeln!(
699 output,
700 "static inline bool {}_new({} v, {}* out) {{",
701 fn_prefix, c_type, c_name
702 )?;
703 writeln!(output, " if (!({check})) return false;", check = check)?;
704 writeln!(output, " out->value = v;")?;
705 writeln!(output, " return true;")?;
706 writeln!(output, "}}")?;
707 writeln!(output)?;
708
709 writeln!(
711 output,
712 "/** Create {} without validation (use with caution). */",
713 c_name
714 )?;
715 writeln!(
716 output,
717 "static inline {} {}_new_unchecked({} v) {{",
718 c_name, fn_prefix, c_type
719 )?;
720 writeln!(output, " {} out; out.value = v; return out;", c_name)?;
721 writeln!(output, "}}")?;
722 writeln!(output)?;
723
724 writeln!(
726 output,
727 "/** Get the inner {} value of {}. */",
728 c_type, c_name
729 )?;
730 writeln!(
731 output,
732 "static inline {} {}_get(const {}* self) {{ return self->value; }}",
733 c_type, fn_prefix, c_name
734 )?;
735 writeln!(output)?;
736
737 writeln!(
739 output,
740 "/** Check that {} satisfies INTEGER ({}). */",
741 c_name, display
742 )?;
743 writeln!(
744 output,
745 "static inline bool {}_validate(const {}* self) {{",
746 fn_prefix, c_name
747 )?;
748 writeln!(output, " {} v = self->value;", c_type)?;
749 writeln!(output, " return {check};", check = check)?;
750 writeln!(output, "}}")?;
751
752 Ok(())
753}
754
755fn string_base_type_name(ty: &Type) -> &'static str {
757 match ty {
758 Type::IA5String(_) => "IA5String",
759 Type::PrintableString(_) => "PrintableString",
760 Type::Utf8String(_) => "UTF8String",
761 Type::OctetString(_) => "OCTET STRING",
762 Type::BitString(_) => "BIT STRING",
763 _ => "STRING",
764 }
765}
766
767fn generate_c_length_check(len_var: &str, size_constraint: &SubtypeConstraint) -> String {
773 match size_constraint {
774 SubtypeConstraint::SingleValue(val) => match val {
775 ConstraintValue::Integer(n) => format!("{} == {}U", len_var, n),
776 ConstraintValue::Min | ConstraintValue::Max => "1".to_string(),
777 ConstraintValue::NamedValue(name) => format!("{} == {}", len_var, name),
778 },
779 SubtypeConstraint::ValueRange { min, max } => {
780 let mut parts: Vec<String> = Vec::new();
781 match min {
782 ConstraintValue::Integer(n) if *n > 0 => {
783 parts.push(format!("{} >= {}U", len_var, n));
784 }
785 ConstraintValue::Integer(_) | ConstraintValue::Min => {}
787 ConstraintValue::Max => parts.push(format!("{} >= UINT32_MAX", len_var)),
788 ConstraintValue::NamedValue(name) => {
789 parts.push(format!("{} >= {}", len_var, name));
790 }
791 }
792 match max {
793 ConstraintValue::Integer(n) => parts.push(format!("{} <= {}U", len_var, n)),
794 ConstraintValue::Max => {}
795 ConstraintValue::Min => parts.push(format!("{} == 0", len_var)),
796 ConstraintValue::NamedValue(name) => {
797 parts.push(format!("{} <= {}", len_var, name));
798 }
799 }
800 if parts.is_empty() {
801 "1".to_string()
802 } else {
803 format!("({})", parts.join(" && "))
804 }
805 }
806 SubtypeConstraint::Union(elements) => {
807 let checks: Vec<String> = elements
808 .iter()
809 .map(|e| generate_c_length_check(len_var, e))
810 .collect();
811 format!("({})", checks.join(" || "))
812 }
813 SubtypeConstraint::Intersection(elements) => {
814 let checks: Vec<String> = elements
815 .iter()
816 .map(|e| generate_c_length_check(len_var, e))
817 .collect();
818 format!("({})", checks.join(" && "))
819 }
820 SubtypeConstraint::Complement(inner) => {
821 let inner_check = generate_c_length_check(len_var, inner);
822 format!("!({})", inner_check)
823 }
824 _ => "1 /* unsupported size constraint */".to_string(),
825 }
826}
827
828pub(crate) fn format_c_char_literal(c: char) -> String {
830 if c == '\'' || c == '\\' {
831 format!("'\\{}'", c)
832 } else if c.is_ascii() && (c as u8) >= 0x20 && (c as u8) < 0x7f {
833 format!("'{}'", c)
834 } else {
835 format!("'\\x{:02x}'", c as u32)
836 }
837}
838
839pub(crate) fn generate_c_alphabet_expr(ranges: &[CharRange]) -> String {
844 if ranges.is_empty() {
845 return "1 /* no alphabet constraint */".to_string();
846 }
847 let parts: Vec<String> = ranges
848 .iter()
849 .map(|r| {
850 if r.min == r.max {
851 format!("_c == {}", format_c_char_literal(r.min))
852 } else {
853 format!(
854 "(_c >= {} && _c <= {})",
855 format_c_char_literal(r.min),
856 format_c_char_literal(r.max)
857 )
858 }
859 })
860 .collect();
861 parts.join(" || ")
862}
863
864fn emit_c_string_validation_stmts(
870 out: &mut String,
871 indent: &str,
872 constraint: &SubtypeConstraint,
873 len_var: &str,
874 value_expr: &str,
875) -> Result<(), Box<dyn std::error::Error>> {
876 match constraint {
877 SubtypeConstraint::SizeConstraint(inner) => {
878 let check = generate_c_length_check(len_var, inner);
879 writeln!(
880 out,
881 "{}if (!({check})) return false;",
882 indent,
883 check = check
884 )?;
885 }
886 SubtypeConstraint::PermittedAlphabet(ranges) => {
887 let alpha_expr = generate_c_alphabet_expr(ranges);
888 writeln!(out, "{}{{", indent)?;
889 writeln!(out, "{} uint32_t _i;", indent)?;
890 writeln!(
891 out,
892 "{} const unsigned char *_ap = (const unsigned char *){}.data;",
893 indent, value_expr
894 )?;
895 writeln!(out, "{} bool _ok = true;", indent)?;
896 writeln!(
897 out,
898 "{} for (_i = 0; _i < {lv} && _ok; _i++) {{",
899 indent,
900 lv = len_var
901 )?;
902 writeln!(out, "{} unsigned char _c = _ap[_i];", indent)?;
903 writeln!(out, "{} _ok = {alpha};", indent, alpha = alpha_expr)?;
904 writeln!(out, "{} }}", indent)?;
905 writeln!(out, "{} if (!_ok) return false;", indent)?;
906 writeln!(out, "{}}}", indent)?;
907 }
908 SubtypeConstraint::Pattern(p) => {
909 writeln!(
910 out,
911 "{}/* PATTERN constraint \"{}\" not enforced at runtime; use --with-regex or --with-pcre with --impl (single-file) or --emit impl/both (multi-file) to enable */",
912 indent, p
913 )?;
914 }
915 SubtypeConstraint::ContainedSubtype(_ty) => {
916 writeln!(
917 out,
918 "{}/* CONTAINING constraint not enforced at runtime; use --with-containing with --impl (single-file) or --emit impl/both (multi-file) to enable */",
919 indent
920 )?;
921 }
922 SubtypeConstraint::Intersection(parts) => {
923 for part in parts {
924 emit_c_string_validation_stmts(out, indent, part, len_var, value_expr)?;
925 }
926 }
927 SubtypeConstraint::Union(_) => {
928 writeln!(
929 out,
930 "{}/* Union constraint: complex, treated as unchecked */",
931 indent
932 )?;
933 }
934 SubtypeConstraint::Complement(_) => {
935 writeln!(
936 out,
937 "{}/* Complement constraint: complex, treated as unchecked */",
938 indent
939 )?;
940 }
941 _ => {
942 writeln!(out, "{}/* unsupported constraint: skipped */", indent)?;
943 }
944 }
945 Ok(())
946}
947
948fn generate_named_bit_string_c(
956 output: &mut String,
957 name: &str,
958 bits: &[NamedNumber],
959 generate_helpers: bool,
960) -> Result<(), Box<dyn std::error::Error>> {
961 let struct_name = to_pascal_case(name);
962 let prefix = to_screaming_snake_case(name);
965
966 writeln!(output, "/* {} — BIT STRING with named bits */", struct_name)?;
967 writeln!(output, "typedef SyntaBitString {};", struct_name)?;
968
969 if bits.is_empty() {
970 return Ok(());
971 }
972
973 writeln!(output)?;
974 writeln!(output, "/* Named bit positions for {} */", struct_name)?;
975
976 let macro_names: Vec<String> = bits
978 .iter()
979 .map(|b| {
980 let bit_upper = to_snake_case(&b.name).to_uppercase();
981 format!("{prefix}_{bit_upper}_BIT")
982 })
983 .collect();
984 let max_len = macro_names.iter().map(|s| s.len()).max().unwrap_or(0);
985
986 for (bit, macro_name) in bits.iter().zip(¯o_names) {
987 writeln!(
988 output,
989 "#define {macro_name:width$} {val}",
990 width = max_len,
991 val = bit.value
992 )?;
993 }
994
995 if generate_helpers {
996 writeln!(output)?;
997 writeln!(output, "/* Bit-operation helpers for {} */", struct_name)?;
998 writeln!(
999 output,
1000 "/* (requires synta_bitstring_is_set/set/clear from synta.h) */"
1001 )?;
1002 let is_set = format!("{prefix}_IS_SET(bs, bit)");
1003 let set = format!("{prefix}_SET(bs, bit)");
1004 let clear = format!("{prefix}_CLEAR(bs, bit)");
1005 let helper_max = [is_set.len(), set.len(), clear.len()]
1006 .iter()
1007 .copied()
1008 .max()
1009 .unwrap_or(0);
1010 writeln!(
1011 output,
1012 "#define {is_set:width$} synta_bitstring_is_set(&(bs).data, (bit))",
1013 width = helper_max
1014 )?;
1015 writeln!(
1016 output,
1017 "#define {set:width$} synta_bitstring_set(&(bs).data, (bit))",
1018 width = helper_max
1019 )?;
1020 writeln!(
1021 output,
1022 "#define {clear:width$} synta_bitstring_clear(&(bs).data, (bit))",
1023 width = helper_max
1024 )?;
1025 }
1026
1027 Ok(())
1028}
1029
1030fn generate_constrained_string_c(
1040 output: &mut String,
1041 name: &str,
1042 base_type_str: &str,
1043 constraint: &SubtypeConstraint,
1044) -> Result<(), Box<dyn std::error::Error>> {
1045 let c_name = to_pascal_case(name);
1046 let fn_prefix = to_snake_case(name);
1047 let display = format_c_constraint_display(constraint);
1048
1049 writeln!(output, "/* {} ({}) */", base_type_str, display)?;
1051 writeln!(
1052 output,
1053 "typedef struct {{ SyntaByteArray value; }} {};",
1054 c_name
1055 )?;
1056 writeln!(output)?;
1057
1058 writeln!(
1060 output,
1061 "/** Create {c}: validates {bt} ({d}). Returns false if constraint violated. */",
1062 c = c_name,
1063 bt = base_type_str,
1064 d = display
1065 )?;
1066 writeln!(
1067 output,
1068 "static inline bool {fn}_new(SyntaByteArray value, {c}* out) {{",
1069 fn = fn_prefix,
1070 c = c_name
1071 )?;
1072 writeln!(output, " uint32_t _len = value.len;")?;
1073 emit_c_string_validation_stmts(output, " ", constraint, "_len", "value")?;
1074 writeln!(output, " out->value = value;")?;
1075 writeln!(output, " return true;")?;
1076 writeln!(output, "}}")?;
1077 writeln!(output)?;
1078
1079 writeln!(
1081 output,
1082 "/** Create {} without validation (use with caution). */",
1083 c_name
1084 )?;
1085 writeln!(
1086 output,
1087 "static inline {c} {fn}_new_unchecked(SyntaByteArray value) {{",
1088 c = c_name,
1089 fn = fn_prefix
1090 )?;
1091 writeln!(
1092 output,
1093 " {c} out; out.value = value; return out;",
1094 c = c_name
1095 )?;
1096 writeln!(output, "}}")?;
1097 writeln!(output)?;
1098
1099 writeln!(
1101 output,
1102 "/** Get a borrowed view of the {} value (owned flag cleared). */",
1103 c_name
1104 )?;
1105 writeln!(
1106 output,
1107 "static inline SyntaByteArray {fn}_get(const {c}* self) {{",
1108 fn = fn_prefix,
1109 c = c_name
1110 )?;
1111 writeln!(
1112 output,
1113 " SyntaByteArray r = self->value; r.owned = 0; return r;"
1114 )?;
1115 writeln!(output, "}}")?;
1116 writeln!(output)?;
1117
1118 writeln!(
1120 output,
1121 "/** Check that {} satisfies {} ({}). */",
1122 c_name, base_type_str, display
1123 )?;
1124 writeln!(
1125 output,
1126 "static inline bool {fn}_validate(const {c}* self) {{",
1127 fn = fn_prefix,
1128 c = c_name
1129 )?;
1130 writeln!(output, " uint32_t _len = self->value.len;")?;
1131 emit_c_string_validation_stmts(output, " ", constraint, "_len", "self->value")?;
1132 writeln!(output, " return true;")?;
1133 writeln!(output, "}}")?;
1134 writeln!(output)?;
1135
1136 writeln!(
1138 output,
1139 "/** Free {} if its value buffer is owned. */",
1140 c_name
1141 )?;
1142 writeln!(
1143 output,
1144 "static inline void {fn}_free({c}* self) {{",
1145 fn = fn_prefix,
1146 c = c_name
1147 )?;
1148 writeln!(
1149 output,
1150 " if (self->value.owned != 0) {{ synta_byte_array_free(&self->value); self->value.owned = 0; }}"
1151 )?;
1152 writeln!(output, "}}")?;
1153
1154 Ok(())
1155}
1156
1157fn generate_type_definition(
1159 output: &mut String,
1160 def: &Definition,
1161 generate_helpers: bool,
1162) -> Result<(), Box<dyn std::error::Error>> {
1163 match &def.ty {
1164 Type::Sequence(fields) | Type::Set(fields) => {
1165 generate_sequence_struct(output, &def.name, fields)?;
1166 }
1167 Type::Choice(variants) => {
1168 generate_choice_struct(output, &def.name, variants)?;
1169 }
1170 Type::Integer(_, named_numbers) if !named_numbers.is_empty() => {
1171 generate_defines_for_integer(output, &def.name, named_numbers)?;
1172 }
1173 Type::Enumerated(named_values) => {
1174 generate_enum_for_integer(output, &def.name, named_values)?;
1175 }
1176 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1177 let struct_name = to_pascal_case(&def.name);
1181 let elem_type = get_c_type(inner);
1182 writeln!(output, "struct {} {{", struct_name)?;
1183 writeln!(output, " size_t count;")?;
1184 writeln!(output, " {}* items;", elem_type)?;
1185 writeln!(output, "}};")?;
1186 }
1187 Type::Constrained {
1188 base_type,
1189 constraint,
1190 } => {
1191 match (base_type.as_ref(), &constraint.spec) {
1192 (Type::Integer(_, named_numbers), ConstraintSpec::Subtype(subtype)) => {
1193 generate_constrained_integer_c(output, &def.name, subtype, named_numbers)?;
1194 }
1195 (
1197 Type::BitString(_),
1198 ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(bits)),
1199 ) => {
1200 generate_named_bit_string_c(output, &def.name, bits, generate_helpers)?;
1201 }
1202 (
1208 Type::BitString(_),
1209 ConstraintSpec::Subtype(SubtypeConstraint::Intersection(parts)),
1210 ) if parts
1211 .iter()
1212 .any(|p| matches!(p, SubtypeConstraint::NamedBitList(_))) =>
1213 {
1214 let bits = parts
1215 .iter()
1216 .find_map(|p| {
1217 if let SubtypeConstraint::NamedBitList(b) = p {
1218 Some(b.as_slice())
1219 } else {
1220 None
1221 }
1222 })
1223 .unwrap_or(&[]);
1224 generate_named_bit_string_c(output, &def.name, bits, generate_helpers)?;
1225 }
1226 (
1227 Type::IA5String(_)
1228 | Type::PrintableString(_)
1229 | Type::Utf8String(_)
1230 | Type::OctetString(_)
1231 | Type::BitString(_),
1232 ConstraintSpec::Subtype(subtype),
1233 ) => {
1234 let base_name = string_base_type_name(base_type);
1235 generate_constrained_string_c(output, &def.name, base_name, subtype)?;
1236 }
1237 _ => {
1238 let c_name = to_pascal_case(&def.name);
1240 let base_c_type = get_c_type(base_type);
1241 writeln!(
1242 output,
1243 "/* Constrained type: constraint validation not yet implemented */"
1244 )?;
1245 writeln!(output, "typedef {} {};", base_c_type, c_name)?;
1246 }
1247 }
1248 }
1249 Type::TypeRef(_) => {
1250 let c_name = to_pascal_case(&def.name);
1252 let base_type = get_c_type(&def.ty);
1253 writeln!(output, "typedef {} {};", base_type, c_name)?;
1254 }
1255 _ => {
1256 let c_name = to_pascal_case(&def.name);
1258 let base_type = get_c_type(&def.ty);
1259 writeln!(output, "typedef {} {};", base_type, c_name)?;
1260 }
1261 }
1262 Ok(())
1263}
1264
1265fn tag_annotation_comment(ty: &Type) -> Option<String> {
1269 if let Type::Tagged { tag: tag_info, .. } = ty {
1270 let cls = match tag_info.class {
1271 TagClass::Universal => format!("UNIVERSAL {}", tag_info.number),
1272 TagClass::Application => format!("APPLICATION {}", tag_info.number),
1273 TagClass::ContextSpecific => tag_info.number.to_string(),
1274 TagClass::Private => format!("PRIVATE {}", tag_info.number),
1275 };
1276 let mode = match tag_info.tagging {
1277 Tagging::Explicit => "EXPLICIT",
1278 Tagging::Implicit => "IMPLICIT",
1279 };
1280 Some(format!("[{}] {}", cls, mode))
1281 } else {
1282 None
1283 }
1284}
1285
1286fn generate_sequence_struct(
1288 output: &mut String,
1289 name: &str,
1290 fields: &[SequenceField],
1291) -> Result<(), Box<dyn std::error::Error>> {
1292 let struct_name = to_pascal_case(name);
1293 writeln!(output, "struct {} {{", struct_name)?;
1294
1295 for field in fields {
1296 if matches!(field.ty, Type::Null) {
1298 continue;
1299 }
1300
1301 let field_name = to_snake_case(&field.name);
1302
1303 match &field.ty {
1305 Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
1306 if field.optional {
1308 writeln!(output, " bool has_{};", field_name)?;
1309 }
1310 writeln!(output, " struct {{")?;
1311 for inner_field in inner_fields {
1312 if matches!(inner_field.ty, Type::Null) {
1313 continue;
1314 }
1315 let inner_name = to_snake_case(&inner_field.name);
1316 let inner_type = get_c_type_for_field(&inner_field.ty);
1317 if inner_field.optional {
1318 writeln!(output, " bool has_{};", inner_name)?;
1319 writeln!(output, " {} {};", inner_type, inner_name)?;
1320 } else {
1321 writeln!(output, " {} {};", inner_type, inner_name)?;
1322 }
1323 }
1324 writeln!(output, " }} {};", field_name)?;
1325 }
1326 _ => {
1327 let field_type = get_c_type_for_field(&field.ty);
1328 let tag_note = tag_annotation_comment(&field.ty);
1329
1330 if matches!(field.ty, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1332 writeln!(output, " size_t {}_count;", field_name)?;
1333 writeln!(output, " {} {};", field_type, field_name)?;
1334 } else if field.optional {
1335 writeln!(output, " bool has_{};", field_name)?;
1336 if let Some(ref tn) = tag_note {
1337 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1338 } else {
1339 writeln!(output, " {} {};", field_type, field_name)?;
1340 }
1341 } else if let Some(ref dv) = field.default {
1342 if let Some(ref tn) = tag_note {
1343 writeln!(
1344 output,
1345 " {} {}; /* {} DEFAULT {} */",
1346 field_type, field_name, tn, dv
1347 )?;
1348 } else {
1349 writeln!(
1350 output,
1351 " {} {}; /* DEFAULT {} */",
1352 field_type, field_name, dv
1353 )?;
1354 }
1355 } else if let Some(ref tn) = tag_note {
1356 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1357 } else {
1358 writeln!(output, " {} {};", field_type, field_name)?;
1359 }
1360 }
1361 }
1362 }
1363
1364 writeln!(output, "}};")?;
1365 Ok(())
1366}
1367
1368fn get_c_type_for_field(ty: &Type) -> String {
1370 match ty {
1371 Type::TypeRef(name) => to_pascal_case(name),
1372 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1373 format!("{}*", get_c_type(inner))
1375 }
1376 _ => get_c_type(ty),
1377 }
1378}
1379
1380fn generate_choice_struct(
1382 output: &mut String,
1383 name: &str,
1384 variants: &[ChoiceVariant],
1385) -> Result<(), Box<dyn std::error::Error>> {
1386 let struct_name = to_pascal_case(name);
1387 let tag_enum_name = format!("{}Tag", struct_name);
1388
1389 writeln!(output, "typedef enum {} {{", tag_enum_name)?;
1391 for (i, variant) in variants.iter().enumerate() {
1392 let variant_name = to_pascal_case(&variant.name);
1393 let comma = if i < variants.len() - 1 { "," } else { "" };
1394 writeln!(output, " {}_{}{}", tag_enum_name, variant_name, comma)?;
1395 }
1396 writeln!(output, "}} {};", tag_enum_name)?;
1397 writeln!(output)?;
1398
1399 writeln!(output, "struct {} {{", struct_name)?;
1401 writeln!(output, " {} tag;", tag_enum_name)?;
1402 writeln!(output, " union {{")?;
1403
1404 for variant in variants {
1405 if matches!(variant.ty, Type::Null) {
1407 continue;
1408 }
1409
1410 let variant_name = to_snake_case(&variant.name);
1411 if matches!(peel_type(&variant.ty), Type::Sequence(_) | Type::Set(_)) {
1414 writeln!(
1415 output,
1416 " void* {}; /* inline SEQUENCE/SET — define separately and cast */",
1417 variant_name
1418 )?;
1419 } else {
1420 let variant_type = get_c_type_for_field(&variant.ty);
1421 writeln!(output, " {} {};", variant_type, variant_name)?;
1422 }
1423 }
1424
1425 writeln!(output, " }} value;")?;
1426 writeln!(output, "}};")?;
1427 Ok(())
1428}
1429
1430fn generate_enum_for_integer(
1432 output: &mut String,
1433 name: &str,
1434 named_numbers: &[NamedNumber],
1435) -> Result<(), Box<dyn std::error::Error>> {
1436 let enum_name = to_pascal_case(name);
1437 writeln!(output, "enum {} {{", enum_name)?;
1438
1439 for (i, nn) in named_numbers.iter().enumerate() {
1440 let variant_name = to_screaming_snake_case(&nn.name);
1441 let comma = if i < named_numbers.len() - 1 { "," } else { "" };
1442 writeln!(
1443 output,
1444 " {}_{} = {}{}",
1445 enum_name, variant_name, nn.value, comma
1446 )?;
1447 }
1448
1449 writeln!(output, "}};")?;
1450 Ok(())
1451}
1452
1453fn generate_defines_for_integer(
1482 output: &mut String,
1483 name: &str,
1484 named_numbers: &[NamedNumber],
1485) -> Result<(), Box<dyn std::error::Error>> {
1486 let type_name = to_pascal_case(name);
1487 let prefix = to_screaming_snake_case(name);
1488
1489 writeln!(output, "/* Named integer values for {} */", type_name)?;
1490
1491 let macro_names: Vec<String> = named_numbers
1493 .iter()
1494 .map(|nn| format!("{}_{}", prefix, to_snake_case(&nn.name).to_uppercase()))
1495 .collect();
1496 let max_len = macro_names.iter().map(|s| s.len()).max().unwrap_or(0);
1497
1498 for (nn, macro_name) in named_numbers.iter().zip(¯o_names) {
1499 writeln!(
1500 output,
1501 "#define {:width$} ((int64_t){})",
1502 macro_name,
1503 nn.value,
1504 width = max_len
1505 )?;
1506 }
1507
1508 Ok(())
1509}
1510
1511fn generate_encoder_decoder_prototypes(
1513 output: &mut String,
1514 def: &Definition,
1515 arena_mode: bool,
1516) -> Result<(), Box<dyn std::error::Error>> {
1517 let c_name = to_pascal_case(&def.name);
1518 let fn_prefix = to_snake_case(&def.name);
1519
1520 writeln!(
1522 output,
1523 "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out);",
1524 fn_prefix, c_name
1525 )?;
1526
1527 if arena_mode {
1529 writeln!(
1530 output,
1531 "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out);",
1532 fn_prefix, c_name
1533 )?;
1534 }
1535
1536 writeln!(
1538 output,
1539 "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value);",
1540 fn_prefix, c_name
1541 )?;
1542
1543 match &def.ty {
1545 Type::Sequence(_)
1546 | Type::Set(_)
1547 | Type::Choice(_)
1548 | Type::SequenceOf(_, _)
1549 | Type::SetOf(_, _) => {
1550 writeln!(output, "void {}_free({}* value);", fn_prefix, c_name)?;
1551 }
1552 _ => {}
1553 }
1554
1555 if let Type::Sequence(fields) | Type::Set(fields) = peel_type(&def.ty) {
1557 if fields.iter().all(|f| f.optional || f.default.is_some()) {
1558 writeln!(output, "{} {}_default(void);", c_name, fn_prefix)?;
1559 }
1560 }
1561
1562 Ok(())
1563}
1564
1565fn generate_helper_functions(
1567 output: &mut String,
1568 def: &Definition,
1569) -> Result<(), Box<dyn std::error::Error>> {
1570 let c_name = to_pascal_case(&def.name);
1571 let fn_prefix = to_snake_case(&def.name);
1572
1573 match &def.ty {
1574 Type::Sequence(fields) | Type::Set(fields) => {
1575 generate_init_helper(output, &c_name, &fn_prefix)?;
1576 writeln!(output)?;
1577 generate_validate_sequence(output, &c_name, &fn_prefix, fields)?;
1578 writeln!(output)?;
1579 generate_print_sequence(output, &c_name, &fn_prefix, fields)?;
1580 }
1581 Type::Choice(variants) => {
1582 generate_validate_choice(output, &c_name, &fn_prefix, variants)?;
1583 writeln!(output)?;
1584 generate_print_choice(output, &c_name, &fn_prefix, variants)?;
1585 }
1586 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1587 generate_init_helper(output, &c_name, &fn_prefix)?;
1588 writeln!(output)?;
1589 generate_validate_array(output, &c_name, &fn_prefix)?;
1590 writeln!(output)?;
1591 generate_print_array(output, &c_name, &fn_prefix, inner)?;
1592 }
1593 _ => {}
1594 }
1595
1596 Ok(())
1597}
1598
1599fn generate_init_helper(
1604 output: &mut String,
1605 c_name: &str,
1606 fn_prefix: &str,
1607) -> Result<(), Box<dyn std::error::Error>> {
1608 writeln!(
1609 output,
1610 "static inline void {}_init({}* value) {{",
1611 fn_prefix, c_name
1612 )?;
1613 writeln!(output, " if (value != NULL) {{")?;
1614 writeln!(output, " memset(value, 0, sizeof({}));", c_name)?;
1615 writeln!(output, " }}")?;
1616 writeln!(output, "}}")?;
1617 Ok(())
1618}
1619
1620fn generate_validate_sequence(
1628 output: &mut String,
1629 c_name: &str,
1630 fn_prefix: &str,
1631 fields: &[SequenceField],
1632) -> Result<(), Box<dyn std::error::Error>> {
1633 writeln!(
1634 output,
1635 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1636 fn_prefix, c_name
1637 )?;
1638 writeln!(
1639 output,
1640 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1641 )?;
1642 for field in fields {
1643 if matches!(field.ty, Type::Null) {
1644 continue;
1645 }
1646 let field_name = to_snake_case(&field.name);
1647 if is_pointer_c_type(&field.ty) {
1648 if field.optional {
1649 writeln!(
1650 output,
1651 " if (value->has_{name} && value->{name} == NULL) return SyntaErrorCode_InvalidArgument;",
1652 name = field_name
1653 )?;
1654 } else {
1655 writeln!(
1656 output,
1657 " if (value->{} == NULL) return SyntaErrorCode_InvalidArgument;",
1658 field_name
1659 )?;
1660 }
1661 }
1662 }
1663 writeln!(output, " return SyntaErrorCode_Success;")?;
1664 writeln!(output, "}}")?;
1665 Ok(())
1666}
1667
1668fn generate_print_sequence(
1675 output: &mut String,
1676 c_name: &str,
1677 fn_prefix: &str,
1678 fields: &[SequenceField],
1679) -> Result<(), Box<dyn std::error::Error>> {
1680 writeln!(
1681 output,
1682 "static inline void {}_print(const {}* value, FILE* stream) {{",
1683 fn_prefix, c_name
1684 )?;
1685 writeln!(
1686 output,
1687 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1688 c_name
1689 )?;
1690 writeln!(output, " fprintf(stream, \"{}{{\\n\");", c_name)?;
1691 for field in fields {
1692 if matches!(field.ty, Type::Null) {
1693 continue;
1694 }
1695 let field_name = to_snake_case(&field.name);
1696 write_sequence_field_print(output, &field_name, &field.ty, field.optional)?;
1697 }
1698 writeln!(output, " fprintf(stream, \"}}\\n\");")?;
1699 writeln!(output, "}}")?;
1700 Ok(())
1701}
1702
1703fn write_sequence_field_print(
1705 output: &mut String,
1706 field_name: &str,
1707 ty: &Type,
1708 optional: bool,
1709) -> Result<(), Box<dyn std::error::Error>> {
1710 let base = peel_type(ty);
1711
1712 if matches!(base, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1714 write_field_value_print(
1715 output,
1716 field_name,
1717 base,
1718 &format!("value->{}", field_name),
1719 " ",
1720 )?;
1721 return Ok(());
1722 }
1723
1724 if optional {
1725 writeln!(output, " if (value->has_{}) {{", field_name)?;
1726 write_field_value_print(
1727 output,
1728 field_name,
1729 base,
1730 &format!("value->{}", field_name),
1731 " ",
1732 )?;
1733 writeln!(output, " }} else {{")?;
1734 writeln!(
1735 output,
1736 " fprintf(stream, \" {}: (absent)\\n\");",
1737 field_name
1738 )?;
1739 writeln!(output, " }}")?;
1740 } else {
1741 write_field_value_print(
1742 output,
1743 field_name,
1744 base,
1745 &format!("value->{}", field_name),
1746 " ",
1747 )?;
1748 }
1749 Ok(())
1750}
1751
1752fn write_field_value_print(
1754 output: &mut String,
1755 field_name: &str,
1756 ty: &Type,
1757 field_expr: &str,
1758 indent: &str,
1759) -> Result<(), Box<dyn std::error::Error>> {
1760 match ty {
1761 Type::Boolean => {
1762 writeln!(
1763 output,
1764 "{}fprintf(stream, \" {}: %s\\n\", {} ? \"true\" : \"false\");",
1765 indent, field_name, field_expr
1766 )?;
1767 }
1768 Type::Real => {
1769 writeln!(
1770 output,
1771 "{}fprintf(stream, \" {}: %g\\n\", {});",
1772 indent, field_name, field_expr
1773 )?;
1774 }
1775 Type::OctetString(_)
1776 | Type::Utf8String(_)
1777 | Type::PrintableString(_)
1778 | Type::IA5String(_)
1779 | Type::UtcTime
1780 | Type::GeneralizedTime
1781 | Type::Any
1782 | Type::AnyDefinedBy(_) => {
1783 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1784 writeln!(
1785 output,
1786 "{} fprintf(stream, \" {}: <string(%zu bytes)>\\n\", synta_octet_string_len({}));",
1787 indent, field_name, field_expr
1788 )?;
1789 writeln!(output, "{}else", indent)?;
1790 writeln!(
1791 output,
1792 "{} fprintf(stream, \" {}: NULL\\n\");",
1793 indent, field_name
1794 )?;
1795 }
1796 Type::Integer(_, _) | Type::Enumerated(_) => {
1797 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1798 writeln!(
1799 output,
1800 "{} fprintf(stream, \" {}: <integer>\\n\");",
1801 indent, field_name
1802 )?;
1803 writeln!(output, "{}else", indent)?;
1804 writeln!(
1805 output,
1806 "{} fprintf(stream, \" {}: NULL\\n\");",
1807 indent, field_name
1808 )?;
1809 }
1810 Type::ObjectIdentifier => {
1811 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1812 writeln!(
1813 output,
1814 "{} fprintf(stream, \" {}: <oid>\\n\");",
1815 indent, field_name
1816 )?;
1817 writeln!(output, "{}else", indent)?;
1818 writeln!(
1819 output,
1820 "{} fprintf(stream, \" {}: NULL\\n\");",
1821 indent, field_name
1822 )?;
1823 }
1824 Type::BitString(_) => {
1825 writeln!(
1826 output,
1827 "{}fprintf(stream, \" {}: <bit-string(%u bytes)>\\n\", (unsigned int){}.data.len);",
1828 indent, field_name, field_expr
1829 )?;
1830 }
1831 Type::TypeRef(name) => {
1832 let type_name = to_pascal_case(name);
1833 writeln!(
1834 output,
1835 "{}fprintf(stream, \" {}: <{}>\\n\");",
1836 indent, field_name, type_name
1837 )?;
1838 }
1839 Type::Sequence(_) | Type::Set(_) => {
1840 writeln!(
1841 output,
1842 "{}fprintf(stream, \" {}: <struct>\\n\");",
1843 indent, field_name
1844 )?;
1845 }
1846 Type::SequenceOf(_, _) | Type::SetOf(_, _) => {
1847 writeln!(
1849 output,
1850 "{}fprintf(stream, \" {}: [%zu elements]\\n\", {}_count);",
1851 indent, field_name, field_expr
1852 )?;
1853 }
1854 Type::Choice(_) => {
1855 writeln!(
1856 output,
1857 "{}fprintf(stream, \" {}: <choice>\\n\");",
1858 indent, field_name
1859 )?;
1860 }
1861 Type::Null => {}
1862 _ => {
1864 writeln!(
1865 output,
1866 "{}fprintf(stream, \" {}: <value>\\n\");",
1867 indent, field_name
1868 )?;
1869 }
1870 }
1871 Ok(())
1872}
1873
1874fn generate_validate_choice(
1881 output: &mut String,
1882 c_name: &str,
1883 fn_prefix: &str,
1884 variants: &[ChoiceVariant],
1885) -> Result<(), Box<dyn std::error::Error>> {
1886 let tag_enum = format!("{}Tag", c_name);
1887 writeln!(
1888 output,
1889 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1890 fn_prefix, c_name
1891 )?;
1892 writeln!(
1893 output,
1894 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1895 )?;
1896 writeln!(output, " switch (value->tag) {{")?;
1897 for variant in variants {
1898 let variant_name = to_pascal_case(&variant.name);
1899 writeln!(output, " case {}_{}: break;", tag_enum, variant_name)?;
1900 }
1901 writeln!(
1902 output,
1903 " default: return SyntaErrorCode_InvalidEncoding;"
1904 )?;
1905 writeln!(output, " }}")?;
1906 writeln!(output, " return SyntaErrorCode_Success;")?;
1907 writeln!(output, "}}")?;
1908 Ok(())
1909}
1910
1911fn generate_print_choice(
1917 output: &mut String,
1918 c_name: &str,
1919 fn_prefix: &str,
1920 variants: &[ChoiceVariant],
1921) -> Result<(), Box<dyn std::error::Error>> {
1922 let tag_enum = format!("{}Tag", c_name);
1923 writeln!(
1924 output,
1925 "static inline void {}_print(const {}* value, FILE* stream) {{",
1926 fn_prefix, c_name
1927 )?;
1928 writeln!(
1929 output,
1930 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1931 c_name
1932 )?;
1933 writeln!(output, " switch (value->tag) {{")?;
1934 for variant in variants {
1935 let variant_name = to_pascal_case(&variant.name);
1936 let variant_field = to_snake_case(&variant.name);
1937 writeln!(output, " case {}_{}:", tag_enum, variant_name)?;
1938 writeln!(
1939 output,
1940 " fprintf(stream, \"{}{{ {} }}\\n\");",
1941 c_name, variant_field
1942 )?;
1943 writeln!(output, " break;")?;
1944 }
1945 writeln!(output, " default:")?;
1946 writeln!(
1947 output,
1948 " fprintf(stream, \"{}{{ <unknown tag> }}\\n\");",
1949 c_name
1950 )?;
1951 writeln!(output, " break;")?;
1952 writeln!(output, " }}")?;
1953 writeln!(output, "}}")?;
1954 Ok(())
1955}
1956
1957fn generate_validate_array(
1964 output: &mut String,
1965 c_name: &str,
1966 fn_prefix: &str,
1967) -> Result<(), Box<dyn std::error::Error>> {
1968 writeln!(
1969 output,
1970 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1971 fn_prefix, c_name
1972 )?;
1973 writeln!(
1974 output,
1975 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1976 )?;
1977 writeln!(
1978 output,
1979 " if (value->count > 0 && value->items == NULL) return SyntaErrorCode_InvalidArgument;"
1980 )?;
1981 writeln!(output, " return SyntaErrorCode_Success;")?;
1982 writeln!(output, "}}")?;
1983 Ok(())
1984}
1985
1986fn generate_print_array(
1994 output: &mut String,
1995 c_name: &str,
1996 fn_prefix: &str,
1997 _inner: &Type,
1998) -> Result<(), Box<dyn std::error::Error>> {
1999 writeln!(
2000 output,
2001 "static inline void {}_print(const {}* value, FILE* stream) {{",
2002 fn_prefix, c_name
2003 )?;
2004 writeln!(
2005 output,
2006 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
2007 c_name
2008 )?;
2009 writeln!(
2010 output,
2011 " fprintf(stream, \"{}[%zu]\\n\", value->count);",
2012 c_name
2013 )?;
2014 writeln!(output, "}}")?;
2015 Ok(())
2016}
2017
2018fn build_c_oid_registry(values: &[ValueAssignment]) -> std::collections::HashMap<String, Vec<u32>> {
2025 use std::collections::HashMap;
2026 let mut registry: HashMap<String, Vec<u32>> = HashMap::new();
2027
2028 let mut changed = true;
2029 while changed {
2030 changed = false;
2031 for va in values {
2032 if registry.contains_key(&va.name) {
2033 continue;
2034 }
2035 if let Value::ObjectIdentifier(components) = &va.value {
2036 let mut resolved = Vec::new();
2037 let mut can_resolve = true;
2038 for component in components {
2039 match component {
2040 OidComponent::Number(n) => resolved.push(*n),
2041 OidComponent::NamedRef(name) => {
2042 if let Some(base) = registry.get(name) {
2043 resolved.extend_from_slice(base);
2044 } else {
2045 can_resolve = false;
2046 break;
2047 }
2048 }
2049 }
2050 }
2051 if can_resolve {
2052 registry.insert(va.name.clone(), resolved);
2053 changed = true;
2054 }
2055 }
2056 }
2057 }
2058 registry
2059}
2060
2061fn escape_c_string(s: &str) -> String {
2066 let mut out = String::with_capacity(s.len());
2067 for ch in s.chars() {
2068 match ch {
2069 '\\' => out.push_str("\\\\"),
2070 '"' => out.push_str("\\\""),
2071 '\n' => out.push_str("\\n"),
2072 '\r' => out.push_str("\\r"),
2073 '\t' => out.push_str("\\t"),
2074 c if c.is_ascii() && (c as u8) >= 0x20 && (c as u8) < 0x7f => out.push(c),
2075 c => {
2076 for byte in c.to_string().as_bytes() {
2077 out.push_str(&format!("\\x{:02x}", byte));
2078 }
2079 }
2080 }
2081 }
2082 out
2083}
2084
2085fn generate_value_constants(
2099 output: &mut String,
2100 module: &Module,
2101) -> Result<(), Box<dyn std::error::Error>> {
2102 if module.values.is_empty() {
2103 return Ok(());
2104 }
2105
2106 writeln!(output, "/* Value constants */")?;
2107 writeln!(output)?;
2108
2109 let oid_registry = build_c_oid_registry(&module.values);
2110
2111 for va in &module.values {
2112 let c_name = to_screaming_snake_case(&va.name);
2113 match &va.value {
2114 Value::ObjectIdentifier(_) => {
2115 if let Some(arcs) = oid_registry.get(&va.name) {
2116 write!(output, "static const uint32_t {}[] = {{", c_name)?;
2117 for (i, n) in arcs.iter().enumerate() {
2118 if i > 0 {
2119 write!(output, ", ")?;
2120 }
2121 write!(output, "{}", n)?;
2122 }
2123 writeln!(output, "}};")?;
2124 writeln!(output, "#define {}_LEN {}", c_name, arcs.len())?;
2125 writeln!(output)?;
2126 } else {
2127 writeln!(
2128 output,
2129 "/* OID {} could not be fully resolved (unresolved named reference) */",
2130 va.name
2131 )?;
2132 writeln!(output)?;
2133 }
2134 }
2135 Value::Integer(n) => {
2136 writeln!(output, "#define {} ((int64_t){})", c_name, n)?;
2137 writeln!(output)?;
2138 }
2139 Value::Boolean(b) => {
2140 writeln!(
2141 output,
2142 "#define {} ({})",
2143 c_name,
2144 if *b { "true" } else { "false" }
2145 )?;
2146 writeln!(output)?;
2147 }
2148 Value::String(s) => {
2149 writeln!(output, "#define {} \"{}\"", c_name, escape_c_string(s))?;
2150 writeln!(output)?;
2151 }
2152 }
2153 }
2154
2155 Ok(())
2156}
2157
2158fn peel_type(ty: &Type) -> &Type {
2160 match ty {
2161 Type::Tagged { inner, .. }
2162 | Type::Constrained {
2163 base_type: inner, ..
2164 } => peel_type(inner),
2165 _ => ty,
2166 }
2167}
2168
2169fn is_pointer_c_type(ty: &Type) -> bool {
2171 matches!(
2172 peel_type(ty),
2173 Type::Integer(_, _)
2174 | Type::Enumerated(_)
2175 | Type::OctetString(_)
2176 | Type::ObjectIdentifier
2177 | Type::Utf8String(_)
2178 | Type::PrintableString(_)
2179 | Type::IA5String(_)
2180 | Type::UtcTime
2181 | Type::GeneralizedTime
2182 | Type::Any
2183 | Type::AnyDefinedBy(_)
2184 )
2185}
2186
2187pub(crate) fn get_c_type(ty: &Type) -> String {
2189 match ty {
2190 Type::Integer(_, _) => "SyntaInteger*".to_string(),
2191 Type::Enumerated(_) => "SyntaInteger*".to_string(),
2192 Type::Real => "double".to_string(),
2193 Type::Boolean => "bool".to_string(),
2194 Type::OctetString(_) => "SyntaOctetString*".to_string(),
2195 Type::BitString(_) => "SyntaBitString".to_string(),
2196 Type::ObjectIdentifier => "SyntaObjectIdentifier*".to_string(),
2197 Type::Null => "void".to_string(),
2198 Type::Utf8String(_)
2199 | Type::PrintableString(_)
2200 | Type::IA5String(_)
2201 | Type::TeletexString(_)
2202 | Type::UniversalString(_)
2203 | Type::BmpString(_)
2204 | Type::GeneralString(_)
2205 | Type::NumericString(_)
2206 | Type::VisibleString(_) => {
2207 "SyntaOctetString*".to_string() }
2209 Type::UtcTime | Type::GeneralizedTime => "SyntaOctetString*".to_string(), Type::Sequence(_) | Type::Set(_) => "struct /* complex type */".to_string(),
2211 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
2212 format!("{}*", get_c_type(inner))
2213 }
2214 Type::Choice(_) => "union /* choice */".to_string(),
2215 Type::TypeRef(name) => to_pascal_case(name),
2216 Type::Tagged { inner, .. } => get_c_type(inner),
2217 Type::Constrained { base_type, .. } => get_c_type(base_type),
2218 Type::Any => "SyntaOctetString*".to_string(), Type::AnyDefinedBy(_) => "SyntaOctetString*".to_string(), Type::Class(_) => "void /* class */".to_string(), }
2222}
2223
2224#[cfg(test)]
2225mod tests {
2226 use super::*;
2227
2228 #[test]
2229 fn test_generate_simple_sequence() {
2230 let module = Module {
2231 name: "TestModule".to_string(),
2232 oid: None,
2233 values: vec![],
2234 tagging_mode: None,
2235 imports: vec![],
2236 exports: vec![],
2237 definitions: vec![Definition {
2238 name: "SimpleSeq".to_string(),
2239 ty: Type::Sequence(vec![
2240 SequenceField {
2241 name: "version".to_string(),
2242 ty: Type::Integer(None, vec![]),
2243 optional: false,
2244 default: None,
2245 },
2246 SequenceField {
2247 name: "serialNumber".to_string(),
2248 ty: Type::Integer(None, vec![]),
2249 optional: false,
2250 default: None,
2251 },
2252 ]),
2253 }],
2254 };
2255
2256 let result = generate_c(&module).unwrap();
2257 assert!(result.contains("typedef struct SimpleSeq"));
2258 assert!(result.contains("SyntaInteger* version;"));
2259 assert!(result.contains("SyntaInteger* serial_number;"));
2260 assert!(result.contains("simple_seq_decode"));
2261 assert!(result.contains("simple_seq_encode"));
2262 }
2263
2264 #[test]
2265 fn test_generate_choice() {
2266 let module = Module {
2267 name: "TestModule".to_string(),
2268 oid: None,
2269 values: vec![],
2270 tagging_mode: None,
2271 imports: vec![],
2272 exports: vec![],
2273 definitions: vec![Definition {
2274 name: "MyChoice".to_string(),
2275 ty: Type::Choice(vec![
2276 ChoiceVariant {
2277 name: "intVal".to_string(),
2278 ty: Type::Integer(None, vec![]),
2279 },
2280 ChoiceVariant {
2281 name: "boolVal".to_string(),
2282 ty: Type::Boolean,
2283 },
2284 ]),
2285 }],
2286 };
2287
2288 let result = generate_c(&module).unwrap();
2289 assert!(result.contains("typedef enum MyChoiceTag"));
2290 assert!(result.contains("typedef struct MyChoice"));
2291 assert!(result.contains("MyChoiceTag tag;"));
2292 assert!(result.contains("union {"));
2293 }
2294
2295 #[test]
2296 fn test_generate_helpers() {
2297 let module = Module {
2298 name: "TestModule".to_string(),
2299 oid: None,
2300 values: vec![],
2301 tagging_mode: None,
2302 imports: vec![],
2303 exports: vec![],
2304 definitions: vec![
2305 Definition {
2306 name: "SimpleSeq".to_string(),
2307 ty: Type::Sequence(vec![
2308 SequenceField {
2309 name: "version".to_string(),
2310 ty: Type::Integer(None, vec![]),
2311 optional: false,
2312 default: None,
2313 },
2314 SequenceField {
2315 name: "flag".to_string(),
2316 ty: Type::Boolean,
2317 optional: true,
2318 default: None,
2319 },
2320 ]),
2321 },
2322 Definition {
2323 name: "MyChoice".to_string(),
2324 ty: Type::Choice(vec![
2325 ChoiceVariant {
2326 name: "intVal".to_string(),
2327 ty: Type::Integer(None, vec![]),
2328 },
2329 ChoiceVariant {
2330 name: "boolVal".to_string(),
2331 ty: Type::Boolean,
2332 },
2333 ]),
2334 },
2335 ],
2336 };
2337
2338 let config = CCodeGenConfig {
2339 generate_helpers: true,
2340 ..Default::default()
2341 };
2342 let result = generate_c_with_config(&module, config).unwrap();
2343
2344 assert!(result.contains("#include <stdio.h>"));
2346
2347 assert!(result.contains("simple_seq_init"));
2349 assert!(result.contains("simple_seq_validate"));
2351 assert!(result.contains("SyntaErrorCode_NullPointer"));
2352 assert!(result.contains("value->version == NULL"));
2354 assert!(!result.contains("value->flag == NULL"));
2356 assert!(result.contains("simple_seq_print"));
2358 assert!(result.contains("FILE* stream"));
2359
2360 assert!(result.contains("my_choice_validate"));
2362 assert!(result.contains("MyChoiceTag_IntVal"));
2363 assert!(result.contains("SyntaErrorCode_InvalidEncoding"));
2364 assert!(result.contains("my_choice_print"));
2366 }
2367
2368 #[test]
2369 fn test_generate_named_integer() {
2370 let module = Module {
2371 name: "TestModule".to_string(),
2372 oid: None,
2373 values: vec![],
2374 tagging_mode: None,
2375 imports: vec![],
2376 exports: vec![],
2377 definitions: vec![Definition {
2378 name: "Protocol".to_string(),
2379 ty: Type::Integer(
2380 None,
2381 vec![
2382 NamedNumber {
2383 name: "tcp".to_string(),
2384 value: 6,
2385 },
2386 NamedNumber {
2387 name: "udp".to_string(),
2388 value: 17,
2389 },
2390 ],
2391 ),
2392 }],
2393 };
2394
2395 let result = generate_c(&module).unwrap();
2396 assert!(result.contains("typedef int64_t Protocol;"));
2398 assert!(result.contains("#define PROTOCOL_TCP"));
2400 assert!(result.contains("((int64_t)6)"));
2401 assert!(result.contains("#define PROTOCOL_UDP"));
2402 assert!(result.contains("((int64_t)17)"));
2403 assert!(!result.contains("enum Protocol {"));
2405 }
2406
2407 fn make_constrained_integer_module(
2412 type_name: &str,
2413 constraint: SubtypeConstraint,
2414 named_numbers: Vec<NamedNumber>,
2415 ) -> Module {
2416 Module {
2417 name: "TestModule".to_string(),
2418 oid: None,
2419 values: vec![],
2420 tagging_mode: None,
2421 imports: vec![],
2422 exports: vec![],
2423 definitions: vec![Definition {
2424 name: type_name.to_string(),
2425 ty: Type::Constrained {
2426 base_type: Box::new(Type::Integer(None, named_numbers)),
2427 constraint: Constraint {
2428 spec: ConstraintSpec::Subtype(constraint),
2429 exception: None,
2430 },
2431 },
2432 }],
2433 }
2434 }
2435
2436 #[test]
2437 fn test_constrained_integer_value_range() {
2438 let module = make_constrained_integer_module(
2440 "Int32",
2441 SubtypeConstraint::ValueRange {
2442 min: ConstraintValue::Integer(-2147483648),
2443 max: ConstraintValue::Integer(2147483647),
2444 },
2445 vec![],
2446 );
2447 let result = generate_c(&module).unwrap();
2448
2449 assert!(
2451 result.contains("typedef struct { int32_t value; } Int32;"),
2452 "missing struct typedef:\n{}",
2453 result
2454 );
2455 assert!(result.contains("INTEGER (-2147483648..2147483647)"));
2457 assert!(result.contains("int32_new(int32_t v, Int32* out)"));
2459 assert!(result.contains("v >= -2147483648LL") && result.contains("v <= 2147483647LL"));
2460 assert!(result.contains("int32_new_unchecked(int32_t v)"));
2462 assert!(result.contains("int32_get(const Int32* self)"));
2464 assert!(result.contains("int32_validate(const Int32* self)"));
2466 assert!(result.contains("int32_decode(SyntaDecoder*"));
2468 assert!(result.contains("int32_encode(SyntaEncoder*"));
2469 assert!(!result.contains("int32_free"));
2471 }
2472
2473 #[test]
2474 fn test_constrained_integer_single_value() {
2475 let module = make_constrained_integer_module(
2477 "PvNo",
2478 SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2479 vec![],
2480 );
2481 let result = generate_c(&module).unwrap();
2482
2483 assert!(result.contains("typedef struct { uint8_t value; } PvNo;"));
2485 assert!(result.contains("INTEGER (5)"));
2486 assert!(result.contains("v == 5LL"));
2487 assert!(result.contains("pv_no_new(uint8_t v, PvNo* out)"));
2488 assert!(result.contains("pv_no_validate(const PvNo* self)"));
2489 }
2490
2491 #[test]
2492 fn test_constrained_integer_min_max_unconstrained() {
2493 let module = make_constrained_integer_module(
2495 "UncheckedInt",
2496 SubtypeConstraint::ValueRange {
2497 min: ConstraintValue::Min,
2498 max: ConstraintValue::Max,
2499 },
2500 vec![],
2501 );
2502 let result = generate_c(&module).unwrap();
2503
2504 assert!(result.contains("typedef struct { int64_t value; } UncheckedInt;"));
2505 assert!(result.contains("return 1;"));
2507 assert!(result.contains("if (!(1)) return false;"));
2510 }
2511
2512 #[test]
2513 fn test_constrained_integer_half_open_range() {
2514 let module = make_constrained_integer_module(
2516 "NonNegInt",
2517 SubtypeConstraint::ValueRange {
2518 min: ConstraintValue::Integer(0),
2519 max: ConstraintValue::Max,
2520 },
2521 vec![],
2522 );
2523 let result = generate_c(&module).unwrap();
2524
2525 assert!(result.contains("v >= 0LL"));
2527 assert!(!result.contains("v <= INT64_MAX"));
2528 }
2529
2530 #[test]
2531 fn test_constrained_integer_union() {
2532 let module = make_constrained_integer_module(
2534 "SmallOrLarge",
2535 SubtypeConstraint::Union(vec![
2536 SubtypeConstraint::ValueRange {
2537 min: ConstraintValue::Integer(0),
2538 max: ConstraintValue::Integer(10),
2539 },
2540 SubtypeConstraint::ValueRange {
2541 min: ConstraintValue::Integer(100),
2542 max: ConstraintValue::Integer(200),
2543 },
2544 ]),
2545 vec![],
2546 );
2547 let result = generate_c(&module).unwrap();
2548
2549 assert!(result.contains("typedef struct { int64_t value; } SmallOrLarge;"));
2550 assert!(result.contains("||"));
2552 assert!(result.contains("v >= 0LL") && result.contains("v <= 10LL"));
2553 assert!(result.contains("v >= 100LL") && result.contains("v <= 200LL"));
2554 }
2555
2556 #[test]
2557 fn test_constrained_integer_complement() {
2558 let module = make_constrained_integer_module(
2560 "NotZero",
2561 SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2562 ConstraintValue::Integer(0),
2563 ))),
2564 vec![],
2565 );
2566 let result = generate_c(&module).unwrap();
2567
2568 assert!(result.contains("typedef struct { int64_t value; } NotZero;"));
2569 assert!(result.contains("!(v == 0LL)"));
2570 }
2571
2572 #[test]
2573 fn test_constrained_integer_with_named_numbers() {
2574 let module = make_constrained_integer_module(
2576 "MsgType",
2577 SubtypeConstraint::ValueRange {
2578 min: ConstraintValue::Integer(0),
2579 max: ConstraintValue::Integer(30),
2580 },
2581 vec![
2582 NamedNumber {
2583 name: "asReq".to_string(),
2584 value: 10,
2585 },
2586 NamedNumber {
2587 name: "asRep".to_string(),
2588 value: 11,
2589 },
2590 ],
2591 );
2592 let result = generate_c(&module).unwrap();
2593
2594 assert!(result.contains("typedef struct { uint8_t value; } MsgType;"));
2596 assert!(result.contains("#define MsgType_AS_REQ ((uint8_t)10)"));
2598 assert!(result.contains("#define MsgType_AS_REP ((uint8_t)11)"));
2599 assert!(result.contains("msg_type_new(uint8_t v, MsgType* out)"));
2601 assert!(result.contains("msg_type_validate(const MsgType* self)"));
2602 }
2603
2604 #[test]
2605 fn test_format_c_constraint_display() {
2606 assert_eq!(
2607 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2608 min: ConstraintValue::Integer(-128),
2609 max: ConstraintValue::Integer(127),
2610 }),
2611 "-128..127"
2612 );
2613 assert_eq!(
2614 format_c_constraint_display(&SubtypeConstraint::SingleValue(ConstraintValue::Integer(
2615 42
2616 ))),
2617 "42"
2618 );
2619 assert_eq!(
2620 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2621 min: ConstraintValue::Min,
2622 max: ConstraintValue::Max,
2623 }),
2624 "MIN..MAX"
2625 );
2626 }
2627
2628 #[test]
2629 fn test_generate_c_constraint_check() {
2630 assert_eq!(
2632 generate_c_constraint_check(
2633 "val",
2634 &SubtypeConstraint::ValueRange {
2635 min: ConstraintValue::Integer(0),
2636 max: ConstraintValue::Integer(100),
2637 },
2638 "int64_t",
2639 ),
2640 "(val >= 0LL && val <= 100LL)"
2641 );
2642 assert_eq!(
2644 generate_c_constraint_check(
2645 "val",
2646 &SubtypeConstraint::ValueRange {
2647 min: ConstraintValue::Integer(0),
2648 max: ConstraintValue::Integer(100),
2649 },
2650 "uint8_t",
2651 ),
2652 "(val <= 100LL)"
2653 );
2654 assert_eq!(
2656 generate_c_constraint_check(
2657 "val",
2658 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2659 "int64_t",
2660 ),
2661 "val == 5LL"
2662 );
2663 assert_eq!(
2665 generate_c_constraint_check(
2666 "val",
2667 &SubtypeConstraint::ValueRange {
2668 min: ConstraintValue::Min,
2669 max: ConstraintValue::Max,
2670 },
2671 "int64_t",
2672 ),
2673 "1"
2674 );
2675 assert_eq!(
2677 generate_c_constraint_check(
2678 "val",
2679 &SubtypeConstraint::ValueRange {
2680 min: ConstraintValue::Integer(0),
2681 max: ConstraintValue::Max,
2682 },
2683 "int64_t",
2684 ),
2685 "(val >= 0LL)"
2686 );
2687 assert_eq!(
2689 generate_c_constraint_check(
2690 "val",
2691 &SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2692 ConstraintValue::Integer(0)
2693 ))),
2694 "int64_t",
2695 ),
2696 "!(val == 0LL)"
2697 );
2698 }
2699
2700 fn make_constrained_string_module(
2705 type_name: &str,
2706 base_ty: Type,
2707 constraint: SubtypeConstraint,
2708 ) -> Module {
2709 Module {
2710 name: "TestModule".to_string(),
2711 oid: None,
2712 values: vec![],
2713 tagging_mode: None,
2714 imports: vec![],
2715 exports: vec![],
2716 definitions: vec![Definition {
2717 name: type_name.to_string(),
2718 ty: Type::Constrained {
2719 base_type: Box::new(base_ty),
2720 constraint: Constraint {
2721 spec: ConstraintSpec::Subtype(constraint),
2722 exception: None,
2723 },
2724 },
2725 }],
2726 }
2727 }
2728
2729 #[test]
2730 fn test_constrained_string_size_only() {
2731 let module = make_constrained_string_module(
2733 "Realm",
2734 Type::IA5String(None),
2735 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2736 min: ConstraintValue::Integer(1),
2737 max: ConstraintValue::Max,
2738 })),
2739 );
2740 let result = generate_c(&module).unwrap();
2741
2742 assert!(
2744 result.contains("typedef struct { SyntaByteArray value; } Realm;"),
2745 "missing struct typedef:\n{}",
2746 result
2747 );
2748 assert!(result.contains("/* IA5String (SIZE (1..MAX)) */"));
2750 assert!(result.contains("realm_new(SyntaByteArray value, Realm* out)"));
2752 assert!(result.contains("uint32_t _len = value.len;"));
2753 assert!(result.contains("_len >= 1U"));
2754 assert!(result.contains("realm_new_unchecked(SyntaByteArray value)"));
2756 assert!(result.contains("realm_get(const Realm* self)"));
2758 assert!(result.contains("r.owned = 0"));
2759 assert!(result.contains("realm_validate(const Realm* self)"));
2761 assert!(result.contains("uint32_t _len = self->value.len;"));
2762 assert!(result.contains("realm_free(Realm* self)"));
2764 assert!(result.contains("synta_byte_array_free"));
2765 assert!(result.contains("realm_decode(SyntaDecoder*"));
2767 assert!(result.contains("realm_encode(SyntaEncoder*"));
2768 }
2769
2770 #[test]
2771 fn test_constrained_string_size_exact() {
2772 let module = make_constrained_string_module(
2774 "FixedTag",
2775 Type::OctetString(None),
2776 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::SingleValue(
2777 ConstraintValue::Integer(4),
2778 ))),
2779 );
2780 let result = generate_c(&module).unwrap();
2781
2782 assert!(result.contains("/* OCTET STRING (SIZE (4)) */"));
2783 assert!(result.contains("typedef struct { SyntaByteArray value; } FixedTag;"));
2784 assert!(result.contains("_len == 4U"));
2785 assert!(result.contains("fixed_tag_new(SyntaByteArray value, FixedTag* out)"));
2786 assert!(result.contains("fixed_tag_validate(const FixedTag* self)"));
2787 assert!(result.contains("fixed_tag_free(FixedTag* self)"));
2788 }
2789
2790 #[test]
2791 fn test_constrained_string_size_zero_min() {
2792 let module = make_constrained_string_module(
2794 "OptStr",
2795 Type::IA5String(None),
2796 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2797 min: ConstraintValue::Integer(0),
2798 max: ConstraintValue::Integer(255),
2799 })),
2800 );
2801 let result = generate_c(&module).unwrap();
2802
2803 assert!(result.contains("_len <= 255U"));
2805 assert!(!result.contains("_len >= 0U"));
2807 }
2808
2809 #[test]
2810 fn test_constrained_string_min_max_size() {
2811 let module = make_constrained_string_module(
2813 "AnyStr",
2814 Type::IA5String(None),
2815 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2816 min: ConstraintValue::Min,
2817 max: ConstraintValue::Max,
2818 })),
2819 );
2820 let result = generate_c(&module).unwrap();
2821
2822 assert!(result.contains("if (!(1)) return false;"));
2824 }
2825
2826 #[test]
2827 fn test_constrained_string_alphabet_only() {
2828 let module = make_constrained_string_module(
2830 "DigitStr",
2831 Type::IA5String(None),
2832 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: '0', max: '9' }]),
2833 );
2834 let result = generate_c(&module).unwrap();
2835
2836 assert!(result.contains("/* IA5String (FROM (\"0\"..\"9\")) */"));
2837 assert!(result.contains("const unsigned char *_ap ="));
2839 assert!(result.contains("unsigned char _c = _ap[_i]"));
2840 assert!(result.contains("_ok = (_c >= '0' && _c <= '9')"));
2841 assert!(result.contains("if (!_ok) return false;"));
2842 }
2843
2844 #[test]
2845 fn test_constrained_string_alphabet_single_char() {
2846 let module = make_constrained_string_module(
2848 "SingleChar",
2849 Type::IA5String(None),
2850 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'x', max: 'x' }]),
2851 );
2852 let result = generate_c(&module).unwrap();
2853 assert!(result.contains("_ok = _c == 'x'"));
2855 }
2856
2857 #[test]
2858 fn test_constrained_string_size_and_alphabet() {
2859 let module = make_constrained_string_module(
2861 "VisStr",
2862 Type::PrintableString(None),
2863 SubtypeConstraint::Intersection(vec![
2864 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2865 min: ConstraintValue::Integer(1),
2866 max: ConstraintValue::Integer(64),
2867 })),
2868 SubtypeConstraint::PermittedAlphabet(vec![
2869 CharRange { min: 'A', max: 'Z' },
2870 CharRange { min: 'a', max: 'z' },
2871 ]),
2872 ]),
2873 );
2874 let result = generate_c(&module).unwrap();
2875
2876 assert!(result.contains("/* PrintableString"));
2877 assert!(result.contains("_len >= 1U") && result.contains("_len <= 64U"));
2879 assert!(result.contains("(_c >= 'A' && _c <= 'Z') || (_c >= 'a' && _c <= 'z')"));
2881 assert!(result.contains("uint32_t _len = self->value.len;"));
2883 }
2884
2885 #[test]
2886 fn test_constrained_string_pattern_placeholder() {
2887 let module = make_constrained_string_module(
2889 "PatStr",
2890 Type::IA5String(None),
2891 SubtypeConstraint::Pattern("[0-9]+".to_string()),
2892 );
2893 let result = generate_c(&module).unwrap();
2894
2895 assert!(result.contains("PATTERN constraint \"[0-9]+\" not enforced at runtime"));
2896 assert!(result.contains("typedef struct { SyntaByteArray value; } PatStr;"));
2898 assert!(result.contains("pat_str_new(SyntaByteArray value, PatStr* out)"));
2899 }
2900
2901 #[test]
2902 fn test_constrained_utf8string() {
2903 let module = make_constrained_string_module(
2905 "Label",
2906 Type::Utf8String(None),
2907 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2908 min: ConstraintValue::Integer(1),
2909 max: ConstraintValue::Integer(255),
2910 })),
2911 );
2912 let result = generate_c(&module).unwrap();
2913
2914 assert!(result.contains("/* UTF8String (SIZE (1..255)) */"));
2915 assert!(result.contains("typedef struct { SyntaByteArray value; } Label;"));
2916 assert!(result.contains("label_new(SyntaByteArray value, Label* out)"));
2917 assert!(result.contains("label_free(Label* self)"));
2918 }
2919
2920 #[test]
2921 fn test_generate_c_length_check() {
2922 assert_eq!(
2924 generate_c_length_check(
2925 "_len",
2926 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(4))
2927 ),
2928 "_len == 4U"
2929 );
2930 assert_eq!(
2932 generate_c_length_check(
2933 "_len",
2934 &SubtypeConstraint::ValueRange {
2935 min: ConstraintValue::Integer(1),
2936 max: ConstraintValue::Max,
2937 }
2938 ),
2939 "(_len >= 1U)"
2940 );
2941 assert_eq!(
2943 generate_c_length_check(
2944 "_len",
2945 &SubtypeConstraint::ValueRange {
2946 min: ConstraintValue::Integer(0),
2947 max: ConstraintValue::Integer(256),
2948 }
2949 ),
2950 "(_len <= 256U)"
2951 );
2952 assert_eq!(
2954 generate_c_length_check(
2955 "_len",
2956 &SubtypeConstraint::ValueRange {
2957 min: ConstraintValue::Min,
2958 max: ConstraintValue::Max,
2959 }
2960 ),
2961 "1"
2962 );
2963 }
2964
2965 #[test]
2966 fn test_format_c_char_literal() {
2967 assert_eq!(format_c_char_literal('A'), "'A'");
2968 assert_eq!(format_c_char_literal('0'), "'0'");
2969 assert_eq!(format_c_char_literal('\''), "'\\''");
2970 assert_eq!(format_c_char_literal('\\'), "'\\\\'");
2971 assert_eq!(format_c_char_literal('\x01'), "'\\x01'");
2972 }
2973
2974 fn make_named_bit_module(name: &str, bits: Vec<NamedNumber>) -> Module {
2979 Module {
2980 name: "TestModule".to_string(),
2981 oid: None,
2982 values: vec![],
2983 tagging_mode: None,
2984 imports: vec![],
2985 exports: vec![],
2986 definitions: vec![Definition {
2987 name: name.to_string(),
2988 ty: Type::Constrained {
2989 base_type: Box::new(Type::BitString(None)),
2990 constraint: Constraint {
2991 spec: ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(bits)),
2992 exception: None,
2993 },
2994 },
2995 }],
2996 }
2997 }
2998
2999 #[test]
3000 fn test_named_bit_string_typedef_and_defines() {
3001 let module = make_named_bit_module(
3003 "TicketFlags",
3004 vec![
3005 NamedNumber {
3006 name: "reserved".to_string(),
3007 value: 0,
3008 },
3009 NamedNumber {
3010 name: "forwardable".to_string(),
3011 value: 1,
3012 },
3013 NamedNumber {
3014 name: "proxiable".to_string(),
3015 value: 3,
3016 },
3017 ],
3018 );
3019 let result = generate_c(&module).unwrap();
3020
3021 assert!(
3023 result.contains("typedef SyntaBitString TicketFlags;"),
3024 "typedef present"
3025 );
3026 assert!(
3028 result.contains("TICKET_FLAGS_RESERVED_BIT"),
3029 "reserved bit define"
3030 );
3031 assert!(
3032 result.contains("TICKET_FLAGS_FORWARDABLE_BIT"),
3033 "forwardable bit define"
3034 );
3035 assert!(
3036 result.contains("TICKET_FLAGS_PROXIABLE_BIT"),
3037 "proxiable bit define"
3038 );
3039 assert!(
3041 result.contains("TICKET_FLAGS_RESERVED_BIT") && result.contains(" 0"),
3042 "value 0 present"
3043 );
3044 assert!(result.contains(" 1"), "value 1 present");
3045 assert!(result.contains(" 3"), "value 3 present");
3046 assert!(!result.contains("IS_SET"), "no IS_SET without helpers");
3048 }
3049
3050 #[test]
3051 fn test_named_bit_string_hyphenated_name() {
3052 let module = make_named_bit_module(
3054 "kdc-options",
3055 vec![
3056 NamedNumber {
3057 name: "reserved".to_string(),
3058 value: 0,
3059 },
3060 NamedNumber {
3061 name: "forwardable".to_string(),
3062 value: 1,
3063 },
3064 ],
3065 );
3066 let result = generate_c(&module).unwrap();
3067 assert!(
3069 result.contains("typedef SyntaBitString KdcOptions;"),
3070 "typedef with PascalCase"
3071 );
3072 assert!(
3073 result.contains("KDC_OPTIONS_RESERVED_BIT"),
3074 "hyphenated prefix uses underscore"
3075 );
3076 assert!(
3077 result.contains("KDC_OPTIONS_FORWARDABLE_BIT"),
3078 "forwardable bit define"
3079 );
3080 }
3081
3082 #[test]
3083 fn test_named_bit_string_camel_case_bit_name() {
3084 let module = make_named_bit_module(
3086 "KeyUsage",
3087 vec![
3088 NamedNumber {
3089 name: "digitalSignature".to_string(),
3090 value: 0,
3091 },
3092 NamedNumber {
3093 name: "nonRepudiation".to_string(),
3094 value: 1,
3095 },
3096 ],
3097 );
3098 let result = generate_c(&module).unwrap();
3099 assert!(
3101 result.contains("KEY_USAGE_DIGITAL_SIGNATURE_BIT"),
3102 "camelCase bit → SCREAMING_SNAKE"
3103 );
3104 assert!(
3105 result.contains("KEY_USAGE_NON_REPUDIATION_BIT"),
3106 "nonRepudiation bit"
3107 );
3108 }
3109
3110 #[test]
3111 fn test_named_bit_string_with_helpers() {
3112 let module = make_named_bit_module(
3113 "TicketFlags",
3114 vec![NamedNumber {
3115 name: "forwardable".to_string(),
3116 value: 1,
3117 }],
3118 );
3119 let config = CCodeGenConfig {
3120 generate_helpers: true,
3121 ..Default::default()
3122 };
3123 let result = generate_c_with_config(&module, config).unwrap();
3124 assert!(
3126 result.contains("TICKET_FLAGS_IS_SET(bs, bit)"),
3127 "IS_SET helper"
3128 );
3129 assert!(result.contains("TICKET_FLAGS_SET(bs, bit)"), "SET helper");
3130 assert!(
3131 result.contains("TICKET_FLAGS_CLEAR(bs, bit)"),
3132 "CLEAR helper"
3133 );
3134 assert!(result.contains("synta_bitstring_is_set"), "is_set API call");
3136 assert!(result.contains("synta_bitstring_set"), "set API call");
3137 assert!(result.contains("synta_bitstring_clear"), "clear API call");
3138 }
3139
3140 #[test]
3141 fn test_named_bit_string_empty_list() {
3142 let module = make_named_bit_module("EmptyFlags", vec![]);
3144 let result = generate_c(&module).unwrap();
3145 assert!(
3146 result.contains("typedef SyntaBitString EmptyFlags;"),
3147 "typedef present"
3148 );
3149 assert!(
3150 !result.contains("EMPTY_FLAGS_"),
3151 "no defines for empty list"
3152 );
3153 }
3154
3155 fn make_named_bit_with_size_module(name: &str, bits: Vec<NamedNumber>) -> Module {
3165 Module {
3167 name: "TestModule".to_string(),
3168 oid: None,
3169 values: vec![],
3170 tagging_mode: None,
3171 imports: vec![],
3172 exports: vec![],
3173 definitions: vec![Definition {
3174 name: name.to_string(),
3175 ty: Type::Constrained {
3176 base_type: Box::new(Type::BitString(None)),
3177 constraint: Constraint {
3178 spec: ConstraintSpec::Subtype(SubtypeConstraint::Intersection(vec![
3179 SubtypeConstraint::NamedBitList(bits),
3180 SubtypeConstraint::SizeConstraint(Box::new(
3181 SubtypeConstraint::ValueRange {
3182 min: ConstraintValue::Integer(32),
3183 max: ConstraintValue::Max,
3184 },
3185 )),
3186 ])),
3187 exception: None,
3188 },
3189 },
3190 }],
3191 }
3192 }
3193
3194 #[test]
3195 fn test_named_bit_string_with_size_emits_typedef() {
3196 let module = make_named_bit_with_size_module(
3198 "TicketFlags",
3199 vec![
3200 NamedNumber {
3201 name: "forwardable".to_string(),
3202 value: 1,
3203 },
3204 NamedNumber {
3205 name: "proxiable".to_string(),
3206 value: 3,
3207 },
3208 ],
3209 );
3210 let result = generate_c(&module).unwrap();
3211 assert!(
3212 result.contains("typedef SyntaBitString TicketFlags;"),
3213 "combined form must still typedef SyntaBitString; got:\n{}",
3214 result
3215 );
3216 assert!(
3217 result.contains("#define TICKET_FLAGS_FORWARDABLE_BIT"),
3218 "FORWARDABLE_BIT define must appear; got:\n{}",
3219 result
3220 );
3221 assert!(
3222 result.contains("#define TICKET_FLAGS_PROXIABLE_BIT"),
3223 "PROXIABLE_BIT define must appear; got:\n{}",
3224 result
3225 );
3226 assert!(
3228 !result.contains("typedef struct { SyntaByteArray value; } TicketFlags;"),
3229 "combined form must not fall through to constrained-string struct; got:\n{}",
3230 result
3231 );
3232 }
3233
3234 #[test]
3235 fn test_named_bit_string_with_size_helpers() {
3236 let module = make_named_bit_with_size_module(
3238 "KdcOptions",
3239 vec![NamedNumber {
3240 name: "forwardable".to_string(),
3241 value: 1,
3242 }],
3243 );
3244 let config = CCodeGenConfig {
3245 generate_helpers: true,
3246 ..Default::default()
3247 };
3248 let result = generate_c_with_config(&module, config).unwrap();
3249 assert!(
3250 result.contains("KDC_OPTIONS_IS_SET(bs, bit)"),
3251 "IS_SET helper must appear; got:\n{}",
3252 result
3253 );
3254 }
3255
3256 fn make_default_module(fields: Vec<SequenceField>) -> Module {
3261 Module {
3262 name: "TestModule".to_string(),
3263 oid: None,
3264 values: vec![],
3265 tagging_mode: None,
3266 imports: vec![],
3267 exports: vec![],
3268 definitions: vec![Definition {
3269 name: "Config".to_string(),
3270 ty: Type::Sequence(fields),
3271 }],
3272 }
3273 }
3274
3275 #[test]
3276 fn test_sequence_default_comment_in_struct() {
3277 let module = make_default_module(vec![
3279 SequenceField {
3280 name: "port".to_string(),
3281 ty: Type::Integer(None, vec![]),
3282 optional: false,
3283 default: Some("8080".to_string()),
3284 },
3285 SequenceField {
3286 name: "enabled".to_string(),
3287 ty: Type::Boolean,
3288 optional: false,
3289 default: Some("TRUE".to_string()),
3290 },
3291 ]);
3292 let result = generate_c(&module).unwrap();
3293 assert!(
3294 result.contains("SyntaInteger* port; /* DEFAULT 8080 */"),
3295 "integer default comment"
3296 );
3297 assert!(
3298 result.contains("bool enabled; /* DEFAULT TRUE */"),
3299 "boolean default comment"
3300 );
3301 }
3302
3303 #[test]
3304 fn test_sequence_default_prototype_generated() {
3305 let module = make_default_module(vec![SequenceField {
3307 name: "port".to_string(),
3308 ty: Type::Integer(None, vec![]),
3309 optional: false,
3310 default: Some("8080".to_string()),
3311 }]);
3312 let result = generate_c(&module).unwrap();
3313 assert!(
3314 result.contains("Config config_default(void);"),
3315 "default prototype generated"
3316 );
3317 }
3318
3319 #[test]
3320 fn test_sequence_no_default_prototype_for_required_field() {
3321 let module = make_default_module(vec![SequenceField {
3323 name: "name".to_string(),
3324 ty: Type::Integer(None, vec![]),
3325 optional: false,
3326 default: None,
3327 }]);
3328 let result = generate_c(&module).unwrap();
3329 assert!(
3330 !result.contains("config_default(void)"),
3331 "no prototype for required-only sequence"
3332 );
3333 }
3334
3335 fn make_tagged_seq_module(
3340 field_name: &str,
3341 class: TagClass,
3342 number: u32,
3343 tagging: Tagging,
3344 inner: Type,
3345 ) -> Module {
3346 Module {
3347 name: "TestModule".to_string(),
3348 oid: None,
3349 values: vec![],
3350 tagging_mode: None,
3351 imports: vec![],
3352 exports: vec![],
3353 definitions: vec![Definition {
3354 name: "Msg".to_string(),
3355 ty: Type::Sequence(vec![SequenceField {
3356 name: field_name.to_string(),
3357 ty: Type::Tagged {
3358 tag: TagInfo {
3359 class,
3360 number,
3361 tagging,
3362 },
3363 inner: Box::new(inner),
3364 },
3365 optional: false,
3366 default: None,
3367 }]),
3368 }],
3369 }
3370 }
3371
3372 #[test]
3373 fn test_explicit_tag_annotation_in_struct() {
3374 let module = make_tagged_seq_module(
3376 "id",
3377 TagClass::ContextSpecific,
3378 0,
3379 Tagging::Explicit,
3380 Type::Integer(None, vec![]),
3381 );
3382 let result = generate_c(&module).unwrap();
3383 assert!(
3384 result.contains("SyntaInteger* id; /* [0] EXPLICIT */"),
3385 "explicit tag comment missing; got:\n{}",
3386 result
3387 );
3388 }
3389
3390 #[test]
3391 fn test_implicit_tag_annotation_in_struct() {
3392 let module = make_tagged_seq_module(
3394 "data",
3395 TagClass::ContextSpecific,
3396 1,
3397 Tagging::Implicit,
3398 Type::OctetString(None),
3399 );
3400 let result = generate_c(&module).unwrap();
3401 assert!(
3402 result.contains("SyntaOctetString* data; /* [1] IMPLICIT */"),
3403 "implicit tag comment missing; got:\n{}",
3404 result
3405 );
3406 }
3407
3408 #[test]
3409 fn test_application_tag_annotation_in_struct() {
3410 let module = make_tagged_seq_module(
3412 "val",
3413 TagClass::Application,
3414 2,
3415 Tagging::Implicit,
3416 Type::Integer(None, vec![]),
3417 );
3418 let result = generate_c(&module).unwrap();
3419 assert!(
3420 result.contains("SyntaInteger* val; /* [APPLICATION 2] IMPLICIT */"),
3421 "APPLICATION tag comment missing; got:\n{}",
3422 result
3423 );
3424 }
3425
3426 fn make_values_module(values: Vec<crate::ast::ValueAssignment>) -> Module {
3431 Module {
3432 name: "TestModule".to_string(),
3433 oid: None,
3434 values,
3435 tagging_mode: None,
3436 imports: vec![],
3437 exports: vec![],
3438 definitions: vec![],
3439 }
3440 }
3441
3442 #[test]
3443 fn test_oid_value_constant_emitted() {
3444 let module = make_values_module(vec![crate::ast::ValueAssignment {
3446 name: "id-ori".to_string(),
3447 ty: Type::ObjectIdentifier,
3448 value: Value::ObjectIdentifier(vec![
3449 OidComponent::Number(1),
3450 OidComponent::Number(2),
3451 OidComponent::Number(840),
3452 OidComponent::Number(113549),
3453 OidComponent::Number(1),
3454 OidComponent::Number(9),
3455 OidComponent::Number(16),
3456 OidComponent::Number(13),
3457 ]),
3458 }]);
3459 let result = generate_c(&module).unwrap();
3460 assert!(
3461 result.contains("static const uint32_t ID_ORI[] = {1, 2, 840, 113549, 1, 9, 16, 13};"),
3462 "OID array missing:\n{}",
3463 result
3464 );
3465 assert!(
3466 result.contains("#define ID_ORI_LEN 8"),
3467 "_LEN define missing:\n{}",
3468 result
3469 );
3470 }
3471
3472 #[test]
3473 fn test_oid_named_reference_resolved() {
3474 let module = make_values_module(vec![
3477 crate::ast::ValueAssignment {
3478 name: "id-ori".to_string(),
3479 ty: Type::ObjectIdentifier,
3480 value: Value::ObjectIdentifier(vec![
3481 OidComponent::Number(1),
3482 OidComponent::Number(2),
3483 OidComponent::Number(840),
3484 OidComponent::Number(113549),
3485 OidComponent::Number(1),
3486 OidComponent::Number(9),
3487 OidComponent::Number(16),
3488 OidComponent::Number(13),
3489 ]),
3490 },
3491 crate::ast::ValueAssignment {
3492 name: "id-ori-kem".to_string(),
3493 ty: Type::ObjectIdentifier,
3494 value: Value::ObjectIdentifier(vec![
3495 OidComponent::NamedRef("id-ori".to_string()),
3496 OidComponent::Number(3),
3497 ]),
3498 },
3499 ]);
3500 let result = generate_c(&module).unwrap();
3501 assert!(
3502 result.contains(
3503 "static const uint32_t ID_ORI_KEM[] = {1, 2, 840, 113549, 1, 9, 16, 13, 3};"
3504 ),
3505 "resolved child OID missing:\n{}",
3506 result
3507 );
3508 assert!(
3509 result.contains("#define ID_ORI_KEM_LEN 9"),
3510 "_LEN for child OID missing:\n{}",
3511 result
3512 );
3513 }
3514
3515 #[test]
3516 fn test_oid_unresolvable_named_ref_emits_comment() {
3517 let module = make_values_module(vec![crate::ast::ValueAssignment {
3519 name: "my-oid".to_string(),
3520 ty: Type::ObjectIdentifier,
3521 value: Value::ObjectIdentifier(vec![
3522 OidComponent::NamedRef("undefined-base".to_string()),
3523 OidComponent::Number(1),
3524 ]),
3525 }]);
3526 let result = generate_c(&module).unwrap();
3527 assert!(
3528 result.contains("could not be fully resolved"),
3529 "unresolvable OID should produce a comment:\n{}",
3530 result
3531 );
3532 assert!(
3534 !result.contains("static const uint32_t MY_OID[] ="),
3535 "broken array must not be emitted:\n{}",
3536 result
3537 );
3538 }
3539
3540 #[test]
3541 fn test_integer_value_constant() {
3542 let module = make_values_module(vec![crate::ast::ValueAssignment {
3543 name: "max-count".to_string(),
3544 ty: Type::Integer(None, vec![]),
3545 value: Value::Integer(256),
3546 }]);
3547 let result = generate_c(&module).unwrap();
3548 assert!(
3549 result.contains("#define MAX_COUNT ((int64_t)256)"),
3550 "integer constant missing:\n{}",
3551 result
3552 );
3553 }
3554
3555 #[test]
3556 fn test_boolean_value_constant() {
3557 let module = make_values_module(vec![
3558 crate::ast::ValueAssignment {
3559 name: "flag-true".to_string(),
3560 ty: Type::Boolean,
3561 value: Value::Boolean(true),
3562 },
3563 crate::ast::ValueAssignment {
3564 name: "flag-false".to_string(),
3565 ty: Type::Boolean,
3566 value: Value::Boolean(false),
3567 },
3568 ]);
3569 let result = generate_c(&module).unwrap();
3570 assert!(
3571 result.contains("#define FLAG_TRUE (true)"),
3572 "true constant missing:\n{}",
3573 result
3574 );
3575 assert!(
3576 result.contains("#define FLAG_FALSE (false)"),
3577 "false constant missing:\n{}",
3578 result
3579 );
3580 }
3581
3582 #[test]
3583 fn test_string_value_constant() {
3584 let module = make_values_module(vec![crate::ast::ValueAssignment {
3585 name: "default-realm".to_string(),
3586 ty: Type::Utf8String(None),
3587 value: Value::String("EXAMPLE.COM".to_string()),
3588 }]);
3589 let result = generate_c(&module).unwrap();
3590 assert!(
3591 result.contains("#define DEFAULT_REALM \"EXAMPLE.COM\""),
3592 "string constant missing:\n{}",
3593 result
3594 );
3595 }
3596
3597 #[test]
3598 fn test_string_escape_in_constant() {
3599 let module = make_values_module(vec![crate::ast::ValueAssignment {
3600 name: "path".to_string(),
3601 ty: Type::Utf8String(None),
3602 value: Value::String("C:\\foo\\bar".to_string()),
3603 }]);
3604 let result = generate_c(&module).unwrap();
3605 assert!(
3606 result.contains("#define PATH \"C:\\\\foo\\\\bar\""),
3607 "backslash not escaped:\n{}",
3608 result
3609 );
3610 }
3611
3612 #[test]
3613 fn test_value_constants_section_header() {
3614 let module = make_values_module(vec![crate::ast::ValueAssignment {
3616 name: "x".to_string(),
3617 ty: Type::Integer(None, vec![]),
3618 value: Value::Integer(1),
3619 }]);
3620 let result = generate_c(&module).unwrap();
3621 assert!(
3622 result.contains("/* Value constants */"),
3623 "section comment missing"
3624 );
3625 }
3626
3627 #[test]
3628 fn test_no_value_constants_section_when_empty() {
3629 let module = make_values_module(vec![]);
3631 let result = generate_c(&module).unwrap();
3632 assert!(
3633 !result.contains("/* Value constants */"),
3634 "spurious section comment"
3635 );
3636 }
3637
3638 fn make_import_module(imports: Vec<Import>) -> Module {
3643 Module {
3644 name: "TestModule".to_string(),
3645 oid: None,
3646 values: vec![],
3647 tagging_mode: None,
3648 imports,
3649 exports: vec![],
3650 definitions: vec![],
3651 }
3652 }
3653
3654 #[test]
3655 fn test_import_generates_include() {
3656 let module = make_import_module(vec![Import {
3658 symbols: vec!["AlgorithmIdentifier".to_string()],
3659 module_name: "AlgorithmInformation-2009".to_string(),
3660 }]);
3661 let result = generate_c(&module).unwrap();
3662 assert!(
3663 result.contains("#include \"algorithm_information_2009.h\""),
3664 "import include missing:\n{}",
3665 result
3666 );
3667 assert!(
3668 result.contains("/* Imported module headers */"),
3669 "import section comment missing:\n{}",
3670 result
3671 );
3672 }
3673
3674 #[test]
3675 fn test_multiple_imports_generate_includes() {
3676 let module = make_import_module(vec![
3677 Import {
3678 symbols: vec!["Name".to_string()],
3679 module_name: "PKIX1Explicit88".to_string(),
3680 },
3681 Import {
3682 symbols: vec!["AlgorithmIdentifier".to_string()],
3683 module_name: "AlgorithmInformation-2009".to_string(),
3684 },
3685 ]);
3686 let result = generate_c(&module).unwrap();
3687 assert!(
3688 result.contains("#include \"pkix1_explicit88.h\""),
3689 "first import missing:\n{}",
3690 result
3691 );
3692 assert!(
3693 result.contains("#include \"algorithm_information_2009.h\""),
3694 "second import missing:\n{}",
3695 result
3696 );
3697 }
3698
3699 #[test]
3700 fn test_no_imports_no_import_section() {
3701 let module = make_import_module(vec![]);
3702 let result = generate_c(&module).unwrap();
3703 assert!(
3704 !result.contains("/* Imported module headers */"),
3705 "spurious import section:\n{}",
3706 result
3707 );
3708 }
3709
3710 #[test]
3715 fn test_choice_inline_sequence_generates_named_struct() {
3716 let module = Module {
3720 name: "TestModule".to_string(),
3721 oid: None,
3722 values: vec![],
3723 tagging_mode: None,
3724 imports: vec![],
3725 exports: vec![],
3726 definitions: vec![Definition {
3727 name: "MyChoice".to_string(),
3728 ty: Type::Choice(vec![
3729 ChoiceVariant {
3730 name: "seqVal".to_string(),
3731 ty: Type::Sequence(vec![SequenceField {
3732 name: "x".to_string(),
3733 ty: Type::Integer(None, vec![]),
3734 optional: false,
3735 default: None,
3736 }]),
3737 },
3738 ChoiceVariant {
3739 name: "intVal".to_string(),
3740 ty: Type::Integer(None, vec![]),
3741 },
3742 ]),
3743 }],
3744 };
3745 let result = generate_c(&module).unwrap();
3746 assert!(
3748 result.contains("struct MyChoiceSeqVal {"),
3749 "expected named struct MyChoiceSeqVal for inline SEQUENCE variant:\n{}",
3750 result
3751 );
3752 assert!(
3754 result.contains("MyChoiceSeqVal seq_val;"),
3755 "union member should be 'MyChoiceSeqVal seq_val;':\n{}",
3756 result
3757 );
3758 assert!(
3760 !result.contains("void* seq_val"),
3761 "void* placeholder must not appear after expansion:\n{}",
3762 result
3763 );
3764 assert!(
3766 result.contains("SyntaInteger* int_val;"),
3767 "regular integer variant missing:\n{}",
3768 result
3769 );
3770 let fwd_inner = result
3772 .find("typedef struct MyChoiceSeqVal")
3773 .unwrap_or(usize::MAX);
3774 let fwd_outer = result
3775 .find("typedef struct MyChoice MyChoice;")
3776 .unwrap_or(usize::MAX);
3777 assert!(
3778 fwd_inner < fwd_outer,
3779 "MyChoiceSeqVal forward decl must precede MyChoice:\n{}",
3780 result
3781 );
3782 }
3783
3784 #[test]
3785 fn test_sequence_all_optional_gets_default_prototype() {
3786 let module = make_default_module(vec![
3789 SequenceField {
3790 name: "host".to_string(),
3791 ty: Type::OctetString(None),
3792 optional: true,
3793 default: None,
3794 },
3795 SequenceField {
3796 name: "port".to_string(),
3797 ty: Type::Integer(None, vec![]),
3798 optional: true,
3799 default: None,
3800 },
3801 ]);
3802 let result = generate_c(&module).unwrap();
3803 assert!(
3804 result.contains("Config config_default(void);"),
3805 "prototype for all-optional sequence"
3806 );
3807 }
3808}