//========================================== // File: primitives.ts // Funzioni di validazione dei dati prmitivi // author: "villari.andrea@libero.it" // version: "1.0.0 2026-06-18" //========================================== export type FieldErrors = Record; export type ValidationResult = | { success: true; data: T } | { success: false; errors: FieldErrors }; const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; export function normalizeString(value: unknown): string { return value?.toString().trim() ?? ""; } export function normalizeEmail(value: unknown): string { return normalizeString(value).toLowerCase(); } export function normalizeBoolean(value: unknown, defaultValue = false): boolean { if (value === true || value === "true") return true; if (value === false || value === "false") return false; return defaultValue; } export function normalizeDecimal(value: unknown): number { if (typeof value === "number") return value; const normalized = value?.toString().trim().replace(",", ".") ?? ""; if (!normalized) return Number.NaN; return Number(normalized); } export function normalizePositiveInt(value: unknown): number { if (typeof value === "number") return value; const parsed = Number(value); return Number.isInteger(parsed) && parsed > 0 ? parsed : Number.NaN; } export function normalizeTime(value: unknown): string { const str = normalizeString(value); if (!str) return ""; if (/^\d{2}:\d{2}$/.test(str)) return `${str}:00`; return str; } export function validateRequired( errors: FieldErrors, field: string, value: string, message: string ): void { if (!value) errors[field] = message; } export function validateMaxLength( errors: FieldErrors, field: string, value: string, maxLength: number, label: string ): void { if (value.length > maxLength) { errors[field] = `${label} deve contenere al massimo ${maxLength} caratteri`; } } export function validateMinLength( errors: FieldErrors, field: string, value: string, minLength: number, label: string ): void { if (value && value.length < minLength) { errors[field] = `${label} deve contenere almeno ${minLength} caratteri`; } } export function validateEmailFormat( errors: FieldErrors, field: string, value: string ): void { if (value && !EMAIL_REGEX.test(value)) { errors[field] = "Formato email non valido"; } } export function validateEnum( errors: FieldErrors, field: string, value: string, allowedValues: readonly string[], label: string ): void { if (value && !allowedValues.includes(value)) { errors[field] = `${label} non valido`; } } export function validateBoolean( errors: FieldErrors, field: string, value: unknown, label: string ): void { if (value === undefined || value === null || value === "") return; if ( value !== true && value !== false && value !== "true" && value !== "false" ) { errors[field] = `${label} non valido`; } } const DATE_REGEX = /^\d{4}-\d{2}-\d{2}$/; const TIME_REGEX = /^\d{2}:\d{2}:\d{2}$/; export function validateDate( errors: FieldErrors, field: string, value: string, label: string ): void { if (!value) return; if (!DATE_REGEX.test(value)) { errors[field] = `${label} non valida`; return; } const parsed = new Date(`${value}T00:00:00`); if (Number.isNaN(parsed.getTime())) { errors[field] = `${label} non valida`; } } export function validateTimeFormat( errors: FieldErrors, field: string, value: string, label: string ): void { if (!value) return; if (!TIME_REGEX.test(value)) { errors[field] = `${label} non valida`; } } export function validatePositiveInt( errors: FieldErrors, field: string, value: number, label: string, required = true ): void { if (Number.isNaN(value) || value <= 0) { errors[field] = required ? `${label} obbligatorio` : `${label} non valido`; } } export function validateTimeRange( errors: FieldErrors, startField: string, endField: string, start: string, end: string ): void { if (!start || !end) return; if (start >= end) { errors[endField] = "L'ora di fine deve essere successiva all'ora di inizio"; } } export function validateDecimalPositive( errors: FieldErrors, field: string, value: unknown, label: string, min = 0.01 ): void { if (value === undefined || value === null || value === "") return; const num = typeof value === "number" ? value : Number(value); if (Number.isNaN(num) || num < min) { errors[field] = `${label} deve essere almeno ${min}`; } } export function buildResult(data: T, errors: FieldErrors): ValidationResult { return Object.keys(errors).length > 0 ? { success: false, errors } : { success: true, data }; }