1use crate::model::{
7 NamedNode, Object, ObjectTerm, Predicate, RdfTerm, Subject, SubjectTerm, Triple,
8};
9use crate::query::algebra::{AlgebraTriplePattern, TermPattern};
10use crate::OxirsError;
11use std::fmt;
12use std::sync::Arc;
13
14#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
16pub struct QuotedTriple {
17 inner: Arc<Triple>,
19}
20
21impl QuotedTriple {
22 pub fn new(triple: Triple) -> Self {
24 QuotedTriple {
25 inner: Arc::new(triple),
26 }
27 }
28
29 pub fn from_arc(triple: Arc<Triple>) -> Self {
31 QuotedTriple { inner: triple }
32 }
33
34 pub fn inner(&self) -> &Triple {
36 &self.inner
37 }
38
39 pub fn subject(&self) -> &Subject {
41 self.inner.subject()
42 }
43
44 pub fn predicate(&self) -> &Predicate {
46 self.inner.predicate()
47 }
48
49 pub fn object(&self) -> &Object {
51 self.inner.object()
52 }
53
54 pub fn as_ref(&self) -> QuotedTripleRef<'_> {
56 QuotedTripleRef { inner: &self.inner }
57 }
58}
59
60impl fmt::Display for QuotedTriple {
61 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62 write!(f, "<< {} >>", self.inner)
63 }
64}
65
66impl RdfTerm for QuotedTriple {
67 fn as_str(&self) -> &str {
68 "<<quoted-triple>>"
70 }
71
72 fn is_quoted_triple(&self) -> bool {
73 true
74 }
75}
76
77impl SubjectTerm for QuotedTriple {}
78impl ObjectTerm for QuotedTriple {}
79
80#[cfg(feature = "serde")]
82impl serde::Serialize for QuotedTriple {
83 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84 where
85 S: serde::Serializer,
86 {
87 self.inner.as_ref().serialize(serializer)
89 }
90}
91
92#[cfg(feature = "serde")]
93impl<'de> serde::Deserialize<'de> for QuotedTriple {
94 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
95 where
96 D: serde::Deserializer<'de>,
97 {
98 let triple = Triple::deserialize(deserializer)?;
99 Ok(QuotedTriple::new(triple))
100 }
101}
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
105pub struct QuotedTripleRef<'a> {
106 inner: &'a Triple,
107}
108
109impl<'a> QuotedTripleRef<'a> {
110 pub fn new(triple: &'a Triple) -> Self {
112 QuotedTripleRef { inner: triple }
113 }
114
115 pub fn inner(&self) -> &'a Triple {
117 self.inner
118 }
119
120 pub fn to_owned(&self) -> QuotedTriple {
122 QuotedTriple::new(self.inner.clone())
123 }
124}
125
126impl<'a> fmt::Display for QuotedTripleRef<'a> {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 write!(f, "<< {} >>", self.inner)
129 }
130}
131
132impl<'a> RdfTerm for QuotedTripleRef<'a> {
133 fn as_str(&self) -> &str {
134 "<<quoted-triple>>"
135 }
136
137 fn is_quoted_triple(&self) -> bool {
138 true
139 }
140}
141
142pub struct Annotation {
144 pub statement: QuotedTriple,
146 pub property: NamedNode,
148 pub value: Object,
150}
151
152impl Annotation {
153 pub fn new(statement: Triple, property: NamedNode, value: Object) -> Self {
155 Annotation {
156 statement: QuotedTriple::new(statement),
157 property,
158 value,
159 }
160 }
161
162 pub fn to_triple(&self) -> Triple {
164 Triple::new(
165 Subject::QuotedTriple(Box::new(self.statement.clone())),
166 self.property.clone(),
167 self.value.clone(),
168 )
169 }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Hash)]
174pub enum StarPattern {
175 Triple(AlgebraTriplePattern),
177 QuotedTriple {
179 subject: Box<StarPattern>,
181 predicate: TermPattern,
183 object: Box<StarPattern>,
185 },
186 Annotation {
188 statement: Box<StarPattern>,
190 property: TermPattern,
192 value: TermPattern,
194 },
195}
196
197impl StarPattern {
198 pub fn has_variables(&self) -> bool {
200 match self {
201 StarPattern::Triple(pattern) => {
202 matches!(pattern.subject, TermPattern::Variable(_))
203 || matches!(pattern.predicate, TermPattern::Variable(_))
204 || matches!(pattern.object, TermPattern::Variable(_))
205 }
206 StarPattern::QuotedTriple {
207 subject,
208 predicate: _,
209 object,
210 } => subject.has_variables() || object.has_variables(),
211 StarPattern::Annotation {
212 statement,
213 property: _,
214 value: _,
215 } => statement.has_variables(),
216 }
217 }
218
219 pub fn variables(&self) -> Vec<crate::model::Variable> {
221 let mut vars = Vec::new();
222 self.collect_variables(&mut vars);
223 vars
224 }
225
226 fn collect_variables(&self, _vars: &mut Vec<crate::model::Variable>) {
227 match self {
228 StarPattern::Triple(pattern) => {
229 if let TermPattern::Variable(ref v) = pattern.subject {
230 _vars.push(v.clone());
231 }
232 if let TermPattern::Variable(ref v) = pattern.predicate {
233 _vars.push(v.clone());
234 }
235 if let TermPattern::Variable(ref v) = pattern.object {
236 _vars.push(v.clone());
237 }
238 }
239 StarPattern::QuotedTriple {
240 subject,
241 predicate: _,
242 object,
243 } => {
244 subject.collect_variables(_vars);
245 object.collect_variables(_vars);
246 }
247 StarPattern::Annotation {
248 statement,
249 property: _,
250 value: _,
251 } => {
252 statement.collect_variables(_vars);
253 }
254 }
255 }
256}
257
258pub mod serialization {
260 use super::*;
261
262 pub mod turtle_star {
264 use super::*;
265
266 pub fn serialize_quoted_triple(qt: &QuotedTriple) -> String {
268 format!("<< {} {} {} >>", qt.subject(), qt.predicate(), qt.object())
269 }
270
271 pub fn parse_quoted_triple(input: &str) -> Result<QuotedTriple, OxirsError> {
273 let trimmed = input.trim();
275 if !trimmed.starts_with("<<") || !trimmed.ends_with(">>") {
276 return Err(OxirsError::Parse(
277 "Invalid quoted triple syntax".to_string(),
278 ));
279 }
280
281 let _inner = &trimmed[2..trimmed.len() - 2].trim();
283
284 Err(OxirsError::Parse(
287 "Quoted triple parsing not yet implemented".to_string(),
288 ))
289 }
290 }
291
292 pub mod sparql_star {
294 use super::*;
295
296 pub fn format_star_pattern(pattern: &StarPattern) -> String {
298 match pattern {
299 StarPattern::Triple(pattern) => pattern.to_string(),
300 StarPattern::QuotedTriple {
301 subject,
302 predicate: _,
303 object,
304 } => {
305 format!(
306 "<< {} {} {} >>",
307 format_star_pattern(subject),
308 "PREDICATE",
309 format_star_pattern(object)
310 )
311 }
312 StarPattern::Annotation {
313 statement,
314 property: _,
315 value: _,
316 } => {
317 format!(
318 "{} {} {}",
319 format_star_pattern(statement),
320 "PROPERTY",
321 "VALUE"
322 )
323 }
324 }
325 }
326 }
327}
328
329#[cfg(test)]
330mod tests {
331 use super::*;
332 use crate::model::{Literal, NamedNode};
333
334 #[test]
335 fn test_quoted_triple() {
336 let subject = NamedNode::new("http://example.org/alice").unwrap();
337 let predicate = NamedNode::new("http://example.org/says").unwrap();
338 let object = Object::Literal(Literal::new("Hello"));
339
340 let triple = Triple::new(subject, predicate, object);
341 let quoted = QuotedTriple::new(triple.clone());
342
343 assert_eq!(quoted.inner(), &triple);
344 assert_eq!(
345 format!("{quoted}"),
346 "<< <http://example.org/alice> <http://example.org/says> \"Hello\" . >>"
347 );
348 }
349
350 #[test]
351 fn test_annotation() {
352 let subject = NamedNode::new("http://example.org/alice").unwrap();
353 let predicate = NamedNode::new("http://example.org/age").unwrap();
354 let object = Object::Literal(Literal::new_typed(
355 "30",
356 NamedNode::new("http://www.w3.org/2001/XMLSchema#integer").unwrap(),
357 ));
358
359 let statement = Triple::new(subject, predicate, object);
360 let ann_property = NamedNode::new("http://example.org/confidence").unwrap();
361 let ann_value = Object::Literal(Literal::new_typed(
362 "0.9",
363 NamedNode::new("http://www.w3.org/2001/XMLSchema#double").unwrap(),
364 ));
365
366 let annotation = Annotation::new(statement, ann_property, ann_value);
367 let ann_triple = annotation.to_triple();
368
369 assert!(matches!(ann_triple.subject(), Subject::QuotedTriple(_)));
370 }
371}