use crate::diagnostics::RototoRuleId;
use crate::diagnostics::{LintDiagnostic, LintStage, SemanticEntity};
use super::super::source::{DocumentKind, SourceDocument};
pub(super) fn read_error_diagnostic(document: &SourceDocument, read_error: &str) -> LintDiagnostic {
LintDiagnostic::rototo(
parse_failed_rule(&document.kind),
LintStage::Parse,
entity_for_document(document),
document.document_location(),
format!("failed to read {}: {read_error}", document.path),
)
}
pub(super) fn toml_span_parse_diagnostic(
document: &SourceDocument,
err: &::toml_span::Error,
) -> LintDiagnostic {
let start = floor_char_boundary(&document.text, err.span.start.min(document.text.len()));
let end = parse_error_end(document, start, err.span.end);
let location = if start == end {
document.document_location()
} else {
document.span_location(start..end)
};
LintDiagnostic::rototo(
parse_failed_rule(&document.kind),
LintStage::Parse,
entity_for_document(document),
location,
format!("failed to parse {}: {err}", document.path),
)
}
fn parse_error_end(document: &SourceDocument, start: usize, raw_end: usize) -> usize {
let bounded_end =
ceil_char_boundary(&document.text, raw_end.min(document.text.len())).max(start);
let Some(slice) = document.text.get(start..bounded_end) else {
return start;
};
let Some(relative_newline) = slice.find('\n') else {
return bounded_end;
};
start + relative_newline + 1
}
pub(super) fn json_parse_diagnostic(
document: &SourceDocument,
err: &::serde_json::Error,
) -> LintDiagnostic {
let line = err.line().saturating_sub(1);
let column = err.column().saturating_sub(1);
let start = document.line_index.offset_for_line_character(line, column);
let end = start.saturating_add(1).min(document.text.len());
LintDiagnostic::rototo(
parse_failed_rule(&document.kind),
LintStage::Parse,
entity_for_document(document),
document.span_location(start..end),
format!("failed to parse {}: {err}", document.path),
)
}
fn floor_char_boundary(text: &str, mut offset: usize) -> usize {
while offset > 0 && !text.is_char_boundary(offset) {
offset -= 1;
}
offset
}
fn ceil_char_boundary(text: &str, mut offset: usize) -> usize {
while offset < text.len() && !text.is_char_boundary(offset) {
offset += 1;
}
offset
}
fn parse_failed_rule(kind: &DocumentKind) -> RototoRuleId {
match kind {
DocumentKind::Manifest => RototoRuleId::PackageManifestParseFailed,
DocumentKind::Variable { .. } => RototoRuleId::VariableParseFailed,
DocumentKind::List { .. } => RototoRuleId::ListParseFailed,
DocumentKind::Layer { .. } => RototoRuleId::LayerParseFailed,
DocumentKind::Governance => RototoRuleId::GovernanceParseFailed,
DocumentKind::Catalog { .. } => RototoRuleId::CatalogParseFailed,
DocumentKind::CatalogEntry { .. } => RototoRuleId::CatalogEntryParseFailed,
DocumentKind::EvaluationContext { .. } => RototoRuleId::EvaluationContextParseFailed,
DocumentKind::EvaluationContextSample { .. } => {
RototoRuleId::EvaluationContextSampleParseFailed
}
DocumentKind::CustomLint => RototoRuleId::CustomLintFailed,
}
}
fn entity_for_document(document: &SourceDocument) -> SemanticEntity {
match &document.kind {
DocumentKind::Manifest => SemanticEntity::Manifest,
DocumentKind::Variable { id } => SemanticEntity::Variable { id: id.clone() },
DocumentKind::List { id } => SemanticEntity::List { id: id.clone() },
DocumentKind::Layer { id } => SemanticEntity::Layer { id: id.clone() },
DocumentKind::Governance => SemanticEntity::Governance,
DocumentKind::Catalog { id } => SemanticEntity::Catalog { id: id.clone() },
DocumentKind::CatalogEntry {
catalog_id,
entry_id,
} => SemanticEntity::CatalogEntry {
catalog: catalog_id.clone(),
key: entry_id.clone(),
},
DocumentKind::EvaluationContext { id } => {
SemanticEntity::EvaluationContext { id: id.clone() }
}
DocumentKind::EvaluationContextSample {
evaluation_context_id,
sample_id,
} => SemanticEntity::EvaluationContextSample {
evaluation_context: evaluation_context_id.clone(),
key: sample_id.clone(),
},
DocumentKind::CustomLint => SemanticEntity::CustomLint {
path: document.path.clone(),
},
}
}