corrierifrontend/src/validation/primitives.ts

200 lines
5 KiB
TypeScript
Raw Normal View History

//==========================================
// 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<string, string>;
export type ValidationResult<T> =
| { 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<T>(data: T, errors: FieldErrors): ValidationResult<T> {
2026-06-24 18:59:14 +02:00
if (Object.keys(errors).length > 0) {
return { success: false as const, errors };
}
return { success: true as const, data };
}