1use crate::writer::{RdfTerm, TermType};
7use std::collections::{BTreeMap, HashSet};
8use std::fmt;
9
10#[derive(Debug, Clone)]
14pub struct PatchError {
15 pub line: usize,
17 pub message: String,
19}
20
21impl PatchError {
22 pub(crate) fn new(line: usize, message: impl Into<String>) -> Self {
23 Self {
24 line,
25 message: message.into(),
26 }
27 }
28
29 pub(crate) fn at(line: usize, msg: impl fmt::Display) -> Self {
30 Self::new(line, msg.to_string())
31 }
32}
33
34impl fmt::Display for PatchError {
35 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36 write!(f, "patch error at line {}: {}", self.line, self.message)
37 }
38}
39
40impl std::error::Error for PatchError {}
41
42pub type PatchResult<T> = Result<T, PatchError>;
44
45#[derive(Debug, Clone, PartialEq, Eq)]
49pub enum PatchHeader {
50 Version(String),
52 Previous(String),
54 Id(String),
56 Unknown {
58 key: String,
60 value: String,
62 },
63}
64
65impl PatchHeader {
66 pub fn key(&self) -> &str {
68 match self {
69 PatchHeader::Version(_) => "version",
70 PatchHeader::Previous(_) => "prev",
71 PatchHeader::Id(_) => "id",
72 PatchHeader::Unknown { key, .. } => key.as_str(),
73 }
74 }
75
76 pub fn value(&self) -> &str {
78 match self {
79 PatchHeader::Version(v) | PatchHeader::Previous(v) | PatchHeader::Id(v) => v.as_str(),
80 PatchHeader::Unknown { value, .. } => value.as_str(),
81 }
82 }
83}
84
85impl fmt::Display for PatchHeader {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "H {} {}", self.key(), self.value())
88 }
89}
90
91#[derive(Debug, Clone, PartialEq, Eq)]
94pub struct PatchTerm(pub RdfTerm);
95
96impl PatchTerm {
97 pub fn iri(iri: impl Into<String>) -> Self {
99 Self(RdfTerm::iri(iri))
100 }
101
102 pub fn blank_node(id: impl Into<String>) -> Self {
104 Self(RdfTerm::blank_node(id))
105 }
106
107 pub fn literal(value: impl Into<String>) -> Self {
109 Self(RdfTerm::simple_literal(value))
110 }
111
112 pub fn lang_literal(value: impl Into<String>, lang: impl Into<String>) -> Self {
114 Self(RdfTerm::lang_literal(value, lang))
115 }
116
117 pub fn typed_literal(value: impl Into<String>, datatype: impl Into<String>) -> Self {
119 Self(RdfTerm::typed_literal(value, datatype))
120 }
121
122 pub fn term(&self) -> &RdfTerm {
124 &self.0
125 }
126
127 pub fn is_iri(&self) -> bool {
129 self.0.term_type == TermType::Iri
130 }
131
132 pub fn is_blank_node(&self) -> bool {
134 self.0.term_type == TermType::BlankNode
135 }
136
137 pub fn value(&self) -> &str {
139 &self.0.value
140 }
141}
142
143impl fmt::Display for PatchTerm {
144 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
145 match &self.0.term_type {
146 TermType::Iri => write!(f, "<{}>", self.0.value),
147 TermType::BlankNode => write!(f, "_:{}", self.0.value),
148 TermType::Literal { datatype, lang } => {
149 let escaped = self.0.value.replace('\\', "\\\\").replace('"', "\\\"");
151 write!(f, "\"{escaped}\"")?;
152 if let Some(l) = lang {
153 write!(f, "@{l}")?;
154 } else if let Some(dt) = datatype {
155 write!(f, "^^<{dt}>")?;
156 }
157 Ok(())
158 }
159 }
160 }
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
165pub struct PatchTriple {
166 pub subject: PatchTerm,
168 pub predicate: PatchTerm,
170 pub object: PatchTerm,
172}
173
174impl PatchTriple {
175 pub fn new(subject: PatchTerm, predicate: PatchTerm, object: PatchTerm) -> Self {
177 Self {
178 subject,
179 predicate,
180 object,
181 }
182 }
183}
184
185impl fmt::Display for PatchTriple {
186 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187 write!(f, "{} {} {} .", self.subject, self.predicate, self.object)
188 }
189}
190
191#[derive(Debug, Clone, PartialEq, Eq)]
193pub struct PatchQuad {
194 pub subject: PatchTerm,
196 pub predicate: PatchTerm,
198 pub object: PatchTerm,
200 pub graph: PatchTerm,
202}
203
204impl PatchQuad {
205 pub fn new(
207 subject: PatchTerm,
208 predicate: PatchTerm,
209 object: PatchTerm,
210 graph: PatchTerm,
211 ) -> Self {
212 Self {
213 subject,
214 predicate,
215 object,
216 graph,
217 }
218 }
219}
220
221impl fmt::Display for PatchQuad {
222 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223 write!(
224 f,
225 "{} {} {} {} .",
226 self.subject, self.predicate, self.object, self.graph
227 )
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Eq)]
233pub enum PatchChange {
234 AddPrefix {
236 prefix: String,
238 iri: String,
240 },
241 DeletePrefix {
243 prefix: String,
245 iri: String,
247 },
248 AddTriple(PatchTriple),
250 DeleteTriple(PatchTriple),
252 AddQuad(PatchQuad),
254 DeleteQuad(PatchQuad),
256 TransactionBegin,
258 TransactionCommit,
260 TransactionAbort,
262}
263
264impl PatchChange {
265 pub fn line_prefix(&self) -> &'static str {
267 match self {
268 PatchChange::AddPrefix { .. } => "PA",
269 PatchChange::DeletePrefix { .. } => "PD",
270 PatchChange::AddTriple(_) => "A",
271 PatchChange::DeleteTriple(_) => "D",
272 PatchChange::AddQuad(_) => "A",
273 PatchChange::DeleteQuad(_) => "D",
274 PatchChange::TransactionBegin => "TX",
275 PatchChange::TransactionCommit => "TC",
276 PatchChange::TransactionAbort => "TA",
277 }
278 }
279
280 pub fn is_add(&self) -> bool {
282 matches!(
283 self,
284 PatchChange::AddTriple(_) | PatchChange::AddQuad(_) | PatchChange::AddPrefix { .. }
285 )
286 }
287
288 pub fn is_delete(&self) -> bool {
290 matches!(
291 self,
292 PatchChange::DeleteTriple(_)
293 | PatchChange::DeleteQuad(_)
294 | PatchChange::DeletePrefix { .. }
295 )
296 }
297
298 pub fn is_transaction_control(&self) -> bool {
300 matches!(
301 self,
302 PatchChange::TransactionBegin
303 | PatchChange::TransactionCommit
304 | PatchChange::TransactionAbort
305 )
306 }
307}
308
309impl fmt::Display for PatchChange {
310 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311 match self {
312 PatchChange::AddPrefix { prefix, iri } => {
313 write!(f, "PA {prefix} <{iri}>")
314 }
315 PatchChange::DeletePrefix { prefix, iri } => {
316 write!(f, "PD {prefix} <{iri}>")
317 }
318 PatchChange::AddTriple(t) => write!(f, "A {t}"),
319 PatchChange::DeleteTriple(t) => write!(f, "D {t}"),
320 PatchChange::AddQuad(q) => write!(f, "A {q}"),
321 PatchChange::DeleteQuad(q) => write!(f, "D {q}"),
322 PatchChange::TransactionBegin => write!(f, "TX"),
323 PatchChange::TransactionCommit => write!(f, "TC"),
324 PatchChange::TransactionAbort => write!(f, "TA"),
325 }
326 }
327}
328
329#[derive(Debug, Clone, Default)]
333pub struct RdfPatch {
334 pub headers: Vec<PatchHeader>,
336 pub changes: Vec<PatchChange>,
338}
339
340impl RdfPatch {
341 pub fn new() -> Self {
343 Self::default()
344 }
345
346 pub fn with_changes(headers: Vec<PatchHeader>, changes: Vec<PatchChange>) -> Self {
348 Self { headers, changes }
349 }
350
351 pub fn id(&self) -> Option<&str> {
353 self.headers.iter().find_map(|h| {
354 if let PatchHeader::Id(v) = h {
355 Some(v.as_str())
356 } else {
357 None
358 }
359 })
360 }
361
362 pub fn previous(&self) -> Option<&str> {
364 self.headers.iter().find_map(|h| {
365 if let PatchHeader::Previous(v) = h {
366 Some(v.as_str())
367 } else {
368 None
369 }
370 })
371 }
372
373 pub fn add_count(&self) -> usize {
375 self.changes
376 .iter()
377 .filter(|c| matches!(c, PatchChange::AddTriple(_) | PatchChange::AddQuad(_)))
378 .count()
379 }
380
381 pub fn delete_count(&self) -> usize {
383 self.changes
384 .iter()
385 .filter(|c| matches!(c, PatchChange::DeleteTriple(_) | PatchChange::DeleteQuad(_)))
386 .count()
387 }
388
389 pub fn is_empty(&self) -> bool {
391 self.headers.is_empty() && self.changes.is_empty()
392 }
393}
394
395#[derive(Debug, Clone, Default, PartialEq, Eq)]
399pub struct PatchStats {
400 pub triples_added: usize,
402 pub triples_deleted: usize,
404 pub prefixes_added: usize,
406 pub prefixes_deleted: usize,
408 pub transactions: usize,
410 pub aborts: usize,
412}
413
414#[derive(Debug, Clone, Default)]
421pub struct Graph {
422 pub triples: HashSet<String>,
424 pub prefixes: BTreeMap<String, String>,
426 triple_objects: Vec<PatchTriple>,
428}
429
430impl Graph {
431 pub fn new() -> Self {
433 Self::default()
434 }
435
436 pub fn add_triple(&mut self, triple: PatchTriple) -> bool {
438 let key = Self::triple_key(&triple);
439 if self.triples.insert(key) {
440 self.triple_objects.push(triple);
441 true
442 } else {
443 false
444 }
445 }
446
447 pub fn remove_triple(&mut self, triple: &PatchTriple) -> bool {
449 let key = Self::triple_key(triple);
450 if self.triples.remove(&key) {
451 self.triple_objects.retain(|t| Self::triple_key(t) != key);
452 true
453 } else {
454 false
455 }
456 }
457
458 pub fn contains(&self, triple: &PatchTriple) -> bool {
460 self.triples.contains(&Self::triple_key(triple))
461 }
462
463 pub fn len(&self) -> usize {
465 self.triples.len()
466 }
467
468 pub fn is_empty(&self) -> bool {
470 self.triples.is_empty()
471 }
472
473 pub fn iter(&self) -> impl Iterator<Item = &PatchTriple> {
475 self.triple_objects.iter()
476 }
477
478 pub(crate) fn triple_key(t: &PatchTriple) -> String {
479 format!("{}\x00{}\x00{}", t.subject, t.predicate, t.object)
480 }
481}