use std::fmt;
use rupg_common::SqlState;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Error {
pub code: SqlState,
pub message: String,
pub detail: Option<&'static str>,
pub hint: Option<&'static str>,
pub location: Option<usize>,
}
impl Error {
pub(crate) fn syntax(text: &[u8], message: &str, start: usize, end: usize) -> Error {
let message = if start >= text.len() {
format!("{message} at end of input")
} else {
let near = String::from_utf8_lossy(&text[start..end.clamp(start, text.len())]);
format!("{message} at or near \"{near}\"")
};
Error {
code: SqlState::SYNTAX_ERROR,
message,
detail: None,
hint: None,
location: Some(start),
}
}
pub(crate) fn at(code: SqlState, message: &str, location: usize) -> Error {
Error {
code,
message: message.to_owned(),
detail: None,
hint: None,
location: Some(location),
}
}
pub(crate) fn not_ported(rule: &str) -> Error {
Error {
code: SqlState::FEATURE_NOT_SUPPORTED,
message: format!("the action of the rule {rule} of gram.y is not ported"),
detail: None,
hint: None,
location: None,
}
}
pub(crate) fn internal(message: &str) -> Error {
Error {
code: SqlState::INTERNAL_ERROR,
message: message.to_owned(),
detail: None,
hint: None,
location: None,
}
}
pub(crate) fn encoding(bad: &[u8]) -> Error {
let first = bad.first().copied().unwrap_or(0);
let length = match first {
b if b & 0x80 == 0 => 1,
b if b & 0xe0 == 0xc0 => 2,
b if b & 0xf0 == 0xe0 => 3,
b if b & 0xf8 == 0xf0 => 4,
_ => 1,
};
let bytes: Vec<String> = bad.iter().take(length).map(|b| format!("0x{b:02x}")).collect();
Error {
code: SqlState::CHARACTER_NOT_IN_REPERTOIRE,
message: format!("invalid byte sequence for encoding \"UTF8\": {}", bytes.join(" ")),
detail: None,
hint: None,
location: None,
}
}
pub fn position(&self, text: &str) -> Option<usize> {
let location = self.location?.min(text.len());
let characters = text.as_bytes()[..location].iter().filter(|&&b| b & 0xc0 != 0x80).count();
Some(characters + 1)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for Error {}
impl From<Error> for rupg_common::Error {
fn from(error: Error) -> Self {
let mut common = rupg_common::Error::new(error.code, error.message);
if let Some(detail) = error.detail {
common = common.with_detail(detail);
}
if let Some(hint) = error.hint {
common = common.with_hint(hint);
}
common
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Notice {
pub severity: Severity,
pub code: SqlState,
pub message: String,
pub location: Option<usize>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Severity {
Notice,
Warning,
}