#[cfg(feature = "with-serde")]
use crate::encoding::Annotations;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "with-serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Warning {
pub message: String,
pub r#type: String,
pub code: u64,
#[cfg_attr(feature = "with-serde", serde(default))]
pub filename: Option<String>,
#[cfg_attr(feature = "with-serde", serde(default))]
pub hint: Option<String>,
#[cfg_attr(feature = "with-serde", serde(default))]
pub details: Option<String>,
#[cfg_attr(
feature = "with-serde",
serde(deserialize_with = "deserialize_usize_from_str", default)
)]
pub start: Option<usize>,
#[cfg_attr(
feature = "with-serde",
serde(deserialize_with = "deserialize_usize_from_str", default)
)]
pub end: Option<usize>,
#[cfg_attr(
feature = "with-serde",
serde(deserialize_with = "deserialize_usize_from_str", default)
)]
pub line: Option<usize>,
#[cfg_attr(
feature = "with-serde",
serde(deserialize_with = "deserialize_usize_from_str", default)
)]
pub col: Option<usize>,
}
impl std::fmt::Display for Warning {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let Warning {
filename,
line,
col,
r#type,
message,
..
} = self;
let filename = filename
.as_ref()
.map(|f| format!("{f}:"))
.unwrap_or_default();
let line = line.clone().unwrap_or(1);
let col = col.clone().unwrap_or(1);
write!(f, "{type} at {filename}{line}:{col} {message}")
}
}
#[cfg(feature = "with-serde")]
pub fn decode_warnings(annotations: &Annotations) -> Result<Vec<Warning>, gel_errors::Error> {
use gel_errors::{ErrorKind, ProtocolEncodingError};
const ANN_NAME: &str = "warnings";
if let Some(warnings) = annotations.get(ANN_NAME) {
serde_json::from_str::<Vec<_>>(warnings).map_err(|e| {
ProtocolEncodingError::with_source(e)
.context("Invalid JSON while decoding 'warnings' annotation")
})
} else {
Ok(vec![])
}
}
#[cfg(feature = "with-serde")]
fn deserialize_usize_from_str<'de, D: serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Option<usize>, D::Error> {
use serde::Deserialize;
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrInt {
String(String),
Number(usize),
}
Option::<StringOrInt>::deserialize(deserializer)?
.map(|x| match x {
StringOrInt::String(s) => s.parse::<usize>().map_err(serde::de::Error::custom),
StringOrInt::Number(i) => Ok(i),
})
.transpose()
}
#[test]
#[cfg(feature = "with-serde")]
fn deserialize_warning() {
let a: Warning =
serde_json::from_str(r#"{"message": "a", "type": "WarningException", "code": 1}"#).unwrap();
assert_eq!(
a,
Warning {
message: "a".to_string(),
r#type: "WarningException".to_string(),
code: 1,
filename: None,
hint: None,
details: None,
start: None,
end: None,
line: None,
col: None
}
);
let a: Warning = serde_json::from_str(
r#"{"message": "a", "type": "WarningException", "code": 1, "start": null}"#,
)
.unwrap();
assert_eq!(
a,
Warning {
message: "a".to_string(),
r#type: "WarningException".to_string(),
code: 1,
filename: None,
hint: None,
details: None,
start: None,
end: None,
line: None,
col: None
}
);
let a: Warning = serde_json::from_str(
r#"{"message": "a", "type": "WarningException", "code": 1, "start": 23}"#,
)
.unwrap();
assert_eq!(
a,
Warning {
message: "a".to_string(),
r#type: "WarningException".to_string(),
code: 1,
filename: None,
hint: None,
details: None,
start: Some(23),
end: None,
line: None,
col: None
}
);
let a: Warning = serde_json::from_str(
r#"{"message": "a", "type": "WarningException", "code": 1, "start": "23"}"#,
)
.unwrap();
assert_eq!(
a,
Warning {
message: "a".to_string(),
r#type: "WarningException".to_string(),
code: 1,
filename: None,
hint: None,
details: None,
start: Some(23),
end: None,
line: None,
col: None
}
);
}