Expand description
CFI (Classification of Financial Instruments) — the ISO 10962 six-letter code that classifies a financial instrument by category, group, and four attributes.
This module provides the validated Rust representation (Cfi) and the parsing, validation,
and error types that surround it. It accepts the canonical 6-character form (optionally
surrounded by whitespace, in any ASCII case), normalizes it, and guarantees that any constructed
Cfi describes a combination actually defined by ISO 10962. There is no partially validated
state: if you hold a Cfi, it is valid.
§What this type represents
A CFI has 6 characters, all uppercase letters, split into three parts:
| Positions | Length | Segment | Meaning |
|---|---|---|---|
| 1 | 1 | Category | The broadest class of instrument (e.g. E = equities) |
| 2 | 1 | Group | A subdivision within the category (meaning depends on the category) |
| 3–6 | 4 | Attributes | Four attribute codes whose meaning depends on the category and group |
┌────────────────────────────────────────┐
│ Cat │ Grp │ Attribute 1..4 (4 chars) │
│ E │ S │ V U F R │
└────────────────────────────────────────┘Cfi stores those 6 characters as normalized uppercase ASCII and exposes borrowed/char
accessors for the category (Cfi::category), the group (Cfi::group), the four attributes
(Cfi::attributes), and the whole value (Cfi::as_str).
§Validation rules — taxonomy, not checksum
Unlike Cnpj (Módulo 11) or Isin (Luhn), a CFI carries no check
digit. Its validity is defined entirely by the ISO 10962 code taxonomy, which this crate embeds
as a generated, no_std lookup table. Every fallible constructor runs the same rules, in order,
and each maps to one CfiError variant:
- Length — after surrounding whitespace is trimmed, the input must contain exactly 6
characters (
CfiError::InvalidLength).Cfi::parserejects empty input up front (CfiError::Empty). - Character class — every position must be an uppercase ASCII letter
(
CfiError::InvalidCharacter). - Category — position 1 must be a category defined by ISO 10962
(
CfiError::UnknownCategory). - Group — position 2 must be a group defined for that category (
CfiError::UnknownGroup). - Attributes — each of positions 3–6 must be a code the standard permits for the resolved
category and group at that attribute position (
CfiError::InvalidAttribute).
Only the classification codes are embedded — not ISO’s descriptive text — so this crate can tell you a CFI is well-formed and which position is wrong, but it does not resolve the codes to their human-readable meanings.
§Design notes
- No invalid state is representable.
Cfi’s only field is private; the only ways to obtain one —Cfi::parse,Cfi::new,Cfi::from_bytes,FromStr, andTryFrom<&str>— all run full validation. There is no unchecked constructor. - Zero allocation,
Copy,no_std-friendly.Cfiis a 6-byte value type wrapping[u8; 6]. Parsing, validating, and every accessor operate on the stack; the taxonomy lookup is a couple of binary searches and bitmask tests over astatictable. - Ordering and hashing are byte-wise.
CfiderivesOrdandHashdirectly over its ASCII bytes, matchingstrordering onCfi::as_str. This is lexicographic string order, with no taxonomic meaning. - Safe to use as a map/set key.
CfiimplementsEqandHashconsistently withPartialEq, so it works as aHashMap/HashSetorBTreeMap/BTreeSetkey 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 Cfi::parse or the validation rules above:
serde— (de)serializesCfias its 6-character string (e.g."ESVUFR"). Deserialization re-runs full validation, so an untrusted payload can never produce an invalidCfi.schemars— implementsJsonSchemaforCfi, describing it as a pattern-constrained string (^[A-Z]{6}$). The pattern is structural only; it cannot express which combinations are taxonomically valid. Impliesserde.arbitrary— implementsArbitraryforCfi, generating taxonomically valid values for fuzz targets by walking the embedded table.proptest— exposes reusablepropteststrategies (ftracker_identifiers::cfi::proptest, when this feature is enabled) for generating validCfivalues.
§Error handling
Every fallible constructor returns CfiError, 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::{Cfi, CfiError};
match Cfi::parse("ESZUFR") {
Ok(cfi) => println!("valid: {cfi}"),
Err(CfiError::InvalidAttribute { index, code, .. }) => {
println!("attribute {index} rejected: {code}");
}
Err(other) => println!("rejected: {other}"),
}§Examples
use ftracker_identifiers::Cfi;
let cfi = Cfi::parse("ESVUFR").unwrap();
assert_eq!(cfi.category(), 'E');
assert_eq!(cfi.group(), 'S');
assert_eq!(cfi.attributes(), ['V', 'U', 'F', 'R']);
assert_eq!(cfi.as_str(), "ESVUFR");Sorting and deduplicating a batch of CFIs, e.g. after importing them from a spreadsheet:
use ftracker_identifiers::Cfi;
let mut cfis: Vec<Cfi> = ["ESVUFR", "DBFTFB", "ESVUFR"]
.into_iter()
.map(|s| Cfi::parse(s).unwrap())
.collect();
cfis.sort();
cfis.dedup();
assert_eq!(cfis.len(), 2);Modules§
Structs§
- Cfi
- A validated CFI (Classification of Financial Instruments, ISO 10962).
Enums§
- CfiError
- The set of reasons a CFI string can fail validation.