#![no_std]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
html_root_url = "https://docs.rs/const-oid/0.9.0"
)]
#![forbid(unsafe_code, clippy::unwrap_used)]
#![warn(missing_docs, rust_2018_idioms)]
#[cfg(feature = "std")]
extern crate std;
mod arcs;
mod encoder;
mod error;
mod parser;
#[cfg(feature = "db")]
#[cfg_attr(docsrs, doc(cfg(feature = "db")))]
pub mod db;
pub use crate::{
arcs::{Arc, Arcs},
error::{Error, Result},
};
use crate::encoder::Encoder;
use core::{fmt, str::FromStr};
pub trait AssociatedOid {
const OID: ObjectIdentifier;
}
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ObjectIdentifier {
length: u8,
bytes: [u8; Self::MAX_SIZE],
}
#[allow(clippy::len_without_is_empty)]
impl ObjectIdentifier {
pub const MAX_SIZE: usize = 39;
pub const fn new_unwrap(s: &str) -> Self {
match Self::new(s) {
Ok(oid) => oid,
Err(Error::ArcInvalid { .. } | Error::ArcTooBig) => panic!("OID contains invalid arc"),
Err(Error::Base128) => panic!("OID contains arc with invalid base 128 encoding"),
Err(Error::DigitExpected { .. }) => panic!("OID expected to start with digit"),
Err(Error::Empty) => panic!("OID value is empty"),
Err(Error::Length) => panic!("OID length invalid"),
Err(Error::NotEnoughArcs) => panic!("OID requires minimum of 3 arcs"),
Err(Error::TrailingDot) => panic!("OID ends with invalid trailing '.'"),
}
}
pub const fn new(s: &str) -> Result<Self> {
match parser::Parser::parse(s) {
Ok(parser) => parser.finish(),
Err(err) => Err(err),
}
}
pub fn from_arcs(arcs: impl IntoIterator<Item = Arc>) -> Result<Self> {
let mut encoder = Encoder::new();
for arc in arcs {
encoder = encoder.arc(arc)?;
}
encoder.finish()
}
pub fn from_bytes(ber_bytes: &[u8]) -> Result<Self> {
let len = ber_bytes.len();
match len {
0 => return Err(Error::Empty),
3..=Self::MAX_SIZE => (),
_ => return Err(Error::NotEnoughArcs),
}
let mut bytes = [0u8; Self::MAX_SIZE];
bytes[..len].copy_from_slice(ber_bytes);
let oid = Self {
bytes,
length: len as u8,
};
let mut arcs = oid.arcs();
while arcs.try_next()?.is_some() {}
Ok(oid)
}
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.length as usize]
}
pub fn arc(&self, index: usize) -> Option<Arc> {
self.arcs().nth(index)
}
pub fn arcs(&self) -> Arcs<'_> {
Arcs::new(self)
}
pub fn len(&self) -> usize {
self.arcs().count()
}
pub fn parent(&self) -> Option<Self> {
let num_arcs = self.len().checked_sub(1)?;
Self::from_arcs(self.arcs().take(num_arcs)).ok()
}
pub const fn push_arc(self, arc: Arc) -> Result<Self> {
match Encoder::extend(self).arc(arc) {
Ok(encoder) => encoder.finish(),
Err(err) => Err(err),
}
}
}
impl AsRef<[u8]> for ObjectIdentifier {
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl FromStr for ObjectIdentifier {
type Err = Error;
fn from_str(string: &str) -> Result<Self> {
Self::new(string)
}
}
impl TryFrom<&[u8]> for ObjectIdentifier {
type Error = Error;
fn try_from(ber_bytes: &[u8]) -> Result<Self> {
Self::from_bytes(ber_bytes)
}
}
impl From<&ObjectIdentifier> for ObjectIdentifier {
fn from(oid: &ObjectIdentifier) -> ObjectIdentifier {
*oid
}
}
impl fmt::Debug for ObjectIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ObjectIdentifier({})", self)
}
}
impl fmt::Display for ObjectIdentifier {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let len = self.arcs().count();
for (i, arc) in self.arcs().enumerate() {
write!(f, "{}", arc)?;
if i < len - 1 {
write!(f, ".")?;
}
}
Ok(())
}
}