forked from viamrobotics/rdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.go
154 lines (130 loc) · 4.73 KB
/
model.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package resource
import (
"encoding/json"
"fmt"
"regexp"
"strings"
"github.com/pkg/errors"
)
// ModelNamespaceRDK is the namespace to use for models implemented by the rdk.
const ModelNamespaceRDK = ModelNamespace("rdk")
var (
// DefaultModelFamily is the rdk:builtin model family for built-in resources.
DefaultModelFamily = ModelNamespaceRDK.WithFamily("builtin")
// DefaultServiceModel is used for builtin services.
DefaultServiceModel = DefaultModelFamily.WithModel("builtin")
modelRegexValidator = regexp.MustCompile(`^([\w-]+):([\w-]+):([\w-]+)$`)
// this is used for the legacy style model name at the lowest level (e.g. rdk:builtin:->builtin<-).
singleFieldRegexValidator = regexp.MustCompile(`^([\w-]+)$`)
)
// Model represents an individual model within a family.
// It consists of a Namespace, Family, and Name.
type Model struct {
Family ModelFamily `json:","`
Name string `json:"name"`
}
// ModelFamily is a family of related models.
type ModelFamily struct {
Namespace ModelNamespace `json:"namespace"`
Name string `json:"model_family"`
}
// WithModel returns a new model with the given name.
func (f ModelFamily) WithModel(name string) Model {
return Model{f, name}
}
// ModelNamespace identifies the namespaces resource models can live in.
type ModelNamespace string
// WithFamily returns a new model family with the given name.
func (n ModelNamespace) WithFamily(name string) ModelFamily {
return ModelFamily{n, name}
}
// Validate ensures that important fields exist and are valid.
func (f ModelFamily) Validate() error {
if f.Namespace == "" {
return errors.New("namespace field for model missing")
}
if f.Name == "" {
return errors.New("model_family field for model missing")
}
if err := ContainsReservedCharacter(string(f.Namespace)); err != nil {
return err
}
if err := ContainsReservedCharacter(f.Name); err != nil {
return err
}
if !singleFieldRegexValidator.MatchString(string(f.Namespace)) {
return errors.Errorf("string %q is not a valid model namespace", f.Namespace)
}
if !singleFieldRegexValidator.MatchString(f.Name) {
return errors.Errorf("string %q is not a valid model family", f.Name)
}
return nil
}
// String returns the model family string for the resource.
func (f ModelFamily) String() string {
return fmt.Sprintf("%s:%s", f.Namespace, f.Name)
}
// NewModel return a new model from a triplet like acme:demo:mygizmo.
func NewModel(namespace, family, modelName string) Model {
return ModelNamespace(namespace).WithFamily(family).WithModel(modelName)
}
// NewModelFamily returns a new family from the given namespace and family.
func NewModelFamily(namespace, family string) ModelFamily {
return ModelNamespace(namespace).WithFamily(family)
}
// NewModelFromString creates a new Name based on a fully qualified resource name string passed in.
func NewModelFromString(modelStr string) (Model, error) {
if modelRegexValidator.MatchString(modelStr) {
matches := modelRegexValidator.FindStringSubmatch(modelStr)
return ModelNamespace(matches[1]).WithFamily(matches[2]).WithModel(matches[3]), nil
}
if singleFieldRegexValidator.MatchString(modelStr) {
return DefaultModelFamily.WithModel(modelStr), nil
}
return Model{}, errors.Errorf("string %q is not a valid model name", modelStr)
}
// Validate ensures that important fields exist and are valid.
func (m Model) Validate() error {
if err := m.Family.Validate(); err != nil {
return err
}
if m.Name == "" {
return errors.New("name field for model missing")
}
if err := ContainsReservedCharacter(m.Name); err != nil {
return err
}
if !singleFieldRegexValidator.MatchString(m.Name) {
return errors.Errorf("string %q is not a valid model name", m.Name)
}
return nil
}
// String returns the resource model name in its triplet form.
func (m Model) String() string {
return fmt.Sprintf("%s:%s:%s", m.Family.Namespace, m.Family.Name, m.Name)
}
// MarshalJSON marshals the model name in its triplet form.
func (m Model) MarshalJSON() ([]byte, error) {
return json.Marshal(m.String())
}
// UnmarshalJSON parses namespace:family:modelname strings to the full Model{} struct.
func (m *Model) UnmarshalJSON(data []byte) error {
modelStr := strings.Trim(string(data), "\"'")
if modelRegexValidator.MatchString(modelStr) {
matches := modelRegexValidator.FindStringSubmatch(modelStr)
*m = ModelNamespace(matches[1]).WithFamily(matches[2]).WithModel(matches[3])
return nil
}
if singleFieldRegexValidator.MatchString(modelStr) {
*m = DefaultModelFamily.WithModel(modelStr)
return nil
}
var tempModel map[string]string
if err := json.Unmarshal(data, &tempModel); err != nil {
return err
}
m.Family.Namespace = ModelNamespace(tempModel["namespace"])
m.Family.Name = tempModel["model_family"]
m.Name = tempModel["name"]
return m.Validate()
}