package rules import ( "fmt" "io/fs" "gopkg.in/yaml.v3" ) // LoadDatenklasse lädt und validiert die Datenklasse-Ableitungstabelle. func LoadDatenklasse(fsys fs.FS, path string) (DatenklasseRegelwerk, error) { var rw DatenklasseRegelwerk data, err := fs.ReadFile(fsys, path) if err != nil { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse lesen: %w", err) } if err := yaml.Unmarshal(data, &rw); err != nil { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse parsen: %w", err) } if rw.Version < 1 { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: version fehlt oder ungültig") } if len(rw.Stufen) == 0 { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: keine stufen definiert") } seenIDs := map[string]bool{} seenRaenge := map[int]string{} for _, st := range rw.Stufen { if st.ID == "" { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: stufe ohne id") } if seenIDs[st.ID] { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: doppelte stufe-id %q", st.ID) } seenIDs[st.ID] = true if other, ok := seenRaenge[st.Rang]; ok { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: stufen %q und %q teilen sich rang %d — \"höchste Stufe gewinnt\" braucht eindeutige Ränge", st.ID, other, st.Rang) } seenRaenge[st.Rang] = st.ID if len(st.Ausloeser) == 0 { return DatenklasseRegelwerk{}, fmt.Errorf("rules: datenklasse: stufe %q hat keine ausloeser", st.ID) } } return rw, nil } // LoadEinstufung lädt und validiert die KI-VO-Einstufungstabelle. func LoadEinstufung(fsys fs.FS, path string) (EinstufungRegelwerk, error) { var rw EinstufungRegelwerk data, err := fs.ReadFile(fsys, path) if err != nil { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung lesen: %w", err) } if err := yaml.Unmarshal(data, &rw); err != nil { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung parsen: %w", err) } if rw.Version < 1 { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung: version fehlt oder ungültig") } if len(rw.Stufen) == 0 { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung: keine stufen definiert") } seenIDs := map[string]bool{} for i, st := range rw.Stufen { if st.ID == "" { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung: stufe ohne id") } if seenIDs[st.ID] { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung: doppelte stufe-id %q", st.ID) } seenIDs[st.ID] = true // Die letzte Stufe darf eine Auffangregel ohne Bedingungen sein // (z. B. "minimal"), alle davor brauchen mindestens eine Variante. if len(st.Varianten) == 0 && i != len(rw.Stufen)-1 { return EinstufungRegelwerk{}, fmt.Errorf("rules: einstufung: stufe %q hat keine varianten (nur die letzte Stufe darf eine Auffangregel sein)", st.ID) } } return rw, nil } // LoadAnforderungen lädt und validiert die Anforderungsprofil-Tabelle. func LoadAnforderungen(fsys fs.FS, path string) (AnforderungsRegelwerk, error) { var rw AnforderungsRegelwerk data, err := fs.ReadFile(fsys, path) if err != nil { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen lesen: %w", err) } if err := yaml.Unmarshal(data, &rw); err != nil { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen parsen: %w", err) } if rw.Version < 1 { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen: version fehlt oder ungültig") } if len(rw.Anforderungen) == 0 { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen: keine anforderungen definiert") } seenIDs := map[string]bool{} for _, a := range rw.Anforderungen { if a.ID == "" { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen: anforderung ohne id") } if seenIDs[a.ID] { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen: doppelte anforderung-id %q", a.ID) } seenIDs[a.ID] = true if len(a.AusDatenklassen) == 0 && len(a.AusEinstufungen) == 0 { return AnforderungsRegelwerk{}, fmt.Errorf("rules: anforderungen: anforderung %q hat weder aus_datenklassen noch aus_einstufungen", a.ID) } } return rw, nil }