Skip to content

Commit 72a8130

Browse files
committed
Wrap portType in its own type
This provides much better data locality and removes the need to handle types with suffix request/response.
1 parent 45740f9 commit 72a8130

File tree

7 files changed

+123
-93
lines changed

7 files changed

+123
-93
lines changed

README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,12 @@ import (
3838
)
3939
4040
func main() {
41-
cli := &soap.Client{
41+
cli := soap.Client{
4242
URL: "http://server",
4343
Namespace: hello.Namespace,
4444
}
45-
req := &hello.EchoRequest{Data: "echo"}
46-
reply, err := hello.Echo(cli, req)
45+
conn := hello.NewService(&cli)
46+
reply, err := conn.Echo(cli, &hello.EchoRequest{Data: "echo"})
4747
...
4848
}
4949
```

wsdlgo/encoder.go

+59-59
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ type goEncoder struct {
4343
// types cache
4444
stypes map[string]*wsdl.SimpleType
4545
ctypes map[string]*wsdl.ComplexType
46-
rtypes map[string]string // renamed ctypes old->new
4746

4847
// funcs cache
4948
funcs map[string]*wsdl.Operation
@@ -59,7 +58,7 @@ type goEncoder struct {
5958
needsTimeType bool
6059
needsDateTimeType bool
6160
needsDurationType bool
62-
needsNSTag map[string]string
61+
needsTag map[string]bool
6362
needsStdPkg map[string]bool
6463
needsExtPkg map[string]bool
6564
}
@@ -71,11 +70,10 @@ func NewEncoder(w io.Writer) Encoder {
7170
http: http.DefaultClient,
7271
stypes: make(map[string]*wsdl.SimpleType),
7372
ctypes: make(map[string]*wsdl.ComplexType),
74-
rtypes: make(map[string]string),
7573
funcs: make(map[string]*wsdl.Operation),
7674
messages: make(map[string]*wsdl.Message),
7775
soapOps: make(map[string]*wsdl.BindingOperation),
78-
needsNSTag: make(map[string]string),
76+
needsTag: make(map[string]bool),
7977
needsStdPkg: make(map[string]bool),
8078
needsExtPkg: make(map[string]bool),
8179
}
@@ -133,6 +131,10 @@ func (ge *goEncoder) encode(w io.Writer, d *wsdl.Definitions) error {
133131
pkg = "internal"
134132
}
135133
var b bytes.Buffer
134+
if len(ge.soapOps) > 0 {
135+
// TODO: rpc? meh.
136+
ge.writePortType(&b, d)
137+
}
136138
err = ge.writeGoFuncs(&b, d) // functions first, for clarity
137139
if err != nil {
138140
return err
@@ -242,6 +244,25 @@ func (ge *goEncoder) cacheSOAPOperations(d *wsdl.Definitions) {
242244
}
243245
}
244246

247+
var portTypeT = template.Must(template.New("portType").Parse(`
248+
// {{.}} was auto-generated from WSDL.
249+
type {{.}} struct {
250+
cli *soap.Client
251+
}
252+
253+
// New{{.}} creates an initializes a {{.}}.
254+
func New{{.}}(cli *soap.Client) *{{.}} {
255+
return &{{.}}{cli}
256+
}
257+
`))
258+
259+
func (ge *goEncoder) writePortType(w io.Writer, d *wsdl.Definitions) error {
260+
if d.PortType.Operations == nil || len(d.PortType.Operations) == 0 {
261+
return nil
262+
}
263+
return portTypeT.Execute(w, strings.Title(d.PortType.Name))
264+
}
265+
245266
// writeGoFuncs writes Go function definitions from WSDL types to w.
246267
// Functions are written in the same order of the WSDL document.
247268
func (ge *goEncoder) writeGoFuncs(w io.Writer, d *wsdl.Definitions) error {
@@ -257,7 +278,6 @@ func (ge *goEncoder) writeGoFuncs(w io.Writer, d *wsdl.Definitions) error {
257278
return nil
258279
}
259280
for _, op := range d.PortType.Operations {
260-
// TODO: really rename input to have Request suffix?
261281
ge.writeComments(w, op.Name, op.Doc)
262282
in, err := ge.inputParams(op)
263283
if err != nil {
@@ -274,7 +294,7 @@ func (ge *goEncoder) writeGoFuncs(w io.Writer, d *wsdl.Definitions) error {
274294
ret[i] = ge.wsdl2goDefault(parts[1])
275295
}
276296
}
277-
ok := ge.writeSOAPFunc(w, op, in, out, ret)
297+
ok := ge.writeSOAPFunc(w, d, op, in, out, ret)
278298
if !ok {
279299
ge.needsStdPkg["errors"] = true
280300
ge.needsExtPkg["golang.org/x/net/context"] = true
@@ -291,22 +311,22 @@ func (ge *goEncoder) writeGoFuncs(w io.Writer, d *wsdl.Definitions) error {
291311
return nil
292312
}
293313

294-
var soapFuncT = template.Must(template.New("soapFunc").
295-
Parse(`func {{.Name}}(cli soap.RoundTripper, {{.Input}}) ({{.Output}}) {
314+
var soapFuncT = template.Must(template.New("soapFunc").Parse(
315+
`func (p *{{.PortType}}) {{.Name}}({{.Input}}) ({{.Output}}) {
296316
γ := struct {
297317
XMLName xml.Name ` + "`xml:\"Envelope\"`" + `
298318
Body struct {
299319
M {{.OutputType}} ` + "`xml:\"{{.OutputType}}\"`" + `
300320
}
301321
}{}
302-
if err = cli.RoundTrip(α, &γ); err != nil {
322+
if err = p.cli.RoundTrip(α, &γ); err != nil {
303323
return {{.RetDef}}, err
304324
}
305325
return {{if .RetPtr}}&{{end}}γ.Body.M, nil
306326
}
307327
`))
308328

309-
func (ge *goEncoder) writeSOAPFunc(w io.Writer, op *wsdl.Operation, in, out, ret []string) bool {
329+
func (ge *goEncoder) writeSOAPFunc(w io.Writer, d *wsdl.Definitions, op *wsdl.Operation, in, out, ret []string) bool {
310330
if _, exists := ge.soapOps[op.Name]; !exists {
311331
return false
312332
}
@@ -322,13 +342,15 @@ func (ge *goEncoder) writeSOAPFunc(w io.Writer, op *wsdl.Operation, in, out, ret
322342
ret[0] = "nil"
323343
}
324344
soapFuncT.Execute(w, &struct {
345+
PortType string
325346
Name string
326347
Input string
327348
Output string
328349
OutputType string
329350
RetPtr bool
330351
RetDef string
331352
}{
353+
strings.Title(d.PortType.Name),
332354
strings.Title(op.Name),
333355
strings.Join(in, ","),
334356
strings.Join(out, ","),
@@ -357,7 +379,7 @@ func (ge *goEncoder) inputParams(op *wsdl.Operation) ([]string, error) {
357379
if !ok {
358380
return nil, fmt.Errorf("operation %q wants input message %q but it's not defined", op.Name, im)
359381
}
360-
return ge.genParams(req, "Request"), nil
382+
return ge.genParams(req, true), nil
361383
}
362384

363385
// returns list of function output parameters plus error.
@@ -371,20 +393,23 @@ func (ge *goEncoder) outputParams(op *wsdl.Operation) ([]string, error) {
371393
if !ok {
372394
return nil, fmt.Errorf("operation %q wants output message %q but it's not defined", op.Name, om)
373395
}
374-
return append(ge.genParams(resp, "Response"), out[0]), nil
396+
return append(ge.genParams(resp, false), out[0]), nil
375397
}
376398

377-
func (ge *goEncoder) genParams(m *wsdl.Message, suffix string) []string {
399+
func (ge *goEncoder) genParams(m *wsdl.Message, needsTag bool) []string {
378400
params := make([]string, len(m.Parts))
379401
for i, param := range m.Parts {
380402
var t string
381403
switch {
382404
case param.Type != "":
383-
t = ge.wsdl2goType(param.Type, suffix)
405+
t = ge.wsdl2goType(param.Type)
384406
case param.Element != "":
385-
t = ge.wsdl2goType(param.Element, suffix)
407+
t = ge.wsdl2goType(param.Element)
386408
}
387409
params[i] = param.Name + " " + t
410+
if needsTag {
411+
ge.needsTag[strings.TrimPrefix(t, "*")] = true
412+
}
388413
}
389414
return params
390415
}
@@ -408,13 +433,8 @@ func (ge *goEncoder) fixParamConflicts(req, resp []string) {
408433
}
409434
}
410435

411-
// Converts types from wsdl type to Go type. If t is a complex type
412-
// (a struct, as opposed to int or string) and a suffix is provided,
413-
// we look for the suffix in its name and add if needed. When we do
414-
// that, we also update the list of cached ctypes to match this new
415-
// type name, with the suffix (e.g. ping -> pingRequest). This is
416-
// to avoid ambiguous parameter and function names.
417-
func (ge *goEncoder) wsdl2goType(t, suffix string) string {
436+
// Converts types from wsdl type to Go type.
437+
func (ge *goEncoder) wsdl2goType(t string) string {
418438
// TODO: support other types.
419439
v := ge.trimns(t)
420440
if _, exists := ge.stypes[v]; exists {
@@ -446,28 +466,7 @@ func (ge *goEncoder) wsdl2goType(t, suffix string) string {
446466
ge.needsDurationType = true
447467
return "Duration"
448468
default:
449-
if newname, exists := ge.rtypes[t]; exists {
450-
t = newname
451-
} else if suffix != "" {
452-
// These types are parameters to functions. Since
453-
// they are structs, we set their names to have
454-
// suffixes Request and Response based on how
455-
// they're used.
456-
if strings.HasSuffix(t, suffix) {
457-
t = v
458-
} else {
459-
ge.renameType(t, t+suffix)
460-
t = v + suffix
461-
}
462-
// Request types need a special xml tag to enable
463-
// encoding them correct. Responses cannot have it.
464-
if suffix == "Request" {
465-
ge.needsNSTag[t] = v
466-
}
467-
} else {
468-
t = v
469-
}
470-
return "*" + strings.Title(t)
469+
return "*" + strings.Title(v)
471470
}
472471
}
473472

@@ -510,7 +509,6 @@ func (ge *goEncoder) renameType(old, name string) {
510509
if !exists {
511510
return
512511
}
513-
ge.rtypes[old] = name
514512
name = ge.trimns(name)
515513
}
516514
ct.Name = name
@@ -530,13 +528,13 @@ func (ge *goEncoder) writeGoTypes(w io.Writer, d *wsdl.Definitions) error {
530528
continue
531529
}
532530
ge.writeComments(&b, st.Name, "")
533-
fmt.Fprintf(&b, "type %s %s\n\n", st.Name, ge.wsdl2goType(st.Restriction.Base, ""))
531+
fmt.Fprintf(&b, "type %s %s\n\n", st.Name, ge.wsdl2goType(st.Restriction.Base))
534532
ge.genValidator(&b, st.Name, st.Restriction)
535533
}
536534
var err error
537535
for _, name := range ge.sortedComplexTypes() {
538536
ct := ge.ctypes[name]
539-
err = ge.genGoStruct(&b, ct)
537+
err = ge.genGoStruct(&b, d, ct)
540538
if err != nil {
541539
return err
542540
}
@@ -623,7 +621,7 @@ func (ge *goEncoder) genValidator(w io.Writer, typeName string, r *wsdl.Restrict
623621
return
624622
}
625623
args := make([]string, len(r.Enum))
626-
t := ge.wsdl2goType(r.Base, "")
624+
t := ge.wsdl2goType(r.Base)
627625
for i, v := range r.Enum {
628626
if t == "string" {
629627
args[i] = strconv.Quote(v.Value)
@@ -643,7 +641,7 @@ func (ge *goEncoder) genValidator(w io.Writer, typeName string, r *wsdl.Restrict
643641
})
644642
}
645643

646-
func (ge *goEncoder) genGoStruct(w io.Writer, ct *wsdl.ComplexType) error {
644+
func (ge *goEncoder) genGoStruct(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error {
647645
if ct.Abstract {
648646
return nil
649647
}
@@ -663,36 +661,38 @@ func (ge *goEncoder) genGoStruct(w io.Writer, ct *wsdl.ComplexType) error {
663661
// dont generate empty structs
664662
return nil
665663
}
666-
ge.writeComments(w, ct.Name, ct.Doc)
667-
fmt.Fprintf(w, "type %s struct {\n", strings.Title(ct.Name))
668-
if tag, exists := ge.needsNSTag[ct.Name]; exists {
669-
fmt.Fprintf(w, "XMLName xml.Name `xml:\"ns:%s\" json:\"-\" yaml:\"-\"`\n", tag)
664+
name := strings.Title(ct.Name)
665+
ge.writeComments(w, name, ct.Doc)
666+
fmt.Fprintf(w, "type %s struct {\n", name)
667+
if ge.needsTag[name] {
668+
fmt.Fprintf(w, "XMLName xml.Name `xml:\"%s %s\" json:\"-\" yaml:\"-\"`\n",
669+
d.TargetNamespace, name)
670670
}
671-
err := ge.genStructFields(w, ct)
671+
err := ge.genStructFields(w, d, ct)
672672
if err != nil {
673673
return err
674674
}
675675
fmt.Fprintf(w, "}\n\n")
676676
return nil
677677
}
678678

679-
func (ge *goEncoder) genStructFields(w io.Writer, ct *wsdl.ComplexType) error {
680-
err := ge.genComplexContent(w, ct)
679+
func (ge *goEncoder) genStructFields(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error {
680+
err := ge.genComplexContent(w, d, ct)
681681
if err != nil {
682682
return err
683683
}
684684
return ge.genElements(w, ct)
685685
}
686686

687-
func (ge *goEncoder) genComplexContent(w io.Writer, ct *wsdl.ComplexType) error {
687+
func (ge *goEncoder) genComplexContent(w io.Writer, d *wsdl.Definitions, ct *wsdl.ComplexType) error {
688688
if ct.ComplexContent == nil || ct.ComplexContent.Extension == nil {
689689
return nil
690690
}
691691
ext := ct.ComplexContent.Extension
692692
if ext.Base != "" {
693693
base, exists := ge.ctypes[ge.trimns(ext.Base)]
694694
if exists {
695-
err := ge.genStructFields(w, base)
695+
err := ge.genStructFields(w, d, base)
696696
if err != nil {
697697
return err
698698
}
@@ -746,7 +746,7 @@ func (ge *goEncoder) genElementField(w io.Writer, el *wsdl.Element) {
746746
tag = el.Name + ">" + slicetype
747747
}
748748
}
749-
typ := ge.wsdl2goType(el.Type, "")
749+
typ := ge.wsdl2goType(el.Type)
750750
if el.Nillable || el.Min == 0 {
751751
tag += ",omitempty"
752752
}

wsdlgo/testdata/importer.wsdl

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<definitions name="MemoryService"
2-
targetNamespace="http://localhost:8080/EchoService.wsdl"
2+
targetNamespace="http://localhost:8080/MemoryService.wsdl"
33
xmlns="http://schemas.xmlsoap.org/wsdl/"
44
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
55
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
6-
xmlns:tns="http://localhost:8080/EchoService.wsdl"
6+
xmlns:tns="http://localhost:8080/MemoryService.wsdl"
77

88
<import namespace="http://localhost:9999" location="http://localhost:9999/importer-root.wsdl"></import>
99

0 commit comments

Comments
 (0)