1use std::fmt::{Display, Formatter};
23
24use amplify::confinement::{SmallVec, TinyVec};
25use strict_encoding::Ident;
26
27pub trait Predicate: Clone + Eq {
28 type Attr: Attribute;
29}
30
31pub trait Expression: Clone + Eq + Display {}
32
33pub trait Attribute: Clone + Eq {
34 type Expression: Expression;
35
36 fn is_named(&self) -> bool { self.name().is_some() }
37 fn name(&self) -> Option<Ident>;
38 fn value(&self) -> AttrVal<Self::Expression>;
39}
40
41#[derive(Clone, Eq, PartialEq, Debug, Display)]
42#[display(inner)]
43pub enum AttrVal<E: Expression> {
44 Ident(Ident),
45 Expr(E),
46}
47
48#[derive(Clone, Eq, PartialEq, Debug)]
49pub struct TExpr<P: Predicate> {
50 pub subject: Ident,
51 pub predicate: P,
52 pub attributes: SmallVec<P::Attr>,
53 pub content: TinyVec<Box<TExpr<P>>>,
54 pub comment: Option<String>,
55}
56
57impl<P: Predicate> TExpr<P> {
58 pub fn display(&self) -> TExprDisplay<P>
59 where P: Display {
60 TExprDisplay {
61 expr: self,
62 indent: 0,
63 tab: s!(" "),
64 }
65 }
66}
67
68pub struct TExprDisplay<'expr, P: Predicate>
69where P: Display
70{
71 expr: &'expr TExpr<P>,
72 indent: usize,
73 tab: String,
74}
75
76impl<'expr, P: Predicate> TExprDisplay<'expr, P>
77where P: Display
78{
79 pub fn indented(parent: &Self, expr: &'expr TExpr<P>) -> TExprDisplay<'expr, P> {
80 TExprDisplay {
81 expr,
82 indent: parent.indent + 1,
83 tab: parent.tab.clone(),
84 }
85 }
86}
87
88impl<'expr, P: Predicate> Display for TExprDisplay<'expr, P>
89where P: Display
90{
91 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
92 const MAX_LINE_VARS: usize = 8;
93
94 let expr = self.expr;
95 let attrs = &expr.attributes;
96
97 let indent = self.tab.repeat(self.indent);
98 write!(f, "{indent}{} {}", expr.predicate, expr.subject)?;
99
100 if attrs.len() > MAX_LINE_VARS {
101 write!(f, " {{\n{indent}{}", self.tab)?;
102 } else if !attrs.is_empty() {
103 f.write_str(", ")?;
104 }
105 for (pos, attr) in expr.attributes.iter().enumerate() {
106 if pos == 1 || (pos > 0 && pos % MAX_LINE_VARS != 1) {
107 f.write_str(", ")?;
108 }
109 if let Some(name) = attr.name() {
110 write!(f, "{name} ")?;
111 }
112 write!(f, "{}", attr.value())?;
113
114 if pos > 0 && pos % MAX_LINE_VARS == 0 {
115 write!(f, "\n{indent}{}", self.tab)?;
116 }
117 }
118 if attrs.len() > MAX_LINE_VARS {
119 write!(f, "\n{indent}}}")?;
120 }
121
122 if let Some(comment) = &expr.comment {
123 write!(f, " -- {comment}")?;
124 }
125 writeln!(f)?;
126
127 for expr in &expr.content {
128 let display = TExprDisplay::indented(self, expr.as_ref());
129 Display::fmt(&display, f)?;
130 }
131 Ok(())
132 }
133}