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 {
1270 tag: tag_info,
1271 inner,
1272 } = ty
1273 {
1274 let cls = match tag_info.class {
1275 TagClass::Universal => format!("UNIVERSAL {}", tag_info.number),
1276 TagClass::Application => format!("APPLICATION {}", tag_info.number),
1277 TagClass::ContextSpecific => tag_info.number.to_string(),
1278 TagClass::Private => format!("PRIVATE {}", tag_info.number),
1279 };
1280 let inner_is_choice_or_any = matches!(
1283 inner.as_ref(),
1284 Type::Choice(_) | Type::Any | Type::AnyDefinedBy(_)
1285 );
1286 let mode = match tag_info.tagging {
1287 Tagging::Explicit => "EXPLICIT",
1288 Tagging::Implicit if inner_is_choice_or_any => "EXPLICIT",
1289 Tagging::Implicit => "IMPLICIT",
1290 };
1291 Some(format!("[{}] {}", cls, mode))
1292 } else {
1293 None
1294 }
1295}
1296
1297fn generate_sequence_struct(
1299 output: &mut String,
1300 name: &str,
1301 fields: &[SequenceField],
1302) -> Result<(), Box<dyn std::error::Error>> {
1303 let struct_name = to_pascal_case(name);
1304 writeln!(output, "struct {} {{", struct_name)?;
1305
1306 for field in fields {
1307 if matches!(field.ty, Type::Null) {
1309 continue;
1310 }
1311
1312 let field_name = to_snake_case(&field.name);
1313
1314 match &field.ty {
1316 Type::Sequence(inner_fields) | Type::Set(inner_fields) => {
1317 if field.optional {
1319 writeln!(output, " bool has_{};", field_name)?;
1320 }
1321 writeln!(output, " struct {{")?;
1322 for inner_field in inner_fields {
1323 if matches!(inner_field.ty, Type::Null) {
1324 continue;
1325 }
1326 let inner_name = to_snake_case(&inner_field.name);
1327 let inner_type = get_c_type_for_field(&inner_field.ty);
1328 if inner_field.optional {
1329 writeln!(output, " bool has_{};", inner_name)?;
1330 writeln!(output, " {} {};", inner_type, inner_name)?;
1331 } else {
1332 writeln!(output, " {} {};", inner_type, inner_name)?;
1333 }
1334 }
1335 writeln!(output, " }} {};", field_name)?;
1336 }
1337 _ => {
1338 let field_type = get_c_type_for_field(&field.ty);
1339 let tag_note = tag_annotation_comment(&field.ty);
1340
1341 if matches!(field.ty, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1343 writeln!(output, " size_t {}_count;", field_name)?;
1344 writeln!(output, " {} {};", field_type, field_name)?;
1345 } else if field.optional {
1346 writeln!(output, " bool has_{};", field_name)?;
1347 if let Some(ref tn) = tag_note {
1348 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1349 } else {
1350 writeln!(output, " {} {};", field_type, field_name)?;
1351 }
1352 } else if let Some(ref dv) = field.default {
1353 if let Some(ref tn) = tag_note {
1354 writeln!(
1355 output,
1356 " {} {}; /* {} DEFAULT {} */",
1357 field_type, field_name, tn, dv
1358 )?;
1359 } else {
1360 writeln!(
1361 output,
1362 " {} {}; /* DEFAULT {} */",
1363 field_type, field_name, dv
1364 )?;
1365 }
1366 } else if let Some(ref tn) = tag_note {
1367 writeln!(output, " {} {}; /* {} */", field_type, field_name, tn)?;
1368 } else {
1369 writeln!(output, " {} {};", field_type, field_name)?;
1370 }
1371 }
1372 }
1373 }
1374
1375 writeln!(output, "}};")?;
1376 Ok(())
1377}
1378
1379fn get_c_type_for_field(ty: &Type) -> String {
1381 match ty {
1382 Type::TypeRef(name) => to_pascal_case(name),
1383 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1384 format!("{}*", get_c_type(inner))
1386 }
1387 _ => get_c_type(ty),
1388 }
1389}
1390
1391fn generate_choice_struct(
1393 output: &mut String,
1394 name: &str,
1395 variants: &[ChoiceVariant],
1396) -> Result<(), Box<dyn std::error::Error>> {
1397 let struct_name = to_pascal_case(name);
1398 let tag_enum_name = format!("{}Tag", struct_name);
1399
1400 writeln!(output, "typedef enum {} {{", tag_enum_name)?;
1402 for (i, variant) in variants.iter().enumerate() {
1403 let variant_name = to_pascal_case(&variant.name);
1404 let comma = if i < variants.len() - 1 { "," } else { "" };
1405 writeln!(output, " {}_{}{}", tag_enum_name, variant_name, comma)?;
1406 }
1407 writeln!(output, "}} {};", tag_enum_name)?;
1408 writeln!(output)?;
1409
1410 writeln!(output, "struct {} {{", struct_name)?;
1412 writeln!(output, " {} tag;", tag_enum_name)?;
1413 writeln!(output, " union {{")?;
1414
1415 for variant in variants {
1416 if matches!(variant.ty, Type::Null) {
1418 continue;
1419 }
1420
1421 let variant_name = to_snake_case(&variant.name);
1422 if matches!(peel_type(&variant.ty), Type::Sequence(_) | Type::Set(_)) {
1425 writeln!(
1426 output,
1427 " void* {}; /* inline SEQUENCE/SET — define separately and cast */",
1428 variant_name
1429 )?;
1430 } else {
1431 let variant_type = get_c_type_for_field(&variant.ty);
1432 writeln!(output, " {} {};", variant_type, variant_name)?;
1433 }
1434 }
1435
1436 writeln!(output, " }} value;")?;
1437 writeln!(output, "}};")?;
1438 Ok(())
1439}
1440
1441fn generate_enum_for_integer(
1443 output: &mut String,
1444 name: &str,
1445 named_numbers: &[NamedNumber],
1446) -> Result<(), Box<dyn std::error::Error>> {
1447 let enum_name = to_pascal_case(name);
1448 writeln!(output, "enum {} {{", enum_name)?;
1449
1450 for (i, nn) in named_numbers.iter().enumerate() {
1451 let variant_name = to_screaming_snake_case(&nn.name);
1452 let comma = if i < named_numbers.len() - 1 { "," } else { "" };
1453 writeln!(
1454 output,
1455 " {}_{} = {}{}",
1456 enum_name, variant_name, nn.value, comma
1457 )?;
1458 }
1459
1460 writeln!(output, "}};")?;
1461 Ok(())
1462}
1463
1464fn generate_defines_for_integer(
1493 output: &mut String,
1494 name: &str,
1495 named_numbers: &[NamedNumber],
1496) -> Result<(), Box<dyn std::error::Error>> {
1497 let type_name = to_pascal_case(name);
1498 let prefix = to_screaming_snake_case(name);
1499
1500 writeln!(output, "/* Named integer values for {} */", type_name)?;
1501
1502 let macro_names: Vec<String> = named_numbers
1504 .iter()
1505 .map(|nn| format!("{}_{}", prefix, to_snake_case(&nn.name).to_uppercase()))
1506 .collect();
1507 let max_len = macro_names.iter().map(|s| s.len()).max().unwrap_or(0);
1508
1509 for (nn, macro_name) in named_numbers.iter().zip(¯o_names) {
1510 writeln!(
1511 output,
1512 "#define {:width$} ((int64_t){})",
1513 macro_name,
1514 nn.value,
1515 width = max_len
1516 )?;
1517 }
1518
1519 Ok(())
1520}
1521
1522fn generate_encoder_decoder_prototypes(
1524 output: &mut String,
1525 def: &Definition,
1526 arena_mode: bool,
1527) -> Result<(), Box<dyn std::error::Error>> {
1528 let c_name = to_pascal_case(&def.name);
1529 let fn_prefix = to_snake_case(&def.name);
1530
1531 writeln!(
1533 output,
1534 "SyntaErrorCode {}_decode(SyntaDecoder* decoder, {}* out);",
1535 fn_prefix, c_name
1536 )?;
1537
1538 if arena_mode {
1540 writeln!(
1541 output,
1542 "SyntaErrorCode {}_decode_arena(SyntaDecoder* decoder, SyntaArena* arena, {}* out);",
1543 fn_prefix, c_name
1544 )?;
1545 }
1546
1547 writeln!(
1549 output,
1550 "SyntaErrorCode {}_encode(SyntaEncoder* encoder, const {}* value);",
1551 fn_prefix, c_name
1552 )?;
1553
1554 match &def.ty {
1556 Type::Sequence(_)
1557 | Type::Set(_)
1558 | Type::Choice(_)
1559 | Type::SequenceOf(_, _)
1560 | Type::SetOf(_, _) => {
1561 writeln!(output, "void {}_free({}* value);", fn_prefix, c_name)?;
1562 }
1563 _ => {}
1564 }
1565
1566 if let Type::Sequence(fields) | Type::Set(fields) = peel_type(&def.ty) {
1568 if fields.iter().all(|f| f.optional || f.default.is_some()) {
1569 writeln!(output, "{} {}_default(void);", c_name, fn_prefix)?;
1570 }
1571 }
1572
1573 Ok(())
1574}
1575
1576fn generate_helper_functions(
1578 output: &mut String,
1579 def: &Definition,
1580) -> Result<(), Box<dyn std::error::Error>> {
1581 let c_name = to_pascal_case(&def.name);
1582 let fn_prefix = to_snake_case(&def.name);
1583
1584 match &def.ty {
1585 Type::Sequence(fields) | Type::Set(fields) => {
1586 generate_init_helper(output, &c_name, &fn_prefix)?;
1587 writeln!(output)?;
1588 generate_validate_sequence(output, &c_name, &fn_prefix, fields)?;
1589 writeln!(output)?;
1590 generate_print_sequence(output, &c_name, &fn_prefix, fields)?;
1591 }
1592 Type::Choice(variants) => {
1593 generate_validate_choice(output, &c_name, &fn_prefix, variants)?;
1594 writeln!(output)?;
1595 generate_print_choice(output, &c_name, &fn_prefix, variants)?;
1596 }
1597 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
1598 generate_init_helper(output, &c_name, &fn_prefix)?;
1599 writeln!(output)?;
1600 generate_validate_array(output, &c_name, &fn_prefix)?;
1601 writeln!(output)?;
1602 generate_print_array(output, &c_name, &fn_prefix, inner)?;
1603 }
1604 _ => {}
1605 }
1606
1607 Ok(())
1608}
1609
1610fn generate_init_helper(
1615 output: &mut String,
1616 c_name: &str,
1617 fn_prefix: &str,
1618) -> Result<(), Box<dyn std::error::Error>> {
1619 writeln!(
1620 output,
1621 "static inline void {}_init({}* value) {{",
1622 fn_prefix, c_name
1623 )?;
1624 writeln!(output, " if (value != NULL) {{")?;
1625 writeln!(output, " memset(value, 0, sizeof({}));", c_name)?;
1626 writeln!(output, " }}")?;
1627 writeln!(output, "}}")?;
1628 Ok(())
1629}
1630
1631fn generate_validate_sequence(
1639 output: &mut String,
1640 c_name: &str,
1641 fn_prefix: &str,
1642 fields: &[SequenceField],
1643) -> Result<(), Box<dyn std::error::Error>> {
1644 writeln!(
1645 output,
1646 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1647 fn_prefix, c_name
1648 )?;
1649 writeln!(
1650 output,
1651 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1652 )?;
1653 for field in fields {
1654 if matches!(field.ty, Type::Null) {
1655 continue;
1656 }
1657 let field_name = to_snake_case(&field.name);
1658 if is_pointer_c_type(&field.ty) {
1659 if field.optional {
1660 writeln!(
1661 output,
1662 " if (value->has_{name} && value->{name} == NULL) return SyntaErrorCode_InvalidArgument;",
1663 name = field_name
1664 )?;
1665 } else {
1666 writeln!(
1667 output,
1668 " if (value->{} == NULL) return SyntaErrorCode_InvalidArgument;",
1669 field_name
1670 )?;
1671 }
1672 }
1673 }
1674 writeln!(output, " return SyntaErrorCode_Success;")?;
1675 writeln!(output, "}}")?;
1676 Ok(())
1677}
1678
1679fn generate_print_sequence(
1686 output: &mut String,
1687 c_name: &str,
1688 fn_prefix: &str,
1689 fields: &[SequenceField],
1690) -> Result<(), Box<dyn std::error::Error>> {
1691 writeln!(
1692 output,
1693 "static inline void {}_print(const {}* value, FILE* stream) {{",
1694 fn_prefix, c_name
1695 )?;
1696 writeln!(
1697 output,
1698 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1699 c_name
1700 )?;
1701 writeln!(output, " fprintf(stream, \"{}{{\\n\");", c_name)?;
1702 for field in fields {
1703 if matches!(field.ty, Type::Null) {
1704 continue;
1705 }
1706 let field_name = to_snake_case(&field.name);
1707 write_sequence_field_print(output, &field_name, &field.ty, field.optional)?;
1708 }
1709 writeln!(output, " fprintf(stream, \"}}\\n\");")?;
1710 writeln!(output, "}}")?;
1711 Ok(())
1712}
1713
1714fn write_sequence_field_print(
1716 output: &mut String,
1717 field_name: &str,
1718 ty: &Type,
1719 optional: bool,
1720) -> Result<(), Box<dyn std::error::Error>> {
1721 let base = peel_type(ty);
1722
1723 if matches!(base, Type::SequenceOf(_, _) | Type::SetOf(_, _)) {
1725 write_field_value_print(
1726 output,
1727 field_name,
1728 base,
1729 &format!("value->{}", field_name),
1730 " ",
1731 )?;
1732 return Ok(());
1733 }
1734
1735 if optional {
1736 writeln!(output, " if (value->has_{}) {{", field_name)?;
1737 write_field_value_print(
1738 output,
1739 field_name,
1740 base,
1741 &format!("value->{}", field_name),
1742 " ",
1743 )?;
1744 writeln!(output, " }} else {{")?;
1745 writeln!(
1746 output,
1747 " fprintf(stream, \" {}: (absent)\\n\");",
1748 field_name
1749 )?;
1750 writeln!(output, " }}")?;
1751 } else {
1752 write_field_value_print(
1753 output,
1754 field_name,
1755 base,
1756 &format!("value->{}", field_name),
1757 " ",
1758 )?;
1759 }
1760 Ok(())
1761}
1762
1763fn write_field_value_print(
1765 output: &mut String,
1766 field_name: &str,
1767 ty: &Type,
1768 field_expr: &str,
1769 indent: &str,
1770) -> Result<(), Box<dyn std::error::Error>> {
1771 match ty {
1772 Type::Boolean => {
1773 writeln!(
1774 output,
1775 "{}fprintf(stream, \" {}: %s\\n\", {} ? \"true\" : \"false\");",
1776 indent, field_name, field_expr
1777 )?;
1778 }
1779 Type::Real => {
1780 writeln!(
1781 output,
1782 "{}fprintf(stream, \" {}: %g\\n\", {});",
1783 indent, field_name, field_expr
1784 )?;
1785 }
1786 Type::OctetString(_)
1787 | Type::Utf8String(_)
1788 | Type::PrintableString(_)
1789 | Type::IA5String(_)
1790 | Type::UtcTime
1791 | Type::GeneralizedTime
1792 | Type::Any
1793 | Type::AnyDefinedBy(_) => {
1794 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1795 writeln!(
1796 output,
1797 "{} fprintf(stream, \" {}: <string(%zu bytes)>\\n\", synta_octet_string_len({}));",
1798 indent, field_name, field_expr
1799 )?;
1800 writeln!(output, "{}else", indent)?;
1801 writeln!(
1802 output,
1803 "{} fprintf(stream, \" {}: NULL\\n\");",
1804 indent, field_name
1805 )?;
1806 }
1807 Type::Integer(_, _) | Type::Enumerated(_) => {
1808 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1809 writeln!(
1810 output,
1811 "{} fprintf(stream, \" {}: <integer>\\n\");",
1812 indent, field_name
1813 )?;
1814 writeln!(output, "{}else", indent)?;
1815 writeln!(
1816 output,
1817 "{} fprintf(stream, \" {}: NULL\\n\");",
1818 indent, field_name
1819 )?;
1820 }
1821 Type::ObjectIdentifier => {
1822 writeln!(output, "{}if ({} != NULL)", indent, field_expr)?;
1823 writeln!(
1824 output,
1825 "{} fprintf(stream, \" {}: <oid>\\n\");",
1826 indent, field_name
1827 )?;
1828 writeln!(output, "{}else", indent)?;
1829 writeln!(
1830 output,
1831 "{} fprintf(stream, \" {}: NULL\\n\");",
1832 indent, field_name
1833 )?;
1834 }
1835 Type::BitString(_) => {
1836 writeln!(
1837 output,
1838 "{}fprintf(stream, \" {}: <bit-string(%u bytes)>\\n\", (unsigned int){}.data.len);",
1839 indent, field_name, field_expr
1840 )?;
1841 }
1842 Type::TypeRef(name) => {
1843 let type_name = to_pascal_case(name);
1844 writeln!(
1845 output,
1846 "{}fprintf(stream, \" {}: <{}>\\n\");",
1847 indent, field_name, type_name
1848 )?;
1849 }
1850 Type::Sequence(_) | Type::Set(_) => {
1851 writeln!(
1852 output,
1853 "{}fprintf(stream, \" {}: <struct>\\n\");",
1854 indent, field_name
1855 )?;
1856 }
1857 Type::SequenceOf(_, _) | Type::SetOf(_, _) => {
1858 writeln!(
1860 output,
1861 "{}fprintf(stream, \" {}: [%zu elements]\\n\", {}_count);",
1862 indent, field_name, field_expr
1863 )?;
1864 }
1865 Type::Choice(_) => {
1866 writeln!(
1867 output,
1868 "{}fprintf(stream, \" {}: <choice>\\n\");",
1869 indent, field_name
1870 )?;
1871 }
1872 Type::Null => {}
1873 _ => {
1875 writeln!(
1876 output,
1877 "{}fprintf(stream, \" {}: <value>\\n\");",
1878 indent, field_name
1879 )?;
1880 }
1881 }
1882 Ok(())
1883}
1884
1885fn generate_validate_choice(
1892 output: &mut String,
1893 c_name: &str,
1894 fn_prefix: &str,
1895 variants: &[ChoiceVariant],
1896) -> Result<(), Box<dyn std::error::Error>> {
1897 let tag_enum = format!("{}Tag", c_name);
1898 writeln!(
1899 output,
1900 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1901 fn_prefix, c_name
1902 )?;
1903 writeln!(
1904 output,
1905 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1906 )?;
1907 writeln!(output, " switch (value->tag) {{")?;
1908 for variant in variants {
1909 let variant_name = to_pascal_case(&variant.name);
1910 writeln!(output, " case {}_{}: break;", tag_enum, variant_name)?;
1911 }
1912 writeln!(
1913 output,
1914 " default: return SyntaErrorCode_InvalidEncoding;"
1915 )?;
1916 writeln!(output, " }}")?;
1917 writeln!(output, " return SyntaErrorCode_Success;")?;
1918 writeln!(output, "}}")?;
1919 Ok(())
1920}
1921
1922fn generate_print_choice(
1928 output: &mut String,
1929 c_name: &str,
1930 fn_prefix: &str,
1931 variants: &[ChoiceVariant],
1932) -> Result<(), Box<dyn std::error::Error>> {
1933 let tag_enum = format!("{}Tag", c_name);
1934 writeln!(
1935 output,
1936 "static inline void {}_print(const {}* value, FILE* stream) {{",
1937 fn_prefix, c_name
1938 )?;
1939 writeln!(
1940 output,
1941 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
1942 c_name
1943 )?;
1944 writeln!(output, " switch (value->tag) {{")?;
1945 for variant in variants {
1946 let variant_name = to_pascal_case(&variant.name);
1947 let variant_field = to_snake_case(&variant.name);
1948 writeln!(output, " case {}_{}:", tag_enum, variant_name)?;
1949 writeln!(
1950 output,
1951 " fprintf(stream, \"{}{{ {} }}\\n\");",
1952 c_name, variant_field
1953 )?;
1954 writeln!(output, " break;")?;
1955 }
1956 writeln!(output, " default:")?;
1957 writeln!(
1958 output,
1959 " fprintf(stream, \"{}{{ <unknown tag> }}\\n\");",
1960 c_name
1961 )?;
1962 writeln!(output, " break;")?;
1963 writeln!(output, " }}")?;
1964 writeln!(output, "}}")?;
1965 Ok(())
1966}
1967
1968fn generate_validate_array(
1975 output: &mut String,
1976 c_name: &str,
1977 fn_prefix: &str,
1978) -> Result<(), Box<dyn std::error::Error>> {
1979 writeln!(
1980 output,
1981 "static inline SyntaErrorCode {}_validate(const {}* value) {{",
1982 fn_prefix, c_name
1983 )?;
1984 writeln!(
1985 output,
1986 " if (value == NULL) return SyntaErrorCode_NullPointer;"
1987 )?;
1988 writeln!(
1989 output,
1990 " if (value->count > 0 && value->items == NULL) return SyntaErrorCode_InvalidArgument;"
1991 )?;
1992 writeln!(output, " return SyntaErrorCode_Success;")?;
1993 writeln!(output, "}}")?;
1994 Ok(())
1995}
1996
1997fn generate_print_array(
2005 output: &mut String,
2006 c_name: &str,
2007 fn_prefix: &str,
2008 _inner: &Type,
2009) -> Result<(), Box<dyn std::error::Error>> {
2010 writeln!(
2011 output,
2012 "static inline void {}_print(const {}* value, FILE* stream) {{",
2013 fn_prefix, c_name
2014 )?;
2015 writeln!(
2016 output,
2017 " if (value == NULL) {{ fprintf(stream, \"{}(null)\\n\"); return; }}",
2018 c_name
2019 )?;
2020 writeln!(
2021 output,
2022 " fprintf(stream, \"{}[%zu]\\n\", value->count);",
2023 c_name
2024 )?;
2025 writeln!(output, "}}")?;
2026 Ok(())
2027}
2028
2029fn build_c_oid_registry(values: &[ValueAssignment]) -> std::collections::HashMap<String, Vec<u32>> {
2036 use std::collections::HashMap;
2037 let mut registry: HashMap<String, Vec<u32>> = HashMap::new();
2038
2039 let mut changed = true;
2040 while changed {
2041 changed = false;
2042 for va in values {
2043 if registry.contains_key(&va.name) {
2044 continue;
2045 }
2046 if let Value::ObjectIdentifier(components) = &va.value {
2047 let mut resolved = Vec::new();
2048 let mut can_resolve = true;
2049 for component in components {
2050 match component {
2051 OidComponent::Number(n) => resolved.push(*n),
2052 OidComponent::NamedRef(name) => {
2053 if let Some(base) = registry.get(name) {
2054 resolved.extend_from_slice(base);
2055 } else {
2056 can_resolve = false;
2057 break;
2058 }
2059 }
2060 }
2061 }
2062 if can_resolve {
2063 registry.insert(va.name.clone(), resolved);
2064 changed = true;
2065 }
2066 }
2067 }
2068 }
2069 registry
2070}
2071
2072fn escape_c_string(s: &str) -> String {
2077 let mut out = String::with_capacity(s.len());
2078 for ch in s.chars() {
2079 match ch {
2080 '\\' => out.push_str("\\\\"),
2081 '"' => out.push_str("\\\""),
2082 '\n' => out.push_str("\\n"),
2083 '\r' => out.push_str("\\r"),
2084 '\t' => out.push_str("\\t"),
2085 c if c.is_ascii() && (c as u8) >= 0x20 && (c as u8) < 0x7f => out.push(c),
2086 c => {
2087 for byte in c.to_string().as_bytes() {
2088 out.push_str(&format!("\\x{:02x}", byte));
2089 }
2090 }
2091 }
2092 }
2093 out
2094}
2095
2096fn generate_value_constants(
2110 output: &mut String,
2111 module: &Module,
2112) -> Result<(), Box<dyn std::error::Error>> {
2113 if module.values.is_empty() {
2114 return Ok(());
2115 }
2116
2117 writeln!(output, "/* Value constants */")?;
2118 writeln!(output)?;
2119
2120 let oid_registry = build_c_oid_registry(&module.values);
2121
2122 for va in &module.values {
2123 let c_name = to_screaming_snake_case(&va.name);
2124 match &va.value {
2125 Value::ObjectIdentifier(_) => {
2126 if let Some(arcs) = oid_registry.get(&va.name) {
2127 write!(output, "static const uint32_t {}[] = {{", c_name)?;
2128 for (i, n) in arcs.iter().enumerate() {
2129 if i > 0 {
2130 write!(output, ", ")?;
2131 }
2132 write!(output, "{}", n)?;
2133 }
2134 writeln!(output, "}};")?;
2135 writeln!(output, "#define {}_LEN {}", c_name, arcs.len())?;
2136 writeln!(output)?;
2137 } else {
2138 writeln!(
2139 output,
2140 "/* OID {} could not be fully resolved (unresolved named reference) */",
2141 va.name
2142 )?;
2143 writeln!(output)?;
2144 }
2145 }
2146 Value::Integer(n) => {
2147 writeln!(output, "#define {} ((int64_t){})", c_name, n)?;
2148 writeln!(output)?;
2149 }
2150 Value::Boolean(b) => {
2151 writeln!(
2152 output,
2153 "#define {} ({})",
2154 c_name,
2155 if *b { "true" } else { "false" }
2156 )?;
2157 writeln!(output)?;
2158 }
2159 Value::String(s) => {
2160 writeln!(output, "#define {} \"{}\"", c_name, escape_c_string(s))?;
2161 writeln!(output)?;
2162 }
2163 }
2164 }
2165
2166 Ok(())
2167}
2168
2169fn peel_type(ty: &Type) -> &Type {
2171 match ty {
2172 Type::Tagged { inner, .. }
2173 | Type::Constrained {
2174 base_type: inner, ..
2175 } => peel_type(inner),
2176 _ => ty,
2177 }
2178}
2179
2180fn is_pointer_c_type(ty: &Type) -> bool {
2182 matches!(
2183 peel_type(ty),
2184 Type::Integer(_, _)
2185 | Type::Enumerated(_)
2186 | Type::OctetString(_)
2187 | Type::ObjectIdentifier
2188 | Type::Utf8String(_)
2189 | Type::PrintableString(_)
2190 | Type::IA5String(_)
2191 | Type::UtcTime
2192 | Type::GeneralizedTime
2193 | Type::Any
2194 | Type::AnyDefinedBy(_)
2195 )
2196}
2197
2198pub(crate) fn get_c_type(ty: &Type) -> String {
2200 match ty {
2201 Type::Integer(_, _) => "SyntaInteger*".to_string(),
2202 Type::Enumerated(_) => "SyntaInteger*".to_string(),
2203 Type::Real => "double".to_string(),
2204 Type::Boolean => "bool".to_string(),
2205 Type::OctetString(_) => "SyntaOctetString*".to_string(),
2206 Type::BitString(_) => "SyntaBitString".to_string(),
2207 Type::ObjectIdentifier => "SyntaObjectIdentifier*".to_string(),
2208 Type::Null => "void".to_string(),
2209 Type::Utf8String(_)
2210 | Type::PrintableString(_)
2211 | Type::IA5String(_)
2212 | Type::TeletexString(_)
2213 | Type::UniversalString(_)
2214 | Type::BmpString(_)
2215 | Type::GeneralString(_)
2216 | Type::NumericString(_)
2217 | Type::VisibleString(_) => {
2218 "SyntaOctetString*".to_string() }
2220 Type::UtcTime | Type::GeneralizedTime => "SyntaOctetString*".to_string(), Type::Sequence(_) | Type::Set(_) => "struct /* complex type */".to_string(),
2222 Type::SequenceOf(inner, _) | Type::SetOf(inner, _) => {
2223 format!("{}*", get_c_type(inner))
2224 }
2225 Type::Choice(_) => "union /* choice */".to_string(),
2226 Type::TypeRef(name) => to_pascal_case(name),
2227 Type::Tagged { inner, .. } => get_c_type(inner),
2228 Type::Constrained { base_type, .. } => get_c_type(base_type),
2229 Type::Any => "SyntaOctetString*".to_string(), Type::AnyDefinedBy(_) => "SyntaOctetString*".to_string(), Type::Class(_) => "void /* class */".to_string(), }
2233}
2234
2235#[cfg(test)]
2236mod tests {
2237 use super::*;
2238
2239 #[test]
2240 fn test_generate_simple_sequence() {
2241 let module = Module {
2242 name: "TestModule".to_string(),
2243 oid: None,
2244 values: vec![],
2245 tagging_mode: None,
2246 imports: vec![],
2247 exports: vec![],
2248 definitions: vec![Definition {
2249 name: "SimpleSeq".to_string(),
2250 ty: Type::Sequence(vec![
2251 SequenceField {
2252 name: "version".to_string(),
2253 ty: Type::Integer(None, vec![]),
2254 optional: false,
2255 default: None,
2256 },
2257 SequenceField {
2258 name: "serialNumber".to_string(),
2259 ty: Type::Integer(None, vec![]),
2260 optional: false,
2261 default: None,
2262 },
2263 ]),
2264 }],
2265 };
2266
2267 let result = generate_c(&module).unwrap();
2268 assert!(result.contains("typedef struct SimpleSeq"));
2269 assert!(result.contains("SyntaInteger* version;"));
2270 assert!(result.contains("SyntaInteger* serial_number;"));
2271 assert!(result.contains("simple_seq_decode"));
2272 assert!(result.contains("simple_seq_encode"));
2273 }
2274
2275 #[test]
2276 fn test_generate_choice() {
2277 let module = Module {
2278 name: "TestModule".to_string(),
2279 oid: None,
2280 values: vec![],
2281 tagging_mode: None,
2282 imports: vec![],
2283 exports: vec![],
2284 definitions: vec![Definition {
2285 name: "MyChoice".to_string(),
2286 ty: Type::Choice(vec![
2287 ChoiceVariant {
2288 name: "intVal".to_string(),
2289 ty: Type::Integer(None, vec![]),
2290 },
2291 ChoiceVariant {
2292 name: "boolVal".to_string(),
2293 ty: Type::Boolean,
2294 },
2295 ]),
2296 }],
2297 };
2298
2299 let result = generate_c(&module).unwrap();
2300 assert!(result.contains("typedef enum MyChoiceTag"));
2301 assert!(result.contains("typedef struct MyChoice"));
2302 assert!(result.contains("MyChoiceTag tag;"));
2303 assert!(result.contains("union {"));
2304 }
2305
2306 #[test]
2307 fn test_generate_helpers() {
2308 let module = Module {
2309 name: "TestModule".to_string(),
2310 oid: None,
2311 values: vec![],
2312 tagging_mode: None,
2313 imports: vec![],
2314 exports: vec![],
2315 definitions: vec![
2316 Definition {
2317 name: "SimpleSeq".to_string(),
2318 ty: Type::Sequence(vec![
2319 SequenceField {
2320 name: "version".to_string(),
2321 ty: Type::Integer(None, vec![]),
2322 optional: false,
2323 default: None,
2324 },
2325 SequenceField {
2326 name: "flag".to_string(),
2327 ty: Type::Boolean,
2328 optional: true,
2329 default: None,
2330 },
2331 ]),
2332 },
2333 Definition {
2334 name: "MyChoice".to_string(),
2335 ty: Type::Choice(vec![
2336 ChoiceVariant {
2337 name: "intVal".to_string(),
2338 ty: Type::Integer(None, vec![]),
2339 },
2340 ChoiceVariant {
2341 name: "boolVal".to_string(),
2342 ty: Type::Boolean,
2343 },
2344 ]),
2345 },
2346 ],
2347 };
2348
2349 let config = CCodeGenConfig {
2350 generate_helpers: true,
2351 ..Default::default()
2352 };
2353 let result = generate_c_with_config(&module, config).unwrap();
2354
2355 assert!(result.contains("#include <stdio.h>"));
2357
2358 assert!(result.contains("simple_seq_init"));
2360 assert!(result.contains("simple_seq_validate"));
2362 assert!(result.contains("SyntaErrorCode_NullPointer"));
2363 assert!(result.contains("value->version == NULL"));
2365 assert!(!result.contains("value->flag == NULL"));
2367 assert!(result.contains("simple_seq_print"));
2369 assert!(result.contains("FILE* stream"));
2370
2371 assert!(result.contains("my_choice_validate"));
2373 assert!(result.contains("MyChoiceTag_IntVal"));
2374 assert!(result.contains("SyntaErrorCode_InvalidEncoding"));
2375 assert!(result.contains("my_choice_print"));
2377 }
2378
2379 #[test]
2380 fn test_generate_named_integer() {
2381 let module = Module {
2382 name: "TestModule".to_string(),
2383 oid: None,
2384 values: vec![],
2385 tagging_mode: None,
2386 imports: vec![],
2387 exports: vec![],
2388 definitions: vec![Definition {
2389 name: "Protocol".to_string(),
2390 ty: Type::Integer(
2391 None,
2392 vec![
2393 NamedNumber {
2394 name: "tcp".to_string(),
2395 value: 6,
2396 },
2397 NamedNumber {
2398 name: "udp".to_string(),
2399 value: 17,
2400 },
2401 ],
2402 ),
2403 }],
2404 };
2405
2406 let result = generate_c(&module).unwrap();
2407 assert!(result.contains("typedef int64_t Protocol;"));
2409 assert!(result.contains("#define PROTOCOL_TCP"));
2411 assert!(result.contains("((int64_t)6)"));
2412 assert!(result.contains("#define PROTOCOL_UDP"));
2413 assert!(result.contains("((int64_t)17)"));
2414 assert!(!result.contains("enum Protocol {"));
2416 }
2417
2418 fn make_constrained_integer_module(
2423 type_name: &str,
2424 constraint: SubtypeConstraint,
2425 named_numbers: Vec<NamedNumber>,
2426 ) -> Module {
2427 Module {
2428 name: "TestModule".to_string(),
2429 oid: None,
2430 values: vec![],
2431 tagging_mode: None,
2432 imports: vec![],
2433 exports: vec![],
2434 definitions: vec![Definition {
2435 name: type_name.to_string(),
2436 ty: Type::Constrained {
2437 base_type: Box::new(Type::Integer(None, named_numbers)),
2438 constraint: Constraint {
2439 spec: ConstraintSpec::Subtype(constraint),
2440 exception: None,
2441 },
2442 },
2443 }],
2444 }
2445 }
2446
2447 #[test]
2448 fn test_constrained_integer_value_range() {
2449 let module = make_constrained_integer_module(
2451 "Int32",
2452 SubtypeConstraint::ValueRange {
2453 min: ConstraintValue::Integer(-2147483648),
2454 max: ConstraintValue::Integer(2147483647),
2455 },
2456 vec![],
2457 );
2458 let result = generate_c(&module).unwrap();
2459
2460 assert!(
2462 result.contains("typedef struct { int32_t value; } Int32;"),
2463 "missing struct typedef:\n{}",
2464 result
2465 );
2466 assert!(result.contains("INTEGER (-2147483648..2147483647)"));
2468 assert!(result.contains("int32_new(int32_t v, Int32* out)"));
2470 assert!(result.contains("v >= -2147483648LL") && result.contains("v <= 2147483647LL"));
2471 assert!(result.contains("int32_new_unchecked(int32_t v)"));
2473 assert!(result.contains("int32_get(const Int32* self)"));
2475 assert!(result.contains("int32_validate(const Int32* self)"));
2477 assert!(result.contains("int32_decode(SyntaDecoder*"));
2479 assert!(result.contains("int32_encode(SyntaEncoder*"));
2480 assert!(!result.contains("int32_free"));
2482 }
2483
2484 #[test]
2485 fn test_constrained_integer_single_value() {
2486 let module = make_constrained_integer_module(
2488 "PvNo",
2489 SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2490 vec![],
2491 );
2492 let result = generate_c(&module).unwrap();
2493
2494 assert!(result.contains("typedef struct { uint8_t value; } PvNo;"));
2496 assert!(result.contains("INTEGER (5)"));
2497 assert!(result.contains("v == 5LL"));
2498 assert!(result.contains("pv_no_new(uint8_t v, PvNo* out)"));
2499 assert!(result.contains("pv_no_validate(const PvNo* self)"));
2500 }
2501
2502 #[test]
2503 fn test_constrained_integer_min_max_unconstrained() {
2504 let module = make_constrained_integer_module(
2506 "UncheckedInt",
2507 SubtypeConstraint::ValueRange {
2508 min: ConstraintValue::Min,
2509 max: ConstraintValue::Max,
2510 },
2511 vec![],
2512 );
2513 let result = generate_c(&module).unwrap();
2514
2515 assert!(result.contains("typedef struct { int64_t value; } UncheckedInt;"));
2516 assert!(result.contains("return 1;"));
2518 assert!(result.contains("if (!(1)) return false;"));
2521 }
2522
2523 #[test]
2524 fn test_constrained_integer_half_open_range() {
2525 let module = make_constrained_integer_module(
2527 "NonNegInt",
2528 SubtypeConstraint::ValueRange {
2529 min: ConstraintValue::Integer(0),
2530 max: ConstraintValue::Max,
2531 },
2532 vec![],
2533 );
2534 let result = generate_c(&module).unwrap();
2535
2536 assert!(result.contains("v >= 0LL"));
2538 assert!(!result.contains("v <= INT64_MAX"));
2539 }
2540
2541 #[test]
2542 fn test_constrained_integer_union() {
2543 let module = make_constrained_integer_module(
2545 "SmallOrLarge",
2546 SubtypeConstraint::Union(vec![
2547 SubtypeConstraint::ValueRange {
2548 min: ConstraintValue::Integer(0),
2549 max: ConstraintValue::Integer(10),
2550 },
2551 SubtypeConstraint::ValueRange {
2552 min: ConstraintValue::Integer(100),
2553 max: ConstraintValue::Integer(200),
2554 },
2555 ]),
2556 vec![],
2557 );
2558 let result = generate_c(&module).unwrap();
2559
2560 assert!(result.contains("typedef struct { int64_t value; } SmallOrLarge;"));
2561 assert!(result.contains("||"));
2563 assert!(result.contains("v >= 0LL") && result.contains("v <= 10LL"));
2564 assert!(result.contains("v >= 100LL") && result.contains("v <= 200LL"));
2565 }
2566
2567 #[test]
2568 fn test_constrained_integer_complement() {
2569 let module = make_constrained_integer_module(
2571 "NotZero",
2572 SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2573 ConstraintValue::Integer(0),
2574 ))),
2575 vec![],
2576 );
2577 let result = generate_c(&module).unwrap();
2578
2579 assert!(result.contains("typedef struct { int64_t value; } NotZero;"));
2580 assert!(result.contains("!(v == 0LL)"));
2581 }
2582
2583 #[test]
2584 fn test_constrained_integer_with_named_numbers() {
2585 let module = make_constrained_integer_module(
2587 "MsgType",
2588 SubtypeConstraint::ValueRange {
2589 min: ConstraintValue::Integer(0),
2590 max: ConstraintValue::Integer(30),
2591 },
2592 vec![
2593 NamedNumber {
2594 name: "asReq".to_string(),
2595 value: 10,
2596 },
2597 NamedNumber {
2598 name: "asRep".to_string(),
2599 value: 11,
2600 },
2601 ],
2602 );
2603 let result = generate_c(&module).unwrap();
2604
2605 assert!(result.contains("typedef struct { uint8_t value; } MsgType;"));
2607 assert!(result.contains("#define MsgType_AS_REQ ((uint8_t)10)"));
2609 assert!(result.contains("#define MsgType_AS_REP ((uint8_t)11)"));
2610 assert!(result.contains("msg_type_new(uint8_t v, MsgType* out)"));
2612 assert!(result.contains("msg_type_validate(const MsgType* self)"));
2613 }
2614
2615 #[test]
2616 fn test_format_c_constraint_display() {
2617 assert_eq!(
2618 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2619 min: ConstraintValue::Integer(-128),
2620 max: ConstraintValue::Integer(127),
2621 }),
2622 "-128..127"
2623 );
2624 assert_eq!(
2625 format_c_constraint_display(&SubtypeConstraint::SingleValue(ConstraintValue::Integer(
2626 42
2627 ))),
2628 "42"
2629 );
2630 assert_eq!(
2631 format_c_constraint_display(&SubtypeConstraint::ValueRange {
2632 min: ConstraintValue::Min,
2633 max: ConstraintValue::Max,
2634 }),
2635 "MIN..MAX"
2636 );
2637 }
2638
2639 #[test]
2640 fn test_generate_c_constraint_check() {
2641 assert_eq!(
2643 generate_c_constraint_check(
2644 "val",
2645 &SubtypeConstraint::ValueRange {
2646 min: ConstraintValue::Integer(0),
2647 max: ConstraintValue::Integer(100),
2648 },
2649 "int64_t",
2650 ),
2651 "(val >= 0LL && val <= 100LL)"
2652 );
2653 assert_eq!(
2655 generate_c_constraint_check(
2656 "val",
2657 &SubtypeConstraint::ValueRange {
2658 min: ConstraintValue::Integer(0),
2659 max: ConstraintValue::Integer(100),
2660 },
2661 "uint8_t",
2662 ),
2663 "(val <= 100LL)"
2664 );
2665 assert_eq!(
2667 generate_c_constraint_check(
2668 "val",
2669 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(5)),
2670 "int64_t",
2671 ),
2672 "val == 5LL"
2673 );
2674 assert_eq!(
2676 generate_c_constraint_check(
2677 "val",
2678 &SubtypeConstraint::ValueRange {
2679 min: ConstraintValue::Min,
2680 max: ConstraintValue::Max,
2681 },
2682 "int64_t",
2683 ),
2684 "1"
2685 );
2686 assert_eq!(
2688 generate_c_constraint_check(
2689 "val",
2690 &SubtypeConstraint::ValueRange {
2691 min: ConstraintValue::Integer(0),
2692 max: ConstraintValue::Max,
2693 },
2694 "int64_t",
2695 ),
2696 "(val >= 0LL)"
2697 );
2698 assert_eq!(
2700 generate_c_constraint_check(
2701 "val",
2702 &SubtypeConstraint::Complement(Box::new(SubtypeConstraint::SingleValue(
2703 ConstraintValue::Integer(0)
2704 ))),
2705 "int64_t",
2706 ),
2707 "!(val == 0LL)"
2708 );
2709 }
2710
2711 fn make_constrained_string_module(
2716 type_name: &str,
2717 base_ty: Type,
2718 constraint: SubtypeConstraint,
2719 ) -> Module {
2720 Module {
2721 name: "TestModule".to_string(),
2722 oid: None,
2723 values: vec![],
2724 tagging_mode: None,
2725 imports: vec![],
2726 exports: vec![],
2727 definitions: vec![Definition {
2728 name: type_name.to_string(),
2729 ty: Type::Constrained {
2730 base_type: Box::new(base_ty),
2731 constraint: Constraint {
2732 spec: ConstraintSpec::Subtype(constraint),
2733 exception: None,
2734 },
2735 },
2736 }],
2737 }
2738 }
2739
2740 #[test]
2741 fn test_constrained_string_size_only() {
2742 let module = make_constrained_string_module(
2744 "Realm",
2745 Type::IA5String(None),
2746 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2747 min: ConstraintValue::Integer(1),
2748 max: ConstraintValue::Max,
2749 })),
2750 );
2751 let result = generate_c(&module).unwrap();
2752
2753 assert!(
2755 result.contains("typedef struct { SyntaByteArray value; } Realm;"),
2756 "missing struct typedef:\n{}",
2757 result
2758 );
2759 assert!(result.contains("/* IA5String (SIZE (1..MAX)) */"));
2761 assert!(result.contains("realm_new(SyntaByteArray value, Realm* out)"));
2763 assert!(result.contains("uint32_t _len = value.len;"));
2764 assert!(result.contains("_len >= 1U"));
2765 assert!(result.contains("realm_new_unchecked(SyntaByteArray value)"));
2767 assert!(result.contains("realm_get(const Realm* self)"));
2769 assert!(result.contains("r.owned = 0"));
2770 assert!(result.contains("realm_validate(const Realm* self)"));
2772 assert!(result.contains("uint32_t _len = self->value.len;"));
2773 assert!(result.contains("realm_free(Realm* self)"));
2775 assert!(result.contains("synta_byte_array_free"));
2776 assert!(result.contains("realm_decode(SyntaDecoder*"));
2778 assert!(result.contains("realm_encode(SyntaEncoder*"));
2779 }
2780
2781 #[test]
2782 fn test_constrained_string_size_exact() {
2783 let module = make_constrained_string_module(
2785 "FixedTag",
2786 Type::OctetString(None),
2787 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::SingleValue(
2788 ConstraintValue::Integer(4),
2789 ))),
2790 );
2791 let result = generate_c(&module).unwrap();
2792
2793 assert!(result.contains("/* OCTET STRING (SIZE (4)) */"));
2794 assert!(result.contains("typedef struct { SyntaByteArray value; } FixedTag;"));
2795 assert!(result.contains("_len == 4U"));
2796 assert!(result.contains("fixed_tag_new(SyntaByteArray value, FixedTag* out)"));
2797 assert!(result.contains("fixed_tag_validate(const FixedTag* self)"));
2798 assert!(result.contains("fixed_tag_free(FixedTag* self)"));
2799 }
2800
2801 #[test]
2802 fn test_constrained_string_size_zero_min() {
2803 let module = make_constrained_string_module(
2805 "OptStr",
2806 Type::IA5String(None),
2807 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2808 min: ConstraintValue::Integer(0),
2809 max: ConstraintValue::Integer(255),
2810 })),
2811 );
2812 let result = generate_c(&module).unwrap();
2813
2814 assert!(result.contains("_len <= 255U"));
2816 assert!(!result.contains("_len >= 0U"));
2818 }
2819
2820 #[test]
2821 fn test_constrained_string_min_max_size() {
2822 let module = make_constrained_string_module(
2824 "AnyStr",
2825 Type::IA5String(None),
2826 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2827 min: ConstraintValue::Min,
2828 max: ConstraintValue::Max,
2829 })),
2830 );
2831 let result = generate_c(&module).unwrap();
2832
2833 assert!(result.contains("if (!(1)) return false;"));
2835 }
2836
2837 #[test]
2838 fn test_constrained_string_alphabet_only() {
2839 let module = make_constrained_string_module(
2841 "DigitStr",
2842 Type::IA5String(None),
2843 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: '0', max: '9' }]),
2844 );
2845 let result = generate_c(&module).unwrap();
2846
2847 assert!(result.contains("/* IA5String (FROM (\"0\"..\"9\")) */"));
2848 assert!(result.contains("const unsigned char *_ap ="));
2850 assert!(result.contains("unsigned char _c = _ap[_i]"));
2851 assert!(result.contains("_ok = (_c >= '0' && _c <= '9')"));
2852 assert!(result.contains("if (!_ok) return false;"));
2853 }
2854
2855 #[test]
2856 fn test_constrained_string_alphabet_single_char() {
2857 let module = make_constrained_string_module(
2859 "SingleChar",
2860 Type::IA5String(None),
2861 SubtypeConstraint::PermittedAlphabet(vec![CharRange { min: 'x', max: 'x' }]),
2862 );
2863 let result = generate_c(&module).unwrap();
2864 assert!(result.contains("_ok = _c == 'x'"));
2866 }
2867
2868 #[test]
2869 fn test_constrained_string_size_and_alphabet() {
2870 let module = make_constrained_string_module(
2872 "VisStr",
2873 Type::PrintableString(None),
2874 SubtypeConstraint::Intersection(vec![
2875 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2876 min: ConstraintValue::Integer(1),
2877 max: ConstraintValue::Integer(64),
2878 })),
2879 SubtypeConstraint::PermittedAlphabet(vec![
2880 CharRange { min: 'A', max: 'Z' },
2881 CharRange { min: 'a', max: 'z' },
2882 ]),
2883 ]),
2884 );
2885 let result = generate_c(&module).unwrap();
2886
2887 assert!(result.contains("/* PrintableString"));
2888 assert!(result.contains("_len >= 1U") && result.contains("_len <= 64U"));
2890 assert!(result.contains("(_c >= 'A' && _c <= 'Z') || (_c >= 'a' && _c <= 'z')"));
2892 assert!(result.contains("uint32_t _len = self->value.len;"));
2894 }
2895
2896 #[test]
2897 fn test_constrained_string_pattern_placeholder() {
2898 let module = make_constrained_string_module(
2900 "PatStr",
2901 Type::IA5String(None),
2902 SubtypeConstraint::Pattern("[0-9]+".to_string()),
2903 );
2904 let result = generate_c(&module).unwrap();
2905
2906 assert!(result.contains("PATTERN constraint \"[0-9]+\" not enforced at runtime"));
2907 assert!(result.contains("typedef struct { SyntaByteArray value; } PatStr;"));
2909 assert!(result.contains("pat_str_new(SyntaByteArray value, PatStr* out)"));
2910 }
2911
2912 #[test]
2913 fn test_constrained_utf8string() {
2914 let module = make_constrained_string_module(
2916 "Label",
2917 Type::Utf8String(None),
2918 SubtypeConstraint::SizeConstraint(Box::new(SubtypeConstraint::ValueRange {
2919 min: ConstraintValue::Integer(1),
2920 max: ConstraintValue::Integer(255),
2921 })),
2922 );
2923 let result = generate_c(&module).unwrap();
2924
2925 assert!(result.contains("/* UTF8String (SIZE (1..255)) */"));
2926 assert!(result.contains("typedef struct { SyntaByteArray value; } Label;"));
2927 assert!(result.contains("label_new(SyntaByteArray value, Label* out)"));
2928 assert!(result.contains("label_free(Label* self)"));
2929 }
2930
2931 #[test]
2932 fn test_generate_c_length_check() {
2933 assert_eq!(
2935 generate_c_length_check(
2936 "_len",
2937 &SubtypeConstraint::SingleValue(ConstraintValue::Integer(4))
2938 ),
2939 "_len == 4U"
2940 );
2941 assert_eq!(
2943 generate_c_length_check(
2944 "_len",
2945 &SubtypeConstraint::ValueRange {
2946 min: ConstraintValue::Integer(1),
2947 max: ConstraintValue::Max,
2948 }
2949 ),
2950 "(_len >= 1U)"
2951 );
2952 assert_eq!(
2954 generate_c_length_check(
2955 "_len",
2956 &SubtypeConstraint::ValueRange {
2957 min: ConstraintValue::Integer(0),
2958 max: ConstraintValue::Integer(256),
2959 }
2960 ),
2961 "(_len <= 256U)"
2962 );
2963 assert_eq!(
2965 generate_c_length_check(
2966 "_len",
2967 &SubtypeConstraint::ValueRange {
2968 min: ConstraintValue::Min,
2969 max: ConstraintValue::Max,
2970 }
2971 ),
2972 "1"
2973 );
2974 }
2975
2976 #[test]
2977 fn test_format_c_char_literal() {
2978 assert_eq!(format_c_char_literal('A'), "'A'");
2979 assert_eq!(format_c_char_literal('0'), "'0'");
2980 assert_eq!(format_c_char_literal('\''), "'\\''");
2981 assert_eq!(format_c_char_literal('\\'), "'\\\\'");
2982 assert_eq!(format_c_char_literal('\x01'), "'\\x01'");
2983 }
2984
2985 fn make_named_bit_module(name: &str, bits: Vec<NamedNumber>) -> Module {
2990 Module {
2991 name: "TestModule".to_string(),
2992 oid: None,
2993 values: vec![],
2994 tagging_mode: None,
2995 imports: vec![],
2996 exports: vec![],
2997 definitions: vec![Definition {
2998 name: name.to_string(),
2999 ty: Type::Constrained {
3000 base_type: Box::new(Type::BitString(None)),
3001 constraint: Constraint {
3002 spec: ConstraintSpec::Subtype(SubtypeConstraint::NamedBitList(bits)),
3003 exception: None,
3004 },
3005 },
3006 }],
3007 }
3008 }
3009
3010 #[test]
3011 fn test_named_bit_string_typedef_and_defines() {
3012 let module = make_named_bit_module(
3014 "TicketFlags",
3015 vec![
3016 NamedNumber {
3017 name: "reserved".to_string(),
3018 value: 0,
3019 },
3020 NamedNumber {
3021 name: "forwardable".to_string(),
3022 value: 1,
3023 },
3024 NamedNumber {
3025 name: "proxiable".to_string(),
3026 value: 3,
3027 },
3028 ],
3029 );
3030 let result = generate_c(&module).unwrap();
3031
3032 assert!(
3034 result.contains("typedef SyntaBitString TicketFlags;"),
3035 "typedef present"
3036 );
3037 assert!(
3039 result.contains("TICKET_FLAGS_RESERVED_BIT"),
3040 "reserved bit define"
3041 );
3042 assert!(
3043 result.contains("TICKET_FLAGS_FORWARDABLE_BIT"),
3044 "forwardable bit define"
3045 );
3046 assert!(
3047 result.contains("TICKET_FLAGS_PROXIABLE_BIT"),
3048 "proxiable bit define"
3049 );
3050 assert!(
3052 result.contains("TICKET_FLAGS_RESERVED_BIT") && result.contains(" 0"),
3053 "value 0 present"
3054 );
3055 assert!(result.contains(" 1"), "value 1 present");
3056 assert!(result.contains(" 3"), "value 3 present");
3057 assert!(!result.contains("IS_SET"), "no IS_SET without helpers");
3059 }
3060
3061 #[test]
3062 fn test_named_bit_string_hyphenated_name() {
3063 let module = make_named_bit_module(
3065 "kdc-options",
3066 vec![
3067 NamedNumber {
3068 name: "reserved".to_string(),
3069 value: 0,
3070 },
3071 NamedNumber {
3072 name: "forwardable".to_string(),
3073 value: 1,
3074 },
3075 ],
3076 );
3077 let result = generate_c(&module).unwrap();
3078 assert!(
3080 result.contains("typedef SyntaBitString KdcOptions;"),
3081 "typedef with PascalCase"
3082 );
3083 assert!(
3084 result.contains("KDC_OPTIONS_RESERVED_BIT"),
3085 "hyphenated prefix uses underscore"
3086 );
3087 assert!(
3088 result.contains("KDC_OPTIONS_FORWARDABLE_BIT"),
3089 "forwardable bit define"
3090 );
3091 }
3092
3093 #[test]
3094 fn test_named_bit_string_camel_case_bit_name() {
3095 let module = make_named_bit_module(
3097 "KeyUsage",
3098 vec![
3099 NamedNumber {
3100 name: "digitalSignature".to_string(),
3101 value: 0,
3102 },
3103 NamedNumber {
3104 name: "nonRepudiation".to_string(),
3105 value: 1,
3106 },
3107 ],
3108 );
3109 let result = generate_c(&module).unwrap();
3110 assert!(
3112 result.contains("KEY_USAGE_DIGITAL_SIGNATURE_BIT"),
3113 "camelCase bit → SCREAMING_SNAKE"
3114 );
3115 assert!(
3116 result.contains("KEY_USAGE_NON_REPUDIATION_BIT"),
3117 "nonRepudiation bit"
3118 );
3119 }
3120
3121 #[test]
3122 fn test_named_bit_string_with_helpers() {
3123 let module = make_named_bit_module(
3124 "TicketFlags",
3125 vec![NamedNumber {
3126 name: "forwardable".to_string(),
3127 value: 1,
3128 }],
3129 );
3130 let config = CCodeGenConfig {
3131 generate_helpers: true,
3132 ..Default::default()
3133 };
3134 let result = generate_c_with_config(&module, config).unwrap();
3135 assert!(
3137 result.contains("TICKET_FLAGS_IS_SET(bs, bit)"),
3138 "IS_SET helper"
3139 );
3140 assert!(result.contains("TICKET_FLAGS_SET(bs, bit)"), "SET helper");
3141 assert!(
3142 result.contains("TICKET_FLAGS_CLEAR(bs, bit)"),
3143 "CLEAR helper"
3144 );
3145 assert!(result.contains("synta_bitstring_is_set"), "is_set API call");
3147 assert!(result.contains("synta_bitstring_set"), "set API call");
3148 assert!(result.contains("synta_bitstring_clear"), "clear API call");
3149 }
3150
3151 #[test]
3152 fn test_named_bit_string_empty_list() {
3153 let module = make_named_bit_module("EmptyFlags", vec![]);
3155 let result = generate_c(&module).unwrap();
3156 assert!(
3157 result.contains("typedef SyntaBitString EmptyFlags;"),
3158 "typedef present"
3159 );
3160 assert!(
3161 !result.contains("EMPTY_FLAGS_"),
3162 "no defines for empty list"
3163 );
3164 }
3165
3166 fn make_named_bit_with_size_module(name: &str, bits: Vec<NamedNumber>) -> Module {
3176 Module {
3178 name: "TestModule".to_string(),
3179 oid: None,
3180 values: vec![],
3181 tagging_mode: None,
3182 imports: vec![],
3183 exports: vec![],
3184 definitions: vec![Definition {
3185 name: name.to_string(),
3186 ty: Type::Constrained {
3187 base_type: Box::new(Type::BitString(None)),
3188 constraint: Constraint {
3189 spec: ConstraintSpec::Subtype(SubtypeConstraint::Intersection(vec![
3190 SubtypeConstraint::NamedBitList(bits),
3191 SubtypeConstraint::SizeConstraint(Box::new(
3192 SubtypeConstraint::ValueRange {
3193 min: ConstraintValue::Integer(32),
3194 max: ConstraintValue::Max,
3195 },
3196 )),
3197 ])),
3198 exception: None,
3199 },
3200 },
3201 }],
3202 }
3203 }
3204
3205 #[test]
3206 fn test_named_bit_string_with_size_emits_typedef() {
3207 let module = make_named_bit_with_size_module(
3209 "TicketFlags",
3210 vec![
3211 NamedNumber {
3212 name: "forwardable".to_string(),
3213 value: 1,
3214 },
3215 NamedNumber {
3216 name: "proxiable".to_string(),
3217 value: 3,
3218 },
3219 ],
3220 );
3221 let result = generate_c(&module).unwrap();
3222 assert!(
3223 result.contains("typedef SyntaBitString TicketFlags;"),
3224 "combined form must still typedef SyntaBitString; got:\n{}",
3225 result
3226 );
3227 assert!(
3228 result.contains("#define TICKET_FLAGS_FORWARDABLE_BIT"),
3229 "FORWARDABLE_BIT define must appear; got:\n{}",
3230 result
3231 );
3232 assert!(
3233 result.contains("#define TICKET_FLAGS_PROXIABLE_BIT"),
3234 "PROXIABLE_BIT define must appear; got:\n{}",
3235 result
3236 );
3237 assert!(
3239 !result.contains("typedef struct { SyntaByteArray value; } TicketFlags;"),
3240 "combined form must not fall through to constrained-string struct; got:\n{}",
3241 result
3242 );
3243 }
3244
3245 #[test]
3246 fn test_named_bit_string_with_size_helpers() {
3247 let module = make_named_bit_with_size_module(
3249 "KdcOptions",
3250 vec![NamedNumber {
3251 name: "forwardable".to_string(),
3252 value: 1,
3253 }],
3254 );
3255 let config = CCodeGenConfig {
3256 generate_helpers: true,
3257 ..Default::default()
3258 };
3259 let result = generate_c_with_config(&module, config).unwrap();
3260 assert!(
3261 result.contains("KDC_OPTIONS_IS_SET(bs, bit)"),
3262 "IS_SET helper must appear; got:\n{}",
3263 result
3264 );
3265 }
3266
3267 fn make_default_module(fields: Vec<SequenceField>) -> Module {
3272 Module {
3273 name: "TestModule".to_string(),
3274 oid: None,
3275 values: vec![],
3276 tagging_mode: None,
3277 imports: vec![],
3278 exports: vec![],
3279 definitions: vec![Definition {
3280 name: "Config".to_string(),
3281 ty: Type::Sequence(fields),
3282 }],
3283 }
3284 }
3285
3286 #[test]
3287 fn test_sequence_default_comment_in_struct() {
3288 let module = make_default_module(vec![
3290 SequenceField {
3291 name: "port".to_string(),
3292 ty: Type::Integer(None, vec![]),
3293 optional: false,
3294 default: Some("8080".to_string()),
3295 },
3296 SequenceField {
3297 name: "enabled".to_string(),
3298 ty: Type::Boolean,
3299 optional: false,
3300 default: Some("TRUE".to_string()),
3301 },
3302 ]);
3303 let result = generate_c(&module).unwrap();
3304 assert!(
3305 result.contains("SyntaInteger* port; /* DEFAULT 8080 */"),
3306 "integer default comment"
3307 );
3308 assert!(
3309 result.contains("bool enabled; /* DEFAULT TRUE */"),
3310 "boolean default comment"
3311 );
3312 }
3313
3314 #[test]
3315 fn test_sequence_default_prototype_generated() {
3316 let module = make_default_module(vec![SequenceField {
3318 name: "port".to_string(),
3319 ty: Type::Integer(None, vec![]),
3320 optional: false,
3321 default: Some("8080".to_string()),
3322 }]);
3323 let result = generate_c(&module).unwrap();
3324 assert!(
3325 result.contains("Config config_default(void);"),
3326 "default prototype generated"
3327 );
3328 }
3329
3330 #[test]
3331 fn test_sequence_no_default_prototype_for_required_field() {
3332 let module = make_default_module(vec![SequenceField {
3334 name: "name".to_string(),
3335 ty: Type::Integer(None, vec![]),
3336 optional: false,
3337 default: None,
3338 }]);
3339 let result = generate_c(&module).unwrap();
3340 assert!(
3341 !result.contains("config_default(void)"),
3342 "no prototype for required-only sequence"
3343 );
3344 }
3345
3346 fn make_tagged_seq_module(
3351 field_name: &str,
3352 class: TagClass,
3353 number: u32,
3354 tagging: Tagging,
3355 inner: Type,
3356 ) -> Module {
3357 Module {
3358 name: "TestModule".to_string(),
3359 oid: None,
3360 values: vec![],
3361 tagging_mode: None,
3362 imports: vec![],
3363 exports: vec![],
3364 definitions: vec![Definition {
3365 name: "Msg".to_string(),
3366 ty: Type::Sequence(vec![SequenceField {
3367 name: field_name.to_string(),
3368 ty: Type::Tagged {
3369 tag: TagInfo {
3370 class,
3371 number,
3372 tagging,
3373 },
3374 inner: Box::new(inner),
3375 },
3376 optional: false,
3377 default: None,
3378 }]),
3379 }],
3380 }
3381 }
3382
3383 #[test]
3384 fn test_explicit_tag_annotation_in_struct() {
3385 let module = make_tagged_seq_module(
3387 "id",
3388 TagClass::ContextSpecific,
3389 0,
3390 Tagging::Explicit,
3391 Type::Integer(None, vec![]),
3392 );
3393 let result = generate_c(&module).unwrap();
3394 assert!(
3395 result.contains("SyntaInteger* id; /* [0] EXPLICIT */"),
3396 "explicit tag comment missing; got:\n{}",
3397 result
3398 );
3399 }
3400
3401 #[test]
3402 fn test_implicit_tag_annotation_in_struct() {
3403 let module = make_tagged_seq_module(
3405 "data",
3406 TagClass::ContextSpecific,
3407 1,
3408 Tagging::Implicit,
3409 Type::OctetString(None),
3410 );
3411 let result = generate_c(&module).unwrap();
3412 assert!(
3413 result.contains("SyntaOctetString* data; /* [1] IMPLICIT */"),
3414 "implicit tag comment missing; got:\n{}",
3415 result
3416 );
3417 }
3418
3419 #[test]
3420 fn test_application_tag_annotation_in_struct() {
3421 let module = make_tagged_seq_module(
3423 "val",
3424 TagClass::Application,
3425 2,
3426 Tagging::Implicit,
3427 Type::Integer(None, vec![]),
3428 );
3429 let result = generate_c(&module).unwrap();
3430 assert!(
3431 result.contains("SyntaInteger* val; /* [APPLICATION 2] IMPLICIT */"),
3432 "APPLICATION tag comment missing; got:\n{}",
3433 result
3434 );
3435 }
3436
3437 fn make_values_module(values: Vec<crate::ast::ValueAssignment>) -> Module {
3442 Module {
3443 name: "TestModule".to_string(),
3444 oid: None,
3445 values,
3446 tagging_mode: None,
3447 imports: vec![],
3448 exports: vec![],
3449 definitions: vec![],
3450 }
3451 }
3452
3453 #[test]
3454 fn test_oid_value_constant_emitted() {
3455 let module = make_values_module(vec![crate::ast::ValueAssignment {
3457 name: "id-ori".to_string(),
3458 ty: Type::ObjectIdentifier,
3459 value: Value::ObjectIdentifier(vec![
3460 OidComponent::Number(1),
3461 OidComponent::Number(2),
3462 OidComponent::Number(840),
3463 OidComponent::Number(113549),
3464 OidComponent::Number(1),
3465 OidComponent::Number(9),
3466 OidComponent::Number(16),
3467 OidComponent::Number(13),
3468 ]),
3469 }]);
3470 let result = generate_c(&module).unwrap();
3471 assert!(
3472 result.contains("static const uint32_t ID_ORI[] = {1, 2, 840, 113549, 1, 9, 16, 13};"),
3473 "OID array missing:\n{}",
3474 result
3475 );
3476 assert!(
3477 result.contains("#define ID_ORI_LEN 8"),
3478 "_LEN define missing:\n{}",
3479 result
3480 );
3481 }
3482
3483 #[test]
3484 fn test_oid_named_reference_resolved() {
3485 let module = make_values_module(vec![
3488 crate::ast::ValueAssignment {
3489 name: "id-ori".to_string(),
3490 ty: Type::ObjectIdentifier,
3491 value: Value::ObjectIdentifier(vec![
3492 OidComponent::Number(1),
3493 OidComponent::Number(2),
3494 OidComponent::Number(840),
3495 OidComponent::Number(113549),
3496 OidComponent::Number(1),
3497 OidComponent::Number(9),
3498 OidComponent::Number(16),
3499 OidComponent::Number(13),
3500 ]),
3501 },
3502 crate::ast::ValueAssignment {
3503 name: "id-ori-kem".to_string(),
3504 ty: Type::ObjectIdentifier,
3505 value: Value::ObjectIdentifier(vec![
3506 OidComponent::NamedRef("id-ori".to_string()),
3507 OidComponent::Number(3),
3508 ]),
3509 },
3510 ]);
3511 let result = generate_c(&module).unwrap();
3512 assert!(
3513 result.contains(
3514 "static const uint32_t ID_ORI_KEM[] = {1, 2, 840, 113549, 1, 9, 16, 13, 3};"
3515 ),
3516 "resolved child OID missing:\n{}",
3517 result
3518 );
3519 assert!(
3520 result.contains("#define ID_ORI_KEM_LEN 9"),
3521 "_LEN for child OID missing:\n{}",
3522 result
3523 );
3524 }
3525
3526 #[test]
3527 fn test_oid_unresolvable_named_ref_emits_comment() {
3528 let module = make_values_module(vec![crate::ast::ValueAssignment {
3530 name: "my-oid".to_string(),
3531 ty: Type::ObjectIdentifier,
3532 value: Value::ObjectIdentifier(vec![
3533 OidComponent::NamedRef("undefined-base".to_string()),
3534 OidComponent::Number(1),
3535 ]),
3536 }]);
3537 let result = generate_c(&module).unwrap();
3538 assert!(
3539 result.contains("could not be fully resolved"),
3540 "unresolvable OID should produce a comment:\n{}",
3541 result
3542 );
3543 assert!(
3545 !result.contains("static const uint32_t MY_OID[] ="),
3546 "broken array must not be emitted:\n{}",
3547 result
3548 );
3549 }
3550
3551 #[test]
3552 fn test_integer_value_constant() {
3553 let module = make_values_module(vec![crate::ast::ValueAssignment {
3554 name: "max-count".to_string(),
3555 ty: Type::Integer(None, vec![]),
3556 value: Value::Integer(256),
3557 }]);
3558 let result = generate_c(&module).unwrap();
3559 assert!(
3560 result.contains("#define MAX_COUNT ((int64_t)256)"),
3561 "integer constant missing:\n{}",
3562 result
3563 );
3564 }
3565
3566 #[test]
3567 fn test_boolean_value_constant() {
3568 let module = make_values_module(vec![
3569 crate::ast::ValueAssignment {
3570 name: "flag-true".to_string(),
3571 ty: Type::Boolean,
3572 value: Value::Boolean(true),
3573 },
3574 crate::ast::ValueAssignment {
3575 name: "flag-false".to_string(),
3576 ty: Type::Boolean,
3577 value: Value::Boolean(false),
3578 },
3579 ]);
3580 let result = generate_c(&module).unwrap();
3581 assert!(
3582 result.contains("#define FLAG_TRUE (true)"),
3583 "true constant missing:\n{}",
3584 result
3585 );
3586 assert!(
3587 result.contains("#define FLAG_FALSE (false)"),
3588 "false constant missing:\n{}",
3589 result
3590 );
3591 }
3592
3593 #[test]
3594 fn test_string_value_constant() {
3595 let module = make_values_module(vec![crate::ast::ValueAssignment {
3596 name: "default-realm".to_string(),
3597 ty: Type::Utf8String(None),
3598 value: Value::String("EXAMPLE.COM".to_string()),
3599 }]);
3600 let result = generate_c(&module).unwrap();
3601 assert!(
3602 result.contains("#define DEFAULT_REALM \"EXAMPLE.COM\""),
3603 "string constant missing:\n{}",
3604 result
3605 );
3606 }
3607
3608 #[test]
3609 fn test_string_escape_in_constant() {
3610 let module = make_values_module(vec![crate::ast::ValueAssignment {
3611 name: "path".to_string(),
3612 ty: Type::Utf8String(None),
3613 value: Value::String("C:\\foo\\bar".to_string()),
3614 }]);
3615 let result = generate_c(&module).unwrap();
3616 assert!(
3617 result.contains("#define PATH \"C:\\\\foo\\\\bar\""),
3618 "backslash not escaped:\n{}",
3619 result
3620 );
3621 }
3622
3623 #[test]
3624 fn test_value_constants_section_header() {
3625 let module = make_values_module(vec![crate::ast::ValueAssignment {
3627 name: "x".to_string(),
3628 ty: Type::Integer(None, vec![]),
3629 value: Value::Integer(1),
3630 }]);
3631 let result = generate_c(&module).unwrap();
3632 assert!(
3633 result.contains("/* Value constants */"),
3634 "section comment missing"
3635 );
3636 }
3637
3638 #[test]
3639 fn test_no_value_constants_section_when_empty() {
3640 let module = make_values_module(vec![]);
3642 let result = generate_c(&module).unwrap();
3643 assert!(
3644 !result.contains("/* Value constants */"),
3645 "spurious section comment"
3646 );
3647 }
3648
3649 fn make_import_module(imports: Vec<Import>) -> Module {
3654 Module {
3655 name: "TestModule".to_string(),
3656 oid: None,
3657 values: vec![],
3658 tagging_mode: None,
3659 imports,
3660 exports: vec![],
3661 definitions: vec![],
3662 }
3663 }
3664
3665 #[test]
3666 fn test_import_generates_include() {
3667 let module = make_import_module(vec![Import {
3669 symbols: vec!["AlgorithmIdentifier".to_string()],
3670 module_name: "AlgorithmInformation-2009".to_string(),
3671 }]);
3672 let result = generate_c(&module).unwrap();
3673 assert!(
3674 result.contains("#include \"algorithm_information_2009.h\""),
3675 "import include missing:\n{}",
3676 result
3677 );
3678 assert!(
3679 result.contains("/* Imported module headers */"),
3680 "import section comment missing:\n{}",
3681 result
3682 );
3683 }
3684
3685 #[test]
3686 fn test_multiple_imports_generate_includes() {
3687 let module = make_import_module(vec![
3688 Import {
3689 symbols: vec!["Name".to_string()],
3690 module_name: "PKIX1Explicit88".to_string(),
3691 },
3692 Import {
3693 symbols: vec!["AlgorithmIdentifier".to_string()],
3694 module_name: "AlgorithmInformation-2009".to_string(),
3695 },
3696 ]);
3697 let result = generate_c(&module).unwrap();
3698 assert!(
3699 result.contains("#include \"pkix1_explicit88.h\""),
3700 "first import missing:\n{}",
3701 result
3702 );
3703 assert!(
3704 result.contains("#include \"algorithm_information_2009.h\""),
3705 "second import missing:\n{}",
3706 result
3707 );
3708 }
3709
3710 #[test]
3711 fn test_no_imports_no_import_section() {
3712 let module = make_import_module(vec![]);
3713 let result = generate_c(&module).unwrap();
3714 assert!(
3715 !result.contains("/* Imported module headers */"),
3716 "spurious import section:\n{}",
3717 result
3718 );
3719 }
3720
3721 #[test]
3726 fn test_choice_inline_sequence_generates_named_struct() {
3727 let module = Module {
3731 name: "TestModule".to_string(),
3732 oid: None,
3733 values: vec![],
3734 tagging_mode: None,
3735 imports: vec![],
3736 exports: vec![],
3737 definitions: vec![Definition {
3738 name: "MyChoice".to_string(),
3739 ty: Type::Choice(vec![
3740 ChoiceVariant {
3741 name: "seqVal".to_string(),
3742 ty: Type::Sequence(vec![SequenceField {
3743 name: "x".to_string(),
3744 ty: Type::Integer(None, vec![]),
3745 optional: false,
3746 default: None,
3747 }]),
3748 },
3749 ChoiceVariant {
3750 name: "intVal".to_string(),
3751 ty: Type::Integer(None, vec![]),
3752 },
3753 ]),
3754 }],
3755 };
3756 let result = generate_c(&module).unwrap();
3757 assert!(
3759 result.contains("struct MyChoiceSeqVal {"),
3760 "expected named struct MyChoiceSeqVal for inline SEQUENCE variant:\n{}",
3761 result
3762 );
3763 assert!(
3765 result.contains("MyChoiceSeqVal seq_val;"),
3766 "union member should be 'MyChoiceSeqVal seq_val;':\n{}",
3767 result
3768 );
3769 assert!(
3771 !result.contains("void* seq_val"),
3772 "void* placeholder must not appear after expansion:\n{}",
3773 result
3774 );
3775 assert!(
3777 result.contains("SyntaInteger* int_val;"),
3778 "regular integer variant missing:\n{}",
3779 result
3780 );
3781 let fwd_inner = result
3783 .find("typedef struct MyChoiceSeqVal")
3784 .unwrap_or(usize::MAX);
3785 let fwd_outer = result
3786 .find("typedef struct MyChoice MyChoice;")
3787 .unwrap_or(usize::MAX);
3788 assert!(
3789 fwd_inner < fwd_outer,
3790 "MyChoiceSeqVal forward decl must precede MyChoice:\n{}",
3791 result
3792 );
3793 }
3794
3795 #[test]
3796 fn test_sequence_all_optional_gets_default_prototype() {
3797 let module = make_default_module(vec![
3800 SequenceField {
3801 name: "host".to_string(),
3802 ty: Type::OctetString(None),
3803 optional: true,
3804 default: None,
3805 },
3806 SequenceField {
3807 name: "port".to_string(),
3808 ty: Type::Integer(None, vec![]),
3809 optional: true,
3810 default: None,
3811 },
3812 ]);
3813 let result = generate_c(&module).unwrap();
3814 assert!(
3815 result.contains("Config config_default(void);"),
3816 "prototype for all-optional sequence"
3817 );
3818 }
3819}