Skip to main content

ftracker_identifiers/isin/
arbitrary.rs

1use arbitrary::{Arbitrary, Unstructured};
2
3use super::Isin;
4use super::validation::{ALPHANUMERIC, BASE_LEN, LETTERS, build_valid_isin_bytes};
5
6impl<'a> Arbitrary<'a> for Isin {
7    fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
8        // Positions 1-2: ISO 3166-1 alpha-2 country code (letters only).
9        let country = [
10            u.arbitrary::<u8>()? as usize % LETTERS.len(),
11            u.arbitrary::<u8>()? as usize % LETTERS.len(),
12        ];
13
14        // Positions 3-11: alphanumeric NSIN.
15        let mut nsin = [0usize; BASE_LEN - 2];
16        for slot in &mut nsin {
17            *slot = u.arbitrary::<u8>()? as usize % ALPHANUMERIC.len();
18        }
19
20        let bytes = build_valid_isin_bytes(country, &nsin);
21
22        Isin::from_bytes(bytes).map_err(|_| arbitrary::Error::IncorrectFormat)
23    }
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29
30    #[test]
31    fn always_produces_valid_isins() {
32        for seed in 0u32..256 {
33            let data = seed.to_le_bytes().repeat(8);
34            let mut u = Unstructured::new(&data);
35            let isin = Isin::arbitrary(&mut u).expect("arbitrary should always succeed");
36            // Re-validating via parse() proves the value round-trips through the exact same
37            // checks a hand-typed input would.
38            assert!(Isin::parse(isin.as_str()).is_ok());
39        }
40    }
41}