aboutsummaryrefslogtreecommitdiffstats
path: root/binapigen
diff options
context:
space:
mode:
Diffstat (limited to 'binapigen')
-rw-r--r--binapigen/generate.go138
-rw-r--r--binapigen/generate_test.go134
-rw-r--r--binapigen/types.go12
3 files changed, 279 insertions, 5 deletions
diff --git a/binapigen/generate.go b/binapigen/generate.go
index 8a34445..d35427f 100644
--- a/binapigen/generate.go
+++ b/binapigen/generate.go
@@ -118,9 +118,12 @@ func generateImports(ctx *GenFile, w io.Writer) {
fmt.Fprintln(w, ` "bytes"`)
fmt.Fprintln(w, ` "context"`)
fmt.Fprintln(w, ` "encoding/binary"`)
+ fmt.Fprintln(w, ` "fmt"`)
fmt.Fprintln(w, ` "io"`)
fmt.Fprintln(w, ` "math"`)
+ fmt.Fprintln(w, ` "net"`)
fmt.Fprintln(w, ` "strconv"`)
+ fmt.Fprintln(w, ` "strings"`)
fmt.Fprintln(w)
fmt.Fprintf(w, "\tapi \"%s\"\n", "git.fd.io/govpp.git/api")
fmt.Fprintf(w, "\tcodec \"%s\"\n", "git.fd.io/govpp.git/codec")
@@ -164,7 +167,9 @@ func generateTypes(ctx *GenFile, w io.Writer) {
if len(ctx.file.Enums) > 0 {
for _, enum := range ctx.file.Enums {
if imp, ok := ctx.file.imports[enum.Name]; ok {
- generateImportedAlias(ctx, w, enum.GoName, imp)
+ if strings.HasSuffix(ctx.file.Name, "_types") {
+ generateImportedAlias(ctx, w, enum.GoName, imp)
+ }
continue
}
generateEnum(ctx, w, enum)
@@ -175,7 +180,9 @@ func generateTypes(ctx *GenFile, w io.Writer) {
if len(ctx.file.Aliases) > 0 {
for _, alias := range ctx.file.Aliases {
if imp, ok := ctx.file.imports[alias.Name]; ok {
- generateImportedAlias(ctx, w, alias.GoName, imp)
+ if strings.HasSuffix(ctx.file.Name, "_types") {
+ generateImportedAlias(ctx, w, alias.GoName, imp)
+ }
continue
}
generateAlias(ctx, w, alias)
@@ -186,7 +193,9 @@ func generateTypes(ctx *GenFile, w io.Writer) {
if len(ctx.file.Structs) > 0 {
for _, typ := range ctx.file.Structs {
if imp, ok := ctx.file.imports[typ.Name]; ok {
- generateImportedAlias(ctx, w, typ.GoName, imp)
+ if strings.HasSuffix(ctx.file.Name, "_types") {
+ generateImportedAlias(ctx, w, typ.GoName, imp)
+ }
continue
}
generateStruct(ctx, w, typ)
@@ -197,7 +206,9 @@ func generateTypes(ctx *GenFile, w io.Writer) {
if len(ctx.file.Unions) > 0 {
for _, union := range ctx.file.Unions {
if imp, ok := ctx.file.imports[union.Name]; ok {
- generateImportedAlias(ctx, w, union.GoName, imp)
+ if strings.HasSuffix(ctx.file.Name, "_types") {
+ generateImportedAlias(ctx, w, union.GoName, imp)
+ }
continue
}
generateUnion(ctx, w, union)
@@ -245,9 +256,12 @@ func generateImportRefs(ctx *GenFile, w io.Writer) {
fmt.Fprintf(w, "var _ = context.Background\n")
fmt.Fprintf(w, "var _ = io.Copy\n")
fmt.Fprintf(w, "var _ = strconv.Itoa\n")
+ fmt.Fprintf(w, "var _ = strings.Contains\n")
fmt.Fprintf(w, "var _ = struc.Pack\n")
fmt.Fprintf(w, "var _ = binary.BigEndian\n")
fmt.Fprintf(w, "var _ = math.Float32bits\n")
+ fmt.Fprintf(w, "var _ = net.ParseIP\n")
+ fmt.Fprintf(w, "var _ = fmt.Errorf\n")
}
func generateComment(ctx *GenFile, w io.Writer, goName string, vppName string, objKind string) {
@@ -325,6 +339,13 @@ func generateAlias(ctx *GenFile, w io.Writer, alias *Alias) {
dataType := convertToGoType(ctx.file, alias.Type)
fmt.Fprintf(w, "%s\n", dataType)
+ // generate alias-specific methods
+ switch alias.Name {
+ case "mac_address":
+ fmt.Fprintln(w)
+ generateMacAddressConversion(w, name)
+ }
+
fmt.Fprintln(w)
}
@@ -356,6 +377,16 @@ func generateStruct(ctx *GenFile, w io.Writer, typ *Struct) {
// generate name getter
generateTypeNameGetter(w, name, typ.Name)
+ // generate type-specific methods
+ switch typ.Name {
+ case "address":
+ fmt.Fprintln(w)
+ generateIPAddressConversion(w, name)
+ case "prefix":
+ fmt.Fprintln(w)
+ generatePrefixConversion(w, name)
+ }
+
fmt.Fprintln(w)
}
@@ -1198,6 +1229,105 @@ func generateTypeNameGetter(w io.Writer, structName, msgName string) {
fmt.Fprintf(w, "func (*%s) GetTypeName() string { return %q }\n", structName, msgName)
}
+func generateIPAddressConversion(w io.Writer, structName string) {
+ f1 := func(ipVer, ipVerExt int) string {
+ return fmt.Sprintf(`address.Af = ADDRESS_IP%[1]d
+ var ip%[1]daddr IP%[1]dAddress
+ copy(ip%[1]daddr[:], netIP.To%[2]d())
+ address.Un.SetIP%[1]d(ip%[1]daddr)`, ipVer, ipVerExt)
+ }
+ f2 := func(ipVer, ipVerExt int) string {
+ return fmt.Sprintf("ip%[1]dAddress := a.Un.GetIP%[1]d()\nip = net.IP(ip%[1]dAddress[:]).To%[2]d().String()",
+ ipVer, ipVerExt)
+ }
+ // IP to Address
+ fmt.Fprintf(w, `func ParseAddress(ip string) (%[1]s, error) {
+ var address %[1]s
+ netIP := net.ParseIP(ip)
+ if netIP == nil {
+ return address, fmt.Errorf("invalid address: %[2]s", ip)
+ }
+ if ip4 := netIP.To4(); ip4 == nil {
+ %[3]s
+ } else {
+ %[4]s
+ }
+ return address, nil
+}
+`, structName, "%s", f1(6, 16), f1(4, 4))
+ fmt.Fprintln(w)
+
+ // Address to IP
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, `func (a *%[1]s) ToString() string {
+ var ip string
+ if a.Af == ADDRESS_IP6 {
+ %[2]s
+ } else {
+ %[3]s
+ }
+ return ip
+}`, structName, f2(6, 16), f2(4, 4))
+}
+
+func generatePrefixConversion(w io.Writer, structName string) {
+ fErr := func() string {
+ return fmt.Sprintf(`if err != nil {
+ return Prefix{}, fmt.Errorf("invalid IP %s: %s", ip, err)
+ }`, "%s", "%v")
+ }
+
+ // IP to Prefix
+ fmt.Fprintf(w, `func ParsePrefix(ip string) (prefix %[1]s, err error) {
+ hasPrefix := strings.Contains(ip, "/")
+ if hasPrefix {
+ netIP, network, err := net.ParseCIDR(ip)
+ %[2]s
+ maskSize, _ := network.Mask.Size()
+ prefix.Len = byte(maskSize)
+ prefix.Address, err = ParseAddress(netIP.String())
+ %[2]s
+ } else {
+ netIP := net.ParseIP(ip)
+ defaultMaskSize, _ := net.CIDRMask(32, 32).Size()
+ if netIP.To4() == nil {
+ defaultMaskSize, _ = net.CIDRMask(128, 128).Size()
+ }
+ prefix.Len = byte(defaultMaskSize)
+ prefix.Address, err = ParseAddress(netIP.String())
+ %[2]s
+ }
+ return prefix, nil
+}`, structName, fErr(), nil)
+ fmt.Fprintln(w)
+
+ // Prefix to IP
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, `func (p *%[1]s) ToString() string {
+ ip := p.Address.ToString()
+ return ip + "/" + strconv.Itoa(int(p.Len))
+ }`, structName)
+}
+
+func generateMacAddressConversion(w io.Writer, structName string) {
+ // string to MAC
+ fmt.Fprintf(w, `func ParseMAC(mac string) (parsed %[1]s, err error) {
+ var hw net.HardwareAddr
+ if hw, err = net.ParseMAC(mac); err != nil {
+ return
+ }
+ copy(parsed[:], hw[:])
+ return
+}`, structName)
+ fmt.Fprintln(w)
+
+ // MAC to string
+ fmt.Fprintln(w)
+ fmt.Fprintf(w, `func (m *%[1]s) ToString() string {
+ return net.HardwareAddr(m[:]).String()
+ }`, structName)
+}
+
func generateCrcGetter(w io.Writer, structName, crc string) {
crc = strings.TrimPrefix(crc, "0x")
fmt.Fprintf(w, "func (*%s) GetCrcString() string { return %q }\n", structName, crc)
diff --git a/binapigen/generate_test.go b/binapigen/generate_test.go
index aab62cd..46cc5eb 100644
--- a/binapigen/generate_test.go
+++ b/binapigen/generate_test.go
@@ -15,7 +15,10 @@
package binapigen
import (
+ "git.fd.io/govpp.git/examples/binapi/interfaces"
+ "git.fd.io/govpp.git/examples/binapi/ip_types"
"os"
+ "strings"
"testing"
. "github.com/onsi/gomega"
@@ -97,6 +100,137 @@ func TestGenerateFromFileGeneratePackageError(t *testing.T) {
Expect(err).Should(HaveOccurred())
}
+func TestGeneratedParseAddress(t *testing.T) {
+ RegisterTestingT(t)
+
+ var data = []struct {
+ input string
+ result ip_types.Address
+ }{
+ {"192.168.0.1", ip_types.Address{
+ Af: ip_types.ADDRESS_IP4,
+ Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
+ }},
+ {"aac1:0:ab45::", ip_types.Address{
+ Af: ip_types.ADDRESS_IP6,
+ Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
+ }},
+ }
+
+ for _, entry := range data {
+ t.Run(entry.input, func(t *testing.T) {
+ parsedAddress, err := ip_types.ParseAddress(entry.input)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(parsedAddress).To(Equal(entry.result))
+
+ originAddress := parsedAddress.ToString()
+ Expect(originAddress).To(Equal(entry.input))
+ })
+ }
+}
+
+func TestGeneratedParseAddressError(t *testing.T) {
+ RegisterTestingT(t)
+
+ _, err := ip_types.ParseAddress("malformed_ip")
+ Expect(err).Should(HaveOccurred())
+}
+
+func TestGeneratedParsePrefix(t *testing.T) {
+ RegisterTestingT(t)
+
+ var data = []struct {
+ input string
+ result ip_types.Prefix
+ }{
+ {"192.168.0.1/24", ip_types.Prefix{
+ Address: ip_types.Address{
+ Af: ip_types.ADDRESS_IP4,
+ Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
+ },
+ Len: 24,
+ }},
+ {"192.168.0.1", ip_types.Prefix{
+ Address: ip_types.Address{
+ Af: ip_types.ADDRESS_IP4,
+ Un: ip_types.AddressUnionIP4(ip_types.IP4Address{192, 168, 0, 1}),
+ },
+ Len: 32,
+ }},
+ {"aac1:0:ab45::/96", ip_types.Prefix{
+ Address: ip_types.Address{
+ Af: ip_types.ADDRESS_IP6,
+ Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
+ },
+ Len: 96,
+ }},
+ {"aac1:0:ab45::", ip_types.Prefix{
+ Address: ip_types.Address{
+ Af: ip_types.ADDRESS_IP6,
+ Un: ip_types.AddressUnionIP6(ip_types.IP6Address{170, 193, 0, 0, 171, 69, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}),
+ },
+ Len: 128,
+ }},
+ }
+
+ for _, entry := range data {
+ t.Run(entry.input, func(t *testing.T) {
+ parsedAddress, err := ip_types.ParsePrefix(entry.input)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(parsedAddress).To(Equal(entry.result))
+
+ // Parsed IP without prefix receives a default one
+ // so the input data must be adjusted
+ if entry.result.Address.Af == ip_types.ADDRESS_IP4 && !strings.Contains(entry.input, "/") {
+ entry.input = entry.input + "/32"
+ }
+ if entry.result.Address.Af == ip_types.ADDRESS_IP6 && !strings.Contains(entry.input, "/") {
+ entry.input = entry.input + "/128"
+ }
+ originAddress := parsedAddress.ToString()
+ Expect(originAddress).To(Equal(entry.input))
+ })
+ }
+}
+
+func TestGeneratedParsePrefixError(t *testing.T) {
+ RegisterTestingT(t)
+
+ _, err := ip_types.ParsePrefix("malformed_ip")
+ Expect(err).Should(HaveOccurred())
+}
+
+func TestGeneratedParseMAC(t *testing.T) {
+ RegisterTestingT(t)
+
+ var data = []struct {
+ input string
+ result interfaces.MacAddress
+ }{
+ {"b7:b9:bb:a1:5c:af", interfaces.MacAddress{183, 185, 187, 161, 92, 175}},
+ {"47:4b:c7:3e:06:c8", interfaces.MacAddress{71, 75, 199, 62, 6, 200}},
+ {"a7:cc:9f:10:18:e3", interfaces.MacAddress{167, 204, 159, 16, 24, 227}},
+ }
+
+ for _, entry := range data {
+ t.Run(entry.input, func(t *testing.T) {
+ parsedMac, err := interfaces.ParseMAC(entry.input)
+ Expect(err).ShouldNot(HaveOccurred())
+ Expect(parsedMac).To(Equal(entry.result))
+
+ originAddress := parsedMac.ToString()
+ Expect(originAddress).To(Equal(entry.input))
+ })
+ }
+}
+
+func TestGeneratedParseMACError(t *testing.T) {
+ RegisterTestingT(t)
+
+ _, err := interfaces.ParseMAC("malformed_mac")
+ Expect(err).Should(HaveOccurred())
+}
+
/*func TestGetContext(t *testing.T) {
RegisterTestingT(t)
outDir := "test_output_directory"
diff --git a/binapigen/types.go b/binapigen/types.go
index 0dbbeb1..96ae870 100644
--- a/binapigen/types.go
+++ b/binapigen/types.go
@@ -15,6 +15,7 @@
package binapigen
import (
+ "fmt"
"strings"
"github.com/sirupsen/logrus"
@@ -213,14 +214,23 @@ func getActualType(file *File, typ string) (actual string) {
return typ
}
-// convertToGoType translates the VPP binary API type into Go type
+// convertToGoType translates the VPP binary API type into Go type.
+// Imported types are with import prefix.
func convertToGoType(file *File, binapiType string) (typ string) {
if t, ok := binapiTypes[binapiType]; ok {
// basic types
typ = t
} else if r, ok := file.refmap[binapiType]; ok {
// specific types (enums/types/unions)
+ var prefix string
typ = camelCaseName(r)
+ // look in imports using name and type name eventually
+ if imp, ok := file.imports[typ]; ok {
+ prefix = fmt.Sprintf("%s.", imp)
+ } else if imp, ok := file.imports[fromApiType(binapiType)]; ok {
+ prefix = fmt.Sprintf("%s.", imp)
+ }
+ typ = fmt.Sprintf("%s%s", prefix, typ)
} else {
switch binapiType {
case "bool", "string":