# synta-codegen
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
- [Overview](#overview)
- [Target Languages](#target-languages)
- [Features](#features)
- [Language & output targets](#language-output-targets)
- [Type system & constraints](#type-system-constraints)
- [Module system](#module-system)
- [C build integration](#c-build-integration)
- [Implementation quality](#implementation-quality)
- [Quick Start](#quick-start)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [Example](#example)
- [Multi-File Projects](#multi-file-projects)
- [Documentation](#documentation)
- [ASN.1 Reference](#asn1-reference)
- [Language-Specific Generation](#language-specific-generation)
- [Getting Started](#getting-started)
- [Reference](#reference)
- [Supported ASN.1 Constructs](#supported-asn1-constructs)
- [Type Definitions](#type-definitions)
- [X.680 Constraints](#x680-constraints)
- [Field Modifiers](#field-modifiers)
- [Module Features](#module-features)
- [synta-codegen vs. Manual Implementation](#synta-codegen-vs-manual-implementation)
- [Real-World Support](#real-world-support)
- [IMPLICIT-tagged CHOICE variants](#implicit-tagged-choice-variants)
- [Development](#development)
- [Contributing](#contributing)
- [Current Limitations](#current-limitations)
- [Optional Features](#optional-features)
- [Regex Validation for PATTERN Constraints](#regex-validation-for-pattern-constraints)
- [Runtime Validation for CONTAINING Constraints](#runtime-validation-for-containing-constraints)
- [See Also](#see-also)
- [License](#license)
ASN.1 schema parser and Rust/C code generator for the `synta` library with full X.680 constraint support.
## Overview
`synta-codegen` parses ASN.1 module definitions (.asn1 files) and generates type-safe, validated Rust code or C code using the `synta` library. It supports ITU-T X.680 constraints and generates validated newtypes with runtime checks, eliminating the need to manually write encoding/decoding implementations.
### Target Languages
- **Rust** (default): Generates type-safe Rust code with derive macros and validation
- **C**: Generates C header files with struct definitions and encoder/decoder functions using the libcsynta C API
## Features
### Language & output targets
- **Multi-Language Support** - Generate Rust or C code from the same ASN.1 schema
- **Owned or Zero-Copy String Types** - `StringTypeMode::Borrowed` emits `OctetStringRef<'a>`, `BitStringRef<'a>`, `Utf8StringRef<'a>`, etc. for zero-allocation parse-only workloads (Rust)
- **Idiomatic Code** - PascalCase types, snake_case fields, proper conventions
### Type system & constraints
- **X.680 Constraint Support** - Full support for value ranges, size constraints, permitted alphabet, and more
- **ANY Type Support** - Support for legacy ANY and ANY DEFINED BY types
- **Validated Newtypes** - Generates type-safe wrappers with runtime validation (Rust)
- **DEFAULT Values** - Automatic Default trait implementation (Rust)
- **Named Value Constants** - Generate constants for INTEGER named values
- **Subtype Definitions** - Support for type references with additional constraints
- **PATTERN Regex Validation** - Optional regex validation with `regex` feature flag (Rust only)
- **CONTAINING Validation** - Optional runtime validation of encoded types with `validate_containing` feature (Rust only)
### Module system
- **IMPORTS/EXPORTS** - Full module dependency support with topological ordering
- **Multi-File Projects** - Automatic cross-module use statement generation (Rust) and multi-file output via `--output-dir`
- **Circular Import Detection** - Detects and reports import cycles before code generation (`--check-imports`)
### C build integration
- **Build System Integration** - Generate `CMakeLists.txt` (`--cmake`) or `meson.build` (`--meson`) for C projects
- **C Helper Functions** - `_init`, `_validate`, and `_print` helpers for every C type (`--with-helpers`)
### Implementation quality
- **Zero Dependencies** - No external crates required for code generation
See [../docs/asn1-support.md](../docs/asn1-support.md) for the complete ASN.1 support matrix.
## Quick Start
### Installation
```bash
cargo install synta-codegen
```
Or add to your `Cargo.toml`:
```toml
[build-dependencies]
synta-codegen = "0.1"
```
### Basic Usage
**Command Line (Rust):**
```bash
synta-codegen schema.asn1 -o generated.rs
```
**Command Line (C):**
```bash
# Single-file: generate header, then implementation
synta-codegen schema.asn1 --c -o generated.h
synta-codegen schema.asn1 --impl generated.h -o generated.c
# With helper functions (_init, _validate, _print)
synta-codegen schema.asn1 --c --with-helpers -o generated.h
# Multi-file: headers and implementations together
synta-codegen a.asn1 b.asn1 --c --emit both --output-dir ./generated/
# Generate CMake or Meson build files (single-file)
synta-codegen schema.asn1 --cmake -o CMakeLists.txt
synta-codegen schema.asn1 --meson -o meson.build
```
**Library (owned types, default):**
```rust
use synta_codegen::{parse, generate};
let schema = r#"
MyModule DEFINITIONS ::= BEGIN
Person ::= SEQUENCE {
name UTF8String,
age INTEGER (0..150)
}
END
"#;
let module = parse(schema)?;
let code = generate(&module)?;
```
**Library (zero-copy borrowed types, parse-only workloads):**
```rust
use synta_codegen::{parse, generate_with_config, CodeGenConfig, StringTypeMode};
let module = parse(schema)?;
let config = CodeGenConfig {
string_type_mode: StringTypeMode::Borrowed,
..Default::default()
};
// Emits Utf8StringRef<'a> instead of Utf8String, struct gains <'a> lifetime
let code = generate_with_config(&module, config)?;
```
**C Code Generation:**
```rust
use synta_codegen::{parse, generate_c};
let schema = "...";
let module = parse(schema)?;
let c_code = generate_c(&module)?;
```
See [../docs/api-reference.md](../docs/api-reference.md) for the complete CLI and library API reference,
[../docs/rust-generation.md](../docs/rust-generation.md) for Rust code generation details, and
[../docs/c-generation.md](../docs/c-generation.md) for C code generation and the libcsynta API.
## Example
**Input ASN.1:**
```asn1
Types DEFINITIONS ::= BEGIN
Percentage ::= INTEGER (0..100)
Protocol ::= INTEGER {tcp(6), udp(17), sctp(132)}
User ::= SEQUENCE {
name IA5String (SIZE (1..64)),
age Percentage,
email IA5String OPTIONAL
}
END
```
**Generated Rust:**
```rust
/// INTEGER (0..100)
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Percentage(u8);
impl Percentage {
pub fn new(value: u8) -> Result<Self, &'static str> {
let val: i64 = value as i64;
if (0..=100).contains(&val) {
Ok(Percentage(value))
} else {
Err("must be in range 0..100")
}
}
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 }
}
pub type Protocol = Integer;
impl Protocol {
pub const TCP: i64 = 6;
pub const UDP: i64 = 17;
pub const SCTP: i64 = 132;
}
#[derive(Debug, Clone, PartialEq)]
pub struct User {
pub name: Name,
pub age: Percentage,
pub email: Option<IA5String>,
}
```
**Usage:**
```rust
let user = User {
name: Name::new(IA5String::from("Alice"))?,
age: Percentage::new(25)?,
email: None,
};
// Validation at compile time and runtime
let invalid = Percentage::new(150); // Err("must be in range 0..100")
```
## Multi-File Projects
synta-codegen supports automatic cross-module use statement generation and multi-file output:
```bash
# Rust: generate with crate-relative imports
synta-codegen base_types.asn1 --crate-imports -o src/base_types.rs
synta-codegen user_module.asn1 --crate-imports -o src/user_module.rs
# C: generate headers only into a directory (default)
synta-codegen a.asn1 b.asn1 c.asn1 --c --output-dir ./generated
# C: generate both headers and implementations
synta-codegen a.asn1 b.asn1 c.asn1 --c --emit both --output-dir ./generated
# C: generate build file + all C sources in one invocation
synta-codegen a.asn1 b.asn1 c.asn1 --cmake --output-dir ./generated
synta-codegen a.asn1 b.asn1 c.asn1 --meson --output-dir ./generated
# Validate imports without generating code
synta-codegen a.asn1 b.asn1 c.asn1 --check-imports
```
**Generated Rust use statements:**
```rust
use crate::base_types::{Percentage, ShortString};
```
See [../docs/rust-generation.md](../docs/rust-generation.md#9-buildrs-integration) for the complete multi-module build.rs guide.
## Documentation
### ASN.1 Reference
- **[ASN.1 Support](../docs/asn1-support.md)** - What ASN.1 syntax is supported and what is not; complete support matrix
### Language-Specific Generation
- **[Rust Generation](../docs/rust-generation.md)** - Type mappings, naming, constraint validation, build.rs integration
- **[C Generation](../docs/c-generation.md)** - Type mappings, libcsynta API reference, memory contract, arena mode
### Getting Started
- **[Tutorial](../docs/tutorial.md)** - Step-by-step guide from basics to real-world examples
- **[API Reference](../docs/api-reference.md)** - Complete CLI options, examples, and library API
### Reference
- **[Limitations](../docs/limitations.md)** - Unsupported constructs and workarounds
- **[Troubleshooting](../docs/troubleshooting.md)** - Common issues and solutions
## Supported ASN.1 Constructs
### Type Definitions
- SEQUENCE, SET, CHOICE
- SEQUENCE OF, SET OF
- All primitive types (INTEGER, BOOLEAN, OCTET STRING, etc.)
### X.680 Constraints
- Value ranges: `INTEGER (0..100)`
- Size constraints: `IA5String (SIZE (1..64))`
- Permitted alphabet: `IA5String (FROM ("0".."9"))`
- PATTERN constraints: `IA5String (PATTERN "[0-9]+")` (with optional `regex` feature)
- CONTAINING constraints: `OCTET STRING (CONTAINING Certificate)` (with optional `validate_containing` feature)
- Combined constraints: `IA5String (SIZE (4..6) FROM ("0".."9"))`
- Union, intersection, complement
- Inner type constraints
### Field Modifiers
- OPTIONAL fields
- DEFAULT values
- Tagged fields ([0] EXPLICIT, [1] IMPLICIT)
### Module Features
- IMPORTS and EXPORTS
- Type references
- Nested definitions
See [../docs/asn1-support.md](../docs/asn1-support.md) for the complete support matrix.
## synta-codegen vs. Manual Implementation
| Type definitions | Manual struct definitions | Auto-generated |
| Constraint validation | Manual checks in constructors | Auto-generated from schema |
| OPTIONAL fields | Manual Option handling | Auto-generated |
| DEFAULT values | Manual Default impl | Auto-generated |
| Type references | Manual imports | Auto-generated use statements |
| Maintenance | Update code when schema changes | Regenerate |
## Real-World Support
synta-codegen can parse and generate code for:
- X.509 certificates (PKIX schemas), including SET OF RDN fields with correct 0x31 tag
- X.509 Certificate Revocation Lists (CRL, RFC 5280)
- PKCS#10 Certificate Signing Requests (CSR, RFC 2986)
- OCSP request/response (RFC 6960)
- RFC 3279 algorithm parameter types (DSA/DH domain params, ECDSA signature values, EC domain params)
- RFC 5755 Attribute Certificates (AC v2: `AttributeCertificate`, `Holder`, `AttCertIssuer`, etc.)
- RFC 4211 Certificate Request Message Format (CRMF: `CertTemplate`, `ProofOfPossession`, `PKIArchiveOptions`, etc.)
- RFC 9810 Certificate Management Protocol v3 (CMP: `PKIMessage`, `PKIHeader`, `PKIBody`, etc.)
- PKCS#7 / CMS (RFC 5652), PKCS#8 (RFC 5958), PKCS#9 (RFC 2985), PKCS#12 (RFC 7292)
- Kerberos V5 (RFC 4120), SPNEGO (RFC 4178), FAST (RFC 6113)
- Merkle Tree Certificates (draft-ietf-tls-merkle-tree-certs)
- LDAP directory schemas
- SNMP management information bases
- 5G/LTE protocol specifications
- Custom ASN.1 schemas
### IMPLICIT-tagged CHOICE variants
When a CHOICE variant is annotated with `#[asn1(tag(N, implicit))]`, the codegen
selects the correct decode path based on whether the inner type has a lifetime
parameter:
- **Owned types** (no lifetime): uses `ImplicitTag<T>` as before
- **Borrowed types** (lifetime `'a`): reads the context tag + length and calls
`<T as DecodeImplicit>::decode_implicit(content_bytes, encoding)` directly,
avoiding the `for<'b> Decode<'b>` HRTB that borrowed types cannot satisfy
This is transparent to schema authors: write `[N] IMPLICIT SomeType` in the schema
and the codegen picks the right path automatically.
Note: IMPLICIT tagging of a CHOICE inner type is prohibited by ASN.1 (CHOICE has
no single tag to replace). If the inner type of a variant is itself a CHOICE, use
EXPLICIT tagging: `[N] EXPLICIT ChoiceType`. The codegen emits EXPLICIT when the
inner type is a CHOICE or a type alias for one.
## Development
Run tests:
```bash
cargo test -p synta-codegen
```
Build the CLI:
```bash
cargo build --release -p synta-codegen
```
## Contributing
Contributions are welcome! Please see the main [synta repository](https://codeberg.org/abbra/synta) for contribution guidelines.
## Current Limitations
- Table constraints and information objects not yet implemented
- Some X.680 advanced features not yet supported
See [../docs/limitations.md](../docs/limitations.md) for details and workarounds.
## Optional Features
### Regex Validation for PATTERN Constraints
To enable runtime regex validation for PATTERN constraints, add the `regex` feature when using the generated code:
```toml
[dependencies]
synta = { version = "0.1", features = ["regex"] }
regex = "1.10"
once_cell = "1.19"
```
**Example:**
```asn1
Email ::= IA5String (PATTERN "[a-z]+@[a-z]+\\.com")
```
**Generated code (with `regex` feature):**
```rust
// Static regex pattern compiled once
static PATTERN_0: Lazy<Regex> = Lazy::new(|| Regex::new(r"[a-z]+@[a-z]+\.com").unwrap());
pub fn new(value: IA5String) -> Result<Self, String> {
if !PATTERN_0.is_match(value.as_ref()) {
return Err(format!("value does not match pattern: [a-z]+@[a-z]+\\.com"));
}
Ok(Email(value))
}
```
**Without the `regex` feature**, the generated code includes a comment noting that pattern validation is disabled.
### Runtime Validation for CONTAINING Constraints
To enable runtime validation for CONTAINING constraints, add the `validate_containing` feature when using the generated code:
```toml
[dependencies]
synta = { version = "0.1", features = ["validate_containing"] }
```
**Example:**
```asn1
EncodedCertificate ::= OCTET STRING (CONTAINING Certificate)
```
**Generated code (with `validate_containing` feature):**
```rust,ignore
pub fn new(value: OctetString) -> Result<Self, String> {
// Validate that value contains a valid DER-encoded Certificate
use synta::der::Decoder;
let bytes = value.as_ref();
let mut decoder = Decoder::new(bytes, synta::Encoding::Der)?;
let _decoded: Certificate = decoder.decode().map_err(|e| {
format!("invalid Certificate in CONTAINING constraint: {}", e)
})?;
// Verify complete consumption
if !decoder.is_empty() {
return Err("trailing bytes after Certificate in CONTAINING constraint".into());
}
Ok(EncodedCertificate(value))
}
```
**Without the `validate_containing` feature**, the generated code includes a comment noting that CONTAINING validation is disabled. This allows for zero-overhead when validation is not needed.
## See Also
- [synta](../README.md) - The main ASN.1 encoding/decoding library
- [synta-derive](../synta-derive/README.md) - Derive macros for ASN.1 types
- [ITU-T X.680](https://www.itu.int/rec/T-REC-X.680) - ASN.1 specification
- [ITU-T X.690](https://www.itu.int/rec/T-REC-X.690) - BER/DER/CER encoding rules
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.