spargebra/
update.rs

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