1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3use std::path::PathBuf;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum OntologyFormat {
9 Turtle,
10 RdfXml,
11 Owl,
12 JsonLd,
13 NTriples,
14 NQuads,
15 TriG,
16 Obo,
17 Unknown,
18}
19
20impl OntologyFormat {
21 pub fn from_extension(ext: &str) -> Self {
22 match ext.to_ascii_lowercase().as_str() {
23 "ttl" => Self::Turtle,
24 "rdf" => Self::RdfXml,
25 "owl" => Self::Owl,
26 "jsonld" | "json-ld" => Self::JsonLd,
27 "nt" => Self::NTriples,
28 "nq" => Self::NQuads,
29 "trig" => Self::TriG,
30 "obo" => Self::Obo,
31 _ => Self::Unknown,
32 }
33 }
34
35 pub fn as_str(&self) -> &'static str {
36 match self {
37 Self::Turtle => "turtle",
38 Self::RdfXml => "rdf_xml",
39 Self::Owl => "owl",
40 Self::JsonLd => "json_ld",
41 Self::NTriples => "n_triples",
42 Self::NQuads => "n_quads",
43 Self::TriG => "trig",
44 Self::Obo => "obo",
45 Self::Unknown => "unknown",
46 }
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
51#[serde(rename_all = "snake_case")]
52pub enum ParseStatus {
53 Ok,
54 Warning,
55 Error,
56}
57
58impl ParseStatus {
59 pub fn as_str(&self) -> &'static str {
60 match self {
61 Self::Ok => "ok",
62 Self::Warning => "warning",
63 Self::Error => "error",
64 }
65 }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
69#[serde(rename_all = "snake_case")]
70pub enum EntityKind {
71 Class,
72 ObjectProperty,
73 DataProperty,
74 AnnotationProperty,
75 Individual,
76 Ontology,
77 Other,
78}
79
80impl EntityKind {
81 pub fn as_str(&self) -> &'static str {
82 match self {
83 Self::Class => "class",
84 Self::ObjectProperty => "object_property",
85 Self::DataProperty => "data_property",
86 Self::AnnotationProperty => "annotation_property",
87 Self::Individual => "individual",
88 Self::Ontology => "ontology",
89 Self::Other => "other",
90 }
91 }
92}
93
94#[derive(Debug, Clone, Default, Serialize, Deserialize)]
95pub struct SourceLocation {
96 pub line: Option<u64>,
97 pub column: Option<u64>,
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub start_byte: Option<u64>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub end_byte: Option<u64>,
102}
103
104impl SourceLocation {
105 pub fn is_empty(&self) -> bool {
106 self.line.is_none()
107 && self.column.is_none()
108 && self.start_byte.is_none()
109 && self.end_byte.is_none()
110 }
111
112 pub fn at_line_col(line: u64, column: u64) -> Self {
113 Self { line: Some(line), column: Some(column), start_byte: None, end_byte: None }
114 }
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
118#[serde(rename_all = "snake_case")]
119pub enum DiagnosticSeverity {
120 Error,
121 Warning,
122 Info,
123}
124
125impl DiagnosticSeverity {
126 pub fn as_str(&self) -> &'static str {
127 match self {
128 Self::Error => "error",
129 Self::Warning => "warning",
130 Self::Info => "info",
131 }
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
136#[serde(rename_all = "snake_case")]
137pub enum DiagnosticCode {
138 ParseError,
139 BrokenImport,
140 UndefinedPrefix,
141 DuplicateLabel,
142 MissingLabel,
143 OrphanClass,
144 OwlBridgeFailed,
145 IoReadError,
146}
147
148impl DiagnosticCode {
149 pub fn as_str(&self) -> &'static str {
150 match self {
151 Self::ParseError => "parse_error",
152 Self::BrokenImport => "broken_import",
153 Self::UndefinedPrefix => "undefined_prefix",
154 Self::DuplicateLabel => "duplicate_label",
155 Self::MissingLabel => "missing_label",
156 Self::OrphanClass => "orphan_class",
157 Self::OwlBridgeFailed => "owl_bridge_failed",
158 Self::IoReadError => "io_read_error",
159 }
160 }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct Diagnostic {
165 pub code: DiagnosticCode,
166 pub severity: DiagnosticSeverity,
167 pub message: String,
168 pub file: PathBuf,
169 pub range: SourceLocation,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub entity_iri: Option<String>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub quick_fix: Option<String>,
174}
175
176#[derive(Debug, Clone, Serialize, Deserialize)]
177pub struct OntologyDocument {
178 pub id: String,
179 pub path: PathBuf,
180 pub format: OntologyFormat,
181 pub base_iri: Option<String>,
182 pub imports: Vec<String>,
183 pub namespaces: BTreeMap<String, String>,
184 pub parse_status: ParseStatus,
185 pub content_hash: String,
186 pub modified_time: u64,
187 pub parse_message: Option<String>,
188 pub parse_error_location: Option<SourceLocation>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct Entity {
193 pub iri: String,
194 pub short_name: String,
195 pub kind: EntityKind,
196 pub ontology_id: String,
197 pub source_location: SourceLocation,
198 pub labels: Vec<String>,
199 pub comments: Vec<String>,
200 pub deprecated: bool,
201 #[serde(default, skip_serializing_if = "Option::is_none")]
202 pub obo_id: Option<String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize)]
206pub struct Annotation {
207 pub subject: String,
208 pub predicate: String,
209 pub object: String,
210 pub ontology_id: String,
211 #[serde(default, skip_serializing_if = "SourceLocation::is_empty")]
212 pub source_location: SourceLocation,
213}
214
215#[derive(Debug, Clone, Serialize, Deserialize)]
216pub struct Axiom {
217 pub id: String,
218 pub ontology_id: String,
219 pub subject: String,
220 pub predicate: String,
221 pub object: String,
222 pub axiom_kind: String,
223 #[serde(default, skip_serializing_if = "SourceLocation::is_empty")]
224 pub source_location: SourceLocation,
225}
226
227#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct Namespace {
229 pub prefix: String,
230 pub iri: String,
231 pub ontology_id: String,
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct Import {
236 pub ontology_id: String,
237 pub import_iri: String,
238}
239
240pub const AXIOM_KIND_SUB_CLASS_OF: &str = "sub_class_of";
242pub const AXIOM_KIND_EQUIVALENT_CLASS: &str = "equivalent_class";
243pub const AXIOM_KIND_DISJOINT_CLASS: &str = "disjoint_class";
244pub const AXIOM_KIND_DOMAIN: &str = "domain";
245pub const AXIOM_KIND_RANGE: &str = "range";
246pub const AXIOM_KIND_PROPERTY_CHAIN: &str = "property_chain";
247
248#[cfg(test)]
249mod tests {
250 use super::ParseStatus;
251
252 #[test]
253 fn parse_status_as_str_matches_serde() {
254 assert_eq!(ParseStatus::Ok.as_str(), "ok");
255 assert_eq!(ParseStatus::Warning.as_str(), "warning");
256 assert_eq!(ParseStatus::Error.as_str(), "error");
257 }
258}