@@ -0,0 +1,68 @@
|
||||
// Copyright (C) 2020 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/syncthing/syncthing/lib/config"
|
||||
)
|
||||
|
||||
func main() {
|
||||
new, err := os.Create("tags.csv")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(filepath.Abs(new.Name()))
|
||||
w := csv.NewWriter(new)
|
||||
w.Write([]string{
|
||||
"path", "json", "xml", "default", "restart",
|
||||
})
|
||||
walk(w, "", &config.Configuration{})
|
||||
w.Flush()
|
||||
new.Close()
|
||||
}
|
||||
|
||||
func walk(w *csv.Writer, prefix string, data interface{}) {
|
||||
s := reflect.ValueOf(data).Elem()
|
||||
t := s.Type()
|
||||
for i := 0; i < s.NumField(); i++ {
|
||||
f := s.Field(i)
|
||||
ft := t.Field(i)
|
||||
|
||||
for f.Kind() == reflect.Ptr {
|
||||
f = f.Elem()
|
||||
}
|
||||
|
||||
pfx := prefix + "." + s.Type().Field(i).Name
|
||||
if f.Kind() == reflect.Slice {
|
||||
slc := reflect.MakeSlice(f.Type(), 1, 1)
|
||||
f = slc.Index(0)
|
||||
pfx = prefix + "." + s.Type().Field(i).Name + "[]"
|
||||
}
|
||||
|
||||
if f.Kind() == reflect.Struct && strings.HasPrefix(f.Type().PkgPath(), "github.com/syncthing/syncthing") {
|
||||
walk(w, pfx, f.Addr().Interface())
|
||||
} else {
|
||||
jsonTag := ft.Tag.Get("json")
|
||||
xmlTag := ft.Tag.Get("xml")
|
||||
defaultTag := ft.Tag.Get("default")
|
||||
restartTag := ft.Tag.Get("restart")
|
||||
w.Write([]string{
|
||||
strings.ToLower(pfx), jsonTag, xmlTag, defaultTag, restartTag,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
// Copyright (C) 2020 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
//+build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/syncthing/syncthing/proto/ext"
|
||||
|
||||
"github.com/gogo/protobuf/gogoproto"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/gogo/protobuf/protoc-gen-gogo/descriptor"
|
||||
"github.com/gogo/protobuf/vanity"
|
||||
"github.com/gogo/protobuf/vanity/command"
|
||||
)
|
||||
|
||||
func main() {
|
||||
req := command.Read()
|
||||
files := req.GetProtoFile()
|
||||
files = vanity.FilterFiles(files, vanity.NotGoogleProtobufDescriptorProto)
|
||||
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoGettersAll)
|
||||
vanity.ForEachFile(files, TurnOnProtoSizerAll)
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoEnumPrefixAll)
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoUnrecognizedAll)
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoUnkeyedAll)
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoSizecacheAll)
|
||||
vanity.ForEachFile(files, vanity.TurnOffGoEnumStringerAll)
|
||||
vanity.ForEachEnumInFiles(files, HandleCustomEnumExtensions)
|
||||
vanity.ForEachFile(files, SetPackagePrefix("github.com/syncthing/syncthing"))
|
||||
|
||||
vanity.ForEachMessageInFiles(files, HandleCustomExtensions)
|
||||
vanity.ForEachFieldInFilesExcludingExtensions(files, TurnOffNullableForMessages)
|
||||
|
||||
resp := command.Generate(req)
|
||||
command.Write(resp)
|
||||
}
|
||||
|
||||
func TurnOnProtoSizerAll(file *descriptor.FileDescriptorProto) {
|
||||
vanity.SetBoolFileOption(gogoproto.E_ProtosizerAll, true)(file)
|
||||
}
|
||||
|
||||
func TurnOffNullableForMessages(field *descriptor.FieldDescriptorProto) {
|
||||
if !vanity.FieldHasBoolExtension(field, gogoproto.E_Nullable) {
|
||||
_, hasCustomType := GetFieldStringExtension(field, gogoproto.E_Customtype)
|
||||
if field.IsMessage() || hasCustomType {
|
||||
vanity.SetBoolFieldOption(gogoproto.E_Nullable, false)(field)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func HandleCustomEnumExtensions(enum *descriptor.EnumDescriptorProto) {
|
||||
for _, field := range enum.Value {
|
||||
if field == nil {
|
||||
continue
|
||||
}
|
||||
if field.Options == nil {
|
||||
field.Options = &descriptor.EnumValueOptions{}
|
||||
}
|
||||
customName := gogoproto.GetEnumValueCustomName(field)
|
||||
if customName != "" {
|
||||
continue
|
||||
}
|
||||
if v, ok := GetEnumValueStringExtension(field, ext.E_Enumgoname); ok {
|
||||
SetEnumValueStringFieldOption(field, gogoproto.E_EnumvalueCustomname, v)
|
||||
} else {
|
||||
SetEnumValueStringFieldOption(field, gogoproto.E_EnumvalueCustomname, toCamelCase(*field.Name, true))
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func SetPackagePrefix(prefix string) func(file *descriptor.FileDescriptorProto) {
|
||||
return func(file *descriptor.FileDescriptorProto) {
|
||||
if file.Options.GoPackage == nil {
|
||||
pkg, _ := filepath.Split(file.GetName())
|
||||
fullPkg := prefix + "/" + strings.TrimSuffix(pkg, "/")
|
||||
file.Options.GoPackage = &fullPkg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func toCamelCase(input string, firstUpper bool) string {
|
||||
runes := []rune(strings.ToLower(input))
|
||||
outputRunes := make([]rune, 0, len(runes))
|
||||
|
||||
nextUpper := false
|
||||
for i, rune := range runes {
|
||||
if rune == '_' {
|
||||
nextUpper = true
|
||||
continue
|
||||
}
|
||||
if (firstUpper && i == 0) || nextUpper {
|
||||
rune = unicode.ToUpper(rune)
|
||||
nextUpper = false
|
||||
}
|
||||
outputRunes = append(outputRunes, rune)
|
||||
}
|
||||
return string(outputRunes)
|
||||
}
|
||||
|
||||
func SetStringFieldOption(field *descriptor.FieldDescriptorProto, extension *proto.ExtensionDesc, value string) {
|
||||
if _, ok := GetFieldStringExtension(field, extension); ok {
|
||||
return
|
||||
}
|
||||
if field.Options == nil {
|
||||
field.Options = &descriptor.FieldOptions{}
|
||||
}
|
||||
if err := proto.SetExtension(field.Options, extension, &value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func SetEnumValueStringFieldOption(field *descriptor.EnumValueDescriptorProto, extension *proto.ExtensionDesc, value string) {
|
||||
if _, ok := GetEnumValueStringExtension(field, extension); ok {
|
||||
return
|
||||
}
|
||||
if field.Options == nil {
|
||||
field.Options = &descriptor.EnumValueOptions{}
|
||||
}
|
||||
if err := proto.SetExtension(field.Options, extension, &value); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetEnumValueStringExtension(enumValue *descriptor.EnumValueDescriptorProto, extension *proto.ExtensionDesc) (string, bool) {
|
||||
if enumValue.Options == nil {
|
||||
return "", false
|
||||
}
|
||||
value, err := proto.GetExtension(enumValue.Options, extension)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
if value == nil {
|
||||
return "", false
|
||||
}
|
||||
if v, ok := value.(*string); !ok || v == nil {
|
||||
return "", false
|
||||
} else {
|
||||
return *v, true
|
||||
}
|
||||
}
|
||||
|
||||
func GetFieldStringExtension(field *descriptor.FieldDescriptorProto, extension *proto.ExtensionDesc) (string, bool) {
|
||||
if field.Options == nil {
|
||||
return "", false
|
||||
}
|
||||
value, err := proto.GetExtension(field.Options, extension)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
if value == nil {
|
||||
return "", false
|
||||
}
|
||||
if v, ok := value.(*string); !ok || v == nil {
|
||||
return "", false
|
||||
} else {
|
||||
return *v, true
|
||||
}
|
||||
}
|
||||
|
||||
func GetFieldBooleanExtension(field *descriptor.FieldDescriptorProto, extension *proto.ExtensionDesc) (bool, bool) {
|
||||
if field.Options == nil {
|
||||
return false, false
|
||||
}
|
||||
value, err := proto.GetExtension(field.Options, extension)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
if value == nil {
|
||||
return false, false
|
||||
}
|
||||
if v, ok := value.(*bool); !ok || v == nil {
|
||||
return false, false
|
||||
} else {
|
||||
return *v, true
|
||||
}
|
||||
}
|
||||
|
||||
func GetMessageBoolExtension(msg *descriptor.DescriptorProto, extension *proto.ExtensionDesc) (bool, bool) {
|
||||
if msg.Options == nil {
|
||||
return false, false
|
||||
}
|
||||
value, err := proto.GetExtension(msg.Options, extension)
|
||||
if err != nil {
|
||||
return false, false
|
||||
}
|
||||
if value == nil {
|
||||
return false, false
|
||||
}
|
||||
val, ok := value.(*bool)
|
||||
if !ok || val == nil {
|
||||
return false, false
|
||||
}
|
||||
return *val, true
|
||||
}
|
||||
|
||||
func HandleCustomExtensions(msg *descriptor.DescriptorProto) {
|
||||
generateXmlTags := true
|
||||
if generate, ok := GetMessageBoolExtension(msg, ext.E_XmlTags); ok {
|
||||
generateXmlTags = generate
|
||||
}
|
||||
|
||||
vanity.ForEachField([]*descriptor.DescriptorProto{msg}, func(field *descriptor.FieldDescriptorProto) {
|
||||
if field.Options == nil {
|
||||
field.Options = &descriptor.FieldOptions{}
|
||||
}
|
||||
deprecated := field.Options.Deprecated != nil && *field.Options.Deprecated == true
|
||||
|
||||
if field.Type != nil && *field.Type == descriptor.FieldDescriptorProto_TYPE_INT32 {
|
||||
SetStringFieldOption(field, gogoproto.E_Casttype, "int")
|
||||
}
|
||||
|
||||
if field.TypeName != nil && *field.TypeName == ".google.protobuf.Timestamp" {
|
||||
vanity.SetBoolFieldOption(gogoproto.E_Stdtime, true)(field)
|
||||
}
|
||||
|
||||
if goName, ok := GetFieldStringExtension(field, ext.E_Goname); ok {
|
||||
SetStringFieldOption(field, gogoproto.E_Customname, goName)
|
||||
} else if deprecated {
|
||||
SetStringFieldOption(field, gogoproto.E_Customname, "Deprecated"+toCamelCase(*field.Name, true))
|
||||
}
|
||||
|
||||
if val, ok := GetFieldBooleanExtension(field, ext.E_DeviceId); ok && val {
|
||||
SetStringFieldOption(field, gogoproto.E_Customtype, "github.com/syncthing/syncthing/lib/protocol.DeviceID")
|
||||
}
|
||||
|
||||
if jsonValue, ok := GetFieldStringExtension(field, ext.E_Json); ok {
|
||||
SetStringFieldOption(field, gogoproto.E_Jsontag, jsonValue)
|
||||
} else if deprecated {
|
||||
SetStringFieldOption(field, gogoproto.E_Jsontag, "-")
|
||||
} else {
|
||||
SetStringFieldOption(field, gogoproto.E_Jsontag, toCamelCase(*field.Name, false))
|
||||
}
|
||||
|
||||
current := ""
|
||||
if v, ok := GetFieldStringExtension(field, gogoproto.E_Moretags); ok {
|
||||
current = v
|
||||
}
|
||||
|
||||
if generateXmlTags {
|
||||
if len(current) > 0 {
|
||||
current += " "
|
||||
}
|
||||
if xmlValue, ok := GetFieldStringExtension(field, ext.E_Xml); ok {
|
||||
current += fmt.Sprintf(`xml:"%s"`, xmlValue)
|
||||
} else {
|
||||
xmlValue = toCamelCase(*field.Name, false)
|
||||
// XML dictates element name within the collection, not collection name, so trim plural suffix.
|
||||
if field.IsRepeated() {
|
||||
if strings.HasSuffix(xmlValue, "ses") {
|
||||
// addresses -> address
|
||||
xmlValue = strings.TrimSuffix(xmlValue, "es")
|
||||
} else {
|
||||
// devices -> device
|
||||
xmlValue = strings.TrimSuffix(xmlValue, "s")
|
||||
}
|
||||
}
|
||||
if deprecated {
|
||||
xmlValue += ",omitempty"
|
||||
}
|
||||
current += fmt.Sprintf(`xml:"%s"`, xmlValue)
|
||||
}
|
||||
}
|
||||
|
||||
if defaultValue, ok := GetFieldStringExtension(field, ext.E_Default); ok {
|
||||
if len(current) > 0 {
|
||||
current += " "
|
||||
}
|
||||
current += fmt.Sprintf(`default:"%s"`, defaultValue)
|
||||
}
|
||||
|
||||
if restartValue, ok := GetFieldBooleanExtension(field, ext.E_Restart); ok {
|
||||
if len(current) > 0 {
|
||||
current += " "
|
||||
}
|
||||
current += fmt.Sprintf(`restart:"%t"`, restartValue)
|
||||
}
|
||||
|
||||
SetStringFieldOption(field, gogoproto.E_Moretags, current)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2016 The Syncthing Authors.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
// You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
for _, arg := range flag.Args() {
|
||||
matches, err := filepath.Glob(arg)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, file := range matches {
|
||||
if stat, err := os.Stat(file); err != nil {
|
||||
log.Fatal(err)
|
||||
} else if stat.IsDir() {
|
||||
err := filepath.Walk(file, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if filepath.Ext(path) == ".proto" {
|
||||
return formatProtoFile(path)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else {
|
||||
if err := formatProtoFile(file); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatProtoFile(file string) error {
|
||||
log.Println("Formatting", file)
|
||||
in, err := os.Open(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer in.Close()
|
||||
out, err := os.Create(file + ".tmp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer out.Close()
|
||||
if err := formatProto(in, out); err != nil {
|
||||
return err
|
||||
}
|
||||
in.Close()
|
||||
out.Close()
|
||||
return os.Rename(file+".tmp", file)
|
||||
}
|
||||
|
||||
func formatProto(in io.Reader, out io.Writer) error {
|
||||
sc := bufio.NewScanner(in)
|
||||
lineExp := regexp.MustCompile(`([^=]+)\s+([^=\s]+?)\s*=(.+)`)
|
||||
var tw *tabwriter.Writer
|
||||
for sc.Scan() {
|
||||
line := sc.Text()
|
||||
if strings.HasPrefix(line, "//") {
|
||||
if _, err := fmt.Fprintln(out, line); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ms := lineExp.FindStringSubmatch(line)
|
||||
for i := range ms {
|
||||
ms[i] = strings.TrimSpace(ms[i])
|
||||
}
|
||||
if len(ms) == 4 && ms[1] != "option" {
|
||||
typ := strings.Join(strings.Fields(ms[1]), " ")
|
||||
name := ms[2]
|
||||
id := ms[3]
|
||||
if tw == nil {
|
||||
tw = tabwriter.NewWriter(out, 4, 4, 1, ' ', 0)
|
||||
}
|
||||
if typ == "" {
|
||||
// We're in an enum
|
||||
fmt.Fprintf(tw, "\t%s\t= %s\n", name, id)
|
||||
} else {
|
||||
// Message
|
||||
fmt.Fprintf(tw, "\t%s\t%s\t= %s\n", typ, name, id)
|
||||
}
|
||||
} else {
|
||||
if tw != nil {
|
||||
if err := tw.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
tw = nil
|
||||
}
|
||||
if _, err := fmt.Fprintln(out, line); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user