1use std::fmt::{Display, Formatter};
24
25use amplify::confinement::{SmallVec, TinyVec};
26use strict_encoding::Ident;
27
28pub trait Predicate: Clone + Eq {
29 type Attr: Attribute;
30}
31
32pub trait Expression: Clone + Eq + Display {}
33
34pub trait Attribute: Clone + Eq {
35 type Expression: Expression;
36
37 fn is_named(&self) -> bool { self.name().is_some() }
38 fn name(&self) -> Option<Ident>;
39 fn value(&self) -> AttrVal<Self::Expression>;
40}
41
42#[derive(Clone, Eq, PartialEq, Debug, Display)]
43#[display(inner)]
44pub enum AttrVal<E: Expression> {
45 Ident(Ident),
46 Expr(E),
47}
48
49#[derive(Clone, Eq, PartialEq, Debug)]
50pub struct TExpr<P: Predicate> {
51 pub subject: Ident,
52 pub predicate: P,
53 pub attributes: SmallVec<P::Attr>,
54 pub content: TinyVec<Box<TExpr<P>>>,
55 pub comment: Option<String>,
56}
57
58impl<P: Predicate> TExpr<P> {
59 pub fn display(&self) -> TExprDisplay<'_, P>
60 where P: Display {
61 TExprDisplay {
62 expr: self,
63 indent: 0,
64 tab: s!(" "),
65 }
66 }
67}
68
69pub struct TExprDisplay<'expr, P: Predicate>
70where P: Display
71{
72 expr: &'expr TExpr<P>,
73 indent: usize,
74 tab: String,
75}
76
77impl<'expr, P: Predicate> TExprDisplay<'expr, P>
78where P: Display
79{
80 pub fn indented(parent: &Self, expr: &'expr TExpr<P>) -> TExprDisplay<'expr, P> {
81 TExprDisplay {
82 expr,
83 indent: parent.indent + 1,
84 tab: parent.tab.clone(),
85 }
86 }
87}
88
89impl<'expr, P: Predicate> Display for TExprDisplay<'expr, P>
90where P: Display
91{
92 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93 const MAX_LINE_VARS: usize = 8;
94
95 let expr = self.expr;
96 let attrs = &expr.attributes;
97
98 let indent = self.tab.repeat(self.indent);
99 write!(f, "{indent}{} {}", expr.predicate, expr.subject)?;
100
101 if !attrs.is_empty() {
102 f.write_str(": ")?;
103 }
104
105 let mut iter = expr.attributes.iter().enumerate().peekable();
106 while let Some((pos, attr)) = iter.next() {
107 if let Some(name) = attr.name() {
108 write!(f, "{name} ")?;
109 }
110 write!(f, "{}", attr.value())?;
111
112 if iter.peek().is_some() {
113 f.write_str(",")?;
114 }
115 if pos > 0 && pos % MAX_LINE_VARS == 0 {
116 write!(f, "\n{indent}{}", self.tab)?;
117 } else {
118 f.write_str(" ")?;
119 }
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}
134
135#[cfg(test)]
136mod tests {
137 use std::str::FromStr;
138 use super::*;
139
140 #[derive(Copy, Clone, Eq, PartialEq, Debug, Display)]
141 #[display("predicate")]
142 struct Test;
143 impl Predicate for Test {
144 type Attr = TestAttr;
145 }
146
147 #[derive(Copy, Clone, Eq, PartialEq, Debug, Display)]
148 enum TestAttr {
149 #[display("attr1")]
150 TestAttr1,
151 #[display("attr2")]
152 TestAttr2,
153 }
154 impl Attribute for TestAttr {
155 type Expression = String;
156
157 fn name(&self) -> Option<Ident> {
158 Some(Ident::from_str(&self.to_string()).unwrap())
159 }
160
161 fn value(&self) -> AttrVal<Self::Expression> {
162 AttrVal::Expr("value".to_string())
163 }
164 }
165
166 impl Expression for String {}
167
168 #[test]
169 fn display() {
170 let expr = TExpr {
171 subject: strict_encoding::ident!("test"),
172 predicate: Test,
173 attributes: small_vec![ TestAttr::TestAttr1, TestAttr::TestAttr2 ],
174 content: Default::default(),
175 comment: None,
176 };
177 assert_eq!(expr.display().to_string(), "predicate test: attr1 value, attr2 value \n");
178 }
179}