use chrono::{DateTime, Utc};
use regex::Regex;
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use std::sync::LazyLock;
use super::datetime_format;
static CATEGORY_REGEX: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[a-zA-Z0-9_/-]+$").unwrap());
pub fn validate_category(category: &str) -> Result<(), String> {
if category.is_empty() {
return Err("Category cannot be empty".to_string());
}
if category.len() > 64 {
return Err("Category cannot exceed 64 characters".to_string());
}
if !CATEGORY_REGEX.is_match(category) {
return Err("Category can only contain alphanumeric characters, hyphens, underscores, and forward slashes".to_string());
}
Ok(())
}
pub fn validate_value(value: &str) -> Result<(), String> {
if value.is_empty() {
return Err("Value cannot be empty".to_string());
}
if value.len() > 256 {
return Err("Value cannot exceed 256 characters".to_string());
}
Ok(())
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct Tag {
pub id: String,
pub category: String,
pub value: String,
pub added_by: String,
#[serde(with = "datetime_format")]
pub added_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct AddTagRequest {
pub category: String,
pub value: String,
}
impl AddTagRequest {
pub fn validate(&self) -> Result<(), String> {
validate_category(&self.category)?;
validate_value(&self.value)?;
Ok(())
}
}