use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ValidationErrorKind {
Missing,
NotAllowed,
OutOfRange,
Unsupported,
Symbol,
NotWhole,
Structure,
Tick,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ValidationError {
kind: ValidationErrorKind,
field: Option<String>,
message: String,
}
impl ValidationError {
pub(crate) fn new(
kind: ValidationErrorKind,
field: Option<&str>,
message: impl Into<String>,
) -> Self {
Self {
kind,
field: field.map(str::to_owned),
message: message.into(),
}
}
pub fn kind(&self) -> ValidationErrorKind {
self.kind
}
pub fn field(&self) -> Option<&str> {
self.field.as_deref()
}
pub fn message(&self) -> &str {
&self.message
}
pub(crate) fn within(mut self, prefix: &str) -> Self {
self.field = Some(match self.field {
Some(field) => format!("{prefix}.{field}"),
None => prefix.to_owned(),
});
self
}
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for ValidationError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nested_fields_are_placed_under_their_parents() {
let error = ValidationError::new(ValidationErrorKind::NotWhole, Some("quantity"), "whole")
.within("orderLegCollection[1]")
.within("childOrderStrategies[0]");
assert_eq!(
error.field(),
Some("childOrderStrategies[0].orderLegCollection[1].quantity")
);
assert_eq!(error.to_string(), "whole");
assert_eq!(error.kind(), ValidationErrorKind::NotWhole);
}
}