添加了对 proto 的解析支持
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
package zm_proto
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/emicklei/proto"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type Field struct {
|
||||
Name string
|
||||
Type string
|
||||
Repeated bool
|
||||
}
|
||||
|
||||
type Proto struct {
|
||||
Imports map[string]Import
|
||||
|
||||
Enum map[string]bool
|
||||
|
||||
Dir []string
|
||||
}
|
||||
|
||||
func NewProto() *Proto {
|
||||
return &Proto{
|
||||
Imports: make(map[string]Import),
|
||||
Enum: make(map[string]bool),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proto) AddImport(name string, imp Import) {
|
||||
p.Imports[name] = imp
|
||||
p.AddEnum(imp.Enum)
|
||||
}
|
||||
|
||||
func (p *Proto) AddEnum(name map[string]bool) {
|
||||
for k := range name {
|
||||
p.Enum[k] = true
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Proto) Include(dir ...string) *Proto {
|
||||
p.Dir = append(p.Dir, dir...)
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *Proto) FindPathByDir(protoImport string) string {
|
||||
for _, dir := range p.Dir {
|
||||
path := filepath.Join(dir, protoImport)
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return path
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *Proto) ParseAllImport() error {
|
||||
for _, dir := range p.Dir {
|
||||
readDir, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read dir %s: %w", dir, err)
|
||||
}
|
||||
for _, f := range readDir {
|
||||
if f.IsDir() {
|
||||
continue
|
||||
}
|
||||
if filepath.Ext(f.Name()) != ".proto" {
|
||||
continue
|
||||
}
|
||||
if p.Imports[f.Name()].Name != "" {
|
||||
continue
|
||||
}
|
||||
if err = p.parseImport(filepath.Join(dir, f.Name())); err != nil {
|
||||
return fmt.Errorf("failed to parse file %s: %w", f.Name(), err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Proto) ParseImport(protoImport string) error {
|
||||
path := p.FindPathByDir(protoImport)
|
||||
if path == "" {
|
||||
return fmt.Errorf("import %s not found", path)
|
||||
}
|
||||
err := p.parseImport(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse import %s: %w", path, err)
|
||||
}
|
||||
for _, i := range p.Imports[protoImport].Imports {
|
||||
if err = p.ParseImport(i); err != nil {
|
||||
return fmt.Errorf("failed to parse import %s: %w", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Proto) parseImport(path string) error {
|
||||
//ext := filepath.Ext(path)
|
||||
//name := filepath.Base(path)[:len(filepath.Base(path))-len(ext)]
|
||||
|
||||
name := filepath.Base(path)
|
||||
reader, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
parser := proto.NewParser(reader)
|
||||
definition, err := parser.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var msgs []*Message
|
||||
var enums = make(map[string]bool)
|
||||
var imports []string
|
||||
|
||||
var imp = Import{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Proto: p,
|
||||
}
|
||||
proto.Walk(definition,
|
||||
proto.WithImport(func(i *proto.Import) {
|
||||
//ext1 := filepath.Ext(i.Filename)
|
||||
//name1 := filepath.Base(i.Filename)[:len(filepath.Base(i.Filename))-len(ext1)]
|
||||
imports = append(imports, i.Filename)
|
||||
}),
|
||||
|
||||
proto.WithEnum(func(enum *proto.Enum) {
|
||||
enums[enum.Name] = true
|
||||
}),
|
||||
proto.WithNormalField(func(field *proto.NormalField) {
|
||||
lastmsg := msgs[len(msgs)-1]
|
||||
lastmsg.Fields = append(lastmsg.Fields, Field{
|
||||
Name: field.Name,
|
||||
Type: field.Type,
|
||||
Repeated: field.Repeated,
|
||||
})
|
||||
|
||||
}),
|
||||
proto.WithMessage(func(message *proto.Message) {
|
||||
msgs = append(msgs, &Message{
|
||||
Name: message.Name,
|
||||
Fields: []Field{},
|
||||
Import: &imp,
|
||||
})
|
||||
}))
|
||||
|
||||
imp.Imports = imports
|
||||
imp.Enum = enums
|
||||
|
||||
for _, msg := range msgs {
|
||||
imp.Messages = append(imp.Messages, *msg)
|
||||
}
|
||||
|
||||
p.AddImport(name, imp)
|
||||
return nil
|
||||
}
|
||||
|
||||
var defaultTypeMap = map[string]any{
|
||||
"int32": 0,
|
||||
"int64": 0,
|
||||
"bool": false,
|
||||
"string": "ok",
|
||||
"byte": "byte",
|
||||
}
|
||||
|
||||
func (p *Proto) MessageToMap(name string) (string, map[string]any) {
|
||||
var msg = make(map[string]any)
|
||||
var erlMod = ""
|
||||
for _, i := range p.Imports {
|
||||
for _, m := range i.Messages {
|
||||
if m.Name == name {
|
||||
ext := filepath.Ext(i.Name)
|
||||
erlMod = filepath.Base(i.Name)[:len(filepath.Base(i.Name))-len(ext)]
|
||||
|
||||
for _, field := range m.Fields {
|
||||
defaultValue := defaultTypeMap[field.Type]
|
||||
if defaultValue == nil {
|
||||
_, defaultValue = p.MessageToMap(field.Type)
|
||||
}
|
||||
if field.Repeated {
|
||||
if defaultValue == nil {
|
||||
defaultValue = []any{}
|
||||
}
|
||||
}
|
||||
msg[field.Name] = defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(msg) == 0 || erlMod == "" {
|
||||
return "single_str", nil
|
||||
}
|
||||
return erlMod, msg
|
||||
}
|
||||
|
||||
// MessageToErlMod z
|
||||
func (p *Proto) MessageToErlMod(name string) string {
|
||||
for _, i := range p.Imports {
|
||||
for _, m := range i.Messages {
|
||||
if m.Name == name {
|
||||
ext := filepath.Ext(i.Name)
|
||||
erlMod := filepath.Base(i.Name)[:len(filepath.Base(i.Name))-len(ext)]
|
||||
return erlMod
|
||||
}
|
||||
}
|
||||
}
|
||||
return "single_str"
|
||||
}
|
||||
Reference in New Issue
Block a user