use chrono::NaiveDate;
use std::collections::BTreeMap;
use strum::{Display, EnumString};
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
pub enum Sex {
#[strum(serialize = "1")]
Male,
#[strum(serialize = "2")]
Female,
#[strum(serialize = "9")]
NotSpecified,
}
#[derive(Debug, Clone, PartialEq, Eq, EnumString, Display)]
#[strum(ascii_case_insensitive)]
pub enum EyeColor {
#[strum(serialize = "BLK")]
Black,
#[strum(serialize = "BLU")]
Blue,
#[strum(serialize = "BRO", serialize = "BRN")]
Brown,
#[strum(serialize = "GRY")]
Gray,
#[strum(serialize = "GRN")]
Green,
#[strum(serialize = "HAZ")]
Hazel,
#[strum(serialize = "MAR")]
Maroon,
#[strum(serialize = "PNK")]
Pink,
#[strum(serialize = "DIC")]
Dichromatic,
#[strum(serialize = "UNK")]
Unknown,
#[strum(default)]
Other(String),
}
#[derive(Debug, Clone, PartialEq, Eq, EnumString, Display)]
#[strum(ascii_case_insensitive)]
pub enum HairColor {
#[strum(serialize = "BAL")]
Bald,
#[strum(serialize = "BLK")]
Black,
#[strum(serialize = "BLN")]
Blond,
#[strum(serialize = "BRO", serialize = "BRN")]
Brown,
#[strum(serialize = "GRY")]
Gray,
#[strum(serialize = "RED")]
Red,
#[strum(serialize = "SDY")]
Sandy,
#[strum(serialize = "WHI")]
White,
#[strum(serialize = "UNK")]
Unknown,
#[strum(default)]
Other(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
pub enum Country {
#[strum(serialize = "USA")]
Usa,
#[strum(serialize = "CAN")]
Canada,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
pub enum Compliance {
#[strum(serialize = "F")]
Compliant,
#[strum(serialize = "N")]
NonCompliant,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Height {
Inches(u16),
Centimetres(u16),
}
impl Height {
pub(crate) fn parse(raw: &str) -> Option<Self> {
let trimmed = raw.trim();
let digits: String = trimmed.chars().take_while(|c| c.is_ascii_digit()).collect();
if digits.is_empty() {
return None;
}
let value: u16 = digits.parse().ok()?;
let unit = trimmed[digits.len()..].trim().to_ascii_lowercase();
match unit.as_str() {
"" | "in" => Some(Height::Inches(value)),
"cm" => Some(Height::Centimetres(value)),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumString, Display)]
pub enum Truncation {
#[strum(serialize = "T")]
Truncated,
#[strum(serialize = "N")]
NotTruncated,
#[strum(serialize = "U")]
Unknown,
}
#[derive(Debug, Clone)]
pub struct AamvaHeader {
pub iin: String,
pub aamva_version: u8,
pub jurisdiction_version: u8,
pub entry_count: u8,
pub subfiles: Vec<SubfileDesignator>,
}
#[derive(Debug, Clone)]
pub struct SubfileDesignator {
pub subfile_type: String,
pub offset: usize,
pub length: usize,
}
#[derive(Debug, Clone, Default)]
pub struct AamvaLicense {
pub header: Option<AamvaHeader>,
pub elements: BTreeMap<String, String>,
pub family_name: Option<String>,
pub first_name: Option<String>,
pub middle_name: Option<String>,
pub name_suffix: Option<String>,
pub family_name_truncation: Option<Truncation>,
pub first_name_truncation: Option<Truncation>,
pub middle_name_truncation: Option<Truncation>,
pub document_number: Option<String>,
pub document_discriminator: Option<String>,
pub country: Option<Country>,
pub jurisdiction: Option<String>,
pub date_of_birth: Option<NaiveDate>,
pub issue_date: Option<NaiveDate>,
pub expiry_date: Option<NaiveDate>,
pub card_revision_date: Option<NaiveDate>,
pub under_18_until: Option<NaiveDate>,
pub under_19_until: Option<NaiveDate>,
pub under_21_until: Option<NaiveDate>,
pub sex: Option<Sex>,
pub eye_color: Option<EyeColor>,
pub hair_color: Option<HairColor>,
pub height: Option<Height>,
pub weight_lb: Option<u32>,
pub weight_kg: Option<u32>,
pub weight_range: Option<u8>,
pub address_street_1: Option<String>,
pub address_street_2: Option<String>,
pub city: Option<String>,
pub postal_code: Option<String>,
pub vehicle_class: Option<String>,
pub restrictions: Option<String>,
pub endorsements: Option<String>,
pub organ_donor: Option<bool>,
pub veteran: Option<bool>,
pub compliance: Option<Compliance>,
}