use camino::Utf8PathBuf;
use dibs_query_schema::Span;
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
pub struct QSource {
pub source: String,
pub source_path: Utf8PathBuf,
}
#[derive(Clone)]
pub struct QError {
pub source: Arc<QSource>,
pub span: Span,
pub kind: QErrorKind,
}
#[derive(Debug, Clone)]
pub enum QErrorKind {
ColumnNotFound {
table: String,
column: String,
available: Vec<String>,
},
TableNotFound {
table: String,
available: Vec<String>,
},
SchemaMismatch {
table: String,
column: String,
reason: String,
},
PlanMissing {
reason: String,
},
Parse {
message: String,
},
InvalidFilterArgCount {
filter: String,
expected: usize,
actual: usize,
},
InvalidFilterArgType {
filter: String,
reason: String,
},
}
impl fmt::Display for QErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
QErrorKind::ColumnNotFound {
table,
column,
available,
} => {
write!(f, "column '{}' not found in table '{}'", column, table)?;
if available.is_empty() {
write!(f, " (table has no columns)")
} else {
write!(f, " (table has: {})", available.join(", "))
}
}
QErrorKind::TableNotFound { table, available } => {
if available.is_empty() {
write!(
f,
"table '{table}' not found: the schema is empty (0 tables). \
The crate defining your #[facet(dibs::table)] types was probably \
not linked, so dibs collected zero tables — call its \
`ensure_linked()` in build.rs before generating queries (a \
`TypeId::of`/`type_name` reference does NOT force linkage)."
)
} else {
write!(
f,
"table '{table}' not found: the schema has {} table(s): {}",
available.len(),
available.join(", ")
)
}
}
QErrorKind::SchemaMismatch {
table,
column,
reason,
} => {
write!(f, "schema mismatch for '{}.{}': {}", table, column, reason)
}
QErrorKind::PlanMissing { reason } => {
write!(f, "query plan missing: {}", reason)
}
QErrorKind::Parse { message } => {
write!(f, "{}", message)
}
QErrorKind::InvalidFilterArgCount {
filter,
expected,
actual,
} => {
write!(
f,
"invalid arguments for filter '{}': expected {} arguments, got {}",
filter, expected, actual
)
}
QErrorKind::InvalidFilterArgType { filter, reason } => {
write!(f, "invalid argument for filter '{}': {}", filter, reason)
}
}
}
}
impl fmt::Debug for QError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl fmt::Display for QError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use ariadne::{Config, Label, Report, ReportKind, Source};
let message = self.kind.to_string();
let start = self.span.offset as usize;
let end = start + self.span.len as usize;
let mut output = Vec::new();
let report = Report::build(ReportKind::Error, (&self.source.source_path, start..end))
.with_message(&message)
.with_config(Config::default().with_color(false))
.with_label(Label::new((&self.source.source_path, start..end)).with_message(&message))
.finish();
report
.write(
(&self.source.source_path, Source::from(&self.source.source)),
&mut output,
)
.ok();
write!(f, "{}", String::from_utf8_lossy(&output))
}
}
impl std::error::Error for QError {}