Skip to main content

rdf_parsers/
model.rs

1use std::{fmt::Display, ops::Range};
2
3use crate::Spanned;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum StringStyle {
7    DoubleLong,
8    Double,
9    SingleLong,
10    Single,
11}
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct Variable(pub String, pub usize);
14
15#[derive(Clone, Debug, PartialEq, Eq)]
16pub enum Literal {
17    RDF(RDFLiteral),
18    Boolean(bool),
19    Numeric(String),
20}
21
22impl Literal {
23    pub fn plain_string(&self) -> String {
24        match self {
25            Literal::RDF(s) => s.plain_string(),
26            Literal::Boolean(x) => x.to_string(),
27            Literal::Numeric(x) => x.clone(),
28        }
29    }
30}
31impl Display for Literal {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Literal::RDF(x) => x.fmt(f),
35            Literal::Boolean(x) => write!(f, "{}", x),
36            Literal::Numeric(x) => write!(f, "{}", x),
37        }
38    }
39}
40
41#[derive(Clone, Debug, PartialEq, Eq)]
42pub struct RDFLiteral {
43    pub value: String,
44    pub quote_style: StringStyle,
45    /// Language tag (without the leading `@`), spanned over the `@lang` token.
46    pub lang: Option<Spanned<String>>,
47    /// Datatype IRI, spanned over the `^^<iri>` datatype term.
48    pub ty: Option<Spanned<NamedNode>>,
49    // Span of tokens
50    pub idx: usize,
51    pub len: usize,
52}
53
54impl RDFLiteral {
55    pub fn plain_string(&self) -> String {
56        self.value.to_string()
57    }
58}
59
60impl Display for RDFLiteral {
61    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62        let quote = match self.quote_style {
63            StringStyle::DoubleLong => "\"\"\"",
64            StringStyle::Double => "\"",
65            StringStyle::SingleLong => "'''",
66            StringStyle::Single => "'",
67        };
68        match (&self.lang, &self.ty) {
69            (None, None) => write!(f, "{}{}{}", quote, self.value, quote),
70            (None, Some(t)) => write!(f, "{}{}{}^^{}", quote, self.value, quote, t.value()),
71            (Some(l), None) => write!(f, "{}{}{}@{}", quote, self.value, quote, l.value()),
72            (Some(l), Some(t)) => {
73                write!(
74                    f,
75                    "{}{}{}@{}^^{}",
76                    quote,
77                    self.value,
78                    quote,
79                    l.value(),
80                    t.value()
81                )
82            }
83        }
84    }
85}
86
87#[derive(Clone, Debug, PartialEq, Eq)]
88pub enum NamedNode {
89    Full(String, usize),
90    Prefixed {
91        prefix: String,
92        value: String,
93        idx: usize,
94        /// Pre-resolved absolute IRI, set by parsers (currently JSON-LD) that
95        /// expand IRIs against a context whose semantics are *not* equivalent to
96        /// plain prefix substitution (whole-term precedence, scoped contexts,
97        /// `@vocab`). When present, downstream consumers should use this value
98        /// instead of re-expanding `prefix`/`value` against the prefix map.
99        /// `None` for the syntactic parsers (Turtle/TriG/SPARQL/N3), which leave
100        /// expansion to the consumer.
101        computed: Option<String>,
102    },
103    A(usize),
104    Invalid,
105}
106
107impl Display for NamedNode {
108    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109        match self {
110            NamedNode::Full(x, _) => write!(f, "<{}>", x),
111            NamedNode::Prefixed { prefix, value, .. } => {
112                // A JSON-LD whole-term compact IRI is stored as `Prefixed(term, "")`
113                // where `term` already contains the ':'; render it verbatim rather
114                // than appending a spurious trailing colon.
115                if value.is_empty() && prefix.contains(':') {
116                    write!(f, "{}", prefix)
117                } else {
118                    write!(f, "{}:{}", prefix, value)
119                }
120            }
121            NamedNode::A(_) => write!(f, "a"),
122            NamedNode::Invalid => write!(f, "invalid"),
123        }
124    }
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
128pub enum BlankNode {
129    Named(String, usize),
130    Unnamed(Vec<Spanned<PO>>, usize, usize),
131    Invalid,
132}
133
134impl Display for BlankNode {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        match self {
137            BlankNode::Named(x, _) => write!(f, "_:{}", x),
138            BlankNode::Unnamed(pos, _, _) => {
139                if pos.len() == 0 {
140                    write!(f, "[ ]")
141                } else {
142                    write!(f, "[ ")?;
143
144                    for po in pos {
145                        write!(f, "{} ;", po.value())?;
146                    }
147
148                    write!(f, " ]")
149                }
150            }
151            BlankNode::Invalid => write!(f, "invalid"),
152        }
153    }
154}
155
156#[derive(Clone, Debug, PartialEq, Eq)]
157pub enum Term {
158    Literal(Literal),
159    BlankNode(BlankNode),
160    NamedNode(NamedNode),
161    Collection(Vec<Spanned<Term>>),
162    Variable(Variable),
163    Invalid,
164}
165
166impl Term {
167    pub fn named_node(&self) -> Option<&NamedNode> {
168        match self {
169            Term::NamedNode(nn) => Some(&nn),
170            _ => None,
171        }
172    }
173
174    pub fn is_subject(&self) -> bool {
175        match self {
176            Term::BlankNode(_) => true,
177            Term::Variable(_) => true,
178            Term::NamedNode(NamedNode::A(_)) => false,
179            Term::NamedNode(_) => true,
180            Term::Invalid => true,
181            Term::Collection(_) => true,
182            _ => false,
183        }
184    }
185    pub fn is_predicate(&self) -> bool {
186        match self {
187            Term::NamedNode(_) => true,
188            Term::Variable(_) => true,
189            Term::Invalid => true,
190            _ => false,
191        }
192    }
193
194    pub fn is_object(&self) -> bool {
195        match self {
196            Term::NamedNode(NamedNode::A(_)) => false,
197            Term::Variable(_) => true,
198            Term::Invalid => true,
199            Term::Collection(_) => true,
200            _ => true,
201        }
202    }
203    pub fn is_variable(&self) -> bool {
204        match self {
205            Term::Variable(_) => true,
206            Term::Invalid => true,
207            _ => false,
208        }
209    }
210    pub fn ty(&self) -> &'static str {
211        match self {
212            Term::Literal(_) => "literal",
213            Term::BlankNode(_) => "blank node",
214            Term::NamedNode(_) => "named node",
215            Term::Collection(_) => "collection",
216            Term::Invalid => "invalid",
217            Term::Variable(_) => "variable",
218        }
219    }
220}
221
222impl Display for Term {
223    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224        match self {
225            Term::Literal(l) => l.fmt(f),
226            Term::BlankNode(b) => b.fmt(f),
227            Term::NamedNode(n) => n.fmt(f),
228            Term::Collection(n) => {
229                write!(f, "( ")?;
230                for l in n {
231                    l.fmt(f)?;
232                }
233                write!(f, "  )")?;
234                Ok(())
235            }
236            Term::Invalid => write!(f, "invalid"),
237            Term::Variable(x) => write!(f, "?{}", x.0),
238        }
239    }
240}
241
242#[derive(Clone, Debug, PartialEq, Eq)]
243pub struct Triple {
244    pub subject: Spanned<Term>,
245    pub po: Vec<Spanned<PO>>,
246    pub graph: Option<Spanned<Term>>,
247}
248
249impl Display for Triple {
250    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251        if let Some(graph) = &self.graph {
252            write!(f, "GRAPH {} {{\n  ", graph.value())?;
253        }
254
255        write!(f, "{}", self.subject.value())?;
256
257        if !self.po.is_empty() {
258            write!(f, " {}", self.po[0].value())?;
259            for po in &self.po[1..] {
260                if self.graph.is_some() {
261                    write!(f, ";\n    {}", po.value())?;
262                } else {
263                    write!(f, ";\n  {}", po.value())?;
264                }
265            }
266        }
267
268        write!(f, " .\n")?;
269
270        if self.graph.is_some() {
271            write!(f, "}}\n")?;
272        }
273
274        Ok(())
275    }
276}
277
278#[derive(Clone, Debug, PartialEq, Eq)]
279pub struct PO {
280    pub predicate: Spanned<Term>,
281    pub object: Vec<Spanned<Term>>,
282}
283
284impl Display for PO {
285    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
286        if let Some(o) = self.object.first() {
287            write!(f, "{} {}", self.predicate.value(), o.value())?;
288        } else {
289            write!(f, "{} undefined", self.predicate.value())?;
290        }
291
292        if self.object.len() > 1 {
293            for po in &self.object[1..] {
294                write!(f, ", {}", po.value())?;
295            }
296        }
297
298        Ok(())
299    }
300}
301
302#[derive(Clone, Debug, PartialEq, Eq)]
303pub struct Base(pub Range<usize>, pub Spanned<NamedNode>);
304impl Display for Base {
305    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
306        write!(f, "@base {} .", self.1.value())
307    }
308}
309
310#[derive(Clone, Debug, PartialEq, Eq)]
311pub struct TurtlePrefix {
312    pub span: Range<usize>,
313    pub prefix: Spanned<String>,
314    pub value: Spanned<NamedNode>,
315}
316
317impl Display for TurtlePrefix {
318    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
319        write!(
320            f,
321            "@prefix {}: {} .",
322            self.prefix.value(),
323            self.value.value()
324        )
325    }
326}
327
328#[derive(Debug)]
329pub enum TurtleSimpleError {
330    UnexpectedBase(&'static str),
331    UnexpectedBaseString(String),
332}
333
334#[derive(Clone, Debug, PartialEq, Eq)]
335pub struct Turtle {
336    pub base: Option<Spanned<Base>>,
337    pub prefixes: Vec<Spanned<TurtlePrefix>>,
338    pub triples: Vec<Spanned<Triple>>,
339    /// Document base URL for prefix/IRI resolution. Set by the language server
340    /// after parsing; not populated by the A* parser or CST converter.
341    pub set_base: Option<String>,
342}
343
344impl Turtle {
345    pub fn empty() -> Self {
346        Self::new(None, Vec::new(), Vec::new())
347    }
348
349    pub fn new(
350        base: Option<Spanned<Base>>,
351        prefixes: Vec<Spanned<TurtlePrefix>>,
352        triples: Vec<Spanned<Triple>>,
353    ) -> Self {
354        Self {
355            base,
356            prefixes,
357            triples,
358            set_base: None,
359        }
360    }
361}
362
363impl Display for Turtle {
364    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
365        if let Some(b) = &self.base {
366            writeln!(f, "{}", b.value())?;
367        }
368
369        self.prefixes
370            .iter()
371            .map(|x| x.value())
372            .try_for_each(|x| writeln!(f, "{}", x))?;
373
374        self.triples
375            .iter()
376            .map(|x| x.value())
377            .try_for_each(|x| writeln!(f, "{}", x))?;
378
379        Ok(())
380    }
381}