Skip to main content

oxirs_star/
lib.rs

1//! # OxiRS RDF-Star
2//!
3//! [![Version](https://img.shields.io/badge/version-0.2.2-blue)](https://github.com/cool-japan/oxirs/releases)
4//! [![docs.rs](https://docs.rs/oxirs-star/badge.svg)](https://docs.rs/oxirs-star)
5//!
6//! **Status**: Production Release (v0.2.2)
7//! **Stability**: Public APIs are stable. Production-ready with comprehensive testing.
8//!
9//! RDF-star and SPARQL-star implementation providing comprehensive support for quoted triples.
10//!
11//! This crate extends the standard RDF model with RDF-star capabilities, allowing triples
12//! to be used as subjects or objects in other triples (quoted triples). It provides:
13//!
14//! - Complete RDF-star data model with proper type safety
15//! - Parsing support for Turtle-star, N-Triples-star, TriG-star, and N-Quads-star
16//! - SPARQL-star query execution with quoted triple patterns
17//! - Serialization to all major RDF-star formats
18//! - Storage backend integration with oxirs-core
19//! - Performance-optimized handling of nested quoted triples
20//! - Comprehensive CLI tools for validation and debugging
21//! - Advanced error handling with context and recovery suggestions
22//!
23//! ## Quick Start
24//!
25//! ### Basic Quoted Triple Creation
26//!
27//! ```rust,ignore
28//! use oxirs_star::{StarStore, StarTriple, StarTerm};
29//!
30//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
31//! let mut store = StarStore::new();
32//!
33//! // Create a quoted triple
34//! let quoted = StarTriple::new(
35//!     StarTerm::iri("http://example.org/person1")?,
36//!     StarTerm::iri("http://example.org/age")?,
37//!     StarTerm::literal("25")?,
38//! );
39//!
40//! // Use the quoted triple as a subject
41//! let meta_triple = StarTriple::new(
42//!     StarTerm::quoted_triple(quoted),
43//!     StarTerm::iri("http://example.org/certainty")?,
44//!     StarTerm::literal("0.9")?,
45//! );
46//!
47//! store.insert(&meta_triple)?;
48//! println!("Stored {} triples", store.len());
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! ### Parsing RDF-star Data
54//!
55//! ```rust,ignore
56//! use oxirs_star::parser::{StarParser, StarFormat};
57//!
58//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
59//! let turtle_star = r#"
60//!     @prefix ex: <http://example.org/> .
61//!
62//!     << ex:alice ex:age 30 >> ex:certainty 0.9 .
63//!     << ex:alice ex:name "Alice" >> ex:source ex:census2020 .
64//! "#;
65//!
66//! let mut parser = StarParser::new();
67//! let graph = parser.parse_str(turtle_star, StarFormat::TurtleStar)?;
68//!
69//! println!("Parsed {} quoted triples", graph.len());
70//! # Ok(())
71//! # }
72//! ```
73//!
74//! ### Using the CLI Tools
75//!
76//! ```bash
77//! # Validate an RDF-star file
78//! oxirs-star validate data.ttls --strict
79//!
80//! # Convert between formats
81//! oxirs-star convert input.ttls output.nts --to ntriples-star --pretty
82//!
83//! # Analyze data structure
84//! oxirs-star analyze large_dataset.ttls --json --output report.json
85//!
86//! # Debug parsing issues
87//! oxirs-star debug problematic.ttls --line 42 --context 5
88//! ```
89//!
90//! ## Advanced Usage
91//!
92//! ### Nested Quoted Triples
93//!
94//! ```rust,ignore
95//! use oxirs_star::{StarStore, StarTriple, StarTerm, StarConfig};
96//!
97//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
98//! // Configure for deep nesting
99//! let config = StarConfig {
100//!     max_nesting_depth: 20,
101//!     ..Default::default()
102//! };
103//!
104//! let mut store = StarStore::with_config(config);
105//!
106//! // Create deeply nested structure
107//! let base = StarTriple::new(
108//!     StarTerm::iri("http://example.org/alice")?,
109//!     StarTerm::iri("http://example.org/age")?,
110//!     StarTerm::literal("30")?,
111//! );
112//!
113//! let meta = StarTriple::new(
114//!     StarTerm::quoted_triple(base),
115//!     StarTerm::iri("http://example.org/certainty")?,
116//!     StarTerm::literal("0.9")?,
117//! );
118//!
119//! let meta_meta = StarTriple::new(
120//!     StarTerm::quoted_triple(meta),
121//!     StarTerm::iri("http://example.org/source")?,
122//!     StarTerm::iri("http://example.org/study2023")?,
123//! );
124//!
125//! store.insert(&meta_meta)?;
126//! let stats = store.statistics();
127//! println!("Max nesting depth: {}", stats.max_nesting_encountered);
128//! # Ok(())
129//! # }
130//! ```
131//!
132//! ### Performance Optimization
133//!
134//! ```rust,ignore
135//! use oxirs_star::{StarStore, StarConfig};
136//!
137//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
138//! // Configure for high-performance scenarios
139//! let config = StarConfig {
140//!     max_nesting_depth: 10,
141//!     enable_reification_fallback: true,
142//!     buffer_size: 16384,  // Larger buffer for streaming
143//!     strict_mode: false,  // Allow recovery from minor issues
144//!     ..Default::default()
145//! };
146//!
147//! let store = StarStore::with_config(config);
148//! println!("Store configured for high performance");
149//! # Ok(())
150//! # }
151//! ```
152//!
153//! ## Error Handling
154//!
155//! The crate provides detailed error types with context and recovery suggestions:
156//!
157//! ```rust,ignore
158//! use oxirs_star::{StarError, StarResult};
159//!
160//! fn handle_errors() -> StarResult<()> {
161//!     // ... some operation that might fail
162//!     Err(StarError::NestingDepthExceeded {
163//!         max_depth: 10,
164//!         current_depth: 15,
165//!         context: Some("While parsing complex quoted triple".to_string()),
166//!     })
167//! }
168//!
169//! # fn main() {
170//! match handle_errors() {
171//!     Err(e) => {
172//!         eprintln!("Error: {}", e);
173//!         for suggestion in e.recovery_suggestions() {
174//!             eprintln!("Suggestion: {}", suggestion);
175//!         }
176//!     }
177//!     Ok(_) => println!("Success"),
178//! }
179//! # }
180//! ```
181//!
182//! ## Troubleshooting
183//!
184//! ### Common Issues
185//!
186//! 1. **Parsing Errors**: Use `oxirs-star debug` to identify syntax issues
187//! 2. **Performance Issues**: Enable indexing and adjust buffer sizes
188//! 3. **Memory Usage**: Reduce nesting depth or enable reification fallback
189//! 4. **Format Detection**: Explicitly specify format if auto-detection fails
190//!
191//! ### Getting Help
192//!
193//! - Use the `dev_tools` module for validation and diagnostics
194//! - Check the comprehensive documentation in the `docs` module
195//! - Run `oxirs-star --help` for CLI usage information
196//! - See the examples directory for real-world usage patterns
197
198use oxirs_core::OxirsError;
199use serde::{Deserialize, Serialize};
200use thiserror::Error;
201use tracing::{debug, info, span, Level};
202
203pub mod adaptive_query_optimizer;
204pub mod advanced_query;
205pub mod annotation_aggregation;
206pub mod annotation_lifecycle;
207pub mod annotation_paths;
208pub mod annotation_profile;
209pub mod annotations;
210pub mod backup_restore;
211pub mod bloom_filter;
212pub mod cache;
213pub mod cli;
214pub mod cluster_scaling;
215pub mod compact_annotation_storage;
216pub mod compatibility;
217pub mod compliance;
218pub mod compliance_reporting;
219pub mod cryptographic_provenance;
220pub mod distributed;
221pub mod docs;
222pub mod enhanced_errors;
223pub mod execution;
224pub mod functions;
225pub mod governance;
226pub mod gpu_acceleration;
227pub mod graph_diff;
228pub mod graphql_star;
229pub mod hdt_star;
230pub mod index;
231pub mod jit_query_engine;
232pub mod kg_embeddings;
233pub mod lsm_annotation_store;
234pub mod materialized_views;
235pub mod memory_efficient_store;
236pub mod migration_tools;
237pub mod ml_embedding_pipeline;
238pub mod ml_sparql_optimizer;
239pub mod model;
240pub mod monitoring;
241pub mod parallel_query;
242pub mod parser;
243pub mod production;
244pub mod profiling;
245pub mod property_graph_bridge;
246pub mod quantum_sparql_optimizer;
247pub mod query;
248pub mod query_optimizer;
249pub mod quoted_graph;
250pub mod rdf_star_formats;
251pub mod reasoning;
252pub mod reification;
253pub mod reification_bridge;
254pub mod security_audit;
255pub mod semantics;
256pub mod serialization;
257pub mod serializer;
258pub mod shacl_star;
259pub mod sparql;
260pub mod sparql_enhanced;
261pub mod sparql_star_bind_values;
262pub mod sparql_star_extended;
263pub mod storage;
264pub mod storage_integration;
265pub mod store;
266pub mod streaming_query;
267pub mod temporal_versioning;
268pub mod testing_utilities;
269pub mod tiered_storage;
270pub mod troubleshooting;
271pub mod trust_scoring;
272pub mod validation_framework;
273pub mod w3c_compliance;
274// RDF-star reification mapper (v1.1.0 round 5)
275pub mod reification_mapper;
276pub mod write_ahead_log;
277
278// RDF 1.1 reification ↔ RDF-star bidirectional converter (v1.1.0 round 7)
279pub mod triple_reifier;
280
281// RDF-star provenance tracker (v1.1.0 round 6)
282pub mod provenance_tracker;
283
284// W3C RDF Patch A/D/TX format (v1.1.0 round 8)
285pub mod rdf_patch;
286
287// In-memory store for RDF-star quoted triples (v1.1.0 round 9)
288pub mod quoted_triple_store;
289
290// RDF-star annotation graph for triple metadata (v1.1.0 round 10)
291pub mod annotation_graph;
292
293// RDF-star serialization to Turtle-star, N-Triples-star, JSON-LD-star (v1.1.0 round 11)
294pub mod rdf_star_serializer;
295
296// RDF-star pattern matching for nested triple queries (v1.1.0 round 13)
297pub mod star_pattern_matcher;
298
299// RDF-star graph normalization (v1.1.0 round 12)
300pub mod star_normalizer;
301
302// RDF-star query to standard RDF rewriting (v1.1.0 round 11)
303pub mod star_query_rewriter;
304
305/// RDF / RDF-star graph diff: added/removed triples, RDF Patch generation/application,
306/// symmetric difference, blank-node-aware isomorphic diff (v1.1.0 round 13)
307pub mod triple_diff;
308
309/// RDF-star graph statistics collector (v1.1.0 round 14).
310pub mod star_statistics;
311
312/// RDF-star graph merging with conflict resolution (v1.1.0 round 15).
313pub mod graph_merger;
314
315// Re-export main types
316pub use enhanced_errors::{
317    EnhancedError, EnhancedResult, ErrorAggregator, ErrorCategory, ErrorContext, ErrorSeverity,
318    WithErrorContext,
319};
320pub use model::*;
321pub use store::StarStore;
322pub use troubleshooting::{DiagnosticAnalyzer, MigrationAssistant, TroubleshootingGuide};
323
324/// Parse error details for RDF-star format
325#[derive(Debug, Error)]
326#[error("Parse error: {message}")]
327pub struct ParseErrorDetails {
328    pub message: String,
329    pub line: Option<usize>,
330    pub column: Option<usize>,
331    pub input_fragment: Option<String>,
332    pub expected: Option<String>,
333    pub suggestion: Option<String>,
334}
335
336/// RDF-star specific error types
337#[derive(Debug, Error)]
338pub enum StarError {
339    #[error("Invalid quoted triple: {message}")]
340    InvalidQuotedTriple {
341        message: String,
342        context: Option<String>,
343        suggestion: Option<String>,
344    },
345    #[error("Parse error in RDF-star format: {0}")]
346    ParseError(#[from] Box<ParseErrorDetails>),
347    #[error("Serialization error: {message}")]
348    SerializationError {
349        message: String,
350        format: Option<String>,
351        context: Option<String>,
352    },
353    #[error("SPARQL-star query error: {message}")]
354    QueryError {
355        message: String,
356        query_fragment: Option<String>,
357        position: Option<usize>,
358        suggestion: Option<String>,
359    },
360    #[error("Core RDF error: {0}")]
361    CoreError(#[from] OxirsError),
362    #[error("Reification error: {message}")]
363    ReificationError {
364        message: String,
365        reification_strategy: Option<String>,
366        context: Option<String>,
367    },
368    #[error("Invalid term type for RDF-star context: {message}")]
369    InvalidTermType {
370        message: String,
371        term_type: Option<String>,
372        expected_types: Option<Vec<String>>,
373        suggestion: Option<String>,
374    },
375    #[error("Nesting depth exceeded: maximum depth {max_depth} reached")]
376    NestingDepthExceeded {
377        max_depth: usize,
378        current_depth: usize,
379        context: Option<String>,
380    },
381    #[error("Format not supported: {format}")]
382    UnsupportedFormat {
383        format: String,
384        available_formats: Vec<String>,
385    },
386    #[error("Configuration error: {message}")]
387    ConfigurationError {
388        message: String,
389        parameter: Option<String>,
390        valid_range: Option<String>,
391    },
392    #[error("Internal error: {message}")]
393    InternalError {
394        message: String,
395        context: Option<String>,
396    },
397}
398
399/// Result type for RDF-star operations
400pub type StarResult<T> = std::result::Result<T, StarError>;
401
402impl StarError {
403    /// Create a simple invalid quoted triple error (backward compatibility)
404    pub fn invalid_quoted_triple(message: impl Into<String>) -> Self {
405        Self::InvalidQuotedTriple {
406            message: message.into(),
407            context: None,
408            suggestion: None,
409        }
410    }
411
412    /// Create a simple parse error (backward compatibility)
413    pub fn parse_error(message: impl Into<String>) -> Self {
414        Self::ParseError(Box::new(ParseErrorDetails {
415            message: message.into(),
416            line: None,
417            column: None,
418            input_fragment: None,
419            expected: None,
420            suggestion: None,
421        }))
422    }
423
424    /// Create a simple serialization error (backward compatibility)
425    pub fn serialization_error(message: impl Into<String>) -> Self {
426        Self::SerializationError {
427            message: message.into(),
428            format: None,
429            context: None,
430        }
431    }
432
433    /// Create a simple query error (backward compatibility)
434    pub fn query_error(message: impl Into<String>) -> Self {
435        Self::QueryError {
436            message: message.into(),
437            query_fragment: None,
438            position: None,
439            suggestion: None,
440        }
441    }
442
443    /// Create a simple reification error (backward compatibility)
444    pub fn reification_error(message: impl Into<String>) -> Self {
445        Self::ReificationError {
446            message: message.into(),
447            reification_strategy: None,
448            context: None,
449        }
450    }
451
452    /// Create a simple invalid term type error (backward compatibility)
453    pub fn invalid_term_type(message: impl Into<String>) -> Self {
454        Self::InvalidTermType {
455            message: message.into(),
456            term_type: None,
457            expected_types: None,
458            suggestion: None,
459        }
460    }
461
462    /// Create a nesting depth error
463    pub fn nesting_depth_exceeded(
464        max_depth: usize,
465        current_depth: usize,
466        context: Option<String>,
467    ) -> Self {
468        Self::NestingDepthExceeded {
469            max_depth,
470            current_depth,
471            context,
472        }
473    }
474
475    /// Create a configuration error
476    pub fn configuration_error(message: impl Into<String>) -> Self {
477        Self::ConfigurationError {
478            message: message.into(),
479            parameter: None,
480            valid_range: None,
481        }
482    }
483
484    /// Create an internal error (for unexpected conditions such as lock poisoning)
485    pub fn internal_error(message: impl Into<String>) -> Self {
486        Self::InternalError {
487            message: message.into(),
488            context: None,
489        }
490    }
491
492    /// Create an internal error for lock poisoning
493    pub fn lock_error(context: impl Into<String>) -> Self {
494        Self::InternalError {
495            message: "Lock poisoned".to_string(),
496            context: Some(context.into()),
497        }
498    }
499
500    /// Create an unsupported format error with available alternatives
501    pub fn unsupported_format(format: impl Into<String>, available: Vec<String>) -> Self {
502        Self::UnsupportedFormat {
503            format: format.into(),
504            available_formats: available,
505        }
506    }
507
508    /// Get available recovery suggestions for the error
509    pub fn recovery_suggestions(&self) -> Vec<String> {
510        let mut suggestions = Vec::new();
511
512        match self {
513            Self::NestingDepthExceeded { max_depth, .. } => {
514                suggestions.push(format!(
515                    "Consider increasing max_nesting_depth beyond {max_depth}"
516                ));
517                suggestions.push("Check for circular references in quoted triples".to_string());
518            }
519            Self::UnsupportedFormat {
520                available_formats, ..
521            } => {
522                suggestions.push(format!(
523                    "Supported formats: {}",
524                    available_formats.join(", ")
525                ));
526            }
527            Self::ConfigurationError {
528                valid_range: Some(range),
529                ..
530            } => {
531                suggestions.push(format!("Valid range: {range}"));
532            }
533            Self::ConfigurationError {
534                valid_range: None, ..
535            } => {}
536            _ => {}
537        }
538
539        suggestions
540    }
541
542    /// Create a resource error (backward compatibility)
543    pub fn resource_error(message: impl Into<String>) -> Self {
544        Self::ConfigurationError {
545            message: message.into(),
546            parameter: Some("resource".to_string()),
547            valid_range: None,
548        }
549    }
550
551    /// Create a processing error (backward compatibility)
552    pub fn processing_error(message: impl Into<String>) -> Self {
553        Self::ConfigurationError {
554            message: message.into(),
555            parameter: Some("processing".to_string()),
556            valid_range: None,
557        }
558    }
559}
560
561/// Configuration for RDF-star processing
562#[derive(Debug, Clone, Serialize, Deserialize)]
563pub struct StarConfig {
564    /// Maximum nesting depth for quoted triples (default: 10)
565    pub max_nesting_depth: usize,
566    /// Enable automatic reification fallback
567    pub enable_reification_fallback: bool,
568    /// Strict mode for parsing (reject invalid constructs)
569    pub strict_mode: bool,
570    /// Enable SPARQL-star extensions
571    pub enable_sparql_star: bool,
572    /// Buffer size for streaming operations
573    pub buffer_size: usize,
574    /// Maximum parse errors before aborting (None for unlimited)
575    pub max_parse_errors: Option<usize>,
576}
577
578impl Default for StarConfig {
579    fn default() -> Self {
580        Self {
581            max_nesting_depth: 10,
582            enable_reification_fallback: true,
583            strict_mode: false,
584            enable_sparql_star: true,
585            buffer_size: 8192,
586            max_parse_errors: Some(100),
587        }
588    }
589}
590
591/// Statistics for RDF-star processing
592#[derive(Debug, Clone, Default, Serialize, Deserialize)]
593pub struct StarStatistics {
594    /// Total number of quoted triples processed
595    pub quoted_triples_count: usize,
596    /// Maximum nesting depth encountered
597    pub max_nesting_encountered: usize,
598    /// Number of reified triples
599    pub reified_triples_count: usize,
600    /// Number of SPARQL-star queries executed
601    pub sparql_star_queries_count: usize,
602    /// Processing time statistics (in microseconds)
603    pub processing_time_us: u64,
604}
605
606/// Initialize the RDF-star system with configuration
607pub fn init_star_system(config: StarConfig) -> StarResult<()> {
608    let span = span!(Level::INFO, "init_star_system");
609    let _enter = span.enter();
610
611    info!("Initializing OxiRS RDF-star system");
612    debug!("Configuration: {:?}", config);
613
614    // Validate configuration
615    if config.max_nesting_depth == 0 {
616        return Err(StarError::ConfigurationError {
617            message: "Max nesting depth must be greater than 0".to_string(),
618            parameter: Some("max_nesting_depth".to_string()),
619            valid_range: Some("1..=1000".to_string()),
620        });
621    }
622
623    if config.buffer_size == 0 {
624        return Err(StarError::ConfigurationError {
625            message: "Buffer size must be greater than 0".to_string(),
626            parameter: Some("buffer_size".to_string()),
627            valid_range: Some("1..=1048576".to_string()),
628        });
629    }
630
631    // Additional validation for reasonable limits
632    if config.max_nesting_depth > 1000 {
633        return Err(StarError::ConfigurationError {
634            message: "Max nesting depth is too large and may cause performance issues".to_string(),
635            parameter: Some("max_nesting_depth".to_string()),
636            valid_range: Some("1..=1000".to_string()),
637        });
638    }
639
640    info!("RDF-star system initialized successfully");
641    Ok(())
642}
643
644/// Utility function to validate quoted triple nesting depth
645pub fn validate_nesting_depth(term: &StarTerm, max_depth: usize) -> StarResult<()> {
646    fn check_depth(term: &StarTerm, current_depth: usize, max_depth: usize) -> StarResult<usize> {
647        match term {
648            StarTerm::QuotedTriple(triple) => {
649                if current_depth >= max_depth {
650                    return Err(StarError::InvalidQuotedTriple {
651                        message: format!(
652                            "Nesting depth {current_depth} exceeds maximum {max_depth}"
653                        ),
654                        context: None,
655                        suggestion: None,
656                    });
657                }
658
659                let subj_depth = check_depth(&triple.subject, current_depth + 1, max_depth)?;
660                let pred_depth = check_depth(&triple.predicate, current_depth + 1, max_depth)?;
661                let obj_depth = check_depth(&triple.object, current_depth + 1, max_depth)?;
662
663                Ok(subj_depth.max(pred_depth).max(obj_depth))
664            }
665            _ => Ok(current_depth),
666        }
667    }
668
669    check_depth(term, 0, max_depth)?;
670    Ok(())
671}
672
673/// Version information
674pub const VERSION: &str = env!("CARGO_PKG_VERSION");
675
676/// Developer tooling and debugging utilities
677///
678/// This module provides comprehensive tools for validating, debugging, and analyzing
679/// RDF-star data. It's designed to help developers identify issues, optimize performance,
680/// and ensure data quality.
681///
682/// # Examples
683///
684/// ```rust,ignore
685/// use oxirs_star::dev_tools::{detect_format, validate_content, StarProfiler};
686/// use oxirs_star::StarConfig;
687///
688/// // Detect format from content
689/// let content = "<< :s :p :o >> :meta :value .";
690/// let format = detect_format(content);
691/// println!("Detected format: {:?}", format);
692///
693/// // Validate content with detailed diagnostics
694/// let config = StarConfig::default();
695/// let result = validate_content(content, &config);
696/// if !result.is_valid() {
697///     for error in &result.errors {
698///         println!("Error: {}", error);
699///     }
700/// }
701///
702/// // Profile performance
703/// let mut profiler = StarProfiler::new();
704/// let result = profiler.time_operation("parsing", || {
705///     // ... parsing operation
706///     42
707/// });
708/// println!("Operation took: {:?}", profiler.total_time());
709/// ```
710pub mod dev_tools {
711    use super::*;
712    use std::collections::HashMap;
713
714    /// RDF-star format detection result
715    #[derive(Debug, Clone, PartialEq)]
716    pub enum DetectedFormat {
717        TurtleStar,
718        NTriplesStar,
719        TrigStar,
720        NQuadsStar,
721        Unknown,
722    }
723
724    /// Detect RDF-star format from input content
725    pub fn detect_format(content: &str) -> DetectedFormat {
726        let content = content.trim();
727
728        // Check for TriG-star indicators
729        if content.contains("GRAPH") || content.contains("{") && content.contains("}") {
730            return DetectedFormat::TrigStar;
731        }
732
733        // Check for N-Quads-star (4 terms per line)
734        let lines: Vec<&str> = content
735            .lines()
736            .filter(|line| !line.trim().is_empty() && !line.trim().starts_with('#'))
737            .collect();
738        if !lines.is_empty() {
739            let first_line = lines[0].trim();
740            let terms: Vec<&str> = first_line.split_whitespace().collect();
741            if terms.len() >= 4 && first_line.ends_with('.') {
742                return DetectedFormat::NQuadsStar;
743            }
744        }
745
746        // Check for quoted triples (RDF-star indicator)
747        if content.contains("<<") && content.contains(">>") {
748            // If has quotes and prefixes, likely Turtle-star
749            if content.contains("@prefix") || content.contains("PREFIX") {
750                return DetectedFormat::TurtleStar;
751            }
752            // Otherwise, likely N-Triples-star
753            return DetectedFormat::NTriplesStar;
754        }
755
756        // Check for Turtle-star prefixes
757        if content.contains("@prefix") || content.contains("@base") {
758            return DetectedFormat::TurtleStar;
759        }
760
761        DetectedFormat::Unknown
762    }
763
764    /// Validate RDF-star content and return detailed diagnostic information
765    pub fn validate_content(content: &str, config: &StarConfig) -> ValidationResult {
766        let mut result = ValidationResult::new();
767
768        // Basic format detection
769        result.detected_format = detect_format(content);
770
771        // Count quoted triples
772        let quoted_count = content.matches("<<").count();
773        result.quoted_triple_count = quoted_count;
774
775        // Check for potential issues
776        if quoted_count > 10000 && !config.enable_reification_fallback {
777            result.warnings.push("Large number of quoted triples detected. Consider enabling reification fallback for better performance.".to_string());
778        }
779
780        // Check nesting depth by counting nested <<
781        let max_nesting = find_max_nesting_depth(content);
782        result.max_nesting_depth = max_nesting;
783
784        if max_nesting > config.max_nesting_depth {
785            result.errors.push(format!(
786                "Nesting depth {} exceeds configured maximum {}",
787                max_nesting, config.max_nesting_depth
788            ));
789        }
790
791        // Check for common syntax issues
792        check_syntax_issues(content, &mut result);
793
794        result
795    }
796
797    /// Validation result with detailed diagnostics
798    #[derive(Debug, Clone)]
799    pub struct ValidationResult {
800        pub detected_format: DetectedFormat,
801        pub quoted_triple_count: usize,
802        pub max_nesting_depth: usize,
803        pub errors: Vec<String>,
804        pub warnings: Vec<String>,
805        pub suggestions: Vec<String>,
806        pub line_errors: HashMap<u32, String>,
807    }
808
809    impl ValidationResult {
810        fn new() -> Self {
811            Self {
812                detected_format: DetectedFormat::Unknown,
813                quoted_triple_count: 0,
814                max_nesting_depth: 0,
815                errors: Vec::new(),
816                warnings: Vec::new(),
817                suggestions: Vec::new(),
818                line_errors: HashMap::new(),
819            }
820        }
821
822        /// Check if validation passed without errors
823        pub fn is_valid(&self) -> bool {
824            self.errors.is_empty()
825        }
826
827        /// Get a summary report of the validation
828        pub fn summary(&self) -> String {
829            let mut summary = String::new();
830            summary.push_str(&format!("Format: {:?}\n", self.detected_format));
831            summary.push_str(&format!("Quoted triples: {}\n", self.quoted_triple_count));
832            summary.push_str(&format!("Max nesting depth: {}\n", self.max_nesting_depth));
833
834            if !self.errors.is_empty() {
835                summary.push_str(&format!("Errors: {}\n", self.errors.len()));
836            }
837
838            if !self.warnings.is_empty() {
839                summary.push_str(&format!("Warnings: {}\n", self.warnings.len()));
840            }
841
842            summary
843        }
844    }
845
846    fn find_max_nesting_depth(content: &str) -> usize {
847        let mut max_depth: usize = 0;
848        let mut current_depth: i32 = 0;
849
850        for ch in content.chars() {
851            match ch {
852                '<' => {
853                    // Look ahead for another '<' to detect quoted triple start
854                    current_depth += 1;
855                }
856                '>' => {
857                    current_depth = current_depth.saturating_sub(1);
858                }
859                _ => {}
860            }
861            max_depth = max_depth.max((current_depth / 2).max(0) as usize); // Divide by 2 since we count both < and >
862        }
863
864        max_depth
865    }
866
867    fn check_syntax_issues(content: &str, result: &mut ValidationResult) {
868        let lines: Vec<&str> = content.lines().collect();
869
870        for (line_num, line) in lines.iter().enumerate() {
871            let line_num = line_num as u32 + 1;
872            let trimmed = line.trim();
873
874            // Skip comments and empty lines
875            if trimmed.is_empty() || trimmed.starts_with('#') {
876                continue;
877            }
878
879            // Check for unmatched quoted triple brackets
880            let open_count = trimmed.matches("<<").count();
881            let close_count = trimmed.matches(">>").count();
882
883            if open_count != close_count {
884                result.line_errors.insert(
885                    line_num,
886                    format!(
887                        "Unmatched quoted triple brackets: {open_count} << vs {close_count} >>"
888                    ),
889                );
890            }
891
892            // Check for missing periods in N-Triples/N-Quads style
893            if (result.detected_format == DetectedFormat::NTriplesStar
894                || result.detected_format == DetectedFormat::NQuadsStar)
895                && !trimmed.ends_with('.')
896                && !trimmed.starts_with('@')
897                && !trimmed.starts_with("PREFIX")
898            {
899                result.warnings.push(format!(
900                    "Line {line_num}: Missing period at end of statement"
901                ));
902            }
903        }
904    }
905
906    /// Performance profiler for RDF-star operations
907    pub struct StarProfiler {
908        start_time: std::time::Instant,
909        operation_times: HashMap<String, u64>,
910    }
911
912    impl StarProfiler {
913        pub fn new() -> Self {
914            Self {
915                start_time: std::time::Instant::now(),
916                operation_times: HashMap::new(),
917            }
918        }
919
920        pub fn time_operation<F, R>(&mut self, name: &str, operation: F) -> R
921        where
922            F: FnOnce() -> R,
923        {
924            let start = std::time::Instant::now();
925            let result = operation();
926            let duration = start.elapsed().as_micros() as u64;
927            self.operation_times.insert(name.to_string(), duration);
928            result
929        }
930
931        pub fn get_stats(&self) -> HashMap<String, u64> {
932            self.operation_times.clone()
933        }
934
935        pub fn total_time(&self) -> u64 {
936            self.start_time.elapsed().as_micros() as u64
937        }
938    }
939
940    impl Default for StarProfiler {
941        fn default() -> Self {
942            Self::new()
943        }
944    }
945
946    /// Generate a diagnostic report for RDF-star content
947    pub fn generate_diagnostic_report(content: &str, config: &StarConfig) -> String {
948        let validation = validate_content(content, config);
949        let mut report = String::new();
950
951        report.push_str("=== RDF-star Diagnostic Report ===\n\n");
952        report.push_str(&validation.summary());
953
954        if !validation.errors.is_empty() {
955            report.push_str("\nErrors:\n");
956            for (i, error) in validation.errors.iter().enumerate() {
957                report.push_str(&format!("  {}. {}\n", i + 1, error));
958            }
959        }
960
961        if !validation.warnings.is_empty() {
962            report.push_str("\nWarnings:\n");
963            for (i, warning) in validation.warnings.iter().enumerate() {
964                report.push_str(&format!("  {}. {}\n", i + 1, warning));
965            }
966        }
967
968        if !validation.suggestions.is_empty() {
969            report.push_str("\nSuggestions:\n");
970            for (i, suggestion) in validation.suggestions.iter().enumerate() {
971                report.push_str(&format!("  {}. {}\n", i + 1, suggestion));
972            }
973        }
974
975        if !validation.line_errors.is_empty() {
976            report.push_str("\nLine-specific issues:\n");
977            let mut sorted_lines: Vec<_> = validation.line_errors.iter().collect();
978            sorted_lines.sort_by_key(|(line, _)| *line);
979
980            for (line, error) in sorted_lines {
981                report.push_str(&format!("  Line {line}: {error}\n"));
982            }
983        }
984
985        report
986    }
987}
988
989#[cfg(test)]
990mod tests {
991    use super::*;
992
993    #[test]
994    fn test_config_default() {
995        let config = StarConfig::default();
996        assert_eq!(config.max_nesting_depth, 10);
997        assert!(config.enable_reification_fallback);
998        assert!(!config.strict_mode);
999        assert!(config.enable_sparql_star);
1000    }
1001
1002    #[test]
1003    fn test_nesting_depth_validation() {
1004        let simple_term = StarTerm::iri("http://example.org/test").unwrap();
1005        assert!(validate_nesting_depth(&simple_term, 5).is_ok());
1006
1007        // Test nested quoted triple
1008        let inner_triple = StarTriple::new(
1009            StarTerm::iri("http://example.org/s").unwrap(),
1010            StarTerm::iri("http://example.org/p").unwrap(),
1011            StarTerm::iri("http://example.org/o").unwrap(),
1012        );
1013        let nested_term = StarTerm::quoted_triple(inner_triple);
1014        assert!(validate_nesting_depth(&nested_term, 5).is_ok());
1015        assert!(validate_nesting_depth(&nested_term, 0).is_err());
1016    }
1017}