Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Adding a New Identifier

This crate is organized so that every identifier type — CNPJ today, ISIN and CFI planned — follows the same shape, both in src/ and in this book. Sticking to the pattern keeps the crate predictable to use and cheap to review.

Source layout

Each identifier gets its own module directory, named after the identifier in lowercase (mirror src/cnpj/):

src/
  <identifier>.rs             # public type, module docs, inherent methods
  <identifier>/
    error.rs                  # the <Identifier>Error enum + Display
    fmt.rs                    # Display/Debug + any zero-allocation formatted-string helper
    parser.rs                 # string -> normalized candidate (formatting/case only)
    validation.rs              # normalized candidate -> Result<(), Error> (structural + checksum rules)
    serde.rs                   # #[cfg(feature = "serde")]
    schema.rs                  # #[cfg(feature = "schemars")]
    arbitrary.rs                # #[cfg(feature = "arbitrary")]
    proptest.rs                 # #[cfg(any(test, feature = "proptest"))], pub
    tests.rs                    # #[cfg(test)]

The parser vs. validation split matters: parser only knows about formatting (stripping punctuation, folding case) and has no opinion on which characters are valid where; validation only knows about rules (character class, checksum, degenerate input) and has no opinion on how the original string was formatted. This keeps each half testable independently and makes it obvious where a new rule belongs.

Feature flags

Follow the same Cargo.toml convention as CNPJ — every integration is optional and additive:

[features]
serde = ["dep:serde"]
schemars = ["dep:schemars", "serde"]
arbitrary = ["dep:arbitrary"]
proptest = ["dep:proptest"]

A new identifier should gate its serde/schemars/arbitrary/proptest modules behind the same feature names as CNPJ (not per-identifier features like cnpj-serde), so enabling serde once turns it on for every identifier type in the crate.

lib.rs

Re-export the new public type (and its error type, and any formatted-string helper type) at the crate root, the same way CNPJ does:

mod isin;
pub use isin::{Isin, IsinError, FormattedIsin};

Forgetting this step means the error type technically exists but can never be named by downstream code — the same gap this documentation pass caught and fixed for Cnpj.

Documentation checklist

For the module itself (src/<identifier>.rs):

  • Module-level doc comment covering: what the identifier represents, its segment structure (a table works well), format history/variants if any, the full list of validation rules mapped to error variants, relevant design notes ( allocation, Copy, ordering/hashing semantics), feature flags, and at least one runnable example.
  • Every public method has a doc comment with a runnable # Examples section.
  • Every public trait impl (FromStr, TryFrom, AsRef, …) has a one-line doc comment on the implementing method.
  • # Errors sections on every fallible public function.

For this book (docs/src/identifiers/<identifier>/):

  • README.md — landing page: what the identifier is, a minimal example, a table of contents for the rest of the chapter.
  • format.md — structure and any format history.
  • parsing-and-validation.md — constructors and validation rules.
  • formatting-and-display.md — rendering, Display/Debug.
  • error-handling.md — error variant table and a matching example.
  • feature-flags.md — one section per optional integration.
  • examples.md — end-to-end usage beyond the quick start.

Code blocks in this book use ```rust,ignore rather than ```rust, since mdbook test isn’t currently wired up to link against the compiled crate. The examples themselves should still mirror real, compiler-verified doctests from the source — copy them from src/<identifier>.rs rather than writing new, untested snippets from scratch.

Wiring it into SUMMARY.md

Replace the draft placeholder for your identifier with a real link, and add the same sub-chapters CNPJ has:

- [ISIN](./identifiers/isin/README.md)
    - [Structure & Formats](./identifiers/isin/format.md)
    - [Parsing & Validation](./identifiers/isin/parsing-and-validation.md)
    - [Formatting & Display](./identifiers/isin/formatting-and-display.md)
    - [Error Handling](./identifiers/isin/error-handling.md)
    - [Feature Flags](./identifiers/isin/feature-flags.md)
    - [Examples](./identifiers/isin/examples.md)

A draft entry (- [ISIN](), with no link target) renders as a disabled item in the sidebar and requires no file to exist yet — that’s how ISIN and CFI are currently listed. Only replace it once the chapter files actually exist; a link to a missing file breaks the mdbook build.