RTIX / src /domain /validation.rs
github-actions
deploy: clean backend production release
c33971d
use once_cell::sync::Lazy;
use regex::Regex;
static PHONE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[6-9]\d{9}$").unwrap());
static EMAIL_REGEX: Lazy<Regex> =
Lazy::new(|| Regex::new(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$").unwrap());
static PINCODE_REGEX: Lazy<Regex> = Lazy::new(|| Regex::new(r"^[1-9]\d{5}$").unwrap());
pub fn validate_email(email: &str) -> Result<(), ValidationError> {
if email.is_empty() {
return Err(ValidationError::new("Email cannot be empty"));
}
if email.len() > 255 {
return Err(ValidationError::new("Email too long"));
}
if !EMAIL_REGEX.is_match(email) {
return Err(ValidationError::new("Invalid email format"));
}
Ok(())
}
pub fn validate_pincode(pincode: &str) -> Result<(), ValidationError> {
if pincode.is_empty() {
return Err(ValidationError::new("Pincode cannot be empty"));
}
if !PINCODE_REGEX.is_match(pincode) {
return Err(ValidationError::new(
"Invalid Indian pincode format (6 digits starting with 1-9)",
));
}
Ok(())
}
pub fn validate_price(price: f64) -> Result<(), ValidationError> {
if price <= 0.0 {
return Err(ValidationError::new("Price must be greater than 0"));
}
if price > 1_000_000.0 {
return Err(ValidationError::new("Price exceeds maximum allowed value"));
}
if price.is_nan() || price.is_infinite() {
return Err(ValidationError::new("Invalid price value"));
}
Ok(())
}
pub fn validate_product_name(name: &str) -> Result<(), ValidationError> {
if name.trim().is_empty() {
return Err(ValidationError::new("Product name cannot be empty"));
}
if name.len() > 200 {
return Err(ValidationError::new(
"Product name too long (max 200 characters)",
));
}
Ok(())
}
pub fn validate_weight(weight: f64) -> Result<(), ValidationError> {
if weight < 0.0 {
return Err(ValidationError::new("Weight cannot be negative"));
}
if weight > 100_000.0 {
// 100kg limit for social checkout
return Err(ValidationError::new(
"Weight exceeds maximum allowed value (100kg)",
));
}
if weight.is_nan() || weight.is_infinite() {
return Err(ValidationError::new("Invalid weight value"));
}
Ok(())
}
pub fn sanitize_string(input: &str) -> String {
input
.trim()
.chars()
.filter(|c| !c.is_control())
.take(1000)
.collect()
}
pub fn normalize_phone(phone: &str) -> Result<String, ValidationError> {
if phone.is_empty() {
return Err(ValidationError::new("Phone cannot be empty"));
}
let digits: String = phone.chars().filter(|c| c.is_ascii_digit()).collect();
let normalized = match digits.len() {
10 => digits,
11 if digits.starts_with('0') => digits[1..].to_string(),
12 if digits.starts_with("91") => digits[2..].to_string(),
_ => return Err(ValidationError::new("Invalid Indian phone number format")),
};
if !PHONE_REGEX.is_match(&normalized) {
return Err(ValidationError::new("Invalid Indian phone number format"));
}
Ok(normalized)
}
pub fn validate_base64_payload(data: &str, max_bytes: usize) -> Result<Vec<u8>, ValidationError> {
if data.is_empty() {
return Err(ValidationError::new("Base64 payload cannot be empty"));
}
let raw_data = if let Some(pos) = data.find(',') {
&data[pos + 1..]
} else {
data
};
use base64::{engine::general_purpose, Engine as _};
match general_purpose::STANDARD.decode(raw_data) {
Ok(bytes) => {
if bytes.len() > max_bytes {
return Err(ValidationError::new(&format!(
"Payload exceeds maximum size of {} bytes",
max_bytes
)));
}
Ok(bytes)
}
Err(_) => Err(ValidationError::new("Invalid Base64 encoding")),
}
}
pub fn sanitize_filename(filename: &str) -> String {
let sanitized: String = filename
.replace("..", "")
.chars()
.filter(|c| c.is_alphanumeric() || *c == '.' || *c == '-' || *c == '_')
.collect();
if sanitized.starts_with('.') || sanitized.is_empty() {
return "unnamed".to_string();
}
sanitized.chars().take(255).collect()
}
#[derive(Debug, Clone)]
pub struct ValidationError {
pub message: String,
}
impl ValidationError {
pub fn new(msg: &str) -> Self {
Self {
message: msg.to_string(),
}
}
}
impl std::fmt::Display for ValidationError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for ValidationError {}