//! OID Names Database //! //! The contents of this database are generated from the official IANA //! [Object Identifier Descriptors] Registry CSV file and from [RFC 5280]. //! If we are missing values you care about, please contribute a patch to //! `oiddbgen` (a subcrate in the source code) to generate the values from //! the relevant standard. //! //! [RFC 5280]: https://datatracker.ietf.org/doc/html/rfc5280 //! [Object Identifier Descriptors]: https://www.iana.org/assignments/ldap-parameters/ldap-parameters.xhtml#ldap-parameters-3 #![allow(clippy::integer_arithmetic, missing_docs)] mod gen; pub use gen::*; use crate::{Error, ObjectIdentifier}; /// A const implementation of byte equals. const fn eq(lhs: &[u8], rhs: &[u8]) -> bool { if lhs.len() != rhs.len() { return false; } let mut i = 0usize; while i < lhs.len() { if lhs[i] != rhs[i] { return false; } i += 1; } true } /// A const implementation of case-insensitive ASCII equals. const fn eq_case(lhs: &[u8], rhs: &[u8]) -> bool { if lhs.len() != rhs.len() { return false; } let mut i = 0usize; while i < lhs.len() { if !lhs[i].eq_ignore_ascii_case(&rhs[i]) { return false; } i += 1; } true } /// A query interface for OIDs/Names. #[derive(Copy, Clone)] pub struct Database<'a>(&'a [(&'a ObjectIdentifier, &'a str)]); impl<'a> Database<'a> { /// Looks up a name for an OID. /// /// Errors if the input is not a valid OID. /// Returns the input if no name is found. pub fn resolve<'b>(&self, oid: &'b str) -> Result<&'b str, Error> where 'a: 'b, { Ok(self.by_oid(&oid.parse()?).unwrap_or(oid)) } /// Finds a named oid by its associated OID. pub const fn by_oid(&self, oid: &ObjectIdentifier) -> Option<&'a str> { let mut i = 0; while i < self.0.len() { let lhs = self.0[i].0; if lhs.length == oid.length && eq(&lhs.bytes, &oid.bytes) { return Some(self.0[i].1); } i += 1; } None } /// Finds a named oid by its associated name. pub const fn by_name(&self, name: &str) -> Option<&'a ObjectIdentifier> { let mut i = 0; while i < self.0.len() { let lhs = self.0[i].1; if eq_case(lhs.as_bytes(), name.as_bytes()) { return Some(self.0[i].0); } i += 1; } None } /// Return the list of matched name for the OID. pub const fn find_names_for_oid(&self, oid: ObjectIdentifier) -> Names<'a> { Names { database: *self, oid, position: 0, } } } /// Iterator returning the multiple names that may be associated with an OID. pub struct Names<'a> { database: Database<'a>, oid: ObjectIdentifier, position: usize, } impl<'a> Iterator for Names<'a> { type Item = &'a str; fn next(&mut self) -> Option<&'a str> { let mut i = self.position; while i < self.database.0.len() { let lhs = self.database.0[i].0; if lhs.as_bytes().eq(self.oid.as_bytes()) { self.position = i + 1; return Some(self.database.0[i].1); } i += 1; } None } } #[cfg(test)] mod tests { use crate::ObjectIdentifier; use super::rfc4519::CN; #[test] fn by_oid() { let cn = super::DB.by_oid(&CN).expect("cn not found"); assert_eq!("cn", cn); let none = ObjectIdentifier::new_unwrap("0.1.2.3.4.5.6.7.8.9"); assert_eq!(None, super::DB.by_oid(&none)); } #[test] fn by_name() { let cn = super::DB.by_name("CN").expect("cn not found"); assert_eq!(&CN, cn); assert_eq!(None, super::DB.by_name("purplePeopleEater")); } }