mirror of https://github.com/tidwall/tile38.git
537 lines
13 KiB
Go
537 lines
13 KiB
Go
|
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
// Source code and contact info at http://github.com/streadway/amqp
|
||
|
|
||
|
// +build ignore
|
||
|
|
||
|
package main
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"encoding/xml"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"os"
|
||
|
"regexp"
|
||
|
"strings"
|
||
|
"text/template"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
ErrUnknownType = errors.New("Unknown field type in gen")
|
||
|
ErrUnknownDomain = errors.New("Unknown domain type in gen")
|
||
|
)
|
||
|
|
||
|
var amqpTypeToNative = map[string]string{
|
||
|
"bit": "bool",
|
||
|
"octet": "byte",
|
||
|
"shortshort": "uint8",
|
||
|
"short": "uint16",
|
||
|
"long": "uint32",
|
||
|
"longlong": "uint64",
|
||
|
"timestamp": "time.Time",
|
||
|
"table": "Table",
|
||
|
"shortstr": "string",
|
||
|
"longstr": "string",
|
||
|
}
|
||
|
|
||
|
type Rule struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Docs []string `xml:"doc"`
|
||
|
}
|
||
|
|
||
|
type Doc struct {
|
||
|
Type string `xml:"type,attr"`
|
||
|
Body string `xml:",innerxml"`
|
||
|
}
|
||
|
|
||
|
type Chassis struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Implement string `xml:"implement,attr"`
|
||
|
}
|
||
|
|
||
|
type Assert struct {
|
||
|
Check string `xml:"check,attr"`
|
||
|
Value string `xml:"value,attr"`
|
||
|
Method string `xml:"method,attr"`
|
||
|
}
|
||
|
|
||
|
type Field struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Domain string `xml:"domain,attr"`
|
||
|
Type string `xml:"type,attr"`
|
||
|
Label string `xml:"label,attr"`
|
||
|
Reserved bool `xml:"reserved,attr"`
|
||
|
Docs []Doc `xml:"doc"`
|
||
|
Asserts []Assert `xml:"assert"`
|
||
|
}
|
||
|
|
||
|
type Response struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
}
|
||
|
|
||
|
type Method struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Response Response `xml:"response"`
|
||
|
Synchronous bool `xml:"synchronous,attr"`
|
||
|
Content bool `xml:"content,attr"`
|
||
|
Index string `xml:"index,attr"`
|
||
|
Label string `xml:"label,attr"`
|
||
|
Docs []Doc `xml:"doc"`
|
||
|
Rules []Rule `xml:"rule"`
|
||
|
Fields []Field `xml:"field"`
|
||
|
Chassis []Chassis `xml:"chassis"`
|
||
|
}
|
||
|
|
||
|
type Class struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Handler string `xml:"handler,attr"`
|
||
|
Index string `xml:"index,attr"`
|
||
|
Label string `xml:"label,attr"`
|
||
|
Docs []Doc `xml:"doc"`
|
||
|
Methods []Method `xml:"method"`
|
||
|
Chassis []Chassis `xml:"chassis"`
|
||
|
}
|
||
|
|
||
|
type Domain struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Type string `xml:"type,attr"`
|
||
|
Label string `xml:"label,attr"`
|
||
|
Rules []Rule `xml:"rule"`
|
||
|
Docs []Doc `xml:"doc"`
|
||
|
}
|
||
|
|
||
|
type Constant struct {
|
||
|
Name string `xml:"name,attr"`
|
||
|
Value int `xml:"value,attr"`
|
||
|
Class string `xml:"class,attr"`
|
||
|
Doc string `xml:"doc"`
|
||
|
}
|
||
|
|
||
|
type Amqp struct {
|
||
|
Major int `xml:"major,attr"`
|
||
|
Minor int `xml:"minor,attr"`
|
||
|
Port int `xml:"port,attr"`
|
||
|
Comment string `xml:"comment,attr"`
|
||
|
|
||
|
Constants []Constant `xml:"constant"`
|
||
|
Domains []Domain `xml:"domain"`
|
||
|
Classes []Class `xml:"class"`
|
||
|
}
|
||
|
|
||
|
type renderer struct {
|
||
|
Root Amqp
|
||
|
bitcounter int
|
||
|
}
|
||
|
|
||
|
type fieldset struct {
|
||
|
AmqpType string
|
||
|
NativeType string
|
||
|
Fields []Field
|
||
|
*renderer
|
||
|
}
|
||
|
|
||
|
var (
|
||
|
helpers = template.FuncMap{
|
||
|
"public": public,
|
||
|
"private": private,
|
||
|
"clean": clean,
|
||
|
}
|
||
|
|
||
|
packageTemplate = template.Must(template.New("package").Funcs(helpers).Parse(`
|
||
|
// Copyright (c) 2012, Sean Treadway, SoundCloud Ltd.
|
||
|
// Use of this source code is governed by a BSD-style
|
||
|
// license that can be found in the LICENSE file.
|
||
|
// Source code and contact info at http://github.com/streadway/amqp
|
||
|
|
||
|
/* GENERATED FILE - DO NOT EDIT */
|
||
|
/* Rebuild from the spec/gen.go tool */
|
||
|
|
||
|
{{with .Root}}
|
||
|
package amqp
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"encoding/binary"
|
||
|
"io"
|
||
|
)
|
||
|
|
||
|
// Error codes that can be sent from the server during a connection or
|
||
|
// channel exception or used by the client to indicate a class of error like
|
||
|
// ErrCredentials. The text of the error is likely more interesting than
|
||
|
// these constants.
|
||
|
const (
|
||
|
{{range $c := .Constants}}
|
||
|
{{if $c.IsError}}{{.Name | public}}{{else}}{{.Name | private}}{{end}} = {{.Value}}{{end}}
|
||
|
)
|
||
|
|
||
|
func isSoftExceptionCode(code int) bool {
|
||
|
switch code {
|
||
|
{{range $c := .Constants}} {{if $c.IsSoftError}} case {{$c.Value}}:
|
||
|
return true
|
||
|
{{end}}{{end}}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
{{range .Classes}}
|
||
|
{{$class := .}}
|
||
|
{{range .Methods}}
|
||
|
{{$method := .}}
|
||
|
{{$struct := $.StructName $class.Name $method.Name}}
|
||
|
{{if .Docs}}/* {{range .Docs}} {{.Body | clean}} {{end}} */{{end}}
|
||
|
type {{$struct}} struct {
|
||
|
{{range .Fields}}
|
||
|
{{$.FieldName .}} {{$.FieldType . | $.NativeType}} {{if .Label}}// {{.Label}}{{end}}{{end}}
|
||
|
{{if .Content}}Properties properties
|
||
|
Body []byte{{end}}
|
||
|
}
|
||
|
|
||
|
func (msg *{{$struct}}) id() (uint16, uint16) {
|
||
|
return {{$class.Index}}, {{$method.Index}}
|
||
|
}
|
||
|
|
||
|
func (msg *{{$struct}}) wait() (bool) {
|
||
|
return {{.Synchronous}}{{if $.HasField "NoWait" .}} && !msg.NoWait{{end}}
|
||
|
}
|
||
|
|
||
|
{{if .Content}}
|
||
|
func (msg *{{$struct}}) getContent() (properties, []byte) {
|
||
|
return msg.Properties, msg.Body
|
||
|
}
|
||
|
|
||
|
func (msg *{{$struct}}) setContent(props properties, body []byte) {
|
||
|
msg.Properties, msg.Body = props, body
|
||
|
}
|
||
|
{{end}}
|
||
|
func (msg *{{$struct}}) write(w io.Writer) (err error) {
|
||
|
{{if $.HasType "bit" $method}}var bits byte{{end}}
|
||
|
{{.Fields | $.Fieldsets | $.Partial "enc-"}}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (msg *{{$struct}}) read(r io.Reader) (err error) {
|
||
|
{{if $.HasType "bit" $method}}var bits byte{{end}}
|
||
|
{{.Fields | $.Fieldsets | $.Partial "dec-"}}
|
||
|
return
|
||
|
}
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
|
||
|
func (r *reader) parseMethodFrame(channel uint16, size uint32) (f frame, err error) {
|
||
|
mf := &methodFrame {
|
||
|
ChannelId: channel,
|
||
|
}
|
||
|
|
||
|
if err = binary.Read(r.r, binary.BigEndian, &mf.ClassId); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if err = binary.Read(r.r, binary.BigEndian, &mf.MethodId); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch mf.ClassId {
|
||
|
{{range .Classes}}
|
||
|
{{$class := .}}
|
||
|
case {{.Index}}: // {{.Name}}
|
||
|
switch mf.MethodId {
|
||
|
{{range .Methods}}
|
||
|
case {{.Index}}: // {{$class.Name}} {{.Name}}
|
||
|
//fmt.Println("NextMethod: class:{{$class.Index}} method:{{.Index}}")
|
||
|
method := &{{$.StructName $class.Name .Name}}{}
|
||
|
if err = method.read(r.r); err != nil {
|
||
|
return
|
||
|
}
|
||
|
mf.Method = method
|
||
|
{{end}}
|
||
|
default:
|
||
|
return nil, fmt.Errorf("Bad method frame, unknown method %d for class %d", mf.MethodId, mf.ClassId)
|
||
|
}
|
||
|
{{end}}
|
||
|
default:
|
||
|
return nil, fmt.Errorf("Bad method frame, unknown class %d", mf.ClassId)
|
||
|
}
|
||
|
|
||
|
return mf, nil
|
||
|
}
|
||
|
{{end}}
|
||
|
|
||
|
{{define "enc-bit"}}
|
||
|
{{range $off, $field := .Fields}}
|
||
|
if msg.{{$field | $.FieldName}} { bits |= 1 << {{$off}} }
|
||
|
{{end}}
|
||
|
if err = binary.Write(w, binary.BigEndian, bits); err != nil { return }
|
||
|
{{end}}
|
||
|
{{define "enc-octet"}}
|
||
|
{{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-shortshort"}}
|
||
|
{{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-short"}}
|
||
|
{{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-long"}}
|
||
|
{{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-longlong"}}
|
||
|
{{range .Fields}} if err = binary.Write(w, binary.BigEndian, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-timestamp"}}
|
||
|
{{range .Fields}} if err = writeTimestamp(w, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-shortstr"}}
|
||
|
{{range .Fields}} if err = writeShortstr(w, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-longstr"}}
|
||
|
{{range .Fields}} if err = writeLongstr(w, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "enc-table"}}
|
||
|
{{range .Fields}} if err = writeTable(w, msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
|
||
|
{{define "dec-bit"}}
|
||
|
if err = binary.Read(r, binary.BigEndian, &bits); err != nil {
|
||
|
return
|
||
|
}
|
||
|
{{range $off, $field := .Fields}} msg.{{$field | $.FieldName}} = (bits & (1 << {{$off}}) > 0)
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-octet"}}
|
||
|
{{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-shortshort"}}
|
||
|
{{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-short"}}
|
||
|
{{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-long"}}
|
||
|
{{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-longlong"}}
|
||
|
{{range .Fields}} if err = binary.Read(r, binary.BigEndian, &msg.{{. | $.FieldName}}); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-timestamp"}}
|
||
|
{{range .Fields}} if msg.{{. | $.FieldName}}, err = readTimestamp(r); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-shortstr"}}
|
||
|
{{range .Fields}} if msg.{{. | $.FieldName}}, err = readShortstr(r); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-longstr"}}
|
||
|
{{range .Fields}} if msg.{{. | $.FieldName}}, err = readLongstr(r); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
{{define "dec-table"}}
|
||
|
{{range .Fields}} if msg.{{. | $.FieldName}}, err = readTable(r); err != nil { return }
|
||
|
{{end}}
|
||
|
{{end}}
|
||
|
|
||
|
`))
|
||
|
)
|
||
|
|
||
|
func (c *Constant) IsError() bool {
|
||
|
return strings.Contains(c.Class, "error")
|
||
|
}
|
||
|
|
||
|
func (c *Constant) IsSoftError() bool {
|
||
|
return c.Class == "soft-error"
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) Partial(prefix string, fields []fieldset) (s string, err error) {
|
||
|
var buf bytes.Buffer
|
||
|
for _, set := range fields {
|
||
|
name := prefix + set.AmqpType
|
||
|
t := packageTemplate.Lookup(name)
|
||
|
if t == nil {
|
||
|
return "", errors.New(fmt.Sprintf("Missing template: %s", name))
|
||
|
}
|
||
|
if err = t.Execute(&buf, set); err != nil {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
return string(buf.Bytes()), nil
|
||
|
}
|
||
|
|
||
|
// Groups the fields so that the right encoder/decoder can be called
|
||
|
func (renderer *renderer) Fieldsets(fields []Field) (f []fieldset, err error) {
|
||
|
if len(fields) > 0 {
|
||
|
for _, field := range fields {
|
||
|
cur := fieldset{}
|
||
|
cur.AmqpType, err = renderer.FieldType(field)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cur.NativeType, err = renderer.NativeType(cur.AmqpType)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
cur.Fields = append(cur.Fields, field)
|
||
|
f = append(f, cur)
|
||
|
}
|
||
|
|
||
|
i, j := 0, 1
|
||
|
for j < len(f) {
|
||
|
if f[i].AmqpType == f[j].AmqpType {
|
||
|
f[i].Fields = append(f[i].Fields, f[j].Fields...)
|
||
|
} else {
|
||
|
i++
|
||
|
f[i] = f[j]
|
||
|
}
|
||
|
j++
|
||
|
}
|
||
|
return f[:i+1], nil
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) HasType(typ string, method Method) bool {
|
||
|
for _, f := range method.Fields {
|
||
|
name, _ := renderer.FieldType(f)
|
||
|
if name == typ {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) HasField(field string, method Method) bool {
|
||
|
for _, f := range method.Fields {
|
||
|
name := renderer.FieldName(f)
|
||
|
if name == field {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) Domain(field Field) (domain Domain, err error) {
|
||
|
for _, domain = range renderer.Root.Domains {
|
||
|
if field.Domain == domain.Name {
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
return domain, nil
|
||
|
//return domain, ErrUnknownDomain
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) FieldName(field Field) (t string) {
|
||
|
t = public(field.Name)
|
||
|
|
||
|
if field.Reserved {
|
||
|
t = strings.ToLower(t)
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) FieldType(field Field) (t string, err error) {
|
||
|
t = field.Type
|
||
|
|
||
|
if t == "" {
|
||
|
var domain Domain
|
||
|
domain, err = renderer.Domain(field)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
t = domain.Type
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) NativeType(amqpType string) (t string, err error) {
|
||
|
if t, ok := amqpTypeToNative[amqpType]; ok {
|
||
|
return t, nil
|
||
|
}
|
||
|
return "", ErrUnknownType
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) Tag(d Domain) string {
|
||
|
label := "`"
|
||
|
|
||
|
label += `domain:"` + d.Name + `"`
|
||
|
|
||
|
if len(d.Type) > 0 {
|
||
|
label += `,type:"` + d.Type + `"`
|
||
|
}
|
||
|
|
||
|
label += "`"
|
||
|
|
||
|
return label
|
||
|
}
|
||
|
|
||
|
func (renderer *renderer) StructName(parts ...string) string {
|
||
|
return parts[0] + public(parts[1:]...)
|
||
|
}
|
||
|
|
||
|
func clean(body string) (res string) {
|
||
|
return strings.Replace(body, "\r", "", -1)
|
||
|
}
|
||
|
|
||
|
func private(parts ...string) string {
|
||
|
return export(regexp.MustCompile(`[-_]\w`), parts...)
|
||
|
}
|
||
|
|
||
|
func public(parts ...string) string {
|
||
|
return export(regexp.MustCompile(`^\w|[-_]\w`), parts...)
|
||
|
}
|
||
|
|
||
|
func export(delim *regexp.Regexp, parts ...string) (res string) {
|
||
|
for _, in := range parts {
|
||
|
|
||
|
res += delim.ReplaceAllStringFunc(in, func(match string) string {
|
||
|
switch len(match) {
|
||
|
case 1:
|
||
|
return strings.ToUpper(match)
|
||
|
case 2:
|
||
|
return strings.ToUpper(match[1:])
|
||
|
}
|
||
|
panic("unreachable")
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func main() {
|
||
|
var r renderer
|
||
|
|
||
|
spec, err := ioutil.ReadAll(os.Stdin)
|
||
|
if err != nil {
|
||
|
log.Fatalln("Please pass spec on stdin", err)
|
||
|
}
|
||
|
|
||
|
err = xml.Unmarshal(spec, &r.Root)
|
||
|
|
||
|
if err != nil {
|
||
|
log.Fatalln("Could not parse XML:", err)
|
||
|
}
|
||
|
|
||
|
if err = packageTemplate.Execute(os.Stdout, &r); err != nil {
|
||
|
log.Fatalln("Generate error: ", err)
|
||
|
}
|
||
|
}
|