use crate::components::validation_patterns::{CommonValidationError, StringLengthValidator};
use crate::error::AppError;
use crate::validation::Validator;
use serde_json::Value;
#[derive(Debug, Clone)]
pub enum MessageValidationError {
Common(CommonValidationError),
InvalidJson { reason: String },
InvalidCharacters { characters: String },
}
impl MessageValidationError {
pub fn user_message(&self) -> String {
match self {
MessageValidationError::Common(common_error) => common_error.user_message(),
MessageValidationError::InvalidJson { reason } => {
format!(
"Invalid JSON format!\n\nError: {reason}\n\n\n Please check your JSON syntax and try again."
)
}
MessageValidationError::InvalidCharacters { characters } => {
format!(
"Message contains invalid characters!\n\nInvalid characters: {characters}\n\n\n Please remove these characters and try again."
)
}
}
}
pub fn invalid_json(reason: impl Into<String>) -> Self {
Self::InvalidJson {
reason: reason.into(),
}
}
pub fn invalid_characters(characters: impl Into<String>) -> Self {
Self::InvalidCharacters {
characters: characters.into(),
}
}
}
impl From<CommonValidationError> for MessageValidationError {
fn from(error: CommonValidationError) -> Self {
MessageValidationError::Common(error)
}
}
impl From<MessageValidationError> for AppError {
fn from(error: MessageValidationError) -> Self {
AppError::Config(error.user_message())
}
}
pub struct MessageContentValidator {
validator: StringLengthValidator,
}
impl Default for MessageContentValidator {
fn default() -> Self {
Self::new()
}
}
impl MessageContentValidator {
pub fn new() -> Self {
Self {
validator: StringLengthValidator::new("message content").with_min_length(1), }
}
}
impl Validator<str> for MessageContentValidator {
type Error = MessageValidationError;
fn validate(&self, input: &str) -> Result<(), Self::Error> {
let trimmed = input.trim();
self.validator.validate(trimmed).map_err(Into::into)
}
}
pub struct MessageSizeValidator {
validator: StringLengthValidator,
}
impl MessageSizeValidator {
pub fn new(max_size: usize) -> Self {
Self {
validator: StringLengthValidator::new("message content").with_max_length(max_size),
}
}
}
impl Validator<str> for MessageSizeValidator {
type Error = MessageValidationError;
fn validate(&self, input: &str) -> Result<(), Self::Error> {
self.validator.validate(input).map_err(Into::into)
}
}
pub struct JsonFormatValidator;
impl Validator<str> for JsonFormatValidator {
type Error = MessageValidationError;
fn validate(&self, input: &str) -> Result<(), Self::Error> {
match serde_json::from_str::<Value>(input) {
Ok(_) => Ok(()),
Err(e) => Err(MessageValidationError::invalid_json(e.to_string())),
}
}
}
pub struct MessageEncodingValidator;
impl Validator<str> for MessageEncodingValidator {
type Error = MessageValidationError;
fn validate(&self, input: &str) -> Result<(), Self::Error> {
for (i, ch) in input.char_indices() {
if ch.is_control() && ch != '\n' && ch != '\r' && ch != '\t' {
return Err(MessageValidationError::invalid_characters(format!(
"Control character at position {i}: {ch:?}"
)));
}
}
Ok(())
}
}
pub struct CompleteMessageValidator {
content_validator: MessageContentValidator,
size_validator: MessageSizeValidator,
json_validator: JsonFormatValidator,
encoding_validator: MessageEncodingValidator,
}
impl CompleteMessageValidator {
pub fn new(max_size: usize) -> Self {
Self {
content_validator: MessageContentValidator::new(),
size_validator: MessageSizeValidator::new(max_size),
json_validator: JsonFormatValidator,
encoding_validator: MessageEncodingValidator,
}
}
pub fn azure_default() -> Self {
Self::new(256 * 1024) }
}
impl Validator<str> for CompleteMessageValidator {
type Error = MessageValidationError;
fn validate(&self, input: &str) -> Result<(), Self::Error> {
self.content_validator.validate(input)?;
self.size_validator.validate(input)?;
self.encoding_validator.validate(input)?;
self.json_validator.validate(input)?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_content_validator() {
let validator = MessageContentValidator::new();
assert!(validator.validate("Hello world").is_ok());
assert!(validator.validate(" Valid content ").is_ok());
assert!(validator.validate("").is_err());
assert!(validator.validate(" ").is_err());
assert!(validator.validate("\n\t").is_err());
}
#[test]
fn test_message_size_validator() {
let validator = MessageSizeValidator::new(10);
assert!(validator.validate("hello").is_ok());
assert!(validator.validate("1234567890").is_ok());
assert!(validator.validate("12345678901").is_err());
}
#[test]
fn test_json_format_validator() {
let validator = JsonFormatValidator;
assert!(validator.validate(r#"{"key": "value"}"#).is_ok());
assert!(validator.validate("[]").is_ok());
assert!(validator.validate("null").is_ok());
assert!(validator.validate("true").is_ok());
assert!(validator.validate("42").is_ok());
assert!(validator.validate("{key: value}").is_err()); assert!(validator.validate("{'key': 'value'}").is_err()); assert!(validator.validate("undefined").is_err()); }
#[test]
fn test_message_encoding_validator() {
let validator = MessageEncodingValidator;
assert!(validator.validate("Hello world").is_ok());
assert!(validator.validate("Unicode: 🚀 ñ ü").is_ok());
assert!(validator.validate("Newlines\nand\ttabs\rare\rok").is_ok());
assert!(validator.validate("Bell\x07character").is_err());
assert!(validator.validate("Null\x00character").is_err());
}
#[test]
fn test_complete_message_validator() {
let validator = CompleteMessageValidator::new(100);
assert!(validator.validate(r#"{"message": "Hello world"}"#).is_ok());
assert!(validator.validate("").is_err());
let large_json = format!(r#"{{"data": "{}"}}"#, "x".repeat(200));
assert!(validator.validate(&large_json).is_err());
assert!(validator.validate("not json").is_err());
}
}