diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2023-03-28 08:18:22 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2023-03-28 08:18:22 +0000 |
commit | 930dfa49953345e7e74a11c21f7c62fd86a03044 (patch) | |
tree | 52ddb6e645db2fa36d5772c15b5d4cc47a662d62 | |
parent | 94de443db7c9dd53a8358bec9612466989799791 (diff) | |
parent | 9a9d43c8c3946bbcda3d4bb1e249989f641dbe61 (diff) | |
download | protobuf-codegen-930dfa49953345e7e74a11c21f7c62fd86a03044.tar.gz |
Introduce libprotobuf_codegen_deprecated am: 9a9d43c8c3
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/protobuf-codegen/+/2509876
Change-Id: I1c22f3dcf344b31ceefe5d1ee4e3f9eda0c99057
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
37 files changed, 6854 insertions, 0 deletions
diff --git a/2.27.1/.cargo_vcs_info.json b/2.27.1/.cargo_vcs_info.json new file mode 100644 index 0000000..8ec5394 --- /dev/null +++ b/2.27.1/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "ec31ce829473039ac598ca6fdcb245cbd6fa82ba" + }, + "path_in_vcs": "protobuf-codegen" +}
\ No newline at end of file diff --git a/2.27.1/Android.bp b/2.27.1/Android.bp new file mode 100644 index 0000000..2a96b07 --- /dev/null +++ b/2.27.1/Android.bp @@ -0,0 +1,48 @@ +// This file is generated by cargo2android.py --config cargo2android.json. +// Do not modify this file as changes will be overridden on upgrade. + +rust_library_host { + name: "libprotobuf_codegen_deprecated", + crate_name: "protobuf_codegen", + cargo_env_compat: true, + cargo_pkg_version: "2.27.1", + srcs: ["src/lib.rs"], + edition: "2015", + rustlibs: [ + "libprotobuf_deprecated", + ], + product_available: true, + vendor_available: true, +} + +rust_test_host { + name: "protobuf-codegen_deprecated_test_src_lib", + crate_name: "protobuf_codegen", + cargo_env_compat: true, + cargo_pkg_version: "2.27.1", + srcs: ["src/lib.rs"], + test_suites: ["general-tests"], + auto_gen_config: true, + test_options: { + unit_test: true, + }, + edition: "2015", + rustlibs: [ + "libprotobuf_deprecated", + ], +} + +rust_binary_host { + name: "protoc-gen-rust-deprecated", + crate_name: "protoc_gen_rust", + cargo_env_compat: true, + cargo_pkg_version: "2.27.1", + srcs: ["src/bin/protoc-gen-rust.rs"], + edition: "2015", + rustlibs: [ + "libprotobuf_deprecated", + "libprotobuf_codegen_deprecated", + ], + product_available: true, + vendor_available: true, +} diff --git a/2.27.1/Cargo.lock b/2.27.1/Cargo.lock new file mode 100644 index 0000000..e9b0703 --- /dev/null +++ b/2.27.1/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "protobuf" +version = "2.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf7e6d18738ecd0902d30d1ad232c9125985a3422929b16c65517b38adc14f96" + +[[package]] +name = "protobuf-codegen" +version = "2.27.1" +dependencies = [ + "protobuf", +] diff --git a/2.27.1/Cargo.toml b/2.27.1/Cargo.toml new file mode 100644 index 0000000..ca327e2 --- /dev/null +++ b/2.27.1/Cargo.toml @@ -0,0 +1,44 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +name = "protobuf-codegen" +version = "2.27.1" +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. +""" +homepage = "https://github.com/stepancheg/rust-protobuf/" +license = "MIT" +repository = "https://github.com/stepancheg/rust-protobuf/" + +[package.metadata.docs.rs] +all-features = true + +[lib] +bench = false + +[[bin]] +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.protobuf] +version = "=2.27.1" diff --git a/2.27.1/Cargo.toml.orig b/2.27.1/Cargo.toml.orig new file mode 100644 index 0000000..3400e66 --- /dev/null +++ b/2.27.1/Cargo.toml.orig @@ -0,0 +1,35 @@ +[package] +name = "protobuf-codegen" +version = "2.27.1" +authors = ["Stepan Koltsov <stepan.koltsov@gmail.com>"] +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. +""" + +[lib] +bench = false + +[dependencies] +protobuf = { path = "../protobuf", version = "=2.27.1" } + +[[bin]] + +name = "protoc-gen-rust" +path = "src/bin/protoc-gen-rust.rs" +test = false + +[[bin]] + +name = "protobuf-bin-gen-rust-do-not-use" +path = "src/bin/protobuf-bin-gen-rust-do-not-use.rs" +test = false + +[package.metadata.docs.rs] +all-features = true diff --git a/2.27.1/LICENSE b/2.27.1/LICENSE new file mode 120000 index 0000000..85de3d4 --- /dev/null +++ b/2.27.1/LICENSE @@ -0,0 +1 @@ +LICENSE.txt
\ No newline at end of file diff --git a/2.27.1/LICENSE.txt b/2.27.1/LICENSE.txt new file mode 100644 index 0000000..acce639 --- /dev/null +++ b/2.27.1/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2019 Stepan Koltsov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file diff --git a/2.27.1/METADATA b/2.27.1/METADATA new file mode 100644 index 0000000..4593845 --- /dev/null +++ b/2.27.1/METADATA @@ -0,0 +1,19 @@ +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 { + url { + type: HOMEPAGE + value: "https://crates.io/crates/protobuf-codegen" + } + url { + type: ARCHIVE + value: "https://static.crates.io/crates/protobuf-codegen/protobuf-codegen-2.27.1.crate" + } + version: "2.27.1" + license_type: NOTICE + last_upgrade_date { + year: 2022 + month: 3 + day: 1 + } +} diff --git a/2.27.1/MODULE_LICENSE_MIT b/2.27.1/MODULE_LICENSE_MIT new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/2.27.1/MODULE_LICENSE_MIT diff --git a/2.27.1/NOTICE b/2.27.1/NOTICE new file mode 120000 index 0000000..7a694c9 --- /dev/null +++ b/2.27.1/NOTICE @@ -0,0 +1 @@ +LICENSE
\ No newline at end of file diff --git a/2.27.1/OWNERS b/2.27.1/OWNERS new file mode 100644 index 0000000..46fc303 --- /dev/null +++ b/2.27.1/OWNERS @@ -0,0 +1 @@ +include platform/prebuilts/rust:/OWNERS diff --git a/2.27.1/README.md b/2.27.1/README.md new file mode 100644 index 0000000..2ab7e05 --- /dev/null +++ b/2.27.1/README.md @@ -0,0 +1,80 @@ +<!-- cargo-sync-readme start --> + +# 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. + +When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates. + +## How to use `protoc-gen-rust` if you have to + +(Note `protoc` can be invoked programmatically with +[protoc crate](https://docs.rs/protoc)) + +0) Install protobuf for `protoc` binary. + +On OS X [Homebrew](https://github.com/Homebrew/brew) can be used: + +```sh +brew install protobuf +``` + +On Ubuntu, `protobuf-compiler` package can be installed: + +```sh +apt-get install protobuf-compiler +``` + +Protobuf is needed only for code generation, `rust-protobuf` runtime +does not use `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. + +2) Add `protoc-gen-rust` to $PATH + +If you installed it with cargo, it should be + +```sh +PATH="$HOME/.cargo/bin:$PATH" +``` + +3) Generate .rs files: + +```sh +protoc --rust_out . foo.proto +``` + +This will generate .rs files in current directory. + +# Version 2 + +This is documentation for version 2 of the crate. + +[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. + +<!-- cargo-sync-readme end --> diff --git a/2.27.1/cargo2android.json b/2.27.1/cargo2android.json new file mode 100644 index 0000000..341300b --- /dev/null +++ b/2.27.1/cargo2android.json @@ -0,0 +1,4 @@ +{ + "run": true, + "tests": true +}
\ No newline at end of file diff --git a/2.27.1/src/bin/protobuf-bin-gen-rust-do-not-use.rs b/2.27.1/src/bin/protobuf-bin-gen-rust-do-not-use.rs new file mode 100644 index 0000000..a6f96ca --- /dev/null +++ b/2.27.1/src/bin/protobuf-bin-gen-rust-do-not-use.rs @@ -0,0 +1,37 @@ +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/2.27.1/src/bin/protoc-gen-rust.rs b/2.27.1/src/bin/protoc-gen-rust.rs new file mode 100644 index 0000000..97ac7d2 --- /dev/null +++ b/2.27.1/src/bin/protoc-gen-rust.rs @@ -0,0 +1,5 @@ +extern crate protobuf_codegen; + +fn main() { + protobuf_codegen::protoc_gen_rust_main(); +} diff --git a/2.27.1/src/code_writer.rs b/2.27.1/src/code_writer.rs new file mode 100644 index 0000000..50eaeb8 --- /dev/null +++ b/2.27.1/src/code_writer.rs @@ -0,0 +1,387 @@ +// 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/2.27.1/src/customize.rs b/2.27.1/src/customize.rs new file mode 100644 index 0000000..b3415ab --- /dev/null +++ b/2.27.1/src/customize.rs @@ -0,0 +1,237 @@ +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/2.27.1/src/enums.rs b/2.27.1/src/enums.rs new file mode 100644 index 0000000..ec3444e --- /dev/null +++ b/2.27.1/src/enums.rs @@ -0,0 +1,377 @@ +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/2.27.1/src/extensions.rs b/2.27.1/src/extensions.rs new file mode 100644 index 0000000..def5948 --- /dev/null +++ b/2.27.1/src/extensions.rs @@ -0,0 +1,116 @@ +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/2.27.1/src/field/mod.rs b/2.27.1/src/field/mod.rs new file mode 100644 index 0000000..e4be944 --- /dev/null +++ b/2.27.1/src/field/mod.rs @@ -0,0 +1,2031 @@ +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/2.27.1/src/file.rs b/2.27.1/src/file.rs new file mode 100644 index 0000000..413e1bc --- /dev/null +++ b/2.27.1/src/file.rs @@ -0,0 +1,87 @@ +use crate::rust; +use crate::rust_name::RustIdent; +use crate::strx; + +// Copy-pasted from libsyntax. +fn ident_start(c: char) -> bool { + (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' +} + +// Copy-pasted from libsyntax. +fn ident_continue(c: char) -> bool { + (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_' +} + +pub(crate) fn proto_path_to_rust_mod(path: &str) -> RustIdent { + let without_dir = strx::remove_to(path, std::path::is_separator); + let without_suffix = strx::remove_suffix(without_dir, ".proto"); + + let name = without_suffix + .chars() + .enumerate() + .map(|(i, c)| { + let valid = if i == 0 { + ident_start(c) + } else { + ident_continue(c) + }; + if valid { + c + } else { + '_' + } + }) + .collect::<String>(); + + let name = if rust::is_rust_keyword(&name) { + format!("{}_pb", name) + } else { + name + }; + RustIdent::from(name) +} + +#[cfg(test)] +mod test { + + use super::proto_path_to_rust_mod; + use crate::rust_name::RustIdent; + + #[test] + fn test_mod_path_proto_ext() { + assert_eq!( + RustIdent::from("proto"), + proto_path_to_rust_mod("proto.proto") + ); + } + + #[test] + fn test_mod_path_unknown_ext() { + assert_eq!( + RustIdent::from("proto_proto3"), + proto_path_to_rust_mod("proto.proto3") + ); + } + + #[test] + fn test_mod_path_empty_ext() { + assert_eq!(RustIdent::from("proto"), proto_path_to_rust_mod("proto")); + } + + #[test] + fn test_mod_path_dir() { + assert_eq!( + RustIdent::from("baz"), + proto_path_to_rust_mod("foo/bar/baz.proto"), + ) + } + + #[cfg(target_os = "windows")] + #[test] + fn test_mod_path_dir_backslashes() { + assert_eq!( + RustIdent::from("baz"), + proto_path_to_rust_mod("foo\\bar\\baz.proto"), + ) + } +} diff --git a/2.27.1/src/file_and_mod.rs b/2.27.1/src/file_and_mod.rs new file mode 100644 index 0000000..4f4e8c6 --- /dev/null +++ b/2.27.1/src/file_and_mod.rs @@ -0,0 +1,9 @@ +use rust_name::RustRelativePath; +use Customize; + +#[allow(dead_code)] +pub(crate) struct FileAndMod { + pub file: String, + pub relative_mod: RustRelativePath, + pub customize: Customize, +} diff --git a/2.27.1/src/file_descriptor.rs b/2.27.1/src/file_descriptor.rs new file mode 100644 index 0000000..dde8603 --- /dev/null +++ b/2.27.1/src/file_descriptor.rs @@ -0,0 +1,5 @@ +use crate::scope::Scope; + +pub(crate) fn file_descriptor_proto_expr(_scope: &Scope) -> String { + format!("file_descriptor_proto()") +} diff --git a/2.27.1/src/float.rs b/2.27.1/src/float.rs new file mode 100644 index 0000000..78ca622 --- /dev/null +++ b/2.27.1/src/float.rs @@ -0,0 +1,67 @@ +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/2.27.1/src/inside.rs b/2.27.1/src/inside.rs new file mode 100644 index 0000000..bbdcfdb --- /dev/null +++ b/2.27.1/src/inside.rs @@ -0,0 +1,10 @@ +use Customize; + +/// Path to `protobuf` crate, different when `.proto` file is +/// used inside or outside of protobuf crate. +pub(crate) fn protobuf_crate_path(customize: &Customize) -> &str { + match customize.inside_protobuf { + Some(true) => "crate", + _ => "::protobuf", + } +} diff --git a/2.27.1/src/lib.rs b/2.27.1/src/lib.rs new file mode 100644 index 0000000..826e18b --- /dev/null +++ b/2.27.1/src/lib.rs @@ -0,0 +1,387 @@ +//! # 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. +//! +//! When building with cargo, consider using `protoc-rust` or `protobuf-codegen-pure` crates. +//! +//! ## How to use `protoc-gen-rust` if you have to +//! +//! (Note `protoc` can be invoked programmatically with +//! [protoc crate](https://docs.rs/protoc)) +//! +//! 0) Install protobuf for `protoc` binary. +//! +//! On OS X [Homebrew](https://github.com/Homebrew/brew) can be used: +//! +//! ```sh +//! brew install protobuf +//! ``` +//! +//! On Ubuntu, `protobuf-compiler` package can be installed: +//! +//! ```sh +//! apt-get install protobuf-compiler +//! ``` +//! +//! Protobuf is needed only for code generation, `rust-protobuf` runtime +//! does not use `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. +//! +//! 2) Add `protoc-gen-rust` to $PATH +//! +//! If you installed it with cargo, it should be +//! +//! ```sh +//! PATH="$HOME/.cargo/bin:$PATH" +//! ``` +//! +//! 3) Generate .rs files: +//! +//! ```sh +//! protoc --rust_out . foo.proto +//! ``` +//! +//! This will generate .rs files in current directory. +//! +//! # Version 2 +//! +//! This is documentation for version 2 of the crate. +//! +//! [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. + +#![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 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; + +use customize::customize_from_rustproto_for_file; +#[doc(hidden)] +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 +#[doc(hidden)] +pub fn proto_name_to_rs(name: &str) -> String { + format!("{}.rs", proto_path_to_rust_mod(name)) +} diff --git a/2.27.1/src/message.rs b/2.27.1/src/message.rs new file mode 100644 index 0000000..fbe9ed4 --- /dev/null +++ b/2.27.1/src/message.rs @@ -0,0 +1,626 @@ +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/2.27.1/src/oneof.rs b/2.27.1/src/oneof.rs new file mode 100644 index 0000000..44201ea --- /dev/null +++ b/2.27.1/src/oneof.rs @@ -0,0 +1,198 @@ +//! 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/2.27.1/src/protobuf_name.rs b/2.27.1/src/protobuf_name.rs new file mode 100644 index 0000000..c40a69c --- /dev/null +++ b/2.27.1/src/protobuf_name.rs @@ -0,0 +1,374 @@ +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/2.27.1/src/rust.rs b/2.27.1/src/rust.rs new file mode 100644 index 0000000..263cba0 --- /dev/null +++ b/2.27.1/src/rust.rs @@ -0,0 +1,62 @@ +#[cfg_attr(rustfmt, rustfmt_skip)] +static RUST_KEYWORDS: &'static [&'static str] = &[ + "as", + "async", + "await", + "break", + "crate", + "dyn", + "else", + "enum", + "extern", + "false", + "fn", + "for", + "if", + "impl", + "in", + "let", + "loop", + "match", + "mod", + "move", + "mut", + "pub", + "ref", + "return", + "static", + "self", + "Self", + "struct", + "super", + "true", + "trait", + "type", + "unsafe", + "use", + "while", + "continue", + "box", + "const", + "where", + "virtual", + "proc", + "alignof", + "become", + "offsetof", + "priv", + "pure", + "sizeof", + "typeof", + "unsized", + "yield", + "do", + "abstract", + "final", + "override", + "macro", +]; + +pub fn is_rust_keyword(ident: &str) -> bool { + RUST_KEYWORDS.contains(&ident) +} diff --git a/2.27.1/src/rust_name.rs b/2.27.1/src/rust_name.rs new file mode 100644 index 0000000..234925b --- /dev/null +++ b/2.27.1/src/rust_name.rs @@ -0,0 +1,273 @@ +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/2.27.1/src/rust_types_values.rs b/2.27.1/src/rust_types_values.rs new file mode 100644 index 0000000..e9b017d --- /dev/null +++ b/2.27.1/src/rust_types_values.rs @@ -0,0 +1,593 @@ +use std::cmp; + +use inside::protobuf_crate_path; +use message::RustTypeMessage; +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; + +// Represent subset of rust types used in generated code +#[derive(Debug, Clone, PartialEq, Eq)] +pub(crate) enum RustType { + // integer: signed?, size in bits + Int(bool, u32), + // param is size in bits + Float(u32), + Bool, + Vec(Box<RustType>), + HashMap(Box<RustType>, Box<RustType>), + String, + // [T], not &[T] + Slice(Box<RustType>), + // str, not &str + Str, + Option(Box<RustType>), + SingularField(Box<RustType>), + SingularPtrField(Box<RustType>), + RepeatedField(Box<RustType>), + // Box<T> + Uniq(Box<RustType>), + // &T + Ref(Box<RustType>), + // protobuf message + Message(RustTypeMessage), + // protobuf enum, not any enum + Enum(String, RustIdent), + // oneof enum + Oneof(String), + // bytes::Bytes + Bytes, + // chars::Chars + Chars, + // group + Group, +} + +impl RustType { + #[inline] + pub(crate) fn to_code(&self, customize: &Customize) -> String { + match *self { + RustType::Int(true, bits) => format!("i{}", bits), + RustType::Int(false, bits) => format!("u{}", bits), + RustType::Float(bits) => format!("f{}", bits), + RustType::Bool => format!("bool"), + RustType::Vec(ref param) => format!("::std::vec::Vec<{}>", param.to_code(customize)), + RustType::HashMap(ref key, ref value) => format!( + "::std::collections::HashMap<{}, {}>", + key.to_code(customize), + value.to_code(customize) + ), + RustType::String => format!("::std::string::String"), + RustType::Slice(ref param) => format!("[{}]", param.to_code(customize)), + RustType::Str => format!("str"), + 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<{}>", + 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::Group => format!("<group>"), + RustType::Bytes => format!("::bytes::Bytes"), + RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)), + } + } +} + +impl RustType { + pub fn u8() -> RustType { + RustType::Int(false, 8) + } + + /// Type is rust primitive? + pub fn is_primitive(&self) -> bool { + match *self { + RustType::Int(..) | RustType::Float(..) | RustType::Bool => true, + _ => false, + } + } + + pub fn is_u8(&self) -> bool { + match *self { + RustType::Int(false, 8) => true, + _ => false, + } + } + + pub fn is_copy(&self) -> bool { + if self.is_primitive() { + true + } else if let RustType::Enum(..) = *self { + true + } else { + false + } + } + + fn is_str(&self) -> bool { + match *self { + RustType::Str => true, + _ => false, + } + } + + fn is_string(&self) -> bool { + match *self { + RustType::String => true, + _ => false, + } + } + + fn is_slice(&self) -> Option<&RustType> { + match *self { + RustType::Slice(ref v) => Some(&**v), + _ => None, + } + } + + fn is_slice_u8(&self) -> bool { + match self.is_slice() { + Some(t) => t.is_u8(), + None => false, + } + } + + fn is_message(&self) -> bool { + match *self { + RustType::Message(..) => true, + _ => false, + } + } + + fn is_enum(&self) -> bool { + match *self { + RustType::Enum(..) => true, + _ => false, + } + } + + pub fn is_ref(&self) -> bool { + match *self { + RustType::Ref(..) => true, + _ => false, + } + } + + // default value for type + pub fn default_value(&self, customize: &Customize) -> 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::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::Message(ref name) => format!("{}::new()", name), + RustType::Ref(ref m) if m.is_message() => match **m { + RustType::Message(ref name) => name.default_instance(customize), + _ => 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), + } + } + + pub fn default_value_typed(self, customize: &Customize) -> RustValueTyped { + RustValueTyped { + value: self.default_value(customize), + rust_type: self, + } + } + + /// 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::Vec(..) + | RustType::Bytes + | RustType::String + | RustType::RepeatedField(..) + | RustType::SingularField(..) + | RustType::SingularPtrField(..) + | 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)) + } + 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, ()> { + match (self, target) { + (x, y) if x == y => return Ok(format!("{}", v)), + (&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)), + (x, &RustType::Uniq(ref y)) if *x == **y => { + return Ok(format!("::std::boxed::Box::new({})", v)) + } + (&RustType::Uniq(ref x), y) if **x == *y => return Ok(format!("*{}", v)), + (&RustType::String, &RustType::Ref(ref t)) if **t == RustType::Str => { + return Ok(format!("&{}", v)) + } + (&RustType::Chars, &RustType::Ref(ref t)) if **t == RustType::Str => { + return Ok(format!("&{}", v)) + } + (&RustType::Ref(ref t1), &RustType::Ref(ref t2)) if t1.is_string() && t2.is_str() => { + return Ok(format!("&{}", v)) + } + (&RustType::Ref(ref t1), &RustType::String) + if match **t1 { + 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::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::Vec(ref x), &RustType::Ref(ref t)) + if match **t { + 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::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::Enum(..), &RustType::Int(true, 32)) => { + return Ok(format!( + "{}::ProtobufEnum::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 + )) + } + _ => (), + }; + + if let &RustType::Ref(ref s) = self { + if let Ok(conv) = s.try_into_target(target, v, customize) { + return Ok(conv); + } + } + + Err(()) + } + + /// Type to view data of this type + 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::Bytes => RustType::Slice(Box::new(RustType::u8())), + &RustType::Message(ref p) => RustType::Message(p.clone()), + x => panic!("no ref type for {:?}", x), + })) + } + + pub fn elem_type(&self) -> RustType { + match self { + &RustType::Option(ref ty) => (**ty).clone(), + x => panic!("cannot get elem type of {:?}", x), + } + } + + // type of `v` in `for v in xxx` + pub fn iter_elem_type(&self) -> 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()), + x => panic!("cannot iterate {:?}", x), + } + } + + pub fn value(self, value: String) -> RustValueTyped { + RustValueTyped { + value: value, + rust_type: self, + } + } +} + +/// Representation of an expression in code generator: text and type +pub(crate) struct RustValueTyped { + pub value: String, + pub rust_type: RustType, +} + +impl RustValueTyped { + pub fn into_type(&self, target: RustType, customize: &Customize) -> RustValueTyped { + let target_value = self.rust_type.into_target(&target, &self.value, customize); + RustValueTyped { + value: target_value, + rust_type: target, + } + } + + pub fn boxed(self, customize: &Customize) -> RustValueTyped { + self.into_type(RustType::Uniq(Box::new(self.rust_type.clone())), customize) + } +} + +// 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); + &file[cmp::max(fs, bs)..] +} + +#[cfg(test)] +#[test] +fn test_file_last_component() { + assert_eq!("ab.proto", file_last_component("ab.proto")); + assert_eq!("ab.proto", file_last_component("xx/ab.proto")); + assert_eq!("ab.proto", file_last_component("xx\\ab.proto")); + 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" +} + +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() { + // 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) { + // 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()) { + // Messages defined in descriptor.proto + format!( + "{}::descriptor::{}", + protobuf_crate_path(customize), + message_or_enum.name_to_package() + ) + } else { + if subm { + format!("super::super::{}", message_or_enum.rust_fq_name()) + } else { + format!("super::{}", message_or_enum.rust_fq_name()) + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum PrimitiveTypeVariant { + Default, + Carllerche, +} + +pub enum _CarllercheBytesType { + Bytes, + Chars, +} + +// ProtobufType trait name +pub enum ProtobufTypeGen { + Primitive(FieldDescriptorProto_Type, PrimitiveTypeVariant), + Message(String), + Enum(String), +} + +impl ProtobufTypeGen { + pub fn rust_type(&self, customize: &Customize) -> String { + match self { + &ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!( + "{}::types::ProtobufType{}", + protobuf_crate_path(customize), + capitalize(protobuf_name(t)) + ), + &ProtobufTypeGen::Primitive( + FieldDescriptorProto_Type::TYPE_BYTES, + PrimitiveTypeVariant::Carllerche, + ) => format!( + "{}::types::ProtobufTypeCarllercheBytes", + protobuf_crate_path(customize) + ), + &ProtobufTypeGen::Primitive( + FieldDescriptorProto_Type::TYPE_STRING, + PrimitiveTypeVariant::Carllerche, + ) => format!( + "{}::types::ProtobufTypeCarllercheChars", + protobuf_crate_path(customize) + ), + &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::Carllerche) => unreachable!(), + &ProtobufTypeGen::Message(ref name) => format!( + "{}::types::ProtobufTypeMessage<{}>", + protobuf_crate_path(customize), + name + ), + &ProtobufTypeGen::Enum(ref name) => format!( + "{}::types::ProtobufTypeEnum<{}>", + protobuf_crate_path(customize), + name + ), + } + } +} diff --git a/2.27.1/src/scope.rs b/2.27.1/src/scope.rs new file mode 100644 index 0000000..5f92d08 --- /dev/null +++ b/2.27.1/src/scope.rs @@ -0,0 +1,555 @@ +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/2.27.1/src/serde.rs b/2.27.1/src/serde.rs new file mode 100644 index 0000000..f799611 --- /dev/null +++ b/2.27.1/src/serde.rs @@ -0,0 +1,9 @@ +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/2.27.1/src/strx.rs b/2.27.1/src/strx.rs new file mode 100644 index 0000000..d1b26fa --- /dev/null +++ b/2.27.1/src/strx.rs @@ -0,0 +1,57 @@ +pub fn remove_to<'s, P>(s: &'s str, pattern: P) -> &'s str +where + P: Fn(char) -> bool, +{ + match s.rfind(pattern) { + Some(pos) => &s[(pos + 1)..], + None => s, + } +} + +pub fn remove_suffix<'s>(s: &'s str, suffix: &str) -> &'s str { + if !s.ends_with(suffix) { + s + } else { + &s[..(s.len() - suffix.len())] + } +} + +pub fn capitalize(s: &str) -> String { + if s.is_empty() { + return String::new(); + } + let mut char_indices = s.char_indices(); + char_indices.next().unwrap(); + match char_indices.next() { + None => s.to_uppercase(), + Some((i, _)) => s[..i].to_uppercase() + &s[i..], + } +} + +#[cfg(test)] +mod test { + + use super::capitalize; + use super::remove_suffix; + use super::remove_to; + + #[test] + fn test_remove_to() { + assert_eq!("aaa", remove_to("aaa", |c| c == '.')); + assert_eq!("bbb", remove_to("aaa.bbb", |c| c == '.')); + assert_eq!("ccc", remove_to("aaa.bbb.ccc", |c| c == '.')); + } + + #[test] + fn test_remove_suffix() { + assert_eq!("bbb", remove_suffix("bbbaaa", "aaa")); + assert_eq!("aaa", remove_suffix("aaa", "bbb")); + } + + #[test] + fn test_capitalize() { + assert_eq!("", capitalize("")); + assert_eq!("F", capitalize("f")); + assert_eq!("Foo", capitalize("foo")); + } +} diff --git a/2.27.1/src/syntax.rs b/2.27.1/src/syntax.rs new file mode 100644 index 0000000..d075063 --- /dev/null +++ b/2.27.1/src/syntax.rs @@ -0,0 +1,15 @@ +#[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/2.27.1/src/well_known_types.rs b/2.27.1/src/well_known_types.rs new file mode 100644 index 0000000..6264947 --- /dev/null +++ b/2.27.1/src/well_known_types.rs @@ -0,0 +1,63 @@ +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")); + } +} |