blob: abafa154f5de69ecf3332d0a4f9d7331e41c04c3 [file] [log] [blame]
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +04001package installer
2
3import (
4 "encoding/json"
5 "fmt"
6 "strings"
7
8 "cuelang.org/go/cue"
9 "cuelang.org/go/cue/cuecontext"
10)
11
12type Kind int
13
14const (
15 KindBoolean Kind = 0
16 KindString = 1
17 KindStruct = 2
18 KindNetwork = 3
19)
20
21type Schema interface {
22 Kind() Kind
23 Fields() map[string]Schema
24}
25
26const networkSchema = `
27#Network: {
28 IngressClass: string
29 CertificateIssuer: string
30 Domain: string
31}
32
33value: %s
34
35valid: #Network & value
36`
37
38func isNetwork(v cue.Value) bool {
39 if v.Value().Kind() != cue.StructKind {
40 return false
41 }
42 value := fmt.Sprintf("%#v", v)
43 s := fmt.Sprintf(networkSchema, value)
44 c := cuecontext.New()
45 u := c.CompileString(s)
46 return u.Err() == nil && u.Validate() == nil
47}
48
49type basicSchema struct {
50 kind Kind
51}
52
53func (s basicSchema) Kind() Kind {
54 return s.kind
55}
56
57func (s basicSchema) Fields() map[string]Schema {
58 return nil
59}
60
61type structSchema struct {
62 fields map[string]Schema
63}
64
65func (s structSchema) Kind() Kind {
66 return KindStruct
67}
68
69func (s structSchema) Fields() map[string]Schema {
70 return s.fields
71}
72
73func NewCueSchema(v cue.Value) (Schema, error) {
74 switch v.Value().Kind() {
75 case cue.StringKind:
76 return basicSchema{KindString}, nil
77 case cue.StructKind:
78 if isNetwork(v) {
79 return basicSchema{KindNetwork}, nil
80 }
81 s := structSchema{make(map[string]Schema)}
82 f, err := v.Fields()
83 if err != nil {
84 return nil, err
85 }
86 for f.Next() {
87 scm, err := NewCueSchema(f.Value())
88 if err != nil {
89 return nil, err
90 }
91 s.fields[f.Selector().String()] = scm
92 }
93 return s, nil
94 default:
95 return nil, fmt.Errorf("SHOULD NOT REACH!")
96 }
97}
98
99func newSchema(schema map[string]any) (Schema, error) {
100 switch schema["type"] {
101 case "string":
102 if r, ok := schema["role"]; ok && r == "network" {
103 return basicSchema{KindNetwork}, nil
104 } else {
105 return basicSchema{KindString}, nil
106 }
107 case "object":
108 s := structSchema{make(map[string]Schema)}
109 props := schema["properties"].(map[string]any)
110 for name, schema := range props {
111 sm, _ := schema.(map[string]any)
112 scm, err := newSchema(sm)
113 if err != nil {
114 return nil, err
115 }
116 s.fields[name] = scm
117 }
118 return s, nil
119 default:
120 return nil, fmt.Errorf("SHOULD NOT REACH!")
121 }
122}
123
124func NewJSONSchema(schema string) (Schema, error) {
125 ret := make(map[string]any)
126 if err := json.NewDecoder(strings.NewReader(schema)).Decode(&ret); err != nil {
127 return nil, err
128 }
129 return newSchema(ret)
130}