Initial code to generate service code.
This commit is contained in:
parent
0e8fff04df
commit
e3e16da35f
@ -6,14 +6,22 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/huin/goupnp/v2alpha/cmd/goupnp2srvgen/tmplfuncs"
|
||||||
"github.com/huin/goupnp/v2alpha/cmd/goupnp2srvgen/zipread"
|
"github.com/huin/goupnp/v2alpha/cmd/goupnp2srvgen/zipread"
|
||||||
"github.com/huin/goupnp/v2alpha/description/srvdesc"
|
"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/description/xmlsrvdesc"
|
||||||
|
"github.com/huin/goupnp/v2alpha/soap/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
srvTemplate = flag.String("srv_template", "", "Path to srv.gotemplate.")
|
||||||
upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.")
|
upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -29,6 +37,17 @@ func run() error {
|
|||||||
if len(flag.Args()) > 0 {
|
if len(flag.Args()) > 0 {
|
||||||
return fmt.Errorf("unused arguments: %s", strings.Join(flag.Args(), " "))
|
return fmt.Errorf("unused arguments: %s", strings.Join(flag.Args(), " "))
|
||||||
}
|
}
|
||||||
|
if *srvTemplate == "" {
|
||||||
|
return errors.New("-srv_template is a required flag.")
|
||||||
|
}
|
||||||
|
tmpl, err := template.New(filepath.Base(*srvTemplate)).Funcs(template.FuncMap{
|
||||||
|
"args": tmplfuncs.Args,
|
||||||
|
"quote": strconv.Quote,
|
||||||
|
}).ParseFiles(*srvTemplate)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading srv_template %q: %w", *srvTemplate, err)
|
||||||
|
}
|
||||||
|
|
||||||
if *upnpresourcesZip == "" {
|
if *upnpresourcesZip == "" {
|
||||||
return errors.New("-upnpresources_zip is a required flag.")
|
return errors.New("-upnpresources_zip is a required flag.")
|
||||||
}
|
}
|
||||||
@ -41,8 +60,13 @@ func run() error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use default type map for now. Addtional types could be use instead or
|
||||||
|
// as well as necessary for extended types.
|
||||||
|
typeMap := types.TypeMap()
|
||||||
|
|
||||||
for _, m := range manifests {
|
for _, m := range manifests {
|
||||||
if err := processDCP(upnpresources, m); err != nil {
|
if err := processDCP(upnpresources, m, typeMap, tmpl); err != nil {
|
||||||
return fmt.Errorf("processing DCP %s: %w", m.Path, err)
|
return fmt.Errorf("processing DCP %s: %w", m.Path, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -52,9 +76,17 @@ func run() error {
|
|||||||
var manifests = []*DCPSpecManifest{
|
var manifests = []*DCPSpecManifest{
|
||||||
{
|
{
|
||||||
Path: "standardizeddcps/Internet Gateway_2/UPnP-gw-IGD-TestFiles-20101210.zip",
|
Path: "standardizeddcps/Internet Gateway_2/UPnP-gw-IGD-TestFiles-20101210.zip",
|
||||||
Services: map[string]string{
|
Services: []*ServiceManifest{
|
||||||
"LANHostConfigManagement:1": "xml data files/service/LANHostConfigManagement1.xml",
|
{
|
||||||
"WANPPPConnection:1": "xml data files/service/WANPPPConnection1.xml",
|
Package: "lanhostconfigmanagement1",
|
||||||
|
Type: "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",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -62,14 +94,16 @@ var manifests = []*DCPSpecManifest{
|
|||||||
func processDCP(
|
func processDCP(
|
||||||
upnpresources *zipread.ZipRead,
|
upnpresources *zipread.ZipRead,
|
||||||
manifest *DCPSpecManifest,
|
manifest *DCPSpecManifest,
|
||||||
|
typeMap typedesc.TypeMap,
|
||||||
|
tmpl *template.Template,
|
||||||
) error {
|
) error {
|
||||||
dcpSpecData, err := upnpresources.OpenZip(manifest.Path)
|
dcpSpecData, err := upnpresources.OpenZip(manifest.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for name, path := range manifest.Services {
|
for _, srvManifest := range manifest.Services {
|
||||||
if err := processService(dcpSpecData, name, path); err != nil {
|
if err := processService(dcpSpecData, srvManifest, typeMap, tmpl); err != nil {
|
||||||
return fmt.Errorf("processing service %s: %w", name, err)
|
return fmt.Errorf("processing service %s: %w", srvManifest.Type, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -77,11 +111,11 @@ func processDCP(
|
|||||||
|
|
||||||
func processService(
|
func processService(
|
||||||
dcpSpecData *zipread.ZipRead,
|
dcpSpecData *zipread.ZipRead,
|
||||||
name string,
|
srvManifest *ServiceManifest,
|
||||||
path string,
|
typeMap typedesc.TypeMap,
|
||||||
|
tmpl *template.Template,
|
||||||
) error {
|
) error {
|
||||||
fmt.Printf("%s\n", name)
|
f, err := dcpSpecData.Open(srvManifest.Path)
|
||||||
f, err := dcpSpecData.Open(path)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -108,10 +142,24 @@ func processService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = srvdesc.FromXML(xmlSCPD)
|
sd, err := srvdesc.FromXML(xmlSCPD)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("transforming service description: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
imps, err := accumulateImports(sd, typeMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = tmpl.ExecuteTemplate(os.Stdout, "service", tmplArgs{
|
||||||
|
Imps: imps,
|
||||||
|
SCPD: sd,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("executing srv_template: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,5 +168,128 @@ type DCPSpecManifest struct {
|
|||||||
Path string
|
Path string
|
||||||
// Services maps from a service name (e.g. "FooBar:1") to a path within the DCP spec ZIP file
|
// Services maps from a service name (e.g. "FooBar:1") to a path within the DCP spec ZIP file
|
||||||
// (e.g. "xml data files/service/FooBar1.xml").
|
// (e.g. "xml data files/service/FooBar1.xml").
|
||||||
Services map[string]string
|
Services []*ServiceManifest
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
// "urn:schemas-upnp-org:service:Foo:1"
|
||||||
|
Type string
|
||||||
|
// Path within the DCP spec ZIP file e.g. "xml data files/service/Foo1.xml".
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
type tmplArgs struct {
|
||||||
|
Imps *imports
|
||||||
|
SCPD *srvdesc.SCPD
|
||||||
|
}
|
||||||
|
|
||||||
|
type imports struct {
|
||||||
|
// Maps from a type name like "ui4" to the `alias.name` for the import.
|
||||||
|
TypeRefByTypeName map[string]string
|
||||||
|
// Each required import line, ordered by path.
|
||||||
|
ImportLines []importItem
|
||||||
|
}
|
||||||
|
|
||||||
|
type importItem struct {
|
||||||
|
Alias string
|
||||||
|
Path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func accumulateImports(srvDesc *srvdesc.SCPD, typeMap typedesc.TypeMap) (*imports, error) {
|
||||||
|
typeNames := make(map[string]bool)
|
||||||
|
err := visitTypesSCPD(srvDesc, func(typeName string) {
|
||||||
|
typeNames[typeName] = true
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have sorted list of import package paths. Partly for aesthetics of generated code, but also
|
||||||
|
// to have stable-generated aliases.
|
||||||
|
paths := make(map[string]bool)
|
||||||
|
for typeName := range typeNames {
|
||||||
|
t, ok := typeMap[typeName]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unknown type %q", typeName)
|
||||||
|
}
|
||||||
|
pkgPath := t.GoType.PkgPath()
|
||||||
|
if pkgPath == "" {
|
||||||
|
// Builtin type, ignore.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
paths[pkgPath] = true
|
||||||
|
}
|
||||||
|
sortedPaths := make([]string, 0, len(paths))
|
||||||
|
for path := range paths {
|
||||||
|
sortedPaths = append(sortedPaths, path)
|
||||||
|
}
|
||||||
|
sort.Strings(sortedPaths)
|
||||||
|
|
||||||
|
// Generate import aliases.
|
||||||
|
index := 1
|
||||||
|
aliasByPath := make(map[string]string, len(paths))
|
||||||
|
importLines := make([]importItem, 0, len(paths))
|
||||||
|
for _, path := range sortedPaths {
|
||||||
|
alias := fmt.Sprintf("pkg%d", index)
|
||||||
|
index++
|
||||||
|
importLines = append(importLines, importItem{
|
||||||
|
Alias: alias,
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
aliasByPath[path] = alias
|
||||||
|
}
|
||||||
|
|
||||||
|
// Populate typeRefByTypeName.
|
||||||
|
typeRefByTypeName := make(map[string]string, len(typeNames))
|
||||||
|
for typeName := range typeNames {
|
||||||
|
goType := typeMap[typeName]
|
||||||
|
pkgPath := goType.GoType.PkgPath()
|
||||||
|
alias := aliasByPath[pkgPath]
|
||||||
|
if alias == "" {
|
||||||
|
// Builtin type.
|
||||||
|
typeRefByTypeName[typeName] = goType.GoType.Name()
|
||||||
|
} else {
|
||||||
|
typeRefByTypeName[typeName] = fmt.Sprintf(
|
||||||
|
"%s.%s", alias, goType.GoType.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &imports{
|
||||||
|
TypeRefByTypeName: typeRefByTypeName,
|
||||||
|
ImportLines: importLines,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type typeVisitor func(typeName string)
|
||||||
|
|
||||||
|
// visitTypesSCPD calls `visitor` with each data type name (e.g. "ui4") referenced
|
||||||
|
// by action arguments.`
|
||||||
|
func visitTypesSCPD(scpd *srvdesc.SCPD, visitor typeVisitor) error {
|
||||||
|
for _, action := range scpd.ActionByName {
|
||||||
|
if err := visitTypesAction(action, visitor); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func visitTypesAction(action *srvdesc.Action, visitor typeVisitor) error {
|
||||||
|
for _, arg := range action.InArgs {
|
||||||
|
sv, err := arg.RelatedStateVariable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
visitor(sv.DataType)
|
||||||
|
}
|
||||||
|
for _, arg := range action.OutArgs {
|
||||||
|
sv, err := arg.RelatedStateVariable()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
visitor(sv.DataType)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
30
v2alpha/cmd/goupnp2srvgen/tmplfuncs/tmplfuncs.go
Normal file
30
v2alpha/cmd/goupnp2srvgen/tmplfuncs/tmplfuncs.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
// Package tmplfuncs contains functions for injection into templates.
|
||||||
|
package tmplfuncs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Args accepts pairs of string names and any values and constructs a map from them.
|
||||||
|
// This is to help passing multiple arguments to a template from within a template.
|
||||||
|
func Args(args ...any) (map[string]any, error) {
|
||||||
|
if len(args)%2 != 0 {
|
||||||
|
return nil, errors.New("args must have an even number of arguments")
|
||||||
|
}
|
||||||
|
res := make(map[string]any, len(args)/2)
|
||||||
|
|
||||||
|
for i := 0; i < len(args); i += 2 {
|
||||||
|
name, ok := args[i].(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("argument %d: want string, got %T", i, args[i])
|
||||||
|
}
|
||||||
|
value := args[i+1]
|
||||||
|
if _, exists := res[name]; exists {
|
||||||
|
return nil, fmt.Errorf("argument name %q occurs more than once", name)
|
||||||
|
}
|
||||||
|
res[name] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
@ -11,85 +11,84 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
BadDescriptionError = errors.New("bad XML description")
|
BadDescriptionError = errors.New("bad XML description")
|
||||||
|
MissingDefinitionError = errors.New("missing definition")
|
||||||
UnsupportedDescriptionError = errors.New("unsupported XML description")
|
UnsupportedDescriptionError = errors.New("unsupported XML description")
|
||||||
)
|
)
|
||||||
|
|
||||||
// SCPD is the top level service description.
|
// SCPD is the top level service description.
|
||||||
type SCPD struct {
|
type SCPD struct {
|
||||||
actionByName map[string]*Action
|
ActionByName map[string]*Action
|
||||||
variableByName map[string]*StateVariable
|
VariableByName map[string]*StateVariable
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromXML creates an SCPD from XML data.
|
// FromXML creates an SCPD from XML data.
|
||||||
//
|
//
|
||||||
// It assumes that xmlDesc.Clean() has been called.
|
// It assumes that xmlDesc.Clean() has been called.
|
||||||
func FromXML(xmlDesc *xmlsrvdesc.SCPD) (*SCPD, error) {
|
func FromXML(xmlDesc *xmlsrvdesc.SCPD) (*SCPD, error) {
|
||||||
stateVariables := make(map[string]*StateVariable, len(xmlDesc.StateVariables))
|
scpd := &SCPD{
|
||||||
|
ActionByName: make(map[string]*Action, len(xmlDesc.Actions)),
|
||||||
|
VariableByName: make(map[string]*StateVariable, len(xmlDesc.StateVariables)),
|
||||||
|
}
|
||||||
|
stateVariables := scpd.VariableByName
|
||||||
for _, xmlSV := range xmlDesc.StateVariables {
|
for _, xmlSV := range xmlDesc.StateVariables {
|
||||||
sv, err := stateVariableFromXML(xmlSV)
|
sv, err := stateVariableFromXML(xmlSV)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("processing state variable %q: %w", xmlSV.Name, err)
|
return nil, fmt.Errorf("processing state variable %q: %w", xmlSV.Name, err)
|
||||||
}
|
}
|
||||||
if _, exists := stateVariables[sv.name]; exists {
|
if _, exists := stateVariables[sv.Name]; exists {
|
||||||
return nil, fmt.Errorf("%w: multiple state variables with name %q",
|
return nil, fmt.Errorf("%w: multiple state variables with name %q",
|
||||||
BadDescriptionError, sv.name)
|
BadDescriptionError, sv.Name)
|
||||||
}
|
}
|
||||||
stateVariables[sv.name] = sv
|
stateVariables[sv.Name] = sv
|
||||||
}
|
}
|
||||||
actions := make(map[string]*Action, len(xmlDesc.Actions))
|
actions := scpd.ActionByName
|
||||||
for _, xmlAction := range xmlDesc.Actions {
|
for _, xmlAction := range xmlDesc.Actions {
|
||||||
action, err := actionFromXML(xmlAction)
|
action, err := actionFromXML(xmlAction, scpd)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("processing action %q: %w", xmlAction.Name, err)
|
return nil, fmt.Errorf("processing action %q: %w", xmlAction.Name, err)
|
||||||
}
|
}
|
||||||
if _, exists := actions[action.name]; exists {
|
if _, exists := actions[action.Name]; exists {
|
||||||
return nil, fmt.Errorf("%w: multiple actions with name %q",
|
return nil, fmt.Errorf("%w: multiple actions with name %q",
|
||||||
BadDescriptionError, action.name)
|
BadDescriptionError, action.Name)
|
||||||
}
|
}
|
||||||
actions[action.name] = action
|
actions[action.Name] = action
|
||||||
}
|
}
|
||||||
return &SCPD{
|
return scpd, nil
|
||||||
actionByName: actions,
|
|
||||||
variableByName: stateVariables,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ActionNames returns the ordered names of each action.
|
// SortedActions returns the actions, in order of name.
|
||||||
func (scpd *SCPD) ActionNames() []string {
|
func (scpd *SCPD) SortedActions() []*Action {
|
||||||
names := make([]string, 0, len(scpd.actionByName))
|
actions := make([]*Action, 0, len(scpd.ActionByName))
|
||||||
for name := range scpd.actionByName {
|
for _, a := range scpd.ActionByName {
|
||||||
names = append(names, name)
|
actions = append(actions, a)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Slice(actions, func(i, j int) bool {
|
||||||
return names
|
return actions[i].Name < actions[j].Name
|
||||||
}
|
})
|
||||||
|
return actions
|
||||||
// Action returns an action with the given name.
|
|
||||||
func (scpd *SCPD) Action(name string) *Action {
|
|
||||||
return scpd.actionByName[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Variable returns a state variable with the given name.
|
|
||||||
func (scpd *SCPD) Variable(name string) *StateVariable {
|
|
||||||
return scpd.variableByName[name]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Action describes a single UPnP SOAP action.
|
// Action describes a single UPnP SOAP action.
|
||||||
type Action struct {
|
type Action struct {
|
||||||
name string
|
SCPD *SCPD
|
||||||
inArgs []*Argument
|
Name string
|
||||||
outArgs []*Argument
|
InArgs []*Argument
|
||||||
|
OutArgs []*Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
// actionFromXML creates an Action from the given XML description.
|
// actionFromXML creates an Action from the given XML description.
|
||||||
func actionFromXML(xmlAction *xmlsrvdesc.Action) (*Action, error) {
|
func actionFromXML(xmlAction *xmlsrvdesc.Action, scpd *SCPD) (*Action, error) {
|
||||||
if xmlAction.Name == "" {
|
if xmlAction.Name == "" {
|
||||||
return nil, fmt.Errorf("%w: empty action name", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty action name", BadDescriptionError)
|
||||||
}
|
}
|
||||||
|
action := &Action{
|
||||||
|
SCPD: scpd,
|
||||||
|
Name: xmlAction.Name,
|
||||||
|
}
|
||||||
var inArgs []*Argument
|
var inArgs []*Argument
|
||||||
var outArgs []*Argument
|
var outArgs []*Argument
|
||||||
for _, xmlArg := range xmlAction.Arguments {
|
for _, xmlArg := range xmlAction.Arguments {
|
||||||
arg, err := argumentFromXML(xmlArg)
|
arg, err := argumentFromXML(xmlArg, action)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("processing argument %q: %w", xmlArg.Name, err)
|
return nil, fmt.Errorf("processing argument %q: %w", xmlArg.Name, err)
|
||||||
}
|
}
|
||||||
@ -103,21 +102,20 @@ func actionFromXML(xmlAction *xmlsrvdesc.Action) (*Action, error) {
|
|||||||
BadDescriptionError, xmlArg.Name, xmlArg.Direction)
|
BadDescriptionError, xmlArg.Name, xmlArg.Direction)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &Action{
|
action.InArgs = inArgs
|
||||||
name: xmlAction.Name,
|
action.OutArgs = outArgs
|
||||||
inArgs: inArgs,
|
return action, nil
|
||||||
outArgs: outArgs,
|
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Argument description data.
|
// Argument description data.
|
||||||
type Argument struct {
|
type Argument struct {
|
||||||
name string
|
Action *Action
|
||||||
relatedStateVariable string
|
Name string
|
||||||
|
RelatedStateVariableName string
|
||||||
}
|
}
|
||||||
|
|
||||||
// argumentFromXML creates an Argument from the XML description.
|
// argumentFromXML creates an Argument from the XML description.
|
||||||
func argumentFromXML(xmlArg *xmlsrvdesc.Argument) (*Argument, error) {
|
func argumentFromXML(xmlArg *xmlsrvdesc.Argument, action *Action) (*Argument, error) {
|
||||||
if xmlArg.Name == "" {
|
if xmlArg.Name == "" {
|
||||||
return nil, fmt.Errorf("%w: empty argument name", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty argument name", BadDescriptionError)
|
||||||
}
|
}
|
||||||
@ -125,23 +123,23 @@ func argumentFromXML(xmlArg *xmlsrvdesc.Argument) (*Argument, error) {
|
|||||||
return nil, fmt.Errorf("%w: empty related state variable", BadDescriptionError)
|
return nil, fmt.Errorf("%w: empty related state variable", BadDescriptionError)
|
||||||
}
|
}
|
||||||
return &Argument{
|
return &Argument{
|
||||||
name: xmlArg.Name,
|
Action: action,
|
||||||
relatedStateVariable: xmlArg.RelatedStateVariable,
|
Name: xmlArg.Name,
|
||||||
|
RelatedStateVariableName: xmlArg.RelatedStateVariable,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (arg *Argument) Name() string {
|
func (arg *Argument) RelatedStateVariable() (*StateVariable, error) {
|
||||||
return arg.name
|
if v, ok := arg.Action.SCPD.VariableByName[arg.RelatedStateVariableName]; ok {
|
||||||
}
|
return v, nil
|
||||||
|
}
|
||||||
func (arg *Argument) RelatedStateVariableName() string {
|
return nil, fmt.Errorf("%w: state variable %q", MissingDefinitionError, arg.RelatedStateVariableName)
|
||||||
return arg.relatedStateVariable
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// StateVariable description data.
|
// StateVariable description data.
|
||||||
type StateVariable struct {
|
type StateVariable struct {
|
||||||
name string
|
Name string
|
||||||
dataType string
|
DataType string
|
||||||
}
|
}
|
||||||
|
|
||||||
func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, error) {
|
func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, error) {
|
||||||
@ -153,15 +151,7 @@ func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, erro
|
|||||||
UnsupportedDescriptionError, xmlSV.DataType.Type)
|
UnsupportedDescriptionError, xmlSV.DataType.Type)
|
||||||
}
|
}
|
||||||
return &StateVariable{
|
return &StateVariable{
|
||||||
name: xmlSV.Name,
|
Name: xmlSV.Name,
|
||||||
dataType: xmlSV.DataType.Name,
|
DataType: xmlSV.DataType.Name,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *StateVariable) Name() string {
|
|
||||||
return sv.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sv *StateVariable) DataTypeName() string {
|
|
||||||
return sv.dataType
|
|
||||||
}
|
|
||||||
|
9
v2alpha/description/typedesc/typedesc.go
Normal file
9
v2alpha/description/typedesc/typedesc.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package typedesc
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
type TypeDesc struct {
|
||||||
|
GoType reflect.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeMap map[string]TypeDesc
|
@ -14,12 +14,47 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
|
"github.com/huin/goupnp/v2alpha/description/typedesc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TypeMap returns the builtin type map description.
|
||||||
|
func TypeMap() typedesc.TypeMap {
|
||||||
|
return typedesc.TypeMap{
|
||||||
|
"ui1": {GoType: reflect.TypeOf(UI1(0))},
|
||||||
|
"ui2": {GoType: reflect.TypeOf(UI2(0))},
|
||||||
|
"ui4": {GoType: reflect.TypeOf(UI4(0))},
|
||||||
|
"ui8": {GoType: reflect.TypeOf(UI8(0))},
|
||||||
|
"i1": {GoType: reflect.TypeOf(I1(0))},
|
||||||
|
"i2": {GoType: reflect.TypeOf(I2(0))},
|
||||||
|
"i4": {GoType: reflect.TypeOf(I4(0))},
|
||||||
|
"i8": {GoType: reflect.TypeOf(I8(0))},
|
||||||
|
"int": {GoType: reflect.TypeOf(I8(0))},
|
||||||
|
"r4": {GoType: reflect.TypeOf(R4(0))},
|
||||||
|
"r8": {GoType: reflect.TypeOf(R8(0))},
|
||||||
|
"number": {GoType: reflect.TypeOf(R8(0))},
|
||||||
|
"fixed.14.4": {GoType: reflect.TypeOf(Fixed14_4{})},
|
||||||
|
"float": {GoType: reflect.TypeOf(R8(0))},
|
||||||
|
"char": {GoType: reflect.TypeOf(Char(' '))},
|
||||||
|
"string": {GoType: reflect.TypeOf("")},
|
||||||
|
"date": {GoType: reflect.TypeOf(Date{})},
|
||||||
|
"dateTime": {GoType: reflect.TypeOf(DateTime{})},
|
||||||
|
"dateTime.tz": {GoType: reflect.TypeOf(DateTimeTZ{})},
|
||||||
|
"time": {GoType: reflect.TypeOf(TimeOfDay{})},
|
||||||
|
"time.tz": {GoType: reflect.TypeOf(TimeOfDayTZ{})},
|
||||||
|
"boolean": {GoType: reflect.TypeOf(Boolean(false))},
|
||||||
|
"bin.base64": {GoType: reflect.TypeOf(BinBase64{})},
|
||||||
|
"bin.hex": {GoType: reflect.TypeOf(BinHex{})},
|
||||||
|
"uri": {GoType: reflect.TypeOf(URI{})},
|
||||||
|
"uuid": {GoType: reflect.TypeOf("")},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type SOAPValue interface {
|
type SOAPValue interface {
|
||||||
encoding.TextMarshaler
|
encoding.TextMarshaler
|
||||||
encoding.TextUnmarshaler
|
encoding.TextUnmarshaler
|
||||||
|
31
v2alpha/srv/srv.gotemplate
Normal file
31
v2alpha/srv/srv.gotemplate
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{{define "service"}}
|
||||||
|
{{- $Imps := .Imps}}
|
||||||
|
package TODO
|
||||||
|
|
||||||
|
import (
|
||||||
|
{{- range .Imps.ImportLines}}
|
||||||
|
{{quote .Alias}} {{.Path}}
|
||||||
|
{{- end}}
|
||||||
|
)
|
||||||
|
|
||||||
|
{{range .SCPD.SortedActions}}
|
||||||
|
{{- template "action" args "Action" . "Imps" $Imps}}
|
||||||
|
{{end}}
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{define "action"}}
|
||||||
|
type {{.Action.Name}}Request struct {
|
||||||
|
{{- template "args" args "Args" .Action.InArgs "Imps" .Imps -}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type {{.Action.Name}}Response struct {
|
||||||
|
{{- template "args" args "Args" .Action.OutArgs "Imps" .Imps -}}
|
||||||
|
}
|
||||||
|
{{- end}}
|
||||||
|
|
||||||
|
{{define "args"}}
|
||||||
|
{{- $Imps := .Imps}}
|
||||||
|
{{- range .Args}}
|
||||||
|
{{.Name}} {{index $Imps.TypeRefByTypeName .RelatedStateVariable.DataType}}
|
||||||
|
{{end}}
|
||||||
|
{{- end}}
|
Loading…
x
Reference in New Issue
Block a user