use std::hash::Hash;
use std::str::FromStr;
use crate::EntityRoot;
use crate::errors::ErnError;
use crate::model::{Account, Category, Domain, Ern, Part, Parts};
use crate::traits::ErnComponent;
pub struct ErnBuilder<State> {
builder: PrivateErnBuilder,
_marker: std::marker::PhantomData<State>,
}
impl ErnBuilder<()> {
pub fn new() -> ErnBuilder<Domain> {
ErnBuilder {
builder: PrivateErnBuilder::new(),
_marker: std::marker::PhantomData,
}
}
}
impl ErnBuilder<Part> {
pub fn build(self) -> Result<Ern, ErnError> {
self.builder.build()
}
}
impl ErnBuilder<Parts> {
pub fn build(self) -> Result<Ern, ErnError> {
self.builder.build()
}
}
impl<Component: ErnComponent + Hash + Clone + PartialEq + Eq> ErnBuilder<Component> {
pub fn with<N>(self, part: impl Into<String>) -> Result<ErnBuilder<N::NextState>, ErnError>
where
N: ErnComponent<NextState = Component::NextState> + Hash,
{
Ok(ErnBuilder {
builder: self.builder.add_part(N::prefix(), part.into())?,
_marker: std::marker::PhantomData,
})
}
}
struct PrivateErnBuilder {
domain: Option<Domain>,
category: Option<Category>,
account: Option<Account>,
root: Option<EntityRoot>,
parts: Parts,
}
impl PrivateErnBuilder {
fn new() -> Self {
Self {
domain: None,
category: None,
account: None,
root: None,
parts: Parts::new(Vec::new()),
}
}
fn add_part(mut self, prefix: &'static str, part: String) -> Result<Self, ErnError> {
match prefix {
p if p == Domain::prefix() => {
self.domain = Some(Domain::new(part)?);
}
"" => {
if self.domain.is_some() && self.category.is_none() {
self.category = Some(Category::new(part)?);
} else if self.category.is_some() && self.account.is_none() {
self.account = Some(Account::new(part)?);
} else if self.account.is_some() && self.root.is_none() {
self.root = Some(EntityRoot::from_str(part.as_str()).unwrap());
} else {
self.parts = self.parts.add_part(Part::new(part)?)?;
}
}
":" => {
self.parts = self.parts.add_part(Part::new(part)?)?;
}
_ => return Err(ErnError::InvalidPrefix(prefix.to_string())),
}
Ok(self)
}
fn build(self) -> Result<Ern, ErnError> {
let domain = self
.domain
.ok_or(ErnError::MissingPart("domain".to_string()))?;
let category = self
.category
.ok_or(ErnError::MissingPart("category".to_string()))?;
let account = self
.account
.ok_or(ErnError::MissingPart("account".to_string()))?;
let root = self.root.ok_or(ErnError::MissingPart("root".to_string()))?;
Ok(Ern::new(domain, category, account, root, self.parts))
}
}