1use crate::edb::Fact;
15use crate::error::Result;
16use crate::idb::{query::Query, Rule};
17use crate::{Collection, Program, ProgramCore, QuerySet, Relation, RuleSet};
18
19pub trait ProgramVisitor {
24 fn start_program(&self, _program: &Program) -> Result<()> {
25 Ok(())
26 }
27 fn end_program(&self, _program: &Program) -> Result<()> {
28 Ok(())
29 }
30
31 fn relation_visitor(&self) -> Option<&dyn RelationVisitor> {
32 None
33 }
34
35 fn rule_visitor(&self) -> Option<&dyn RuleVisitor> {
36 None
37 }
38
39 fn query_visitor(&self) -> Option<&dyn QueryVisitor> {
40 None
41 }
42}
43
44pub trait ProgramWriter: ProgramVisitor {}
45
46pub trait RelationVisitor {
47 fn start_relation(&self, _relation: &Relation, _extensional: bool) -> Result<()> {
48 Ok(())
49 }
50 fn fact(&self, _fact: &Fact) -> Result<()> {
51 Ok(())
52 }
53 fn end_relation(&self, _relation: &Relation, _extensional: bool) -> Result<()> {
54 Ok(())
55 }
56}
57
58pub trait RuleVisitor {
59 fn start_rules(&self, _rules: &RuleSet) -> Result<()> {
60 Ok(())
61 }
62 fn rule(&self, _rule: &Rule) -> Result<()> {
63 Ok(())
64 }
65 fn end_rules(&self, _rules: &RuleSet) -> Result<()> {
66 Ok(())
67 }
68}
69
70pub trait QueryVisitor {
71 fn start_queries(&self, _queries: &QuerySet) -> Result<()> {
72 Ok(())
73 }
74 fn query(&self, _query: &Query) -> Result<()> {
75 Ok(())
76 }
77 fn end_queries(&self, _queries: &QuerySet) -> Result<()> {
78 Ok(())
79 }
80}
81
82pub fn write_program(program: &Program, visitor: &impl ProgramWriter) -> Result<()> {
87 visit_program(program, visitor)
88}
89
90pub fn visit_program(program: &Program, visitor: &impl ProgramVisitor) -> Result<()> {
91 visitor.start_program(program)?;
92
93 if let Some(relation_visitor) = visitor.relation_visitor() {
94 for edb in [true, false] {
95 let relations = if edb {
96 program.extensional()
97 } else {
98 program.intensional()
99 };
100 if !relations.is_empty() {
101 for relation in relations.iter() {
102 relation_visitor.start_relation(relation, edb)?;
103 for fact in relation.iter() {
104 relation_visitor.fact(fact)?;
105 }
106 relation_visitor.end_relation(relation, edb)?;
107 }
108 }
109 }
110 }
111
112 let rules = program.rules();
113 if !rules.is_empty() {
114 if let Some(rule_visitor) = visitor.rule_visitor() {
115 rule_visitor.start_rules(&rules)?;
116 for rule in rules.iter() {
117 rule_visitor.rule(rule)?;
118 }
119 rule_visitor.end_rules(&rules)?;
120 }
121 }
122
123 let queries = program.queries();
124 if !queries.is_empty() {
125 if let Some(query_visitor) = visitor.query_visitor() {
126 query_visitor.start_queries(queries)?;
127 for query in queries.iter() {
128 query_visitor.query(query)?;
129 }
130 query_visitor.end_queries(queries)?;
131 }
132 }
133
134 visitor.end_program(program)
135}
136
137mod latex;
142pub use latex::{make_latex_writer, LatexFormatter};
143
144mod native;
145pub use native::{make_native_writer, NativeFormatter};