blob: 69e7ec07bb767dd293916b40c59ed777b8fd6000 [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: {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040028 name: string
29 ingressClass: string
30 certificateIssuer: string
31 domain: string
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040032}
33
34value: %s
35
36valid: #Network & value
37`
38
39func isNetwork(v cue.Value) bool {
40 if v.Value().Kind() != cue.StructKind {
41 return false
42 }
43 value := fmt.Sprintf("%#v", v)
44 s := fmt.Sprintf(networkSchema, value)
45 c := cuecontext.New()
46 u := c.CompileString(s)
47 return u.Err() == nil && u.Validate() == nil
48}
49
50type basicSchema struct {
51 kind Kind
52}
53
54func (s basicSchema) Kind() Kind {
55 return s.kind
56}
57
58func (s basicSchema) Fields() map[string]Schema {
59 return nil
60}
61
62type structSchema struct {
63 fields map[string]Schema
64}
65
66func (s structSchema) Kind() Kind {
67 return KindStruct
68}
69
70func (s structSchema) Fields() map[string]Schema {
71 return s.fields
72}
73
74func NewCueSchema(v cue.Value) (Schema, error) {
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040075 switch v.IncompleteKind() {
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040076 case cue.StringKind:
77 return basicSchema{KindString}, nil
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040078 case cue.BoolKind:
79 return basicSchema{KindBoolean}, nil
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040080 case cue.StructKind:
81 if isNetwork(v) {
82 return basicSchema{KindNetwork}, nil
83 }
84 s := structSchema{make(map[string]Schema)}
Giorgi Lekveishvili9b52ab92024-01-05 13:12:48 +040085 f, err := v.Fields(cue.Schema())
Giorgi Lekveishvili7c427602024-01-04 00:13:55 +040086 if err != nil {
87 return nil, err
88 }
89 for f.Next() {
90 scm, err := NewCueSchema(f.Value())
91 if err != nil {
92 return nil, err
93 }
94 s.fields[f.Selector().String()] = scm
95 }
96 return s, nil
97 default:
98 return nil, fmt.Errorf("SHOULD NOT REACH!")
99 }
100}
101
102func newSchema(schema map[string]any) (Schema, error) {
103 switch schema["type"] {
104 case "string":
105 if r, ok := schema["role"]; ok && r == "network" {
106 return basicSchema{KindNetwork}, nil
107 } else {
108 return basicSchema{KindString}, nil
109 }
110 case "object":
111 s := structSchema{make(map[string]Schema)}
112 props := schema["properties"].(map[string]any)
113 for name, schema := range props {
114 sm, _ := schema.(map[string]any)
115 scm, err := newSchema(sm)
116 if err != nil {
117 return nil, err
118 }
119 s.fields[name] = scm
120 }
121 return s, nil
122 default:
123 return nil, fmt.Errorf("SHOULD NOT REACH!")
124 }
125}
126
127func NewJSONSchema(schema string) (Schema, error) {
128 ret := make(map[string]any)
129 if err := json.NewDecoder(strings.NewReader(schema)).Decode(&ret); err != nil {
130 return nil, err
131 }
132 return newSchema(ret)
133}