blob: a0c3d5884415577fc072f016935bdd2278b74367 [file] [log] [blame] [edit]
package main
import (
"errors"
"fmt"
"os"
"slices"
"github.com/goccy/go-yaml"
"github.com/santhosh-tekuri/jsonschema/v5"
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
)
func ValidateYamls(schemaPath string, yamlPaths []string) error {
// Validation through json schema
for _, yamlPath := range yamlPaths {
yamlFile, err := os.ReadFile(yamlPath)
if err != nil {
return fmt.Errorf("ValidateYaml: %w", err)
}
var yml map[string]any
if err := yaml.Unmarshal(yamlFile, &yml); err != nil {
return fmt.Errorf("ValidateYaml: %w", err)
}
schema := jsonschema.MustCompile(schemaPath)
if err := schema.Validate(yml); err != nil {
return fmt.Errorf("ValidateYaml: %w", err)
}
}
// Validation of possible duplication of entries across multiple yaml files
if err := mergeAndValidateDuplicates(yamlPaths); err != nil {
panic(err)
}
// TODO: add dependency check validations
return nil
}
func mergeAndValidateDuplicates(yamlPaths []string) (errs error) {
constants := make(map[string]Constant)
enums := make(map[string]Enum)
bitflags := make(map[string]Bitflag)
structs := make(map[string]Struct)
callbacks := make(map[string]Callback)
functions := make(map[string]Function)
objects := make(map[string]Object)
for _, yamlPath := range yamlPaths {
src, err := os.ReadFile(yamlPath)
if err != nil {
panic(err)
}
var data Yml
if err := yaml.Unmarshal(src, &data); err != nil {
panic(err)
}
for _, c := range data.Constants {
if _, ok := constants[c.Name]; ok {
errs = errors.Join(errs, fmt.Errorf("merge: constants.%s in %s was already found previously while parsing, duplicates are not allowed", c.Name, yamlPath))
}
constants[c.Name] = c
}
for _, e := range data.Enums {
if prevEnum, ok := enums[e.Name]; ok {
if !e.Extended {
errs = errors.Join(errs, fmt.Errorf("merge: enums.%s in %s is being extended but isn't marked as one", e.Name, yamlPath))
}
for _, entry := range e.Entries {
if entry != nil {
if slices.ContainsFunc(prevEnum.Entries, func(e *EnumEntry) bool { return e != nil && e.Name == entry.Name }) {
errs = errors.Join(errs, fmt.Errorf("merge: enums.%s.%s in %s was already found previously while parsing, duplicates are not allowed", e.Name, entry.Name, yamlPath))
}
prevEnum.Entries = append(prevEnum.Entries, entry)
}
}
enums[e.Name] = prevEnum
} else {
enums[e.Name] = e
}
}
for _, bf := range data.Bitflags {
if prevBf, ok := bitflags[bf.Name]; ok {
if !bf.Extended {
errs = errors.Join(errs, fmt.Errorf("merge: bitflags.%s in %s is being extended but isn't marked as one", bf.Name, yamlPath))
}
for _, entry := range bf.Entries {
if slices.ContainsFunc(prevBf.Entries, func(e BitflagEntry) bool { return e.Name == entry.Name }) {
errs = errors.Join(errs, fmt.Errorf("merge: bitflags.%s.%s in %s was already found previously while parsing, duplicates are not allowed", bf.Name, entry.Name, yamlPath))
}
if entry.Value == "" && len(entry.ValueCombination) == 0 {
errs = errors.Join(errs, fmt.Errorf("merge: bitflags.%s.%s in %s was extended but doesn't have a value or value_combination, extended bitflag entries must have an explicit value", bf.Name, entry.Name, yamlPath))
}
prevBf.Entries = append(prevBf.Entries, entry)
}
bitflags[bf.Name] = prevBf
} else {
bitflags[bf.Name] = bf
}
}
for _, c := range data.Callbacks {
if _, ok := callbacks[c.Name]; ok {
errs = errors.Join(errs, fmt.Errorf("merge: callbacks.%s in %s was already found previously while parsing, duplicates are not allowed", c.Name, yamlPath))
}
callbacks[c.Name] = c
}
for _, s := range data.Structs {
if _, ok := structs[s.Name]; ok {
errs = errors.Join(errs, fmt.Errorf("merge: structs.%s in %s was already found previously while parsing, duplicates are not allowed", s.Name, yamlPath))
}
structs[s.Name] = s
}
for _, f := range data.Functions {
if _, ok := functions[f.Name]; ok {
errs = errors.Join(errs, fmt.Errorf("merge: functions.%s in %s was already found previously while parsing, duplicates are not allowed", f.Name, yamlPath))
}
functions[f.Name] = f
}
for _, o := range data.Objects {
if prevObj, ok := objects[o.Name]; ok {
if !o.Extended {
errs = errors.Join(errs, fmt.Errorf("merge: objects.%s in %s is being extended but isn't marked as one", o.Name, yamlPath))
}
for _, method := range o.Methods {
if slices.ContainsFunc(prevObj.Methods, func(f Function) bool { return f.Name == method.Name }) {
errs = errors.Join(errs, fmt.Errorf("merge: objects.%s.%s in %s was already found previously while parsing, duplicates are not allowed", o.Name, method.Name, yamlPath))
}
prevObj.Methods = append(prevObj.Methods, method)
}
objects[o.Name] = prevObj
} else {
objects[o.Name] = o
}
}
}
return
}