Skip to main content

jsonld_core/
print.rs

1//! Pretty-printing of expanded documents through a vocabulary.
2//!
3//! Printing is infallible by design: every identifier is expected to resolve
4//! in the vocabulary the document was built against. Passing a different
5//! vocabulary is a caller bug; identifiers that fail to resolve are rendered
6//! as the literal string `"<unresolved iri>"` rather than panicking.
7pub use jstrict::print::{Options, Print, Printed};
8use jstrict::print::{
9    PrecomputeSize,
10    PrecomputeSizeWithContext,
11    PrintWithContext,
12    PrintWithSize,
13    PrintWithSizeAndContext,
14    Size,
15    pre_compute_array_size,
16    pre_compute_object_size,
17    print_array,
18    print_object,
19    printed_string_size,
20    string_literal,
21};
22
23use crate::{ExpandedDocument, Id, Indexed, Object, object};
24use contextual::WithContext;
25use rdfx::vocabulary::{IriVocabulary, Vocabulary};
26
27/// Values printable through a vocabulary, given the size of what surrounds
28/// them.
29pub trait PrintWithSizeAndVocabulary<V> {
30    /// Prints this value through the given vocabulary and layout options.
31    ///
32    /// # Errors
33    ///
34    /// Returns an error when the underlying formatter does.
35    fn fmt_with_size_and(
36        &self,
37        vocabulary: &V,
38        f: &mut std::fmt::Formatter,
39        options: &Options,
40        indent: usize,
41        sizes: &[Size],
42        index: &mut usize,
43    ) -> std::fmt::Result;
44}
45
46impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for ExpandedDocument<T, B> {
47    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
48        pre_compute_array_size(self.objects().iter().map(|o| o.with(vocabulary)), options, sizes)
49    }
50}
51
52impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithContext<N> for ExpandedDocument<T, B> {
53    fn contextual_fmt_with(&self, vocabulary: &N, f: &mut std::fmt::Formatter, options: &Options, indent: usize) -> std::fmt::Result {
54        let mut sizes = Vec::with_capacity(self.count(|i| i.is_json_array() || i.is_json_object()));
55        self.contextual_pre_compute_size(vocabulary, options, &mut sizes);
56        let mut index = 0;
57        self.contextual_fmt_with_size(vocabulary, f, options, indent, &sizes, &mut index)
58    }
59}
60
61impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithContext<N> for object::Node<T, B> {
62    fn contextual_fmt_with(&self, vocabulary: &N, f: &mut std::fmt::Formatter, options: &Options, indent: usize) -> std::fmt::Result {
63        let mut sizes = Vec::with_capacity(self.count(|i| i.is_json_array() || i.is_json_object()));
64        self.contextual_pre_compute_size(vocabulary, options, &mut sizes);
65        let mut index = 0;
66        self.contextual_fmt_with_size(vocabulary, f, options, indent, &sizes, &mut index)
67    }
68}
69
70impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for ExpandedDocument<T, B> {
71    fn contextual_fmt_with_size(
72        &self,
73        vocabulary: &N,
74        f: &mut std::fmt::Formatter,
75        options: &Options,
76        indent: usize,
77        sizes: &[Size],
78        index: &mut usize,
79    ) -> std::fmt::Result {
80        print_array(self.objects().iter().map(|o| o.with(vocabulary)), f, options, indent, sizes, index)
81    }
82}
83
84impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for Id<T, B> {
85    fn contextual_pre_compute_size(&self, vocabulary: &N, _options: &Options, _sizes: &mut Vec<Size>) -> Size {
86        Size::Width(printed_string_size(self.with(vocabulary).as_str()))
87    }
88}
89
90impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithContext<N> for Id<T, B> {
91    fn contextual_fmt_with(&self, vocabulary: &N, f: &mut std::fmt::Formatter, _options: &Options, _indent: usize) -> std::fmt::Result {
92        string_literal(self.with(vocabulary).as_str(), f)
93    }
94}
95
96impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for Id<T, B> {
97    fn contextual_fmt_with_size(
98        &self,
99        vocabulary: &N,
100        f: &mut std::fmt::Formatter,
101        _options: &Options,
102        _indent: usize,
103        _sizes: &[Size],
104        _index: &mut usize,
105    ) -> std::fmt::Result {
106        string_literal(self.with(vocabulary).as_str(), f)
107    }
108}
109
110impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for Indexed<Object<T, B>> {
111    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
112        pre_compute_object_size(
113            self.entries().map(|e| {
114                let (k, v) = e.into_key_value();
115                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
116            }),
117            options,
118            sizes,
119        )
120    }
121}
122
123impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for Indexed<Object<T, B>> {
124    fn contextual_fmt_with_size(
125        &self,
126        vocabulary: &N,
127        f: &mut std::fmt::Formatter,
128        options: &Options,
129        indent: usize,
130        sizes: &[Size],
131        index: &mut usize,
132    ) -> std::fmt::Result {
133        print_object(
134            self.entries().map(|e| {
135                let (k, v) = e.into_key_value();
136                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
137            }),
138            f,
139            options,
140            indent,
141            sizes,
142            index,
143        )
144    }
145}
146
147impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::IndexedEntryValueRef<'_, T, B> {
148    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
149        match self {
150            object::IndexedEntryValueRef::Index(s) => Size::Width(printed_string_size(s)),
151            object::IndexedEntryValueRef::Object(e) => e.into_with(vocabulary).pre_compute_size(options, sizes),
152        }
153    }
154}
155
156impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::IndexedEntryValueRef<'_, T, B> {
157    fn contextual_fmt_with_size(
158        &self,
159        vocabulary: &N,
160        f: &mut std::fmt::Formatter,
161        options: &Options,
162        indent: usize,
163        sizes: &[Size],
164        index: &mut usize,
165    ) -> std::fmt::Result {
166        match self {
167            object::IndexedEntryValueRef::Index(s) => string_literal(s, f),
168            object::IndexedEntryValueRef::Object(e) => e.into_with(vocabulary).fmt_with_size(f, options, indent, sizes, index),
169        }
170    }
171}
172
173impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::EntryValueRef<'_, T, B> {
174    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
175        match self {
176            object::EntryValueRef::Value(v) => v.into_with(vocabulary).pre_compute_size(options, sizes),
177            object::EntryValueRef::List(l) => pre_compute_array_size(l.iter().map(|i| i.with(vocabulary)), options, sizes),
178            object::EntryValueRef::Node(n) => n.into_with(vocabulary).pre_compute_size(options, sizes),
179        }
180    }
181}
182
183impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::EntryValueRef<'_, T, B> {
184    fn contextual_fmt_with_size(
185        &self,
186        vocabulary: &N,
187        f: &mut std::fmt::Formatter,
188        options: &Options,
189        indent: usize,
190        sizes: &[Size],
191        index: &mut usize,
192    ) -> std::fmt::Result {
193        match self {
194            object::EntryValueRef::Value(v) => v.into_with(vocabulary).fmt_with_size(f, options, indent, sizes, index),
195            object::EntryValueRef::List(l) => print_array(l.iter().map(|i| i.with(vocabulary)), f, options, indent, sizes, index),
196            object::EntryValueRef::Node(n) => n.into_with(vocabulary).fmt_with_size(f, options, indent, sizes, index),
197        }
198    }
199}
200
201impl<T, N: IriVocabulary<Iri = T>> PrecomputeSizeWithContext<N> for object::value::EntryRef<'_, T> {
202    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
203        match self {
204            object::value::EntryRef::Value(v) => v.pre_compute_size(options, sizes),
205            object::value::EntryRef::Type(t) => t.into_with(vocabulary).pre_compute_size(options, sizes),
206            object::value::EntryRef::Language(l) => l.pre_compute_size(options, sizes),
207            object::value::EntryRef::Direction(d) => d.pre_compute_size(options, sizes),
208        }
209    }
210}
211
212impl<T, N: IriVocabulary<Iri = T>> PrintWithSizeAndContext<N> for object::value::EntryRef<'_, T> {
213    fn contextual_fmt_with_size(
214        &self,
215        vocabulary: &N,
216        f: &mut std::fmt::Formatter,
217        options: &Options,
218        indent: usize,
219        sizes: &[Size],
220        index: &mut usize,
221    ) -> std::fmt::Result {
222        match self {
223            object::value::EntryRef::Value(v) => v.fmt_with_size(f, options, indent, sizes, index),
224            object::value::EntryRef::Type(t) => t.into_with(vocabulary).fmt_with(f, options, indent),
225            object::value::EntryRef::Language(l) => l.fmt_with(f, options, indent),
226            object::value::EntryRef::Direction(d) => d.fmt_with(f, options, indent),
227        }
228    }
229}
230
231impl<T, N: IriVocabulary<Iri = T>> PrecomputeSizeWithContext<N> for object::value::TypeRef<'_, T> {
232    fn contextual_pre_compute_size(&self, vocabulary: &N, _options: &Options, _sizes: &mut Vec<Size>) -> Size {
233        match self {
234            object::value::TypeRef::Id(id) => Size::Width(printed_string_size(vocabulary.iri(id).map_or("<unresolved iri>", iri_rs::Iri::into_inner))),
235            object::value::TypeRef::Json => Size::Width(printed_string_size("@json")),
236        }
237    }
238}
239
240impl<T, N: IriVocabulary<Iri = T>> PrintWithContext<N> for object::value::TypeRef<'_, T> {
241    fn contextual_fmt_with(&self, vocabulary: &N, f: &mut std::fmt::Formatter, _options: &Options, _indent: usize) -> std::fmt::Result {
242        match self {
243            object::value::TypeRef::Id(id) => string_literal(vocabulary.iri(id).map_or("<unresolved iri>", iri_rs::Iri::into_inner), f),
244            object::value::TypeRef::Json => string_literal("@json", f),
245        }
246    }
247}
248
249impl PrecomputeSize for object::value::ValueEntryRef<'_> {
250    fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
251        match self {
252            Self::Literal(l) => l.pre_compute_size(options, sizes),
253            Self::LangString(s) => Size::Width(printed_string_size(s)),
254            Self::Json(j) => j.pre_compute_size(options, sizes),
255        }
256    }
257}
258
259impl PrintWithSize for object::value::ValueEntryRef<'_> {
260    fn fmt_with_size(&self, f: &mut std::fmt::Formatter, options: &Options, indent: usize, sizes: &[Size], index: &mut usize) -> std::fmt::Result {
261        match self {
262            Self::Literal(l) => l.fmt_with(f, options, indent),
263            Self::LangString(s) => string_literal(s, f),
264            Self::Json(j) => j.fmt_with_size(f, options, indent, sizes, index),
265        }
266    }
267}
268
269impl PrecomputeSize for object::value::Literal {
270    fn pre_compute_size(&self, options: &Options, sizes: &mut Vec<Size>) -> Size {
271        match self {
272            Self::Null => Size::Width(4),
273            Self::Boolean(b) => b.pre_compute_size(options, sizes),
274            Self::Number(n) => Size::Width(n.as_str().len()),
275            Self::String(s) => Size::Width(printed_string_size(s)),
276        }
277    }
278}
279
280impl Print for object::value::Literal {
281    fn fmt_with(&self, f: &mut std::fmt::Formatter, options: &Options, indent: usize) -> std::fmt::Result {
282        match self {
283            Self::Null => write!(f, "null"),
284            Self::Boolean(b) => b.fmt_with(f, options, indent),
285            Self::Number(n) => std::fmt::Display::fmt(n, f),
286            Self::String(s) => string_literal(s, f),
287        }
288    }
289}
290
291impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::Node<T, B> {
292    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
293        pre_compute_object_size(
294            self.entries().map(|e| {
295                let (k, v) = e.into_key_value();
296                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
297            }),
298            options,
299            sizes,
300        )
301    }
302}
303
304impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::Node<T, B> {
305    fn contextual_fmt_with_size(
306        &self,
307        vocabulary: &N,
308        f: &mut std::fmt::Formatter,
309        options: &Options,
310        indent: usize,
311        sizes: &[Size],
312        index: &mut usize,
313    ) -> std::fmt::Result {
314        print_object(
315            self.entries().map(|e| {
316                let (k, v) = e.into_key_value();
317                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
318            }),
319            f,
320            options,
321            indent,
322            sizes,
323            index,
324        )
325    }
326}
327
328impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for Indexed<object::Node<T, B>> {
329    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
330        pre_compute_object_size(
331            self.entries().map(|e| {
332                let (k, v) = e.into_key_value();
333                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
334            }),
335            options,
336            sizes,
337        )
338    }
339}
340
341impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for Indexed<object::Node<T, B>> {
342    fn contextual_fmt_with_size(
343        &self,
344        vocabulary: &N,
345        f: &mut std::fmt::Formatter,
346        options: &Options,
347        indent: usize,
348        sizes: &[Size],
349        index: &mut usize,
350    ) -> std::fmt::Result {
351        print_object(
352            self.entries().map(|e| {
353                let (k, v) = e.into_key_value();
354                (k.into_with(vocabulary).into_str(), v.into_with(vocabulary))
355            }),
356            f,
357            options,
358            indent,
359            sizes,
360            index,
361        )
362    }
363}
364
365impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::node::IndexedEntryValueRef<'_, T, B> {
366    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
367        match self {
368            object::node::IndexedEntryValueRef::Index(s) => Size::Width(printed_string_size(s)),
369            object::node::IndexedEntryValueRef::Node(e) => e.into_with(vocabulary).pre_compute_size(options, sizes),
370        }
371    }
372}
373
374impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::node::IndexedEntryValueRef<'_, T, B> {
375    fn contextual_fmt_with_size(
376        &self,
377        vocabulary: &N,
378        f: &mut std::fmt::Formatter,
379        options: &Options,
380        indent: usize,
381        sizes: &[Size],
382        index: &mut usize,
383    ) -> std::fmt::Result {
384        match self {
385            object::node::IndexedEntryValueRef::Index(s) => string_literal(s, f),
386            object::node::IndexedEntryValueRef::Node(e) => e.into_with(vocabulary).fmt_with_size(f, options, indent, sizes, index),
387        }
388    }
389}
390
391impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::node::EntryValueRef<'_, T, B> {
392    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
393        match *self {
394            object::node::EntryValueRef::Id(v) => v.contextual_pre_compute_size(vocabulary, options, sizes),
395            object::node::EntryValueRef::Type(v) => pre_compute_array_size(v.iter().map(|i| i.with(vocabulary)), options, sizes),
396            object::node::EntryValueRef::Graph(v) => pre_compute_array_size(v.iter().map(|i| i.with(vocabulary)), options, sizes),
397            object::node::EntryValueRef::Included(v) => pre_compute_array_size(v.iter().map(|i| i.with(vocabulary)), options, sizes),
398            object::node::EntryValueRef::Reverse(v) => v.contextual_pre_compute_size(vocabulary, options, sizes),
399            object::node::EntryValueRef::Property(v) => pre_compute_array_size(v.iter().map(|i| i.with(vocabulary)), options, sizes),
400        }
401    }
402}
403
404impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::node::EntryValueRef<'_, T, B> {
405    fn contextual_fmt_with_size(
406        &self,
407        vocabulary: &N,
408        f: &mut std::fmt::Formatter,
409        options: &Options,
410        indent: usize,
411        sizes: &[Size],
412        index: &mut usize,
413    ) -> std::fmt::Result {
414        match self {
415            object::node::EntryValueRef::Id(v) => v.with(vocabulary).fmt_with(f, options, indent),
416            object::node::EntryValueRef::Type(v) => print_array(v.iter().map(|i| i.with(vocabulary)), f, options, indent, sizes, index),
417            object::node::EntryValueRef::Graph(v) => print_array(v.iter().map(|i| i.with(vocabulary)), f, options, indent, sizes, index),
418            object::node::EntryValueRef::Included(v) => print_array(v.iter().map(|i| i.with(vocabulary)), f, options, indent, sizes, index),
419            object::node::EntryValueRef::Reverse(v) => v.contextual_fmt_with_size(vocabulary, f, options, indent, sizes, index),
420            object::node::EntryValueRef::Property(v) => print_array(v.iter().map(|i| i.with(vocabulary)), f, options, indent, sizes, index),
421        }
422    }
423}
424
425impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrecomputeSizeWithContext<N> for object::node::ReverseProperties<T, B> {
426    fn contextual_pre_compute_size(&self, vocabulary: &N, options: &Options, sizes: &mut Vec<Size>) -> Size {
427        pre_compute_object_size(
428            self.iter().map(|(k, v)| (k.into_with(vocabulary).as_str(), v.into_with(vocabulary))),
429            options,
430            sizes,
431        )
432    }
433}
434
435impl<T, B, N: Vocabulary<Iri = T, BlankId = B>> PrintWithSizeAndContext<N> for object::node::ReverseProperties<T, B> {
436    fn contextual_fmt_with_size(
437        &self,
438        vocabulary: &N,
439        f: &mut std::fmt::Formatter,
440        options: &Options,
441        indent: usize,
442        sizes: &[Size],
443        index: &mut usize,
444    ) -> std::fmt::Result {
445        print_object(
446            self.iter().map(|(k, v)| (k.into_with(vocabulary).as_str(), v.into_with(vocabulary))),
447            f,
448            options,
449            indent,
450            sizes,
451            index,
452        )
453    }
454}