Skip to main content

jsonld_expansion/
warning.rs

1use contextual::DisplayWithContext;
2use jsonld_context_processing::algorithm::MalformedIri;
3use langtag::InvalidLangTag;
4use rdfx::vocabulary::BlankIdVocabulary;
5use std::fmt;
6
7#[derive(Debug)]
8/// Warning raised while expanding a document.
9pub enum Warning<B> {
10    /// A term expanded to a string that is not a well-formed IRI. It becomes
11    /// an invalid identifier, which the [`Policy`](crate::Policy) in use
12    /// decides to keep, drop or reject.
13    MalformedIri(String),
14    /// A JSON object has an empty key, which no context can define as a term.
15    EmptyTerm,
16    /// A key expanded to a blank node identifier, making it a property with no
17    /// RDF equivalent unless generalized RDF is allowed.
18    BlankNodeIdProperty(B),
19    /// A language tag is not well-formed per BCP 47. It is kept in the expanded
20    /// document as it is; the warning carries the tag and the reason it was
21    /// rejected.
22    MalformedLanguageTag(String, InvalidLangTag<String>),
23    /// A warning raised while processing a scoped or local `@context`.
24    ContextProcessing(jsonld_context_processing::Warning),
25}
26
27impl<B> Warning<B> {
28    /// Rewrites the blank node identifier this warning carries, if any.
29    ///
30    /// [`Warning::BlankNodeIdProperty`] is the only variant holding one. Use
31    /// this when a warning crosses from one vocabulary to another, so that the
32    /// handler resolving the identifier for display looks it up in the
33    /// vocabulary that minted it rather than printing the wrong blank node.
34    pub fn map_blank_id<C>(self, map: impl FnOnce(B) -> C) -> Warning<C> {
35        match self {
36            Self::MalformedIri(s) => Warning::MalformedIri(s),
37            Self::EmptyTerm => Warning::EmptyTerm,
38            Self::BlankNodeIdProperty(b) => Warning::BlankNodeIdProperty(map(b)),
39            Self::MalformedLanguageTag(t, e) => Warning::MalformedLanguageTag(t, e),
40            Self::ContextProcessing(w) => Warning::ContextProcessing(w),
41        }
42    }
43}
44
45impl<B> From<jsonld_context_processing::Warning> for Warning<B> {
46    fn from(w: jsonld_context_processing::Warning) -> Self {
47        Self::ContextProcessing(w)
48    }
49}
50
51/// Forwards context-processing warnings into the expansion warning handler,
52/// wrapped as [`Warning::ContextProcessing`].
53///
54/// Scoped and local `@context`s are processed in the middle of expansion, and
55/// the context processing algorithm reports warnings of its own type. This
56/// adapter re-types them so they all reach the single handler given to
57/// [`Expand::expand_full`](crate::Expand::expand_full), rather than the
58/// [`Print`](jsonld_core::warning::Print) handler that context processing
59/// falls back to on its own.
60pub(crate) struct ContextWarnings<'a, W>(pub &'a mut W);
61
62impl<N, W> jsonld_core::warning::Handler<N, jsonld_context_processing::Warning> for ContextWarnings<'_, W>
63where
64    N: BlankIdVocabulary,
65    W: crate::WarningHandler<N>,
66{
67    fn handle(&mut self, vocabulary: &N, warning: jsonld_context_processing::Warning) {
68        self.0.handle(vocabulary, Warning::ContextProcessing(warning));
69    }
70}
71
72impl<B> From<MalformedIri> for Warning<B> {
73    fn from(MalformedIri(s): MalformedIri) -> Self {
74        Self::MalformedIri(s)
75    }
76}
77
78impl<B: fmt::Display> fmt::Display for Warning<B> {
79    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80        match self {
81            Self::MalformedIri(s) => write!(f, "malformed IRI `{s}`"),
82            Self::EmptyTerm => write!(f, "empty term"),
83            Self::BlankNodeIdProperty(b) => {
84                write!(f, "blank node identifier `{b}` used as property")
85            }
86            Self::MalformedLanguageTag(t, e) => write!(f, "invalid language tag `{t}`: {e}"),
87            Self::ContextProcessing(w) => w.fmt(f),
88        }
89    }
90}
91
92impl<B, N: BlankIdVocabulary<BlankId = B>> DisplayWithContext<N> for Warning<B> {
93    fn fmt_with(&self, vocabulary: &N, f: &mut fmt::Formatter) -> fmt::Result {
94        match self {
95            Self::MalformedIri(s) => write!(f, "malformed IRI `{s}`"),
96            Self::EmptyTerm => write!(f, "empty term"),
97            Self::BlankNodeIdProperty(b) => match vocabulary.blank_id(b) {
98                Some(s) => write!(f, "blank node identifier `{s}` used as property"),
99                None => f.write_str("blank node identifier `<unresolved blank>` used as property"),
100            },
101            Self::MalformedLanguageTag(t, e) => write!(f, "invalid language tag `{t}`: {e}"),
102            Self::ContextProcessing(w) => write!(f, "{w}"),
103        }
104    }
105}