first commit

This commit is contained in:
xuthus5 2024-03-19 22:49:04 +08:00
commit 993f06cb18
Signed by: xuthus5
GPG Key ID: A23CF9620CBB55F9
12 changed files with 967 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.idea

51
aligned.go Normal file
View File

@ -0,0 +1,51 @@
package protofmt
import "strings"
import "bytes"
type aligned struct {
source string
left bool
padding bool
}
var (
alignedEquals = leftAligned(" = ")
alignedShortEquals = leftAligned("=")
alignedSpace = leftAligned(" ")
alignedComma = leftAligned(", ")
alignedEmpty = leftAligned("")
alignedSemicolon = notAligned(";")
alignedColon = notAligned(":")
)
func leftAligned(src string) aligned { return aligned{src, true, true} }
func rightAligned(src string) aligned { return aligned{src, false, true} }
func notAligned(src string) aligned { return aligned{src, false, false} }
func (a aligned) preferredWidth() int {
if !a.hasAlignment() {
return 0 // means do not force padding because of this source
}
return len(a.source)
}
func (a aligned) formatted(indentSeparator string, indentLevel, width int) string {
if !a.padding {
// if the source has newlines then make sure the correct indent level is applied
buf := new(bytes.Buffer)
for _, each := range a.source {
buf.WriteRune(each)
if '\n' == each {
buf.WriteString(strings.Repeat(indentSeparator, indentLevel))
}
}
return buf.String()
}
if a.left {
return a.source + strings.Repeat(" ", width-len(a.source))
}
return strings.Repeat(" ", width-len(a.source)) + a.source
}
func (a aligned) hasAlignment() bool { return a.left || a.padding }

206
columns.go Normal file
View File

@ -0,0 +1,206 @@
package protofmt
import (
"bytes"
"fmt"
"io"
"strconv"
"github.com/emicklei/proto"
)
func columns(v proto.Visitee) []aligned {
return asColumnsPrintable(v).columns()
}
// columnsPrintable is for elements that can be printed in aligned columns.
type columnsPrintable interface {
columns() (cols []aligned)
}
type columnsPrinter struct {
cols []aligned
visitee proto.Visitee
}
func asColumnsPrintable(v proto.Visitee) *columnsPrinter {
p := new(columnsPrinter)
p.visitee = v
return p
}
// columns is part of columnsPrintable
func (p *columnsPrinter) columns() []aligned {
p.visitee.Accept(p)
return p.cols
}
func (p *columnsPrinter) VisitMessage(m *proto.Message) {}
func (p *columnsPrinter) VisitService(v *proto.Service) {}
func (p *columnsPrinter) VisitSyntax(s *proto.Syntax) {}
func (p *columnsPrinter) VisitPackage(pkg *proto.Package) {
p.cols = append(p.cols, notAligned("package "), notAligned(pkg.Name), alignedSemicolon)
if pkg.InlineComment != nil {
p.cols = append(p.cols, notAligned(" //"), notAligned(pkg.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitOption(o *proto.Option) {
if !o.IsEmbedded {
p.cols = append(p.cols, leftAligned("option "))
} else {
p.cols = append(p.cols, leftAligned(" ["))
}
p.cols = append(p.cols, keyValuePair(o, o.IsEmbedded)...)
if o.IsEmbedded {
p.cols = append(p.cols, leftAligned("]"))
}
if !o.IsEmbedded {
p.cols = append(p.cols, alignedSemicolon)
if o.InlineComment != nil {
p.cols = append(p.cols, notAligned(" //"), notAligned(o.InlineComment.Message()))
}
}
}
func (p *columnsPrinter) VisitImport(i *proto.Import) {
p.cols = append(p.cols, leftAligned("import"), alignedSpace)
if len(i.Kind) > 0 {
p.cols = append(p.cols, leftAligned(i.Kind), alignedSpace)
}
p.cols = append(p.cols, notAligned(fmt.Sprintf("%q", i.Filename)), alignedSemicolon)
if i.InlineComment != nil {
p.cols = append(p.cols, notAligned(" //"), notAligned(i.InlineComment.Message()))
}
}
// VisitNormalField
// [|repeated][|optional][space][name][equals][sequence][|option]
func (p *columnsPrinter) VisitNormalField(f *proto.NormalField) {
if f.Repeated {
p.cols = append(p.cols, leftAligned("repeated "))
} else if f.Optional {
p.cols = append(p.cols, leftAligned("optional "))
} else if f.Required {
p.cols = append(p.cols, leftAligned("required "))
} else {
p.cols = append(p.cols, alignedEmpty)
}
p.cols = append(p.cols, leftAligned(f.Type), alignedSpace, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Sequence)))
if len(f.Options) > 0 {
p.cols = append(p.cols, leftAligned(" ["))
for i, each := range f.Options {
if i > 0 {
p.cols = append(p.cols, alignedComma)
}
p.cols = append(p.cols, keyValuePair(each, true)...)
}
p.cols = append(p.cols, leftAligned("]"))
}
p.cols = append(p.cols, alignedSemicolon)
if f.InlineComment != nil {
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitEnumField(f *proto.EnumField) {
p.cols = append(p.cols, leftAligned(f.Name), alignedEquals, rightAligned(strconv.Itoa(f.Integer)))
if f.ValueOption != nil {
p.cols = append(p.cols, columns(f.ValueOption)...)
}
p.cols = append(p.cols, alignedSemicolon)
if f.InlineComment != nil {
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitEnum(e *proto.Enum) {}
func (p *columnsPrinter) VisitComment(e *proto.Comment) {}
func (p *columnsPrinter) VisitOneof(o *proto.Oneof) {}
func (p *columnsPrinter) VisitOneofField(o *proto.OneOfField) {
p.cols = append(p.cols,
leftAligned(o.Type),
alignedSpace,
leftAligned(o.Name),
alignedEquals,
rightAligned(strconv.Itoa(o.Sequence)))
if len(o.Options) > 0 {
p.cols = append(p.cols, leftAligned(" ["))
for i, each := range o.Options {
if i > 0 {
p.cols = append(p.cols, alignedComma)
}
p.cols = append(p.cols, keyValuePair(each, true)...)
}
p.cols = append(p.cols, leftAligned("]"))
}
p.cols = append(p.cols, alignedSemicolon)
if o.InlineComment != nil {
p.cols = append(p.cols, notAligned(" //"), notAligned(o.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitReserved(rs *proto.Reserved) {}
func (p *columnsPrinter) VisitRPC(r *proto.RPC) {
p.cols = append(p.cols,
leftAligned("rpc "),
leftAligned(r.Name),
leftAligned(" ("))
if r.StreamsRequest {
p.cols = append(p.cols, leftAligned("stream "))
} else {
p.cols = append(p.cols, alignedEmpty)
}
p.cols = append(p.cols,
leftAligned(r.RequestType),
leftAligned(") "),
leftAligned("returns"),
leftAligned(" ("))
if r.StreamsReturns {
p.cols = append(p.cols, leftAligned("stream "))
} else {
p.cols = append(p.cols, alignedEmpty)
}
p.cols = append(p.cols,
leftAligned(r.ReturnsType),
leftAligned(")"))
if len(r.Elements) > 0 {
buf := new(bytes.Buffer)
io.WriteString(buf, " {\n")
f := NewFormatter(buf, " ") // TODO get separator, now 2 spaces
f.level(1)
for _, each := range r.Elements {
each.Accept(f)
io.WriteString(buf, "\n")
}
f.indent(-1)
io.WriteString(buf, "}")
p.cols = append(p.cols, notAligned(buf.String()))
} else {
p.cols = append(p.cols, alignedSemicolon)
}
if r.InlineComment != nil {
p.cols = append(p.cols, notAligned(" //"), notAligned(r.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitMapField(f *proto.MapField) {
p.cols = append(p.cols,
alignedEmpty, // no repeated no optional
leftAligned(fmt.Sprintf("map <%s,%s>", f.KeyType, f.Type)),
alignedSpace,
leftAligned(f.Name),
alignedEquals,
rightAligned(strconv.Itoa(f.Sequence)))
if len(f.Options) > 0 {
p.cols = append(p.cols, leftAligned(" ["))
for i, each := range f.Options {
if i > 0 {
p.cols = append(p.cols, alignedComma)
}
p.cols = append(p.cols, keyValuePair(each, true)...)
}
p.cols = append(p.cols, leftAligned("]"))
}
p.cols = append(p.cols, alignedSemicolon)
if f.InlineComment != nil {
p.cols = append(p.cols, alignedInlinePrefix(f.InlineComment), notAligned(f.InlineComment.Message()))
}
}
func (p *columnsPrinter) VisitGroup(g *proto.Group) {}
func (p *columnsPrinter) VisitExtensions(e *proto.Extensions) {}

47
extensions.go Normal file
View File

@ -0,0 +1,47 @@
package protofmt
import "github.com/emicklei/proto"
// keyValuePair returns key = value or "value"
func keyValuePair(o *proto.Option, embedded bool) (cols []aligned) {
equals := alignedEquals
name := o.Name
if len(o.Constant.OrderedMap) > 0 {
cols = append(cols, leftAligned(name), equals)
cols = append(cols, columnsPrintablesFromMap(o.Constant.OrderedMap)...)
return
}
if embedded {
return append(cols, leftAligned(name), equals, leftAligned(o.Constant.SourceRepresentation())) // numbers right, strings left? TODO
}
return append(cols, rightAligned(name), equals, rightAligned(o.Constant.SourceRepresentation()))
}
func alignedInlinePrefix(c *proto.Comment) aligned {
prefix := " //"
if c.ExtraSlash {
prefix = " ///"
}
return notAligned(prefix)
}
func columnsPrintables(c *proto.Comment) (list []columnsPrintable) {
for _, each := range c.Lines {
list = append(list, inlineComment{each, c.ExtraSlash})
}
return
}
func typeAssertColumnsPrintable(v proto.Visitee) (columnsPrintable, bool) {
return asColumnsPrintable(v), len(asColumnsPrintable(v).columns()) > 0
}
func columnsPrintablesFromMap(m proto.LiteralMap) (cols []aligned) {
cols = append(cols, leftAligned("{"), alignedSpace)
for _, each := range m {
// TODO only works for simple constants
cols = append(cols, leftAligned(each.Name), alignedColon, leftAligned(each.SourceRepresentation()))
}
cols = append(cols, alignedSpace, leftAligned("}"))
return
}

233
formatter.go Normal file
View File

@ -0,0 +1,233 @@
package protofmt
import (
"fmt"
"io"
"github.com/emicklei/proto"
)
// Formatter visits a Proto and writes formatted source.
type Formatter struct {
w io.Writer
indentSeparator string
indentLevel int
lastStmt string
lastLevel int
}
// NewFormatter returns a new Formatter. Only the indentation separator is configurable.
func NewFormatter(writer io.Writer, indentSeparator string) *Formatter {
return &Formatter{w: writer, indentSeparator: indentSeparator}
}
// Format visits all proto elements and writes formatted source.
func (f *Formatter) Format(p *proto.Proto) {
for _, each := range p.Elements {
each.Accept(f)
}
}
// VisitComment formats a Comment and writes enclosing newlines.
func (f *Formatter) VisitComment(c *proto.Comment) {
f.printComment(c)
f.nl()
}
// VisitEnum formats a Enum.
func (f *Formatter) VisitEnum(e *proto.Enum) {
f.begin("enum", e)
if _, ok := e.Parent.(*proto.Message); !ok && (e.Comment == nil || len(e.Comment.Lines) == 0) {
f.nl()
}
fmt.Fprintf(f.w, "enum %s {", e.Name)
if len(e.Elements) > 0 {
f.nl()
f.level(1)
f.printAsGroups(e.Elements)
f.indent(-1)
}
io.WriteString(f.w, "}\n")
f.end("enum")
}
// VisitEnumField formats a EnumField.
func (f *Formatter) VisitEnumField(e *proto.EnumField) {
f.printAsGroups([]proto.Visitee{e})
}
// VisitImport formats a Import.
func (f *Formatter) VisitImport(i *proto.Import) {
f.beginNoDoc("import", i)
f.printAsGroups([]proto.Visitee{i})
f.end("import")
}
// VisitMessage formats a Message.
func (f *Formatter) VisitMessage(m *proto.Message) {
f.begin("message", m)
if _, ok := m.Parent.(*proto.Message); !ok && (m.Comment == nil || len(m.Comment.Lines) == 0) {
f.nl()
}
if m.IsExtend {
fmt.Fprintf(f.w, "extend ")
} else {
fmt.Fprintf(f.w, "message ")
}
fmt.Fprintf(f.w, "%s {", m.Name)
if len(m.Elements) > 0 {
f.nl()
f.level(1)
f.printAsGroups(m.Elements)
f.indent(-1)
}
io.WriteString(f.w, "}\n")
f.end("message")
}
// VisitOption formats a Option.
func (f *Formatter) VisitOption(o *proto.Option) {
f.begin("option", o)
fmt.Fprintf(f.w, "option %s = ", o.Name)
f.formatLiteral(&o.Constant)
fmt.Fprintf(f.w, ";\n")
if o.InlineComment != nil {
fmt.Fprintf(f.w, " //%s", o.InlineComment.Message())
}
f.end("option")
}
func (f *Formatter) formatLiteral(l *proto.Literal) {
if len(l.OrderedMap) == 0 && len(l.Array) == 0 {
fmt.Fprintf(f.w, "%s", l.SourceRepresentation())
return
}
fmt.Fprintf(f.w, "{\n")
for _, other := range l.OrderedMap {
f.indent(1)
fmt.Fprintf(f.w, "%s", other.Name)
if other.PrintsColon {
fmt.Fprintf(f.w, ": ")
}
f.formatLiteral(other.Literal)
f.level(-1)
f.nl()
}
f.indent(0)
fmt.Fprintf(f.w, "}")
}
// VisitPackage formats a Package.
func (f *Formatter) VisitPackage(p *proto.Package) {
f.begin("package", p)
f.printAsGroups([]proto.Visitee{p})
f.end("package")
}
// VisitService formats a Service.
func (f *Formatter) VisitService(s *proto.Service) {
f.begin("service", s)
fmt.Fprintf(f.w, "service %s {", s.Name)
if len(s.Elements) > 0 {
f.nl()
f.level(1)
f.printAsGroups(s.Elements)
f.indent(-1)
}
io.WriteString(f.w, "}\n\n")
f.end("service")
}
// VisitSyntax formats a Syntax.
func (f *Formatter) VisitSyntax(s *proto.Syntax) {
f.begin("syntax", s)
fmt.Fprintf(f.w, "syntax = %q", s.Value)
f.endWithComment(s.InlineComment)
f.end("syntax")
}
// VisitOneof formats a Oneof.
func (f *Formatter) VisitOneof(o *proto.Oneof) {
f.begin("oneof", o)
fmt.Fprintf(f.w, "oneof %s {", o.Name)
if len(o.Elements) > 0 {
f.nl()
f.level(1)
f.printAsGroups(o.Elements)
f.indent(-1)
}
io.WriteString(f.w, "}\n")
f.end("oneof")
}
// VisitOneofField formats a OneofField.
func (f *Formatter) VisitOneofField(o *proto.OneOfField) {
f.printAsGroups([]proto.Visitee{o})
}
// VisitReserved formats a Reserved.
func (f *Formatter) VisitReserved(r *proto.Reserved) {
f.begin("reserved", r)
io.WriteString(f.w, "reserved ")
if len(r.Ranges) > 0 {
for i, each := range r.Ranges {
if i > 0 {
io.WriteString(f.w, ", ")
}
fmt.Fprintf(f.w, "%s", each.SourceRepresentation())
}
} else {
for i, each := range r.FieldNames {
if i > 0 {
io.WriteString(f.w, ", ")
}
fmt.Fprintf(f.w, "%q", each)
}
}
f.endWithComment(r.InlineComment)
}
// VisitRPC formats a RPC.
func (f *Formatter) VisitRPC(r *proto.RPC) {
f.printAsGroups([]proto.Visitee{r})
}
// VisitMapField formats a MapField.
func (f *Formatter) VisitMapField(m *proto.MapField) {
f.printAsGroups([]proto.Visitee{m})
}
// VisitNormalField formats a NormalField.
func (f *Formatter) VisitNormalField(f1 *proto.NormalField) {
f.printAsGroups([]proto.Visitee{f1})
}
// VisitGroup formats a proto2 Group.
func (f *Formatter) VisitGroup(g *proto.Group) {
f.begin("group", g)
if g.Optional {
io.WriteString(f.w, "optional ")
}
fmt.Fprintf(f.w, "group %s = %d {", g.Name, g.Sequence)
if len(g.Elements) > 0 {
f.nl()
f.level(1)
f.printAsGroups(g.Elements)
f.indent(-1)
}
io.WriteString(f.w, "}\n")
f.end("group")
}
// VisitExtensions formats a proto2 Extensions.
func (f *Formatter) VisitExtensions(e *proto.Extensions) {
f.begin("extensions", e)
io.WriteString(f.w, "extensions ")
for i, each := range e.Ranges {
if i > 0 {
io.WriteString(f.w, ", ")
}
fmt.Fprintf(f.w, "%s", each.SourceRepresentation())
}
f.endWithComment(e.InlineComment)
}

22
formatter_test.go Normal file
View File

@ -0,0 +1,22 @@
package protofmt
import (
"bytes"
"github.com/stretchr/testify/assert"
"os"
"testing"
"github.com/emicklei/proto"
)
func TestPrint(t *testing.T) {
def, err := os.ReadFile("testdata.proto")
assert.NoError(t, err)
parser := proto.NewParser(bytes.NewReader(def))
parse, err := parser.Parse()
assert.NoError(t, err)
var buf = new(bytes.Buffer)
NewFormatter(buf, " ").Format(parse)
err = os.WriteFile("testdata.proto", buf.Bytes(), os.ModePerm)
assert.NoError(t, err)
}

14
go.mod Normal file
View File

@ -0,0 +1,14 @@
module gitter.top/common/protofmt
go 1.21
require (
github.com/emicklei/proto v1.13.2
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

12
go.sum Normal file
View File

@ -0,0 +1,12 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emicklei/proto v1.13.2 h1:z/etSFO3uyXeuEsVPzfl56WNgzcvIr42aQazXaQmFZY=
github.com/emicklei/proto v1.13.2/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

17
inline_comment.go Normal file
View File

@ -0,0 +1,17 @@
package protofmt
type inlineComment struct {
line string
extraSlash bool
}
func (i inlineComment) columns() (list []aligned) {
if len(i.line) == 0 {
return append(list, notAligned(""))
}
prefix := "//"
if i.extraSlash {
prefix = "///"
}
return append(list, notAligned(prefix+i.line))
}

35
reflector.go Normal file
View File

@ -0,0 +1,35 @@
package protofmt
import "github.com/emicklei/proto"
// reflector is a Visitor that can tell the short type name of a Visitee.
type reflector struct {
name string
}
// sole instance of reflector
var namer = new(reflector)
func (r *reflector) VisitMessage(m *proto.Message) { r.name = "Message" }
func (r *reflector) VisitService(v *proto.Service) { r.name = "Service" }
func (r *reflector) VisitSyntax(s *proto.Syntax) { r.name = "Syntax" }
func (r *reflector) VisitPackage(p *proto.Package) { r.name = "Package" }
func (r *reflector) VisitOption(o *proto.Option) { r.name = "Option" }
func (r *reflector) VisitImport(i *proto.Import) { r.name = "Import" }
func (r *reflector) VisitNormalField(i *proto.NormalField) { r.name = "NormalField" }
func (r *reflector) VisitEnumField(i *proto.EnumField) { r.name = "EnumField" }
func (r *reflector) VisitEnum(e *proto.Enum) { r.name = "Enum" }
func (r *reflector) VisitComment(e *proto.Comment) { r.name = "Comment" }
func (r *reflector) VisitOneof(o *proto.Oneof) { r.name = "Oneof" }
func (r *reflector) VisitOneofField(o *proto.OneOfField) { r.name = "OneOfField" }
func (r *reflector) VisitReserved(rs *proto.Reserved) { r.name = "Reserved" }
func (r *reflector) VisitRPC(rpc *proto.RPC) { r.name = "RPC" }
func (r *reflector) VisitMapField(f *proto.MapField) { r.name = "NormalField" } // handle as
func (r *reflector) VisitGroup(g *proto.Group) { r.name = "Group" }
func (r *reflector) VisitExtensions(e *proto.Extensions) { r.name = "Extensions" }
// nameOfVisitee returns the short type name of a Visitee.
func nameOfVisitee(e proto.Visitee) string {
e.Accept(namer)
return namer.name
}

139
testdata.proto Normal file
View File

@ -0,0 +1,139 @@
syntax = "proto3";
package api_services.v1;
option go_package = "filesaver/gen;genv1";
// @route_group: true
// @base_url: /v1/file
// @gen_to: ./services/controller/v1/file_controller.go
service FileService {
// @desc:
// @author: Young Xu
// @method: GET
// @api: /list
rpc List (FileServiceListRequest) returns (FileServiceListResponse);
// @desc:
// @author: Young Xu
// @method: POST
// @api: /upload
rpc Upload (FileServiceUploadRequest) returns (FileServiceUploadResponse);
// @desc:
// @author: Young Xu
// @method: DELETE
// @api: /delete
rpc Delete (FileServiceDeleteRequest) returns (FileServiceDeleteResponse);
// @desc:
// @author: Young Xu
// @method: GET
// @api: /download
rpc Download (FileServiceDownloadRequest) returns (FileServiceDownloadResponse);
}
message FileServiceListRequest {
string dirname = 1; //
}
message FileServiceListResponse {
message Metadata {
string domain = 1; //
}
message Item {
string file_id = 1; // ID
string filename = 2; //
string file_size = 3; //
string created_at = 4; //
string dirname = 5; //
bool is_directory = 6; //
}
repeated Item items = 1; //
Metadata metadata = 2; //
}
message FileServiceUploadRequest {}
message FileServiceUploadResponse {}
message FileServiceDeleteRequest {
string file_id = 1; // ID
string dirname = 2; //
string filename = 3; //
}
message FileServiceDeleteResponse {}
message FileServiceDownloadRequest {
string file_id = 1; // ID
string dirname = 2; //
string filename = 3; //
}
message FileServiceDownloadResponse {}
// @route_group: true
// @base_url: /v1/setting
// @gen_to: ./services/controller/v1/setting_controller.go
service SettingService {
// @desc:
// @author: Young Xu
// @method: POST
// @api: /adapter
rpc SettingServiceChangeAdapter (SettingServiceChangeAdapterReq) returns (SettingServiceChangeAdapterResp);
// @desc:
// @author: Young Xu
// @method: POST
// @api: /cf_r2
rpc ConfigCloudFlareR2 (SettingServiceConfigCloudFlareR2Req) returns (SettingServiceConfigCloudFlareR2Resp);
// @desc: cloudflare r2配置
// @author: Young Xu
// @method: GET
// @api: /cf_r2
rpc GetCloudFlareR2 (SettingServiceGetCloudFlareR2Req) returns (SettingServiceGetCloudFlareR2Resp);
// @desc:
// @author: Young Xu
// @method: GET
// @api: /adapter
rpc GetAdapter (SettingServiceGetAdapterReq) returns (SettingServiceGetAdapterResp);
}
message SettingServiceChangeAdapterReq {
string adapter = 1;
}
message SettingServiceChangeAdapterResp {}
message SettingServiceConfigCloudFlareR2Req {
string api_token = 1;
string user_email = 2;
string user_id = 3;
string bucket = 4;
string s3_api = 5;
string domain = 6;
string secret_id = 7;
string secret_key = 8;
}
message SettingServiceConfigCloudFlareR2Resp {}
message SettingServiceGetCloudFlareR2Req {}
message SettingServiceGetCloudFlareR2Resp {
string api_token = 1;
string user_email = 2;
string user_id = 3;
string bucket = 4;
string s3_api = 5;
string domain = 6;
string secret_id = 7;
string secret_key = 8;
}
message SettingServiceGetAdapterReq {}
message SettingServiceGetAdapterResp {
string adapter = 1;
}

190
utils.go Normal file
View File

@ -0,0 +1,190 @@
package protofmt
import (
"fmt"
"io"
"strings"
"github.com/emicklei/proto"
)
// printDoc writes documentation is available
func (f *Formatter) printDoc(v proto.Visitee) {
if hasDoc, ok := v.(proto.Documented); ok {
if doc := hasDoc.Doc(); doc != nil {
f.printComment(doc)
}
}
}
// printComment formats a Comment.
func (f *Formatter) printComment(c *proto.Comment) {
f.nl()
if c.Cstyle {
f.indent(0)
fmt.Fprintln(f.w, "/*")
}
for i, each := range c.Lines {
each = strings.TrimRight(each, " ")
f.indent(0)
if c.Cstyle {
// only skip first and last empty lines
skip := (i == 0 && len(each) == 0) ||
(i == len(c.Lines)-1 && len(each) == 0)
if !skip {
fmt.Fprintf(f.w, "%s\n", each)
}
} else {
if c.ExtraSlash {
fmt.Fprint(f.w, "/")
}
fmt.Fprintf(f.w, "//%s\n", each)
}
}
if c.Cstyle {
fmt.Fprintf(f.w, "*/\n")
}
}
// begin writes a newline if the last statement kind is different. always indents.
// if the Visitee has comment then print it.
func (f *Formatter) begin(stmt string, v proto.Visitee) {
// if not the first statement and different from last and on same indent level.
if len(f.lastStmt) > 0 && f.lastStmt != stmt && f.lastLevel == f.indentLevel {
f.nl()
}
f.lastStmt = stmt
f.printDoc(v)
f.indent(0)
}
// beginNoDoc writes a newline if the last statement kind is different. always indents.
func (f *Formatter) beginNoDoc(stmt string, v proto.Visitee) {
// if not the first statement and different from last and on same indent level.
if len(f.lastStmt) > 0 && f.lastStmt != stmt && f.lastLevel == f.indentLevel {
f.nl()
}
f.lastStmt = stmt
f.indent(0)
}
func (f *Formatter) end(stmt string) {
f.lastStmt = stmt
}
// indent changes the indent level and writes indentation.
func (f *Formatter) indent(diff int) {
f.level(diff)
for i := 0; i < f.indentLevel; i++ {
io.WriteString(f.w, f.indentSeparator)
}
}
func (f *Formatter) printListOfColumns(list []columnsPrintable) {
if len(list) == 0 {
return
}
// collect all column values
values := [][]aligned{}
widths := map[int]int{}
for _, each := range list {
cols := each.columns()
values = append(values, cols)
// update max widths per column
for i, other := range cols {
pw := other.preferredWidth()
w, ok := widths[i]
if ok {
if pw > w {
widths[i] = pw
}
} else {
widths[i] = pw
}
}
}
// now print all values
for _, each := range values {
f.indent(0)
for c := 0; c < len(widths); c++ {
pw := widths[c]
// only print if there is a value
if c < len(each) {
// using space padding to match the max width
io.WriteString(f.w, each[c].formatted(f.indentSeparator, f.indentLevel, pw))
}
}
f.nl()
}
}
// nl writes a newline.
func (f *Formatter) nl() {
io.WriteString(f.w, "\n")
}
// level changes the current indentLevel but does not print indentation.
func (f *Formatter) level(diff int) {
f.lastLevel = f.indentLevel
f.indentLevel += diff
}
// printAsGroups prints the list in groups of the same element type.
func (f *Formatter) printAsGroups(list []proto.Visitee) {
group := []columnsPrintable{}
lastGroupName := ""
for _, each := range list {
groupName := nameOfVisitee(each)
printable, isColumnsPrintable := typeAssertColumnsPrintable(each)
if isColumnsPrintable {
if lastGroupName != groupName {
lastGroupName = groupName
// print current group
if len(group) > 0 {
f.printListOfColumns(group)
// begin new group
group = []columnsPrintable{}
}
}
// comment as a group entity
if hasDoc, ok := each.(proto.Documented); ok {
if doc := hasDoc.Doc(); doc != nil {
f.printListOfColumns(group)
// begin new group
//group = []columnsPrintable{}
//if len(doc.Lines) > 0 { // if comment then add newline before it
// group = append(group, inlineComment{line: "", extraSlash: false})
//}
group = append([]columnsPrintable{}, columnsPrintables(doc)...)
}
}
group = append(group, printable)
} else {
// not printable in group
lastGroupName = groupName
// print current group
if len(group) > 0 {
f.printListOfColumns(group)
// begin new group
group = []columnsPrintable{}
}
each.Accept(f)
}
}
// print last group
f.printListOfColumns(group)
}
// endWithComment writes a statement end (;) followed by inline comment if present.
func (f *Formatter) endWithComment(commentOrNil *proto.Comment) {
io.WriteString(f.w, ";")
if commentOrNil != nil {
if commentOrNil.ExtraSlash {
io.WriteString(f.w, " ///")
} else {
io.WriteString(f.w, " //")
}
io.WriteString(f.w, commentOrNil.Message())
}
io.WriteString(f.w, "\n")
}