synta 0.1.8

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
# Constraints

Constraints are checked inside a `new()` constructor on the generated newtype.
The constructor returns `Result<Self, &'static str>`.

## Value range constraints (INTEGER)

```asn1
Port ::= INTEGER (1..65535)
```

Generated Rust:

```rust
pub struct Port(u16);

impl Port {
    pub fn new(value: u16) -> std::result::Result<Self, &'static str> {
        let val: i64 = value as i64;
        if (1..=65535).contains(&val) {
            Ok(Self(value))
        } else {
            Err("must be in range 1..65535")
        }
    }
    pub const fn new_unchecked(value: u16) -> Self { Self(value) }
    pub const fn get(&self) -> u16 { self.0 }
    pub fn into_inner(self) -> u16 { self.0 }
}

// TryFrom<Integer> for the decode path
impl core::convert::TryFrom<Integer> for Port {
    type Error = &'static str;
    fn try_from(value: Integer) -> std::result::Result<Self, Self::Error> {
        let n = value.as_i64().map_err(|_| "integer value out of i64 range")?;
        let v = u16::try_from(n).map_err(|_| "must be in range 1..65535")?;
        Self::new(v)
    }
}
```

MIN and MAX are accepted as open-ended bounds:

```asn1
LargeId ::= INTEGER (1..MAX)
Count   ::= INTEGER (MIN..100)
```

## Size constraints

```asn1
Label ::= UTF8String (SIZE (1..64))
```

Generated Rust:

```rust
pub struct Label(pub synta::Utf8String);

impl Label {
    pub fn new(value: synta::Utf8String) -> std::result::Result<Self, &'static str> {
        let len = value.as_str().len();
        if !(1..=64).contains(&len) {
            return Err("Label: size out of range 1..64");
        }
        Ok(Self(value))
    }
    pub fn new_unchecked(value: synta::Utf8String) -> Self { Self(value) }
    pub fn get(&self) -> &synta::Utf8String { &self.0 }
    pub fn as_str(&self) -> &str { self.0.as_str() }
    pub fn into_inner(self) -> synta::Utf8String { self.0 }
}
```

Note: `as_str()` is generated for text string types (`IA5String`,
`PrintableString`, `Utf8String`).  It is not generated for `OctetString` or
`BitString` constrained newtypes.

## Permitted alphabet (FROM)

```asn1
Upper ::= PrintableString (FROM ("A".."Z"))
```

The `new()` constructor iterates over each character and checks membership in
the permitted set.

## PATTERN constraints

When the `regex` feature is enabled, a static `Lazy<Regex>` is generated and
matched inside `new()`.  When the feature is absent, a TODO comment is emitted:

```rust
pub struct Foo(pub synta::PrintableString);

impl Foo {
    pub fn new(value: synta::PrintableString) -> std::result::Result<Self, &'static str> {
        // TODO: pattern constraint ^[A-Z]{2}$ not validated (enable feature "regex")
        Ok(Self(value))
    }
}
```

Enable pattern validation by adding the `regex` feature to your **consuming crate**
(not synta itself) and declaring the `regex` and `once_cell` dependencies:

```toml
[features]
regex = ["dep:regex", "dep:once_cell"]

[dependencies]
regex = { version = "1.10", optional = true }
once_cell = { version = "1.19", optional = true }
```

## CONTAINING constraints

When the `validate_containing` feature is enabled in your **consuming crate**,
the constructor decodes the inner type from the raw value using a scratch decoder.
Without the feature, a TODO comment is emitted.

```toml
[features]
validate_containing = []
```

## Named bits

```asn1
KeyUsage ::= BIT STRING {
    digitalSignature (0),
    keyEncipherment  (2)
}
```

Generated Rust:

```rust
pub struct KeyUsage(pub synta::BitString);

impl KeyUsage {
    pub const DIGITAL_SIGNATURE: u32 = 0;
    pub const KEY_ENCIPHERMENT: u32 = 2;
}
```

## UNION and INTERSECTION

- Union (`|`): `new()` returns `Ok` if any one condition passes.
- Intersection (`^`): `new()` returns `Ok` only if all conditions pass.
- Complement (`ALL EXCEPT`): `new()` returns `Ok` if the inner constraint does
  NOT pass.

```asn1
Combined    ::= INTEGER (1..10 | 20..30)
Both        ::= INTEGER (0..255 ^ 100..200)
NotReserved ::= INTEGER (ALL EXCEPT (0 | 255))
```

## Inner type constraints

For `SEQUENCE OF` or `SET OF` with element constraints, `new()` iterates over
elements and validates each one.

```asn1
PortList ::= SEQUENCE OF INTEGER (1..65535)
```

The generated `new()` on `PortList` checks that every element is within
`1..65535`.

## Using `new_unchecked`

For data from trusted sources (e.g., already validated in a database), use the
unchecked constructor to skip validation:

```rust
// Trusted source — bypass validation
let port = Port::new_unchecked(8080u16);

// Untrusted source — always validate
let port = Port::new(request_port)?;
```