synta 0.2.6

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

Guidelines for using synta-codegen effectively in production environments.

## Schema design

### Use descriptive type names

**Good:**
```asn1
UserIdentifier ::= INTEGER (1..999999)
EmailAddress ::= IA5String (SIZE (5..255))
```

**Avoid:**
```asn1
UID ::= INTEGER    -- Too short, unclear
String1 ::= IA5String  -- Generic, unclear purpose
```

### Leverage constraints for validation

**Good:**
```asn1
Port ::= INTEGER (1..65535)
Username ::= IA5String (SIZE (3..32) FROM ("a".."z" | "A".."Z" | "0".."9" | "-" | "_"))
```

**Avoid:**
```asn1
Port ::= INTEGER     -- No validation
Username ::= IA5String  -- Allows any string
```

### Use named values for constants

```asn1
Protocol ::= INTEGER {
    tcp(6),
    udp(17),
    sctp(132)
}
```

Usage:
```rust
let proto = Protocol::TCP;  // Type-safe constant
```

### Design for extensibility

```asn1
Request ::= SEQUENCE {
    version     INTEGER,
    messageId   INTEGER,
    payload     CHOICE {
        v1  [0] RequestV1,
        v2  [1] RequestV2
        -- Future versions can be added without breaking compatibility
    }
}
```

### Avoid deep nesting

**Good:**
```asn1
Address ::= SEQUENCE {
    street  IA5String,
    city    IA5String,
    zip     IA5String
}

User ::= SEQUENCE {
    name    IA5String,
    address Address  -- Separate named type
}
```

**Avoid:** deeply nested anonymous inline SEQUENCEs — they generate
`/* nested sequence */` comments in codegen and are harder to reference.

## Code generation

### Use build scripts for automation

```rust
// build.rs
fn main() {
    println!("cargo:rerun-if-changed=schemas/");
    // use synta-codegen library API — see codegen/overview.md
}
```

### Version control generated code

Committing generated code to version control makes changes reviewable in PRs
and eliminates a generation step in CI/CD.

Add to `.gitignore` exceptions:

```gitignore
# Don't ignore generated code
!src/generated/*.rs
```

### Document generation commands

**Makefile:**
```makefile
.PHONY: generate
generate:
	synta-codegen schemas/base.asn1 --crate-imports -o src/base.rs
	synta-codegen schemas/user.asn1 --crate-imports -o src/user.rs
	cargo fmt
```

## Project organization

### Recommended directory structure

```
my-protocol/
├── Cargo.toml
├── build.rs               # Auto-generation (optional)
├── schemas/               # ASN.1 source files
│   ├── base-types.asn1
│   ├── messages.asn1
│   └── extensions.asn1
├── src/
│   ├── lib.rs
│   ├── base_types.rs      # Generated
│   ├── messages.rs        # Generated
│   └── utils.rs           # Hand-written utilities
└── tests/
    └── integration.rs
```

### Separate generated and manual code

```
src/
├── generated/    # All generated code
│   ├── mod.rs
│   ├── user.rs
│   └── auth.rs
└── manual/       # Hand-written code
    ├── mod.rs
    ├── client.rs
    └── server.rs
```

### Use feature flags appropriately

```toml
[features]
default = ["std"]
std = []
# PATTERN constraint validation: add regex and once_cell to your crate,
# not to synta (synta does not have a "regex" feature)
regex-validation = ["dep:regex", "dep:once_cell"]

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

## Performance

### Use `new_unchecked` for trusted data

```rust
// Trusted source (already validated in DB)
let user_id = UserId::new_unchecked(db_value);

// Untrusted source (network input)
let user_id = UserId::new(untrusted_value)?;
```

### Cache validated instances

```rust
use std::collections::HashMap;

let mut port_cache: HashMap<u16, Port> = HashMap::new();
let port = port_cache.entry(raw_value)
    .or_insert_with(|| Port::new(raw_value).expect("invalid port"));
```

### Minimize constraint complexity

**Good:**
```asn1
ValidPort ::= INTEGER (1024..65535)
```

**Avoid (slower to validate):**
```asn1
ManyPorts ::= INTEGER (8001|8002|8003|...|8100)
```

### Reuse encode buffers for hot paths

```rust
let mut buffer = Vec::with_capacity(1024);
for item in &items {
    buffer.clear();
    let mut encoder = Encoder::new(Encoding::Der);
    encoder.encode(item)?;
    buffer.extend_from_slice(&encoder.finish()?);
    send(&buffer);
}
```

## Testing

### Test roundtrip encoding

```rust
use synta::{Decoder, Encoder, Encoding};

#[test]
fn test_user_roundtrip() {
    let user = User {
        id: Integer::from(42),
        username: IA5String::new("alice".to_string()).unwrap(),
        active: Boolean::new(true),
    };
    let mut encoder = Encoder::new(Encoding::Der);
    encoder.encode(&user).unwrap();
    let encoded = encoder.finish().unwrap();
    let mut decoder = Decoder::new(&encoded, Encoding::Der);
    let decoded: User = decoder.decode().unwrap();
    assert_eq!(user, decoded);
}
```

### Test constraint validation

```rust
#[test]
fn test_constraint_validation() {
    assert!(UserId::new(Integer::from(100)).is_ok());
    assert!(UserId::new(Integer::from(0)).is_err());      // too small
    assert!(UserId::new(Integer::from(1000000)).is_err()); // too large
}
```

### Test with real-world data

```rust
use synta::{Decoder, Encoding};
use synta_certificate::Certificate;

#[test]
fn test_parse_real_certificate() {
    let cert_bytes = include_bytes!("../test-data/real-cert.der");
    let mut decoder = Decoder::new(cert_bytes, Encoding::Der);
    let cert: Certificate = decoder.decode()
        .expect("Failed to parse real certificate");
    // cert.tbs_certificate.version is Option<Integer>
    assert_eq!(cert.tbs_certificate.version.as_ref().map(|v| v.as_i64().unwrap()), Some(2));
}
```

### Fuzz testing

```rust
#[test]
fn fuzz_decode_doesnt_panic() {
    use synta::{Decoder, Encoding};
    for _ in 0..1000 {
        let random_data: Vec<u8> = (0..100).map(|_| rand::random()).collect();
        let _ = Decoder::new(&random_data, Encoding::Der).decode::<User>();
        // May error, but must not panic
    }
}
```

## Maintenance

### Use semantic versioning for schema changes

**Breaking changes:**
- Removing fields from SEQUENCE
- Changing field types
- Removing variants from CHOICE
- Tightening constraints

**Non-breaking changes:**
- Adding OPTIONAL fields
- Adding CHOICE variants
- Adding DEFAULT values
- Loosening constraints

### Regenerate after schema updates

```bash
make generate
cargo test
git diff src/generated/   # Review changes
git commit -am "Regenerate after schema update"
```

## Security

### Validate all external input

```rust,ignore
use synta::{Decoder, Encoding};

fn handle_network_message(bytes: &[u8]) -> Result<User> {
    let user: User = Decoder::new(bytes, Encoding::Der).decode()?;
    // Additional business logic validation
    if user.id.value() == 0 {
        return Err("User ID cannot be zero".into());
    }
    Ok(user)
}
```

### Use constrained types for security-sensitive fields

```asn1
Password ::= OCTET STRING (SIZE (8..128))  -- Enforce min/max length
Age ::= INTEGER (0..150)                    -- No negative or absurd values
```

### Limit resource usage

Use `DecoderConfig` when parsing untrusted data to prevent resource exhaustion:

```rust
use synta::{Decoder, DecoderConfig, Encoding};

let bytes: &[u8] = &[];
let config = DecoderConfig {
    max_depth: 16,
    max_sequence_elements: 1_000,
    max_length: 1 * 1024 * 1024, // 1 MiB
};
let mut decoder = Decoder::new_with_config(bytes, Encoding::Der, &config);
```

### Sanitize error messages

```rust
fn safe_error(e: synta::Error) -> String {
    match e {
        synta::Error::InvalidEncoding { .. } => "Invalid data format".to_string(),
        _ => "Internal error".to_string(),
    }
}
```

## Summary checklist

**Schema design:**
- [ ] Descriptive type names
- [ ] Constraints for validation
- [ ] Named values for constants
- [ ] Extensibility (CHOICE variants, OPTIONAL fields)
- [ ] No deep nesting

**Code generation:**
- [ ] Build scripts for automation
- [ ] Generated code in version control
- [ ] Documented generation commands

**Performance:**
- [ ] `new_unchecked` for trusted data
- [ ] Cached validated instances
- [ ] Reused encode buffers

**Testing:**
- [ ] Roundtrip encode/decode test
- [ ] Constraint validation tests
- [ ] Real-world data tests

**Security:**
- [ ] All external input validated
- [ ] Constrained types for security fields
- [ ] `DecoderConfig` limits for untrusted input
- [ ] Error messages sanitized