Skip to main content

Module isin

Module isin 

Source
Expand description

ISIN (International Securities Identification Number) — the ISO 6166 identifier for a fungible financial security.

This module provides the validated Rust representation (Isin) and the parsing, validation, and error types that surround it. It accepts the canonical 12-character form (optionally surrounded by whitespace, in any ASCII case), normalizes it, and guarantees that any constructed Isin satisfies the structural rules and the Luhn check digit described below. There is no partially-validated state: if you hold an Isin, it is valid.

§What this type represents

An ISIN has 12 characters, split into three segments:

PositionsLengthSegmentMeaning
1–22Country codeISO 3166-1 alpha-2 prefix of the issuing national numbering agency
3–119NSINNational Securities Identifying Number, alphanumeric
121Check digitLuhn (modulus 10) digit computed over the first 11 characters
┌──────────────────────────────────────────────────────┐
│  CC    │           NSIN (9 chars)          │ Check (1) │
│  A  A  │       N  N  N  N  N  N  N  N  N    │     D     │
└──────────────────────────────────────────────────────┘

Isin stores those 12 characters as normalized uppercase ASCII and exposes borrowed accessors for the country code (Isin::country_code), the NSIN (Isin::nsin), the check digit (Isin::check_digit), and the whole value (Isin::as_str).

§Validation rules

Every fallible constructor runs the same rules, in order, and each maps to one IsinError variant:

  1. Length — after surrounding whitespace is trimmed, the input must contain exactly 12 characters (IsinError::InvalidLength). Isin::parse rejects empty input up front (IsinError::Empty).
  2. Character class — positions 1–2 accept an uppercase letter, positions 3–11 accept a digit or an uppercase letter, and position 12 accepts only a digit (IsinError::InvalidCharacter).
  3. Check digit — position 12 must match the ISO 6166 Luhn digit computed from the first 11 characters (IsinError::InvalidCheckDigit).

The country code is validated structurally (two uppercase letters); this crate deliberately does not check it against the live ISO 3166-1 country registry, which changes over time and is out of scope for a checksum-oriented value type.

§Design notes

  • No invalid state is representable. Isin’s only field is private; the only ways to obtain one — Isin::parse, Isin::new, Isin::from_bytes, FromStr, and TryFrom<&str> — all run full validation. There is no unchecked constructor.
  • Zero allocation, Copy, no_std-friendly. Isin is a 12-byte value type wrapping [u8; 12]. Parsing, validating, and every accessor operate on the stack.
  • Ordering and hashing are byte-wise. Isin derives Ord and Hash directly over its ASCII bytes, which matches str ordering on Isin::as_str. This is lexicographic string order, not any notion of issuance order.
  • Safe to use as a map/set key. Isin implements Eq and Hash consistently with PartialEq, so it works as a HashMap/HashSet or BTreeMap/BTreeSet key out of the box.

§Feature flags

This module’s optional integrations are off by default and purely additive — enabling one never changes the behavior of Isin::parse or the validation rules above:

  • serde — (de)serializes Isin as its 12-character string (e.g. "US0378331005"). Deserialization re-runs full validation, so an untrusted payload can never produce an invalid Isin.
  • schemars — implements JsonSchema for Isin, describing it as a pattern-constrained string (^[A-Z]{2}[A-Z0-9]{9}[0-9]$). Implies serde.
  • arbitrary — implements Arbitrary for Isin, generating structurally valid, checksum-correct values for fuzz targets.
  • proptest — exposes reusable proptest strategies (ftracker_identifiers::isin::proptest, when this feature is enabled) for generating checksum-valid Isin values.

§Error handling

Every fallible constructor returns IsinError, which is Clone + PartialEq + Eq and implements core::error::Error and core::fmt::Display, so it composes with ? and with error-aggregation crates alike:

use ftracker_identifiers::{Isin, IsinError};

match Isin::parse("US0378331006") {
    Ok(isin) => println!("valid: {isin}"),
    Err(IsinError::InvalidCheckDigit { expected, found }) => {
        println!("checksum mismatch: expected {expected}, found {found}");
    }
    Err(other) => println!("rejected: {other}"),
}

§Examples

use ftracker_identifiers::Isin;

let apple = Isin::parse("US0378331005").unwrap();
assert_eq!(apple.country_code(), "US");
assert_eq!(apple.nsin(), "037833100");
assert_eq!(apple.check_digit(), 5);
assert_eq!(apple.as_str(), "US0378331005");

Sorting and deduplicating a batch of ISINs, e.g. after importing them from a spreadsheet:

use ftracker_identifiers::Isin;

let mut isins: Vec<Isin> = ["US0231351067", "US0378331005", "US0378331005"]
    .into_iter()
    .map(|s| Isin::parse(s).unwrap())
    .collect();
isins.sort();
isins.dedup();
assert_eq!(isins.len(), 2);

Modules§

proptest
Reusable proptest strategies for Isin.

Structs§

Isin
A validated ISIN (International Securities Identification Number, ISO 6166).

Enums§

IsinError
The set of reasons an ISIN string can fail validation.