# mx20022
Production-grade ISO 20022 toolkit for Rust. Strongly-typed message models
generated from official XSD schemas, with built-in validation for FedNow, SEPA,
and CBPR+ payment rails and bidirectional SWIFT MT ↔ MX translation — everything
you need to build, validate, and migrate financial messages in a single crate.
[](https://crates.io/crates/mx20022)
[](https://docs.rs/mx20022)
[](https://github.com/socrates8300/mx20022/actions/workflows/ci.yml)
[](#license)
[](https://releases.rs/docs/1.75.0/)
## Features
- **13 message schemas** — pacs.008, pacs.002, pacs.004, pacs.009, pacs.028,
pain.001, pain.002, pain.013, camt.053, camt.054, camt.056, camt.029,
head.001
- **Strongly typed** — every element, attribute, and enum variant is a named
Rust type with serde Serialize/Deserialize
- **Builder pattern** — all struct types expose a builder with required-field
validation
- **XML round-trip** — parse and serialize ISO 20022 XML via quick-xml + serde
- **MT parser** — SWIFT FIN message parser for MT103, MT202, MT940
- **Bidirectional translation** — MT103 ↔ pacs.008, MT202 ↔ pacs.009,
MT940 ↔ camt.053
- **Validation** — IBAN, BIC, LEI, currency, amount format, plus scheme-level
rules for FedNow, SEPA, and CBPR+
- **XSD constraint validation** — every generated newtype validates pattern,
length, and range constraints at construction (`TryFrom<String>`) and on
full message trees via the `Validatable` trait
- **Code generation** — XSD → intermediate representation → Rust source
## Installation
### Umbrella crate (recommended)
The easiest way to get started — re-exports model, parse, validate, and translate
with all message families enabled:
```bash
cargo add mx20022
```
### Individual crates
Pick only what you need for smaller compile times and dependency footprint:
```bash
cargo add mx20022-model # Generated types + common types
cargo add mx20022-parse # XML parsing and serialization
cargo add mx20022-validate # Schema, business rule, and scheme validation
cargo add mx20022-translate # MT ↔ MX bidirectional translation
cargo add mx20022-codegen # XSD → Rust code generator
```
### CLI tool
```bash
cargo install mx20022-cli
```
### Feature flags
Feature flags on `mx20022-model` control which message families are compiled.
The umbrella crate enables all of them.
| `pacs` | pacs.002, pacs.004, pacs.008, pacs.009, pacs.028 | yes |
| `pain` | pain.001, pain.002, pain.013 | no |
| `camt` | camt.029, camt.053, camt.054, camt.056 | no |
| `head` | head.001 | no |
| `all` | all of the above | no |
## Usage
### Parse an ISO 20022 message
```rust
use mx20022::model::generated::pacs::pacs_008_001_13::Document;
use mx20022::parse::{de, envelope};
let xml = std::fs::read_to_string("message.xml")?;
// Detect the message type from the XML namespace
let msg_id = envelope::detect_message_type(&xml)?;
println!("Message type: {}", msg_id.dotted()); // e.g. "pacs.008.001.13"
// Deserialize to a strongly-typed struct
let doc: Document = de::from_str(&xml)?;
let hdr = &doc.fi_to_fi_cstmr_cdt_trf.grp_hdr;
println!("Message ID: {}", hdr.msg_id.0);
println!("Transactions: {}", hdr.nb_of_txs.0);
```
### Validate against FedNow rules
```rust
use mx20022::validate::schemes::{fednow::FedNowValidator, SchemeValidator};
let validator = FedNowValidator::new(); // $500K default limit
let result = validator.validate(&xml, "pacs.008.001.13");
if result.is_valid() {
println!("Valid ({} warnings)", result.warning_count());
} else {
for err in &result.errors {
println!("[{}] {} — {}", err.rule_id, err.path, err.message);
}
}
```
### Validate XSD constraints on a message
```rust
use mx20022::model::common::validate::IsoMessage;
use mx20022::model::generated::pacs::pacs_008_001_13::Document;
use mx20022::parse::de;
let xml = std::fs::read_to_string("message.xml")?;
let doc: Document = de::from_str(&xml)?;
let violations = doc.validate_message();
if violations.is_empty() {
println!("All XSD constraints satisfied");
} else {
for v in &violations {
println!("[{:?}] {} — {}", v.kind, v.path, v.message);
}
}
```
### Translate MT103 → pacs.008
```rust
use mx20022::translate::mt;
use mx20022::translate::mt::fields::mt103::parse_mt103;
use mx20022::translate::mappings::mt103_to_pacs008::mt103_to_pacs008;
use mx20022::parse::ser;
let mt_text = std::fs::read_to_string("mt103.txt")?;
// Parse raw SWIFT FIN blocks → MT103 fields
let msg = mt::parse(&mt_text)?;
let mt103 = parse_mt103(&msg.block4)?;
// Translate to pacs.008 document
let result = mt103_to_pacs008(&mt103, "MSG-001", "2024-01-15T10:00:00")?;
// Serialize back to XML
let xml = ser::to_string(&result.message)?;
println!("{xml}");
```
## CLI
### Commands
```bash
# Inspect message structure
mx20022-cli inspect testdata/xml/pacs/pacs_008_001_13_minimal.xml
# Validate with optional scheme rules
mx20022-cli validate testdata/xml/pacs/pacs_008_001_13_minimal.xml
mx20022-cli validate message.xml --scheme fednow
# Translate between MT and MX
mx20022-cli translate testdata/mt/mt103.txt --to pacs008
mx20022-cli translate message.xml --to mt103
# Generate Rust types from an XSD
mx20022-cli codegen schemas/pacs/pacs.008.001.13.xsd -o output.rs
```
Supported `--to` targets: `pacs008`, `mt103`, `pacs009`, `mt202`, `camt053`, `mt940`.
Supported `--scheme` values: `fednow`, `sepa`, `cbpr`.
The CLI rejects input files larger than 10 MB to prevent out-of-memory conditions.
## Development
```bash
# Run all checks (check + test + clippy + fmt)
just
# Individual commands
just check # cargo check --workspace --all-features
just test # cargo test --workspace --all-features
just clippy # clippy with -D warnings
just fmt # auto-format
just fmt-check # format check only
just deny # cargo-deny license/advisory audit
just doc # build documentation
just bench # run criterion benchmarks
just coverage # HTML coverage report via llvm-cov
just msrv # verify MSRV 1.75.0 compatibility
# Full CI suite locally
just ci
```
### Project Layout
```
mx20022/
├── crates/
│ ├── mx20022/ # Umbrella re-export + examples
│ ├── mx20022-model/ # Generated types (src/generated/) + common types (src/common/)
│ ├── mx20022-parse/ # XML parsing and serialization
│ ├── mx20022-validate/ # Schema, business rule, and scheme validation
│ ├── mx20022-translate/ # MT ↔ MX translation + MT parser
│ ├── mx20022-codegen/ # XSD → Rust code generator
│ └── mx20022-cli/ # Command-line tool
├── schemas/ # ISO 20022 XSD source files
├── testdata/ # Shared test fixtures (XML, MT, translation pairs)
├── docs/ # Project documentation
├── justfile # Task runner
├── deny.toml # cargo-deny configuration
└── rust-toolchain.toml # Rust stable + components
```
**Key boundary:** Files under `crates/mx20022-model/src/generated/` are
machine-written by the codegen tool. Do not hand-edit them — modify the code
generator instead.
See [ARCHITECTURE.md](ARCHITECTURE.md) for a detailed design walkthrough of
each crate, the code generation pipeline, and key architectural decisions.
### Running Examples
```bash
cargo run -p mx20022 --example parse_pacs008
cargo run -p mx20022 --example validate_message
cargo run -p mx20022 --example translate_mt103
cargo run -p mx20022 --example roundtrip
```
## Supported Messages
| head | 001.001 | 04 | Business Application Header |
| pacs | 008.001 | 13 | FI to FI Customer Credit Transfer |
| pacs | 002.001 | 14 | Payment Status Report |
| pacs | 004.001 | 11 | Return of Funds |
| pacs | 009.001 | 10 | FI to FI Credit Transfer |
| pacs | 028.001 | 05 | FI Status Request |
| pain | 001.001 | 11 | Customer Credit Transfer Initiation |
| pain | 002.001 | 13 | Customer Payment Status Report |
| pain | 013.001 | 09 | Creditor Payment Activation Request |
| camt | 053.001 | 11 | Bank to Customer Statement |
| camt | 054.001 | 11 | Bank to Customer Debit/Credit Notification |
| camt | 056.001 | 11 | Payment Cancellation Request |
| camt | 029.001 | 12 | Resolution of Investigation |
## Contributing
1. Fork the repository
2. Create a feature branch (`git checkout -b feat/my-change`)
3. Ensure all checks pass: `just ci`
4. Open a pull request against `main`
All code must pass `cargo check`, `cargo test`, `cargo clippy -- -D warnings`,
and `cargo fmt --check` before merge. CI runs these on both stable and MSRV
1.75.0.
`unsafe` code is forbidden workspace-wide.
## License
Licensed under the Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE)
or <https://www.apache.org/licenses/LICENSE-2.0>).