1use serde::{Deserialize, Serialize};
2use std::collections::BTreeMap;
3use strixonomy_catalog::{CatalogStats, ClassHierarchy, EntityDetail, SubclassEdge};
4use strixonomy_core::{Diagnostic, Entity, OntologyDocument};
5use strixonomy_reasoner::ReasonerSnapshot;
6
7#[derive(Debug, Clone, Serialize)]
8pub struct DiagnosticSummary {
9 pub code: String,
10 pub severity: String,
11 pub message: String,
12 pub file: String,
13 pub line: Option<u64>,
14 pub column: Option<u64>,
15 #[serde(skip_serializing_if = "Option::is_none")]
16 pub entity_iri: Option<String>,
17}
18
19impl From<&Diagnostic> for DiagnosticSummary {
20 fn from(d: &Diagnostic) -> Self {
21 Self {
22 code: d.display_code(),
23 severity: d.severity.as_str().to_string(),
24 message: d.message.clone(),
25 file: d.file.display().to_string(),
26 line: d.range.line,
27 column: d.range.column,
28 entity_iri: d.entity_iri.clone(),
29 }
30 }
31}
32
33#[derive(Debug, Deserialize)]
34pub struct IndexWorkspaceParams {
35 #[serde(alias = "workspaceUri", default)]
37 pub workspace_uri: Option<String>,
38 #[serde(default)]
40 pub disk_cache: bool,
41}
42
43#[derive(Debug, Serialize)]
44pub struct IndexWorkspaceResult {
45 pub stats: CatalogStats,
46 pub indexed_at: u64,
47}
48
49#[derive(Debug, Serialize)]
50pub struct CatalogSnapshot {
51 pub documents: Vec<OntologyDocument>,
52 pub entities: Vec<Entity>,
53 pub hierarchy: ClassHierarchy,
54 pub diagnostics: Vec<DiagnosticSummary>,
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub reasoner: Option<ReasonerSnapshot>,
57 #[serde(skip_serializing_if = "Option::is_none")]
59 pub stats: Option<CatalogStats>,
60 #[serde(skip_serializing_if = "Option::is_none")]
62 pub active_ontology_id: Option<String>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
66#[serde(rename_all = "snake_case")]
67pub enum CommandEnablement {
68 Always,
69 HasOntology,
70 IsDirty,
71 HasSelection,
72 ReasonerRunning,
73 ReasonerIdle,
74 CanEditSelection,
75}
76
77#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct CommandDescriptor {
79 pub id: String,
80 pub title: String,
81 pub category: String,
82 #[serde(default, skip_serializing_if = "Vec::is_empty")]
83 pub enablement: Vec<CommandEnablement>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub undo_label: Option<String>,
86 #[serde(skip_serializing_if = "Option::is_none")]
87 pub dialog_id: Option<String>,
88}
89
90#[derive(Debug, Serialize)]
91pub struct ListCommandsResult {
92 pub commands: Vec<CommandDescriptor>,
93}
94
95#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
96pub struct OntologyRegistryEntrySnapshot {
97 pub id: String,
98 pub uri: String,
99 pub path: String,
100 pub format: String,
101 pub role: String,
102 pub editable: bool,
103 pub dirty: bool,
104 pub version: u32,
105 pub active: bool,
106}
107
108#[derive(Debug, Deserialize)]
109pub struct WorkspaceUiStateParams {
110 #[serde(default)]
112 pub selection_iri: Option<String>,
113 #[serde(default)]
115 pub dirty_document_count: u32,
116 #[serde(default)]
118 pub active_ontology_id: Option<String>,
119 #[serde(default)]
121 pub ontology_registry: Vec<OntologyRegistryEntrySnapshot>,
122}
123
124#[derive(Debug, Serialize)]
125pub struct WorkspaceUiState {
126 pub has_ontology: bool,
127 pub ontology_count: usize,
128 pub is_dirty: bool,
129 pub has_selection: bool,
130 pub selection_iri: Option<String>,
131 pub selection_editable: bool,
132 pub reasoner_running: bool,
133 pub reasoner_dirty: bool,
134 pub reasoner_consistent: Option<bool>,
135 pub active_ontology_id: Option<String>,
136 #[serde(skip_serializing_if = "Option::is_none")]
137 pub stats: Option<CatalogStats>,
138 #[serde(skip_serializing_if = "Vec::is_empty", default)]
139 pub ontology_registry: Vec<OntologyRegistryEntrySnapshot>,
140}
141
142#[derive(Debug, Deserialize)]
143pub struct GetDialogSchemaParams {
144 pub dialog_id: String,
145}
146
147#[derive(Debug, Serialize)]
148pub struct DialogFieldSchema {
149 pub id: String,
150 pub label: String,
151 pub field_type: String,
152 #[serde(default)]
153 pub required: bool,
154 #[serde(skip_serializing_if = "Option::is_none")]
155 pub placeholder: Option<String>,
156 #[serde(default, skip_serializing_if = "Vec::is_empty")]
157 pub validation: Vec<String>,
158}
159
160#[derive(Debug, Serialize)]
161pub struct DialogSchema {
162 pub id: String,
163 pub title: String,
164 pub fields: Vec<DialogFieldSchema>,
165 pub primary_action: String,
166}
167
168#[derive(Debug, Serialize)]
169pub struct GetDialogSchemaResult {
170 pub schema: DialogSchema,
171}
172
173#[derive(Debug, Deserialize)]
174pub struct CreateOntologyParams {
175 pub path: String,
176 pub ontology_iri: String,
177 #[serde(default)]
178 pub version_iri: Option<String>,
179 #[serde(default)]
180 pub format: Option<String>,
181 #[serde(default)]
182 pub prefixes: Option<BTreeMap<String, String>>,
183}
184
185#[derive(Debug, Serialize)]
186pub struct CreateOntologyResult {
187 pub path: String,
188 pub ontology_iri: String,
189}
190
191#[derive(Debug, Deserialize)]
192pub struct ExportOntologyParams {
193 pub source_path: String,
194 pub output_path: String,
195 #[serde(default)]
196 pub format: Option<String>,
197}
198
199#[derive(Debug, Serialize)]
200pub struct ExportOntologyResult {
201 pub output_path: String,
202 pub success: bool,
203 #[serde(skip_serializing_if = "Option::is_none")]
204 pub logs: Option<String>,
205}
206
207#[derive(Debug, Deserialize)]
208pub struct SetActiveOntologyParams {
209 pub ontology_id: String,
210}
211
212#[derive(Debug, Serialize)]
213pub struct SetActiveOntologyResult {
214 pub active_ontology_id: String,
215}
216
217#[derive(Debug, Deserialize)]
218pub struct DeleteImpactParams {
219 pub entity_iri: String,
220}
221
222#[derive(Debug, Serialize)]
223pub struct DeleteImpactResult {
224 pub entity_iri: String,
225 pub usage_count: usize,
226 pub axiom_count: usize,
227 pub referencing_entities: Vec<String>,
228 pub warnings: Vec<String>,
229}
230
231#[derive(Debug, Deserialize)]
232pub struct GetEntityParams {
233 pub iri: String,
234}
235
236#[derive(Debug, Serialize)]
237pub struct GetEntityResult {
238 pub detail: EntityDetail,
239}
240
241#[derive(Debug, Deserialize)]
242pub struct ApplyAxiomPatchParams {
243 pub document_uri: String,
244 pub patches: serde_json::Value,
246 #[serde(default)]
247 pub preview_only: bool,
248}
249
250#[derive(Debug, Deserialize)]
251pub struct QueryParams {
252 pub sql: String,
253}
254
255#[derive(Debug, Deserialize)]
256pub struct SparqlParams {
257 pub query: String,
258}
259
260#[derive(Debug, Deserialize)]
261pub struct DlQueryParams {
262 pub expression: String,
263 #[serde(default = "default_dl_query_profile")]
264 pub profile: String,
265 #[serde(default = "default_dl_query_mode")]
267 pub mode: String,
268 #[serde(default)]
269 pub document_uri: Option<String>,
270}
271
272fn default_dl_query_profile() -> String {
273 "dl".to_string()
274}
275
276fn default_dl_query_mode() -> String {
277 "inferred".to_string()
278}
279
280#[derive(Debug, Serialize)]
281pub struct DlQueryResult {
282 pub expression: String,
283 pub normalized: String,
284 pub query_class_iri: String,
285 pub subclasses: Vec<String>,
286 pub superclasses: Vec<String>,
287 pub equivalents: Vec<String>,
288 pub instances: Vec<String>,
289 pub profile: String,
290 pub mode: String,
291 pub duration_ms: u64,
292 #[serde(default, skip_serializing_if = "Vec::is_empty")]
293 pub warnings: Vec<String>,
294 #[serde(default, skip_serializing_if = "Vec::is_empty")]
295 pub diagnostics: Vec<String>,
296}
297
298#[derive(Debug, Deserialize)]
299pub struct SearchParams {
300 pub query: String,
301 #[serde(default)]
303 pub limit: Option<usize>,
304}
305
306#[derive(Debug, Serialize)]
307pub struct SearchResult {
308 pub entities: Vec<EntityDetail>,
309}
310
311#[derive(Debug, Serialize)]
312pub struct TabularQueryResult {
313 pub columns: Vec<String>,
314 pub rows: Vec<BTreeMap<String, String>>,
315 #[serde(skip_serializing_if = "Option::is_none")]
316 pub truncated: Option<bool>,
317}
318
319#[derive(Debug, Deserialize)]
320pub struct ParseManchesterParams {
321 pub expression: String,
322 pub axiom_kind: String,
323 #[serde(default)]
324 pub entity_iri: Option<String>,
325 #[serde(default)]
326 pub document_uri: Option<String>,
327}
328
329#[derive(Debug, Serialize)]
330pub struct ManchesterCompletions {
331 pub classes: Vec<String>,
332 pub object_properties: Vec<String>,
333 pub data_properties: Vec<String>,
334 pub datatypes: Vec<String>,
335}
336
337#[derive(Debug, Serialize)]
338pub struct ParseManchesterResult {
339 pub normalized: String,
340 pub turtle_fragment: String,
341 pub tree: serde_json::Value,
342 pub diagnostics: Vec<strixonomy_owl::PatchDiagnostic>,
343 pub completions: ManchesterCompletions,
344}
345
346#[derive(Debug, Serialize)]
347pub struct ApplyAxiomPatchResult {
348 #[serde(flatten)]
349 pub patch: strixonomy_owl::ApplyPatchResult,
350 #[serde(skip_serializing_if = "Option::is_none")]
351 pub entity_detail: Option<EntityDetail>,
352 #[serde(skip_serializing_if = "Option::is_none")]
354 pub reindex_warning: Option<String>,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub workspace_edit: Option<lsp_types::WorkspaceEdit>,
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub undo_patches: Option<serde_json::Value>,
361}
362
363#[derive(Debug, Serialize)]
365pub struct LspErrorPayload {
366 pub code: String,
367 pub message: String,
368 pub recoverable: bool,
369 pub user_action: Option<String>,
370}
371
372impl LspErrorPayload {
373 pub fn not_indexed() -> Self {
374 Self {
375 code: "NOT_INDEXED".to_string(),
376 message: "Workspace has not been indexed yet".to_string(),
377 recoverable: true,
378 user_action: Some("Run Strixonomy: Index Workspace".to_string()),
379 }
380 }
381
382 pub fn not_found(iri: &str) -> Self {
383 Self {
384 code: "ENTITY_NOT_FOUND".to_string(),
385 message: format!("Entity not found: {iri}"),
386 recoverable: true,
387 user_action: None,
388 }
389 }
390
391 pub fn index_failed(message: String) -> Self {
392 Self {
393 code: "INDEX_FAILED".to_string(),
394 message,
395 recoverable: true,
396 user_action: Some("Check ontology files for parse errors".to_string()),
397 }
398 }
399
400 pub fn invalid_params(message: String) -> Self {
401 Self { code: "INVALID_PARAMS".to_string(), message, recoverable: true, user_action: None }
402 }
403
404 pub fn graph_failed(message: String) -> Self {
405 Self {
406 code: "GRAPH_FAILED".to_string(),
407 message,
408 recoverable: true,
409 user_action: Some("Adjust graph kind, root IRI, or filters".to_string()),
410 }
411 }
412
413 pub fn robot_failed(message: String) -> Self {
414 Self {
415 code: "ROBOT_FAILED".to_string(),
416 message,
417 recoverable: true,
418 user_action: Some("Check ROBOT CLI installation and arguments".to_string()),
419 }
420 }
421
422 pub fn patch_invalid(message: String) -> Self {
423 Self {
424 code: "PATCH_INVALID".to_string(),
425 message,
426 recoverable: true,
427 user_action: Some("Check patch parameters and entity IRIs".to_string()),
428 }
429 }
430
431 pub fn unsupported_format(message: String) -> Self {
432 Self {
433 code: "UNSUPPORTED_FORMAT".to_string(),
434 message,
435 recoverable: true,
436 user_action: Some("Save as Turtle (.ttl) for write-back".to_string()),
437 }
438 }
439
440 pub fn query_failed(message: String) -> Self {
441 Self {
442 code: "QUERY_FAILED".to_string(),
443 message,
444 recoverable: true,
445 user_action: Some("Check query syntax and virtual table names".to_string()),
446 }
447 }
448
449 pub fn manchester_invalid(message: String) -> Self {
450 Self {
451 code: "MANCHESTER_INVALID".to_string(),
452 message,
453 recoverable: true,
454 user_action: Some("Fix the Manchester class expression".to_string()),
455 }
456 }
457
458 pub fn applied_not_indexed(message: String) -> Self {
459 Self {
460 code: "APPLIED_NOT_INDEXED".to_string(),
461 message,
462 recoverable: true,
463 user_action: Some("Patch was saved; run Strixonomy: Index Workspace".to_string()),
464 }
465 }
466
467 pub fn reasoner_failed(message: String) -> Self {
468 Self {
469 code: "REASONER_FAILED".to_string(),
470 message,
471 recoverable: true,
472 user_action: Some(
473 "Try a different reasoner profile or fix ontology axioms".to_string(),
474 ),
475 }
476 }
477
478 pub fn explanation_failed(message: String) -> Self {
479 Self {
480 code: "EXPLANATION_FAILED".to_string(),
481 message,
482 recoverable: true,
483 user_action: Some("Run the reasoner first or choose another class".to_string()),
484 }
485 }
486
487 pub fn refactor_failed(message: String) -> Self {
488 Self {
489 code: "REFACTOR_FAILED".to_string(),
490 message,
491 recoverable: true,
492 user_action: Some("Preview the refactor plan and check Turtle files".to_string()),
493 }
494 }
495}
496
497#[derive(Debug, Deserialize)]
498pub struct RunReasonerParams {
499 #[serde(default = "default_reasoner_profile")]
500 pub profile: String,
501 #[serde(default = "default_auto_profile")]
502 pub auto_detect: bool,
503}
504
505fn default_reasoner_profile() -> String {
506 "el".to_string()
507}
508
509fn default_auto_profile() -> bool {
510 true
511}
512
513#[derive(Debug, Serialize)]
514pub struct RunReasonerResult {
515 pub profile_used: String,
516 pub consistent: bool,
517 pub unsatisfiable: Vec<String>,
518 pub inferred_edge_count: usize,
519 pub new_inferences: Vec<SubclassEdge>,
520 pub warnings: Vec<strixonomy_reasoner::ReasonerWarning>,
521 pub duration_ms: u64,
522 pub snapshot: ReasonerSnapshot,
523}
524
525#[derive(Debug, Deserialize)]
526pub struct GetExplanationParams {
527 pub class_iri: String,
528 #[serde(default = "default_reasoner_profile")]
529 pub profile: String,
530}
531
532#[derive(Debug, Serialize)]
533pub struct GetGraphResult {
534 pub graph: strixonomy_catalog::GraphPayload,
535}
536
537#[derive(Debug, Deserialize)]
538pub struct RunRobotParams {
539 pub subcommand: String,
540 #[serde(default)]
541 pub args: Vec<String>,
542 #[serde(default)]
543 pub robot_path: Option<String>,
544}
545
546#[derive(Debug, Serialize)]
547pub struct RunRobotResult {
548 pub exit_code: i32,
549 pub stdout: String,
550 pub stderr: String,
551}
552
553#[derive(Debug, Deserialize)]
554pub struct CheckInstanceParams {
555 pub individual_iri: String,
556 pub class_iri: String,
557 #[serde(default = "default_reasoner_profile")]
558 pub profile: String,
559}
560
561#[derive(Debug, Serialize)]
562pub struct CheckInstanceResult {
563 pub individual_iri: String,
564 pub class_iri: String,
565 pub entailed: bool,
566 pub profile_used: String,
567 pub duration_ms: u64,
568}
569
570#[derive(Debug, Serialize)]
571pub struct GetExplanationResult {
572 pub class_iri: String,
573 pub steps: Vec<strixonomy_reasoner::ExplanationStep>,
574 pub text: String,
575 #[serde(default, skip_serializing_if = "Vec::is_empty")]
576 pub alternatives: Vec<strixonomy_reasoner::ExplanationResult>,
577 pub indexed_at: u64,
578 pub content_hash: String,
579}
580
581#[derive(Debug, Deserialize)]
582pub struct FindUsagesParams {
583 pub iri: String,
584}
585
586#[derive(Debug, Serialize)]
587pub struct UsageSummary {
588 pub iri: String,
589 pub referenced_iri: String,
590 pub file: String,
591 pub line: Option<u64>,
592 pub column: Option<u64>,
593 pub kind: String,
594 pub context: String,
595}
596
597#[derive(Debug, Serialize)]
598pub struct FindUsagesResult {
599 pub usages: Vec<UsageSummary>,
600}
601
602#[derive(Debug, Deserialize)]
603pub struct PreviewRefactorParams {
604 #[serde(flatten)]
605 pub request: strixonomy_refactor::RefactorRequest,
606}
607
608#[derive(Debug, Serialize)]
609pub struct PreviewRefactorResult {
610 #[serde(flatten)]
611 pub plan: strixonomy_refactor::RefactorPlan,
612}
613
614#[derive(Debug, Deserialize)]
615pub struct ApplyRefactorParams {
616 pub plan: strixonomy_refactor::RefactorPlan,
617 pub request: strixonomy_refactor::RefactorRequest,
618 #[serde(default)]
619 pub preview_only: bool,
620}
621
622#[derive(Debug, Deserialize)]
623pub struct SemanticDiffParams {
624 #[serde(default)]
626 pub left_ref: Option<String>,
627 #[serde(default)]
629 pub right_ref: Option<String>,
630 #[serde(default)]
632 pub left_path: Option<String>,
633 #[serde(default)]
635 pub right_path: Option<String>,
636 #[serde(default)]
638 pub reasoner: bool,
639 #[serde(default)]
641 pub format: Option<String>,
642}
643
644#[derive(Debug, Serialize)]
645pub struct SemanticDiffResult {
646 pub diff: strixonomy_diff::DiffResult,
647 #[serde(skip_serializing_if = "Option::is_none")]
648 pub formatted: Option<String>,
649}
650
651#[derive(Debug, Serialize)]
652pub struct ListSqlSchemaResult {
653 pub tables: Vec<strixonomy_query::SqlTableSchema>,
654}
655
656#[derive(Debug, Serialize)]
657pub struct ApplyRefactorResult {
658 pub files_written: usize,
659 #[serde(skip_serializing_if = "Option::is_none")]
660 pub reindex_warning: Option<String>,
661 #[serde(skip_serializing_if = "Option::is_none")]
662 pub workspace_edit: Option<lsp_types::WorkspaceEdit>,
663}
664
665#[derive(Debug, Serialize)]
666pub struct ListPluginsResult {
667 pub plugins: Vec<strixonomy_plugin::PluginDescriptor>,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
672pub struct SwrlRuleListItem {
673 pub id: String,
674 pub label: String,
675 pub body_count: usize,
676 pub head_count: usize,
677 pub enabled: bool,
678 #[serde(skip_serializing_if = "Option::is_none")]
680 pub rule_json: Option<String>,
681 #[serde(skip_serializing_if = "Option::is_none")]
682 pub document_uri: Option<String>,
683 #[serde(skip_serializing_if = "Option::is_none")]
684 pub ontology_iri: Option<String>,
685}
686
687#[derive(Debug, Serialize)]
688pub struct ListSwrlRulesResult {
689 pub rules: Vec<SwrlRuleListItem>,
690}
691
692#[derive(Debug, Deserialize)]
693pub struct ValidateSwrlRuleParams {
694 pub rule_json: String,
695}
696
697#[derive(Debug, Serialize)]
698pub struct ValidateSwrlRuleResult {
699 pub diagnostics: Vec<strixonomy_swrl::SwrlDiagnostic>,
700}
701
702#[derive(Debug, Deserialize)]
703pub struct ParseSwrlRuleParams {
704 pub rule_json: String,
705}
706
707#[derive(Debug, Serialize)]
708pub struct ParseSwrlRuleResult {
709 pub rule: strixonomy_swrl::SwrlRule,
710 pub diagnostics: Vec<strixonomy_swrl::SwrlDiagnostic>,
711}
712
713#[derive(Debug, Deserialize)]
714pub struct RunPluginParams {
715 pub plugin_id: String,
716 #[serde(default = "default_validate_action")]
717 pub action: String,
718 #[serde(default)]
719 pub step: Option<String>,
720 #[serde(default)]
722 pub view_id: Option<String>,
723 #[serde(default)]
725 pub query: Option<String>,
726 #[serde(default)]
728 pub focus_iri: Option<String>,
729}
730
731fn default_validate_action() -> String {
732 "validate".to_string()
733}
734
735#[derive(Debug, Serialize)]
736pub struct RunPluginResult {
737 pub diagnostics: Vec<DiagnosticSummary>,
738 #[serde(default, skip_serializing_if = "Vec::is_empty")]
739 pub output_paths: Vec<String>,
740 #[serde(skip_serializing_if = "Option::is_none")]
741 pub logs: Option<String>,
742 #[serde(skip_serializing_if = "Option::is_none")]
744 pub view_html: Option<String>,
745 pub success: bool,
746 #[serde(skip_serializing_if = "Option::is_none")]
747 pub result: Option<serde_json::Value>,
748 #[serde(skip_serializing_if = "Option::is_none")]
749 pub columns: Option<Vec<String>>,
750 #[serde(skip_serializing_if = "Option::is_none")]
751 pub rows: Option<Vec<Vec<String>>>,
752 #[serde(skip_serializing_if = "Option::is_none")]
753 pub unsatisfiable: Option<Vec<String>>,
754 #[serde(skip_serializing_if = "Option::is_none")]
755 pub affected_iris: Option<Vec<String>>,
756 #[serde(skip_serializing_if = "Option::is_none")]
757 pub root_iris: Option<Vec<String>>,
758 #[serde(skip_serializing_if = "Option::is_none")]
759 pub graph_kind: Option<String>,
760 #[serde(skip_serializing_if = "Option::is_none")]
761 pub hints: Option<Vec<String>>,
762 #[serde(skip_serializing_if = "Option::is_none")]
763 pub profile: Option<String>,
764}