use std::fmt;
use crate::errors::ErnError;
use derive_more::{AsRef, From, Into};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(AsRef, From, Into, Eq, Debug, PartialEq, Clone, Hash, PartialOrd)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Account(pub(crate) String);
impl Account {
pub fn as_str(&self) -> &str {
&self.0
}
pub fn new(value: impl Into<String>) -> Result<Self, ErnError> {
let val = value.into();
if val.is_empty() {
return Err(ErnError::ParseFailure(
"Account",
"cannot be empty".to_string(),
));
}
if val.len() > 63 {
return Err(ErnError::ParseFailure(
"Account",
format!(
"length exceeds maximum of 63 characters (got {})",
val.len()
),
));
}
let valid_chars = val
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '_');
if !valid_chars {
return Err(ErnError::ParseFailure(
"Account",
"can only contain alphanumeric characters, hyphens, and underscores".to_string(),
));
}
if val.starts_with(['-', '_'].as_ref()) || val.ends_with(['-', '_'].as_ref()) {
return Err(ErnError::ParseFailure(
"Account",
"cannot start or end with a hyphen or underscore".to_string(),
));
}
Ok(Account(val))
}
pub fn into_owned(self) -> Account {
Account(self.0.to_string())
}
}
impl Default for Account {
fn default() -> Self {
Account("component".to_string())
}
}
impl fmt::Display for Account {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::str::FromStr for Account {
type Err = ErnError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Account::new(s)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_account_creation() -> anyhow::Result<()> {
let account = Account::new("test123")?;
assert_eq!(account.as_str(), "test123");
Ok(())
}
#[test]
fn test_account_default() {
let account = Account::default();
assert_eq!(account.as_str(), "component");
}
#[test]
fn test_account_display() -> anyhow::Result<()> {
let account = Account::new("example456")?;
assert_eq!(format!("{}", account), "example456");
Ok(())
}
#[test]
fn test_account_from_str() {
let account: Account = "test789".parse().unwrap();
assert_eq!(account.as_str(), "test789");
}
#[test]
fn test_account_equality() -> anyhow::Result<()> {
let account1 = Account::new("test123")?;
let account2 = Account::new("test123")?;
let account3 = Account::new("other456")?;
assert_eq!(account1, account2);
assert_ne!(account1, account3);
Ok(())
}
#[test]
fn test_account_into_string() -> anyhow::Result<()> {
let account = Account::new("test123")?;
let string: String = account.into();
assert_eq!(string, "test123");
Ok(())
}
#[test]
fn test_account_validation_empty() {
let result = Account::new("");
assert!(result.is_err());
match result {
Err(ErnError::ParseFailure(component, msg)) => {
assert_eq!(component, "Account");
assert!(msg.contains("empty"));
}
_ => panic!("Expected ParseFailure error for empty account"),
}
}
#[test]
fn test_account_validation_too_long() {
let long_account = "a".repeat(64);
let result = Account::new(long_account);
assert!(result.is_err());
match result {
Err(ErnError::ParseFailure(component, msg)) => {
assert_eq!(component, "Account");
assert!(msg.contains("length exceeds maximum"));
}
_ => panic!("Expected ParseFailure error for too long account"),
}
}
#[test]
fn test_account_validation_invalid_chars() {
let result = Account::new("invalid.account$");
assert!(result.is_err());
match result {
Err(ErnError::ParseFailure(component, msg)) => {
assert_eq!(component, "Account");
assert!(msg.contains("can only contain"));
}
_ => panic!("Expected ParseFailure error for invalid characters"),
}
}
#[test]
fn test_account_validation_hyphen_underscore_start_end() {
let result1 = Account::new("-invalid");
let result2 = Account::new("invalid-");
let result3 = Account::new("_invalid");
let result4 = Account::new("invalid_");
assert!(result1.is_err());
assert!(result2.is_err());
assert!(result3.is_err());
assert!(result4.is_err());
match result1 {
Err(ErnError::ParseFailure(component, msg)) => {
assert_eq!(component, "Account");
assert!(msg.contains("cannot start or end with a hyphen or underscore"));
}
_ => panic!("Expected ParseFailure error for account starting with hyphen"),
}
}
#[test]
fn test_account_validation_valid_complex() -> anyhow::Result<()> {
let result = Account::new("valid-account_123")?;
assert_eq!(result.as_str(), "valid-account_123");
Ok(())
}
}