-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprinter.go
More file actions
184 lines (143 loc) · 4.06 KB
/
printer.go
File metadata and controls
184 lines (143 loc) · 4.06 KB
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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package goty
import (
"errors"
"fmt"
"io"
"os"
"strings"
)
// Header is printed before anything else.
const Header = `/* Auto-generated. DO NOT EDIT. Generator: https://golift.io/goty
* Edit the source code and run goty again to make updates.` + "\n */\n\n"
// ErrNoStructs is returned if no structs are found to print or write.
var ErrNoStructs = errors.New("no structs to write, run Parse() first")
// Print prints all the structs in the output to stdout as typescript interfaces.
func (g *Goty) Print() {
g.print(os.Stdout)
}
// Write writes all the structs in the output to a file as typescript interfaces.
// If the file exists and overwrite is false, returns an error.
func (g *Goty) Write(fileName string, overwrite bool) error {
if len(g.output) == 0 {
return ErrNoStructs
}
_, err := os.Stat(fileName)
if !os.IsNotExist(err) && !overwrite {
return fmt.Errorf("file exists: %s: %w", fileName, os.ErrExist)
}
file, err := os.Create(fileName) //nolint:gosec // user chooses their own demise.
if err != nil {
return fmt.Errorf("failed to create file: %w", err)
}
defer file.Close()
g.print(file)
return nil
}
// Print a struct as a typescript interface to an io.Writer.
func (s *DataStruct) Print(indent string, output io.Writer) {
golangRef := "\n * @see golang: <" + s.GoName + ">"
doc := formatDocs(false, indent, s.doc.Type(s.Type), s.ovr.Comment)
fmt.Fprintln(output, `/**`+doc+golangRef+"\n"+` */`)
if len(s.Elements) > 0 {
s.printElements(indent, output)
return
}
exported := "export "
if s.ovr.NoExport {
exported = ""
}
if len(s.Extends) > 0 {
fmt.Fprintf(output, indent+exported+`interface %s extends %s {`,
s.Name, strings.Join(s.Extends, `, `))
} else {
fmt.Fprint(output, indent+exported+`interface `+s.Name+` {`)
}
if len(s.Members) > 0 {
fmt.Fprintln(output)
}
for _, m := range s.Members {
m.Print(indent+` `, output)
}
fmt.Fprintln(output, indent+"};\n")
}
// Print prints a struct member as a typescript interface member.
func (m *StructMember) Print(indent string, output io.Writer) {
optional := ""
if m.Optional {
optional = "?"
}
doc := formatDocs(true, indent, m.doc.Member(m.parent.Type, m.Member.Name), m.ovr.Comment)
extends := ""
if len(m.Extends) > 0 {
extends = strings.Join(m.Extends, ` & `) + ` & `
}
if m.Members == nil {
fmt.Fprintln(output, doc+indent+m.Name+optional+`: `+extends+m.Type+`;`)
return
}
if optional = ""; m.Optional {
optional = "null | "
}
fmt.Fprintln(output, indent+m.Name+`: `+optional+extends+`{`)
for _, m := range m.Members {
m.Print(indent+` `, output)
}
fmt.Fprintln(output, indent+`};`)
}
func (g *Goty) print(output io.Writer) {
if len(g.output) == 0 {
panic(ErrNoStructs)
}
fmt.Fprint(output, Header)
for _, s := range g.output {
s.Print("", output)
}
if len(g.pkgPaths) < 1 {
return
}
fmt.Fprintln(output, "// Packages parsed:")
for idx, pkg := range g.Pkgs() {
fmt.Fprintf(output, "// %3d. %s\n", idx+1, pkg)
}
}
// formatDocs formats the documentation for an interface and an interface member.
// It wraps the documentation in JSDoc format if wrap is true.
func formatDocs(wrap bool, indent, doc string, extra ...string) string {
for _, e := range extra {
if e != "" {
doc += strings.Trim(e, "\n")
}
}
if doc == "" {
return ""
}
output := ""
if wrap {
output = indent + "/**\n"
}
// sorry. :( it tries to make pretty JSDoc.
output += strings.ReplaceAll(indent+" * "+doc, "\n", "\n "+indent+"* ")
if wrap {
return output + "\n" + indent + " */\n"
}
return "\n" + output
}
func (s *DataStruct) printElements(indent string, output io.Writer) {
longest := 0
for _, v := range s.Elements {
if len(v.Name) > longest {
longest = len(v.Name)
}
}
exported := "export "
if s.ovr.NoExport {
exported = ""
}
fmt.Fprintln(output, indent+exported+`enum `+s.Name+` {`)
// We use the formatter to align the enum values visually.
formatter := fmt.Sprintf("%s %%-%ds = %%s,\n", indent, longest)
for _, v := range s.Elements {
fmt.Fprintf(output, formatter, v.Name, v.Value)
}
fmt.Fprintln(output, indent+"};\n")
}