protobuf-codegen-3.7.2/.cargo_vcs_info.json0000644000000001560000000000100143110ustar { "git": { "sha1": "4cb84f305c05f0376ff51b555a2740c5251c1280" }, "path_in_vcs": "protobuf-codegen" }protobuf-codegen-3.7.2/Cargo.lock0000644000000250230000000000100122640ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anyhow" version = "1.0.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", "windows-sys", ] [[package]] name = "fastrand" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "getrandom" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" dependencies = [ "cfg-if", "libc", "wasi", "windows-targets", ] [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "home" version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ "windows-sys", ] [[package]] name = "indexmap" version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "libc" version = "0.2.170" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" [[package]] name = "linux-raw-sys" version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6db9c683daf087dc577b7506e9695b3d556a9f3849903fa28186283afd6809e9" [[package]] name = "log" version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "once_cell" version = "1.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "protobuf" version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d65a1d4ddae7d8b5de68153b48f6aa3bba8cb002b243dbdbc55a5afbc98f99f4" dependencies = [ "once_cell", "protobuf-support", "thiserror", ] [[package]] name = "protobuf-codegen" version = "3.7.2" dependencies = [ "anyhow", "once_cell", "protobuf", "protobuf-parse", "regex", "tempfile", "thiserror", ] [[package]] name = "protobuf-parse" version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4aeaa1f2460f1d348eeaeed86aea999ce98c1bded6f089ff8514c9d9dbdc973" dependencies = [ "anyhow", "indexmap", "log", "protobuf", "protobuf-support", "tempfile", "thiserror", "which", ] [[package]] name = "protobuf-support" version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e36c2f31e0a47f9280fb347ef5e461ffcd2c52dd520d8e216b52f93b0b0d7d6" dependencies = [ "thiserror", ] [[package]] name = "quote" version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ "proc-macro2", ] [[package]] name = "regex" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustix" version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.4.15", "windows-sys", ] [[package]] name = "rustix" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7178faa4b75a30e269c71e61c353ce2748cf3d76f0c44c393f4e60abf49b825" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys 0.9.2", "windows-sys", ] [[package]] name = "syn" version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c317e0a526ee6120d8dabad239c8dadca62b24b6f168914bbbc8e2fb1f0e567" dependencies = [ "cfg-if", "fastrand", "getrandom", "once_cell", "rustix 1.0.2", "windows-sys", ] [[package]] name = "thiserror" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "wasi" version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" dependencies = [ "wit-bindgen-rt", ] [[package]] name = "which" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", "home", "once_cell", "rustix 0.38.44", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "wit-bindgen-rt" version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ "bitflags", ] protobuf-codegen-3.7.2/Cargo.toml0000644000000027450000000000100123150ustar # 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] edition = "2021" name = "protobuf-codegen" version = "3.7.2" authors = ["Stepan Koltsov "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = """ Code generator for rust-protobuf. Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rs` binary. """ homepage = "https://github.com/stepancheg/rust-protobuf/" readme = "README.md" license = "MIT" repository = "https://github.com/stepancheg/rust-protobuf/" [package.metadata.docs.rs] all-features = true [lib] name = "protobuf_codegen" path = "src/lib.rs" bench = false [[bin]] name = "protoc-gen-rs" path = "src/bin/protoc-gen-rs.rs" test = false [dependencies.anyhow] version = "1.0.53" [dependencies.once_cell] version = "1.10.0" [dependencies.protobuf] version = "=3.7.2" [dependencies.protobuf-parse] version = "=3.7.2" [dependencies.regex] version = "1.5.5" [dependencies.tempfile] version = "3" [dependencies.thiserror] version = "1.0.30" protobuf-codegen-3.7.2/Cargo.toml.orig000064400000000000000000000014451046102023000157720ustar 00000000000000[package] name = "protobuf-codegen" version = "3.7.2" authors = ["Stepan Koltsov "] edition = "2021" license = "MIT" homepage = "https://github.com/stepancheg/rust-protobuf/" repository = "https://github.com/stepancheg/rust-protobuf/" description = """ Code generator for rust-protobuf. Includes a library to invoke programmatically (e. g. from `build.rs`) and `protoc-gen-rs` binary. """ [lib] bench = false [dependencies] thiserror = "1.0.30" anyhow = "1.0.53" regex = "1.5.5" once_cell = "1.10.0" tempfile = "3" protobuf = { path = "../protobuf", version = "=3.7.2" } protobuf-parse = { path = "../protobuf-parse", version = "=3.7.2" } [[bin]] name = "protoc-gen-rs" path = "src/bin/protoc-gen-rs.rs" test = false [package.metadata.docs.rs] all-features = true protobuf-codegen-3.7.2/LICENSE.txt000064400000000000000000000020421046102023000147200ustar 00000000000000Copyright (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. protobuf-codegen-3.7.2/README.md000064400000000000000000000077051046102023000143670ustar 00000000000000 # Protobuf code generator for `protobuf` crate This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build. # How to generate code There are three main ways to generate `.rs` files from `.proto` files: * using `protoc` command line tool and `protoc-gen-rs` plugin * using this crate `Codegen` with pure rust parser * using this crate `Codegen` with `protoc` parser Which one should you use depends on your needs. If you are using non-cargo build system (like Bazel), you might prefer using `protoc-gen-rs` plugin for `protoc`. If you build with `cargo`, you probably want to use `Codegen` from this crate. # Protoc parser vs pure rust parser There are two protobuf parsers which can be plugged into this crate: * `protoc`-based parser (`protoc` is a command like utility from Google protobuf) * pure rust parser (`protobuf-parse` crate) `protoc`-based parser is expected to parse `.proto` files very correctly: all Google's protobuf implementations rely on it. While there are no known bugs in `protobuf-parse`, it is not tested very well. Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser, mostly complex message options specified in `.proto` files. I never saw anyone using them, but you have been warned. Note `protoc` command can be obtained from [`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate. # Example ```rust // Use this in build.rs protobuf_codegen::Codegen::new() // Use `protoc` parser, optional. .protoc() // Use `protoc-bin-vendored` bundled protoc command, optional. .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap()) // All inputs and imports from the inputs must reside in `includes` directories. .includes(&["src/protos"]) // Inputs must reside in some of include paths. .input("src/protos/apple.proto") .input("src/protos/banana.proto") // Specify output directory relative to Cargo output directory. .cargo_out_dir("protos") .run_from_script(); ``` ## How to use `protoc-gen-rs` 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 C++ protobuf library. 1) Install `protoc-gen-rs` program (which is `protoc` plugin) It can be installed either from source or with `cargo install protobuf-codegen` command. 2) Add `protoc-gen-rs` to $PATH If you installed it with cargo, it should be ```sh PATH="$HOME/.cargo/bin:$PATH" ``` 3) Generate .rs files: ```sh protoc --rs_out . foo.proto ``` This will generate .rs files in current directory. # Customize generate code Sometimes generated code need to be adjusted, e. g. to have custom derives. rust-protobuf provides two options to do that: * generated `.rs` files contain `@@protoc_insertion_point(...)` markers (similar markers inserts Google's protobuf generator for C++ or Java). Simple script `sed` one-liners can be used to replace these markers with custom annotations. * `Codegen::customize_callback` can be used to patch generated code when invoked from `build.rs` script. # Serde rust-protobuf since version 3 no longer directly supports serde. Rust-protobuf 3 fully supports: * runtime reflection * JSON parsing and printing via [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping) Which covers the most of serde use cases. If you still need serde, generic customization callback (see above) can be used to insert `#[serde(...)]` annotations. [Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde) in the rust-protobuf repository demonstrates how to do it. protobuf-codegen-3.7.2/src/bin/protoc-gen-rs.rs000064400000000000000000000001131046102023000174760ustar 00000000000000fn main() { protobuf_codegen::protoc_gen_rs::protoc_gen_rust_main(); } protobuf-codegen-3.7.2/src/codegen/mod.rs000064400000000000000000000175511046102023000164300ustar 00000000000000use std::env; use std::ffi::OsString; use std::fs; use std::path::Component; use std::path::Path; use std::path::PathBuf; use std::process; use anyhow::Context; use protobuf_parse::Parser; use crate::customize::CustomizeCallback; use crate::customize::CustomizeCallbackHolder; use crate::gen_and_write::gen_and_write; use crate::Customize; #[derive(Debug)] enum WhichParser { Pure, Protoc, } impl Default for WhichParser { fn default() -> WhichParser { WhichParser::Pure } } #[derive(Debug, thiserror::Error)] enum CodegenError { #[error("out_dir is not specified")] OutDirNotSpecified, } /// Entry point for `.proto` to `.rs` code generation. /// /// This is similar to `protoc --rs_out...`. #[derive(Debug, Default)] pub struct Codegen { /// What parser to use to parse `.proto` files. which_parser: Option, /// Create out directory. create_out_dir: bool, /// --lang_out= param out_dir: Option, /// -I args includes: Vec, /// List of .proto files to compile inputs: Vec, /// Customize code generation customize: Customize, /// Customize code generation customize_callback: CustomizeCallbackHolder, /// Protoc command path protoc: Option, /// Extra `protoc` args protoc_extra_args: Vec, /// Capture stderr when running `protoc`. capture_stderr: bool, } impl Codegen { /// Create new codegen object. /// /// Uses `protoc` from `$PATH` by default. /// /// Can be switched to pure rust parser using [`pure`](Self::pure) function. pub fn new() -> Self { Self::default() } /// Switch to pure Rust parser of `.proto` files. pub fn pure(&mut self) -> &mut Self { self.which_parser = Some(WhichParser::Pure); self } /// Switch to `protoc` parser of `.proto` files. pub fn protoc(&mut self) -> &mut Self { self.which_parser = Some(WhichParser::Protoc); self } /// Output directory for generated code. /// /// When invoking from `build.rs`, consider using /// [`cargo_out_dir`](Self::cargo_out_dir) instead. pub fn out_dir(&mut self, out_dir: impl AsRef) -> &mut Self { self.out_dir = Some(out_dir.as_ref().to_owned()); self } /// Set output directory relative to Cargo output dir. /// /// With this option, output directory is erased and recreated during invocation. pub fn cargo_out_dir(&mut self, rel: &str) -> &mut Self { let rel = Path::new(rel); let mut not_empty = false; for comp in rel.components() { match comp { Component::ParentDir => { panic!("parent path in components of rel path: `{}`", rel.display()); } Component::CurDir => { continue; } Component::Normal(..) => {} Component::RootDir | Component::Prefix(..) => { panic!("root dir in components of rel path: `{}`", rel.display()); } } not_empty = true; } if !not_empty { panic!("empty rel path: `{}`", rel.display()); } let cargo_out_dir = env::var("OUT_DIR").expect("OUT_DIR env var not set"); let mut path = PathBuf::from(cargo_out_dir); path.push(rel); self.create_out_dir = true; self.out_dir(path) } /// Add an include directory. pub fn include(&mut self, include: impl AsRef) -> &mut Self { self.includes.push(include.as_ref().to_owned()); self } /// Add include directories. pub fn includes(&mut self, includes: impl IntoIterator>) -> &mut Self { for include in includes { self.include(include); } self } /// Append a `.proto` file path to compile pub fn input(&mut self, input: impl AsRef) -> &mut Self { self.inputs.push(input.as_ref().to_owned()); self } /// Append multiple `.proto` file paths to compile pub fn inputs(&mut self, inputs: impl IntoIterator>) -> &mut Self { for input in inputs { self.input(input); } self } /// Specify `protoc` command path to be used when invoking code generation. /// /// # Examples /// /// ```no_run /// # mod protoc_bin_vendored { /// # pub fn protoc_bin_path() -> Result { /// # unimplemented!() /// # } /// # } /// /// use protobuf_codegen::Codegen; /// /// Codegen::new() /// .protoc() /// .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap()) /// // ... /// .run() /// .unwrap(); /// ``` /// /// This option is ignored when pure Rust parser is used. pub fn protoc_path(&mut self, protoc: &Path) -> &mut Self { self.protoc = Some(protoc.to_owned()); self } /// Capture stderr to error when running `protoc`. pub fn capture_stderr(&mut self) -> &mut Self { self.capture_stderr = true; self } /// Extra command line flags for `protoc` invocation. /// /// For example, `--experimental_allow_proto3_optional` option. /// /// This option is ignored when pure Rust parser is used. pub fn protoc_extra_arg(&mut self, arg: impl Into) -> &mut Self { self.protoc_extra_args.push(arg.into()); self } /// Set options to customize code generation pub fn customize(&mut self, customize: Customize) -> &mut Self { self.customize.update_with(&customize); self } /// Callback for dynamic per-element customization. pub fn customize_callback(&mut self, callback: impl CustomizeCallback) -> &mut Self { self.customize_callback = CustomizeCallbackHolder::new(callback); self } /// Invoke the code generation. /// /// This is roughly equivalent to `protoc --rs_out=...` but /// without requiring `protoc-gen-rs` command in `$PATH`. /// /// This function uses pure Rust parser or `protoc` parser depending on /// how this object was configured. pub fn run(&self) -> anyhow::Result<()> { let out_dir = match &self.out_dir { Some(out_dir) => out_dir, None => return Err(CodegenError::OutDirNotSpecified.into()), }; if self.create_out_dir { if out_dir.exists() { fs::remove_dir_all(&out_dir)?; } fs::create_dir(&out_dir)?; } let mut parser = Parser::new(); parser.protoc(); if let Some(protoc) = &self.protoc { parser.protoc_path(protoc); } match &self.which_parser { Some(WhichParser::Protoc) => { parser.protoc(); } Some(WhichParser::Pure) => { parser.pure(); } None => {} } parser.inputs(&self.inputs); parser.includes(&self.includes); parser.protoc_extra_args(&self.protoc_extra_args); if self.capture_stderr { parser.capture_stderr(); } let parsed_and_typechecked = parser .parse_and_typecheck() .context("parse and typecheck")?; gen_and_write( &parsed_and_typechecked.file_descriptors, &parsed_and_typechecked.parser, &parsed_and_typechecked.relative_paths, &out_dir, &self.customize, &*self.customize_callback, ) } /// Similar to `run`, but prints the message to stderr and exits the process on error. pub fn run_from_script(&self) { if let Err(e) = self.run() { eprintln!("codegen failed: {:?}", e); process::exit(1); } } } protobuf-codegen-3.7.2/src/compiler_plugin.rs000064400000000000000000000026361046102023000174330ustar 00000000000000use std::io::stdin; use std::io::stdout; use std::str; use protobuf::descriptor::FileDescriptorProto; use protobuf::plugin::*; use protobuf::Message; use protobuf_parse::ProtoPathBuf; pub struct GenRequest<'a> { pub file_descriptors: &'a [FileDescriptorProto], pub files_to_generate: &'a [ProtoPathBuf], pub parameter: &'a str, } pub struct GenResult { pub name: String, pub content: Vec, } pub fn plugin_main(gen: F) -> anyhow::Result<()> where F: Fn(&GenRequest) -> anyhow::Result>, { let req = CodeGeneratorRequest::parse_from_reader(&mut stdin()).unwrap(); let result = gen(&GenRequest { file_descriptors: &req.proto_file, files_to_generate: &req .file_to_generate .iter() .map(|n| ProtoPathBuf::new(n.to_owned())) .collect::>>()?, parameter: req.parameter(), })?; let mut resp = CodeGeneratorResponse::new(); resp.set_supported_features(code_generator_response::Feature::FEATURE_PROTO3_OPTIONAL as u64); resp.file = result .iter() .map(|file| { let mut r = code_generator_response::File::new(); r.set_name(file.name.to_string()); r.set_content(str::from_utf8(file.content.as_ref()).unwrap().to_string()); r }) .collect(); resp.write_to_writer(&mut stdout()).unwrap(); Ok(()) } protobuf-codegen-3.7.2/src/customize/ctx.rs000064400000000000000000000050431046102023000170560ustar 00000000000000use std::fmt; use protobuf::reflect::EnumDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; use crate::customize::CustomizeCallback; use crate::Customize; #[derive(Clone)] pub(crate) struct CustomizeElemCtx<'a> { pub(crate) for_elem: Customize, pub(crate) for_children: Customize, pub(crate) callback: &'a dyn CustomizeCallback, } impl<'a> fmt::Debug for CustomizeElemCtx<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("CustomizeElemCtx") .field("for_elem", &self.for_elem) .field("for_children", &self.for_children) .finish_non_exhaustive() } } impl<'a> CustomizeElemCtx<'a> { pub(crate) fn child( &self, elem_from_rustproto: &Customize, elem_descriptor: &impl DescriptorForCustomize, ) -> CustomizeElemCtx<'a> { let mut for_elem = self.for_children.clone(); for_elem.update_with(elem_from_rustproto); let for_children = for_elem.clone(); for_elem.update_with(&elem_descriptor.customize(self.callback)); CustomizeElemCtx { for_elem, for_children, callback: self.callback, } } } pub(crate) trait DescriptorForCustomize { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize; } impl DescriptorForCustomize for MessageDescriptor { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.message(self) } } impl DescriptorForCustomize for FieldDescriptor { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.field(self) } } impl DescriptorForCustomize for EnumDescriptor { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.enumeration(self) } } impl DescriptorForCustomize for OneofDescriptor { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.oneof(self) } } impl DescriptorForCustomize for FileDescriptor { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.file(self) } } pub(crate) struct SpecialFieldPseudoDescriptor<'a> { pub(crate) message: &'a MessageDescriptor, pub(crate) field: &'a str, } impl<'a> DescriptorForCustomize for SpecialFieldPseudoDescriptor<'a> { fn customize(&self, callback: &dyn CustomizeCallback) -> Customize { callback.special_field(self.message, self.field) } } protobuf-codegen-3.7.2/src/customize/mod.rs000064400000000000000000000221151046102023000170360ustar 00000000000000pub(crate) mod ctx; pub(crate) mod rustproto_proto; use std::fmt; use std::ops::Deref; use std::rc::Rc; use protobuf::reflect::EnumDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; /// Dynamic callback to customize code generation. pub trait CustomizeCallback: 'static { fn file(&self, file: &FileDescriptor) -> Customize { let _ = file; Customize::default() } fn message(&self, message: &MessageDescriptor) -> Customize { let _ = message; Customize::default() } fn field(&self, field: &FieldDescriptor) -> Customize { let _ = field; Customize::default() } fn special_field(&self, message: &MessageDescriptor, field: &str) -> Customize { let _ = (message, field); Customize::default() } fn enumeration(&self, enum_type: &EnumDescriptor) -> Customize { let _ = enum_type; Customize::default() } fn oneof(&self, oneof: &OneofDescriptor) -> Customize { let _ = oneof; Customize::default() } } pub(crate) struct CustomizeCallbackDefault; impl CustomizeCallback for CustomizeCallbackDefault {} #[derive(Clone)] pub(crate) struct CustomizeCallbackHolder(pub(crate) Rc); impl CustomizeCallbackHolder { pub(crate) fn new(callback: impl CustomizeCallback) -> CustomizeCallbackHolder { CustomizeCallbackHolder(Rc::new(callback)) } } impl Deref for CustomizeCallbackHolder { type Target = dyn CustomizeCallback; fn deref(&self) -> &Self::Target { &*self.0 } } impl Default for CustomizeCallbackHolder { fn default() -> Self { CustomizeCallbackHolder(Rc::new(CustomizeCallbackDefault)) } } impl PartialEq for CustomizeCallbackHolder { fn eq(&self, other: &Self) -> bool { Rc::ptr_eq(&self.0, &other.0) } } impl fmt::Debug for CustomizeCallbackHolder { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("CustomizeCallbackWrapper") .finish_non_exhaustive() } } /// Specifies style of generated code. /// Generated files can be customized using this proto /// or using `rustproto.proto` options. #[derive(Default, Debug, Clone, PartialEq)] pub struct Customize { /// Code to insert before the element in the generated file. pub(crate) before: Option, /// When false, `get_`, `set_`, `mut_` etc. accessors are not generated pub(crate) generate_accessors: Option, /// When false, `get_` is not generated even if `syntax = "proto2"` pub(crate) generate_getter: Option, /// Use `bytes::Bytes` for `bytes` fields pub(crate) tokio_bytes: Option, /// Use `bytes::Bytes` for `string` fields pub(crate) tokio_bytes_for_string: Option, /// When false, `#[non_exhaustive]` is not generated for `oneof` fields. pub(crate) oneofs_non_exhaustive: Option, /// Enable lite runtime. pub(crate) lite_runtime: Option, /// Generate `mod.rs` in the output directory. /// /// This option allows inclusion of generated files from cargo output directory. /// /// This option will likely be on by default in rust-protobuf version 3. pub(crate) gen_mod_rs: Option, /// Used internally to generate protos bundled in protobuf crate /// like `descriptor.proto` pub(crate) inside_protobuf: Option, /// When true, protobuf maps are represented with `std::collections::BTreeMap` pub(crate) btreemap: Option, } #[derive(Debug, thiserror::Error)] pub(crate) enum CustomizeParseParameterError { #[error("Cannot parse bool option value: {:?}", .0)] CannotParseBool(String), #[error("Unknown option name: {:?}", .0)] UnknownOptionName(String), } impl Customize { /// Insert code before the element in the generated file /// (e. g. serde annotations, see /// [example here](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde)). pub fn before(mut self, before: &str) -> Self { self.before = Some(before.to_owned()); self } pub fn generate_accessors(mut self, generate_accessors: bool) -> Self { self.generate_accessors = Some(generate_accessors); self } pub fn generate_getter(mut self, generate_getter: bool) -> Self { self.generate_getter = Some(generate_getter); self } pub fn tokio_bytes(mut self, tokio_bytes: bool) -> Self { self.tokio_bytes = Some(tokio_bytes); self } pub fn tokio_bytes_for_string(mut self, tokio_bytes_for_string: bool) -> Self { self.tokio_bytes_for_string = Some(tokio_bytes_for_string); self } pub fn oneofs_non_exhaustive(mut self, non_exhaustive: bool) -> Self { self.oneofs_non_exhaustive = Some(non_exhaustive); self } /// Generate code for "lite runtime". Generated code contains no code for reflection. /// So the generated code (and more importantly, generated binary size) is smaller, /// but reflection, text format, JSON serialization won't work. /// /// Note when using `protoc` plugin `protoc-gen-rs`, the option name is just `lite`. pub fn lite_runtime(mut self, lite_runtime: bool) -> Self { self.lite_runtime = Some(lite_runtime); self } /// Generate `mod.rs` with all the generated modules. /// This option is on by default in rust-protobuf version 3. pub fn gen_mod_rs(mut self, gen_mod_rs: bool) -> Self { self.gen_mod_rs = Some(gen_mod_rs); self } /// Generate code bundled in protobuf crate. Regular users don't need this option. pub fn inside_protobuf(mut self, inside_protobuf: bool) -> Self { self.inside_protobuf = Some(inside_protobuf); self } /// Use btreemaps for maps representation pub fn btreemaps(self, use_btreemaps: bool) -> Self { Self { btreemap: Some(use_btreemaps), ..self } } /// Update fields of self with fields defined in other customize pub fn update_with(&mut self, that: &Customize) { if let Some(v) = &that.before { self.before = Some(v.clone()); } if let Some(v) = that.generate_accessors { self.generate_accessors = Some(v); } if let Some(v) = that.generate_getter { self.generate_getter = Some(v); } if let Some(v) = that.tokio_bytes { self.tokio_bytes = Some(v); } if let Some(v) = that.tokio_bytes_for_string { self.tokio_bytes_for_string = Some(v); } if let Some(v) = that.lite_runtime { self.lite_runtime = Some(v); } if let Some(v) = that.gen_mod_rs { self.gen_mod_rs = Some(v); } if let Some(v) = that.inside_protobuf { self.inside_protobuf = Some(v); } if let Some(v) = that.btreemap { self.btreemap = Some(v); } } /// Update unset fields of self with fields from other customize pub fn set_defaults_from(&mut self, other: &Customize) { let mut tmp = other.clone(); tmp.update_with(self); *self = tmp; } /// Parse customize options from a string passed via protoc flag. pub fn parse_from_parameter(parameter: &str) -> anyhow::Result { fn parse_bool(v: &str) -> anyhow::Result { v.parse() .map_err(|_| CustomizeParseParameterError::CannotParseBool(v.to_owned()).into()) } let mut r = Customize::default(); for nv in parameter.split_whitespace() { let (n, v) = match nv.find('=') { Some(eq) => { let n = &nv[..eq]; let v = &nv[eq + 1..]; (n, v) } None => (nv, "true"), }; if n == "generate_accessors" { r.generate_accessors = Some(parse_bool(v)?); } else if n == "generate_getter" { r.generate_getter = Some(parse_bool(v)?); } else if n == "tokio_bytes" { r.tokio_bytes = Some(parse_bool(v)?); } else if n == "tokio_bytes_for_string" { r.tokio_bytes_for_string = Some(parse_bool(v)?); } else if n == "lite_runtime" { r.lite_runtime = Some(parse_bool(v)?); } else if n == "gen_mod_rs" { r.gen_mod_rs = Some(parse_bool(v)?); } else if n == "btreemap" { r.btreemap = Some(parse_bool(v)?); } else if n == "inside_protobuf" { r.inside_protobuf = Some(parse_bool(v)?); } else if n == "lite" { // Support Java and C++ protoc plugin syntax: // https://github.com/protocolbuffers/protobuf/issues/6489 r.lite_runtime = Some(parse_bool(v)?); } else { return Err(CustomizeParseParameterError::UnknownOptionName(n.to_owned()).into()); } } Ok(r) } } protobuf-codegen-3.7.2/src/customize/rustproto_proto.rs000064400000000000000000000060761046102023000215730ustar 00000000000000use protobuf::descriptor::EnumOptions; use protobuf::descriptor::FieldOptions; use protobuf::descriptor::FileOptions; use protobuf::descriptor::MessageOptions; use protobuf::rustproto; use crate::Customize; pub(crate) fn customize_from_rustproto_for_message(source: &MessageOptions) -> Customize { let before = None; let generate_accessors = rustproto::exts::generate_accessors.get(source); let generate_getter = rustproto::exts::generate_getter.get(source); let tokio_bytes = rustproto::exts::tokio_bytes.get(source); let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string.get(source); let oneofs_non_exhaustive = rustproto::exts::oneofs_non_exhaustive.get(source); let btreemap = rustproto::exts::btreemap.get(source); let lite_runtime = None; let gen_mod_rs = None; let inside_protobuf = None; Customize { before, generate_accessors, generate_getter, tokio_bytes, tokio_bytes_for_string, oneofs_non_exhaustive, lite_runtime, gen_mod_rs, inside_protobuf, btreemap, } } pub(crate) fn customize_from_rustproto_for_enum(_source: &EnumOptions) -> Customize { Customize::default() } pub(crate) fn customize_from_rustproto_for_field(source: &FieldOptions) -> Customize { let before = None; let generate_accessors = rustproto::exts::generate_accessors_field.get(source); let generate_getter = rustproto::exts::generate_getter_field.get(source); let tokio_bytes = rustproto::exts::tokio_bytes_field.get(source); let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_field.get(source); let oneofs_non_exhaustive = rustproto::exts::oneofs_non_exhaustive_field.get(source); let btreemap = rustproto::exts::btreemap_field.get(source); let lite_runtime = None; let gen_mod_rs = None; let inside_protobuf = None; Customize { before, generate_accessors, generate_getter, tokio_bytes, tokio_bytes_for_string, oneofs_non_exhaustive, lite_runtime, gen_mod_rs, inside_protobuf, btreemap, } } pub(crate) fn customize_from_rustproto_for_file(source: &FileOptions) -> Customize { let before = None; let generate_accessors = rustproto::exts::generate_accessors_all.get(source); let generate_getter = rustproto::exts::generate_getter_all.get(source); let tokio_bytes = rustproto::exts::tokio_bytes_all.get(source); let tokio_bytes_for_string = rustproto::exts::tokio_bytes_for_string_all.get(source); let oneofs_non_exhaustive = rustproto::exts::oneofs_non_exhaustive_all.get(source); let lite_runtime = rustproto::exts::lite_runtime_all.get(source); let btreemap = rustproto::exts::btreemap_all.get(source); let gen_mod_rs = None; let inside_protobuf = None; Customize { before, generate_accessors, generate_getter, tokio_bytes, tokio_bytes_for_string, oneofs_non_exhaustive, lite_runtime, inside_protobuf, gen_mod_rs, btreemap, } } protobuf-codegen-3.7.2/src/gen/all.rs000064400000000000000000000040321046102023000155540ustar 00000000000000use std::collections::HashMap; use protobuf::descriptor::FileDescriptorProto; use protobuf::reflect::FileDescriptor; use protobuf_parse::ProtoPath; use protobuf_parse::ProtoPathBuf; use crate::compiler_plugin; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::CustomizeCallback; use crate::gen::file::gen_file; use crate::gen::mod_rs::gen_mod_rs; use crate::gen::scope::RootScope; use crate::gen::well_known_types::gen_well_known_types_mod; use crate::Customize; pub(crate) fn gen_all( file_descriptors: &[FileDescriptorProto], parser: &str, files_to_generate: &[ProtoPathBuf], customize: &Customize, customize_callback: &dyn CustomizeCallback, ) -> anyhow::Result> { let file_descriptors = FileDescriptor::new_dynamic_fds(file_descriptors.to_vec(), &[])?; let root_scope = RootScope { file_descriptors: &file_descriptors, }; let mut results: Vec = Vec::new(); let files_map: HashMap<&ProtoPath, &FileDescriptor> = file_descriptors .iter() .map(|f| Ok((ProtoPath::new(f.proto().name())?, f))) .collect::>()?; let mut mods = Vec::new(); let customize = CustomizeElemCtx { for_elem: customize.clone(), for_children: customize.clone(), callback: customize_callback, }; for file_name in files_to_generate { let file = files_map.get(file_name.as_path()).expect(&format!( "file not found in file descriptors: {:?}, files: {:?}", file_name, files_map.keys() )); let gen_file_result = gen_file(file, &files_map, &root_scope, &customize, parser)?; results.push(gen_file_result.compiler_plugin_result); mods.push(gen_file_result.mod_name); } if customize.for_elem.inside_protobuf.unwrap_or(false) { results.push(gen_well_known_types_mod()); } if customize.for_elem.gen_mod_rs.unwrap_or(true) { results.push(gen_mod_rs(&mods)); } Ok(results) } protobuf-codegen-3.7.2/src/gen/code_writer.rs000064400000000000000000000324571046102023000173260ustar 00000000000000use std::convert::Infallible; use crate::gen::rust::rel_path::RustRelativePath; /// Field visibility. pub(crate) enum Visibility { Public, Default, Path(RustRelativePath), } pub(crate) struct CodeWriter<'a> { writer: &'a mut String, indent: String, } impl<'a> CodeWriter<'a> { pub(crate) fn new(writer: &'a mut String) -> CodeWriter<'a> { CodeWriter { writer, indent: "".to_string(), } } pub(crate) fn with_no_error(f: impl FnOnce(&mut CodeWriter)) -> String { Self::with_impl::(|w| Ok(f(w))).unwrap_or_else(|e| match e {}) } pub(crate) fn with(f: F) -> anyhow::Result where F: FnOnce(&mut CodeWriter) -> anyhow::Result<()>, { Self::with_impl(f) } fn with_impl(f: F) -> Result where F: FnOnce(&mut CodeWriter) -> Result<(), E>, { let mut writer = String::new(); { let mut cw = CodeWriter::new(&mut writer); f(&mut cw)?; } Ok(writer) } pub(crate) fn write_line>(&mut self, line: S) { if line.as_ref().is_empty() { self.writer.push_str("\n"); } else { let s: String = [self.indent.as_ref(), line.as_ref(), "\n"].concat(); self.writer.push_str(&s); } } pub(crate) fn _write_text(&mut self, text: &str) { for line in text.lines() { self.write_line(line); } } pub(crate) fn write_generated_by(&mut self, pkg: &str, version: &str, parser: &str) { self.write_line(format!( "// This file is generated by {pkg} {version}. Do not edit", pkg = pkg, version = version )); self.write_line(format!( "// .proto file is parsed by {parser}", parser = parser )); self.write_generated_common(); } fn write_generated_common(&mut self) { // https://secure.phabricator.com/T784 self.write_line(&format!("// {}generated", "@")); self.write_line(""); self.comment("https://github.com/rust-lang/rust-clippy/issues/702"); self.write_line("#![allow(unknown_lints)]"); self.write_line("#![allow(clippy::all)]"); self.write_line(""); self.write_line("#![allow(unused_attributes)]"); self.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]"); self.write_line(""); self.write_line("#![allow(dead_code)]"); self.write_line("#![allow(missing_docs)]"); self.write_line("#![allow(non_camel_case_types)]"); self.write_line("#![allow(non_snake_case)]"); self.write_line("#![allow(non_upper_case_globals)]"); self.write_line("#![allow(trivial_casts)]"); self.write_line("#![allow(unused_results)]"); self.write_line("#![allow(unused_mut)]"); } pub(crate) fn unimplemented(&mut self) { self.write_line(format!("unimplemented!();")); } pub(crate) fn indented(&mut self, cb: F) where F: FnOnce(&mut CodeWriter), { cb(&mut CodeWriter { writer: self.writer, indent: format!("{} ", self.indent), }); } #[allow(dead_code)] pub(crate) fn commented(&mut self, cb: F) where F: Fn(&mut CodeWriter), { cb(&mut CodeWriter { writer: self.writer, indent: format!("// {}", self.indent), }); } pub(crate) fn pub_const(&mut self, name: &str, field_type: &str, init: &str) { self.write_line(&format!("pub const {}: {} = {};", name, field_type, init)); } pub(crate) fn lazy_static(&mut self, name: &str, ty: &str, protobuf_crate_path: &str) { self.write_line(&format!( "static {}: {}::rt::Lazy<{}> = {}::rt::Lazy::new();", name, protobuf_crate_path, ty, protobuf_crate_path, )); } pub(crate) fn lazy_static_decl_get_simple( &mut self, name: &str, ty: &str, init: &str, protobuf_crate_path: &str, ) { self.lazy_static(name, ty, protobuf_crate_path); self.write_line(&format!("{}.get({})", name, init)); } pub(crate) fn lazy_static_decl_get( &mut self, name: &str, ty: &str, protobuf_crate_path: &str, init: impl FnOnce(&mut CodeWriter), ) { self.lazy_static(name, ty, protobuf_crate_path); self.block(&format!("{}.get(|| {{", name), "})", init); } pub(crate) fn block(&mut self, first_line: &str, last_line: &str, cb: F) where F: FnOnce(&mut CodeWriter), { self.write_line(first_line); self.indented(cb); self.write_line(last_line); } pub(crate) fn expr_block(&mut self, prefix: &str, cb: F) where F: FnOnce(&mut CodeWriter), { self.block(&format!("{} {{", prefix), "}", cb); } pub(crate) fn stmt_block, F>(&mut self, prefix: S, cb: F) where F: FnOnce(&mut CodeWriter), { self.block(&format!("{} {{", prefix.as_ref()), "};", cb); } pub(crate) fn impl_self_block, F>(&mut self, name: S, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("impl {}", name.as_ref()), cb); } pub(crate) fn impl_for_block, S2: AsRef, F>( &mut self, tr: S1, ty: S2, cb: F, ) where F: Fn(&mut CodeWriter), { self.impl_args_for_block(&[], tr.as_ref(), ty.as_ref(), cb); } pub(crate) fn impl_args_for_block(&mut self, args: &[&str], tr: &str, ty: &str, cb: F) where F: Fn(&mut CodeWriter), { let args_str = if args.is_empty() { "".to_owned() } else { format!("<{}>", args.join(", ")) }; self.expr_block(&format!("impl{} {} for {}", args_str, tr, ty), cb); } pub(crate) fn pub_struct, F>(&mut self, name: S, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("pub struct {}", name.as_ref()), cb); } pub(crate) fn pub_enum(&mut self, name: &str, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("pub enum {}", name), cb); } pub(crate) fn field_entry(&mut self, name: &str, value: &str) { self.write_line(&format!("{}: {},", name, value)); } pub(crate) fn field_decl(&mut self, name: &str, field_type: &str) { self.write_line(&format!("{}: {},", name, field_type)); } pub(crate) fn pub_field_decl(&mut self, name: &str, field_type: &str) { self.write_line(&format!("pub {}: {},", name, field_type)); } pub(crate) fn field_decl_vis(&mut self, vis: Visibility, name: &str, field_type: &str) { match vis { Visibility::Public => self.pub_field_decl(name, field_type), Visibility::Default => self.field_decl(name, field_type), Visibility::Path(..) => unimplemented!(), } } pub(crate) fn derive(&mut self, derive: &[&str]) { let v: Vec = derive.iter().map(|&s| s.to_string()).collect(); self.write_line(&format!("#[derive({})]", v.join(","))); } pub(crate) fn allow(&mut self, what: &[&str]) { let v: Vec = what.iter().map(|&s| s.to_string()).collect(); self.write_line(&format!("#[allow({})]", v.join(","))); } pub(crate) fn comment(&mut self, comment: &str) { if comment.is_empty() { self.write_line("//"); } else { self.write_line(&format!("// {}", comment)); } } fn documentation(&mut self, comment: &str) { if comment.is_empty() { self.write_line("///"); } else { self.write_line(&format!("/// {}", comment)); } } pub(crate) fn mod_doc(&mut self, comment: &str) { if comment.is_empty() { self.write_line("//!"); } else { self.write_line(&format!("//! {}", comment)); } } /// Writes the documentation of the given path. /// /// Protobuf paths are defined in proto/google/protobuf/descriptor.proto, /// in the `SourceCodeInfo` message. /// /// For example, say we have a file like: /// /// ```ignore /// message Foo { /// optional string foo = 1; /// } /// ``` /// /// Let's look at just the field definition. We have the following paths: /// /// ```ignore /// path represents /// [ 4, 0, 2, 0 ] The whole field definition. /// [ 4, 0, 2, 0, 4 ] The label (optional). /// [ 4, 0, 2, 0, 5 ] The type (string). /// [ 4, 0, 2, 0, 1 ] The name (foo). /// [ 4, 0, 2, 0, 3 ] The number (1). /// ``` /// /// The `4`s can be obtained using simple introspection: /// /// ``` /// use protobuf::descriptor::FileDescriptorProto; /// use protobuf::reflect::MessageDescriptor; /// /// let id = MessageDescriptor::for_type::() /// .field_by_name("message_type") /// .expect("`message_type` must exist") /// .proto() /// .number(); /// /// assert_eq!(id, 4); /// ``` /// /// The first `0` here means this path refers to the first message. /// /// The `2` then refers to the `field` field on the `DescriptorProto` message. /// /// Then comes another `0` to refer to the first field of the current message. /// /// Etc. pub(crate) fn all_documentation( &mut self, info: Option<&protobuf::descriptor::SourceCodeInfo>, path: &[i32], ) { let doc = info .map(|v| &v.location) .and_then(|ls| ls.iter().find(|l| l.path == path)) .map(|l| l.leading_comments()); let lines = doc .iter() .map(|doc| doc.lines()) .flatten() .collect::>(); // Skip comments with code blocks to avoid rustdoc trying to compile them. if !lines.iter().any(|line| line.starts_with(" ")) { for doc in &lines { self.documentation(doc); } } } pub(crate) fn fn_block(&mut self, vis: Visibility, sig: &str, cb: F) where F: FnOnce(&mut CodeWriter), { match vis { Visibility::Public => self.expr_block(&format!("pub fn {}", sig), cb), Visibility::Default => self.expr_block(&format!("fn {}", sig), cb), Visibility::Path(p) if p.is_empty() => self.expr_block(&format!("fn {}", sig), cb), Visibility::Path(p) => self.expr_block(&format!("pub(in {}) fn {}", p, sig), cb), } } pub(crate) fn pub_fn(&mut self, sig: &str, cb: F) where F: FnOnce(&mut CodeWriter), { self.fn_block(Visibility::Public, sig, cb); } pub(crate) fn def_fn(&mut self, sig: &str, cb: F) where F: Fn(&mut CodeWriter), { self.fn_block(Visibility::Default, sig, cb); } pub(crate) fn pub_mod(&mut self, name: &str, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("pub mod {}", name), cb) } pub(crate) fn while_block, F>(&mut self, cond: S, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("while {}", cond.as_ref()), cb); } // if ... { ... } pub(crate) fn if_stmt, F>(&mut self, cond: S, cb: F) where F: FnOnce(&mut CodeWriter), { self.expr_block(&format!("if {}", cond.as_ref()), cb); } // if ... {} else { ... } pub(crate) fn if_else_stmt, F>(&mut self, cond: S, cb: F) where F: Fn(&mut CodeWriter), { self.write_line(&format!("if {} {{", cond.as_ref())); self.write_line("} else {"); self.indented(cb); self.write_line("}"); } // if let ... = ... { ... } pub(crate) fn if_let_stmt(&mut self, decl: &str, expr: &str, cb: F) where F: FnOnce(&mut CodeWriter), { self.if_stmt(&format!("let {} = {}", decl, expr), cb); } // if let ... = ... { } else { ... } pub(crate) fn if_let_else_stmt(&mut self, decl: &str, expr: &str, cb: F) where F: Fn(&mut CodeWriter), { self.if_else_stmt(&format!("let {} = {}", decl, expr), cb); } pub(crate) fn for_stmt, S2: AsRef, F>(&mut self, over: S1, varn: S2, cb: F) where F: FnOnce(&mut CodeWriter), { self.stmt_block(&format!("for {} in {}", varn.as_ref(), over.as_ref()), cb) } pub(crate) fn match_block, F>(&mut self, value: S, cb: F) where F: FnOnce(&mut CodeWriter), { self.stmt_block(&format!("match {}", value.as_ref()), cb); } pub(crate) fn match_expr, F>(&mut self, value: S, cb: F) where F: Fn(&mut CodeWriter), { self.expr_block(&format!("match {}", value.as_ref()), cb); } pub(crate) fn case_block, F>(&mut self, cond: S, cb: F) where F: Fn(&mut CodeWriter), { self.block(&format!("{} => {{", cond.as_ref()), "},", cb); } pub(crate) fn case_expr, S2: AsRef>(&mut self, cond: S1, body: S2) { self.write_line(&format!("{} => {},", cond.as_ref(), body.as_ref())); } } protobuf-codegen-3.7.2/src/gen/descriptor.rs000064400000000000000000000037571046102023000171770ustar 00000000000000use protobuf::reflect::EnumDescriptor; use protobuf::reflect::MessageDescriptor; use crate::gen::code_writer::CodeWriter; use crate::gen::file_descriptor::file_descriptor_call_expr; use crate::gen::inside::protobuf_crate_path; use crate::gen::scope::Scope; use crate::Customize; /// Abstract message or enum descriptor. pub(crate) trait Descriptor { const DESCRIPTOR_FN: &'static str; const TYPE_NAME: &'static str; const GET_BY_RELATIVE_NAME_NAME: &'static str; fn name_to_package(&self) -> &str; } impl Descriptor for MessageDescriptor { const DESCRIPTOR_FN: &'static str = "descriptor"; const TYPE_NAME: &'static str = "MessageDescriptor"; const GET_BY_RELATIVE_NAME_NAME: &'static str = "message_by_package_relative_name"; fn name_to_package(&self) -> &str { self.name_to_package() } } impl Descriptor for EnumDescriptor { const DESCRIPTOR_FN: &'static str = "enum_descriptor"; const TYPE_NAME: &'static str = "EnumDescriptor"; const GET_BY_RELATIVE_NAME_NAME: &'static str = "enum_by_package_relative_name"; fn name_to_package(&self) -> &str { self.name_to_package() } } pub(crate) fn write_fn_descriptor( descriptor: &D, scope: &Scope, customize: &Customize, w: &mut CodeWriter, ) { let sig = format!( "{}() -> {}::reflect::{}", D::DESCRIPTOR_FN, protobuf_crate_path(customize), D::TYPE_NAME, ); w.def_fn(&sig, |w| { let expr = format!( "{}.{}(\"{}\").unwrap()", file_descriptor_call_expr(scope), D::GET_BY_RELATIVE_NAME_NAME, descriptor.name_to_package() ); w.lazy_static( "descriptor", &format!( "{}::reflect::{}", protobuf_crate_path(customize), D::TYPE_NAME, ), &protobuf_crate_path(customize).to_string(), ); w.write_line(&format!("descriptor.get(|| {}).clone()", expr)); }); } protobuf-codegen-3.7.2/src/gen/enums.rs000064400000000000000000000341301046102023000161350ustar 00000000000000use std::collections::HashSet; use protobuf::descriptor::*; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::rustproto_proto::customize_from_rustproto_for_enum; use crate::gen::code_writer::CodeWriter; use crate::gen::code_writer::Visibility; use crate::gen::descriptor::write_fn_descriptor; use crate::gen::inside::protobuf_crate_path; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_enum_value; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::snippets::EXPR_NONE; use crate::gen::scope::EnumValueWithContext; use crate::gen::scope::EnumWithScope; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; #[derive(Clone)] pub(crate) struct EnumValueGen<'a> { value: EnumValueWithContext<'a>, enum_rust_name: RustIdentWithPath, } impl<'a> EnumValueGen<'a> { fn parse( value: EnumValueWithContext<'a>, enum_rust_name: &RustIdentWithPath, ) -> EnumValueGen<'a> { EnumValueGen { value: value.clone(), enum_rust_name: enum_rust_name.clone(), } } // enum value fn number(&self) -> i32 { self.value.proto.proto().number() } // name of enum variant in generated rust code pub fn rust_name_inner(&self) -> RustIdent { self.value.rust_name() } pub fn rust_name_outer(&self) -> RustIdentWithPath { self.enum_rust_name .to_path() .with_ident(self.rust_name_inner()) } } // Codegen for enum definition pub(crate) struct EnumGen<'a> { enum_with_scope: &'a EnumWithScope<'a>, type_name: RustIdentWithPath, lite_runtime: bool, customize: CustomizeElemCtx<'a>, path: &'a [i32], info: Option<&'a SourceCodeInfo>, } impl<'a> EnumGen<'a> { pub fn new( enum_with_scope: &'a EnumWithScope<'a>, customize: &CustomizeElemCtx<'a>, _root_scope: &RootScope, path: &'a [i32], info: Option<&'a SourceCodeInfo>, ) -> EnumGen<'a> { let customize = customize.child( &customize_from_rustproto_for_enum(enum_with_scope.en.proto().options.get_or_default()), &enum_with_scope.en, ); let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { enum_with_scope .file_descriptor() .proto() .options .optimize_for() == file_options::OptimizeMode::LITE_RUNTIME }); EnumGen { enum_with_scope, type_name: enum_with_scope.rust_name().to_path(), lite_runtime, customize, path, info, } } fn allow_alias(&self) -> bool { self.enum_with_scope .en .proto() .options .get_or_default() .allow_alias() } fn values_all(&self) -> Vec { let mut r = Vec::new(); for p in self.enum_with_scope.values() { r.push(EnumValueGen::parse(p, &self.type_name)); } r } fn values_unique(&self) -> Vec { let mut used = HashSet::new(); let mut r = Vec::new(); for p in self.enum_with_scope.values() { if !used.insert(p.proto.proto().number()) { continue; } r.push(EnumValueGen::parse(p, &self.type_name)); } r } pub fn write(&self, w: &mut CodeWriter) { self.write_enum(w); if self.allow_alias() { w.write_line(""); self.write_impl_eq(w); w.write_line(""); self.write_impl_hash(w); } w.write_line(""); self.write_impl_enum(w); if !self.lite_runtime { w.write_line(""); self.write_impl_enum_full(w); } w.write_line(""); self.write_impl_default(w); w.write_line(""); self.write_impl_self(w); } fn write_impl_self(&self, w: &mut CodeWriter) { if !self.lite_runtime { w.impl_self_block(&format!("{}", self.type_name), |w| { self.write_generated_enum_descriptor_data(w); }); } } fn write_enum(&self, w: &mut CodeWriter) { w.all_documentation(self.info, self.path); let mut derive = Vec::new(); derive.push("Clone"); derive.push("Copy"); if !self.allow_alias() { derive.push("PartialEq"); } derive.push("Eq"); derive.push("Debug"); if !self.allow_alias() { derive.push("Hash"); } else { w.comment("Note: you cannot use pattern matching for enums with allow_alias option"); } w.derive(&derive); let ref type_name = self.type_name; write_protoc_insertion_point_for_enum( w, &self.customize.for_elem, &self.enum_with_scope.en, ); w.expr_block(&format!("pub enum {}", type_name), |w| { for value in self.values_all() { write_protoc_insertion_point_for_enum_value( w, &self.customize.for_children, &value.value.proto, ); if self.allow_alias() { w.write_line(&format!( "{}, // {}", value.rust_name_inner(), value.number() )); } else { w.write_line(&format!( "{} = {},", value.rust_name_inner(), value.number() )); } } }); } fn write_impl_enum_fn_value(&self, w: &mut CodeWriter) { w.def_fn("value(&self) -> i32", |w| { if self.allow_alias() { w.match_expr("*self", |w| { for value in self.values_all() { w.case_expr( &format!("{}", value.rust_name_outer()), &format!("{}", value.number()), ); } }); } else { w.write_line("*self as i32") } }); } fn write_impl_enum_const_name(&self, w: &mut CodeWriter) { w.write_line(&format!( "const NAME: &'static str = \"{}\";", self.enum_with_scope.en.name() )); } fn write_impl_enum_fn_from_i32(&self, w: &mut CodeWriter) { w.def_fn( &format!( "from_i32(value: i32) -> ::std::option::Option<{}>", self.type_name ), |w| { w.match_expr("value", |w| { let values = self.values_unique(); for value in values { w.write_line(&format!( "{} => ::std::option::Option::Some({}),", value.number(), value.rust_name_outer() )); } w.write_line(&format!("_ => {}", EXPR_NONE)); }); }, ); } fn write_impl_enum_fn_from_str(&self, w: &mut CodeWriter) { w.def_fn( &format!( "from_str(str: &str) -> ::std::option::Option<{}>", self.type_name ), |w| { w.match_expr("str", |w| { let values = self.values_unique(); for value in values { w.write_line(&format!( "\"{}\" => ::std::option::Option::Some({}),", value.value.proto.name(), value.rust_name_outer() )); } w.write_line(&format!("_ => {}", EXPR_NONE)); }); }, ); } fn write_impl_enum_const_values(&self, w: &mut CodeWriter) { w.write_line(&format!("const VALUES: &'static [{}] = &[", self.type_name)); w.indented(|w| { for value in self.values_all() { w.write_line(&format!("{},", value.rust_name_outer())); } }); w.write_line("];"); } fn write_impl_enum(&self, w: &mut CodeWriter) { w.impl_for_block( &format!("{}::Enum", protobuf_crate_path(&self.customize.for_elem)), &format!("{}", self.type_name), |w| { self.write_impl_enum_const_name(w); w.write_line(""); self.write_impl_enum_fn_value(w); w.write_line(""); self.write_impl_enum_fn_from_i32(w); w.write_line(""); self.write_impl_enum_fn_from_str(w); w.write_line(""); self.write_impl_enum_const_values(w); }, ); } fn write_impl_enum_full(&self, w: &mut CodeWriter) { let ref type_name = self.type_name; w.impl_for_block( &format!( "{}::EnumFull", protobuf_crate_path(&self.customize.for_elem) ), &format!("{}", type_name), |w| { self.write_impl_enum_full_fn_enum_descriptor(w); w.write_line(""); self.write_impl_enum_full_fn_descriptor(w); }, ); } fn write_impl_enum_full_fn_enum_descriptor(&self, w: &mut CodeWriter) { write_fn_descriptor( &self.enum_with_scope.en, self.enum_with_scope.scope(), &self.customize.for_elem, w, ); } fn rust_enum_descriptor_is_enum_index(&self) -> bool { if self.allow_alias() { false } else { self.values_all() .into_iter() .enumerate() .all(|(i, value)| (i as i32) == value.number()) } } fn write_impl_enum_full_fn_descriptor(&self, w: &mut CodeWriter) { let sig = format!( "descriptor(&self) -> {}::reflect::EnumValueDescriptor", protobuf_crate_path(&self.customize.for_elem) ); w.def_fn(&sig, |w| { if self.rust_enum_descriptor_is_enum_index() { w.write_line("let index = *self as usize;"); } else { w.write_line("let index = match self {"); w.indented(|w| { for (i, value) in self.values_all().into_iter().enumerate() { w.write_line(&format!( "{}::{} => {},", self.type_name, value.rust_name_inner(), i )); } }); w.write_line("};"); } w.write_line(&format!("Self::enum_descriptor().value_by_index(index)")); }); } fn write_generated_enum_descriptor_data(&self, w: &mut CodeWriter) { let sig = format!( "generated_enum_descriptor_data() -> {}::reflect::GeneratedEnumDescriptorData", protobuf_crate_path(&self.customize.for_elem) ); w.fn_block( Visibility::Path( self.enum_with_scope .scope() .rust_path_to_file() .to_reverse(), ), &sig, |w| { w.write_line(&format!( "{}::reflect::GeneratedEnumDescriptorData::new::<{}>(\"{}\")", protobuf_crate_path(&self.customize.for_elem), self.type_name, self.enum_with_scope.name_to_package(), )); }, ); } fn write_impl_eq(&self, w: &mut CodeWriter) { assert!(self.allow_alias()); w.impl_for_block( "::std::cmp::PartialEq", &format!("{}", self.type_name), |w| { w.def_fn("eq(&self, other: &Self) -> bool", |w| { w.write_line(&format!( "{}::Enum::value(self) == {}::Enum::value(other)", protobuf_crate_path(&self.customize.for_elem), protobuf_crate_path(&self.customize.for_elem) )); }); }, ); } fn write_impl_hash(&self, w: &mut CodeWriter) { assert!(self.allow_alias()); w.impl_for_block("::std::hash::Hash", &format!("{}", self.type_name), |w| { w.def_fn("hash(&self, state: &mut H)", |w| { w.write_line(&format!( "state.write_i32({}::Enum::value(self))", protobuf_crate_path(&self.customize.for_elem) )); }); }); } fn write_impl_default(&self, w: &mut CodeWriter) { let first_value = &self.enum_with_scope.values()[0]; if first_value.proto.proto().number() != 0 { // This warning is emitted only for proto2 // (because in proto3 first enum variant number is always 0). // `Default` implemented unconditionally to simplify certain // generic operations, e. g. reading a map. // Also, note that even in proto2 some operations fallback to // first enum value, e. g. `get_xxx` for unset field, // so this implementation is not completely unreasonable. w.comment("Note, `Default` is implemented although default value is not 0"); } w.impl_for_block( "::std::default::Default", &format!("{}", self.type_name), |w| { w.def_fn("default() -> Self", |w| { w.write_line(&format!( "{}::{}", &self.type_name, &first_value.rust_name() )) }); }, ); } } protobuf-codegen-3.7.2/src/gen/extensions.rs000064400000000000000000000106061046102023000172070ustar 00000000000000use protobuf::descriptor::*; use protobuf::reflect::FileDescriptor; use protobuf_parse::ProtobufAbsPath; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::Customize; use crate::gen::code_writer::CodeWriter; use crate::gen::field::rust_field_name_for_protobuf_field_name; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::RustTypeMessage; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust_types_values::*; use crate::gen::scope::RootScope; struct ExtGen<'a> { file: &'a FileDescriptor, root_scope: &'a RootScope<'a>, field: &'a FieldDescriptorProto, customize: Customize, } impl<'a> ExtGen<'a> { fn extendee_rust_name(&self) -> RustIdentWithPath { type_name_to_rust_relative( &ProtobufAbsPath::from(self.field.extendee()), &FileAndMod { file: self.file.proto().name().to_owned(), relative_mod: RustRelativePath::from("exts"), customize: self.customize.clone(), }, self.root_scope, ) } fn repeated(&self) -> bool { match self.field.label() { field_descriptor_proto::Label::LABEL_REPEATED => true, field_descriptor_proto::Label::LABEL_OPTIONAL => false, field_descriptor_proto::Label::LABEL_REQUIRED => { panic!("required ext field: {}", self.field.name()) } } } fn return_type_gen(&self) -> ProtobufTypeGen { if self.field.has_type_name() { let rust_name_relative = type_name_to_rust_relative( &ProtobufAbsPath::from(self.field.type_name()), &FileAndMod { file: self.file.proto().name().to_owned(), relative_mod: RustRelativePath::from("exts"), customize: self.customize.clone(), }, self.root_scope, ); match self.field.type_() { field_descriptor_proto::Type::TYPE_MESSAGE => { ProtobufTypeGen::Message(RustTypeMessage(rust_name_relative)) } field_descriptor_proto::Type::TYPE_ENUM => { ProtobufTypeGen::EnumOrUnknown(rust_name_relative) } t => panic!("unknown type: {:?}", t), } } else { ProtobufTypeGen::Primitive(self.field.type_(), PrimitiveTypeVariant::Default) } } fn write(&self, w: &mut CodeWriter) { let suffix = if self.repeated() { "ExtFieldRepeated" } else { "ExtFieldOptional" }; let field_type = format!( "{protobuf_crate}::ext::{suffix}", protobuf_crate = protobuf_crate_path(&self.customize) ); w.pub_const( &rust_field_name_for_protobuf_field_name(self.field.name()).to_string(), &format!( "{field_type}<{extendee}, {rust_type}>", extendee=self.extendee_rust_name(), rust_type=self.return_type_gen().protobuf_value(&self.customize), ), &format!( "{field_type}::new({field_number}, {protobuf_crate}::descriptor::field_descriptor_proto::Type::{t:?})", field_number=self.field.number(), protobuf_crate = protobuf_crate_path(&self.customize), t=self.field.type_(), ), ); } } pub(crate) fn write_extensions( file: &FileDescriptor, root_scope: &RootScope, w: &mut CodeWriter, customize: &CustomizeElemCtx, ) { if file.proto().extension.is_empty() { return; } if customize.for_elem.lite_runtime.unwrap_or(false) { w.write_line(""); w.comment("Extension generation with lite runtime is not supported"); return; } w.write_line(""); w.write_line("/// Extension fields"); w.pub_mod("exts", |w| { for field in &file.proto().extension { if field.type_() == field_descriptor_proto::Type::TYPE_GROUP { continue; } w.write_line(""); ExtGen { file, root_scope, field, customize: customize.for_elem.clone(), } .write(w); } }); } protobuf-codegen-3.7.2/src/gen/field/accessor.rs000064400000000000000000000207501046102023000176760ustar 00000000000000use crate::gen::code_writer::CodeWriter; use crate::gen::field::elem::FieldElem; use crate::gen::field::elem::FieldElemEnum; use crate::gen::field::option_kind::OptionKind; use crate::gen::field::repeated::RepeatedField; use crate::gen::field::repeated::RepeatedFieldKind; use crate::gen::field::singular::SingularField; use crate::gen::field::singular::SingularFieldFlag; use crate::gen::field::FieldGen; use crate::gen::field::FieldKind; use crate::gen::field::MapField; use crate::gen::inside::protobuf_crate_path; use crate::gen::oneof::OneofField; use crate::gen::rust_types_values::RustType; use crate::gen::scope::WithScope; struct AccessorFn { name: String, // function type params after first underscore type_params: Vec, callback_params: Vec, } impl AccessorFn { fn sig(&self) -> String { let mut s = self.name.clone(); s.push_str("::<_"); for p in &self.type_params { s.push_str(", "); s.push_str(&p); } s.push_str(">"); s } } impl FieldGen<'_> { fn make_accessor_fns_lambda(&self) -> Vec { let message = self.proto_field.message.rust_name(); vec![ format!("|m: &{}| {{ &m.{} }}", message, self.rust_name), format!("|m: &mut {}| {{ &mut m.{} }}", message, self.rust_name), ] } fn make_accessor_fns_has_get_set(&self) -> Vec { let message = self.proto_field.message.rust_name(); vec![ format!("{}::{}", message, self.has_name()), format!("{}::{}", message, self.rust_name), format!("{}::{}", message, self.set_name()), ] } fn make_accessor_fns_has_get_mut_set(&self) -> Vec { let message = self.proto_field.message.rust_name(); vec![ format!("{}::{}", message, self.has_name()), format!("{}::{}", message, self.rust_name), format!("{}::{}", message, self.mut_name()), format!("{}::{}", message, self.set_name()), ] } fn accessor_fn_map(&self, map_field: &MapField) -> AccessorFn { let MapField { .. } = map_field; AccessorFn { name: "make_map_simpler_accessor_new".to_owned(), type_params: vec![format!("_")], callback_params: self.make_accessor_fns_lambda(), } } fn accessor_fn_repeated(&self, repeated_field: &RepeatedField) -> AccessorFn { let RepeatedField { .. } = repeated_field; let name = match repeated_field.kind() { RepeatedFieldKind::Vec => "make_vec_simpler_accessor", }; AccessorFn { name: name.to_owned(), type_params: vec![format!("_")], callback_params: self.make_accessor_fns_lambda(), } } fn accessor_fn_oneof_enum(&self, oneof: &OneofField, en: &FieldElemEnum) -> AccessorFn { let message = self.proto_field.message.rust_name(); let variant_path = oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()); let getter = CodeWriter::with_no_error(|w| { w.expr_block( &format!( "|message: &{}| match &message.{}", message, oneof.oneof_field_name ), |w| { w.case_expr( &format!("::std::option::Option::Some({}(e))", variant_path), "::std::option::Option::Some(*e)", ); w.case_expr("_", "::std::option::Option::None"); }, ); }); let setter = CodeWriter::with_no_error(|w| { w.expr_block( &format!( "|message: &mut {}, e: {}::EnumOrUnknown<{}>|", message, protobuf_crate_path(&self.customize), en.enum_rust_type(&self.file_and_mod()) .to_code(&self.customize) ), |w| { w.write_line(&format!( "message.{} = ::std::option::Option::Some({}(e));", oneof.oneof_field_name, variant_path )); }, ) }); let default = self.xxx_default_value_rust(); AccessorFn { name: "make_oneof_enum_accessors".to_owned(), type_params: vec![format!("_")], callback_params: vec![getter, setter, default], } } fn accessor_fn_singular_without_flag(&self, _elem: &FieldElem) -> AccessorFn { AccessorFn { name: "make_simpler_field_accessor".to_owned(), type_params: vec![format!("_")], callback_params: self.make_accessor_fns_lambda(), } } fn accessor_fn_singular_with_flag( &self, elem: &FieldElem, _option_kind: OptionKind, ) -> AccessorFn { match elem { FieldElem::Message(m) => AccessorFn { name: "make_message_field_accessor".to_owned(), type_params: vec![format!("{}", m.rust_name_relative(&self.file_and_mod()))], callback_params: self.make_accessor_fns_lambda(), }, FieldElem::Primitive(..) | FieldElem::Enum(..) => AccessorFn { name: "make_option_accessor".to_owned(), type_params: vec!["_".to_owned()], callback_params: self.make_accessor_fns_lambda(), }, FieldElem::Group => { unreachable!("no accessor for group field"); } } } fn accessor_fn_oneof(&self, oneof: &OneofField) -> AccessorFn { let OneofField { ref elem, .. } = oneof; let reference = self .proto_field .message .scope .file_and_mod(self.customize.clone()); if let FieldElem::Enum(en) = &oneof.elem { return self.accessor_fn_oneof_enum(oneof, en); } if elem.is_copy() { return AccessorFn { name: "make_oneof_copy_has_get_set_simpler_accessors".to_owned(), type_params: vec![format!("_")], callback_params: self.make_accessor_fns_has_get_set(), }; } if let RustType::Message(name) = elem.rust_storage_elem_type(&reference) { return AccessorFn { name: "make_oneof_message_has_get_mut_set_accessor".to_owned(), type_params: vec![format!("{}", name)], callback_params: self.make_accessor_fns_has_get_mut_set(), }; } // string or bytes AccessorFn { name: "make_oneof_deref_has_get_set_simpler_accessor".to_owned(), type_params: vec![format!("_")], callback_params: self.make_accessor_fns_has_get_set(), } } fn accessor_fn(&self) -> AccessorFn { match self.kind { FieldKind::Repeated(ref repeated_field) => self.accessor_fn_repeated(repeated_field), FieldKind::Map(ref map_field) => self.accessor_fn_map(map_field), FieldKind::Singular(SingularField { ref elem, flag: SingularFieldFlag::WithoutFlag, }) => self.accessor_fn_singular_without_flag(elem), FieldKind::Singular(SingularField { ref elem, flag: SingularFieldFlag::WithFlag { option_kind, .. }, }) => self.accessor_fn_singular_with_flag(elem, option_kind), FieldKind::Oneof(ref oneof) => self.accessor_fn_oneof(oneof), } } pub fn write_push_accessor(&self, fields_var: &str, w: &mut CodeWriter) { let accessor_fn = self.accessor_fn(); w.write_line(&format!( "{}.push({}::reflect::rt::v2::{}(", fields_var, protobuf_crate_path(&self.customize), accessor_fn.sig() )); w.indented(|w| { w.write_line(&format!("\"{}\",", self.proto_field.name())); for callback in &accessor_fn.callback_params { let callback_lines: Vec<&str> = callback.lines().collect(); for (i, callback_line) in callback_lines.iter().enumerate() { let comma = if i == callback_lines.len() - 1 { "," } else { "" }; w.write_line(&format!("{}{}", callback_line, comma)); } } }); w.write_line("));"); } } protobuf-codegen-3.7.2/src/gen/field/elem.rs000064400000000000000000000265311046102023000170210ustar 00000000000000use protobuf::descriptor::field_descriptor_proto::Type; use protobuf::reflect::RuntimeFieldType; use protobuf::rt::tag_size; use protobuf_parse::ProtobufAbsPath; use crate::gen::code_writer::CodeWriter; use crate::gen::field::type_ext::TypeExt; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::RustTypeMessage; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust_types_values::message_or_enum_to_rust_relative; use crate::gen::rust_types_values::PrimitiveTypeVariant; use crate::gen::rust_types_values::RustType; use crate::gen::rust_types_values::RustValueTyped; use crate::gen::scope::EnumValueWithContext; use crate::gen::scope::FieldWithContext; use crate::gen::scope::MessageOrEnumWithScope; use crate::gen::scope::MessageWithScope; use crate::gen::scope::RootScope; use crate::Customize; #[derive(Clone, Debug)] pub(crate) struct FieldElemEnum<'a> { /// Enum default value variant, either from proto or from enum definition default_value: EnumValueWithContext<'a>, } impl<'a> FieldElemEnum<'a> { fn rust_name_relative(&self, reference: &FileAndMod) -> RustIdentWithPath { message_or_enum_to_rust_relative(&self.default_value.en, reference) } pub(crate) fn enum_rust_type(&self, reference: &FileAndMod) -> RustType { RustType::Enum( self.rust_name_relative(reference), self.default_value.rust_name(), self.default_value.proto.proto().number(), ) } fn enum_or_unknown_rust_type(&self, reference: &FileAndMod) -> RustType { RustType::EnumOrUnknown( self.rust_name_relative(reference), self.default_value.rust_name(), self.default_value.proto.proto().number(), ) } pub(crate) fn default_value_rust_expr(&self, reference: &FileAndMod) -> RustIdentWithPath { self.rust_name_relative(reference) .to_path() .with_ident(self.default_value.rust_name()) } } #[derive(Clone, Debug)] pub(crate) struct FieldElemMessage<'a> { pub message: MessageWithScope<'a>, } impl<'a> FieldElemMessage<'a> { pub(crate) fn rust_name_relative(&self, reference: &FileAndMod) -> RustTypeMessage { RustTypeMessage(message_or_enum_to_rust_relative(&self.message, reference)) } fn rust_type(&self, reference: &FileAndMod) -> RustType { RustType::Message(self.rust_name_relative(reference)) } } #[derive(Clone, Debug)] pub(crate) enum FieldElem<'a> { Primitive(Type, PrimitiveTypeVariant), Message(FieldElemMessage<'a>), Enum(FieldElemEnum<'a>), Group, } pub(crate) enum HowToGetMessageSize { Compute, GetCached, } impl<'a> FieldElem<'a> { pub(crate) fn proto_type(&self) -> Type { match *self { FieldElem::Primitive(t, ..) => t, FieldElem::Group => Type::TYPE_GROUP, FieldElem::Message(..) => Type::TYPE_MESSAGE, FieldElem::Enum(..) => Type::TYPE_ENUM, } } pub(crate) fn is_copy(&self) -> bool { self.proto_type().is_copy() } pub(crate) fn rust_storage_elem_type(&self, reference: &FileAndMod) -> RustType { match *self { FieldElem::Primitive(t, PrimitiveTypeVariant::Default) => t.rust_type(), FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => { RustType::Chars } FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => { RustType::Bytes } FieldElem::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(), FieldElem::Group => RustType::Group, FieldElem::Message(ref m) => m.rust_type(reference), FieldElem::Enum(ref en) => en.enum_or_unknown_rust_type(reference), } } // Type of set_xxx function parameter type for singular fields pub(crate) fn rust_set_xxx_param_type(&self, reference: &FileAndMod) -> RustType { if let FieldElem::Enum(ref en) = *self { en.enum_rust_type(reference) } else { self.rust_storage_elem_type(reference) } } pub(crate) fn primitive_type_variant(&self) -> PrimitiveTypeVariant { match self { &FieldElem::Primitive(_, v) => v, _ => PrimitiveTypeVariant::Default, } } pub(crate) fn singular_field_size( &self, field_number: u32, var: &RustValueTyped, customize: &Customize, ) -> String { let tag_size = tag_size(field_number); match self.proto_type().encoded_size() { Some(data_size) => format!("{tag_size} + {data_size}"), None => match self.proto_type() { Type::TYPE_MESSAGE => panic!("not a single-liner"), // We are not inlining `bytes_size` here, // assuming the compiler is smart enough to do it for us. // https://rust.godbolt.org/z/GrKa5zxq6 Type::TYPE_BYTES => format!( "{}::rt::bytes_size({}, &{})", protobuf_crate_path(customize), field_number, var.value ), Type::TYPE_STRING => format!( "{}::rt::string_size({}, &{})", protobuf_crate_path(customize), field_number, var.value ), Type::TYPE_ENUM => { format!( "{}::rt::int32_size({}, {}.value())", protobuf_crate_path(customize), field_number, var.value, ) } _ => { let param_type = match &var.rust_type { RustType::Ref(t) => (**t).clone(), t => t.clone(), }; let f = match self.proto_type() { Type::TYPE_SINT32 => "sint32_size", Type::TYPE_SINT64 => "sint64_size", Type::TYPE_INT32 => "int32_size", Type::TYPE_INT64 => "int64_size", Type::TYPE_UINT32 => "uint32_size", Type::TYPE_UINT64 => "uint64_size", t => unreachable!("unexpected type: {:?}", t), }; format!( "{}::rt::{f}({}, {})", protobuf_crate_path(customize), field_number, var.into_type(param_type, customize).value ) } }, } } pub(crate) fn write_element_size( &self, field_number: u32, item_var: &RustValueTyped, how_to_get_message_size: HowToGetMessageSize, sum_var: &str, customize: &Customize, w: &mut CodeWriter, ) { let tag_size = tag_size(field_number); match self.proto_type() { Type::TYPE_MESSAGE => { match how_to_get_message_size { HowToGetMessageSize::Compute => { w.write_line(&format!("let len = {}.compute_size();", item_var.value)) } HowToGetMessageSize::GetCached => w.write_line(&format!( "let len = {}.cached_size() as u64;", item_var.value )), } w.write_line(&format!( "{sum_var} += {tag_size} + {}::rt::compute_raw_varint64_size(len) + len;", protobuf_crate_path(customize), )); } _ => { w.write_line(&format!( "{sum_var} += {};", self.singular_field_size(field_number, item_var, customize) )); } } } pub(crate) fn write_write_element( &self, field_number: u32, v: &RustValueTyped, file_and_mod: &FileAndMod, customize: &Customize, os: &str, w: &mut CodeWriter, ) { match self.proto_type() { Type::TYPE_MESSAGE => { let param_type = RustType::Ref(Box::new(self.rust_storage_elem_type(file_and_mod))); w.write_line(&format!( "{}::rt::write_message_field_with_cached_size({}, {}, {})?;", protobuf_crate_path(customize), field_number, v.into_type(param_type, customize).value, os )); } _ => { let param_type = self.proto_type().os_write_fn_param_type(); let os_write_fn_suffix = self.proto_type().protobuf_name(); w.write_line(&format!( "{}.write_{}({}, {})?;", os, os_write_fn_suffix, field_number, v.into_type(param_type, customize).value )); } } } pub(crate) fn read_one_liner(&self) -> String { format!( "{}?", self.proto_type().read("is", self.primitive_type_variant()) ) } } pub(crate) fn field_elem<'a>( field: &FieldWithContext, root_scope: &'a RootScope<'a>, customize: &Customize, ) -> FieldElem<'a> { if let RuntimeFieldType::Map(..) = field.field.runtime_field_type() { unreachable!(); } if field.field.proto().type_() == Type::TYPE_GROUP { FieldElem::Group } else if field.field.proto().has_type_name() { let message_or_enum = root_scope .find_message_or_enum(&ProtobufAbsPath::from(field.field.proto().type_name())); match (field.field.proto().type_(), message_or_enum) { (Type::TYPE_MESSAGE, MessageOrEnumWithScope::Message(message)) => { FieldElem::Message(FieldElemMessage { message: message.clone(), }) } (Type::TYPE_ENUM, MessageOrEnumWithScope::Enum(enum_with_scope)) => { let default_value = if field.field.proto().has_default_value() { enum_with_scope.value_by_name(field.field.proto().default_value()) } else { enum_with_scope.values()[0].clone() }; FieldElem::Enum(FieldElemEnum { default_value }) } _ => panic!("unknown named type: {:?}", field.field.proto().type_()), } } else if field.field.proto().has_type() { let tokio_for_bytes = customize.tokio_bytes.unwrap_or(false); let tokio_for_string = customize.tokio_bytes_for_string.unwrap_or(false); let elem = match field.field.proto().type_() { Type::TYPE_STRING if tokio_for_string => { FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) } Type::TYPE_BYTES if tokio_for_bytes => { FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) } t => FieldElem::Primitive(t, PrimitiveTypeVariant::Default), }; elem } else { panic!( "neither type_name, nor field_type specified for field: {}", field.field.name() ); } } protobuf-codegen-3.7.2/src/gen/field/mod.rs000064400000000000000000002125121046102023000166520ustar 00000000000000mod accessor; pub(crate) mod elem; mod option_kind; mod repeated; mod singular; mod tag; pub(crate) mod type_ext; use protobuf::descriptor::field_descriptor_proto::Type; use protobuf::descriptor::*; use protobuf::reflect::ReflectValueRef; use protobuf::reflect::RuntimeFieldType; use protobuf::reflect::Syntax; use protobuf::rt; use protobuf::rt::WireType; use protobuf_parse::camel_case; use protobuf_parse::ProtobufAbsPath; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::rustproto_proto::customize_from_rustproto_for_field; use crate::customize::Customize; use crate::gen::code_writer::CodeWriter; use crate::gen::code_writer::Visibility; use crate::gen::field::elem::field_elem; use crate::gen::field::elem::FieldElem; use crate::gen::field::elem::FieldElemEnum; use crate::gen::field::elem::HowToGetMessageSize; use crate::gen::field::option_kind::OptionKind; use crate::gen::field::repeated::RepeatedField; use crate::gen::field::singular::SingularField; use crate::gen::field::singular::SingularFieldFlag; use crate::gen::field::tag::make_tag; use crate::gen::field::type_ext::TypeExt; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::map::map_entry; use crate::gen::oneof::OneofField; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_field; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::quote::quote_escape_bytes; use crate::gen::rust::quote::quote_escape_str; use crate::gen::rust::snippets::EXPR_NONE; use crate::gen::rust_types_values::PrimitiveTypeVariant; use crate::gen::rust_types_values::RustType; use crate::gen::rust_types_values::RustValueTyped; use crate::gen::scope::FieldWithContext; use crate::gen::scope::MessageWithScope; use crate::gen::scope::RootScope; fn field_type_protobuf_name<'a>(field: &'a FieldDescriptorProto) -> &'a str { if field.has_type_name() { field.type_name() } else { field.type_().protobuf_name() } } #[derive(Clone)] pub struct MapField<'a> { _message: MessageWithScope<'a>, key: FieldElem<'a>, value: FieldElem<'a>, } #[derive(Clone)] pub(crate) enum FieldKind<'a> { // optional or required Singular(SingularField<'a>), // repeated except map Repeated(RepeatedField<'a>), // map Map(MapField<'a>), // part of oneof Oneof(OneofField<'a>), } impl<'a> FieldKind<'a> { pub(crate) fn default( &self, customize: &Customize, reference: &FileAndMod, const_expr: bool, ) -> String { match self { FieldKind::Singular(s) => s.default_value(customize, reference, const_expr), FieldKind::Repeated(r) => r.default(), FieldKind::Oneof(..) => EXPR_NONE.to_owned(), FieldKind::Map(..) => panic!("map fields cannot have field value"), } } } #[derive(Clone)] pub(crate) enum SingularOrOneofField<'a> { Singular(SingularField<'a>), Oneof(OneofField<'a>), } impl<'a> SingularOrOneofField<'a> { fn elem(&self) -> &FieldElem { match self { SingularOrOneofField::Singular(SingularField { ref elem, .. }) => elem, SingularOrOneofField::Oneof(OneofField { ref elem, .. }) => elem, } } // Type of `xxx` function for singular type. pub(crate) fn getter_return_type(&self, reference: &FileAndMod) -> RustType { let elem = self.elem(); if let FieldElem::Enum(ref en) = elem { en.enum_rust_type(reference) } else if elem.is_copy() { elem.rust_storage_elem_type(reference) } else { elem.rust_storage_elem_type(reference).ref_type() } } } #[derive(Clone)] pub(crate) struct FieldGen<'a> { syntax: Syntax, pub proto_field: FieldWithContext<'a>, // field name in generated code pub rust_name: RustIdent, pub proto_type: Type, wire_type: WireType, pub kind: FieldKind<'a>, pub generate_accessors: bool, pub generate_getter: bool, customize: Customize, path: Vec, info: Option<&'a SourceCodeInfo>, } impl<'a> FieldGen<'a> { pub(crate) fn parse( field: FieldWithContext<'a>, root_scope: &'a RootScope<'a>, parent_customize: &CustomizeElemCtx<'a>, path: Vec, info: Option<&'a SourceCodeInfo>, ) -> anyhow::Result> { let customize = parent_customize .child( &customize_from_rustproto_for_field(field.field.proto().options.get_or_default()), &field.field, ) .for_elem; let syntax = field.message.scope.file_scope.syntax(); let field_may_have_custom_default_value = syntax == Syntax::Proto2 && field.field.proto().label() != field_descriptor_proto::Label::LABEL_REPEATED && field.field.proto().type_() != Type::TYPE_MESSAGE; let generate_accessors = customize .generate_accessors .unwrap_or(field_may_have_custom_default_value) || field.is_oneof(); let default_generate_getter = generate_accessors || field_may_have_custom_default_value; let generate_getter = customize.generate_getter.unwrap_or(default_generate_getter) || field.is_oneof(); let kind = match field.field.runtime_field_type() { RuntimeFieldType::Map(..) => { let message = root_scope .find_message(&ProtobufAbsPath::from(field.field.proto().type_name())); let (key, value) = map_entry(&message).unwrap(); let key = field_elem(&key, root_scope, &customize); let value = field_elem(&value, root_scope, &customize); FieldKind::Map(MapField { _message: message, key, value, }) } RuntimeFieldType::Repeated(..) => { let elem = field_elem(&field, root_scope, &customize); let primitive = match field.field.proto().type_() { Type::TYPE_DOUBLE | Type::TYPE_FLOAT | Type::TYPE_INT64 | Type::TYPE_UINT64 | Type::TYPE_INT32 | Type::TYPE_FIXED64 | Type::TYPE_FIXED32 | Type::TYPE_BOOL | Type::TYPE_UINT32 | Type::TYPE_SFIXED32 | Type::TYPE_SFIXED64 | Type::TYPE_SINT32 | Type::TYPE_SINT64 | Type::TYPE_ENUM => true, Type::TYPE_STRING | Type::TYPE_GROUP | Type::TYPE_MESSAGE | Type::TYPE_BYTES => false, }; let packed = field .field .proto() .options .get_or_default() .packed .unwrap_or(match field.message.scope.file_scope.syntax() { Syntax::Proto2 => false, // in proto3, repeated primitive types are packed by default Syntax::Proto3 => primitive, }); if packed && !primitive { anyhow::bail!( "[packed = true] can only be specified for repeated primitive fields" ); } FieldKind::Repeated(RepeatedField { elem, packed }) } RuntimeFieldType::Singular(..) => { let elem = field_elem(&field, root_scope, &customize); if let Some(oneof) = field.oneof() { FieldKind::Oneof(OneofField::parse(&oneof, &field.field, elem, root_scope)) } else { let flag = if field.message.scope.file_scope.syntax() == Syntax::Proto3 && field.field.proto().type_() != field_descriptor_proto::Type::TYPE_MESSAGE && !field.field.proto().proto3_optional() { SingularFieldFlag::WithoutFlag } else { let required = field.field.proto().label() == field_descriptor_proto::Label::LABEL_REQUIRED; let option_kind = match field.field.proto().type_() { field_descriptor_proto::Type::TYPE_MESSAGE => OptionKind::MessageField, _ => OptionKind::Option, }; SingularFieldFlag::WithFlag { required, option_kind, } }; FieldKind::Singular(SingularField { elem, flag }) } } }; Ok(FieldGen { syntax: field.message.message.file_descriptor().syntax(), rust_name: rust_field_name_for_protobuf_field_name(&field.field.name()), proto_type: field.field.proto().type_(), wire_type: WireType::for_type(field.field.proto().type_()), proto_field: field, kind, generate_accessors, generate_getter, customize, path, info, }) } // for message level fn file_and_mod(&self) -> FileAndMod { self.proto_field .message .scope .file_and_mod(self.customize.clone()) } fn tag_size(&self) -> u32 { rt::tag_size(self.proto_field.number() as u32) as u32 } fn is_singular(&self) -> bool { match self.kind { FieldKind::Singular(..) => true, _ => false, } } fn is_repeated_packed(&self) -> bool { match self.kind { FieldKind::Repeated(RepeatedField { packed: true, .. }) => true, _ => false, } } pub(crate) fn elem(&self) -> &FieldElem { match self.kind { FieldKind::Singular(SingularField { ref elem, .. }) => &elem, FieldKind::Repeated(RepeatedField { ref elem, .. }) => &elem, FieldKind::Oneof(OneofField { ref elem, .. }) => &elem, FieldKind::Map(..) => unreachable!(), } } // type of field in struct pub(crate) fn full_storage_type(&self, reference: &FileAndMod) -> RustType { match self.kind { FieldKind::Repeated(ref repeated) => repeated.rust_type(reference), FieldKind::Map(MapField { ref key, ref value, .. }) if self.customize.btreemap == Some(true) => RustType::BTreeMap( Box::new(key.rust_storage_elem_type(reference)), Box::new(value.rust_storage_elem_type(reference)), ), FieldKind::Map(MapField { ref key, ref value, .. }) => RustType::HashMap( Box::new(key.rust_storage_elem_type(reference)), Box::new(value.rust_storage_elem_type(reference)), ), FieldKind::Singular(ref singular) => singular.rust_storage_type(reference), FieldKind::Oneof(..) => unreachable!(), } } // type of `v` in `for v in field` fn full_storage_iter_elem_type(&self, reference: &FileAndMod) -> RustType { if let FieldKind::Oneof(ref oneof) = self.kind { oneof.elem.rust_storage_elem_type(reference) } else { self.full_storage_type(reference).iter_elem_type() } } // suffix `xxx` as in `os.write_xxx_no_tag(..)` fn os_write_fn_suffix(&self) -> &str { self.proto_type.protobuf_name() } fn os_write_fn_suffix_with_unknown_for_enum(&self) -> &str { if self.proto_type == field_descriptor_proto::Type::TYPE_ENUM { "enum_or_unknown" } else { self.os_write_fn_suffix() } } // for field `foo`, type of param of `fn set_foo(..)` fn set_xxx_param_type(&self, reference: &FileAndMod) -> RustType { match self.kind { FieldKind::Singular(SingularField { ref elem, .. }) | FieldKind::Oneof(OneofField { ref elem, .. }) => { elem.rust_set_xxx_param_type(reference) } FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference), } } // for field `foo`, return type if `fn take_foo(..)` fn take_xxx_return_type(&self, reference: &FileAndMod) -> RustType { self.set_xxx_param_type(reference) } // for field `foo`, return type of `fn mut_foo(..)` fn mut_xxx_return_type(&self, reference: &FileAndMod) -> RustType { RustType::Ref(Box::new(match self.kind { FieldKind::Singular(SingularField { ref elem, .. }) | FieldKind::Oneof(OneofField { ref elem, .. }) => { elem.rust_storage_elem_type(reference) } FieldKind::Repeated(..) | FieldKind::Map(..) => self.full_storage_type(reference), })) } // for field `foo`, return type of `fn foo(..)` fn getter_return_type(&self) -> RustType { let reference = self .proto_field .message .scope .file_and_mod(self.customize.clone()); match &self.kind { FieldKind::Singular(s) => { SingularOrOneofField::Singular(s.clone()).getter_return_type(&reference) } FieldKind::Oneof(o) => { SingularOrOneofField::Oneof(o.clone()).getter_return_type(&reference) } FieldKind::Repeated(RepeatedField { ref elem, .. }) => RustType::Ref(Box::new( RustType::Slice(Box::new(elem.rust_storage_elem_type(&reference))), )), FieldKind::Map(..) => RustType::Ref(Box::new(self.full_storage_type(&reference))), } } // elem data is not stored in heap pub(crate) fn elem_type_is_copy(&self) -> bool { self.proto_type.is_copy() } fn defaut_value_from_proto_float(f: f64, type_name: &str) -> String { if f.is_nan() { format!("::std::{}::NAN", type_name) } else if f.is_infinite() { if f > 0.0 { format!("::std::{}::INFINITY", type_name) } else { format!("::std::{}::NEG_INFINITY", type_name) } } else { format!("{:?}{}", f, type_name) } } fn singular_or_oneof_default_value_from_proto(&self, elem: &FieldElem) -> Option { if !self.proto_field.field.proto().has_default_value() { return None; } let default_value = self.proto_field.field.singular_default_value(); Some(match default_value { ReflectValueRef::Bool(b) => format!("{}", b), ReflectValueRef::I32(v) => format!("{}i32", v), ReflectValueRef::I64(v) => format!("{}i64", v), ReflectValueRef::U32(v) => format!("{}u32", v), ReflectValueRef::U64(v) => format!("{}u64", v), ReflectValueRef::String(v) => quote_escape_str(v), ReflectValueRef::Bytes(v) => quote_escape_bytes(v), ReflectValueRef::F32(v) => Self::defaut_value_from_proto_float(v as f64, "f32"), ReflectValueRef::F64(v) => Self::defaut_value_from_proto_float(v as f64, "f64"), ReflectValueRef::Enum(_e, _v) => { if let &FieldElem::Enum(ref e) = elem { format!("{}", e.default_value_rust_expr(&self.file_and_mod())) } else { unreachable!() } } t => panic!("default value is not implemented for type: {:?}", t), }) } fn default_value_from_proto(&self) -> Option { match self.kind { FieldKind::Oneof(OneofField { ref elem, .. }) | FieldKind::Singular(SingularField { ref elem, .. }) => { self.singular_or_oneof_default_value_from_proto(elem) } _ => unreachable!(), } } fn default_value_from_proto_typed(&self) -> Option { self.default_value_from_proto().map(|v| { let default_value_type = match self.proto_type { field_descriptor_proto::Type::TYPE_STRING => RustType::Ref(Box::new(RustType::Str)), field_descriptor_proto::Type::TYPE_BYTES => { RustType::Ref(Box::new(RustType::Slice(Box::new(RustType::u8())))) } _ => self.full_storage_iter_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), }; RustValueTyped { value: v, rust_type: default_value_type, } }) } // default value to be returned from `fn xxx` for field `xxx`. fn xxx_default_value_rust(&self) -> String { match self.kind { FieldKind::Singular(..) | FieldKind::Oneof(..) => { self.default_value_from_proto().unwrap_or_else(|| { self.getter_return_type() .default_value(&self.customize, false) }) } _ => unreachable!(), } } // default to be assigned to field fn element_default_value_rust(&self) -> RustValueTyped { match self.kind { FieldKind::Singular(..) | FieldKind::Oneof(..) => { self.default_value_from_proto_typed().unwrap_or_else(|| { self.elem() .rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .default_value_typed(&self.customize, false) }) } _ => unreachable!(), } } pub(crate) fn reconstruct_def(&self) -> String { let prefix = match (self.proto_field.field.proto().label(), self.syntax) { (field_descriptor_proto::Label::LABEL_REPEATED, _) => "repeated ", (_, Syntax::Proto3) => "", (field_descriptor_proto::Label::LABEL_OPTIONAL, _) => "optional ", (field_descriptor_proto::Label::LABEL_REQUIRED, _) => "required ", }; format!( "{}{} {} = {}", prefix, field_type_protobuf_name(self.proto_field.field.proto()), self.proto_field.name(), self.proto_field.number() ) } pub(crate) fn write_clear(&self, w: &mut CodeWriter) { match self.kind { FieldKind::Oneof(ref o) => { w.write_line(&format!( "self.{} = ::std::option::Option::None;", o.oneof_field_name )); } _ => { let clear_expr = self .full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .clear(&self.self_field(), &self.customize); w.write_line(&format!("{};", clear_expr)); } } } // output code that writes single element to stream pub(crate) fn write_write_element( &self, elem: &FieldElem, w: &mut CodeWriter, os: &str, v: &RustValueTyped, ) { assert!(!self.is_repeated_packed()); elem.write_write_element( self.proto_field.number() as u32, v, &self.file_and_mod(), &self.customize, os, w, ); } fn self_field(&self) -> String { format!("self.{}", self.rust_name) } fn self_field_is_some(&self) -> String { assert!(self.is_singular()); format!("{}.is_some()", self.self_field()) } fn self_field_is_none(&self) -> String { assert!(self.is_singular()); format!("{}.is_none()", self.self_field()) } // field data viewed as Option fn self_field_as_option(&self, elem: &FieldElem, option_kind: OptionKind) -> RustValueTyped { match self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) { RustType::Option(ref e) if e.is_copy() => { return RustType::Option(e.clone()).value(self.self_field()); } _ => {} }; let as_option_type = option_kind.as_ref_type( elem.rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), ); as_option_type.value(format!("{}.as_ref()", self.self_field())) } pub(crate) fn write_struct_field(&self, w: &mut CodeWriter) { if self.proto_type == field_descriptor_proto::Type::TYPE_GROUP { w.comment(&format!("{}: ", &self.rust_name)); } else { w.all_documentation(self.info, &self.path); write_protoc_insertion_point_for_field(w, &self.customize, &self.proto_field.field); w.field_decl_vis( Visibility::Public, &self.rust_name.to_string(), &self .full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .to_code(&self.customize), ); } } fn write_if_let_self_field_is_some(&self, s: &SingularField, w: &mut CodeWriter, cb: F) where F: Fn(&RustValueTyped, &mut CodeWriter), { match s { SingularField { flag: SingularFieldFlag::WithFlag { option_kind, .. }, ref elem, } => { let var = "v"; let ref_prefix = match elem .rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .is_copy() { true => "", false => "", }; let as_option = self.self_field_as_option(elem, *option_kind); w.if_let_stmt( &format!("Some({}{})", ref_prefix, var), &as_option.value, |w| { let v = RustValueTyped { value: var.to_owned(), rust_type: as_option.rust_type.elem_type(), }; cb(&v, w); }, ); } SingularField { flag: SingularFieldFlag::WithoutFlag, ref elem, } => match *elem { FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..) | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => { w.if_stmt(format!("!{}.is_empty()", self.self_field()), |w| { let v = RustValueTyped { value: self.self_field(), rust_type: self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), }; cb(&v, w); }); } _ => { w.if_stmt( format!( "{} != {}", self.self_field(), self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()) ) .default_value(&self.customize, false) ), |w| { let v = RustValueTyped { value: self.self_field(), rust_type: self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), }; cb(&v, w); }, ); } }, } } pub(crate) fn write_if_self_field_is_none(&self, w: &mut CodeWriter, cb: F) where F: Fn(&mut CodeWriter), { let self_field_is_none = self.self_field_is_none(); w.if_stmt(self_field_is_none, cb) } // repeated or singular pub(crate) fn write_for_self_field(&self, w: &mut CodeWriter, varn: &str, cb: F) where F: Fn(&mut CodeWriter, &RustType), { let file_and_mod = self .proto_field .message .scope .file_and_mod(self.customize.clone()); match &self.kind { FieldKind::Oneof(oneof_field) => { let cond = format!( "Some({}(ref {}))", oneof_field.variant_path(&file_and_mod.relative_mod), varn ); w.if_let_stmt( &cond, &format!("self.{}", oneof_field.oneof_field_name), |w| cb(w, &oneof_field.elem.rust_storage_elem_type(&file_and_mod)), ) } _ => { let v_type = self.full_storage_iter_elem_type(&file_and_mod); let self_field = self.self_field(); w.for_stmt(&format!("&{}", self_field), varn, |w| cb(w, &v_type)); } } } fn write_self_field_assign(&self, w: &mut CodeWriter, value: &str) { let self_field = self.self_field(); w.write_line(&format!("{} = {};", self_field, value)); } fn write_self_field_assign_some(&self, w: &mut CodeWriter, s: &SingularField, value: &str) { match s { &SingularField { flag: SingularFieldFlag::WithFlag { option_kind, .. }, .. } => { self.write_self_field_assign(w, &option_kind.wrap_value(value, &self.customize)); } &SingularField { flag: SingularFieldFlag::WithoutFlag, .. } => { self.write_self_field_assign(w, value); } } } fn write_self_field_assign_value_singular( &self, w: &mut CodeWriter, s: &SingularField, value: &RustValueTyped, ) { let SingularField { ref elem, ref flag } = s; let converted = value.into_type( elem.rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .clone(), &self.customize, ); let wrapped = match flag { SingularFieldFlag::WithoutFlag => converted.value, SingularFieldFlag::WithFlag { option_kind, .. } => { option_kind.wrap_value(&converted.value, &self.customize) } }; self.write_self_field_assign(w, &wrapped); } fn write_self_field_assign_value(&self, w: &mut CodeWriter, value: &RustValueTyped) { match self.kind { FieldKind::Repeated(..) | FieldKind::Map(..) => { let converted = value.into_type( self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), &self.customize, ); self.write_self_field_assign(w, &converted.value); } FieldKind::Singular(ref s) => { self.write_self_field_assign_value_singular(w, s, value); } FieldKind::Oneof(..) => unreachable!(), } } fn write_self_field_assign_default( &self, field_kind: &SingularOrOneofField, w: &mut CodeWriter, ) { match field_kind { SingularOrOneofField::Oneof(oneof) => { w.write_line(format!( "self.{} = ::std::option::Option::Some({}({}))", oneof.oneof_field_name, oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()), // TODO: default from .proto is not needed here (?) self.element_default_value_rust() .into_type( self.full_storage_iter_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()) ), &self.customize ) .value )); } SingularOrOneofField::Singular(singular) => { // Note it is different from C++ protobuf, where field is initialized // with default value match singular.flag { SingularFieldFlag::WithFlag { option_kind, .. } => match option_kind { OptionKind::MessageField => { let self_field = self.self_field(); w.write_line(&format!("{}.set_default();", self_field)); } _ => { self.write_self_field_assign_some( w, singular, &self .elem() .rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .default_value_typed(&self.customize, false) .into_type( singular.elem.rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), &self.customize, ) .value, ); } }, SingularFieldFlag::WithoutFlag => unimplemented!(), } } } } fn self_field_vec_packed_size(&self) -> String { let fn_name = match self.proto_type { Type::TYPE_ENUM => "vec_packed_enum_or_unknown_size", Type::TYPE_SINT32 => "vec_packed_sint32_size", Type::TYPE_SINT64 => "vec_packed_sint64_size", Type::TYPE_INT32 => "vec_packed_int32_size", Type::TYPE_INT64 => "vec_packed_int64_size", Type::TYPE_UINT32 => "vec_packed_uint32_size", Type::TYPE_UINT64 => "vec_packed_uint64_size", Type::TYPE_BOOL => "vec_packed_bool_size", Type::TYPE_FIXED32 => "vec_packed_fixed32_size", Type::TYPE_FIXED64 => "vec_packed_fixed64_size", Type::TYPE_SFIXED32 => "vec_packed_sfixed32_size", Type::TYPE_SFIXED64 => "vec_packed_sfixed64_size", Type::TYPE_FLOAT => "vec_packed_float_size", Type::TYPE_DOUBLE => "vec_packed_double_size", t => unreachable!("{:?}", t), }; format!( "{}::rt::{fn_name}({}, &{})", protobuf_crate_path(&self.customize), self.proto_field.number(), self.self_field() ) } pub(crate) fn clear_field_func(&self) -> String { format!("clear_{}", self.rust_name) } fn write_merge_from_field_message_string_bytes_repeated( &self, r: &RepeatedField, w: &mut CodeWriter, ) { let read_fn = match &r.elem { FieldElem::Message(..) => "read_message", FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::Default) => "read_string", FieldElem::Primitive(Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => { "read_tokio_chars" } FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::Default) => "read_bytes", FieldElem::Primitive(Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => { "read_tokio_bytes" } _ => unreachable!("for field {}", self.proto_field.field), }; w.write_line(&format!("self.{}.push(is.{}()?);", self.rust_name, read_fn,)); } fn tag_with_wire_type(&self, wire_type: WireType) -> u32 { make_tag(self.proto_field.number() as u32, wire_type) } fn tag(&self) -> u32 { self.tag_with_wire_type(self.wire_type) } // Write `merge_from` part for this oneof field fn write_merge_from_oneof_case_block(&self, o: &OneofField, w: &mut CodeWriter) { w.case_block(&format!("{}", self.tag()), |w| { let typed = RustValueTyped { value: format!( "{}?", self.proto_type.read("is", o.elem.primitive_type_variant()) ), rust_type: self.full_storage_iter_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), }; let maybe_boxed = if o.boxed { typed.boxed(&self.customize) } else { typed }; w.write_line(&format!( "self.{} = ::std::option::Option::Some({}({}));", o.oneof_field_name, o.variant_path(&self.proto_field.message.scope.rust_path_to_file()), maybe_boxed.value )); }) } // Write `merge_from` part for this map field fn write_merge_from_map_case_block(&self, map: &MapField, w: &mut CodeWriter) { let MapField { key, value, .. } = map; w.case_block(&format!("{}", self.tag()), |w| { w.write_line(&format!("let len = is.read_raw_varint32()?;",)); w.write_line(&format!("let old_limit = is.push_limit(len as u64)?;")); w.write_line(&format!( "let mut key = ::std::default::Default::default();" )); w.write_line(&format!( "let mut value = ::std::default::Default::default();" )); w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| { w.match_block("tag", |w| { let key_tag = make_tag(1, WireType::for_type(key.proto_type())); let value_tag = make_tag(2, WireType::for_type(value.proto_type())); w.case_expr( &format!("{key_tag}"), &format!("key = {read}", read = key.read_one_liner()), ); w.case_expr( &format!("{value_tag}"), &format!("value = {read}", read = value.read_one_liner()), ); w.case_expr( "_", &format!( "{protobuf_crate}::rt::skip_field_for_tag(tag, is)?", protobuf_crate = protobuf_crate_path(&self.customize) ), ); }); }); w.write_line(&format!("is.pop_limit(old_limit);")); w.write_line(&format!( "{field}.insert(key, value);", field = self.self_field() )); }); } // Write `merge_from` part for this singular field fn write_merge_from_singular_case_block(&self, s: &SingularField, w: &mut CodeWriter) { w.case_block(&format!("{}", self.tag()), |w| match s.elem { FieldElem::Message(..) => { w.write_line(&format!( "{}::rt::read_singular_message_into_field(is, &mut self.{})?;", protobuf_crate_path(&self.customize), self.rust_name, )); } _ => { let read_proc = s.elem.read_one_liner(); self.write_self_field_assign_some(w, s, &read_proc); } }) } // Write `merge_from` part for this repeated field fn write_merge_from_repeated_case_block(&self, w: &mut CodeWriter) { let field = match self.kind { FieldKind::Repeated(ref field) => field, _ => panic!(), }; match field.elem { FieldElem::Message(..) | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_STRING, ..) | FieldElem::Primitive(field_descriptor_proto::Type::TYPE_BYTES, ..) => { w.case_block(&format!("{}", self.tag()), |w| { self.write_merge_from_field_message_string_bytes_repeated(field, w); }) } FieldElem::Enum(..) => { w.case_block( &format!("{}", self.tag_with_wire_type(WireType::Varint)), |w| { w.write_line(&format!( "self.{}.push(is.read_enum_or_unknown()?);", self.rust_name, )); }, ); w.case_block( &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)), |w| { w.write_line(&format!( "{}::rt::read_repeated_packed_enum_or_unknown_into(is, &mut self.{})?", protobuf_crate_path(&self.customize), self.rust_name, )); }, ); } _ => { assert_ne!(self.wire_type, WireType::LengthDelimited); w.case_block( &format!("{}", self.tag_with_wire_type(WireType::LengthDelimited)), |w| { w.write_line(&format!( "is.read_repeated_packed_{}_into(&mut self.{})?;", self.proto_type.protobuf_name(), self.rust_name )); }, ); w.case_block(&format!("{}", self.tag()), |w| { w.write_line(&format!( "self.{}.push(is.read_{}()?);", self.rust_name, self.proto_type.protobuf_name(), )); }); } } } /// Write `merge_from` part for this field pub(crate) fn write_merge_from_field_case_block(&self, w: &mut CodeWriter) { match &self.kind { FieldKind::Oneof(oneof) => self.write_merge_from_oneof_case_block(oneof, w), FieldKind::Map(map) => self.write_merge_from_map_case_block(map, w), FieldKind::Singular(ref s) => self.write_merge_from_singular_case_block(s, w), FieldKind::Repeated(..) => self.write_merge_from_repeated_case_block(w), } } pub(crate) fn write_element_size( &self, elem: &FieldElem, w: &mut CodeWriter, item_var: &RustValueTyped, sum_var: &str, ) { assert!(!self.is_repeated_packed()); elem.write_element_size( self.proto_field.number() as u32, item_var, HowToGetMessageSize::Compute, sum_var, &self.customize, w, ); } fn write_write_map_field( &self, key: &FieldElem, value: &FieldElem, os: &str, w: &mut CodeWriter, ) { self.for_each_map_entry(key, value, w, |k, v, w| { w.write_line("let mut entry_size = 0;"); key.write_element_size( 1, k, HowToGetMessageSize::GetCached, "entry_size", &self.customize, w, ); value.write_element_size( 2, v, HowToGetMessageSize::GetCached, "entry_size", &self.customize, w, ); w.write_line(&format!( "{os}.write_raw_varint32({tag})?; // Tag.", tag = make_tag(self.proto_field.number() as u32, WireType::LengthDelimited), )); w.write_line(&format!("{os}.write_raw_varint32(entry_size as u32)?;",)); key.write_write_element(1, k, &self.file_and_mod(), &self.customize, os, w); value.write_write_element(2, v, &self.file_and_mod(), &self.customize, os, w); }); } pub(crate) fn write_message_write_field(&self, os: &str, w: &mut CodeWriter) { match &self.kind { FieldKind::Singular(s @ SingularField { elem, .. }) => { self.write_if_let_self_field_is_some(s, w, |v, w| { self.write_write_element(&elem, w, os, &v); }); } FieldKind::Repeated(RepeatedField { packed: false, elem, .. }) => { self.write_for_self_field(w, "v", |w, v_type| { let v = RustValueTyped { value: "v".to_owned(), rust_type: v_type.clone(), }; self.write_write_element(elem, w, "os", &v); }); } FieldKind::Repeated(RepeatedField { packed: true, .. }) => { w.write_line(&format!( "os.write_repeated_packed_{}({}, &{})?;", self.os_write_fn_suffix_with_unknown_for_enum(), self.proto_field.number(), self.self_field() )); } FieldKind::Map(MapField { key, value, .. }) => { self.write_write_map_field(key, value, os, w) } FieldKind::Oneof(..) => unreachable!(), }; } fn for_each_map_entry( &self, key: &FieldElem, value: &FieldElem, w: &mut CodeWriter, cb: impl FnOnce(&RustValueTyped, &RustValueTyped, &mut CodeWriter), ) { w.for_stmt(&format!("&{}", self.self_field()), "(k, v)", move |w| { let k = RustValueTyped { value: "k".to_owned(), rust_type: key.rust_storage_elem_type(&self.file_and_mod()).wrap_ref(), }; let v = RustValueTyped { value: "v".to_owned(), rust_type: value .rust_storage_elem_type(&self.file_and_mod()) .wrap_ref(), }; cb(&k, &v, w) }); } fn write_compute_map_field_size( &self, sum_var: &str, key: &FieldElem<'a>, value: &FieldElem<'a>, w: &mut CodeWriter, ) { self.for_each_map_entry(key, value, w, |k, v, w| { w.write_line("let mut entry_size = 0;"); key.write_element_size(1, k, HowToGetMessageSize::Compute, "entry_size", &self.customize, w); value.write_element_size(2, v, HowToGetMessageSize::Compute, "entry_size", &self.customize, w); w.write_line(&format!("{sum_var} += {tag_size} + {protobuf_crate}::rt::compute_raw_varint64_size(entry_size) + entry_size", tag_size = self.tag_size(), protobuf_crate = protobuf_crate_path(&self.customize), )); }); } pub(crate) fn write_message_compute_field_size(&self, sum_var: &str, w: &mut CodeWriter) { match &self.kind { FieldKind::Singular(s @ SingularField { elem, .. }) => { self.write_if_let_self_field_is_some(s, w, |v, w| { self.write_element_size(&elem, w, v, sum_var) }); } FieldKind::Repeated(RepeatedField { packed: false, elem, .. }) => { match elem.proto_type().encoded_size() { Some(s) => { let tag_size = self.tag_size(); let self_field = self.self_field(); w.write_line(&format!( "{} += {} * {}.len() as u64;", sum_var, (s + tag_size) as isize, self_field )); } None => { self.write_for_self_field(w, "value", |w, value_type| { self.write_element_size( elem, w, &RustValueTyped { value: "value".to_owned(), rust_type: value_type.clone(), }, sum_var, ); }); } }; } FieldKind::Repeated(RepeatedField { packed: true, .. }) => { let size_expr = self.self_field_vec_packed_size(); w.write_line(&format!("{} += {};", sum_var, size_expr)); } FieldKind::Map(MapField { key, value, .. }) => { self.write_compute_map_field_size(sum_var, key, value, w) } FieldKind::Oneof(..) => unreachable!(), } } fn write_message_field_get_singular_message(&self, s: &SingularField, w: &mut CodeWriter) { match s.flag { SingularFieldFlag::WithoutFlag => unimplemented!(), SingularFieldFlag::WithFlag { option_kind, .. } => { let self_field = self.self_field(); let ref field_type_name = self.elem().rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); w.write_line(option_kind.unwrap_ref_or_else( &format!("{}.as_ref()", self_field), &format!( "<{} as {}::Message>::default_instance()", field_type_name.to_code(&self.customize), protobuf_crate_path(&self.customize), ), )); } } } fn write_message_field_get_singular_enum( &self, flag: SingularFieldFlag, _elem: &FieldElemEnum, w: &mut CodeWriter, ) { match flag { SingularFieldFlag::WithoutFlag => { w.write_line(&format!("self.{}.enum_value_or_default()", self.rust_name)); } SingularFieldFlag::WithFlag { .. } => { w.match_expr(&self.self_field(), |w| { let default_value = self.xxx_default_value_rust(); w.case_expr("Some(e)", &format!("e.enum_value_or({})", default_value)); w.case_expr("None", &format!("{}", default_value)); }); } } } fn write_message_field_get_singular(&self, singular: &SingularField, w: &mut CodeWriter) { let get_xxx_return_type = self.getter_return_type(); match singular.elem { FieldElem::Message(..) => self.write_message_field_get_singular_message(singular, w), FieldElem::Enum(ref en) => { self.write_message_field_get_singular_enum(singular.flag, en, w) } _ => { let get_xxx_default_value_rust = self.xxx_default_value_rust(); let self_field = self.self_field(); match singular { &SingularField { ref elem, flag: SingularFieldFlag::WithFlag { option_kind, .. }, .. } => { if get_xxx_return_type.is_ref().is_some() { let as_option = self.self_field_as_option(elem, option_kind); w.match_expr(&as_option.value, |w| { let v_type = as_option.rust_type.elem_type(); let r_type = self.getter_return_type(); w.case_expr( "Some(v)", v_type.into_target(&r_type, "v", &self.customize), ); let get_xxx_default_value_rust = self.xxx_default_value_rust(); w.case_expr("None", get_xxx_default_value_rust); }); } else { w.write_line(&format!( "{}.unwrap_or({})", self_field, get_xxx_default_value_rust )); } } &SingularField { flag: SingularFieldFlag::WithoutFlag, .. } => { w.write_line( self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .into_target( &get_xxx_return_type, &self_field, &self.customize, ), ); } } } } } fn write_message_field_get_oneof(&self, o: &OneofField, w: &mut CodeWriter) { let get_xxx_return_type = SingularOrOneofField::Oneof(o.clone()).getter_return_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); let OneofField { ref elem, .. } = o; w.match_expr(&format!("self.{}", o.oneof_field_name), |w| { let (refv, vtype) = if !elem.is_copy() { ( "ref v", elem.rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .ref_type(), ) } else { ( "v", elem.rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), ) }; w.case_expr( format!( "::std::option::Option::Some({}({}))", o.variant_path(&self.proto_field.message.scope.rust_path_to_file()), refv ), vtype.into_target(&get_xxx_return_type, "v", &self.customize), ); w.case_expr("_", self.xxx_default_value_rust()); }); } fn write_message_field_get(&self, w: &mut CodeWriter) { let get_xxx_return_type = self.getter_return_type(); let fn_def = format!( "{}(&self) -> {}", self.rust_name, get_xxx_return_type.to_code(&self.customize) ); w.pub_fn(&fn_def, |w| match self.kind { FieldKind::Oneof(ref o) => { self.write_message_field_get_oneof(o, w); } FieldKind::Singular(ref s) => { self.write_message_field_get_singular(s, w); } FieldKind::Repeated(..) | FieldKind::Map(..) => { let self_field = self.self_field(); w.write_line(&format!("&{}", self_field)); } }); } fn has_has(&self) -> bool { match self.kind { FieldKind::Repeated(..) | FieldKind::Map(..) => false, FieldKind::Singular(SingularField { flag: SingularFieldFlag::WithFlag { .. }, .. }) => true, FieldKind::Singular(SingularField { flag: SingularFieldFlag::WithoutFlag, .. }) => false, FieldKind::Oneof(..) => true, } } fn has_mut(&self) -> bool { match self.kind { FieldKind::Repeated(..) | FieldKind::Map(..) => true, // TODO: string should be public, and mut is not needed FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(), } } fn has_take(&self) -> bool { match self.kind { FieldKind::Repeated(..) | FieldKind::Map(..) => true, // TODO: string should be public, and mut is not needed FieldKind::Singular(..) | FieldKind::Oneof(..) => !self.elem_type_is_copy(), } } fn has_name(&self) -> RustIdent { RustIdent::new(&format!("has_{}", self.rust_name.get())) } fn set_name(&self) -> RustIdent { RustIdent::new(&format!("set_{}", self.rust_name.get())) } fn mut_name(&self) -> RustIdent { RustIdent::new(&format!("mut_{}", self.rust_name.get())) } fn write_message_field_has(&self, w: &mut CodeWriter) { w.pub_fn( &format!("{}(&self) -> bool", self.has_name()), |w| match self.kind { FieldKind::Oneof(ref oneof) => { w.match_expr(&format!("self.{}", oneof.oneof_field_name), |w| { w.case_expr( format!( "::std::option::Option::Some({}(..))", oneof.variant_path( &self.proto_field.message.scope.rust_path_to_file() ) ), "true", ); w.case_expr("_", "false"); }); } _ => { let self_field_is_some = self.self_field_is_some(); w.write_line(self_field_is_some); } }, ); } fn write_message_field_set(&self, w: &mut CodeWriter) { let set_xxx_param_type = self.set_xxx_param_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); w.comment("Param is passed by value, moved"); w.pub_fn( &format!( "{}(&mut self, v: {})", self.set_name(), set_xxx_param_type.to_code(&self.customize) ), |w| { let value_typed = RustValueTyped { value: "v".to_owned(), rust_type: set_xxx_param_type.clone(), }; match self.kind { FieldKind::Oneof(ref oneof) => { let v = set_xxx_param_type.into_target( &oneof.rust_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), "v", &self.customize, ); w.write_line(&format!( "self.{} = ::std::option::Option::Some({}({}))", oneof.oneof_field_name, oneof.variant_path(&self.proto_field.message.scope.rust_path_to_file()), v )); } _ => { self.write_self_field_assign_value(w, &value_typed); } } }, ); } fn write_message_field_mut_singular_with_flag( &self, s: &SingularField, option_kind: OptionKind, w: &mut CodeWriter, ) { let self_field = self.self_field(); match option_kind { OptionKind::MessageField => { w.write_line(&format!("{}.mut_or_insert_default()", self_field)) } OptionKind::Option => { self.write_if_self_field_is_none(w, |w| { self.write_self_field_assign_default( &SingularOrOneofField::Singular(s.clone()), w, ); }); w.write_line(&format!("{}.as_mut().unwrap()", self_field)); } } } fn write_message_field_mut_singular(&self, s: &SingularField, w: &mut CodeWriter) { match s { s @ SingularField { flag: SingularFieldFlag::WithFlag { option_kind, .. }, .. } => self.write_message_field_mut_singular_with_flag(s, *option_kind, w), SingularField { flag: SingularFieldFlag::WithoutFlag, .. } => w.write_line(&format!("&mut {}", self.self_field())), } } fn write_message_field_mut(&self, w: &mut CodeWriter) { let mut_xxx_return_type = self.mut_xxx_return_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); w.comment("Mutable pointer to the field."); if self.is_singular() { w.comment("If field is not initialized, it is initialized with default value first."); } let fn_def = match mut_xxx_return_type { RustType::Ref(ref param) => format!( "{}(&mut self) -> &mut {}", self.mut_name(), param.to_code(&self.customize) ), _ => panic!( "not a ref: {}", mut_xxx_return_type.to_code(&self.customize) ), }; w.pub_fn(&fn_def, |w| { match self.kind { FieldKind::Repeated(..) | FieldKind::Map(..) => { let self_field = self.self_field(); w.write_line(&format!("&mut {}", self_field)); } FieldKind::Singular(ref s) => { self.write_message_field_mut_singular(s, w); } FieldKind::Oneof(ref o) => { let self_field_oneof = format!("self.{}", o.oneof_field_name); // if oneof does not contain current field w.if_let_else_stmt( &format!( "::std::option::Option::Some({}(_))", o.variant_path(&self.proto_field.message.scope.rust_path_to_file()) )[..], &self_field_oneof[..], |w| { // initialize it with default value w.write_line(&format!( "{} = ::std::option::Option::Some({}({}));", self_field_oneof, o.variant_path(&self.proto_field.message.scope.rust_path_to_file()), self.element_default_value_rust() .into_type( o.rust_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()) ), &self.customize ) .value )); }, ); // extract field w.match_expr(self_field_oneof, |w| { w.case_expr( format!( "::std::option::Option::Some({}(ref mut v))", o.variant_path(&self.proto_field.message.scope.rust_path_to_file()) ), "v", ); w.case_expr("_", "panic!()"); }); } } }); } fn write_message_field_take_oneof(&self, o: &OneofField, w: &mut CodeWriter) { let take_xxx_return_type = self.take_xxx_return_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); // TODO: replace with if let w.write_line(&format!("if self.{}() {{", self.has_name())); w.indented(|w| { let self_field_oneof = format!("self.{}", o.oneof_field_name); w.match_expr(format!("{}.take()", self_field_oneof), |w| { let value_in_some = o .rust_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .value("v".to_owned()); let converted = value_in_some.into_type( self.take_xxx_return_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ), &self.customize, ); w.case_expr( format!( "::std::option::Option::Some({}(v))", o.variant_path(&self.proto_field.message.scope.rust_path_to_file()) ), &converted.value, ); w.case_expr("_", "panic!()"); }); }); w.write_line("} else {"); w.indented(|w| { w.write_line( self.elem() .rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .default_value_typed(&self.customize, false) .into_type(take_xxx_return_type.clone(), &self.customize) .value, ); }); w.write_line("}"); } fn write_message_field_take_singular(&self, s: &SingularField, w: &mut CodeWriter) { match s { SingularField { ref elem, flag: SingularFieldFlag::WithFlag { option_kind, .. }, } => { if !elem.is_copy() { w.write_line( &option_kind.unwrap_or_else( &format!("{}.take()", self.self_field()), &elem .rust_storage_elem_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ) .default_value(&self.customize, false), ), ); } else { w.write_line(&format!( "{}.take().unwrap_or({})", self.self_field(), self.element_default_value_rust().value )); } } SingularField { flag: SingularFieldFlag::WithoutFlag, .. } => w.write_line(&format!( "::std::mem::replace(&mut {}, {})", self.self_field(), self.full_storage_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()) ) .default_value(&self.customize, false) )), } } fn write_message_field_take(&self, w: &mut CodeWriter) { let take_xxx_return_type = self.take_xxx_return_type( &self .proto_field .message .scope .file_and_mod(self.customize.clone()), ); w.comment("Take field"); w.pub_fn( &format!( "take_{}(&mut self) -> {}", self.rust_name, take_xxx_return_type.to_code(&self.customize) ), |w| match self.kind { FieldKind::Singular(ref s) => self.write_message_field_take_singular(&s, w), FieldKind::Oneof(ref o) => self.write_message_field_take_oneof(o, w), FieldKind::Repeated(..) | FieldKind::Map(..) => { w.write_line(&format!( "::std::mem::replace(&mut self.{}, {})", self.rust_name, take_xxx_return_type.default_value(&self.customize, false) )); } }, ); } pub(crate) fn write_message_single_field_accessors(&self, w: &mut CodeWriter) { if self.generate_accessors || self.generate_getter { w.write_line(""); let reconstruct_def = self.reconstruct_def(); w.comment(&(reconstruct_def + ";")); } if self.generate_getter { w.write_line(""); self.write_message_field_get(w); } if !self.generate_accessors { return; } w.write_line(""); let clear_field_func = self.clear_field_func(); w.pub_fn(&format!("{}(&mut self)", clear_field_func), |w| { self.write_clear(w); }); if self.has_has() { w.write_line(""); self.write_message_field_has(w); } w.write_line(""); self.write_message_field_set(w); if self.has_mut() { w.write_line(""); self.write_message_field_mut(w); } if self.has_take() { w.write_line(""); self.write_message_field_take(w); } } } pub(crate) fn rust_field_name_for_protobuf_field_name(name: &str) -> RustIdent { RustIdent::new(name) } pub(crate) fn rust_variant_name_for_protobuf_oneof_field_name(name: &str) -> RustIdent { let name = camel_case(name); RustIdent::new(&name) } protobuf-codegen-3.7.2/src/gen/field/option_kind.rs000064400000000000000000000036701046102023000204130ustar 00000000000000use crate::gen::inside::protobuf_crate_path; use crate::gen::rust_types_values::RustType; use crate::Customize; /// Optional fields can be stored are `Option` or `SingularPtrField`. #[derive(Copy, Clone, Eq, PartialEq)] pub enum OptionKind { /// Field is `Option` Option, /// Field is `SingularPtrField` MessageField, } impl OptionKind { pub(crate) fn wrap_element(&self, element_type: RustType) -> RustType { let element_type = Box::new(element_type); match self { OptionKind::Option => RustType::Option(element_type), OptionKind::MessageField => RustType::MessageField(element_type), } } // Type of `as_ref()` operation pub(crate) fn as_ref_type(&self, element_type: RustType) -> RustType { match self { OptionKind::Option => RustType::Option(Box::new(element_type.ref_type())), OptionKind::MessageField => RustType::MessageField(Box::new(element_type.ref_type())), } } fn _as_option_ref(&self, v: &str) -> String { match self { OptionKind::Option | OptionKind::MessageField => format!("{}.as_ref()", v), } } pub(crate) fn unwrap_or_else(&self, what: &str, default_value: &str) -> String { match self { _ => format!("{}.unwrap_or_else(|| {})", what, default_value), } } pub(crate) fn unwrap_ref_or_else(&self, what: &str, default_value: &str) -> String { match self { _ => format!("{}.unwrap_or_else(|| {})", what, default_value), } } pub(crate) fn wrap_value(&self, value: &str, customize: &Customize) -> String { match self { OptionKind::Option => format!("::std::option::Option::Some({})", value), OptionKind::MessageField => format!( "{}::MessageField::some({})", protobuf_crate_path(customize), value ), } } } protobuf-codegen-3.7.2/src/gen/field/repeated.rs000064400000000000000000000022311046102023000176570ustar 00000000000000use crate::gen::field::elem::FieldElem; use crate::gen::file_and_mod::FileAndMod; use crate::gen::rust::snippets::EXPR_VEC_NEW; use crate::gen::rust_types_values::RustType; /// Repeated field can be `Vec` or `RepeatedField`. #[derive(Eq, PartialEq, Copy, Clone)] pub enum RepeatedFieldKind { Vec, } impl RepeatedFieldKind { fn wrap_element(&self, element_type: RustType) -> RustType { let element_type = Box::new(element_type); match self { RepeatedFieldKind::Vec => RustType::Vec(element_type), } } fn default(&self) -> String { match self { RepeatedFieldKind::Vec => EXPR_VEC_NEW.to_owned(), } } } #[derive(Clone)] pub(crate) struct RepeatedField<'a> { pub elem: FieldElem<'a>, pub packed: bool, } impl<'a> RepeatedField<'a> { pub(crate) fn kind(&self) -> RepeatedFieldKind { RepeatedFieldKind::Vec } pub(crate) fn rust_type(&self, reference: &FileAndMod) -> RustType { self.kind() .wrap_element(self.elem.rust_storage_elem_type(reference)) } pub(crate) fn default(&self) -> String { self.kind().default() } } protobuf-codegen-3.7.2/src/gen/field/singular.rs000064400000000000000000000026171046102023000177220ustar 00000000000000use crate::gen::field::elem::FieldElem; use crate::gen::field::option_kind::OptionKind; use crate::gen::file_and_mod::FileAndMod; use crate::gen::rust_types_values::RustType; use crate::Customize; #[derive(Clone, PartialEq, Eq, Copy)] pub enum SingularFieldFlag { // proto2 or proto3 message WithFlag { required: bool, option_kind: OptionKind, }, // proto3 WithoutFlag, } impl SingularFieldFlag { pub fn is_required(&self) -> bool { match *self { SingularFieldFlag::WithFlag { required, .. } => required, SingularFieldFlag::WithoutFlag => false, } } } #[derive(Clone)] pub(crate) struct SingularField<'a> { pub flag: SingularFieldFlag, pub elem: FieldElem<'a>, } impl<'a> SingularField<'a> { pub(crate) fn rust_storage_type(&self, reference: &FileAndMod) -> RustType { match self.flag { SingularFieldFlag::WithFlag { option_kind, .. } => { option_kind.wrap_element(self.elem.rust_storage_elem_type(reference)) } SingularFieldFlag::WithoutFlag => self.elem.rust_storage_elem_type(reference), } } pub(crate) fn default_value( &self, customize: &Customize, reference: &FileAndMod, const_expr: bool, ) -> String { self.rust_storage_type(reference) .default_value(customize, const_expr) } } protobuf-codegen-3.7.2/src/gen/field/tag.rs000064400000000000000000000002241046102023000166410ustar 00000000000000use protobuf::rt::WireType; pub(crate) fn make_tag(field_number: u32, wire_type: WireType) -> u32 { (field_number << 3) | (wire_type as u32) } protobuf-codegen-3.7.2/src/gen/field/type_ext.rs000064400000000000000000000102661046102023000177360ustar 00000000000000use protobuf::descriptor::field_descriptor_proto::Type; use crate::gen::rust_types_values::PrimitiveTypeVariant; use crate::gen::rust_types_values::RustType; pub(crate) trait TypeExt { fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String; fn _is_s_varint(&self) -> bool; fn is_copy(&self) -> bool; fn protobuf_name(&self) -> &'static str; fn rust_type(&self) -> RustType; fn os_write_fn_param_type(&self) -> RustType; fn encoded_size(&self) -> Option; } impl TypeExt for Type { fn read(&self, is: &str, primitive_type_variant: PrimitiveTypeVariant) -> String { match (self, primitive_type_variant) { (Type::TYPE_ENUM, _) => format!("{}.read_enum_or_unknown()", is), (Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes) => { format!("{}.read_tokio_bytes()", is) } (Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes) => { format!("{}.read_tokio_chars()", is) } _ => format!("{}.read_{}()", is, self.protobuf_name()), } } /// True if self is signed integer with zigzag encoding fn _is_s_varint(&self) -> bool { match *self { Type::TYPE_SINT32 | Type::TYPE_SINT64 => true, _ => false, } } fn is_copy(&self) -> bool { match self { Type::TYPE_MESSAGE | Type::TYPE_STRING | Type::TYPE_BYTES => false, _ => true, } } fn protobuf_name(&self) -> &'static str { match self { Type::TYPE_DOUBLE => "double", Type::TYPE_FLOAT => "float", Type::TYPE_INT32 => "int32", Type::TYPE_INT64 => "int64", Type::TYPE_UINT32 => "uint32", Type::TYPE_UINT64 => "uint64", Type::TYPE_SINT32 => "sint32", Type::TYPE_SINT64 => "sint64", Type::TYPE_FIXED32 => "fixed32", Type::TYPE_FIXED64 => "fixed64", Type::TYPE_SFIXED32 => "sfixed32", Type::TYPE_SFIXED64 => "sfixed64", Type::TYPE_BOOL => "bool", Type::TYPE_STRING => "string", Type::TYPE_BYTES => "bytes", Type::TYPE_ENUM => "enum", Type::TYPE_MESSAGE => "message", Type::TYPE_GROUP => "group", } } /// Rust type for protobuf base type. fn rust_type(&self) -> RustType { match self { Type::TYPE_DOUBLE => RustType::Float(64), Type::TYPE_FLOAT => RustType::Float(32), Type::TYPE_INT32 => RustType::Int(true, 32), Type::TYPE_INT64 => RustType::Int(true, 64), Type::TYPE_UINT32 => RustType::Int(false, 32), Type::TYPE_UINT64 => RustType::Int(false, 64), Type::TYPE_SINT32 => RustType::Int(true, 32), Type::TYPE_SINT64 => RustType::Int(true, 64), Type::TYPE_FIXED32 => RustType::Int(false, 32), Type::TYPE_FIXED64 => RustType::Int(false, 64), Type::TYPE_SFIXED32 => RustType::Int(true, 32), Type::TYPE_SFIXED64 => RustType::Int(true, 64), Type::TYPE_BOOL => RustType::Bool, Type::TYPE_STRING => RustType::String, Type::TYPE_BYTES => RustType::Vec(Box::new(RustType::u8())), Type::TYPE_ENUM | Type::TYPE_GROUP | Type::TYPE_MESSAGE => { panic!("there is no rust name for {:?}", self) } } } // type of `v` in `os.write_xxx_no_tag(v)` fn os_write_fn_param_type(&self) -> RustType { match self { Type::TYPE_STRING => RustType::amp_str(), Type::TYPE_BYTES => RustType::amp_slice_of_u8(), Type::TYPE_ENUM => RustType::i32(), t => t.rust_type(), } } /// Size of value for type, None if variable. fn encoded_size(&self) -> Option { match self { Type::TYPE_BOOL => Some(1), Type::TYPE_FIXED32 => Some(4), Type::TYPE_FIXED64 => Some(8), Type::TYPE_SFIXED32 => Some(4), Type::TYPE_SFIXED64 => Some(8), Type::TYPE_FLOAT => Some(4), Type::TYPE_DOUBLE => Some(8), _ => None, } } } protobuf-codegen-3.7.2/src/gen/file.rs000064400000000000000000000120461046102023000157270ustar 00000000000000use std::collections::HashMap; use protobuf::descriptor::file_options; use protobuf::descriptor::FileDescriptorProto; use protobuf::reflect::FileDescriptor; use protobuf_parse::ProtoPath; use crate::compiler_plugin; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::rustproto_proto::customize_from_rustproto_for_file; use crate::gen::code_writer::CodeWriter; use crate::gen::enums::EnumGen; use crate::gen::extensions::write_extensions; use crate::gen::file_descriptor::write_file_descriptor_data; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::MessageGen; use crate::gen::paths::proto_path_to_rust_mod; use crate::gen::scope::FileScope; use crate::gen::scope::RootScope; use crate::proto_name_to_rs; pub(crate) struct GenFileResult { pub(crate) compiler_plugin_result: compiler_plugin::GenResult, pub(crate) mod_name: String, } pub(crate) fn gen_file( file_descriptor: &FileDescriptor, _files_map: &HashMap<&ProtoPath, &FileDescriptor>, root_scope: &RootScope, parent_customize: &CustomizeElemCtx, parser: &str, ) -> anyhow::Result { let lite_runtime_from_builtin_option = file_descriptor .proto() .options .get_or_default() .optimize_for() == file_options::OptimizeMode::LITE_RUNTIME; let mut customize_from_proto = customize_from_rustproto_for_file(file_descriptor.proto().options.get_or_default()); if customize_from_proto.lite_runtime.is_none() && parent_customize.for_elem.lite_runtime.is_none() { customize_from_proto.lite_runtime = Some(lite_runtime_from_builtin_option); } let customize = parent_customize.child(&customize_from_proto, file_descriptor); let file_scope = FileScope { file_descriptor }; let scope = file_scope.to_scope(); let lite_runtime = customize.for_elem.lite_runtime.unwrap_or(false); let v = CodeWriter::with(|w| { w.write_generated_by("rust-protobuf", env!("CARGO_PKG_VERSION"), parser); w.write_line(""); w.write_line(&format!( "//! Generated file from `{}`", file_descriptor.proto().name() )); if customize.for_elem.lite_runtime.unwrap_or(false) { w.comment("Generated for lite runtime"); } if customize.for_elem.inside_protobuf != Some(true) { w.write_line(""); w.write_line("/// Generated files are compatible only with the same version"); w.write_line("/// of protobuf runtime."); w.write_line(&format!( "const _PROTOBUF_VERSION_CHECK: () = {}::{};", protobuf_crate_path(&customize.for_elem), protobuf::VERSION_IDENT )); } static NESTED_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let message_type_number = *NESTED_TYPE_NUMBER.get(|| { protobuf::reflect::MessageDescriptor::for_type::() .field_by_name("message_type") .expect("`message_type` must exist") .proto() .number() }); let mut path = vec![message_type_number, 0]; for (id, message) in scope.messages().iter().enumerate() { // ignore map entries, because they are not used in map fields if !message.is_map() { path[1] = id as i32; w.write_line(""); MessageGen::new( file_descriptor, message, &root_scope, &customize, &path, file_descriptor.proto().source_code_info.as_ref(), )? .write(w)?; } } static ENUM_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let enum_type_number = *ENUM_TYPE_NUMBER.get(|| { protobuf::reflect::MessageDescriptor::for_type::() .field_by_name("enum_type") .expect("`enum_type` must exist") .proto() .number() }); let mut path = vec![enum_type_number, 0]; for (id, enum_type) in scope.enums().iter().enumerate() { path[1] = id as i32; w.write_line(""); EnumGen::new( enum_type, &customize, root_scope, &path, file_descriptor.proto().source_code_info.as_ref(), ) .write(w); } write_extensions(file_descriptor, &root_scope, w, &customize); if !lite_runtime { w.write_line(""); write_file_descriptor_data(file_descriptor, &customize.for_elem, w); } Ok(()) })?; Ok(GenFileResult { compiler_plugin_result: compiler_plugin::GenResult { name: proto_name_to_rs(file_descriptor.proto().name()), content: v.into_bytes(), }, mod_name: proto_path_to_rust_mod(file_descriptor.proto().name()).into_string(), }) } protobuf-codegen-3.7.2/src/gen/file_and_mod.rs000064400000000000000000000003211046102023000174010ustar 00000000000000use crate::customize::Customize; use crate::gen::rust::rel_path::RustRelativePath; pub(crate) struct FileAndMod { pub file: String, pub relative_mod: RustRelativePath, pub customize: Customize, } protobuf-codegen-3.7.2/src/gen/file_descriptor.rs000064400000000000000000000151521046102023000201660ustar 00000000000000use std::fmt::Write as _; use protobuf::reflect::FileDescriptor; use protobuf::Message; use crate::gen::code_writer::CodeWriter; use crate::gen::inside::protobuf_crate_path; use crate::gen::paths::proto_path_to_fn_file_descriptor; use crate::gen::rust::snippets::expr_vec_with_capacity_const; use crate::gen::scope::FileScope; use crate::gen::scope::Scope; use crate::gen::scope::WithScope; use crate::Customize; fn escape_byte(s: &mut String, b: u8) { if b == b'\n' { write!(s, "\\n").unwrap(); } else if b == b'\r' { write!(s, "\\r").unwrap(); } else if b == b'\t' { write!(s, "\\t").unwrap(); } else if b == b'\\' || b == b'"' { write!(s, "\\{}", b as char).unwrap(); } else if b == b'\0' { write!(s, "\\0").unwrap(); // ASCII printable except space } else if b > 0x20 && b < 0x7f { write!(s, "{}", b as char).unwrap(); } else { write!(s, "\\x{:02x}", b).unwrap(); } } fn write_generate_file_descriptor( file_descriptor: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, ) { let deps = &file_descriptor.proto().dependency; w.write_line(&format!( "let mut deps = {vec_with_capacity};", vec_with_capacity = expr_vec_with_capacity_const(deps.len()) )); for f in deps { w.write_line(&format!( "deps.push({}().clone());", proto_path_to_fn_file_descriptor(f, customize) )); } let scope = FileScope { file_descriptor }; let messages = scope.find_messages_except_map(); w.write_line(&format!( "let mut messages = {vec_with_capacity};", vec_with_capacity = expr_vec_with_capacity_const(messages.len()) )); for m in &messages { w.write_line(&format!( "messages.push({}::generated_message_descriptor_data());", m.rust_name_to_file(), )); } let enums = scope.find_enums(); w.write_line(&format!( "let mut enums = {};", expr_vec_with_capacity_const(enums.len()) )); for e in &enums { w.write_line(&format!( "enums.push({}::generated_enum_descriptor_data());", e.rust_name_to_file(), )); } w.write_line(&format!( "{}::reflect::GeneratedFileDescriptor::new_generated(", protobuf_crate_path(&customize), )); w.indented(|w| { w.write_line(&format!("file_descriptor_proto(),")); w.write_line(&format!("deps,")); w.write_line(&format!("messages,")); w.write_line(&format!("enums,")); }); w.write_line(")"); } fn write_file_descriptor( file_descriptor: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, ) { w.write_line("/// `FileDescriptor` object which allows dynamic access to files"); w.pub_fn( &format!( "file_descriptor() -> &'static {protobuf_crate}::reflect::FileDescriptor", protobuf_crate = protobuf_crate_path(customize) ), |w| { w.lazy_static( "generated_file_descriptor_lazy", &format!( "{protobuf_crate}::reflect::GeneratedFileDescriptor", protobuf_crate = protobuf_crate_path(customize) ), &format!("{}", protobuf_crate_path(customize)), ); w.lazy_static_decl_get( "file_descriptor", &format!( "{protobuf_crate}::reflect::FileDescriptor", protobuf_crate = protobuf_crate_path(customize) ), &format!("{}", protobuf_crate_path(customize)), |w| { w.block( "let generated_file_descriptor = generated_file_descriptor_lazy.get(|| {", "});", |w| write_generate_file_descriptor(file_descriptor, customize, w), ); w.write_line(&format!( "{protobuf_crate}::reflect::FileDescriptor::new_generated_2(generated_file_descriptor)", protobuf_crate=protobuf_crate_path(&customize), )); } ); }, ); } pub(crate) fn write_file_descriptor_data( file: &FileDescriptor, customize: &Customize, w: &mut CodeWriter, ) { let fdp_bytes = file.proto().write_to_bytes().unwrap(); w.write_line("static file_descriptor_proto_data: &'static [u8] = b\"\\"); w.indented(|w| { const MAX_LINE_LEN: usize = 72; let mut s = String::new(); for &b in &fdp_bytes { let prev_len = s.len(); escape_byte(&mut s, b); let truncate = s.len() > MAX_LINE_LEN; if truncate { s.truncate(prev_len); } if truncate || s.len() == MAX_LINE_LEN { write!(s, "\\").unwrap(); w.write_line(&s); s.clear(); } if truncate { escape_byte(&mut s, b); } } if !s.is_empty() { write!(s, "\\").unwrap(); w.write_line(&s); s.clear(); } }); w.write_line("\";"); w.write_line(""); write_file_descriptor_proto(&customize, w); w.write_line(""); write_file_descriptor(file, &customize, w); } fn write_file_descriptor_proto(customize: &Customize, w: &mut CodeWriter) { w.write_line("/// `FileDescriptorProto` object which was a source for this generated file"); w.def_fn( &format!( "file_descriptor_proto() -> &'static {protobuf_crate}::descriptor::FileDescriptorProto", protobuf_crate=protobuf_crate_path(customize) ), |w| { w.lazy_static_decl_get( "file_descriptor_proto_lazy", &format!( "{protobuf_crate}::descriptor::FileDescriptorProto", protobuf_crate=protobuf_crate_path(customize) ), &format!("{}", protobuf_crate_path(customize)), |w| { w.write_line(&format!( "{protobuf_crate}::Message::parse_from_bytes(file_descriptor_proto_data).unwrap()", protobuf_crate=protobuf_crate_path(customize) )); }, ); }, ); } /// Code to generate call `module::file_descriptor()`. pub(crate) fn file_descriptor_call_expr(scope: &Scope) -> String { format!( "{}()", scope .rust_path_to_file() .to_reverse() .append_ident("file_descriptor".into()) ) } protobuf-codegen-3.7.2/src/gen/inside.rs000064400000000000000000000006031046102023000162570ustar 00000000000000use crate::customize::Customize; use crate::gen::rust::path::RustPath; /// Path to `protobuf` crate, different when `.proto` file is /// used inside or outside of protobuf crate. pub(crate) fn protobuf_crate_path(customize: &Customize) -> RustPath { match customize.inside_protobuf { Some(true) => RustPath::from("crate"), _ => RustPath::from("::protobuf"), } } protobuf-codegen-3.7.2/src/gen/map.rs000064400000000000000000000007471046102023000155720ustar 00000000000000use crate::gen::scope::FieldWithContext; use crate::gen::scope::MessageWithScope; /// Pair of (key, value) if this message is map entry pub(crate) fn map_entry<'a>( d: &'a MessageWithScope, ) -> Option<(FieldWithContext<'a>, FieldWithContext<'a>)> { if d.message.is_map_entry() { // `MessageDescriptor` validated the fields. let key = d.fields()[0].clone(); let value = d.fields()[1].clone(); Some((key, value)) } else { None } } protobuf-codegen-3.7.2/src/gen/message.rs000064400000000000000000000666071046102023000164500ustar 00000000000000use std::fmt; use protobuf::descriptor::*; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf_parse::snake_case; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::ctx::SpecialFieldPseudoDescriptor; use crate::customize::rustproto_proto::customize_from_rustproto_for_message; use crate::gen::code_writer::*; use crate::gen::descriptor::write_fn_descriptor; use crate::gen::enums::*; use crate::gen::field::FieldGen; use crate::gen::field::FieldKind; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::oneof::OneofGen; use crate::gen::oneof::OneofVariantGen; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_message; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_special_field; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust::snippets::expr_vec_with_capacity_const; use crate::gen::rust::snippets::EXPR_NONE; use crate::gen::rust_types_values::*; use crate::gen::scope::MessageWithScope; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; use crate::Customize; /// Protobuf message Rust type name #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) struct RustTypeMessage(pub RustIdentWithPath); impl fmt::Display for RustTypeMessage { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(&self.0, f) } } impl> From for RustTypeMessage { fn from(s: S) -> Self { RustTypeMessage(s.into()) } } impl RustTypeMessage { /// Code which emits default instance. pub fn default_instance(&self, customize: &Customize) -> String { format!( "<{} as {}::Message>::default_instance()", self.0, protobuf_crate_path(customize) ) } } /// Message info for codegen pub(crate) struct MessageGen<'a> { file_descriptor: &'a FileDescriptor, message_descriptor: MessageDescriptor, pub message: &'a MessageWithScope<'a>, pub root_scope: &'a RootScope<'a>, pub fields: Vec>, pub lite_runtime: bool, customize: CustomizeElemCtx<'a>, path: &'a [i32], info: Option<&'a SourceCodeInfo>, } impl<'a> MessageGen<'a> { pub fn new( file_descriptor: &'a FileDescriptor, message: &'a MessageWithScope<'a>, root_scope: &'a RootScope<'a>, parent_customize: &CustomizeElemCtx<'a>, path: &'a [i32], info: Option<&'a SourceCodeInfo>, ) -> anyhow::Result> { let message_descriptor = file_descriptor .message_by_package_relative_name(&format!("{}", message.protobuf_name_to_package())) .unwrap(); let customize = parent_customize.child( &customize_from_rustproto_for_message(message.message.proto().options.get_or_default()), &message.message, ); static FIELD_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let field_number = *FIELD_NUMBER.get(|| { protobuf::reflect::MessageDescriptor::for_type::() .field_by_name("field") .expect("`field` must exist") .proto() .number() }); let fields: Vec<_> = message .fields() .into_iter() .enumerate() .map(|(id, field)| { let mut path = path.to_vec(); path.extend_from_slice(&[field_number, id as i32]); FieldGen::parse(field, root_scope, &customize, path, info) }) .collect::>>()?; let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { message.file_descriptor().proto().options.optimize_for() == file_options::OptimizeMode::LITE_RUNTIME }); Ok(MessageGen { message_descriptor, file_descriptor, message, root_scope, fields, lite_runtime, customize, path, info, }) } fn rust_name(&self) -> RustIdent { self.message.rust_name() } fn mod_name(&self) -> RustRelativePath { self.message.scope.rust_path_to_file() } pub fn file_and_mod(&self) -> FileAndMod { self.message .scope .file_and_mod(self.customize.for_elem.clone()) } fn oneofs(&'a self) -> Vec> { self.message .oneofs() .into_iter() .map(|oneof| OneofGen::parse(self, oneof, &self.customize)) .collect() } fn required_fields(&'a self) -> Vec<&'a FieldGen<'a>> { 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<'a>> { self.fields .iter() .filter(|f| f.proto_type == field_descriptor_proto::Type::TYPE_MESSAGE) .collect() } fn fields_except_oneof(&'a self) -> Vec<&'a FieldGen<'a>> { self.fields .iter() .filter(|f| match f.kind { FieldKind::Oneof(..) => false, _ => true, }) .collect() } fn fields_except_group(&'a self) -> Vec<&'a FieldGen<'a>> { self.fields .iter() .filter(|f| f.proto_type != field_descriptor_proto::Type::TYPE_GROUP) .collect() } fn fields_except_oneof_and_group(&'a self) -> Vec<&'a FieldGen<'a>> { self.fields .iter() .filter(|f| match f.kind { FieldKind::Oneof(..) => false, _ => f.proto_type != field_descriptor_proto::Type::TYPE_GROUP, }) .collect() } fn write_match_each_oneof_variant(&self, w: &mut CodeWriter, cb: F) where F: Fn(&mut CodeWriter, &OneofVariantGen, &RustValueTyped), { for oneof in self.oneofs() { let variants = oneof.variants_except_group(); if variants.is_empty() { // Special case because // https://github.com/rust-lang/rust/issues/50642 continue; } w.if_let_stmt( "::std::option::Option::Some(ref v)", &format!("self.{}", oneof.oneof.field_name())[..], |w| { w.match_block("v", |w| { for variant in variants { let ref field = variant.field; let (refv, vtype) = if field.elem_type_is_copy() { ("v", variant.rust_type(&self.file_and_mod())) } else { ("ref v", variant.rust_type(&self.file_and_mod()).ref_type()) }; w.case_block( format!("&{}({})", variant.path(&self.file_and_mod()), refv), |w| { cb( w, &variant, &RustValueTyped { value: "v".to_owned(), rust_type: vtype.clone(), }, ); }, ); } }); }, ); } } fn write_write_to_with_cached_sizes(&self, w: &mut CodeWriter) { let sig = format!( "write_to_with_cached_sizes(&self, os: &mut {protobuf_crate}::CodedOutputStream<'_>) -> {protobuf_crate}::Result<()>", protobuf_crate=protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { // To have access to its methods but not polute the name space. for f in self.fields_except_oneof_and_group() { f.write_message_write_field("os", w); } self.write_match_each_oneof_variant(w, |w, variant, v| { variant .field .write_write_element(variant.elem(), w, "os", v); }); w.write_line("os.write_unknown_fields(self.special_fields.unknown_fields())?;"); w.write_line("::std::result::Result::Ok(())"); }); } fn write_default_instance_lazy(&self, w: &mut CodeWriter) { w.lazy_static_decl_get_simple( "instance", &format!("{}", self.rust_name()), &format!("{}::new", self.rust_name()), &format!("{}", protobuf_crate_path(&self.customize.for_elem)), ); } fn write_default_instance_static(&self, w: &mut CodeWriter) { w.stmt_block( &format!( "static instance: {} = {}", self.rust_name(), self.rust_name() ), |w| { for f in &self.fields_except_oneof_and_group() { w.field_entry( &f.rust_name.to_string(), &f.kind .default(&self.customize.for_elem, &self.file_and_mod(), true), ); } for o in &self.oneofs() { w.field_entry(&o.oneof.field_name().to_string(), EXPR_NONE); } w.field_entry( "special_fields", &format!( "{}::SpecialFields::new()", protobuf_crate_path(&self.customize.for_elem) ), ); }, ); w.write_line("&instance"); } fn write_default_instance(&self, w: &mut CodeWriter) { w.def_fn( &format!("default_instance() -> &'static {}", self.rust_name()), |w| { let has_map_field = self.fields.iter().any(|f| match f.kind { FieldKind::Map(..) => true, _ => false, }); if has_map_field { self.write_default_instance_lazy(w) } else { self.write_default_instance_static(w) } }, ); } fn write_compute_size(&self, w: &mut CodeWriter) { // Append sizes of messages in the tree to the specified vector. // First appended element is size of self, and then nested message sizes. // in serialization order are appended recursively."); w.comment("Compute sizes of nested messages"); // there are unused variables in oneof w.allow(&["unused_variables"]); w.def_fn("compute_size(&self) -> u64", |w| { // To have access to its methods but not polute the name space. w.write_line("let mut my_size = 0;"); for field in self.fields_except_oneof_and_group() { field.write_message_compute_field_size("my_size", w); } self.write_match_each_oneof_variant(w, |w, variant, v| { variant .field .write_element_size(variant.elem(), w, v, "my_size"); }); w.write_line(&format!( "my_size += {}::rt::unknown_fields_size(self.special_fields.unknown_fields());", protobuf_crate_path(&self.customize.for_elem) )); w.write_line("self.special_fields.cached_size().set(my_size as u32);"); w.write_line("my_size"); }); } fn write_field_accessors(&self, w: &mut CodeWriter) { for f in self.fields_except_group() { f.write_message_single_field_accessors(w); } } fn write_impl_self(&self, w: &mut CodeWriter) { w.impl_self_block(&format!("{}", self.rust_name()), |w| { w.pub_fn(&format!("new() -> {}", self.rust_name()), |w| { w.write_line("::std::default::Default::default()"); }); self.write_field_accessors(w); if !self.lite_runtime { w.write_line(""); self.write_generated_message_descriptor_data(w); } }); } fn write_unknown_fields(&self, w: &mut CodeWriter) { let sig = format!( "special_fields(&self) -> &{}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ); w.def_fn(&sig, |w| { w.write_line("&self.special_fields"); }); w.write_line(""); let sig = format!( "mut_special_fields(&mut self) -> &mut {}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ); w.def_fn(&sig, |w| { w.write_line("&mut self.special_fields"); }); } fn write_merge_from(&self, w: &mut CodeWriter) { let sig = format!( "merge_from(&mut self, is: &mut {}::CodedInputStream<'_>) -> {}::Result<()>", protobuf_crate_path(&self.customize.for_elem), protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { w.while_block("let Some(tag) = is.read_raw_tag_or_eof()?", |w| { w.match_block("tag", |w| { for f in &self.fields_except_group() { f.write_merge_from_field_case_block(w); } w.case_block("tag", |w| { w.write_line(&format!("{}::rt::read_unknown_or_skip_group(tag, is, self.special_fields.mut_unknown_fields())?;", protobuf_crate_path(&self.customize.for_elem))); }); }); }); w.write_line("::std::result::Result::Ok(())"); }); } fn write_impl_message_full_fn_descriptor(&self, w: &mut CodeWriter) { write_fn_descriptor( &self.message.message, self.message.scope(), &self.customize.for_elem, w, ); } fn write_generated_message_descriptor_data(&self, w: &mut CodeWriter) { let sig = format!( "generated_message_descriptor_data() -> {}::reflect::GeneratedMessageDescriptorData", protobuf_crate_path(&self.customize.for_elem) ); w.fn_block( Visibility::Path(self.message.scope().rust_path_to_file().to_reverse()), &sig, |w| { let fields = self.fields_except_group(); let oneofs = self.oneofs(); w.write_line(&format!( "let mut fields = {};", expr_vec_with_capacity_const(fields.len()) )); w.write_line(&format!( "let mut oneofs = {};", expr_vec_with_capacity_const(oneofs.len()) )); for field in fields { field.write_push_accessor("fields", w); } for oneof in oneofs { w.write_line(&format!( "oneofs.push({}::generated_oneof_descriptor_data());", oneof.type_name_relative(&self.mod_name()) )); } w.write_line(&format!( "{}::reflect::GeneratedMessageDescriptorData::new_2::<{}>(", protobuf_crate_path(&self.customize.for_elem), self.rust_name(), )); w.indented(|w| { w.write_line(&format!("\"{}\",", self.message.name_to_package())); w.write_line("fields,"); w.write_line("oneofs,"); }); w.write_line(")"); }, ); } fn write_is_initialized(&self, w: &mut CodeWriter) { w.def_fn(&format!("is_initialized(&self) -> bool"), |w| { if !self.message.message.is_initialized_is_always_true() { // TODO: use single loop for f in self.required_fields() { f.write_if_self_field_is_none(w, |w| { w.write_line("return false;"); }); } for f in self.message_fields() { if let FieldKind::Map(..) = f.kind { // TODO w.comment("TODO: check map values are initialized"); continue; } f.write_for_self_field(w, "v", |w, _t| { w.if_stmt("!v.is_initialized()", |w| { w.write_line("return false;"); }); }); } } w.write_line("true"); }); } fn write_impl_message(&self, w: &mut CodeWriter) { w.impl_for_block( &format!("{}::Message", protobuf_crate_path(&self.customize.for_elem),), &format!("{}", self.rust_name()), |w| { w.write_line(&format!( "const NAME: &'static str = \"{}\";", self.message.message.name() )); w.write_line(""); self.write_is_initialized(w); w.write_line(""); self.write_merge_from(w); w.write_line(""); self.write_compute_size(w); w.write_line(""); self.write_write_to_with_cached_sizes(w); w.write_line(""); self.write_unknown_fields(w); w.write_line(""); w.def_fn(&format!("new() -> {}", self.rust_name()), |w| { w.write_line(&format!("{}::new()", self.rust_name())); }); w.write_line(""); w.def_fn("clear(&mut self)", |w| { for f in self.fields_except_group() { f.write_clear(w); } w.write_line("self.special_fields.clear();"); }); w.write_line(""); self.write_default_instance(w); }, ); } fn write_impl_message_full(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::MessageFull", protobuf_crate_path(&self.customize.for_elem), ), &format!("{}", self.rust_name()), |w| { self.write_impl_message_full_fn_descriptor(w); }, ); } fn write_impl_value(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::reflect::ProtobufValue", protobuf_crate_path(&self.customize.for_elem) ), &format!("{}", self.rust_name()), |w| { w.write_line(&format!( "type RuntimeType = {}::reflect::rt::RuntimeTypeMessage;", protobuf_crate_path(&self.customize.for_elem) )); }, ) } fn write_impl_display(&self, w: &mut CodeWriter) { w.impl_for_block( "::std::fmt::Display", &format!("{}", self.rust_name()), |w| { w.def_fn( "fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result", |w| { w.write_line(&format!( "{}::text_format::fmt(self, f)", protobuf_crate_path(&self.customize.for_elem) )); }, ); }, ); } fn supports_derive_partial_eq(&self) -> bool { // There's stack overflow in the compiler when struct has too many fields // https://github.com/rust-lang/rust/issues/40119 self.fields.len() <= 500 } fn write_struct(&self, w: &mut CodeWriter) { write_protoc_insertion_point_for_message( w, &self.customize.for_elem, &self.message_descriptor, ); let mut derive = Vec::new(); if self.supports_derive_partial_eq() { derive.push("PartialEq"); } derive.extend(&["Clone", "Default", "Debug"]); w.derive(&derive); w.pub_struct(&format!("{}", self.rust_name()), |w| { if !self.fields_except_oneof().is_empty() { w.comment("message fields"); for field in self.fields_except_oneof() { field.write_struct_field(w); } } if !self.oneofs().is_empty() { w.comment("message oneof groups"); for oneof in self.oneofs() { w.field_decl_vis( Visibility::Public, &oneof.oneof.field_name().to_string(), &oneof.full_storage_type().to_code(&self.customize.for_elem), ); } } w.comment("special fields"); let customize_special_fields = self .customize .child( &Customize::default(), &SpecialFieldPseudoDescriptor { message: &self.message.message, field: "special_fields", }, ) .for_elem; write_protoc_insertion_point_for_special_field( w, &customize_special_fields, &self.message_descriptor, "special_fields", ); w.pub_field_decl( "special_fields", &format!( "{}::SpecialFields", protobuf_crate_path(&self.customize.for_elem) ), ); }); } fn write_impl_default_for_amp(&self, w: &mut CodeWriter) { w.impl_args_for_block( &["'a"], "::std::default::Default", &format!("&'a {}", self.rust_name()), |w| { w.def_fn(&format!("default() -> &'a {}", self.rust_name()), |w| { w.write_line(&format!( "<{} as {}::Message>::default_instance()", self.rust_name(), protobuf_crate_path(&self.customize.for_elem), )); }); }, ); } fn write_dummy_impl_partial_eq(&self, w: &mut CodeWriter) { w.impl_for_block( "::std::cmp::PartialEq", &format!("{}", self.rust_name()), |w| { w.def_fn("eq(&self, _: &Self) -> bool", |w| { w.comment("https://github.com/rust-lang/rust/issues/40119"); w.unimplemented(); }); }, ); } pub fn write(&self, w: &mut CodeWriter) -> anyhow::Result<()> { w.all_documentation(self.info, self.path); self.write_struct(w); w.write_line(""); self.write_impl_default_for_amp(w); if !self.supports_derive_partial_eq() { w.write_line(""); self.write_dummy_impl_partial_eq(w); } w.write_line(""); self.write_impl_self(w); w.write_line(""); self.write_impl_message(w); if !self.lite_runtime { w.write_line(""); self.write_impl_message_full(w); } if !self.lite_runtime { w.write_line(""); self.write_impl_display(w); w.write_line(""); self.write_impl_value(w); } let mod_name = message_name_to_nested_mod_name(&self.message.message.name()); let oneofs = self.oneofs(); let nested_messages: Vec<_> = self .message .to_scope() .messages() .into_iter() .filter(|nested| { // ignore map entries, because they are not used in map fields !nested.is_map() }) .collect(); let nested_enums = self.message.to_scope().enums(); if !oneofs.is_empty() || !nested_messages.is_empty() || !nested_enums.is_empty() { w.write_line(""); w.write_line(&format!( "/// Nested message and enums of message `{}`", self.message.message.name() )); w.pub_mod(&mod_name.to_string(), |w| { let mut first = true; for oneof in &oneofs { w.write_line(""); oneof.write(w); } static NESTED_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let nested_type_number = *NESTED_TYPE_NUMBER.get(|| { MessageDescriptor::for_type::() .field_by_name("nested_type") .expect("`nested_type` must exist") .proto() .number() }); let mut path = self.path.to_vec(); path.extend(&[nested_type_number, 0]); for (id, nested) in nested_messages.iter().enumerate() { let len = path.len() - 1; path[len] = id as i32; if !first { w.write_line(""); } first = false; MessageGen::new( &self.file_descriptor, nested, self.root_scope, &self.customize, &path, self.info, ) // TODO: do not unwrap. .unwrap() .write(w) // TODO: do not unwrap. .unwrap(); } static ENUM_TYPE_NUMBER: protobuf::rt::Lazy = protobuf::rt::Lazy::new(); let enum_type_number = *ENUM_TYPE_NUMBER.get(|| { MessageDescriptor::for_type::() .field_by_name("enum_type") .expect("`enum_type` must exist") .proto() .number() }); let len = path.len() - 2; path[len] = enum_type_number; for (id, enum_type) in self.message.to_scope().enums().iter().enumerate() { let len = path.len() - 1; path[len] = id as i32; if !first { w.write_line(""); } first = false; EnumGen::new( enum_type, &self.customize, self.root_scope, &path, self.info, ) .write(w); } }); } Ok(()) } } pub(crate) fn message_name_to_nested_mod_name(message_name: &str) -> RustIdent { let mod_name = snake_case(message_name); RustIdent::new(&mod_name) } protobuf-codegen-3.7.2/src/gen/mod.rs000064400000000000000000000010111046102023000155550ustar 00000000000000pub(crate) mod all; pub(crate) mod code_writer; pub(crate) mod descriptor; pub(crate) mod enums; pub(crate) mod extensions; pub(crate) mod field; pub(crate) mod file; pub(crate) mod file_and_mod; pub(crate) mod file_descriptor; pub(crate) mod inside; mod map; pub(crate) mod message; pub(crate) mod mod_rs; pub(crate) mod oneof; pub(crate) mod paths; pub(crate) mod protoc_insertion_point; pub(crate) mod rust; pub(crate) mod rust_types_values; pub(crate) mod scope; pub(crate) mod strx; pub(crate) mod well_known_types; protobuf-codegen-3.7.2/src/gen/mod_rs.rs000064400000000000000000000010521046102023000162660ustar 00000000000000use crate::compiler_plugin; use crate::gen::code_writer::CodeWriter; pub(crate) fn gen_mod_rs(mods: &[String]) -> compiler_plugin::GenResult { let v = CodeWriter::with_no_error(|w| { w.comment(&format!("{}generated", "@")); w.write_line(""); let mut mods: Vec<&String> = mods.into_iter().collect(); mods.sort(); for m in mods { w.write_line(&format!("pub mod {};", m)); } }); compiler_plugin::GenResult { name: "mod.rs".to_owned(), content: v.into_bytes(), } } protobuf-codegen-3.7.2/src/gen/oneof.rs000064400000000000000000000300611046102023000161130ustar 00000000000000//! Oneof-related codegen functions. use std::collections::HashSet; use protobuf::descriptor::field_descriptor_proto; use protobuf::descriptor::file_options; use protobuf::reflect::FieldDescriptor; use protobuf_parse::ProtobufAbsPath; use crate::customize::ctx::CustomizeElemCtx; use crate::customize::Customize; use crate::gen::code_writer::CodeWriter; use crate::gen::code_writer::Visibility; use crate::gen::field::elem::FieldElem; use crate::gen::field::rust_variant_name_for_protobuf_oneof_field_name; use crate::gen::field::FieldGen; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::MessageGen; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof; use crate::gen::protoc_insertion_point::write_protoc_insertion_point_for_oneof_field; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::path::RustPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust_types_values::make_path; use crate::gen::rust_types_values::RustType; use crate::gen::scope::OneofVariantWithContext; use crate::gen::scope::OneofWithContext; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; // oneof one { ... } #[derive(Clone)] pub(crate) struct OneofField<'a> { pub elem: FieldElem<'a>, pub oneof_variant_rust_name: RustIdent, pub oneof_field_name: RustIdent, pub type_name: RustIdentWithPath, pub boxed: bool, } impl<'a> OneofField<'a> { // Detecting recursion: if oneof fields contains a self-reference // or another message which has a reference to self, // put oneof variant into a box. fn need_boxed( field: &FieldDescriptor, root_scope: &RootScope, owner_name: &ProtobufAbsPath, ) -> bool { let mut visited_messages = HashSet::new(); let mut fields = vec![field.clone()]; while let Some(field) = fields.pop() { if field.proto().type_() == field_descriptor_proto::Type::TYPE_MESSAGE { let message_name = ProtobufAbsPath::from(field.proto().type_name()); if !visited_messages.insert(message_name.clone()) { continue; } if message_name == *owner_name { return true; } let message = root_scope.find_message(&message_name); fields.extend( message .message .fields() .into_iter() .filter(|f| f.containing_oneof().is_some()), ); } } false } pub fn parse( oneof: &OneofWithContext<'a>, field: &FieldDescriptor, elem: FieldElem<'a>, root_scope: &RootScope, ) -> OneofField<'a> { let boxed = OneofField::need_boxed(field, root_scope, &oneof.message.name_absolute()); OneofField { elem, type_name: oneof.rust_name(), boxed, oneof_variant_rust_name: rust_variant_name_for_protobuf_oneof_field_name(field.name()), oneof_field_name: oneof.field_name(), } } pub fn rust_type(&self, reference: &FileAndMod) -> RustType { let t = self.elem.rust_storage_elem_type(reference); if self.boxed { RustType::Uniq(Box::new(t)) } else { t } } pub fn variant_path(&self, reference: &RustRelativePath) -> RustIdentWithPath { make_path( reference, &self .type_name .to_path() .with_ident(self.oneof_variant_rust_name.clone()), ) } } #[derive(Clone)] pub(crate) struct OneofVariantGen<'a> { oneof: &'a OneofGen<'a>, _variant: OneofVariantWithContext<'a>, oneof_field: OneofField<'a>, pub field: FieldGen<'a>, _path: String, } impl<'a> OneofVariantGen<'a> { fn parse( oneof: &'a OneofGen<'a>, variant: OneofVariantWithContext<'a>, field: &'a FieldGen, _root_scope: &RootScope, ) -> OneofVariantGen<'a> { OneofVariantGen { oneof, _variant: variant.clone(), field: field.clone(), _path: format!( "{}::{}", oneof.type_name_relative(&oneof.oneof.message.scope.rust_path_to_file()), field.rust_name ), oneof_field: OneofField::parse( variant.oneof, &field.proto_field.field, field.elem().clone(), oneof.message.root_scope, ), } } pub fn rust_type(&self, reference: &FileAndMod) -> RustType { self.oneof_field.rust_type(reference) } pub fn path(&self, reference: &FileAndMod) -> RustPath { RustPath::from(format!( "{}::{}", self.oneof.type_name_relative(&reference.relative_mod), self.oneof_field.oneof_variant_rust_name, )) } pub(crate) fn elem(&self) -> &FieldElem<'_> { self.field.elem() } } pub(crate) struct OneofGen<'a> { // Message containing this oneof message: &'a MessageGen<'a>, pub oneof: OneofWithContext<'a>, customize: CustomizeElemCtx<'a>, lite_runtime: bool, } impl<'a> OneofGen<'a> { pub fn parse( message: &'a MessageGen, oneof: OneofWithContext<'a>, parent_customize: &CustomizeElemCtx<'a>, ) -> OneofGen<'a> { let customize = parent_customize.child(&Customize::default(), &oneof.oneof); let lite_runtime = customize.for_elem.lite_runtime.unwrap_or_else(|| { oneof .message .file_descriptor() .proto() .options .optimize_for() == file_options::OptimizeMode::LITE_RUNTIME }); OneofGen { message, oneof, customize, lite_runtime, } } pub fn type_name_relative(&self, source: &RustRelativePath) -> RustIdentWithPath { make_path(source, &self.oneof.rust_name()) } pub fn variants_except_group(&'a self) -> Vec> { self.oneof .variants() .into_iter() .filter_map(|v| { let field = self .message .fields .iter() .filter(|f| f.proto_field.name() == v.field.name()) .next() .expect(&format!("field not found by name: {}", v.field.name())); match field.proto_type { field_descriptor_proto::Type::TYPE_GROUP => None, _ => Some(OneofVariantGen::parse( self, v, field, self.message.root_scope, )), } }) .collect() } pub fn full_storage_type(&self) -> RustType { RustType::Option(Box::new(RustType::Oneof( self.type_name_relative( &self .oneof .message .scope .file_and_mod(self.customize.for_elem.clone()) .relative_mod, ) .clone(), ))) } fn file_and_mod(&self) -> FileAndMod { let mut file_and_mod = self .message .message .scope .file_and_mod(self.customize.for_elem.clone()); file_and_mod .relative_mod .push_ident(self.message.message.mod_name()); file_and_mod } fn write_enum(&self, w: &mut CodeWriter) { let derive = vec!["Clone", "PartialEq", "Debug"]; w.derive(&derive); if self .customize .for_elem .oneofs_non_exhaustive .unwrap_or(true) { w.write_line("#[non_exhaustive]"); } write_protoc_insertion_point_for_oneof(w, &self.customize.for_elem, &self.oneof.oneof); w.pub_enum(&self.oneof.rust_name().ident.to_string(), |w| { for variant in self.variants_except_group() { write_protoc_insertion_point_for_oneof_field( w, &self.customize.for_children, &variant.field.proto_field.field, ); w.write_line(&format!( "{}({}),", variant.oneof_field.oneof_variant_rust_name, &variant .rust_type(&self.file_and_mod()) .to_code(&self.customize.for_elem) )); } }); } fn write_impl_oneof(&self, w: &mut CodeWriter) { w.impl_for_block( &format!("{}::Oneof", protobuf_crate_path(&self.customize.for_elem)), self.oneof.rust_name().ident.to_string(), |_w| { // nothing here yet }, ); } fn write_impl_oneof_full_fn_descriptor(&self, w: &mut CodeWriter) { let sig = format!( "descriptor() -> {}::reflect::OneofDescriptor", protobuf_crate_path(&self.customize.for_elem), ); w.def_fn(&sig, |w| { w.lazy_static( "descriptor", &format!( "{}::reflect::OneofDescriptor", protobuf_crate_path(&self.customize.for_elem), ), &protobuf_crate_path(&self.customize.for_elem).to_string(), ); let message_type = make_path( &self .oneof .message .scope() .rust_path_to_file() .append(self.oneof.message.mod_name().into_rel_path()), &self.oneof.message.rust_name_to_file(), ); let expr = format!( "<{} as {}::MessageFull>::descriptor().oneof_by_name(\"{}\").unwrap()", message_type, protobuf_crate_path(&self.customize.for_elem), self.oneof.oneof.name() ); w.write_line(&format!("descriptor.get(|| {}).clone()", expr)); }); } fn write_impl_oneof_full(&self, w: &mut CodeWriter) { w.impl_for_block( &format!( "{}::OneofFull", protobuf_crate_path(&self.customize.for_elem) ), self.oneof.rust_name().ident.to_string(), |w| self.write_impl_oneof_full_fn_descriptor(w), ) } fn write_generated_oneof_descriptor_data(&self, w: &mut CodeWriter) { let sig = format!( "generated_oneof_descriptor_data() -> {}::reflect::GeneratedOneofDescriptorData", protobuf_crate_path(&self.customize.for_elem) ); w.fn_block( Visibility::Path( self.oneof .rust_name() .path .into_relative_or_panic() .to_reverse(), ), &sig, |w| { w.write_line(&format!( "{}::reflect::GeneratedOneofDescriptorData::new::<{}>(\"{}\")", protobuf_crate_path(&self.customize.for_elem), &self.oneof.rust_name().ident, self.oneof.oneof.name(), )); }, ); } fn write_impl_self(&self, w: &mut CodeWriter) { w.impl_self_block(&format!("{}", &self.oneof.rust_name().ident), |w| { if !self.lite_runtime { self.write_generated_oneof_descriptor_data(w); } }); } pub fn write(&self, w: &mut CodeWriter) { self.write_enum(w); w.write_line(""); self.write_impl_oneof(w); if !self.lite_runtime { w.write_line(""); self.write_impl_oneof_full(w); } w.write_line(""); self.write_impl_self(w); } } protobuf-codegen-3.7.2/src/gen/paths.rs000064400000000000000000000060321046102023000161250ustar 00000000000000use crate::gen::inside::protobuf_crate_path; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::path::RustPath; use crate::gen::strx; use crate::gen::well_known_types::WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES; use crate::Customize; // Copy-pasted from libsyntax. fn ident_start(c: char) -> bool { (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::(); RustIdent::new(&name) } /// Used in protobuf-codegen-identical-test pub fn proto_name_to_rs(proto_file_path: &str) -> String { format!("{}.rs", proto_path_to_rust_mod(proto_file_path)) } pub(crate) fn proto_path_to_fn_file_descriptor( proto_path: &str, customize: &Customize, ) -> RustPath { let protobuf_crate = protobuf_crate_path(customize); match proto_path { "rustproto.proto" => protobuf_crate.append("rustproto::file_descriptor".into()), "google/protobuf/descriptor.proto" => { protobuf_crate.append("descriptor::file_descriptor".into()) } s if WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES.contains(&s) => protobuf_crate .append_ident("well_known_types".into()) .append_ident(proto_path_to_rust_mod(s)) .append_ident("file_descriptor".into()), s => RustPath::super_path() .append_ident(proto_path_to_rust_mod(s)) .append_ident("file_descriptor".into()), } } #[cfg(test)] mod test { use super::proto_path_to_rust_mod; use crate::gen::rust::ident::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"), ) } } protobuf-codegen-3.7.2/src/gen/protoc_insertion_point.rs000064400000000000000000000046131046102023000216220ustar 00000000000000use protobuf::reflect::EnumDescriptor; use protobuf::reflect::EnumValueDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; use crate::gen::code_writer::CodeWriter; use crate::Customize; /// Write `// @protoc_insertion_point(...)` before the element. /// /// This is similar to what `protoc` codegen does for C++ or Java. /// This can be used to modify the generated code. fn write_protoc_insertion_point(w: &mut CodeWriter, customize: &Customize, arg: &str) { for line in customize.before.iter().flat_map(|s| s.lines()) { w.write_line(line); } w.comment(&format!("@@protoc_insertion_point({})", arg)); } pub(crate) fn write_protoc_insertion_point_for_message( w: &mut CodeWriter, customize: &Customize, message: &MessageDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("message:{}", message.full_name())); } pub(crate) fn write_protoc_insertion_point_for_field( w: &mut CodeWriter, customize: &Customize, field: &FieldDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("field:{}", field.full_name())); } pub(crate) fn write_protoc_insertion_point_for_special_field( w: &mut CodeWriter, customize: &Customize, message: &MessageDescriptor, field: &str, ) { write_protoc_insertion_point( w, customize, &format!("special_field:{}.{}", message.full_name(), field), ); } pub(crate) fn write_protoc_insertion_point_for_enum( w: &mut CodeWriter, customize: &Customize, enumeration: &EnumDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("enum:{}", enumeration.full_name())); } pub(crate) fn write_protoc_insertion_point_for_enum_value( w: &mut CodeWriter, customize: &Customize, value: &EnumValueDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("enum_value:{}", value.full_name())); } pub(crate) fn write_protoc_insertion_point_for_oneof( w: &mut CodeWriter, customize: &Customize, oneof: &OneofDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("oneof:{}", oneof.full_name())); } pub(crate) fn write_protoc_insertion_point_for_oneof_field( w: &mut CodeWriter, customize: &Customize, field: &FieldDescriptor, ) { write_protoc_insertion_point(w, customize, &format!("oneof_field:{}", field.full_name())); } protobuf-codegen-3.7.2/src/gen/rust/component.rs000064400000000000000000000017571046102023000200160ustar 00000000000000use std::fmt; use std::fmt::Formatter; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::keywords::parse_rust_keyword; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub(crate) enum RustPathComponent { Ident(RustIdent), Keyword(&'static str), } impl fmt::Display for RustPathComponent { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { RustPathComponent::Ident(ident) => write!(f, "{}", ident), RustPathComponent::Keyword(keyword) => write!(f, "{}", keyword), } } } impl RustPathComponent { pub(crate) const SUPER: RustPathComponent = RustPathComponent::Keyword("super"); pub(crate) fn parse(s: &str) -> RustPathComponent { if s.starts_with("r#") { RustPathComponent::Ident(RustIdent::new(&s[2..])) } else if let Some(kw) = parse_rust_keyword(s) { RustPathComponent::Keyword(kw) } else { RustPathComponent::Ident(RustIdent::new(s)) } } } protobuf-codegen-3.7.2/src/gen/rust/ident.rs000064400000000000000000000031351046102023000171070ustar 00000000000000use std::fmt; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::keywords::is_rust_keyword; use crate::gen::rust::rel_path::RustRelativePath; /// Valid Rust identifier #[derive(Eq, PartialEq, Debug, Clone, Hash)] pub(crate) struct RustIdent(String); impl RustIdent { pub fn new(s: &str) -> RustIdent { assert!(!s.is_empty()); assert!(!s.contains("/"), "{}", s); assert!(!s.contains("."), "{}", s); assert!(!s.contains(":"), "{}", s); assert!(!s.contains(" "), "{}", s); assert!(!s.contains("#"), "{}", s); RustIdent(s.to_owned()) } pub(crate) fn get(&self) -> &str { &self.0 } pub fn into_string(self) -> String { self.0 } pub fn to_path(&self) -> RustIdentWithPath { RustIdentWithPath::from(&self.0) } pub(crate) fn into_rel_path(self) -> RustRelativePath { RustRelativePath::from_idents([self]) } } impl fmt::Display for RustIdent { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // Rust-protobuf uses `_` suffix to escape identifiers instead of raw identifiers // because some identifiers cannot be escaped as raw identifiers, // e.g. `r#self` is not a valid raw identifier. if is_rust_keyword(&self.0) { write!(f, "{}_", self.0) } else { write!(f, "{}", self.0) } } } impl From<&'_ str> for RustIdent { fn from(s: &str) -> Self { RustIdent::new(s) } } impl From for RustIdent { fn from(s: String) -> Self { RustIdent::new(&s) } } protobuf-codegen-3.7.2/src/gen/rust/ident_with_path.rs000064400000000000000000000023231046102023000211540ustar 00000000000000use std::fmt; use crate::gen::rust::component::RustPathComponent; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::path::RustPath; #[derive(Eq, PartialEq, Debug, Clone)] pub(crate) struct RustIdentWithPath { pub path: RustPath, pub ident: RustIdent, } impl RustIdentWithPath { pub fn new(s: String) -> RustIdentWithPath { let mut path = RustPath::from(s); let ident = match path.path.path.pop() { None => panic!("empty path"), Some(RustPathComponent::Ident(ident)) => ident, Some(RustPathComponent::Keyword(kw)) => { panic!("last path component is a keyword: {}", kw) } }; RustIdentWithPath { path, ident } } pub fn prepend_ident(&mut self, ident: RustIdent) { self.path.prepend_ident(ident) } pub fn to_path(&self) -> RustPath { self.path.clone().append_ident(self.ident.clone()) } } impl> From 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) } } protobuf-codegen-3.7.2/src/gen/rust/keywords.rs000064400000000000000000000027411046102023000176550ustar 00000000000000#[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", ]; // https://internals.rust-lang.org/t/raw-identifiers-dont-work-for-all-identifiers/9094/3 #[cfg_attr(rustfmt, rustfmt_skip)] static RUST_KEYWORDS_WHICH_CANNOT_BE_RAW: &'static [&'static str] = &[ "super", "self", "Self", "extern", "crate", ]; pub(crate) fn parse_rust_keyword(word: &str) -> Option<&'static str> { RUST_KEYWORDS.iter().cloned().find(|&kw| kw == word) } pub(crate) fn is_rust_keyword(ident: &str) -> bool { parse_rust_keyword(ident).is_some() } #[allow(dead_code)] pub(crate) fn is_rust_keyword_which_cannot_be_raw(ident: &str) -> bool { RUST_KEYWORDS_WHICH_CANNOT_BE_RAW .iter() .cloned() .find(|&kw| kw == ident) .is_some() } protobuf-codegen-3.7.2/src/gen/rust/mod.rs000064400000000000000000000003061046102023000165600ustar 00000000000000pub(crate) mod component; pub(crate) mod ident; pub(crate) mod ident_with_path; pub(crate) mod keywords; pub(crate) mod path; pub(crate) mod quote; pub(crate) mod rel_path; pub(crate) mod snippets; protobuf-codegen-3.7.2/src/gen/rust/path.rs000064400000000000000000000046421046102023000167440ustar 00000000000000use std::fmt; use crate::gen::rust::component::RustPathComponent; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; #[derive(Default, Eq, PartialEq, Debug, Clone)] pub(crate) struct RustPath { pub(crate) absolute: bool, pub(crate) path: RustRelativePath, } impl RustPath { pub fn super_path() -> RustPath { RustPath::from("super") } pub fn is_absolute(&self) -> bool { self.absolute } pub fn with_ident(self, ident: RustIdent) -> RustIdentWithPath { RustIdentWithPath { path: self, ident } } pub fn first(&self) -> Option { assert!(!self.absolute); self.path.first() } pub fn remove_first(&mut self) -> Option { assert!(!self.absolute); self.path.remove_first() } pub fn prepend_ident(&mut self, ident: RustIdent) { assert!(!self.absolute); self.path.prepend_ident(ident); } pub fn append(self, path: RustPath) -> RustPath { if path.absolute { path } else { RustPath { absolute: self.absolute, path: self.path.append(path.path), } } } pub(crate) fn append_component(mut self, component: RustPathComponent) -> RustPath { self.path.path.push(component); self } pub fn append_ident(self, ident: RustIdent) -> RustPath { self.append_component(RustPathComponent::Ident(ident)) } pub fn append_with_ident(self, path: RustIdentWithPath) -> RustIdentWithPath { self.append(path.path).with_ident(path.ident) } pub fn into_relative_or_panic(self) -> RustRelativePath { assert!(!self.absolute); self.path } } impl From<&'_ str> for RustPath { fn from(s: &str) -> Self { let (s, absolute) = if s.starts_with("::") { (&s[2..], true) } else { (s, false) }; RustPath { absolute, path: RustRelativePath::from(s), } } } impl From 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) } } protobuf-codegen-3.7.2/src/gen/rust/quote.rs000064400000000000000000000030051046102023000171350ustar 00000000000000fn hex_digit(value: u32) -> char { if value < 10 { (b'0' + value as u8) as char } else if value < 0x10 { (b'a' + value as u8 - 10) as char } else { unreachable!() } } pub fn quote_escape_str(s: &str) -> String { let mut buf = String::new(); buf.push('"'); buf.extend(s.chars().flat_map(|c| c.escape_default())); buf.push('"'); buf } pub fn quote_escape_bytes(bytes: &[u8]) -> String { let mut buf = String::new(); buf.push('b'); buf.push('"'); for &b in bytes { match b { b'\n' => buf.push_str(r"\n"), b'\r' => buf.push_str(r"\r"), b'\t' => buf.push_str(r"\t"), b'"' => buf.push_str("\\\""), b'\\' => buf.push_str(r"\\"), b'\x20'..=b'\x7e' => buf.push(b as char), _ => { buf.push_str(r"\x"); buf.push(hex_digit((b as u32) >> 4)); buf.push(hex_digit((b as u32) & 0x0f)); } } } buf.push('"'); buf } #[cfg(test)] mod test { use super::*; #[test] fn test_quote_escape_bytes() { assert_eq!("b\"\"", quote_escape_bytes(b"")); assert_eq!("b\"xyZW\"", quote_escape_bytes(b"xyZW")); assert_eq!("b\"aa\\\"bb\"", quote_escape_bytes(b"aa\"bb")); assert_eq!("b\"aa\\r\\n\\tbb\"", quote_escape_bytes(b"aa\r\n\tbb")); assert_eq!( "b\"\\x00\\x01\\x12\\xfe\\xff\"", quote_escape_bytes(b"\x00\x01\x12\xfe\xff") ); } } protobuf-codegen-3.7.2/src/gen/rust/rel_path.rs000064400000000000000000000047431046102023000176100ustar 00000000000000use std::fmt; use std::iter; use crate::gen::rust::component::RustPathComponent; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::path::RustPath; #[derive(Default, Eq, PartialEq, Debug, Clone)] pub(crate) struct RustRelativePath { pub(crate) path: Vec, } 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: I) -> RustRelativePath { RustRelativePath { path: i.into_iter().collect(), } } pub fn from_idents>(i: I) -> RustRelativePath { Self::from_components(i.into_iter().map(RustPathComponent::Ident)) } pub fn is_empty(&self) -> bool { self.path.is_empty() } pub fn first(&self) -> Option { self.path.iter().cloned().next() } pub fn remove_first(&mut self) -> Option { if self.path.is_empty() { None } else { Some(self.path.remove(0)) } } pub fn prepend_ident(&mut self, ident: RustIdent) { self.path.insert(0, RustPathComponent::Ident(ident)); } pub fn append(mut self, path: RustRelativePath) -> RustRelativePath { for c in path.path { self.path.push(c); } self } pub fn push_ident(&mut self, ident: RustIdent) { self.path.push(RustPathComponent::Ident(ident)); } pub fn append_ident(mut self, ident: RustIdent) -> RustRelativePath { self.push_ident(ident); self } pub fn to_reverse(&self) -> RustRelativePath { RustRelativePath::from_components( iter::repeat(RustPathComponent::SUPER).take(self.path.len()), ) } } impl fmt::Display for RustRelativePath { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for (i, c) in self.path.iter().enumerate() { if i != 0 { write!(f, "::")?; } write!(f, "{}", c)?; } Ok(()) } } impl From<&'_ str> for RustRelativePath { fn from(s: &str) -> Self { assert!(!s.starts_with("::"), "path is absolute: {:?}", s); RustRelativePath { path: s.split("::").map(RustPathComponent::parse).collect(), } } } protobuf-codegen-3.7.2/src/gen/rust/snippets.rs000064400000000000000000000005641046102023000176540ustar 00000000000000pub(crate) const EXPR_NONE: &str = "::std::option::Option::None"; pub(crate) const EXPR_VEC_NEW: &str = "::std::vec::Vec::new()"; fn expr_vec_with_capacity(capacity: &str) -> String { format!("::std::vec::Vec::with_capacity({})", capacity) } pub(crate) fn expr_vec_with_capacity_const(capacity: usize) -> String { expr_vec_with_capacity(&capacity.to_string()) } protobuf-codegen-3.7.2/src/gen/rust_types_values.rs000064400000000000000000000544031046102023000206130ustar 00000000000000use std::cmp; use once_cell::sync::Lazy; use protobuf::descriptor::*; use protobuf::reflect::FileDescriptor; use protobuf_parse::ProtobufAbsPath; use regex::Regex; use crate::customize::Customize; use crate::gen::field::type_ext::TypeExt; use crate::gen::file_and_mod::FileAndMod; use crate::gen::inside::protobuf_crate_path; use crate::gen::message::RustTypeMessage; use crate::gen::paths::proto_path_to_rust_mod; use crate::gen::rust::component::RustPathComponent; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::path::RustPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::rust::snippets::EXPR_NONE; use crate::gen::rust::snippets::EXPR_VEC_NEW; use crate::gen::scope::RootScope; use crate::gen::scope::WithScope; use crate::gen::strx::capitalize; use crate::gen::well_known_types::is_well_known_type_full; // Represent subset of rust types used in generated code #[derive(Debug, Clone, PartialEq, Eq)] pub(crate) enum RustType { // integer: signed?, size in bits Int(bool, u32), // param is size in bits Float(u32), Bool, Vec(Box), HashMap(Box, Box), BTreeMap(Box, Box), String, // [T], not &[T] Slice(Box), // str, not &str Str, Option(Box), MessageField(Box), // Box Uniq(Box), // &T Ref(Box), // protobuf message Message(RustTypeMessage), // protobuf enum, not any enum Enum(RustIdentWithPath, RustIdent, i32), // protobuf enum or unknown EnumOrUnknown(RustIdentWithPath, RustIdent, i32), // oneof enum Oneof(RustIdentWithPath), // 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::BTreeMap(ref key, ref value) => format!( "::std::collections::BTreeMap<{}, {}>", 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::MessageField(ref param) => format!( "{}::MessageField<{}>", protobuf_crate_path(customize), param.to_code(customize) ), RustType::Uniq(ref param) => format!("::std::boxed::Box<{}>", param.to_code(customize)), RustType::Ref(ref param) => format!("&{}", param.to_code(customize)), RustType::Message(ref name) => format!("{}", name), RustType::Enum(ref name, ..) | RustType::Oneof(ref name) => format!("{}", name), RustType::EnumOrUnknown(ref name, ..) => format!( "{}::EnumOrUnknown<{}>", protobuf_crate_path(customize), name ), RustType::Group => format!(""), RustType::Bytes => format!("::bytes::Bytes"), RustType::Chars => format!("{}::Chars", protobuf_crate_path(customize)), } } } impl RustType { pub(crate) fn u8() -> RustType { RustType::Int(false, 8) } pub(crate) fn i32() -> RustType { RustType::Int(true, 32) } /// `&str`. pub(crate) fn amp_str() -> RustType { RustType::Str.wrap_ref() } /// `&[u8]`. pub(crate) fn amp_slice_of_u8() -> RustType { RustType::u8().wrap_slice().wrap_ref() } /// Type is rust primitive? pub(crate) 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 if let RustType::EnumOrUnknown(..) = *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, } } fn is_enum_or_unknown(&self) -> bool { match *self { RustType::EnumOrUnknown(..) => true, _ => false, } } pub fn is_ref(&self) -> Option<&RustType> { match *self { RustType::Ref(ref v) => Some(&**v), _ => None, } } pub fn is_box(&self) -> Option<&RustType> { match *self { RustType::Uniq(ref v) => Some(&**v), _ => None, } } // default value for type pub fn default_value(&self, customize: &Customize, const_expr: bool) -> String { match *self { RustType::Ref(ref t) if t.is_str() => "\"\"".to_owned(), RustType::Ref(ref t) if t.is_slice().is_some() => "&[]".to_owned(), RustType::Int(..) => "0".to_owned(), RustType::Float(..) => "0.".to_owned(), RustType::Bool => "false".to_owned(), RustType::Vec(..) => EXPR_VEC_NEW.to_owned(), RustType::HashMap(..) => "::std::collections::HashMap::new()".to_owned(), RustType::BTreeMap(..) => "::std::collections::BTreeMap::new()".to_owned(), RustType::String => "::std::string::String::new()".to_owned(), RustType::Bytes => "::bytes::Bytes::new()".to_owned(), RustType::Chars => format!("{}::Chars::new()", protobuf_crate_path(customize)), RustType::Option(..) => EXPR_NONE.to_owned(), RustType::MessageField(..) => { format!("{}::MessageField::none()", protobuf_crate_path(customize)) } RustType::Message(ref name) => format!("{}::new()", name), RustType::Ref(ref m) if m.is_message() => match **m { 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), RustType::EnumOrUnknown(_, _, number) if const_expr => format!( "{}::EnumOrUnknown::from_i32({})", protobuf_crate_path(customize), number, ), RustType::EnumOrUnknown(ref name, ref default, ..) if !const_expr => format!( "{}::EnumOrUnknown::new({}::{})", protobuf_crate_path(customize), name, default ), _ => panic!("cannot create default value for: {:?}", self), } } pub fn default_value_typed(self, customize: &Customize, const_expr: bool) -> RustValueTyped { RustValueTyped { value: self.default_value(customize, const_expr), 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!("{} = {}", v, EXPR_NONE), RustType::Vec(..) | RustType::Bytes | RustType::Chars | RustType::String | RustType::MessageField(..) | RustType::HashMap(..) | RustType::BTreeMap(..) => format!("{}.clear()", v), RustType::Bool | RustType::Float(..) | RustType::Int(..) | RustType::Enum(..) | RustType::EnumOrUnknown(..) => { format!("{} = {}", v, self.default_value(customize, false)) } ref ty => panic!("cannot clear type: {:?}", ty), } } // 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)) } // https://github.com/rust-lang-nursery/rustfmt/issues/3131 #[cfg_attr(rustfmt, rustfmt_skip)] fn try_into_target(&self, target: &RustType, v: &str, customize: &Customize) -> Result { { if let Some(t1) = self.is_ref().and_then(|t| t.is_box()) { if let Some(t2) = target.is_ref() { if t1 == t2 { return Ok(format!("&**{}", v)); } } } } match (self, target) { (x, y) if x == y => return Ok(format!("{}", v)), (&RustType::Ref(ref x), y) if **x == *y => return Ok(format!("*{}", v)), (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, } => { 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!("{}::Enum::value(&{})", protobuf_crate_path(customize), v)) }, (&RustType::EnumOrUnknown(..), &RustType::Int(true, 32)) => { return Ok(format!("{}::EnumOrUnknown::value(&{})", protobuf_crate_path(customize), v)) }, (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum() => { return Ok(format!("{}::Enum::value({})", protobuf_crate_path(customize), v)) } (&RustType::Ref(ref t), &RustType::Int(true, 32)) if t.is_enum_or_unknown() => { return Ok(format!("{}::EnumOrUnknown::value({})", protobuf_crate_path(customize), v)) }, (&RustType::EnumOrUnknown(ref f, ..), &RustType::Enum(ref t, ..)) if f == t => { return Ok(format!("{}::EnumOrUnknown::enum_value_or_default(&{})", protobuf_crate_path(customize), v)) } (&RustType::Enum(ref f, ..), &RustType::EnumOrUnknown(ref t, ..)) if f == t => { return Ok(format!("{}::EnumOrUnknown::new({})", protobuf_crate_path(customize), v)) } _ => (), }; 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::Slice(p.clone()), &RustType::Bytes => RustType::Slice(Box::new(RustType::u8())), &RustType::Message(ref p) => RustType::Message(p.clone()), &RustType::Uniq(ref p) => RustType::Uniq(p.clone()), x => panic!("no ref type for {:?}", x), })) } pub(crate) fn wrap_ref(&self) -> RustType { RustType::Ref(Box::new(self.clone())) } pub(crate) fn wrap_slice(&self) -> RustType { RustType::Slice(Box::new(self.clone())) } pub fn elem_type(&self) -> RustType { match self { &RustType::Option(ref ty) => (**ty).clone(), &RustType::MessageField(ref ty) => (**ty).clone(), x => panic!("cannot get elem type of {:?}", x), } } // 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::MessageField(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) } } 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: &FileDescriptor) -> bool { file.package() == "google.protobuf" && file_last_component(file.name()) == "descriptor.proto" } fn make_path_to_path(source: &RustRelativePath, dest: &RustPath) -> RustPath { if dest.is_absolute() { return dest.clone(); } let mut source = source.clone(); let mut dest = dest.clone(); while !source.is_empty() && source.first() == dest.first() { source.remove_first().unwrap(); dest.remove_first().unwrap(); } source.to_reverse().into_path().append(dest) } pub(crate) fn make_path(source: &RustRelativePath, dest: &RustIdentWithPath) -> RustIdentWithPath { make_path_to_path(source, &dest.path).with_ident(dest.ident.clone()) } pub(crate) fn message_or_enum_to_rust_relative( message_or_enum: &dyn WithScope, current: &FileAndMod, ) -> RustIdentWithPath { let same_file = message_or_enum.file_descriptor().name() == current.file; if same_file { // field type is a message or enum declared in the same file make_path(¤t.relative_mod, &message_or_enum.rust_name_to_file()) } else if let Some(name) = is_well_known_type_full(&message_or_enum.name_absolute()) { // Well-known types are included in rust-protobuf library // https://developers.google.com/protocol-buffers/docs/reference/google.protobuf let file_descriptor = message_or_enum.file_descriptor(); static REGEX: Lazy = Lazy::new(|| Regex::new(r"^google/protobuf/([^/]+\.proto)$").unwrap()); let captures = REGEX .captures(file_descriptor.name()) .unwrap_or_else(|| panic!("`{}` does not match the regex", file_descriptor.name())); let file_name = captures.get(1).unwrap().as_str(); let mod_name = proto_path_to_rust_mod(file_name); RustIdentWithPath::from(format!( "{protobuf_crate}::well_known_types::{mod_name}::{name}", protobuf_crate = protobuf_crate_path(¤t.customize), )) } else if is_descriptor_proto(&message_or_enum.file_descriptor()) { // Messages defined in descriptor.proto RustIdentWithPath::from(format!( "{}::descriptor::{}", protobuf_crate_path(¤t.customize), message_or_enum.rust_name_to_file() )) } else { current .relative_mod .to_reverse() .into_path() .append_component(RustPathComponent::SUPER) .append_with_ident(message_or_enum.rust_name_with_file()) } } pub(crate) fn type_name_to_rust_relative( type_name: &ProtobufAbsPath, current: &FileAndMod, root_scope: &RootScope, ) -> RustIdentWithPath { assert!(!type_name.is_root()); let message_or_enum = root_scope.find_message_or_enum(type_name); message_or_enum_to_rust_relative(&message_or_enum, current) } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum PrimitiveTypeVariant { Default, TokioBytes, } pub enum _TokioBytesType { Bytes, Chars, } // ProtobufType trait name pub(crate) enum ProtobufTypeGen { Primitive(field_descriptor_proto::Type, PrimitiveTypeVariant), Message(RustTypeMessage), EnumOrUnknown(RustIdentWithPath), } impl ProtobufTypeGen { pub(crate) fn protobuf_value(&self, customize: &Customize) -> String { match self { ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => { t.rust_type().to_code(customize) } ProtobufTypeGen::Primitive(_, PrimitiveTypeVariant::TokioBytes) => unimplemented!(), ProtobufTypeGen::Message(m) => m.0.to_string(), ProtobufTypeGen::EnumOrUnknown(e) => format!( "{protobuf_crate}::EnumOrUnknown<{e}>", protobuf_crate = protobuf_crate_path(customize) ), } } pub(crate) fn _rust_type(&self, customize: &Customize) -> String { match self { &ProtobufTypeGen::Primitive(t, PrimitiveTypeVariant::Default) => format!( "{}::reflect::types::ProtobufType{}", protobuf_crate_path(customize), capitalize(t.protobuf_name()) ), &ProtobufTypeGen::Primitive( field_descriptor_proto::Type::TYPE_BYTES, PrimitiveTypeVariant::TokioBytes, ) => format!( "{}::reflect::types::ProtobufTypeTokioBytes", protobuf_crate_path(customize) ), &ProtobufTypeGen::Primitive( field_descriptor_proto::Type::TYPE_STRING, PrimitiveTypeVariant::TokioBytes, ) => format!( "{}::reflect::types::ProtobufTypeTokioChars", protobuf_crate_path(customize) ), &ProtobufTypeGen::Primitive(.., PrimitiveTypeVariant::TokioBytes) => unreachable!(), &ProtobufTypeGen::Message(ref name) => format!( "{}::reflect::types::ProtobufTypeMessage<{}>", protobuf_crate_path(customize), name ), &ProtobufTypeGen::EnumOrUnknown(ref name) => format!( "{}::reflect::types::ProtobufTypeEnumOrUnknown<{}>", protobuf_crate_path(customize), name ), } } } #[cfg(test)] mod test { use super::*; #[test] fn into_target_ref_box_to_ref() { let t1 = RustType::Ref(Box::new(RustType::Uniq(Box::new(RustType::Message( RustTypeMessage::from("Ab"), ))))); let t2 = RustType::Ref(Box::new(RustType::Message(RustTypeMessage::from("Ab")))); assert_eq!("&**v", t1.into_target(&t2, "v", &Customize::default())); } } protobuf-codegen-3.7.2/src/gen/scope.rs000064400000000000000000000344111046102023000161210ustar 00000000000000use std::ops::Deref; use protobuf::reflect::EnumDescriptor; use protobuf::reflect::EnumValueDescriptor; use protobuf::reflect::FieldDescriptor; use protobuf::reflect::FileDescriptor; use protobuf::reflect::MessageDescriptor; use protobuf::reflect::OneofDescriptor; use protobuf_parse::ProtobufAbsPath; use protobuf_parse::ProtobufAbsPathRef; use protobuf_parse::ProtobufIdentRef; use protobuf_parse::ProtobufRelPath; use protobuf_parse::ProtobufRelPathRef; use crate::customize::Customize; use crate::gen::field::rust_field_name_for_protobuf_field_name; use crate::gen::file_and_mod::FileAndMod; use crate::gen::map::map_entry; use crate::gen::message::message_name_to_nested_mod_name; use crate::gen::paths::proto_path_to_rust_mod; use crate::gen::rust::ident::RustIdent; use crate::gen::rust::ident_with_path::RustIdentWithPath; use crate::gen::rust::rel_path::RustRelativePath; use crate::gen::strx::capitalize; pub(crate) struct RootScope<'a> { pub file_descriptors: &'a [FileDescriptor], } impl<'a> RootScope<'a> { fn packages(&'a self) -> Vec> { self.file_descriptors .iter() .map(|fd| FileScope { file_descriptor: fd, }) .collect() } // find enum by fully qualified name pub fn _find_enum(&'a self, fqn: &ProtobufAbsPath) -> EnumWithScope<'a> { match self.find_message_or_enum(fqn) { MessageOrEnumWithScope::Enum(e) => e, _ => panic!("not an enum: {}", fqn), } } // find message by fully qualified name pub fn find_message(&'a self, fqn: &ProtobufAbsPath) -> MessageWithScope<'a> { match self.find_message_or_enum(fqn) { MessageOrEnumWithScope::Message(m) => m, _ => panic!("not a message: {}", fqn), } } // find message or enum by fully qualified name pub fn find_message_or_enum(&'a self, fqn: &ProtobufAbsPath) -> MessageOrEnumWithScope<'a> { assert!(!fqn.is_root()); self.packages() .into_iter() .flat_map(|p| p.find_message_or_enum_abs(fqn)) .next() .expect(&format!("enum not found by name: {}", fqn)) } } #[derive(Clone, Debug)] pub(crate) struct FileScope<'a> { pub file_descriptor: &'a FileDescriptor, } impl<'a> Deref for FileScope<'a> { type Target = FileDescriptor; fn deref(&self) -> &Self::Target { self.file_descriptor } } impl<'a> FileScope<'a> { fn package(&self) -> ProtobufAbsPath { ProtobufAbsPath::package_from_file_descriptor(self.file_descriptor) } pub fn to_scope(&self) -> Scope<'a> { Scope { file_scope: self.clone(), path: Vec::new(), } } fn find_message_or_enum( &self, name: &ProtobufRelPathRef, ) -> Option> { self.find_messages_and_enums() .into_iter() .filter(|e| e.protobuf_name_to_package().as_ref() == name) .next() } fn find_message_or_enum_abs( &self, name: &ProtobufAbsPathRef, ) -> Option> { let name = name.to_owned(); match name.remove_prefix(&self.package()) { Some(rem) => self.find_message_or_enum(&rem), None => None, } } // find all enums in given file descriptor pub fn find_enums(&self) -> Vec> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.enums()); }); r } /// Find all messages in given file descriptor pub fn find_messages(&self) -> Vec> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.messages()); }); r } /// Find all messages in given file descriptor, except map messages pub fn find_messages_except_map(&self) -> Vec> { self.find_messages() .into_iter() .filter(|m| !m.is_map()) .collect() } /// find all messages and enums in given file descriptor pub fn find_messages_and_enums(&self) -> Vec> { let mut r = Vec::new(); self.to_scope().walk_scopes(|scope| { r.extend(scope.messages_and_enums()); }); r } } #[derive(Clone, Debug)] pub(crate) struct Scope<'a> { pub file_scope: FileScope<'a>, pub path: Vec, } impl<'a> Scope<'a> { pub(crate) fn file_descriptor(&self) -> FileDescriptor { self.file_scope.file_descriptor.clone() } // get message descriptors in this scope fn message_descriptors(&self) -> Vec { if self.path.is_empty() { self.file_scope.file_descriptor.messages().collect() } else { self.path.last().unwrap().nested_messages().collect() } } // get enum descriptors in this scope fn enum_descriptors(&self) -> Vec { if self.path.is_empty() { self.file_scope.file_descriptor.enums().collect() } else { self.path.last().unwrap().nested_enums().collect() } } // get messages with attached scopes in this scope pub fn messages(&self) -> Vec> { self.message_descriptors() .into_iter() .map(|message| MessageWithScope { scope: self.clone(), message, }) .collect() } // get enums with attached scopes in this scope pub fn enums(&self) -> Vec> { self.enum_descriptors() .into_iter() .map(|en| EnumWithScope { scope: self.clone(), en, }) .collect() } // get messages and enums with attached scopes in this scope pub fn messages_and_enums(&self) -> Vec> { self.messages() .into_iter() .map(|m| MessageOrEnumWithScope::Message(m)) .chain( self.enums() .into_iter() .map(|m| MessageOrEnumWithScope::Enum(m)), ) .collect() } // nested scopes, i. e. scopes of nested messages fn nested_scopes(&self) -> Vec> { self.message_descriptors() .into_iter() .map(|m| { let mut nested = self.clone(); nested.path.push(m); nested }) .collect() } fn walk_scopes_impl)>(&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(&self, mut callback: F) where F: FnMut(&Scope<'a>), { self.walk_scopes_impl(&mut callback); } pub fn rust_path_to_file(&self) -> RustRelativePath { RustRelativePath::from_idents( self.path .iter() .map(|m| message_name_to_nested_mod_name(m.name())), ) } pub fn path_str(&self) -> String { let v: Vec<&str> = self.path.iter().map(|m| m.name()).collect(); v.join(".") } pub fn prefix(&self) -> String { let path_str = self.path_str(); if path_str.is_empty() { path_str } else { format!("{}.", path_str) } } pub fn protobuf_path_to_file(&self) -> ProtobufRelPath { ProtobufRelPath::from_components(self.path.iter().map(|m| ProtobufIdentRef::new(m.name()))) } pub fn protobuf_absolute_path(&self) -> ProtobufAbsPath { let mut r = self.file_scope.package(); r.push_relative(&self.protobuf_path_to_file()); r } pub fn file_and_mod(&self, customize: Customize) -> FileAndMod { FileAndMod { file: self.file_scope.file_descriptor.proto().name().to_owned(), relative_mod: self.rust_path_to_file(), customize, } } } pub(crate) trait WithScope<'a> { fn scope(&self) -> &Scope<'a>; fn file_descriptor(&self) -> FileDescriptor { self.scope().file_descriptor() } // message or enum name fn name(&self) -> &ProtobufIdentRef; fn name_to_package(&self) -> String { let mut r = self.scope().prefix(); r.push_str(&self.name()); r } fn protobuf_name_to_package(&self) -> ProtobufRelPath { let r = self.scope().protobuf_path_to_file(); r.append_ident(ProtobufIdentRef::new(self.name())) } /// Return absolute name starting with dot fn name_absolute(&self) -> ProtobufAbsPath { let mut path = self.scope().protobuf_absolute_path(); path.push_simple(self.name()); path } // rust type name of this descriptor fn rust_name(&self) -> RustIdent { let rust_name = capitalize(&self.name()); RustIdent::new(&rust_name) } fn rust_name_to_file(&self) -> RustIdentWithPath { self.scope() .rust_path_to_file() .into_path() .with_ident(self.rust_name()) } // fully-qualified name of this type fn rust_name_with_file(&self) -> RustIdentWithPath { let mut r = self.rust_name_to_file(); r.prepend_ident(proto_path_to_rust_mod( self.scope().file_descriptor().name(), )); r } } #[derive(Clone, Debug)] pub(crate) struct MessageWithScope<'a> { pub scope: Scope<'a>, pub message: MessageDescriptor, } impl<'a> WithScope<'a> for MessageWithScope<'a> { fn scope(&self) -> &Scope<'a> { &self.scope } fn name(&self) -> &ProtobufIdentRef { ProtobufIdentRef::new(self.message.name()) } } impl<'a> MessageWithScope<'a> { pub fn into_scope(mut self) -> Scope<'a> { self.scope.path.push(self.message); self.scope } pub fn to_scope(&self) -> Scope<'a> { self.clone().into_scope() } pub fn fields(&self) -> Vec> { self.message .fields() .into_iter() .map(|field| FieldWithContext { field, message: self.clone(), }) .collect() } pub fn oneofs(&self) -> Vec> { self.message .oneofs() .into_iter() .map(|oneof| OneofWithContext { message: self.clone(), oneof, }) .collect() } pub fn mod_name(&self) -> RustIdent { message_name_to_nested_mod_name(self.message.name()) } /// This message is a special message which is a map. pub fn is_map(&self) -> bool { map_entry(self).is_some() } } #[derive(Clone, Debug)] pub(crate) struct EnumWithScope<'a> { pub scope: Scope<'a>, pub en: EnumDescriptor, } impl<'a> EnumWithScope<'a> { pub fn values(&self) -> Vec> { self.en .values() .into_iter() .map(|v| EnumValueWithContext { en: self.clone(), proto: v, }) .collect() } // find enum value by protobuf name pub fn value_by_name(&self, name: &str) -> EnumValueWithContext<'a> { self.values() .into_iter() .find(|v| v.proto.proto().name() == name) .unwrap() } } #[derive(Clone, Debug)] pub(crate) struct EnumValueWithContext<'a> { pub en: EnumWithScope<'a>, pub proto: EnumValueDescriptor, } impl<'a> EnumValueWithContext<'a> { pub fn rust_name(&self) -> RustIdent { // TODO: camel case or something. RustIdent::new(self.proto.name()) } } impl<'a> WithScope<'a> for EnumWithScope<'a> { fn scope(&self) -> &Scope<'a> { &self.scope } fn name(&self) -> &ProtobufIdentRef { ProtobufIdentRef::new(self.en.name()) } } pub(crate) enum MessageOrEnumWithScope<'a> { Message(MessageWithScope<'a>), Enum(EnumWithScope<'a>), } impl<'a> WithScope<'a> for MessageOrEnumWithScope<'a> { fn scope(&self) -> &Scope<'a> { match self { MessageOrEnumWithScope::Message(m) => m.scope(), MessageOrEnumWithScope::Enum(e) => e.scope(), } } fn name(&self) -> &ProtobufIdentRef { match self { MessageOrEnumWithScope::Message(m) => m.name(), MessageOrEnumWithScope::Enum(e) => e.name(), } } } #[derive(Clone)] pub(crate) struct FieldWithContext<'a> { pub field: FieldDescriptor, pub message: MessageWithScope<'a>, } impl<'a> Deref for FieldWithContext<'a> { type Target = FieldDescriptor; fn deref(&self) -> &Self::Target { &self.field } } impl<'a> FieldWithContext<'a> { pub fn is_oneof(&self) -> bool { self.field.containing_oneof().is_some() } pub fn oneof(&self) -> Option> { match self.field.containing_oneof() { Some(oneof) => Some(OneofWithContext { message: self.message.clone(), oneof, }), None => None, } } } #[derive(Clone)] pub(crate) struct OneofVariantWithContext<'a> { pub oneof: &'a OneofWithContext<'a>, pub field: FieldDescriptor, } #[derive(Clone)] pub(crate) struct OneofWithContext<'a> { pub oneof: OneofDescriptor, pub message: MessageWithScope<'a>, } impl<'a> OneofWithContext<'a> { pub fn field_name(&'a self) -> RustIdent { return rust_field_name_for_protobuf_field_name(self.oneof.name()); } // rust type name of enum pub fn rust_name(&self) -> RustIdentWithPath { let type_name = RustIdent::from(capitalize(self.oneof.name())); self.message .to_scope() .rust_path_to_file() .into_path() .with_ident(type_name) } pub fn variants(&'a self) -> Vec> { self.message .fields() .into_iter() .filter(|f| f.field.containing_oneof().as_ref() == Some(&self.oneof)) .map(|f| OneofVariantWithContext { oneof: self, field: f.field, }) .collect() } } protobuf-codegen-3.7.2/src/gen/strx.rs000064400000000000000000000024761046102023000160160ustar 00000000000000pub 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")); } } protobuf-codegen-3.7.2/src/gen/well_known_types.rs000064400000000000000000000062271046102023000204170ustar 00000000000000use protobuf_parse::ProtobufAbsPath; use protobuf_parse::ProtobufRelPath; use protobuf_parse::ProtobufRelPathRef; use crate::compiler_plugin; use crate::gen::code_writer::CodeWriter; use crate::gen::paths::proto_path_to_rust_mod; pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_NAMES: &[&str] = &[ "any.proto", "api.proto", "duration.proto", "empty.proto", "field_mask.proto", "source_context.proto", "struct.proto", "timestamp.proto", "type.proto", "wrappers.proto", ]; pub(crate) static WELL_KNOWN_TYPES_PROTO_FILE_FULL_NAMES: &[&str] = &[ "google/protobuf/any.proto", "google/protobuf/api.proto", "google/protobuf/duration.proto", "google/protobuf/empty.proto", "google/protobuf/field_mask.proto", "google/protobuf/source_context.proto", "google/protobuf/struct.proto", "google/protobuf/timestamp.proto", "google/protobuf/type.proto", "google/protobuf/wrappers.proto", ]; static NAMES: &'static [&'static str] = &[ "Any", "Api", "BoolValue", "BytesValue", "DoubleValue", "Duration", "Empty", "Enum", "EnumValue", "Field", "Field.Cardinality", "Field.Kind", "FieldMask", "FloatValue", "Int32Value", "Int64Value", "ListValue", "Method", "Mixin", "NullValue", "Option", "SourceContext", "StringValue", "Struct", "Syntax", "Timestamp", "Type", "UInt32Value", "UInt64Value", "Value", ]; fn is_well_known_type(name: &ProtobufRelPathRef) -> bool { NAMES.iter().any(|&n| n == format!("{}", name)) } pub(crate) fn is_well_known_type_full(name: &ProtobufAbsPath) -> Option { if let Some(rem) = name.remove_prefix(&ProtobufAbsPath::from(".google.protobuf")) { if is_well_known_type(rem) { Some(rem.to_owned()) } else { None } } else { None } } pub(crate) fn gen_well_known_types_mod() -> compiler_plugin::GenResult { let v = CodeWriter::with_no_error(|w| { w.comment("This file is generated. Do not edit"); w.comment("@generated"); w.mod_doc("Generated code for \"well known types\""); w.mod_doc(""); w.mod_doc("[This document](https://developers.google.com/protocol-buffers/docs/reference/google.protobuf) describes these types."); w.write_line(""); w.write_line("#![allow(unused_attributes)]"); w.write_line("#![cfg_attr(rustfmt, rustfmt::skip)]"); w.write_line(""); for m in WELL_KNOWN_TYPES_PROTO_FILE_NAMES { w.write_line(&format!("pub mod {};", proto_path_to_rust_mod(m))); } }); compiler_plugin::GenResult { name: "well_known_types_mod.rs".to_string(), content: v.into_bytes(), } } #[cfg(test)] mod test { use super::*; #[test] fn test_is_well_known_type_full() { assert_eq!( Some(ProtobufRelPath::from("BoolValue")), is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.BoolValue")) ); assert_eq!( None, is_well_known_type_full(&ProtobufAbsPath::from(".google.protobuf.Fgfg")) ); } } protobuf-codegen-3.7.2/src/gen_and_write.rs000064400000000000000000000032021046102023000170360ustar 00000000000000#![doc(hidden)] use std::fs; use std::io; use std::path::Path; use protobuf::descriptor::FileDescriptorProto; use protobuf_parse::ProtoPathBuf; use crate::customize::CustomizeCallback; use crate::gen::all::gen_all; use crate::Customize; #[derive(Debug, thiserror::Error)] enum Error { #[error("output path `{0}` is not a directory")] OutputIsNotDirectory(String), #[error("output path `{0}` does not exist or not accessible")] OutputDoesNotExistOrNotAccssible(String, #[source] io::Error), #[error("failed to create file `{0}`: {1}")] FailedToWriteFile(String, #[source] io::Error), } #[doc(hidden)] pub fn gen_and_write( file_descriptors: &[FileDescriptorProto], parser: &str, files_to_generate: &[ProtoPathBuf], out_dir: &Path, customize: &Customize, customize_callback: &dyn CustomizeCallback, ) -> anyhow::Result<()> { match out_dir.metadata() { Ok(m) => { if !m.is_dir() { return Err(Error::OutputIsNotDirectory(out_dir.display().to_string()).into()); } } Err(e) => { return Err( Error::OutputDoesNotExistOrNotAccssible(out_dir.display().to_string(), e).into(), ); } } let results = gen_all( file_descriptors, parser, files_to_generate, customize, customize_callback, )?; for r in &results { let mut file_path = out_dir.to_owned(); file_path.push(&r.name); fs::write(&file_path, r.content.as_slice()) .map_err(|e| Error::FailedToWriteFile(file_path.display().to_string(), e))?; } Ok(()) } protobuf-codegen-3.7.2/src/lib.rs000064400000000000000000000114321046102023000150030ustar 00000000000000//! # Protobuf code generator for `protobuf` crate //! //! This crate is useful mostly from `build.rs` scripts to generate `.rs` files during the build. //! //! # How to generate code //! //! There are three main ways to generate `.rs` files from `.proto` files: //! * using `protoc` command line tool and `protoc-gen-rs` plugin //! * using this crate `Codegen` with pure rust parser //! * using this crate `Codegen` with `protoc` parser //! //! Which one should you use depends on your needs. //! //! If you are using non-cargo build system (like Bazel), you might prefer //! using `protoc-gen-rs` plugin for `protoc`. //! //! If you build with `cargo`, you probably want to use `Codegen` from this crate. //! //! # Protoc parser vs pure rust parser //! //! There are two protobuf parsers which can be plugged into this crate: //! * `protoc`-based parser (`protoc` is a command like utility from Google protobuf) //! * pure rust parser (`protobuf-parse` crate) //! //! `protoc`-based parser is expected to parse `.proto` files very correctly: //! all Google's protobuf implementations rely on it. //! //! While there are no known bugs in `protobuf-parse`, it is not tested very well. //! Also `protobuf-parse` does not implement certain rarely used features of `.proto` parser, //! mostly complex message options specified in `.proto` files. //! I never saw anyone using them, but you have been warned. //! //! Note `protoc` command can be obtained from //! [`protoc-bin-vendored`](https://docs.rs/protoc-bin-vendored) crate. //! //! # Example //! //! ```no_run //! # mod protoc_bin_vendored { //! # pub fn protoc_bin_path() -> Result { //! # unimplemented!() //! # } //! # } //! // Use this in build.rs //! protobuf_codegen::Codegen::new() //! // Use `protoc` parser, optional. //! .protoc() //! // Use `protoc-bin-vendored` bundled protoc command, optional. //! .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap()) //! // All inputs and imports from the inputs must reside in `includes` directories. //! .includes(&["src/protos"]) //! // Inputs must reside in some of include paths. //! .input("src/protos/apple.proto") //! .input("src/protos/banana.proto") //! // Specify output directory relative to Cargo output directory. //! .cargo_out_dir("protos") //! .run_from_script(); //! ``` //! //! ## How to use `protoc-gen-rs` //! //! 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 C++ protobuf library. //! //! 1) Install `protoc-gen-rs` program (which is `protoc` plugin) //! //! It can be installed either from source or with `cargo install protobuf-codegen` command. //! //! 2) Add `protoc-gen-rs` to $PATH //! //! If you installed it with cargo, it should be //! //! ```sh //! PATH="$HOME/.cargo/bin:$PATH" //! ``` //! //! 3) Generate .rs files: //! //! ```sh //! protoc --rs_out . foo.proto //! ``` //! //! This will generate .rs files in current directory. //! //! # Customize generate code //! //! Sometimes generated code need to be adjusted, e. g. to have custom derives. //! //! rust-protobuf provides two options to do that: //! * generated `.rs` files contain `@@protoc_insertion_point(...)` markers //! (similar markers inserts Google's protobuf generator for C++ or Java). //! Simple script `sed` one-liners can be used to replace these markers with custom annotations. //! * `Codegen::customize_callback` can be used to patch generated code //! when invoked from `build.rs` script. //! //! # Serde //! //! rust-protobuf since version 3 no longer directly supports serde. //! //! Rust-protobuf 3 fully supports: //! * runtime reflection //! * JSON parsing and printing via //! [`protobuf-json-mapping`](https://docs.rs/protobuf-json-mapping) //! //! Which covers the most of serde use cases. //! //! If you still need serde, generic customization callback (see above) can be used //! to insert `#[serde(...)]` annotations. //! //! [Example project](https://github.com/stepancheg/rust-protobuf/tree/master/protobuf-examples/customize-serde) //! in the rust-protobuf repository demonstrates how to do it. #![deny(rustdoc::broken_intra_doc_links)] mod codegen; mod compiler_plugin; mod customize; mod gen; pub mod gen_and_write; pub mod protoc_gen_rs; pub use codegen::Codegen; pub use customize::Customize; pub use customize::CustomizeCallback; #[doc(hidden)] pub use gen::paths::proto_name_to_rs; protobuf-codegen-3.7.2/src/protoc_gen_rs.rs000064400000000000000000000010531046102023000170760ustar 00000000000000#![doc(hidden)] use crate::compiler_plugin; use crate::customize::CustomizeCallbackDefault; use crate::gen::all::gen_all; use crate::Customize; #[doc(hidden)] pub fn protoc_gen_rust_main() { compiler_plugin::plugin_main(|r| { let customize = Customize::parse_from_parameter(r.parameter).expect("parse options"); gen_all( r.file_descriptors, "protoc --rs_out=...", r.files_to_generate, &customize, &CustomizeCallbackDefault, ) }) .expect("plugin failed"); }