1package gen
2
3import (
4	"fmt"
5)
6
7type Visitor interface {
8	Visit(node SchemaNode) (w Visitor)
9}
10
11type SchemaNode interface {
12	node()
13}
14
15type Schema struct {
16	Title        string
17	Version      string
18	SeriesLimit  *SeriesLimit `toml:"series-limit"`
19	Measurements Measurements
20}
21
22func (*Schema) node() {}
23
24type Measurements []Measurement
25
26func (Measurements) node() {}
27
28type Tags []Tag
29
30func (Tags) node() {}
31
32type Fields []Field
33
34func (Fields) node() {}
35
36type Measurement struct {
37	Name        string
38	SeriesLimit *SeriesLimit `toml:"series-limit"`
39	Sample      *sample
40	Tags        Tags
41	Fields      Fields
42}
43
44func (*Measurement) node() {}
45
46type TagSource interface {
47	fmt.Stringer
48	SchemaNode
49	tagsource()
50}
51
52type Tag struct {
53	Name   string
54	Source TagSource
55}
56
57func (*Tag) node() {}
58
59type TagArraySource struct {
60	Values []string
61}
62
63func (*TagArraySource) node()      {}
64func (*TagArraySource) tagsource() {}
65
66func (s *TagArraySource) String() string {
67	return fmt.Sprintf("array, source=%#v", s.Values)
68}
69
70type TagSequenceSource struct {
71	Format string
72	Start  int64
73	Count  int64
74}
75
76func (*TagSequenceSource) node()      {}
77func (*TagSequenceSource) tagsource() {}
78
79func (t *TagSequenceSource) String() string {
80	return fmt.Sprintf("sequence, prefix=%q, range=[%d,%d)", t.Format, t.Start, t.Start+t.Count)
81}
82
83type TagFileSource struct {
84	Path string
85}
86
87func (*TagFileSource) node()      {}
88func (*TagFileSource) tagsource() {}
89
90func (s *TagFileSource) String() string {
91	return fmt.Sprintf("file, path=%s", s.Path)
92}
93
94type FieldSource interface {
95	fmt.Stringer
96	SchemaNode
97	fieldsource()
98}
99
100type Field struct {
101	Name          string
102	Count         int64
103	TimePrecision *precision `toml:"time-precision"` // TimePrecision determines the precision for generated timestamp values
104	TimeInterval  *duration  `toml:"time-interval"`  // TimeInterval determines the duration between timestamp values
105	Source        FieldSource
106}
107
108func (t *Field) TimeSequenceSpec() TimeSequenceSpec {
109	if t.TimeInterval != nil {
110		return TimeSequenceSpec{
111			Count: int(t.Count),
112			Delta: t.TimeInterval.Duration,
113		}
114	}
115
116	if t.TimePrecision != nil {
117		return TimeSequenceSpec{
118			Count:     int(t.Count),
119			Precision: t.TimePrecision.ToDuration(),
120		}
121	}
122
123	panic("TimeInterval and TimePrecision are nil")
124}
125
126func (*Field) node() {}
127
128type FieldConstantValue struct {
129	Value interface{}
130}
131
132func (*FieldConstantValue) node()        {}
133func (*FieldConstantValue) fieldsource() {}
134
135func (f *FieldConstantValue) String() string {
136	return fmt.Sprintf("constant, source=%#v", f.Value)
137}
138
139type FieldArraySource struct {
140	Value interface{}
141}
142
143func (*FieldArraySource) node()        {}
144func (*FieldArraySource) fieldsource() {}
145
146func (f *FieldArraySource) String() string {
147	return fmt.Sprintf("array, source=%#v", f.Value)
148}
149
150type FieldFloatRandomSource struct {
151	Seed     int64
152	Min, Max float64
153}
154
155func (*FieldFloatRandomSource) node()        {}
156func (*FieldFloatRandomSource) fieldsource() {}
157
158func (f *FieldFloatRandomSource) String() string {
159	return fmt.Sprintf("rand<float>, seed=%d, min=%f, max=%f", f.Seed, f.Max, f.Max)
160}
161
162type FieldIntegerZipfSource struct {
163	Seed int64
164	S, V float64
165	IMAX uint64
166}
167
168func (*FieldIntegerZipfSource) node()        {}
169func (*FieldIntegerZipfSource) fieldsource() {}
170
171func (f *FieldIntegerZipfSource) String() string {
172	return fmt.Sprintf("rand<float>, seed=%d, s=%f, v=%f, imax=%d", f.Seed, f.S, f.V, f.IMAX)
173}
174
175type VisitorFn func(node SchemaNode) bool
176
177func (fn VisitorFn) Visit(node SchemaNode) (w Visitor) {
178	if fn(node) {
179		return fn
180	}
181	return nil
182}
183
184// WalkDown performs a pre-order, depth-first traversal of the graph, calling v for each node.
185// Pre-order starts by calling the visitor for the root and each child as it traverses down
186// the graph to the leaves.
187func WalkDown(v Visitor, node SchemaNode) {
188	walk(v, node, false)
189}
190
191// WalkUp performs a post-order, depth-first traversal of the graph, calling v for each node.
192// Post-order starts by calling the visitor for the leaves then each parent as it traverses up
193// the graph to the root.
194func WalkUp(v Visitor, node SchemaNode) {
195	walk(v, node, true)
196}
197
198func walk(v Visitor, node SchemaNode, up bool) Visitor {
199	if v == nil {
200		return nil
201	}
202
203	if !up {
204		if v = v.Visit(node); v == nil {
205			return nil
206		}
207	}
208
209	switch n := node.(type) {
210	case *Schema:
211		walk(v, n.Measurements, up)
212
213	case Measurements:
214		v := v
215		for i := range n {
216			v = walk(v, &n[i], up)
217		}
218
219	case *Measurement:
220		v := v
221		v = walk(v, n.Tags, up)
222		walk(v, n.Fields, up)
223
224	case Fields:
225		v := v
226		for i := 0; i < len(n); i++ {
227			v = walk(v, &n[i], up)
228		}
229
230	case Tags:
231		v := v
232		for i := 0; i < len(n); i++ {
233			v = walk(v, &n[i], up)
234		}
235
236	case *Tag:
237		walk(v, n.Source, up)
238
239	case *TagArraySource, *TagSequenceSource, *TagFileSource:
240		// nothing to do
241
242	case *Field:
243		walk(v, n.Source, up)
244
245	case *FieldConstantValue, *FieldArraySource, *FieldFloatRandomSource, *FieldIntegerZipfSource:
246		// nothing to do
247
248	default:
249		panic(fmt.Sprintf("schema.Walk: unexpected node type %T", n))
250	}
251
252	if up && v != nil {
253		v = v.Visit(node)
254	}
255
256	return v
257}
258