/
generator.go
110 lines (83 loc) · 2.64 KB
/
generator.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
package jstn
import (
"bytes"
"io"
"sort"
"strings"
)
// indentationString defines the whitespace string to use for indentation when
// generating using the pretty format.
const indentationString = " "
// Generate formats t into a JSTN type declaration using the concise format
// defined by the JSTN specification.
func Generate(t Type) ([]byte, error) {
return (generator{Pretty: false}).generate(t, 0), nil
}
// GeneratePretty formats t into a JSTN type declaration using the pretty
// format defined by the JSTN specification.
func GeneratePretty(t Type) ([]byte, error) {
return (generator{Pretty: true, Indentation: indentationString}).generate(t, 0), nil
}
type generator struct {
Pretty bool // Whether to render in pretty mode.
Indentation string // When in pretty mode, the indentation character to use.
}
// generate formats t into a JSTN document. Because it is a recursive function,
// depth tracks the object hierarchy depth for use when pretty-printing.
func (g generator) generate(t Type, depth int) []byte {
var buf bytes.Buffer
switch t.Kind {
case String:
io.WriteString(&buf, "string") // token: string
case Number:
io.WriteString(&buf, "number") // token: number
case Boolean:
io.WriteString(&buf, "boolean") // token: boolean
case Null:
io.WriteString(&buf, "null") // token: null
case Object:
io.WriteString(&buf, "{") // token: begin-object
// writePretty adds whitespace, but only if the generator is in pretty mode.
writePretty := func(s string) {
if g.Pretty && len(t.Properties) > 0 {
io.WriteString(&buf, s)
}
}
// Sort property names for determinism.
var propertyNames []string
for k := range t.Properties {
propertyNames = append(propertyNames, k)
}
sort.Strings(propertyNames)
writePretty("\n")
for i, k := range propertyNames {
// In pretty mode, indent the property declaration line.
writePretty(strings.Repeat(g.Indentation, depth+1))
// token: name
io.WriteString(&buf, k)
// token: name-separator
io.WriteString(&buf, ":")
writePretty(" ")
// token: member
buf.Write(g.generate(*t.Properties[k], depth+1))
// token: delimiter
writePretty("\n")
if !g.Pretty && i < len(propertyNames)-1 {
io.WriteString(&buf, ";")
}
}
writePretty(strings.Repeat(g.Indentation, depth))
io.WriteString(&buf, "}") // token: end-object
case Array:
io.WriteString(&buf, "[") // token: begin-array
// token: type-declaration
if t.Items != nil {
buf.Write(g.generate(*t.Items, depth))
}
io.WriteString(&buf, "]") // token: end-array
}
if t.Optional {
io.WriteString(&buf, "?") // token: value-optional
}
return buf.Bytes()
}