ontocore_plugin/
protocol.rs1use ontocore_core::{
2 validate_workspace_scope, Diagnostic, DiagnosticCode, DiagnosticSeverity, SourceLocation,
3};
4use serde::{Deserialize, Serialize};
5use std::path::{Path, PathBuf};
6
7#[derive(Debug, Clone, Default, Serialize, Deserialize)]
9pub struct PluginOutput {
10 #[serde(default)]
11 pub diagnostics: Vec<PluginDiagnosticWire>,
12 #[serde(default)]
13 pub output_paths: Vec<String>,
14 #[serde(default)]
15 pub logs: Option<String>,
16 #[serde(default)]
17 pub exit_message: Option<String>,
18 #[serde(default)]
20 pub view_html: Option<String>,
21 #[serde(default)]
23 pub violations: Vec<String>,
24 #[serde(default)]
26 pub result: Option<serde_json::Value>,
27 #[serde(default)]
28 pub columns: Option<Vec<String>>,
29 #[serde(default)]
30 pub rows: Option<Vec<Vec<String>>>,
31 #[serde(default)]
32 pub unsatisfiable: Option<Vec<String>>,
33 #[serde(default)]
34 pub affected_iris: Option<Vec<String>>,
35 #[serde(default)]
36 pub root_iris: Option<Vec<String>>,
37 #[serde(default)]
38 pub graph_kind: Option<String>,
39 #[serde(default)]
40 pub hints: Option<Vec<String>>,
41 #[serde(default)]
42 pub profile: Option<String>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct PluginDiagnosticWire {
47 pub code: String,
48 pub severity: String,
49 pub message: String,
50 pub file: String,
51 #[serde(default)]
52 pub line: Option<u64>,
53 #[serde(default)]
54 pub column: Option<u64>,
55 #[serde(default)]
56 pub entity_iri: Option<String>,
57}
58
59impl PluginDiagnosticWire {
60 pub fn into_diagnostic(self, plugin_id: &str, workspace: &Path) -> Diagnostic {
61 let requested = PathBuf::from(&self.file);
62 let (file, message) = match validate_workspace_scope(&requested, workspace) {
63 Ok(file) => (file, self.message),
64 Err(err) => (
65 workspace.to_path_buf(),
66 format!(
67 "{} (rejected plugin diagnostic file path '{}': {})",
68 self.message, self.file, err
69 ),
70 ),
71 };
72 Diagnostic {
73 code: DiagnosticCode::PluginViolation,
74 severity: match self.severity.as_str() {
75 "error" => DiagnosticSeverity::Error,
76 "warning" => DiagnosticSeverity::Warning,
77 _ => DiagnosticSeverity::Info,
78 },
79 message,
80 file,
81 range: SourceLocation {
82 line: self.line,
83 column: self.column,
84 start_byte: None,
85 end_byte: None,
86 },
87 entity_iri: self.entity_iri,
88 quick_fix: None,
89 plugin_id: Some(plugin_id.to_string()),
90 plugin_code: Some(self.code),
91 }
92 }
93}
94
95pub fn jail_output_paths(
96 plugin_id: &str,
97 workspace: &Path,
98 mut output: PluginOutput,
99) -> PluginOutput {
100 let mut jailed = Vec::new();
101 for p in output.output_paths.drain(..) {
102 let requested = PathBuf::from(&p);
103 match validate_workspace_scope(&requested, workspace) {
104 Ok(path) => jailed.push(path.display().to_string()),
105 Err(err) => output
106 .violations
107 .push(format!("rejected plugin output path '{p}' for {plugin_id}: {err}")),
108 }
109 }
110 output.output_paths = jailed;
111 output
112}
113
114pub fn plugin_diagnostic(
115 plugin_id: &str,
116 code: &str,
117 severity: DiagnosticSeverity,
118 message: impl Into<String>,
119 file: PathBuf,
120 entity_iri: Option<String>,
121) -> Diagnostic {
122 Diagnostic {
123 code: DiagnosticCode::PluginViolation,
124 severity,
125 message: message.into(),
126 file,
127 range: SourceLocation::default(),
128 entity_iri,
129 quick_fix: None,
130 plugin_id: Some(plugin_id.to_string()),
131 plugin_code: Some(code.to_string()),
132 }
133}