1use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "snake_case")]
7pub enum SwrlIArg {
8 Individual(String),
9 Variable(String),
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum SwrlDArg {
15 Literal { lexical: String, datatype: Option<String> },
16 Variable(String),
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(tag = "kind", rename_all = "snake_case")]
21pub enum SwrlAtom {
22 Class { class: String, arg: SwrlIArg },
23 ObjectProperty { property: String, subject: SwrlIArg, object: SwrlIArg },
24 DataProperty { property: String, subject: SwrlIArg, value: SwrlDArg },
25 SameIndividual { left: SwrlIArg, right: SwrlIArg },
26 DifferentIndividuals { left: SwrlIArg, right: SwrlIArg },
27 BuiltIn { predicate: String, args: Vec<SwrlDArg> },
28 DataRange { range: String, arg: SwrlDArg },
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct SwrlRule {
33 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub id: Option<String>,
36 pub body: Vec<SwrlAtom>,
37 pub head: Vec<SwrlAtom>,
38 #[serde(default = "default_enabled")]
39 pub enabled: bool,
40}
41
42fn default_enabled() -> bool {
43 true
44}
45
46#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
47pub struct SwrlRuleSummary {
48 pub id: String,
49 pub label: String,
50 pub body_count: usize,
51 pub head_count: usize,
52 pub enabled: bool,
53}
54
55impl SwrlRule {
56 pub fn summary(&self, index: usize) -> SwrlRuleSummary {
57 let id = self.id.clone().unwrap_or_else(|| format!("rule-{index}"));
58 let label = format!(
59 "{} → {}",
60 self.body.len(),
61 self.head.first().map(atom_short).unwrap_or_else(|| "∅".into())
62 );
63 SwrlRuleSummary {
64 id,
65 label,
66 body_count: self.body.len(),
67 head_count: self.head.len(),
68 enabled: self.enabled,
69 }
70 }
71
72 pub fn referenced_iris(&self) -> Vec<String> {
73 let mut out = Vec::new();
74 for atom in self.body.iter().chain(self.head.iter()) {
75 collect_iris(atom, &mut out);
76 }
77 out.sort();
78 out.dedup();
79 out
80 }
81
82 pub fn remap_iri(&mut self, from: &str, to: &str) -> bool {
84 if from == to {
85 return false;
86 }
87 let mut changed = false;
88 for atom in self.body.iter_mut().chain(self.head.iter_mut()) {
89 if remap_atom(atom, from, to) {
90 changed = true;
91 }
92 }
93 if let Some(id) = &mut self.id {
94 if id == from {
95 *id = to.to_string();
96 changed = true;
97 }
98 }
99 changed
100 }
101}
102
103fn remap_atom(atom: &mut SwrlAtom, from: &str, to: &str) -> bool {
104 let mut changed = false;
105 let replace = |s: &mut String| {
106 if s.as_str() == from {
107 *s = to.to_string();
108 true
109 } else {
110 false
111 }
112 };
113 match atom {
114 SwrlAtom::Class { class, arg } => {
115 changed |= replace(class);
116 if let SwrlIArg::Individual(i) = arg {
117 changed |= replace(i);
118 }
119 }
120 SwrlAtom::ObjectProperty { property, subject, object } => {
121 changed |= replace(property);
122 if let SwrlIArg::Individual(i) = subject {
123 changed |= replace(i);
124 }
125 if let SwrlIArg::Individual(i) = object {
126 changed |= replace(i);
127 }
128 }
129 SwrlAtom::DataProperty { property, subject, .. } => {
130 changed |= replace(property);
131 if let SwrlIArg::Individual(i) = subject {
132 changed |= replace(i);
133 }
134 }
135 SwrlAtom::SameIndividual { left: a, right: b }
136 | SwrlAtom::DifferentIndividuals { left: a, right: b } => {
137 if let SwrlIArg::Individual(i) = a {
138 changed |= replace(i);
139 }
140 if let SwrlIArg::Individual(i) = b {
141 changed |= replace(i);
142 }
143 }
144 SwrlAtom::BuiltIn { predicate, .. } => {
145 changed |= replace(predicate);
146 }
147 SwrlAtom::DataRange { range, .. } => {
148 changed |= replace(range);
149 }
150 }
151 changed
152}
153
154fn atom_short(atom: &SwrlAtom) -> String {
155 match atom {
156 SwrlAtom::Class { class, .. } => short(class),
157 SwrlAtom::ObjectProperty { property, .. } => short(property),
158 SwrlAtom::DataProperty { property, .. } => short(property),
159 SwrlAtom::BuiltIn { predicate, .. } => short(predicate),
160 SwrlAtom::SameIndividual { .. } => "sameAs".into(),
161 SwrlAtom::DifferentIndividuals { .. } => "differentFrom".into(),
162 SwrlAtom::DataRange { range, .. } => short(range),
163 }
164}
165
166fn short(iri: &str) -> String {
167 iri.rsplit(['#', '/']).next().unwrap_or(iri).to_string()
168}
169
170fn collect_iris(atom: &SwrlAtom, out: &mut Vec<String>) {
171 match atom {
172 SwrlAtom::Class { class, arg } => {
173 out.push(class.clone());
174 if let SwrlIArg::Individual(i) = arg {
175 out.push(i.clone());
176 }
177 }
178 SwrlAtom::ObjectProperty { property, subject, object } => {
179 out.push(property.clone());
180 if let SwrlIArg::Individual(i) = subject {
181 out.push(i.clone());
182 }
183 if let SwrlIArg::Individual(i) = object {
184 out.push(i.clone());
185 }
186 }
187 SwrlAtom::DataProperty { property, subject, .. } => {
188 out.push(property.clone());
189 if let SwrlIArg::Individual(i) = subject {
190 out.push(i.clone());
191 }
192 }
193 SwrlAtom::SameIndividual { left: a, right: b }
194 | SwrlAtom::DifferentIndividuals { left: a, right: b } => {
195 if let SwrlIArg::Individual(i) = a {
196 out.push(i.clone());
197 }
198 if let SwrlIArg::Individual(i) = b {
199 out.push(i.clone());
200 }
201 }
202 SwrlAtom::BuiltIn { predicate, .. } => out.push(predicate.clone()),
203 SwrlAtom::DataRange { range, .. } => out.push(range.clone()),
204 }
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
208#[serde(rename_all = "snake_case")]
209pub enum SwrlSeverity {
210 Error,
211 Warning,
212 Info,
213}
214
215#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
216pub struct SwrlDiagnostic {
217 pub code: String,
218 pub severity: SwrlSeverity,
219 pub message: String,
220}