1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
//! Core validation logic bridging ass-core analysis and basic checks.
//!
//! Implements the analysis-backed and fallback validation paths for
//! `LazyValidator`, plus structural sanity checks applied to all documents.
use super::{LazyValidator, ValidationIssue, ValidationSeverity};
use crate::core::{EditorDocument, Result};
#[cfg(feature = "analysis")]
use ass_core::analysis::linting::IssueSeverity;
#[cfg(feature = "analysis")]
use ass_core::analysis::ScriptAnalysis;
#[cfg(not(feature = "std"))]
use alloc::{string::ToString, vec::Vec};
impl LazyValidator {
/// Validate using ass-core's ScriptAnalysis
#[cfg(feature = "analysis")]
pub(super) fn validate_with_core(
&self,
content: &str,
document: &EditorDocument,
) -> Result<Vec<ValidationIssue>> {
let mut issues = Vec::new();
// Parse and analyze with ass-core
document.parse_script_with(|script| {
// Create ScriptAnalysis with our configuration
match ScriptAnalysis::analyze_with_config(script, self.analysis_config.clone()) {
Ok(analysis) => {
// Convert core lint issues to our format
for lint_issue in analysis.lint_issues() {
let severity = match lint_issue.severity() {
IssueSeverity::Hint => ValidationSeverity::Info,
IssueSeverity::Info => ValidationSeverity::Info,
IssueSeverity::Warning => ValidationSeverity::Warning,
IssueSeverity::Error => ValidationSeverity::Error,
IssueSeverity::Critical => ValidationSeverity::Critical,
};
let (line, column) = if let Some(location) = lint_issue.location() {
(Some(location.line), Some(location.column))
} else {
(None, None)
};
let issue = ValidationIssue {
severity,
line,
column,
message: lint_issue.message().to_string(),
rule: lint_issue.rule_id().to_string(),
suggestion: lint_issue.suggested_fix().map(|s| s.to_string()),
};
issues.push(issue);
}
}
Err(_) => {
// If analysis fails, add a basic error
issues.push(ValidationIssue::new(
ValidationSeverity::Error,
"Failed to analyze script".to_string(),
"analyzer".to_string(),
));
}
}
})?;
// Add basic structural checks even with analysis feature
self.add_basic_checks(content, &mut issues);
// Apply severity threshold filter
issues.retain(|issue| issue.severity >= self.config.severity_threshold);
// Apply max issues limit
if self.config.max_issues > 0 && issues.len() > self.config.max_issues {
issues.truncate(self.config.max_issues);
}
Ok(issues)
}
/// Fallback validation without ass-core analysis
#[cfg(not(feature = "analysis"))]
pub(super) fn validate_with_core(
&self,
content: &str,
_document: &EditorDocument,
) -> Result<Vec<ValidationIssue>> {
let mut issues = Vec::new();
// Basic validation without core analysis
// Note: We can't do full parsing validation without the analysis feature,
// so we do basic structural checks only
self.add_basic_checks(content, &mut issues);
// Apply severity threshold filter
issues.retain(|issue| issue.severity >= self.config.severity_threshold);
// Apply max issues limit
if self.config.max_issues > 0 && issues.len() > self.config.max_issues {
issues.truncate(self.config.max_issues);
}
Ok(issues)
}
/// Add basic structural checks that work regardless of analysis feature
fn add_basic_checks(&self, content: &str, issues: &mut Vec<ValidationIssue>) {
// Basic checks
if content.is_empty() {
issues.push(ValidationIssue::new(
ValidationSeverity::Warning,
"Document is empty".to_string(),
"basic".to_string(),
));
}
if !content.contains("[Script Info]") {
issues.push(ValidationIssue::new(
ValidationSeverity::Warning,
"Missing [Script Info] section".to_string(),
"structure".to_string(),
));
}
if !content.contains("[Events]") {
issues.push(ValidationIssue::new(
ValidationSeverity::Warning,
"Missing [Events] section".to_string(),
"structure".to_string(),
));
}
}
}