use std::fmt;
use std::fmt::Write as _;
use thiserror::Error;
pub const MAX_FOUNDATIONDB_TENANT_NAME_LENGTH: usize = 63;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FoundationDbTenantName {
bytes: [u8; MAX_FOUNDATIONDB_TENANT_NAME_LENGTH],
length: u8,
}
impl FoundationDbTenantName {
pub fn new(value: &str) -> Result<Self, FoundationDbTenantNameError> {
let value_bytes = value.as_bytes();
if value_bytes.is_empty() {
return Err(FoundationDbTenantNameError::Invalid {
reason: FoundationDbTenantNameErrorReason::Empty,
});
}
if value_bytes.len() > MAX_FOUNDATIONDB_TENANT_NAME_LENGTH {
return Err(FoundationDbTenantNameError::Invalid {
reason: FoundationDbTenantNameErrorReason::TooLong,
});
}
if value_bytes.first() == Some(&b'-') || value_bytes.last() == Some(&b'-') {
return Err(FoundationDbTenantNameError::Invalid {
reason: FoundationDbTenantNameErrorReason::InvalidBoundary,
});
}
if !value_bytes
.iter()
.all(|byte| byte.is_ascii_lowercase() || byte.is_ascii_digit() || *byte == b'-')
{
return Err(FoundationDbTenantNameError::Invalid {
reason: FoundationDbTenantNameErrorReason::InvalidCharacter,
});
}
let length =
u8::try_from(value_bytes.len()).map_err(|_| FoundationDbTenantNameError::Invalid {
reason: FoundationDbTenantNameErrorReason::TooLong,
})?;
let mut bytes = [0_u8; MAX_FOUNDATIONDB_TENANT_NAME_LENGTH];
bytes[..value_bytes.len()].copy_from_slice(value_bytes);
Ok(Self { bytes, length })
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..usize::from(self.length)]
}
}
impl fmt::Debug for FoundationDbTenantName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("FoundationDbTenantName(")?;
fmt::Display::fmt(self, formatter)?;
formatter.write_char(')')
}
}
impl fmt::Display for FoundationDbTenantName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.as_bytes() {
formatter.write_char(char::from(*byte))?;
}
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum FoundationDbTenantNameError {
#[error("invalid foundationdb tenant name: {reason}")]
Invalid {
reason: FoundationDbTenantNameErrorReason,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
pub enum FoundationDbTenantNameErrorReason {
#[error("empty")]
Empty,
#[error("too long")]
TooLong,
#[error("invalid character")]
InvalidCharacter,
#[error("invalid boundary")]
InvalidBoundary,
}
#[cfg(test)]
#[path = "tenant_name_tests.rs"]
mod tests;