1use crate::common::inc;
4use crate::reasoner::{Instantiations, Quad};
5use crate::translator::Translator;
6use alloc::collections::BTreeMap;
7use alloc::collections::BTreeSet;
8use core::fmt::Debug;
9use core::fmt::Display;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
12pub(crate) struct LowRule {
25 pub if_all: Vec<Quad>, pub then: Vec<Quad>, pub inst: Instantiations, }
29
30#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
31#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
32pub enum Entity<Unbound, Bound> {
33 Unbound(Unbound),
34 Bound(Bound),
35}
36
37impl<Unbound, Bound> Entity<Unbound, Bound> {
38 pub fn as_unbound(&self) -> Option<&Unbound> {
39 match self {
40 Entity::Unbound(s) => Some(s),
41 Entity::Bound(_) => None,
42 }
43 }
44
45 pub fn as_bound(&self) -> Option<&Bound> {
46 match self {
47 Entity::Unbound(_) => None,
48 Entity::Bound(s) => Some(s),
49 }
50 }
51}
52
53#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Clone)]
54#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
55pub struct Rule<Unbound, Bound> {
64 if_all: Vec<[Entity<Unbound, Bound>; 4]>,
65 then: Vec<[Entity<Unbound, Bound>; 4]>,
66}
67
68impl<'a, Unbound: Ord + Clone, Bound: Ord> Rule<Unbound, Bound> {
69 pub fn create(
73 if_all: Vec<[Entity<Unbound, Bound>; 4]>,
74 then: Vec<[Entity<Unbound, Bound>; 4]>,
75 ) -> Result<Self, InvalidRule<Unbound>> {
76 let unbound_if = if_all.iter().flatten().filter_map(Entity::as_unbound);
77 let unbound_then = then.iter().flatten().filter_map(Entity::as_unbound);
78
79 for th in unbound_then.clone() {
80 if !unbound_if.clone().any(|ifa| ifa == th) {
81 return Err(InvalidRule::UnboundImplied(th.clone()));
82 }
83 }
84
85 Ok(Self { if_all, then })
86 }
87
88 pub(crate) fn lower(&self, tran: &Translator<Bound>) -> Result<LowRule, NoTranslation<&Bound>> {
93 let mut next_local = 0usize;
102 let unbound_map: BTreeMap<&Unbound, usize> = assign_ids(
103 self.if_all.iter().flatten().filter_map(Entity::as_unbound),
104 &mut next_local,
105 );
106 let bound_map = assign_ids(
107 self.iter_entities().filter_map(Entity::as_bound),
108 &mut next_local,
109 );
110 debug_assert!(
111 bound_map.values().all(|bound_local| unbound_map
112 .values()
113 .all(|unbound_local| bound_local > unbound_local)),
114 "unbound names are smaller than bound names"
115 );
116 debug_assert_eq!(
117 (0..next_local).collect::<BTreeSet<usize>>(),
118 unbound_map
119 .values()
120 .chain(bound_map.values())
121 .cloned()
122 .collect(),
123 "no names slots are wasted"
124 );
125 debug_assert_eq!(
126 next_local,
127 unbound_map.values().chain(bound_map.values()).count(),
128 "no duplicate assignments"
129 );
130
131 let local_name = |entity: &Entity<Unbound, Bound>| -> usize {
133 match entity {
134 Entity::Unbound(s) => unbound_map[s],
135 Entity::Bound(s) => bound_map[s],
136 }
137 };
138 let to_requirements = |hu: &[[Entity<Unbound, Bound>; 4]]| -> Vec<Quad> {
141 hu.iter()
142 .map(|[s, p, o, g]| {
143 [
144 local_name(&s),
145 local_name(&p),
146 local_name(&o),
147 local_name(&g),
148 ]
149 .into()
150 })
151 .collect()
152 };
153
154 Ok(LowRule {
155 if_all: to_requirements(&self.if_all),
156 then: to_requirements(&self.then),
157 inst: bound_map
158 .iter()
159 .map(|(human_name, local_name)| {
160 let global_name = tran.forward(human_name).ok_or(NoTranslation(*human_name))?;
161 Ok((*local_name, global_name))
162 })
163 .collect::<Result<_, _>>()?,
164 })
165 }
166}
167
168impl<'a, Unbound: Ord, Bound> Rule<Unbound, Bound> {
169 pub(crate) fn cononical_unbound(&self) -> impl Iterator<Item = &Unbound> {
171 let mut listed = BTreeSet::<&Unbound>::new();
172 self.if_all
173 .iter()
174 .flatten()
175 .filter_map(Entity::as_unbound)
176 .filter(move |unbound| listed.insert(unbound))
177 }
178}
179
180impl<'a, Unbound, Bound> Rule<Unbound, Bound> {
181 pub fn iter_entities(&self) -> impl Iterator<Item = &Entity<Unbound, Bound>> {
182 self.if_all.iter().chain(self.then.iter()).flatten()
183 }
184
185 pub fn if_all(&self) -> &[[Entity<Unbound, Bound>; 4]] {
186 &self.if_all
187 }
188
189 pub fn then(&self) -> &[[Entity<Unbound, Bound>; 4]] {
190 &self.then
191 }
192}
193
194#[derive(Clone, Debug, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
196pub enum InvalidRule<Unbound> {
197 UnboundImplied(Unbound),
210}
211
212impl<Unbound: Debug> Display for InvalidRule<Unbound> {
213 fn fmt(&self, fmtr: &mut core::fmt::Formatter<'_>) -> Result<(), core::fmt::Error> {
214 Debug::fmt(self, fmtr)
215 }
216}
217
218#[cfg(feature = "std")]
219impl<Unbound> std::error::Error for InvalidRule<Unbound> where InvalidRule<Unbound>: Debug + Display {}
220
221#[derive(Debug, Eq, PartialEq)]
222pub struct NoTranslation<T>(T);
224
225fn assign_ids<T: Ord>(inp: impl Iterator<Item = T>, next_id: &mut usize) -> BTreeMap<T, usize> {
229 let mut ret: BTreeMap<T, usize> = BTreeMap::new();
230 for unbound in inp {
231 ret.entry(unbound).or_insert_with(|| inc(next_id));
232 }
233 ret
234}
235
236#[cfg(test)]
237mod test {
238 use super::Entity::{Bound, Unbound};
239 use super::*;
240 use core::iter::FromIterator;
241
242 #[test]
243 fn similar_names() {
244 let rule = Rule::<&str, &str> {
249 if_all: vec![[Unbound("a"), Bound("a"), Unbound("b"), Unbound("g")]],
250 then: vec![],
251 };
252 let trans: Translator<&str> = ["a"].iter().cloned().collect();
253 let rr = rule.lower(&trans).unwrap();
254
255 assert_ne!(rr.if_all[0].s.0, rr.if_all[0].p.0);
257 }
258
259 #[test]
260 fn lower() {
261 let trans: Translator<&str> = ["parent", "ancestor"].iter().cloned().collect();
265
266 {
267 let rulea = Rule::<u16, &str> {
268 if_all: vec![[Unbound(0xa), Bound("parent"), Unbound(0xb), Unbound(0xc)]],
269 then: vec![[Unbound(0xa), Bound("ancestor"), Unbound(0xb), Unbound(0xc)]],
270 };
271
272 let re_rulea = rulea.lower(&trans).unwrap();
273 let keys = [
274 re_rulea.if_all[0].s.0,
275 re_rulea.if_all[0].p.0,
276 re_rulea.if_all[0].o.0,
277 re_rulea.if_all[0].g.0,
278 re_rulea.then[0].s.0,
279 re_rulea.then[0].p.0,
280 re_rulea.then[0].o.0,
281 re_rulea.then[0].g.0,
282 ];
283 let vals: Vec<Option<&str>> = keys
284 .iter()
285 .map(|local_name| {
286 re_rulea
287 .inst
288 .get(*local_name)
289 .map(|global_name| trans.back(*global_name).unwrap().clone())
290 })
291 .collect();
292 assert_eq!(
293 &vals,
294 &[
295 None,
296 Some("parent"),
297 None,
298 None,
299 None,
300 Some("ancestor"),
301 None,
302 None,
303 ]
304 );
305
306 let ifa = re_rulea.if_all;
307 let then = re_rulea.then;
308 assert_ne!(ifa[0].p.0, then[0].p.0); assert_eq!(ifa[0].s.0, then[0].s.0); assert_eq!(ifa[0].o.0, then[0].o.0); }
312
313 {
314 let ruleb = Rule::<&str, &str> {
315 if_all: vec![
316 [Unbound("a"), Bound("ancestor"), Unbound("b"), Unbound("g")],
317 [Unbound("b"), Bound("ancestor"), Unbound("c"), Unbound("g")],
318 ],
319 then: vec![[Unbound("a"), Bound("ancestor"), Unbound("c"), Unbound("g")]],
320 };
321
322 let re_ruleb = ruleb.lower(&trans).unwrap();
323 let keys = [
324 re_ruleb.if_all[0].s.0,
325 re_ruleb.if_all[0].p.0,
326 re_ruleb.if_all[0].o.0,
327 re_ruleb.if_all[0].g.0,
328 re_ruleb.if_all[1].s.0,
329 re_ruleb.if_all[1].p.0,
330 re_ruleb.if_all[1].o.0,
331 re_ruleb.if_all[1].g.0,
332 re_ruleb.then[0].s.0,
333 re_ruleb.then[0].p.0,
334 re_ruleb.then[0].o.0,
335 re_ruleb.then[0].g.0,
336 ];
337 let vals: Vec<Option<&str>> = keys
338 .iter()
339 .map(|local_name| {
340 re_ruleb
341 .inst
342 .get(*local_name)
343 .map(|global_name| trans.back(*global_name).unwrap().clone())
344 })
345 .collect();
346 assert_eq!(
347 &vals,
348 &[
349 None,
350 Some("ancestor"),
351 None,
352 None,
353 None,
354 Some("ancestor"),
355 None,
356 None,
357 None,
358 Some("ancestor"),
359 None,
360 None,
361 ]
362 );
363
364 let ifa = re_ruleb.if_all;
365 let then = re_ruleb.then;
366 assert_ne!(ifa[0].s.0, ifa[1].s.0); assert_ne!(ifa[0].o.0, then[0].o.0); assert_eq!(ifa[0].p.0, ifa[1].p.0);
371 assert_eq!(ifa[1].p.0, then[0].p.0);
372
373 assert_eq!(ifa[0].s.0, then[0].s.0); assert_eq!(ifa[1].o.0, then[0].o.0); }
376 }
377
378 #[test]
379 fn lower_no_translation_err() {
380 let trans = Translator::<&str>::from_iter(vec![]);
381
382 let r = Rule::create(
383 vec![[Unbound("a"), Bound("unknown"), Unbound("b"), Unbound("g")]],
384 vec![],
385 )
386 .unwrap();
387 let err = r.lower(&trans).unwrap_err();
388 assert_eq!(err, NoTranslation(&"unknown"));
389
390 let r = Rule::<&str, &str>::create(
391 vec![],
392 vec![[
393 Bound("unknown"),
394 Bound("unknown"),
395 Bound("unknown"),
396 Bound("unknown"),
397 ]],
398 )
399 .unwrap();
400 let err = r.lower(&trans).unwrap_err();
401 assert_eq!(err, NoTranslation(&"unknown"));
402 }
403
404 #[test]
405 fn create_invalid() {
406 use Bound as B;
407 use Unbound as U;
408
409 Rule::<&str, &str>::create(vec![], vec![[U("a"), U("a"), U("a"), U("a")]]).unwrap_err();
410
411 let ret = Rule::<&str, &str>::create(
422 vec![
423 [U("super"), B("claims"), U("claim1"), U("g")],
425 [U("claim1"), B("subject"), U("minor"), U("g")],
426 [U("claim1"), B("predicate"), B("mayclaim"), U("g")],
427 [U("claim1"), B("object"), U("pred"), U("g")],
428 [U("minor"), B("claims"), U("claim2"), U("g")],
430 [U("claim2"), B("subject"), U("s"), U("g")],
431 [U("claim2"), B("predicate"), U("pred"), U("g")],
432 [U("claim2"), B("object"), U("o"), U("g")],
433 ],
434 vec![
435 [U("super"), B("claims"), U("claim3"), U("g")],
437 [U("claim3"), B("subject"), U("s"), U("g")],
438 [U("claim3"), B("predicate"), U("pred"), U("g")],
439 [U("claim3"), B("object"), U("o"), U("g")],
440 ],
441 );
442 assert_eq!(ret, Err(InvalidRule::UnboundImplied("claim3")));
443
444 }
483
484 #[test]
485 fn serde() {
486 #[derive(Debug, serde::Serialize, serde::Deserialize, PartialEq, Eq)]
487 enum Term {
488 Blank(String),
489 Iri(String),
490 Literal {
491 value: String,
492 datatype: String,
493 #[serde(skip_serializing_if = "Option::is_none")]
494 language: Option<String>,
495 },
496 DefaultGraph,
497 }
498
499 let jsonrule = serde_json::json![{
500 "if_all": [
501 [
502 { "Unbound": "pig" },
503 { "Bound": { "Iri": "https://example.com/Ability" } },
504 { "Bound": { "Iri": "https://example.com/Flight" } },
505 { "Bound": "DefaultGraph" },
506 ],
507 [
508 { "Unbound": "pig" },
509 { "Bound": { "Iri": "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" } },
510 { "Bound": { "Iri": "https://example.com/Pig" } },
511 { "Bound": "DefaultGraph" },
512 ],
513 ],
514 "then": [
515 [
516 { "Bound": { "Iri": "did:dock:bddap" } },
517 { "Bound": { "Iri": "http://xmlns.com/foaf/spec/#term_firstName" } },
518 {
519 "Bound": {
520 "Literal": {
521 "value": "Gorgadon",
522 "datatype": "http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral",
523 },
524 },
525 },
526 { "Bound": "DefaultGraph" },
527 ],
528 ],
529 }];
530
531 let rule = Rule::<String, Term> {
532 if_all: vec![
533 [
534 Entity::Unbound("pig".to_string()),
535 Entity::Bound(Term::Iri("https://example.com/Ability".to_string())),
536 Entity::Bound(Term::Iri("https://example.com/Flight".to_string())),
537 Entity::Bound(Term::DefaultGraph),
538 ],
539 [
540 Entity::Unbound("pig".to_string()),
541 Entity::Bound(Term::Iri(
542 "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
543 )),
544 Entity::Bound(Term::Iri("https://example.com/Pig".to_string())),
545 Entity::Bound(Term::DefaultGraph),
546 ],
547 ],
548 then: vec![[
549 Entity::Bound(Term::Iri("did:dock:bddap".to_string())),
550 Entity::Bound(Term::Iri(
551 "http://xmlns.com/foaf/spec/#term_firstName".to_string(),
552 )),
553 Entity::Bound(Term::Literal {
554 value: "Gorgadon".to_string(),
555 datatype: "http://www.w3.org/1999/02/22-rdf-syntax-ns#PlainLiteral".to_string(),
556 language: None,
557 }),
558 Entity::Bound(Term::DefaultGraph),
559 ]],
560 };
561
562 assert_eq!(
563 &serde_json::from_value::<Rule::<String, Term>>(jsonrule.clone()).unwrap(),
564 &rule
565 );
566 assert_eq!(
567 &jsonrule,
568 &serde_json::to_value::<Rule::<String, Term>>(rule).unwrap()
569 );
570 }
571}