package rules import ( "fmt" "io/fs" "path/filepath" "gopkg.in/yaml.v3" ) // Load liest alle *.yaml-Dateien aus fsys (nicht rekursiv) und parst sie // als Regeln. Ein Fehler in einer Datei (Parse-Fehler, fehlende ID/ // Version) bricht das Laden komplett ab, statt die Datei stillschweigend // zu überspringen — ein halb geladenes Regelwerk ist gefährlicher als // ein Start, der mit einem klaren Fehler abbricht. func Load(fsys fs.FS) ([]Rule, error) { entries, err := fs.ReadDir(fsys, ".") if err != nil { return nil, fmt.Errorf("rules: read dir: %w", err) } seen := make(map[string]bool) var result []Rule for _, entry := range entries { if entry.IsDir() || filepath.Ext(entry.Name()) != ".yaml" { continue } data, err := fs.ReadFile(fsys, entry.Name()) if err != nil { return nil, fmt.Errorf("rules: read %s: %w", entry.Name(), err) } var r Rule if err := yaml.Unmarshal(data, &r); err != nil { return nil, fmt.Errorf("rules: parse %s: %w", entry.Name(), err) } if r.ID == "" { return nil, fmt.Errorf("rules: %s: missing id", entry.Name()) } if r.Version == 0 { return nil, fmt.Errorf("rules: %s: missing version", entry.Name()) } if r.Jurisdiction == "" { return nil, fmt.Errorf("rules: %s: missing land (jurisdiction)", entry.Name()) } if seen[r.ID] { return nil, fmt.Errorf("rules: %s: duplicate rule id %s", entry.Name(), r.ID) } seen[r.ID] = true result = append(result, r) } return result, nil }