Initial code to generate service code.
This commit is contained in:
		| @@ -6,14 +6,22 @@ import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"sort" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"text/template" | ||||
|  | ||||
| 	"github.com/huin/goupnp/v2alpha/cmd/goupnp2srvgen/tmplfuncs" | ||||
| 	"github.com/huin/goupnp/v2alpha/cmd/goupnp2srvgen/zipread" | ||||
| 	"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/types" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	srvTemplate      = flag.String("srv_template", "", "Path to srv.gotemplate.") | ||||
| 	upnpresourcesZip = flag.String("upnpresources_zip", "", "Path to upnpresources.zip.") | ||||
| ) | ||||
|  | ||||
| @@ -29,6 +37,17 @@ func run() error { | ||||
| 	if len(flag.Args()) > 0 { | ||||
| 		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 == "" { | ||||
| 		return errors.New("-upnpresources_zip is a required flag.") | ||||
| 	} | ||||
| @@ -41,8 +60,13 @@ func run() error { | ||||
| 	if err != nil { | ||||
| 		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 { | ||||
| 		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) | ||||
| 		} | ||||
| 	} | ||||
| @@ -52,9 +76,17 @@ func run() error { | ||||
| var manifests = []*DCPSpecManifest{ | ||||
| 	{ | ||||
| 		Path: "standardizeddcps/Internet Gateway_2/UPnP-gw-IGD-TestFiles-20101210.zip", | ||||
| 		Services: map[string]string{ | ||||
| 			"LANHostConfigManagement:1": "xml data files/service/LANHostConfigManagement1.xml", | ||||
| 			"WANPPPConnection:1":        "xml data files/service/WANPPPConnection1.xml", | ||||
| 		Services: []*ServiceManifest{ | ||||
| 			{ | ||||
| 				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( | ||||
| 	upnpresources *zipread.ZipRead, | ||||
| 	manifest *DCPSpecManifest, | ||||
| 	typeMap typedesc.TypeMap, | ||||
| 	tmpl *template.Template, | ||||
| ) error { | ||||
| 	dcpSpecData, err := upnpresources.OpenZip(manifest.Path) | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	for name, path := range manifest.Services { | ||||
| 		if err := processService(dcpSpecData, name, path); err != nil { | ||||
| 			return fmt.Errorf("processing service %s: %w", name, err) | ||||
| 	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 nil | ||||
| @@ -77,11 +111,11 @@ func processDCP( | ||||
|  | ||||
| func processService( | ||||
| 	dcpSpecData *zipread.ZipRead, | ||||
| 	name string, | ||||
| 	path string, | ||||
| 	srvManifest *ServiceManifest, | ||||
| 	typeMap typedesc.TypeMap, | ||||
| 	tmpl *template.Template, | ||||
| ) error { | ||||
| 	fmt.Printf("%s\n", name) | ||||
| 	f, err := dcpSpecData.Open(path) | ||||
| 	f, err := dcpSpecData.Open(srvManifest.Path) | ||||
| 	if err != nil { | ||||
| 		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 { | ||||
| 		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 | ||||
| } | ||||
|  | ||||
| @@ -120,5 +168,128 @@ type DCPSpecManifest struct { | ||||
| 	Path string | ||||
| 	// 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"). | ||||
| 	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 ( | ||||
| 	BadDescriptionError         = errors.New("bad XML description") | ||||
| 	MissingDefinitionError      = errors.New("missing definition") | ||||
| 	UnsupportedDescriptionError = errors.New("unsupported XML description") | ||||
| ) | ||||
|  | ||||
| // SCPD is the top level service description. | ||||
| type SCPD struct { | ||||
| 	actionByName   map[string]*Action | ||||
| 	variableByName map[string]*StateVariable | ||||
| 	ActionByName   map[string]*Action | ||||
| 	VariableByName map[string]*StateVariable | ||||
| } | ||||
|  | ||||
| // FromXML creates an SCPD from XML data. | ||||
| // | ||||
| // It assumes that xmlDesc.Clean() has been called. | ||||
| 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 { | ||||
| 		sv, err := stateVariableFromXML(xmlSV) | ||||
| 		if err != nil { | ||||
| 			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", | ||||
| 				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 { | ||||
| 		action, err := actionFromXML(xmlAction) | ||||
| 		action, err := actionFromXML(xmlAction, scpd) | ||||
| 		if err != nil { | ||||
| 			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", | ||||
| 				BadDescriptionError, action.name) | ||||
| 				BadDescriptionError, action.Name) | ||||
| 		} | ||||
| 		actions[action.name] = action | ||||
| 		actions[action.Name] = action | ||||
| 	} | ||||
| 	return &SCPD{ | ||||
| 		actionByName:   actions, | ||||
| 		variableByName: stateVariables, | ||||
| 	}, nil | ||||
| 	return scpd, nil | ||||
| } | ||||
|  | ||||
| // ActionNames returns the ordered names of each action. | ||||
| func (scpd *SCPD) ActionNames() []string { | ||||
| 	names := make([]string, 0, len(scpd.actionByName)) | ||||
| 	for name := range scpd.actionByName { | ||||
| 		names = append(names, name) | ||||
| // SortedActions returns the actions, in order of name. | ||||
| func (scpd *SCPD) SortedActions() []*Action { | ||||
| 	actions := make([]*Action, 0, len(scpd.ActionByName)) | ||||
| 	for _, a := range scpd.ActionByName { | ||||
| 		actions = append(actions, a) | ||||
| 	} | ||||
| 	sort.Strings(names) | ||||
| 	return names | ||||
| } | ||||
|  | ||||
| // 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] | ||||
| 	sort.Slice(actions, func(i, j int) bool { | ||||
| 		return actions[i].Name < actions[j].Name | ||||
| 	}) | ||||
| 	return actions | ||||
| } | ||||
|  | ||||
| // Action describes a single UPnP SOAP action. | ||||
| type Action struct { | ||||
| 	name    string | ||||
| 	inArgs  []*Argument | ||||
| 	outArgs []*Argument | ||||
| 	SCPD    *SCPD | ||||
| 	Name    string | ||||
| 	InArgs  []*Argument | ||||
| 	OutArgs []*Argument | ||||
| } | ||||
|  | ||||
| // 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 == "" { | ||||
| 		return nil, fmt.Errorf("%w: empty action name", BadDescriptionError) | ||||
| 	} | ||||
| 	action := &Action{ | ||||
| 		SCPD: scpd, | ||||
| 		Name: xmlAction.Name, | ||||
| 	} | ||||
| 	var inArgs []*Argument | ||||
| 	var outArgs []*Argument | ||||
| 	for _, xmlArg := range xmlAction.Arguments { | ||||
| 		arg, err := argumentFromXML(xmlArg) | ||||
| 		arg, err := argumentFromXML(xmlArg, action) | ||||
| 		if err != nil { | ||||
| 			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) | ||||
| 		} | ||||
| 	} | ||||
| 	return &Action{ | ||||
| 		name:    xmlAction.Name, | ||||
| 		inArgs:  inArgs, | ||||
| 		outArgs: outArgs, | ||||
| 	}, nil | ||||
| 	action.InArgs = inArgs | ||||
| 	action.OutArgs = outArgs | ||||
| 	return action, nil | ||||
| } | ||||
|  | ||||
| // Argument description data. | ||||
| type Argument struct { | ||||
| 	name                 string | ||||
| 	relatedStateVariable string | ||||
| 	Action                   *Action | ||||
| 	Name                     string | ||||
| 	RelatedStateVariableName string | ||||
| } | ||||
|  | ||||
| // 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 == "" { | ||||
| 		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 &Argument{ | ||||
| 		name:                 xmlArg.Name, | ||||
| 		relatedStateVariable: xmlArg.RelatedStateVariable, | ||||
| 		Action:                   action, | ||||
| 		Name:                     xmlArg.Name, | ||||
| 		RelatedStateVariableName: xmlArg.RelatedStateVariable, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (arg *Argument) Name() string { | ||||
| 	return arg.name | ||||
| } | ||||
|  | ||||
| func (arg *Argument) RelatedStateVariableName() string { | ||||
| 	return arg.relatedStateVariable | ||||
| func (arg *Argument) RelatedStateVariable() (*StateVariable, error) { | ||||
| 	if v, ok := arg.Action.SCPD.VariableByName[arg.RelatedStateVariableName]; ok { | ||||
| 		return v, nil | ||||
| 	} | ||||
| 	return nil, fmt.Errorf("%w: state variable %q", MissingDefinitionError, arg.RelatedStateVariableName) | ||||
| } | ||||
|  | ||||
| // StateVariable description data. | ||||
| type StateVariable struct { | ||||
| 	name     string | ||||
| 	dataType string | ||||
| 	Name     string | ||||
| 	DataType string | ||||
| } | ||||
|  | ||||
| func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, error) { | ||||
| @@ -153,15 +151,7 @@ func stateVariableFromXML(xmlSV *xmlsrvdesc.StateVariable) (*StateVariable, erro | ||||
| 			UnsupportedDescriptionError, xmlSV.DataType.Type) | ||||
| 	} | ||||
| 	return &StateVariable{ | ||||
| 		name:     xmlSV.Name, | ||||
| 		dataType: xmlSV.DataType.Name, | ||||
| 		Name:     xmlSV.Name, | ||||
| 		DataType: xmlSV.DataType.Name, | ||||
| 	}, 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" | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 	"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 { | ||||
| 	encoding.TextMarshaler | ||||
| 	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}} | ||||
		Reference in New Issue
	
	Block a user