use alloc::string::String;
use core::fmt;
pub const MESSAGE_ID_MAX_LEN: usize = 36;
pub type Result<T> = core::result::Result<T, ConstraintViolation>;
#[derive(Debug, Clone, PartialEq)]
pub struct ConstraintViolation {
pub path: String,
pub kind: ConstraintKind,
}
#[derive(Debug, Clone, PartialEq)]
pub enum ConstraintKind {
MaxLength { max: usize, actual: usize },
MinItems { min: usize, actual: usize },
MaxItems { max: usize, actual: usize },
Minimum { min: f64, actual: f64 },
Maximum { max: f64, actual: f64 },
MessageIdTooLong { actual: usize },
UnknownAction,
Custom(String),
}
impl ConstraintViolation {
#[must_use]
pub fn max_length(path: impl Into<String>, max: usize, actual: usize) -> Self {
Self {
path: path.into(),
kind: ConstraintKind::MaxLength { max, actual },
}
}
#[must_use]
pub fn min_items(path: impl Into<String>, min: usize, actual: usize) -> Self {
Self {
path: path.into(),
kind: ConstraintKind::MinItems { min, actual },
}
}
#[must_use]
pub fn max_items(path: impl Into<String>, max: usize, actual: usize) -> Self {
Self {
path: path.into(),
kind: ConstraintKind::MaxItems { max, actual },
}
}
#[must_use]
pub fn minimum(path: impl Into<String>, min: f64, actual: f64) -> Self {
Self {
path: path.into(),
kind: ConstraintKind::Minimum { min, actual },
}
}
#[must_use]
pub fn maximum(path: impl Into<String>, max: f64, actual: f64) -> Self {
Self {
path: path.into(),
kind: ConstraintKind::Maximum { max, actual },
}
}
#[must_use]
pub fn message_id_too_long(actual: usize) -> Self {
Self {
path: "messageId".into(),
kind: ConstraintKind::MessageIdTooLong { actual },
}
}
#[must_use]
pub fn unknown_action(action: &str) -> Self {
Self {
path: action.into(),
kind: ConstraintKind::UnknownAction,
}
}
}
impl fmt::Display for ConstraintViolation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.kind {
ConstraintKind::MaxLength { max, actual } => {
write!(
f,
"maxLength at {}: max {max}, actual {actual}",
self.path
)
}
ConstraintKind::MinItems { min, actual } => {
write!(
f,
"minItems at {}: min {min}, actual {actual}",
self.path
)
}
ConstraintKind::MaxItems { max, actual } => {
write!(
f,
"maxItems at {}: max {max}, actual {actual}",
self.path
)
}
ConstraintKind::Minimum { min, actual } => {
write!(f, "minimum at {}: min {min}, actual {actual}", self.path)
}
ConstraintKind::Maximum { max, actual } => {
write!(f, "maximum at {}: max {max}, actual {actual}", self.path)
}
ConstraintKind::MessageIdTooLong { actual } => {
write!(
f,
"messageId length {actual} exceeds max {MESSAGE_ID_MAX_LEN}"
)
}
ConstraintKind::UnknownAction => {
write!(f, "unknown action for schema validation: {}", self.path)
}
ConstraintKind::Custom(msg) => write!(f, "{msg}"),
}
}
}
pub fn check_message_id_len(id: &str) -> Result<()> {
let len = id.chars().count();
if len > MESSAGE_ID_MAX_LEN {
return Err(ConstraintViolation::message_id_too_long(len));
}
Ok(())
}
pub trait Validate {
fn validate(&self) -> Result<()>;
}
impl Validate for () {
fn validate(&self) -> Result<()> {
Ok(())
}
}
impl Validate for String {
fn validate(&self) -> Result<()> {
Ok(())
}
}