forked from droyo/go-xml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcli.go
156 lines (146 loc) · 4.48 KB
/
cli.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
155
156
package xsdgen
import (
"errors"
"flag"
"fmt"
"go/ast"
"io/ioutil"
"strings"
"aqwari.net/xml/internal/commandline"
"aqwari.net/xml/internal/gen"
"aqwari.net/xml/xsd"
)
// GenCode reads all xml schema definitions from the provided
// data. If succesful, the returned *Code value can be used to
// lookup identifiers and generate Go code.
func (cfg *Config) GenCode(data ...[]byte) (*Code, error) {
if len(cfg.namespaces) == 0 {
cfg.Option(Namespaces(lookupTargetNS(data...)...))
cfg.debugf("setting namespaces to %q", cfg.namespaces)
}
deps, err := xsd.Parse(data...)
if err != nil {
return nil, err
}
primaries := make([]xsd.Schema, 0, len(cfg.namespaces))
for _, s := range deps {
for _, ns := range cfg.namespaces {
if s.TargetNS == ns {
primaries = append(primaries, s)
break
}
}
}
if len(primaries) < len(cfg.namespaces) {
missing := make([]string, 0, len(cfg.namespaces)-len(primaries))
have := make(map[string]bool)
for _, schema := range primaries {
have[schema.TargetNS] = true
}
for _, ns := range cfg.namespaces {
if !have[ns] {
missing = append(missing, ns)
}
}
return nil, fmt.Errorf("could not find schema for %q", strings.Join(missing, ", "))
}
cfg.addStandardHelpers()
return cfg.gen(primaries, deps)
}
// GenAST creates an *ast.File containing type declarations and
// associated methods based on a set of XML schema.
func (cfg *Config) GenAST(files ...string) (*ast.File, error) {
data, err := cfg.readFiles(files...)
code, err := cfg.GenCode(data...)
if err != nil {
return nil, err
}
return code.GenAST()
}
func (cfg *Config) readFiles(files ...string) ([][]byte,error) {
data := make([][]byte, 0, len(files))
for _, filename := range files {
b, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
cfg.debugf("read %s", filename)
if cfg.followImports {
importedRefs, err := xsd.Imports(b)
if err != nil {
return nil, fmt.Errorf("error discovering imports: %v", err)
}
importedFiles := make([]string, 0, len(importedRefs))
for _, r := range importedRefs {
importedFiles = append(importedFiles, r.Location)
}
referencedData, err := cfg.readFiles(importedFiles...)
if err != nil {
return nil, fmt.Errorf("error reading imported files: %v", err)
}
for _, d := range referencedData {
// prepend imported refs (i.e. append before the referencing file)
data = append(data, d)
}
}
data = append(data, b)
}
return data, nil
}
// The GenSource method converts the AST returned by GenAST to formatted
// Go source code.
func (cfg *Config) GenSource(files ...string) ([]byte, error) {
file, err := cfg.GenAST(files...)
if err != nil {
return nil, err
}
return gen.FormattedSource(file, "fixme.go")
}
// GenCLI creates a file containing Go source generated from an XML
// Schema. Main is meant to be called as part of a command, and can
// be used to change the behavior of the xsdgen command in ways that
// its command-line arguments do not allow. The arguments are the
// same as those passed to the xsdgen command.
func (cfg *Config) GenCLI(arguments ...string) error {
var (
err error
replaceRules commandline.ReplaceRuleList
xmlns commandline.Strings
fs = flag.NewFlagSet("xsdgen", flag.ExitOnError)
packageName = fs.String("pkg", "", "name of the the generated package")
output = fs.String("o", "xsdgen_output.go", "name of the output file")
followImports = fs.Bool("f", false, "follow import statements; load imported references recursively into scope")
verbose = fs.Bool("v", false, "print verbose output")
debug = fs.Bool("vv", false, "print debug output")
)
fs.Var(&replaceRules, "r", "replacement rule 'regex -> repl' (can be used multiple times)")
fs.Var(&xmlns, "ns", "target namespace(s) to generate types for")
if err = fs.Parse(arguments); err != nil {
return err
}
if fs.NArg() == 0 {
return errors.New("Usage: xsdgen [-ns xmlns] [-r rule] [-o file] [-pkg pkg] file ...")
}
if *debug {
cfg.Option(LogLevel(5))
} else if *verbose {
cfg.Option(LogLevel(1))
}
cfg.Option(Namespaces(xmlns...))
cfg.Option(FollowImports(*followImports))
for _, r := range replaceRules {
cfg.Option(replaceAllNamesRegex(r.From, r.To))
}
if *packageName != "" {
cfg.Option(PackageName(*packageName))
}
file, err := cfg.GenAST(fs.Args()...)
if err != nil {
return err
}
data, err := gen.FormattedSource(file, *output)
if err != nil {
return err
}
return ioutil.WriteFile(*output, data, 0666)
}