Skip to main content

oxirs_star/
lib.rs

1//! # OxiRS RDF-Star
2//!
3//! [![Version](https://img.shields.io/badge/version-0.4.1-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.4.1)
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 cli_commands;
215pub(crate) mod cli_executor;
216pub(crate) mod cli_output;
217#[cfg(test)]
218mod cli_tests;
219pub mod cluster_scaling;
220pub mod compact_annotation_storage;
221pub mod compatibility;
222pub mod compliance;
223pub mod compliance_reporting;
224pub mod cryptographic_provenance;
225pub mod distributed;
226pub mod docs;
227pub mod enhanced_errors;
228pub mod execution;
229pub mod functions;
230pub mod governance;
231pub mod gpu_acceleration;
232pub mod graph_diff;
233pub mod graphql_star;
234pub mod hdt_star;
235pub mod index;
236pub mod jit_query_engine;
237pub mod kg_embeddings;
238pub mod lsm_annotation_store;
239pub mod materialized_views;
240pub mod memory_efficient_store;
241pub mod migration_tools;
242pub mod ml_embedding_pipeline;
243pub mod ml_sparql_optimizer;
244pub mod model;
245pub mod monitoring;
246pub mod parallel_query;
247pub mod parser;
248pub mod parser_ast;
249#[cfg(test)]
250mod parser_inline_tests;
251pub mod parser_lexer;
252pub mod parser_rdfstar;
253pub mod parser_statements;
254pub mod parser_tests;
255pub mod production;
256pub mod profiling;
257pub mod property_graph_bridge;
258pub mod quantum_sparql_optimizer;
259pub mod query;
260pub mod query_optimizer;
261pub mod quoted_graph;
262pub mod rdf_star_formats;
263pub mod reasoning;
264pub mod reification;
265pub mod reification_bridge;
266pub mod security_audit;
267pub mod semantics;
268pub mod serialization;
269pub mod serializer;
270pub mod shacl_star;
271pub mod sparql;
272pub mod sparql_enhanced;
273pub mod sparql_star_bind_values;
274pub mod sparql_star_extended;
275pub mod storage;
276pub mod storage_integration;
277pub mod store;
278pub mod store_core;
279pub mod store_indexing;
280pub mod store_query;
281#[cfg(test)]
282mod store_tests;
283pub mod streaming_query;
284pub mod temporal_versioning;
285pub mod testing_utilities;
286pub mod tiered_storage;
287pub mod troubleshooting;
288pub mod trust_scoring;
289pub mod validation_framework;
290pub mod w3c_compliance;
291// RDF-star reification mapper (v1.1.0 round 5)
292pub mod reification_mapper;
293pub mod write_ahead_log;
294
295// RDF 1.1 reification ↔ RDF-star bidirectional converter (v1.1.0 round 7)
296pub mod triple_reifier;
297
298// RDF-star provenance tracker (v1.1.0 round 6)
299pub mod provenance_tracker;
300
301// W3C RDF Patch A/D/TX format (v1.1.0 round 8)
302pub mod rdf_patch;
303
304// In-memory store for RDF-star quoted triples (v1.1.0 round 9)
305pub mod quoted_triple_store;
306
307// RDF-star annotation graph for triple metadata (v1.1.0 round 10)
308pub mod annotation_graph;
309
310// RDF-star serialization to Turtle-star, N-Triples-star, JSON-LD-star (v1.1.0 round 11)
311pub mod rdf_star_serializer;
312
313// RDF-star pattern matching for nested triple queries (v1.1.0 round 13)
314pub mod star_pattern_matcher;
315
316// RDF-star graph normalization (v1.1.0 round 12)
317pub mod star_normalizer;
318
319// RDF-star query to standard RDF rewriting (v1.1.0 round 11)
320pub mod star_query_rewriter;
321
322/// RDF / RDF-star graph diff: added/removed triples, RDF Patch generation/application,
323/// symmetric difference, blank-node-aware isomorphic diff (v1.1.0 round 13)
324pub mod triple_diff;
325
326/// RDF-star graph statistics collector (v1.1.0 round 14).
327pub mod star_statistics;
328
329/// RDF-star graph merging with conflict resolution (v1.1.0 round 15).
330pub mod graph_merger;
331
332/// RDF-star annotation syntax module: `{| ... |}` shorthand parsing and expansion.
333pub mod annotation_syntax;
334
335// Re-export main types
336pub use enhanced_errors::{
337    EnhancedError, EnhancedResult, ErrorAggregator, ErrorCategory, ErrorContext, ErrorSeverity,
338    WithErrorContext,
339};
340pub use model::*;
341pub use store::StarStore;
342pub use troubleshooting::{DiagnosticAnalyzer, MigrationAssistant, TroubleshootingGuide};
343
344/// Parse error details for RDF-star format
345#[derive(Debug, Error)]
346#[error("Parse error: {message}")]
347pub struct ParseErrorDetails {
348    pub message: String,
349    pub line: Option<usize>,
350    pub column: Option<usize>,
351    pub input_fragment: Option<String>,
352    pub expected: Option<String>,
353    pub suggestion: Option<String>,
354}
355
356/// RDF-star specific error types
357#[derive(Debug, Error)]
358pub enum StarError {
359    #[error("Invalid quoted triple: {message}")]
360    InvalidQuotedTriple {
361        message: String,
362        context: Option<String>,
363        suggestion: Option<String>,
364    },
365    #[error("Parse error in RDF-star format: {0}")]
366    ParseError(#[from] Box<ParseErrorDetails>),
367    #[error("Serialization error: {message}")]
368    SerializationError {
369        message: String,
370        format: Option<String>,
371        context: Option<String>,
372    },
373    #[error("SPARQL-star query error: {message}")]
374    QueryError {
375        message: String,
376        query_fragment: Option<String>,
377        position: Option<usize>,
378        suggestion: Option<String>,
379    },
380    #[error("Core RDF error: {0}")]
381    CoreError(#[from] OxirsError),
382    #[error("Reification error: {message}")]
383    ReificationError {
384        message: String,
385        reification_strategy: Option<String>,
386        context: Option<String>,
387    },
388    #[error("Invalid term type for RDF-star context: {message}")]
389    InvalidTermType {
390        message: String,
391        term_type: Option<String>,
392        expected_types: Option<Vec<String>>,
393        suggestion: Option<String>,
394    },
395    #[error("Nesting depth exceeded: maximum depth {max_depth} reached")]
396    NestingDepthExceeded {
397        max_depth: usize,
398        current_depth: usize,
399        context: Option<String>,
400    },
401    #[error("Format not supported: {format}")]
402    UnsupportedFormat {
403        format: String,
404        available_formats: Vec<String>,
405    },
406    #[error("Configuration error: {message}")]
407    ConfigurationError {
408        message: String,
409        parameter: Option<String>,
410        valid_range: Option<String>,
411    },
412    #[error("Internal error: {message}")]
413    InternalError {
414        message: String,
415        context: Option<String>,
416    },
417}
418
419/// Result type for RDF-star operations
420pub type StarResult<T> = std::result::Result<T, StarError>;
421
422impl StarError {
423    /// Create a simple invalid quoted triple error (backward compatibility)
424    pub fn invalid_quoted_triple(message: impl Into<String>) -> Self {
425        Self::InvalidQuotedTriple {
426            message: message.into(),
427            context: None,
428            suggestion: None,
429        }
430    }
431
432    /// Create a simple parse error (backward compatibility)
433    pub fn parse_error(message: impl Into<String>) -> Self {
434        Self::ParseError(Box::new(ParseErrorDetails {
435            message: message.into(),
436            line: None,
437            column: None,
438            input_fragment: None,
439            expected: None,
440            suggestion: None,
441        }))
442    }
443
444    /// Create a simple serialization error (backward compatibility)
445    pub fn serialization_error(message: impl Into<String>) -> Self {
446        Self::SerializationError {
447            message: message.into(),
448            format: None,
449            context: None,
450        }
451    }
452
453    /// Create a simple query error (backward compatibility)
454    pub fn query_error(message: impl Into<String>) -> Self {
455        Self::QueryError {
456            message: message.into(),
457            query_fragment: None,
458            position: None,
459            suggestion: None,
460        }
461    }
462
463    /// Create a simple reification error (backward compatibility)
464    pub fn reification_error(message: impl Into<String>) -> Self {
465        Self::ReificationError {
466            message: message.into(),
467            reification_strategy: None,
468            context: None,
469        }
470    }
471
472    /// Create a simple invalid term type error (backward compatibility)
473    pub fn invalid_term_type(message: impl Into<String>) -> Self {
474        Self::InvalidTermType {
475            message: message.into(),
476            term_type: None,
477            expected_types: None,
478            suggestion: None,
479        }
480    }
481
482    /// Create a nesting depth error
483    pub fn nesting_depth_exceeded(
484        max_depth: usize,
485        current_depth: usize,
486        context: Option<String>,
487    ) -> Self {
488        Self::NestingDepthExceeded {
489            max_depth,
490            current_depth,
491            context,
492        }
493    }
494
495    /// Create a configuration error
496    pub fn configuration_error(message: impl Into<String>) -> Self {
497        Self::ConfigurationError {
498            message: message.into(),
499            parameter: None,
500            valid_range: None,
501        }
502    }
503
504    /// Create an internal error (for unexpected conditions such as lock poisoning)
505    pub fn internal_error(message: impl Into<String>) -> Self {
506        Self::InternalError {
507            message: message.into(),
508            context: None,
509        }
510    }
511
512    /// Create an internal error for lock poisoning
513    pub fn lock_error(context: impl Into<String>) -> Self {
514        Self::InternalError {
515            message: "Lock poisoned".to_string(),
516            context: Some(context.into()),
517        }
518    }
519
520    /// Create an unsupported format error with available alternatives
521    pub fn unsupported_format(format: impl Into<String>, available: Vec<String>) -> Self {
522        Self::UnsupportedFormat {
523            format: format.into(),
524            available_formats: available,
525        }
526    }
527
528    /// Get available recovery suggestions for the error
529    pub fn recovery_suggestions(&self) -> Vec<String> {
530        let mut suggestions = Vec::new();
531
532        match self {
533            Self::NestingDepthExceeded { max_depth, .. } => {
534                suggestions.push(format!(
535                    "Consider increasing max_nesting_depth beyond {max_depth}"
536                ));
537                suggestions.push("Check for circular references in quoted triples".to_string());
538            }
539            Self::UnsupportedFormat {
540                available_formats, ..
541            } => {
542                suggestions.push(format!(
543                    "Supported formats: {}",
544                    available_formats.join(", ")
545                ));
546            }
547            Self::ConfigurationError {
548                valid_range: Some(range),
549                ..
550            } => {
551                suggestions.push(format!("Valid range: {range}"));
552            }
553            Self::ConfigurationError {
554                valid_range: None, ..
555            } => {}
556            _ => {}
557        }
558
559        suggestions
560    }
561
562    /// Create a resource error (backward compatibility)
563    pub fn resource_error(message: impl Into<String>) -> Self {
564        Self::ConfigurationError {
565            message: message.into(),
566            parameter: Some("resource".to_string()),
567            valid_range: None,
568        }
569    }
570
571    /// Create a processing error (backward compatibility)
572    pub fn processing_error(message: impl Into<String>) -> Self {
573        Self::ConfigurationError {
574            message: message.into(),
575            parameter: Some("processing".to_string()),
576            valid_range: None,
577        }
578    }
579}
580
581/// Configuration for RDF-star processing
582#[derive(Debug, Clone, Serialize, Deserialize)]
583pub struct StarConfig {
584    /// Maximum nesting depth for quoted triples (default: 10)
585    pub max_nesting_depth: usize,
586    /// Enable automatic reification fallback
587    pub enable_reification_fallback: bool,
588    /// Strict mode for parsing (reject invalid constructs)
589    pub strict_mode: bool,
590    /// Enable SPARQL-star extensions
591    pub enable_sparql_star: bool,
592    /// Buffer size for streaming operations
593    pub buffer_size: usize,
594    /// Maximum parse errors before aborting (None for unlimited)
595    pub max_parse_errors: Option<usize>,
596}
597
598impl Default for StarConfig {
599    fn default() -> Self {
600        Self {
601            max_nesting_depth: 10,
602            enable_reification_fallback: true,
603            strict_mode: false,
604            enable_sparql_star: true,
605            buffer_size: 8192,
606            max_parse_errors: Some(100),
607        }
608    }
609}
610
611/// Statistics for RDF-star processing
612#[derive(Debug, Clone, Default, Serialize, Deserialize)]
613pub struct StarStatistics {
614    /// Total number of quoted triples processed
615    pub quoted_triples_count: usize,
616    /// Maximum nesting depth encountered
617    pub max_nesting_encountered: usize,
618    /// Number of reified triples
619    pub reified_triples_count: usize,
620    /// Number of SPARQL-star queries executed
621    pub sparql_star_queries_count: usize,
622    /// Processing time statistics (in microseconds)
623    pub processing_time_us: u64,
624}
625
626/// Initialize the RDF-star system with configuration
627pub fn init_star_system(config: StarConfig) -> StarResult<()> {
628    let span = span!(Level::INFO, "init_star_system");
629    let _enter = span.enter();
630
631    info!("Initializing OxiRS RDF-star system");
632    debug!("Configuration: {:?}", config);
633
634    // Validate configuration
635    if config.max_nesting_depth == 0 {
636        return Err(StarError::ConfigurationError {
637            message: "Max nesting depth must be greater than 0".to_string(),
638            parameter: Some("max_nesting_depth".to_string()),
639            valid_range: Some("1..=1000".to_string()),
640        });
641    }
642
643    if config.buffer_size == 0 {
644        return Err(StarError::ConfigurationError {
645            message: "Buffer size must be greater than 0".to_string(),
646            parameter: Some("buffer_size".to_string()),
647            valid_range: Some("1..=1048576".to_string()),
648        });
649    }
650
651    // Additional validation for reasonable limits
652    if config.max_nesting_depth > 1000 {
653        return Err(StarError::ConfigurationError {
654            message: "Max nesting depth is too large and may cause performance issues".to_string(),
655            parameter: Some("max_nesting_depth".to_string()),
656            valid_range: Some("1..=1000".to_string()),
657        });
658    }
659
660    info!("RDF-star system initialized successfully");
661    Ok(())
662}
663
664/// Utility function to validate quoted triple nesting depth
665pub fn validate_nesting_depth(term: &StarTerm, max_depth: usize) -> StarResult<()> {
666    fn check_depth(term: &StarTerm, current_depth: usize, max_depth: usize) -> StarResult<usize> {
667        match term {
668            StarTerm::QuotedTriple(triple) => {
669                if current_depth >= max_depth {
670                    return Err(StarError::InvalidQuotedTriple {
671                        message: format!(
672                            "Nesting depth {current_depth} exceeds maximum {max_depth}"
673                        ),
674                        context: None,
675                        suggestion: None,
676                    });
677                }
678
679                let subj_depth = check_depth(&triple.subject, current_depth + 1, max_depth)?;
680                let pred_depth = check_depth(&triple.predicate, current_depth + 1, max_depth)?;
681                let obj_depth = check_depth(&triple.object, current_depth + 1, max_depth)?;
682
683                Ok(subj_depth.max(pred_depth).max(obj_depth))
684            }
685            _ => Ok(current_depth),
686        }
687    }
688
689    check_depth(term, 0, max_depth)?;
690    Ok(())
691}
692
693/// Version information
694pub const VERSION: &str = env!("CARGO_PKG_VERSION");
695
696/// Developer tooling and debugging utilities
697///
698/// This module provides comprehensive tools for validating, debugging, and analyzing
699/// RDF-star data. It's designed to help developers identify issues, optimize performance,
700/// and ensure data quality.
701///
702/// # Examples
703///
704/// ```rust,ignore
705/// use oxirs_star::dev_tools::{detect_format, validate_content, StarProfiler};
706/// use oxirs_star::StarConfig;
707///
708/// // Detect format from content
709/// let content = "<< :s :p :o >> :meta :value .";
710/// let format = detect_format(content);
711/// println!("Detected format: {:?}", format);
712///
713/// // Validate content with detailed diagnostics
714/// let config = StarConfig::default();
715/// let result = validate_content(content, &config);
716/// if !result.is_valid() {
717///     for error in &result.errors {
718///         println!("Error: {}", error);
719///     }
720/// }
721///
722/// // Profile performance
723/// let mut profiler = StarProfiler::new();
724/// let result = profiler.time_operation("parsing", || {
725///     // ... parsing operation
726///     42
727/// });
728/// println!("Operation took: {:?}", profiler.total_time());
729/// ```
730pub mod dev_tools {
731    use super::*;
732    use std::collections::HashMap;
733
734    /// RDF-star format detection result
735    #[derive(Debug, Clone, PartialEq)]
736    pub enum DetectedFormat {
737        TurtleStar,
738        NTriplesStar,
739        TrigStar,
740        NQuadsStar,
741        Unknown,
742    }
743
744    /// Detect RDF-star format from input content
745    pub fn detect_format(content: &str) -> DetectedFormat {
746        let content = content.trim();
747
748        // Check for TriG-star indicators
749        if content.contains("GRAPH") || content.contains("{") && content.contains("}") {
750            return DetectedFormat::TrigStar;
751        }
752
753        // Check for N-Quads-star (4 terms per line)
754        let lines: Vec<&str> = content
755            .lines()
756            .filter(|line| !line.trim().is_empty() && !line.trim().starts_with('#'))
757            .collect();
758        if !lines.is_empty() {
759            let first_line = lines[0].trim();
760            let terms: Vec<&str> = first_line.split_whitespace().collect();
761            if terms.len() >= 4 && first_line.ends_with('.') {
762                return DetectedFormat::NQuadsStar;
763            }
764        }
765
766        // Check for quoted triples (RDF-star indicator)
767        if content.contains("<<") && content.contains(">>") {
768            // If has quotes and prefixes, likely Turtle-star
769            if content.contains("@prefix") || content.contains("PREFIX") {
770                return DetectedFormat::TurtleStar;
771            }
772            // Otherwise, likely N-Triples-star
773            return DetectedFormat::NTriplesStar;
774        }
775
776        // Check for Turtle-star prefixes
777        if content.contains("@prefix") || content.contains("@base") {
778            return DetectedFormat::TurtleStar;
779        }
780
781        DetectedFormat::Unknown
782    }
783
784    /// Validate RDF-star content and return detailed diagnostic information
785    pub fn validate_content(content: &str, config: &StarConfig) -> ValidationResult {
786        let mut result = ValidationResult::new();
787
788        // Basic format detection
789        result.detected_format = detect_format(content);
790
791        // Count quoted triples
792        let quoted_count = content.matches("<<").count();
793        result.quoted_triple_count = quoted_count;
794
795        // Check for potential issues
796        if quoted_count > 10000 && !config.enable_reification_fallback {
797            result.warnings.push("Large number of quoted triples detected. Consider enabling reification fallback for better performance.".to_string());
798        }
799
800        // Check nesting depth by counting nested <<
801        let max_nesting = find_max_nesting_depth(content);
802        result.max_nesting_depth = max_nesting;
803
804        if max_nesting > config.max_nesting_depth {
805            result.errors.push(format!(
806                "Nesting depth {} exceeds configured maximum {}",
807                max_nesting, config.max_nesting_depth
808            ));
809        }
810
811        // Check for common syntax issues
812        check_syntax_issues(content, &mut result);
813
814        result
815    }
816
817    /// Validation result with detailed diagnostics
818    #[derive(Debug, Clone)]
819    pub struct ValidationResult {
820        pub detected_format: DetectedFormat,
821        pub quoted_triple_count: usize,
822        pub max_nesting_depth: usize,
823        pub errors: Vec<String>,
824        pub warnings: Vec<String>,
825        pub suggestions: Vec<String>,
826        pub line_errors: HashMap<u32, String>,
827    }
828
829    impl ValidationResult {
830        fn new() -> Self {
831            Self {
832                detected_format: DetectedFormat::Unknown,
833                quoted_triple_count: 0,
834                max_nesting_depth: 0,
835                errors: Vec::new(),
836                warnings: Vec::new(),
837                suggestions: Vec::new(),
838                line_errors: HashMap::new(),
839            }
840        }
841
842        /// Check if validation passed without errors
843        pub fn is_valid(&self) -> bool {
844            self.errors.is_empty()
845        }
846
847        /// Get a summary report of the validation
848        pub fn summary(&self) -> String {
849            let mut summary = String::new();
850            summary.push_str(&format!("Format: {:?}\n", self.detected_format));
851            summary.push_str(&format!("Quoted triples: {}\n", self.quoted_triple_count));
852            summary.push_str(&format!("Max nesting depth: {}\n", self.max_nesting_depth));
853
854            if !self.errors.is_empty() {
855                summary.push_str(&format!("Errors: {}\n", self.errors.len()));
856            }
857
858            if !self.warnings.is_empty() {
859                summary.push_str(&format!("Warnings: {}\n", self.warnings.len()));
860            }
861
862            summary
863        }
864    }
865
866    fn find_max_nesting_depth(content: &str) -> usize {
867        let mut max_depth: usize = 0;
868        let mut current_depth: i32 = 0;
869
870        for ch in content.chars() {
871            match ch {
872                '<' => {
873                    // Look ahead for another '<' to detect quoted triple start
874                    current_depth += 1;
875                }
876                '>' => {
877                    current_depth = current_depth.saturating_sub(1);
878                }
879                _ => {}
880            }
881            max_depth = max_depth.max((current_depth / 2).max(0) as usize); // Divide by 2 since we count both < and >
882        }
883
884        max_depth
885    }
886
887    fn check_syntax_issues(content: &str, result: &mut ValidationResult) {
888        let lines: Vec<&str> = content.lines().collect();
889
890        for (line_num, line) in lines.iter().enumerate() {
891            let line_num = line_num as u32 + 1;
892            let trimmed = line.trim();
893
894            // Skip comments and empty lines
895            if trimmed.is_empty() || trimmed.starts_with('#') {
896                continue;
897            }
898
899            // Check for unmatched quoted triple brackets
900            let open_count = trimmed.matches("<<").count();
901            let close_count = trimmed.matches(">>").count();
902
903            if open_count != close_count {
904                result.line_errors.insert(
905                    line_num,
906                    format!(
907                        "Unmatched quoted triple brackets: {open_count} << vs {close_count} >>"
908                    ),
909                );
910            }
911
912            // Check for missing periods in N-Triples/N-Quads style
913            if (result.detected_format == DetectedFormat::NTriplesStar
914                || result.detected_format == DetectedFormat::NQuadsStar)
915                && !trimmed.ends_with('.')
916                && !trimmed.starts_with('@')
917                && !trimmed.starts_with("PREFIX")
918            {
919                result.warnings.push(format!(
920                    "Line {line_num}: Missing period at end of statement"
921                ));
922            }
923        }
924    }
925
926    /// Performance profiler for RDF-star operations
927    pub struct StarProfiler {
928        start_time: std::time::Instant,
929        operation_times: HashMap<String, u64>,
930    }
931
932    impl StarProfiler {
933        pub fn new() -> Self {
934            Self {
935                start_time: std::time::Instant::now(),
936                operation_times: HashMap::new(),
937            }
938        }
939
940        pub fn time_operation<F, R>(&mut self, name: &str, operation: F) -> R
941        where
942            F: FnOnce() -> R,
943        {
944            let start = std::time::Instant::now();
945            let result = operation();
946            let duration = start.elapsed().as_micros() as u64;
947            self.operation_times.insert(name.to_string(), duration);
948            result
949        }
950
951        pub fn get_stats(&self) -> HashMap<String, u64> {
952            self.operation_times.clone()
953        }
954
955        pub fn total_time(&self) -> u64 {
956            self.start_time.elapsed().as_micros() as u64
957        }
958    }
959
960    impl Default for StarProfiler {
961        fn default() -> Self {
962            Self::new()
963        }
964    }
965
966    /// Generate a diagnostic report for RDF-star content
967    pub fn generate_diagnostic_report(content: &str, config: &StarConfig) -> String {
968        let validation = validate_content(content, config);
969        let mut report = String::new();
970
971        report.push_str("=== RDF-star Diagnostic Report ===\n\n");
972        report.push_str(&validation.summary());
973
974        if !validation.errors.is_empty() {
975            report.push_str("\nErrors:\n");
976            for (i, error) in validation.errors.iter().enumerate() {
977                report.push_str(&format!("  {}. {}\n", i + 1, error));
978            }
979        }
980
981        if !validation.warnings.is_empty() {
982            report.push_str("\nWarnings:\n");
983            for (i, warning) in validation.warnings.iter().enumerate() {
984                report.push_str(&format!("  {}. {}\n", i + 1, warning));
985            }
986        }
987
988        if !validation.suggestions.is_empty() {
989            report.push_str("\nSuggestions:\n");
990            for (i, suggestion) in validation.suggestions.iter().enumerate() {
991                report.push_str(&format!("  {}. {}\n", i + 1, suggestion));
992            }
993        }
994
995        if !validation.line_errors.is_empty() {
996            report.push_str("\nLine-specific issues:\n");
997            let mut sorted_lines: Vec<_> = validation.line_errors.iter().collect();
998            sorted_lines.sort_by_key(|(line, _)| *line);
999
1000            for (line, error) in sorted_lines {
1001                report.push_str(&format!("  Line {line}: {error}\n"));
1002            }
1003        }
1004
1005        report
1006    }
1007}
1008
1009#[cfg(test)]
1010mod tests {
1011    use super::*;
1012
1013    #[test]
1014    fn test_config_default() {
1015        let config = StarConfig::default();
1016        assert_eq!(config.max_nesting_depth, 10);
1017        assert!(config.enable_reification_fallback);
1018        assert!(!config.strict_mode);
1019        assert!(config.enable_sparql_star);
1020    }
1021
1022    #[test]
1023    fn test_nesting_depth_validation() {
1024        let simple_term = StarTerm::iri("http://example.org/test").unwrap();
1025        assert!(validate_nesting_depth(&simple_term, 5).is_ok());
1026
1027        // Test nested quoted triple
1028        let inner_triple = StarTriple::new(
1029            StarTerm::iri("http://example.org/s").unwrap(),
1030            StarTerm::iri("http://example.org/p").unwrap(),
1031            StarTerm::iri("http://example.org/o").unwrap(),
1032        );
1033        let nested_term = StarTerm::quoted_triple(inner_triple);
1034        assert!(validate_nesting_depth(&nested_term, 5).is_ok());
1035        assert!(validate_nesting_depth(&nested_term, 0).is_err());
1036    }
1037}