aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2023-04-12 20:33:09 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-04-12 20:33:09 +0000
commit70061dcc79a8b9376f28b576fd563a6c13aceac2 (patch)
tree85c8d7e85cf832024820c11fe6f7b0f6cbeb3fb2
parent18e379ced04722ec258358af06b25ed39a4f52be (diff)
parentb74e02b975387b4fd543bddf71dc17c845ac0b97 (diff)
downloadprotobuf-codegen-70061dcc79a8b9376f28b576fd563a6c13aceac2.tar.gz
Merge "Upgrade protobuf-codegen to 3.2.0" am: e4db0b7bb5 am: b74e02b975emu-33-dev
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/protobuf-codegen/+/2519368 Change-Id: I6d4955b757436955cfe9e7c635e30aee7fce047f Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp30
-rw-r--r--Cargo.lock279
-rw-r--r--Cargo.toml31
-rw-r--r--Cargo.toml.orig22
-rw-r--r--METADATA14
-rw-r--r--README.md105
-rw-r--r--src/bin/protobuf-bin-gen-rust-do-not-use.rs37
-rw-r--r--src/bin/protoc-gen-rust.rs4
-rw-r--r--src/code_writer.rs387
-rw-r--r--src/codegen/mod.rs272
-rw-r--r--src/compiler_plugin.rs48
-rw-r--r--src/customize.rs237
-rw-r--r--src/customize/ctx.rs92
-rw-r--r--src/customize/mod.rs251
-rw-r--r--src/customize/rustproto_proto.rs74
-rw-r--r--src/enums.rs377
-rw-r--r--src/extensions.rs116
-rw-r--r--src/field/mod.rs2031
-rw-r--r--src/file_descriptor.rs5
-rw-r--r--src/float.rs67
-rw-r--r--src/gen/all.rs64
-rw-r--r--src/gen/code_writer.rs455
-rw-r--r--src/gen/descriptor.rs68
-rw-r--r--src/gen/enums.rs413
-rw-r--r--src/gen/extensions.rs133
-rw-r--r--src/gen/field/accessor.rs241
-rw-r--r--src/gen/field/elem.rs319
-rw-r--r--src/gen/field/mod.rs1850
-rw-r--r--src/gen/field/option_kind.rs59
-rw-r--r--src/gen/field/repeated.rs46
-rw-r--r--src/gen/field/singular.rs52
-rw-r--r--src/gen/field/tag.rs5
-rw-r--r--src/gen/field/type_ext.rs115
-rw-r--r--src/gen/file.rs149
-rw-r--r--src/gen/file_and_mod.rs (renamed from src/file_and_mod.rs)5
-rw-r--r--src/gen/file_descriptor.rs208
-rw-r--r--src/gen/inside.rs11
-rw-r--r--src/gen/map.rs16
-rw-r--r--src/gen/message.rs786
-rw-r--r--src/gen/mod.rs21
-rw-r--r--src/gen/mod_rs.rs18
-rw-r--r--src/gen/oneof.rs373
-rw-r--r--src/gen/paths.rs (renamed from src/file.rs)44
-rw-r--r--src/gen/protoc_insertion_point.rs80
-rw-r--r--src/gen/rust/component.rs34
-rw-r--r--src/gen/rust/ident.rs62
-rw-r--r--src/gen/rust/ident_with_path.rs45
-rw-r--r--src/gen/rust/keywords.rs (renamed from src/rust.rs)28
-rw-r--r--src/gen/rust/mod.rs8
-rw-r--r--src/gen/rust/path.rs99
-rw-r--r--src/gen/rust/quote.rs58
-rw-r--r--src/gen/rust/rel_path.rs97
-rw-r--r--src/gen/rust/snippets.rs10
-rw-r--r--src/gen/rust_types_values.rs (renamed from src/rust_types_values.rs)518
-rw-r--r--src/gen/scope.rs536
-rw-r--r--src/gen/strx.rs (renamed from src/strx.rs)0
-rw-r--r--src/gen/well_known_types.rs123
-rw-r--r--src/gen_and_write.rs62
-rw-r--r--src/inside.rs10
-rw-r--r--src/lib.rs434
-rw-r--r--src/message.rs626
-rw-r--r--src/oneof.rs198
-rw-r--r--src/protobuf_name.rs374
-rw-r--r--src/protoc_gen_rust.rs21
-rw-r--r--src/rust_name.rs273
-rw-r--r--src/scope.rs555
-rw-r--r--src/serde.rs9
-rw-r--r--src/syntax.rs15
-rw-r--r--src/well_known_types.rs63
70 files changed, 8223 insertions, 6047 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 8ec5394..0cfc90b 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "ec31ce829473039ac598ca6fdcb245cbd6fa82ba"
+ "sha1": "7155092f3df112159d55132081937e1fe5c30490"
},
"path_in_vcs": "protobuf-codegen"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index a698555..b0ae081 100644
--- a/Android.bp
+++ b/Android.bp
@@ -24,11 +24,17 @@ rust_library_host {
name: "libprotobuf_codegen",
crate_name: "protobuf_codegen",
cargo_env_compat: true,
- cargo_pkg_version: "2.27.1",
+ cargo_pkg_version: "3.2.0",
srcs: ["src/lib.rs"],
- edition: "2015",
+ edition: "2021",
rustlibs: [
+ "libanyhow",
+ "libonce_cell",
"libprotobuf",
+ "libprotobuf_parse",
+ "libregex",
+ "libtempfile",
+ "libthiserror",
],
product_available: true,
vendor_available: true,
@@ -38,16 +44,22 @@ rust_test_host {
name: "protobuf-codegen_test_src_lib",
crate_name: "protobuf_codegen",
cargo_env_compat: true,
- cargo_pkg_version: "2.27.1",
+ cargo_pkg_version: "3.2.0",
srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
test_options: {
unit_test: true,
},
- edition: "2015",
+ edition: "2021",
rustlibs: [
+ "libanyhow",
+ "libonce_cell",
"libprotobuf",
+ "libprotobuf_parse",
+ "libregex",
+ "libtempfile",
+ "libthiserror",
],
}
@@ -55,12 +67,18 @@ rust_binary_host {
name: "protoc-gen-rust",
crate_name: "protoc_gen_rust",
cargo_env_compat: true,
- cargo_pkg_version: "2.27.1",
+ cargo_pkg_version: "3.2.0",
srcs: ["src/bin/protoc-gen-rust.rs"],
- edition: "2015",
+ edition: "2021",
rustlibs: [
+ "libanyhow",
+ "libonce_cell",
"libprotobuf",
"libprotobuf_codegen",
+ "libprotobuf_parse",
+ "libregex",
+ "libtempfile",
+ "libthiserror",
],
product_available: true,
vendor_available: true,
diff --git a/Cargo.lock b/Cargo.lock
index e9b0703..c92bdea 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,14 +3,287 @@
version = 3
[[package]]
+name = "aho-corasick"
+version = "0.7.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4f55bd91a0978cbfd91c457a164bab8b4001c833b7f323132c0a4e1922dd44e"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.65"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602"
+
+[[package]]
+name = "autocfg"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
+
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+
+[[package]]
+name = "either"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797"
+
+[[package]]
+name = "fastrand"
+version = "1.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499"
+dependencies = [
+ "instant",
+]
+
+[[package]]
+name = "hashbrown"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
+
+[[package]]
+name = "indexmap"
+version = "1.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
+dependencies = [
+ "autocfg",
+ "hashbrown",
+]
+
+[[package]]
+name = "instant"
+version = "0.1.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.133"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0f80d65747a3e43d1596c7c5492d95d5edddaabd45a7fcdb02b95f644164966"
+
+[[package]]
+name = "log"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "memchr"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
+
+[[package]]
+name = "once_cell"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7bd7356a8122b6c4a24a82b278680c73357984ca2fc79a0f9fa6dea7dced7c58"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
name = "protobuf"
-version = "2.27.1"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96"
+checksum = "b55bad9126f378a853655831eb7363b7b01b81d19f8cb1218861086ca4a1a61e"
+dependencies = [
+ "once_cell",
+ "protobuf-support",
+ "thiserror",
+]
[[package]]
name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
+dependencies = [
+ "anyhow",
+ "once_cell",
+ "protobuf",
+ "protobuf-parse",
+ "regex",
+ "tempfile",
+ "thiserror",
+]
+
+[[package]]
+name = "protobuf-parse"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d39b14605eaa1f6a340aec7f320b34064feb26c93aec35d6a9a2272a8ddfa49"
dependencies = [
+ "anyhow",
+ "indexmap",
+ "log",
"protobuf",
+ "protobuf-support",
+ "tempfile",
+ "thiserror",
+ "which",
+]
+
+[[package]]
+name = "protobuf-support"
+version = "3.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a5d4d7b8601c814cfb36bcebb79f0e61e45e1e93640cf778837833bbed05c372"
+dependencies = [
+ "thiserror",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.6.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244"
+
+[[package]]
+name = "remove_dir_all"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
+dependencies = [
+ "winapi",
+]
+
+[[package]]
+name = "syn"
+version = "1.0.100"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52205623b1b0f064a4e71182c3b18ae902267282930c6d5462c91b859668426e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tempfile"
+version = "3.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "libc",
+ "redox_syscall",
+ "remove_dir_all",
+ "winapi",
+]
+
+[[package]]
+name = "thiserror"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a99cb8c4b9a8ef0e7907cd3b617cc8dc04d571c4e73c8ae403d80ac160bb122"
+dependencies = [
+ "thiserror-impl",
]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a891860d3c8d66fec8e73ddb3765f90082374dbaaa833407b904a94f1a7eb43"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
+
+[[package]]
+name = "which"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
+dependencies = [
+ "either",
+ "libc",
+ "once_cell",
+]
+
+[[package]]
+name = "winapi"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
+dependencies = [
+ "winapi-i686-pc-windows-gnu",
+ "winapi-x86_64-pc-windows-gnu",
+]
+
+[[package]]
+name = "winapi-i686-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
+
+[[package]]
+name = "winapi-x86_64-pc-windows-gnu"
+version = "0.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
diff --git a/Cargo.toml b/Cargo.toml
index ca327e2..43e4376 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,17 +10,17 @@
# See Cargo.toml.orig for the original contents.
[package]
+edition = "2021"
name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"]
description = """
Code generator for rust-protobuf.
-Includes a library and `protoc-gen-rust` binary.
-
-See `protoc-rust` and `protobuf-codegen-pure` crates.
+Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rust` binary.
"""
homepage = "https://github.com/stepancheg/rust-protobuf/"
+readme = "README.md"
license = "MIT"
repository = "https://github.com/stepancheg/rust-protobuf/"
@@ -35,10 +35,23 @@ name = "protoc-gen-rust"
path = "src/bin/protoc-gen-rust.rs"
test = false
-[[bin]]
-name = "protobuf-bin-gen-rust-do-not-use"
-path = "src/bin/protobuf-bin-gen-rust-do-not-use.rs"
-test = false
+[dependencies.anyhow]
+version = "1.0.53"
+
+[dependencies.once_cell]
+version = "1.10.0"
[dependencies.protobuf]
-version = "=2.27.1"
+version = "=3.2.0"
+
+[dependencies.protobuf-parse]
+version = "=3.2.0"
+
+[dependencies.regex]
+version = "1.5.5"
+
+[dependencies.tempfile]
+version = "3"
+
+[dependencies.thiserror]
+version = "1.0.30"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index 3400e66..4cfd140 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,23 +1,29 @@
[package]
name = "protobuf-codegen"
-version = "2.27.1"
+version = "3.2.0"
authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"]
+edition = "2021"
license = "MIT"
homepage = "https://github.com/stepancheg/rust-protobuf/"
repository = "https://github.com/stepancheg/rust-protobuf/"
description = """
Code generator for rust-protobuf.
-Includes a library and `protoc-gen-rust` binary.
-
-See `protoc-rust` and `protobuf-codegen-pure` crates.
+Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rust` binary.
"""
[lib]
bench = false
[dependencies]
-protobuf = { path = "../protobuf", version = "=2.27.1" }
+thiserror = "1.0.30"
+anyhow = "1.0.53"
+regex = "1.5.5"
+once_cell = "1.10.0"
+tempfile = "3"
+
+protobuf = { path = "../protobuf", version = "=3.2.0" }
+protobuf-parse = { path = "../protobuf-parse", version = "=3.2.0" }
[[bin]]
@@ -25,11 +31,5 @@ name = "protoc-gen-rust"
path = "src/bin/protoc-gen-rust.rs"
test = false
-[[bin]]
-
-name = "protobuf-bin-gen-rust-do-not-use"
-path = "src/bin/protobuf-bin-gen-rust-do-not-use.rs"
-test = false
-
[package.metadata.docs.rs]
all-features = true
diff --git a/METADATA b/METADATA
index 4593845..1aefc7a 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,7 @@
+# This project was upgraded with external_updater.
+# Usage: tools/external_updater/updater.sh update rust/crates/protobuf-codegen
+# For more info, check https://cs.android.com/android/platform/superproject/+/master:tools/external_updater/README.md
+
name: "protobuf-codegen"
description: "Code generator for rust-protobuf. Includes a library and `protoc-gen-rust` binary. See `protoc-rust` and `protobuf-codegen-pure` crates."
third_party {
@@ -7,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-2.27.1.crate"
+ value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-3.2.0.crate"
}
- version: "2.27.1"
+ version: "3.2.0"
license_type: NOTICE
last_upgrade_date {
- year: 2022
- month: 3
- day: 1
+ year: 2023
+ month: 4
+ day: 3
}
}
diff --git a/README.md b/README.md
index 2ab7e05..5f1e2d7 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,65 @@
<!-- cargo-sync-readme start -->
-# Protobuf code generator
+# Protobuf code generator for `protobuf` crate
-This crate contains protobuf code generator implementation
-and a `protoc-gen-rust` `protoc` plugin.
+This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build.
-This crate:
-* provides `protoc-gen-rust` plugin for `protoc` command
-* implement protobuf codegen
+# How to generate code
-This crate is not meant to be used directly, in fact, it does not provide any public API
-(except for `protoc-gen-rust` binary).
+There are three main ways to generate `.rs` files from `.proto` files:
+* using `protoc` command line tool and `protoc-gen-rust` plugin
+* using this crate `Codegen` with pure rust parser
+* using this crate `Codegen` with `protoc` parser
-Code can be generated with either:
-* `protoc-gen-rust` plugin for `protoc` or
-* [`protoc-rust`](https://docs.rs/protoc) crate
- (code generator which depends on `protoc` binary for parsing of `.proto` files)
-* [`protobuf-codegen-pure`](https://docs.rs/protobuf-codegen-pure) crate,
- similar API to `protoc-rust`, but uses pure rust parser of `.proto` files.
+Which one should you use depends on your needs.
-# `protoc-gen-rust` plugin for `protoc`
+If you are using non-cargo build system (like Bazel), you might prefer
+using `protoc-gen-rust` plugin for `protoc`.
-When non-cargo build system is used, consider using standard protobuf code generation pattern:
-`protoc` command does all the work of handling paths and parsing `.proto` files.
-When `protoc` is invoked with `--rust_out=` option, it invokes `protoc-gen-rust` plugin.
-provided by this crate.
+If you build with `cargo`, you probably want to use `Codegen` from this crate.
-When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+# Protoc parser vs pure rust parser
-## How to use `protoc-gen-rust` if you have to
+There are two protobuf parsers which can be plugged into this crate:
+* `protoc`-based parser (`protoc` is a command like utility from Google protobuf)
+* pure rust parser (`protobuf-parse` crate)
+
+`protoc`-based parser is expected to parse `.proto` files very correctly:
+all Google's protobuf implementations rely on it.
+
+While there are no known bugs in `protobuf-parse`, it is not tested very well.
+Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser,
+mostly complex message options specified in `.proto` files.
+I never saw anyone using them, but you have been warned.
+
+Note `protoc` command can be obtained from
+[`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate.
+
+# Example
+
+```rust
+// Use this in build.rs
+protobuf_codegen::Codegen::new()
+ // Use `protoc` parser, optional.
+ .protoc()
+ // Use `protoc-bin-vendored` bundled protoc command, optional.
+ .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+ // All inputs and imports from the inputs must reside in `includes` directories.
+ .includes(&["src/protos"])
+ // Inputs must reside in some of include paths.
+ .input("src/protos/apple.proto")
+ .input("src/protos/banana.proto")
+ // Specify output directory relative to Cargo output directory.
+ .cargo_out_dir("protos")
+ .run_from_script();
+```
+
+## How to use `protoc-gen-rust`
+
+If you have to.
(Note `protoc` can be invoked programmatically with
-[protoc crate](https://docs.rs/protoc))
+[protoc crate](https://docs.rs/protoc/%3E=3.0.0-alpha))
0) Install protobuf for `protoc` binary.
@@ -48,11 +76,11 @@ apt-get install protobuf-compiler
```
Protobuf is needed only for code generation, `rust-protobuf` runtime
-does not use `protobuf` library.
+does not use C++ protobuf library.
1) Install `protoc-gen-rust` program (which is `protoc` plugin)
-It can be installed either from source or with `cargo install protobuf` command.
+It can be installed either from source or with `cargo install protobuf-codegen` command.
2) Add `protoc-gen-rust` to $PATH
@@ -70,11 +98,32 @@ protoc --rust_out . foo.proto
This will generate .rs files in current directory.
-# Version 2
+# Customize generate code
+
+Sometimes generated code need to be adjusted, e. g. to have custom derives.
+
+rust-protobuf provides two options to do that:
+* generated `.rs` files contain `@@protoc_insertion_point(...)` markers
+ (similar markers inserts Google's protobuf generator for C++ or Java).
+ Simple script `sed` one-liners can be used to replace these markers with custom annotations.
+* `Codegen::customize_callback` can be used to patch generated code
+ when invoked from `build.rs` script.
+
+# Serde
+
+rust-protobuf since version 3 no longer directly supports serde.
+
+Rust-protobuf 3 fully supports:
+* runtime reflection
+* JSON parsing and printing via
+ [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping)
+
+Which covers the most of serde use cases.
-This is documentation for version 2 of the crate.
+If you still need serde, generic customization callback (see above) can be used
+to insert `#[serde(...)]` annotations.
-[Version 3 of the crate](https://docs.rs/protobuf-codegen/%3E=3.0.0-alpha)
-(currently in development) encapsulates both `protoc` and pure codegens in this crate.
+[Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)
+in the rust-protobuf repository demonstrates how to do it.
<!-- cargo-sync-readme end -->
diff --git a/src/bin/protobuf-bin-gen-rust-do-not-use.rs b/src/bin/protobuf-bin-gen-rust-do-not-use.rs
deleted file mode 100644
index a6f96ca..0000000
--- a/src/bin/protobuf-bin-gen-rust-do-not-use.rs
+++ /dev/null
@@ -1,37 +0,0 @@
-extern crate protobuf;
-extern crate protobuf_codegen;
-
-use std::fs::*;
-use std::io::Read;
-use std::path::Path;
-
-use protobuf::descriptor::*;
-use protobuf::Message;
-use protobuf_codegen::*;
-
-fn write_file(bin: &str) {
- let mut is = File::open(&Path::new(bin)).unwrap();
- let fds = FileDescriptorSet::parse_from_reader(&mut is as &mut dyn Read).unwrap();
-
- let file_names: Vec<String> = fds
- .get_file()
- .iter()
- .map(|f| f.get_name().to_string())
- .collect();
- gen_and_write(
- fds.get_file(),
- &file_names,
- Path::new("."),
- &Default::default(),
- )
- .expect("gen_and_write");
-}
-
-fn main() {
- let args: Vec<String> = std::env::args().collect();
- if args.len() != 2 {
- panic!("must have exactly one argument");
- }
- let ref pb_bin = args[1];
- write_file(&pb_bin);
-}
diff --git a/src/bin/protoc-gen-rust.rs b/src/bin/protoc-gen-rust.rs
index 97ac7d2..8c34e49 100644
--- a/src/bin/protoc-gen-rust.rs
+++ b/src/bin/protoc-gen-rust.rs
@@ -1,5 +1,3 @@
-extern crate protobuf_codegen;
-
fn main() {
- protobuf_codegen::protoc_gen_rust_main();
+ protobuf_codegen::protoc_gen_rust::protoc_gen_rust_main();
}
diff --git a/src/code_writer.rs b/src/code_writer.rs
deleted file mode 100644
index 50eaeb8..0000000
--- a/src/code_writer.rs
+++ /dev/null
@@ -1,387 +0,0 @@
-// TODO: used by grpc-rust, should move it into separate crate.
-#![doc(hidden)]
-
-use std::io::Write;
-
-use inside::protobuf_crate_path;
-use Customize;
-
-/// Field visibility.
-pub enum Visibility {
- Public,
- Default,
-}
-
-pub struct CodeWriter<'a> {
- writer: &'a mut (dyn Write + 'a),
- indent: String,
-}
-
-impl<'a> CodeWriter<'a> {
- pub fn new(writer: &'a mut dyn Write) -> CodeWriter<'a> {
- CodeWriter {
- writer: writer,
- indent: "".to_string(),
- }
- }
-
- pub fn write_line<S: AsRef<str>>(&mut self, line: S) {
- (if line.as_ref().is_empty() {
- self.writer.write_all("\n".as_bytes())
- } else {
- let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
- self.writer.write_all(s.as_bytes())
- })
- .unwrap();
- }
-
- pub fn write_generated(&mut self) {
- self.write_line("// This file is generated. Do not edit");
- self.write_generated_common();
- }
-
- pub fn write_generated_by(&mut self, pkg: &str, version: &str) {
- self.write_line(format!(
- "// This file is generated by {pkg} {version}. Do not edit",
- pkg = pkg,
- version = version
- ));
- self.write_generated_common();
- }
-
- fn write_generated_common(&mut self) {
- // https://secure.phabricator.com/T784
- self.write_line("// @generated");
-
- self.write_line("");
- self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
- self.write_line("#![allow(unknown_lints)]");
- self.write_line("#![allow(clippy::all)]");
- self.write_line("");
- self.write_line("#![allow(unused_attributes)]");
- self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
- self.write_line("");
- self.write_line("#![allow(box_pointers)]");
- self.write_line("#![allow(dead_code)]");
- self.write_line("#![allow(missing_docs)]");
- self.write_line("#![allow(non_camel_case_types)]");
- self.write_line("#![allow(non_snake_case)]");
- self.write_line("#![allow(non_upper_case_globals)]");
- self.write_line("#![allow(trivial_casts)]");
- self.write_line("#![allow(unused_imports)]");
- self.write_line("#![allow(unused_results)]");
- }
-
- pub fn todo(&mut self, message: &str) {
- self.write_line(format!("panic!(\"TODO: {}\");", message));
- }
-
- pub fn unimplemented(&mut self) {
- self.write_line(format!("unimplemented!();"));
- }
-
- pub fn indented<F>(&mut self, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- cb(&mut CodeWriter {
- writer: self.writer,
- indent: format!("{} ", self.indent),
- });
- }
-
- #[allow(dead_code)]
- pub fn commented<F>(&mut self, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- cb(&mut CodeWriter {
- writer: self.writer,
- indent: format!("// {}", self.indent),
- });
- }
-
- pub fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
- self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
- }
-
- pub fn lazy_static(&mut self, name: &str, ty: &str, customize: &Customize) {
- self.write_line(&format!(
- "static {}: {}::rt::LazyV2<{}> = {}::rt::LazyV2::INIT;",
- name,
- protobuf_crate_path(customize),
- ty,
- protobuf_crate_path(customize),
- ));
- }
-
- pub fn lazy_static_decl_get<F>(&mut self, name: &str, ty: &str, customize: &Customize, init: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.lazy_static(name, ty, customize);
- self.write_line(&format!("{}.get(|| {{", name));
- self.indented(|w| init(w));
- self.write_line(&format!("}})"));
- }
-
- pub fn lazy_static_decl_get_simple(
- &mut self,
- name: &str,
- ty: &str,
- init: &str,
- customize: &Customize,
- ) {
- self.lazy_static(name, ty, customize);
- self.write_line(&format!("{}.get({})", name, init));
- }
-
- pub fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.write_line(first_line);
- self.indented(cb);
- self.write_line(last_line);
- }
-
- pub fn expr_block<F>(&mut self, prefix: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.block(&format!("{} {{", prefix), "}", cb);
- }
-
- pub fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
- }
-
- pub fn unsafe_expr<F>(&mut self, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block("unsafe", cb);
- }
-
- pub fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("impl {}", name.as_ref()), cb);
- }
-
- pub fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, tr: S1, ty: S2, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
- }
-
- pub fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- let args_str = if args.is_empty() {
- "".to_owned()
- } else {
- format!("<{}>", args.join(", "))
- };
- self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
- }
-
- pub fn unsafe_impl(&mut self, what: &str, for_what: &str) {
- self.write_line(&format!("unsafe impl {} for {} {{}}", what, for_what));
- }
-
- pub fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
- }
-
- pub fn def_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("struct {}", name.as_ref()), cb);
- }
-
- pub fn pub_enum<F>(&mut self, name: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("pub enum {}", name), cb);
- }
-
- pub fn pub_trait<F>(&mut self, name: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("pub trait {}", name), cb);
- }
-
- pub fn pub_trait_extend<F>(&mut self, name: &str, extend: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("pub trait {} : {}", name, extend), cb);
- }
-
- pub fn field_entry(&mut self, name: &str, value: &str) {
- self.write_line(&format!("{}: {},", name, value));
- }
-
- pub fn field_decl(&mut self, name: &str, field_type: &str) {
- self.write_line(&format!("{}: {},", name, field_type));
- }
-
- pub fn pub_field_decl(&mut self, name: &str, field_type: &str) {
- self.write_line(&format!("pub {}: {},", name, field_type));
- }
-
- pub fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
- match vis {
- Visibility::Public => self.pub_field_decl(name, field_type),
- Visibility::Default => self.field_decl(name, field_type),
- }
- }
-
- pub fn derive(&mut self, derive: &[&str]) {
- let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
- self.write_line(&format!("#[derive({})]", v.join(",")));
- }
-
- pub fn allow(&mut self, what: &[&str]) {
- let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
- self.write_line(&format!("#[allow({})]", v.join(",")));
- }
-
- pub fn comment(&mut self, comment: &str) {
- if comment.is_empty() {
- self.write_line("//");
- } else {
- self.write_line(&format!("// {}", comment));
- }
- }
-
- pub fn fn_def(&mut self, sig: &str) {
- self.write_line(&format!("fn {};", sig));
- }
-
- pub fn fn_block<F>(&mut self, public: bool, sig: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- if public {
- self.expr_block(&format!("pub fn {}", sig), cb);
- } else {
- self.expr_block(&format!("fn {}", sig), cb);
- }
- }
-
- pub fn pub_fn<F>(&mut self, sig: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.fn_block(true, sig, cb);
- }
-
- pub fn def_fn<F>(&mut self, sig: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.fn_block(false, sig, cb);
- }
-
- pub fn def_mod<F>(&mut self, name: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("mod {}", name), cb)
- }
-
- pub fn pub_mod<F>(&mut self, name: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("pub mod {}", name), cb)
- }
-
- pub fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("while {}", cond.as_ref()), cb);
- }
-
- // if ... { ... }
- pub fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("if {}", cond.as_ref()), cb);
- }
-
- // if ... {} else { ... }
- pub fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.write_line(&format!("if {} {{", cond.as_ref()));
- self.write_line("} else {");
- self.indented(cb);
- self.write_line("}");
- }
-
- // if let ... = ... { ... }
- pub fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.if_stmt(&format!("let {} = {}", decl, expr), cb);
- }
-
- // if let ... = ... { } else { ... }
- pub fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
- }
-
- pub fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
- }
-
- pub fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.stmt_block(&format!("match {}", value.as_ref()), cb);
- }
-
- pub fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.expr_block(&format!("match {}", value.as_ref()), cb);
- }
-
- pub fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
- }
-
- pub fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
- self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
- }
-}
diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
new file mode 100644
index 0000000..3b8f629
--- /dev/null
+++ b/src/codegen/mod.rs
@@ -0,0 +1,272 @@
+use std::env;
+use std::ffi::OsString;
+use std::fs;
+use std::path::Component;
+use std::path::Path;
+use std::path::PathBuf;
+use std::process;
+
+use anyhow::Context;
+use protobuf_parse::Parser;
+
+use crate::customize::CustomizeCallback;
+use crate::customize::CustomizeCallbackHolder;
+use crate::gen_and_write::gen_and_write;
+use crate::Customize;
+
+#[derive(Debug)]
+enum WhichParser {
+ Pure,
+ Protoc,
+}
+
+impl Default for WhichParser {
+ fn default() -> WhichParser {
+ WhichParser::Pure
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+enum CodegenError {
+ #[error("out_dir is not specified")]
+ OutDirNotSpecified,
+}
+
+/// Entry point for `.proto` to `.rs` code generation.
+///
+/// This is similar to `protoc --rust_out...`.
+#[derive(Debug, Default)]
+pub struct Codegen {
+ /// What parser to use to parse `.proto` files.
+ which_parser: Option<WhichParser>,
+ /// Create out directory.
+ create_out_dir: bool,
+ /// --lang_out= param
+ out_dir: Option<PathBuf>,
+ /// -I args
+ includes: Vec<PathBuf>,
+ /// List of .proto files to compile
+ inputs: Vec<PathBuf>,
+ /// Customize code generation
+ customize: Customize,
+ /// Customize code generation
+ customize_callback: CustomizeCallbackHolder,
+ /// Protoc command path
+ protoc: Option<PathBuf>,
+ /// Extra `protoc` args
+ protoc_extra_args: Vec<OsString>,
+ /// Capture stderr when running `protoc`.
+ capture_stderr: bool,
+}
+
+impl Codegen {
+ /// Create new codegen object.
+ ///
+ /// Uses `protoc` from `$PATH` by default.
+ ///
+ /// Can be switched to pure rust parser using [`pure`](Self::pure) function.
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Switch to pure Rust parser of `.proto` files.
+ pub fn pure(&mut self) -> &mut Self {
+ self.which_parser = Some(WhichParser::Pure);
+ self
+ }
+
+ /// Switch to `protoc` parser of `.proto` files.
+ pub fn protoc(&mut self) -> &mut Self {
+ self.which_parser = Some(WhichParser::Protoc);
+ self
+ }
+
+ /// Output directory for generated code.
+ ///
+ /// When invoking from `build.rs`, consider using
+ /// [`cargo_out_dir`](Self::cargo_out_dir) instead.
+ pub fn out_dir(&mut self, out_dir: impl AsRef<Path>) -> &mut Self {
+ self.out_dir = Some(out_dir.as_ref().to_owned());
+ self
+ }
+
+ /// Set output directory relative to Cargo output dir.
+ ///
+ /// With this option, output directory is erased and recreated during invocation.
+ pub fn cargo_out_dir(&mut self, rel: &str) -> &mut Self {
+ let rel = Path::new(rel);
+ let mut not_empty = false;
+ for comp in rel.components() {
+ match comp {
+ Component::ParentDir => {
+ panic!("parent path in components of rel path: `{}`", rel.display());
+ }
+ Component::CurDir => {
+ continue;
+ }
+ Component::Normal(..) => {}
+ Component::RootDir | Component::Prefix(..) => {
+ panic!("root dir in components of rel path: `{}`", rel.display());
+ }
+ }
+ not_empty = true;
+ }
+
+ if !not_empty {
+ panic!("empty rel path: `{}`", rel.display());
+ }
+
+ let cargo_out_dir = env::var("OUT_DIR").expect("OUT_DIR env var not set");
+ let mut path = PathBuf::from(cargo_out_dir);
+ path.push(rel);
+ self.create_out_dir = true;
+ self.out_dir(path)
+ }
+
+ /// Add an include directory.
+ pub fn include(&mut self, include: impl AsRef<Path>) -> &mut Self {
+ self.includes.push(include.as_ref().to_owned());
+ self
+ }
+
+ /// Add include directories.
+ pub fn includes(&mut self, includes: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
+ for include in includes {
+ self.include(include);
+ }
+ self
+ }
+
+ /// Append a `.proto` file path to compile
+ pub fn input(&mut self, input: impl AsRef<Path>) -> &mut Self {
+ self.inputs.push(input.as_ref().to_owned());
+ self
+ }
+
+ /// Append multiple `.proto` file paths to compile
+ pub fn inputs(&mut self, inputs: impl IntoIterator<Item = impl AsRef<Path>>) -> &mut Self {
+ for input in inputs {
+ self.input(input);
+ }
+ self
+ }
+
+ /// Specify `protoc` command path to be used when invoking code generation.
+ ///
+ /// # Examples
+ ///
+ /// ```no_run
+ /// # mod protoc_bin_vendored {
+ /// # pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
+ /// # unimplemented!()
+ /// # }
+ /// # }
+ ///
+ /// use protobuf_codegen::Codegen;
+ ///
+ /// Codegen::new()
+ /// .protoc()
+ /// .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+ /// // ...
+ /// .run()
+ /// .unwrap();
+ /// ```
+ ///
+ /// This option is ignored when pure Rust parser is used.
+ pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self {
+ self.protoc = Some(protoc.to_owned());
+ self
+ }
+
+ /// Capture stderr to error when running `protoc`.
+ pub fn capture_stderr(&mut self) -> &mut Self {
+ self.capture_stderr = true;
+ self
+ }
+
+ /// Extra command line flags for `protoc` invocation.
+ ///
+ /// For example, `--experimental_allow_proto3_optional` option.
+ ///
+ /// This option is ignored when pure Rust parser is used.
+ pub fn protoc_extra_arg(&mut self, arg: impl Into<OsString>) -> &mut Self {
+ self.protoc_extra_args.push(arg.into());
+ self
+ }
+
+ /// Set options to customize code generation
+ pub fn customize(&mut self, customize: Customize) -> &mut Self {
+ self.customize.update_with(&customize);
+ self
+ }
+
+ /// Callback for dynamic per-element customization.
+ pub fn customize_callback(&mut self, callback: impl CustomizeCallback) -> &mut Self {
+ self.customize_callback = CustomizeCallbackHolder::new(callback);
+ self
+ }
+
+ /// Invoke the code generation.
+ ///
+ /// This is roughly equivalent to `protoc --rust_out=...` but
+ /// without requiring `protoc-gen-rust` command in `$PATH`.
+ ///
+ /// This function uses pure Rust parser or `protoc` parser depending on
+ /// how this object was configured.
+ pub fn run(&self) -> anyhow::Result<()> {
+ let out_dir = match &self.out_dir {
+ Some(out_dir) => out_dir,
+ None => return Err(CodegenError::OutDirNotSpecified.into()),
+ };
+
+ if self.create_out_dir {
+ if out_dir.exists() {
+ fs::remove_dir_all(&out_dir)?;
+ }
+ fs::create_dir(&out_dir)?;
+ }
+
+ let mut parser = Parser::new();
+ parser.protoc();
+ if let Some(protoc) = &self.protoc {
+ parser.protoc_path(protoc);
+ }
+ match &self.which_parser {
+ Some(WhichParser::Protoc) => {
+ parser.protoc();
+ }
+ Some(WhichParser::Pure) => {
+ parser.pure();
+ }
+ None => {}
+ }
+
+ parser.inputs(&self.inputs);
+ parser.includes(&self.includes);
+
+ if self.capture_stderr {
+ parser.capture_stderr();
+ }
+
+ let parsed_and_typechecked = parser
+ .parse_and_typecheck()
+ .context("parse and typecheck")?;
+
+ gen_and_write(
+ &parsed_and_typechecked.file_descriptors,
+ &parsed_and_typechecked.parser,
+ &parsed_and_typechecked.relative_paths,
+ &out_dir,
+ &self.customize,
+ &*self.customize_callback,
+ )
+ }
+
+ /// Similar to `run`, but prints the message to stderr and exits the process on error.
+ pub fn run_from_script(&self) {
+ if let Err(e) = self.run() {
+ eprintln!("codegen failed: {:?}", e);
+ process::exit(1);
+ }
+ }
+}
diff --git a/src/compiler_plugin.rs b/src/compiler_plugin.rs
new file mode 100644
index 0000000..b7ff6ea
--- /dev/null
+++ b/src/compiler_plugin.rs
@@ -0,0 +1,48 @@
+use std::io::stdin;
+use std::io::stdout;
+use std::str;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::plugin::*;
+use protobuf::Message;
+use protobuf_parse::ProtoPathBuf;
+
+pub struct GenRequest<'a> {
+ pub file_descriptors: &'a [FileDescriptorProto],
+ pub files_to_generate: &'a [ProtoPathBuf],
+ pub parameter: &'a str,
+}
+
+pub struct GenResult {
+ pub name: String,
+ pub content: Vec<u8>,
+}
+
+pub fn plugin_main<F>(gen: F) -> anyhow::Result<()>
+where
+ F: Fn(&GenRequest) -> anyhow::Result<Vec<GenResult>>,
+{
+ let req = CodeGeneratorRequest::parse_from_reader(&mut stdin()).unwrap();
+ let result = gen(&GenRequest {
+ file_descriptors: &req.proto_file,
+ files_to_generate: &req
+ .file_to_generate
+ .iter()
+ .map(|n| ProtoPathBuf::new(n.to_owned()))
+ .collect::<anyhow::Result<Vec<_>>>()?,
+ parameter: req.parameter(),
+ })?;
+ let mut resp = CodeGeneratorResponse::new();
+ resp.set_supported_features(code_generator_response::Feature::FEATURE_PROTO3_OPTIONAL as u64);
+ resp.file = result
+ .iter()
+ .map(|file| {
+ let mut r = code_generator_response::File::new();
+ r.set_name(file.name.to_string());
+ r.set_content(str::from_utf8(file.content.as_ref()).unwrap().to_string());
+ r
+ })
+ .collect();
+ resp.write_to_writer(&mut stdout()).unwrap();
+ Ok(())
+}
diff --git a/src/customize.rs b/src/customize.rs
deleted file mode 100644
index b3415ab..0000000
--- a/src/customize.rs
+++ /dev/null
@@ -1,237 +0,0 @@
-use protobuf::descriptor::EnumOptions;
-use protobuf::descriptor::FieldOptions;
-use protobuf::descriptor::FileOptions;
-use protobuf::descriptor::MessageOptions;
-use protobuf::rustproto;
-
-/// Specifies style of generated code.
-#[derive(Default, Debug, Clone)]
-pub struct Customize {
- /// Make oneof enum public.
- pub expose_oneof: Option<bool>,
- /// When true all fields are public, and accessors are not generated
- pub expose_fields: Option<bool>,
- /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
- pub generate_accessors: Option<bool>,
- /// Use `bytes::Bytes` for `bytes` fields
- pub carllerche_bytes_for_bytes: Option<bool>,
- /// Use `bytes::Bytes` for `string` fields
- pub carllerche_bytes_for_string: Option<bool>,
- /// Implement serde_derive for messages
- pub serde_derive: Option<bool>,
- /// When `serde_derive` is set, serde annotations will be guarded with `#[cfg(cfg, ...)]`.
- pub serde_derive_cfg: Option<String>,
- /// When `serde_derive` is set, use attribute rename_all
- pub serde_rename_all: Option<String>,
- /// Enable lite runtime
- pub lite_runtime: Option<bool>,
- /// Generate `mod.rs` in the output directory.
- ///
- /// This option allows inclusion of generated files from cargo output directory.
- ///
- /// This option will likely be on by default in rust-protobuf version 3.
- pub gen_mod_rs: Option<bool>,
- /// Used internally to generate protos bundled in protobuf crate
- /// like `descriptor.proto`
- pub inside_protobuf: Option<bool>,
-
- // When adding more options please keep in sync with `parse_from_parameter` below.
- /// Make sure `Customize` is always used with `..Default::default()`
- /// for future compatibility.
- pub _future_options: (),
-}
-
-#[derive(Debug)]
-pub enum CustomizeParseParameterError {
- EqNotFound,
- CannotParseBool,
- UnknownOptionName(String),
-}
-
-pub type CustomizeParseParameterResult<T> = Result<T, CustomizeParseParameterError>;
-
-impl Customize {
- /// Update fields of self with fields defined in other customize
- pub fn update_with(&mut self, that: &Customize) {
- if let Some(v) = that.expose_oneof {
- self.expose_oneof = Some(v);
- }
- if let Some(v) = that.expose_fields {
- self.expose_fields = Some(v);
- }
- if let Some(v) = that.generate_accessors {
- self.generate_accessors = Some(v);
- }
- if let Some(v) = that.carllerche_bytes_for_bytes {
- self.carllerche_bytes_for_bytes = Some(v);
- }
- if let Some(v) = that.carllerche_bytes_for_string {
- self.carllerche_bytes_for_string = Some(v);
- }
- if let Some(v) = that.serde_derive {
- self.serde_derive = Some(v);
- }
- if let Some(ref v) = that.serde_derive_cfg {
- self.serde_derive_cfg = Some(v.clone());
- }
- if let Some(ref v) = that.serde_rename_all {
- self.serde_rename_all = Some(v.clone());
- }
- if let Some(v) = that.lite_runtime {
- self.lite_runtime = Some(v);
- }
- if let Some(v) = that.gen_mod_rs {
- self.gen_mod_rs = Some(v);
- }
- if let Some(v) = that.inside_protobuf {
- self.inside_protobuf = Some(v);
- }
- }
-
- /// Update unset fields of self with fields from other customize
- pub fn set_defaults_from(&mut self, other: &Customize) {
- let mut tmp = other.clone();
- tmp.update_with(self);
- *self = tmp;
- }
-
- /// Parse customize options from a string passed via protoc flag.
- pub fn parse_from_parameter(parameter: &str) -> CustomizeParseParameterResult<Customize> {
- fn parse_bool(v: &str) -> CustomizeParseParameterResult<bool> {
- v.parse()
- .map_err(|_| CustomizeParseParameterError::CannotParseBool)
- }
-
- let mut r = Customize::default();
- for nv in parameter.split_whitespace() {
- let eq = match nv.find('=') {
- Some(eq) => eq,
- None => return Err(CustomizeParseParameterError::EqNotFound),
- };
-
- let n = &nv[..eq];
- let v = &nv[eq + 1..];
-
- if n == "expose_oneof" {
- r.expose_oneof = Some(parse_bool(v)?);
- } else if n == "expose_fields" {
- r.expose_fields = Some(parse_bool(v)?);
- } else if n == "generate_accessors" {
- r.generate_accessors = Some(parse_bool(v)?);
- } else if n == "carllerche_bytes_for_bytes" {
- r.carllerche_bytes_for_bytes = Some(parse_bool(v)?);
- } else if n == "carllerche_bytes_for_string" {
- r.carllerche_bytes_for_string = Some(parse_bool(v)?);
- } else if n == "serde_derive" {
- r.serde_derive = Some(parse_bool(v)?);
- } else if n == "serde_derive_cfg" {
- r.serde_derive_cfg = Some(v.to_owned());
- } else if n == "serde_rename_all" {
- r.serde_rename_all = Some(v.to_owned());
- } else if n == "lite_runtime" {
- r.lite_runtime = Some(parse_bool(v)?);
- } else if n == "gen_mod_rs" {
- r.gen_mod_rs = Some(parse_bool(v)?);
- } else if n == "inside_protobuf" {
- r.inside_protobuf = Some(parse_bool(v)?);
- } else {
- return Err(CustomizeParseParameterError::UnknownOptionName(
- n.to_owned(),
- ));
- }
- }
- Ok(r)
- }
-}
-
-pub fn customize_from_rustproto_for_message(source: &MessageOptions) -> Customize {
- let expose_oneof = rustproto::exts::expose_oneof.get(source);
- let expose_fields = rustproto::exts::expose_fields.get(source);
- let generate_accessors = rustproto::exts::generate_accessors.get(source);
- let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes.get(source);
- let carllerche_bytes_for_string = rustproto::exts::carllerche_bytes_for_string.get(source);
- let serde_derive = rustproto::exts::serde_derive.get(source);
- let serde_derive_cfg = rustproto::exts::serde_derive_cfg.get(source);
- let lite_runtime = None;
- let gen_mod_rs = None;
- let inside_protobuf = None;
- let serde_rename_all = None;
- Customize {
- expose_oneof,
- expose_fields,
- generate_accessors,
- carllerche_bytes_for_bytes,
- carllerche_bytes_for_string,
- serde_derive,
- serde_derive_cfg,
- serde_rename_all,
- lite_runtime,
- gen_mod_rs,
- inside_protobuf,
- _future_options: (),
- }
-}
-
-pub fn customize_from_rustproto_for_enum(source: &EnumOptions) -> Customize {
- let serde_rename_all = rustproto::exts::serde_rename_all.get(source);
- let mut r = Customize::default();
- r.serde_rename_all = serde_rename_all;
- return r;
-}
-
-pub fn customize_from_rustproto_for_field(source: &FieldOptions) -> Customize {
- let expose_oneof = None;
- let expose_fields = rustproto::exts::expose_fields_field.get(source);
- let generate_accessors = rustproto::exts::generate_accessors_field.get(source);
- let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes_field.get(source);
- let carllerche_bytes_for_string =
- rustproto::exts::carllerche_bytes_for_string_field.get(source);
- let serde_rename_all = None;
- let serde_derive = None;
- let serde_derive_cfg = None;
- let lite_runtime = None;
- let gen_mod_rs = None;
- let inside_protobuf = None;
- Customize {
- expose_oneof,
- expose_fields,
- generate_accessors,
- carllerche_bytes_for_bytes,
- carllerche_bytes_for_string,
- serde_derive,
- serde_derive_cfg,
- serde_rename_all,
- lite_runtime,
- gen_mod_rs,
- inside_protobuf,
- _future_options: (),
- }
-}
-
-pub fn customize_from_rustproto_for_file(source: &FileOptions) -> Customize {
- let expose_oneof = rustproto::exts::expose_oneof_all.get(source);
- let expose_fields = rustproto::exts::expose_fields_all.get(source);
- let generate_accessors = rustproto::exts::generate_accessors_all.get(source);
- let carllerche_bytes_for_bytes = rustproto::exts::carllerche_bytes_for_bytes_all.get(source);
- let carllerche_bytes_for_string = rustproto::exts::carllerche_bytes_for_string_all.get(source);
- let serde_derive = rustproto::exts::serde_derive_all.get(source);
- let serde_derive_cfg = rustproto::exts::serde_derive_cfg_all.get(source);
- let lite_runtime = rustproto::exts::lite_runtime_all.get(source);
- let gen_mod_rs = None;
- let inside_protobuf = None;
- let serde_rename_all = None;
- Customize {
- expose_oneof,
- expose_fields,
- generate_accessors,
- carllerche_bytes_for_bytes,
- carllerche_bytes_for_string,
- serde_derive,
- serde_derive_cfg,
- serde_rename_all,
- lite_runtime,
- inside_protobuf,
- gen_mod_rs,
- _future_options: (),
- }
-}
diff --git a/src/customize/ctx.rs b/src/customize/ctx.rs
new file mode 100644
index 0000000..185fc31
--- /dev/null
+++ b/src/customize/ctx.rs
@@ -0,0 +1,92 @@
+use std::fmt;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+use crate::customize::CustomizeCallback;
+use crate::Customize;
+
+#[derive(Clone)]
+pub(crate) struct CustomizeElemCtx<'a> {
+ pub(crate) for_elem: Customize,
+ pub(crate) for_children: Customize,
+ pub(crate) callback: &'a dyn CustomizeCallback,
+}
+
+impl<'a> fmt::Debug for CustomizeElemCtx<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("CustomizeElemCtx")
+ .field("for_elem", &self.for_elem)
+ .field("for_children", &self.for_children)
+ .finish_non_exhaustive()
+ }
+}
+
+impl<'a> CustomizeElemCtx<'a> {
+ pub(crate) fn child(
+ &self,
+ elem_from_rustproto: &Customize,
+ elem_descriptor: &impl DescriptorForCustomize,
+ ) -> CustomizeElemCtx<'a> {
+ let mut for_elem = self.for_children.clone();
+ for_elem.update_with(elem_from_rustproto);
+
+ let for_children = for_elem.clone();
+
+ for_elem.update_with(&elem_descriptor.customize(self.callback));
+
+ CustomizeElemCtx {
+ for_elem,
+ for_children,
+ callback: self.callback,
+ }
+ }
+}
+
+pub(crate) trait DescriptorForCustomize {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize;
+}
+
+impl DescriptorForCustomize for MessageDescriptor {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.message(self)
+ }
+}
+
+impl DescriptorForCustomize for FieldDescriptor {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.field(self)
+ }
+}
+
+impl DescriptorForCustomize for EnumDescriptor {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.enumeration(self)
+ }
+}
+
+impl DescriptorForCustomize for OneofDescriptor {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.oneof(self)
+ }
+}
+
+impl DescriptorForCustomize for FileDescriptor {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.file(self)
+ }
+}
+
+pub(crate) struct SpecialFieldPseudoDescriptor<'a> {
+ pub(crate) message: &'a MessageDescriptor,
+ pub(crate) field: &'a str,
+}
+
+impl<'a> DescriptorForCustomize for SpecialFieldPseudoDescriptor<'a> {
+ fn customize(&self, callback: &dyn CustomizeCallback) -> Customize {
+ callback.special_field(self.message, self.field)
+ }
+}
diff --git a/src/customize/mod.rs b/src/customize/mod.rs
new file mode 100644
index 0000000..826bd3f
--- /dev/null
+++ b/src/customize/mod.rs
@@ -0,0 +1,251 @@
+pub(crate) mod ctx;
+pub(crate) mod rustproto_proto;
+
+use std::fmt;
+use std::ops::Deref;
+use std::rc::Rc;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+/// Dynamic callback to customize code generation.
+pub trait CustomizeCallback: 'static {
+ fn file(&self, file: &FileDescriptor) -> Customize {
+ let _ = file;
+ Customize::default()
+ }
+
+ fn message(&self, message: &MessageDescriptor) -> Customize {
+ let _ = message;
+ Customize::default()
+ }
+
+ fn field(&self, field: &FieldDescriptor) -> Customize {
+ let _ = field;
+ Customize::default()
+ }
+
+ fn special_field(&self, message: &MessageDescriptor, field: &str) -> Customize {
+ let _ = (message, field);
+ Customize::default()
+ }
+
+ fn enumeration(&self, enum_type: &EnumDescriptor) -> Customize {
+ let _ = enum_type;
+ Customize::default()
+ }
+
+ fn oneof(&self, oneof: &OneofDescriptor) -> Customize {
+ let _ = oneof;
+ Customize::default()
+ }
+}
+
+pub(crate) struct CustomizeCallbackDefault;
+impl CustomizeCallback for CustomizeCallbackDefault {}
+
+#[derive(Clone)]
+pub(crate) struct CustomizeCallbackHolder(pub(crate) Rc<dyn CustomizeCallback>);
+
+impl CustomizeCallbackHolder {
+ pub(crate) fn new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder {
+ CustomizeCallbackHolder(Rc::new(callback))
+ }
+}
+
+impl Deref for CustomizeCallbackHolder {
+ type Target = dyn CustomizeCallback;
+
+ fn deref(&self) -> &Self::Target {
+ &*self.0
+ }
+}
+
+impl Default for CustomizeCallbackHolder {
+ fn default() -> Self {
+ CustomizeCallbackHolder(Rc::new(CustomizeCallbackDefault))
+ }
+}
+
+impl PartialEq for CustomizeCallbackHolder {
+ fn eq(&self, other: &Self) -> bool {
+ Rc::ptr_eq(&self.0, &other.0)
+ }
+}
+
+impl fmt::Debug for CustomizeCallbackHolder {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("CustomizeCallbackWrapper")
+ .finish_non_exhaustive()
+ }
+}
+
+/// Specifies style of generated code.
+/// Generated files can be customized using this proto
+/// or using `rustproto.proto` options.
+#[derive(Default, Debug, Clone, PartialEq)]
+pub struct Customize {
+ /// Code to insert before the element in the generated file.
+ pub(crate) before: Option<String>,
+ /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated
+ pub(crate) generate_accessors: Option<bool>,
+ /// When false, `get_` is not generated even if `syntax = "proto2"`
+ pub(crate) generate_getter: Option<bool>,
+ /// Use `bytes::Bytes` for `bytes` fields
+ pub(crate) tokio_bytes: Option<bool>,
+ /// Use `bytes::Bytes` for `string` fields
+ pub(crate) tokio_bytes_for_string: Option<bool>,
+ /// Enable lite runtime.
+ pub(crate) lite_runtime: Option<bool>,
+ /// Generate `mod.rs` in the output directory.
+ ///
+ /// This option allows inclusion of generated files from cargo output directory.
+ ///
+ /// This option will likely be on by default in rust-protobuf version 3.
+ pub(crate) gen_mod_rs: Option<bool>,
+ /// Used internally to generate protos bundled in protobuf crate
+ /// like `descriptor.proto`
+ pub(crate) inside_protobuf: Option<bool>,
+}
+
+#[derive(Debug, thiserror::Error)]
+pub(crate) enum CustomizeParseParameterError {
+ #[error("Cannot parse bool option value: {:?}", .0)]
+ CannotParseBool(String),
+ #[error("Unknown option name: {:?}", .0)]
+ UnknownOptionName(String),
+}
+
+impl Customize {
+ /// Insert code before the element in the generated file
+ /// (e. g. serde annotations, see
+ /// [example here](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)).
+ pub fn before(mut self, before: &str) -> Self {
+ self.before = Some(before.to_owned());
+ self
+ }
+
+ pub fn generate_accessors(mut self, generate_accessors: bool) -> Self {
+ self.generate_accessors = Some(generate_accessors);
+ self
+ }
+
+ pub fn generate_getter(mut self, generate_getter: bool) -> Self {
+ self.generate_getter = Some(generate_getter);
+ self
+ }
+
+ pub fn tokio_bytes(mut self, tokio_bytes: bool) -> Self {
+ self.tokio_bytes = Some(tokio_bytes);
+ self
+ }
+
+ pub fn tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self {
+ self.tokio_bytes_for_string = Some(tokio_bytes_for_string);
+ self
+ }
+
+ /// Generate code for "lite runtime". Generated code contains no code for reflection.
+ /// So the generated code (and more importantly, generated binary size) is smaller,
+ /// but reflection, text format, JSON serialization won't work.
+ ///
+ /// Note when using `protoc` plugin `protoc-gen-rust`, the option name is just `lite`.
+ pub fn lite_runtime(mut self, lite_runtime: bool) -> Self {
+ self.lite_runtime = Some(lite_runtime);
+ self
+ }
+
+ /// Generate `mod.rs` with all the generated modules.
+ /// This option is on by default in rust-protobuf version 3.
+ pub fn gen_mod_rs(mut self, gen_mod_rs: bool) -> Self {
+ self.gen_mod_rs = Some(gen_mod_rs);
+ self
+ }
+
+ /// Generate code bundled in protobuf crate. Regular users don't need this option.
+ pub fn inside_protobuf(mut self, inside_protobuf: bool) -> Self {
+ self.inside_protobuf = Some(inside_protobuf);
+ self
+ }
+
+ /// Update fields of self with fields defined in other customize
+ pub fn update_with(&mut self, that: &Customize) {
+ if let Some(v) = &that.before {
+ self.before = Some(v.clone());
+ }
+ if let Some(v) = that.generate_accessors {
+ self.generate_accessors = Some(v);
+ }
+ if let Some(v) = that.generate_getter {
+ self.generate_getter = Some(v);
+ }
+ if let Some(v) = that.tokio_bytes {
+ self.tokio_bytes = Some(v);
+ }
+ if let Some(v) = that.tokio_bytes_for_string {
+ self.tokio_bytes_for_string = Some(v);
+ }
+ if let Some(v) = that.lite_runtime {
+ self.lite_runtime = Some(v);
+ }
+ if let Some(v) = that.gen_mod_rs {
+ self.gen_mod_rs = Some(v);
+ }
+ if let Some(v) = that.inside_protobuf {
+ self.inside_protobuf = Some(v);
+ }
+ }
+
+ /// Update unset fields of self with fields from other customize
+ pub fn set_defaults_from(&mut self, other: &Customize) {
+ let mut tmp = other.clone();
+ tmp.update_with(self);
+ *self = tmp;
+ }
+
+ /// Parse customize options from a string passed via protoc flag.
+ pub fn parse_from_parameter(parameter: &str) -> anyhow::Result<Customize> {
+ fn parse_bool(v: &str) -> anyhow::Result<bool> {
+ v.parse()
+ .map_err(|_| CustomizeParseParameterError::CannotParseBool(v.to_owned()).into())
+ }
+
+ let mut r = Customize::default();
+ for nv in parameter.split_whitespace() {
+ let (n, v) = match nv.find('=') {
+ Some(eq) => {
+ let n = &nv[..eq];
+ let v = &nv[eq + 1..];
+ (n, v)
+ }
+ None => (nv, "true"),
+ };
+
+ if n == "generate_accessors" {
+ r.generate_accessors = Some(parse_bool(v)?);
+ } else if n == "generate_getter" {
+ r.generate_getter = Some(parse_bool(v)?);
+ } else if n == "tokio_bytes" {
+ r.tokio_bytes = Some(parse_bool(v)?);
+ } else if n == "tokio_bytes_for_string" {
+ r.tokio_bytes_for_string = Some(parse_bool(v)?);
+ } else if n == "lite_runtime" {
+ r.lite_runtime = Some(parse_bool(v)?);
+ } else if n == "gen_mod_rs" {
+ r.gen_mod_rs = Some(parse_bool(v)?);
+ } else if n == "inside_protobuf" {
+ r.inside_protobuf = Some(parse_bool(v)?);
+ } else if n == "lite" {
+ // Support Java and C++ protoc plugin syntax:
+ // https://github.com/protocolbuffers/protobuf/issues/6489
+ r.lite_runtime = Some(parse_bool(v)?);
+ } else {
+ return Err(CustomizeParseParameterError::UnknownOptionName(n.to_owned()).into());
+ }
+ }
+ Ok(r)
+ }
+}
diff --git a/src/customize/rustproto_proto.rs b/src/customize/rustproto_proto.rs
new file mode 100644
index 0000000..3d9a77b
--- /dev/null
+++ b/src/customize/rustproto_proto.rs
@@ -0,0 +1,74 @@
+use protobuf::descriptor::EnumOptions;
+use protobuf::descriptor::FieldOptions;
+use protobuf::descriptor::FileOptions;
+use protobuf::descriptor::MessageOptions;
+use protobuf::rustproto;
+
+use crate::Customize;
+
+pub(crate) fn customize_from_rustproto_for_message(source: &MessageOptions) -> Customize {
+ let before = None;
+ let generate_accessors = rustproto::exts::generate_accessors.get(source);
+ let generate_getter = rustproto::exts::generate_getter.get(source);
+ let tokio_bytes = rustproto::exts::tokio_bytes.get(source);
+ let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string.get(source);
+ let lite_runtime = None;
+ let gen_mod_rs = None;
+ let inside_protobuf = None;
+ Customize {
+ before,
+ generate_accessors,
+ generate_getter,
+ tokio_bytes,
+ tokio_bytes_for_string,
+ lite_runtime,
+ gen_mod_rs,
+ inside_protobuf,
+ }
+}
+
+pub(crate) fn customize_from_rustproto_for_enum(_source: &EnumOptions) -> Customize {
+ Customize::default()
+}
+
+pub(crate) fn customize_from_rustproto_for_field(source: &FieldOptions) -> Customize {
+ let before = None;
+ let generate_accessors = rustproto::exts::generate_accessors_field.get(source);
+ let generate_getter = rustproto::exts::generate_getter_field.get(source);
+ let tokio_bytes = rustproto::exts::tokio_bytes_field.get(source);
+ let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_field.get(source);
+ let lite_runtime = None;
+ let gen_mod_rs = None;
+ let inside_protobuf = None;
+ Customize {
+ before,
+ generate_accessors,
+ generate_getter,
+ tokio_bytes,
+ tokio_bytes_for_string,
+ lite_runtime,
+ gen_mod_rs,
+ inside_protobuf,
+ }
+}
+
+pub(crate) fn customize_from_rustproto_for_file(source: &FileOptions) -> Customize {
+ let before = None;
+ let generate_accessors = rustproto::exts::generate_accessors_all.get(source);
+ let generate_getter = rustproto::exts::generate_getter_all.get(source);
+ let tokio_bytes = rustproto::exts::tokio_bytes_all.get(source);
+ let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_all.get(source);
+ let lite_runtime = rustproto::exts::lite_runtime_all.get(source);
+ let gen_mod_rs = None;
+ let inside_protobuf = None;
+ Customize {
+ before,
+ generate_accessors,
+ generate_getter,
+ tokio_bytes,
+ tokio_bytes_for_string,
+ lite_runtime,
+ inside_protobuf,
+ gen_mod_rs,
+ }
+}
diff --git a/src/enums.rs b/src/enums.rs
deleted file mode 100644
index ec3444e..0000000
--- a/src/enums.rs
+++ /dev/null
@@ -1,377 +0,0 @@
-use std::collections::HashSet;
-
-use file_descriptor::file_descriptor_proto_expr;
-use inside::protobuf_crate_path;
-use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_types_values::type_name_to_rust_relative;
-use scope::EnumWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-use CodeWriter;
-
-use crate::customize::customize_from_rustproto_for_enum;
-use crate::Customize;
-
-#[derive(Clone)]
-pub struct EnumValueGen {
- proto: EnumValueDescriptorProto,
- enum_rust_name: String,
- variant_rust_name: String,
-}
-
-impl EnumValueGen {
- fn parse(
- proto: &EnumValueDescriptorProto,
- enum_rust_name: &str,
- variant_rust_name: &str,
- ) -> EnumValueGen {
- EnumValueGen {
- proto: proto.clone(),
- enum_rust_name: enum_rust_name.to_string(),
- variant_rust_name: variant_rust_name.to_string(),
- }
- }
-
- // enum value
- fn number(&self) -> i32 {
- self.proto.get_number()
- }
-
- // name of enum variant in generated rust code
- fn rust_name_inner(&self) -> String {
- self.variant_rust_name.clone()
- }
-
- pub fn rust_name_outer(&self) -> String {
- let mut r = String::new();
- r.push_str(&self.enum_rust_name);
- r.push_str("::");
- r.push_str(&self.rust_name_inner());
- r
- }
-}
-
-pub(crate) struct EnumGen<'a> {
- enum_with_scope: &'a EnumWithScope<'a>,
- type_name: String,
- lite_runtime: bool,
- customize: Customize,
-}
-
-impl<'a> EnumGen<'a> {
- pub fn new(
- enum_with_scope: &'a EnumWithScope<'a>,
- current_file: &FileDescriptorProto,
- customize: &Customize,
- root_scope: &RootScope,
- ) -> EnumGen<'a> {
- let rust_name = if enum_with_scope.get_scope().get_file_descriptor().get_name()
- == current_file.get_name()
- {
- // field type is a message or enum declared in the same file
- enum_with_scope.rust_name().to_string()
- } else {
- type_name_to_rust_relative(
- &ProtobufAbsolutePath::from(enum_with_scope.name_absolute()),
- current_file,
- false,
- root_scope,
- customize,
- )
- .to_string()
- };
- let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
- enum_with_scope
- .get_scope()
- .get_file_descriptor()
- .get_options()
- .get_optimize_for()
- == FileOptions_OptimizeMode::LITE_RUNTIME
- });
-
- let mut customize = customize.clone();
- customize.update_with(&customize_from_rustproto_for_enum(
- enum_with_scope.en.options.as_ref().unwrap_or_default(),
- ));
-
- EnumGen {
- enum_with_scope,
- type_name: rust_name,
- lite_runtime,
- customize,
- }
- }
-
- fn allow_alias(&self) -> bool {
- self.enum_with_scope.en.get_options().get_allow_alias()
- }
-
- fn values_all(&self) -> Vec<EnumValueGen> {
- let mut r = Vec::new();
- for p in self.enum_with_scope.values() {
- r.push(EnumValueGen::parse(
- &p.proto,
- &self.type_name,
- p.rust_name().get(),
- ));
- }
- r
- }
-
- pub fn values_unique(&self) -> Vec<EnumValueGen> {
- let mut used = HashSet::new();
- let mut r = Vec::new();
- for p in self.enum_with_scope.values() {
- // skipping non-unique enums
- // TODO: should support it
- if !used.insert(p.proto.get_number()) {
- continue;
- }
- r.push(EnumValueGen::parse(
- p.proto,
- &self.type_name,
- p.rust_name().get(),
- ));
- }
- r
- }
-
- // find enum value by name
- pub fn value_by_name(&'a self, name: &str) -> EnumValueGen {
- let v = self.enum_with_scope.value_by_name(name);
- EnumValueGen::parse(v.proto, &self.type_name, v.rust_name().get())
- }
-
- pub fn write(&self, w: &mut CodeWriter) {
- self.write_struct(w);
- if self.allow_alias() {
- w.write_line("");
- self.write_impl_eq(w);
- w.write_line("");
- self.write_impl_hash(w);
- }
- w.write_line("");
- self.write_impl_enum(w);
- w.write_line("");
- self.write_impl_copy(w);
- w.write_line("");
- self.write_impl_default(w);
- w.write_line("");
- self.write_impl_value(w);
- }
-
- fn write_struct(&self, w: &mut CodeWriter) {
- let mut derive = Vec::new();
- derive.push("Clone");
- if !self.allow_alias() {
- derive.push("PartialEq");
- }
- derive.push("Eq");
- derive.push("Debug");
- if !self.allow_alias() {
- derive.push("Hash");
- } else {
- w.comment("Note: you cannot use pattern matching for enums with allow_alias option");
- }
- w.derive(&derive);
- serde::write_serde_attr(
- w,
- &self.customize,
- "derive(::serde::Serialize, ::serde::Deserialize)",
- );
- if let Some(ref ren) = self.customize.serde_rename_all {
- let attr = format!("serde(rename_all = \"{}\")", ren);
- serde::write_serde_attr(w, &self.customize, &attr);
- }
- let ref type_name = self.type_name;
- w.expr_block(&format!("pub enum {}", type_name), |w| {
- for value in self.values_all() {
- if self.allow_alias() {
- w.write_line(&format!(
- "{}, // {}",
- value.rust_name_inner(),
- value.number()
- ));
- } else {
- w.write_line(&format!(
- "{} = {},",
- value.rust_name_inner(),
- value.number()
- ));
- }
- }
- });
- }
-
- fn write_fn_value(&self, w: &mut CodeWriter) {
- w.def_fn("value(&self) -> i32", |w| {
- if self.allow_alias() {
- w.match_expr("*self", |w| {
- for value in self.values_all() {
- w.case_expr(value.rust_name_outer(), format!("{}", value.number()));
- }
- });
- } else {
- w.write_line("*self as i32")
- }
- });
- }
-
- fn write_impl_enum(&self, w: &mut CodeWriter) {
- let ref type_name = self.type_name;
- w.impl_for_block(
- &format!("{}::ProtobufEnum", protobuf_crate_path(&self.customize)),
- &format!("{}", type_name),
- |w| {
- self.write_fn_value(w);
-
- w.write_line("");
- let ref type_name = self.type_name;
- w.def_fn(
- &format!(
- "from_i32(value: i32) -> ::std::option::Option<{}>",
- type_name
- ),
- |w| {
- w.match_expr("value", |w| {
- let values = self.values_unique();
- for value in values {
- w.write_line(&format!(
- "{} => ::std::option::Option::Some({}),",
- value.number(),
- value.rust_name_outer()
- ));
- }
- w.write_line(&format!("_ => ::std::option::Option::None"));
- });
- },
- );
-
- w.write_line("");
- w.def_fn(&format!("values() -> &'static [Self]"), |w| {
- w.write_line(&format!("static values: &'static [{}] = &[", type_name));
- w.indented(|w| {
- for value in self.values_all() {
- w.write_line(&format!("{},", value.rust_name_outer()));
- }
- });
- w.write_line("];");
- w.write_line("values");
- });
-
- if !self.lite_runtime {
- w.write_line("");
- w.def_fn(
- &format!(
- "enum_descriptor_static() -> &'static {}::reflect::EnumDescriptor",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.lazy_static_decl_get(
- "descriptor",
- &format!(
- "{}::reflect::EnumDescriptor",
- protobuf_crate_path(&self.customize)
- ),
- &self.customize,
- |w| {
- let ref type_name = self.type_name;
- w.write_line(&format!(
- "{}::reflect::EnumDescriptor::new_pb_name::<{}>(\"{}\", {})",
- protobuf_crate_path(&self.customize),
- type_name,
- self.enum_with_scope.name_to_package(),
- file_descriptor_proto_expr(&self.enum_with_scope.scope)
- ));
- },
- );
- },
- );
- }
- },
- );
- }
-
- fn write_impl_value(&self, w: &mut CodeWriter) {
- w.impl_for_block(
- &format!(
- "{}::reflect::ProtobufValue",
- protobuf_crate_path(&self.customize)
- ),
- &format!("{}", self.type_name),
- |w| {
- w.def_fn(
- &format!(
- "as_ref(&self) -> {}::reflect::ReflectValueRef",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.write_line(&format!(
- "{}::reflect::ReflectValueRef::Enum({}::ProtobufEnum::descriptor(self))",
- protobuf_crate_path(&self.customize),
- protobuf_crate_path(&self.customize)
- ))
- },
- )
- },
- )
- }
-
- fn write_impl_copy(&self, w: &mut CodeWriter) {
- w.impl_for_block("::std::marker::Copy", &self.type_name, |_w| {});
- }
-
- fn write_impl_eq(&self, w: &mut CodeWriter) {
- assert!(self.allow_alias());
- w.impl_for_block(
- "::std::cmp::PartialEq",
- &format!("{}", self.type_name),
- |w| {
- w.def_fn("eq(&self, other: &Self) -> bool", |w| {
- w.write_line(&format!(
- "{}::ProtobufEnum::value(self) == {}::ProtobufEnum::value(other)",
- protobuf_crate_path(&self.customize),
- protobuf_crate_path(&self.customize)
- ));
- });
- },
- );
- }
-
- fn write_impl_hash(&self, w: &mut CodeWriter) {
- assert!(self.allow_alias());
- w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
- w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
- w.write_line(&format!(
- "state.write_i32({}::ProtobufEnum::value(self))",
- protobuf_crate_path(&self.customize)
- ));
- });
- });
- }
-
- fn write_impl_default(&self, w: &mut CodeWriter) {
- let first_value = &self.enum_with_scope.values()[0];
- if first_value.proto.get_number() != 0 {
- // This warning is emitted only for proto2
- // (because in proto3 first enum variant number is always 0).
- // `Default` implemented unconditionally to simplify certain
- // generic operations, e. g. reading a map.
- // Also, note that even in proto2 some operations fallback to
- // first enum value, e. g. `get_xxx` for unset field,
- // so this implementation is not completely unreasonable.
- w.comment("Note, `Default` is implemented although default value is not 0");
- }
- w.impl_for_block("::std::default::Default", &self.type_name, |w| {
- w.def_fn("default() -> Self", |w| {
- w.write_line(&format!(
- "{}::{}",
- &self.type_name,
- &first_value.rust_name()
- ))
- });
- });
- }
-}
diff --git a/src/extensions.rs b/src/extensions.rs
deleted file mode 100644
index def5948..0000000
--- a/src/extensions.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use field::rust_field_name_for_protobuf_field_name;
-use inside::protobuf_crate_path;
-use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use scope::RootScope;
-use Customize;
-
-use super::code_writer::CodeWriter;
-use super::rust_types_values::*;
-
-struct ExtGen<'a> {
- file: &'a FileDescriptorProto,
- root_scope: &'a RootScope<'a>,
- field: &'a FieldDescriptorProto,
- customize: Customize,
-}
-
-impl<'a> ExtGen<'a> {
- fn extendee_rust_name(&self) -> String {
- type_name_to_rust_relative(
- &ProtobufAbsolutePath::from(self.field.get_extendee()),
- self.file,
- true,
- self.root_scope,
- &self.customize,
- )
- }
-
- fn repeated(&self) -> bool {
- match self.field.get_label() {
- FieldDescriptorProto_Label::LABEL_REPEATED => true,
- FieldDescriptorProto_Label::LABEL_OPTIONAL => false,
- FieldDescriptorProto_Label::LABEL_REQUIRED => {
- panic!("required ext field: {}", self.field.get_name())
- }
- }
- }
-
- fn return_type_gen(&self) -> ProtobufTypeGen {
- if self.field.has_type_name() {
- let rust_name_relative = type_name_to_rust_relative(
- &ProtobufAbsolutePath::from(self.field.get_type_name()),
- self.file,
- true,
- self.root_scope,
- &self.customize,
- );
- match self.field.get_field_type() {
- FieldDescriptorProto_Type::TYPE_MESSAGE => {
- ProtobufTypeGen::Message(rust_name_relative)
- }
- FieldDescriptorProto_Type::TYPE_ENUM => ProtobufTypeGen::Enum(rust_name_relative),
- t => panic!("unknown type: {:?}", t),
- }
- } else {
- ProtobufTypeGen::Primitive(self.field.get_field_type(), PrimitiveTypeVariant::Default)
- }
- }
-
- fn write(&self, w: &mut CodeWriter) {
- let suffix = if self.repeated() {
- "Repeated"
- } else {
- "Optional"
- };
- let field_type = format!(
- "{}::ext::ExtField{}",
- protobuf_crate_path(&self.customize),
- suffix
- );
- w.pub_const(
- rust_field_name_for_protobuf_field_name(self.field.get_name()).get(),
- &format!(
- "{}<{}, {}>",
- field_type,
- self.extendee_rust_name(),
- self.return_type_gen().rust_type(&self.customize),
- ),
- &format!(
- "{} {{ field_number: {}, phantom: ::std::marker::PhantomData }}",
- field_type,
- self.field.get_number()
- ),
- );
- }
-}
-
-pub(crate) fn write_extensions(
- file: &FileDescriptorProto,
- root_scope: &RootScope,
- w: &mut CodeWriter,
- customize: &Customize,
-) {
- if file.get_extension().is_empty() {
- return;
- }
-
- w.write_line("");
- w.write_line("/// Extension fields");
- w.pub_mod("exts", |w| {
- for field in file.get_extension() {
- if field.get_field_type() == FieldDescriptorProto_Type::TYPE_GROUP {
- continue;
- }
-
- w.write_line("");
- ExtGen {
- file: file,
- root_scope: root_scope,
- field: field,
- customize: customize.clone(),
- }
- .write(w);
- }
- });
-}
diff --git a/src/field/mod.rs b/src/field/mod.rs
deleted file mode 100644
index e4be944..0000000
--- a/src/field/mod.rs
+++ /dev/null
@@ -1,2031 +0,0 @@
-use std::marker;
-
-use float;
-use inside::protobuf_crate_path;
-use message::RustTypeMessage;
-use oneof::OneofField;
-use protobuf::descriptor::*;
-use protobuf::rt;
-use protobuf::rust;
-use protobuf::text_format;
-use protobuf::wire_format;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use rust_name::RustIdentWithPath;
-use scope::FieldWithContext;
-use scope::MessageOrEnumWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use syntax::Syntax;
-
-use super::code_writer::CodeWriter;
-use super::customize::customize_from_rustproto_for_field;
-use super::customize::Customize;
-use super::enums::*;
-use super::rust_types_values::*;
-
-fn type_is_copy(field_type: FieldDescriptorProto_Type) -> bool {
- match field_type {
- FieldDescriptorProto_Type::TYPE_MESSAGE
- | FieldDescriptorProto_Type::TYPE_STRING
- | FieldDescriptorProto_Type::TYPE_BYTES => false,
- _ => true,
- }
-}
-
-trait FieldDescriptorProtoTypeExt {
- fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String;
- fn is_s_varint(&self) -> bool;
-}
-
-impl FieldDescriptorProtoTypeExt for FieldDescriptorProto_Type {
- fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String {
- match primitive_type_variant {
- PrimitiveTypeVariant::Default => format!("{}.read_{}()", is, protobuf_name(*self)),
- PrimitiveTypeVariant::Carllerche => {
- let protobuf_name = match self {
- &FieldDescriptorProto_Type::TYPE_STRING => "chars",
- _ => protobuf_name(*self),
- };
- format!("{}.read_carllerche_{}()", is, protobuf_name)
- }
- }
- }
-
- /// True if self is signed integer with zigzag encoding
- fn is_s_varint(&self) -> bool {
- match *self {
- FieldDescriptorProto_Type::TYPE_SINT32 | FieldDescriptorProto_Type::TYPE_SINT64 => true,
- _ => false,
- }
- }
-}
-
-fn field_type_wire_type(field_type: FieldDescriptorProto_Type) -> wire_format::WireType {
- use protobuf::wire_format::*;
- match field_type {
- FieldDescriptorProto_Type::TYPE_INT32 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_INT64 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_UINT32 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_UINT64 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_SINT32 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_SINT64 => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_BOOL => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_ENUM => WireTypeVarint,
- FieldDescriptorProto_Type::TYPE_FIXED32 => WireTypeFixed32,
- FieldDescriptorProto_Type::TYPE_FIXED64 => WireTypeFixed64,
- FieldDescriptorProto_Type::TYPE_SFIXED32 => WireTypeFixed32,
- FieldDescriptorProto_Type::TYPE_SFIXED64 => WireTypeFixed64,
- FieldDescriptorProto_Type::TYPE_FLOAT => WireTypeFixed32,
- FieldDescriptorProto_Type::TYPE_DOUBLE => WireTypeFixed64,
- FieldDescriptorProto_Type::TYPE_STRING => WireTypeLengthDelimited,
- FieldDescriptorProto_Type::TYPE_BYTES => WireTypeLengthDelimited,
- FieldDescriptorProto_Type::TYPE_MESSAGE => WireTypeLengthDelimited,
- FieldDescriptorProto_Type::TYPE_GROUP => WireTypeLengthDelimited, // not true
- }
-}
-
-fn type_protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str {
- match field_type {
- FieldDescriptorProto_Type::TYPE_INT32 => "int32",
- FieldDescriptorProto_Type::TYPE_INT64 => "int64",
- FieldDescriptorProto_Type::TYPE_UINT32 => "uint32",
- FieldDescriptorProto_Type::TYPE_UINT64 => "uint64",
- FieldDescriptorProto_Type::TYPE_SINT32 => "sint32",
- FieldDescriptorProto_Type::TYPE_SINT64 => "sint64",
- FieldDescriptorProto_Type::TYPE_BOOL => "bool",
- FieldDescriptorProto_Type::TYPE_FIXED32 => "fixed32",
- FieldDescriptorProto_Type::TYPE_FIXED64 => "fixed64",
- FieldDescriptorProto_Type::TYPE_SFIXED32 => "sfixed32",
- FieldDescriptorProto_Type::TYPE_SFIXED64 => "sfixed64",
- FieldDescriptorProto_Type::TYPE_FLOAT => "float",
- FieldDescriptorProto_Type::TYPE_DOUBLE => "double",
- FieldDescriptorProto_Type::TYPE_STRING => "string",
- FieldDescriptorProto_Type::TYPE_BYTES => "bytes",
- FieldDescriptorProto_Type::TYPE_ENUM
- | FieldDescriptorProto_Type::TYPE_MESSAGE
- | FieldDescriptorProto_Type::TYPE_GROUP => panic!(),
- }
-}
-
-fn field_type_protobuf_name<'a>(field: &'a FieldDescriptorProto) -> &'a str {
- if field.has_type_name() {
- field.get_type_name()
- } else {
- type_protobuf_name(field.get_field_type())
- }
-}
-
-// size of value for type, None if variable
-fn field_type_size(field_type: FieldDescriptorProto_Type) -> Option<u32> {
- match field_type {
- FieldDescriptorProto_Type::TYPE_BOOL => Some(1),
- t if field_type_wire_type(t) == wire_format::WireTypeFixed32 => Some(4),
- t if field_type_wire_type(t) == wire_format::WireTypeFixed64 => Some(8),
- _ => None,
- }
-}
-
-#[derive(Clone, PartialEq, Eq)]
-pub enum SingularFieldFlag {
- // proto2 or proto3 message
- WithFlag { required: bool },
- // proto3
- WithoutFlag,
-}
-
-impl SingularFieldFlag {
- pub fn is_required(&self) -> bool {
- match *self {
- SingularFieldFlag::WithFlag { required, .. } => required,
- SingularFieldFlag::WithoutFlag => false,
- }
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct SingularField<'a> {
- pub flag: SingularFieldFlag,
- pub elem: FieldElem<'a>,
-}
-
-impl<'a> SingularField<'a> {
- fn rust_storage_type(&self) -> RustType {
- match self.flag {
- SingularFieldFlag::WithFlag { .. } => match self.elem.proto_type() {
- FieldDescriptorProto_Type::TYPE_MESSAGE => {
- RustType::SingularPtrField(Box::new(self.elem.rust_storage_type()))
- }
- FieldDescriptorProto_Type::TYPE_STRING | FieldDescriptorProto_Type::TYPE_BYTES
- if self.elem.primitive_type_variant() == PrimitiveTypeVariant::Default =>
- {
- RustType::SingularField(Box::new(self.elem.rust_storage_type()))
- }
- _ => RustType::Option(Box::new(self.elem.rust_storage_type())),
- },
- SingularFieldFlag::WithoutFlag => self.elem.rust_storage_type(),
- }
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct RepeatedField<'a> {
- pub elem: FieldElem<'a>,
- pub packed: bool,
-}
-
-impl<'a> RepeatedField<'a> {
- fn rust_type(&self) -> RustType {
- if !self.elem.is_copy()
- && self.elem.primitive_type_variant() != PrimitiveTypeVariant::Carllerche
- {
- RustType::RepeatedField(Box::new(self.elem.rust_storage_type()))
- } else {
- RustType::Vec(Box::new(self.elem.rust_storage_type()))
- }
- }
-}
-
-#[derive(Clone)]
-pub struct MapField<'a> {
- _name: String,
- key: FieldElem<'a>,
- value: FieldElem<'a>,
-}
-
-#[derive(Clone)]
-pub(crate) enum FieldKind<'a> {
- // optional or required
- Singular(SingularField<'a>),
- // repeated except map
- Repeated(RepeatedField<'a>),
- // map
- Map(MapField<'a>),
- // part of oneof
- Oneof(OneofField<'a>),
-}
-
-impl<'a> FieldKind<'a> {
- fn elem(&self) -> &FieldElem {
- match self {
- &FieldKind::Singular(ref s) => &s.elem,
- &FieldKind::Repeated(ref r) => &r.elem,
- &FieldKind::Oneof(ref o) => &o.elem,
- &FieldKind::Map(..) => {
- panic!("no single elem type for map field");
- }
- }
- }
-
- fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
- self.elem().primitive_type_variant()
- }
-}
-
-// Representation of map entry: key type and value type
-#[derive(Clone, Debug)]
-pub struct EntryKeyValue<'a>(FieldElem<'a>, FieldElem<'a>);
-
-#[derive(Clone, Debug)]
-pub(crate) enum FieldElem<'a> {
- Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant),
- // name, file name, entry
- Message(
- String,
- String,
- Option<Box<EntryKeyValue<'a>>>,
- marker::PhantomData<&'a ()>,
- ),
- // name, file name, default value
- Enum(String, String, RustIdent),
- Group,
-}
-
-impl<'a> FieldElem<'a> {
- fn proto_type(&self) -> FieldDescriptorProto_Type {
- match *self {
- FieldElem::Primitive(t, ..) => t,
- FieldElem::Group => FieldDescriptorProto_Type::TYPE_GROUP,
- FieldElem::Message(..) => FieldDescriptorProto_Type::TYPE_MESSAGE,
- FieldElem::Enum(..) => FieldDescriptorProto_Type::TYPE_ENUM,
- }
- }
-
- fn is_copy(&self) -> bool {
- type_is_copy(self.proto_type())
- }
-
- pub fn rust_storage_type(&self) -> RustType {
- match *self {
- FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => rust_name(t),
- FieldElem::Primitive(
- FieldDescriptorProto_Type::TYPE_STRING,
- PrimitiveTypeVariant::Carllerche,
- ) => RustType::Chars,
- FieldElem::Primitive(
- FieldDescriptorProto_Type::TYPE_BYTES,
- PrimitiveTypeVariant::Carllerche,
- ) => RustType::Bytes,
- FieldElem::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(),
- FieldElem::Group => RustType::Group,
- FieldElem::Message(ref name, ..) => {
- RustType::Message(RustTypeMessage(RustIdentWithPath::new(name.clone())))
- }
- FieldElem::Enum(ref name, _, ref default_value) => {
- RustType::Enum(name.clone(), default_value.clone())
- }
- }
- }
-
- fn protobuf_type_gen(&self) -> ProtobufTypeGen {
- match *self {
- FieldElem::Primitive(t, v) => ProtobufTypeGen::Primitive(t, v),
- FieldElem::Message(ref name, ..) => ProtobufTypeGen::Message(name.clone()),
- FieldElem::Enum(ref name, ..) => ProtobufTypeGen::Enum(name.clone()),
- FieldElem::Group => unreachable!(),
- }
- }
-
- /// implementation of ProtobufType trait
- fn lib_protobuf_type(&self, customize: &Customize) -> String {
- self.protobuf_type_gen().rust_type(customize)
- }
-
- fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
- match self {
- &FieldElem::Primitive(_, v) => v,
- _ => PrimitiveTypeVariant::Default,
- }
- }
-}
-
-fn field_elem<'a>(
- field: &FieldWithContext,
- root_scope: &'a RootScope<'a>,
- parse_map: bool,
- customize: &Customize,
-) -> (FieldElem<'a>, Option<EnumValueGen>) {
- if field.field.get_field_type() == FieldDescriptorProto_Type::TYPE_GROUP {
- (FieldElem::Group, None)
- } else if field.field.has_type_name() {
- let message_or_enum = root_scope
- .find_message_or_enum(&ProtobufAbsolutePath::from(field.field.get_type_name()));
- let file_name = message_or_enum
- .get_scope()
- .file_scope
- .file_descriptor
- .get_name()
- .to_owned();
- let rust_relative_name = type_name_to_rust_relative(
- &ProtobufAbsolutePath::from(field.field.get_type_name()),
- field.message.get_scope().file_scope.file_descriptor,
- false,
- root_scope,
- customize,
- );
- match (field.field.get_field_type(), message_or_enum) {
- (
- FieldDescriptorProto_Type::TYPE_MESSAGE,
- MessageOrEnumWithScope::Message(message_with_scope),
- ) => {
- let entry_key_value = if let (true, Some((key, value))) =
- (parse_map, message_with_scope.map_entry())
- {
- Some(Box::new(EntryKeyValue(
- field_elem(&key, root_scope, false, customize).0,
- field_elem(&value, root_scope, false, customize).0,
- )))
- } else {
- None
- };
- (
- FieldElem::Message(
- rust_relative_name,
- file_name,
- entry_key_value,
- marker::PhantomData,
- ),
- None,
- )
- }
- (
- FieldDescriptorProto_Type::TYPE_ENUM,
- MessageOrEnumWithScope::Enum(enum_with_scope),
- ) => {
- let e = EnumGen::new(
- &enum_with_scope,
- field.message.get_scope().get_file_descriptor(),
- customize,
- root_scope,
- );
- let ev = if field.field.has_default_value() {
- e.value_by_name(field.field.get_default_value()).clone()
- } else {
- e.values_unique().into_iter().next().unwrap()
- };
- (
- FieldElem::Enum(
- rust_relative_name,
- file_name,
- RustIdent::from(enum_with_scope.values()[0].rust_name().to_owned()),
- ),
- Some(ev),
- )
- }
- _ => panic!("unknown named type: {:?}", field.field.get_field_type()),
- }
- } else if field.field.has_field_type() {
- let carllerche_for_bytes = customize.carllerche_bytes_for_bytes.unwrap_or(false);
- let carllerche_for_string = customize.carllerche_bytes_for_string.unwrap_or(false);
-
- let elem = match field.field.get_field_type() {
- FieldDescriptorProto_Type::TYPE_STRING if carllerche_for_string => {
- FieldElem::Primitive(
- FieldDescriptorProto_Type::TYPE_STRING,
- PrimitiveTypeVariant::Carllerche,
- )
- }
- FieldDescriptorProto_Type::TYPE_BYTES if carllerche_for_bytes => FieldElem::Primitive(
- FieldDescriptorProto_Type::TYPE_BYTES,
- PrimitiveTypeVariant::Carllerche,
- ),
- t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default),
- };
-
- (elem, None)
- } else {
- panic!(
- "neither type_name, nor field_type specified for field: {}",
- field.field.get_name()
- );
- }
-}
-
-pub enum AccessorStyle {
- Lambda,
- HasGet,
-}
-
-pub struct AccessorFn {
- name: String,
- type_params: Vec<String>,
- pub style: AccessorStyle,
-}
-
-impl AccessorFn {
- pub fn sig(&self) -> String {
- let mut s = self.name.clone();
- s.push_str("::<_");
- for p in &self.type_params {
- s.push_str(", ");
- s.push_str(&p);
- }
- s.push_str(">");
- s
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct FieldGen<'a> {
- _root_scope: &'a RootScope<'a>,
- syntax: Syntax,
- pub proto_field: FieldWithContext<'a>,
- // field name in generated code
- pub rust_name: RustIdent,
- pub proto_type: FieldDescriptorProto_Type,
- wire_type: wire_format::WireType,
- enum_default_value: Option<EnumValueGen>,
- pub kind: FieldKind<'a>,
- pub expose_field: bool,
- pub generate_accessors: bool,
- pub(crate) customize: Customize,
-}
-
-impl<'a> FieldGen<'a> {
- pub fn parse(
- field: FieldWithContext<'a>,
- root_scope: &'a RootScope<'a>,
- customize: &Customize,
- ) -> FieldGen<'a> {
- let mut customize = customize.clone();
- customize.update_with(&customize_from_rustproto_for_field(
- &field.field.get_options(),
- ));
-
- let (elem, enum_default_value) = field_elem(&field, root_scope, true, &customize);
-
- let generate_accessors = customize.generate_accessors.unwrap_or(true);
-
- let syntax = field.message.scope.file_scope.syntax();
-
- let field_may_have_custom_default_value = syntax == Syntax::PROTO2
- && field.field.get_label() != FieldDescriptorProto_Label::LABEL_REPEATED
- && field.field.get_field_type() != FieldDescriptorProto_Type::TYPE_MESSAGE;
-
- let default_expose_field = !field_may_have_custom_default_value;
-
- let expose_field = customize.expose_fields.unwrap_or(default_expose_field);
-
- let kind = if field.field.get_label() == FieldDescriptorProto_Label::LABEL_REPEATED {
- match (elem, true) {
- // map field
- (FieldElem::Message(name, _, Some(key_value), _), true) => {
- FieldKind::Map(MapField {
- _name: name,
- key: key_value.0.clone(),
- value: key_value.1.clone(),
- })
- }
- // regular repeated field
- (elem, _) => FieldKind::Repeated(RepeatedField {
- elem,
- packed: field.field.get_options().get_packed(),
- }),
- }
- } else if let Some(oneof) = field.oneof() {
- FieldKind::Oneof(OneofField::parse(&oneof, &field, elem, root_scope))
- } else {
- let flag = if field.message.scope.file_scope.syntax() == Syntax::PROTO3
- && field.field.get_field_type() != FieldDescriptorProto_Type::TYPE_MESSAGE
- {
- SingularFieldFlag::WithoutFlag
- } else {
- SingularFieldFlag::WithFlag {
- required: field.field.get_label() == FieldDescriptorProto_Label::LABEL_REQUIRED,
- }
- };
- FieldKind::Singular(SingularField { elem, flag })
- };
-
- FieldGen {
- _root_scope: root_scope,
- syntax: field.message.get_scope().file_scope.syntax(),
- rust_name: field.rust_name(),
- proto_type: field.field.get_field_type(),
- wire_type: field_type_wire_type(field.field.get_field_type()),
- enum_default_value,
- proto_field: field.clone(),
- kind,
- expose_field,
- generate_accessors,
- customize,
- }
- }
-
- fn tag_size(&self) -> u32 {
- rt::tag_size(self.proto_field.number())
- }
-
- pub fn is_oneof(&self) -> bool {
- match self.kind {
- FieldKind::Oneof(..) => true,
- _ => false,
- }
- }
-
- pub fn oneof(&self) -> &OneofField {
- match self.kind {
- FieldKind::Oneof(ref oneof) => &oneof,
- _ => panic!("not a oneof field: {}", self.reconstruct_def()),
- }
- }
-
- fn is_singular(&self) -> bool {
- match self.kind {
- FieldKind::Singular(..) => true,
- _ => false,
- }
- }
-
- fn is_repeated_not_map(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(..) => true,
- _ => false,
- }
- }
-
- fn is_repeated_or_map(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => true,
- _ => false,
- }
- }
-
- fn is_repeated_packed(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(RepeatedField { packed: true, .. }) => true,
- _ => false,
- }
- }
-
- #[allow(dead_code)]
- fn repeated(&self) -> &RepeatedField {
- match self.kind {
- FieldKind::Repeated(ref repeated) => &repeated,
- _ => panic!("not a repeated field: {}", self.reconstruct_def()),
- }
- }
-
- fn singular(&self) -> &SingularField {
- match self.kind {
- FieldKind::Singular(ref singular) => &singular,
- _ => panic!("not a singular field: {}", self.reconstruct_def()),
- }
- }
-
- fn map(&self) -> &MapField {
- match self.kind {
- FieldKind::Map(ref map) => &map,
- _ => panic!("not a map field: {}", self.reconstruct_def()),
- }
- }
-
- fn variant_path(&self) -> String {
- // TODO: should reuse code from OneofVariantGen
- format!(
- "{}::{}",
- self.oneof().oneof_type_name.to_code(&self.customize),
- self.rust_name
- )
- }
-
- // TODO: drop it
- pub fn elem(&self) -> &FieldElem {
- match self.kind {
- FieldKind::Singular(SingularField { ref elem, .. }) => &elem,
- FieldKind::Repeated(RepeatedField { ref elem, .. }) => &elem,
- FieldKind::Oneof(OneofField { ref elem, .. }) => &elem,
- FieldKind::Map(..) => unreachable!(),
- }
- }
-
- // type of field in struct
- pub fn full_storage_type(&self) -> RustType {
- match self.kind {
- FieldKind::Repeated(ref repeated) => repeated.rust_type(),
- FieldKind::Map(MapField {
- ref key, ref value, ..
- }) => RustType::HashMap(
- Box::new(key.rust_storage_type()),
- Box::new(value.rust_storage_type()),
- ),
- FieldKind::Singular(ref singular) => singular.rust_storage_type(),
- FieldKind::Oneof(..) => unreachable!(),
- }
- }
-
- // type of `v` in `for v in field`
- fn full_storage_iter_elem_type(&self) -> RustType {
- if let FieldKind::Oneof(ref oneof) = self.kind {
- oneof.elem.rust_storage_type()
- } else {
- self.full_storage_type().iter_elem_type()
- }
- }
-
- // suffix `xxx` as in `os.write_xxx_no_tag(..)`
- fn os_write_fn_suffix(&self) -> &str {
- protobuf_name(self.proto_type)
- }
-
- // type of `v` in `os.write_xxx_no_tag(v)`
- fn os_write_fn_param_type(&self) -> RustType {
- match self.proto_type {
- FieldDescriptorProto_Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
- FieldDescriptorProto_Type::TYPE_BYTES => {
- RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::Int(false, 8)))))
- }
- FieldDescriptorProto_Type::TYPE_ENUM => RustType::Int(true, 32),
- t => rust_name(t),
- }
- }
-
- // for field `foo`, type of param of `fn set_foo(..)`
- fn set_xxx_param_type(&self) -> RustType {
- match self.kind {
- FieldKind::Singular(SingularField { ref elem, .. })
- | FieldKind::Oneof(OneofField { ref elem, .. }) => elem.rust_storage_type(),
- FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(),
- }
- }
-
- // for field `foo`, return type if `fn take_foo(..)`
- fn take_xxx_return_type(&self) -> RustType {
- self.set_xxx_param_type()
- }
-
- // for field `foo`, return type of `fn mut_foo(..)`
- fn mut_xxx_return_type(&self) -> RustType {
- RustType::Ref(Box::new(match self.kind {
- FieldKind::Singular(SingularField { ref elem, .. })
- | FieldKind::Oneof(OneofField { ref elem, .. }) => elem.rust_storage_type(),
- FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(),
- }))
- }
-
- // for field `foo`, return type of `fn get_foo(..)`
- fn get_xxx_return_type(&self) -> RustType {
- match self.kind {
- FieldKind::Singular(SingularField { ref elem, .. })
- | FieldKind::Oneof(OneofField { ref elem, .. }) => match elem.is_copy() {
- true => elem.rust_storage_type(),
- false => elem.rust_storage_type().ref_type(),
- },
- FieldKind::Repeated(RepeatedField { ref elem, .. }) => RustType::Ref(Box::new(
- RustType::Slice(Box::new(elem.rust_storage_type())),
- )),
- FieldKind::Map(..) => RustType::Ref(Box::new(self.full_storage_type())),
- }
- }
-
- // fixed size type?
- fn is_fixed(&self) -> bool {
- field_type_size(self.proto_type).is_some()
- }
-
- // must use zigzag encoding?
- fn is_zigzag(&self) -> bool {
- match self.proto_type {
- FieldDescriptorProto_Type::TYPE_SINT32 | FieldDescriptorProto_Type::TYPE_SINT64 => true,
- _ => false,
- }
- }
-
- // data is enum
- fn is_enum(&self) -> bool {
- match self.proto_type {
- FieldDescriptorProto_Type::TYPE_ENUM => true,
- _ => false,
- }
- }
-
- // elem data is not stored in heap
- pub fn elem_type_is_copy(&self) -> bool {
- type_is_copy(self.proto_type)
- }
-
- fn defaut_value_from_proto_float(&self) -> String {
- assert!(self.proto_field.field.has_default_value());
-
- let type_name = match self.proto_type {
- FieldDescriptorProto_Type::TYPE_FLOAT => "f32",
- FieldDescriptorProto_Type::TYPE_DOUBLE => "f64",
- _ => unreachable!(),
- };
- let proto_default = self.proto_field.field.get_default_value();
-
- let f = float::parse_protobuf_float(proto_default)
- .expect(&format!("failed to parse float: {:?}", proto_default));
-
- if f.is_nan() {
- format!("::std::{}::NAN", type_name)
- } else if f.is_infinite() {
- if f > 0.0 {
- format!("::std::{}::INFINITY", type_name)
- } else {
- format!("::std::{}::NEG_INFINITY", type_name)
- }
- } else {
- format!("{:?}{}", f, type_name)
- }
- }
-
- fn default_value_from_proto(&self) -> Option<String> {
- assert!(self.is_singular() || self.is_oneof());
- if self.enum_default_value.is_some() {
- Some(self.enum_default_value.as_ref().unwrap().rust_name_outer())
- } else if self.proto_field.field.has_default_value() {
- let proto_default = self.proto_field.field.get_default_value();
- Some(match self.proto_type {
- // For numeric types, contains the original text representation of the value
- FieldDescriptorProto_Type::TYPE_DOUBLE | FieldDescriptorProto_Type::TYPE_FLOAT => {
- self.defaut_value_from_proto_float()
- }
- FieldDescriptorProto_Type::TYPE_INT32
- | FieldDescriptorProto_Type::TYPE_SINT32
- | FieldDescriptorProto_Type::TYPE_SFIXED32 => format!("{}i32", proto_default),
- FieldDescriptorProto_Type::TYPE_UINT32
- | FieldDescriptorProto_Type::TYPE_FIXED32 => format!("{}u32", proto_default),
- FieldDescriptorProto_Type::TYPE_INT64
- | FieldDescriptorProto_Type::TYPE_SINT64
- | FieldDescriptorProto_Type::TYPE_SFIXED64 => format!("{}i64", proto_default),
- FieldDescriptorProto_Type::TYPE_UINT64
- | FieldDescriptorProto_Type::TYPE_FIXED64 => format!("{}u64", proto_default),
-
- // For booleans, "true" or "false"
- FieldDescriptorProto_Type::TYPE_BOOL => format!("{}", proto_default),
- // For strings, contains the default text contents (not escaped in any way)
- FieldDescriptorProto_Type::TYPE_STRING => rust::quote_escape_str(proto_default),
- // For bytes, contains the C escaped value. All bytes >= 128 are escaped
- FieldDescriptorProto_Type::TYPE_BYTES => {
- rust::quote_escape_bytes(&text_format::unescape_string(proto_default))
- }
- // TODO: resolve outer message prefix
- FieldDescriptorProto_Type::TYPE_GROUP | FieldDescriptorProto_Type::TYPE_ENUM => {
- unreachable!()
- }
- FieldDescriptorProto_Type::TYPE_MESSAGE => panic!(
- "default value is not implemented for type: {:?}",
- self.proto_type
- ),
- })
- } else {
- None
- }
- }
-
- fn default_value_from_proto_typed(&self) -> Option<RustValueTyped> {
- self.default_value_from_proto().map(|v| {
- let default_value_type = match self.proto_type {
- FieldDescriptorProto_Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
- FieldDescriptorProto_Type::TYPE_BYTES => {
- RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::u8()))))
- }
- _ => self.full_storage_iter_elem_type(),
- };
-
- RustValueTyped {
- value: v,
- rust_type: default_value_type,
- }
- })
- }
-
- // default value to be returned from fn get_xxx
- fn get_xxx_default_value_rust(&self) -> String {
- assert!(self.is_singular() || self.is_oneof());
- self.default_value_from_proto()
- .unwrap_or_else(|| self.get_xxx_return_type().default_value(&self.customize))
- }
-
- // default to be assigned to field
- fn element_default_value_rust(&self) -> RustValueTyped {
- assert!(
- self.is_singular() || self.is_oneof(),
- "field is not singular: {}",
- self.reconstruct_def()
- );
- self.default_value_from_proto_typed().unwrap_or_else(|| {
- self.elem()
- .rust_storage_type()
- .default_value_typed(&self.customize)
- })
- }
-
- pub fn reconstruct_def(&self) -> String {
- let prefix = match (self.proto_field.field.get_label(), self.syntax) {
- (FieldDescriptorProto_Label::LABEL_REPEATED, _) => "repeated ",
- (_, Syntax::PROTO3) => "",
- (FieldDescriptorProto_Label::LABEL_OPTIONAL, _) => "optional ",
- (FieldDescriptorProto_Label::LABEL_REQUIRED, _) => "required ",
- };
- format!(
- "{}{} {} = {}",
- prefix,
- field_type_protobuf_name(&self.proto_field.field),
- self.proto_field.name(),
- self.proto_field.number()
- )
- }
-
- pub fn accessor_fn(&self) -> AccessorFn {
- match self.kind {
- FieldKind::Repeated(RepeatedField { ref elem, .. }) => {
- let coll = match self.full_storage_type() {
- RustType::Vec(..) => "vec",
- RustType::RepeatedField(..) => "repeated_field",
- _ => unreachable!(),
- };
- let name = format!("make_{}_accessor", coll);
- AccessorFn {
- name: name,
- type_params: vec![elem.lib_protobuf_type(&self.customize)],
- style: AccessorStyle::Lambda,
- }
- }
- FieldKind::Map(MapField {
- ref key, ref value, ..
- }) => AccessorFn {
- name: "make_map_accessor".to_owned(),
- type_params: vec![
- key.lib_protobuf_type(&self.customize),
- value.lib_protobuf_type(&self.customize),
- ],
- style: AccessorStyle::Lambda,
- },
- FieldKind::Singular(SingularField {
- ref elem,
- flag: SingularFieldFlag::WithoutFlag,
- }) => {
- if let &FieldElem::Message(ref name, ..) = elem {
- // TODO: old style, needed because of default instance
-
- AccessorFn {
- name: "make_singular_message_accessor".to_owned(),
- type_params: vec![name.clone()],
- style: AccessorStyle::HasGet,
- }
- } else {
- AccessorFn {
- name: "make_simple_field_accessor".to_owned(),
- type_params: vec![elem.lib_protobuf_type(&self.customize)],
- style: AccessorStyle::Lambda,
- }
- }
- }
- FieldKind::Singular(SingularField {
- ref elem,
- flag: SingularFieldFlag::WithFlag { .. },
- }) => {
- let coll = match self.full_storage_type() {
- RustType::Option(..) => "option",
- RustType::SingularField(..) => "singular_field",
- RustType::SingularPtrField(..) => "singular_ptr_field",
- _ => unreachable!(),
- };
- let name = format!("make_{}_accessor", coll);
- AccessorFn {
- name: name,
- type_params: vec![elem.lib_protobuf_type(&self.customize)],
- style: AccessorStyle::Lambda,
- }
- }
- FieldKind::Oneof(OneofField { ref elem, .. }) => {
- // TODO: uses old style
-
- let suffix = match &self.elem().rust_storage_type() {
- t if t.is_primitive() => t.to_code(&self.customize),
- &RustType::String | &RustType::Chars => "string".to_string(),
- &RustType::Vec(ref t) if t.is_u8() => "bytes".to_string(),
- &RustType::Bytes => "bytes".to_string(),
- &RustType::Enum(..) => "enum".to_string(),
- &RustType::Message(..) => "message".to_string(),
- t => panic!("unexpected field type: {:?}", t),
- };
-
- let name = format!("make_singular_{}_accessor", suffix);
-
- let mut type_params = Vec::new();
- match elem {
- &FieldElem::Message(ref name, ..) | &FieldElem::Enum(ref name, ..) => {
- type_params.push(name.to_owned());
- }
- _ => (),
- }
-
- AccessorFn {
- name: name,
- type_params: type_params,
- style: AccessorStyle::HasGet,
- }
- }
- }
- }
-
- pub fn write_clear(&self, w: &mut CodeWriter) {
- if self.is_oneof() {
- w.write_line(&format!(
- "self.{} = ::std::option::Option::None;",
- self.oneof().oneof_rust_field_name
- ));
- } else {
- let clear_expr = self
- .full_storage_type()
- .clear(&self.self_field(), &self.customize);
- w.write_line(&format!("{};", clear_expr));
- }
- }
-
- // expression that returns size of data is variable
- fn element_size(&self, var: &str, var_type: &RustType) -> String {
- assert!(!self.is_repeated_packed());
-
- match field_type_size(self.proto_type) {
- Some(data_size) => format!("{}", data_size + self.tag_size()),
- None => match self.proto_type {
- FieldDescriptorProto_Type::TYPE_MESSAGE => panic!("not a single-liner"),
- FieldDescriptorProto_Type::TYPE_BYTES => format!(
- "{}::rt::bytes_size({}, &{})",
- protobuf_crate_path(&self.customize),
- self.proto_field.number(),
- var
- ),
- FieldDescriptorProto_Type::TYPE_STRING => format!(
- "{}::rt::string_size({}, &{})",
- protobuf_crate_path(&self.customize),
- self.proto_field.number(),
- var
- ),
- FieldDescriptorProto_Type::TYPE_ENUM => {
- let param_type = match var_type {
- &RustType::Ref(ref t) => (**t).clone(),
- t => t.clone(),
- };
- format!(
- "{}::rt::enum_size({}, {})",
- protobuf_crate_path(&self.customize),
- self.proto_field.number(),
- var_type.into_target(&param_type, var, &self.customize)
- )
- }
- _ => {
- let param_type = match var_type {
- &RustType::Ref(ref t) => (**t).clone(),
- t => t.clone(),
- };
- if self.proto_type.is_s_varint() {
- format!(
- "{}::rt::value_varint_zigzag_size({}, {})",
- protobuf_crate_path(&self.customize),
- self.proto_field.number(),
- var_type.into_target(&param_type, var, &self.customize)
- )
- } else {
- format!(
- "{}::rt::value_size({}, {}, {}::wire_format::{:?})",
- protobuf_crate_path(&self.customize),
- self.proto_field.number(),
- var_type.into_target(&param_type, var, &self.customize),
- protobuf_crate_path(&self.customize),
- self.wire_type,
- )
- }
- }
- },
- }
- }
-
- // output code that writes single element to stream
- pub fn write_write_element(&self, w: &mut CodeWriter, os: &str, var: &str, ty: &RustType) {
- if let FieldKind::Repeated(RepeatedField { packed: true, .. }) = self.kind {
- unreachable!();
- };
-
- match self.proto_type {
- FieldDescriptorProto_Type::TYPE_MESSAGE => {
- w.write_line(&format!(
- "{}.write_tag({}, {}::wire_format::{:?})?;",
- os,
- self.proto_field.number(),
- protobuf_crate_path(&self.customize),
- wire_format::WireTypeLengthDelimited
- ));
- w.write_line(&format!(
- "{}.write_raw_varint32({}.get_cached_size())?;",
- os, var
- ));
- w.write_line(&format!("{}.write_to_with_cached_sizes({})?;", var, os));
- }
- _ => {
- let param_type = self.os_write_fn_param_type();
- let os_write_fn_suffix = self.os_write_fn_suffix();
- let number = self.proto_field.number();
- w.write_line(&format!(
- "{}.write_{}({}, {})?;",
- os,
- os_write_fn_suffix,
- number,
- ty.into_target(&param_type, var, &self.customize)
- ));
- }
- }
- }
-
- fn self_field(&self) -> String {
- format!("self.{}", self.rust_name)
- }
-
- fn self_field_is_some(&self) -> String {
- assert!(self.is_singular());
- format!("{}.is_some()", self.self_field())
- }
-
- fn self_field_is_not_empty(&self) -> String {
- assert!(self.is_repeated_or_map());
- format!("!{}.is_empty()", self.self_field())
- }
-
- fn self_field_is_none(&self) -> String {
- assert!(self.is_singular());
- format!("{}.is_none()", self.self_field())
- }
-
- // type of expression returned by `as_option()`
- fn as_option_type(&self) -> RustType {
- assert!(self.is_singular());
- match self.full_storage_type() {
- RustType::Option(ref e) if e.is_copy() => RustType::Option(e.clone()),
- RustType::Option(e) => RustType::Option(Box::new(e.ref_type())),
- RustType::SingularField(ty) | RustType::SingularPtrField(ty) => {
- RustType::Option(Box::new(RustType::Ref(ty)))
- }
- x => panic!("cannot convert {:?} to option", x),
- }
- }
-
- // field data viewed as Option
- fn self_field_as_option(&self) -> RustValueTyped {
- assert!(self.is_singular());
-
- let suffix = match self.full_storage_type() {
- RustType::Option(ref e) if e.is_copy() => "",
- _ => ".as_ref()",
- };
-
- self.as_option_type()
- .value(format!("{}{}", self.self_field(), suffix))
- }
-
- fn write_if_let_self_field_is_some<F>(&self, w: &mut CodeWriter, cb: F)
- where
- F: Fn(&str, &RustType, &mut CodeWriter),
- {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => panic!("field is not singular"),
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ref elem,
- }) => {
- let var = "v";
- let ref_prefix = match elem.rust_storage_type().is_copy() {
- true => "",
- false => "ref ",
- };
- let as_option = self.self_field_as_option();
- w.if_let_stmt(
- &format!("Some({}{})", ref_prefix, var),
- &as_option.value,
- |w| {
- let v_type = as_option.rust_type.elem_type();
- cb(var, &v_type, w);
- },
- );
- }
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ref elem,
- }) => match *elem {
- FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
- | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
- w.if_stmt(format!("!{}.is_empty()", self.self_field()), |w| {
- cb(&self.self_field(), &self.full_storage_type(), w);
- });
- }
- _ => {
- w.if_stmt(
- format!(
- "{} != {}",
- self.self_field(),
- self.full_storage_type().default_value(&self.customize)
- ),
- |w| {
- cb(&self.self_field(), &self.full_storage_type(), w);
- },
- );
- }
- },
- FieldKind::Oneof(..) => unreachable!(),
- }
- }
-
- fn write_if_self_field_is_not_empty<F>(&self, w: &mut CodeWriter, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- assert!(self.is_repeated_or_map());
- let self_field_is_not_empty = self.self_field_is_not_empty();
- w.if_stmt(self_field_is_not_empty, cb);
- }
-
- pub fn write_if_self_field_is_none<F>(&self, w: &mut CodeWriter, cb: F)
- where
- F: Fn(&mut CodeWriter),
- {
- let self_field_is_none = self.self_field_is_none();
- w.if_stmt(self_field_is_none, cb)
- }
-
- // repeated or singular
- pub fn write_for_self_field<F>(&self, w: &mut CodeWriter, varn: &str, cb: F)
- where
- F: Fn(&mut CodeWriter, &RustType),
- {
- match self.kind {
- FieldKind::Oneof(OneofField {
- ref elem,
- ref oneof_type_name,
- ..
- }) => {
- let cond = format!(
- "Some({}::{}(ref {}))",
- oneof_type_name.to_code(&self.customize),
- self.rust_name,
- varn
- );
- w.if_let_stmt(&cond, &self.self_field_oneof(), |w| {
- cb(w, &elem.rust_storage_type())
- })
- }
- _ => {
- let v_type = self.full_storage_iter_elem_type();
- let self_field = self.self_field();
- w.for_stmt(&format!("&{}", self_field), varn, |w| cb(w, &v_type));
- }
- }
- }
-
- fn write_self_field_assign(&self, w: &mut CodeWriter, value: &str) {
- let self_field = self.self_field();
- w.write_line(&format!("{} = {};", self_field, value));
- }
-
- fn write_self_field_assign_some(&self, w: &mut CodeWriter, value: &str) {
- let full_storage_type = self.full_storage_type();
- match self.singular() {
- &SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ..
- } => {
- self.write_self_field_assign(
- w,
- &full_storage_type.wrap_value(value, &self.customize),
- );
- }
- &SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- } => {
- self.write_self_field_assign(w, value);
- }
- }
- }
-
- fn write_self_field_assign_value(&self, w: &mut CodeWriter, value: &str, ty: &RustType) {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => {
- let converted = ty.into_target(&self.full_storage_type(), value, &self.customize);
- self.write_self_field_assign(w, &converted);
- }
- FieldKind::Singular(SingularField { ref elem, ref flag }) => {
- let converted = ty.into_target(&elem.rust_storage_type(), value, &self.customize);
- let wrapped = if *flag == SingularFieldFlag::WithoutFlag {
- converted
- } else {
- self.full_storage_type()
- .wrap_value(&converted, &self.customize)
- };
- self.write_self_field_assign(w, &wrapped);
- }
- FieldKind::Oneof(..) => unreachable!(),
- }
- }
-
- fn write_self_field_assign_default(&self, w: &mut CodeWriter) {
- assert!(self.is_singular());
- if self.is_oneof() {
- let self_field_oneof = self.self_field_oneof();
- w.write_line(format!(
- "{} = ::std::option::Option::Some({}({}))",
- self_field_oneof,
- self.variant_path(),
- // TODO: default from .proto is not needed here (?)
- self.element_default_value_rust()
- .into_type(self.full_storage_iter_elem_type(), &self.customize)
- .value
- ));
- } else {
- // Note it is different from C++ protobuf, where field is initialized
- // with default value
- match self.full_storage_type() {
- RustType::SingularField(..) | RustType::SingularPtrField(..) => {
- let self_field = self.self_field();
- w.write_line(&format!("{}.set_default();", self_field));
- }
- _ => {
- self.write_self_field_assign_some(
- w,
- &self
- .elem()
- .rust_storage_type()
- .default_value_typed(&self.customize)
- .into_type(self.elem().rust_storage_type(), &self.customize)
- .value,
- );
- }
- }
- }
- }
-
- fn self_field_vec_packed_fixed_data_size(&self) -> String {
- assert!(self.is_fixed());
- format!(
- "({}.len() * {}) as u32",
- self.self_field(),
- field_type_size(self.proto_type).unwrap()
- )
- }
-
- fn self_field_vec_packed_varint_data_size(&self) -> String {
- assert!(!self.is_fixed());
- let fn_name = if self.is_enum() {
- "vec_packed_enum_data_size".to_string()
- } else {
- let zigzag_suffix = if self.is_zigzag() { "_zigzag" } else { "" };
- format!("vec_packed_varint{}_data_size", zigzag_suffix)
- };
- format!(
- "{}::rt::{}(&{})",
- protobuf_crate_path(&self.customize),
- fn_name,
- self.self_field()
- )
- }
-
- fn self_field_vec_packed_data_size(&self) -> String {
- assert!(self.is_repeated_not_map());
- if self.is_fixed() {
- self.self_field_vec_packed_fixed_data_size()
- } else {
- self.self_field_vec_packed_varint_data_size()
- }
- }
-
- fn self_field_vec_packed_fixed_size(&self) -> String {
- // zero is filtered outside
- format!(
- "{} + {}::rt::compute_raw_varint32_size({}) + {}",
- self.tag_size(),
- protobuf_crate_path(&self.customize),
- self.self_field_vec_packed_fixed_data_size(),
- self.self_field_vec_packed_fixed_data_size()
- )
- }
-
- fn self_field_vec_packed_varint_size(&self) -> String {
- // zero is filtered outside
- assert!(!self.is_fixed());
- let fn_name = if self.is_enum() {
- "vec_packed_enum_size".to_string()
- } else {
- let zigzag_suffix = if self.is_zigzag() { "_zigzag" } else { "" };
- format!("vec_packed_varint{}_size", zigzag_suffix)
- };
- format!(
- "{}::rt::{}({}, &{})",
- protobuf_crate_path(&self.customize),
- fn_name,
- self.proto_field.number(),
- self.self_field()
- )
- }
-
- fn self_field_oneof(&self) -> String {
- format!("self.{}", self.oneof().oneof_rust_field_name)
- }
-
- pub fn clear_field_func(&self) -> String {
- format!("clear_{}", self.rust_name)
- }
-
- // Write `merge_from` part for this singular or repeated field
- // of type message, string or bytes
- fn write_merge_from_field_message_string_bytes(&self, w: &mut CodeWriter) {
- let singular_or_repeated = match self.kind {
- FieldKind::Repeated(..) => "repeated",
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ..
- }) => "singular",
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- }) => "singular_proto3",
- FieldKind::Map(..) | FieldKind::Oneof(..) => unreachable!(),
- };
- let carllerche = match self.kind.primitive_type_variant() {
- PrimitiveTypeVariant::Carllerche => "carllerche_",
- PrimitiveTypeVariant::Default => "",
- };
- let type_name_for_fn = protobuf_name(self.proto_type);
- w.write_line(&format!(
- "{}::rt::read_{}_{}{}_into(wire_type, is, &mut self.{})?;",
- protobuf_crate_path(&self.customize),
- singular_or_repeated,
- carllerche,
- type_name_for_fn,
- self.rust_name
- ));
- }
-
- fn write_error_unexpected_wire_type(&self, wire_type_var: &str, w: &mut CodeWriter) {
- w.write_line(&format!(
- "return ::std::result::Result::Err({}::rt::unexpected_wire_type({}));",
- protobuf_crate_path(&self.customize),
- wire_type_var
- ));
- }
-
- fn write_assert_wire_type(&self, wire_type_var: &str, w: &mut CodeWriter) {
- w.if_stmt(
- &format!(
- "{} != {}::wire_format::{:?}",
- wire_type_var,
- protobuf_crate_path(&self.customize),
- self.wire_type
- ),
- |w| {
- self.write_error_unexpected_wire_type(wire_type_var, w);
- },
- );
- }
-
- // Write `merge_from` part for this oneof field
- fn write_merge_from_oneof(&self, f: &OneofField, wire_type_var: &str, w: &mut CodeWriter) {
- self.write_assert_wire_type(wire_type_var, w);
-
- let typed = RustValueTyped {
- value: format!(
- "{}?",
- self.proto_type.read("is", f.elem.primitive_type_variant())
- ),
- rust_type: self.full_storage_iter_elem_type(),
- };
-
- let maybe_boxed = if f.boxed {
- typed.boxed(&self.customize)
- } else {
- typed
- };
-
- w.write_line(&format!(
- "self.{} = ::std::option::Option::Some({}({}));",
- self.oneof().oneof_rust_field_name,
- self.variant_path(),
- maybe_boxed.value
- )); // TODO: into_type
- }
-
- // Write `merge_from` part for this map field
- fn write_merge_from_map(&self, w: &mut CodeWriter) {
- let &MapField {
- ref key, ref value, ..
- } = self.map();
- w.write_line(&format!(
- "{}::rt::read_map_into::<{}, {}>(wire_type, is, &mut {})?;",
- protobuf_crate_path(&self.customize),
- key.lib_protobuf_type(&self.customize),
- value.lib_protobuf_type(&self.customize),
- self.self_field()
- ));
- }
-
- // Write `merge_from` part for this singular field
- fn write_merge_from_singular(&self, wire_type_var: &str, w: &mut CodeWriter) {
- let field = match self.kind {
- FieldKind::Singular(ref field) => field,
- _ => panic!(),
- };
-
- match field.elem {
- FieldElem::Message(..)
- | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
- | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
- self.write_merge_from_field_message_string_bytes(w);
- }
- FieldElem::Enum(..) => {
- let version = match field.flag {
- SingularFieldFlag::WithFlag { .. } => "proto2",
- SingularFieldFlag::WithoutFlag => "proto3",
- };
- w.write_line(&format!(
- "{}::rt::read_{}_enum_with_unknown_fields_into({}, is, &mut self.{}, {}, &mut self.unknown_fields)?",
- protobuf_crate_path(&self.customize),
- version,
- wire_type_var,
- self.rust_name,
- self.proto_field.number()
- ));
- }
- _ => {
- let read_proc = format!(
- "{}?",
- self.proto_type.read("is", PrimitiveTypeVariant::Default)
- );
-
- self.write_assert_wire_type(wire_type_var, w);
- w.write_line(&format!("let tmp = {};", read_proc));
- self.write_self_field_assign_some(w, "tmp");
- }
- }
- }
-
- // Write `merge_from` part for this repeated field
- fn write_merge_from_repeated(&self, wire_type_var: &str, w: &mut CodeWriter) {
- let field = match self.kind {
- FieldKind::Repeated(ref field) => field,
- _ => panic!(),
- };
-
- match field.elem {
- FieldElem::Message(..)
- | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_STRING, ..)
- | FieldElem::Primitive(FieldDescriptorProto_Type::TYPE_BYTES, ..) => {
- self.write_merge_from_field_message_string_bytes(w);
- }
- FieldElem::Enum(..) => {
- w.write_line(&format!(
- "{}::rt::read_repeated_enum_with_unknown_fields_into({}, is, &mut self.{}, {}, &mut self.unknown_fields)?",
- protobuf_crate_path(&self.customize),
- wire_type_var,
- self.rust_name,
- self.proto_field.number()
- ));
- }
- _ => {
- w.write_line(&format!(
- "{}::rt::read_repeated_{}_into({}, is, &mut self.{})?;",
- protobuf_crate_path(&self.customize),
- protobuf_name(self.proto_type),
- wire_type_var,
- self.rust_name
- ));
- }
- }
- }
-
- // Write `merge_from` part for this field
- pub fn write_merge_from_field(&self, wire_type_var: &str, w: &mut CodeWriter) {
- match self.kind {
- FieldKind::Oneof(ref f) => self.write_merge_from_oneof(&f, wire_type_var, w),
- FieldKind::Map(..) => self.write_merge_from_map(w),
- FieldKind::Singular(..) => self.write_merge_from_singular(wire_type_var, w),
- FieldKind::Repeated(..) => self.write_merge_from_repeated(wire_type_var, w),
- }
- }
-
- fn self_field_vec_packed_size(&self) -> String {
- match self.kind {
- FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
- // zero is filtered outside
- if self.is_fixed() {
- self.self_field_vec_packed_fixed_size()
- } else {
- self.self_field_vec_packed_varint_size()
- }
- }
- _ => {
- panic!("not packed");
- }
- }
- }
-
- pub fn write_element_size(
- &self,
- w: &mut CodeWriter,
- item_var: &str,
- item_var_type: &RustType,
- sum_var: &str,
- ) {
- assert!(!self.is_repeated_packed());
-
- match self.proto_type {
- FieldDescriptorProto_Type::TYPE_MESSAGE => {
- w.write_line(&format!("let len = {}.compute_size();", item_var));
- let tag_size = self.tag_size();
- w.write_line(&format!(
- "{} += {} + {}::rt::compute_raw_varint32_size(len) + len;",
- sum_var,
- tag_size,
- protobuf_crate_path(&self.customize)
- ));
- }
- _ => {
- w.write_line(&format!(
- "{} += {};",
- sum_var,
- self.element_size(item_var, item_var_type)
- ));
- }
- }
- }
-
- pub fn write_message_write_field(&self, w: &mut CodeWriter) {
- match self.kind {
- FieldKind::Singular(..) => {
- self.write_if_let_self_field_is_some(w, |v, v_type, w| {
- self.write_write_element(w, "os", v, v_type);
- });
- }
- FieldKind::Repeated(RepeatedField { packed: false, .. }) => {
- self.write_for_self_field(w, "v", |w, v_type| {
- self.write_write_element(w, "os", "v", v_type);
- });
- }
- FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
- self.write_if_self_field_is_not_empty(w, |w| {
- let number = self.proto_field.number();
- w.write_line(&format!(
- "os.write_tag({}, {}::wire_format::{:?})?;",
- number,
- protobuf_crate_path(&self.customize),
- wire_format::WireTypeLengthDelimited
- ));
- w.comment("TODO: Data size is computed again, it should be cached");
- let data_size_expr = self.self_field_vec_packed_data_size();
- w.write_line(&format!("os.write_raw_varint32({})?;", data_size_expr));
- self.write_for_self_field(w, "v", |w, v_type| {
- let param_type = self.os_write_fn_param_type();
- let os_write_fn_suffix = self.os_write_fn_suffix();
- w.write_line(&format!(
- "os.write_{}_no_tag({})?;",
- os_write_fn_suffix,
- v_type.into_target(&param_type, "v", &self.customize)
- ));
- });
- });
- }
- FieldKind::Map(MapField {
- ref key, ref value, ..
- }) => {
- w.write_line(&format!(
- "{}::rt::write_map_with_cached_sizes::<{}, {}>({}, &{}, os)?;",
- protobuf_crate_path(&self.customize),
- key.lib_protobuf_type(&self.customize),
- value.lib_protobuf_type(&self.customize),
- self.proto_field.number(),
- self.self_field()
- ));
- }
- FieldKind::Oneof(..) => unreachable!(),
- };
- }
-
- pub fn write_message_compute_field_size(&self, sum_var: &str, w: &mut CodeWriter) {
- match self.kind {
- FieldKind::Singular(..) => {
- self.write_if_let_self_field_is_some(w, |v, v_type, w| {
- match field_type_size(self.proto_type) {
- Some(s) => {
- let tag_size = self.tag_size();
- w.write_line(&format!("{} += {};", sum_var, (s + tag_size) as isize));
- }
- None => {
- self.write_element_size(w, v, v_type, sum_var);
- }
- };
- });
- }
- FieldKind::Repeated(RepeatedField { packed: false, .. }) => {
- match field_type_size(self.proto_type) {
- Some(s) => {
- let tag_size = self.tag_size();
- let self_field = self.self_field();
- w.write_line(&format!(
- "{} += {} * {}.len() as u32;",
- sum_var,
- (s + tag_size) as isize,
- self_field
- ));
- }
- None => {
- self.write_for_self_field(w, "value", |w, value_type| {
- self.write_element_size(w, "value", value_type, sum_var);
- });
- }
- };
- }
- FieldKind::Map(MapField {
- ref key, ref value, ..
- }) => {
- w.write_line(&format!(
- "{} += {}::rt::compute_map_size::<{}, {}>({}, &{});",
- sum_var,
- protobuf_crate_path(&self.customize),
- key.lib_protobuf_type(&self.customize),
- value.lib_protobuf_type(&self.customize),
- self.proto_field.number(),
- self.self_field()
- ));
- }
- FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
- self.write_if_self_field_is_not_empty(w, |w| {
- let size_expr = self.self_field_vec_packed_size();
- w.write_line(&format!("{} += {};", sum_var, size_expr));
- });
- }
- FieldKind::Oneof(..) => unreachable!(),
- }
- }
-
- fn write_message_field_get_singular(&self, w: &mut CodeWriter) {
- let get_xxx_return_type = self.get_xxx_return_type();
-
- if self.proto_type == FieldDescriptorProto_Type::TYPE_MESSAGE {
- let self_field = self.self_field();
- let ref rust_type_message = match self.elem().rust_storage_type() {
- RustType::Message(m) => m,
- _ => unreachable!(),
- };
- w.write_line(&format!(
- "{}.as_ref().unwrap_or_else(|| {})",
- self_field,
- rust_type_message.default_instance(&self.customize)
- ));
- } else {
- let get_xxx_default_value_rust = self.get_xxx_default_value_rust();
- let self_field = self.self_field();
- match self.singular() {
- &SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ..
- } => {
- if get_xxx_return_type.is_ref() {
- let as_option = self.self_field_as_option();
- w.match_expr(&as_option.value, |w| {
- let v_type = as_option.rust_type.elem_type();
- let r_type = self.get_xxx_return_type();
- w.case_expr(
- "Some(v)",
- v_type.into_target(&r_type, "v", &self.customize),
- );
- let get_xxx_default_value_rust = self.get_xxx_default_value_rust();
- w.case_expr("None", get_xxx_default_value_rust);
- });
- } else {
- w.write_line(&format!(
- "{}.unwrap_or({})",
- self_field, get_xxx_default_value_rust
- ));
- }
- }
- &SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- } => {
- w.write_line(self.full_storage_type().into_target(
- &get_xxx_return_type,
- &self_field,
- &self.customize,
- ));
- }
- }
- }
- }
-
- fn write_message_field_get(&self, w: &mut CodeWriter) {
- let get_xxx_return_type = self.get_xxx_return_type();
- let fn_def = format!(
- "get_{}(&self) -> {}",
- self.rust_name,
- get_xxx_return_type.to_code(&self.customize)
- );
-
- w.pub_fn(&fn_def, |w| match self.kind {
- FieldKind::Oneof(OneofField { ref elem, .. }) => {
- let self_field_oneof = self.self_field_oneof();
- w.match_expr(self_field_oneof, |w| {
- let (refv, vtype) = if !self.elem_type_is_copy() {
- ("ref v", elem.rust_storage_type().ref_type())
- } else {
- ("v", elem.rust_storage_type())
- };
- w.case_expr(
- format!(
- "::std::option::Option::Some({}({}))",
- self.variant_path(),
- refv
- ),
- vtype.into_target(&get_xxx_return_type, "v", &self.customize),
- );
- w.case_expr("_", self.get_xxx_default_value_rust());
- })
- }
- FieldKind::Singular(..) => {
- self.write_message_field_get_singular(w);
- }
- FieldKind::Repeated(..) | FieldKind::Map(..) => {
- let self_field = self.self_field();
- w.write_line(&format!("&{}", self_field));
- }
- });
- }
-
- fn has_has(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => false,
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ..
- }) => true,
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- }) => false,
- FieldKind::Oneof(..) => true,
- }
- }
-
- fn has_mut(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => true,
- // TODO: string should be public, and mut is not needed
- FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
- }
- }
-
- fn has_take(&self) -> bool {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => true,
- // TODO: string should be public, and mut is not needed
- FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
- }
- }
-
- fn has_name(&self) -> String {
- format!("has_{}", self.rust_name)
- }
-
- fn write_message_field_has(&self, w: &mut CodeWriter) {
- w.pub_fn(&format!("{}(&self) -> bool", self.has_name()), |w| {
- if !self.is_oneof() {
- let self_field_is_some = self.self_field_is_some();
- w.write_line(self_field_is_some);
- } else {
- let self_field_oneof = self.self_field_oneof();
- w.match_expr(self_field_oneof, |w| {
- w.case_expr(
- format!("::std::option::Option::Some({}(..))", self.variant_path()),
- "true",
- );
- w.case_expr("_", "false");
- });
- }
- });
- }
-
- fn write_message_field_set(&self, w: &mut CodeWriter) {
- let set_xxx_param_type = self.set_xxx_param_type();
- w.comment("Param is passed by value, moved");
- let ref name = self.rust_name;
- w.pub_fn(
- &format!(
- "set_{}(&mut self, v: {})",
- name,
- set_xxx_param_type.to_code(&self.customize)
- ),
- |w| {
- if !self.is_oneof() {
- self.write_self_field_assign_value(w, "v", &set_xxx_param_type);
- } else {
- let self_field_oneof = self.self_field_oneof();
- let v = set_xxx_param_type.into_target(
- &self.oneof().rust_type(),
- "v",
- &self.customize,
- );
- w.write_line(&format!(
- "{} = ::std::option::Option::Some({}({}))",
- self_field_oneof,
- self.variant_path(),
- v
- ));
- }
- },
- );
- }
-
- fn write_message_field_mut(&self, w: &mut CodeWriter) {
- let mut_xxx_return_type = self.mut_xxx_return_type();
- w.comment("Mutable pointer to the field.");
- if self.is_singular() {
- w.comment("If field is not initialized, it is initialized with default value first.");
- }
- let fn_def = match mut_xxx_return_type {
- RustType::Ref(ref param) => format!(
- "mut_{}(&mut self) -> &mut {}",
- self.rust_name,
- param.to_code(&self.customize)
- ),
- _ => panic!(
- "not a ref: {}",
- mut_xxx_return_type.to_code(&self.customize)
- ),
- };
- w.pub_fn(&fn_def, |w| {
- match self.kind {
- FieldKind::Repeated(..) | FieldKind::Map(..) => {
- let self_field = self.self_field();
- w.write_line(&format!("&mut {}", self_field));
- }
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithFlag { .. },
- ..
- }) => {
- self.write_if_self_field_is_none(w, |w| {
- self.write_self_field_assign_default(w);
- });
- let self_field = self.self_field();
- w.write_line(&format!("{}.as_mut().unwrap()", self_field));
- }
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- }) => w.write_line(&format!("&mut {}", self.self_field())),
- FieldKind::Oneof(..) => {
- let self_field_oneof = self.self_field_oneof();
-
- // if oneof does not contain current field
- w.if_let_else_stmt(
- &format!("::std::option::Option::Some({}(_))", self.variant_path())[..],
- &self_field_oneof[..],
- |w| {
- // initialize it with default value
- w.write_line(&format!(
- "{} = ::std::option::Option::Some({}({}));",
- self_field_oneof,
- self.variant_path(),
- self.element_default_value_rust()
- .into_type(self.oneof().rust_type(), &self.customize)
- .value
- ));
- },
- );
-
- // extract field
- w.match_expr(self_field_oneof, |w| {
- w.case_expr(
- format!(
- "::std::option::Option::Some({}(ref mut v))",
- self.variant_path()
- ),
- "v",
- );
- w.case_expr("_", "panic!()");
- });
- }
- }
- });
- }
-
- fn write_message_field_take_oneof(&self, w: &mut CodeWriter) {
- let take_xxx_return_type = self.take_xxx_return_type();
-
- // TODO: replace with if let
- w.write_line(&format!("if self.{}() {{", self.has_name()));
- w.indented(|w| {
- let self_field_oneof = self.self_field_oneof();
- w.match_expr(format!("{}.take()", self_field_oneof), |w| {
- let value_in_some = self.oneof().rust_type().value("v".to_owned());
- let converted =
- value_in_some.into_type(self.take_xxx_return_type(), &self.customize);
- w.case_expr(
- format!("::std::option::Option::Some({}(v))", self.variant_path()),
- &converted.value,
- );
- w.case_expr("_", "panic!()");
- });
- });
- w.write_line("} else {");
- w.indented(|w| {
- w.write_line(
- self.elem()
- .rust_storage_type()
- .default_value_typed(&self.customize)
- .into_type(take_xxx_return_type.clone(), &self.customize)
- .value,
- );
- });
- w.write_line("}");
- }
-
- fn write_message_field_take(&self, w: &mut CodeWriter) {
- let take_xxx_return_type = self.take_xxx_return_type();
- w.comment("Take field");
- w.pub_fn(
- &format!(
- "take_{}(&mut self) -> {}",
- self.rust_name,
- take_xxx_return_type.to_code(&self.customize)
- ),
- |w| match self.kind {
- FieldKind::Oneof(..) => {
- self.write_message_field_take_oneof(w);
- }
- FieldKind::Repeated(..) | FieldKind::Map(..) => {
- w.write_line(&format!(
- "::std::mem::replace(&mut self.{}, {})",
- self.rust_name,
- take_xxx_return_type.default_value(&self.customize)
- ));
- }
- FieldKind::Singular(SingularField {
- ref elem,
- flag: SingularFieldFlag::WithFlag { .. },
- }) => {
- if !elem.is_copy() {
- w.write_line(&format!(
- "{}.take().unwrap_or_else(|| {})",
- self.self_field(),
- elem.rust_storage_type().default_value(&self.customize)
- ));
- } else {
- w.write_line(&format!(
- "{}.take().unwrap_or({})",
- self.self_field(),
- self.element_default_value_rust().value
- ));
- }
- }
- FieldKind::Singular(SingularField {
- flag: SingularFieldFlag::WithoutFlag,
- ..
- }) => w.write_line(&format!(
- "::std::mem::replace(&mut {}, {})",
- self.self_field(),
- self.full_storage_type().default_value(&self.customize)
- )),
- },
- );
- }
-
- pub fn write_message_single_field_accessors(&self, w: &mut CodeWriter) {
- // TODO: do not generate `get` when !proto2 and !generate_accessors`
- w.write_line("");
- self.write_message_field_get(w);
-
- if !self.generate_accessors {
- return;
- }
-
- let clear_field_func = self.clear_field_func();
- w.pub_fn(&format!("{}(&mut self)", clear_field_func), |w| {
- self.write_clear(w);
- });
-
- if self.has_has() {
- w.write_line("");
- self.write_message_field_has(w);
- }
-
- w.write_line("");
- self.write_message_field_set(w);
-
- if self.has_mut() {
- w.write_line("");
- self.write_message_field_mut(w);
- }
-
- if self.has_take() {
- w.write_line("");
- self.write_message_field_take(w);
- }
- }
-}
-
-pub(crate) fn rust_field_name_for_protobuf_field_name(name: &str) -> RustIdent {
- if rust::is_rust_keyword(name) {
- RustIdent::new(&format!("field_{}", name))
- } else {
- RustIdent::new(name)
- }
-}
diff --git a/src/file_descriptor.rs b/src/file_descriptor.rs
deleted file mode 100644
index dde8603..0000000
--- a/src/file_descriptor.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-use crate::scope::Scope;
-
-pub(crate) fn file_descriptor_proto_expr(_scope: &Scope) -> String {
- format!("file_descriptor_proto()")
-}
diff --git a/src/float.rs b/src/float.rs
deleted file mode 100644
index 78ca622..0000000
--- a/src/float.rs
+++ /dev/null
@@ -1,67 +0,0 @@
-use std::f64;
-
-#[derive(Debug)]
-pub enum ProtobufFloatParseError {
- EmptyString,
- CannotParseFloat,
-}
-
-pub type ProtobufFloatParseResult<T> = Result<T, ProtobufFloatParseError>;
-
-pub const PROTOBUF_NAN: &str = "nan";
-pub const PROTOBUF_INF: &str = "inf";
-
-/// Format float as in protobuf `.proto` files
-pub fn format_protobuf_float(f: f64) -> String {
- if f.is_nan() {
- PROTOBUF_NAN.to_owned()
- } else if f.is_infinite() {
- if f > 0.0 {
- format!("{}", PROTOBUF_INF)
- } else {
- format!("-{}", PROTOBUF_INF)
- }
- } else {
- let i = f as i64;
- if i as f64 == f {
- // Older rust versions did print float without `.0` suffix
- format!("{:?}.0", i)
- } else {
- // TODO: make sure doesn't lose precision
- format!("{:?}", f)
- }
- }
-}
-
-/// Parse float from `.proto` format
-pub fn parse_protobuf_float(s: &str) -> ProtobufFloatParseResult<f64> {
- if s.is_empty() {
- return Err(ProtobufFloatParseError::EmptyString);
- }
- if s == PROTOBUF_NAN {
- return Ok(f64::NAN);
- }
- if s == PROTOBUF_INF || s == format!("+{}", PROTOBUF_INF) {
- return Ok(f64::INFINITY);
- }
- if s == format!("-{}", PROTOBUF_INF) {
- return Ok(f64::NEG_INFINITY);
- }
- match s.parse() {
- Ok(f) => Ok(f),
- Err(_) => Err(ProtobufFloatParseError::CannotParseFloat),
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_format_protobuf_float() {
- assert_eq!("10.0", format_protobuf_float(10.0));
- assert_eq!("-10.0", format_protobuf_float(-10.0));
- assert_eq!("10.5", format_protobuf_float(10.5));
- assert_eq!("-10.5", format_protobuf_float(-10.5));
- }
-}
diff --git a/src/gen/all.rs b/src/gen/all.rs
new file mode 100644
index 0000000..312b8db
--- /dev/null
+++ b/src/gen/all.rs
@@ -0,0 +1,64 @@
+use std::collections::HashMap;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtoPath;
+use protobuf_parse::ProtoPathBuf;
+
+use crate::compiler_plugin;
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::CustomizeCallback;
+use crate::gen::file::gen_file;
+use crate::gen::mod_rs::gen_mod_rs;
+use crate::gen::scope::RootScope;
+use crate::gen::well_known_types::gen_well_known_types_mod;
+use crate::Customize;
+
+pub(crate) fn gen_all(
+ file_descriptors: &[FileDescriptorProto],
+ parser: &str,
+ files_to_generate: &[ProtoPathBuf],
+ customize: &Customize,
+ customize_callback: &dyn CustomizeCallback,
+) -> anyhow::Result<Vec<compiler_plugin::GenResult>> {
+ let file_descriptors = FileDescriptor::new_dynamic_fds(file_descriptors.to_vec(), &[])?;
+
+ let root_scope = RootScope {
+ file_descriptors: &file_descriptors,
+ };
+
+ let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
+ let files_map: HashMap<&ProtoPath, &FileDescriptor> = file_descriptors
+ .iter()
+ .map(|f| Ok((ProtoPath::new(f.proto().name())?, f)))
+ .collect::<Result<_, anyhow::Error>>()?;
+
+ let mut mods = Vec::new();
+
+ let customize = CustomizeElemCtx {
+ for_elem: customize.clone(),
+ for_children: customize.clone(),
+ callback: customize_callback,
+ };
+
+ for file_name in files_to_generate {
+ let file = files_map.get(file_name.as_path()).expect(&format!(
+ "file not found in file descriptors: {:?}, files: {:?}",
+ file_name,
+ files_map.keys()
+ ));
+ let gen_file_result = gen_file(file, &files_map, &root_scope, &customize, parser)?;
+ results.push(gen_file_result.compiler_plugin_result);
+ mods.push(gen_file_result.mod_name);
+ }
+
+ if customize.for_elem.inside_protobuf.unwrap_or(false) {
+ results.push(gen_well_known_types_mod());
+ }
+
+ if customize.for_elem.gen_mod_rs.unwrap_or(true) {
+ results.push(gen_mod_rs(&mods));
+ }
+
+ Ok(results)
+}
diff --git a/src/gen/code_writer.rs b/src/gen/code_writer.rs
new file mode 100644
index 0000000..28b3817
--- /dev/null
+++ b/src/gen/code_writer.rs
@@ -0,0 +1,455 @@
+use std::convert::Infallible;
+
+use crate::gen::rust::rel_path::RustRelativePath;
+
+/// Field visibility.
+pub(crate) enum Visibility {
+ Public,
+ Default,
+ Path(RustRelativePath),
+}
+
+pub(crate) struct CodeWriter<'a> {
+ writer: &'a mut String,
+ indent: String,
+}
+
+impl<'a> CodeWriter<'a> {
+ pub(crate) fn new(writer: &'a mut String) -> CodeWriter<'a> {
+ CodeWriter {
+ writer,
+ indent: "".to_string(),
+ }
+ }
+
+ pub(crate) fn with_no_error(f: impl FnOnce(&mut CodeWriter)) -> String {
+ Self::with_impl::<Infallible, _>(|w| Ok(f(w))).unwrap_or_else(|e| match e {})
+ }
+
+ pub(crate) fn with<F>(f: F) -> anyhow::Result<String>
+ where
+ F: FnOnce(&mut CodeWriter) -> anyhow::Result<()>,
+ {
+ Self::with_impl(f)
+ }
+
+ fn with_impl<E, F>(f: F) -> Result<String, E>
+ where
+ F: FnOnce(&mut CodeWriter) -> Result<(), E>,
+ {
+ let mut writer = String::new();
+ {
+ let mut cw = CodeWriter::new(&mut writer);
+ f(&mut cw)?;
+ }
+ Ok(writer)
+ }
+
+ pub(crate) fn write_line<S: AsRef<str>>(&mut self, line: S) {
+ if line.as_ref().is_empty() {
+ self.writer.push_str("\n");
+ } else {
+ let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat();
+ self.writer.push_str(&s);
+ }
+ }
+
+ pub(crate) fn _write_text(&mut self, text: &str) {
+ for line in text.lines() {
+ self.write_line(line);
+ }
+ }
+
+ pub(crate) fn write_generated_by(&mut self, pkg: &str, version: &str, parser: &str) {
+ self.write_line(format!(
+ "// This file is generated by {pkg} {version}. Do not edit",
+ pkg = pkg,
+ version = version
+ ));
+ self.write_line(format!(
+ "// .proto file is parsed by {parser}",
+ parser = parser
+ ));
+ self.write_generated_common();
+ }
+
+ fn write_generated_common(&mut self) {
+ // https://secure.phabricator.com/T784
+ self.write_line(&format!("// {}generated", "@"));
+
+ self.write_line("");
+ self.comment("https://github.com/rust-lang/rust-clippy/issues/702");
+ self.write_line("#![allow(unknown_lints)]");
+ self.write_line("#![allow(clippy::all)]");
+ self.write_line("");
+ self.write_line("#![allow(unused_attributes)]");
+ self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
+ self.write_line("");
+ self.write_line("#![allow(box_pointers)]");
+ self.write_line("#![allow(dead_code)]");
+ self.write_line("#![allow(missing_docs)]");
+ self.write_line("#![allow(non_camel_case_types)]");
+ self.write_line("#![allow(non_snake_case)]");
+ self.write_line("#![allow(non_upper_case_globals)]");
+ self.write_line("#![allow(trivial_casts)]");
+ self.write_line("#![allow(unused_results)]");
+ self.write_line("#![allow(unused_mut)]");
+ }
+
+ pub(crate) fn unimplemented(&mut self) {
+ self.write_line(format!("unimplemented!();"));
+ }
+
+ pub(crate) fn indented<F>(&mut self, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ cb(&mut CodeWriter {
+ writer: self.writer,
+ indent: format!("{} ", self.indent),
+ });
+ }
+
+ #[allow(dead_code)]
+ pub(crate) fn commented<F>(&mut self, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ cb(&mut CodeWriter {
+ writer: self.writer,
+ indent: format!("// {}", self.indent),
+ });
+ }
+
+ pub(crate) fn pub_const(&mut self, name: &str, field_type: &str, init: &str) {
+ self.write_line(&format!("pub const {}: {} = {};", name, field_type, init));
+ }
+
+ pub(crate) fn lazy_static(&mut self, name: &str, ty: &str, protobuf_crate_path: &str) {
+ self.write_line(&format!(
+ "static {}: {}::rt::Lazy<{}> = {}::rt::Lazy::new();",
+ name, protobuf_crate_path, ty, protobuf_crate_path,
+ ));
+ }
+
+ pub(crate) fn lazy_static_decl_get_simple(
+ &mut self,
+ name: &str,
+ ty: &str,
+ init: &str,
+ protobuf_crate_path: &str,
+ ) {
+ self.lazy_static(name, ty, protobuf_crate_path);
+ self.write_line(&format!("{}.get({})", name, init));
+ }
+
+ pub(crate) fn lazy_static_decl_get(
+ &mut self,
+ name: &str,
+ ty: &str,
+ protobuf_crate_path: &str,
+ init: impl FnOnce(&mut CodeWriter),
+ ) {
+ self.lazy_static(name, ty, protobuf_crate_path);
+ self.block(&format!("{}.get(|| {{", name), "})", init);
+ }
+
+ pub(crate) fn block<F>(&mut self, first_line: &str, last_line: &str, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.write_line(first_line);
+ self.indented(cb);
+ self.write_line(last_line);
+ }
+
+ pub(crate) fn expr_block<F>(&mut self, prefix: &str, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.block(&format!("{} {{", prefix), "}", cb);
+ }
+
+ pub(crate) fn stmt_block<S: AsRef<str>, F>(&mut self, prefix: S, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.block(&format!("{} {{", prefix.as_ref()), "};", cb);
+ }
+
+ pub(crate) fn impl_self_block<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("impl {}", name.as_ref()), cb);
+ }
+
+ pub(crate) fn impl_for_block<S1: AsRef<str>, S2: AsRef<str>, F>(
+ &mut self,
+ tr: S1,
+ ty: S2,
+ cb: F,
+ ) where
+ F: Fn(&mut CodeWriter),
+ {
+ self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb);
+ }
+
+ pub(crate) fn impl_args_for_block<F>(&mut self, args: &[&str], tr: &str, ty: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ let args_str = if args.is_empty() {
+ "".to_owned()
+ } else {
+ format!("<{}>", args.join(", "))
+ };
+ self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb);
+ }
+
+ pub(crate) fn pub_struct<S: AsRef<str>, F>(&mut self, name: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("pub struct {}", name.as_ref()), cb);
+ }
+
+ pub(crate) fn pub_enum<F>(&mut self, name: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("pub enum {}", name), cb);
+ }
+
+ pub(crate) fn field_entry(&mut self, name: &str, value: &str) {
+ self.write_line(&format!("{}: {},", name, value));
+ }
+
+ pub(crate) fn field_decl(&mut self, name: &str, field_type: &str) {
+ self.write_line(&format!("{}: {},", name, field_type));
+ }
+
+ pub(crate) fn pub_field_decl(&mut self, name: &str, field_type: &str) {
+ self.write_line(&format!("pub {}: {},", name, field_type));
+ }
+
+ pub(crate) fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) {
+ match vis {
+ Visibility::Public => self.pub_field_decl(name, field_type),
+ Visibility::Default => self.field_decl(name, field_type),
+ Visibility::Path(..) => unimplemented!(),
+ }
+ }
+
+ pub(crate) fn derive(&mut self, derive: &[&str]) {
+ let v: Vec<String> = derive.iter().map(|&s| s.to_string()).collect();
+ self.write_line(&format!("#[derive({})]", v.join(",")));
+ }
+
+ pub(crate) fn allow(&mut self, what: &[&str]) {
+ let v: Vec<String> = what.iter().map(|&s| s.to_string()).collect();
+ self.write_line(&format!("#[allow({})]", v.join(",")));
+ }
+
+ pub(crate) fn comment(&mut self, comment: &str) {
+ if comment.is_empty() {
+ self.write_line("//");
+ } else {
+ self.write_line(&format!("// {}", comment));
+ }
+ }
+
+ fn documentation(&mut self, comment: &str) {
+ if comment.is_empty() {
+ self.write_line("///");
+ } else {
+ self.write_line(&format!("/// {}", comment));
+ }
+ }
+
+ pub(crate) fn mod_doc(&mut self, comment: &str) {
+ if comment.is_empty() {
+ self.write_line("//!");
+ } else {
+ self.write_line(&format!("//! {}", comment));
+ }
+ }
+
+ /// Writes the documentation of the given path.
+ ///
+ /// Protobuf paths are defined in proto/google/protobuf/descriptor.proto,
+ /// in the `SourceCodeInfo` message.
+ ///
+ /// For example, say we have a file like:
+ ///
+ /// ```ignore
+ /// message Foo {
+ /// optional string foo = 1;
+ /// }
+ /// ```
+ ///
+ /// Let's look at just the field definition. We have the following paths:
+ ///
+ /// ```ignore
+ /// path represents
+ /// [ 4, 0, 2, 0 ] The whole field definition.
+ /// [ 4, 0, 2, 0, 4 ] The label (optional).
+ /// [ 4, 0, 2, 0, 5 ] The type (string).
+ /// [ 4, 0, 2, 0, 1 ] The name (foo).
+ /// [ 4, 0, 2, 0, 3 ] The number (1).
+ /// ```
+ ///
+ /// The `4`s can be obtained using simple introspection:
+ ///
+ /// ```
+ /// use protobuf::descriptor::FileDescriptorProto;
+ /// use protobuf::reflect::MessageDescriptor;
+ ///
+ /// let id = MessageDescriptor::for_type::<FileDescriptorProto>()
+ /// .field_by_name("message_type")
+ /// .expect("`message_type` must exist")
+ /// .proto()
+ /// .number();
+ ///
+ /// assert_eq!(id, 4);
+ /// ```
+ ///
+ /// The first `0` here means this path refers to the first message.
+ ///
+ /// The `2` then refers to the `field` field on the `DescriptorProto` message.
+ ///
+ /// Then comes another `0` to refer to the first field of the current message.
+ ///
+ /// Etc.
+
+ pub(crate) fn all_documentation(
+ &mut self,
+ info: Option<&protobuf::descriptor::SourceCodeInfo>,
+ path: &[i32],
+ ) {
+ let doc = info
+ .map(|v| &v.location)
+ .and_then(|ls| ls.iter().find(|l| l.path == path))
+ .map(|l| l.leading_comments());
+
+ let lines = doc
+ .iter()
+ .map(|doc| doc.lines())
+ .flatten()
+ .collect::<Vec<_>>();
+
+ // Skip comments with code blocks to avoid rustdoc trying to compile them.
+ if !lines.iter().any(|line| line.starts_with(" ")) {
+ for doc in &lines {
+ self.documentation(doc);
+ }
+ }
+ }
+
+ pub(crate) fn fn_block<F>(&mut self, vis: Visibility, sig: &str, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ match vis {
+ Visibility::Public => self.expr_block(&format!("pub fn {}", sig), cb),
+ Visibility::Default => self.expr_block(&format!("fn {}", sig), cb),
+ Visibility::Path(p) if p.is_empty() => self.expr_block(&format!("fn {}", sig), cb),
+ Visibility::Path(p) => self.expr_block(&format!("pub(in {}) fn {}", p, sig), cb),
+ }
+ }
+
+ pub(crate) fn pub_fn<F>(&mut self, sig: &str, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.fn_block(Visibility::Public, sig, cb);
+ }
+
+ pub(crate) fn def_fn<F>(&mut self, sig: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.fn_block(Visibility::Default, sig, cb);
+ }
+
+ pub(crate) fn pub_mod<F>(&mut self, name: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("pub mod {}", name), cb)
+ }
+
+ pub(crate) fn while_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("while {}", cond.as_ref()), cb);
+ }
+
+ // if ... { ... }
+ pub(crate) fn if_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.expr_block(&format!("if {}", cond.as_ref()), cb);
+ }
+
+ // if ... {} else { ... }
+ pub(crate) fn if_else_stmt<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.write_line(&format!("if {} {{", cond.as_ref()));
+ self.write_line("} else {");
+ self.indented(cb);
+ self.write_line("}");
+ }
+
+ // if let ... = ... { ... }
+ pub(crate) fn if_let_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.if_stmt(&format!("let {} = {}", decl, expr), cb);
+ }
+
+ // if let ... = ... { } else { ... }
+ pub(crate) fn if_let_else_stmt<F>(&mut self, decl: &str, expr: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.if_else_stmt(&format!("let {} = {}", decl, expr), cb);
+ }
+
+ pub(crate) fn for_stmt<S1: AsRef<str>, S2: AsRef<str>, F>(&mut self, over: S1, varn: S2, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb)
+ }
+
+ pub(crate) fn match_block<S: AsRef<str>, F>(&mut self, value: S, cb: F)
+ where
+ F: FnOnce(&mut CodeWriter),
+ {
+ self.stmt_block(&format!("match {}", value.as_ref()), cb);
+ }
+
+ pub(crate) fn match_expr<S: AsRef<str>, F>(&mut self, value: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.expr_block(&format!("match {}", value.as_ref()), cb);
+ }
+
+ pub(crate) fn case_block<S: AsRef<str>, F>(&mut self, cond: S, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ self.block(&format!("{} => {{", cond.as_ref()), "},", cb);
+ }
+
+ pub(crate) fn case_expr<S1: AsRef<str>, S2: AsRef<str>>(&mut self, cond: S1, body: S2) {
+ self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref()));
+ }
+}
diff --git a/src/gen/descriptor.rs b/src/gen/descriptor.rs
new file mode 100644
index 0000000..53ed061
--- /dev/null
+++ b/src/gen/descriptor.rs
@@ -0,0 +1,68 @@
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::MessageDescriptor;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::file_descriptor::file_descriptor_call_expr;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::scope::Scope;
+use crate::Customize;
+
+/// Abstract message or enum descriptor.
+pub(crate) trait Descriptor {
+ const DESCRIPTOR_FN: &'static str;
+ const TYPE_NAME: &'static str;
+ const GET_BY_RELATIVE_NAME_NAME: &'static str;
+ fn name_to_package(&self) -> &str;
+}
+
+impl Descriptor for MessageDescriptor {
+ const DESCRIPTOR_FN: &'static str = "descriptor";
+ const TYPE_NAME: &'static str = "MessageDescriptor";
+ const GET_BY_RELATIVE_NAME_NAME: &'static str = "message_by_package_relative_name";
+
+ fn name_to_package(&self) -> &str {
+ self.name_to_package()
+ }
+}
+
+impl Descriptor for EnumDescriptor {
+ const DESCRIPTOR_FN: &'static str = "enum_descriptor";
+ const TYPE_NAME: &'static str = "EnumDescriptor";
+ const GET_BY_RELATIVE_NAME_NAME: &'static str = "enum_by_package_relative_name";
+
+ fn name_to_package(&self) -> &str {
+ self.name_to_package()
+ }
+}
+
+pub(crate) fn write_fn_descriptor<D: Descriptor>(
+ descriptor: &D,
+ scope: &Scope,
+ customize: &Customize,
+ w: &mut CodeWriter,
+) {
+ let sig = format!(
+ "{}() -> {}::reflect::{}",
+ D::DESCRIPTOR_FN,
+ protobuf_crate_path(customize),
+ D::TYPE_NAME,
+ );
+ w.def_fn(&sig, |w| {
+ let expr = format!(
+ "{}.{}(\"{}\").unwrap()",
+ file_descriptor_call_expr(scope),
+ D::GET_BY_RELATIVE_NAME_NAME,
+ descriptor.name_to_package()
+ );
+ w.lazy_static(
+ "descriptor",
+ &format!(
+ "{}::reflect::{}",
+ protobuf_crate_path(customize),
+ D::TYPE_NAME,
+ ),
+ &protobuf_crate_path(customize).to_string(),
+ );
+ w.write_line(&format!("descriptor.get(|| {}).clone()", expr));
+ });
+}
diff --git a/src/gen/enums.rs b/src/gen/enums.rs
new file mode 100644
index 0000000..69e9e14
--- /dev/null
+++ b/src/gen/enums.rs
@@ -0,0 +1,413 @@
+use std::collections::HashSet;
+
+use protobuf::descriptor::*;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_enum;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::descriptor::write_fn_descriptor;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum_value;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::scope::EnumValueWithContext;
+use crate::gen::scope::EnumWithScope;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+
+#[derive(Clone)]
+pub(crate) struct EnumValueGen<'a> {
+ value: EnumValueWithContext<'a>,
+ enum_rust_name: RustIdentWithPath,
+}
+
+impl<'a> EnumValueGen<'a> {
+ fn parse(
+ value: EnumValueWithContext<'a>,
+ enum_rust_name: &RustIdentWithPath,
+ ) -> EnumValueGen<'a> {
+ EnumValueGen {
+ value: value.clone(),
+ enum_rust_name: enum_rust_name.clone(),
+ }
+ }
+
+ // enum value
+ fn number(&self) -> i32 {
+ self.value.proto.proto().number()
+ }
+
+ // name of enum variant in generated rust code
+ pub fn rust_name_inner(&self) -> RustIdent {
+ self.value.rust_name()
+ }
+
+ pub fn rust_name_outer(&self) -> RustIdentWithPath {
+ self.enum_rust_name
+ .to_path()
+ .with_ident(self.rust_name_inner())
+ }
+}
+
+// Codegen for enum definition
+pub(crate) struct EnumGen<'a> {
+ enum_with_scope: &'a EnumWithScope<'a>,
+ type_name: RustIdentWithPath,
+ lite_runtime: bool,
+ customize: CustomizeElemCtx<'a>,
+ path: &'a [i32],
+ info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> EnumGen<'a> {
+ pub fn new(
+ enum_with_scope: &'a EnumWithScope<'a>,
+ customize: &CustomizeElemCtx<'a>,
+ _root_scope: &RootScope,
+ path: &'a [i32],
+ info: Option<&'a SourceCodeInfo>,
+ ) -> EnumGen<'a> {
+ let customize = customize.child(
+ &customize_from_rustproto_for_enum(enum_with_scope.en.proto().options.get_or_default()),
+ &enum_with_scope.en,
+ );
+ let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+ enum_with_scope
+ .file_descriptor()
+ .proto()
+ .options
+ .optimize_for()
+ == file_options::OptimizeMode::LITE_RUNTIME
+ });
+
+ EnumGen {
+ enum_with_scope,
+ type_name: enum_with_scope.rust_name().to_path(),
+ lite_runtime,
+ customize,
+ path,
+ info,
+ }
+ }
+
+ fn allow_alias(&self) -> bool {
+ self.enum_with_scope
+ .en
+ .proto()
+ .options
+ .get_or_default()
+ .allow_alias()
+ }
+
+ fn values_all(&self) -> Vec<EnumValueGen> {
+ let mut r = Vec::new();
+ for p in self.enum_with_scope.values() {
+ r.push(EnumValueGen::parse(p, &self.type_name));
+ }
+ r
+ }
+
+ fn values_unique(&self) -> Vec<EnumValueGen> {
+ let mut used = HashSet::new();
+ let mut r = Vec::new();
+ for p in self.enum_with_scope.values() {
+ if !used.insert(p.proto.proto().number()) {
+ continue;
+ }
+ r.push(EnumValueGen::parse(p, &self.type_name));
+ }
+ r
+ }
+
+ pub fn write(&self, w: &mut CodeWriter) {
+ self.write_enum(w);
+ if self.allow_alias() {
+ w.write_line("");
+ self.write_impl_eq(w);
+ w.write_line("");
+ self.write_impl_hash(w);
+ }
+ w.write_line("");
+ self.write_impl_enum(w);
+ if !self.lite_runtime {
+ w.write_line("");
+ self.write_impl_enum_full(w);
+ }
+ w.write_line("");
+ self.write_impl_default(w);
+ w.write_line("");
+ self.write_impl_self(w);
+ }
+
+ fn write_impl_self(&self, w: &mut CodeWriter) {
+ if !self.lite_runtime {
+ w.impl_self_block(&format!("{}", self.type_name), |w| {
+ self.write_generated_enum_descriptor_data(w);
+ });
+ }
+ }
+
+ fn write_enum(&self, w: &mut CodeWriter) {
+ w.all_documentation(self.info, self.path);
+
+ let mut derive = Vec::new();
+ derive.push("Clone");
+ derive.push("Copy");
+ if !self.allow_alias() {
+ derive.push("PartialEq");
+ }
+ derive.push("Eq");
+ derive.push("Debug");
+ if !self.allow_alias() {
+ derive.push("Hash");
+ } else {
+ w.comment("Note: you cannot use pattern matching for enums with allow_alias option");
+ }
+ w.derive(&derive);
+ let ref type_name = self.type_name;
+ write_protoc_insertion_point_for_enum(
+ w,
+ &self.customize.for_elem,
+ &self.enum_with_scope.en,
+ );
+ w.expr_block(&format!("pub enum {}", type_name), |w| {
+ for value in self.values_all() {
+ write_protoc_insertion_point_for_enum_value(
+ w,
+ &self.customize.for_children,
+ &value.value.proto,
+ );
+ if self.allow_alias() {
+ w.write_line(&format!(
+ "{}, // {}",
+ value.rust_name_inner(),
+ value.number()
+ ));
+ } else {
+ w.write_line(&format!(
+ "{} = {},",
+ value.rust_name_inner(),
+ value.number()
+ ));
+ }
+ }
+ });
+ }
+
+ fn write_impl_enum_fn_value(&self, w: &mut CodeWriter) {
+ w.def_fn("value(&self) -> i32", |w| {
+ if self.allow_alias() {
+ w.match_expr("*self", |w| {
+ for value in self.values_all() {
+ w.case_expr(
+ &format!("{}", value.rust_name_outer()),
+ &format!("{}", value.number()),
+ );
+ }
+ });
+ } else {
+ w.write_line("*self as i32")
+ }
+ });
+ }
+
+ fn write_impl_enum_const_name(&self, w: &mut CodeWriter) {
+ w.write_line(&format!(
+ "const NAME: &'static str = \"{}\";",
+ self.enum_with_scope.en.name()
+ ));
+ }
+
+ fn write_impl_enum_fn_from_i32(&self, w: &mut CodeWriter) {
+ w.def_fn(
+ &format!(
+ "from_i32(value: i32) -> ::std::option::Option<{}>",
+ self.type_name
+ ),
+ |w| {
+ w.match_expr("value", |w| {
+ let values = self.values_unique();
+ for value in values {
+ w.write_line(&format!(
+ "{} => ::std::option::Option::Some({}),",
+ value.number(),
+ value.rust_name_outer()
+ ));
+ }
+ w.write_line(&format!("_ => {}", EXPR_NONE));
+ });
+ },
+ );
+ }
+
+ fn write_impl_enum_const_values(&self, w: &mut CodeWriter) {
+ w.write_line(&format!("const VALUES: &'static [{}] = &[", self.type_name));
+ w.indented(|w| {
+ for value in self.values_all() {
+ w.write_line(&format!("{},", value.rust_name_outer()));
+ }
+ });
+ w.write_line("];");
+ }
+
+ fn write_impl_enum(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!("{}::Enum", protobuf_crate_path(&self.customize.for_elem)),
+ &format!("{}", self.type_name),
+ |w| {
+ self.write_impl_enum_const_name(w);
+ w.write_line("");
+ self.write_impl_enum_fn_value(w);
+ w.write_line("");
+ self.write_impl_enum_fn_from_i32(w);
+ w.write_line("");
+ self.write_impl_enum_const_values(w);
+ },
+ );
+ }
+
+ fn write_impl_enum_full(&self, w: &mut CodeWriter) {
+ let ref type_name = self.type_name;
+ w.impl_for_block(
+ &format!(
+ "{}::EnumFull",
+ protobuf_crate_path(&self.customize.for_elem)
+ ),
+ &format!("{}", type_name),
+ |w| {
+ self.write_impl_enum_full_fn_enum_descriptor(w);
+ w.write_line("");
+ self.write_impl_enum_full_fn_descriptor(w);
+ },
+ );
+ }
+
+ fn write_impl_enum_full_fn_enum_descriptor(&self, w: &mut CodeWriter) {
+ write_fn_descriptor(
+ &self.enum_with_scope.en,
+ self.enum_with_scope.scope(),
+ &self.customize.for_elem,
+ w,
+ );
+ }
+
+ fn rust_enum_descriptor_is_enum_index(&self) -> bool {
+ if self.allow_alias() {
+ false
+ } else {
+ self.values_all()
+ .into_iter()
+ .enumerate()
+ .all(|(i, value)| (i as i32) == value.number())
+ }
+ }
+
+ fn write_impl_enum_full_fn_descriptor(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "descriptor(&self) -> {}::reflect::EnumValueDescriptor",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.def_fn(&sig, |w| {
+ if self.rust_enum_descriptor_is_enum_index() {
+ w.write_line("let index = *self as usize;");
+ } else {
+ w.write_line("let index = match self {");
+ w.indented(|w| {
+ for (i, value) in self.values_all().into_iter().enumerate() {
+ w.write_line(&format!(
+ "{}::{} => {},",
+ self.type_name,
+ value.rust_name_inner(),
+ i
+ ));
+ }
+ });
+ w.write_line("};");
+ }
+ w.write_line(&format!("Self::enum_descriptor().value_by_index(index)"));
+ });
+ }
+
+ fn write_generated_enum_descriptor_data(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "generated_enum_descriptor_data() -> {}::reflect::GeneratedEnumDescriptorData",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.fn_block(
+ Visibility::Path(
+ self.enum_with_scope
+ .scope()
+ .rust_path_to_file()
+ .to_reverse(),
+ ),
+ &sig,
+ |w| {
+ w.write_line(&format!(
+ "{}::reflect::GeneratedEnumDescriptorData::new::<{}>(\"{}\")",
+ protobuf_crate_path(&self.customize.for_elem),
+ self.type_name,
+ self.enum_with_scope.name_to_package(),
+ ));
+ },
+ );
+ }
+
+ fn write_impl_eq(&self, w: &mut CodeWriter) {
+ assert!(self.allow_alias());
+ w.impl_for_block(
+ "::std::cmp::PartialEq",
+ &format!("{}", self.type_name),
+ |w| {
+ w.def_fn("eq(&self, other: &Self) -> bool", |w| {
+ w.write_line(&format!(
+ "{}::Enum::value(self) == {}::Enum::value(other)",
+ protobuf_crate_path(&self.customize.for_elem),
+ protobuf_crate_path(&self.customize.for_elem)
+ ));
+ });
+ },
+ );
+ }
+
+ fn write_impl_hash(&self, w: &mut CodeWriter) {
+ assert!(self.allow_alias());
+ w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| {
+ w.def_fn("hash<H : ::std::hash::Hasher>(&self, state: &mut H)", |w| {
+ w.write_line(&format!(
+ "state.write_i32({}::Enum::value(self))",
+ protobuf_crate_path(&self.customize.for_elem)
+ ));
+ });
+ });
+ }
+
+ fn write_impl_default(&self, w: &mut CodeWriter) {
+ let first_value = &self.enum_with_scope.values()[0];
+ if first_value.proto.proto().number() != 0 {
+ // This warning is emitted only for proto2
+ // (because in proto3 first enum variant number is always 0).
+ // `Default` implemented unconditionally to simplify certain
+ // generic operations, e. g. reading a map.
+ // Also, note that even in proto2 some operations fallback to
+ // first enum value, e. g. `get_xxx` for unset field,
+ // so this implementation is not completely unreasonable.
+ w.comment("Note, `Default` is implemented although default value is not 0");
+ }
+ w.impl_for_block(
+ "::std::default::Default",
+ &format!("{}", self.type_name),
+ |w| {
+ w.def_fn("default() -> Self", |w| {
+ w.write_line(&format!(
+ "{}::{}",
+ &self.type_name,
+ &first_value.rust_name()
+ ))
+ });
+ },
+ );
+ }
+}
diff --git a/src/gen/extensions.rs b/src/gen/extensions.rs
new file mode 100644
index 0000000..bfd4267
--- /dev/null
+++ b/src/gen/extensions.rs
@@ -0,0 +1,133 @@
+use protobuf::descriptor::*;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::rust_field_name_for_protobuf_field_name;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust_types_values::*;
+use crate::gen::scope::RootScope;
+
+struct ExtGen<'a> {
+ file: &'a FileDescriptor,
+ root_scope: &'a RootScope<'a>,
+ field: &'a FieldDescriptorProto,
+ customize: Customize,
+}
+
+impl<'a> ExtGen<'a> {
+ fn extendee_rust_name(&self) -> RustIdentWithPath {
+ type_name_to_rust_relative(
+ &ProtobufAbsPath::from(self.field.extendee()),
+ &FileAndMod {
+ file: self.file.proto().name().to_owned(),
+ relative_mod: RustRelativePath::from("exts"),
+ customize: self.customize.clone(),
+ },
+ self.root_scope,
+ )
+ }
+
+ fn repeated(&self) -> bool {
+ match self.field.label() {
+ field_descriptor_proto::Label::LABEL_REPEATED => true,
+ field_descriptor_proto::Label::LABEL_OPTIONAL => false,
+ field_descriptor_proto::Label::LABEL_REQUIRED => {
+ panic!("required ext field: {}", self.field.name())
+ }
+ }
+ }
+
+ fn return_type_gen(&self) -> ProtobufTypeGen {
+ if self.field.has_type_name() {
+ let rust_name_relative = type_name_to_rust_relative(
+ &ProtobufAbsPath::from(self.field.type_name()),
+ &FileAndMod {
+ file: self.file.proto().name().to_owned(),
+ relative_mod: RustRelativePath::from("exts"),
+ customize: self.customize.clone(),
+ },
+ self.root_scope,
+ );
+ match self.field.type_() {
+ field_descriptor_proto::Type::TYPE_MESSAGE => {
+ ProtobufTypeGen::Message(RustTypeMessage(rust_name_relative))
+ }
+ field_descriptor_proto::Type::TYPE_ENUM => {
+ ProtobufTypeGen::EnumOrUnknown(rust_name_relative)
+ }
+ t => panic!("unknown type: {:?}", t),
+ }
+ } else {
+ ProtobufTypeGen::Primitive(self.field.type_(), PrimitiveTypeVariant::Default)
+ }
+ }
+
+ fn write(&self, w: &mut CodeWriter) {
+ let suffix = if self.repeated() {
+ "ExtFieldRepeated"
+ } else {
+ "ExtFieldOptional"
+ };
+ let field_type = format!(
+ "{protobuf_crate}::ext::{suffix}",
+ protobuf_crate = protobuf_crate_path(&self.customize)
+ );
+ w.pub_const(
+ &rust_field_name_for_protobuf_field_name(self.field.name()).to_string(),
+ &format!(
+ "{field_type}<{extendee}, {rust_type}>",
+ extendee=self.extendee_rust_name(),
+ rust_type=self.return_type_gen().protobuf_value(&self.customize),
+ ),
+ &format!(
+ "{field_type}::new({field_number}, {protobuf_crate}::descriptor::field_descriptor_proto::Type::{t:?})",
+ field_number=self.field.number(),
+ protobuf_crate = protobuf_crate_path(&self.customize),
+ t=self.field.type_(),
+ ),
+ );
+ }
+}
+
+pub(crate) fn write_extensions(
+ file: &FileDescriptor,
+ root_scope: &RootScope,
+ w: &mut CodeWriter,
+ customize: &CustomizeElemCtx,
+) {
+ if file.proto().extension.is_empty() {
+ return;
+ }
+
+ if customize.for_elem.lite_runtime.unwrap_or(false) {
+ w.write_line("");
+ w.comment("Extension generation with lite runtime is not supported");
+ return;
+ }
+
+ w.write_line("");
+ w.write_line("/// Extension fields");
+ w.pub_mod("exts", |w| {
+ for field in &file.proto().extension {
+ if field.type_() == field_descriptor_proto::Type::TYPE_GROUP {
+ continue;
+ }
+
+ w.write_line("");
+ ExtGen {
+ file,
+ root_scope,
+ field,
+ customize: customize.for_elem.clone(),
+ }
+ .write(w);
+ }
+ });
+}
diff --git a/src/gen/field/accessor.rs b/src/gen/field/accessor.rs
new file mode 100644
index 0000000..26f0ef4
--- /dev/null
+++ b/src/gen/field/accessor.rs
@@ -0,0 +1,241 @@
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::elem::FieldElemEnum;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::field::repeated::RepeatedField;
+use crate::gen::field::repeated::RepeatedFieldKind;
+use crate::gen::field::singular::SingularField;
+use crate::gen::field::singular::SingularFieldFlag;
+use crate::gen::field::FieldGen;
+use crate::gen::field::FieldKind;
+use crate::gen::field::MapField;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::oneof::OneofField;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::scope::WithScope;
+
+struct AccessorFn {
+ name: String,
+ // function type params after first underscore
+ type_params: Vec<String>,
+ callback_params: Vec<String>,
+}
+
+impl AccessorFn {
+ fn sig(&self) -> String {
+ let mut s = self.name.clone();
+ s.push_str("::<_");
+ for p in &self.type_params {
+ s.push_str(", ");
+ s.push_str(&p);
+ }
+ s.push_str(">");
+ s
+ }
+}
+
+impl FieldGen<'_> {
+ fn make_accessor_fns_lambda(&self) -> Vec<String> {
+ let message = self.proto_field.message.rust_name();
+ vec![
+ format!("|m: &{}| {{ &m.{} }}", message, self.rust_name),
+ format!("|m: &mut {}| {{ &mut m.{} }}", message, self.rust_name),
+ ]
+ }
+
+ fn make_accessor_fns_has_get_set(&self) -> Vec<String> {
+ let message = self.proto_field.message.rust_name();
+ vec![
+ format!("{}::{}", message, self.has_name()),
+ format!("{}::{}", message, self.rust_name),
+ format!("{}::{}", message, self.set_name()),
+ ]
+ }
+
+ fn make_accessor_fns_has_get_mut_set(&self) -> Vec<String> {
+ let message = self.proto_field.message.rust_name();
+ vec![
+ format!("{}::{}", message, self.has_name()),
+ format!("{}::{}", message, self.rust_name),
+ format!("{}::{}", message, self.mut_name()),
+ format!("{}::{}", message, self.set_name()),
+ ]
+ }
+
+ fn accessor_fn_map(&self, map_field: &MapField) -> AccessorFn {
+ let MapField { .. } = map_field;
+ AccessorFn {
+ name: "make_map_simpler_accessor".to_owned(),
+ type_params: vec![format!("_"), format!("_")],
+ callback_params: self.make_accessor_fns_lambda(),
+ }
+ }
+
+ fn accessor_fn_repeated(&self, repeated_field: &RepeatedField) -> AccessorFn {
+ let RepeatedField { .. } = repeated_field;
+ let name = match repeated_field.kind() {
+ RepeatedFieldKind::Vec => "make_vec_simpler_accessor",
+ };
+ AccessorFn {
+ name: name.to_owned(),
+ type_params: vec![format!("_")],
+ callback_params: self.make_accessor_fns_lambda(),
+ }
+ }
+
+ fn accessor_fn_oneof_enum(&self, oneof: &OneofField, en: &FieldElemEnum) -> AccessorFn {
+ let message = self.proto_field.message.rust_name();
+
+ let variant_path = oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file());
+
+ let getter = CodeWriter::with_no_error(|w| {
+ w.expr_block(
+ &format!(
+ "|message: &{}| match &message.{}",
+ message, oneof.oneof_field_name
+ ),
+ |w| {
+ w.case_expr(
+ &format!("::std::option::Option::Some({}(e))", variant_path),
+ "::std::option::Option::Some(*e)",
+ );
+ w.case_expr("_", "::std::option::Option::None");
+ },
+ );
+ });
+
+ let setter = CodeWriter::with_no_error(|w| {
+ w.expr_block(
+ &format!(
+ "|message: &mut {}, e: {}::EnumOrUnknown<{}>|",
+ message,
+ protobuf_crate_path(&self.customize),
+ en.enum_rust_type(&self.file_and_mod())
+ .to_code(&self.customize)
+ ),
+ |w| {
+ w.write_line(&format!(
+ "message.{} = ::std::option::Option::Some({}(e));",
+ oneof.oneof_field_name, variant_path
+ ));
+ },
+ )
+ });
+
+ let default = self.xxx_default_value_rust();
+
+ AccessorFn {
+ name: "make_oneof_enum_accessors".to_owned(),
+ type_params: vec![format!("_")],
+ callback_params: vec![getter, setter, default],
+ }
+ }
+
+ fn accessor_fn_singular_without_flag(&self, _elem: &FieldElem) -> AccessorFn {
+ AccessorFn {
+ name: "make_simpler_field_accessor".to_owned(),
+ type_params: vec![format!("_")],
+ callback_params: self.make_accessor_fns_lambda(),
+ }
+ }
+
+ fn accessor_fn_singular_with_flag(
+ &self,
+ elem: &FieldElem,
+ _option_kind: OptionKind,
+ ) -> AccessorFn {
+ match elem {
+ FieldElem::Message(m) => AccessorFn {
+ name: "make_message_field_accessor".to_owned(),
+ type_params: vec![format!("{}", m.rust_name_relative(&self.file_and_mod()))],
+ callback_params: self.make_accessor_fns_lambda(),
+ },
+ FieldElem::Primitive(..) | FieldElem::Enum(..) => AccessorFn {
+ name: "make_option_accessor".to_owned(),
+ type_params: vec!["_".to_owned()],
+ callback_params: self.make_accessor_fns_lambda(),
+ },
+ FieldElem::Group => {
+ unreachable!("no accessor for group field");
+ }
+ }
+ }
+
+ fn accessor_fn_oneof(&self, oneof: &OneofField) -> AccessorFn {
+ let OneofField { ref elem, .. } = oneof;
+
+ let reference = self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone());
+
+ if let FieldElem::Enum(en) = &oneof.elem {
+ return self.accessor_fn_oneof_enum(oneof, en);
+ }
+
+ if elem.is_copy() {
+ return AccessorFn {
+ name: "make_oneof_copy_has_get_set_simpler_accessors".to_owned(),
+ type_params: vec![format!("_")],
+ callback_params: self.make_accessor_fns_has_get_set(),
+ };
+ }
+
+ if let RustType::Message(name) = elem.rust_storage_elem_type(&reference) {
+ return AccessorFn {
+ name: "make_oneof_message_has_get_mut_set_accessor".to_owned(),
+ type_params: vec![format!("{}", name)],
+ callback_params: self.make_accessor_fns_has_get_mut_set(),
+ };
+ }
+
+ // string or bytes
+ AccessorFn {
+ name: "make_oneof_deref_has_get_set_simpler_accessor".to_owned(),
+ type_params: vec![format!("_")],
+ callback_params: self.make_accessor_fns_has_get_set(),
+ }
+ }
+
+ fn accessor_fn(&self) -> AccessorFn {
+ match self.kind {
+ FieldKind::Repeated(ref repeated_field) => self.accessor_fn_repeated(repeated_field),
+ FieldKind::Map(ref map_field) => self.accessor_fn_map(map_field),
+ FieldKind::Singular(SingularField {
+ ref elem,
+ flag: SingularFieldFlag::WithoutFlag,
+ }) => self.accessor_fn_singular_without_flag(elem),
+ FieldKind::Singular(SingularField {
+ ref elem,
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ }) => self.accessor_fn_singular_with_flag(elem, option_kind),
+ FieldKind::Oneof(ref oneof) => self.accessor_fn_oneof(oneof),
+ }
+ }
+
+ pub fn write_push_accessor(&self, fields_var: &str, w: &mut CodeWriter) {
+ let accessor_fn = self.accessor_fn();
+ w.write_line(&format!(
+ "{}.push({}::reflect::rt::v2::{}(",
+ fields_var,
+ protobuf_crate_path(&self.customize),
+ accessor_fn.sig()
+ ));
+ w.indented(|w| {
+ w.write_line(&format!("\"{}\",", self.proto_field.name()));
+ for callback in &accessor_fn.callback_params {
+ let callback_lines: Vec<&str> = callback.lines().collect();
+ for (i, callback_line) in callback_lines.iter().enumerate() {
+ let comma = if i == callback_lines.len() - 1 {
+ ","
+ } else {
+ ""
+ };
+ w.write_line(&format!("{}{}", callback_line, comma));
+ }
+ }
+ });
+ w.write_line("));");
+ }
+}
diff --git a/src/gen/field/elem.rs b/src/gen/field/elem.rs
new file mode 100644
index 0000000..fd06b4c
--- /dev/null
+++ b/src/gen/field/elem.rs
@@ -0,0 +1,319 @@
+use protobuf::descriptor::field_descriptor_proto::Type;
+use protobuf::reflect::RuntimeFieldType;
+use protobuf::rt::tag_size;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust_types_values::message_or_enum_to_rust_relative;
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::rust_types_values::RustValueTyped;
+use crate::gen::scope::EnumValueWithContext;
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageOrEnumWithScope;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+use crate::Customize;
+
+#[derive(Clone, Debug)]
+pub(crate) struct FieldElemEnum<'a> {
+ /// Enum default value variant, either from proto or from enum definition
+ default_value: EnumValueWithContext<'a>,
+}
+
+impl<'a> FieldElemEnum<'a> {
+ fn rust_name_relative(&self, reference: &FileAndMod) -> RustIdentWithPath {
+ message_or_enum_to_rust_relative(&self.default_value.en, reference)
+ }
+
+ pub(crate) fn enum_rust_type(&self, reference: &FileAndMod) -> RustType {
+ RustType::Enum(
+ self.rust_name_relative(reference),
+ self.default_value.rust_name(),
+ self.default_value.proto.proto().number(),
+ )
+ }
+
+ fn enum_or_unknown_rust_type(&self, reference: &FileAndMod) -> RustType {
+ RustType::EnumOrUnknown(
+ self.rust_name_relative(reference),
+ self.default_value.rust_name(),
+ self.default_value.proto.proto().number(),
+ )
+ }
+
+ pub(crate) fn default_value_rust_expr(&self, reference: &FileAndMod) -> RustIdentWithPath {
+ self.rust_name_relative(reference)
+ .to_path()
+ .with_ident(self.default_value.rust_name())
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FieldElemMessage<'a> {
+ pub message: MessageWithScope<'a>,
+}
+
+impl<'a> FieldElemMessage<'a> {
+ pub(crate) fn rust_name_relative(&self, reference: &FileAndMod) -> RustTypeMessage {
+ RustTypeMessage(message_or_enum_to_rust_relative(&self.message, reference))
+ }
+
+ fn rust_type(&self, reference: &FileAndMod) -> RustType {
+ RustType::Message(self.rust_name_relative(reference))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) enum FieldElem<'a> {
+ Primitive(Type, PrimitiveTypeVariant),
+ Message(FieldElemMessage<'a>),
+ Enum(FieldElemEnum<'a>),
+ Group,
+}
+
+pub(crate) enum HowToGetMessageSize {
+ Compute,
+ GetCached,
+}
+
+impl<'a> FieldElem<'a> {
+ pub(crate) fn proto_type(&self) -> Type {
+ match *self {
+ FieldElem::Primitive(t, ..) => t,
+ FieldElem::Group => Type::TYPE_GROUP,
+ FieldElem::Message(..) => Type::TYPE_MESSAGE,
+ FieldElem::Enum(..) => Type::TYPE_ENUM,
+ }
+ }
+
+ pub(crate) fn is_copy(&self) -> bool {
+ self.proto_type().is_copy()
+ }
+
+ pub(crate) fn rust_storage_elem_type(&self, reference: &FileAndMod) -> RustType {
+ match *self {
+ FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => t.rust_type(),
+ FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+ RustType::Chars
+ }
+ FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+ RustType::Bytes
+ }
+ FieldElem::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(),
+ FieldElem::Group => RustType::Group,
+ FieldElem::Message(ref m) => m.rust_type(reference),
+ FieldElem::Enum(ref en) => en.enum_or_unknown_rust_type(reference),
+ }
+ }
+
+ // Type of set_xxx function parameter type for singular fields
+ pub(crate) fn rust_set_xxx_param_type(&self, reference: &FileAndMod) -> RustType {
+ if let FieldElem::Enum(ref en) = *self {
+ en.enum_rust_type(reference)
+ } else {
+ self.rust_storage_elem_type(reference)
+ }
+ }
+
+ pub(crate) fn primitive_type_variant(&self) -> PrimitiveTypeVariant {
+ match self {
+ &FieldElem::Primitive(_, v) => v,
+ _ => PrimitiveTypeVariant::Default,
+ }
+ }
+
+ pub(crate) fn singular_field_size(
+ &self,
+ field_number: u32,
+ var: &RustValueTyped,
+ customize: &Customize,
+ ) -> String {
+ let tag_size = tag_size(field_number);
+ match self.proto_type().encoded_size() {
+ Some(data_size) => format!("{tag_size} + {data_size}"),
+ None => match self.proto_type() {
+ Type::TYPE_MESSAGE => panic!("not a single-liner"),
+ // We are not inlining `bytes_size` here,
+ // assuming the compiler is smart enough to do it for us.
+ // https://rust.godbolt.org/z/GrKa5zxq6
+ Type::TYPE_BYTES => format!(
+ "{}::rt::bytes_size({}, &{})",
+ protobuf_crate_path(customize),
+ field_number,
+ var.value
+ ),
+ Type::TYPE_STRING => format!(
+ "{}::rt::string_size({}, &{})",
+ protobuf_crate_path(customize),
+ field_number,
+ var.value
+ ),
+ Type::TYPE_ENUM => {
+ format!(
+ "{}::rt::int32_size({}, {}.value())",
+ protobuf_crate_path(customize),
+ field_number,
+ var.value,
+ )
+ }
+ _ => {
+ let param_type = match &var.rust_type {
+ RustType::Ref(t) => (**t).clone(),
+ t => t.clone(),
+ };
+ let f = match self.proto_type() {
+ Type::TYPE_SINT32 => "sint32_size",
+ Type::TYPE_SINT64 => "sint64_size",
+ Type::TYPE_INT32 => "int32_size",
+ Type::TYPE_INT64 => "int64_size",
+ Type::TYPE_UINT32 => "uint32_size",
+ Type::TYPE_UINT64 => "uint64_size",
+ t => unreachable!("unexpected type: {:?}", t),
+ };
+ format!(
+ "{}::rt::{f}({}, {})",
+ protobuf_crate_path(customize),
+ field_number,
+ var.into_type(param_type, customize).value
+ )
+ }
+ },
+ }
+ }
+
+ pub(crate) fn write_element_size(
+ &self,
+ field_number: u32,
+ item_var: &RustValueTyped,
+ how_to_get_message_size: HowToGetMessageSize,
+ sum_var: &str,
+ customize: &Customize,
+ w: &mut CodeWriter,
+ ) {
+ let tag_size = tag_size(field_number);
+ match self.proto_type() {
+ Type::TYPE_MESSAGE => {
+ match how_to_get_message_size {
+ HowToGetMessageSize::Compute => {
+ w.write_line(&format!("let len = {}.compute_size();", item_var.value))
+ }
+ HowToGetMessageSize::GetCached => w.write_line(&format!(
+ "let len = {}.cached_size() as u64;",
+ item_var.value
+ )),
+ }
+ w.write_line(&format!(
+ "{sum_var} += {tag_size} + {}::rt::compute_raw_varint64_size(len) + len;",
+ protobuf_crate_path(customize),
+ ));
+ }
+ _ => {
+ w.write_line(&format!(
+ "{sum_var} += {};",
+ self.singular_field_size(field_number, item_var, customize)
+ ));
+ }
+ }
+ }
+
+ pub(crate) fn write_write_element(
+ &self,
+ field_number: u32,
+ v: &RustValueTyped,
+ file_and_mod: &FileAndMod,
+ customize: &Customize,
+ os: &str,
+ w: &mut CodeWriter,
+ ) {
+ match self.proto_type() {
+ Type::TYPE_MESSAGE => {
+ let param_type = RustType::Ref(Box::new(self.rust_storage_elem_type(file_and_mod)));
+
+ w.write_line(&format!(
+ "{}::rt::write_message_field_with_cached_size({}, {}, {})?;",
+ protobuf_crate_path(customize),
+ field_number,
+ v.into_type(param_type, customize).value,
+ os
+ ));
+ }
+ _ => {
+ let param_type = self.proto_type().os_write_fn_param_type();
+ let os_write_fn_suffix = self.proto_type().protobuf_name();
+ w.write_line(&format!(
+ "{}.write_{}({}, {})?;",
+ os,
+ os_write_fn_suffix,
+ field_number,
+ v.into_type(param_type, customize).value
+ ));
+ }
+ }
+ }
+
+ pub(crate) fn read_one_liner(&self) -> String {
+ format!(
+ "{}?",
+ self.proto_type().read("is", self.primitive_type_variant())
+ )
+ }
+}
+
+pub(crate) fn field_elem<'a>(
+ field: &FieldWithContext,
+ root_scope: &'a RootScope<'a>,
+ customize: &Customize,
+) -> FieldElem<'a> {
+ if let RuntimeFieldType::Map(..) = field.field.runtime_field_type() {
+ unreachable!();
+ }
+
+ if field.field.proto().type_() == Type::TYPE_GROUP {
+ FieldElem::Group
+ } else if field.field.proto().has_type_name() {
+ let message_or_enum = root_scope
+ .find_message_or_enum(&ProtobufAbsPath::from(field.field.proto().type_name()));
+ match (field.field.proto().type_(), message_or_enum) {
+ (Type::TYPE_MESSAGE, MessageOrEnumWithScope::Message(message)) => {
+ FieldElem::Message(FieldElemMessage {
+ message: message.clone(),
+ })
+ }
+ (Type::TYPE_ENUM, MessageOrEnumWithScope::Enum(enum_with_scope)) => {
+ let default_value = if field.field.proto().has_default_value() {
+ enum_with_scope.value_by_name(field.field.proto().default_value())
+ } else {
+ enum_with_scope.values()[0].clone()
+ };
+ FieldElem::Enum(FieldElemEnum { default_value })
+ }
+ _ => panic!("unknown named type: {:?}", field.field.proto().type_()),
+ }
+ } else if field.field.proto().has_type() {
+ let tokio_for_bytes = customize.tokio_bytes.unwrap_or(false);
+ let tokio_for_string = customize.tokio_bytes_for_string.unwrap_or(false);
+
+ let elem = match field.field.proto().type_() {
+ Type::TYPE_STRING if tokio_for_string => {
+ FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes)
+ }
+ Type::TYPE_BYTES if tokio_for_bytes => {
+ FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes)
+ }
+ t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default),
+ };
+
+ elem
+ } else {
+ panic!(
+ "neither type_name, nor field_type specified for field: {}",
+ field.field.name()
+ );
+ }
+}
diff --git a/src/gen/field/mod.rs b/src/gen/field/mod.rs
new file mode 100644
index 0000000..d6a5e44
--- /dev/null
+++ b/src/gen/field/mod.rs
@@ -0,0 +1,1850 @@
+mod accessor;
+pub(crate) mod elem;
+mod option_kind;
+mod repeated;
+mod singular;
+mod tag;
+pub(crate) mod type_ext;
+
+use protobuf::descriptor::field_descriptor_proto::Type;
+use protobuf::descriptor::*;
+use protobuf::reflect::ReflectValueRef;
+use protobuf::reflect::RuntimeFieldType;
+use protobuf::reflect::Syntax;
+use protobuf::rt;
+use protobuf::rt::WireType;
+use protobuf_parse::camel_case;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_field;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::field::elem::field_elem;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::elem::FieldElemEnum;
+use crate::gen::field::elem::HowToGetMessageSize;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::field::repeated::RepeatedField;
+use crate::gen::field::singular::SingularField;
+use crate::gen::field::singular::SingularFieldFlag;
+use crate::gen::field::tag::make_tag;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::map::map_entry;
+use crate::gen::oneof::OneofField;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::quote::quote_escape_bytes;
+use crate::gen::rust::quote::quote_escape_str;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::rust_types_values::RustValueTyped;
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+
+fn field_type_protobuf_name<'a>(field: &'a FieldDescriptorProto) -> &'a str {
+ if field.has_type_name() {
+ field.type_name()
+ } else {
+ field.type_().protobuf_name()
+ }
+}
+
+#[derive(Clone)]
+pub struct MapField<'a> {
+ _message: MessageWithScope<'a>,
+ key: FieldElem<'a>,
+ value: FieldElem<'a>,
+}
+
+#[derive(Clone)]
+pub(crate) enum FieldKind<'a> {
+ // optional or required
+ Singular(SingularField<'a>),
+ // repeated except map
+ Repeated(RepeatedField<'a>),
+ // map
+ Map(MapField<'a>),
+ // part of oneof
+ Oneof(OneofField<'a>),
+}
+
+impl<'a> FieldKind<'a> {
+ pub(crate) fn default(
+ &self,
+ customize: &Customize,
+ reference: &FileAndMod,
+ const_expr: bool,
+ ) -> String {
+ match self {
+ FieldKind::Singular(s) => s.default_value(customize, reference, const_expr),
+ FieldKind::Repeated(r) => r.default(),
+ FieldKind::Oneof(..) => EXPR_NONE.to_owned(),
+ FieldKind::Map(..) => panic!("map fields cannot have field value"),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) enum SingularOrOneofField<'a> {
+ Singular(SingularField<'a>),
+ Oneof(OneofField<'a>),
+}
+
+impl<'a> SingularOrOneofField<'a> {
+ fn elem(&self) -> &FieldElem {
+ match self {
+ SingularOrOneofField::Singular(SingularField { ref elem, .. }) => elem,
+ SingularOrOneofField::Oneof(OneofField { ref elem, .. }) => elem,
+ }
+ }
+
+ // Type of `xxx` function for singular type.
+ pub(crate) fn getter_return_type(&self, reference: &FileAndMod) -> RustType {
+ let elem = self.elem();
+ if let FieldElem::Enum(ref en) = elem {
+ en.enum_rust_type(reference)
+ } else if elem.is_copy() {
+ elem.rust_storage_elem_type(reference)
+ } else {
+ elem.rust_storage_elem_type(reference).ref_type()
+ }
+ }
+}
+
+// Representation of map entry: key type and value type
+#[derive(Clone, Debug)]
+pub struct EntryKeyValue<'a>(FieldElem<'a>, FieldElem<'a>);
+
+#[derive(Clone)]
+pub(crate) struct FieldGen<'a> {
+ syntax: Syntax,
+ pub proto_field: FieldWithContext<'a>,
+ // field name in generated code
+ pub rust_name: RustIdent,
+ pub proto_type: Type,
+ wire_type: WireType,
+ pub kind: FieldKind<'a>,
+ pub generate_accessors: bool,
+ pub generate_getter: bool,
+ customize: Customize,
+ path: Vec<i32>,
+ info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> FieldGen<'a> {
+ pub(crate) fn parse(
+ field: FieldWithContext<'a>,
+ root_scope: &'a RootScope<'a>,
+ parent_customize: &CustomizeElemCtx<'a>,
+ path: Vec<i32>,
+ info: Option<&'a SourceCodeInfo>,
+ ) -> anyhow::Result<FieldGen<'a>> {
+ let customize = parent_customize
+ .child(
+ &customize_from_rustproto_for_field(field.field.proto().options.get_or_default()),
+ &field.field,
+ )
+ .for_elem;
+
+ let syntax = field.message.scope.file_scope.syntax();
+
+ let field_may_have_custom_default_value = syntax == Syntax::Proto2
+ && field.field.proto().label() != field_descriptor_proto::Label::LABEL_REPEATED
+ && field.field.proto().type_() != Type::TYPE_MESSAGE;
+
+ let generate_accessors = customize
+ .generate_accessors
+ .unwrap_or(field_may_have_custom_default_value)
+ || field.is_oneof();
+
+ let default_generate_getter = generate_accessors || field_may_have_custom_default_value;
+ let generate_getter =
+ customize.generate_getter.unwrap_or(default_generate_getter) || field.is_oneof();
+
+ let kind = match field.field.runtime_field_type() {
+ RuntimeFieldType::Map(..) => {
+ let message = root_scope
+ .find_message(&ProtobufAbsPath::from(field.field.proto().type_name()));
+
+ let (key, value) = map_entry(&message).unwrap();
+
+ let key = field_elem(&key, root_scope, &customize);
+ let value = field_elem(&value, root_scope, &customize);
+
+ FieldKind::Map(MapField {
+ _message: message,
+ key,
+ value,
+ })
+ }
+ RuntimeFieldType::Repeated(..) => {
+ let elem = field_elem(&field, root_scope, &customize);
+
+ FieldKind::Repeated(RepeatedField {
+ elem,
+ packed: field.field.proto().options.get_or_default().packed(),
+ })
+ }
+ RuntimeFieldType::Singular(..) => {
+ let elem = field_elem(&field, root_scope, &customize);
+
+ if let Some(oneof) = field.oneof() {
+ FieldKind::Oneof(OneofField::parse(&oneof, &field.field, elem, root_scope))
+ } else {
+ let flag = if field.message.scope.file_scope.syntax() == Syntax::Proto3
+ && field.field.proto().type_() != field_descriptor_proto::Type::TYPE_MESSAGE
+ && !field.field.proto().proto3_optional()
+ {
+ SingularFieldFlag::WithoutFlag
+ } else {
+ let required = field.field.proto().label()
+ == field_descriptor_proto::Label::LABEL_REQUIRED;
+ let option_kind = match field.field.proto().type_() {
+ field_descriptor_proto::Type::TYPE_MESSAGE => OptionKind::MessageField,
+ _ => OptionKind::Option,
+ };
+
+ SingularFieldFlag::WithFlag {
+ required,
+ option_kind,
+ }
+ };
+ FieldKind::Singular(SingularField { elem, flag })
+ }
+ }
+ };
+
+ Ok(FieldGen {
+ syntax: field.message.message.file_descriptor().syntax(),
+ rust_name: rust_field_name_for_protobuf_field_name(&field.field.name()),
+ proto_type: field.field.proto().type_(),
+ wire_type: WireType::for_type(field.field.proto().type_()),
+ proto_field: field,
+ kind,
+ generate_accessors,
+ generate_getter,
+ customize,
+ path,
+ info,
+ })
+ }
+
+ // for message level
+ fn file_and_mod(&self) -> FileAndMod {
+ self.proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone())
+ }
+
+ fn tag_size(&self) -> u32 {
+ rt::tag_size(self.proto_field.number() as u32) as u32
+ }
+
+ fn is_singular(&self) -> bool {
+ match self.kind {
+ FieldKind::Singular(..) => true,
+ _ => false,
+ }
+ }
+
+ fn is_repeated_packed(&self) -> bool {
+ match self.kind {
+ FieldKind::Repeated(RepeatedField { packed: true, .. }) => true,
+ _ => false,
+ }
+ }
+
+ pub(crate) fn elem(&self) -> &FieldElem {
+ match self.kind {
+ FieldKind::Singular(SingularField { ref elem, .. }) => &elem,
+ FieldKind::Repeated(RepeatedField { ref elem, .. }) => &elem,
+ FieldKind::Oneof(OneofField { ref elem, .. }) => &elem,
+ FieldKind::Map(..) => unreachable!(),
+ }
+ }
+
+ // type of field in struct
+ pub(crate) fn full_storage_type(&self, reference: &FileAndMod) -> RustType {
+ match self.kind {
+ FieldKind::Repeated(ref repeated) => repeated.rust_type(reference),
+ FieldKind::Map(MapField {
+ ref key, ref value, ..
+ }) => RustType::HashMap(
+ Box::new(key.rust_storage_elem_type(reference)),
+ Box::new(value.rust_storage_elem_type(reference)),
+ ),
+ FieldKind::Singular(ref singular) => singular.rust_storage_type(reference),
+ FieldKind::Oneof(..) => unreachable!(),
+ }
+ }
+
+ // type of `v` in `for v in field`
+ fn full_storage_iter_elem_type(&self, reference: &FileAndMod) -> RustType {
+ if let FieldKind::Oneof(ref oneof) = self.kind {
+ oneof.elem.rust_storage_elem_type(reference)
+ } else {
+ self.full_storage_type(reference).iter_elem_type()
+ }
+ }
+
+ // suffix `xxx` as in `os.write_xxx_no_tag(..)`
+ fn os_write_fn_suffix(&self) -> &str {
+ self.proto_type.protobuf_name()
+ }
+
+ fn os_write_fn_suffix_with_unknown_for_enum(&self) -> &str {
+ if self.proto_type == field_descriptor_proto::Type::TYPE_ENUM {
+ "enum_or_unknown"
+ } else {
+ self.os_write_fn_suffix()
+ }
+ }
+
+ // for field `foo`, type of param of `fn set_foo(..)`
+ fn set_xxx_param_type(&self, reference: &FileAndMod) -> RustType {
+ match self.kind {
+ FieldKind::Singular(SingularField { ref elem, .. })
+ | FieldKind::Oneof(OneofField { ref elem, .. }) => {
+ elem.rust_set_xxx_param_type(reference)
+ }
+ FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference),
+ }
+ }
+
+ // for field `foo`, return type if `fn take_foo(..)`
+ fn take_xxx_return_type(&self, reference: &FileAndMod) -> RustType {
+ self.set_xxx_param_type(reference)
+ }
+
+ // for field `foo`, return type of `fn mut_foo(..)`
+ fn mut_xxx_return_type(&self, reference: &FileAndMod) -> RustType {
+ RustType::Ref(Box::new(match self.kind {
+ FieldKind::Singular(SingularField { ref elem, .. })
+ | FieldKind::Oneof(OneofField { ref elem, .. }) => {
+ elem.rust_storage_elem_type(reference)
+ }
+ FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference),
+ }))
+ }
+
+ // for field `foo`, return type of `fn foo(..)`
+ fn getter_return_type(&self) -> RustType {
+ let reference = self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone());
+ match &self.kind {
+ FieldKind::Singular(s) => {
+ SingularOrOneofField::Singular(s.clone()).getter_return_type(&reference)
+ }
+ FieldKind::Oneof(o) => {
+ SingularOrOneofField::Oneof(o.clone()).getter_return_type(&reference)
+ }
+ FieldKind::Repeated(RepeatedField { ref elem, .. }) => RustType::Ref(Box::new(
+ RustType::Slice(Box::new(elem.rust_storage_elem_type(&reference))),
+ )),
+ FieldKind::Map(..) => RustType::Ref(Box::new(self.full_storage_type(&reference))),
+ }
+ }
+
+ // elem data is not stored in heap
+ pub(crate) fn elem_type_is_copy(&self) -> bool {
+ self.proto_type.is_copy()
+ }
+
+ fn defaut_value_from_proto_float(f: f64, type_name: &str) -> String {
+ if f.is_nan() {
+ format!("::std::{}::NAN", type_name)
+ } else if f.is_infinite() {
+ if f > 0.0 {
+ format!("::std::{}::INFINITY", type_name)
+ } else {
+ format!("::std::{}::NEG_INFINITY", type_name)
+ }
+ } else {
+ format!("{:?}{}", f, type_name)
+ }
+ }
+
+ fn singular_or_oneof_default_value_from_proto(&self, elem: &FieldElem) -> Option<String> {
+ if !self.proto_field.field.proto().has_default_value() {
+ return None;
+ }
+
+ let default_value = self.proto_field.field.singular_default_value();
+ Some(match default_value {
+ ReflectValueRef::Bool(b) => format!("{}", b),
+ ReflectValueRef::I32(v) => format!("{}i32", v),
+ ReflectValueRef::I64(v) => format!("{}i64", v),
+ ReflectValueRef::U32(v) => format!("{}u32", v),
+ ReflectValueRef::U64(v) => format!("{}u64", v),
+ ReflectValueRef::String(v) => quote_escape_str(v),
+ ReflectValueRef::Bytes(v) => quote_escape_bytes(v),
+ ReflectValueRef::F32(v) => Self::defaut_value_from_proto_float(v as f64, "f32"),
+ ReflectValueRef::F64(v) => Self::defaut_value_from_proto_float(v as f64, "f64"),
+ ReflectValueRef::Enum(_e, _v) => {
+ if let &FieldElem::Enum(ref e) = elem {
+ format!("{}", e.default_value_rust_expr(&self.file_and_mod()))
+ } else {
+ unreachable!()
+ }
+ }
+ t => panic!("default value is not implemented for type: {:?}", t),
+ })
+ }
+
+ fn default_value_from_proto(&self) -> Option<String> {
+ match self.kind {
+ FieldKind::Oneof(OneofField { ref elem, .. })
+ | FieldKind::Singular(SingularField { ref elem, .. }) => {
+ self.singular_or_oneof_default_value_from_proto(elem)
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn default_value_from_proto_typed(&self) -> Option<RustValueTyped> {
+ self.default_value_from_proto().map(|v| {
+ let default_value_type = match self.proto_type {
+ field_descriptor_proto::Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)),
+ field_descriptor_proto::Type::TYPE_BYTES => {
+ RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::u8()))))
+ }
+ _ => self.full_storage_iter_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ };
+
+ RustValueTyped {
+ value: v,
+ rust_type: default_value_type,
+ }
+ })
+ }
+
+ // default value to be returned from `fn xxx` for field `xxx`.
+ fn xxx_default_value_rust(&self) -> String {
+ match self.kind {
+ FieldKind::Singular(..) | FieldKind::Oneof(..) => {
+ self.default_value_from_proto().unwrap_or_else(|| {
+ self.getter_return_type()
+ .default_value(&self.customize, false)
+ })
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ // default to be assigned to field
+ fn element_default_value_rust(&self) -> RustValueTyped {
+ match self.kind {
+ FieldKind::Singular(..) | FieldKind::Oneof(..) => {
+ self.default_value_from_proto_typed().unwrap_or_else(|| {
+ self.elem()
+ .rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .default_value_typed(&self.customize, false)
+ })
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ pub(crate) fn reconstruct_def(&self) -> String {
+ let prefix = match (self.proto_field.field.proto().label(), self.syntax) {
+ (field_descriptor_proto::Label::LABEL_REPEATED, _) => "repeated ",
+ (_, Syntax::Proto3) => "",
+ (field_descriptor_proto::Label::LABEL_OPTIONAL, _) => "optional ",
+ (field_descriptor_proto::Label::LABEL_REQUIRED, _) => "required ",
+ };
+ format!(
+ "{}{} {} = {}",
+ prefix,
+ field_type_protobuf_name(self.proto_field.field.proto()),
+ self.proto_field.name(),
+ self.proto_field.number()
+ )
+ }
+
+ pub(crate) fn write_clear(&self, w: &mut CodeWriter) {
+ match self.kind {
+ FieldKind::Oneof(ref o) => {
+ w.write_line(&format!(
+ "self.{} = ::std::option::Option::None;",
+ o.oneof_field_name
+ ));
+ }
+ _ => {
+ let clear_expr = self
+ .full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .clear(&self.self_field(), &self.customize);
+ w.write_line(&format!("{};", clear_expr));
+ }
+ }
+ }
+
+ // output code that writes single element to stream
+ pub(crate) fn write_write_element(
+ &self,
+ elem: &FieldElem,
+ w: &mut CodeWriter,
+ os: &str,
+ v: &RustValueTyped,
+ ) {
+ assert!(!self.is_repeated_packed());
+
+ elem.write_write_element(
+ self.proto_field.number() as u32,
+ v,
+ &self.file_and_mod(),
+ &self.customize,
+ os,
+ w,
+ );
+ }
+
+ fn self_field(&self) -> String {
+ format!("self.{}", self.rust_name)
+ }
+
+ fn self_field_is_some(&self) -> String {
+ assert!(self.is_singular());
+ format!("{}.is_some()", self.self_field())
+ }
+
+ fn self_field_is_none(&self) -> String {
+ assert!(self.is_singular());
+ format!("{}.is_none()", self.self_field())
+ }
+
+ // field data viewed as Option
+ fn self_field_as_option(&self, elem: &FieldElem, option_kind: OptionKind) -> RustValueTyped {
+ match self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ) {
+ RustType::Option(ref e) if e.is_copy() => {
+ return RustType::Option(e.clone()).value(self.self_field());
+ }
+ _ => {}
+ };
+
+ let as_option_type = option_kind.as_ref_type(
+ elem.rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ );
+
+ as_option_type.value(format!("{}.as_ref()", self.self_field()))
+ }
+
+ pub(crate) fn write_struct_field(&self, w: &mut CodeWriter) {
+ if self.proto_type == field_descriptor_proto::Type::TYPE_GROUP {
+ w.comment(&format!("{}: <group>", &self.rust_name));
+ } else {
+ w.all_documentation(self.info, &self.path);
+
+ write_protoc_insertion_point_for_field(w, &self.customize, &self.proto_field.field);
+ w.field_decl_vis(
+ Visibility::Public,
+ &self.rust_name.to_string(),
+ &self
+ .full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .to_code(&self.customize),
+ );
+ }
+ }
+
+ fn write_if_let_self_field_is_some<F>(&self, s: &SingularField, w: &mut CodeWriter, cb: F)
+ where
+ F: Fn(&RustValueTyped, &mut CodeWriter),
+ {
+ match s {
+ SingularField {
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ ref elem,
+ } => {
+ let var = "v";
+ let ref_prefix = match elem
+ .rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .is_copy()
+ {
+ true => "",
+ false => "",
+ };
+ let as_option = self.self_field_as_option(elem, *option_kind);
+ w.if_let_stmt(
+ &format!("Some({}{})", ref_prefix, var),
+ &as_option.value,
+ |w| {
+ let v = RustValueTyped {
+ value: var.to_owned(),
+ rust_type: as_option.rust_type.elem_type(),
+ };
+ cb(&v, w);
+ },
+ );
+ }
+ SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ref elem,
+ } => match *elem {
+ FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..)
+ | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => {
+ w.if_stmt(format!("!{}.is_empty()", self.self_field()), |w| {
+ let v = RustValueTyped {
+ value: self.self_field(),
+ rust_type: self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ };
+ cb(&v, w);
+ });
+ }
+ _ => {
+ w.if_stmt(
+ format!(
+ "{} != {}",
+ self.self_field(),
+ self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone())
+ )
+ .default_value(&self.customize, false)
+ ),
+ |w| {
+ let v = RustValueTyped {
+ value: self.self_field(),
+ rust_type: self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ };
+ cb(&v, w);
+ },
+ );
+ }
+ },
+ }
+ }
+
+ pub(crate) fn write_if_self_field_is_none<F>(&self, w: &mut CodeWriter, cb: F)
+ where
+ F: Fn(&mut CodeWriter),
+ {
+ let self_field_is_none = self.self_field_is_none();
+ w.if_stmt(self_field_is_none, cb)
+ }
+
+ // repeated or singular
+ pub(crate) fn write_for_self_field<F>(&self, w: &mut CodeWriter, varn: &str, cb: F)
+ where
+ F: Fn(&mut CodeWriter, &RustType),
+ {
+ let file_and_mod = self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone());
+
+ match &self.kind {
+ FieldKind::Oneof(oneof_field) => {
+ let cond = format!(
+ "Some({}(ref {}))",
+ oneof_field.variant_path(&file_and_mod.relative_mod),
+ varn
+ );
+ w.if_let_stmt(
+ &cond,
+ &format!("self.{}", oneof_field.oneof_field_name),
+ |w| cb(w, &oneof_field.elem.rust_storage_elem_type(&file_and_mod)),
+ )
+ }
+ _ => {
+ let v_type = self.full_storage_iter_elem_type(&file_and_mod);
+ let self_field = self.self_field();
+ w.for_stmt(&format!("&{}", self_field), varn, |w| cb(w, &v_type));
+ }
+ }
+ }
+
+ fn write_self_field_assign(&self, w: &mut CodeWriter, value: &str) {
+ let self_field = self.self_field();
+ w.write_line(&format!("{} = {};", self_field, value));
+ }
+
+ fn write_self_field_assign_some(&self, w: &mut CodeWriter, s: &SingularField, value: &str) {
+ match s {
+ &SingularField {
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ ..
+ } => {
+ self.write_self_field_assign(w, &option_kind.wrap_value(value, &self.customize));
+ }
+ &SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ..
+ } => {
+ self.write_self_field_assign(w, value);
+ }
+ }
+ }
+
+ fn write_self_field_assign_value_singular(
+ &self,
+ w: &mut CodeWriter,
+ s: &SingularField,
+ value: &RustValueTyped,
+ ) {
+ let SingularField { ref elem, ref flag } = s;
+ let converted = value.into_type(
+ elem.rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .clone(),
+ &self.customize,
+ );
+ let wrapped = match flag {
+ SingularFieldFlag::WithoutFlag => converted.value,
+ SingularFieldFlag::WithFlag { option_kind, .. } => {
+ option_kind.wrap_value(&converted.value, &self.customize)
+ }
+ };
+ self.write_self_field_assign(w, &wrapped);
+ }
+
+ fn write_self_field_assign_value(&self, w: &mut CodeWriter, value: &RustValueTyped) {
+ match self.kind {
+ FieldKind::Repeated(..) | FieldKind::Map(..) => {
+ let converted = value.into_type(
+ self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ &self.customize,
+ );
+ self.write_self_field_assign(w, &converted.value);
+ }
+ FieldKind::Singular(ref s) => {
+ self.write_self_field_assign_value_singular(w, s, value);
+ }
+ FieldKind::Oneof(..) => unreachable!(),
+ }
+ }
+
+ fn write_self_field_assign_default(
+ &self,
+ field_kind: &SingularOrOneofField,
+ w: &mut CodeWriter,
+ ) {
+ match field_kind {
+ SingularOrOneofField::Oneof(oneof) => {
+ w.write_line(format!(
+ "self.{} = ::std::option::Option::Some({}({}))",
+ oneof.oneof_field_name,
+ oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+ // TODO: default from .proto is not needed here (?)
+ self.element_default_value_rust()
+ .into_type(
+ self.full_storage_iter_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone())
+ ),
+ &self.customize
+ )
+ .value
+ ));
+ }
+ SingularOrOneofField::Singular(singular) => {
+ // Note it is different from C++ protobuf, where field is initialized
+ // with default value
+ match singular.flag {
+ SingularFieldFlag::WithFlag { option_kind, .. } => match option_kind {
+ OptionKind::MessageField => {
+ let self_field = self.self_field();
+ w.write_line(&format!("{}.set_default();", self_field));
+ }
+ _ => {
+ self.write_self_field_assign_some(
+ w,
+ singular,
+ &self
+ .elem()
+ .rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .default_value_typed(&self.customize, false)
+ .into_type(
+ singular.elem.rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ &self.customize,
+ )
+ .value,
+ );
+ }
+ },
+ SingularFieldFlag::WithoutFlag => unimplemented!(),
+ }
+ }
+ }
+ }
+
+ fn self_field_vec_packed_size(&self) -> String {
+ let fn_name = match self.proto_type {
+ Type::TYPE_ENUM => "vec_packed_enum_or_unknown_size",
+ Type::TYPE_SINT32 => "vec_packed_sint32_size",
+ Type::TYPE_SINT64 => "vec_packed_sint64_size",
+ Type::TYPE_INT32 => "vec_packed_int32_size",
+ Type::TYPE_INT64 => "vec_packed_int64_size",
+ Type::TYPE_UINT32 => "vec_packed_uint32_size",
+ Type::TYPE_UINT64 => "vec_packed_uint64_size",
+ Type::TYPE_BOOL => "vec_packed_bool_size",
+ Type::TYPE_FIXED32 => "vec_packed_fixed32_size",
+ Type::TYPE_FIXED64 => "vec_packed_fixed64_size",
+ Type::TYPE_SFIXED32 => "vec_packed_sfixed32_size",
+ Type::TYPE_SFIXED64 => "vec_packed_sfixed64_size",
+ Type::TYPE_FLOAT => "vec_packed_float_size",
+ Type::TYPE_DOUBLE => "vec_packed_double_size",
+ t => unreachable!("{:?}", t),
+ };
+ format!(
+ "{}::rt::{fn_name}({}, &{})",
+ protobuf_crate_path(&self.customize),
+ self.proto_field.number(),
+ self.self_field()
+ )
+ }
+
+ pub(crate) fn clear_field_func(&self) -> String {
+ format!("clear_{}", self.rust_name)
+ }
+
+ fn write_merge_from_field_message_string_bytes_repeated(
+ &self,
+ r: &RepeatedField,
+ w: &mut CodeWriter,
+ ) {
+ let read_fn = match &r.elem {
+ FieldElem::Message(..) => "read_message",
+ FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::Default) => "read_string",
+ FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+ "read_tokio_chars"
+ }
+ FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::Default) => "read_bytes",
+ FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+ "read_tokio_bytes"
+ }
+ _ => unreachable!("for field {}", self.proto_field.field),
+ };
+ w.write_line(&format!("self.{}.push(is.{}()?);", self.rust_name, read_fn,));
+ }
+
+ fn tag_with_wire_type(&self, wire_type: WireType) -> u32 {
+ make_tag(self.proto_field.number() as u32, wire_type)
+ }
+
+ fn tag(&self) -> u32 {
+ self.tag_with_wire_type(self.wire_type)
+ }
+
+ // Write `merge_from` part for this oneof field
+ fn write_merge_from_oneof_case_block(&self, o: &OneofField, w: &mut CodeWriter) {
+ w.case_block(&format!("{}", self.tag()), |w| {
+ let typed = RustValueTyped {
+ value: format!(
+ "{}?",
+ self.proto_type.read("is", o.elem.primitive_type_variant())
+ ),
+ rust_type: self.full_storage_iter_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ };
+
+ let maybe_boxed = if o.boxed {
+ typed.boxed(&self.customize)
+ } else {
+ typed
+ };
+
+ w.write_line(&format!(
+ "self.{} = ::std::option::Option::Some({}({}));",
+ o.oneof_field_name,
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+ maybe_boxed.value
+ ));
+ })
+ }
+
+ // Write `merge_from` part for this map field
+ fn write_merge_from_map_case_block(&self, map: &MapField, w: &mut CodeWriter) {
+ let MapField { key, value, .. } = map;
+ w.case_block(&format!("{}", self.tag()), |w| {
+ w.write_line(&format!("let len = is.read_raw_varint32()?;",));
+ w.write_line(&format!("let old_limit = is.push_limit(len as u64)?;"));
+ w.write_line(&format!(
+ "let mut key = ::std::default::Default::default();"
+ ));
+ w.write_line(&format!(
+ "let mut value = ::std::default::Default::default();"
+ ));
+ w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| {
+ w.match_block("tag", |w| {
+ let key_tag = make_tag(1, WireType::for_type(key.proto_type()));
+ let value_tag = make_tag(2, WireType::for_type(value.proto_type()));
+ w.case_expr(
+ &format!("{key_tag}"),
+ &format!("key = {read}", read = key.read_one_liner()),
+ );
+ w.case_expr(
+ &format!("{value_tag}"),
+ &format!("value = {read}", read = value.read_one_liner()),
+ );
+ w.case_expr(
+ "_",
+ &format!(
+ "{protobuf_crate}::rt::skip_field_for_tag(tag, is)?",
+ protobuf_crate = protobuf_crate_path(&self.customize)
+ ),
+ );
+ });
+ });
+ w.write_line(&format!("is.pop_limit(old_limit);"));
+ w.write_line(&format!(
+ "{field}.insert(key, value);",
+ field = self.self_field()
+ ));
+ });
+ }
+
+ // Write `merge_from` part for this singular field
+ fn write_merge_from_singular_case_block(&self, s: &SingularField, w: &mut CodeWriter) {
+ w.case_block(&format!("{}", self.tag()), |w| match s.elem {
+ FieldElem::Message(..) => {
+ w.write_line(&format!(
+ "{}::rt::read_singular_message_into_field(is, &mut self.{})?;",
+ protobuf_crate_path(&self.customize),
+ self.rust_name,
+ ));
+ }
+ _ => {
+ let read_proc = s.elem.read_one_liner();
+ self.write_self_field_assign_some(w, s, &read_proc);
+ }
+ })
+ }
+
+ // Write `merge_from` part for this repeated field
+ fn write_merge_from_repeated_case_block(&self, w: &mut CodeWriter) {
+ let field = match self.kind {
+ FieldKind::Repeated(ref field) => field,
+ _ => panic!(),
+ };
+
+ match field.elem {
+ FieldElem::Message(..)
+ | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..)
+ | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => {
+ w.case_block(&format!("{}", self.tag()), |w| {
+ self.write_merge_from_field_message_string_bytes_repeated(field, w);
+ })
+ }
+ FieldElem::Enum(..) => {
+ w.case_block(
+ &format!("{}", self.tag_with_wire_type(WireType::Varint)),
+ |w| {
+ w.write_line(&format!(
+ "self.{}.push(is.read_enum_or_unknown()?);",
+ self.rust_name,
+ ));
+ },
+ );
+ w.case_block(
+ &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)),
+ |w| {
+ w.write_line(&format!(
+ "{}::rt::read_repeated_packed_enum_or_unknown_into(is, &mut self.{})?",
+ protobuf_crate_path(&self.customize),
+ self.rust_name,
+ ));
+ },
+ );
+ }
+ _ => {
+ assert_ne!(self.wire_type, WireType::LengthDelimited);
+ w.case_block(
+ &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)),
+ |w| {
+ w.write_line(&format!(
+ "is.read_repeated_packed_{}_into(&mut self.{})?;",
+ self.proto_type.protobuf_name(),
+ self.rust_name
+ ));
+ },
+ );
+ w.case_block(&format!("{}", self.tag()), |w| {
+ w.write_line(&format!(
+ "self.{}.push(is.read_{}()?);",
+ self.rust_name,
+ self.proto_type.protobuf_name(),
+ ));
+ });
+ }
+ }
+ }
+
+ /// Write `merge_from` part for this field
+ pub(crate) fn write_merge_from_field_case_block(&self, w: &mut CodeWriter) {
+ match &self.kind {
+ FieldKind::Oneof(oneof) => self.write_merge_from_oneof_case_block(oneof, w),
+ FieldKind::Map(map) => self.write_merge_from_map_case_block(map, w),
+ FieldKind::Singular(ref s) => self.write_merge_from_singular_case_block(s, w),
+ FieldKind::Repeated(..) => self.write_merge_from_repeated_case_block(w),
+ }
+ }
+
+ pub(crate) fn write_element_size(
+ &self,
+ elem: &FieldElem,
+ w: &mut CodeWriter,
+ item_var: &RustValueTyped,
+ sum_var: &str,
+ ) {
+ assert!(!self.is_repeated_packed());
+
+ elem.write_element_size(
+ self.proto_field.number() as u32,
+ item_var,
+ HowToGetMessageSize::Compute,
+ sum_var,
+ &self.customize,
+ w,
+ );
+ }
+
+ fn write_write_map_field(
+ &self,
+ key: &FieldElem,
+ value: &FieldElem,
+ os: &str,
+ w: &mut CodeWriter,
+ ) {
+ self.for_each_map_entry(key, value, w, |k, v, w| {
+ w.write_line("let mut entry_size = 0;");
+ key.write_element_size(
+ 1,
+ k,
+ HowToGetMessageSize::GetCached,
+ "entry_size",
+ &self.customize,
+ w,
+ );
+ value.write_element_size(
+ 2,
+ v,
+ HowToGetMessageSize::GetCached,
+ "entry_size",
+ &self.customize,
+ w,
+ );
+ w.write_line(&format!(
+ "{os}.write_raw_varint32({tag})?; // Tag.",
+ tag = make_tag(self.proto_field.number() as u32, WireType::LengthDelimited),
+ ));
+ w.write_line(&format!("{os}.write_raw_varint32(entry_size as u32)?;",));
+ key.write_write_element(1, k, &self.file_and_mod(), &self.customize, os, w);
+ value.write_write_element(2, v, &self.file_and_mod(), &self.customize, os, w);
+ });
+ }
+
+ pub(crate) fn write_message_write_field(&self, os: &str, w: &mut CodeWriter) {
+ match &self.kind {
+ FieldKind::Singular(s @ SingularField { elem, .. }) => {
+ self.write_if_let_self_field_is_some(s, w, |v, w| {
+ self.write_write_element(&elem, w, os, &v);
+ });
+ }
+ FieldKind::Repeated(RepeatedField {
+ packed: false,
+ elem,
+ ..
+ }) => {
+ self.write_for_self_field(w, "v", |w, v_type| {
+ let v = RustValueTyped {
+ value: "v".to_owned(),
+ rust_type: v_type.clone(),
+ };
+ self.write_write_element(elem, w, "os", &v);
+ });
+ }
+ FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
+ w.write_line(&format!(
+ "os.write_repeated_packed_{}({}, &{})?;",
+ self.os_write_fn_suffix_with_unknown_for_enum(),
+ self.proto_field.number(),
+ self.self_field()
+ ));
+ }
+ FieldKind::Map(MapField { key, value, .. }) => {
+ self.write_write_map_field(key, value, os, w)
+ }
+ FieldKind::Oneof(..) => unreachable!(),
+ };
+ }
+
+ fn for_each_map_entry(
+ &self,
+ key: &FieldElem,
+ value: &FieldElem,
+ w: &mut CodeWriter,
+ cb: impl FnOnce(&RustValueTyped, &RustValueTyped, &mut CodeWriter),
+ ) {
+ w.for_stmt(&format!("&{}", self.self_field()), "(k, v)", move |w| {
+ let k = RustValueTyped {
+ value: "k".to_owned(),
+ rust_type: key.rust_storage_elem_type(&self.file_and_mod()).wrap_ref(),
+ };
+ let v = RustValueTyped {
+ value: "v".to_owned(),
+ rust_type: value
+ .rust_storage_elem_type(&self.file_and_mod())
+ .wrap_ref(),
+ };
+ cb(&k, &v, w)
+ });
+ }
+
+ fn write_compute_map_field_size(
+ &self,
+ sum_var: &str,
+ key: &FieldElem<'a>,
+ value: &FieldElem<'a>,
+ w: &mut CodeWriter,
+ ) {
+ self.for_each_map_entry(key, value, w, |k, v, w| {
+ w.write_line("let mut entry_size = 0;");
+ key.write_element_size(1, k, HowToGetMessageSize::Compute, "entry_size", &self.customize, w);
+ value.write_element_size(2, v, HowToGetMessageSize::Compute, "entry_size", &self.customize, w);
+ w.write_line(&format!("{sum_var} += {tag_size} + {protobuf_crate}::rt::compute_raw_varint64_size(entry_size) + entry_size",
+ tag_size = self.tag_size(),
+ protobuf_crate = protobuf_crate_path(&self.customize),
+ ));
+ });
+ }
+
+ pub(crate) fn write_message_compute_field_size(&self, sum_var: &str, w: &mut CodeWriter) {
+ match &self.kind {
+ FieldKind::Singular(s @ SingularField { elem, .. }) => {
+ self.write_if_let_self_field_is_some(s, w, |v, w| {
+ self.write_element_size(&elem, w, v, sum_var)
+ });
+ }
+ FieldKind::Repeated(RepeatedField {
+ packed: false,
+ elem,
+ ..
+ }) => {
+ match elem.proto_type().encoded_size() {
+ Some(s) => {
+ let tag_size = self.tag_size();
+ let self_field = self.self_field();
+ w.write_line(&format!(
+ "{} += {} * {}.len() as u64;",
+ sum_var,
+ (s + tag_size) as isize,
+ self_field
+ ));
+ }
+ None => {
+ self.write_for_self_field(w, "value", |w, value_type| {
+ self.write_element_size(
+ elem,
+ w,
+ &RustValueTyped {
+ value: "value".to_owned(),
+ rust_type: value_type.clone(),
+ },
+ sum_var,
+ );
+ });
+ }
+ };
+ }
+ FieldKind::Repeated(RepeatedField { packed: true, .. }) => {
+ let size_expr = self.self_field_vec_packed_size();
+ w.write_line(&format!("{} += {};", sum_var, size_expr));
+ }
+ FieldKind::Map(MapField { key, value, .. }) => {
+ self.write_compute_map_field_size(sum_var, key, value, w)
+ }
+ FieldKind::Oneof(..) => unreachable!(),
+ }
+ }
+
+ fn write_message_field_get_singular_message(&self, s: &SingularField, w: &mut CodeWriter) {
+ match s.flag {
+ SingularFieldFlag::WithoutFlag => unimplemented!(),
+ SingularFieldFlag::WithFlag { option_kind, .. } => {
+ let self_field = self.self_field();
+ let ref field_type_name = self.elem().rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+ w.write_line(option_kind.unwrap_ref_or_else(
+ &format!("{}.as_ref()", self_field),
+ &format!(
+ "<{} as {}::Message>::default_instance()",
+ field_type_name.to_code(&self.customize),
+ protobuf_crate_path(&self.customize),
+ ),
+ ));
+ }
+ }
+ }
+
+ fn write_message_field_get_singular_enum(
+ &self,
+ flag: SingularFieldFlag,
+ _elem: &FieldElemEnum,
+ w: &mut CodeWriter,
+ ) {
+ match flag {
+ SingularFieldFlag::WithoutFlag => {
+ w.write_line(&format!("self.{}.enum_value_or_default()", self.rust_name));
+ }
+ SingularFieldFlag::WithFlag { .. } => {
+ w.match_expr(&self.self_field(), |w| {
+ let default_value = self.xxx_default_value_rust();
+ w.case_expr("Some(e)", &format!("e.enum_value_or({})", default_value));
+ w.case_expr("None", &format!("{}", default_value));
+ });
+ }
+ }
+ }
+
+ fn write_message_field_get_singular(&self, singular: &SingularField, w: &mut CodeWriter) {
+ let get_xxx_return_type = self.getter_return_type();
+
+ match singular.elem {
+ FieldElem::Message(..) => self.write_message_field_get_singular_message(singular, w),
+ FieldElem::Enum(ref en) => {
+ self.write_message_field_get_singular_enum(singular.flag, en, w)
+ }
+ _ => {
+ let get_xxx_default_value_rust = self.xxx_default_value_rust();
+ let self_field = self.self_field();
+ match singular {
+ &SingularField {
+ ref elem,
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ ..
+ } => {
+ if get_xxx_return_type.is_ref().is_some() {
+ let as_option = self.self_field_as_option(elem, option_kind);
+ w.match_expr(&as_option.value, |w| {
+ let v_type = as_option.rust_type.elem_type();
+ let r_type = self.getter_return_type();
+ w.case_expr(
+ "Some(v)",
+ v_type.into_target(&r_type, "v", &self.customize),
+ );
+ let get_xxx_default_value_rust = self.xxx_default_value_rust();
+ w.case_expr("None", get_xxx_default_value_rust);
+ });
+ } else {
+ w.write_line(&format!(
+ "{}.unwrap_or({})",
+ self_field, get_xxx_default_value_rust
+ ));
+ }
+ }
+ &SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ..
+ } => {
+ w.write_line(
+ self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .into_target(
+ &get_xxx_return_type,
+ &self_field,
+ &self.customize,
+ ),
+ );
+ }
+ }
+ }
+ }
+ }
+
+ fn write_message_field_get_oneof(&self, o: &OneofField, w: &mut CodeWriter) {
+ let get_xxx_return_type = SingularOrOneofField::Oneof(o.clone()).getter_return_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+ let OneofField { ref elem, .. } = o;
+ w.match_expr(&format!("self.{}", o.oneof_field_name), |w| {
+ let (refv, vtype) = if !elem.is_copy() {
+ (
+ "ref v",
+ elem.rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .ref_type(),
+ )
+ } else {
+ (
+ "v",
+ elem.rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ )
+ };
+ w.case_expr(
+ format!(
+ "::std::option::Option::Some({}({}))",
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+ refv
+ ),
+ vtype.into_target(&get_xxx_return_type, "v", &self.customize),
+ );
+ w.case_expr("_", self.xxx_default_value_rust());
+ });
+ }
+
+ fn write_message_field_get(&self, w: &mut CodeWriter) {
+ let get_xxx_return_type = self.getter_return_type();
+ let fn_def = format!(
+ "{}(&self) -> {}",
+ self.rust_name,
+ get_xxx_return_type.to_code(&self.customize)
+ );
+
+ w.pub_fn(&fn_def, |w| match self.kind {
+ FieldKind::Oneof(ref o) => {
+ self.write_message_field_get_oneof(o, w);
+ }
+ FieldKind::Singular(ref s) => {
+ self.write_message_field_get_singular(s, w);
+ }
+ FieldKind::Repeated(..) | FieldKind::Map(..) => {
+ let self_field = self.self_field();
+ w.write_line(&format!("&{}", self_field));
+ }
+ });
+ }
+
+ fn has_has(&self) -> bool {
+ match self.kind {
+ FieldKind::Repeated(..) | FieldKind::Map(..) => false,
+ FieldKind::Singular(SingularField {
+ flag: SingularFieldFlag::WithFlag { .. },
+ ..
+ }) => true,
+ FieldKind::Singular(SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ..
+ }) => false,
+ FieldKind::Oneof(..) => true,
+ }
+ }
+
+ fn has_mut(&self) -> bool {
+ match self.kind {
+ FieldKind::Repeated(..) | FieldKind::Map(..) => true,
+ // TODO: string should be public, and mut is not needed
+ FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
+ }
+ }
+
+ fn has_take(&self) -> bool {
+ match self.kind {
+ FieldKind::Repeated(..) | FieldKind::Map(..) => true,
+ // TODO: string should be public, and mut is not needed
+ FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(),
+ }
+ }
+
+ fn has_name(&self) -> RustIdent {
+ RustIdent::new(&format!("has_{}", self.rust_name.get()))
+ }
+
+ fn set_name(&self) -> RustIdent {
+ RustIdent::new(&format!("set_{}", self.rust_name.get()))
+ }
+
+ fn mut_name(&self) -> RustIdent {
+ RustIdent::new(&format!("mut_{}", self.rust_name.get()))
+ }
+
+ fn write_message_field_has(&self, w: &mut CodeWriter) {
+ w.pub_fn(
+ &format!("{}(&self) -> bool", self.has_name()),
+ |w| match self.kind {
+ FieldKind::Oneof(ref oneof) => {
+ w.match_expr(&format!("self.{}", oneof.oneof_field_name), |w| {
+ w.case_expr(
+ format!(
+ "::std::option::Option::Some({}(..))",
+ oneof.variant_path(
+ &self.proto_field.message.scope.rust_path_to_file()
+ )
+ ),
+ "true",
+ );
+ w.case_expr("_", "false");
+ });
+ }
+ _ => {
+ let self_field_is_some = self.self_field_is_some();
+ w.write_line(self_field_is_some);
+ }
+ },
+ );
+ }
+
+ fn write_message_field_set(&self, w: &mut CodeWriter) {
+ let set_xxx_param_type = self.set_xxx_param_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+ w.comment("Param is passed by value, moved");
+ w.pub_fn(
+ &format!(
+ "{}(&mut self, v: {})",
+ self.set_name(),
+ set_xxx_param_type.to_code(&self.customize)
+ ),
+ |w| {
+ let value_typed = RustValueTyped {
+ value: "v".to_owned(),
+ rust_type: set_xxx_param_type.clone(),
+ };
+ match self.kind {
+ FieldKind::Oneof(ref oneof) => {
+ let v = set_xxx_param_type.into_target(
+ &oneof.rust_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ "v",
+ &self.customize,
+ );
+ w.write_line(&format!(
+ "self.{} = ::std::option::Option::Some({}({}))",
+ oneof.oneof_field_name,
+ oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+ v
+ ));
+ }
+ _ => {
+ self.write_self_field_assign_value(w, &value_typed);
+ }
+ }
+ },
+ );
+ }
+
+ fn write_message_field_mut_singular_with_flag(
+ &self,
+ s: &SingularField,
+ option_kind: OptionKind,
+ w: &mut CodeWriter,
+ ) {
+ let self_field = self.self_field();
+ match option_kind {
+ OptionKind::MessageField => {
+ w.write_line(&format!("{}.mut_or_insert_default()", self_field))
+ }
+ OptionKind::Option => {
+ self.write_if_self_field_is_none(w, |w| {
+ self.write_self_field_assign_default(
+ &SingularOrOneofField::Singular(s.clone()),
+ w,
+ );
+ });
+ w.write_line(&format!("{}.as_mut().unwrap()", self_field));
+ }
+ }
+ }
+
+ fn write_message_field_mut_singular(&self, s: &SingularField, w: &mut CodeWriter) {
+ match s {
+ s @ SingularField {
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ ..
+ } => self.write_message_field_mut_singular_with_flag(s, *option_kind, w),
+ SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ..
+ } => w.write_line(&format!("&mut {}", self.self_field())),
+ }
+ }
+
+ fn write_message_field_mut(&self, w: &mut CodeWriter) {
+ let mut_xxx_return_type = self.mut_xxx_return_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+ w.comment("Mutable pointer to the field.");
+ if self.is_singular() {
+ w.comment("If field is not initialized, it is initialized with default value first.");
+ }
+ let fn_def = match mut_xxx_return_type {
+ RustType::Ref(ref param) => format!(
+ "{}(&mut self) -> &mut {}",
+ self.mut_name(),
+ param.to_code(&self.customize)
+ ),
+ _ => panic!(
+ "not a ref: {}",
+ mut_xxx_return_type.to_code(&self.customize)
+ ),
+ };
+ w.pub_fn(&fn_def, |w| {
+ match self.kind {
+ FieldKind::Repeated(..) | FieldKind::Map(..) => {
+ let self_field = self.self_field();
+ w.write_line(&format!("&mut {}", self_field));
+ }
+ FieldKind::Singular(ref s) => {
+ self.write_message_field_mut_singular(s, w);
+ }
+ FieldKind::Oneof(ref o) => {
+ let self_field_oneof = format!("self.{}", o.oneof_field_name);
+
+ // if oneof does not contain current field
+ w.if_let_else_stmt(
+ &format!(
+ "::std::option::Option::Some({}(_))",
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+ )[..],
+ &self_field_oneof[..],
+ |w| {
+ // initialize it with default value
+ w.write_line(&format!(
+ "{} = ::std::option::Option::Some({}({}));",
+ self_field_oneof,
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file()),
+ self.element_default_value_rust()
+ .into_type(
+ o.rust_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone())
+ ),
+ &self.customize
+ )
+ .value
+ ));
+ },
+ );
+
+ // extract field
+ w.match_expr(self_field_oneof, |w| {
+ w.case_expr(
+ format!(
+ "::std::option::Option::Some({}(ref mut v))",
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+ ),
+ "v",
+ );
+ w.case_expr("_", "panic!()");
+ });
+ }
+ }
+ });
+ }
+
+ fn write_message_field_take_oneof(&self, o: &OneofField, w: &mut CodeWriter) {
+ let take_xxx_return_type = self.take_xxx_return_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+
+ // TODO: replace with if let
+ w.write_line(&format!("if self.{}() {{", self.has_name()));
+ w.indented(|w| {
+ let self_field_oneof = format!("self.{}", o.oneof_field_name);
+ w.match_expr(format!("{}.take()", self_field_oneof), |w| {
+ let value_in_some = o
+ .rust_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .value("v".to_owned());
+ let converted = value_in_some.into_type(
+ self.take_xxx_return_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ ),
+ &self.customize,
+ );
+ w.case_expr(
+ format!(
+ "::std::option::Option::Some({}(v))",
+ o.variant_path(&self.proto_field.message.scope.rust_path_to_file())
+ ),
+ &converted.value,
+ );
+ w.case_expr("_", "panic!()");
+ });
+ });
+ w.write_line("} else {");
+ w.indented(|w| {
+ w.write_line(
+ self.elem()
+ .rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .default_value_typed(&self.customize, false)
+ .into_type(take_xxx_return_type.clone(), &self.customize)
+ .value,
+ );
+ });
+ w.write_line("}");
+ }
+
+ fn write_message_field_take_singular(&self, s: &SingularField, w: &mut CodeWriter) {
+ match s {
+ SingularField {
+ ref elem,
+ flag: SingularFieldFlag::WithFlag { option_kind, .. },
+ } => {
+ if !elem.is_copy() {
+ w.write_line(
+ &option_kind.unwrap_or_else(
+ &format!("{}.take()", self.self_field()),
+ &elem
+ .rust_storage_elem_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ )
+ .default_value(&self.customize, false),
+ ),
+ );
+ } else {
+ w.write_line(&format!(
+ "{}.take().unwrap_or({})",
+ self.self_field(),
+ self.element_default_value_rust().value
+ ));
+ }
+ }
+ SingularField {
+ flag: SingularFieldFlag::WithoutFlag,
+ ..
+ } => w.write_line(&format!(
+ "::std::mem::replace(&mut {}, {})",
+ self.self_field(),
+ self.full_storage_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone())
+ )
+ .default_value(&self.customize, false)
+ )),
+ }
+ }
+
+ fn write_message_field_take(&self, w: &mut CodeWriter) {
+ let take_xxx_return_type = self.take_xxx_return_type(
+ &self
+ .proto_field
+ .message
+ .scope
+ .file_and_mod(self.customize.clone()),
+ );
+ w.comment("Take field");
+ w.pub_fn(
+ &format!(
+ "take_{}(&mut self) -> {}",
+ self.rust_name,
+ take_xxx_return_type.to_code(&self.customize)
+ ),
+ |w| match self.kind {
+ FieldKind::Singular(ref s) => self.write_message_field_take_singular(&s, w),
+ FieldKind::Oneof(ref o) => self.write_message_field_take_oneof(o, w),
+ FieldKind::Repeated(..) | FieldKind::Map(..) => {
+ w.write_line(&format!(
+ "::std::mem::replace(&mut self.{}, {})",
+ self.rust_name,
+ take_xxx_return_type.default_value(&self.customize, false)
+ ));
+ }
+ },
+ );
+ }
+
+ pub(crate) fn write_message_single_field_accessors(&self, w: &mut CodeWriter) {
+ if self.generate_accessors || self.generate_getter {
+ w.write_line("");
+ let reconstruct_def = self.reconstruct_def();
+ w.comment(&(reconstruct_def + ";"));
+ }
+
+ if self.generate_getter {
+ w.write_line("");
+ self.write_message_field_get(w);
+ }
+
+ if !self.generate_accessors {
+ return;
+ }
+
+ w.write_line("");
+ let clear_field_func = self.clear_field_func();
+ w.pub_fn(&format!("{}(&mut self)", clear_field_func), |w| {
+ self.write_clear(w);
+ });
+
+ if self.has_has() {
+ w.write_line("");
+ self.write_message_field_has(w);
+ }
+
+ w.write_line("");
+ self.write_message_field_set(w);
+
+ if self.has_mut() {
+ w.write_line("");
+ self.write_message_field_mut(w);
+ }
+
+ if self.has_take() {
+ w.write_line("");
+ self.write_message_field_take(w);
+ }
+ }
+}
+
+pub(crate) fn rust_field_name_for_protobuf_field_name(name: &str) -> RustIdent {
+ RustIdent::new(name)
+}
+
+pub(crate) fn rust_variant_name_for_protobuf_oneof_field_name(name: &str) -> RustIdent {
+ let name = camel_case(name);
+ RustIdent::new(&name)
+}
diff --git a/src/gen/field/option_kind.rs b/src/gen/field/option_kind.rs
new file mode 100644
index 0000000..0fde11a
--- /dev/null
+++ b/src/gen/field/option_kind.rs
@@ -0,0 +1,59 @@
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::rust_types_values::RustType;
+use crate::Customize;
+
+/// Optional fields can be stored are `Option<T>` or `SingularPtrField<T>`.
+#[derive(Copy, Clone, Eq, PartialEq)]
+pub enum OptionKind {
+ /// Field is `Option<T>`
+ Option,
+ /// Field is `SingularPtrField<T>`
+ MessageField,
+}
+
+impl OptionKind {
+ pub(crate) fn wrap_element(&self, element_type: RustType) -> RustType {
+ let element_type = Box::new(element_type);
+ match self {
+ OptionKind::Option => RustType::Option(element_type),
+ OptionKind::MessageField => RustType::MessageField(element_type),
+ }
+ }
+
+ // Type of `as_ref()` operation
+ pub(crate) fn as_ref_type(&self, element_type: RustType) -> RustType {
+ match self {
+ OptionKind::Option => RustType::Option(Box::new(element_type.ref_type())),
+ OptionKind::MessageField => RustType::MessageField(Box::new(element_type.ref_type())),
+ }
+ }
+
+ fn _as_option_ref(&self, v: &str) -> String {
+ match self {
+ OptionKind::Option | OptionKind::MessageField => format!("{}.as_ref()", v),
+ }
+ }
+
+ pub(crate) fn unwrap_or_else(&self, what: &str, default_value: &str) -> String {
+ match self {
+ _ => format!("{}.unwrap_or_else(|| {})", what, default_value),
+ }
+ }
+
+ pub(crate) fn unwrap_ref_or_else(&self, what: &str, default_value: &str) -> String {
+ match self {
+ _ => format!("{}.unwrap_or_else(|| {})", what, default_value),
+ }
+ }
+
+ pub(crate) fn wrap_value(&self, value: &str, customize: &Customize) -> String {
+ match self {
+ OptionKind::Option => format!("::std::option::Option::Some({})", value),
+ OptionKind::MessageField => format!(
+ "{}::MessageField::some({})",
+ protobuf_crate_path(customize),
+ value
+ ),
+ }
+ }
+}
diff --git a/src/gen/field/repeated.rs b/src/gen/field/repeated.rs
new file mode 100644
index 0000000..56e0d0b
--- /dev/null
+++ b/src/gen/field/repeated.rs
@@ -0,0 +1,46 @@
+use crate::gen::field::elem::FieldElem;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::rust::snippets::EXPR_VEC_NEW;
+use crate::gen::rust_types_values::RustType;
+
+/// Repeated field can be `Vec<T>` or `RepeatedField<T>`.
+#[derive(Eq, PartialEq, Copy, Clone)]
+pub enum RepeatedFieldKind {
+ Vec,
+}
+
+impl RepeatedFieldKind {
+ fn wrap_element(&self, element_type: RustType) -> RustType {
+ let element_type = Box::new(element_type);
+ match self {
+ RepeatedFieldKind::Vec => RustType::Vec(element_type),
+ }
+ }
+
+ fn default(&self) -> String {
+ match self {
+ RepeatedFieldKind::Vec => EXPR_VEC_NEW.to_owned(),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct RepeatedField<'a> {
+ pub elem: FieldElem<'a>,
+ pub packed: bool,
+}
+
+impl<'a> RepeatedField<'a> {
+ pub(crate) fn kind(&self) -> RepeatedFieldKind {
+ RepeatedFieldKind::Vec
+ }
+
+ pub(crate) fn rust_type(&self, reference: &FileAndMod) -> RustType {
+ self.kind()
+ .wrap_element(self.elem.rust_storage_elem_type(reference))
+ }
+
+ pub(crate) fn default(&self) -> String {
+ self.kind().default()
+ }
+}
diff --git a/src/gen/field/singular.rs b/src/gen/field/singular.rs
new file mode 100644
index 0000000..3bd223f
--- /dev/null
+++ b/src/gen/field/singular.rs
@@ -0,0 +1,52 @@
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::option_kind::OptionKind;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::rust_types_values::RustType;
+use crate::Customize;
+
+#[derive(Clone, PartialEq, Eq, Copy)]
+pub enum SingularFieldFlag {
+ // proto2 or proto3 message
+ WithFlag {
+ required: bool,
+ option_kind: OptionKind,
+ },
+ // proto3
+ WithoutFlag,
+}
+
+impl SingularFieldFlag {
+ pub fn is_required(&self) -> bool {
+ match *self {
+ SingularFieldFlag::WithFlag { required, .. } => required,
+ SingularFieldFlag::WithoutFlag => false,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct SingularField<'a> {
+ pub flag: SingularFieldFlag,
+ pub elem: FieldElem<'a>,
+}
+
+impl<'a> SingularField<'a> {
+ pub(crate) fn rust_storage_type(&self, reference: &FileAndMod) -> RustType {
+ match self.flag {
+ SingularFieldFlag::WithFlag { option_kind, .. } => {
+ option_kind.wrap_element(self.elem.rust_storage_elem_type(reference))
+ }
+ SingularFieldFlag::WithoutFlag => self.elem.rust_storage_elem_type(reference),
+ }
+ }
+
+ pub(crate) fn default_value(
+ &self,
+ customize: &Customize,
+ reference: &FileAndMod,
+ const_expr: bool,
+ ) -> String {
+ self.rust_storage_type(reference)
+ .default_value(customize, const_expr)
+ }
+}
diff --git a/src/gen/field/tag.rs b/src/gen/field/tag.rs
new file mode 100644
index 0000000..401a140
--- /dev/null
+++ b/src/gen/field/tag.rs
@@ -0,0 +1,5 @@
+use protobuf::rt::WireType;
+
+pub(crate) fn make_tag(field_number: u32, wire_type: WireType) -> u32 {
+ (field_number << 3) | (wire_type as u32)
+}
diff --git a/src/gen/field/type_ext.rs b/src/gen/field/type_ext.rs
new file mode 100644
index 0000000..334168f
--- /dev/null
+++ b/src/gen/field/type_ext.rs
@@ -0,0 +1,115 @@
+use protobuf::descriptor::field_descriptor_proto::Type;
+
+use crate::gen::rust_types_values::PrimitiveTypeVariant;
+use crate::gen::rust_types_values::RustType;
+
+pub(crate) trait TypeExt {
+ fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String;
+ fn is_s_varint(&self) -> bool;
+ fn is_copy(&self) -> bool;
+ fn protobuf_name(&self) -> &'static str;
+ fn rust_type(&self) -> RustType;
+ fn os_write_fn_param_type(&self) -> RustType;
+ fn encoded_size(&self) -> Option<u32>;
+}
+
+impl TypeExt for Type {
+ fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String {
+ match (self, primitive_type_variant) {
+ (Type::TYPE_ENUM, _) => format!("{}.read_enum_or_unknown()", is),
+ (Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => {
+ format!("{}.read_tokio_bytes()", is)
+ }
+ (Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => {
+ format!("{}.read_tokio_chars()", is)
+ }
+ _ => format!("{}.read_{}()", is, self.protobuf_name()),
+ }
+ }
+
+ /// True if self is signed integer with zigzag encoding
+ fn is_s_varint(&self) -> bool {
+ match *self {
+ Type::TYPE_SINT32 | Type::TYPE_SINT64 => true,
+ _ => false,
+ }
+ }
+
+ fn is_copy(&self) -> bool {
+ match self {
+ Type::TYPE_MESSAGE | Type::TYPE_STRING | Type::TYPE_BYTES => false,
+ _ => true,
+ }
+ }
+
+ fn protobuf_name(&self) -> &'static str {
+ match self {
+ Type::TYPE_DOUBLE => "double",
+ Type::TYPE_FLOAT => "float",
+ Type::TYPE_INT32 => "int32",
+ Type::TYPE_INT64 => "int64",
+ Type::TYPE_UINT32 => "uint32",
+ Type::TYPE_UINT64 => "uint64",
+ Type::TYPE_SINT32 => "sint32",
+ Type::TYPE_SINT64 => "sint64",
+ Type::TYPE_FIXED32 => "fixed32",
+ Type::TYPE_FIXED64 => "fixed64",
+ Type::TYPE_SFIXED32 => "sfixed32",
+ Type::TYPE_SFIXED64 => "sfixed64",
+ Type::TYPE_BOOL => "bool",
+ Type::TYPE_STRING => "string",
+ Type::TYPE_BYTES => "bytes",
+ Type::TYPE_ENUM => "enum",
+ Type::TYPE_MESSAGE => "message",
+ Type::TYPE_GROUP => "group",
+ }
+ }
+
+ /// Rust type for protobuf base type.
+ fn rust_type(&self) -> RustType {
+ match self {
+ Type::TYPE_DOUBLE => RustType::Float(64),
+ Type::TYPE_FLOAT => RustType::Float(32),
+ Type::TYPE_INT32 => RustType::Int(true, 32),
+ Type::TYPE_INT64 => RustType::Int(true, 64),
+ Type::TYPE_UINT32 => RustType::Int(false, 32),
+ Type::TYPE_UINT64 => RustType::Int(false, 64),
+ Type::TYPE_SINT32 => RustType::Int(true, 32),
+ Type::TYPE_SINT64 => RustType::Int(true, 64),
+ Type::TYPE_FIXED32 => RustType::Int(false, 32),
+ Type::TYPE_FIXED64 => RustType::Int(false, 64),
+ Type::TYPE_SFIXED32 => RustType::Int(true, 32),
+ Type::TYPE_SFIXED64 => RustType::Int(true, 64),
+ Type::TYPE_BOOL => RustType::Bool,
+ Type::TYPE_STRING => RustType::String,
+ Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::u8())),
+ Type::TYPE_ENUM | Type::TYPE_GROUP | Type::TYPE_MESSAGE => {
+ panic!("there is no rust name for {:?}", self)
+ }
+ }
+ }
+
+ // type of `v` in `os.write_xxx_no_tag(v)`
+ fn os_write_fn_param_type(&self) -> RustType {
+ match self {
+ Type::TYPE_STRING => RustType::amp_str(),
+ Type::TYPE_BYTES => RustType::amp_slice_of_u8(),
+ Type::TYPE_ENUM => RustType::i32(),
+ t => t.rust_type(),
+ }
+ }
+
+ /// Size of value for type, None if variable.
+ fn encoded_size(&self) -> Option<u32> {
+ match self {
+ Type::TYPE_BOOL => Some(1),
+ Type::TYPE_FIXED32 => Some(4),
+ Type::TYPE_FIXED64 => Some(8),
+ Type::TYPE_SFIXED32 => Some(4),
+ Type::TYPE_SFIXED64 => Some(8),
+ Type::TYPE_FLOAT => Some(4),
+ Type::TYPE_DOUBLE => Some(8),
+ _ => None,
+ }
+ }
+}
diff --git a/src/gen/file.rs b/src/gen/file.rs
new file mode 100644
index 0000000..411b069
--- /dev/null
+++ b/src/gen/file.rs
@@ -0,0 +1,149 @@
+use std::collections::HashMap;
+
+use protobuf::descriptor::file_options;
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtoPath;
+
+use crate::compiler_plugin;
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_file;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::enums::EnumGen;
+use crate::gen::extensions::write_extensions;
+use crate::gen::file_descriptor::write_file_descriptor_data;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::MessageGen;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::scope::FileScope;
+use crate::gen::scope::RootScope;
+use crate::proto_name_to_rs;
+
+pub(crate) struct GenFileResult {
+ pub(crate) compiler_plugin_result: compiler_plugin::GenResult,
+ pub(crate) mod_name: String,
+}
+
+pub(crate) fn gen_file(
+ file_descriptor: &FileDescriptor,
+ _files_map: &HashMap<&ProtoPath, &FileDescriptor>,
+ root_scope: &RootScope,
+ parent_customize: &CustomizeElemCtx,
+ parser: &str,
+) -> anyhow::Result<GenFileResult> {
+ let lite_runtime_from_builtin_option = file_descriptor
+ .proto()
+ .options
+ .get_or_default()
+ .optimize_for()
+ == file_options::OptimizeMode::LITE_RUNTIME;
+
+ let mut customize_from_proto =
+ customize_from_rustproto_for_file(file_descriptor.proto().options.get_or_default());
+ if customize_from_proto.lite_runtime.is_none()
+ && parent_customize.for_elem.lite_runtime.is_none()
+ {
+ customize_from_proto.lite_runtime = Some(lite_runtime_from_builtin_option);
+ }
+
+ let customize = parent_customize.child(&customize_from_proto, file_descriptor);
+
+ let file_scope = FileScope { file_descriptor };
+ let scope = file_scope.to_scope();
+
+ let lite_runtime = customize.for_elem.lite_runtime.unwrap_or(false);
+
+ let v = CodeWriter::with(|w| {
+ w.write_generated_by("rust-protobuf", "3.2.0", parser);
+
+ w.write_line("");
+ w.write_line(&format!(
+ "//! Generated file from `{}`",
+ file_descriptor.proto().name()
+ ));
+
+ if customize.for_elem.lite_runtime.unwrap_or(false) {
+ w.comment("Generated for lite runtime");
+ }
+
+ if customize.for_elem.inside_protobuf != Some(true) {
+ w.write_line("");
+ w.write_line("/// Generated files are compatible only with the same version");
+ w.write_line("/// of protobuf runtime.");
+ w.write_line(&format!(
+ "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
+ protobuf_crate_path(&customize.for_elem),
+ protobuf::VERSION_IDENT
+ ));
+ }
+
+ static NESTED_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+ let message_type_number = *NESTED_TYPE_NUMBER.get(|| {
+ protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
+ .field_by_name("message_type")
+ .expect("`message_type` must exist")
+ .proto()
+ .number()
+ });
+
+ let mut path = vec![message_type_number, 0];
+ for (id, message) in scope.messages().iter().enumerate() {
+ // ignore map entries, because they are not used in map fields
+ if !message.is_map() {
+ path[1] = id as i32;
+
+ w.write_line("");
+ MessageGen::new(
+ file_descriptor,
+ message,
+ &root_scope,
+ &customize,
+ &path,
+ file_descriptor.proto().source_code_info.as_ref(),
+ )?
+ .write(w)?;
+ }
+ }
+
+ static ENUM_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+ let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
+ protobuf::reflect::MessageDescriptor::for_type::<FileDescriptorProto>()
+ .field_by_name("enum_type")
+ .expect("`enum_type` must exist")
+ .proto()
+ .number()
+ });
+
+ let mut path = vec![enum_type_number, 0];
+ for (id, enum_type) in scope.enums().iter().enumerate() {
+ path[1] = id as i32;
+
+ w.write_line("");
+ EnumGen::new(
+ enum_type,
+ &customize,
+ root_scope,
+ &path,
+ file_descriptor.proto().source_code_info.as_ref(),
+ )
+ .write(w);
+ }
+
+ write_extensions(file_descriptor, &root_scope, w, &customize);
+
+ if !lite_runtime {
+ w.write_line("");
+ write_file_descriptor_data(file_descriptor, &customize.for_elem, w);
+ }
+
+ Ok(())
+ })?;
+
+ Ok(GenFileResult {
+ compiler_plugin_result: compiler_plugin::GenResult {
+ name: proto_name_to_rs(file_descriptor.proto().name()),
+ content: v.into_bytes(),
+ },
+ mod_name: proto_path_to_rust_mod(file_descriptor.proto().name()).into_string(),
+ })
+}
diff --git a/src/file_and_mod.rs b/src/gen/file_and_mod.rs
index 4f4e8c6..8862c53 100644
--- a/src/file_and_mod.rs
+++ b/src/gen/file_and_mod.rs
@@ -1,7 +1,6 @@
-use rust_name::RustRelativePath;
-use Customize;
+use crate::customize::Customize;
+use crate::gen::rust::rel_path::RustRelativePath;
-#[allow(dead_code)]
pub(crate) struct FileAndMod {
pub file: String,
pub relative_mod: RustRelativePath,
diff --git a/src/gen/file_descriptor.rs b/src/gen/file_descriptor.rs
new file mode 100644
index 0000000..2c8249c
--- /dev/null
+++ b/src/gen/file_descriptor.rs
@@ -0,0 +1,208 @@
+use std::fmt::Write as _;
+
+use protobuf::reflect::FileDescriptor;
+use protobuf::Message;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::paths::proto_path_to_fn_file_descriptor;
+use crate::gen::rust::snippets::expr_vec_with_capacity_const;
+use crate::gen::scope::FileScope;
+use crate::gen::scope::Scope;
+use crate::gen::scope::WithScope;
+use crate::Customize;
+
+fn escape_byte(s: &mut String, b: u8) {
+ if b == b'\n' {
+ write!(s, "\\n").unwrap();
+ } else if b == b'\r' {
+ write!(s, "\\r").unwrap();
+ } else if b == b'\t' {
+ write!(s, "\\t").unwrap();
+ } else if b == b'\\' || b == b'"' {
+ write!(s, "\\{}", b as char).unwrap();
+ } else if b == b'\0' {
+ write!(s, "\\0").unwrap();
+ // ASCII printable except space
+ } else if b > 0x20 && b < 0x7f {
+ write!(s, "{}", b as char).unwrap();
+ } else {
+ write!(s, "\\x{:02x}", b).unwrap();
+ }
+}
+
+fn write_generate_file_descriptor(
+ file_descriptor: &FileDescriptor,
+ customize: &Customize,
+ w: &mut CodeWriter,
+) {
+ let deps = &file_descriptor.proto().dependency;
+ w.write_line(&format!(
+ "let mut deps = {vec_with_capacity};",
+ vec_with_capacity = expr_vec_with_capacity_const(deps.len())
+ ));
+ for f in deps {
+ w.write_line(&format!(
+ "deps.push({}().clone());",
+ proto_path_to_fn_file_descriptor(f, customize)
+ ));
+ }
+
+ let scope = FileScope { file_descriptor };
+
+ let messages = scope.find_messages_except_map();
+ w.write_line(&format!(
+ "let mut messages = {vec_with_capacity};",
+ vec_with_capacity = expr_vec_with_capacity_const(messages.len())
+ ));
+ for m in &messages {
+ w.write_line(&format!(
+ "messages.push({}::generated_message_descriptor_data());",
+ m.rust_name_to_file(),
+ ));
+ }
+
+ let enums = scope.find_enums();
+ w.write_line(&format!(
+ "let mut enums = {};",
+ expr_vec_with_capacity_const(enums.len())
+ ));
+ for e in &enums {
+ w.write_line(&format!(
+ "enums.push({}::generated_enum_descriptor_data());",
+ e.rust_name_to_file(),
+ ));
+ }
+
+ w.write_line(&format!(
+ "{}::reflect::GeneratedFileDescriptor::new_generated(",
+ protobuf_crate_path(&customize),
+ ));
+ w.indented(|w| {
+ w.write_line(&format!("file_descriptor_proto(),"));
+ w.write_line(&format!("deps,"));
+ w.write_line(&format!("messages,"));
+ w.write_line(&format!("enums,"));
+ });
+ w.write_line(")");
+}
+
+fn write_file_descriptor(
+ file_descriptor: &FileDescriptor,
+ customize: &Customize,
+ w: &mut CodeWriter,
+) {
+ w.write_line("/// `FileDescriptor` object which allows dynamic access to files");
+ w.pub_fn(
+ &format!(
+ "file_descriptor() -> &'static {protobuf_crate}::reflect::FileDescriptor",
+ protobuf_crate = protobuf_crate_path(customize)
+ ),
+ |w| {
+ w.lazy_static(
+ "generated_file_descriptor_lazy",
+ &format!(
+ "{protobuf_crate}::reflect::GeneratedFileDescriptor",
+ protobuf_crate = protobuf_crate_path(customize)
+ ),
+ &format!("{}", protobuf_crate_path(customize)),
+ );
+ w.lazy_static_decl_get(
+ "file_descriptor",
+ &format!(
+ "{protobuf_crate}::reflect::FileDescriptor",
+ protobuf_crate = protobuf_crate_path(customize)
+ ),
+ &format!("{}", protobuf_crate_path(customize)),
+ |w| {
+ w.block(
+ "let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {",
+ "});",
+ |w| write_generate_file_descriptor(file_descriptor, customize, w),
+ );
+ w.write_line(&format!(
+ "{protobuf_crate}::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)",
+ protobuf_crate=protobuf_crate_path(&customize),
+ ));
+ }
+ );
+ },
+ );
+}
+
+pub(crate) fn write_file_descriptor_data(
+ file: &FileDescriptor,
+ customize: &Customize,
+ w: &mut CodeWriter,
+) {
+ let fdp_bytes = file.proto().write_to_bytes().unwrap();
+ w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
+ w.indented(|w| {
+ const MAX_LINE_LEN: usize = 72;
+
+ let mut s = String::new();
+ for &b in &fdp_bytes {
+ let prev_len = s.len();
+ escape_byte(&mut s, b);
+ let truncate = s.len() > MAX_LINE_LEN;
+ if truncate {
+ s.truncate(prev_len);
+ }
+ if truncate || s.len() == MAX_LINE_LEN {
+ write!(s, "\\").unwrap();
+ w.write_line(&s);
+ s.clear();
+ }
+ if truncate {
+ escape_byte(&mut s, b);
+ }
+ }
+ if !s.is_empty() {
+ write!(s, "\\").unwrap();
+ w.write_line(&s);
+ s.clear();
+ }
+ });
+ w.write_line("\";");
+ w.write_line("");
+ write_file_descriptor_proto(&customize, w);
+ w.write_line("");
+ write_file_descriptor(file, &customize, w);
+}
+
+fn write_file_descriptor_proto(customize: &Customize, w: &mut CodeWriter) {
+ w.write_line("/// `FileDescriptorProto` object which was a source for this generated file");
+ w.def_fn(
+ &format!(
+ "file_descriptor_proto() -> &'static {protobuf_crate}::descriptor::FileDescriptorProto",
+ protobuf_crate=protobuf_crate_path(customize)
+ ),
+ |w| {
+ w.lazy_static_decl_get(
+ "file_descriptor_proto_lazy",
+ &format!(
+ "{protobuf_crate}::descriptor::FileDescriptorProto",
+ protobuf_crate=protobuf_crate_path(customize)
+ ),
+ &format!("{}", protobuf_crate_path(customize)),
+ |w| {
+ w.write_line(&format!(
+ "{protobuf_crate}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
+ protobuf_crate=protobuf_crate_path(customize)
+ ));
+ },
+ );
+ },
+ );
+}
+
+/// Code to generate call `module::file_descriptor()`.
+pub(crate) fn file_descriptor_call_expr(scope: &Scope) -> String {
+ format!(
+ "{}()",
+ scope
+ .rust_path_to_file()
+ .to_reverse()
+ .append_ident("file_descriptor".into())
+ )
+}
diff --git a/src/gen/inside.rs b/src/gen/inside.rs
new file mode 100644
index 0000000..d416031
--- /dev/null
+++ b/src/gen/inside.rs
@@ -0,0 +1,11 @@
+use crate::customize::Customize;
+use crate::gen::rust::path::RustPath;
+
+/// Path to `protobuf` crate, different when `.proto` file is
+/// used inside or outside of protobuf crate.
+pub(crate) fn protobuf_crate_path(customize: &Customize) -> RustPath {
+ match customize.inside_protobuf {
+ Some(true) => RustPath::from("crate"),
+ _ => RustPath::from("::protobuf"),
+ }
+}
diff --git a/src/gen/map.rs b/src/gen/map.rs
new file mode 100644
index 0000000..ba50eeb
--- /dev/null
+++ b/src/gen/map.rs
@@ -0,0 +1,16 @@
+use crate::gen::scope::FieldWithContext;
+use crate::gen::scope::MessageWithScope;
+
+/// Pair of (key, value) if this message is map entry
+pub(crate) fn map_entry<'a>(
+ d: &'a MessageWithScope,
+) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> {
+ if d.message.is_map_entry() {
+ // `MessageDescriptor` validated the fields.
+ let key = d.fields()[0].clone();
+ let value = d.fields()[1].clone();
+ Some((key, value))
+ } else {
+ None
+ }
+}
diff --git a/src/gen/message.rs b/src/gen/message.rs
new file mode 100644
index 0000000..3471f87
--- /dev/null
+++ b/src/gen/message.rs
@@ -0,0 +1,786 @@
+use std::fmt;
+
+use protobuf::descriptor::*;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf_parse::snake_case;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::ctx::SpecialFieldPseudoDescriptor;
+use crate::customize::rustproto_proto::customize_from_rustproto_for_message;
+use crate::gen::code_writer::*;
+use crate::gen::descriptor::write_fn_descriptor;
+use crate::gen::enums::*;
+use crate::gen::field::FieldGen;
+use crate::gen::field::FieldKind;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::oneof::OneofGen;
+use crate::gen::oneof::OneofVariantGen;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_message;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_special_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust::snippets::expr_vec_with_capacity_const;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust_types_values::*;
+use crate::gen::scope::MessageWithScope;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+use crate::Customize;
+
+/// Protobuf message Rust type name
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub(crate) struct RustTypeMessage(pub RustIdentWithPath);
+
+impl fmt::Display for RustTypeMessage {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+impl<S: Into<RustIdentWithPath>> From<S> for RustTypeMessage {
+ fn from(s: S) -> Self {
+ RustTypeMessage(s.into())
+ }
+}
+
+impl RustTypeMessage {
+ /// Code which emits default instance.
+ pub fn default_instance(&self, customize: &Customize) -> String {
+ format!(
+ "<{} as {}::Message>::default_instance()",
+ self.0,
+ protobuf_crate_path(customize)
+ )
+ }
+}
+
+/// Message info for codegen
+pub(crate) struct MessageGen<'a> {
+ file_descriptor: &'a FileDescriptor,
+ message_descriptor: MessageDescriptor,
+ pub message: &'a MessageWithScope<'a>,
+ pub root_scope: &'a RootScope<'a>,
+ pub fields: Vec<FieldGen<'a>>,
+ pub lite_runtime: bool,
+ customize: CustomizeElemCtx<'a>,
+ path: &'a [i32],
+ info: Option<&'a SourceCodeInfo>,
+}
+
+impl<'a> MessageGen<'a> {
+ pub fn new(
+ file_descriptor: &'a FileDescriptor,
+ message: &'a MessageWithScope<'a>,
+ root_scope: &'a RootScope<'a>,
+ parent_customize: &CustomizeElemCtx<'a>,
+ path: &'a [i32],
+ info: Option<&'a SourceCodeInfo>,
+ ) -> anyhow::Result<MessageGen<'a>> {
+ let message_descriptor = file_descriptor
+ .message_by_package_relative_name(&format!("{}", message.protobuf_name_to_package()))
+ .unwrap();
+
+ let customize = parent_customize.child(
+ &customize_from_rustproto_for_message(message.message.proto().options.get_or_default()),
+ &message.message,
+ );
+
+ static FIELD_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+ let field_number = *FIELD_NUMBER.get(|| {
+ protobuf::reflect::MessageDescriptor::for_type::<DescriptorProto>()
+ .field_by_name("field")
+ .expect("`field` must exist")
+ .proto()
+ .number()
+ });
+
+ let fields: Vec<_> = message
+ .fields()
+ .into_iter()
+ .enumerate()
+ .map(|(id, field)| {
+ let mut path = path.to_vec();
+ path.extend_from_slice(&[field_number, id as i32]);
+ FieldGen::parse(field, root_scope, &customize, path, info)
+ })
+ .collect::<anyhow::Result<Vec<_>>>()?;
+ let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+ message.file_descriptor().proto().options.optimize_for()
+ == file_options::OptimizeMode::LITE_RUNTIME
+ });
+ Ok(MessageGen {
+ message_descriptor,
+ file_descriptor,
+ message,
+ root_scope,
+ fields,
+ lite_runtime,
+ customize,
+ path,
+ info,
+ })
+ }
+
+ fn rust_name(&self) -> RustIdent {
+ self.message.rust_name()
+ }
+
+ fn mod_name(&self) -> RustRelativePath {
+ self.message.scope.rust_path_to_file()
+ }
+
+ pub fn file_and_mod(&self) -> FileAndMod {
+ self.message
+ .scope
+ .file_and_mod(self.customize.for_elem.clone())
+ }
+
+ fn oneofs(&'a self) -> Vec<OneofGen<'a>> {
+ self.message
+ .oneofs()
+ .into_iter()
+ .map(|oneof| OneofGen::parse(self, oneof, &self.customize))
+ .collect()
+ }
+
+ fn required_fields(&'a self) -> Vec<&'a FieldGen> {
+ self.fields
+ .iter()
+ .filter(|f| match f.kind {
+ FieldKind::Singular(ref singular) => singular.flag.is_required(),
+ _ => false,
+ })
+ .collect()
+ }
+
+ fn message_fields(&'a self) -> Vec<&'a FieldGen> {
+ self.fields
+ .iter()
+ .filter(|f| f.proto_type == field_descriptor_proto::Type::TYPE_MESSAGE)
+ .collect()
+ }
+
+ fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen> {
+ self.fields
+ .iter()
+ .filter(|f| match f.kind {
+ FieldKind::Oneof(..) => false,
+ _ => true,
+ })
+ .collect()
+ }
+
+ fn fields_except_group(&'a self) -> Vec<&'a FieldGen> {
+ self.fields
+ .iter()
+ .filter(|f| f.proto_type != field_descriptor_proto::Type::TYPE_GROUP)
+ .collect()
+ }
+
+ fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen> {
+ self.fields
+ .iter()
+ .filter(|f| match f.kind {
+ FieldKind::Oneof(..) => false,
+ _ => f.proto_type != field_descriptor_proto::Type::TYPE_GROUP,
+ })
+ .collect()
+ }
+
+ fn write_match_each_oneof_variant<F>(&self, w: &mut CodeWriter, cb: F)
+ where
+ F: Fn(&mut CodeWriter, &OneofVariantGen, &RustValueTyped),
+ {
+ for oneof in self.oneofs() {
+ let variants = oneof.variants_except_group();
+ if variants.is_empty() {
+ // Special case because
+ // https://github.com/rust-lang/rust/issues/50642
+ continue;
+ }
+ w.if_let_stmt(
+ "::std::option::Option::Some(ref v)",
+ &format!("self.{}", oneof.oneof.field_name())[..],
+ |w| {
+ w.match_block("v", |w| {
+ for variant in variants {
+ let ref field = variant.field;
+
+ let (refv, vtype) = if field.elem_type_is_copy() {
+ ("v", variant.rust_type(&self.file_and_mod()))
+ } else {
+ ("ref v", variant.rust_type(&self.file_and_mod()).ref_type())
+ };
+ w.case_block(
+ format!("&{}({})", variant.path(&self.file_and_mod()), refv),
+ |w| {
+ cb(
+ w,
+ &variant,
+ &RustValueTyped {
+ value: "v".to_owned(),
+ rust_type: vtype.clone(),
+ },
+ );
+ },
+ );
+ }
+ });
+ },
+ );
+ }
+ }
+
+ fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "write_to_with_cached_sizes(&self, os: &mut {protobuf_crate}::CodedOutputStream<'_>) -> {protobuf_crate}::Result<()>",
+ protobuf_crate=protobuf_crate_path(&self.customize.for_elem),
+ );
+ w.def_fn(&sig, |w| {
+ // To have access to its methods but not polute the name space.
+ for f in self.fields_except_oneof_and_group() {
+ f.write_message_write_field("os", w);
+ }
+ self.write_match_each_oneof_variant(w, |w, variant, v| {
+ variant
+ .field
+ .write_write_element(variant.elem(), w, "os", v);
+ });
+ w.write_line("os.write_unknown_fields(self.special_fields.unknown_fields())?;");
+ w.write_line("::std::result::Result::Ok(())");
+ });
+ }
+
+ fn write_default_instance_lazy(&self, w: &mut CodeWriter) {
+ w.lazy_static_decl_get_simple(
+ "instance",
+ &format!("{}", self.rust_name()),
+ &format!("{}::new", self.rust_name()),
+ &format!("{}", protobuf_crate_path(&self.customize.for_elem)),
+ );
+ }
+
+ fn write_default_instance_static(&self, w: &mut CodeWriter) {
+ w.stmt_block(
+ &format!(
+ "static instance: {} = {}",
+ self.rust_name(),
+ self.rust_name()
+ ),
+ |w| {
+ for f in &self.fields_except_oneof_and_group() {
+ w.field_entry(
+ &f.rust_name.to_string(),
+ &f.kind
+ .default(&self.customize.for_elem, &self.file_and_mod(), true),
+ );
+ }
+ for o in &self.oneofs() {
+ w.field_entry(&o.oneof.field_name().to_string(), EXPR_NONE);
+ }
+ w.field_entry(
+ "special_fields",
+ &format!(
+ "{}::SpecialFields::new()",
+ protobuf_crate_path(&self.customize.for_elem)
+ ),
+ );
+ },
+ );
+ w.write_line("&instance");
+ }
+
+ fn write_default_instance(&self, w: &mut CodeWriter) {
+ w.def_fn(
+ &format!("default_instance() -> &'static {}", self.rust_name()),
+ |w| {
+ let has_map_field = self.fields.iter().any(|f| match f.kind {
+ FieldKind::Map(..) => true,
+ _ => false,
+ });
+ if has_map_field {
+ self.write_default_instance_lazy(w)
+ } else {
+ self.write_default_instance_static(w)
+ }
+ },
+ );
+ }
+
+ fn write_compute_size(&self, w: &mut CodeWriter) {
+ // Append sizes of messages in the tree to the specified vector.
+ // First appended element is size of self, and then nested message sizes.
+ // in serialization order are appended recursively.");
+ w.comment("Compute sizes of nested messages");
+ // there are unused variables in oneof
+ w.allow(&["unused_variables"]);
+ w.def_fn("compute_size(&self) -> u64", |w| {
+ // To have access to its methods but not polute the name space.
+ w.write_line("let mut my_size = 0;");
+ for field in self.fields_except_oneof_and_group() {
+ field.write_message_compute_field_size("my_size", w);
+ }
+ self.write_match_each_oneof_variant(w, |w, variant, v| {
+ variant
+ .field
+ .write_element_size(variant.elem(), w, v, "my_size");
+ });
+ w.write_line(&format!(
+ "my_size += {}::rt::unknown_fields_size(self.special_fields.unknown_fields());",
+ protobuf_crate_path(&self.customize.for_elem)
+ ));
+ w.write_line("self.special_fields.cached_size().set(my_size as u32);");
+ w.write_line("my_size");
+ });
+ }
+
+ fn write_field_accessors(&self, w: &mut CodeWriter) {
+ for f in self.fields_except_group() {
+ f.write_message_single_field_accessors(w);
+ }
+ }
+
+ fn write_impl_self(&self, w: &mut CodeWriter) {
+ w.impl_self_block(&format!("{}", self.rust_name()), |w| {
+ w.pub_fn(&format!("new() -> {}", self.rust_name()), |w| {
+ w.write_line("::std::default::Default::default()");
+ });
+
+ self.write_field_accessors(w);
+
+ if !self.lite_runtime {
+ w.write_line("");
+ self.write_generated_message_descriptor_data(w);
+ }
+ });
+ }
+
+ fn write_unknown_fields(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "special_fields(&self) -> &{}::SpecialFields",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.def_fn(&sig, |w| {
+ w.write_line("&self.special_fields");
+ });
+ w.write_line("");
+ let sig = format!(
+ "mut_special_fields(&mut self) -> &mut {}::SpecialFields",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.def_fn(&sig, |w| {
+ w.write_line("&mut self.special_fields");
+ });
+ }
+
+ fn write_merge_from(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::Result<()>",
+ protobuf_crate_path(&self.customize.for_elem),
+ protobuf_crate_path(&self.customize.for_elem),
+ );
+ w.def_fn(&sig, |w| {
+ w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| {
+ w.match_block("tag", |w| {
+ for f in &self.fields_except_group() {
+ f.write_merge_from_field_case_block(w);
+ }
+ w.case_block("tag", |w| {
+ w.write_line(&format!("{}::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;", protobuf_crate_path(&self.customize.for_elem)));
+ });
+ });
+ });
+ w.write_line("::std::result::Result::Ok(())");
+ });
+ }
+
+ fn write_impl_message_full_fn_descriptor(&self, w: &mut CodeWriter) {
+ write_fn_descriptor(
+ &self.message.message,
+ self.message.scope(),
+ &self.customize.for_elem,
+ w,
+ );
+ }
+
+ fn write_generated_message_descriptor_data(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "generated_message_descriptor_data() -> {}::reflect::GeneratedMessageDescriptorData",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.fn_block(
+ Visibility::Path(self.message.scope().rust_path_to_file().to_reverse()),
+ &sig,
+ |w| {
+ let fields = self.fields_except_group();
+ let oneofs = self.oneofs();
+ w.write_line(&format!(
+ "let mut fields = {};",
+ expr_vec_with_capacity_const(fields.len())
+ ));
+ w.write_line(&format!(
+ "let mut oneofs = {};",
+ expr_vec_with_capacity_const(oneofs.len())
+ ));
+ for field in fields {
+ field.write_push_accessor("fields", w);
+ }
+ for oneof in oneofs {
+ w.write_line(&format!(
+ "oneofs.push({}::generated_oneof_descriptor_data());",
+ oneof.type_name_relative(&self.mod_name())
+ ));
+ }
+ w.write_line(&format!(
+ "{}::reflect::GeneratedMessageDescriptorData::new_2::<{}>(",
+ protobuf_crate_path(&self.customize.for_elem),
+ self.rust_name(),
+ ));
+ w.indented(|w| {
+ w.write_line(&format!("\"{}\",", self.message.name_to_package()));
+ w.write_line("fields,");
+ w.write_line("oneofs,");
+ });
+ w.write_line(")");
+ },
+ );
+ }
+
+ fn write_is_initialized(&self, w: &mut CodeWriter) {
+ w.def_fn(&format!("is_initialized(&self) -> bool"), |w| {
+ if !self.message.message.is_initialized_is_always_true() {
+ // TODO: use single loop
+
+ for f in self.required_fields() {
+ f.write_if_self_field_is_none(w, |w| {
+ w.write_line("return false;");
+ });
+ }
+
+ for f in self.message_fields() {
+ if let FieldKind::Map(..) = f.kind {
+ // TODO
+ w.comment("TODO: check map values are initialized");
+ continue;
+ }
+
+ f.write_for_self_field(w, "v", |w, _t| {
+ w.if_stmt("!v.is_initialized()", |w| {
+ w.write_line("return false;");
+ });
+ });
+ }
+ }
+ w.write_line("true");
+ });
+ }
+
+ fn write_impl_message(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!("{}::Message", protobuf_crate_path(&self.customize.for_elem),),
+ &format!("{}", self.rust_name()),
+ |w| {
+ w.write_line(&format!(
+ "const NAME: &'static str = \"{}\";",
+ self.message.message.name()
+ ));
+ w.write_line("");
+ self.write_is_initialized(w);
+ w.write_line("");
+ self.write_merge_from(w);
+ w.write_line("");
+ self.write_compute_size(w);
+ w.write_line("");
+ self.write_write_to_with_cached_sizes(w);
+ w.write_line("");
+ self.write_unknown_fields(w);
+ w.write_line("");
+ w.def_fn(&format!("new() -> {}", self.rust_name()), |w| {
+ w.write_line(&format!("{}::new()", self.rust_name()));
+ });
+ w.write_line("");
+ w.def_fn("clear(&mut self)", |w| {
+ for f in self.fields_except_group() {
+ f.write_clear(w);
+ }
+ w.write_line("self.special_fields.clear();");
+ });
+ w.write_line("");
+ self.write_default_instance(w);
+ },
+ );
+ }
+
+ fn write_impl_message_full(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!(
+ "{}::MessageFull",
+ protobuf_crate_path(&self.customize.for_elem),
+ ),
+ &format!("{}", self.rust_name()),
+ |w| {
+ self.write_impl_message_full_fn_descriptor(w);
+ },
+ );
+ }
+
+ fn write_impl_value(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!(
+ "{}::reflect::ProtobufValue",
+ protobuf_crate_path(&self.customize.for_elem)
+ ),
+ &format!("{}", self.rust_name()),
+ |w| {
+ w.write_line(&format!(
+ "type RuntimeType = {}::reflect::rt::RuntimeTypeMessage<Self>;",
+ protobuf_crate_path(&self.customize.for_elem)
+ ));
+ },
+ )
+ }
+
+ fn write_impl_display(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ "::std::fmt::Display",
+ &format!("{}", self.rust_name()),
+ |w| {
+ w.def_fn(
+ "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result",
+ |w| {
+ w.write_line(&format!(
+ "{}::text_format::fmt(self, f)",
+ protobuf_crate_path(&self.customize.for_elem)
+ ));
+ },
+ );
+ },
+ );
+ }
+
+ fn supports_derive_partial_eq(&self) -> bool {
+ // There's stack overflow in the compiler when struct has too many fields
+ // https://github.com/rust-lang/rust/issues/40119
+ self.fields.len() <= 500
+ }
+
+ fn write_struct(&self, w: &mut CodeWriter) {
+ let mut derive = Vec::new();
+ if self.supports_derive_partial_eq() {
+ derive.push("PartialEq");
+ }
+ derive.extend(&["Clone", "Default", "Debug"]);
+ w.derive(&derive);
+ write_protoc_insertion_point_for_message(
+ w,
+ &self.customize.for_elem,
+ &self.message_descriptor,
+ );
+ w.pub_struct(&format!("{}", self.rust_name()), |w| {
+ if !self.fields_except_oneof().is_empty() {
+ w.comment("message fields");
+ for field in self.fields_except_oneof() {
+ field.write_struct_field(w);
+ }
+ }
+ if !self.oneofs().is_empty() {
+ w.comment("message oneof groups");
+ for oneof in self.oneofs() {
+ w.field_decl_vis(
+ Visibility::Public,
+ &oneof.oneof.field_name().to_string(),
+ &oneof.full_storage_type().to_code(&self.customize.for_elem),
+ );
+ }
+ }
+ w.comment("special fields");
+
+ let customize_special_fields = self
+ .customize
+ .child(
+ &Customize::default(),
+ &SpecialFieldPseudoDescriptor {
+ message: &self.message.message,
+ field: "special_fields",
+ },
+ )
+ .for_elem;
+
+ write_protoc_insertion_point_for_special_field(
+ w,
+ &customize_special_fields,
+ &self.message_descriptor,
+ "special_fields",
+ );
+ w.pub_field_decl(
+ "special_fields",
+ &format!(
+ "{}::SpecialFields",
+ protobuf_crate_path(&self.customize.for_elem)
+ ),
+ );
+ });
+ }
+
+ fn write_impl_default_for_amp(&self, w: &mut CodeWriter) {
+ w.impl_args_for_block(
+ &["'a"],
+ "::std::default::Default",
+ &format!("&'a {}", self.rust_name()),
+ |w| {
+ w.def_fn(&format!("default() -> &'a {}", self.rust_name()), |w| {
+ w.write_line(&format!(
+ "<{} as {}::Message>::default_instance()",
+ self.rust_name(),
+ protobuf_crate_path(&self.customize.for_elem),
+ ));
+ });
+ },
+ );
+ }
+
+ fn write_dummy_impl_partial_eq(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ "::std::cmp::PartialEq",
+ &format!("{}", self.rust_name()),
+ |w| {
+ w.def_fn("eq(&self, _: &Self) -> bool", |w| {
+ w.comment("https://github.com/rust-lang/rust/issues/40119");
+ w.unimplemented();
+ });
+ },
+ );
+ }
+
+ pub fn write(&self, w: &mut CodeWriter) -> anyhow::Result<()> {
+ w.all_documentation(self.info, self.path);
+ self.write_struct(w);
+
+ w.write_line("");
+ self.write_impl_default_for_amp(w);
+
+ if !self.supports_derive_partial_eq() {
+ w.write_line("");
+ self.write_dummy_impl_partial_eq(w);
+ }
+
+ w.write_line("");
+ self.write_impl_self(w);
+ w.write_line("");
+ self.write_impl_message(w);
+ if !self.lite_runtime {
+ w.write_line("");
+ self.write_impl_message_full(w);
+ }
+ if !self.lite_runtime {
+ w.write_line("");
+ self.write_impl_display(w);
+
+ w.write_line("");
+ self.write_impl_value(w);
+ }
+
+ let mod_name = message_name_to_nested_mod_name(&self.message.message.name());
+
+ let oneofs = self.oneofs();
+ let nested_messages: Vec<_> = self
+ .message
+ .to_scope()
+ .messages()
+ .into_iter()
+ .filter(|nested| {
+ // ignore map entries, because they are not used in map fields
+ !nested.is_map()
+ })
+ .collect();
+ let nested_enums = self.message.to_scope().enums();
+
+ if !oneofs.is_empty() || !nested_messages.is_empty() || !nested_enums.is_empty() {
+ w.write_line("");
+ w.write_line(&format!(
+ "/// Nested message and enums of message `{}`",
+ self.message.message.name()
+ ));
+ w.pub_mod(&mod_name.to_string(), |w| {
+ let mut first = true;
+
+ for oneof in &oneofs {
+ w.write_line("");
+ oneof.write(w);
+ }
+
+ static NESTED_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+ let nested_type_number = *NESTED_TYPE_NUMBER.get(|| {
+ MessageDescriptor::for_type::<DescriptorProto>()
+ .field_by_name("nested_type")
+ .expect("`nested_type` must exist")
+ .proto()
+ .number()
+ });
+
+ let mut path = self.path.to_vec();
+ path.extend(&[nested_type_number, 0]);
+ for (id, nested) in nested_messages.iter().enumerate() {
+ let len = path.len() - 1;
+ path[len] = id as i32;
+
+ if !first {
+ w.write_line("");
+ }
+ first = false;
+ MessageGen::new(
+ &self.file_descriptor,
+ nested,
+ self.root_scope,
+ &self.customize,
+ &path,
+ self.info,
+ )
+ // TODO: do not unwrap.
+ .unwrap()
+ .write(w)
+ // TODO: do not unwrap.
+ .unwrap();
+ }
+
+ static ENUM_TYPE_NUMBER: protobuf::rt::Lazy<i32> = protobuf::rt::Lazy::new();
+ let enum_type_number = *ENUM_TYPE_NUMBER.get(|| {
+ MessageDescriptor::for_type::<DescriptorProto>()
+ .field_by_name("enum_type")
+ .expect("`enum_type` must exist")
+ .proto()
+ .number()
+ });
+
+ let len = path.len() - 2;
+ path[len] = enum_type_number;
+ for (id, enum_type) in self.message.to_scope().enums().iter().enumerate() {
+ let len = path.len() - 1;
+ path[len] = id as i32;
+
+ if !first {
+ w.write_line("");
+ }
+ first = false;
+ EnumGen::new(
+ enum_type,
+ &self.customize,
+ self.root_scope,
+ &path,
+ self.info,
+ )
+ .write(w);
+ }
+ });
+ }
+ Ok(())
+ }
+}
+
+pub(crate) fn message_name_to_nested_mod_name(message_name: &str) -> RustIdent {
+ let mod_name = snake_case(message_name);
+ RustIdent::new(&mod_name)
+}
diff --git a/src/gen/mod.rs b/src/gen/mod.rs
new file mode 100644
index 0000000..6d0435c
--- /dev/null
+++ b/src/gen/mod.rs
@@ -0,0 +1,21 @@
+pub(crate) mod all;
+pub(crate) mod code_writer;
+pub(crate) mod descriptor;
+pub(crate) mod enums;
+pub(crate) mod extensions;
+pub(crate) mod field;
+pub(crate) mod file;
+pub(crate) mod file_and_mod;
+pub(crate) mod file_descriptor;
+pub(crate) mod inside;
+mod map;
+pub(crate) mod message;
+pub(crate) mod mod_rs;
+pub(crate) mod oneof;
+pub(crate) mod paths;
+pub(crate) mod protoc_insertion_point;
+pub(crate) mod rust;
+pub(crate) mod rust_types_values;
+pub(crate) mod scope;
+pub(crate) mod strx;
+pub(crate) mod well_known_types;
diff --git a/src/gen/mod_rs.rs b/src/gen/mod_rs.rs
new file mode 100644
index 0000000..a149319
--- /dev/null
+++ b/src/gen/mod_rs.rs
@@ -0,0 +1,18 @@
+use crate::compiler_plugin;
+use crate::gen::code_writer::CodeWriter;
+
+pub(crate) fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
+ let v = CodeWriter::with_no_error(|w| {
+ w.comment(&format!("{}generated", "@"));
+ w.write_line("");
+ let mut mods: Vec<&String> = mods.into_iter().collect();
+ mods.sort();
+ for m in mods {
+ w.write_line(&format!("pub mod {};", m));
+ }
+ });
+ compiler_plugin::GenResult {
+ name: "mod.rs".to_owned(),
+ content: v.into_bytes(),
+ }
+}
diff --git a/src/gen/oneof.rs b/src/gen/oneof.rs
new file mode 100644
index 0000000..3f34cdc
--- /dev/null
+++ b/src/gen/oneof.rs
@@ -0,0 +1,373 @@
+//! Oneof-related codegen functions.
+
+use std::collections::HashSet;
+
+use protobuf::descriptor::field_descriptor_proto;
+use protobuf::descriptor::file_options;
+use protobuf::reflect::FieldDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+
+use crate::customize::ctx::CustomizeElemCtx;
+use crate::customize::Customize;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::code_writer::Visibility;
+use crate::gen::field::elem::FieldElem;
+use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name;
+use crate::gen::field::FieldGen;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::MessageGen;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof;
+use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::path::RustPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust_types_values::make_path;
+use crate::gen::rust_types_values::RustType;
+use crate::gen::scope::OneofVariantWithContext;
+use crate::gen::scope::OneofWithContext;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+
+// oneof one { ... }
+#[derive(Clone)]
+pub(crate) struct OneofField<'a> {
+ pub elem: FieldElem<'a>,
+ pub oneof_variant_rust_name: RustIdent,
+ pub oneof_field_name: RustIdent,
+ pub type_name: RustIdentWithPath,
+ pub boxed: bool,
+}
+
+impl<'a> OneofField<'a> {
+ // Detecting recursion: if oneof fields contains a self-reference
+ // or another message which has a reference to self,
+ // put oneof variant into a box.
+ fn need_boxed(
+ field: &FieldDescriptor,
+ root_scope: &RootScope,
+ owner_name: &ProtobufAbsPath,
+ ) -> bool {
+ let mut visited_messages = HashSet::new();
+ let mut fields = vec![field.clone()];
+ while let Some(field) = fields.pop() {
+ if field.proto().type_() == field_descriptor_proto::Type::TYPE_MESSAGE {
+ let message_name = ProtobufAbsPath::from(field.proto().type_name());
+ if !visited_messages.insert(message_name.clone()) {
+ continue;
+ }
+ if message_name == *owner_name {
+ return true;
+ }
+ let message = root_scope.find_message(&message_name);
+ fields.extend(
+ message
+ .message
+ .fields()
+ .into_iter()
+ .filter(|f| f.containing_oneof().is_some()),
+ );
+ }
+ }
+ false
+ }
+
+ pub fn parse(
+ oneof: &OneofWithContext<'a>,
+ field: &FieldDescriptor,
+ elem: FieldElem<'a>,
+ root_scope: &RootScope,
+ ) -> OneofField<'a> {
+ let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute());
+
+ OneofField {
+ elem,
+ type_name: oneof.rust_name(),
+ boxed,
+ oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()),
+ oneof_field_name: oneof.field_name(),
+ }
+ }
+
+ pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
+ let t = self.elem.rust_storage_elem_type(reference);
+
+ if self.boxed {
+ RustType::Uniq(Box::new(t))
+ } else {
+ t
+ }
+ }
+
+ pub fn variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath {
+ make_path(
+ reference,
+ &self
+ .type_name
+ .to_path()
+ .with_ident(self.oneof_variant_rust_name.clone()),
+ )
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofVariantGen<'a> {
+ oneof: &'a OneofGen<'a>,
+ _variant: OneofVariantWithContext<'a>,
+ oneof_field: OneofField<'a>,
+ pub field: FieldGen<'a>,
+ _path: String,
+}
+
+impl<'a> OneofVariantGen<'a> {
+ fn parse(
+ oneof: &'a OneofGen<'a>,
+ variant: OneofVariantWithContext<'a>,
+ field: &'a FieldGen,
+ _root_scope: &RootScope,
+ ) -> OneofVariantGen<'a> {
+ OneofVariantGen {
+ oneof,
+ _variant: variant.clone(),
+ field: field.clone(),
+ _path: format!(
+ "{}::{}",
+ oneof.type_name_relative(&oneof.oneof.message.scope.rust_path_to_file()),
+ field.rust_name
+ ),
+ oneof_field: OneofField::parse(
+ variant.oneof,
+ &field.proto_field.field,
+ field.elem().clone(),
+ oneof.message.root_scope,
+ ),
+ }
+ }
+
+ pub fn rust_type(&self, reference: &FileAndMod) -> RustType {
+ self.oneof_field.rust_type(reference)
+ }
+
+ pub fn path(&self, reference: &FileAndMod) -> RustPath {
+ RustPath::from(format!(
+ "{}::{}",
+ self.oneof.type_name_relative(&reference.relative_mod),
+ self.oneof_field.oneof_variant_rust_name,
+ ))
+ }
+
+ pub(crate) fn elem(&self) -> &FieldElem<'_> {
+ self.field.elem()
+ }
+}
+
+pub(crate) struct OneofGen<'a> {
+ // Message containing this oneof
+ message: &'a MessageGen<'a>,
+ pub oneof: OneofWithContext<'a>,
+ customize: CustomizeElemCtx<'a>,
+ lite_runtime: bool,
+}
+
+impl<'a> OneofGen<'a> {
+ pub fn parse(
+ message: &'a MessageGen,
+ oneof: OneofWithContext<'a>,
+ parent_customize: &CustomizeElemCtx<'a>,
+ ) -> OneofGen<'a> {
+ let customize = parent_customize.child(&Customize::default(), &oneof.oneof);
+ let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| {
+ oneof
+ .message
+ .file_descriptor()
+ .proto()
+ .options
+ .optimize_for()
+ == file_options::OptimizeMode::LITE_RUNTIME
+ });
+ OneofGen {
+ message,
+ oneof,
+ customize,
+ lite_runtime,
+ }
+ }
+
+ pub fn type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath {
+ make_path(source, &self.oneof.rust_name())
+ }
+
+ pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
+ self.oneof
+ .variants()
+ .into_iter()
+ .filter_map(|v| {
+ let field = self
+ .message
+ .fields
+ .iter()
+ .filter(|f| f.proto_field.name() == v.field.name())
+ .next()
+ .expect(&format!("field not found by name: {}", v.field.name()));
+ match field.proto_type {
+ field_descriptor_proto::Type::TYPE_GROUP => None,
+ _ => Some(OneofVariantGen::parse(
+ self,
+ v,
+ field,
+ self.message.root_scope,
+ )),
+ }
+ })
+ .collect()
+ }
+
+ pub fn full_storage_type(&self) -> RustType {
+ RustType::Option(Box::new(RustType::Oneof(
+ self.type_name_relative(
+ &self
+ .oneof
+ .message
+ .scope
+ .file_and_mod(self.customize.for_elem.clone())
+ .relative_mod,
+ )
+ .clone(),
+ )))
+ }
+
+ fn file_and_mod(&self) -> FileAndMod {
+ let mut file_and_mod = self
+ .message
+ .message
+ .scope
+ .file_and_mod(self.customize.for_elem.clone());
+ file_and_mod
+ .relative_mod
+ .push_ident(self.message.message.mod_name());
+ file_and_mod
+ }
+
+ fn write_enum(&self, w: &mut CodeWriter) {
+ let derive = vec!["Clone", "PartialEq", "Debug"];
+ w.derive(&derive);
+ w.write_line("#[non_exhaustive]");
+ write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof);
+ w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| {
+ for variant in self.variants_except_group() {
+ write_protoc_insertion_point_for_oneof_field(
+ w,
+ &self.customize.for_children,
+ &variant.field.proto_field.field,
+ );
+ w.write_line(&format!(
+ "{}({}),",
+ variant.oneof_field.oneof_variant_rust_name,
+ &variant
+ .rust_type(&self.file_and_mod())
+ .to_code(&self.customize.for_elem)
+ ));
+ }
+ });
+ }
+
+ fn write_impl_oneof(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)),
+ self.oneof.rust_name().ident.to_string(),
+ |_w| {
+ // nothing here yet
+ },
+ );
+ }
+
+ fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "descriptor() -> {}::reflect::OneofDescriptor",
+ protobuf_crate_path(&self.customize.for_elem),
+ );
+ w.def_fn(&sig, |w| {
+ w.lazy_static(
+ "descriptor",
+ &format!(
+ "{}::reflect::OneofDescriptor",
+ protobuf_crate_path(&self.customize.for_elem),
+ ),
+ &protobuf_crate_path(&self.customize.for_elem).to_string(),
+ );
+ let message_type = make_path(
+ &self
+ .oneof
+ .message
+ .scope()
+ .rust_path_to_file()
+ .append(self.oneof.message.mod_name().into_rel_path()),
+ &self.oneof.message.rust_name_to_file(),
+ );
+ let expr = format!(
+ "<{} as {}::MessageFull>::descriptor().oneof_by_name(\"{}\").unwrap()",
+ message_type,
+ protobuf_crate_path(&self.customize.for_elem),
+ self.oneof.oneof.name()
+ );
+ w.write_line(&format!("descriptor.get(|| {}).clone()", expr));
+ });
+ }
+
+ fn write_impl_oneof_full(&self, w: &mut CodeWriter) {
+ w.impl_for_block(
+ &format!(
+ "{}::OneofFull",
+ protobuf_crate_path(&self.customize.for_elem)
+ ),
+ self.oneof.rust_name().ident.to_string(),
+ |w| self.write_impl_oneof_full_fn_descriptor(w),
+ )
+ }
+
+ fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) {
+ let sig = format!(
+ "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData",
+ protobuf_crate_path(&self.customize.for_elem)
+ );
+ w.fn_block(
+ Visibility::Path(
+ self.oneof
+ .rust_name()
+ .path
+ .into_relative_or_panic()
+ .to_reverse(),
+ ),
+ &sig,
+ |w| {
+ w.write_line(&format!(
+ "{}::reflect::GeneratedOneofDescriptorData::new::<{}>(\"{}\")",
+ protobuf_crate_path(&self.customize.for_elem),
+ &self.oneof.rust_name().ident,
+ self.oneof.oneof.name(),
+ ));
+ },
+ );
+ }
+
+ fn write_impl_self(&self, w: &mut CodeWriter) {
+ w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| {
+ if !self.lite_runtime {
+ self.write_generated_oneof_descriptor_data(w);
+ }
+ });
+ }
+
+ pub fn write(&self, w: &mut CodeWriter) {
+ self.write_enum(w);
+ w.write_line("");
+ self.write_impl_oneof(w);
+ if !self.lite_runtime {
+ w.write_line("");
+ self.write_impl_oneof_full(w);
+ }
+ w.write_line("");
+ self.write_impl_self(w);
+ }
+}
diff --git a/src/file.rs b/src/gen/paths.rs
index 413e1bc..2bc958d 100644
--- a/src/file.rs
+++ b/src/gen/paths.rs
@@ -1,6 +1,9 @@
-use crate::rust;
-use crate::rust_name::RustIdent;
-use crate::strx;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+use crate::gen::strx;
+use crate::gen::well_known_types::WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES;
+use crate::Customize;
// Copy-pasted from libsyntax.
fn ident_start(c: char) -> bool {
@@ -33,19 +36,38 @@ pub(crate) fn proto_path_to_rust_mod(path: &str) -> RustIdent {
})
.collect::<String>();
- let name = if rust::is_rust_keyword(&name) {
- format!("{}_pb", name)
- } else {
- name
- };
- RustIdent::from(name)
+ RustIdent::new(&name)
+}
+
+/// Used in protobuf-codegen-identical-test
+pub fn proto_name_to_rs(proto_file_path: &str) -> String {
+ format!("{}.rs", proto_path_to_rust_mod(proto_file_path))
+}
+
+pub(crate) fn proto_path_to_fn_file_descriptor(
+ proto_path: &str,
+ customize: &Customize,
+) -> RustPath {
+ let protobuf_crate = protobuf_crate_path(customize);
+ match proto_path {
+ "rustproto.proto" => protobuf_crate.append("rustproto::file_descriptor".into()),
+ "google/protobuf/descriptor.proto" => {
+ protobuf_crate.append("descriptor::file_descriptor".into())
+ }
+ s if WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES.contains(&s) => protobuf_crate
+ .append_ident("well_known_types".into())
+ .append_ident(proto_path_to_rust_mod(s))
+ .append_ident("file_descriptor".into()),
+ s => RustPath::super_path()
+ .append_ident(proto_path_to_rust_mod(s))
+ .append_ident("file_descriptor".into()),
+ }
}
#[cfg(test)]
mod test {
-
use super::proto_path_to_rust_mod;
- use crate::rust_name::RustIdent;
+ use crate::gen::rust::ident::RustIdent;
#[test]
fn test_mod_path_proto_ext() {
diff --git a/src/gen/protoc_insertion_point.rs b/src/gen/protoc_insertion_point.rs
new file mode 100644
index 0000000..0083215
--- /dev/null
+++ b/src/gen/protoc_insertion_point.rs
@@ -0,0 +1,80 @@
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::EnumValueDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+
+use crate::gen::code_writer::CodeWriter;
+use crate::Customize;
+
+/// Write `// @protoc_insertion_point(...)` before the element.
+///
+/// This is similar to what `protoc` codegen does for C++ or Java.
+/// This can be used to modify the generated code.
+fn write_protoc_insertion_point(w: &mut CodeWriter, customize: &Customize, arg: &str) {
+ for line in customize.before.iter().flat_map(|s| s.lines()) {
+ w.write_line(line);
+ }
+ w.comment(&format!("@@protoc_insertion_point({})", arg));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_message(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ message: &MessageDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("message:{}", message.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_field(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ field: &FieldDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("field:{}", field.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_special_field(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ message: &MessageDescriptor,
+ field: &str,
+) {
+ write_protoc_insertion_point(
+ w,
+ customize,
+ &format!("special_field:{}.{}", message.full_name(), field),
+ );
+}
+
+pub(crate) fn write_protoc_insertion_point_for_enum(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ enumeration: &EnumDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("enum:{}", enumeration.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_enum_value(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ value: &EnumValueDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("enum_value:{}", value.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_oneof(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ oneof: &OneofDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("oneof:{}", oneof.full_name()));
+}
+
+pub(crate) fn write_protoc_insertion_point_for_oneof_field(
+ w: &mut CodeWriter,
+ customize: &Customize,
+ field: &FieldDescriptor,
+) {
+ write_protoc_insertion_point(w, customize, &format!("oneof_field:{}", field.full_name()));
+}
diff --git a/src/gen/rust/component.rs b/src/gen/rust/component.rs
new file mode 100644
index 0000000..8bf9c85
--- /dev/null
+++ b/src/gen/rust/component.rs
@@ -0,0 +1,34 @@
+use std::fmt;
+use std::fmt::Formatter;
+
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::keywords::parse_rust_keyword;
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum RustPathComponent {
+ Ident(RustIdent),
+ Keyword(&'static str),
+}
+
+impl fmt::Display for RustPathComponent {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ match self {
+ RustPathComponent::Ident(ident) => write!(f, "{}", ident),
+ RustPathComponent::Keyword(keyword) => write!(f, "{}", keyword),
+ }
+ }
+}
+
+impl RustPathComponent {
+ pub(crate) const SUPER: RustPathComponent = RustPathComponent::Keyword("super");
+
+ pub(crate) fn parse(s: &str) -> RustPathComponent {
+ if s.starts_with("r#") {
+ RustPathComponent::Ident(RustIdent::new(&s[2..]))
+ } else if let Some(kw) = parse_rust_keyword(s) {
+ RustPathComponent::Keyword(kw)
+ } else {
+ RustPathComponent::Ident(RustIdent::new(s))
+ }
+ }
+}
diff --git a/src/gen/rust/ident.rs b/src/gen/rust/ident.rs
new file mode 100644
index 0000000..df1d1f0
--- /dev/null
+++ b/src/gen/rust/ident.rs
@@ -0,0 +1,62 @@
+use std::fmt;
+
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::keywords::is_rust_keyword;
+use crate::gen::rust::rel_path::RustRelativePath;
+
+/// Valid Rust identifier
+#[derive(Eq, PartialEq, Debug, Clone, Hash)]
+pub(crate) struct RustIdent(String);
+
+impl RustIdent {
+ pub fn new(s: &str) -> RustIdent {
+ assert!(!s.is_empty());
+ assert!(!s.contains("/"), "{}", s);
+ assert!(!s.contains("."), "{}", s);
+ assert!(!s.contains(":"), "{}", s);
+ assert!(!s.contains(" "), "{}", s);
+ assert!(!s.contains("#"), "{}", s);
+ RustIdent(s.to_owned())
+ }
+
+ pub(crate) fn get(&self) -> &str {
+ &self.0
+ }
+
+ pub fn into_string(self) -> String {
+ self.0
+ }
+
+ pub fn to_path(&self) -> RustIdentWithPath {
+ RustIdentWithPath::from(&self.0)
+ }
+
+ pub(crate) fn into_rel_path(self) -> RustRelativePath {
+ RustRelativePath::from_idents([self])
+ }
+}
+
+impl fmt::Display for RustIdent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // Rust-protobuf uses `_` suffix to escape identifiers instead of raw identifiers
+ // because some identifiers cannot be escaped as raw identifiers,
+ // e.g. `r#self` is not a valid raw identifier.
+ if is_rust_keyword(&self.0) {
+ write!(f, "{}_", self.0)
+ } else {
+ write!(f, "{}", self.0)
+ }
+ }
+}
+
+impl From<&'_ str> for RustIdent {
+ fn from(s: &str) -> Self {
+ RustIdent::new(s)
+ }
+}
+
+impl From<String> for RustIdent {
+ fn from(s: String) -> Self {
+ RustIdent::new(&s)
+ }
+}
diff --git a/src/gen/rust/ident_with_path.rs b/src/gen/rust/ident_with_path.rs
new file mode 100644
index 0000000..bb121a7
--- /dev/null
+++ b/src/gen/rust/ident_with_path.rs
@@ -0,0 +1,45 @@
+use std::fmt;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+
+#[derive(Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustIdentWithPath {
+ pub path: RustPath,
+ pub ident: RustIdent,
+}
+
+impl RustIdentWithPath {
+ pub fn new(s: String) -> RustIdentWithPath {
+ let mut path = RustPath::from(s);
+ let ident = match path.path.path.pop() {
+ None => panic!("empty path"),
+ Some(RustPathComponent::Ident(ident)) => ident,
+ Some(RustPathComponent::Keyword(kw)) => {
+ panic!("last path component is a keyword: {}", kw)
+ }
+ };
+ RustIdentWithPath { path, ident }
+ }
+
+ pub fn prepend_ident(&mut self, ident: RustIdent) {
+ self.path.prepend_ident(ident)
+ }
+
+ pub fn to_path(&self) -> RustPath {
+ self.path.clone().append_ident(self.ident.clone())
+ }
+}
+
+impl<S: Into<String>> From<S> for RustIdentWithPath {
+ fn from(s: S) -> Self {
+ RustIdentWithPath::new(s.into())
+ }
+}
+
+impl fmt::Display for RustIdentWithPath {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.to_path(), f)
+ }
+}
diff --git a/src/rust.rs b/src/gen/rust/keywords.rs
index 263cba0..155977d 100644
--- a/src/rust.rs
+++ b/src/gen/rust/keywords.rs
@@ -1,5 +1,6 @@
#[cfg_attr(rustfmt, rustfmt_skip)]
static RUST_KEYWORDS: &'static [&'static str] = &[
+ "_",
"as",
"async",
"await",
@@ -57,6 +58,29 @@ static RUST_KEYWORDS: &'static [&'static str] = &[
"macro",
];
-pub fn is_rust_keyword(ident: &str) -> bool {
- RUST_KEYWORDS.contains(&ident)
+// https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/3
+#[cfg_attr(rustfmt, rustfmt_skip)]
+static RUST_KEYWORDS_WHICH_CANNOT_BE_RAW: &'static [&'static str] = &[
+ "super",
+ "self",
+ "Self",
+ "extern",
+ "crate",
+];
+
+pub(crate) fn parse_rust_keyword(word: &str) -> Option<&'static str> {
+ RUST_KEYWORDS.iter().cloned().find(|&kw| kw == word)
+}
+
+pub(crate) fn is_rust_keyword(ident: &str) -> bool {
+ parse_rust_keyword(ident).is_some()
+}
+
+#[allow(dead_code)]
+pub(crate) fn is_rust_keyword_which_cannot_be_raw(ident: &str) -> bool {
+ RUST_KEYWORDS_WHICH_CANNOT_BE_RAW
+ .iter()
+ .cloned()
+ .find(|&kw| kw == ident)
+ .is_some()
}
diff --git a/src/gen/rust/mod.rs b/src/gen/rust/mod.rs
new file mode 100644
index 0000000..343ba92
--- /dev/null
+++ b/src/gen/rust/mod.rs
@@ -0,0 +1,8 @@
+pub(crate) mod component;
+pub(crate) mod ident;
+pub(crate) mod ident_with_path;
+pub(crate) mod keywords;
+pub(crate) mod path;
+pub(crate) mod quote;
+pub(crate) mod rel_path;
+pub(crate) mod snippets;
diff --git a/src/gen/rust/path.rs b/src/gen/rust/path.rs
new file mode 100644
index 0000000..84d38a3
--- /dev/null
+++ b/src/gen/rust/path.rs
@@ -0,0 +1,99 @@
+use std::fmt;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+
+#[derive(Default, Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustPath {
+ pub(crate) absolute: bool,
+ pub(crate) path: RustRelativePath,
+}
+
+impl RustPath {
+ pub fn super_path() -> RustPath {
+ RustPath::from("super")
+ }
+
+ pub fn is_absolute(&self) -> bool {
+ self.absolute
+ }
+
+ pub fn with_ident(self, ident: RustIdent) -> RustIdentWithPath {
+ RustIdentWithPath { path: self, ident }
+ }
+
+ pub fn first(&self) -> Option<RustPathComponent> {
+ assert!(!self.absolute);
+ self.path.first()
+ }
+
+ pub fn remove_first(&mut self) -> Option<RustPathComponent> {
+ assert!(!self.absolute);
+ self.path.remove_first()
+ }
+
+ pub fn prepend_ident(&mut self, ident: RustIdent) {
+ assert!(!self.absolute);
+ self.path.prepend_ident(ident);
+ }
+
+ pub fn append(self, path: RustPath) -> RustPath {
+ if path.absolute {
+ path
+ } else {
+ RustPath {
+ absolute: self.absolute,
+ path: self.path.append(path.path),
+ }
+ }
+ }
+
+ pub(crate) fn append_component(mut self, component: RustPathComponent) -> RustPath {
+ self.path.path.push(component);
+ self
+ }
+
+ pub fn append_ident(self, ident: RustIdent) -> RustPath {
+ self.append_component(RustPathComponent::Ident(ident))
+ }
+
+ pub fn append_with_ident(self, path: RustIdentWithPath) -> RustIdentWithPath {
+ self.append(path.path).with_ident(path.ident)
+ }
+
+ pub fn into_relative_or_panic(self) -> RustRelativePath {
+ assert!(!self.absolute);
+ self.path
+ }
+}
+
+impl From<&'_ str> for RustPath {
+ fn from(s: &str) -> Self {
+ let (s, absolute) = if s.starts_with("::") {
+ (&s[2..], true)
+ } else {
+ (s, false)
+ };
+ RustPath {
+ absolute,
+ path: RustRelativePath::from(s),
+ }
+ }
+}
+
+impl From<String> for RustPath {
+ fn from(s: String) -> Self {
+ RustPath::from(&s[..])
+ }
+}
+
+impl fmt::Display for RustPath {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if self.absolute {
+ write!(f, "::")?;
+ }
+ write!(f, "{}", self.path)
+ }
+}
diff --git a/src/gen/rust/quote.rs b/src/gen/rust/quote.rs
new file mode 100644
index 0000000..f01b4ee
--- /dev/null
+++ b/src/gen/rust/quote.rs
@@ -0,0 +1,58 @@
+fn hex_digit(value: u32) -> char {
+ if value < 10 {
+ (b'0' + value as u8) as char
+ } else if value < 0x10 {
+ (b'a' + value as u8 - 10) as char
+ } else {
+ unreachable!()
+ }
+}
+
+pub fn quote_escape_str(s: &str) -> String {
+ let mut buf = String::new();
+ buf.push('"');
+ buf.extend(s.chars().flat_map(|c| c.escape_default()));
+ buf.push('"');
+ buf
+}
+
+pub fn quote_escape_bytes(bytes: &[u8]) -> String {
+ let mut buf = String::new();
+ buf.push('b');
+ buf.push('"');
+ for &b in bytes {
+ match b {
+ b'\n' => buf.push_str(r"\n"),
+ b'\r' => buf.push_str(r"\r"),
+ b'\t' => buf.push_str(r"\t"),
+ b'"' => buf.push_str("\\\""),
+ b'\\' => buf.push_str(r"\\"),
+ b'\x20'..=b'\x7e' => buf.push(b as char),
+ _ => {
+ buf.push_str(r"\x");
+ buf.push(hex_digit((b as u32) >> 4));
+ buf.push(hex_digit((b as u32) & 0x0f));
+ }
+ }
+ }
+ buf.push('"');
+ buf
+}
+
+#[cfg(test)]
+mod test {
+
+ use super::*;
+
+ #[test]
+ fn test_quote_escape_bytes() {
+ assert_eq!("b\"\"", quote_escape_bytes(b""));
+ assert_eq!("b\"xyZW\"", quote_escape_bytes(b"xyZW"));
+ assert_eq!("b\"aa\\\"bb\"", quote_escape_bytes(b"aa\"bb"));
+ assert_eq!("b\"aa\\r\\n\\tbb\"", quote_escape_bytes(b"aa\r\n\tbb"));
+ assert_eq!(
+ "b\"\\x00\\x01\\x12\\xfe\\xff\"",
+ quote_escape_bytes(b"\x00\x01\x12\xfe\xff")
+ );
+ }
+}
diff --git a/src/gen/rust/rel_path.rs b/src/gen/rust/rel_path.rs
new file mode 100644
index 0000000..6188ce6
--- /dev/null
+++ b/src/gen/rust/rel_path.rs
@@ -0,0 +1,97 @@
+use std::fmt;
+use std::iter;
+
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::path::RustPath;
+
+#[derive(Default, Eq, PartialEq, Debug, Clone)]
+pub(crate) struct RustRelativePath {
+ pub(crate) path: Vec<RustPathComponent>,
+}
+
+impl RustRelativePath {
+ pub fn into_path(self) -> RustPath {
+ RustPath {
+ absolute: false,
+ path: self,
+ }
+ }
+
+ pub fn _empty() -> RustRelativePath {
+ RustRelativePath { path: Vec::new() }
+ }
+
+ pub fn from_components<I: IntoIterator<Item = RustPathComponent>>(i: I) -> RustRelativePath {
+ RustRelativePath {
+ path: i.into_iter().collect(),
+ }
+ }
+
+ pub fn from_idents<I: IntoIterator<Item = RustIdent>>(i: I) -> RustRelativePath {
+ Self::from_components(i.into_iter().map(RustPathComponent::Ident))
+ }
+
+ pub fn is_empty(&self) -> bool {
+ self.path.is_empty()
+ }
+
+ pub fn first(&self) -> Option<RustPathComponent> {
+ self.path.iter().cloned().next()
+ }
+
+ pub fn remove_first(&mut self) -> Option<RustPathComponent> {
+ if self.path.is_empty() {
+ None
+ } else {
+ Some(self.path.remove(0))
+ }
+ }
+
+ pub fn prepend_ident(&mut self, ident: RustIdent) {
+ self.path.insert(0, RustPathComponent::Ident(ident));
+ }
+
+ pub fn append(mut self, path: RustRelativePath) -> RustRelativePath {
+ for c in path.path {
+ self.path.push(c);
+ }
+ self
+ }
+
+ pub fn push_ident(&mut self, ident: RustIdent) {
+ self.path.push(RustPathComponent::Ident(ident));
+ }
+
+ pub fn append_ident(mut self, ident: RustIdent) -> RustRelativePath {
+ self.push_ident(ident);
+ self
+ }
+
+ pub fn to_reverse(&self) -> RustRelativePath {
+ RustRelativePath::from_components(
+ iter::repeat(RustPathComponent::SUPER).take(self.path.len()),
+ )
+ }
+}
+
+impl fmt::Display for RustRelativePath {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ for (i, c) in self.path.iter().enumerate() {
+ if i != 0 {
+ write!(f, "::")?;
+ }
+ write!(f, "{}", c)?;
+ }
+ Ok(())
+ }
+}
+
+impl From<&'_ str> for RustRelativePath {
+ fn from(s: &str) -> Self {
+ assert!(!s.starts_with("::"), "path is absolute: {:?}", s);
+ RustRelativePath {
+ path: s.split("::").map(RustPathComponent::parse).collect(),
+ }
+ }
+}
diff --git a/src/gen/rust/snippets.rs b/src/gen/rust/snippets.rs
new file mode 100644
index 0000000..c10c4a9
--- /dev/null
+++ b/src/gen/rust/snippets.rs
@@ -0,0 +1,10 @@
+pub(crate) const EXPR_NONE: &str = "::std::option::Option::None";
+pub(crate) const EXPR_VEC_NEW: &str = "::std::vec::Vec::new()";
+
+fn expr_vec_with_capacity(capacity: &str) -> String {
+ format!("::std::vec::Vec::with_capacity({})", capacity)
+}
+
+pub(crate) fn expr_vec_with_capacity_const(capacity: usize) -> String {
+ expr_vec_with_capacity(&capacity.to_string())
+}
diff --git a/src/rust_types_values.rs b/src/gen/rust_types_values.rs
index e9b017d..cd67f2b 100644
--- a/src/rust_types_values.rs
+++ b/src/gen/rust_types_values.rs
@@ -1,16 +1,28 @@
use std::cmp;
-use inside::protobuf_crate_path;
-use message::RustTypeMessage;
+use once_cell::sync::Lazy;
use protobuf::descriptor::*;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use scope::RootScope;
-use scope::WithScope;
-use strx::capitalize;
-use Customize;
-
-use super::well_known_types::is_well_known_type_full;
+use protobuf::reflect::FileDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+use regex::Regex;
+
+use crate::customize::Customize;
+use crate::gen::field::type_ext::TypeExt;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::inside::protobuf_crate_path;
+use crate::gen::message::RustTypeMessage;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::rust::component::RustPathComponent;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::path::RustPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::rust::snippets::EXPR_NONE;
+use crate::gen::rust::snippets::EXPR_VEC_NEW;
+use crate::gen::scope::RootScope;
+use crate::gen::scope::WithScope;
+use crate::gen::strx::capitalize;
+use crate::gen::well_known_types::is_well_known_type_full;
// Represent subset of rust types used in generated code
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -28,9 +40,7 @@ pub(crate) enum RustType {
// str, not &str
Str,
Option(Box<RustType>),
- SingularField(Box<RustType>),
- SingularPtrField(Box<RustType>),
- RepeatedField(Box<RustType>),
+ MessageField(Box<RustType>),
// Box<T>
Uniq(Box<RustType>),
// &T
@@ -38,9 +48,11 @@ pub(crate) enum RustType {
// protobuf message
Message(RustTypeMessage),
// protobuf enum, not any enum
- Enum(String, RustIdent),
+ Enum(RustIdentWithPath, RustIdent, i32),
+ // protobuf enum or unknown
+ EnumOrUnknown(RustIdentWithPath, RustIdent, i32),
// oneof enum
- Oneof(String),
+ Oneof(RustIdentWithPath),
// bytes::Bytes
Bytes,
// chars::Chars
@@ -69,25 +81,20 @@ impl RustType {
RustType::Option(ref param) => {
format!("::std::option::Option<{}>", param.to_code(customize))
}
- RustType::SingularField(ref param) => format!(
- "{}::SingularField<{}>",
- protobuf_crate_path(customize),
- param.to_code(customize)
- ),
- RustType::SingularPtrField(ref param) => format!(
- "{}::SingularPtrField<{}>",
- protobuf_crate_path(customize),
- param.to_code(customize)
- ),
- RustType::RepeatedField(ref param) => format!(
- "{}::RepeatedField<{}>",
+ RustType::MessageField(ref param) => format!(
+ "{}::MessageField<{}>",
protobuf_crate_path(customize),
param.to_code(customize)
),
RustType::Uniq(ref param) => format!("::std::boxed::Box<{}>", param.to_code(customize)),
RustType::Ref(ref param) => format!("&{}", param.to_code(customize)),
RustType::Message(ref name) => format!("{}", name),
- RustType::Enum(ref name, _) | RustType::Oneof(ref name) => format!("{}", name),
+ RustType::Enum(ref name, ..) | RustType::Oneof(ref name) => format!("{}", name),
+ RustType::EnumOrUnknown(ref name, ..) => format!(
+ "{}::EnumOrUnknown<{}>",
+ protobuf_crate_path(customize),
+ name
+ ),
RustType::Group => format!("<group>"),
RustType::Bytes => format!("::bytes::Bytes"),
RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)),
@@ -96,12 +103,26 @@ impl RustType {
}
impl RustType {
- pub fn u8() -> RustType {
+ pub(crate) fn u8() -> RustType {
RustType::Int(false, 8)
}
+ pub(crate) fn i32() -> RustType {
+ RustType::Int(true, 32)
+ }
+
+ /// `&str`.
+ pub(crate) fn amp_str() -> RustType {
+ RustType::Str.wrap_ref()
+ }
+
+ /// `&[u8]`.
+ pub(crate) fn amp_slice_of_u8() -> RustType {
+ RustType::u8().wrap_slice().wrap_ref()
+ }
+
/// Type is rust primitive?
- pub fn is_primitive(&self) -> bool {
+ pub(crate) fn is_primitive(&self) -> bool {
match *self {
RustType::Int(..) | RustType::Float(..) | RustType::Bool => true,
_ => false,
@@ -120,6 +141,8 @@ impl RustType {
true
} else if let RustType::Enum(..) = *self {
true
+ } else if let RustType::EnumOrUnknown(..) = *self {
+ true
} else {
false
}
@@ -167,36 +190,43 @@ impl RustType {
}
}
- pub fn is_ref(&self) -> bool {
+ fn is_enum_or_unknown(&self) -> bool {
match *self {
- RustType::Ref(..) => true,
+ RustType::EnumOrUnknown(..) => true,
_ => false,
}
}
+ pub fn is_ref(&self) -> Option<&RustType> {
+ match *self {
+ RustType::Ref(ref v) => Some(&**v),
+ _ => None,
+ }
+ }
+
+ pub fn is_box(&self) -> Option<&RustType> {
+ match *self {
+ RustType::Uniq(ref v) => Some(&**v),
+ _ => None,
+ }
+ }
+
// default value for type
- pub fn default_value(&self, customize: &Customize) -> String {
+ pub fn default_value(&self, customize: &Customize, const_expr: bool) -> String {
match *self {
RustType::Ref(ref t) if t.is_str() => "\"\"".to_string(),
RustType::Ref(ref t) if t.is_slice().is_some() => "&[]".to_string(),
RustType::Int(..) => "0".to_string(),
RustType::Float(..) => "0.".to_string(),
RustType::Bool => "false".to_string(),
- RustType::Vec(..) => "::std::vec::Vec::new()".to_string(),
+ RustType::Vec(..) => EXPR_VEC_NEW.to_string(),
RustType::HashMap(..) => "::std::collections::HashMap::new()".to_string(),
RustType::String => "::std::string::String::new()".to_string(),
RustType::Bytes => "::bytes::Bytes::new()".to_string(),
RustType::Chars => format!("{}::Chars::new()", protobuf_crate_path(customize)),
- RustType::Option(..) => "::std::option::Option::None".to_string(),
- RustType::SingularField(..) => {
- format!("{}::SingularField::none()", protobuf_crate_path(customize))
- }
- RustType::SingularPtrField(..) => format!(
- "{}::SingularPtrField::none()",
- protobuf_crate_path(customize)
- ),
- RustType::RepeatedField(..) => {
- format!("{}::RepeatedField::new()", protobuf_crate_path(customize))
+ RustType::Option(..) => EXPR_NONE.to_string(),
+ RustType::MessageField(..) => {
+ format!("{}::MessageField::none()", protobuf_crate_path(customize))
}
RustType::Message(ref name) => format!("{}::new()", name),
RustType::Ref(ref m) if m.is_message() => match **m {
@@ -204,14 +234,25 @@ impl RustType {
_ => unreachable!(),
},
// Note: default value of enum type may not be equal to default value of field
- RustType::Enum(ref name, ref default) => format!("{}::{}", name, default),
- _ => panic!("cannot create default value for: {:?}", *self),
+ RustType::Enum(ref name, ref default, ..) => format!("{}::{}", name, default),
+ RustType::EnumOrUnknown(_, _, number) if const_expr => format!(
+ "{}::EnumOrUnknown::from_i32({})",
+ protobuf_crate_path(customize),
+ number,
+ ),
+ RustType::EnumOrUnknown(ref name, ref default, ..) if !const_expr => format!(
+ "{}::EnumOrUnknown::new({}::{})",
+ protobuf_crate_path(customize),
+ name,
+ default
+ ),
+ _ => panic!("cannot create default value for: {:?}", self),
}
}
- pub fn default_value_typed(self, customize: &Customize) -> RustValueTyped {
+ pub fn default_value_typed(self, customize: &Customize, const_expr: bool) -> RustValueTyped {
RustValueTyped {
- value: self.default_value(customize),
+ value: self.default_value(customize, const_expr),
rust_type: self,
}
}
@@ -219,56 +260,43 @@ impl RustType {
/// Emit a code to clear a variable `v`
pub fn clear(&self, v: &str, customize: &Customize) -> String {
match *self {
- RustType::Option(..) => format!("{} = ::std::option::Option::None", v),
+ RustType::Option(..) => format!("{} = {}", v, EXPR_NONE),
RustType::Vec(..)
| RustType::Bytes
+ | RustType::Chars
| RustType::String
- | RustType::RepeatedField(..)
- | RustType::SingularField(..)
- | RustType::SingularPtrField(..)
+ | RustType::MessageField(..)
| RustType::HashMap(..) => format!("{}.clear()", v),
- RustType::Chars => format!(
- "{}::Clear::clear(&mut {})",
- protobuf_crate_path(customize),
- v
- ),
- RustType::Bool | RustType::Float(..) | RustType::Int(..) | RustType::Enum(..) => {
- format!("{} = {}", v, self.default_value(customize))
+ RustType::Bool
+ | RustType::Float(..)
+ | RustType::Int(..)
+ | RustType::Enum(..)
+ | RustType::EnumOrUnknown(..) => {
+ format!("{} = {}", v, self.default_value(customize, false))
}
ref ty => panic!("cannot clear type: {:?}", ty),
}
}
- // wrap value in storage type
- pub fn wrap_value(&self, value: &str, customize: &Customize) -> String {
- match *self {
- RustType::Option(..) => format!("::std::option::Option::Some({})", value),
- RustType::SingularField(..) => format!(
- "{}::SingularField::some({})",
- protobuf_crate_path(customize),
- value
- ),
- RustType::SingularPtrField(..) => format!(
- "{}::SingularPtrField::some({})",
- protobuf_crate_path(customize),
- value
- ),
- _ => panic!("not a wrapper type: {:?}", *self),
- }
- }
-
// expression to convert `v` of type `self` to type `target`
pub fn into_target(&self, target: &RustType, v: &str, customize: &Customize) -> String {
self.try_into_target(target, v, customize)
.expect(&format!("failed to convert {:?} into {:?}", self, target))
}
- fn try_into_target(
- &self,
- target: &RustType,
- v: &str,
- customize: &Customize,
- ) -> Result<String, ()> {
+ // https://github.com/rust-lang-nursery/rustfmt/issues/3131
+ #[cfg_attr(rustfmt, rustfmt_skip)]
+ fn try_into_target(&self, target: &RustType, v: &str, customize: &Customize) -> Result<String, ()> {
+ {
+ if let Some(t1) = self.is_ref().and_then(|t| t.is_box()) {
+ if let Some(t2) = target.is_ref() {
+ if t1 == t2 {
+ return Ok(format!("&**{}", v));
+ }
+ }
+ }
+ }
+
match (self, target) {
(x, y) if x == y => return Ok(format!("{}", v)),
(&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)),
@@ -287,76 +315,57 @@ impl RustType {
}
(&RustType::Ref(ref t1), &RustType::String)
if match **t1 {
- RustType::Str => true,
- _ => false,
- } =>
- {
- return Ok(format!("{}.to_owned()", v))
- }
+ RustType::Str => true,
+ _ => false,
+ } => return Ok(format!("{}.to_owned()", v)),
(&RustType::Ref(ref t1), &RustType::Chars)
if match **t1 {
- RustType::Str => true,
- _ => false,
- // TODO: from_static
- } =>
- {
- return Ok(format!(
- "<{}::Chars as ::std::convert::From<_>>::from({}.to_owned())",
- protobuf_crate_path(customize),
- v
- ))
- }
+ RustType::Str => true,
+ _ => false,
+ } => {
+ return Ok(format!("<{}::Chars as ::std::convert::From<_>>::from({}.to_owned())",
+ protobuf_crate_path(customize), v))
+ },
(&RustType::Ref(ref t1), &RustType::Vec(ref t2))
if match (&**t1, &**t2) {
- (&RustType::Slice(ref x), ref y) => **x == **y,
- _ => false,
- } =>
- {
- return Ok(format!("{}.to_vec()", v))
- }
- (&RustType::Ref(ref t1), &RustType::Bytes) if t1.is_slice_u8() => {
- return Ok(format!(
- "<::bytes::Bytes as ::std::convert::From<_>>::from({}.to_vec())",
- v
- ))
- }
+ (&RustType::Slice(ref x), ref y) => **x == **y,
+ _ => false,
+ } => return Ok(format!("{}.to_vec()", v)),
+ (&RustType::Ref(ref t1), &RustType::Bytes)
+ if t1.is_slice_u8() =>
+ return Ok(format!("<::bytes::Bytes as ::std::convert::From<_>>::from({}.to_vec())", v)),
(&RustType::Vec(ref x), &RustType::Ref(ref t))
if match **t {
- RustType::Slice(ref y) => x == y,
- _ => false,
- } =>
- {
- return Ok(format!("&{}", v))
- }
+ RustType::Slice(ref y) => x == y,
+ _ => false,
+ } => return Ok(format!("&{}", v)),
(&RustType::Bytes, &RustType::Ref(ref t))
if match **t {
- RustType::Slice(ref y) => **y == RustType::u8(),
- _ => false,
- } =>
- {
- return Ok(format!("&{}", v))
- }
+ RustType::Slice(ref y) => **y == RustType::u8(),
+ _ => false,
+ } => return Ok(format!("&{}", v)),
(&RustType::Ref(ref t1), &RustType::Ref(ref t2))
if match (&**t1, &**t2) {
- (&RustType::Vec(ref x), &RustType::Slice(ref y)) => x == y,
- _ => false,
- } =>
- {
- return Ok(format!("&{}", v))
- }
+ (&RustType::Vec(ref x), &RustType::Slice(ref y)) => x == y,
+ _ => false,
+ } => return Ok(format!("&{}", v)),
(&RustType::Enum(..), &RustType::Int(true, 32)) => {
- return Ok(format!(
- "{}::ProtobufEnum::value(&{})",
- protobuf_crate_path(customize),
- v
- ))
- }
+ return Ok(format!("{}::Enum::value(&{})", protobuf_crate_path(customize), v))
+ },
+ (&RustType::EnumOrUnknown(..), &RustType::Int(true, 32)) => {
+ return Ok(format!("{}::EnumOrUnknown::value(&{})", protobuf_crate_path(customize), v))
+ },
(&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum() => {
- return Ok(format!(
- "{}::ProtobufEnum::value({})",
- protobuf_crate_path(customize),
- v
- ))
+ return Ok(format!("{}::Enum::value({})", protobuf_crate_path(customize), v))
+ }
+ (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum_or_unknown() => {
+ return Ok(format!("{}::EnumOrUnknown::value({})", protobuf_crate_path(customize), v))
+ },
+ (&RustType::EnumOrUnknown(ref f, ..), &RustType::Enum(ref t, ..)) if f == t => {
+ return Ok(format!("{}::EnumOrUnknown::enum_value_or_default(&{})", protobuf_crate_path(customize), v))
+ }
+ (&RustType::Enum(ref f, ..), &RustType::EnumOrUnknown(ref t, ..)) if f == t => {
+ return Ok(format!("{}::EnumOrUnknown::new({})", protobuf_crate_path(customize), v))
}
_ => (),
};
@@ -374,16 +383,26 @@ impl RustType {
pub fn ref_type(&self) -> RustType {
RustType::Ref(Box::new(match self {
&RustType::String | &RustType::Chars => RustType::Str,
- &RustType::Vec(ref p) | &RustType::RepeatedField(ref p) => RustType::Slice(p.clone()),
+ &RustType::Vec(ref p) => RustType::Slice(p.clone()),
&RustType::Bytes => RustType::Slice(Box::new(RustType::u8())),
&RustType::Message(ref p) => RustType::Message(p.clone()),
+ &RustType::Uniq(ref p) => RustType::Uniq(p.clone()),
x => panic!("no ref type for {:?}", x),
}))
}
+ pub(crate) fn wrap_ref(&self) -> RustType {
+ RustType::Ref(Box::new(self.clone()))
+ }
+
+ pub(crate) fn wrap_slice(&self) -> RustType {
+ RustType::Slice(Box::new(self.clone()))
+ }
+
pub fn elem_type(&self) -> RustType {
match self {
&RustType::Option(ref ty) => (**ty).clone(),
+ &RustType::MessageField(ref ty) => (**ty).clone(),
x => panic!("cannot get elem type of {:?}", x),
}
}
@@ -393,9 +412,7 @@ impl RustType {
match self {
&RustType::Vec(ref ty)
| &RustType::Option(ref ty)
- | &RustType::RepeatedField(ref ty)
- | &RustType::SingularField(ref ty)
- | &RustType::SingularPtrField(ref ty) => RustType::Ref(ty.clone()),
+ | &RustType::MessageField(ref ty) => RustType::Ref(ty.clone()),
x => panic!("cannot iterate {:?}", x),
}
}
@@ -428,56 +445,6 @@ impl RustValueTyped {
}
}
-// protobuf type name for protobuf base type
-pub fn protobuf_name(field_type: FieldDescriptorProto_Type) -> &'static str {
- match field_type {
- FieldDescriptorProto_Type::TYPE_DOUBLE => "double",
- FieldDescriptorProto_Type::TYPE_FLOAT => "float",
- FieldDescriptorProto_Type::TYPE_INT32 => "int32",
- FieldDescriptorProto_Type::TYPE_INT64 => "int64",
- FieldDescriptorProto_Type::TYPE_UINT32 => "uint32",
- FieldDescriptorProto_Type::TYPE_UINT64 => "uint64",
- FieldDescriptorProto_Type::TYPE_SINT32 => "sint32",
- FieldDescriptorProto_Type::TYPE_SINT64 => "sint64",
- FieldDescriptorProto_Type::TYPE_FIXED32 => "fixed32",
- FieldDescriptorProto_Type::TYPE_FIXED64 => "fixed64",
- FieldDescriptorProto_Type::TYPE_SFIXED32 => "sfixed32",
- FieldDescriptorProto_Type::TYPE_SFIXED64 => "sfixed64",
- FieldDescriptorProto_Type::TYPE_BOOL => "bool",
- FieldDescriptorProto_Type::TYPE_STRING => "string",
- FieldDescriptorProto_Type::TYPE_BYTES => "bytes",
- FieldDescriptorProto_Type::TYPE_ENUM => "enum",
- FieldDescriptorProto_Type::TYPE_MESSAGE => "message",
- FieldDescriptorProto_Type::TYPE_GROUP => "group",
- }
-}
-
-// rust type for protobuf base type
-pub(crate) fn rust_name(field_type: FieldDescriptorProto_Type) -> RustType {
- match field_type {
- FieldDescriptorProto_Type::TYPE_DOUBLE => RustType::Float(64),
- FieldDescriptorProto_Type::TYPE_FLOAT => RustType::Float(32),
- FieldDescriptorProto_Type::TYPE_INT32 => RustType::Int(true, 32),
- FieldDescriptorProto_Type::TYPE_INT64 => RustType::Int(true, 64),
- FieldDescriptorProto_Type::TYPE_UINT32 => RustType::Int(false, 32),
- FieldDescriptorProto_Type::TYPE_UINT64 => RustType::Int(false, 64),
- FieldDescriptorProto_Type::TYPE_SINT32 => RustType::Int(true, 32),
- FieldDescriptorProto_Type::TYPE_SINT64 => RustType::Int(true, 64),
- FieldDescriptorProto_Type::TYPE_FIXED32 => RustType::Int(false, 32),
- FieldDescriptorProto_Type::TYPE_FIXED64 => RustType::Int(false, 64),
- FieldDescriptorProto_Type::TYPE_SFIXED32 => RustType::Int(true, 32),
- FieldDescriptorProto_Type::TYPE_SFIXED64 => RustType::Int(true, 64),
- FieldDescriptorProto_Type::TYPE_BOOL => RustType::Bool,
- FieldDescriptorProto_Type::TYPE_STRING => RustType::String,
- FieldDescriptorProto_Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::Int(false, 8))),
- FieldDescriptorProto_Type::TYPE_ENUM
- | FieldDescriptorProto_Type::TYPE_GROUP
- | FieldDescriptorProto_Type::TYPE_MESSAGE => {
- panic!("there is no rust name for {:?}", field_type)
- }
- }
-}
-
fn file_last_component(file: &str) -> &str {
let bs = file.rfind('\\').map(|i| i + 1).unwrap_or(0);
let fs = file.rfind('/').map(|i| i + 1).unwrap_or(0);
@@ -493,101 +460,158 @@ fn test_file_last_component() {
assert_eq!("ab.proto", file_last_component("yy\\xx\\ab.proto"));
}
-fn is_descriptor_proto(file: &FileDescriptorProto) -> bool {
- file.get_package() == "google.protobuf"
- && file_last_component(file.get_name()) == "descriptor.proto"
+fn is_descriptor_proto(file: &FileDescriptor) -> bool {
+ file.package() == "google.protobuf" && file_last_component(file.name()) == "descriptor.proto"
}
-pub(crate) fn type_name_to_rust_relative(
- type_name: &ProtobufAbsolutePath,
- file: &FileDescriptorProto,
- subm: bool,
- root_scope: &RootScope,
- customize: &Customize,
-) -> String {
- let message_or_enum = root_scope.find_message_or_enum(type_name);
- if message_or_enum.get_scope().get_file_descriptor().get_name() == file.get_name() {
+fn make_path_to_path(source: &RustRelativePath, dest: &RustPath) -> RustPath {
+ if dest.is_absolute() {
+ return dest.clone();
+ }
+
+ let mut source = source.clone();
+ let mut dest = dest.clone();
+ while !source.is_empty() && source.first() == dest.first() {
+ source.remove_first().unwrap();
+ dest.remove_first().unwrap();
+ }
+ source.to_reverse().into_path().append(dest)
+}
+
+pub(crate) fn make_path(source: &RustRelativePath, dest: &RustIdentWithPath) -> RustIdentWithPath {
+ make_path_to_path(source, &dest.path).with_ident(dest.ident.clone())
+}
+
+pub(crate) fn message_or_enum_to_rust_relative(
+ message_or_enum: &dyn WithScope,
+ current: &FileAndMod,
+) -> RustIdentWithPath {
+ let same_file = message_or_enum.file_descriptor().name() == current.file;
+ if same_file {
// field type is a message or enum declared in the same file
- if subm {
- format!("super::{}", message_or_enum.rust_name())
- } else {
- format!("{}", message_or_enum.rust_name())
- }
- } else if let Some(name) = is_well_known_type_full(&type_name.path) {
+ make_path(&current.relative_mod, &message_or_enum.rust_name_to_file())
+ } else if let Some(name) = is_well_known_type_full(&message_or_enum.name_absolute()) {
// Well-known types are included in rust-protobuf library
// https://developers.google.com/protocol-buffers/docs/reference/google.protobuf
- format!(
- "{}::well_known_types::{}",
- protobuf_crate_path(customize),
- name
- )
- } else if is_descriptor_proto(message_or_enum.get_file_descriptor()) {
+ let file_descriptor = message_or_enum.file_descriptor();
+ static REGEX: Lazy<Regex> =
+ Lazy::new(|| Regex::new(r"^google/protobuf/([^/]+\.proto)$").unwrap());
+ let captures = REGEX
+ .captures(file_descriptor.name())
+ .unwrap_or_else(|| panic!("`{}` does not match the regex", file_descriptor.name()));
+ let file_name = captures.get(1).unwrap().as_str();
+ let mod_name = proto_path_to_rust_mod(file_name);
+ RustIdentWithPath::from(format!(
+ "{protobuf_crate}::well_known_types::{mod_name}::{name}",
+ protobuf_crate = protobuf_crate_path(&current.customize),
+ ))
+ } else if is_descriptor_proto(&message_or_enum.file_descriptor()) {
// Messages defined in descriptor.proto
- format!(
+ RustIdentWithPath::from(format!(
"{}::descriptor::{}",
- protobuf_crate_path(customize),
- message_or_enum.name_to_package()
- )
+ protobuf_crate_path(&current.customize),
+ message_or_enum.rust_name_to_file()
+ ))
} else {
- if subm {
- format!("super::super::{}", message_or_enum.rust_fq_name())
- } else {
- format!("super::{}", message_or_enum.rust_fq_name())
- }
+ current
+ .relative_mod
+ .to_reverse()
+ .into_path()
+ .append_component(RustPathComponent::SUPER)
+ .append_with_ident(message_or_enum.rust_name_with_file())
}
}
+pub(crate) fn type_name_to_rust_relative(
+ type_name: &ProtobufAbsPath,
+ current: &FileAndMod,
+ root_scope: &RootScope,
+) -> RustIdentWithPath {
+ assert!(!type_name.is_root());
+ let message_or_enum = root_scope.find_message_or_enum(type_name);
+ message_or_enum_to_rust_relative(&message_or_enum, current)
+}
+
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum PrimitiveTypeVariant {
Default,
- Carllerche,
+ TokioBytes,
}
-pub enum _CarllercheBytesType {
+pub enum _TokioBytesType {
Bytes,
Chars,
}
// ProtobufType trait name
-pub enum ProtobufTypeGen {
- Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant),
- Message(String),
- Enum(String),
+pub(crate) enum ProtobufTypeGen {
+ Primitive(field_descriptor_proto::Type, PrimitiveTypeVariant),
+ Message(RustTypeMessage),
+ EnumOrUnknown(RustIdentWithPath),
}
impl ProtobufTypeGen {
- pub fn rust_type(&self, customize: &Customize) -> String {
+ pub(crate) fn protobuf_value(&self, customize: &Customize) -> String {
+ match self {
+ ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => {
+ t.rust_type().to_code(customize)
+ }
+ ProtobufTypeGen::Primitive(_, PrimitiveTypeVariant::TokioBytes) => unimplemented!(),
+ ProtobufTypeGen::Message(m) => m.0.to_string(),
+ ProtobufTypeGen::EnumOrUnknown(e) => format!(
+ "{protobuf_crate}::EnumOrUnknown<{e}>",
+ protobuf_crate = protobuf_crate_path(customize)
+ ),
+ }
+ }
+
+ pub(crate) fn _rust_type(&self, customize: &Customize) -> String {
match self {
&ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!(
- "{}::types::ProtobufType{}",
+ "{}::reflect::types::ProtobufType{}",
protobuf_crate_path(customize),
- capitalize(protobuf_name(t))
+ capitalize(t.protobuf_name())
),
&ProtobufTypeGen::Primitive(
- FieldDescriptorProto_Type::TYPE_BYTES,
- PrimitiveTypeVariant::Carllerche,
+ field_descriptor_proto::Type::TYPE_BYTES,
+ PrimitiveTypeVariant::TokioBytes,
) => format!(
- "{}::types::ProtobufTypeCarllercheBytes",
+ "{}::reflect::types::ProtobufTypeTokioBytes",
protobuf_crate_path(customize)
),
&ProtobufTypeGen::Primitive(
- FieldDescriptorProto_Type::TYPE_STRING,
- PrimitiveTypeVariant::Carllerche,
+ field_descriptor_proto::Type::TYPE_STRING,
+ PrimitiveTypeVariant::TokioBytes,
) => format!(
- "{}::types::ProtobufTypeCarllercheChars",
+ "{}::reflect::types::ProtobufTypeTokioChars",
protobuf_crate_path(customize)
),
- &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(),
+ &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(),
&ProtobufTypeGen::Message(ref name) => format!(
- "{}::types::ProtobufTypeMessage<{}>",
+ "{}::reflect::types::ProtobufTypeMessage<{}>",
protobuf_crate_path(customize),
name
),
- &ProtobufTypeGen::Enum(ref name) => format!(
- "{}::types::ProtobufTypeEnum<{}>",
+ &ProtobufTypeGen::EnumOrUnknown(ref name) => format!(
+ "{}::reflect::types::ProtobufTypeEnumOrUnknown<{}>",
protobuf_crate_path(customize),
name
),
}
}
}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn into_target_ref_box_to_ref() {
+ let t1 = RustType::Ref(Box::new(RustType::Uniq(Box::new(RustType::Message(
+ RustTypeMessage::from("Ab"),
+ )))));
+ let t2 = RustType::Ref(Box::new(RustType::Message(RustTypeMessage::from("Ab"))));
+
+ assert_eq!("&**v", t1.into_target(&t2, "v", &Customize::default()));
+ }
+}
diff --git a/src/gen/scope.rs b/src/gen/scope.rs
new file mode 100644
index 0000000..f70d233
--- /dev/null
+++ b/src/gen/scope.rs
@@ -0,0 +1,536 @@
+use std::ops::Deref;
+
+use protobuf::reflect::EnumDescriptor;
+use protobuf::reflect::EnumValueDescriptor;
+use protobuf::reflect::FieldDescriptor;
+use protobuf::reflect::FileDescriptor;
+use protobuf::reflect::MessageDescriptor;
+use protobuf::reflect::OneofDescriptor;
+use protobuf_parse::ProtobufAbsPath;
+use protobuf_parse::ProtobufAbsPathRef;
+use protobuf_parse::ProtobufIdentRef;
+use protobuf_parse::ProtobufRelPath;
+use protobuf_parse::ProtobufRelPathRef;
+
+use crate::customize::Customize;
+use crate::gen::field::rust_field_name_for_protobuf_field_name;
+use crate::gen::file_and_mod::FileAndMod;
+use crate::gen::map::map_entry;
+use crate::gen::message::message_name_to_nested_mod_name;
+use crate::gen::paths::proto_path_to_rust_mod;
+use crate::gen::rust::ident::RustIdent;
+use crate::gen::rust::ident_with_path::RustIdentWithPath;
+use crate::gen::rust::rel_path::RustRelativePath;
+use crate::gen::strx::capitalize;
+
+pub(crate) struct RootScope<'a> {
+ pub file_descriptors: &'a [FileDescriptor],
+}
+
+impl<'a> RootScope<'a> {
+ fn packages(&'a self) -> Vec<FileScope<'a>> {
+ self.file_descriptors
+ .iter()
+ .map(|fd| FileScope {
+ file_descriptor: fd,
+ })
+ .collect()
+ }
+
+ // find enum by fully qualified name
+ pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> {
+ match self.find_message_or_enum(fqn) {
+ MessageOrEnumWithScope::Enum(e) => e,
+ _ => panic!("not an enum: {}", fqn),
+ }
+ }
+
+ // find message by fully qualified name
+ pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> {
+ match self.find_message_or_enum(fqn) {
+ MessageOrEnumWithScope::Message(m) => m,
+ _ => panic!("not a message: {}", fqn),
+ }
+ }
+
+ // find message or enum by fully qualified name
+ pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> {
+ assert!(!fqn.is_root());
+ self.packages()
+ .into_iter()
+ .flat_map(|p| p.find_message_or_enum_abs(fqn))
+ .next()
+ .expect(&format!("enum not found by name: {}", fqn))
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct FileScope<'a> {
+ pub file_descriptor: &'a FileDescriptor,
+}
+
+impl<'a> Deref for FileScope<'a> {
+ type Target = FileDescriptor;
+
+ fn deref(&self) -> &Self::Target {
+ self.file_descriptor
+ }
+}
+
+impl<'a> FileScope<'a> {
+ fn package(&self) -> ProtobufAbsPath {
+ ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor)
+ }
+
+ pub fn to_scope(&self) -> Scope<'a> {
+ Scope {
+ file_scope: self.clone(),
+ path: Vec::new(),
+ }
+ }
+
+ fn find_message_or_enum(
+ &self,
+ name: &ProtobufRelPathRef,
+ ) -> Option<MessageOrEnumWithScope<'a>> {
+ self.find_messages_and_enums()
+ .into_iter()
+ .filter(|e| e.protobuf_name_to_package().as_ref() == name)
+ .next()
+ }
+
+ fn find_message_or_enum_abs(
+ &self,
+ name: &ProtobufAbsPathRef,
+ ) -> Option<MessageOrEnumWithScope<'a>> {
+ let name = name.to_owned();
+ match name.remove_prefix(&self.package()) {
+ Some(rem) => self.find_message_or_enum(&rem),
+ None => None,
+ }
+ }
+
+ // find all enums in given file descriptor
+ pub fn find_enums(&self) -> Vec<EnumWithScope<'a>> {
+ let mut r = Vec::new();
+
+ self.to_scope().walk_scopes(|scope| {
+ r.extend(scope.enums());
+ });
+
+ r
+ }
+
+ /// Find all messages in given file descriptor
+ pub fn find_messages(&self) -> Vec<MessageWithScope<'a>> {
+ let mut r = Vec::new();
+
+ self.to_scope().walk_scopes(|scope| {
+ r.extend(scope.messages());
+ });
+
+ r
+ }
+
+ /// Find all messages in given file descriptor, except map messages
+ pub fn find_messages_except_map(&self) -> Vec<MessageWithScope<'a>> {
+ self.find_messages()
+ .into_iter()
+ .filter(|m| !m.is_map())
+ .collect()
+ }
+
+ /// find all messages and enums in given file descriptor
+ pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
+ let mut r = Vec::new();
+
+ self.to_scope().walk_scopes(|scope| {
+ r.extend(scope.messages_and_enums());
+ });
+
+ r
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct Scope<'a> {
+ pub file_scope: FileScope<'a>,
+ pub path: Vec<MessageDescriptor>,
+}
+
+impl<'a> Scope<'a> {
+ pub(crate) fn file_descriptor(&self) -> FileDescriptor {
+ self.file_scope.file_descriptor.clone()
+ }
+
+ // get message descriptors in this scope
+ fn message_descriptors(&self) -> Vec<MessageDescriptor> {
+ if self.path.is_empty() {
+ self.file_scope.file_descriptor.messages().collect()
+ } else {
+ self.path.last().unwrap().nested_messages().collect()
+ }
+ }
+
+ // get enum descriptors in this scope
+ fn enum_descriptors(&self) -> Vec<EnumDescriptor> {
+ if self.path.is_empty() {
+ self.file_scope.file_descriptor.enums().collect()
+ } else {
+ self.path.last().unwrap().nested_enums().collect()
+ }
+ }
+
+ // get messages with attached scopes in this scope
+ pub fn messages(&self) -> Vec<MessageWithScope<'a>> {
+ self.message_descriptors()
+ .into_iter()
+ .map(|message| MessageWithScope {
+ scope: self.clone(),
+ message,
+ })
+ .collect()
+ }
+
+ // get enums with attached scopes in this scope
+ pub fn enums(&self) -> Vec<EnumWithScope<'a>> {
+ self.enum_descriptors()
+ .into_iter()
+ .map(|en| EnumWithScope {
+ scope: self.clone(),
+ en,
+ })
+ .collect()
+ }
+
+ // get messages and enums with attached scopes in this scope
+ pub fn messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
+ self.messages()
+ .into_iter()
+ .map(|m| MessageOrEnumWithScope::Message(m))
+ .chain(
+ self.enums()
+ .into_iter()
+ .map(|m| MessageOrEnumWithScope::Enum(m)),
+ )
+ .collect()
+ }
+
+ // nested scopes, i. e. scopes of nested messages
+ fn nested_scopes(&self) -> Vec<Scope<'a>> {
+ self.message_descriptors()
+ .into_iter()
+ .map(|m| {
+ let mut nested = self.clone();
+ nested.path.push(m);
+ nested
+ })
+ .collect()
+ }
+
+ fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
+ (*callback)(self);
+
+ for nested in self.nested_scopes() {
+ nested.walk_scopes_impl(callback);
+ }
+ }
+
+ // apply callback for this scope and all nested scopes
+ fn walk_scopes<F>(&self, mut callback: F)
+ where
+ F: FnMut(&Scope<'a>),
+ {
+ self.walk_scopes_impl(&mut callback);
+ }
+
+ pub fn rust_path_to_file(&self) -> RustRelativePath {
+ RustRelativePath::from_idents(
+ self.path
+ .iter()
+ .map(|m| message_name_to_nested_mod_name(m.name())),
+ )
+ }
+
+ pub fn path_str(&self) -> String {
+ let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect();
+ v.join(".")
+ }
+
+ pub fn prefix(&self) -> String {
+ let path_str = self.path_str();
+ if path_str.is_empty() {
+ path_str
+ } else {
+ format!("{}.", path_str)
+ }
+ }
+
+ pub fn protobuf_path_to_file(&self) -> ProtobufRelPath {
+ ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name())))
+ }
+
+ pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath {
+ let mut r = self.file_scope.package();
+ r.push_relative(&self.protobuf_path_to_file());
+ r
+ }
+
+ pub fn file_and_mod(&self, customize: Customize) -> FileAndMod {
+ FileAndMod {
+ file: self.file_scope.file_descriptor.proto().name().to_owned(),
+ relative_mod: self.rust_path_to_file(),
+ customize,
+ }
+ }
+}
+
+pub(crate) trait WithScope<'a> {
+ fn scope(&self) -> &Scope<'a>;
+
+ fn file_descriptor(&self) -> FileDescriptor {
+ self.scope().file_descriptor()
+ }
+
+ // message or enum name
+ fn name(&self) -> &ProtobufIdentRef;
+
+ fn name_to_package(&self) -> String {
+ let mut r = self.scope().prefix();
+ r.push_str(&self.name());
+ r
+ }
+
+ fn protobuf_name_to_package(&self) -> ProtobufRelPath {
+ let r = self.scope().protobuf_path_to_file();
+ r.append_ident(ProtobufIdentRef::new(self.name()))
+ }
+
+ /// Return absolute name starting with dot
+ fn name_absolute(&self) -> ProtobufAbsPath {
+ let mut path = self.scope().protobuf_absolute_path();
+ path.push_simple(self.name());
+ path
+ }
+
+ // rust type name of this descriptor
+ fn rust_name(&self) -> RustIdent {
+ let rust_name = capitalize(&self.name());
+ RustIdent::new(&rust_name)
+ }
+
+ fn rust_name_to_file(&self) -> RustIdentWithPath {
+ self.scope()
+ .rust_path_to_file()
+ .into_path()
+ .with_ident(self.rust_name())
+ }
+
+ // fully-qualified name of this type
+ fn rust_name_with_file(&self) -> RustIdentWithPath {
+ let mut r = self.rust_name_to_file();
+ r.prepend_ident(proto_path_to_rust_mod(
+ self.scope().file_descriptor().name(),
+ ));
+ r
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct MessageWithScope<'a> {
+ pub scope: Scope<'a>,
+ pub message: MessageDescriptor,
+}
+
+impl<'a> WithScope<'a> for MessageWithScope<'a> {
+ fn scope(&self) -> &Scope<'a> {
+ &self.scope
+ }
+
+ fn name(&self) -> &ProtobufIdentRef {
+ ProtobufIdentRef::new(self.message.name())
+ }
+}
+
+impl<'a> MessageWithScope<'a> {
+ pub fn into_scope(mut self) -> Scope<'a> {
+ self.scope.path.push(self.message);
+ self.scope
+ }
+
+ pub fn to_scope(&self) -> Scope<'a> {
+ self.clone().into_scope()
+ }
+
+ pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
+ self.message
+ .fields()
+ .into_iter()
+ .map(|field| FieldWithContext {
+ field,
+ message: self.clone(),
+ })
+ .collect()
+ }
+
+ pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
+ self.message
+ .oneofs()
+ .into_iter()
+ .map(|oneof| OneofWithContext {
+ message: self.clone(),
+ oneof,
+ })
+ .collect()
+ }
+
+ pub fn mod_name(&self) -> RustIdent {
+ message_name_to_nested_mod_name(self.message.name())
+ }
+
+ /// This message is a special message which is a map.
+ pub fn is_map(&self) -> bool {
+ map_entry(self).is_some()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct EnumWithScope<'a> {
+ pub scope: Scope<'a>,
+ pub en: EnumDescriptor,
+}
+
+impl<'a> EnumWithScope<'a> {
+ pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
+ self.en
+ .values()
+ .into_iter()
+ .map(|v| EnumValueWithContext {
+ en: self.clone(),
+ proto: v,
+ })
+ .collect()
+ }
+
+ // find enum value by protobuf name
+ pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
+ self.values()
+ .into_iter()
+ .find(|v| v.proto.proto().name() == name)
+ .unwrap()
+ }
+}
+
+#[derive(Clone, Debug)]
+pub(crate) struct EnumValueWithContext<'a> {
+ pub en: EnumWithScope<'a>,
+ pub proto: EnumValueDescriptor,
+}
+
+impl<'a> EnumValueWithContext<'a> {
+ pub fn rust_name(&self) -> RustIdent {
+ // TODO: camel case or something.
+ RustIdent::new(self.proto.name())
+ }
+}
+
+impl<'a> WithScope<'a> for EnumWithScope<'a> {
+ fn scope(&self) -> &Scope<'a> {
+ &self.scope
+ }
+
+ fn name(&self) -> &ProtobufIdentRef {
+ ProtobufIdentRef::new(self.en.name())
+ }
+}
+
+pub(crate) enum MessageOrEnumWithScope<'a> {
+ Message(MessageWithScope<'a>),
+ Enum(EnumWithScope<'a>),
+}
+
+impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
+ fn scope(&self) -> &Scope<'a> {
+ match self {
+ MessageOrEnumWithScope::Message(m) => m.scope(),
+ MessageOrEnumWithScope::Enum(e) => e.scope(),
+ }
+ }
+
+ fn name(&self) -> &ProtobufIdentRef {
+ match self {
+ MessageOrEnumWithScope::Message(m) => m.name(),
+ MessageOrEnumWithScope::Enum(e) => e.name(),
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct FieldWithContext<'a> {
+ pub field: FieldDescriptor,
+ pub message: MessageWithScope<'a>,
+}
+
+impl<'a> Deref for FieldWithContext<'a> {
+ type Target = FieldDescriptor;
+
+ fn deref(&self) -> &Self::Target {
+ &self.field
+ }
+}
+
+impl<'a> FieldWithContext<'a> {
+ pub fn is_oneof(&self) -> bool {
+ self.field.containing_oneof().is_some()
+ }
+
+ pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
+ match self.field.containing_oneof() {
+ Some(oneof) => Some(OneofWithContext {
+ message: self.message.clone(),
+ oneof,
+ }),
+ None => None,
+ }
+ }
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofVariantWithContext<'a> {
+ pub oneof: &'a OneofWithContext<'a>,
+ pub field: FieldDescriptor,
+}
+
+#[derive(Clone)]
+pub(crate) struct OneofWithContext<'a> {
+ pub oneof: OneofDescriptor,
+ pub message: MessageWithScope<'a>,
+}
+
+impl<'a> OneofWithContext<'a> {
+ pub fn field_name(&'a self) -> RustIdent {
+ return rust_field_name_for_protobuf_field_name(self.oneof.name());
+ }
+
+ // rust type name of enum
+ pub fn rust_name(&self) -> RustIdentWithPath {
+ let type_name = RustIdent::from(capitalize(self.oneof.name()));
+ self.message
+ .to_scope()
+ .rust_path_to_file()
+ .into_path()
+ .with_ident(type_name)
+ }
+
+ pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
+ self.message
+ .fields()
+ .into_iter()
+ .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof))
+ .map(|f| OneofVariantWithContext {
+ oneof: self,
+ field: f.field,
+ })
+ .collect()
+ }
+}
diff --git a/src/strx.rs b/src/gen/strx.rs
index d1b26fa..d1b26fa 100644
--- a/src/strx.rs
+++ b/src/gen/strx.rs
diff --git a/src/gen/well_known_types.rs b/src/gen/well_known_types.rs
new file mode 100644
index 0000000..6b0b389
--- /dev/null
+++ b/src/gen/well_known_types.rs
@@ -0,0 +1,123 @@
+use protobuf_parse::ProtobufAbsPath;
+use protobuf_parse::ProtobufRelPath;
+use protobuf_parse::ProtobufRelPathRef;
+
+use crate::compiler_plugin;
+use crate::gen::code_writer::CodeWriter;
+use crate::gen::paths::proto_path_to_rust_mod;
+
+pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_NAMES: &[&str] = &[
+ "any.proto",
+ "api.proto",
+ "duration.proto",
+ "empty.proto",
+ "field_mask.proto",
+ "source_context.proto",
+ "struct.proto",
+ "timestamp.proto",
+ "type.proto",
+ "wrappers.proto",
+];
+
+pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES: &[&str] = &[
+ "google/protobuf/any.proto",
+ "google/protobuf/api.proto",
+ "google/protobuf/duration.proto",
+ "google/protobuf/empty.proto",
+ "google/protobuf/field_mask.proto",
+ "google/protobuf/source_context.proto",
+ "google/protobuf/struct.proto",
+ "google/protobuf/timestamp.proto",
+ "google/protobuf/type.proto",
+ "google/protobuf/wrappers.proto",
+];
+
+static NAMES: &'static [&'static str] = &[
+ "Any",
+ "Api",
+ "BoolValue",
+ "BytesValue",
+ "DoubleValue",
+ "Duration",
+ "Empty",
+ "Enum",
+ "EnumValue",
+ "Field",
+ "Field.Cardinality",
+ "Field.Kind",
+ "FieldMask",
+ "FloatValue",
+ "Int32Value",
+ "Int64Value",
+ "ListValue",
+ "Method",
+ "Mixin",
+ "NullValue",
+ "Option",
+ "SourceContext",
+ "StringValue",
+ "Struct",
+ "Syntax",
+ "Timestamp",
+ "Type",
+ "UInt32Value",
+ "UInt64Value",
+ "Value",
+];
+
+fn is_well_known_type(name: &ProtobufRelPathRef) -> bool {
+ NAMES.iter().any(|&n| n == format!("{}", name))
+}
+
+pub(crate) fn is_well_known_type_full(name: &ProtobufAbsPath) -> Option<ProtobufRelPath> {
+ if let Some(rem) = name.remove_prefix(&ProtobufAbsPath::from(".google.protobuf")) {
+ if is_well_known_type(rem) {
+ Some(rem.to_owned())
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+}
+
+pub(crate) fn gen_well_known_types_mod() -> compiler_plugin::GenResult {
+ let v = CodeWriter::with_no_error(|w| {
+ w.comment("This file is generated. Do not edit");
+ w.comment("@generated");
+ w.mod_doc("Generated code for \"well known types\"");
+ w.mod_doc("");
+ w.mod_doc("[This document](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) describes these types.");
+
+ w.write_line("");
+ w.write_line("#![allow(unused_attributes)]");
+ w.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]");
+
+ w.write_line("");
+ for m in WELL_KNOWN_TYPES_PROTO_FILE_NAMES {
+ w.write_line(&format!("pub mod {};", proto_path_to_rust_mod(m)));
+ }
+ });
+
+ compiler_plugin::GenResult {
+ name: "well_known_types_mod.rs".to_string(),
+ content: v.into_bytes(),
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+
+ #[test]
+ fn test_is_well_known_type_full() {
+ assert_eq!(
+ Some(ProtobufRelPath::from("BoolValue")),
+ is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.BoolValue"))
+ );
+ assert_eq!(
+ None,
+ is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.Fgfg"))
+ );
+ }
+}
diff --git a/src/gen_and_write.rs b/src/gen_and_write.rs
new file mode 100644
index 0000000..95d621e
--- /dev/null
+++ b/src/gen_and_write.rs
@@ -0,0 +1,62 @@
+#![doc(hidden)]
+
+use std::fs;
+use std::io;
+use std::path::Path;
+
+use protobuf::descriptor::FileDescriptorProto;
+use protobuf_parse::ProtoPathBuf;
+
+use crate::customize::CustomizeCallback;
+use crate::gen::all::gen_all;
+use crate::Customize;
+
+#[derive(Debug, thiserror::Error)]
+enum Error {
+ #[error("output path `{0}` is not a directory")]
+ OutputIsNotDirectory(String),
+ #[error("output path `{0}` does not exist or not accessible")]
+ OutputDoesNotExistOrNotAccssible(String, #[source] io::Error),
+ #[error("failed to create file `{0}`: {1}")]
+ FailedToWriteFile(String, #[source] io::Error),
+}
+
+#[doc(hidden)]
+pub fn gen_and_write(
+ file_descriptors: &[FileDescriptorProto],
+ parser: &str,
+ files_to_generate: &[ProtoPathBuf],
+ out_dir: &Path,
+ customize: &Customize,
+ customize_callback: &dyn CustomizeCallback,
+) -> anyhow::Result<()> {
+ match out_dir.metadata() {
+ Ok(m) => {
+ if !m.is_dir() {
+ return Err(Error::OutputIsNotDirectory(out_dir.display().to_string()).into());
+ }
+ }
+ Err(e) => {
+ return Err(
+ Error::OutputDoesNotExistOrNotAccssible(out_dir.display().to_string(), e).into(),
+ );
+ }
+ }
+
+ let results = gen_all(
+ file_descriptors,
+ parser,
+ files_to_generate,
+ customize,
+ customize_callback,
+ )?;
+
+ for r in &results {
+ let mut file_path = out_dir.to_owned();
+ file_path.push(&r.name);
+ fs::write(&file_path, r.content.as_slice())
+ .map_err(|e| Error::FailedToWriteFile(file_path.display().to_string(), e))?;
+ }
+
+ Ok(())
+}
diff --git a/src/inside.rs b/src/inside.rs
deleted file mode 100644
index bbdcfdb..0000000
--- a/src/inside.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-use Customize;
-
-/// Path to `protobuf` crate, different when `.proto` file is
-/// used inside or outside of protobuf crate.
-pub(crate) fn protobuf_crate_path(customize: &Customize) -> &str {
- match customize.inside_protobuf {
- Some(true) => "crate",
- _ => "::protobuf",
- }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 826e18b..0471d7c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,35 +1,68 @@
-//! # Protobuf code generator
-//!
-//! This crate contains protobuf code generator implementation
-//! and a `protoc-gen-rust` `protoc` plugin.
-//!
-//! This crate:
-//! * provides `protoc-gen-rust` plugin for `protoc` command
-//! * implement protobuf codegen
-//!
-//! This crate is not meant to be used directly, in fact, it does not provide any public API
-//! (except for `protoc-gen-rust` binary).
-//!
-//! Code can be generated with either:
-//! * `protoc-gen-rust` plugin for `protoc` or
-//! * [`protoc-rust`](https://docs.rs/protoc) crate
-//! (code generator which depends on `protoc` binary for parsing of `.proto` files)
-//! * [`protobuf-codegen-pure`](https://docs.rs/protobuf-codegen-pure) crate,
-//! similar API to `protoc-rust`, but uses pure rust parser of `.proto` files.
-//!
-//! # `protoc-gen-rust` plugin for `protoc`
-//!
-//! When non-cargo build system is used, consider using standard protobuf code generation pattern:
-//! `protoc` command does all the work of handling paths and parsing `.proto` files.
-//! When `protoc` is invoked with `--rust_out=` option, it invokes `protoc-gen-rust` plugin.
-//! provided by this crate.
+//! # Protobuf code generator for `protobuf` crate
+//!
+//! This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build.
+//!
+//! # How to generate code
+//!
+//! There are three main ways to generate `.rs` files from `.proto` files:
+//! * using `protoc` command line tool and `protoc-gen-rust` plugin
+//! * using this crate `Codegen` with pure rust parser
+//! * using this crate `Codegen` with `protoc` parser
+//!
+//! Which one should you use depends on your needs.
+//!
+//! If you are using non-cargo build system (like Bazel), you might prefer
+//! using `protoc-gen-rust` plugin for `protoc`.
+//!
+//! If you build with `cargo`, you probably want to use `Codegen` from this crate.
+//!
+//! # Protoc parser vs pure rust parser
+//!
+//! There are two protobuf parsers which can be plugged into this crate:
+//! * `protoc`-based parser (`protoc` is a command like utility from Google protobuf)
+//! * pure rust parser (`protobuf-parse` crate)
+//!
+//! `protoc`-based parser is expected to parse `.proto` files very correctly:
+//! all Google's protobuf implementations rely on it.
+//!
+//! While there are no known bugs in `protobuf-parse`, it is not tested very well.
+//! Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser,
+//! mostly complex message options specified in `.proto` files.
+//! I never saw anyone using them, but you have been warned.
+//!
+//! Note `protoc` command can be obtained from
+//! [`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate.
+//!
+//! # Example
+//!
+//! ```no_run
+//! # mod protoc_bin_vendored {
+//! # pub fn protoc_bin_path() -> Result<std::path::PathBuf, std::io::Error> {
+//! # unimplemented!()
+//! # }
+//! # }
+//! // Use this in build.rs
+//! protobuf_codegen::Codegen::new()
+//! // Use `protoc` parser, optional.
+//! .protoc()
+//! // Use `protoc-bin-vendored` bundled protoc command, optional.
+//! .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap())
+//! // All inputs and imports from the inputs must reside in `includes` directories.
+//! .includes(&["src/protos"])
+//! // Inputs must reside in some of include paths.
+//! .input("src/protos/apple.proto")
+//! .input("src/protos/banana.proto")
+//! // Specify output directory relative to Cargo output directory.
+//! .cargo_out_dir("protos")
+//! .run_from_script();
+//! ```
//!
-//! When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates.
+//! ## How to use `protoc-gen-rust`
//!
-//! ## How to use `protoc-gen-rust` if you have to
+//! If you have to.
//!
//! (Note `protoc` can be invoked programmatically with
-//! [protoc crate](https://docs.rs/protoc))
+//! [protoc crate](https://docs.rs/protoc/%3E=3.0.0-alpha))
//!
//! 0) Install protobuf for `protoc` binary.
//!
@@ -46,11 +79,11 @@
//! ```
//!
//! Protobuf is needed only for code generation, `rust-protobuf` runtime
-//! does not use `protobuf` library.
+//! does not use C++ protobuf library.
//!
//! 1) Install `protoc-gen-rust` program (which is `protoc` plugin)
//!
-//! It can be installed either from source or with `cargo install protobuf` command.
+//! It can be installed either from source or with `cargo install protobuf-codegen` command.
//!
//! 2) Add `protoc-gen-rust` to $PATH
//!
@@ -68,320 +101,45 @@
//!
//! This will generate .rs files in current directory.
//!
-//! # Version 2
+//! # Customize generate code
//!
-//! This is documentation for version 2 of the crate.
+//! Sometimes generated code need to be adjusted, e. g. to have custom derives.
//!
-//! [Version 3 of the crate](https://docs.rs/protobuf-codegen/%3E=3.0.0-alpha)
-//! (currently in development) encapsulates both `protoc` and pure codegens in this crate.
+//! rust-protobuf provides two options to do that:
+//! * generated `.rs` files contain `@@protoc_insertion_point(...)` markers
+//! (similar markers inserts Google's protobuf generator for C++ or Java).
+//! Simple script `sed` one-liners can be used to replace these markers with custom annotations.
+//! * `Codegen::customize_callback` can be used to patch generated code
+//! when invoked from `build.rs` script.
+//!
+//! # Serde
+//!
+//! rust-protobuf since version 3 no longer directly supports serde.
+//!
+//! Rust-protobuf 3 fully supports:
+//! * runtime reflection
+//! * JSON parsing and printing via
+//! [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping)
+//!
+//! Which covers the most of serde use cases.
+//!
+//! If you still need serde, generic customization callback (see above) can be used
+//! to insert `#[serde(...)]` annotations.
+//!
+//! [Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)
+//! in the rust-protobuf repository demonstrates how to do it.
#![deny(rustdoc::broken_intra_doc_links)]
-#![deny(missing_docs)]
-
-extern crate protobuf;
-
-use std::collections::hash_map::HashMap;
-use std::fmt::Write as FmtWrite;
-use std::fs::File;
-use std::io;
-use std::io::Write;
-use std::path::Path;
-
-use protobuf::compiler_plugin;
-use protobuf::descriptor::*;
-use protobuf::Message;
+mod codegen;
+mod compiler_plugin;
mod customize;
-mod enums;
-mod extensions;
-mod field;
-mod file;
-mod file_and_mod;
-mod file_descriptor;
-#[doc(hidden)]
-pub mod float;
-mod inside;
-mod message;
-mod oneof;
-mod protobuf_name;
-mod rust_name;
-mod rust_types_values;
-mod serde;
-mod well_known_types;
-
-pub(crate) mod rust;
-pub(crate) mod scope;
-pub(crate) mod strx;
-pub(crate) mod syntax;
+mod gen;
+pub mod gen_and_write;
+pub mod protoc_gen_rust;
-use customize::customize_from_rustproto_for_file;
-#[doc(hidden)]
+pub use codegen::Codegen;
pub use customize::Customize;
-
-pub mod code_writer;
-
-use inside::protobuf_crate_path;
-#[doc(hidden)]
-pub use protobuf_name::ProtobufAbsolutePath;
-#[doc(hidden)]
-pub use protobuf_name::ProtobufIdent;
-#[doc(hidden)]
-pub use protobuf_name::ProtobufRelativePath;
-use scope::FileScope;
-use scope::RootScope;
-
-use self::code_writer::CodeWriter;
-use self::enums::*;
-use self::extensions::*;
-use self::message::*;
-use crate::file::proto_path_to_rust_mod;
-
-fn escape_byte(s: &mut String, b: u8) {
- if b == b'\n' {
- write!(s, "\\n").unwrap();
- } else if b == b'\r' {
- write!(s, "\\r").unwrap();
- } else if b == b'\t' {
- write!(s, "\\t").unwrap();
- } else if b == b'\\' || b == b'"' {
- write!(s, "\\{}", b as char).unwrap();
- } else if b == b'\0' {
- write!(s, "\\0").unwrap();
- // ASCII printable except space
- } else if b > 0x20 && b < 0x7f {
- write!(s, "{}", b as char).unwrap();
- } else {
- write!(s, "\\x{:02x}", b).unwrap();
- }
-}
-
-fn write_file_descriptor_data(
- file: &FileDescriptorProto,
- customize: &Customize,
- w: &mut CodeWriter,
-) {
- let fdp_bytes = file.write_to_bytes().unwrap();
- w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\");
- w.indented(|w| {
- const MAX_LINE_LEN: usize = 72;
-
- let mut s = String::new();
- for &b in &fdp_bytes {
- let prev_len = s.len();
- escape_byte(&mut s, b);
- let truncate = s.len() > MAX_LINE_LEN;
- if truncate {
- s.truncate(prev_len);
- }
- if truncate || s.len() == MAX_LINE_LEN {
- write!(s, "\\").unwrap();
- w.write_line(&s);
- s.clear();
- }
- if truncate {
- escape_byte(&mut s, b);
- }
- }
- if !s.is_empty() {
- write!(s, "\\").unwrap();
- w.write_line(&s);
- s.clear();
- }
- });
- w.write_line("\";");
- w.write_line("");
- w.lazy_static(
- "file_descriptor_proto_lazy",
- &format!(
- "{}::descriptor::FileDescriptorProto",
- protobuf_crate_path(customize)
- ),
- customize,
- );
- w.write_line("");
- w.def_fn(
- &format!(
- "parse_descriptor_proto() -> {}::descriptor::FileDescriptorProto",
- protobuf_crate_path(customize)
- ),
- |w| {
- w.write_line(&format!(
- "{}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()",
- protobuf_crate_path(customize)
- ));
- },
- );
- w.write_line("");
- w.pub_fn(
- &format!(
- "file_descriptor_proto() -> &'static {}::descriptor::FileDescriptorProto",
- protobuf_crate_path(customize)
- ),
- |w| {
- w.block("file_descriptor_proto_lazy.get(|| {", "})", |w| {
- w.write_line("parse_descriptor_proto()");
- });
- },
- );
-}
-
-struct GenFileResult {
- compiler_plugin_result: compiler_plugin::GenResult,
- mod_name: String,
-}
-
-fn gen_file(
- file: &FileDescriptorProto,
- _files_map: &HashMap<&str, &FileDescriptorProto>,
- root_scope: &RootScope,
- customize: &Customize,
-) -> GenFileResult {
- // TODO: use it
- let mut customize = customize.clone();
- // options specified in invocation have precedence over options specified in file
- customize.update_with(&customize_from_rustproto_for_file(file.get_options()));
-
- let scope = FileScope {
- file_descriptor: file,
- }
- .to_scope();
- let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
- file.get_options().get_optimize_for() == FileOptions_OptimizeMode::LITE_RUNTIME
- });
-
- let mut v = Vec::new();
-
- {
- let mut w = CodeWriter::new(&mut v);
-
- w.write_generated_by("rust-protobuf", "2.27.1");
- w.write_line(&format!("//! Generated file from `{}`", file.get_name()));
- if customize.inside_protobuf != Some(true) {
- w.write_line("");
- w.write_line("/// Generated files are compatible only with the same version");
- w.write_line("/// of protobuf runtime.");
- w.commented(|w| {
- w.write_line(&format!(
- "const _PROTOBUF_VERSION_CHECK: () = {}::{};",
- protobuf_crate_path(&customize),
- protobuf::VERSION_IDENT
- ));
- })
- }
-
- for message in &scope.get_messages() {
- // ignore map entries, because they are not used in map fields
- if message.map_entry().is_none() {
- w.write_line("");
- MessageGen::new(message, &root_scope, &customize).write(&mut w);
- }
- }
- for enum_type in &scope.get_enums() {
- w.write_line("");
- EnumGen::new(enum_type, file, &customize, root_scope).write(&mut w);
- }
-
- write_extensions(file, &root_scope, &mut w, &customize);
-
- if !lite_runtime {
- w.write_line("");
- write_file_descriptor_data(file, &customize, &mut w);
- }
- }
-
- GenFileResult {
- compiler_plugin_result: compiler_plugin::GenResult {
- name: format!("{}.rs", proto_path_to_rust_mod(file.get_name())),
- content: v,
- },
- mod_name: proto_path_to_rust_mod(file.get_name()).into_string(),
- }
-}
-
-fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult {
- let mut v = Vec::new();
- let mut w = CodeWriter::new(&mut v);
- w.comment("@generated");
- w.write_line("");
- for m in mods {
- w.write_line(&format!("pub mod {};", m));
- }
- drop(w);
- compiler_plugin::GenResult {
- name: "mod.rs".to_owned(),
- content: v,
- }
-}
-
-// This function is also used externally by cargo plugin
-// https://github.com/plietar/rust-protobuf-build
-// So be careful changing its signature.
-#[doc(hidden)]
-pub fn gen(
- file_descriptors: &[FileDescriptorProto],
- files_to_generate: &[String],
- customize: &Customize,
-) -> Vec<compiler_plugin::GenResult> {
- let root_scope = RootScope {
- file_descriptors: file_descriptors,
- };
-
- let mut results: Vec<compiler_plugin::GenResult> = Vec::new();
- let files_map: HashMap<&str, &FileDescriptorProto> =
- file_descriptors.iter().map(|f| (f.get_name(), f)).collect();
-
- let all_file_names: Vec<&str> = file_descriptors.iter().map(|f| f.get_name()).collect();
-
- let mut mods = Vec::new();
-
- for file_name in files_to_generate {
- let file = files_map.get(&file_name[..]).expect(&format!(
- "file not found in file descriptors: {:?}, files: {:?}",
- file_name, all_file_names
- ));
-
- let gen_file_result = gen_file(file, &files_map, &root_scope, customize);
- results.push(gen_file_result.compiler_plugin_result);
- mods.push(gen_file_result.mod_name);
- }
-
- if customize.gen_mod_rs.unwrap_or(false) {
- results.push(gen_mod_rs(&mods));
- }
-
- results
-}
-
-#[doc(hidden)]
-pub fn gen_and_write(
- file_descriptors: &[FileDescriptorProto],
- files_to_generate: &[String],
- out_dir: &Path,
- customize: &Customize,
-) -> io::Result<()> {
- let results = gen(file_descriptors, files_to_generate, customize);
-
- for r in &results {
- let mut file_path = out_dir.to_owned();
- file_path.push(&r.name);
- let mut file_writer = File::create(&file_path)?;
- file_writer.write_all(&r.content)?;
- file_writer.flush()?;
- }
-
- Ok(())
-}
-
-#[doc(hidden)]
-pub fn protoc_gen_rust_main() {
- compiler_plugin::plugin_main_2(|r| {
- let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
- gen(r.file_descriptors, r.files_to_generate, &customize)
- });
-}
-
-/// Used in protobuf-codegen-identical-test
+pub use customize::CustomizeCallback;
#[doc(hidden)]
-pub fn proto_name_to_rs(name: &str) -> String {
- format!("{}.rs", proto_path_to_rust_mod(name))
-}
+pub use gen::paths::proto_name_to_rs;
diff --git a/src/message.rs b/src/message.rs
deleted file mode 100644
index fbe9ed4..0000000
--- a/src/message.rs
+++ /dev/null
@@ -1,626 +0,0 @@
-use std::fmt;
-
-use file_descriptor::file_descriptor_proto_expr;
-use inside::protobuf_crate_path;
-use oneof::OneofGen;
-use oneof::OneofVariantGen;
-use protobuf::descriptor::*;
-use rust_name::RustIdentWithPath;
-use scope::MessageWithScope;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-
-use super::code_writer::*;
-use super::customize::customize_from_rustproto_for_message;
-use super::customize::Customize;
-use super::enums::*;
-use super::field::*;
-use super::rust_types_values::*;
-
-/// Protobuf message Rust type name
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub(crate) struct RustTypeMessage(pub RustIdentWithPath);
-
-impl fmt::Display for RustTypeMessage {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- fmt::Display::fmt(&self.0, f)
- }
-}
-
-impl RustTypeMessage {
- /// Code which emits default instance.
- pub fn default_instance(&self, customize: &Customize) -> String {
- format!(
- "<{} as {}::Message>::default_instance()",
- self.0,
- protobuf_crate_path(customize)
- )
- }
-}
-
-/// Message info for codegen
-pub(crate) struct MessageGen<'a> {
- pub message: &'a MessageWithScope<'a>,
- pub root_scope: &'a RootScope<'a>,
- type_name: RustIdentWithPath,
- pub fields: Vec<FieldGen<'a>>,
- pub lite_runtime: bool,
- customize: Customize,
-}
-
-impl<'a> MessageGen<'a> {
- pub fn new(
- message: &'a MessageWithScope<'a>,
- root_scope: &'a RootScope<'a>,
- customize: &Customize,
- ) -> MessageGen<'a> {
- let mut customize = customize.clone();
- customize.update_with(&customize_from_rustproto_for_message(
- message.message.get_options(),
- ));
-
- let fields: Vec<_> = message
- .fields()
- .into_iter()
- .map(|field| FieldGen::parse(field, root_scope, &customize))
- .collect();
- let lite_runtime = customize.lite_runtime.unwrap_or_else(|| {
- message
- .get_file_descriptor()
- .get_options()
- .get_optimize_for()
- == FileOptions_OptimizeMode::LITE_RUNTIME
- });
- MessageGen {
- message,
- root_scope,
- type_name: message.rust_name().into(),
- fields,
- lite_runtime,
- customize,
- }
- }
-
- fn expose_oneof(&self) -> bool {
- self.customize.expose_oneof.unwrap_or(true)
- }
-
- fn oneofs(&'a self) -> Vec<OneofGen<'a>> {
- self.message
- .oneofs()
- .into_iter()
- .map(|oneof| OneofGen::parse(self, oneof, &self.customize))
- .collect()
- }
-
- fn required_fields(&'a self) -> Vec<&'a FieldGen> {
- self.fields
- .iter()
- .filter(|f| match f.kind {
- FieldKind::Singular(ref singular) => singular.flag.is_required(),
- _ => false,
- })
- .collect()
- }
-
- fn message_fields(&'a self) -> Vec<&'a FieldGen> {
- self.fields
- .iter()
- .filter(|f| f.proto_type == FieldDescriptorProto_Type::TYPE_MESSAGE)
- .collect()
- }
-
- fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen> {
- self.fields.iter().filter(|f| !f.is_oneof()).collect()
- }
-
- fn fields_except_group(&'a self) -> Vec<&'a FieldGen> {
- self.fields
- .iter()
- .filter(|f| f.proto_type != FieldDescriptorProto_Type::TYPE_GROUP)
- .collect()
- }
-
- fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen> {
- self.fields
- .iter()
- .filter(|f| !f.is_oneof() && f.proto_type != FieldDescriptorProto_Type::TYPE_GROUP)
- .collect()
- }
-
- fn write_match_each_oneof_variant<F>(&self, w: &mut CodeWriter, cb: F)
- where
- F: Fn(&mut CodeWriter, &OneofVariantGen, &str, &RustType),
- {
- for oneof in self.oneofs() {
- w.if_let_stmt(
- "::std::option::Option::Some(ref v)",
- &format!("self.{}", oneof.oneof.field_name())[..],
- |w| {
- w.match_block("v", |w| {
- for variant in oneof.variants_except_group() {
- let ref field = variant.field;
- let (refv, vtype) = if !field.elem_type_is_copy() {
- ("ref v", field.elem().rust_storage_type().ref_type())
- } else {
- ("v", field.elem().rust_storage_type())
- };
- w.case_block(format!("&{}({})", variant.path(), refv), |w| {
- cb(w, &variant, "v", &vtype);
- });
- }
- });
- },
- );
- }
- }
-
- fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) {
- let sig = format!(
- "write_to_with_cached_sizes(&self, os: &mut {}::CodedOutputStream<'_>) -> {}::ProtobufResult<()>",
- protobuf_crate_path(&self.customize),
- protobuf_crate_path(&self.customize),
- );
- w.def_fn(&sig, |w| {
- // To have access to its methods but not polute the name space.
- for f in self.fields_except_oneof_and_group() {
- f.write_message_write_field(w);
- }
- self.write_match_each_oneof_variant(w, |w, variant, v, v_type| {
- variant.field.write_write_element(w, "os", v, v_type);
- });
- w.write_line("os.write_unknown_fields(self.get_unknown_fields())?;");
- w.write_line("::std::result::Result::Ok(())");
- });
- }
-
- fn write_get_cached_size(&self, w: &mut CodeWriter) {
- w.def_fn("get_cached_size(&self) -> u32", |w| {
- w.write_line("self.cached_size.get()");
- });
- }
-
- fn write_default_instance(&self, w: &mut CodeWriter) {
- w.def_fn(
- &format!("default_instance() -> &'static {}", self.type_name),
- |w| {
- w.lazy_static_decl_get_simple(
- "instance",
- &self.type_name.to_string(),
- &format!("{}::new", self.type_name),
- &self.customize,
- );
- },
- );
- }
-
- fn write_compute_size(&self, w: &mut CodeWriter) {
- // Append sizes of messages in the tree to the specified vector.
- // First appended element is size of self, and then nested message sizes.
- // in serialization order are appended recursively.");
- w.comment("Compute sizes of nested messages");
- // there are unused variables in oneof
- w.allow(&["unused_variables"]);
- w.def_fn("compute_size(&self) -> u32", |w| {
- // To have access to its methods but not polute the name space.
- w.write_line("let mut my_size = 0;");
- for field in self.fields_except_oneof_and_group() {
- field.write_message_compute_field_size("my_size", w);
- }
- self.write_match_each_oneof_variant(w, |w, variant, v, vtype| {
- variant.field.write_element_size(w, v, vtype, "my_size");
- });
- w.write_line(&format!(
- "my_size += {}::rt::unknown_fields_size(self.get_unknown_fields());",
- protobuf_crate_path(&self.customize)
- ));
- w.write_line("self.cached_size.set(my_size);");
- w.write_line("my_size");
- });
- }
-
- fn write_field_accessors(&self, w: &mut CodeWriter) {
- for f in self.fields_except_group() {
- w.write_line("");
- let reconstruct_def = f.reconstruct_def();
- w.comment(&(reconstruct_def + ";"));
- w.write_line("");
- f.write_message_single_field_accessors(w);
- }
- }
-
- fn write_impl_self(&self, w: &mut CodeWriter) {
- w.impl_self_block(&self.type_name.to_string(), |w| {
- // TODO: new should probably be a part of Message trait
- w.pub_fn(&format!("new() -> {}", self.type_name), |w| {
- w.write_line("::std::default::Default::default()");
- });
-
- self.write_field_accessors(w);
- });
- }
-
- fn write_unknown_fields(&self, w: &mut CodeWriter) {
- w.def_fn(
- &format!(
- "get_unknown_fields(&self) -> &{}::UnknownFields",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.write_line("&self.unknown_fields");
- },
- );
- w.write_line("");
- w.def_fn(
- &format!(
- "mut_unknown_fields(&mut self) -> &mut {}::UnknownFields",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.write_line("&mut self.unknown_fields");
- },
- );
- }
-
- fn write_merge_from(&self, w: &mut CodeWriter) {
- let sig = format!(
- "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::ProtobufResult<()>",
- protobuf_crate_path(&self.customize),
- protobuf_crate_path(&self.customize),
- );
- w.def_fn(&sig, |w| {
- w.while_block("!is.eof()?", |w| {
- w.write_line(&format!("let (field_number, wire_type) = is.read_tag_unpack()?;"));
- w.match_block("field_number", |w| {
- for f in &self.fields_except_group() {
- let number = f.proto_field.number();
- w.case_block(number.to_string(), |w| {
- f.write_merge_from_field("wire_type", w);
- });
- }
- w.case_block("_", |w| {
- w.write_line(&format!("{}::rt::read_unknown_or_skip_group(field_number, wire_type, is, self.mut_unknown_fields())?;", protobuf_crate_path(&self.customize)));
- });
- });
- });
- w.write_line("::std::result::Result::Ok(())");
- });
- }
-
- fn write_descriptor_field(&self, fields_var: &str, field: &FieldGen, w: &mut CodeWriter) {
- let accessor_fn = field.accessor_fn();
- w.write_line(&format!(
- "{}.push({}::reflect::accessor::{}(",
- fields_var,
- protobuf_crate_path(&self.customize),
- accessor_fn.sig()
- ));
- w.indented(|w| {
- w.write_line(&format!("\"{}\",", field.proto_field.name()));
- match accessor_fn.style {
- AccessorStyle::Lambda => {
- w.write_line(&format!(
- "|m: &{}| {{ &m.{} }},",
- self.type_name, field.rust_name
- ));
- w.write_line(&format!(
- "|m: &mut {}| {{ &mut m.{} }},",
- self.type_name, field.rust_name
- ));
- }
- AccessorStyle::HasGet => {
- w.write_line(&format!("{}::has_{},", self.type_name, field.rust_name));
- w.write_line(&format!("{}::get_{},", self.type_name, field.rust_name));
- }
- }
- });
- w.write_line("));");
- }
-
- fn write_descriptor_static(&self, w: &mut CodeWriter) {
- w.def_fn(
- &format!(
- "descriptor_static() -> &'static {}::reflect::MessageDescriptor",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.lazy_static_decl_get(
- "descriptor",
- &format!(
- "{}::reflect::MessageDescriptor",
- protobuf_crate_path(&self.customize)
- ),
- &self.customize,
- |w| {
- let fields = self.fields_except_group();
- if fields.is_empty() {
- w.write_line(&format!("let fields = ::std::vec::Vec::new();"));
- } else {
- w.write_line(&format!("let mut fields = ::std::vec::Vec::new();"));
- }
- for field in fields {
- self.write_descriptor_field("fields", field, w);
- }
- w.write_line(&format!(
- "{}::reflect::MessageDescriptor::new_pb_name::<{}>(",
- protobuf_crate_path(&self.customize),
- self.type_name
- ));
- w.indented(|w| {
- w.write_line(&format!("\"{}\",", self.message.name_to_package()));
- w.write_line("fields,");
- w.write_line(&file_descriptor_proto_expr(&self.message.scope));
- });
- w.write_line(")");
- },
- );
- },
- );
- }
-
- fn write_is_initialized(&self, w: &mut CodeWriter) {
- w.def_fn(&format!("is_initialized(&self) -> bool"), |w| {
- // TODO: use single loop
-
- for f in self.required_fields() {
- f.write_if_self_field_is_none(w, |w| {
- w.write_line("return false;");
- });
- }
-
- for f in self.message_fields() {
- if let FieldKind::Map(..) = f.kind {
- // TODO: check values
- continue;
- }
-
- // TODO:
- // if message is declared in this file and has no message fields,
- // we could skip the check here
- f.write_for_self_field(w, "v", |w, _t| {
- w.if_stmt("!v.is_initialized()", |w| {
- w.write_line("return false;");
- });
- });
- }
- w.write_line("true");
- });
- }
-
- fn write_impl_message(&self, w: &mut CodeWriter) {
- w.impl_for_block(
- &format!("{}::Message", protobuf_crate_path(&self.customize)),
- &self.type_name.to_string(), |w| {
- self.write_is_initialized(w);
- w.write_line("");
- self.write_merge_from(w);
- w.write_line("");
- self.write_compute_size(w);
- w.write_line("");
- self.write_write_to_with_cached_sizes(w);
- w.write_line("");
- self.write_get_cached_size(w);
- w.write_line("");
- self.write_unknown_fields(w);
- w.write_line("");
- w.def_fn("as_any(&self) -> &dyn (::std::any::Any)", |w| {
- w.write_line("self as &dyn (::std::any::Any)");
- });
- w.def_fn("as_any_mut(&mut self) -> &mut dyn (::std::any::Any)", |w| {
- w.write_line("self as &mut dyn (::std::any::Any)");
- });
- w.def_fn(
- "into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn (::std::any::Any)>",
- |w| {
- w.write_line("self");
- },
- );
- w.write_line("");
- w.def_fn(
- &format!("descriptor(&self) -> &'static {}::reflect::MessageDescriptor", protobuf_crate_path(&self.customize)),
- |w| {
- w.write_line("Self::descriptor_static()");
- },
- );
- w.write_line("");
- w.def_fn(&format!("new() -> {}", self.type_name), |w| {
- w.write_line(&format!("{}::new()", self.type_name));
- });
- if !self.lite_runtime {
- w.write_line("");
- self.write_descriptor_static(w);
- }
- w.write_line("");
- self.write_default_instance(w);
- });
- }
-
- fn write_impl_value(&self, w: &mut CodeWriter) {
- w.impl_for_block(
- &format!(
- "{}::reflect::ProtobufValue",
- protobuf_crate_path(&self.customize)
- ),
- &self.type_name.to_string(),
- |w| {
- w.def_fn(
- &format!(
- "as_ref(&self) -> {}::reflect::ReflectValueRef",
- protobuf_crate_path(&self.customize)
- ),
- |w| {
- w.write_line(&format!(
- "{}::reflect::ReflectValueRef::Message(self)",
- protobuf_crate_path(&self.customize)
- ))
- },
- )
- },
- )
- }
-
- fn write_impl_show(&self, w: &mut CodeWriter) {
- w.impl_for_block("::std::fmt::Debug", &self.type_name.to_string(), |w| {
- w.def_fn(
- "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result",
- |w| {
- w.write_line(&format!(
- "{}::text_format::fmt(self, f)",
- protobuf_crate_path(&self.customize)
- ));
- },
- );
- });
- }
-
- fn write_impl_clear(&self, w: &mut CodeWriter) {
- w.impl_for_block(
- &format!("{}::Clear", protobuf_crate_path(&self.customize)),
- &format!("{}", self.type_name),
- |w| {
- w.def_fn("clear(&mut self)", |w| {
- for f in self.fields_except_group() {
- f.write_clear(w);
- }
- w.write_line("self.unknown_fields.clear();");
- });
- },
- );
- }
-
- #[allow(dead_code)]
- fn supports_derive_partial_eq(&self) -> bool {
- // There's stack overflow in the compiler when struct has too many fields
- // https://github.com/rust-lang/rust/issues/40119
- self.fields.len() <= 500
- }
-
- fn write_struct(&self, w: &mut CodeWriter) {
- let mut derive = vec!["PartialEq", "Clone", "Default"];
- if self.lite_runtime {
- derive.push("Debug");
- }
- w.derive(&derive);
- serde::write_serde_attr(
- w,
- &self.customize,
- "derive(::serde::Serialize, ::serde::Deserialize)",
- );
- w.pub_struct(&self.type_name.to_string(), |w| {
- if !self.fields_except_oneof().is_empty() {
- w.comment("message fields");
- for field in self.fields_except_oneof() {
- if field.proto_type == FieldDescriptorProto_Type::TYPE_GROUP {
- w.comment(&format!("{}: <group>", &field.rust_name));
- } else {
- let vis = if field.expose_field {
- Visibility::Public
- } else {
- match field.kind {
- FieldKind::Repeated(..) => Visibility::Default,
- FieldKind::Singular(SingularField { ref flag, .. }) => {
- match *flag {
- SingularFieldFlag::WithFlag { .. } => Visibility::Default,
- SingularFieldFlag::WithoutFlag => Visibility::Public,
- }
- }
- FieldKind::Map(..) => Visibility::Public,
- FieldKind::Oneof(..) => unreachable!(),
- }
- };
- w.field_decl_vis(
- vis,
- &field.rust_name.get(),
- &field.full_storage_type().to_code(&self.customize),
- );
- }
- }
- }
- if !self.oneofs().is_empty() {
- w.comment("message oneof groups");
- for oneof in self.oneofs() {
- let vis = match self.expose_oneof() {
- true => Visibility::Public,
- false => Visibility::Default,
- };
- w.field_decl_vis(
- vis,
- oneof.oneof.field_name().get(),
- &oneof.full_storage_type().to_code(&self.customize),
- );
- }
- }
- w.comment("special fields");
- serde::write_serde_attr(w, &self.customize, "serde(skip)");
- w.pub_field_decl(
- "unknown_fields",
- &format!("{}::UnknownFields", protobuf_crate_path(&self.customize)),
- );
- serde::write_serde_attr(w, &self.customize, "serde(skip)");
- w.pub_field_decl(
- "cached_size",
- &format!("{}::CachedSize", protobuf_crate_path(&self.customize)),
- );
- });
- }
-
- fn write_impl_default_for_amp(&self, w: &mut CodeWriter) {
- w.impl_args_for_block(
- &["'a"],
- "::std::default::Default",
- &format!("&'a {}", self.type_name),
- |w| {
- w.def_fn(&format!("default() -> &'a {}", self.type_name), |w| {
- w.write_line(&format!(
- "<{} as {}::Message>::default_instance()",
- self.type_name,
- protobuf_crate_path(&self.customize),
- ));
- });
- },
- );
- }
-
- pub fn write(&self, w: &mut CodeWriter) {
- self.write_struct(w);
-
- w.write_line("");
- self.write_impl_default_for_amp(w);
-
- for oneof in self.oneofs() {
- w.write_line("");
- oneof.write_enum(w);
- }
-
- w.write_line("");
- self.write_impl_self(w);
- w.write_line("");
- self.write_impl_message(w);
- w.write_line("");
- self.write_impl_clear(w);
- if !self.lite_runtime {
- w.write_line("");
- self.write_impl_show(w);
- }
- w.write_line("");
- self.write_impl_value(w);
-
- let mut nested_prefix = self.type_name.to_string();
- nested_prefix.push_str("_");
-
- for nested in &self.message.to_scope().get_messages() {
- // ignore map entries, because they are not used in map fields
- if nested.map_entry().is_none() {
- w.write_line("");
- MessageGen::new(nested, self.root_scope, &self.customize).write(w);
- }
- }
-
- for enum_type in &self.message.to_scope().get_enums() {
- w.write_line("");
- let current_file = self.message.get_scope().get_file_descriptor();
- EnumGen::new(enum_type, current_file, &self.customize, self.root_scope).write(w);
- }
- }
-}
diff --git a/src/oneof.rs b/src/oneof.rs
deleted file mode 100644
index 44201ea..0000000
--- a/src/oneof.rs
+++ /dev/null
@@ -1,198 +0,0 @@
-//! Oneof-related codegen functions.
-
-use std::collections::HashSet;
-
-use code_writer::CodeWriter;
-use field::FieldElem;
-use field::FieldGen;
-use message::MessageGen;
-use protobuf::descriptor::FieldDescriptorProto_Type;
-use protobuf_name::ProtobufAbsolutePath;
-use rust_name::RustIdent;
-use rust_types_values::RustType;
-use scope::FieldWithContext;
-use scope::OneofVariantWithContext;
-use scope::OneofWithContext;
-use scope::RootScope;
-use scope::WithScope;
-use serde;
-use Customize;
-
-// oneof one { ... }
-#[derive(Clone)]
-pub(crate) struct OneofField<'a> {
- pub elem: FieldElem<'a>,
- pub oneof_rust_field_name: RustIdent,
- pub oneof_type_name: RustType,
- pub boxed: bool,
-}
-
-impl<'a> OneofField<'a> {
- // Detecting recursion: if oneof fields contains a self-reference
- // or another message which has a reference to self,
- // put oneof variant into a box.
- fn need_boxed(field: &FieldWithContext, root_scope: &RootScope, owner_name: &str) -> bool {
- let mut visited_messages = HashSet::new();
- let mut fields = vec![field.clone()];
- while let Some(field) = fields.pop() {
- if field.field.get_field_type() == FieldDescriptorProto_Type::TYPE_MESSAGE {
- let message_name = ProtobufAbsolutePath::from(field.field.get_type_name());
- if !visited_messages.insert(message_name.clone()) {
- continue;
- }
- if message_name.path == owner_name {
- return true;
- }
- let message = root_scope.find_message(&message_name);
- fields.extend(message.fields().into_iter().filter(|f| f.is_oneof()));
- }
- }
- false
- }
-
- pub fn parse(
- oneof: &OneofWithContext<'a>,
- field: &FieldWithContext<'a>,
- elem: FieldElem<'a>,
- root_scope: &RootScope,
- ) -> OneofField<'a> {
- let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute().path);
-
- OneofField {
- elem,
- boxed,
- oneof_rust_field_name: oneof.field_name().into(),
- oneof_type_name: RustType::Oneof(oneof.rust_name().to_string()),
- }
- }
-
- pub fn rust_type(&self) -> RustType {
- let t = self.elem.rust_storage_type();
-
- if self.boxed {
- RustType::Uniq(Box::new(t))
- } else {
- t
- }
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofVariantGen<'a> {
- _oneof: &'a OneofGen<'a>,
- _variant: OneofVariantWithContext<'a>,
- oneof_field: OneofField<'a>,
- pub field: FieldGen<'a>,
- path: String,
- _customize: Customize,
-}
-
-impl<'a> OneofVariantGen<'a> {
- fn parse(
- oneof: &'a OneofGen<'a>,
- variant: OneofVariantWithContext<'a>,
- field: &'a FieldGen,
- _root_scope: &RootScope,
- customize: Customize,
- ) -> OneofVariantGen<'a> {
- OneofVariantGen {
- _oneof: oneof,
- _variant: variant.clone(),
- field: field.clone(),
- path: format!(
- "{}::{}",
- oneof.type_name.to_code(&field.customize),
- field.rust_name
- ),
- oneof_field: OneofField::parse(
- variant.oneof,
- &field.proto_field,
- field.oneof().elem.clone(),
- oneof.message.root_scope,
- ),
- _customize: customize,
- }
- }
-
- fn rust_type(&self) -> RustType {
- self.oneof_field.rust_type()
- }
-
- pub fn path(&self) -> String {
- self.path.clone()
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofGen<'a> {
- // Message containing this oneof
- message: &'a MessageGen<'a>,
- pub oneof: OneofWithContext<'a>,
- type_name: RustType,
- customize: Customize,
-}
-
-impl<'a> OneofGen<'a> {
- pub fn parse(
- message: &'a MessageGen,
- oneof: OneofWithContext<'a>,
- customize: &Customize,
- ) -> OneofGen<'a> {
- let rust_name = oneof.rust_name();
- OneofGen {
- message,
- oneof,
- type_name: RustType::Oneof(rust_name.to_string()),
- customize: customize.clone(),
- }
- }
-
- pub fn variants_except_group(&'a self) -> Vec<OneofVariantGen<'a>> {
- self.oneof
- .variants()
- .into_iter()
- .filter_map(|v| {
- let field = self
- .message
- .fields
- .iter()
- .filter(|f| f.proto_field.name() == v.field.get_name())
- .next()
- .expect(&format!("field not found by name: {}", v.field.get_name()));
- match field.proto_type {
- FieldDescriptorProto_Type::TYPE_GROUP => None,
- _ => Some(OneofVariantGen::parse(
- self,
- v,
- field,
- self.message.root_scope,
- self.customize.clone(),
- )),
- }
- })
- .collect()
- }
-
- pub fn full_storage_type(&self) -> RustType {
- RustType::Option(Box::new(self.type_name.clone()))
- }
-
- pub fn write_enum(&self, w: &mut CodeWriter) {
- let derive = vec!["Clone", "PartialEq", "Debug"];
- w.derive(&derive);
- serde::write_serde_attr(
- w,
- &self.customize,
- "derive(::serde::Serialize, ::serde::Deserialize)",
- );
- w.pub_enum(&self.type_name.to_code(&self.customize), |w| {
- for variant in self.variants_except_group() {
- w.write_line(&format!(
- "{}({}),",
- variant.field.rust_name,
- &variant.rust_type().to_code(&self.customize)
- ));
- }
- });
- }
-}
diff --git a/src/protobuf_name.rs b/src/protobuf_name.rs
deleted file mode 100644
index c40a69c..0000000
--- a/src/protobuf_name.rs
+++ /dev/null
@@ -1,374 +0,0 @@
-use std::fmt;
-
-/// Identifier in `.proto` file
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub struct ProtobufIdent(String);
-
-impl ProtobufIdent {
- /// New ident from a string.
- #[allow(dead_code)]
- pub fn new(s: &str) -> ProtobufIdent {
- assert!(!s.is_empty());
- assert!(!s.contains("/"));
- assert!(!s.contains("."));
- assert!(!s.contains(":"));
- ProtobufIdent(s.to_owned())
- }
-
- /// Get as a string.
- pub fn get(&self) -> &str {
- &self.0
- }
-}
-
-impl From<&'_ str> for ProtobufIdent {
- fn from(s: &str) -> Self {
- ProtobufIdent::new(s)
- }
-}
-
-impl From<String> for ProtobufIdent {
- fn from(s: String) -> Self {
- ProtobufIdent::new(&s)
- }
-}
-
-impl fmt::Display for ProtobufIdent {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.get(), f)
- }
-}
-
-/// Relative protobuf identifier path.
-#[derive(Debug, Eq, PartialEq, Clone)]
-pub struct ProtobufRelativePath {
- /// The path
- pub path: String,
-}
-
-#[allow(dead_code)]
-impl ProtobufRelativePath {
- /// Empty relative path.
- pub fn empty() -> ProtobufRelativePath {
- ProtobufRelativePath::new(String::new())
- }
-
- /// New path from a string.
- pub fn new(path: String) -> ProtobufRelativePath {
- assert!(!path.starts_with("."));
-
- ProtobufRelativePath { path }
- }
-
- /// From path components.
- pub fn from_components<I: IntoIterator<Item = ProtobufIdent>>(i: I) -> ProtobufRelativePath {
- let v: Vec<String> = i.into_iter().map(|c| c.get().to_owned()).collect();
- ProtobufRelativePath::from(v.join("."))
- }
-
- /// Get the string.
- pub fn get(&self) -> &str {
- &self.path
- }
-
- /// The path is empty.
- pub fn is_empty(&self) -> bool {
- self.path.is_empty()
- }
-
- /// As absolute path from root namespace.
- pub fn into_absolute(self) -> ProtobufAbsolutePath {
- if self.is_empty() {
- ProtobufAbsolutePath::root()
- } else {
- ProtobufAbsolutePath::from(format!(".{}", self))
- }
- }
-
- fn _last_part(&self) -> Option<&str> {
- match self.path.rfind('.') {
- Some(pos) => Some(&self.path[pos + 1..]),
- None => {
- if self.path.is_empty() {
- None
- } else {
- Some(&self.path)
- }
- }
- }
- }
-
- fn parent(&self) -> Option<ProtobufRelativePath> {
- match self.path.rfind('.') {
- Some(pos) => Some(ProtobufRelativePath::new(self.path[..pos].to_owned())),
- None => {
- if self.path.is_empty() {
- None
- } else {
- Some(ProtobufRelativePath::empty())
- }
- }
- }
- }
-
- /// Self path and parent paths.
- pub fn self_and_parents(&self) -> Vec<ProtobufRelativePath> {
- let mut tmp = self.clone();
-
- let mut r = Vec::new();
-
- r.push(self.clone());
-
- while let Some(parent) = tmp.parent() {
- r.push(parent.clone());
- tmp = parent;
- }
-
- r
- }
-
- /// Append path component.
- pub fn append(&self, simple: &ProtobufRelativePath) -> ProtobufRelativePath {
- if self.path.is_empty() {
- ProtobufRelativePath::from(simple.get())
- } else {
- ProtobufRelativePath::new(format!("{}.{}", self.path, simple))
- }
- }
-
- /// Append identifier to the path.
- pub fn append_ident(&self, simple: &ProtobufIdent) -> ProtobufRelativePath {
- self.append(&ProtobufRelativePath::from(simple.clone()))
- }
-
- /// Get first component path and remaining.
- pub fn split_first_rem(&self) -> Option<(ProtobufIdent, ProtobufRelativePath)> {
- if self.is_empty() {
- None
- } else {
- Some(match self.path.find('.') {
- Some(dot) => (
- ProtobufIdent::from(&self.path[..dot]),
- ProtobufRelativePath::new(self.path[dot + 1..].to_owned()),
- ),
- None => (
- ProtobufIdent::from(self.path.clone()),
- ProtobufRelativePath::empty(),
- ),
- })
- }
- }
-}
-
-impl From<&'_ str> for ProtobufRelativePath {
- fn from(s: &str) -> ProtobufRelativePath {
- ProtobufRelativePath::from(s.to_owned())
- }
-}
-
-impl From<String> for ProtobufRelativePath {
- fn from(s: String) -> ProtobufRelativePath {
- ProtobufRelativePath::new(s)
- }
-}
-
-impl From<ProtobufIdent> for ProtobufRelativePath {
- fn from(s: ProtobufIdent) -> ProtobufRelativePath {
- ProtobufRelativePath::from(s.get())
- }
-}
-
-impl From<Vec<ProtobufIdent>> for ProtobufRelativePath {
- fn from(s: Vec<ProtobufIdent>) -> ProtobufRelativePath {
- ProtobufRelativePath::from_components(s.into_iter())
- }
-}
-
-impl fmt::Display for ProtobufRelativePath {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.path, f)
- }
-}
-
-#[cfg(test)]
-mod relative_path_test {
- use super::*;
-
- #[test]
- fn parent() {
- assert_eq!(None, ProtobufRelativePath::empty().parent());
- assert_eq!(
- Some(ProtobufRelativePath::empty()),
- ProtobufRelativePath::new("aaa".to_owned()).parent()
- );
- assert_eq!(
- Some(ProtobufRelativePath::new("abc".to_owned())),
- ProtobufRelativePath::new("abc.def".to_owned()).parent()
- );
- assert_eq!(
- Some(ProtobufRelativePath::new("abc.def".to_owned())),
- ProtobufRelativePath::new("abc.def.gh".to_owned()).parent()
- );
- }
-
- #[test]
- fn last_part() {
- assert_eq!(None, ProtobufRelativePath::empty()._last_part());
- assert_eq!(
- Some("aaa"),
- ProtobufRelativePath::new("aaa".to_owned())._last_part()
- );
- assert_eq!(
- Some("def"),
- ProtobufRelativePath::new("abc.def".to_owned())._last_part()
- );
- assert_eq!(
- Some("gh"),
- ProtobufRelativePath::new("abc.def.gh".to_owned())._last_part()
- );
- }
-}
-
-/// Absolute protobuf path (e. g. package).
-///
-/// This is not filesystem path.
-#[derive(Clone, Eq, PartialEq, Debug, Hash)]
-pub struct ProtobufAbsolutePath {
- /// The path.
- pub path: String,
-}
-
-impl ProtobufAbsolutePath {
- fn root() -> ProtobufAbsolutePath {
- ProtobufAbsolutePath::new(String::new())
- }
-
- /// From string.
- pub fn new(path: String) -> ProtobufAbsolutePath {
- assert!(path.is_empty() || path.starts_with("."), "{}", path);
- assert!(!path.ends_with("."), "{}", path);
- ProtobufAbsolutePath { path }
- }
-
- /// The path is empty.
- pub fn is_empty(&self) -> bool {
- self.path.is_empty()
- }
-
- /// From a path without leading dot.
- ///
- /// (Protobuf paths start with dot).
- pub fn from_path_without_dot(path: &str) -> ProtobufAbsolutePath {
- if path.is_empty() {
- ProtobufAbsolutePath::root()
- } else {
- assert!(!path.starts_with("."));
- assert!(!path.ends_with("."));
- ProtobufAbsolutePath::new(format!(".{}", path))
- }
- }
-
- /// Parse absolute path.
- #[allow(dead_code)]
- pub fn from_package_path(path: Option<&str>) -> ProtobufAbsolutePath {
- match path {
- None => ProtobufAbsolutePath::root(),
- Some(path) => ProtobufAbsolutePath::from_path_without_dot(path),
- }
- }
-
- /// Construct abs path from a string which may start with a dot.
- pub fn from_path_maybe_dot(path: &str) -> ProtobufAbsolutePath {
- if path.starts_with(".") {
- ProtobufAbsolutePath::new(path.to_owned())
- } else {
- ProtobufAbsolutePath::from_path_without_dot(path)
- }
- }
-
- /// Push identifier to the path.
- pub fn push_simple(&mut self, simple: ProtobufIdent) {
- self.path.push('.');
- self.path.push_str(simple.get());
- }
-
- /// Push relative path.
- pub fn push_relative(&mut self, relative: &ProtobufRelativePath) {
- if !relative.is_empty() {
- self.path.push('.');
- self.path.push_str(&relative.path);
- }
- }
-
- /// Try remove a prefix.
- pub fn remove_prefix(&self, prefix: &ProtobufAbsolutePath) -> Option<ProtobufRelativePath> {
- if self.path.starts_with(&prefix.path) {
- let rem = &self.path[prefix.path.len()..];
- if rem.is_empty() {
- return Some(ProtobufRelativePath::empty());
- }
- if rem.starts_with('.') {
- return Some(ProtobufRelativePath::new(rem[1..].to_owned()));
- }
- }
- None
- }
-}
-
-impl From<&'_ str> for ProtobufAbsolutePath {
- fn from(s: &str) -> Self {
- ProtobufAbsolutePath::new(s.to_owned())
- }
-}
-
-impl From<String> for ProtobufAbsolutePath {
- fn from(s: String) -> Self {
- ProtobufAbsolutePath::new(s)
- }
-}
-
-impl fmt::Display for ProtobufAbsolutePath {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.path, f)
- }
-}
-
-#[cfg(test)]
-mod absolute_path_test {
- use super::*;
-
- #[test]
- fn absolute_path_push_simple() {
- let mut foo = ProtobufAbsolutePath::new(".foo".to_owned());
- foo.push_simple(ProtobufIdent::from("bar"));
- assert_eq!(ProtobufAbsolutePath::new(".foo.bar".to_owned()), foo);
-
- let mut foo = ProtobufAbsolutePath::root();
- foo.push_simple(ProtobufIdent::from("bar"));
- assert_eq!(ProtobufAbsolutePath::new(".bar".to_owned()), foo);
- }
-
- #[test]
- fn absolute_path_remove_prefix() {
- assert_eq!(
- Some(ProtobufRelativePath::empty()),
- ProtobufAbsolutePath::new(".foo".to_owned())
- .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
- );
- assert_eq!(
- Some(ProtobufRelativePath::new("bar".to_owned())),
- ProtobufAbsolutePath::new(".foo.bar".to_owned())
- .remove_prefix(&ProtobufAbsolutePath::new(".foo".to_owned()))
- );
- assert_eq!(
- Some(ProtobufRelativePath::new("baz.qux".to_owned())),
- ProtobufAbsolutePath::new(".foo.bar.baz.qux".to_owned())
- .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
- );
- assert_eq!(
- None,
- ProtobufAbsolutePath::new(".foo.barbaz".to_owned())
- .remove_prefix(&ProtobufAbsolutePath::new(".foo.bar".to_owned()))
- );
- }
-}
diff --git a/src/protoc_gen_rust.rs b/src/protoc_gen_rust.rs
new file mode 100644
index 0000000..014db6a
--- /dev/null
+++ b/src/protoc_gen_rust.rs
@@ -0,0 +1,21 @@
+#![doc(hidden)]
+
+use crate::compiler_plugin;
+use crate::customize::CustomizeCallbackDefault;
+use crate::gen::all::gen_all;
+use crate::Customize;
+
+#[doc(hidden)]
+pub fn protoc_gen_rust_main() {
+ compiler_plugin::plugin_main(|r| {
+ let customize = Customize::parse_from_parameter(r.parameter).expect("parse options");
+ gen_all(
+ r.file_descriptors,
+ "protoc --rust-out=...",
+ r.files_to_generate,
+ &customize,
+ &CustomizeCallbackDefault,
+ )
+ })
+ .expect("plugin failed");
+}
diff --git a/src/rust_name.rs b/src/rust_name.rs
deleted file mode 100644
index 234925b..0000000
--- a/src/rust_name.rs
+++ /dev/null
@@ -1,273 +0,0 @@
-use std::fmt;
-use std::iter;
-
-/// Valid Rust identifier
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustIdent(String);
-
-#[allow(dead_code)]
-impl RustIdent {
- pub fn new(s: &str) -> RustIdent {
- assert!(!s.is_empty());
- assert!(!s.contains("/"), "{}", s);
- assert!(!s.contains("."), "{}", s);
- assert!(!s.contains(":"), "{}", s);
- RustIdent(s.to_owned())
- }
-
- pub fn super_ident() -> RustIdent {
- RustIdent::new("super")
- }
-
- pub fn get(&self) -> &str {
- &self.0
- }
-
- pub fn into_string(self) -> String {
- self.0
- }
-
- pub fn to_path(&self) -> RustIdentWithPath {
- RustIdentWithPath::from(&self.0)
- }
-}
-
-impl fmt::Display for RustIdent {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.get(), f)
- }
-}
-
-impl From<&'_ str> for RustIdent {
- fn from(s: &str) -> Self {
- RustIdent::new(s)
- }
-}
-
-impl From<String> for RustIdent {
- fn from(s: String) -> Self {
- RustIdent::new(&s)
- }
-}
-
-impl Into<String> for RustIdent {
- fn into(self) -> String {
- self.0
- }
-}
-
-#[derive(Default, Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustRelativePath {
- path: Vec<RustIdent>,
-}
-
-#[allow(dead_code)]
-impl RustRelativePath {
- pub fn into_path(self) -> RustPath {
- RustPath {
- absolute: false,
- path: self,
- }
- }
-
- pub fn empty() -> RustRelativePath {
- RustRelativePath { path: Vec::new() }
- }
-
- pub fn from_components<I: IntoIterator<Item = RustIdent>>(i: I) -> RustRelativePath {
- RustRelativePath {
- path: i.into_iter().collect(),
- }
- }
-
- pub fn is_empty(&self) -> bool {
- self.path.is_empty()
- }
-
- pub fn first(&self) -> Option<RustIdent> {
- self.path.iter().cloned().next()
- }
-
- pub fn remove_first(&mut self) -> Option<RustIdent> {
- if self.path.is_empty() {
- None
- } else {
- Some(self.path.remove(0))
- }
- }
-
- pub fn prepend_ident(&mut self, ident: RustIdent) {
- self.path.insert(0, ident);
- }
-
- pub fn append(mut self, path: RustRelativePath) -> RustRelativePath {
- for c in path.path {
- self.path.push(c);
- }
- self
- }
-
- pub fn push_ident(&mut self, ident: RustIdent) {
- self.path.push(ident);
- }
-
- pub fn _append_ident(mut self, ident: RustIdent) -> RustRelativePath {
- self.push_ident(ident);
- self
- }
-
- pub fn to_reverse(&self) -> RustRelativePath {
- RustRelativePath::from_components(
- iter::repeat(RustIdent::super_ident()).take(self.path.len()),
- )
- }
-}
-
-#[derive(Default, Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustPath {
- absolute: bool,
- path: RustRelativePath,
-}
-
-impl fmt::Display for RustRelativePath {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- for (i, c) in self.path.iter().enumerate() {
- if i != 0 {
- write!(f, "::")?;
- }
- write!(f, "{}", c)?;
- }
- Ok(())
- }
-}
-
-impl From<&'_ str> for RustRelativePath {
- fn from(s: &str) -> Self {
- RustRelativePath {
- path: s.split("::").map(RustIdent::from).collect(),
- }
- }
-}
-
-#[allow(dead_code)]
-impl RustPath {
- pub fn is_absolute(&self) -> bool {
- self.absolute
- }
-
- pub fn is_empty(&self) -> bool {
- assert!(!self.absolute);
- self.path.is_empty()
- }
-
- pub fn with_ident(self, ident: RustIdent) -> RustIdentWithPath {
- RustIdentWithPath { path: self, ident }
- }
-
- pub fn first(&self) -> Option<RustIdent> {
- assert!(!self.absolute);
- self.path.first()
- }
-
- pub fn remove_first(&mut self) -> Option<RustIdent> {
- assert!(!self.absolute);
- self.path.remove_first()
- }
-
- pub fn to_reverse(&self) -> RustPath {
- assert!(!self.absolute);
- RustPath {
- absolute: false,
- path: self.path.to_reverse(),
- }
- }
-
- pub fn prepend_ident(&mut self, ident: RustIdent) {
- assert!(!self.absolute);
- self.path.prepend_ident(ident);
- }
-
- pub fn append(self, path: RustPath) -> RustPath {
- if path.absolute {
- path
- } else {
- RustPath {
- absolute: self.absolute,
- path: self.path.append(path.path),
- }
- }
- }
-
- pub fn append_ident(mut self, ident: RustIdent) -> RustPath {
- self.path.path.push(ident);
- self
- }
-
- pub fn append_with_ident(self, path: RustIdentWithPath) -> RustIdentWithPath {
- self.append(path.path).with_ident(path.ident)
- }
-}
-
-impl From<&'_ str> for RustPath {
- fn from(s: &str) -> Self {
- let (s, absolute) = if s.starts_with("::") {
- (&s[2..], true)
- } else {
- (s, false)
- };
- RustPath {
- absolute,
- path: RustRelativePath::from(s),
- }
- }
-}
-
-impl From<String> for RustPath {
- fn from(s: String) -> Self {
- RustPath::from(&s[..])
- }
-}
-
-impl fmt::Display for RustPath {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- if self.absolute {
- write!(f, "::")?;
- }
- write!(f, "{}", self.path)
- }
-}
-
-#[derive(Eq, PartialEq, Debug, Clone)]
-pub(crate) struct RustIdentWithPath {
- pub path: RustPath,
- pub ident: RustIdent,
-}
-
-#[allow(dead_code)]
-impl RustIdentWithPath {
- pub fn new(s: String) -> RustIdentWithPath {
- let mut path = RustPath::from(s);
- let ident = path.path.path.pop().unwrap();
- RustIdentWithPath { path, ident }
- }
-
- pub fn prepend_ident(&mut self, ident: RustIdent) {
- self.path.prepend_ident(ident)
- }
-
- pub fn to_path(&self) -> RustPath {
- self.path.clone().append_ident(self.ident.clone())
- }
-}
-
-impl<S: Into<String>> From<S> for RustIdentWithPath {
- fn from(s: S) -> Self {
- RustIdentWithPath::new(s.into())
- }
-}
-
-impl fmt::Display for RustIdentWithPath {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- fmt::Display::fmt(&self.to_path(), f)
- }
-}
diff --git a/src/scope.rs b/src/scope.rs
deleted file mode 100644
index 5f92d08..0000000
--- a/src/scope.rs
+++ /dev/null
@@ -1,555 +0,0 @@
-use protobuf::descriptor::DescriptorProto;
-use protobuf::descriptor::EnumDescriptorProto;
-use protobuf::descriptor::EnumValueDescriptorProto;
-use protobuf::descriptor::FieldDescriptorProto;
-use protobuf::descriptor::FileDescriptorProto;
-use protobuf::descriptor::OneofDescriptorProto;
-
-use crate::field::rust_field_name_for_protobuf_field_name;
-use crate::file::proto_path_to_rust_mod;
-use crate::protobuf_name::ProtobufAbsolutePath;
-use crate::protobuf_name::ProtobufIdent;
-use crate::protobuf_name::ProtobufRelativePath;
-use crate::rust;
-use crate::rust_name::RustIdent;
-use crate::rust_name::RustIdentWithPath;
-use crate::syntax::Syntax;
-
-pub(crate) struct RootScope<'a> {
- pub file_descriptors: &'a [FileDescriptorProto],
-}
-
-impl<'a> RootScope<'a> {
- fn packages(&'a self) -> Vec<FileScope<'a>> {
- self.file_descriptors
- .iter()
- .map(|fd| FileScope {
- file_descriptor: fd,
- })
- .collect()
- }
-
- // find enum by fully qualified name
- pub fn _find_enum(&'a self, fqn: &ProtobufAbsolutePath) -> EnumWithScope<'a> {
- match self.find_message_or_enum(fqn) {
- MessageOrEnumWithScope::Enum(e) => e,
- _ => panic!("not an enum: {}", fqn),
- }
- }
-
- // find message by fully qualified name
- pub fn find_message(&'a self, fqn: &ProtobufAbsolutePath) -> MessageWithScope<'a> {
- match self.find_message_or_enum(fqn) {
- MessageOrEnumWithScope::Message(m) => m,
- _ => panic!("not a message: {}", fqn),
- }
- }
-
- // find message or enum by fully qualified name
- pub fn find_message_or_enum(
- &'a self,
- fqn: &ProtobufAbsolutePath,
- ) -> MessageOrEnumWithScope<'a> {
- assert!(!fqn.is_empty());
- self.packages()
- .into_iter()
- .flat_map(|p| p.find_message_or_enum_abs(fqn))
- .next()
- .expect(&format!("enum not found by name: {}", fqn))
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct FileScope<'a> {
- pub file_descriptor: &'a FileDescriptorProto,
-}
-
-impl<'a> FileScope<'a> {
- fn get_package(&self) -> ProtobufAbsolutePath {
- ProtobufRelativePath::from(self.file_descriptor.get_package()).into_absolute()
- }
-
- pub fn syntax(&self) -> Syntax {
- Syntax::parse(self.file_descriptor.get_syntax())
- }
-
- pub fn to_scope(&self) -> Scope<'a> {
- Scope {
- file_scope: self.clone(),
- path: Vec::new(),
- }
- }
-
- fn find_message_or_enum(
- &self,
- name: &ProtobufRelativePath,
- ) -> Option<MessageOrEnumWithScope<'a>> {
- self.find_messages_and_enums()
- .into_iter()
- .filter(|e| e.protobuf_name_to_package() == *name)
- .next()
- }
-
- fn find_message_or_enum_abs(
- &self,
- name: &ProtobufAbsolutePath,
- ) -> Option<MessageOrEnumWithScope<'a>> {
- match name.remove_prefix(&self.get_package()) {
- Some(ref rem) => self.find_message_or_enum(rem),
- None => None,
- }
- }
-
- // find all enums in given file descriptor
- pub fn _find_enums(&self) -> Vec<EnumWithScope<'a>> {
- let mut r = Vec::new();
-
- self.to_scope().walk_scopes(|scope| {
- r.extend(scope.get_enums());
- });
-
- r
- }
-
- // find all messages in given file descriptor
- pub fn _find_messages(&self) -> Vec<MessageWithScope<'a>> {
- let mut r = Vec::new();
-
- self.to_scope().walk_scopes(|scope| {
- r.extend(scope.get_messages());
- });
-
- r
- }
-
- // find all messages and enums in given file descriptor
- pub fn find_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
- let mut r = Vec::new();
-
- self.to_scope().walk_scopes(|scope| {
- r.extend(scope.get_messages_and_enums());
- });
-
- r
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct Scope<'a> {
- pub file_scope: FileScope<'a>,
- pub path: Vec<&'a DescriptorProto>,
-}
-
-impl<'a> Scope<'a> {
- pub fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
- self.file_scope.file_descriptor
- }
-
- // get message descriptors in this scope
- fn get_message_descriptors(&self) -> &'a [DescriptorProto] {
- if self.path.is_empty() {
- &self.file_scope.file_descriptor.get_message_type()
- } else {
- &self.path.last().unwrap().get_nested_type()
- }
- }
-
- // get enum descriptors in this scope
- fn get_enum_descriptors(&self) -> &'a [EnumDescriptorProto] {
- if self.path.is_empty() {
- &self.file_scope.file_descriptor.get_enum_type()
- } else {
- &self.path.last().unwrap().get_enum_type()
- }
- }
-
- // get messages with attached scopes in this scope
- pub fn get_messages(&self) -> Vec<MessageWithScope<'a>> {
- self.get_message_descriptors()
- .iter()
- .map(|m| MessageWithScope {
- scope: self.clone(),
- message: m,
- })
- .collect()
- }
-
- // get enums with attached scopes in this scope
- pub fn get_enums(&self) -> Vec<EnumWithScope<'a>> {
- self.get_enum_descriptors()
- .iter()
- .map(|e| EnumWithScope {
- scope: self.clone(),
- en: e,
- })
- .collect()
- }
-
- // get messages and enums with attached scopes in this scope
- pub fn get_messages_and_enums(&self) -> Vec<MessageOrEnumWithScope<'a>> {
- self.get_messages()
- .into_iter()
- .map(|m| MessageOrEnumWithScope::Message(m))
- .chain(
- self.get_enums()
- .into_iter()
- .map(|m| MessageOrEnumWithScope::Enum(m)),
- )
- .collect()
- }
-
- // nested scopes, i. e. scopes of nested messages
- fn nested_scopes(&self) -> Vec<Scope<'a>> {
- self.get_message_descriptors()
- .iter()
- .map(|m| {
- let mut nested = self.clone();
- nested.path.push(m);
- nested
- })
- .collect()
- }
-
- fn walk_scopes_impl<F: FnMut(&Scope<'a>)>(&self, callback: &mut F) {
- (*callback)(self);
-
- for nested in self.nested_scopes() {
- nested.walk_scopes_impl(callback);
- }
- }
-
- // apply callback for this scope and all nested scopes
- fn walk_scopes<F>(&self, mut callback: F)
- where
- F: FnMut(&Scope<'a>),
- {
- self.walk_scopes_impl(&mut callback);
- }
-
- pub fn prefix(&self) -> String {
- if self.path.is_empty() {
- "".to_string()
- } else {
- let v: Vec<&'a str> = self.path.iter().map(|m| m.get_name()).collect();
- let mut r = v.join(".");
- r.push_str(".");
- r
- }
- }
-
- pub fn protobuf_path_to_file(&self) -> ProtobufRelativePath {
- ProtobufRelativePath::from_components(
- self.path.iter().map(|m| ProtobufIdent::from(m.get_name())),
- )
- }
-
- pub fn protobuf_absolute_path(&self) -> ProtobufAbsolutePath {
- let mut r = self.file_scope.get_package();
- r.push_relative(&self.protobuf_path_to_file());
- r
- }
-
- // rust type name prefix for this scope
- pub fn rust_prefix(&self) -> String {
- self.prefix().replace(".", "_")
- }
-}
-
-pub(crate) trait WithScope<'a> {
- fn get_scope(&self) -> &Scope<'a>;
-
- fn get_file_descriptor(&self) -> &'a FileDescriptorProto {
- self.get_scope().get_file_descriptor()
- }
-
- // message or enum name
- fn get_name(&self) -> ProtobufIdent;
-
- fn escape_prefix(&self) -> &'static str;
-
- fn name_to_package(&self) -> String {
- let mut r = self.get_scope().prefix();
- r.push_str(self.get_name().get());
- r
- }
-
- fn protobuf_name_to_package(&self) -> ProtobufRelativePath {
- let r = self.get_scope().protobuf_path_to_file();
- r.append_ident(&ProtobufIdent::from(self.get_name()))
- }
-
- /// Return absolute name starting with dot
- fn name_absolute(&self) -> ProtobufAbsolutePath {
- let mut path = self.get_scope().protobuf_absolute_path();
- path.push_simple(self.get_name());
- path
- }
-
- // rust type name of this descriptor
- fn rust_name(&self) -> RustIdent {
- let mut r = self.get_scope().rust_prefix();
- // Only escape if prefix is not empty
- if r.is_empty() && rust::is_rust_keyword(self.get_name().get()) {
- r.push_str(self.escape_prefix());
- }
- r.push_str(self.get_name().get());
- RustIdent::from(r)
- }
-
- // fully-qualified name of this type
- fn rust_fq_name(&self) -> String {
- format!(
- "{}::{}",
- proto_path_to_rust_mod(self.get_scope().get_file_descriptor().get_name()),
- self.rust_name()
- )
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct MessageWithScope<'a> {
- pub scope: Scope<'a>,
- pub message: &'a DescriptorProto,
-}
-
-impl<'a> WithScope<'a> for MessageWithScope<'a> {
- fn get_scope(&self) -> &Scope<'a> {
- &self.scope
- }
-
- fn escape_prefix(&self) -> &'static str {
- "message_"
- }
-
- fn get_name(&self) -> ProtobufIdent {
- ProtobufIdent::from(self.message.get_name())
- }
-}
-
-impl<'a> MessageWithScope<'a> {
- pub fn into_scope(mut self) -> Scope<'a> {
- self.scope.path.push(self.message);
- self.scope
- }
-
- pub fn to_scope(&self) -> Scope<'a> {
- self.clone().into_scope()
- }
-
- pub fn fields(&self) -> Vec<FieldWithContext<'a>> {
- self.message
- .get_field()
- .iter()
- .map(|f| FieldWithContext {
- field: f,
- message: self.clone(),
- })
- .collect()
- }
-
- pub fn oneofs(&self) -> Vec<OneofWithContext<'a>> {
- self.message
- .get_oneof_decl()
- .iter()
- .enumerate()
- .map(|(index, oneof)| OneofWithContext {
- message: self.clone(),
- oneof: oneof,
- index: index as u32,
- })
- .collect()
- }
-
- pub fn oneof_by_index(&self, index: u32) -> OneofWithContext<'a> {
- self.oneofs().swap_remove(index as usize)
- }
-
- /// Pair of (key, value) if this message is map entry
- pub fn map_entry(&'a self) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> {
- if self.message.get_options().get_map_entry() {
- let key = self
- .fields()
- .into_iter()
- .find(|f| f.field.get_number() == 1)
- .unwrap();
- let value = self
- .fields()
- .into_iter()
- .find(|f| f.field.get_number() == 2)
- .unwrap();
- Some((key, value))
- } else {
- None
- }
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct EnumWithScope<'a> {
- pub scope: Scope<'a>,
- pub en: &'a EnumDescriptorProto,
-}
-
-impl<'a> EnumWithScope<'a> {
- pub fn values(&self) -> Vec<EnumValueWithContext<'a>> {
- self.en
- .get_value()
- .iter()
- .map(|v| EnumValueWithContext {
- _en: self.clone(),
- proto: v,
- })
- .collect()
- }
-
- // find enum value by protobuf name
- pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> {
- self.values()
- .into_iter()
- .find(|v| v.proto.get_name() == name)
- .unwrap()
- }
-}
-
-#[derive(Clone, Debug)]
-pub(crate) struct EnumValueWithContext<'a> {
- _en: EnumWithScope<'a>,
- pub proto: &'a EnumValueDescriptorProto,
-}
-
-impl<'a> EnumValueWithContext<'a> {
- pub fn rust_name(&self) -> RustIdent {
- let mut r = String::new();
- if rust::is_rust_keyword(self.proto.get_name()) {
- r.push_str("value_");
- }
- r.push_str(self.proto.get_name());
- RustIdent::new(&r)
- }
-}
-
-impl<'a> WithScope<'a> for EnumWithScope<'a> {
- fn get_scope(&self) -> &Scope<'a> {
- &self.scope
- }
-
- fn escape_prefix(&self) -> &'static str {
- "enum_"
- }
-
- fn get_name(&self) -> ProtobufIdent {
- ProtobufIdent::from(self.en.get_name())
- }
-}
-
-pub(crate) enum MessageOrEnumWithScope<'a> {
- Message(MessageWithScope<'a>),
- Enum(EnumWithScope<'a>),
-}
-
-impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> {
- fn get_scope(&self) -> &Scope<'a> {
- match self {
- &MessageOrEnumWithScope::Message(ref m) => m.get_scope(),
- &MessageOrEnumWithScope::Enum(ref e) => e.get_scope(),
- }
- }
-
- fn escape_prefix(&self) -> &'static str {
- match self {
- &MessageOrEnumWithScope::Message(ref m) => m.escape_prefix(),
- &MessageOrEnumWithScope::Enum(ref e) => e.escape_prefix(),
- }
- }
-
- fn get_name(&self) -> ProtobufIdent {
- match self {
- &MessageOrEnumWithScope::Message(ref m) => m.get_name(),
- &MessageOrEnumWithScope::Enum(ref e) => e.get_name(),
- }
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct FieldWithContext<'a> {
- pub field: &'a FieldDescriptorProto,
- pub message: MessageWithScope<'a>,
-}
-
-impl<'a> FieldWithContext<'a> {
- pub fn is_oneof(&self) -> bool {
- self.field.has_oneof_index()
- }
-
- pub fn oneof(&self) -> Option<OneofWithContext<'a>> {
- if self.is_oneof() {
- Some(
- self.message
- .oneof_by_index(self.field.get_oneof_index() as u32),
- )
- } else {
- None
- }
- }
-
- pub fn number(&self) -> u32 {
- self.field.get_number() as u32
- }
-
- /// Shortcut
- pub fn name(&self) -> &str {
- self.field.get_name()
- }
-
- pub fn rust_name(&self) -> RustIdent {
- rust_field_name_for_protobuf_field_name(self.name())
- }
-
- // From field to file root
- pub fn _containing_messages(&self) -> Vec<&'a DescriptorProto> {
- let mut r = Vec::new();
- r.push(self.message.message);
- r.extend(self.message.scope.path.iter().rev());
- r
- }
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofVariantWithContext<'a> {
- pub oneof: &'a OneofWithContext<'a>,
- pub field: &'a FieldDescriptorProto,
-}
-
-#[derive(Clone)]
-pub(crate) struct OneofWithContext<'a> {
- pub oneof: &'a OneofDescriptorProto,
- pub index: u32,
- pub message: MessageWithScope<'a>,
-}
-
-impl<'a> OneofWithContext<'a> {
- pub fn field_name(&'a self) -> RustIdent {
- return rust_field_name_for_protobuf_field_name(self.oneof.get_name());
- }
-
- // rust type name of enum
- pub fn rust_name(&self) -> RustIdentWithPath {
- RustIdentWithPath::from(format!(
- "{}_oneof_{}",
- self.message.rust_name(),
- self.oneof.get_name()
- ))
- }
-
- pub fn variants(&'a self) -> Vec<OneofVariantWithContext<'a>> {
- self.message
- .fields()
- .iter()
- .filter(|f| f.field.has_oneof_index() && f.field.get_oneof_index() == self.index as i32)
- .map(|f| OneofVariantWithContext {
- oneof: self,
- field: &f.field,
- })
- .collect()
- }
-}
diff --git a/src/serde.rs b/src/serde.rs
deleted file mode 100644
index f799611..0000000
--- a/src/serde.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-use code_writer::CodeWriter;
-use Customize;
-
-/// Write serde attr according to specified codegen option.
-pub fn write_serde_attr(w: &mut CodeWriter, customize: &Customize, attr: &str) {
- if customize.serde_derive.unwrap_or(false) {
- w.write_line(&format!("#[cfg_attr(feature = \"with-serde\", {})]", attr));
- }
-}
diff --git a/src/syntax.rs b/src/syntax.rs
deleted file mode 100644
index d075063..0000000
--- a/src/syntax.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum Syntax {
- PROTO2,
- PROTO3,
-}
-
-impl Syntax {
- pub fn parse(s: &str) -> Self {
- match s {
- "" | "proto2" => Syntax::PROTO2,
- "proto3" => Syntax::PROTO3,
- _ => panic!("unsupported syntax value: {:?}", s),
- }
- }
-}
diff --git a/src/well_known_types.rs b/src/well_known_types.rs
deleted file mode 100644
index 6264947..0000000
--- a/src/well_known_types.rs
+++ /dev/null
@@ -1,63 +0,0 @@
-static NAMES: &'static [&'static str] = &[
- "Any",
- "Api",
- "BoolValue",
- "BytesValue",
- "DoubleValue",
- "Duration",
- "Empty",
- "Enum",
- "EnumValue",
- "Field",
- // TODO: dotted names
- "Field.Cardinality",
- "Field.Kind",
- "FieldMask",
- "FloatValue",
- "Int32Value",
- "Int64Value",
- "ListValue",
- "Method",
- "Mixin",
- "NullValue",
- "Option",
- "SourceContext",
- "StringValue",
- "Struct",
- "Syntax",
- "Timestamp",
- "Type",
- "UInt32Value",
- "UInt64Value",
- "Value",
-];
-
-fn is_well_known_type(name: &str) -> bool {
- NAMES.iter().any(|&n| n == name)
-}
-
-pub fn is_well_known_type_full(name: &str) -> Option<&str> {
- if let Some(dot) = name.rfind('.') {
- if &name[..dot] == ".google.protobuf" && is_well_known_type(&name[dot + 1..]) {
- Some(&name[dot + 1..])
- } else {
- None
- }
- } else {
- None
- }
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- #[test]
- fn test_is_well_known_type_full() {
- assert_eq!(
- Some("BoolValue"),
- is_well_known_type_full(".google.protobuf.BoolValue")
- );
- assert_eq!(None, is_well_known_type_full(".google.protobuf.Fgfg"));
- }
-}