uuinfo 0.6.6

A tool to debug unique identifiers (UUID, ULID, Snowflake, etc).
use imei;
use mac_address::MacAddress;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::{fmt::Write, str::FromStr};

use crate::schema::{Args, IDInfo};
use crate::utils::factor_size_hex_bits_color_from_text;

pub fn parse_ipv4(args: &Args) -> Option<IDInfo> {
    let ip = Ipv4Addr::from_str(&args.id).ok()?;
    let mut version: Option<String> = None;
    if ip.is_loopback() {
        version = Some("Loopback".to_string());
    }
    if ip.is_private() {
        version = Some("Private".to_string());
    }

    Some(IDInfo {
        id_type: "IPv4 Address".to_string(),
        version,
        standard: args.id.clone(),
        integer: Some(ip.to_bits() as u128),
        parsed: Some("from integer parts".to_string()),
        size: 32,
        hex: Some(hex::encode(ip.to_bits().to_be_bytes())),
        bits: Some(ip.to_bits().to_be_bytes().iter().fold(String::new(), |mut output, c| {
            let _ = write!(output, "{c:08b}");
            output
        })),
        color_map: Some((0..32).map(|_| "0").collect::<String>()),
        high_confidence: true,
        ..Default::default()
    })
}

pub fn parse_ipv6(args: &Args) -> Option<IDInfo> {
    let ip = Ipv6Addr::from_str(&args.id).ok()?;
    let mut version: Option<String> = None;
    if ip.is_loopback() {
        version = Some("Loopback".to_string());
    }

    Some(IDInfo {
        id_type: "IPv6 Address".to_string(),
        version,
        standard: args.id.clone().to_lowercase(),
        integer: Some(ip.to_bits()),
        parsed: Some("from hex parts".to_string()),
        size: 128,
        hex: Some(hex::encode(ip.to_bits().to_be_bytes())),
        bits: Some(ip.to_bits().to_be_bytes().iter().fold(String::new(), |mut output, c| {
            let _ = write!(output, "{c:08b}");
            output
        })),
        color_map: Some((0..128).map(|_| "0").collect::<String>()),
        high_confidence: true,
        ..Default::default()
    })
}

fn fit_u64(bytes: Vec<u8>) -> u64 {
    let mut buffer: Vec<u8> = vec![0; 8 - bytes.len()];
    buffer.extend(bytes);
    let all_bytes: [u8; 8] = buffer.try_into().ok().unwrap();
    u64::from_be_bytes(all_bytes)
}

pub fn parse_mac(args: &Args) -> Option<IDInfo> {
    let mac = MacAddress::from_str(&args.id).ok()?;
    let mac_int = fit_u64((mac.bytes()[0..6]).to_vec());
    let mac_lower = args.id.clone().to_lowercase();
    let prefix = fit_u64((mac.bytes()[0..3]).to_vec());
    let sequence = fit_u64((mac.bytes()[3..6]).to_vec());

    Some(IDInfo {
        id_type: "MAC Address".to_string(),
        standard: mac_lower.clone(),
        integer: Some(mac_int as u128),
        parsed: Some("from hex parts".to_string()),
        size: 48,
        node1: Some(format!("{}, hex: {} (Manufacturer)", prefix, &mac_lower[..8])),
        sequence: Some(sequence as u128),
        hex: Some(hex::encode(mac.bytes())),
        bits: Some(mac.bytes().iter().fold(String::new(), |mut output, c| {
            let _ = write!(output, "{c:08b}");
            output
        })),
        color_map: Some(format!("{}{}", (0..24).map(|_| "4").collect::<String>(), (0..24).map(|_| "6").collect::<String>(),)),
        high_confidence: true,
        ..Default::default()
    })
}

pub fn parse_imei(args: &Args) -> Option<IDInfo> {
    let no_dashes = args.id.replace("-", "");
    if no_dashes.chars().count() != 15 || !imei::valid(&no_dashes) {
        return None;
    }
    let id_int = no_dashes.trim().parse::<u128>().ok()?;
    let (size, hex, bits, _) = factor_size_hex_bits_color_from_text(&no_dashes);
    let standard = format!("{}-{}-{}-{}", &no_dashes[0..2], &no_dashes[2..8], &no_dashes[8..14], &no_dashes[14..15]);

    Some(IDInfo {
        id_type: "IMEI".to_string(),
        standard: standard.clone(),
        integer: Some(id_int),
        parsed: Some("as ASCII, no dashes".to_string()),
        size,
        node1: Some(format!("{} (Type Allocation Code)", &no_dashes[0..8])),
        node2: Some(format!("{} (Check Digit)", &no_dashes[14..15])),
        sequence: Some(no_dashes[8..14].parse::<u128>().unwrap()),
        hex,
        bits,
        color_map: Some(format!(
            "{}{}{}",
            (0..64).map(|_| "4").collect::<String>(),
            (0..48).map(|_| "6").collect::<String>(),
            (0..8).map(|_| "5").collect::<String>(),
        )),
        high_confidence: true,
        ..Default::default()
    })
}