Skip to main content

systemprompt_traits/
validation.rs

1//! Lightweight value-validation traits used by config and metadata types.
2
3use std::fmt::Debug;
4
5#[derive(Debug, Clone)]
6pub struct ValidationError {
7    pub field: String,
8    pub message: String,
9    pub context: Option<String>,
10}
11
12impl ValidationError {
13    #[must_use]
14    pub fn new(field: impl Into<String>, message: impl Into<String>) -> Self {
15        Self {
16            field: field.into(),
17            message: message.into(),
18            context: None,
19        }
20    }
21
22    #[must_use]
23    pub fn with_context(mut self, context: impl Into<String>) -> Self {
24        self.context = Some(context.into());
25        self
26    }
27}
28
29impl std::fmt::Display for ValidationError {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        if let Some(ref ctx) = self.context {
32            write!(
33                f,
34                "VALIDATION ERROR [{}]: {} (context: {})",
35                self.field, self.message, ctx
36            )
37        } else {
38            write!(f, "VALIDATION ERROR [{}]: {}", self.field, self.message)
39        }
40    }
41}
42
43impl std::error::Error for ValidationError {}
44
45pub type ValidationResult<T> = Result<T, ValidationError>;
46
47pub trait Validate: Debug {
48    fn validate(&self) -> ValidationResult<()>;
49}
50
51pub trait MetadataValidation: Validate {
52    fn required_string_fields(&self) -> Vec<(&'static str, &str)>;
53
54    fn validate_required_fields(&self) -> ValidationResult<()> {
55        for (field_name, field_value) in self.required_string_fields() {
56            if field_value.is_empty() {
57                return Err(ValidationError::new(
58                    field_name,
59                    format!("{field_name} cannot be empty"),
60                ));
61            }
62        }
63        Ok(())
64    }
65}