# ftracker-identifiers

Validated, `no_std`-first identifier types for Rust. It is:

* **Correct**: parsing runs full validation up front, so an invalid identifier
  can never be represented. Once you hold one of these types, there is no
  partially validated state to guard against.

* **`no_std` first**: the crate builds on `core` and `alloc` only. The `std`
  feature is additive, not required.

* **Zero-cost**: every type is `Copy`, wraps a fixed-size byte array, and
  performs parsing, validation, and every accessor on the stack.

[![Build Status][actions-badge]][actions-url]
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![MSRV][msrv-badge]][msrv-url]
[![API Docs][docs-badge]][docs-url]

[actions-badge]: https://github.com/lnivva/ftracker-identifiers/workflows/CI/badge.svg

[actions-url]: https://github.com/lnivva/ftracker-identifiers/actions?query=workflow%3ACI+branch%3Amain

[crates-badge]: https://img.shields.io/crates/v/ftracker-identifiers.svg

[crates-url]: https://crates.io/crates/ftracker-identifiers

[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg

[mit-url]: https://github.com/lnivva/ftracker-identifiers/blob/main/LICENSE

[msrv-badge]: https://img.shields.io/badge/rustc-1.93.0+-blue.svg

[msrv-url]: #supported-rust-versions

[docs-badge]: https://img.shields.io/docsrs/ftracker-identifiers

[docs-url]: https://docs.rs/ftracker-identifiers/latest/ftracker_identifiers/

[API Docs](https://docs.rs/ftracker-identifiers) |
[Guide](https://lnivva.github.io/ftracker-identifiers) |
[Contributing](#contributing)

## Overview

This crate provides small, allocation-free value types that can only ever hold a
valid identifier. It currently ships four:

* **`Cnpj`**: Brazil's Cadastro Nacional da Pessoa Jurídica, the national
  registry identifier for legal entities. Supports the punctuated
  `AA.AAA.AAA/AAAA-DD` form, the compact 14-character form, the legacy numeric
  layout, and the 2026 alphanumeric layout, all validated with the Módulo 11
  checksum.
* **`Isin`**: the ISO 6166 International Securities Identification Number, a
  12-character securities identifier validated with the ISO 6166 Luhn check
  digit.
* **`Cfi`**: the ISO 10962 Classification of Financial Instruments code, a
  six-letter taxonomy code validated against an embedded copy of the standard's
  code table.
* **`CountryCode`**: the ISO 3166-1 alpha-2 country code, two letters validated
  against the officially assigned set.

## Example

Add the crate to your `Cargo.toml`:

```toml
[dependencies]
ftracker-identifiers = "0.0.1"
```

Then parse and inspect identifiers:

```rust
use ftracker_identifiers::{Cnpj, Isin, Cfi, CountryCode};

// CNPJ accepts punctuated or compact input and exposes structured accessors.
let cnpj = Cnpj::parse("00.000.000/0001-91").unwrap();
assert_eq!(cnpj.as_str(), "00000000000191");
assert_eq!(cnpj.root(), "00000000");
assert_eq!(cnpj.branch_code(), "0001");

// ISIN exposes its country code as a validated `CountryCode`.
let isin = Isin::parse("US0378331005").unwrap();
assert_eq!(isin.country(), Some(CountryCode::parse("US").unwrap()));
assert_eq!(isin.nsin(), "037833100");

// CFI decomposes into its ISO 10962 category and group letters.
let cfi = Cfi::parse("ESVUFR").unwrap();
assert_eq!(cfi.category(), 'E');
assert_eq!(cfi.group(), 'S');

// Country codes are case-folded to their canonical uppercase form.
let country = CountryCode::parse("br").unwrap();
assert_eq!(country.as_str(), "BR");
```

## Design

* **No invalid state is representable.** Every constructor runs the full
  validation rules and returns a typed error on failure. There is no unchecked
  public constructor.
* **`no_std` first.** The crate is `#![no_std]` by default and relies only on
  `core` and `alloc`. The `std` feature is additive.
* **Zero allocation and `Copy`.** Each type wraps a fixed-size byte array.
  Parsing, validation, and every accessor operate on the stack.
* **Consistent ordering and hashing.** Ordering and hashing operate over the raw
  ASCII bytes and match `str` ordering on the string accessor, so every type
  works as a map or set key.

## Feature flags

* `std` (default): enables the standard library and the `std` support of any
  enabled optional dependency.
* `serde`: (de)serializes each type as its canonical string. Deserialization
  re-runs full validation.
* `schemars`: implements `JsonSchema` for each type. Implies `serde`.
* `arbitrary`: implements `Arbitrary` for each type, generating valid values for
  fuzz targets.
* `proptest`: exposes reusable `proptest` strategies for generating valid
  values.

## Supported Rust Versions

The minimum supported Rust version is **1.93.0**. Raising the MSRV is treated as
a breaking change and only happens in a version bump.

## Contributing

Contributions are welcome. See the [contributing guide][guide] to get started,
and please note that this project follows a [Code of Conduct][coc].

[guide]: docs/src/contributing/adding-a-new-identifier.md

[coc]: CODE_OF_CONDUCT.md

## License

This project is licensed under the [MIT license][mit-url].

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in this crate by you shall be licensed as MIT, without any
additional terms or conditions.
