dasn1-notation 0.1.1

A compiler for the ASN.1 notation language
Documentation
use std::{
    collections::BTreeMap,
    fmt,
    hash::{Hash, Hasher},
};

use derefable::Derefable;
use variation::Variation;

use super::values::Number;

#[derive(Clone, Debug, Derefable, Default, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ObjectIdentifier(#[deref(mutable)] Vec<ObjIdComponent>);

impl ObjectIdentifier {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn from_components(components: Vec<ObjIdComponent>) -> Self {
        Self(components)
    }

    pub fn is_absolute(&self) -> bool {
        !self.is_relative()
    }

    pub fn is_relative(&self) -> bool {
        for component in &self.0 {
            if component.is_reserved_name_form() {
                continue;
            } else if component.is_name() {
                return true;
            }
        }

        false
    }

    pub fn replace(&mut self, map: &BTreeMap<String, ObjectIdentifier>) {
        for (name, id) in map {
            while let Ok(index) = self.0.binary_search(&ObjIdComponent::Name(name.clone())) {
                self.0.splice(index..(index + 1), id.0.iter().cloned());
            }
        }
    }
}

impl fmt::Display for ObjectIdentifier {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{{ ")?;
        for component in &self.0 {
            write!(f, "{} ", component)?;
        }

        write!(f, "}}")
    }
}

#[derive(Clone, Debug, Eq, PartialOrd, Ord, Variation)]
pub enum ObjIdComponent {
    Name(String),
    Number(Number),
    NameAndNumber(String, Number),
}

impl ObjIdComponent {
    pub fn is_reserved_name_form(&self) -> bool {
        match self {
            ObjIdComponent::NameAndNumber(name, _) | ObjIdComponent::Name(name) => match &**name {
                "ITU-T" | "itu-t" | "ccitt" | "ISO" | "iso" | "Joint-ISO-ITU-T"
                | "joint-iso-itu-t" | "joint-iso-ccitt" => true,
                _ => false,
            },
            _ => false,
        }
    }
}

impl fmt::Display for ObjIdComponent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ObjIdComponent::Name(name) => name.fmt(f),
            ObjIdComponent::Number(number) => number.fmt(f),
            ObjIdComponent::NameAndNumber(name, number) => write!(f, "{}({})", name, number),
        }
    }
}

impl Hash for ObjIdComponent {
    fn hash<H: Hasher>(&self, state: &mut H) {
        match self {
            ObjIdComponent::Name(s) => s.hash(state),
            ObjIdComponent::Number(n) => n.hash(state),
            ObjIdComponent::NameAndNumber(s, _) if self.is_reserved_name_form() => s.hash(state),
            ObjIdComponent::NameAndNumber(s, n) => {
                s.hash(state);
                n.hash(state);
            }
        }
    }
}

impl PartialEq for ObjIdComponent {
    fn eq(&self, rhs: &Self) -> bool {
        if self.is_reserved_name_form() {
            let self_name = match self {
                ObjIdComponent::Name(name) | ObjIdComponent::NameAndNumber(name, _) => name,
                _ => unreachable!(),
            };

            if rhs.is_reserved_name_form() {
                let rhs_name = match self {
                    ObjIdComponent::Name(name) | ObjIdComponent::NameAndNumber(name, _) => name,
                    _ => unreachable!(),
                };

                self_name.to_lowercase() == rhs_name.to_lowercase()
            } else {
                false
            }
        } else {
            match (self, rhs) {
                (ObjIdComponent::Name(lhs), ObjIdComponent::Name(rhs)) => lhs == rhs,
                (ObjIdComponent::Number(lhs), ObjIdComponent::Number(rhs)) => lhs == rhs,
                (
                    ObjIdComponent::NameAndNumber(lhs_s, lhs_n),
                    ObjIdComponent::NameAndNumber(rhs_s, rhs_n),
                ) => lhs_s == rhs_s && lhs_n == rhs_n,
                _ => false,
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::collections::hash_map::DefaultHasher;

    fn two_oids() -> (ObjectIdentifier, ObjectIdentifier) {
        (
            ObjectIdentifier::from_components(vec![
                ObjIdComponent::NameAndNumber("joint-iso-itu-t".into(), 2.into()),
                ObjIdComponent::NameAndNumber("ds".into(), 5.into()),
                ObjIdComponent::NameAndNumber("module".into(), 1.into()),
                ObjIdComponent::NameAndNumber("usefulDefinitions".into(), 0.into()),
                ObjIdComponent::Number(3.into()),
            ]),
            ObjectIdentifier::from_components(vec![
                ObjIdComponent::Name("joint-iso-itu-t".into()),
                ObjIdComponent::NameAndNumber("ds".into(), 5.into()),
                ObjIdComponent::NameAndNumber("module".into(), 1.into()),
                ObjIdComponent::NameAndNumber("usefulDefinitions".into(), 0.into()),
                ObjIdComponent::Number(3.into()),
            ]),
        )
    }

    #[test]
    fn oid_partialeq() {
        let (a, b) = two_oids();
        assert_eq!(a, b);
        assert_eq!(b, a);
    }

    #[test]
    fn oid_hash() {
        let (a, b) = two_oids();

        let mut a_hasher = DefaultHasher::new();
        a.hash(&mut a_hasher);

        let mut b_hasher = DefaultHasher::new();
        b.hash(&mut b_hasher);

        assert_eq!(a_hasher.finish(), b_hasher.finish());
    }
}