diff options
author | Audrius Butkevicius <audrius.butkevicius@gmail.com> | 2020-08-25 07:11:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-08-25 08:11:14 +0200 |
commit | d507d932b8975a0c6efeef326d69bac1cab059d1 (patch) | |
tree | b844cdc4254cdc51d817484f0334cf4d35aec826 /proto | |
parent | 5b953033c752f4908804f723442c5c97cef712db (diff) | |
download | syncthing-d507d932b8975a0c6efeef326d69bac1cab059d1.tar.gz syncthing-d507d932b8975a0c6efeef326d69bac1cab059d1.zip |
all: Use protobuf to generate config structs (fixes #6734) (#6900)
Diffstat (limited to 'proto')
24 files changed, 1265 insertions, 0 deletions
diff --git a/proto/ext.proto b/proto/ext.proto new file mode 100644 index 000000000..1b3da600b --- /dev/null +++ b/proto/ext.proto @@ -0,0 +1,24 @@ +syntax = "proto2"; + +package ext; + +import "google/protobuf/descriptor.proto"; + +option go_package = "github.com/syncthing/syncthing/proto/ext"; + +extend google.protobuf.MessageOptions { + optional bool xml_tags = 74001; +} + +extend google.protobuf.FieldOptions { + optional string xml = 75005; + optional string json = 75006; + optional string default = 75007; + optional bool restart = 75008; + optional bool device_id = 75009; + optional string goname = 75010; +} + +extend google.protobuf.EnumValueOptions { + optional string enumgoname = 76010; +} diff --git a/proto/ext/ext.pb.go b/proto/ext/ext.pb.go new file mode 100644 index 000000000..82a6747c6 --- /dev/null +++ b/proto/ext/ext.pb.go @@ -0,0 +1,131 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: ext.proto + +package ext + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + descriptor "github.com/gogo/protobuf/protoc-gen-gogo/descriptor" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +var E_XmlTags = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.MessageOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 74001, + Name: "ext.xml_tags", + Tag: "varint,74001,opt,name=xml_tags", + Filename: "ext.proto", +} + +var E_Xml = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 75005, + Name: "ext.xml", + Tag: "bytes,75005,opt,name=xml", + Filename: "ext.proto", +} + +var E_Json = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 75006, + Name: "ext.json", + Tag: "bytes,75006,opt,name=json", + Filename: "ext.proto", +} + +var E_Default = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 75007, + Name: "ext.default", + Tag: "bytes,75007,opt,name=default", + Filename: "ext.proto", +} + +var E_Restart = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 75008, + Name: "ext.restart", + Tag: "varint,75008,opt,name=restart", + Filename: "ext.proto", +} + +var E_DeviceId = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*bool)(nil), + Field: 75009, + Name: "ext.device_id", + Tag: "varint,75009,opt,name=device_id", + Filename: "ext.proto", +} + +var E_Goname = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.FieldOptions)(nil), + ExtensionType: (*string)(nil), + Field: 75010, + Name: "ext.goname", + Tag: "bytes,75010,opt,name=goname", + Filename: "ext.proto", +} + +var E_Enumgoname = &proto.ExtensionDesc{ + ExtendedType: (*descriptor.EnumValueOptions)(nil), + ExtensionType: (*string)(nil), + Field: 76010, + Name: "ext.enumgoname", + Tag: "bytes,76010,opt,name=enumgoname", + Filename: "ext.proto", +} + +func init() { + proto.RegisterExtension(E_XmlTags) + proto.RegisterExtension(E_Xml) + proto.RegisterExtension(E_Json) + proto.RegisterExtension(E_Default) + proto.RegisterExtension(E_Restart) + proto.RegisterExtension(E_DeviceId) + proto.RegisterExtension(E_Goname) + proto.RegisterExtension(E_Enumgoname) +} + +func init() { proto.RegisterFile("ext.proto", fileDescriptor_95fe6908ffcf64d3) } + +var fileDescriptor_95fe6908ffcf64d3 = []byte{ + // 305 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0xd1, 0x3d, 0x4b, 0x03, 0x31, + 0x18, 0xc0, 0x71, 0x8e, 0x16, 0xdb, 0x66, 0xec, 0x24, 0x82, 0xb5, 0x6e, 0x9d, 0xee, 0x10, 0x41, + 0x31, 0x74, 0x52, 0x14, 0x1c, 0x44, 0x28, 0xe2, 0xe0, 0x52, 0xd2, 0xcb, 0xd3, 0x34, 0x92, 0x97, + 0x72, 0x49, 0x24, 0x6e, 0xea, 0x37, 0xf0, 0x2b, 0x39, 0x69, 0x27, 0xfd, 0x06, 0xd2, 0xd1, 0xef, + 0xe0, 0x0b, 0x77, 0x97, 0x93, 0x42, 0x87, 0xdb, 0x42, 0xf8, 0xff, 0x1e, 0x12, 0x1e, 0xd4, 0x01, + 0x6f, 0xe3, 0x79, 0xa6, 0xad, 0xee, 0x36, 0xc0, 0xdb, 0xad, 0x3e, 0xd3, 0x9a, 0x09, 0x48, 0x8a, + 0xab, 0x89, 0x9b, 0x26, 0x14, 0x4c, 0x9a, 0xf1, 0xb9, 0xd5, 0x59, 0x99, 0xe1, 0x21, 0x6a, 0x7b, + 0x29, 0xc6, 0x96, 0x30, 0xd3, 0xdd, 0x89, 0xcb, 0x3c, 0xae, 0xf2, 0xf8, 0x02, 0x8c, 0x21, 0x0c, + 0x2e, 0xe7, 0x96, 0x6b, 0x65, 0x36, 0x9f, 0x5f, 0x9a, 0xfd, 0x68, 0xd0, 0x1e, 0xb5, 0xbc, 0x14, + 0x57, 0x84, 0x19, 0xbc, 0x87, 0x1a, 0x5e, 0x8a, 0xee, 0xf6, 0x1a, 0x3c, 0xe3, 0x20, 0x68, 0xc5, + 0xbe, 0xdf, 0x72, 0xd6, 0x19, 0xe5, 0x2d, 0xde, 0x47, 0xcd, 0x5b, 0xa3, 0x55, 0x9d, 0xf9, 0x09, + 0xa6, 0x88, 0xf1, 0x11, 0x6a, 0x51, 0x98, 0x12, 0x27, 0x6c, 0x9d, 0xfb, 0x0d, 0xae, 0xea, 0x73, + 0x9a, 0x81, 0xb1, 0x24, 0xab, 0xa5, 0x0f, 0x8b, 0xf0, 0xbb, 0xd0, 0xe3, 0x21, 0xea, 0x50, 0xb8, + 0xe3, 0x29, 0x8c, 0x39, 0xad, 0xc3, 0x8f, 0x01, 0xb7, 0x4b, 0x71, 0x4e, 0xf1, 0x21, 0xda, 0x60, + 0x5a, 0x11, 0x09, 0x75, 0xf4, 0x69, 0x51, 0x3e, 0x39, 0xe4, 0xf8, 0x04, 0x21, 0x50, 0x4e, 0x06, + 0xbc, 0xbb, 0x86, 0x4f, 0x95, 0x93, 0xd7, 0x44, 0xb8, 0xff, 0xb5, 0x7c, 0x7d, 0x94, 0x03, 0x56, + 0xd8, 0xf1, 0xc1, 0xeb, 0xb2, 0x17, 0xbd, 0x2f, 0x7b, 0xd1, 0xe7, 0xb2, 0x17, 0xdd, 0x0c, 0x18, + 0xb7, 0x33, 0x37, 0x89, 0x53, 0x2d, 0x13, 0x73, 0xaf, 0x52, 0x3b, 0xe3, 0x8a, 0xad, 0x9c, 0x8a, + 0xd9, 0x09, 0x78, 0xfb, 0x17, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x40, 0x4c, 0x73, 0x42, 0x02, 0x00, + 0x00, +} diff --git a/proto/generate.go b/proto/generate.go new file mode 100644 index 000000000..4cf477dca --- /dev/null +++ b/proto/generate.go @@ -0,0 +1,55 @@ +// 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 ( + "log" + "os" + "os/exec" + "path/filepath" +) + +//go:generate go run scripts/protofmt.go . + +// First generate extensions using standard proto compiler. +//go:generate protoc -I ../ -I . --gogofast_out=Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/protoc-gen-gogo/descriptor,paths=source_relative:ext ext.proto + +// Then build our vanity compiler that uses the new extensions +//go:generate go build -o scripts/protoc-gen-gosyncthing scripts/protoc_plugin.go + +// Inception, go generate calls the script itself that then deals with generation. +// This is only done because go:generate does not support wildcards in paths. +//go:generate go run generate.go lib/config lib/fs + +// Use the standard compiler here. We can revisit this later, but we don't plan on exposing this via any APIs. +//go:generate protoc -I ../ -I . --gogofast_out=paths=source_relative:.. lib/protocol/bep.proto + +func main() { + for _, path := range os.Args[1:] { + matches, err := filepath.Glob(filepath.Join(path, "*proto")) + if err != nil { + log.Fatal(err) + } + log.Println(path, "returned:", matches) + args := []string{ + "-I", "..", + "-I", ".", + "--plugin=protoc-gen-gosyncthing=scripts/protoc-gen-gosyncthing", + "--gosyncthing_out=paths=source_relative:..", + } + args = append(args, matches...) + cmd := exec.Command("protoc", args...) + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Run(); err != nil { + log.Fatal("Failed generating", path) + } + } +} diff --git a/proto/lib/config/authmode.proto b/proto/lib/config/authmode.proto new file mode 100644 index 000000000..c045385f6 --- /dev/null +++ b/proto/lib/config/authmode.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package config; + +import "repos/protobuf/gogoproto/gogo.proto"; + +import "ext.proto"; + +enum AuthMode { + option (gogoproto.goproto_enum_stringer) = false; + + AUTH_MODE_STATIC = 0; + AUTH_MODE_LDAP = 1 [(ext.enumgoname) = "AuthModeLDAP"]; +} diff --git a/proto/lib/config/blockpullorder.proto b/proto/lib/config/blockpullorder.proto new file mode 100644 index 000000000..124b54844 --- /dev/null +++ b/proto/lib/config/blockpullorder.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package config; + +enum BlockPullOrder { + BLOCK_PULL_ORDER_STANDARD = 0; + BLOCK_PULL_ORDER_RANDOM = 1; + BLOCK_PULL_ORDER_IN_ORDER = 2; +} diff --git a/proto/lib/config/config.proto b/proto/lib/config/config.proto new file mode 100644 index 000000000..3b4f526ce --- /dev/null +++ b/proto/lib/config/config.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package config; + +import "lib/config/folderconfiguration.proto"; +import "lib/config/deviceconfiguration.proto"; +import "lib/config/guiconfiguration.proto"; +import "lib/config/ldapconfiguration.proto"; +import "lib/config/optionsconfiguration.proto"; +import "lib/config/observed.proto"; + +import "ext.proto"; + +message Configuration { + int32 version = 1 [(ext.xml) = "version,attr"]; + repeated FolderConfiguration folders = 2; + repeated DeviceConfiguration devices = 3; + GUIConfiguration gui = 4 [(ext.goname) = "GUI"]; + LDAPConfiguration ldap = 5 [(ext.goname) = "LDAP"]; + OptionsConfiguration options = 6; + repeated ObservedDevice ignored_devices = 7 [(ext.json) = "remoteIgnoredDevices", (ext.xml) = "remoteIgnoredDevice"]; + repeated ObservedDevice pending_devices = 8; +} diff --git a/proto/lib/config/deviceconfiguration.proto b/proto/lib/config/deviceconfiguration.proto new file mode 100644 index 000000000..3494a413d --- /dev/null +++ b/proto/lib/config/deviceconfiguration.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package config; + +import "lib/protocol/bep.proto"; +import "lib/config/observed.proto"; + +import "ext.proto"; + +message DeviceConfiguration { + bytes device_id = 1 [(ext.goname) = "DeviceID", (ext.xml) = "id,attr", (ext.json) = "deviceID", (ext.device_id) = true]; + string name = 2 [(ext.xml) = "name,attr,omitempty"]; + repeated string addresses = 3 [(ext.xml) = "address,omitempty", (ext.default) = "dynamic"]; + protocol.Compression compression = 4 [(ext.xml) = "compression,attr"]; + string cert_name = 5 [(ext.xml) = "certName,attr,omitempty"]; + bool introducer = 6 [(ext.xml) = "introducer,attr"]; + bool skip_introduction_removals = 7 [(ext.xml) = "skipIntroductionRemovals,attr"]; + bytes introduced_by = 8 [(ext.xml) = "introducedBy,attr", (ext.device_id) = true]; + bool paused = 9; + repeated string allowed_networks = 10 [(ext.xml) = "allowedNetwork,omitempty"]; + bool auto_accept_folders = 11; + int32 max_send_kbps = 12; + int32 max_recv_kbps = 13; + repeated ObservedFolder ignored_folders = 14; + repeated ObservedFolder pending_folders = 15; + int32 max_request_kib = 16 [(ext.goname) = "MaxRequestKiB", (ext.xml) = "maxRequestKiB", (ext.json) = "maxRequestKiB"]; +} diff --git a/proto/lib/config/folderconfiguration.proto b/proto/lib/config/folderconfiguration.proto new file mode 100644 index 000000000..0b7093060 --- /dev/null +++ b/proto/lib/config/folderconfiguration.proto @@ -0,0 +1,61 @@ +syntax = "proto3"; + +package config; + +import "lib/config/foldertype.proto"; +import "lib/config/size.proto"; +import "lib/config/pullorder.proto"; +import "lib/config/versioningconfiguration.proto"; +import "lib/config/blockpullorder.proto"; + +import "lib/fs/types.proto"; +import "lib/fs/copyrangemethod.proto"; + +import "ext.proto"; + +message FolderDeviceConfiguration { + bytes device_id = 1 [(ext.goname) = "DeviceID", (ext.xml) = "id,attr", (ext.json) = "deviceID", (ext.device_id) = true]; + bytes introduced_by = 2 [(ext.xml) = "introducedBy,attr", (ext.device_id) = true]; +} + +message FolderConfiguration { + string id = 1 [(ext.goname) = "ID", (ext.xml) = "id,attr"]; + string label = 2 [(ext.xml) = "label,attr", (ext.restart) = false]; + fs.FilesystemType filesystem_type = 3; + string path = 4 [(ext.xml) = "path,attr"]; + FolderType type = 5 [(ext.xml) = "type,attr"]; + repeated FolderDeviceConfiguration devices = 6; + int32 rescan_interval_s = 7 [(ext.xml) = "rescanIntervalS,attr", (ext.default) = "3600"]; + bool fs_watcher_enabled = 8 [(ext.goname) = "FSWatcherEnabled", (ext.xml) = "fsWatcherEnabled,attr", (ext.default) = "true"]; + int32 fs_watcher_delay_s = 9 [(ext.goname) = "FSWatcherDelayS", (ext.xml) = "fsWatcherDelayS,attr", (ext.default) = "10"]; + bool ignore_perms = 10 [(ext.xml) = "ignorePerms,attr"]; + bool auto_normalize = 11 [(ext.xml) = "autoNormalize,attr", (ext.default) = "true"]; + Size min_disk_free = 12; + VersioningConfiguration versioning = 13; + int32 copiers = 14; + int32 puller_max_pending_kib = 15 [(ext.goname) = "PullerMaxPendingKiB", (ext.xml) = "pullerMaxPendingKiB", (ext.json) = "pullerMaxPendingKiB"]; + int32 hashers = 16; + PullOrder order = 17; + bool ignore_delete = 18; + int32 scan_progress_interval_s = 19; + int32 puller_pause_s = 20; + int32 max_conflicts = 21 [(ext.default) = "-1"]; + bool disable_sparse_files = 22; + bool disable_temp_indexes = 23; + bool paused = 24; + int32 weak_hash_threshold_pct = 25; + string marker_name = 26; + bool copy_ownership_from_parent = 27; + int32 mod_time_window_s = 28 [(ext.goname) = "RawModTimeWindowS"]; + int32 max_concurrent_writes = 29 [(ext.default) = "2"]; + bool disable_fsync = 30; + BlockPullOrder block_pull_order = 31; + fs.CopyRangeMethod copy_range_method = 32 [(ext.default) = "standard"]; + bool case_sensitive_fs = 33 [(ext.goname) = "CaseSensitiveFS", (ext.xml) = "caseSensitiveFS", (ext.json) = "caseSensitiveFS"]; + bool follow_junctions = 34 [(ext.goname) = "JunctionsAsDirs", (ext.xml) = "junctionsAsDirs", (ext.json) = "junctionsAsDirs"]; + + // Legacy deprecated + bool read_only = 9000 [deprecated=true, (ext.xml) = "ro,attr,omitempty"]; + double min_disk_free_pct = 9001 [deprecated=true]; + int32 pullers = 9002 [deprecated=true]; +} diff --git a/proto/lib/config/foldertype.proto b/proto/lib/config/foldertype.proto new file mode 100644 index 000000000..5ab498c31 --- /dev/null +++ b/proto/lib/config/foldertype.proto @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package config; + +enum FolderType { + FOLDER_TYPE_SEND_RECEIVE = 0; + FOLDER_TYPE_SEND_ONLY = 1; + FOLDER_TYPE_RECEIVE_ONLY = 2; +} diff --git a/proto/lib/config/guiconfiguration.proto b/proto/lib/config/guiconfiguration.proto new file mode 100644 index 000000000..ed0927599 --- /dev/null +++ b/proto/lib/config/guiconfiguration.proto @@ -0,0 +1,23 @@ +syntax = "proto3"; + +package config; + +import "lib/config/authmode.proto"; + +import "ext.proto"; + +message GUIConfiguration { + bool enabled = 1 [(ext.xml) = "enabled,attr", (ext.default) = "true"]; + string address = 2 [(ext.goname) = "RawAddress", (ext.default) = "127.0.0.1:8384"]; + string unix_socket_permissions = 3 [(ext.goname) = "RawUnixSocketPermissions", (ext.xml) = "unixSocketPermissions,omitempty"]; + string user = 4 [(ext.xml) = "user,omitempty"]; + string password = 5 [(ext.xml) = "password,omitempty"]; + AuthMode auth_mode = 6 [(ext.xml) = "authMode,omitempty"]; + bool use_tls = 7 [(ext.goname) = "RawUseTLS", (ext.xml) = "tls,attr", (ext.json) = "useTLS"]; + string api_key = 8 [(ext.goname) = "APIKey", (ext.xml) = "apikey,omitempty"]; + bool insecure_admin_access = 9 [(ext.xml) = "insecureAdminAccess,omitempty"]; + string theme = 10 [(ext.default) = "default"]; + bool debugging = 11 [(ext.xml) = "debugging,attr"]; + bool insecure_skip_host_check = 12 [(ext.xml) = "insecureSkipHostcheck,omitempty", (ext.json) = "insecureSkipHostcheck"]; + bool insecure_allow_frame_loading = 13 [(ext.xml) = "insecureAllowFrameLoading,omitempty"]; +} diff --git a/proto/lib/config/ldapconfiguration.proto b/proto/lib/config/ldapconfiguration.proto new file mode 100644 index 000000000..6ad989662 --- /dev/null +++ b/proto/lib/config/ldapconfiguration.proto @@ -0,0 +1,17 @@ +syntax = "proto3"; + +package config; + +import "lib/config/ldaptransport.proto"; + +import "ext.proto"; + + +message LDAPConfiguration { + string address = 1 [(ext.xml) = "address,omitempty"]; + string bind_dn = 2 [(ext.goname) = "BindDN", (ext.xml) = "bindDN,omitempty", (ext.json) = "bindDN"]; + LDAPTransport transport = 3 [(ext.xml) = "transport,omitempty"]; + bool insecure_skip_verify = 4 [(ext.xml) = "insecureSkipVerify,omitempty", (ext.default) = "false"]; + string search_base_dn = 5 [(ext.goname) = "SearchBaseDN", (ext.xml) = "searchBaseDN,omitempty", (ext.json) = "searchBaseDN"]; + string search_filter = 6 [(ext.xml) = "searchFilter,omitempty"]; +} diff --git a/proto/lib/config/ldaptransport.proto b/proto/lib/config/ldaptransport.proto new file mode 100644 index 000000000..938f65e90 --- /dev/null +++ b/proto/lib/config/ldaptransport.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; + +package config; + +import "repos/protobuf/gogoproto/gogo.proto"; + +import "ext.proto"; + +enum LDAPTransport { + option (gogoproto.goproto_enum_stringer) = false; + + LDAP_TRANSPORT_PLAIN = 0 [(ext.enumgoname) = "LDAPTransportPlain"]; + LDAP_TRANSPORT_TLS = 2 [(ext.enumgoname) = "LDAPTransportTLS"]; + LDAP_TRANSPORT_START_TLS = 3 [(ext.enumgoname) = "LDAPTransportStartTLS"]; +} diff --git a/proto/lib/config/observed.proto b/proto/lib/config/observed.proto new file mode 100644 index 000000000..a1b44bcf7 --- /dev/null +++ b/proto/lib/config/observed.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package config; + +import "google/protobuf/timestamp.proto"; + +import "ext.proto"; + +message ObservedFolder { + google.protobuf.Timestamp time = 1 [(ext.xml) = "time,attr"]; + string id = 2 [(ext.goname) = "ID", (ext.xml) = "id,attr"]; + string label = 3 [(ext.xml) = "label,attr"]; +} + +message ObservedDevice { + google.protobuf.Timestamp time = 1 [(ext.xml) = "time,attr"]; + bytes id = 2 [(ext.goname) = "ID", (ext.json) = "deviceID", (ext.xml) = "id,attr", (ext.device_id) = true]; + string name = 3 [(ext.xml) = "name,attr"]; + string address = 4 [(ext.xml) = "address,attr"]; +} diff --git a/proto/lib/config/optionsconfiguration.proto b/proto/lib/config/optionsconfiguration.proto new file mode 100644 index 000000000..dfed02241 --- /dev/null +++ b/proto/lib/config/optionsconfiguration.proto @@ -0,0 +1,66 @@ +syntax = "proto3"; + +package config; + +import "lib/config/tuning.proto"; +import "lib/config/size.proto"; + +import "ext.proto"; + +message OptionsConfiguration { + repeated string listen_addresses = 1 [(ext.goname) = "RawListenAddresses", (ext.default) = "default"]; + repeated string global_discovery_servers = 2 [(ext.goname) = "RawGlobalAnnServers", (ext.xml) = "globalAnnounceServer", (ext.json) = "globalAnnounceServers", (ext.default) = "default"]; + bool global_discovery_enabled = 3 [(ext.goname) = "GlobalAnnEnabled", (ext.xml) = "globalAnnounceEnabled", (ext.json) = "globalAnnounceEnabled", (ext.default) = "true"]; + bool local_discovery_enabled = 4 [(ext.goname) = "LocalAnnEnabled", (ext.xml) = "localAnnounceEnabled", (ext.json) = "localAnnounceEnabled", (ext.default) = "true"]; + int32 local_announce_port = 5 [(ext.goname) = "LocalAnnPort", (ext.xml) = "localAnnouncePort", (ext.json) = "localAnnouncePort", (ext.default) = "21027"]; + string local_announce_multicast_address = 6 [(ext.goname) = "LocalAnnMCAddr", (ext.xml) = "localAnnounceMCAddr", (ext.json) = "localAnnounceMCAddr", (ext.default) = "[ff12::8384]:21027"]; + int32 max_send_kbps = 7; + int32 max_recv_kbps = 8; + int32 reconnection_interval_s = 9 [(ext.goname) = "ReconnectIntervalS", (ext.default) = "60"]; + bool relays_enabled = 10 [(ext.default) = "true"]; + int32 relays_reconnect_interval_m = 11 [(ext.goname) = "RelayReconnectIntervalM", (ext.xml) = "relayReconnectIntervalM", (ext.json) = "relayReconnectIntervalM", (ext.default) = "10"]; + bool start_browser = 12 [(ext.default) = "true"]; + bool nat_traversal_enabled = 14 [(ext.goname) = "NATEnabled", (ext.xml) = "natEnabled", (ext.json) = "natEnabled", (ext.default) = "true"]; + int32 nat_traversal_lease_m = 15 [(ext.goname) = "NATLeaseM", (ext.xml) = "natLeaseMinutes", (ext.json) = "natLeaseMinutes", (ext.default) = "60"]; + int32 nat_traversal_renewal_m = 16 [(ext.goname) = "NATRenewalM", (ext.xml) = "natRenewalMinutes", (ext.json) = "natRenewalMinutes", (ext.default) = "30"]; + int32 nat_traversal_timeout_s = 17 [(ext.goname) = "NATTimeoutS", (ext.xml) = "natTimeoutSeconds", (ext.json) = "natTimeoutSeconds", (ext.default) = "10"]; + int32 usage_reporting_accepted = 18 [(ext.goname) = "URAccepted", (ext.xml) = "urAccepted", (ext.json) = "urAccepted"]; + int32 usage_reporting_seen = 19 [(ext.goname) = "URSeen", (ext.xml) = "urSeen", (ext.json) = "urSeen"]; + string usage_reporting_unique_id = 20 [(ext.goname) = "URUniqueID", (ext.xml) = "urUniqueID", (ext.json) = "urUniqueId"]; + string usage_reporting_url = 21 [(ext.goname) = "URURL", (ext.xml) = "urURL", (ext.json) = "urURL", (ext.default) = "https://data.syncthing.net/newdata"]; + bool usage_reporting_post_insecurely = 22 [(ext.goname) = "URPostInsecurely", (ext.xml) = "urPostInsecurely", (ext.json) = "urPostInsecurely", (ext.default) = "false"]; + int32 usage_reporting_initial_delay_s = 23 [(ext.goname) = "URInitialDelayS", (ext.xml) = "urInitialDelayS", (ext.json) = "urInitialDelayS", (ext.default) = "1800"]; + bool restart_on_wakeup = 24 [(ext.default) = "true"]; + int32 auto_upgrade_interval_h = 25 [(ext.default) = "12"]; + bool upgrade_to_pre_releases = 26; + int32 keep_temporaries_h = 27 [(ext.default) = "24"]; + bool cache_ignored_files = 28 [(ext.default) = "false"]; + int32 progress_update_interval_s = 29 [(ext.default) = "5"]; + bool limit_bandwidth_in_lan = 30 [(ext.default) = "false"]; + Size min_home_disk_free = 31 [(ext.default) = "1 %"]; + string releases_url = 32 [(ext.goname) = "ReleasesURL", (ext.xml) = "releasesURL", (ext.json) = "releasesURL", (ext.default) = "https://upgrades.syncthing.net/meta.json"]; + repeated string always_local_nets = 33; + bool overwrite_remote_device_names_on_connect = 34 [(ext.goname) = "OverwriteRemoteDevNames", (ext.default) = "false"]; + int32 temp_index_min_blocks = 35 [(ext.default) = "10"]; + repeated string unacked_notification_ids = 36 [(ext.goname) = "UnackedNotificationIDs", (ext.xml) = "unackedNotificationID", (ext.json) = "unackedNotificationIDs"]; + int32 traffic_class = 37; + string default_folder_path = 38 [(ext.default) = "~"]; + bool set_low_priority = 39 [(ext.default) = "true"]; + int32 max_folder_concurrency = 40 [(ext.goname) = "RawMaxFolderConcurrency"]; + string crash_reporting_url = 41 [(ext.goname) = "CRURL", (ext.xml) = "crashReportingURL", (ext.json) = "crURL", (ext.default) = "https://crash.syncthing.net/newcrash"]; + bool crash_reporting_enabled = 42 [(ext.goname) = "CREnabled", (ext.default) = "true"]; + int32 stun_keepalive_start_s = 43 [(ext.default) = "180"]; + int32 stun_keepalive_min_s = 44 [(ext.default) = "20"]; + repeated string stun_servers = 45 [(ext.goname) = "RawStunServers", (ext.default) = "default"]; + Tuning database_tuning = 46 [(ext.restart) = true]; + int32 max_concurrent_incoming_request_kib = 47 [(ext.goname) = "RawMaxCIRequestKiB", (ext.xml) = "maxConcurrentIncomingRequestKiB", (ext.json) = "maxConcurrentIncomingRequestKiB"]; + + // Legacy deprecated + bool upnp_enabled = 9000 [deprecated = true, (ext.goname) = "DeprecatedUPnPEnabled"]; + int32 upnp_lease_m = 9001 [deprecated = true, (ext.goname) = "DeprecatedUPnPLeaseM", (ext.xml) = "upnpLeaseMinutes,omitempty"]; + int32 upnp_renewal_m = 9002 [deprecated = true, (ext.goname) = "DeprecatedUPnPRenewalM", (ext.xml) = "upnpRenewalMinutes,omitempty"]; + int32 upnp_timeout_s = 9003 [deprecated = true, (ext.goname) = "DeprecatedUPnPTimeoutS", (ext.xml) = "upnpTimeoutSeconds,omitempty"]; + repeated string relay_servers = 9004 [deprecated = true]; + double min_home_disk_free_pct = 9005 [deprecated = true]; + int32 max_concurrent_scans = 9006 [deprecated = true]; +} diff --git a/proto/lib/config/pullorder.proto b/proto/lib/config/pullorder.proto new file mode 100644 index 000000000..b9afb8e0d --- /dev/null +++ b/proto/lib/config/pullorder.proto @@ -0,0 +1,16 @@ +syntax = "proto3"; + +package config; + +import "repos/protobuf/gogoproto/gogo.proto"; + +enum PullOrder { + option (gogoproto.goproto_enum_stringer) = false; + + PULL_ORDER_RANDOM = 0; + PULL_ORDER_ALPHABETIC = 1; + PULL_ORDER_SMALLEST_FIRST = 2; + PULL_ORDER_LARGEST_FIRST = 3; + PULL_ORDER_OLDEST_FIRST = 4; + PULL_ORDER_NEWEST_FIRST = 5; +} diff --git a/proto/lib/config/size.proto b/proto/lib/config/size.proto new file mode 100644 index 000000000..e1795ddb1 --- /dev/null +++ b/proto/lib/config/size.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package config; + +import "repos/protobuf/gogoproto/gogo.proto"; + +import "ext.proto"; + +message Size { + option (gogoproto.goproto_stringer) = false; + + double value = 1 [(ext.xml) = ",chardata"]; + string unit = 2 [(ext.xml) = "unit,attr"]; +} diff --git a/proto/lib/config/tuning.proto b/proto/lib/config/tuning.proto new file mode 100644 index 000000000..edbbf3193 --- /dev/null +++ b/proto/lib/config/tuning.proto @@ -0,0 +1,13 @@ +syntax = "proto3"; + +package config; + +import "repos/protobuf/gogoproto/gogo.proto"; + +enum Tuning { + option (gogoproto.goproto_enum_stringer) = false; + + TUNING_AUTO = 0; + TUNING_SMALL = 1; + TUNING_LARGE = 2; +} diff --git a/proto/lib/config/versioningconfiguration.proto b/proto/lib/config/versioningconfiguration.proto new file mode 100644 index 000000000..1aab2fb18 --- /dev/null +++ b/proto/lib/config/versioningconfiguration.proto @@ -0,0 +1,14 @@ +syntax = "proto3"; + +package config; + +import "ext.proto"; + +// VersioningConfiguration is used in the code and for JSON serialization +message VersioningConfiguration { + option (ext.xml_tags) = false; + + string type = 1; + map<string, string> parameters = 2 [(ext.goname) = "Params", (ext.json) = "params"]; + int32 cleanup_interval_s = 3 [(ext.default) = "3600"]; +} diff --git a/proto/lib/fs/copyrangemethod.proto b/proto/lib/fs/copyrangemethod.proto new file mode 100644 index 000000000..5adaed385 --- /dev/null +++ b/proto/lib/fs/copyrangemethod.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package fs; + +enum CopyRangeMethod { + COPY_RANGE_METHOD_STANDARD = 0; + COPY_RANGE_METHOD_IOCTL = 1; + COPY_RANGE_METHOD_COPY_FILE_RANGE = 2; + COPY_RANGE_METHOD_SEND_FILE = 3; + COPY_RANGE_METHOD_DUPLICATE_EXTENTS = 4; + COPY_RANGE_METHOD_ALL_WITH_FALLBACK = 5; +} diff --git a/proto/lib/fs/types.proto b/proto/lib/fs/types.proto new file mode 100644 index 000000000..994ac123f --- /dev/null +++ b/proto/lib/fs/types.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; + +package fs; + +enum FilesystemType { + FILESYSTEM_TYPE_BASIC = 0; + FILESYSTEM_TYPE_FAKE = 1; +} diff --git a/proto/lib/protocol/bep.proto b/proto/lib/protocol/bep.proto new file mode 100644 index 000000000..f2c222d2c --- /dev/null +++ b/proto/lib/protocol/bep.proto @@ -0,0 +1,214 @@ +syntax = "proto3"; + +package protocol; + +import "repos/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_getters_all) = false; +option (gogoproto.sizer_all) = false; +option (gogoproto.protosizer_all) = true; +option (gogoproto.goproto_enum_stringer_all) = true; +option (gogoproto.goproto_enum_prefix_all) = false; +option (gogoproto.goproto_unkeyed_all) = false; +option (gogoproto.goproto_unrecognized_all) = false; +option (gogoproto.goproto_sizecache_all) = false; + +// --- Pre-auth --- + +message Hello { + string device_name = 1; + string client_name = 2; + string client_version = 3; +} + +// --- Header --- + +message Header { + MessageType type = 1; + MessageCompression compression = 2; +} + +enum MessageType { + CLUSTER_CONFIG = 0 [(gogoproto.enumvalue_customname) = "messageTypeClusterConfig"]; + INDEX = 1 [(gogoproto.enumvalue_customname) = "messageTypeIndex"]; + INDEX_UPDATE = 2 [(gogoproto.enumvalue_customname) = "messageTypeIndexUpdate"]; + REQUEST = 3 [(gogoproto.enumvalue_customname) = "messageTypeRequest"]; + RESPONSE = 4 [(gogoproto.enumvalue_customname) = "messageTypeResponse"]; + DOWNLOAD_PROGRESS = 5 [(gogoproto.enumvalue_customname) = "messageTypeDownloadProgress"]; + PING = 6 [(gogoproto.enumvalue_customname) = "messageTypePing"]; + CLOSE = 7 [(gogoproto.enumvalue_customname) = "messageTypeClose"]; +} + +enum MessageCompression { + NONE = 0 [(gogoproto.enumvalue_customname) = "MessageCompressionNone"]; + LZ4 = 1 [(gogoproto.enumvalue_customname) = "MessageCompressionLZ4"]; +} + +// --- Actual messages --- + +// Cluster Config + +message ClusterConfig { + repeated Folder folders = 1 [(gogoproto.nullable) = false]; +} + +message Folder { + string id = 1 [(gogoproto.customname) = "ID"]; + string label = 2; + bool read_only = 3; + bool ignore_permissions = 4; + bool ignore_delete = 5; + bool disable_temp_indexes = 6; + bool paused = 7; + + repeated Device devices = 16 [(gogoproto.nullable) = false]; +} + +message Device { + bytes id = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "DeviceID", (gogoproto.nullable) = false]; + string name = 2; + repeated string addresses = 3; + Compression compression = 4; + string cert_name = 5; + int64 max_sequence = 6; + bool introducer = 7; + uint64 index_id = 8 [(gogoproto.customname) = "IndexID", (gogoproto.customtype) = "IndexID", (gogoproto.nullable) = false]; + bool skip_introduction_removals = 9; +} + +enum Compression { + METADATA = 0 [(gogoproto.enumvalue_customname) = "CompressMetadata"]; + NEVER = 1 [(gogoproto.enumvalue_customname) = "CompressNever"]; + ALWAYS = 2 [(gogoproto.enumvalue_customname) = "CompressAlways"]; +} + +// Index and Index Update + +message Index { + string folder = 1; + repeated FileInfo files = 2 [(gogoproto.nullable) = false]; +} + +message IndexUpdate { + string folder = 1; + repeated FileInfo files = 2 [(gogoproto.nullable) = false]; +} + +message FileInfo { + option (gogoproto.goproto_stringer) = false; + + // The field ordering here optimizes for struct size / alignment -- + // large types come before smaller ones. + + string name = 1; + int64 size = 3; + int64 modified_s = 5; + uint64 modified_by = 12 [(gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false]; + Vector version = 9 [(gogoproto.nullable) = false]; + int64 sequence = 10; + repeated BlockInfo blocks = 16 [(gogoproto.nullable) = false]; + string symlink_target = 17; + bytes blocks_hash = 18; + FileInfoType type = 2; + uint32 permissions = 4; + int32 modified_ns = 11; + int32 block_size = 13 [(gogoproto.customname) = "RawBlockSize"]; + + // The local_flags fields stores flags that are relevant to the local + // host only. It is not part of the protocol, doesn't get sent or + // received (we make sure to zero it), nonetheless we need it on our + // struct and to be able to serialize it to/from the database. + uint32 local_flags = 1000; + // The version_hash is an implementation detail and not part of the wire + // format. + bytes version_hash = 1001; + + bool deleted = 6; + bool invalid = 7 [(gogoproto.customname) = "RawInvalid"]; + bool no_permissions = 8; +} + +enum FileInfoType { + FILE = 0 [(gogoproto.enumvalue_customname) = "FileInfoTypeFile"]; + DIRECTORY = 1 [(gogoproto.enumvalue_customname) = "FileInfoTypeDirectory"]; + SYMLINK_FILE = 2 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkFile", deprecated = true]; + SYMLINK_DIRECTORY = 3 [(gogoproto.enumvalue_customname) = "FileInfoTypeDeprecatedSymlinkDirectory", deprecated = true]; + SYMLINK = 4 [(gogoproto.enumvalue_customname) = "FileInfoTypeSymlink"]; +} + +message BlockInfo { + option (gogoproto.goproto_stringer) = false; + bytes hash = 3; + int64 offset = 1; + int32 size = 2; + uint32 weak_hash = 4; +} + +message Vector { + repeated Counter counters = 1 [(gogoproto.nullable) = false]; +} + +message Counter { + uint64 id = 1 [(gogoproto.customname) = "ID", (gogoproto.customtype) = "ShortID", (gogoproto.nullable) = false]; + uint64 value = 2; +} + +// Request + +message Request { + int32 id = 1 [(gogoproto.customname) = "ID"]; + string folder = 2; + string name = 3; + int64 offset = 4; + int32 size = 5; + bytes hash = 6; + bool from_temporary = 7; + uint32 weak_hash = 8; +} + +// Response + +message Response { + int32 id = 1 [(gogoproto.customname) = "ID"]; + bytes data = 2; + ErrorCode code = 3; +} + +enum ErrorCode { + NO_ERROR = 0 [(gogoproto.enumvalue_customname) = "ErrorCodeNoError"]; + GENERIC = 1 [(gogoproto.enumvalue_customname) = "ErrorCodeGeneric"]; + NO_SUCH_FILE = 2 [(gogoproto.enumvalue_customname) = "ErrorCodeNoSuchFile"]; + INVALID_FILE = 3 [(gogoproto.enumvalue_customname) = "ErrorCodeInvalidFile"]; +} + +// DownloadProgress + +message DownloadProgress { + string folder = 1; + repeated FileDownloadProgressUpdate updates = 2 [(gogoproto.nullable) = false]; +} + +message FileDownloadProgressUpdate { + FileDownloadProgressUpdateType update_type = 1; + string name = 2; + Vector version = 3 [(gogoproto.nullable) = false]; + repeated int32 block_indexes = 4 [packed=false]; + int32 block_size = 5; +} + +enum FileDownloadProgressUpdateType { + APPEND = 0 [(gogoproto.enumvalue_customname) = "UpdateTypeAppend"]; + FORGET = 1 [(gogoproto.enumvalue_customname) = "UpdateTypeForget"]; +} + +// Ping + +message Ping { +} + +// Close + +message Close { + string reason = 1; +} + diff --git a/proto/scripts/dump_tags.go b/proto/scripts/dump_tags.go new file mode 100644 index 000000000..71d089d4f --- /dev/null +++ b/proto/scripts/dump_tags.go @@ -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, + }) + } + } + +} diff --git a/proto/scripts/protoc_plugin.go b/proto/scripts/protoc_plugin.go new file mode 100644 index 000000000..29565c169 --- /dev/null +++ b/proto/scripts/protoc_plugin.go @@ -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) + }) +} diff --git a/proto/scripts/protofmt.go b/proto/scripts/protofmt.go new file mode 100644 index 000000000..2f0ae69d5 --- /dev/null +++ b/proto/scripts/protofmt.go @@ -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 +} |