1pub mod model;
10pub mod tableau;
11pub mod reasoner;
12pub mod loader;
13
14pub use model::{Ontology, Class, Property, Individual, Axiom};
15pub use reasoner::OwlLiteReasoner;
16pub use loader::OntologyLoader;
17
18use thiserror::Error;
20
21#[derive(Error, Debug)]
22pub enum OwlError {
23 #[error("Loader error: {0}")]
24 LoaderError(String),
25
26 #[error("Reasoning error: {0}")]
27 ReasoningError(String),
28
29 #[error("Consistency error: {0}")]
30 ConsistencyError(String),
31
32 #[error("Unsupported feature: {0}")]
33 UnsupportedFeature(String),
34}
35
36#[cfg(test)]
37fn load_ontology_from_triples(triples: Vec<fukurow_core::model::Triple>) -> Result<crate::model::Ontology, OwlError> {
38 let mut store = fukurow_store::store::RdfStore::new();
39 for triple in triples {
40 store.insert(triple, fukurow_store::provenance::GraphId::Default, fukurow_store::provenance::Provenance::Sensor {
41 source: "test".to_string(),
42 confidence: None,
43 });
44 }
45 let loader = crate::loader::DefaultOntologyLoader;
46 loader.load_from_store(&store)
47}
48
49#[cfg(test)]
50mod tests {
51 use super::*;
52 use fukurow_core::model::Triple;
53
54 mod loader_tests {
55 use super::*;
56 use crate::model::{Ontology, Class, Property, Individual, Axiom, OwlIri};
57
58 fn create_test_triples() -> Vec<Triple> {
59 vec![
60 Triple {
62 subject: "http://example.org/Person".to_string(),
63 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
64 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
65 },
66 Triple {
67 subject: "http://example.org/Animal".to_string(),
68 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
69 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
70 },
71 Triple {
73 subject: "http://example.org/hasParent".to_string(),
74 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
75 object: "http://www.w3.org/2002/07/owl#ObjectProperty".to_string(),
76 },
77 Triple {
78 subject: "http://example.org/hasAge".to_string(),
79 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
80 object: "http://www.w3.org/2002/07/owl#DatatypeProperty".to_string(),
81 },
82 Triple {
84 subject: "http://example.org/john".to_string(),
85 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
86 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
87 },
88 Triple {
89 subject: "http://example.org/mary".to_string(),
90 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
91 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
92 },
93 Triple {
95 subject: "http://example.org/john".to_string(),
96 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
97 object: "http://example.org/Person".to_string(),
98 },
99 Triple {
100 subject: "http://example.org/mary".to_string(),
101 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
102 object: "http://example.org/Person".to_string(),
103 },
104 Triple {
106 subject: "http://example.org/Student".to_string(),
107 predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
108 object: "http://example.org/Person".to_string(),
109 },
110 ]
111 }
112
113
114 #[test]
115 fn test_load_empty_ontology() {
116 let triples = Vec::new();
117 let ontology = load_ontology_from_triples(triples).unwrap();
118
119 assert!(ontology.classes.is_empty());
120 assert!(ontology.properties.is_empty());
121 assert!(ontology.individuals.is_empty());
122 assert!(ontology.axioms.is_empty());
123 }
124
125 #[test]
126 fn test_load_classes() {
127 let triples = vec![
128 Triple {
129 subject: "http://example.org/Person".to_string(),
130 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
131 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
132 },
133 Triple {
134 subject: "http://example.org/Animal".to_string(),
135 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
136 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
137 },
138 ];
139
140 let ontology = load_ontology_from_triples(triples).unwrap();
141
142 assert_eq!(ontology.classes.len(), 2);
143 assert!(ontology.classes.contains(&Class::Named(OwlIri::new("http://example.org/Person".to_string()))));
144 assert!(ontology.classes.contains(&Class::Named(OwlIri::new("http://example.org/Animal".to_string()))));
145 }
146
147 #[test]
148 fn test_load_properties() {
149 let triples = vec![
150 Triple {
151 subject: "http://example.org/hasParent".to_string(),
152 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
153 object: "http://www.w3.org/2002/07/owl#ObjectProperty".to_string(),
154 },
155 Triple {
156 subject: "http://example.org/hasAge".to_string(),
157 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
158 object: "http://www.w3.org/2002/07/owl#DatatypeProperty".to_string(),
159 },
160 ];
161
162 let ontology = load_ontology_from_triples(triples).unwrap();
163
164 assert_eq!(ontology.properties.len(), 2);
165 assert!(ontology.properties.contains(&Property::Object(OwlIri::new("http://example.org/hasParent".to_string()))));
166 assert!(ontology.properties.contains(&Property::Data(OwlIri::new("http://example.org/hasAge".to_string()))));
167 }
168
169 #[test]
170 fn test_load_individuals() {
171 let triples = vec![
172 Triple {
173 subject: "http://example.org/john".to_string(),
174 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
175 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
176 },
177 Triple {
178 subject: "http://example.org/mary".to_string(),
179 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
180 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
181 },
182 ];
183
184 let ontology = load_ontology_from_triples(triples).unwrap();
185
186 assert_eq!(ontology.individuals.len(), 2);
187 assert!(ontology.individuals.contains(&Individual(OwlIri::new("http://example.org/john".to_string()))));
188 assert!(ontology.individuals.contains(&Individual(OwlIri::new("http://example.org/mary".to_string()))));
189 }
190
191 #[test]
192 fn test_load_class_assertions() {
193 let triples = vec![
194 Triple {
196 subject: "http://example.org/john".to_string(),
197 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
198 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
199 },
200 Triple {
202 subject: "http://example.org/Person".to_string(),
203 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
204 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
205 },
206 Triple {
208 subject: "http://example.org/john".to_string(),
209 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
210 object: "http://example.org/Person".to_string(),
211 },
212 ];
213
214 let ontology = load_ontology_from_triples(triples).unwrap();
215
216 assert_eq!(ontology.classes.len(), 1);
217 assert_eq!(ontology.individuals.len(), 1);
218 assert_eq!(ontology.axioms.len(), 1);
219
220 match &ontology.axioms[0] {
222 Axiom::ClassAssertion(class, individual) => {
223 assert_eq!(*class, Class::Named(OwlIri::new("http://example.org/Person".to_string())));
224 assert_eq!(*individual, Individual(OwlIri::new("http://example.org/john".to_string())));
225 }
226 _ => panic!("Expected ClassAssertion axiom"),
227 }
228 }
229
230 #[test]
231 fn test_load_subclass_relation() {
232 let triples = vec![
233 Triple {
235 subject: "http://example.org/Person".to_string(),
236 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
237 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
238 },
239 Triple {
240 subject: "http://example.org/Student".to_string(),
241 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
242 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
243 },
244 Triple {
246 subject: "http://example.org/Student".to_string(),
247 predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
248 object: "http://example.org/Person".to_string(),
249 },
250 ];
251
252 let ontology = load_ontology_from_triples(triples).unwrap();
253
254 assert_eq!(ontology.classes.len(), 2);
255 assert_eq!(ontology.axioms.len(), 1);
256
257 match &ontology.axioms[0] {
259 Axiom::SubClassOf(subclass, superclass) => {
260 assert_eq!(*subclass, Class::Named(OwlIri::new("http://example.org/Student".to_string())));
261 assert_eq!(*superclass, Class::Named(OwlIri::new("http://example.org/Person".to_string())));
262 }
263 _ => panic!("Expected SubClassOf axiom"),
264 }
265 }
266
267 #[test]
268 fn test_load_complex_ontology() {
269 let triples = create_test_triples();
270
271 let ontology = load_ontology_from_triples(triples).unwrap();
272
273 assert_eq!(ontology.classes.len(), 3); assert_eq!(ontology.properties.len(), 2); assert_eq!(ontology.individuals.len(), 2); assert_eq!(ontology.axioms.len(), 3); }
278 }
279
280 mod reasoner_tests {
281 use super::*;
282 use crate::reasoner::OwlLiteReasoner;
283 use crate::model::{Class, Individual, OwlIri};
284
285 #[test]
286 fn test_reasoner_creation() {
287 let reasoner = OwlLiteReasoner::new();
288 assert!(true); }
291
292 #[test]
293 fn test_consistency_check() {
294 let reasoner = OwlLiteReasoner::new();
295
296 let triples = vec![
298 Triple {
299 subject: "http://example.org/Person".to_string(),
300 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
301 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
302 },
303 Triple {
304 subject: "http://example.org/john".to_string(),
305 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
306 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
307 },
308 Triple {
309 subject: "http://example.org/john".to_string(),
310 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
311 object: "http://example.org/Person".to_string(),
312 },
313 ];
314
315 let ontology = load_ontology_from_triples(triples).unwrap();
316 let mut reasoner = OwlLiteReasoner::new();
317 let result = reasoner.is_consistent(&ontology);
318
319 assert!(result.unwrap_or(false));
321 }
322
323 #[test]
324 fn test_class_hierarchy_inference() {
325 let reasoner = OwlLiteReasoner::new();
326
327 let triples = vec![
328 Triple {
330 subject: "http://example.org/Person".to_string(),
331 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
332 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
333 },
334 Triple {
335 subject: "http://example.org/Student".to_string(),
336 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
337 object: "http://www.w3.org/2002/07/owl#Class".to_string(),
338 },
339 Triple {
341 subject: "http://example.org/Student".to_string(),
342 predicate: "http://www.w3.org/2000/01/rdf-schema#subClassOf".to_string(),
343 object: "http://example.org/Person".to_string(),
344 },
345 Triple {
347 subject: "http://example.org/john".to_string(),
348 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
349 object: "http://www.w3.org/2002/07/owl#NamedIndividual".to_string(),
350 },
351 Triple {
353 subject: "http://example.org/john".to_string(),
354 predicate: "http://www.w3.org/1999/02/22-rdf-syntax-ns#type".to_string(),
355 object: "http://example.org/Student".to_string(),
356 },
357 ];
358
359 let ontology = load_ontology_from_triples(triples).unwrap();
360 let mut reasoner = OwlLiteReasoner::new();
361 let inferred = reasoner.get_inferred_axioms(&ontology).unwrap();
362
363 let john_person_assertion = inferred.iter().any(|axiom| {
365 matches!(axiom, Axiom::ClassAssertion(class, individual)
366 if *class == Class::Named(OwlIri::new("http://example.org/Person".to_string()))
367 && *individual == Individual(OwlIri::new("http://example.org/john".to_string())))
368 });
369
370 assert!(john_person_assertion, "john should be inferred to be a Person");
371 }
372 }
373
374 mod model_tests {
375 use super::*;
376 use crate::model::{Ontology, Class, Property, Individual, OwlIri};
377
378 #[test]
379 fn test_ontology_creation() {
380 let ontology = Ontology::new();
381 assert!(ontology.classes.is_empty());
382 assert!(ontology.properties.is_empty());
383 assert!(ontology.individuals.is_empty());
384 assert!(ontology.axioms.is_empty());
385 }
386
387 #[test]
388 fn test_class_equality() {
389 let class1 = Class::Named(OwlIri::new("http://example.org/Person".to_string()));
390 let class2 = Class::Named(OwlIri::new("http://example.org/Person".to_string()));
391 let class3 = Class::Named(OwlIri::new("http://example.org/Animal".to_string()));
392
393 assert_eq!(class1, class2);
394 assert_ne!(class1, class3);
395 }
396
397 #[test]
398 fn test_individual_equality() {
399 let ind1 = Individual(OwlIri::new("http://example.org/john".to_string()));
400 let ind2 = Individual(OwlIri::new("http://example.org/john".to_string()));
401 let ind3 = Individual(OwlIri::new("http://example.org/mary".to_string()));
402
403 assert_eq!(ind1, ind2);
404 assert_ne!(ind1, ind3);
405 }
406
407 #[test]
408 fn test_property_equality() {
409 let prop1 = Property::Object(OwlIri::new("http://example.org/hasParent".to_string()));
410 let prop2 = Property::Object(OwlIri::new("http://example.org/hasParent".to_string()));
411 let prop3 = Property::Data(OwlIri::new("http://example.org/hasAge".to_string()));
412
413 assert_eq!(prop1, prop2);
414 assert_ne!(prop1, prop3);
415 }
416
417 #[test]
418 fn test_owl_iri_creation() {
419 let iri = OwlIri::new("http://example.org/Person".to_string());
420 assert_eq!(iri.0, "http://example.org/Person");
421 }
422 }
423}