use super::{ManchesterError, ManchesterExpr};
pub fn emit(expr: &ManchesterExpr) -> Result<String, ManchesterError> {
emit_inner(expr, true)
}
fn emit_inner(expr: &ManchesterExpr, top_level: bool) -> Result<String, ManchesterError> {
match expr {
ManchesterExpr::Class(name) => {
if name.is_empty() {
return Err(ManchesterError::EmitError(
"Class name must not be empty".to_string(),
));
}
Ok(name.clone())
}
ManchesterExpr::And(arms) => {
if arms.is_empty() {
return Err(ManchesterError::EmitError(
"`And` must have at least one arm".to_string(),
));
}
let parts: Result<Vec<String>, _> =
arms.iter().map(|arm| emit_inner(arm, false)).collect();
let joined = parts?.join(" and ");
if top_level {
Ok(joined)
} else {
Ok(format!("({joined})"))
}
}
ManchesterExpr::Or(arms) => {
if arms.is_empty() {
return Err(ManchesterError::EmitError(
"`Or` must have at least one arm".to_string(),
));
}
let parts: Result<Vec<String>, _> =
arms.iter().map(|arm| emit_inner(arm, false)).collect();
let joined = parts?.join(" or ");
if top_level {
Ok(joined)
} else {
Ok(format!("({joined})"))
}
}
ManchesterExpr::Not(inner) => {
let s = emit_inner(inner, false)?;
Ok(format!("not {s}"))
}
ManchesterExpr::Some { property, filler } => {
validate_property(property)?;
let filler_str = emit_inner(filler, false)?;
Ok(format!("{property} some {filler_str}"))
}
ManchesterExpr::Only { property, filler } => {
validate_property(property)?;
let filler_str = emit_inner(filler, false)?;
Ok(format!("{property} only {filler_str}"))
}
ManchesterExpr::Min {
property,
cardinality,
filler,
} => {
validate_property(property)?;
let filler_str = emit_optional_filler(filler)?;
Ok(format!("{property} min {cardinality}{filler_str}"))
}
ManchesterExpr::Max {
property,
cardinality,
filler,
} => {
validate_property(property)?;
let filler_str = emit_optional_filler(filler)?;
Ok(format!("{property} max {cardinality}{filler_str}"))
}
ManchesterExpr::Exactly {
property,
cardinality,
filler,
} => {
validate_property(property)?;
let filler_str = emit_optional_filler(filler)?;
Ok(format!("{property} exactly {cardinality}{filler_str}"))
}
ManchesterExpr::HasValue {
property,
individual,
} => {
validate_property(property)?;
if individual.is_empty() {
return Err(ManchesterError::EmitError(
"HasValue individual must not be empty".to_string(),
));
}
Ok(format!("{property} value {individual}"))
}
ManchesterExpr::OneOf(individuals) => {
if individuals.is_empty() {
return Err(ManchesterError::EmitError(
"`OneOf` must contain at least one individual".to_string(),
));
}
let joined = individuals.join(" ");
Ok(format!("{{{joined}}}"))
}
}
}
fn emit_optional_filler(filler: &Option<Box<ManchesterExpr>>) -> Result<String, ManchesterError> {
match filler {
None => Ok(String::new()),
Some(f) => {
let s = emit_inner(f, false)?;
Ok(format!(" {s}"))
}
}
}
fn validate_property(property: &str) -> Result<(), ManchesterError> {
if property.is_empty() {
return Err(ManchesterError::EmitError(
"property name must not be empty".to_string(),
));
}
Ok(())
}