diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-14 01:23:14 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-04-14 01:23:14 +0000 |
commit | 7992621ee9ef30ef175d1b24fade174e32120bb5 (patch) | |
tree | 85c8d7e85cf832024820c11fe6f7b0f6cbeb3fb2 | |
parent | 723f91d21517dcbf64537d4adf2a962d93ac09d2 (diff) | |
parent | a9e33385c6912739f5694e6a5635cf8c1bb2b3c5 (diff) | |
download | protobuf-codegen-android-vts-14.0_r1.tar.gz |
Snap for 9939584 from a9e33385c6912739f5694e6a5635cf8c1bb2b3c5 to udc-releaseandroid-vts-14.0_r4android-vts-14.0_r3android-vts-14.0_r2android-vts-14.0_r1android-security-14.0.0_r9android-security-14.0.0_r8android-security-14.0.0_r7android-security-14.0.0_r6android-security-14.0.0_r5android-security-14.0.0_r4android-security-14.0.0_r3android-security-14.0.0_r2android-security-14.0.0_r1android-platform-14.0.0_r8android-platform-14.0.0_r7android-platform-14.0.0_r6android-platform-14.0.0_r5android-platform-14.0.0_r4android-platform-14.0.0_r3android-platform-14.0.0_r2android-platform-14.0.0_r1android-cts-14.0_r4android-cts-14.0_r3android-cts-14.0_r2android-cts-14.0_r1android-14.0.0_r28android-14.0.0_r2android-14.0.0_r15android-14.0.0_r14android-14.0.0_r13android-14.0.0_r1android14-tests-releaseandroid14-security-releaseandroid14-s2-releaseandroid14-s1-releaseandroid14-releaseandroid14-platform-release
Change-Id: If12107c6a279200f8b95ff5b38a540b1c54bc89e
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 @@ -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, @@ -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" @@ -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 @@ -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 } } @@ -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(¶m_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(¶m_type, var, &self.customize) - ) - } else { - format!( - "{}::rt::value_size({}, {}, {}::wire_format::{:?})", - protobuf_crate_path(&self.customize), - self.proto_field.number(), - var_type.into_target(¶m_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(¶m_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(¶m_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(¤t.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(¤t.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(¤t.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", - } -} @@ -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")); - } -} |