/
read.go
137 lines (123 loc) · 3.1 KB
/
read.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
package io
import (
"fmt"
mdl "github.com/briggysmalls/archie/internal/model"
"github.com/mitchellh/mapstructure"
"gopkg.in/yaml.v2"
)
// ParseYaml parses an API model from a yaml string
func ParseYaml(data string) (*mdl.Model, error) {
// Parse the yaml using the package
var sModel Model
err := yaml.UnmarshalStrict([]byte(data), &sModel)
if err != nil {
return nil, err
}
// Convert to an internal model
return toInternalModel(sModel)
}
func toInternalModel(apiModel Model) (*mdl.Model, error) {
// Copy the parsed elements into the new model
m := mdl.NewModel()
for _, rootEl := range apiModel.Elements {
err := updateModelAndRecurse(&m, nil, rootEl)
if err != nil {
return nil, err
}
}
// Add top level associations
err := addAssociations(&m, nil, apiModel.Associations)
if err != nil {
return nil, err
}
return &m, nil
}
// Add associations for element of a model
func addAssociations(model *mdl.Model, parent mdl.Element, assocs []Association) error {
// Copy the parsed relationships into the new model
for _, ass := range assocs {
// Get the elements
var src, dest mdl.Element
var err error
src, err = model.LookupName(ass.Source, parent)
if err != nil {
return err
}
dest, err = model.LookupName(ass.Destination, parent)
if err != nil {
return err
}
// Add a new relationship
model.AddAssociation(src, dest, ass.Tags)
}
return nil
}
func addChildren(model *mdl.Model, parent mdl.Element, children []interface{}) error {
// Iterate through children
for _, child := range children {
// Check whether it is a brief entry or not
switch i := child.(type) {
case string:
// This is a shorthand
new := mdl.NewItem(i, nil)
// Add to the model
model.AddElement(new, parent)
case map[string]interface{}:
// Map into an element structure
var el Element
var err error
err = mapstructure.Decode(i, &el)
if err != nil {
return err
}
// Map into an element structure
err = updateModelAndRecurse(model, parent, el)
if err != nil {
return err
}
case map[interface{}]interface{}:
// See: https://github.com/go-yaml/yaml/issues/139
// Map into an element structure
var el Element
var err error
err = mapstructure.Decode(i, &el)
if err != nil {
return err
}
err = updateModelAndRecurse(model, parent, el)
if err != nil {
return err
}
default:
return fmt.Errorf("Unexpected type %T", i)
}
}
return nil
}
func updateModelAndRecurse(model *mdl.Model, parent mdl.Element, el Element) error {
// Handle creating actors
if el.Kind == "actor" {
if parent != nil {
return fmt.Errorf("Only root elements can be actors, element %s is not.", el.Name)
}
new := mdl.NewActor(el.Name)
model.AddElement(new, parent)
return nil
}
// This is a fully-specified element
new := mdl.NewItem(el.Name, el.Tags)
// Add to the model
model.AddElement(new, parent)
// Add children
err := addChildren(model, new, el.Children)
if err != nil {
return err
}
if len(el.Associations) != 0 {
err := addAssociations(model, new, el.Associations)
if err != nil {
return err
}
}
return nil
}