Skip to main content

ontocore_plugin_shacl/
lib.rs

1use ontocore_catalog::OntologyCatalog;
2use ontocore_core::DiagnosticSeverity;
3use ontocore_plugin::{plugin_diagnostic, ValidatorPlugin};
4use std::path::{Path, PathBuf};
5
6pub const PLUGIN_ID: &str = "ontocode.shacl-validator";
7
8#[derive(Debug, Clone)]
9pub struct ShaclValidatorPlugin {
10    pub shapes_dir: PathBuf,
11}
12
13impl ShaclValidatorPlugin {
14    pub fn new(workspace: &Path, shapes_dir: Option<&str>) -> Self {
15        let rel = shapes_dir.unwrap_or("shapes");
16        Self { shapes_dir: workspace.join(rel) }
17    }
18}
19
20impl ValidatorPlugin for ShaclValidatorPlugin {
21    fn id(&self) -> &str {
22        PLUGIN_ID
23    }
24
25    fn validate(
26        &self,
27        _catalog: &OntologyCatalog,
28        workspace: &Path,
29    ) -> Vec<ontocore_core::Diagnostic> {
30        if !self.shapes_dir.is_dir() {
31            return vec![plugin_diagnostic(
32                PLUGIN_ID,
33                "shapes_missing",
34                DiagnosticSeverity::Info,
35                format!(
36                    "SHACL shapes directory '{}' not found; add .ttl shape files to enable validation",
37                    self.shapes_dir.display()
38                ),
39                workspace.to_path_buf(),
40                None,
41            )];
42        }
43        let shape_files: Vec<_> = std::fs::read_dir(&self.shapes_dir)
44            .ok()
45            .into_iter()
46            .flatten()
47            .filter_map(|e| e.ok())
48            .filter(|e| {
49                e.path()
50                    .extension()
51                    .and_then(|x| x.to_str())
52                    .is_some_and(|ext| matches!(ext, "ttl" | "rdf" | "shacl"))
53            })
54            .collect();
55        if shape_files.is_empty() {
56            return vec![plugin_diagnostic(
57                PLUGIN_ID,
58                "shapes_empty",
59                DiagnosticSeverity::Info,
60                format!("No SHACL shape files in '{}'", self.shapes_dir.display()),
61                workspace.to_path_buf(),
62                None,
63            )];
64        }
65        vec![plugin_diagnostic(
66            PLUGIN_ID,
67            "shacl_pending",
68            DiagnosticSeverity::Info,
69            format!(
70                "Found {} SHACL shape file(s); full rudof validation ships in a future release",
71                shape_files.len()
72            ),
73            workspace.to_path_buf(),
74            None,
75        )]
76    }
77}