rdftk_core/model/
statement.rs

1/*!
2* This module provides types for the RDF Statement (triple) concept.
3*
4* 1. A **statement** comprises a subject, a predicate, and an object.
5* 1. A **subject** may be a blank (unnamed) node, an IRI (named node), or a statement reference
6*    according to RDF-star.
7* 1. A **predicate** is an IRI.
8* 1. An **object** may be a blank (unnamed) node, an IRI (named node), a literal value, or a statement
9*    reference according to RDF-star.
10* 1. A **literal** has a string-like *lexical form* and may have an asserted data type or a language
11*    identifier.
12*
13* # Example
14*
15*
16* ```rust
17* use rdftk_core::model::literal::Literal;
18* use rdftk_core::model::statement::Statement;
19* use rdftk_iri::Iri;
20* use std::str::FromStr;
21*
22* let statement = Statement::new(
23*     Iri::from_str("http://en.wikipedia.org/wiki/Tony_Benn").unwrap(),
24*     Iri::from_str("http://purl.org/dc/elements/1.1/title").unwrap(),
25*     Literal::plain("Tony Benn"),
26* );
27* ```
28*
29*
30*/
31
32#![allow(clippy::module_name_repetitions)]
33
34use crate::error::Result;
35use crate::model::features::Featured;
36use crate::model::features::FEATURE_RDF_STAR;
37use rdftk_iri::Iri;
38use rdftk_names::rdf;
39use std::cmp::Ordering;
40use std::fmt::Formatter;
41use std::fmt::{Debug, Display};
42
43// ------------------------------------------------------------------------------------------------
44// Public Types
45// ------------------------------------------------------------------------------------------------
46
47///
48/// Simple, in-memory implementation of the `Statement` trait.
49///
50#[derive(Clone, Debug, PartialEq, Eq, Hash)]
51pub struct Statement {
52    subject: SubjectNode,
53    predicate: Iri,
54    object: ObjectNode,
55}
56
57// ------------------------------------------------------------------------------------------------
58// Implementations
59// ------------------------------------------------------------------------------------------------
60
61impl Display for Statement {
62    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
63        write!(
64            f,
65            "{} <{}> {}",
66            &self.subject().to_string(),
67            &self.predicate().to_string(),
68            &self.object().to_string(),
69        )
70    }
71}
72
73// ------------------------------------------------------------------------------------------------
74
75impl PartialOrd for Statement {
76    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
77        Some(self.cmp(other))
78    }
79}
80
81impl Ord for Statement {
82    fn cmp(&self, other: &Self) -> Ordering {
83        match self.subject.cmp(&other.subject) {
84            Ordering::Equal => {}
85            ord => return ord,
86        }
87        match self.predicate.cmp(&other.predicate) {
88            Ordering::Equal => {}
89            ord => return ord,
90        }
91        self.object.cmp(&other.object)
92    }
93}
94
95// ------------------------------------------------------------------------------------------------
96
97impl Featured for Statement {
98    fn supports_feature(&self, feature: &Iri) -> bool {
99        *feature == *FEATURE_RDF_STAR || *feature == *FEATURE_STMT_OBJECT_COLLECTIONS
100    }
101}
102
103impl Statement {
104    // --------------------------------------------------------------------------------------------
105    // Constructors
106    // --------------------------------------------------------------------------------------------
107
108    pub fn new<S, O>(subject: S, predicate: Iri, object: O) -> Self
109    where
110        S: Into<SubjectNode>,
111        O: Into<ObjectNode>,
112    {
113        Self {
114            subject: subject.into(),
115            predicate,
116            object: object.into(),
117        }
118    }
119
120    pub fn rdf_type<S, O>(subject: S, object: O) -> Self
121    where
122        S: Into<SubjectNode>,
123        O: Into<ObjectNode>,
124    {
125        Self::new(subject, rdf::a_type().clone(), object)
126    }
127
128    // --------------------------------------------------------------------------------------------
129    // Components
130    // --------------------------------------------------------------------------------------------
131
132    ///
133    /// Return the subject of this statement.
134    ///
135    pub fn subject(&self) -> &SubjectNode {
136        &self.subject
137    }
138
139    ///
140    /// Set the value of this statement's subject.
141    ///
142    pub fn set_subject(&mut self, subject: SubjectNode) {
143        self.subject = subject;
144    }
145
146    ///
147    /// Return the predicate of this statement.
148    ///
149    pub fn predicate(&self) -> &Iri {
150        &self.predicate
151    }
152
153    ///
154    /// Set the value of this statement's predicate.
155    ///
156    pub fn set_predicate(&mut self, predicate: Iri) {
157        self.predicate = predicate;
158    }
159
160    ///
161    /// Return the object of this statement.
162    ///
163    pub fn object(&self) -> &ObjectNode {
164        &self.object
165    }
166
167    ///
168    /// Set the value of this statement's object.
169    ///
170    pub fn set_object(&mut self, object: ObjectNode) {
171        self.object = object;
172    }
173
174    // --------------------------------------------------------------------------------------------
175    // Other
176    // --------------------------------------------------------------------------------------------
177
178    ///
179    /// This statement is considered nested if *either* subject or object is itself a statement
180    /// ([RDF-star](https://w3c.github.io/rdf-star/cg-spec/editors_draft.html))
181    ///
182    pub fn is_nested(&self) -> bool {
183        self.subject().is_statement() || self.object().is_statement()
184    }
185
186    ///
187    /// Reify a single statement, returning the list of resulting statements.
188    ///
189    pub fn reify(&self) -> Result<(SubjectNode, Vec<Self>)> {
190        let mut statements: Vec<Self> = Default::default();
191        let new_subject: SubjectNode = BlankNode::generate().into();
192        statements.push(Self::new(
193            new_subject.clone(),
194            rdf::a_type().clone(),
195            rdf::statement().clone(),
196        ));
197        if let Some(statement) = self.subject().as_statement() {
198            let nested = statement.reify()?;
199            statements.extend(nested.1);
200            statements.push(Self::new(
201                new_subject.clone(),
202                rdf::subject().clone(),
203                nested.0.to_object(),
204            ));
205        } else {
206            statements.push(Self::new(
207                new_subject.clone(),
208                rdf::subject().clone(),
209                self.subject().to_object(),
210            ));
211        }
212        statements.push(Self::new(
213            new_subject.clone(),
214            rdf::predicate().clone(),
215            self.predicate().clone(),
216        ));
217        if let Some(statement) = self.object().as_statement() {
218            let nested = statement.reify()?;
219            statements.extend(nested.1);
220            statements.push(Self::new(
221                new_subject.clone(),
222                rdf::object().clone(),
223                nested.0.to_object(),
224            ));
225        } else {
226            // TODO: check for ObjectNode::Collection
227            statements.push(Self::new(
228                new_subject.clone(),
229                rdf::object().clone(),
230                self.object().clone(),
231            ));
232        }
233        Ok((new_subject, statements))
234    }
235}
236
237// ------------------------------------------------------------------------------------------------
238// Modules
239// ------------------------------------------------------------------------------------------------
240
241mod bnode;
242pub use bnode::*;
243
244mod collection;
245pub use collection::*;
246
247mod subject;
248pub use subject::*;
249
250mod object;
251pub use object::*;
252
253use super::features::FEATURE_STMT_OBJECT_COLLECTIONS;