Skip to main content

Crate synta_codegen

Crate synta_codegen 

Source
Expand description

ASN.1 schema parser and Rust code generator

This library parses ASN.1 module definitions and generates Rust code using the synta library’s derive macros.

§Quick start

use synta_codegen::{parse, generate};

let schema = r#"
    Certificate DEFINITIONS ::= BEGIN
        Certificate ::= SEQUENCE {
            version INTEGER,
            serialNumber INTEGER
        }
    END
"#;

let module = parse(schema).unwrap();
let rust_code = generate(&module).unwrap();
println!("{}", rust_code);

§Configuration

generate_with_config accepts a CodeGenConfig that controls several aspects of the emitted code.

§Owned vs. borrowed string types

By default all ASN.1 string and binary types (OCTET STRING, BIT STRING, UTF8String, PrintableString, IA5String) are generated as owned heap-allocating types (OctetString, BitString, …). This is convenient when constructing structs programmatically.

For parse-only workloads (e.g. X.509 certificate inspection) you can switch to zero-copy borrowed types (OctetStringRef<'a>, BitStringRef<'a>, …) that borrow directly from the input buffer. Structs that contain these fields automatically gain a 'a lifetime parameter.

use synta_codegen::{parse, generate_with_config, CodeGenConfig, StringTypeMode};

let schema = r#"
    Msg DEFINITIONS ::= BEGIN
        Msg ::= SEQUENCE {
            payload OCTET STRING,
            label   UTF8String
        }
    END
"#;

let module = parse(schema).unwrap();
let config = CodeGenConfig {
    string_type_mode: StringTypeMode::Borrowed,
    ..Default::default()
};
// Emits:
//   pub struct Msg<'a> {
//       pub payload: OctetStringRef<'a>,
//       pub label: Utf8StringRef<'a>,
//   }
let rust_code = generate_with_config(&module, config).unwrap();
println!("{}", rust_code);

String types that have no zero-copy variant (TeletexString, BmpString, UniversalString, GeneralString, NumericString, VisibleString) are always emitted as owned types regardless of StringTypeMode.

Named bit strings (BIT STRING { flag(0), … }) are always emitted as owned BitString because they are decoded into a concrete bit-field type.

§Derive macro gating

By default every Asn1Sequence / Asn1Set / Asn1Choice derive and its associated asn1(…) helper attributes are wrapped in #[cfg_attr(feature = "derive", …)]. This lets the consuming crate make synta-derive an optional dependency controlled by a Cargo feature:

# Cargo.toml of the consuming crate (default behaviour)
[dependencies]
synta-derive = { version = "0.1", optional = true }

[features]
derive = ["dep:synta-derive"]

Third-party crates that always depend on synta-derive and do not want to expose a derive Cargo feature can use DeriveMode::Always:

use synta_codegen::{parse, generate_with_config, CodeGenConfig, DeriveMode};

let schema = r#"
    Msg DEFINITIONS ::= BEGIN
        Msg ::= SEQUENCE { id INTEGER }
    END
"#;

let module = parse(schema).unwrap();
let config = CodeGenConfig {
    derive_mode: DeriveMode::Always,
    ..Default::default()
};
// Emits:
//   #[derive(Debug, Clone, PartialEq)]
//   #[derive(Asn1Sequence)]          ← no cfg_attr wrapper
//   pub struct Msg { pub id: Integer }
let rust_code = generate_with_config(&module, config).unwrap();
println!("{}", rust_code);

If the crate uses a feature name other than "derive", pass it via DeriveMode::Custom:

use synta_codegen::{parse, generate_with_config, CodeGenConfig, DeriveMode};

let config = CodeGenConfig {
    derive_mode: DeriveMode::Custom("asn1-derive".to_string()),
    ..Default::default()
};
// Emits:
//   #[cfg_attr(feature = "asn1-derive", derive(Asn1Sequence))]
let rust_code = generate_with_config(&module, config).unwrap();
println!("{}", rust_code);

§Import path prefix

Use CodeGenConfig::with_crate_imports, CodeGenConfig::with_super_imports, or CodeGenConfig::with_custom_prefix to emit use statements instead of the default comment-only import annotations.

§Constrained INTEGER type selection

When a top-level INTEGER type carries a value-range constraint (e.g. INTEGER (0..100)), synta-codegen generates a newtype whose inner field is the smallest native Rust integer primitive that covers the declared range, rather than the arbitrary-precision Integer type:

  • Lower bound ≥ 0 → unsigned: u8 (≤255), u16 (≤65535), u32 (≤4294967295), u64.
  • Lower bound < 0 → signed: i8, i16, i32, i64.
  • Unconstrained bounds (MIN/MAX, named values) → i64.

Using a primitive type means the generated struct automatically derives Copy, PartialOrd, and Ord, and avoids heap allocation. For example, Percentage ::= INTEGER (0..100) produces:

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Percentage(u8);

impl Percentage {
    pub fn new(value: u8) -> Result<Self, &'static str> { ... }
    pub const fn new_unchecked(value: u8) -> Self { Percentage(value) }
    pub const fn get(&self) -> u8 { self.0 }
    pub fn into_inner(self) -> u8 { self.0 }
}

The equivalent C generation uses uint8_t / uint16_t / uint32_t / uint64_t for non-negative ranges and int8_t / int16_t / int32_t / int64_t for signed ranges.

Re-exports§

pub use ast::Definition;
pub use ast::Module;
pub use ast::Type;
pub use c_cmake_codegen::generate_cmake;
pub use c_cmake_codegen::CMakeConfig;
pub use c_codegen::generate_c;
pub use c_codegen::generate_c_with_config;
pub use c_codegen::CCodeGenConfig;
pub use c_impl_codegen::generate_c_impl;
pub use c_impl_codegen::CImplConfig;
pub use c_impl_codegen::PatternMode;
pub use c_meson_codegen::generate_meson;
pub use c_meson_codegen::MesonConfig;
pub use codegen::generate;
pub use codegen::generate_with_config;
pub use codegen::CodeGenConfig;
pub use codegen::DeriveMode;
pub use codegen::StringTypeMode;
pub use import_graph::detect_cycles;
pub use import_graph::topological_order;
pub use import_graph::ImportCycle;
pub use naming::module_file_stem;
pub use parser::parse;
pub use parser::ParseError;

Modules§

ast
Abstract Syntax Tree for ASN.1 schemas
c_cmake_codegen
CMake build-system file generator for synta-generated C code.
c_codegen
C code generator using libcsynta API
c_impl_codegen
C implementation code generator for encoder/decoder functions
c_meson_codegen
Meson build-system file generator for synta-generated C code.
codegen
Rust code generator from ASN.1 AST
import_graph
Import dependency graph and circular-import detection.
naming
Identifier naming helpers shared across code generators.
parser
ASN.1 schema parser

Functions§

parse_and_generate
Parse ASN.1 schema and generate Rust code in one step
parse_and_generate_c
Parse ASN.1 schema and generate C header in one step
parse_and_generate_c_impl
Parse ASN.1 schema and generate C implementation in one step