use std::fmt::{Display, Formatter};
use amplify::confinement::{SmallVec, TinyVec};
use strict_encoding::Ident;
pub trait Predicate: Clone + Eq {
type Attr: Attribute;
}
pub trait Expression: Clone + Eq + Display {}
pub trait Attribute: Clone + Eq {
type Expression: Expression;
fn is_named(&self) -> bool { self.name().is_some() }
fn name(&self) -> Option<Ident>;
fn value(&self) -> AttrVal<Self::Expression>;
}
#[derive(Clone, Eq, PartialEq, Debug, Display)]
#[display(inner)]
pub enum AttrVal<E: Expression> {
Ident(Ident),
Expr(E),
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct TExpr<P: Predicate> {
pub subject: Ident,
pub predicate: P,
pub attributes: SmallVec<P::Attr>,
pub content: TinyVec<Box<TExpr<P>>>,
pub comment: Option<String>,
}
impl<P: Predicate> TExpr<P> {
pub fn display(&self) -> TExprDisplay<P>
where P: Display {
TExprDisplay {
expr: self,
indent: 0,
tab: s!(" "),
}
}
}
pub struct TExprDisplay<'expr, P: Predicate>
where P: Display
{
expr: &'expr TExpr<P>,
indent: usize,
tab: String,
}
impl<'expr, P: Predicate> TExprDisplay<'expr, P>
where P: Display
{
pub fn indented(parent: &Self, expr: &'expr TExpr<P>) -> TExprDisplay<'expr, P> {
TExprDisplay {
expr,
indent: parent.indent + 1,
tab: parent.tab.clone(),
}
}
}
impl<'expr, P: Predicate> Display for TExprDisplay<'expr, P>
where P: Display
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
const MAX_LINE_VARS: usize = 8;
let expr = self.expr;
let attrs = &expr.attributes;
let indent = self.tab.repeat(self.indent);
write!(f, "{indent}{} {}", expr.subject, expr.predicate)?;
if attrs.len() > MAX_LINE_VARS {
write!(f, " {{\n{indent}{}", self.tab)?;
} else if !attrs.is_empty() {
f.write_str(" ")?;
}
for (pos, attr) in expr.attributes.iter().enumerate() {
if pos == 1 || (pos > 0 && pos % MAX_LINE_VARS != 1) {
f.write_str(" ")?;
}
if let Some(name) = attr.name() {
write!(f, "{name}=")?;
}
write!(f, "{}", attr.value())?;
if pos > 0 && pos % MAX_LINE_VARS == 0 {
write!(f, "\n{indent}{}", self.tab)?;
}
}
if attrs.len() > MAX_LINE_VARS {
write!(f, "\n{indent}}}")?;
}
if let Some(comment) = &expr.comment {
write!(f, " -- {comment}")?;
}
writeln!(f)?;
for expr in &expr.content {
let display = TExprDisplay::indented(self, expr.as_ref());
Display::fmt(&display, f)?;
}
Ok(())
}
}