feat: renderer
This commit is contained in:
93
validator/validator.go
Normal file
93
validator/validator.go
Normal file
@@ -0,0 +1,93 @@
|
||||
// Package validator provides a validator for the marka data.
|
||||
package validator
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.max-richter.dev/max/marka/registry"
|
||||
"github.com/santhosh-tekuri/jsonschema/v6"
|
||||
)
|
||||
|
||||
var (
|
||||
loadOnce sync.Once
|
||||
compiler *jsonschema.Compiler
|
||||
compileErr error
|
||||
schemaCache sync.Map
|
||||
)
|
||||
|
||||
// ValidateSchema validates instance against the Schema.org JSON Schema named `schemaName`.
|
||||
// Examples: ValidateSchema(inst, "Recipe"), ValidateSchema(inst, "schema:Recipe").
|
||||
func ValidateSchema(instance any, schemaName string) error {
|
||||
if err := ensureCompiler(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ref := normalizeRef(schemaName)
|
||||
|
||||
if v, ok := schemaCache.Load(ref); ok {
|
||||
if err := v.(*jsonschema.Schema).Validate(instance); err != nil {
|
||||
return fmt.Errorf("validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
sch, err := compiler.Compile(ref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile schema %q: %w", ref, err)
|
||||
}
|
||||
schemaCache.Store(ref, sch)
|
||||
|
||||
if err := sch.Validate(instance); err != nil {
|
||||
return fmt.Errorf("validation failed: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func ensureCompiler() error {
|
||||
loadOnce.Do(func() {
|
||||
c := jsonschema.NewCompiler()
|
||||
|
||||
rawSchemas, err := registry.GetSchemas()
|
||||
if err != nil {
|
||||
compileErr = fmt.Errorf("read schema directory: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, rawSchema := range rawSchemas {
|
||||
js, err := jsonschema.UnmarshalJSON(bytes.NewReader(rawSchema))
|
||||
if err != nil {
|
||||
compileErr = err
|
||||
return
|
||||
}
|
||||
|
||||
id := extractID(rawSchema)
|
||||
if err := c.AddResource(id, js); err != nil {
|
||||
compileErr = fmt.Errorf("add resource %s: %w", id, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
compiler = c
|
||||
})
|
||||
return compileErr
|
||||
}
|
||||
|
||||
func extractID(raw []byte) string {
|
||||
var tmp struct {
|
||||
ID string `json:"$id"`
|
||||
}
|
||||
_ = json.Unmarshal(raw, &tmp)
|
||||
return strings.TrimSpace(tmp.ID)
|
||||
}
|
||||
|
||||
func normalizeRef(name string) string {
|
||||
n := strings.TrimSpace(name)
|
||||
if strings.HasPrefix(n, "schema:") || strings.HasPrefix(n, "http://") || strings.HasPrefix(n, "https://") {
|
||||
return n
|
||||
}
|
||||
return "schema:" + n
|
||||
}
|
Reference in New Issue
Block a user