Skip to content
This repository has been archived by the owner on Jul 1, 2024. It is now read-only.

Commit

Permalink
Merge pull request #196 from zelch/rst_support
Browse files Browse the repository at this point in the history
DBus protocol RST document support.
  • Loading branch information
muka committed Jan 15, 2024
2 parents 664f55c + d00aee3 commit dfdf79b
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 12 deletions.
8 changes: 8 additions & 0 deletions bluez/profile/device/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package device

import "github.com/godbus/dbus/v5"

type SetsItem struct {
Object dbus.ObjectPath
Dict map[string]byte
}
7 changes: 6 additions & 1 deletion gen/generator/generator_tpl.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@ func ErrorsTemplate(filename string, apis []*types.ApiGroup) error {
}

for i, err := range errors {
base := err[strings.LastIndex(err, ".")+1:]
if strings.Contains(err, "obex") {
base = "Obex" + base
}
errorsList.List[i] = types.BluezError{
Name: strings.Replace(err, "org.bluez.Error.", "", 1),
Name: err,
Base: base,
}
}

Expand Down
17 changes: 13 additions & 4 deletions gen/generator/generator_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ import (
"strings"
"text/template"

"github.com/muka/go-bluetooth/gen/override"
"github.com/muka/go-bluetooth/gen/types"
)

var TplPath = "gen/generator/tpl/%s.go.tpl"

//rename variable name to avoid collision with Go languages
// rename variable name to avoid collision with Go languages
func renameReserved(varname string) string {
switch varname {
case "type":
Expand Down Expand Up @@ -61,9 +62,17 @@ func prepareDocs(src string, skipFirstComment bool, leftpad int) string {
}

func getApiPackage(apiGroup *types.ApiGroup) string {
apiName := strings.Replace(apiGroup.FileName, "-api.txt", "", -1)
apiName = strings.Replace(apiName, "-", "_", -1)
apiName = strings.Replace(apiName, " [experimental]", "", -1)
apiName, ok := override.MapFile(apiGroup.FileName)
if !ok {
apiName = apiGroup.FileName
}
apiName = strings.ReplaceAll(apiName, "-api.txt", "")
apiName = strings.ReplaceAll(apiName, "_api.txt", "")
apiName = strings.ReplaceAll(apiName, "org.bluez.", "")
apiName = strings.ReplaceAll(apiName, ".rst", "")
apiName = strings.ReplaceAll(apiName, "-", "_")
apiName = strings.ReplaceAll(apiName, " [experimental]", "")
apiName = strings.ToLower(apiName)
return apiName
}

Expand Down
8 changes: 4 additions & 4 deletions gen/generator/tpl/errors.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ import (

var (
{{- range .List }}
// {{.Name}} map to org.bluez.Error.{{.Name}}
Err{{.Name}} = dbus.Error{
Name: "org.bluez.Error.{{.Name}}",
Body: []interface{}{"{{.Name}}"},
// {{.Base}} map to {{.Name}}
Err{{.Base}} = dbus.Error{
Name: "{{.Name}}",
Body: []interface{}{"{{.Base}}"},
}
{{- end }}
)
65 changes: 65 additions & 0 deletions gen/override/filename.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package override

var filesMap = map[string]string{
"org.bluez.Adapter.rst": "adapter-api.txt",
"org.bluez.AdminPolicySet.rst": "admin-policy-api.txt",
"org.bluez.AdminPolicyStatus.rst": "admin-policy-api.txt",
"org.bluez.AdvertisementMonitor.rst": "advertisement-monitor-api.txt",
"org.bluez.AdvertisementMonitorManager.rst": "advertisement-monitor-api.txt",
"org.bluez.LEAdvertisement.rst": "advertising-api.txt",
"org.bluez.LEAdvertisingManager.rst": "advertising-api.txt",
"org.bluez.AgentManager.rst": "agent-api.txt",
"org.bluez.Agent.rst": "agent-api.txt",
"org.bluez.Battery.rst": "battery-api.txt",
"org.bluez.BatteryProviderManager.rst": "battery-api.txt",
"org.bluez.BatteryProvider.rst": "battery-api.txt",
"org.bluez.Device.rst": "device-api.txt",
"org.bluez.GattService.rst": "gatt-api.txt",
"org.bluez.GattCharacteristic.rst": "gatt-api.txt",
"org.bluez.GattDescriptor.rst": "gatt-api.txt",
"org.bluez.GattProfile.rst": "gatt-api.txt",
"org.bluez.GattManager.rst": "gatt-api.txt",
"org.bluez.HealthManager.rst": "health-api.txt",
"org.bluez.HealthDevice.rst": "health-api.txt",
"org.bluez.HealthChannel.rst": "health-api.txt",
"org.bluez.Input.rst": "input-api.txt",
"org.bluez.Media.rst": "media-api.txt",
"org.bluez.MediaControl.rst": "media-api.txt",
"org.bluez.MediaPlayer.rst": "media-api.txt",
"org.bluez.MediaFolder.rst": "media-api.txt",
"org.bluez.MediaItem.rst": "media-api.txt",
"org.bluez.MediaEndpoint.rst": "media-api.txt",
"org.bluez.MediaTransport.rst": "media-api.txt",
"org.bluez.mesh.Network.rst": "mesh-api.txt",
"org.bluez.mesh.Node.rst": "mesh-api.txt",
"org.bluez.mesh.Management.rst": "mesh-api.txt",
"org.bluez.mesh.Application.rst": "mesh-api.txt",
"org.bluez.mesh.Element.rst": "mesh-api.txt",
"org.bluez.mesh.Attention.rst": "mesh-api.txt",
"org.bluez.mesh.Provisioner.rst": "mesh-api.txt",
"org.bluez.mesh.ProvisionAgent.rst": "mesh-api.txt",
"org.bluez.Network.rst": "network-api.txt",
"org.bluez.NetworkServer.rst": "network-api.txt",
"org.bluez.obex.AgentManager.rst": "obex-agent-api.txt",
"org.bluez.obex.Agent.rst": "obex-agent-api.txt",
"org.bluez.obex.Client.rst": "obex-api.txt",
"org.bluez.obex.Session.rst": "obex-api.txt",
"org.bluez.obex.Transfer.rst": "obex-api.txt",
"org.bluez.obex.ObjectPush.rst": "obex-api.txt",
"org.bluez.obex.FileTransfer.rst": "obex-api.txt",
"org.bluez.obex.PhonebookAccess.rst": "obex-api.txt",
"org.bluez.obex.Synchronization.rst": "obex-api.txt",
"org.bluez.obex.MessageAccess.rst": "obex-api.txt",
"org.bluez.obex.Message.rst": "obex-api.txt",
"org.bluez.ProfileManager.rst": "profile-api.txt",
"org.bluez.Profile.rst": "profile-api.txt",
"org.bluez.SimAccess.rst": "sap-api.txt",
"org.bluez.ThermometerManager.rst": "thermometer-api.txt",
"org.bluez.Thermometer.rst": "thermometer-api.txt",
"org.bluez.ThermometerWatcher.rst": "thermometer-api.txt",
}

func MapFile(rawfile string) (string, bool) {
res, ok := filesMap[rawfile]
return res, ok
}
4 changes: 4 additions & 0 deletions gen/override/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ var PropertyTypes = map[string]map[string]string{
"org.bluez.Device1": {
"ServiceData": "map[string]interface{}",
"ManufacturerData": "map[uint16]interface{}",
"Sets": "[]SetsItem",
},
"org.bluez.DeviceSet1": {
"Devices": "[]dbus.ObjectPath",
},
"org.bluez.GattCharacteristic1": {
"Value": "[]byte `dbus:\"emit\"`",
Expand Down
223 changes: 223 additions & 0 deletions gen/parser/api_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"path/filepath"
"regexp"
"strings"

"github.com/muka/go-bluetooth/gen/filters"
"github.com/muka/go-bluetooth/gen/types"
Expand Down Expand Up @@ -32,7 +33,229 @@ func NewApiGroupParser(debug bool, filtersList []filters.Filter) ApiGroupParser

// Parse load a documentation file and parse the content
func (g *ApiGroupParser) Parse(srcFile string) (*types.ApiGroup, error) {
if strings.HasSuffix(srcFile, ".txt") { // nolint: gocritic
return g.parseTXT(srcFile)
} else if strings.HasSuffix(srcFile, ".rst") {
return g.parseRST(srcFile)
} else {
log.Errorf("Unknown file type for %s", srcFile)
return nil, fmt.Errorf("Unknown file type.")
}
}

func (g *ApiGroupParser) getSection(raw, sectionName string, divider rune) (out string, err error) {
pattern := fmt.Sprintf("(?ms)%s\\n%c+\\n(.*?)\\n((Methods|Signals|Properties)\\n[-`]+\\n|\\z)", sectionName, divider)
re := regexp.MustCompile(pattern)
matches := re.FindStringSubmatch(raw)
if len(matches) > 0 {
return strings.Trim(matches[1], " \t\n"), nil
}

return "", nil
}

func (g *ApiGroupParser) parseFlags(raw string) (flags []types.Flag, err error) {
for _, f := range strings.Split(raw, ", ") {
var flag types.Flag = 0
switch f {
case "readonly":
fallthrough
case "read-only":
// log.Printf("f: %v", f)
flag = types.FlagReadOnly
case "writeonly":
fallthrough
case "write-only":
// log.Printf("f: %v", f)
flag = types.FlagWriteOnly
case "readwrite":
fallthrough
case "read-write":
fallthrough
case "read/write":
// log.Printf("f: %v", f)
flag = types.FlagReadWrite
case "experimental":
fallthrough
case "Experimental":
// log.Printf("f: %v", f)
flag = types.FlagExperimental
case "optional":
// log.Printf("f: %v", f)
flag = types.FlagOptional
default:
log.Warnf("Unknown flag %s", f)
}
if flag != 0 {
flags = append(flags, flag)
}
}

return flags, nil
}

func (g *ApiGroupParser) parseRST(srcFile string) (*types.ApiGroup, error) {
var err error
apiGroup := g.model

if g.debug {
log.Debugf("------------------- Parsing %s -------------------", srcFile)
}

apiGroup.FileName = filepath.Base(srcFile)
apiGroup.Api = make([]*types.Api, 0)

rawBytes, err := util.ReadFile(srcFile)
if err != nil {
return apiGroup, err
}

raw := string(rawBytes)

re := regexp.MustCompile(`-+\n([^\n]+)\n-+\n`)
matches := re.FindStringSubmatch(string(raw))

g.model.Name = matches[1]

section, err := g.getSection(raw, "Description", '=')
if err != nil {
return apiGroup, err
} else if section != "" {
// log.Printf(" RST Description: %+#v", section)
g.model.Description = strings.Trim(section, " \t\n")
}

api := &types.Api{}

section, err = g.getSection(raw, "Interface", '=')
if err != nil {
return apiGroup, err
} else if section != "" {
re = regexp.MustCompile(`[:;]?Service[:;]?\s+(.*?)\n`)
matches = re.FindStringSubmatch(section)
api.Service = matches[1]
// log.Printf("Service Match: %+#v", matches)

re = regexp.MustCompile(`[:;]?Interface[:;]?\s+([^ \n]+)`)
matches = re.FindStringSubmatch(section)
api.Interface = matches[1]
// log.Printf("Interface Match: %+#v", matches)

re = regexp.MustCompile(`[:;]?Object path[:;]?\s+([^\n]+)`)
matches = re.FindStringSubmatch(section)
api.ObjectPath = matches[1]
// log.Printf("ObjectPath Match: %+#v", matches)

api.Title = g.model.Name
}

section, err = g.getSection(raw, "Methods", '-')
switch {
case err != nil:
return apiGroup, err
case section != "":
// log.Printf("Methods Section: %s", section)

re = regexp.MustCompile("(?ms)^(([^\\s`][^\n]*)\\s(\\w*)\\(([^\n)]*)\\))([^\\n\\r]*)\\n`+\\n(\\n(\\t[^\\n]*[\\n\\r]+|[\\n\\r]+)+)")
methodMatches := re.FindAllStringSubmatch(section, -1)
for _, x := range methodMatches {
method := &types.Method{}
method.Name = x[3]
method.ReturnType = x[2]
method.Docs = strings.Trim(x[6], " \t\n")
// log.Printf(" RST Method: %s - %s - %s, %+#v", x[1], x[2], x[3], x)

if x[4] != "" {
for _, arg := range strings.Split(x[4], ", ") {
// log.Printf(" RST Method Arg: %+#v", arg)
v := strings.Split(arg, " ")
if len(v) == 1 { // FIXME: This is a horrible hack to deal with org.bluez.Profile.rst -> NewConnection missing a type for the fd argument.
method.Args = append(method.Args, types.Arg{Name: arg, Type: "int32"})
} else {
method.Args = append(method.Args, types.Arg{Name: v[1], Type: v[0]})
}
}
} else {
method.Args = []types.Arg{}
}

re = regexp.MustCompile("Possible errors:\n\n((?:\t:.*:\n)+)")
errors := re.FindStringSubmatch(method.Docs)
// log.Printf(" RST Errors: %+#v", errors)
if errors != nil {
re = regexp.MustCompile("\t:(.*):\n")
errs := re.FindAllStringSubmatch(errors[1], -1)
// log.Printf(" RST Errors: %#v", errs)
for _, x := range errs {
method.Errors = append(method.Errors, x[1])
}
} else {
method.Errors = []string{}
}

// log.Printf(" RST Method: %+#v", method)
api.Methods = append(api.Methods, method)
}
default:
api.Methods = []*types.Method{}
}

api.Signals = []*types.Method{}

section, err = g.getSection(raw, "Properties", '-')
switch {
case err != nil:
return apiGroup, err
case section != "":
// log.Printf("Properties Section: %s", section)

// re := regexp.MustCompile("(?:\\n|^)((.*?) (\\w+)(?: \\[([a-z, -]+)\\])?)\\n`+\\n(\\n(\\t[^\\n]*[\\n\\r]+|[\\n\\r]+)+)")
// re := regexp.MustCompile("(?:\\n|^)((.*?) (\\w+)(?: \\[([a-z, -]+)\\])?)\\n`+\\n((\\n(\\t[^\\n]*[\\n\\r]+|[\\n\\r]+))+)")
re := regexp.MustCompile(
"(?ms:^)((.*?)" +
" (\\w+)" +
"(?: \\[([a-z, -]+)\\])?" +
")(?: \\(Default:.*\\))?\\n`+\\n+" +

"(" +
"(?:" +
"\\t.+" +
"|" +
"\\n" +
")*" +
")")
// re := regexp.MustCompile("(?:\\n|^)((.*?) (\\w+)(?: \\[([a-z, -]+)\\])?)\\n`+")
matches := re.FindAllStringSubmatch(section, -1)
// log.Printf(" RST Properties: %+#v", matches)

for _, x := range matches {
property := &types.Property{}
property.Name = x[3]
property.Type = x[2]
property.Docs = strings.Trim(x[5], " \t\n")
// property.Docs = x[5]
// log.Printf(" RST Property: %s - %s - %s - %s, %+#v", x[1], x[2], x[3], x[4], x)

if x[4] != "" {
property.Flags, err = g.parseFlags(x[4])
if err != nil {
return apiGroup, err
}
}

// log.Printf(" RST Property: %+#v", property)
api.Properties = append(api.Properties, property)
}
default:
api.Properties = []*types.Property{}
}

// One API per file for the RST files.
apiGroup.Api = append(apiGroup.Api, api)
return apiGroup, nil
}

func (g *ApiGroupParser) parseTXT(srcFile string) (*types.ApiGroup, error) {
var err error
apiGroup := g.model

Expand Down
1 change: 1 addition & 0 deletions gen/types/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package types

type BluezError struct {
Name string
Base string
Error string
}

Expand Down
Loading

0 comments on commit dfdf79b

Please sign in to comment.