Skip to main content

oxirs_core/model/
quad.rs

1//! RDF Quad implementation
2
3use crate::model::{
4    BlankNode, NamedNode, Object, ObjectRef, Predicate, PredicateRef, Subject, SubjectRef, Triple,
5    TripleRef, Variable,
6};
7use std::fmt;
8use std::hash::Hash;
9
10/// Union type for terms that can be graph names
11#[derive(
12    Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
13)]
14pub enum GraphName {
15    NamedNode(NamedNode),
16    BlankNode(BlankNode),
17    Variable(Variable),
18    DefaultGraph,
19}
20
21impl GraphName {
22    /// Returns true if this is the default graph
23    pub fn is_default_graph(&self) -> bool {
24        matches!(self, GraphName::DefaultGraph)
25    }
26}
27
28impl fmt::Display for GraphName {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            GraphName::NamedNode(n) => write!(f, "{n}"),
32            GraphName::BlankNode(b) => write!(f, "{b}"),
33            GraphName::Variable(v) => write!(f, "{v}"),
34            GraphName::DefaultGraph => write!(f, "DEFAULT"),
35        }
36    }
37}
38
39impl AsRef<str> for GraphName {
40    fn as_ref(&self) -> &str {
41        match self {
42            GraphName::NamedNode(n) => n.as_str(),
43            GraphName::BlankNode(b) => b.as_str(),
44            GraphName::Variable(v) => v.name(),
45            GraphName::DefaultGraph => "DEFAULT",
46        }
47    }
48}
49
50impl From<NamedNode> for GraphName {
51    fn from(node: NamedNode) -> Self {
52        GraphName::NamedNode(node)
53    }
54}
55
56impl From<BlankNode> for GraphName {
57    fn from(node: BlankNode) -> Self {
58        GraphName::BlankNode(node)
59    }
60}
61
62impl From<Variable> for GraphName {
63    fn from(variable: Variable) -> Self {
64        GraphName::Variable(variable)
65    }
66}
67
68impl From<crate::model::node::NamedOrBlankNode> for GraphName {
69    fn from(node: crate::model::node::NamedOrBlankNode) -> Self {
70        match node {
71            crate::model::node::NamedOrBlankNode::NamedNode(n) => GraphName::NamedNode(n),
72            crate::model::node::NamedOrBlankNode::BlankNode(b) => GraphName::BlankNode(b),
73        }
74    }
75}
76
77/// An RDF Quad
78///
79/// Represents an RDF statement with subject, predicate, object, and graph name.
80/// This is used in RDF datasets where triples can belong to different named graphs.
81#[derive(
82    Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
83)]
84pub struct Quad {
85    subject: Subject,
86    predicate: Predicate,
87    object: Object,
88    graph_name: GraphName,
89}
90
91impl Quad {
92    /// Creates a new quad
93    pub fn new(
94        subject: impl Into<Subject>,
95        predicate: impl Into<Predicate>,
96        object: impl Into<Object>,
97        graph_name: impl Into<GraphName>,
98    ) -> Self {
99        Quad {
100            subject: subject.into(),
101            predicate: predicate.into(),
102            object: object.into(),
103            graph_name: graph_name.into(),
104        }
105    }
106
107    /// Creates a new quad in the default graph
108    pub fn new_default_graph(
109        subject: impl Into<Subject>,
110        predicate: impl Into<Predicate>,
111        object: impl Into<Object>,
112    ) -> Self {
113        Quad {
114            subject: subject.into(),
115            predicate: predicate.into(),
116            object: object.into(),
117            graph_name: GraphName::DefaultGraph,
118        }
119    }
120
121    /// Creates a quad from a triple, placing it in the default graph
122    pub fn from_triple(triple: Triple) -> Self {
123        let (subject, predicate, object) = triple.into_parts();
124        Quad::new_default_graph(subject, predicate, object)
125    }
126
127    /// Creates a quad from a triple with a specific graph name
128    pub fn from_triple_in_graph(triple: Triple, graph_name: impl Into<GraphName>) -> Self {
129        let (subject, predicate, object) = triple.into_parts();
130        Quad::new(subject, predicate, object, graph_name)
131    }
132
133    /// Returns the subject of this quad
134    pub fn subject(&self) -> &Subject {
135        &self.subject
136    }
137
138    /// Returns the predicate of this quad
139    pub fn predicate(&self) -> &Predicate {
140        &self.predicate
141    }
142
143    /// Returns the object of this quad
144    pub fn object(&self) -> &Object {
145        &self.object
146    }
147
148    /// Returns the graph name of this quad
149    pub fn graph_name(&self) -> &GraphName {
150        &self.graph_name
151    }
152
153    /// Decomposes the quad into its components
154    pub fn into_parts(self) -> (Subject, Predicate, Object, GraphName) {
155        (self.subject, self.predicate, self.object, self.graph_name)
156    }
157
158    /// Converts this quad to a triple, discarding the graph name
159    pub fn to_triple(&self) -> Triple {
160        Triple::new(
161            self.subject.clone(),
162            self.predicate.clone(),
163            self.object.clone(),
164        )
165    }
166
167    /// Returns a reference to this quad
168    pub fn as_ref(&self) -> QuadRef<'_> {
169        QuadRef::from(self)
170    }
171
172    /// Returns true if this quad is in the default graph
173    pub fn is_default_graph(&self) -> bool {
174        matches!(self.graph_name, GraphName::DefaultGraph)
175    }
176
177    /// Returns the triple if this quad is in the default graph, None otherwise
178    pub fn triple_in_default_graph(&self) -> Option<Triple> {
179        if self.is_default_graph() {
180            Some(self.to_triple())
181        } else {
182            None
183        }
184    }
185
186    /// Returns true if this quad contains any variables
187    pub fn has_variables(&self) -> bool {
188        matches!(self.subject, Subject::Variable(_))
189            || matches!(self.predicate, Predicate::Variable(_))
190            || matches!(self.object, Object::Variable(_))
191            || matches!(self.graph_name, GraphName::Variable(_))
192    }
193
194    /// Returns true if this quad is ground (contains no variables)
195    pub fn is_ground(&self) -> bool {
196        !self.has_variables()
197    }
198}
199
200impl fmt::Display for Quad {
201    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
202        match &self.graph_name {
203            GraphName::DefaultGraph => {
204                write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
205            }
206            graph => write!(
207                f,
208                "{} {} {} {} .",
209                self.subject, self.predicate, self.object, graph
210            ),
211        }
212    }
213}
214
215/// A borrowed quad reference for zero-copy operations
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
217pub struct QuadRef<'a> {
218    subject: SubjectRef<'a>,
219    predicate: PredicateRef<'a>,
220    object: ObjectRef<'a>,
221    graph_name: GraphNameRef<'a>,
222}
223
224impl<'a> QuadRef<'a> {
225    /// Creates a new quad reference
226    pub fn new(
227        subject: SubjectRef<'a>,
228        predicate: PredicateRef<'a>,
229        object: ObjectRef<'a>,
230        graph_name: GraphNameRef<'a>,
231    ) -> Self {
232        QuadRef {
233            subject,
234            predicate,
235            object,
236            graph_name,
237        }
238    }
239
240    /// Returns the subject
241    pub fn subject(&self) -> SubjectRef<'a> {
242        self.subject
243    }
244
245    /// Returns the predicate
246    pub fn predicate(&self) -> PredicateRef<'a> {
247        self.predicate
248    }
249
250    /// Returns the object
251    pub fn object(&self) -> ObjectRef<'a> {
252        self.object
253    }
254
255    /// Returns the graph name
256    pub fn graph_name(&self) -> GraphNameRef<'a> {
257        self.graph_name
258    }
259
260    /// Converts to an owned quad
261    pub fn to_owned(&self) -> Quad {
262        Quad {
263            subject: self.subject.to_owned(),
264            predicate: self.predicate.to_owned(),
265            object: self.object.to_owned(),
266            graph_name: self.graph_name.to_owned(),
267        }
268    }
269
270    /// Converts to a triple reference, discarding the graph name
271    pub fn to_triple_ref(&self) -> TripleRef<'a> {
272        TripleRef::new(self.subject, self.predicate, self.object)
273    }
274
275    /// Returns the triple part of this quad (alias for to_triple_ref)
276    pub fn triple(&self) -> TripleRef<'a> {
277        self.to_triple_ref()
278    }
279
280    /// Converts to an owned quad (alias for to_owned)
281    pub fn into_owned(self) -> Quad {
282        self.to_owned()
283    }
284}
285
286impl<'a> fmt::Display for QuadRef<'a> {
287    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        match self.graph_name {
289            GraphNameRef::DefaultGraph => {
290                write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
291            }
292            graph => write!(
293                f,
294                "{} {} {} {} .",
295                self.subject, self.predicate, self.object, graph
296            ),
297        }
298    }
299}
300
301/// Borrowed graph name reference
302#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
303pub enum GraphNameRef<'a> {
304    NamedNode(&'a NamedNode),
305    BlankNode(&'a BlankNode),
306    Variable(&'a Variable),
307    DefaultGraph,
308}
309
310impl<'a> GraphNameRef<'a> {
311    /// Returns true if this is the default graph
312    pub fn is_default_graph(&self) -> bool {
313        matches!(self, GraphNameRef::DefaultGraph)
314    }
315
316    /// Converts to an owned graph name
317    pub fn to_owned(&self) -> GraphName {
318        match self {
319            GraphNameRef::NamedNode(n) => GraphName::NamedNode((*n).clone()),
320            GraphNameRef::BlankNode(b) => GraphName::BlankNode((*b).clone()),
321            GraphNameRef::Variable(v) => GraphName::Variable((*v).clone()),
322            GraphNameRef::DefaultGraph => GraphName::DefaultGraph,
323        }
324    }
325}
326
327impl<'a> fmt::Display for GraphNameRef<'a> {
328    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329        match self {
330            GraphNameRef::NamedNode(n) => write!(f, "{n}"),
331            GraphNameRef::BlankNode(b) => write!(f, "{b}"),
332            GraphNameRef::Variable(v) => write!(f, "{v}"),
333            GraphNameRef::DefaultGraph => write!(f, "DEFAULT"),
334        }
335    }
336}
337
338// Conversion implementations
339impl<'a> From<&'a GraphName> for GraphNameRef<'a> {
340    fn from(graph_name: &'a GraphName) -> Self {
341        match graph_name {
342            GraphName::NamedNode(n) => GraphNameRef::NamedNode(n),
343            GraphName::BlankNode(b) => GraphNameRef::BlankNode(b),
344            GraphName::Variable(v) => GraphNameRef::Variable(v),
345            GraphName::DefaultGraph => GraphNameRef::DefaultGraph,
346        }
347    }
348}
349
350impl<'a> From<&'a Quad> for QuadRef<'a> {
351    fn from(quad: &'a Quad) -> Self {
352        QuadRef {
353            subject: quad.subject().into(),
354            predicate: quad.predicate().into(),
355            object: quad.object().into(),
356            graph_name: quad.graph_name().into(),
357        }
358    }
359}
360
361impl<'a> From<QuadRef<'a>> for Quad {
362    fn from(quad_ref: QuadRef<'a>) -> Self {
363        quad_ref.to_owned()
364    }
365}
366
367impl From<Triple> for Quad {
368    fn from(triple: Triple) -> Self {
369        Quad::from_triple(triple)
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use super::*;
376    use crate::model::{Literal, NamedNode};
377
378    #[test]
379    fn test_quad_creation() {
380        let subject = NamedNode::new("http://example.org/subject").unwrap();
381        let predicate = NamedNode::new("http://example.org/predicate").unwrap();
382        let object = Literal::new("object");
383        let graph = NamedNode::new("http://example.org/graph").unwrap();
384
385        let quad = Quad::new(
386            subject.clone(),
387            predicate.clone(),
388            object.clone(),
389            graph.clone(),
390        );
391
392        assert!(quad.is_ground());
393        assert!(!quad.has_variables());
394        assert!(!quad.is_default_graph());
395    }
396
397    #[test]
398    fn test_quad_default_graph() {
399        let subject = NamedNode::new("http://example.org/subject").unwrap();
400        let predicate = NamedNode::new("http://example.org/predicate").unwrap();
401        let object = Literal::new("object");
402
403        let quad = Quad::new_default_graph(subject, predicate, object);
404
405        assert!(quad.is_default_graph());
406        assert!(quad.graph_name().is_default_graph());
407    }
408
409    #[test]
410    fn test_quad_from_triple() {
411        let subject = NamedNode::new("http://example.org/subject").unwrap();
412        let predicate = NamedNode::new("http://example.org/predicate").unwrap();
413        let object = Literal::new("object");
414
415        let triple = Triple::new(subject.clone(), predicate.clone(), object.clone());
416        let quad = Quad::from_triple(triple.clone());
417
418        assert!(quad.is_default_graph());
419        assert_eq!(quad.to_triple().subject(), triple.subject());
420        assert_eq!(quad.to_triple().predicate(), triple.predicate());
421        assert_eq!(quad.to_triple().object(), triple.object());
422    }
423
424    #[test]
425    fn test_quad_with_variable() {
426        let subject = Variable::new("x").unwrap();
427        let predicate = NamedNode::new("http://example.org/predicate").unwrap();
428        let object = Literal::new("object");
429        let graph = Variable::new("g").unwrap();
430
431        let quad = Quad::new(subject, predicate, object, graph);
432
433        assert!(!quad.is_ground());
434        assert!(quad.has_variables());
435    }
436
437    #[test]
438    fn test_quad_ref() {
439        let subject = NamedNode::new("http://example.org/s").unwrap();
440        let predicate = NamedNode::new("http://example.org/p").unwrap();
441        let object = Literal::new("o");
442        let graph = NamedNode::new("http://example.org/g").unwrap();
443
444        let quad = Quad::new(subject, predicate, object, graph);
445        let quad_ref = QuadRef::from(&quad);
446        let quad_owned = quad_ref.to_owned();
447
448        assert_eq!(quad, quad_owned);
449    }
450}