Skip to main content

spargebra/
update.rs

1use crate::SparqlParser;
2use crate::algebra::*;
3use crate::parser::SparqlSyntaxError;
4use crate::term::*;
5use oxiri::Iri;
6use std::fmt;
7use std::str::FromStr;
8
9/// A parsed [SPARQL update](https://www.w3.org/TR/sparql11-update/).
10///
11/// ```
12/// use spargebra::SparqlParser;
13///
14/// let update_str = "CLEAR ALL ;";
15/// let update = SparqlParser::new().parse_update(update_str)?;
16/// assert_eq!(update.to_string().trim(), update_str);
17/// assert_eq!(update.to_sse(), "(update (clear all))");
18/// # Ok::<_, spargebra::SparqlSyntaxError>(())
19/// ```
20#[derive(Eq, PartialEq, Debug, Clone, Hash)]
21pub struct Update {
22    /// The update base IRI.
23    pub base_iri: Option<Iri<String>>,
24    /// The [update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate).
25    pub operations: Vec<GraphUpdateOperation>,
26}
27
28impl Update {
29    /// Parses a SPARQL update with an optional base IRI to resolve relative IRIs in the query.
30    #[deprecated(
31        note = "Use `SparqlParser::new().parse_update` instead",
32        since = "0.4.0"
33    )]
34    pub fn parse(update: &str, base_iri: Option<&str>) -> Result<Self, SparqlSyntaxError> {
35        let mut parser = SparqlParser::new();
36        if let Some(base_iri) = base_iri {
37            parser = parser
38                .with_base_iri(base_iri)
39                .map_err(SparqlSyntaxError::from_bad_base_iri)?;
40        }
41        parser.parse_update(update)
42    }
43
44    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
45    pub fn to_sse(&self) -> String {
46        let mut buffer = String::new();
47        self.fmt_sse(&mut buffer).unwrap();
48        buffer
49    }
50
51    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
52    fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
53        if let Some(base_iri) = &self.base_iri {
54            write!(f, "(base <{base_iri}> ")?;
55        }
56        f.write_str("(update")?;
57        for op in &self.operations {
58            f.write_str(" ")?;
59            op.fmt_sse(f)?;
60        }
61        f.write_str(")")?;
62        if self.base_iri.is_some() {
63            f.write_str(")")?;
64        }
65        Ok(())
66    }
67}
68
69impl fmt::Display for Update {
70    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71        if let Some(base_iri) = &self.base_iri {
72            writeln!(f, "BASE <{base_iri}>")?;
73        }
74        for update in &self.operations {
75            writeln!(f, "{update} ;")?;
76        }
77        Ok(())
78    }
79}
80
81impl FromStr for Update {
82    type Err = SparqlSyntaxError;
83
84    fn from_str(update: &str) -> Result<Self, Self::Err> {
85        SparqlParser::new().parse_update(update)
86    }
87}
88
89impl TryFrom<&str> for Update {
90    type Error = SparqlSyntaxError;
91
92    fn try_from(update: &str) -> Result<Self, Self::Error> {
93        Self::from_str(update)
94    }
95}
96
97impl TryFrom<&String> for Update {
98    type Error = SparqlSyntaxError;
99
100    fn try_from(update: &String) -> Result<Self, Self::Error> {
101        Self::from_str(update)
102    }
103}
104
105/// The [graph update operations](https://www.w3.org/TR/sparql11-update/#formalModelGraphUpdate).
106#[derive(Eq, PartialEq, Debug, Clone, Hash)]
107pub enum GraphUpdateOperation {
108    /// [insert data](https://www.w3.org/TR/sparql11-update/#defn_insertDataOperation).
109    InsertData { data: Vec<Quad> },
110    /// [delete data](https://www.w3.org/TR/sparql11-update/#defn_deleteDataOperation).
111    DeleteData { data: Vec<GroundQuad> },
112    /// [delete insert](https://www.w3.org/TR/sparql11-update/#defn_deleteInsertOperation).
113    DeleteInsert {
114        delete: Vec<GroundQuadPattern>,
115        insert: Vec<QuadPattern>,
116        using: Option<QueryDataset>,
117        pattern: Box<GraphPattern>,
118    },
119    /// [load](https://www.w3.org/TR/sparql11-update/#defn_loadOperation).
120    Load {
121        silent: bool,
122        source: NamedNode,
123        destination: GraphName,
124    },
125    /// [clear](https://www.w3.org/TR/sparql11-update/#defn_clearOperation).
126    Clear { silent: bool, graph: GraphTarget },
127    /// [create](https://www.w3.org/TR/sparql11-update/#defn_createOperation).
128    Create { silent: bool, graph: NamedNode },
129    /// [drop](https://www.w3.org/TR/sparql11-update/#defn_dropOperation).
130    Drop { silent: bool, graph: GraphTarget },
131}
132
133impl GraphUpdateOperation {
134    /// Formats using the [SPARQL S-Expression syntax](https://jena.apache.org/documentation/notes/sse.html).
135    fn fmt_sse(&self, f: &mut impl fmt::Write) -> fmt::Result {
136        match self {
137            Self::InsertData { data } => {
138                f.write_str("(insertData (")?;
139                for (i, t) in data.iter().enumerate() {
140                    if i > 0 {
141                        f.write_str(" ")?;
142                    }
143                    t.fmt_sse(f)?;
144                }
145                f.write_str("))")
146            }
147            Self::DeleteData { data } => {
148                f.write_str("(deleteData (")?;
149                for (i, t) in data.iter().enumerate() {
150                    if i > 0 {
151                        f.write_str(" ")?;
152                    }
153                    t.fmt_sse(f)?;
154                }
155                f.write_str("))")
156            }
157            Self::DeleteInsert {
158                delete,
159                insert,
160                using,
161                pattern,
162            } => {
163                f.write_str("(modify ")?;
164                if let Some(using) = using {
165                    f.write_str(" (using ")?;
166                    using.fmt_sse(f)?;
167                    f.write_str(" ")?;
168                    pattern.fmt_sse(f)?;
169                    f.write_str(")")?;
170                } else {
171                    pattern.fmt_sse(f)?;
172                }
173                if !delete.is_empty() {
174                    f.write_str(" (delete (")?;
175                    for (i, t) in delete.iter().enumerate() {
176                        if i > 0 {
177                            f.write_str(" ")?;
178                        }
179                        t.fmt_sse(f)?;
180                    }
181                    f.write_str("))")?;
182                }
183                if !insert.is_empty() {
184                    f.write_str(" (insert (")?;
185                    for (i, t) in insert.iter().enumerate() {
186                        if i > 0 {
187                            f.write_str(" ")?;
188                        }
189                        t.fmt_sse(f)?;
190                    }
191                    f.write_str("))")?;
192                }
193                f.write_str(")")
194            }
195            Self::Load {
196                silent,
197                source,
198                destination,
199            } => {
200                f.write_str("(load ")?;
201                if *silent {
202                    f.write_str("silent ")?;
203                }
204                write!(f, "{source} ")?;
205                destination.fmt_sse(f)?;
206                f.write_str(")")
207            }
208            Self::Clear { silent, graph } => {
209                f.write_str("(clear ")?;
210                if *silent {
211                    f.write_str("silent ")?;
212                }
213                graph.fmt_sse(f)?;
214                f.write_str(")")
215            }
216            Self::Create { silent, graph } => {
217                f.write_str("(create ")?;
218                if *silent {
219                    f.write_str("silent ")?;
220                }
221                write!(f, "{graph})")
222            }
223            Self::Drop { silent, graph } => {
224                f.write_str("(drop ")?;
225                if *silent {
226                    f.write_str("silent ")?;
227                }
228                graph.fmt_sse(f)?;
229                f.write_str(")")
230            }
231        }
232    }
233}
234
235impl fmt::Display for GraphUpdateOperation {
236    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237        match self {
238            Self::InsertData { data } => {
239                writeln!(f, "INSERT DATA {{")?;
240                serialize_quads(data, f)?;
241                f.write_str("}")
242            }
243            Self::DeleteData { data } => {
244                writeln!(f, "DELETE DATA {{")?;
245                write_ground_quads(data, f)?;
246                f.write_str("}")
247            }
248            Self::DeleteInsert {
249                delete,
250                insert,
251                using,
252                pattern,
253            } => {
254                if !delete.is_empty() {
255                    writeln!(f, "DELETE {{")?;
256                    for quad in delete {
257                        writeln!(f, "\t{quad} .")?;
258                    }
259                    writeln!(f, "}}")?;
260                }
261                if !insert.is_empty() {
262                    writeln!(f, "INSERT {{")?;
263                    for quad in insert {
264                        writeln!(f, "\t{quad} .")?;
265                    }
266                    writeln!(f, "}}")?;
267                }
268                if let Some(using) = using {
269                    for g in &using.default {
270                        writeln!(f, "USING {g}")?;
271                    }
272                    if let Some(named) = &using.named {
273                        for g in named {
274                            writeln!(f, "USING NAMED {g}")?;
275                        }
276                    }
277                }
278                write!(
279                    f,
280                    "WHERE {{ {} }}",
281                    SparqlGraphRootPattern::new(pattern, None)
282                )
283            }
284            Self::Load {
285                silent,
286                source,
287                destination,
288            } => {
289                f.write_str("LOAD ")?;
290                if *silent {
291                    f.write_str("SILENT ")?;
292                }
293                write!(f, "{source}")?;
294                if destination != &GraphName::DefaultGraph {
295                    write!(f, " INTO GRAPH {destination}")?;
296                }
297                Ok(())
298            }
299            Self::Clear { silent, graph } => {
300                f.write_str("CLEAR ")?;
301                if *silent {
302                    f.write_str("SILENT ")?;
303                }
304                write!(f, "{graph}")
305            }
306            Self::Create { silent, graph } => {
307                f.write_str("CREATE ")?;
308                if *silent {
309                    f.write_str("SILENT ")?;
310                }
311                write!(f, "GRAPH {graph}")
312            }
313            Self::Drop { silent, graph } => {
314                f.write_str("DROP ")?;
315                if *silent {
316                    f.write_str("SILENT ")?;
317                }
318                write!(f, "{graph}")
319            }
320        }
321    }
322}
323
324fn serialize_quads(quads: &[Quad], f: &mut fmt::Formatter<'_>) -> fmt::Result {
325    for quad in quads {
326        if quad.graph_name == GraphName::DefaultGraph {
327            writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?;
328        } else {
329            writeln!(
330                f,
331                "\tGRAPH {} {{ {} {} {} }}",
332                quad.graph_name, quad.subject, quad.predicate, quad.object
333            )?;
334        }
335    }
336    Ok(())
337}
338
339fn write_ground_quads(quads: &[GroundQuad], f: &mut fmt::Formatter<'_>) -> fmt::Result {
340    for quad in quads {
341        if quad.graph_name == GraphName::DefaultGraph {
342            writeln!(f, "\t{} {} {} .", quad.subject, quad.predicate, quad.object)?;
343        } else {
344            writeln!(
345                f,
346                "\tGRAPH {} {{ {} {} {} }}",
347                quad.graph_name, quad.subject, quad.predicate, quad.object
348            )?;
349        }
350    }
351    Ok(())
352}