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§
- find_
asn1_ dir - Locate the
asn1/schema directory containing the ASN.1 schemas. - 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