use crate::{
ext::FmtExt,
postgres::backend::{ErrorResponse, NoticeResponse},
};
pub enum MessageFields {
SeverityLocalized,
Severity,
Code,
Message,
Detail,
Hint,
Position,
InternalPosition,
InternalQuery,
Where,
SchemaName,
TableName,
ColumnName,
DataTypeName,
ConstraintName,
FileName,
Line,
Routine,
}
impl MessageFields {
pub fn debug(body: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut map = f.debug_map();
let mut iter = body.iter().copied().enumerate();
loop {
let Some((i,key)) = iter.next() else {
break;
};
let Some(key) = MessageFields::from_byte(key) else {
break;
};
map.key(&key.as_str());
match iter.find(|(_,e)|matches!(e,b'\0')) {
Some((end,_)) => map.value(&body[i + 1..end].lossy()),
None => map.value(&"<??>"),
};
}
map.finish()
}
pub fn display(body: &[u8], f: &mut std::fmt::Formatter) -> std::fmt::Result {
let mut sevr = None;
let mut code = None;
let mut message = None;
let mut routine = None;
let mut detail = None;
let mut hint = None;
let mut iter = body.iter().copied().enumerate();
loop {
let Some((i,key)) = iter.next() else {
break;
};
let Some((end,_)) = iter.find(|(_,e)|matches!(e,b'\0')) else {
break;
};
match MessageFields::from_byte(key) {
Some(MessageFields::SeverityLocalized) => sevr = Some((i,end)),
Some(MessageFields::Severity) if sevr.is_none() => sevr = Some((i,end)),
Some(MessageFields::Code) => code = Some((i,end)),
Some(MessageFields::Message) => message = Some((i,end)),
Some(MessageFields::Routine) => routine = Some((i,end)),
Some(MessageFields::Detail) => detail = Some((i,end)),
Some(MessageFields::Hint) => hint = Some((i,end)),
_ => {}
}
}
macro_rules! foo {
(@ $f:ident,$s:literal;$($tt:tt)*) => {
'foo: {
let Some((i,end)) = $f else {
$($tt)*
break 'foo
};
write!(f, $s, body[i + 1..end].lossy())?;
}
};
($f:ident,$s:literal,?) => {
foo!(@ $f,$s;();)
};
($f:ident,$s:literal) => {
foo!(@ $f,$s;write!(f, $s, "??")?;)
};
}
foo!(sevr, "[{}]");
foo!(message, " {}");
write!(f, " (")?;
foo!(routine, "{}, ");
foo!(code, "{}");
write!(f, ")")?;
foo!(detail, ",\n\n{}", ?);
foo!(hint, ",\n\nHINT: {}", ?);
Ok(())
}
}
macro_rules! foo {
($($b:literal => $s:ident,)*) => {
pub fn from_byte(byte: u8) -> Option<MessageFields> {
Some(match byte {
$($b => Self::$s,)*
_ => return None,
})
}
pub fn as_str(&self) -> &'static str {
match self {
$(Self::$s => stringify!($s),)*
}
}
};
}
impl MessageFields {
foo! {
b'S' => SeverityLocalized,
b'V' => Severity,
b'C' => Code,
b'M' => Message,
b'D' => Detail,
b'H' => Hint,
b'P' => Position,
b'p' => InternalPosition,
b'q' => InternalQuery,
b'W' => Where,
b's' => SchemaName,
b't' => TableName,
b'c' => ColumnName,
b'd' => DataTypeName,
b'n' => ConstraintName,
b'F' => FileName,
b'L' => Line,
b'R' => Routine,
}
}
impl std::error::Error for ErrorResponse { }
impl std::fmt::Debug for ErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Error ")?;
MessageFields::debug(&self.body, f)
}
}
impl std::fmt::Display for ErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
MessageFields::display(&self.body, f)
}
}
impl std::error::Error for NoticeResponse { }
impl std::fmt::Debug for NoticeResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Notice ")?;
MessageFields::debug(&self.body, f)
}
}
impl std::fmt::Display for NoticeResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
MessageFields::display(&self.body, f)
}
}