import { validerCatégorieVal } from "@constl/utils-ipa"; import { élémentsBd } from "@/types.js"; import type { élémentBdListeDonnées, élémentDonnées } from "@/tableaux.js"; import type { catégorieVariables } from "@/variables.js"; export type typeRègle = "catégorie" | "bornes" | "valeurCatégorique" | "existe"; export type sourceRègle = | { type: "variable"; id: string } | { type: "tableau"; id: string }; export type règleVariableAvecId = { id: string; règle: T; }; export type règleVariable = | règleExiste | règleBornes | règleValeurCatégorique | règleCatégorie; export type règleColonne = { règle: règleVariableAvecId; source: sourceRègle; colonne: string; }; export type typeOp = ">" | "<" | ">=" | "<=" | "≥" | "≤"; export type règleExiste = { typeRègle: "existe"; détails: détailsRègleExiste; }; export type détailsRègleExiste = Record; export type règleBornes = { typeRègle: "bornes"; détails: T; }; // Peut être numérique ou bien l'id d'une autre variable ou l'id d'une colonne sur la même BD export type détailsRègleBornes = | détailsRègleBornesFixe | détailsRègleBornesDynamiqueColonne | détailsRègleBornesDynamiqueVariable; export type détailsRègleBornesFixe = { type: "fixe"; val: number; op: typeOp; }; export type détailsRègleBornesDynamiqueColonne = { type: "dynamiqueColonne"; val: string; op: typeOp; }; export type détailsRègleBornesDynamiqueVariable = { type: "dynamiqueVariable"; val: string; op: typeOp; }; export type règleValeurCatégorique< T extends détailsRègleValeurCatégorique = détailsRègleValeurCatégorique, > = { typeRègle: "valeurCatégorique"; détails: T; }; export type détailsRègleValeurCatégorique = | détailsRègleValeurCatégoriqueFixe | détailsRègleValeurCatégoriqueDynamique; export type détailsRègleValeurCatégoriqueFixe = { type: "fixe"; options: (string | number | boolean)[]; }; export type détailsRègleValeurCatégoriqueDynamique = { type: "dynamique"; tableau: string; colonne: string; }; export type règleCatégorie = { typeRègle: "catégorie"; détails: détailsRègleCatégorie; }; export type détailsRègleCatégorie = { catégorie: catégorieVariables; }; export type Erreur = { règle: règleColonne; }; export type erreurValidation = { id: string; erreur: Erreur; }; export type erreurRègle = | erreurRègleCatégoriqueColonneInexistante | erreurRègleBornesColonneInexistante | erreurRègleBornesVariableNonPrésente; export type erreurRègleCatégoriqueColonneInexistante = { règle: règleColonne< règleValeurCatégorique >; détails: "colonneCatégInexistante"; }; export type erreurRègleBornesColonneInexistante = { règle: règleColonne>; détails: "colonneBornesInexistante"; }; export type erreurRègleBornesVariableNonPrésente = { règle: règleColonne>; détails: "variableBornesNonPrésente"; }; export type schémaFonctionValidation< T extends élémentBdListeDonnées = élémentBdListeDonnées, R extends règleVariable = règleVariable, > = (valeurs: élémentDonnées[]) => erreurValidation[]; export function générerFonctionRègle< T extends élémentBdListeDonnées, R extends règleVariable, >({ règle, varsÀColonnes, donnéesCatégorie, }: { règle: règleColonne; varsÀColonnes: { [key: string]: string }; donnéesCatégorie?: élémentsBd[]; }): schémaFonctionValidation { const règleDeLaVariable = règle.règle; const { colonne } = règle; const { typeRègle } = règleDeLaVariable.règle; switch (typeRègle) { case "existe": { return (vals: élémentDonnées[]): erreurValidation[] => { const nonValides = vals.filter((v) => v.données[colonne] === undefined); return nonValides.map((v: élémentDonnées) => { const { id } = v; const erreur: erreurValidation = { id, erreur: { règle }, }; return erreur; }); }; } case "catégorie": { return (vals: élémentDonnées[]) => { const catégorie = (règleDeLaVariable.règle as règleCatégorie).détails .catégorie; const nonValides = vals.filter( (v) => !validerCatégorieVal({ val: v.données[colonne], catégorie }), ); return nonValides.map((v: élémentDonnées) => { const { id } = v; const erreur: erreurValidation = { id, erreur: { règle }, }; return erreur; }); }; } case "bornes": { const règleTypeBornes = règleDeLaVariable as règleVariableAvecId; let fComp: (v: élémentDonnées) => boolean; let fOp: (v1: number, v2: number) => boolean; const { val, op, type: typeBornes } = règleTypeBornes.règle.détails; const manquantes = (v1?: number, v2?: number): boolean => { return v1 === undefined || v2 === undefined; }; switch (op) { case ">": fOp = (v1?: number, v2?: number) => manquantes(v1, v2) || v1! > v2!; break; case "<": fOp = (v1?: number, v2?: number) => manquantes(v1, v2) || v1! < v2!; break; case "≥": case ">=": fOp = (v1?: number, v2?: number) => manquantes(v1, v2) || v1! >= v2!; break; case "≤": case "<=": fOp = (v1?: number, v2?: number) => manquantes(v1, v2) || v1! <= v2!; break; } if (typeBornes === "fixe") { fComp = (v: élémentDonnées): boolean => { const donnéesCol = v.données[colonne]; return Array.isArray(donnéesCol) ? donnéesCol.every((x) => fOp(x as number, val as number)) : fOp(donnéesCol as number, val as number); }; } else { fComp = (v: élémentDonnées): boolean => { const donnéesCol = v.données[colonne]; // Vérifier s'il s'agit d'une variable ou d'une colonne et s'ajuster en fonction const borne = ( typeBornes === "dynamiqueVariable" ? v.données[varsÀColonnes[val]] : v.données[val] ) as number; return Array.isArray(donnéesCol) ? donnéesCol.every((x) => fOp(x as number, borne)) : fOp(donnéesCol as number, borne); }; } return (vals: élémentDonnées[]) => { const nonValides = vals.filter( (v) => !validerBorneVal({ val: v, fComp }), ); return nonValides.map((v: élémentDonnées) => { const { id } = v; const erreur: erreurValidation = { id, erreur: { règle }, }; return erreur; }); }; } case "valeurCatégorique": { const règleTypeCatégorique = règleDeLaVariable.règle as règleValeurCatégorique; const options = règleTypeCatégorique.détails.type === "fixe" ? règleTypeCatégorique.détails.options : donnéesCatégorie; if (!options) throw new Error("Options non spécifiées"); return (vals: élémentDonnées[]) => { const nonValides = vals.filter( (v: élémentDonnées) => v.données[colonne] !== undefined && !options.includes(v.données[colonne]), ); return nonValides.map((v: élémentDonnées) => { const { id } = v; return { id, colonne, erreur: { règle }, }; }); }; } default: throw Error(`Catégorie ${typeRègle} inconnue.`); } } const validerBorneVal = ({ val, fComp, }: { val: élémentDonnées; fComp: (v: élémentDonnées) => boolean; }) => { if (Array.isArray(val)) { return val.every((v) => fComp(v)); } else { return fComp(val); } };