shex_compact/
shapemap_compact_printer.rs

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/// Struct that can be used to pretty print Shapemaps
9///
10#[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 {}