use alloc::string::String;
use core::str::{self, FromStr};
use encoding::LabelError;
const CERT_STR_SUFFIX: &str = "-cert-v01";
const MAX_ALGORITHM_NAME_LEN: usize = 64;
const MAX_CERT_STR_LEN: usize = MAX_ALGORITHM_NAME_LEN + CERT_STR_SUFFIX.len();
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct AlgorithmName {
id: String,
}
impl AlgorithmName {
pub fn new(id: impl Into<String>) -> Result<Self, LabelError> {
let id = id.into();
validate_algorithm_id(&id, MAX_ALGORITHM_NAME_LEN)?;
split_algorithm_id(&id)?;
Ok(Self { id })
}
pub fn as_str(&self) -> &str {
&self.id
}
pub fn certificate_type(&self) -> String {
let (name, domain) = split_algorithm_id(&self.id).expect("format checked in constructor");
format!("{name}{CERT_STR_SUFFIX}@{domain}")
}
pub fn from_certificate_type(id: &str) -> Result<Self, LabelError> {
validate_algorithm_id(id, MAX_CERT_STR_LEN)?;
let (name, domain) = split_algorithm_id(id)?;
let name = name
.strip_suffix(CERT_STR_SUFFIX)
.ok_or_else(|| LabelError::new(id))?;
let algorithm_name = format!("{name}@{domain}");
Ok(Self { id: algorithm_name })
}
}
impl FromStr for AlgorithmName {
type Err = LabelError;
fn from_str(id: &str) -> Result<Self, LabelError> {
Self::new(id)
}
}
fn validate_algorithm_id(id: &str, n: usize) -> Result<(), LabelError> {
if id.len() > n || !id.is_ascii() {
return Err(LabelError::new(id));
}
Ok(())
}
fn split_algorithm_id(id: &str) -> Result<(&str, &str), LabelError> {
let (name, domain) = id.split_once('@').ok_or_else(|| LabelError::new(id))?;
if name.is_empty() || domain.is_empty() || domain.contains('@') {
return Err(LabelError::new(id));
}
Ok((name, domain))
}