1use crate::{keyword, pp_label, pp_object_value};
2use colored::*;
3use prefixmap::PrefixMap;
4use pretty::{Arena, DocAllocator, DocBuilder};
5use shapemap::{query_shape_map::QueryShapeMap, Association, NodeSelector, ShapeSelector};
6use std::marker::PhantomData;
7
8#[derive(Debug, Clone)]
11pub struct ShapemapFormatter {
12 keyword_color: Option<Color>,
13 qualify_prefix_color: Option<Color>,
14 qualify_semicolon_color: Option<Color>,
15 qualify_localname_color: Option<Color>,
16}
17
18impl ShapemapFormatter {
19 pub fn with_keyword_color(mut self, color: Option<Color>) -> Self {
20 self.keyword_color = color;
21 self
22 }
23 pub fn with_qualify_prefix_color(mut self, color: Option<Color>) -> Self {
24 self.qualify_prefix_color = color;
25 self
26 }
27 pub fn with_semicolon_prefix_color(mut self, color: Option<Color>) -> Self {
28 self.qualify_semicolon_color = color;
29 self
30 }
31
32 pub fn with_qualify_localname_color(mut self, color: Option<Color>) -> Self {
33 self.qualify_localname_color = color;
34 self
35 }
36
37 pub fn without_colors(mut self) -> Self {
38 self.keyword_color = None;
39 self.qualify_localname_color = None;
40 self.qualify_prefix_color = None;
41 self.qualify_semicolon_color = None;
42 self
43 }
44
45 pub fn format_shapemap(&self, shapemap: &QueryShapeMap) -> String {
46 let arena = Arena::<()>::new();
47 let mut printer = ShapemapCompactPrinter::new(shapemap, &arena);
48 printer = printer.with_keyword_color(self.keyword_color);
49 printer = printer.with_qualify_localname_color(self.qualify_localname_color);
50 printer = printer.with_qualify_prefix_color(self.qualify_prefix_color);
51 printer = printer.with_qualify_semicolon_color(self.qualify_semicolon_color);
52 printer.pretty_print()
53 }
54
55 pub fn write_shapemap<W: std::io::Write>(
56 &self,
57 shapemap: &QueryShapeMap,
58 writer: &mut W,
59 ) -> Result<(), std::io::Error> {
60 let arena = Arena::<()>::new();
61 let mut printer = ShapemapCompactPrinter::new(shapemap, &arena);
62 printer = printer.with_keyword_color(self.keyword_color);
63 printer = printer.with_qualify_localname_color(self.qualify_localname_color);
64 printer = printer.with_qualify_prefix_color(self.qualify_prefix_color);
65 printer = printer.with_qualify_semicolon_color(self.qualify_semicolon_color);
66 printer.pretty_print_write(writer)
67 }
68}
69
70impl Default for ShapemapFormatter {
71 fn default() -> Self {
72 Self {
73 keyword_color: DEFAULT_KEYWORD_COLOR,
74 qualify_prefix_color: DEFAULT_QUALIFY_ALIAS_COLOR,
75 qualify_semicolon_color: DEFAULT_QUALIFY_SEMICOLON_COLOR,
76 qualify_localname_color: DEFAULT_QUALIFY_LOCALNAME_COLOR,
77 }
78 }
79}
80
81struct ShapemapCompactPrinter<'a, A>
82where
83 A: Clone,
84{
85 width: usize,
86 keyword_color: Option<Color>,
87 shapemap: &'a QueryShapeMap,
88 doc: &'a Arena<'a, A>,
89 marker: PhantomData<A>,
90 nodes_prefixmap: PrefixMap,
91 shapes_prefixmap: PrefixMap,
92}
93
94const DEFAULT_WIDTH: usize = 100;
95const DEFAULT_QUALIFY_ALIAS_COLOR: Option<Color> = Some(Color::Blue);
96const DEFAULT_QUALIFY_SEMICOLON_COLOR: Option<Color> = Some(Color::BrightGreen);
97const DEFAULT_QUALIFY_LOCALNAME_COLOR: Option<Color> = Some(Color::Black);
98const DEFAULT_KEYWORD_COLOR: Option<Color> = Some(Color::BrightBlue);
99
100impl<'a, A> ShapemapCompactPrinter<'a, A>
101where
102 A: Clone,
103{
104 pub fn new(
105 shapemap: &'a QueryShapeMap,
106 doc: &'a Arena<'a, A>,
107 ) -> ShapemapCompactPrinter<'a, A> {
108 ShapemapCompactPrinter {
109 width: DEFAULT_WIDTH,
110 keyword_color: DEFAULT_KEYWORD_COLOR,
111 shapemap,
112 doc,
113 marker: PhantomData,
114 nodes_prefixmap: shapemap
115 .nodes_prefixmap()
116 .with_qualify_localname_color(DEFAULT_QUALIFY_LOCALNAME_COLOR)
117 .with_qualify_prefix_color(DEFAULT_QUALIFY_ALIAS_COLOR)
118 .with_qualify_semicolon_color(DEFAULT_QUALIFY_SEMICOLON_COLOR),
119 shapes_prefixmap: shapemap
120 .shapes_prefixmap()
121 .with_qualify_localname_color(DEFAULT_QUALIFY_LOCALNAME_COLOR)
122 .with_qualify_prefix_color(DEFAULT_QUALIFY_ALIAS_COLOR)
123 .with_qualify_semicolon_color(DEFAULT_QUALIFY_SEMICOLON_COLOR),
124 }
125 }
126
127 pub fn with_keyword_color(mut self, color: Option<Color>) -> Self {
128 self.keyword_color = color;
129 self
130 }
131
132 pub fn with_qualify_prefix_color(mut self, color: Option<Color>) -> Self {
133 self.nodes_prefixmap = self.nodes_prefixmap.with_qualify_prefix_color(color);
134 self.shapes_prefixmap = self.shapes_prefixmap.with_qualify_prefix_color(color);
135 self
136 }
137
138 pub fn with_qualify_semicolon_color(mut self, color: Option<Color>) -> Self {
139 self.nodes_prefixmap = self.nodes_prefixmap.with_qualify_semicolon_color(color);
140 self.shapes_prefixmap = self.shapes_prefixmap.with_qualify_semicolon_color(color);
141 self
142 }
143
144 pub fn with_qualify_localname_color(mut self, color: Option<Color>) -> Self {
145 self.nodes_prefixmap = self.nodes_prefixmap.with_qualify_localname_color(color);
146 self.shapes_prefixmap = self.shapes_prefixmap.with_qualify_localname_color(color);
147 self
148 }
149
150 pub fn pretty_print_write<W: std::io::Write>(
151 &self,
152 writer: &mut W,
153 ) -> Result<(), std::io::Error> {
154 let doc = self.pp_shapemap();
155 doc.render(self.width, writer)
156 }
157
158 pub fn pretty_print(&self) -> String {
159 let doc = self.pp_shapemap();
160 doc.pretty(self.width).to_string()
161 }
162
163 fn pp_shapemap(&self) -> DocBuilder<'a, Arena<'a, A>, A> {
164 let mut docs = Vec::new();
165 for a in self.shapemap.iter() {
166 docs.push(self.pp_association(a))
167 }
168 self.doc.intersperse(docs, self.doc.hardline())
169 }
170
171 fn pp_association(&self, assoc: &Association) -> DocBuilder<'a, Arena<'a, A>, A> {
172 self.pp_node_selector(&assoc.node_selector)
173 .append(self.doc.text("@"))
174 .append(self.pp_shape_selector(&assoc.shape_selector))
175 }
176
177 fn pp_node_selector(&self, ns: &NodeSelector) -> DocBuilder<'a, Arena<'a, A>, A> {
178 match ns {
179 NodeSelector::Node(v) => pp_object_value(v, self.doc, &self.nodes_prefixmap),
180 NodeSelector::TriplePattern { .. } => todo!(),
181 NodeSelector::TriplePatternPath { .. } => todo!(),
182 NodeSelector::Sparql { .. } => todo!(),
183 NodeSelector::Generic { .. } => todo!(),
184 }
185 }
186
187 fn pp_shape_selector(&self, s: &ShapeSelector) -> DocBuilder<'a, Arena<'a, A>, A> {
188 match s {
189 ShapeSelector::Label(label) => {
190 pp_label(label, self.doc, &self.shapes_prefixmap, self.keyword_color)
191 }
192 ShapeSelector::Start => keyword("START", self.doc, self.keyword_color),
193 }
194 }
195}
196
197#[cfg(test)]
198mod tests {}