use crate::protocol::util;
use byteorder::{LittleEndian, ReadBytesExt};
use std::error::Error;
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Severity {
Warning,
Error,
Fatal,
__UNKNOWN__(i8),
}
impl Severity {
pub(crate) fn from_i8(i: i8) -> Self {
match i {
0 => Self::Warning,
1 => Self::Error,
2 => Self::Fatal,
i => Self::__UNKNOWN__(i),
}
}
pub fn to_i8(&self) -> i8 {
match *self {
Self::Warning => 0,
Self::Error => 1,
Self::Fatal => 2,
Self::__UNKNOWN__(i) => i,
}
}
}
impl std::fmt::Display for Severity {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
Self::Warning => write!(f, "warning")?,
Self::Error => write!(f, "error")?,
Self::Fatal => write!(f, "fatal error")?,
Self::__UNKNOWN__(i) => write!(f, "message of unknown severity ({i})")?,
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct ServerError {
code: i32,
position: i32,
severity: Severity,
sqlstate: Vec<u8>,
text: String,
}
const BASE_SIZE: i32 = 4 + 4 + 4 + 1 + 5;
impl ServerError {
pub fn code(&self) -> i32 {
self.code
}
pub fn position(&self) -> i32 {
self.position
}
pub fn severity(&self) -> &Severity {
&self.severity
}
pub fn sqlstate(&self) -> &[u8] {
&self.sqlstate
}
pub fn text(&self) -> &str {
&self.text
}
pub(crate) fn new(
code: i32,
position: i32,
severity: Severity,
sqlstate: Vec<u8>,
text: String,
) -> Self {
Self {
code,
position,
severity,
sqlstate,
text,
}
}
#[allow(clippy::cast_sign_loss)]
pub(crate) fn parse(
no_of_args: usize,
rdr: &mut dyn std::io::Read,
) -> std::io::Result<Vec<Self>> {
let mut server_errors = Vec::<Self>::new();
for _i in 0..no_of_args {
let code = rdr.read_i32::<LittleEndian>()?; let position = rdr.read_i32::<LittleEndian>()?; let text_length = rdr.read_i32::<LittleEndian>()?; let severity = Severity::from_i8(rdr.read_i8()?); let sqlstate = util::parse_bytes(5_usize, rdr)?; let bytes = util::parse_bytes(text_length as usize, rdr)?; let text = util::string_from_cesu8(bytes).map_err(util::io_error)?;
let pad = 8 - (BASE_SIZE + text_length) % 8;
util::skip_bytes(pad as usize, rdr)?;
let server_error = Self::new(code, position, severity, sqlstate, text);
debug!("ServerError::parse(): found server error {}", server_error);
server_errors.push(server_error);
}
Ok(server_errors)
}
}
impl Error for ServerError {}
impl std::fmt::Display for ServerError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
fmt,
r#"{} [code: {}, sql state: {}] at position: {}: "{}""#,
self.severity,
self.code,
String::from_utf8_lossy(&self.sqlstate),
self.position(),
self.text
)
}
}
impl std::fmt::Debug for ServerError {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(fmt, "{self}")
}
}