1use std::collections::HashMap;
58use std::fmt;
59use std::hash::Hash;
60
61use indexmap::IndexMap;
62use once_cell::sync::Lazy;
63use serde::{Deserialize, Serialize};
64use uuid::Uuid;
65
66use oxirs_core::OxirsError;
67
68pub use crate::optimization::integration::ValidationStrategy;
69
70pub mod advanced_features;
71pub mod analytics;
72pub mod builders;
73pub mod constraints;
74pub mod custom_components;
75pub mod designer;
76pub mod federated_validation;
77pub mod incremental;
78pub mod integration;
79pub mod iri_resolver;
80#[cfg(feature = "lsp")]
81pub mod lsp;
82pub mod optimization;
83pub mod paths;
84pub mod report;
85pub mod scirs_graph_integration;
86pub mod security;
87pub mod shape_import;
88pub mod shape_inheritance;
89pub mod shape_versioning;
90pub mod shapes;
91pub mod sparql;
92pub mod targets;
93pub mod templates;
94pub mod testing;
95pub mod validation;
96pub mod visual_editor;
97pub mod vocabulary;
98pub mod w3c_test_suite;
99pub mod w3c_test_suite_enhanced;
100
101pub use advanced_features::{
103 AdvancedTarget, AdvancedTargetSelector, ConditionalConstraint, ConditionalEvaluator,
104 ConditionalResult, FunctionInvocation, FunctionParameter, FunctionRegistry, FunctionResult,
105 InferenceStrategy, InferredShape, ParameterType, ReturnType, RuleEngine, RuleEngineStats,
106 RuleExecutionResult, ShaclFunction, ShaclRule, ShapeInferenceConfig, ShapeInferenceEngine,
107 ShapeRegistry,
108};
109pub use analytics::ValidationAnalytics;
110pub use builders::*;
111pub use constraints::{
112 Constraint, ConstraintContext, ConstraintEvaluationResult, NodeKindConstraint,
113 PropertyConstraint,
114};
115pub use custom_components::{
116 ComponentMetadata, CustomConstraint, CustomConstraintRegistry, EmailValidationComponent,
117 RangeConstraintComponent, RegexConstraintComponent,
118};
119pub use federated_validation::*;
120pub use incremental::{
121 Changeset, GraphChange, IncrementalConfig, IncrementalStats, IncrementalValidator,
122};
123pub use iri_resolver::*;
124pub use optimization::{
125 NegationOptimizer, OptimizationConfig, OptimizationResult, OptimizationStrategy,
126 ValidationOptimizationEngine,
127};
128pub use paths::*;
129pub use report::{
130 ReportFormat, ReportGenerator, ReportMetadata, ValidationReport, ValidationSummary,
131};
132pub use scirs_graph_integration::{
133 BasicMetrics, ConnectivityAnalysis, GraphValidationConfig, GraphValidationResult,
134 SciRS2GraphValidator,
135};
136pub use security::{SecureSparqlExecutor, SecurityConfig, SecurityPolicy};
137pub use shape_import::*;
138pub use shape_inheritance::*;
139pub use shapes::{
141 format_literal_for_sparql, format_term_for_sparql, ShapeCacheStats, ShapeFactory, ShapeParser,
142 ShapeParsingConfig, ShapeParsingContext, ShapeParsingStats, ShapeValidationReport,
143 ShapeValidator, SingleShapeValidationReport,
144};
145pub use sparql::*;
146pub use targets::{
148 Target, TargetCacheStats, TargetOptimizationConfig, TargetSelectionStats, TargetSelector,
149};
150pub use testing::{
151 ShapeTestSuite, TestAssertions, TestCase, TestExpectation, TestResult, TestStatus,
152 TestSuiteResult, TestSummary,
153};
154pub use validation::{ValidationEngine, ValidationViolation};
155pub use visual_editor::{
156 ColorScheme, ExportFormat, LayoutDirection, ShapeVisualizer, VisualizerConfig,
157};
158pub use w3c_test_suite::*;
159
160pub use designer::{
162 ConstraintSpec, DesignIssue, DesignStep, DesignWizard, Domain, PropertyDesign, PropertyHint,
163 RecommendationEngine, ShapeDesign, ShapeDesigner,
164 ShapeInferenceEngine as DesignerInferenceEngine,
165};
166
167pub static SHACL_NS: &str = "http://www.w3.org/ns/shacl#";
171
172pub static SHACL_VOCAB: Lazy<vocabulary::ShaclVocabulary> =
174 Lazy::new(vocabulary::ShaclVocabulary::new);
175
176pub use iri_resolver::IriResolver;
178
179#[derive(Debug, Clone, thiserror::Error)]
181pub enum ShaclError {
182 #[error("Shape parsing error: {0}")]
183 ShapeParsing(String),
184
185 #[error("Constraint validation error: {0}")]
186 ConstraintValidation(String),
187
188 #[error("Target selection error: {0}")]
189 TargetSelection(String),
190
191 #[error("Property path error: {0}")]
192 PropertyPath(String),
193
194 #[error("Path evaluation error: {0}")]
195 PathEvaluationError(String),
196
197 #[error("SPARQL execution error: {0}")]
198 SparqlExecution(String),
199
200 #[error("Validation engine error: {0}")]
201 ValidationEngine(String),
202
203 #[error("Report generation error: {0}")]
204 ReportGeneration(String),
205
206 #[error("Configuration error: {0}")]
207 Configuration(String),
208
209 #[error("OxiRS core error: {0}")]
210 Core(#[from] OxirsError),
211
212 #[error("IO error: {0}")]
213 Io(String),
214
215 #[error("Regex error: {0}")]
216 Regex(#[from] regex::Error),
217
218 #[error("JSON error: {0}")]
219 Json(String),
220
221 #[error("IRI resolution error: {0}")]
222 IriResolution(#[from] crate::iri_resolver::IriResolutionError),
223
224 #[error("Security violation: {0}")]
225 SecurityViolation(String),
226
227 #[error("Shape validation error: {0}")]
228 ShapeValidation(String),
229
230 #[error("Validation timeout: {0}")]
231 Timeout(String),
232
233 #[error("Memory limit exceeded: {0}")]
234 MemoryLimit(String),
235
236 #[error("Recursion limit exceeded: {0}")]
237 RecursionLimit(String),
238
239 #[error("Memory pool error: {0}")]
240 MemoryPool(String),
241
242 #[error("Memory optimization error: {0}")]
243 MemoryOptimization(String),
244
245 #[error("Async operation error: {0}")]
246 AsyncOperation(String),
247
248 #[error("Unsupported operation: {0}")]
249 UnsupportedOperation(String),
250
251 #[error("Report error: {0}")]
252 ReportError(String),
253}
254
255impl From<serde_json::Error> for ShaclError {
256 fn from(err: serde_json::Error) -> Self {
257 ShaclError::Json(err.to_string())
258 }
259}
260
261impl From<std::io::Error> for ShaclError {
262 fn from(err: std::io::Error) -> Self {
263 ShaclError::Io(err.to_string())
264 }
265}
266
267impl From<serde_yaml::Error> for ShaclError {
268 fn from(err: serde_yaml::Error) -> Self {
269 ShaclError::Json(err.to_string())
270 }
271}
272
273impl From<anyhow::Error> for ShaclError {
274 fn from(err: anyhow::Error) -> Self {
275 ShaclError::ValidationEngine(err.to_string())
276 }
277}
278
279impl From<std::fmt::Error> for ShaclError {
280 fn from(err: std::fmt::Error) -> Self {
281 ShaclError::ReportGeneration(err.to_string())
282 }
283}
284
285pub type Result<T> = std::result::Result<T, ShaclError>;
287
288#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
290pub struct ShapeId(pub String);
291
292impl ShapeId {
293 pub fn new(id: impl Into<String>) -> Self {
294 ShapeId(id.into())
295 }
296
297 pub fn as_str(&self) -> &str {
298 &self.0
299 }
300
301 pub fn generate() -> Self {
302 ShapeId(format!("shape_{}", Uuid::new_v4()))
303 }
304}
305
306impl fmt::Display for ShapeId {
307 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
308 write!(f, "{}", self.0)
309 }
310}
311
312impl From<String> for ShapeId {
313 fn from(s: String) -> Self {
314 ShapeId(s)
315 }
316}
317
318impl From<&str> for ShapeId {
319 fn from(s: &str) -> Self {
320 ShapeId(s.to_string())
321 }
322}
323
324#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
326pub struct ConstraintComponentId(pub String);
327
328impl ConstraintComponentId {
329 pub fn new(id: impl Into<String>) -> Self {
330 ConstraintComponentId(id.into())
331 }
332
333 pub fn as_str(&self) -> &str {
334 &self.0
335 }
336}
337
338impl fmt::Display for ConstraintComponentId {
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 write!(f, "{}", self.0)
341 }
342}
343
344impl From<String> for ConstraintComponentId {
345 fn from(s: String) -> Self {
346 ConstraintComponentId(s)
347 }
348}
349
350impl From<&str> for ConstraintComponentId {
351 fn from(s: &str) -> Self {
352 ConstraintComponentId(s.to_string())
353 }
354}
355
356#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
358pub enum ShapeType {
359 NodeShape,
360 PropertyShape,
361}
362
363#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct Shape {
366 pub id: ShapeId,
368
369 pub shape_type: ShapeType,
371
372 pub targets: Vec<Target>,
374
375 pub path: Option<PropertyPath>,
377
378 pub constraints: IndexMap<ConstraintComponentId, Constraint>,
380
381 pub deactivated: bool,
383
384 pub label: Option<String>,
386
387 pub description: Option<String>,
389
390 pub groups: Vec<String>,
392
393 pub order: Option<i32>,
395
396 pub severity: Severity,
398
399 pub messages: IndexMap<String, String>, pub extends: Vec<ShapeId>,
406
407 pub property_shapes: Vec<ShapeId>,
409
410 pub priority: Option<i32>,
412
413 pub metadata: ShapeMetadata,
415}
416
417impl Shape {
418 pub fn new(id: ShapeId, shape_type: ShapeType) -> Self {
419 Self {
420 id,
421 shape_type,
422 targets: Vec::new(),
423 path: None,
424 constraints: IndexMap::new(),
425 deactivated: false,
426 label: None,
427 description: None,
428 groups: Vec::new(),
429 order: None,
430 severity: Severity::Violation,
431 messages: IndexMap::new(),
432 extends: Vec::new(),
433 property_shapes: Vec::new(),
434 priority: None,
435 metadata: ShapeMetadata::default(),
436 }
437 }
438
439 pub fn node_shape(id: ShapeId) -> Self {
440 Self::new(id, ShapeType::NodeShape)
441 }
442
443 pub fn property_shape(id: ShapeId, path: PropertyPath) -> Self {
444 let mut shape = Self::new(id, ShapeType::PropertyShape);
445 shape.path = Some(path);
446 shape
447 }
448
449 pub fn add_constraint(&mut self, component_id: ConstraintComponentId, constraint: Constraint) {
450 self.constraints.insert(component_id, constraint);
451 }
452
453 pub fn add_target(&mut self, target: Target) {
454 self.targets.push(target);
455 }
456
457 pub fn is_active(&self) -> bool {
458 !self.deactivated
459 }
460
461 pub fn is_node_shape(&self) -> bool {
462 matches!(self.shape_type, ShapeType::NodeShape)
463 }
464
465 pub fn is_property_shape(&self) -> bool {
466 matches!(self.shape_type, ShapeType::PropertyShape)
467 }
468
469 pub fn extends(&mut self, parent_shape_id: ShapeId) -> &mut Self {
471 self.extends.push(parent_shape_id);
472 self
473 }
474
475 pub fn with_priority(&mut self, priority: i32) -> &mut Self {
477 self.priority = Some(priority);
478 self
479 }
480
481 pub fn with_metadata(&mut self, metadata: ShapeMetadata) -> &mut Self {
483 self.metadata = metadata;
484 self
485 }
486
487 pub fn update_metadata<F>(&mut self, updater: F) -> &mut Self
489 where
490 F: FnOnce(&mut ShapeMetadata),
491 {
492 updater(&mut self.metadata);
493 self
494 }
495
496 pub fn effective_priority(&self) -> i32 {
498 self.priority.unwrap_or(0)
499 }
500
501 pub fn extends_shape(&self, shape_id: &ShapeId) -> bool {
503 self.extends.contains(shape_id)
504 }
505
506 pub fn parent_shapes(&self) -> &[ShapeId] {
508 &self.extends
509 }
510}
511
512impl Default for Shape {
513 fn default() -> Self {
514 Self::new(ShapeId("default:shape".to_string()), ShapeType::NodeShape)
515 }
516}
517
518#[derive(Debug, Clone, Serialize, Deserialize, Default)]
520pub struct ShapeMetadata {
521 pub author: Option<String>,
523
524 pub created: Option<chrono::DateTime<chrono::Utc>>,
526
527 pub modified: Option<chrono::DateTime<chrono::Utc>>,
529
530 pub version: Option<String>,
532
533 pub license: Option<String>,
535
536 pub tags: Vec<String>,
538
539 pub custom: HashMap<String, String>,
541}
542
543#[derive(
545 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default,
546)]
547pub enum Severity {
548 Info,
549 Warning,
550 #[default]
551 Violation,
552}
553
554impl fmt::Display for Severity {
555 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
556 match self {
557 Severity::Info => write!(f, "Info"),
558 Severity::Warning => write!(f, "Warning"),
559 Severity::Violation => write!(f, "Violation"),
560 }
561 }
562}
563
564#[derive(Debug, Clone, Serialize, Deserialize)]
566pub struct ValidationConfig {
567 pub max_violations: usize,
569
570 pub include_info: bool,
572
573 pub include_warnings: bool,
575
576 pub fail_fast: bool,
578
579 pub max_recursion_depth: usize,
581
582 pub timeout_ms: Option<u64>,
584
585 pub parallel: bool,
587
588 pub context: HashMap<String, String>,
590
591 pub strategy: ValidationStrategy,
593}
594
595impl Default for ValidationConfig {
596 fn default() -> Self {
597 Self {
598 max_violations: 0,
599 include_info: true,
600 include_warnings: true,
601 fail_fast: false,
602 max_recursion_depth: 50,
603 timeout_ms: None,
604 parallel: false,
605 context: HashMap::new(),
606 strategy: ValidationStrategy::default(),
607 }
608 }
609}
610
611impl ValidationConfig {
612 pub fn with_strategy(mut self, strategy: ValidationStrategy) -> Self {
614 self.strategy = strategy;
615 self
616 }
617
618 pub fn with_inference_enabled(mut self, enabled: bool) -> Self {
620 self.context
621 .insert("inference_enabled".to_string(), enabled.to_string());
622 self
623 }
624}
625
626pub const VERSION: &str = env!("CARGO_PKG_VERSION");
628pub mod validator;
630
631pub use validator::{ValidationStats, Validator, ValidatorBuilder};
633
634pub fn init() -> Result<()> {
636 tracing::info!("Initializing OxiRS SHACL v{}", VERSION);
637 Ok(())
638}