diff --git a/v2alpha/cmd/goupnp2srvgen/main.go b/v2alpha/cmd/goupnp2srvgen/main.go index bd1adc0..3f79555 100644 --- a/v2alpha/cmd/goupnp2srvgen/main.go +++ b/v2alpha/cmd/goupnp2srvgen/main.go @@ -7,6 +7,7 @@ import ( "fmt" "os" "path/filepath" + "reflect" "sort" "strconv" "strings" @@ -17,6 +18,7 @@ import ( "github.com/huin/goupnp/v2alpha/description/srvdesc" "github.com/huin/goupnp/v2alpha/description/typedesc" "github.com/huin/goupnp/v2alpha/description/xmlsrvdesc" + "github.com/huin/goupnp/v2alpha/soap" "github.com/huin/goupnp/v2alpha/soap/types" ) @@ -25,6 +27,8 @@ var ( upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.") ) +const soapActionInterface = "SOAPActionInterface" + func main() { flag.Parse() if err := run(); err != nil { @@ -63,7 +67,10 @@ func run() error { // Use default type map for now. Addtional types could be use instead or // as well as necessary for extended types. - typeMap := types.TypeMap() + typeMap := types.TypeMap().Clone() + typeMap[soapActionInterface] = typedesc.TypeDesc{ + GoType: reflect.TypeOf((*soap.SOAPAction)(nil)).Elem(), + } for _, m := range manifests { if err := processDCP(upnpresources, m, typeMap, tmpl); err != nil { @@ -78,14 +85,14 @@ var manifests = []*DCPSpecManifest{ Path: "standardizeddcps/Internet Gateway_2/UPnP-gw-IGD-TestFiles-20101210.zip", Services: []*ServiceManifest{ { - Package: "lanhostconfigmanagement1", - Type: "urn:schemas-upnp-org:service:LANHostConfigManagement:1", - Path: "xml data files/service/LANHostConfigManagement1.xml", + Package: "lanhostconfigmanagement1", + ServiceType: "urn:schemas-upnp-org:service:LANHostConfigManagement:1", + Path: "xml data files/service/LANHostConfigManagement1.xml", }, { - Package: "wanpppconnection1", - Type: "urn:schemas-upnp-org:service:WANPPPConnection:1", - Path: "xml data files/service/WANPPPConnection1.xml", + Package: "wanpppconnection1", + ServiceType: "urn:schemas-upnp-org:service:WANPPPConnection:1", + Path: "xml data files/service/WANPPPConnection1.xml", }, }, }, @@ -103,7 +110,7 @@ func processDCP( } for _, srvManifest := range manifest.Services { if err := processService(dcpSpecData, srvManifest, typeMap, tmpl); err != nil { - return fmt.Errorf("processing service %s: %w", srvManifest.Type, err) + return fmt.Errorf("processing service %s: %w", srvManifest.ServiceType, err) } } return nil @@ -162,9 +169,9 @@ type DCPSpecManifest struct { type ServiceManifest struct { // Package is the Go package name to generate e.g. "foo1". Package string - // Type is the SOAP namespace and service type that identifes the service e.g. + // ServiceType is the SOAP namespace and service type that identifes the service e.g. // "urn:schemas-upnp-org:service:Foo:1" - Type string + ServiceType string // Path within the DCP spec ZIP file e.g. "xml data files/service/Foo1.xml". Path string } @@ -189,6 +196,8 @@ type importItem struct { func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*imports, error) { typeNames := make(map[string]bool) + typeNames[soapActionInterface] = true + err := visitTypesSCPD(srvDesc, func(typeName string) { typeNames[typeName] = true }) diff --git a/v2alpha/description/typedesc/typedesc.go b/v2alpha/description/typedesc/typedesc.go index 7cc9074..8924041 100644 --- a/v2alpha/description/typedesc/typedesc.go +++ b/v2alpha/description/typedesc/typedesc.go @@ -7,3 +7,11 @@ type TypeDesc struct { } type TypeMap map[string]TypeDesc + +func (tm TypeMap) Clone() TypeMap { + r := make(TypeMap, len(tm)) + for k, v := range tm { + r[k] = v + } + return r +} diff --git a/v2alpha/soap/soap.go b/v2alpha/soap/soap.go new file mode 100644 index 0000000..b7f2311 --- /dev/null +++ b/v2alpha/soap/soap.go @@ -0,0 +1,15 @@ +// Package soap defines basic types used by SOAP packages. +package soap + +// SOAPAction defines the interface for the convenience self-describing action request/response +// struct types. +type SOAPAction interface { + // ServiceType returns Service type, e.g. "urn:schemas-upnp-org:service:Foo:1". + ServiceType() string + // ActionName returns Action name, e.g. "SetBar". + ActionName() string + // RefRequest returns reference to the action request member. + RefRequest() any + // RefResponse returns reference to the action response member. + RefResponse() any +} diff --git a/v2alpha/srv/srv.gotemplate b/v2alpha/srv/srv.gotemplate index 7118ce0..a83e617 100644 --- a/v2alpha/srv/srv.gotemplate +++ b/v2alpha/srv/srv.gotemplate @@ -7,6 +7,9 @@ import ( {{.Alias}} {{quote .Path}} {{- end}} ) + +const ServiceType = {{quote .Manifest.ServiceType}} + {{range .SCPD.SortedActions}} {{- template "action" args "Action" . "Imps" $Imps}} {{end}} @@ -14,6 +17,18 @@ import ( {{define "action"}} {{- $Imps := .Imps}} +type {{.Action.Name}} struct{ + Request {{.Action.Name}}Request + Response {{.Action.Name}}Response +} + +var _ {{index $Imps.TypeRefByTypeName "SOAPActionInterface"}} = &{{.Action.Name}}{{"{}"}} + +func (a *{{.Action.Name}}) ServiceType() string { return ServiceType } +func (a *{{.Action.Name}}) ActionName() string { return {{quote .Action.Name}} } +func (a *{{.Action.Name}}) RefRequest() any { return &a.Request } +func (a *{{.Action.Name}}) RefResponse() any { return &a.Response } + type {{.Action.Name}}Request struct {{- template "args" args "Args" .Action.InArgs "Imps" $Imps}}