ass_core/analysis/mod.rs
1//! Script analysis and linting for ASS subtitle scripts
2//!
3//! Provides comprehensive analysis capabilities including style resolution,
4//! linting for common issues, and performance optimization suggestions.
5//! Designed for editor integration and script validation.
6//!
7//! # Features
8//!
9//! - Style resolution: Compute effective styles from base + overrides
10//! - Linting rules: Detect common problems and spec violations
11//! - Performance analysis: Identify rendering bottlenecks
12//! - Unicode handling: Bidirectional text and linebreak analysis
13//! - Timing validation: Overlap detection and duration checks
14//!
15//! # Performance
16//!
17//! - Target: <2ms analysis for typical scripts
18//! - Memory: Lazy evaluation to avoid allocation spikes
19//! - Thread-safe: Immutable analysis results
20//!
21//! # Example
22//!
23//! ```rust
24//! use ass_core::{Script, analysis::ScriptAnalysis};
25//!
26//! let script_text = r#"
27//! [Script Info]
28//! Title: Test
29//!
30//! [V4+ Styles]
31//! Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
32//! Style: Default,Arial,20,&H00FFFFFF,&H000000FF,&H00000000,&H00000000,0,0,0,0,100,100,0,0,1,2,0,2,10,10,10,1
33//!
34//! [Events\]
35//! Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
36//! Dialogue: 0,0:00:00.00,0:00:05.00,Default,,0,0,0,,Hello World!
37//! "#;
38//!
39//! let script = Script::parse(script_text)?;
40//! let analysis = ScriptAnalysis::analyze(&script)?;
41//!
42//! // Check for issues
43//! for issue in analysis.lint_issues() {
44//! println!("Warning: {}", issue.message());
45//! }
46//!
47//! // Get resolved styles
48//! if let Some(style) = analysis.resolve_style("Default") {
49//! println!("Font: {}", style.font_name());
50//! }
51//! # Ok::<(), Box<dyn std::error::Error>>(())
52//! ```
53
54use crate::parser::Script;
55
56#[cfg(feature = "plugins")]
57use crate::plugin::ExtensionRegistry;
58use alloc::vec::Vec;
59
60bitflags::bitflags! {
61 /// Script analysis options
62 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
63 pub struct ScriptAnalysisOptions: u8 {
64 /// Enable Unicode linebreak analysis (libass 0.17.4+)
65 const UNICODE_LINEBREAKS = 1 << 0;
66 /// Enable performance warnings
67 const PERFORMANCE_HINTS = 1 << 1;
68 /// Enable strict spec compliance checking
69 const STRICT_COMPLIANCE = 1 << 2;
70 /// Enable bidirectional text analysis
71 const BIDI_ANALYSIS = 1 << 3;
72 }
73}
74
75mod construction;
76pub mod events;
77pub mod linting;
78mod queries;
79pub mod styles;
80
81#[cfg(test)]
82mod analysis_tests;
83
84pub use events::{
85 count_overlapping_dialogue_events, count_overlapping_events, find_overlapping_dialogue_events,
86 find_overlapping_events, DialogueInfo,
87};
88pub use linting::{lint_script, LintConfig, LintIssue, LintRule};
89pub use styles::{ResolvedStyle, StyleAnalyzer};
90
91/// Comprehensive analysis of an ASS script
92///
93/// Provides linting, style resolution, and performance analysis.
94/// Results are cached for efficient repeated access.
95#[derive(Debug, Clone)]
96pub struct ScriptAnalysis<'a> {
97 /// Reference to analyzed script
98 pub script: &'a Script<'a>,
99
100 /// Detected lint issues
101 lint_issues: Vec<LintIssue>,
102
103 /// Resolved styles cache
104 resolved_styles: Vec<ResolvedStyle<'a>>,
105
106 /// Dialogue analysis results
107 dialogue_info: Vec<DialogueInfo<'a>>,
108
109 /// Analysis configuration
110 config: AnalysisConfig,
111
112 /// Extension registry for custom tag handlers
113 #[cfg(feature = "plugins")]
114 registry: Option<&'a ExtensionRegistry>,
115}
116
117/// Configuration for script analysis
118#[derive(Debug, Clone)]
119pub struct AnalysisConfig {
120 /// Analysis options flags
121 pub options: ScriptAnalysisOptions,
122
123 /// Maximum allowed events for performance warnings
124 pub max_events_threshold: usize,
125}
126
127impl Default for AnalysisConfig {
128 fn default() -> Self {
129 Self {
130 options: ScriptAnalysisOptions::UNICODE_LINEBREAKS
131 | ScriptAnalysisOptions::PERFORMANCE_HINTS
132 | ScriptAnalysisOptions::BIDI_ANALYSIS,
133 max_events_threshold: 1000,
134 }
135 }
136}
137
138/// Performance analysis summary
139#[derive(Debug, Clone)]
140pub struct PerformanceSummary {
141 /// Total number of dialogue events
142 pub total_events: usize,
143
144 /// Number of overlapping events
145 pub overlapping_events: usize,
146
147 /// Number of complex animations
148 pub complex_animations: usize,
149
150 /// Number of oversized fonts
151 pub large_fonts: usize,
152
153 /// Overall performance score (0-100, higher is better)
154 pub performance_score: u8,
155}
156
157impl PerformanceSummary {
158 /// Check if script has performance concerns
159 #[must_use]
160 pub const fn has_performance_issues(&self) -> bool {
161 self.performance_score < 80
162 }
163
164 /// Get performance recommendation
165 #[must_use]
166 pub const fn recommendation(&self) -> Option<&'static str> {
167 if self.overlapping_events > 10 {
168 Some("Consider reducing overlapping events for better performance")
169 } else if self.complex_animations > 20 {
170 Some("Many complex animations may impact rendering performance")
171 } else if self.large_fonts > 5 {
172 Some("Large font sizes may cause memory issues")
173 } else if self.total_events > 1000 {
174 Some("Very large script - consider splitting into multiple files")
175 } else {
176 None
177 }
178 }
179}