use banshee_config::{BansheeConfig, SeverityLevel};
use banshee_hir::{
AnalysisOptions, BuiltinLintPack, Diagnostic, MigrationColumns, NullSchemaProvider,
SchemaProvider, Severity, analyze_query_with_options, collect_migration_columns,
};
use super::io::InputFile;
pub struct Analyzed {
pub diagnostics: Vec<Diagnostic>,
}
pub fn migration_columns(inputs: &[InputFile]) -> MigrationColumns {
let roots: Vec<_> = inputs
.iter()
.map(|input| banshee_parser::parse(&input.text).syntax())
.collect();
collect_migration_columns(&roots)
}
pub fn analyze(
text: &str,
config: &BansheeConfig,
provider: Option<&dyn SchemaProvider>,
migration_columns: &MigrationColumns,
) -> Analyzed {
let templated = config
.templater
.style
.map(|style| banshee_templater::render(text, style));
let input: &str = templated.as_ref().map_or(text, |t| t.text());
let parse = banshee_parser::parse(input);
let mut options = build_options(config);
options.migration_columns = migration_columns.clone();
let null = NullSchemaProvider;
let active: &dyn SchemaProvider = provider.unwrap_or(&null);
let analysis = analyze_query_with_options(&parse, active, &options);
let schema_aware = provider.is_some();
let mut diagnostics: Vec<Diagnostic> = analysis
.diagnostics
.into_iter()
.filter(|d| match d.code {
None => schema_aware,
Some(code) if code.is_reference() => {
schema_aware && config.lint.is_rule_enabled(code.as_str())
}
Some(code) => config.lint.is_rule_enabled(code.as_str()),
})
.map(|mut d| {
if let Some(code) = d.code
&& let Some(level) = config.lint.severity_for(code.as_str())
{
d.severity = map_severity(level).unwrap_or(d.severity);
}
d
})
.collect();
for err in parse.errors() {
diagnostics.push(Diagnostic::error(err.message.clone()).with_range(err.range));
}
if let Some(t) = &templated {
for d in &mut diagnostics {
if let Some(range) = d.range {
d.range = Some(t.map_range(range));
}
for related in &mut d.related {
related.range = related.range.map(|r| t.map_range(r));
}
d.fixes
.retain(|fix| fix.edits.iter().all(|e| !t.touches_placeholder(e.range)));
for fix in &mut d.fixes {
for edit in &mut fix.edits {
edit.range = t.map_range(edit.range);
}
}
}
}
Analyzed { diagnostics }
}
fn build_options(config: &BansheeConfig) -> AnalysisOptions {
let mut packs = vec![
BuiltinLintPack::Core,
BuiltinLintPack::Jsonb,
BuiltinLintPack::Convention,
BuiltinLintPack::Migration,
];
if config.lint.is_rule_enabled("CP01") || config.lint.is_rule_enabled("CP02") {
packs.push(BuiltinLintPack::Capitalisation);
}
let mut options = AnalysisOptions::new().with_builtin_lint_packs(packs);
options.rule_options = config.lint.rule_options();
options
}
fn map_severity(level: SeverityLevel) -> Option<Severity> {
match level {
SeverityLevel::Error => Some(Severity::Error),
SeverityLevel::Warning => Some(Severity::Warning),
SeverityLevel::Info => Some(Severity::Info),
SeverityLevel::Off => None,
}
}