diff -Nru rust-prost-build-0.12.6/Cargo.toml rust-prost-build-0.13.5/Cargo.toml --- rust-prost-build-0.12.6/Cargo.toml 1970-01-01 00:00:01.000000000 +0000 +++ rust-prost-build-0.13.5/Cargo.toml 1970-01-01 00:00:01.000000000 +0000 @@ -11,30 +11,46 @@ [package] edition = "2021" -rust-version = "1.70" +rust-version = "1.71.1" name = "prost-build" -version = "0.12.6" +version = "0.13.5" authors = [ "Dan Burkert ", "Lucio Franco ", "Casper Meijn ", "Tokio Contributors ", ] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false description = "Generate Prost annotated Rust types from Protocol Buffers files." -documentation = "https://docs.rs/prost-build" readme = "README.md" license = "Apache-2.0" repository = "https://github.com/tokio-rs/prost" -[dependencies.bytes] -version = "1" -default-features = false +[features] +cleanup-markdown = [ + "dep:pulldown-cmark", + "dep:pulldown-cmark-to-cmark", +] +default = ["format"] +format = [ + "dep:prettyplease", + "dep:syn", +] + +[lib] +name = "prost_build" +path = "src/lib.rs" [dependencies.heck] version = ">=0.4, <=0.5" [dependencies.itertools] -version = ">=0.10, <=0.12" +version = ">=0.10, <=0.14" features = ["use_alloc"] default-features = false @@ -49,7 +65,7 @@ version = "1.17.1" [dependencies.petgraph] -version = "0.6" +version = ">=0.6, <=0.7" default-features = false [dependencies.prettyplease] @@ -57,20 +73,20 @@ optional = true [dependencies.prost] -version = "0.12.6" +version = "0.13.5" default-features = false [dependencies.prost-types] -version = "0.12.6" +version = "0.13.5" default-features = false [dependencies.pulldown-cmark] -version = "0.9.1" +version = "0.12" optional = true default-features = false [dependencies.pulldown-cmark-to-cmark] -version = "10.0.1" +version = ">=16, <=20" optional = true [dependencies.regex] @@ -90,16 +106,5 @@ version = "3" [dev-dependencies.env_logger] -version = "0.10" +version = "0.11" default-features = false - -[features] -cleanup-markdown = [ - "dep:pulldown-cmark", - "dep:pulldown-cmark-to-cmark", -] -default = ["format"] -format = [ - "dep:prettyplease", - "dep:syn", -] diff -Nru rust-prost-build-0.12.6/Cargo.toml.orig rust-prost-build-0.13.5/Cargo.toml.orig --- rust-prost-build-0.12.6/Cargo.toml.orig 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/Cargo.toml.orig 2006-07-24 01:21:28.000000000 +0000 @@ -1,19 +1,13 @@ [package] name = "prost-build" -version = "0.12.6" -authors = [ - "Dan Burkert ", - "Lucio Franco ", - "Casper Meijn ", - "Tokio Contributors ", -] -license = "Apache-2.0" -repository = "https://github.com/tokio-rs/prost" -documentation = "https://docs.rs/prost-build" readme = "README.md" description = "Generate Prost annotated Rust types from Protocol Buffers files." -edition = "2021" -rust-version = "1.70" +version.workspace = true +authors.workspace = true +license.workspace = true +repository.workspace = true +edition.workspace = true +rust-version.workspace = true [features] default = ["format"] @@ -21,14 +15,13 @@ cleanup-markdown = ["dep:pulldown-cmark", "dep:pulldown-cmark-to-cmark"] [dependencies] -bytes = { version = "1", default-features = false } heck = { version = ">=0.4, <=0.5" } -itertools = { version = ">=0.10, <=0.12", default-features = false, features = ["use_alloc"] } +itertools = { version = ">=0.10, <=0.14", default-features = false, features = ["use_alloc"] } log = "0.4.4" multimap = { version = ">=0.8, <=0.10", default-features = false } -petgraph = { version = "0.6", default-features = false } -prost = { version = "0.12.6", path = "../prost", default-features = false } -prost-types = { version = "0.12.6", path = "../prost-types", default-features = false } +petgraph = { version = ">=0.6, <=0.7", default-features = false } +prost = { version = "0.13.5", path = "../prost", default-features = false } +prost-types = { version = "0.13.5", path = "../prost-types", default-features = false } tempfile = "3" once_cell = "1.17.1" regex = { version = "1.8.1", default-features = false, features = ["std", "unicode-bool"] } @@ -38,8 +31,8 @@ syn = { version = "2", features = ["full"], optional = true } # These two must be kept in sync, used for `cleanup-markdown` feature. -pulldown-cmark = { version = "0.9.1", optional = true, default-features = false } -pulldown-cmark-to-cmark = { version = "10.0.1", optional = true } +pulldown-cmark = { version = "0.12", optional = true, default-features = false } +pulldown-cmark-to-cmark = { version = ">=16, <=20", optional = true } [dev-dependencies] -env_logger = { version = "0.10", default-features = false } +env_logger = { version = "0.11", default-features = false } diff -Nru rust-prost-build-0.12.6/.cargo_vcs_info.json rust-prost-build-0.13.5/.cargo_vcs_info.json --- rust-prost-build-0.12.6/.cargo_vcs_info.json 1970-01-01 00:00:01.000000000 +0000 +++ rust-prost-build-0.13.5/.cargo_vcs_info.json 1970-01-01 00:00:01.000000000 +0000 @@ -1,6 +1,6 @@ { "git": { - "sha1": "d42c85e790263f78f6c626ceb0dac5fda0edcb41" + "sha1": "d505b184e933e1f9ff5679106ffc51b7e3c2755e" }, "path_in_vcs": "prost-build" } \ No newline at end of file diff -Nru rust-prost-build-0.12.6/debian/changelog rust-prost-build-0.13.5/debian/changelog --- rust-prost-build-0.12.6/debian/changelog 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/changelog 2025-04-10 12:03:14.000000000 +0000 @@ -1,3 +1,10 @@ +rust-prost-build (0.13.5-1) UNRELEASED-FIXME-AUTOGENERATED-DEBCARGO; urgency=medium + + * Team upload. + * Package prost-build 0.13.5 from crates.io using debcargo 2.7.8 + + -- Blair Noctis Thu, 10 Apr 2025 12:03:14 +0000 + rust-prost-build (0.12.6-2) unstable; urgency=medium * Team upload. diff -Nru rust-prost-build-0.12.6/debian/control rust-prost-build-0.13.5/debian/control --- rust-prost-build-0.12.6/debian/control 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/control 2025-04-10 12:03:14.000000000 +0000 @@ -2,19 +2,23 @@ Section: rust Priority: optional Build-Depends: debhelper-compat (= 13), - dh-sequence-cargo, - cargo:native , - rustc:native (>= 1.70) , + dh-sequence-cargo +Build-Depends-Arch: cargo:native , + rustc:native (>= 1.71.1) , libstd-rust-dev , - librust-bytes-1-dev , + librust-heck+default-dev (<< 0.6-~~) , librust-heck+default-dev (>= 0.4-~~) , + librust-itertools+use-alloc-dev (<< 0.15-~~) , librust-itertools+use-alloc-dev (>= 0.10-~~) , librust-log-0.4+default-dev (>= 0.4.4-~~) , + librust-multimap-dev (<< 0.11-~~) , librust-multimap-dev (>= 0.8-~~) , librust-once-cell-1+default-dev (>= 1.17.1-~~) , - librust-petgraph-0.6-dev , - librust-prost-0.12-dev (>= 0.12.6-~~) , - librust-prost-types-0.12-dev (>= 0.12.6-~~) , + librust-petgraph-dev (<< 0.8-~~) , + librust-petgraph-dev (>= 0.6-~~) , + librust-prettyplease-0.2+default-dev , + librust-prost-0.13-dev (>= 0.13.5-~~) , + librust-prost-types-0.13-dev (>= 0.13.5-~~) , librust-regex-1+std-dev (>= 1.8.1-~~) , librust-regex-1+unicode-bool-dev (>= 1.8.1-~~) , librust-syn-2+default-dev , @@ -37,15 +41,19 @@ Multi-Arch: same Depends: ${misc:Depends}, - librust-bytes-1-dev, + librust-heck+default-dev (<< 0.6-~~), librust-heck+default-dev (>= 0.4-~~), + librust-itertools+use-alloc-dev (<< 0.15-~~), librust-itertools+use-alloc-dev (>= 0.10-~~), librust-log-0.4+default-dev (>= 0.4.4-~~), + librust-multimap-dev (<< 0.11-~~), librust-multimap-dev (>= 0.8-~~), librust-once-cell-1+default-dev (>= 1.17.1-~~), - librust-petgraph-0.6-dev, - librust-prost-0.12-dev (>= 0.12.6-~~), - librust-prost-types-0.12-dev (>= 0.12.6-~~), + librust-petgraph-dev (<< 0.8-~~), + librust-petgraph-dev (>= 0.6-~~), + librust-prettyplease-0.2+default-dev, + librust-prost-0.13-dev (>= 0.13.5-~~), + librust-prost-types-0.13-dev (>= 0.13.5-~~), librust-regex-1+std-dev (>= 1.8.1-~~), librust-regex-1+unicode-bool-dev (>= 1.8.1-~~), librust-syn-2+default-dev, @@ -55,15 +63,15 @@ libprotobuf-dev Provides: librust-prost-build+default-dev (= ${binary:Version}), - librust-prost-build+syn-dev (= ${binary:Version}), + librust-prost-build+format-dev (= ${binary:Version}), librust-prost-build-0-dev (= ${binary:Version}), librust-prost-build-0+default-dev (= ${binary:Version}), - librust-prost-build-0+syn-dev (= ${binary:Version}), - librust-prost-build-0.12-dev (= ${binary:Version}), - librust-prost-build-0.12+default-dev (= ${binary:Version}), - librust-prost-build-0.12+syn-dev (= ${binary:Version}), - librust-prost-build-0.12.6-dev (= ${binary:Version}), - librust-prost-build-0.12.6+default-dev (= ${binary:Version}), - librust-prost-build-0.12.6+syn-dev (= ${binary:Version}) + librust-prost-build-0+format-dev (= ${binary:Version}), + librust-prost-build-0.13-dev (= ${binary:Version}), + librust-prost-build-0.13+default-dev (= ${binary:Version}), + librust-prost-build-0.13+format-dev (= ${binary:Version}), + librust-prost-build-0.13.5-dev (= ${binary:Version}), + librust-prost-build-0.13.5+default-dev (= ${binary:Version}), + librust-prost-build-0.13.5+format-dev (= ${binary:Version}) Description: Generate Prost annotated Rust types from Protocol Buffers files - Rust source code Source code for Debianized Rust crate "prost-build" diff -Nru rust-prost-build-0.12.6/debian/copyright.debcargo.hint rust-prost-build-0.13.5/debian/copyright.debcargo.hint --- rust-prost-build-0.12.6/debian/copyright.debcargo.hint 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/copyright.debcargo.hint 2025-04-10 12:03:14.000000000 +0000 @@ -29,8 +29,8 @@ Files: debian/* Copyright: - 2023-2024 Debian Rust Maintainers - 2023-2024 Emanuele Rocca + 2023-2025 Debian Rust Maintainers + 2023-2025 Emanuele Rocca License: Apache-2.0 License: Apache-2.0 diff -Nru rust-prost-build-0.12.6/debian/patches/disable-prettyplease.diff rust-prost-build-0.13.5/debian/patches/disable-prettyplease.diff --- rust-prost-build-0.12.6/debian/patches/disable-prettyplease.diff 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/patches/disable-prettyplease.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -Description: Remove dependency on prettyplease - The functionality provided by prettyplease is purely cosmetic and is not used - by any package in Debian. Remove it to avoid packaging an additional - dependency which is not needed at the moment. Also drop the 'format' feature, - which depends on it. -Author: Emanuele Rocca -Last-Update: 2023-01-26 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ -Index: prost-build/Cargo.toml -=================================================================== ---- prost-build.orig/Cargo.toml -+++ prost-build/Cargo.toml -@@ -52,10 +52,6 @@ version = "1.17.1" - version = "0.6" - default-features = false - --[dependencies.prettyplease] --version = "0.2" --optional = true -- - [dependencies.prost] - version = "0.12.6" - default-features = false -@@ -98,8 +94,4 @@ cleanup-markdown = [ - "dep:pulldown-cmark", - "dep:pulldown-cmark-to-cmark", - ] --default = ["format"] --format = [ -- "dep:prettyplease", -- "dep:syn", --] -+default = ["syn"] diff -Nru rust-prost-build-0.12.6/debian/patches/drop-feature-cleanup-markdown.diff rust-prost-build-0.13.5/debian/patches/drop-feature-cleanup-markdown.diff --- rust-prost-build-0.12.6/debian/patches/drop-feature-cleanup-markdown.diff 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/patches/drop-feature-cleanup-markdown.diff 2025-04-10 12:03:13.000000000 +0000 @@ -6,32 +6,26 @@ Last-Update: 2023-01-26 --- This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ -Index: prost-build/Cargo.toml -=================================================================== ---- prost-build.orig/Cargo.toml -+++ prost-build/Cargo.toml -@@ -60,15 +60,6 @@ default-features = false - version = "0.12.6" - default-features = false - --[dependencies.pulldown-cmark] --version = "0.9.1" --optional = true --default-features = false -- --[dependencies.pulldown-cmark-to-cmark] --version = "10.0.1" --optional = true -- - [dependencies.regex] - version = "1.8.1" - features = [ -@@ -90,8 +81,4 @@ version = "0.10" - default-features = false - +--- a/Cargo.toml ++++ b/Cargo.toml +@@ -34,6 +34,6 @@ [features] -cleanup-markdown = [ - "dep:pulldown-cmark", - "dep:pulldown-cmark-to-cmark", -] - default = ["syn"] ++#cleanup-markdown = [ ++# "dep:pulldown-cmark", ++# "dep:pulldown-cmark-to-cmark", ++#] + default = ["format"] +@@ -82,3 +82,3 @@ + +-[dependencies.pulldown-cmark] ++[disabled.dependencies.pulldown-cmark] + version = "0.12" +@@ -87,3 +87,3 @@ + +-[dependencies.pulldown-cmark-to-cmark] ++[disabled.dependencies.pulldown-cmark-to-cmark] + version = ">=16, <=20" diff -Nru rust-prost-build-0.12.6/debian/patches/relax-deps.patch rust-prost-build-0.13.5/debian/patches/relax-deps.patch --- rust-prost-build-0.12.6/debian/patches/relax-deps.patch 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/patches/relax-deps.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ ---- a/Cargo.toml -+++ b/Cargo.toml -@@ -31,7 +31,7 @@ - default-features = false - - [dependencies.heck] --version = ">=0.4, <=0.5" -+version = ">=0.4" - - [dependencies.itertools] - version = ">=0.10" -@@ -42,7 +42,7 @@ - version = "0.4.4" - - [dependencies.multimap] --version = ">=0.8, <=0.10" -+version = ">=0.8" - default-features = false - - [dependencies.once_cell] -@@ -77,7 +77,7 @@ - version = "3" - - [dev-dependencies.env_logger] --version = "0.10" -+version = "0.11" - default-features = false - - [features] diff -Nru rust-prost-build-0.12.6/debian/patches/relax-itertools.patch rust-prost-build-0.13.5/debian/patches/relax-itertools.patch --- rust-prost-build-0.12.6/debian/patches/relax-itertools.patch 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/patches/relax-itertools.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -Index: prost-build/Cargo.toml -=================================================================== ---- prost-build.orig/Cargo.toml -+++ prost-build/Cargo.toml -@@ -34,7 +34,7 @@ default-features = false - version = ">=0.4, <=0.5" - - [dependencies.itertools] --version = ">=0.10, <=0.12" -+version = ">=0.10" - features = ["use_alloc"] - default-features = false - diff -Nru rust-prost-build-0.12.6/debian/patches/series rust-prost-build-0.13.5/debian/patches/series --- rust-prost-build-0.12.6/debian/patches/series 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/patches/series 2025-04-10 12:03:13.000000000 +0000 @@ -1,4 +1 @@ -disable-prettyplease.diff drop-feature-cleanup-markdown.diff -relax-itertools.patch -relax-deps.patch diff -Nru rust-prost-build-0.12.6/debian/tests/control rust-prost-build-0.13.5/debian/tests/control --- rust-prost-build-0.12.6/debian/tests/control 2024-12-31 12:23:17.000000000 +0000 +++ rust-prost-build-0.13.5/debian/tests/control 2025-04-10 12:03:14.000000000 +0000 @@ -1,19 +1,19 @@ -Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.12.6 --all-targets --all-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.13.5 --all-targets --all-features Features: test-name=rust-prost-build:@ -Depends: dh-cargo (>= 31), rustc (>= 1.70), librust-env-logger-0.11-dev, @ +Depends: dh-cargo (>= 31), rustc (>= 1.71.1), librust-env-logger-0.11-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.12.6 --all-targets +Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.13.5 --all-targets Features: test-name=librust-prost-build-dev:default -Depends: dh-cargo (>= 31), rustc (>= 1.70), librust-env-logger-0.11-dev, @ +Depends: dh-cargo (>= 31), rustc (>= 1.71.1), librust-env-logger-0.11-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.12.6 --all-targets --no-default-features --features syn -Features: test-name=librust-prost-build-dev:syn -Depends: dh-cargo (>= 31), rustc (>= 1.70), librust-env-logger-0.11-dev, @ +Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.13.5 --all-targets --no-default-features --features format +Features: test-name=librust-prost-build-dev:format +Depends: dh-cargo (>= 31), rustc (>= 1.71.1), librust-env-logger-0.11-dev, @ Restrictions: allow-stderr, skip-not-installable -Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.12.6 --all-targets --no-default-features +Test-Command: /usr/share/cargo/bin/cargo-auto-test prost-build 0.13.5 --all-targets --no-default-features Features: test-name=librust-prost-build-dev: -Depends: dh-cargo (>= 31), rustc (>= 1.70), librust-env-logger-0.11-dev, @ +Depends: dh-cargo (>= 31), rustc (>= 1.71.1), librust-env-logger-0.11-dev, @ Restrictions: allow-stderr, skip-not-installable diff -Nru rust-prost-build-0.12.6/README.md rust-prost-build-0.13.5/README.md --- rust-prost-build-0.12.6/README.md 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/README.md 2006-07-24 01:21:28.000000000 +0000 @@ -7,17 +7,6 @@ a Cargo build. See the crate [documentation](https://docs.rs/prost-build/) for examples of how to integrate `prost-build` into a Cargo project. -## `protoc` - -`prost-build` uses `protoc` to parse the proto files. There are two ways to make `protoc` -available for `prost-build`: - -* Include `protoc` in your `PATH`. This can be done by following the [`protoc` install instructions]. -* Pass the `PROTOC=` environment variable with the path to - `protoc`. - -[`protoc` install instructions]: https://github.com/protocolbuffers/protobuf#protocol-compiler-installation - ## License `prost-build` is distributed under the terms of the Apache License (Version 2.0). diff -Nru rust-prost-build-0.12.6/src/ast.rs rust-prost-build-0.13.5/src/ast.rs --- rust-prost-build-0.12.6/src/ast.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/ast.rs 2006-07-24 01:21:28.000000000 +0000 @@ -103,7 +103,7 @@ let mut chars = sanitized_line.chars(); chars .next() - .map_or(false, |c| c != ' ' || chars.next() == Some(' ')) + .is_some_and(|c| c != ' ' || chars.next() == Some(' ')) } /// Sanitizes the line for rustdoc by performing the following operations: @@ -203,9 +203,6 @@ Event::Start(Tag::CodeBlock(kind)) => { Event::Start(Tag::CodeBlock(map_codeblock(kind))) } - Event::End(Tag::CodeBlock(kind)) => { - Event::End(Tag::CodeBlock(map_codeblock(kind))) - } e => e, } }, diff -Nru rust-prost-build-0.12.6/src/code_generator/c_escaping.rs rust-prost-build-0.13.5/src/code_generator/c_escaping.rs --- rust-prost-build-0.12.6/src/code_generator/c_escaping.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/code_generator/c_escaping.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,6 +1,7 @@ use log::debug; /// Based on [`google::protobuf::UnescapeCEscapeString`][1] +/// /// [1]: https://github.com/google/protobuf/blob/3.3.x/src/google/protobuf/stubs/strutil.cc#L312-L322 pub(super) fn unescape_c_escape_string(s: &str) -> Vec { let src = s.as_bytes(); diff -Nru rust-prost-build-0.12.6/src/code_generator.rs rust-prost-build-0.13.5/src/code_generator.rs --- rust-prost-build-0.12.6/src/code_generator.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/code_generator.rs 2006-07-24 01:21:28.000000000 +0000 @@ -15,9 +15,8 @@ }; use crate::ast::{Comments, Method, Service}; -use crate::extern_paths::ExternPaths; +use crate::context::Context; use crate::ident::{strip_enum_prefix, to_snake, to_upper_camel}; -use crate::message_graph::MessageGraph; use crate::Config; mod c_escaping; @@ -26,14 +25,13 @@ mod syntax; use syntax::Syntax; -pub struct CodeGenerator<'a> { - config: &'a mut Config, +/// State object for the code generation process on a single input file. +pub struct CodeGenerator<'a, 'b> { + context: &'a mut Context<'b>, package: String, type_path: Vec, source_info: Option, syntax: Syntax, - message_graph: &'a MessageGraph, - extern_paths: &'a ExternPaths, depth: u8, path: Vec, buf: &'a mut String, @@ -45,10 +43,6 @@ } } -fn prost_path(config: &Config) -> &str { - config.prost_path.as_deref().unwrap_or("::prost") -} - struct Field { descriptor: FieldDescriptorProto, path_index: i32, @@ -87,14 +81,12 @@ } } -impl<'a> CodeGenerator<'a> { - pub fn generate( - config: &mut Config, - message_graph: &MessageGraph, - extern_paths: &ExternPaths, - file: FileDescriptorProto, - buf: &mut String, - ) { +impl<'b> CodeGenerator<'_, 'b> { + fn config(&self) -> &Config { + self.context.config() + } + + pub(crate) fn generate(context: &mut Context<'b>, file: FileDescriptorProto, buf: &mut String) { let source_info = file.source_code_info.map(|mut s| { s.location.retain(|loc| { let len = loc.path.len(); @@ -105,13 +97,11 @@ }); let mut code_gen = CodeGenerator { - config, + context, package: file.package.unwrap_or_default(), type_path: Vec::new(), source_info, syntax: file.syntax.as_deref().into(), - message_graph, - extern_paths, depth: 0, path: Vec::new(), buf, @@ -139,7 +129,7 @@ } code_gen.path.pop(); - if code_gen.config.service_generator.is_some() { + if code_gen.context.service_generator_mut().is_some() { code_gen.path.push(6); for (idx, service) in file.service.into_iter().enumerate() { code_gen.path.push(idx as i32); @@ -147,7 +137,7 @@ code_gen.path.pop(); } - if let Some(service_generator) = code_gen.config.service_generator.as_mut() { + if let Some(service_generator) = code_gen.context.service_generator_mut() { service_generator.finalize(code_gen.buf); } @@ -162,7 +152,11 @@ let fq_message_name = self.fq_name(&message_name); // Skip external types. - if self.extern_paths.resolve_ident(&fq_message_name).is_some() { + if self + .context + .resolve_extern_ident(&fq_message_name) + .is_some() + { return; } @@ -228,11 +222,14 @@ self.append_type_attributes(&fq_message_name); self.append_message_attributes(&fq_message_name); self.push_indent(); - self.buf - .push_str("#[allow(clippy::derive_partial_eq_without_eq)]\n"); self.buf.push_str(&format!( - "#[derive(Clone, PartialEq, {}::Message)]\n", - prost_path(self.config) + "#[derive(Clone, {}PartialEq, {}::Message)]\n", + if self.context.can_message_derive_copy(&fq_message_name) { + "Copy, " + } else { + "" + }, + self.context.prost_path() )); self.append_skip_debug(&fq_message_name); self.push_indent(); @@ -294,15 +291,16 @@ self.pop_mod(); } - if self.config.enable_type_names { + if self.config().enable_type_names { self.append_type_name(&message_name, &fq_message_name); } } fn append_type_name(&mut self, message_name: &str, fq_message_name: &str) { + let prost_path = self.context.prost_path(); + self.buf.push_str(&format!( - "impl {}::Name for {} {{\n", - self.config.prost_path.as_deref().unwrap_or("::prost"), + "impl {prost_path}::Name for {} {{\n", to_upper_camel(message_name) )); self.depth += 1; @@ -316,7 +314,6 @@ self.package, )); - let prost_path = self.config.prost_path.as_deref().unwrap_or("::prost"); let string_path = format!("{prost_path}::alloc::string::String"); let full_name = format!( @@ -326,11 +323,7 @@ self.type_path.join("."), if self.type_path.is_empty() { "" } else { "." }, ); - let domain_name = self - .config - .type_name_domains - .get_first(fq_message_name) - .map_or("", |name| name.as_str()); + let domain_name = self.context.type_name_domain(fq_message_name); self.buf.push_str(&format!( r#"fn full_name() -> {string_path} {{ "{full_name}".into() }}"#, @@ -346,7 +339,7 @@ fn append_type_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.type_attributes.get(fq_message_name) { + for attribute in self.context.type_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -355,20 +348,15 @@ fn append_message_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.message_attributes.get(fq_message_name) { + for attribute in self.context.message_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); } } - fn should_skip_debug(&self, fq_message_name: &str) -> bool { - assert_eq!(b'.', fq_message_name.as_bytes()[0]); - self.config.skip_debug.get(fq_message_name).next().is_some() - } - fn append_skip_debug(&mut self, fq_message_name: &str) { - if self.should_skip_debug(fq_message_name) { + if self.context.should_skip_debug(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str("#[prost(skip_debug)]"); self.buf.push('\n'); @@ -377,7 +365,7 @@ fn append_enum_attributes(&mut self, fq_message_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self.config.enum_attributes.get(fq_message_name) { + for attribute in self.context.enum_attributes(fq_message_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -386,11 +374,7 @@ fn append_field_attributes(&mut self, fq_message_name: &str, field_name: &str) { assert_eq!(b'.', fq_message_name.as_bytes()[0]); - for attribute in self - .config - .field_attributes - .get_field(fq_message_name, field_name) - { + for attribute in self.context.field_attributes(fq_message_name, field_name) { push_indent(self.buf, self.depth); self.buf.push_str(attribute); self.buf.push('\n'); @@ -399,10 +383,12 @@ fn append_field(&mut self, fq_message_name: &str, field: &Field) { let type_ = field.descriptor.r#type(); - let repeated = field.descriptor.label == Some(Label::Repeated as i32); + let repeated = field.descriptor.label() == Label::Repeated; let deprecated = self.deprecated(&field.descriptor); let optional = self.optional(&field.descriptor); - let boxed = self.boxed(&field.descriptor, fq_message_name, None); + let boxed = self + .context + .should_box_message_field(fq_message_name, &field.descriptor); let ty = self.resolve_type(&field.descriptor, fq_message_name); debug!( @@ -426,11 +412,8 @@ if type_ == Type::Bytes { let bytes_type = self - .config - .bytes_type - .get_first_field(fq_message_name, field.descriptor.name()) - .copied() - .unwrap_or_default(); + .context + .bytes_type(fq_message_name, field.descriptor.name()); self.buf .push_str(&format!("={:?}", bytes_type.annotation())); } @@ -474,7 +457,7 @@ self.buf.push_str("\\\""); } else if type_ == Type::Enum { let mut enum_value = to_upper_camel(default); - if self.config.strip_enum_prefix { + if self.config().strip_enum_prefix { // Field types are fully qualified, so we extract // the last segment and strip it from the left // side of the default value. @@ -500,7 +483,7 @@ self.buf.push_str(&field.rust_name()); self.buf.push_str(": "); - let prost_path = prost_path(self.config); + let prost_path = self.context.prost_path(); if repeated { self.buf @@ -543,11 +526,8 @@ self.push_indent(); let map_type = self - .config - .map_type - .get_first_field(fq_message_name, field.descriptor.name()) - .copied() - .unwrap_or_default(); + .context + .map_type(fq_message_name, field.descriptor.name()); let key_tag = self.field_type_tag(key); let value_tag = self.map_value_type_tag(value); @@ -611,11 +591,15 @@ self.append_type_attributes(&oneof_name); self.append_enum_attributes(&oneof_name); self.push_indent(); - self.buf - .push_str("#[allow(clippy::derive_partial_eq_without_eq)]\n"); + + let can_oneof_derive_copy = oneof.fields.iter().all(|field| { + self.context + .can_field_derive_copy(fq_message_name, &field.descriptor) + }); self.buf.push_str(&format!( - "#[derive(Clone, PartialEq, {}::Oneof)]\n", - prost_path(self.config) + "#[derive(Clone, {}PartialEq, {}::Oneof)]\n", + if can_oneof_derive_copy { "Copy, " } else { "" }, + self.context.prost_path() )); self.append_skip_debug(fq_message_name); self.push_indent(); @@ -642,10 +626,10 @@ self.push_indent(); let ty = self.resolve_type(&field.descriptor, fq_message_name); - let boxed = self.boxed( - &field.descriptor, + let boxed = self.context.should_box_oneof_field( fq_message_name, - Some(oneof.descriptor.name()), + oneof.descriptor.name(), + &field.descriptor, ); debug!( @@ -686,15 +670,7 @@ } fn append_doc(&mut self, fq_name: &str, field_name: Option<&str>) { - let append_doc = if let Some(field_name) = field_name { - self.config - .disable_comments - .get_first_field(fq_name, field_name) - .is_none() - } else { - self.config.disable_comments.get(fq_name).next().is_none() - }; - if append_doc { + if !self.context.should_disable_comments(fq_name, field_name) { if let Some(comments) = self.location().map(Comments::from_location) { comments.append_with_indent(self.depth, self.buf); } @@ -711,8 +687,8 @@ let fq_proto_enum_name = self.fq_name(proto_enum_name); if self - .extern_paths - .resolve_ident(&fq_proto_enum_name) + .context + .resolve_extern_ident(&fq_proto_enum_name) .is_some() { return; @@ -722,7 +698,7 @@ self.append_type_attributes(&fq_proto_enum_name); self.append_enum_attributes(&fq_proto_enum_name); self.push_indent(); - let dbg = if self.should_skip_debug(&fq_proto_enum_name) { + let dbg = if self.context.should_skip_debug(&fq_proto_enum_name) { "" } else { "Debug, " @@ -730,7 +706,7 @@ self.buf.push_str(&format!( "#[derive(Clone, Copy, {}PartialEq, Eq, Hash, PartialOrd, Ord, {}::Enumeration)]\n", dbg, - prost_path(self.config), + self.context.prost_path(), )); self.push_indent(); self.buf.push_str("#[repr(i32)]\n"); @@ -740,7 +716,7 @@ self.buf.push_str(" {\n"); let variant_mappings = - build_enum_value_mappings(&enum_name, self.config.strip_enum_prefix, enum_values); + build_enum_value_mappings(&enum_name, self.config().strip_enum_prefix, enum_values); self.depth += 1; self.path.push(2); @@ -796,8 +772,7 @@ for variant in variant_mappings.iter() { self.push_indent(); - self.buf.push_str(&enum_name); - self.buf.push_str("::"); + self.buf.push_str("Self::"); self.buf.push_str(&variant.generated_variant_name); self.buf.push_str(" => \""); self.buf.push_str(variant.proto_name); @@ -907,7 +882,7 @@ options: service.options.unwrap_or_default(), }; - if let Some(service_generator) = self.config.service_generator.as_mut() { + if let Some(service_generator) = self.context.service_generator_mut() { service_generator.generate(service, self.buf) } } @@ -950,13 +925,10 @@ Type::Int32 | Type::Sfixed32 | Type::Sint32 | Type::Enum => String::from("i32"), Type::Int64 | Type::Sfixed64 | Type::Sint64 => String::from("i64"), Type::Bool => String::from("bool"), - Type::String => format!("{}::alloc::string::String", prost_path(self.config)), + Type::String => format!("{}::alloc::string::String", self.context.prost_path()), Type::Bytes => self - .config - .bytes_type - .get_first_field(fq_message_name, field.name()) - .copied() - .unwrap_or_default() + .context + .bytes_type(fq_message_name, field.name()) .rust_type() .to_owned(), Type::Group | Type::Message => self.resolve_ident(field.type_name()), @@ -967,7 +939,7 @@ // protoc should always give fully qualified identifiers. assert_eq!(".", &pb_ident[..1]); - if let Some(proto_ident) = self.extern_paths.resolve_ident(pb_ident) { + if let Some(proto_ident) = self.context.resolve_extern_ident(pb_ident) { return proto_ident; } @@ -980,7 +952,7 @@ // If no package is specified the start of the package name will be '.' // and split will return an empty string ("") which breaks resolution // The fix to this is to ignore the first item if it is empty. - if local_path.peek().map_or(false, |s| s.is_empty()) { + if local_path.peek().is_some_and(|s| s.is_empty()) { local_path.next(); } @@ -1052,55 +1024,9 @@ } } - /// Returns whether the Rust type for this field needs to be `Box<_>`. - /// - /// This can be explicitly configured with `Config::boxed`, or necessary - /// to prevent an infinitely sized type definition in case when the type of - /// a non-repeated message field transitively contains the message itself. - fn boxed( - &self, - field: &FieldDescriptorProto, - fq_message_name: &str, - oneof: Option<&str>, - ) -> bool { - let repeated = field.label == Some(Label::Repeated as i32); - let fd_type = field.r#type(); - if !repeated - && (fd_type == Type::Message || fd_type == Type::Group) - && self - .message_graph - .is_nested(field.type_name(), fq_message_name) - { - return true; - } - let config_path = match oneof { - None => Cow::Borrowed(fq_message_name), - Some(ooname) => Cow::Owned(format!("{fq_message_name}.{ooname}")), - }; - if self - .config - .boxed - .get_first_field(&config_path, field.name()) - .is_some() - { - if repeated { - println!( - "cargo:warning=\ - Field X is repeated and manually marked as boxed. \ - This is deprecated and support will be removed in a later release" - ); - } - return true; - } - false - } - /// Returns `true` if the field options includes the `deprecated` option. fn deprecated(&self, field: &FieldDescriptorProto) -> bool { - field - .options - .as_ref() - .map_or(false, FieldOptions::deprecated) + field.options.as_ref().is_some_and(FieldOptions::deprecated) } /// Returns the fully-qualified name, starting with a dot diff -Nru rust-prost-build-0.12.6/src/collections.rs rust-prost-build-0.13.5/src/collections.rs --- rust-prost-build-0.12.6/src/collections.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/collections.rs 2006-07-24 01:21:28.000000000 +0000 @@ -13,10 +13,10 @@ #[non_exhaustive] #[derive(Default, Clone, Copy, Debug, PartialEq)] pub(crate) enum BytesType { - /// The [`alloc::collections::Vec::`] type. + /// The [`prost::alloc::vec::Vec`] type. #[default] Vec, - /// The [`bytes::Bytes`] type. + /// The [`bytes::Bytes`](prost::bytes::Bytes) type. Bytes, } diff -Nru rust-prost-build-0.12.6/src/config.rs rust-prost-build-0.13.5/src/config.rs --- rust-prost-build-0.12.6/src/config.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/config.rs 2006-07-24 01:21:28.000000000 +0000 @@ -15,6 +15,7 @@ use prost_types::{FileDescriptorProto, FileDescriptorSet}; use crate::code_generator::CodeGenerator; +use crate::context::Context; use crate::extern_paths::ExternPaths; use crate::message_graph::MessageGraph; use crate::path::PathMap; @@ -44,9 +45,11 @@ pub(crate) enable_type_names: bool, pub(crate) type_name_domains: PathMap, pub(crate) protoc_args: Vec, + pub(crate) protoc_executable: PathBuf, pub(crate) disable_comments: PathMap<()>, pub(crate) skip_debug: PathMap<()>, pub(crate) skip_protoc_run: bool, + pub(crate) skip_source_info: bool, pub(crate) include_file: Option, pub(crate) prost_path: Option, #[cfg(feature = "format")] @@ -120,7 +123,7 @@ self } - /// Configure the code generator to generate Rust [`bytes::Bytes`][1] fields for Protobuf + /// Configure the code generator to generate Rust [`bytes::Bytes`](prost::bytes::Bytes) fields for Protobuf /// [`bytes`][2] type fields. /// /// # Arguments @@ -165,7 +168,6 @@ /// config.bytes(&["my_bytes_field", ".foo.bar"]); /// ``` /// - /// [1]: https://docs.rs/bytes/latest/bytes/struct.Bytes.html /// [2]: https://developers.google.com/protocol-buffers/docs/proto3#scalar /// [3]: https://doc.rust-lang.org/std/vec/struct.Vec.html pub fn bytes(&mut self, paths: I) -> &mut Self @@ -186,11 +188,11 @@ /// # Arguments /// /// **`path`** - a path matching any number of fields. These fields get the attribute. - /// For details about matching fields see [`btree_map`](#method.btree_map). + /// For details about matching fields see [`btree_map`](Self::btree_map). /// /// **`attribute`** - an arbitrary string that'll be placed before each matched field. The /// expected usage are additional attributes, usually in concert with whole-type - /// attributes set with [`type_attribute`](method.type_attribute), but it is not + /// attributes set with [`type_attribute`](Self::type_attribute), but it is not /// checked and anything can be put there. /// /// Note that the calls to this method are cumulative ‒ if multiple paths from multiple calls @@ -219,7 +221,7 @@ /// # Arguments /// /// **`paths`** - a path matching any number of types. It works the same way as in - /// [`btree_map`](#method.btree_map), just with the field name omitted. + /// [`btree_map`](Self::btree_map), just with the field name omitted. /// /// **`attribute`** - an arbitrary string to be placed before each matched type. The /// expected usage are additional attributes, but anything is allowed. @@ -229,7 +231,7 @@ /// it. /// /// For things like serde it might be needed to combine with [field - /// attributes](#method.field_attribute). + /// attributes](Self::field_attribute). /// /// # Examples /// @@ -268,7 +270,7 @@ /// # Arguments /// /// **`paths`** - a path matching any number of types. It works the same way as in - /// [`btree_map`](#method.btree_map), just with the field name omitted. + /// [`btree_map`](Self::btree_map), just with the field name omitted. /// /// **`attribute`** - an arbitrary string to be placed before each matched type. The /// expected usage are additional attributes, but anything is allowed. @@ -278,7 +280,7 @@ /// it. /// /// For things like serde it might be needed to combine with [field - /// attributes](#method.field_attribute). + /// attributes](Self::field_attribute). /// /// # Examples /// @@ -307,7 +309,7 @@ /// # Arguments /// /// **`paths`** - a path matching any number of types. It works the same way as in - /// [`btree_map`](#method.btree_map), just with the field name omitted. + /// [`btree_map`](Self::btree_map), just with the field name omitted. /// /// **`attribute`** - an arbitrary string to be placed before each matched type. The /// expected usage are additional attributes, but anything is allowed. @@ -317,7 +319,7 @@ /// it. /// /// For things like serde it might be needed to combine with [field - /// attributes](#method.field_attribute). + /// attributes](Self::field_attribute). /// /// # Examples /// @@ -356,7 +358,7 @@ /// # Arguments /// /// **`path`** - a path matching any number of fields. These fields get the attribute. - /// For details about matching fields see [`btree_map`](#method.btree_map). + /// For details about matching fields see [`btree_map`](Self::btree_map). /// /// # Examples /// @@ -597,6 +599,27 @@ self } + /// Configures the code generator to remove surrounding comments and documentation. + /// + /// If enabled, this will cause `protoc` to not be passed the `--include_source_info` argument. + /// Typically, `--include_source_info` is passed by default, but it results in larger + /// [`FileDescriptorSet`s](https://github.com/protocolbuffers/protobuf/blob/cff254d32f850ba8186227ce6775b3f01a1f8cf8/src/google/protobuf/descriptor.proto#L54-L66) that include information about the + /// original location of each declaration in the source file as well as surrounding + /// comments and documentation. + /// + /// In `build.rs`: + /// + /// ```rust + /// # let mut config = prost_build::Config::new(); + /// config.file_descriptor_set_path("path/from/build/system") + /// .skip_source_info() + /// .compile_protos(&["src/items.proto"], &["src/"]); + /// ``` + pub fn skip_source_info(&mut self) -> &mut Self { + self.skip_source_info = true; + self + } + /// Configures the code generator to not strip the enum name from variant names. /// /// Protobuf enum definitions commonly include the enum name as a prefix of every variant name. @@ -644,7 +667,7 @@ /// # Domains /// /// **`paths`** - a path matching any number of types. It works the same way as in - /// [`btree_map`](#method.btree_map), just with the field name omitted. + /// [`btree_map`](Self::btree_map), just with the field name omitted. /// /// **`domain`** - an arbitrary string to be used as a prefix for type URLs. /// @@ -703,6 +726,30 @@ self } + /// Set the path to `protoc` executable to be used by `prost-build` + /// + /// Use the provided path to find `protoc`. This can either be a file name which is + /// searched for in the `PATH` or an aboslute path to use a specific executable. + /// + /// # Example `build.rs` + /// + /// ```rust,no_run + /// # use std::io::Result; + /// fn main() -> Result<()> { + /// let mut prost_build = prost_build::Config::new(); + /// prost_build.protoc_executable("protoc-27.1"); + /// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?; + /// Ok(()) + /// } + /// ``` + pub fn protoc_executable(&mut self, executable: S) -> &mut Self + where + S: Into, + { + self.protoc_executable = executable.into(); + self + } + /// Configures the optional module filename for easy inclusion of all generated Rust files /// /// If set, generates a file (inside the `OUT_DIR` or `out_dir()` as appropriate) which contains @@ -806,66 +853,62 @@ .expect("every module should have a filename"); let output_path = target.join(file_name); - let previous_content = fs::read(&output_path); - - if previous_content - .map(|previous_content| previous_content == content.as_bytes()) - .unwrap_or(false) - { - trace!("unchanged: {:?}", file_name); - } else { - trace!("writing: {:?}", file_name); - fs::write(output_path, content)?; - } + write_file_if_changed(&output_path, content.as_bytes())?; } if let Some(ref include_file) = self.include_file { - trace!("Writing include file: {:?}", target.join(include_file)); - let mut file = fs::File::create(target.join(include_file))?; - self.write_line(&mut file, 0, "// This file is @generated by prost-build.")?; + let path = target.join(include_file); + trace!("Writing include file: {}", path.display()); + let mut buffer = Vec::new(); + self.write_line(&mut buffer, 0, "// This file is @generated by prost-build.")?; self.write_includes( modules.keys().collect(), - &mut file, + &mut buffer, if target_is_env { None } else { Some(&target) }, &file_names, )?; - file.flush()?; + + write_file_if_changed(&path, &buffer)?; } Ok(()) } - /// Compile `.proto` files into Rust files during a Cargo build with additional code generator - /// configuration options. - /// - /// This method is like the `prost_build::compile_protos` function, with the added ability to - /// specify non-default code generation options. See that function for more information about - /// the arguments and generated outputs. - /// - /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified. + /// Loads `.proto` files as a [`FileDescriptorSet`]. This allows inspection of the descriptors + /// before calling [`Config::compile_fds`]. This could be used to change [`Config`] + /// attributes after introspecting what is actually present in the `.proto` files. /// /// # Example `build.rs` /// /// ```rust,no_run - /// # use std::io::Result; - /// fn main() -> Result<()> { - /// let mut prost_build = prost_build::Config::new(); - /// prost_build.btree_map(&["."]); - /// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?; - /// Ok(()) + /// # use prost_types::FileDescriptorSet; + /// # use prost_build::Config; + /// fn main() -> std::io::Result<()> { + /// let mut config = Config::new(); + /// let file_descriptor_set = config.load_fds(&["src/frontend.proto", "src/backend.proto"], &["src"])?; + /// + /// // Add custom attributes to messages that are service inputs or outputs. + /// for file in &file_descriptor_set.file { + /// for service in &file.service { + /// for method in &service.method { + /// if let Some(input) = &method.input_type { + /// config.message_attribute(input, "#[derive(custom_proto::Input)]"); + /// } + /// if let Some(output) = &method.output_type { + /// config.message_attribute(output, "#[derive(custom_proto::Output)]"); + /// } + /// } + /// } + /// } + /// + /// config.compile_fds(file_descriptor_set) /// } /// ``` - pub fn compile_protos( + pub fn load_fds( &mut self, protos: &[impl AsRef], includes: &[impl AsRef], - ) -> Result<()> { - // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however - // according to [1] if any are output then those paths replace the default crate root, - // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has - // this figured out. - // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script - + ) -> Result { let tmp; let file_descriptor_set_path = if let Some(path) = &self.file_descriptor_set_path { path.clone() @@ -881,13 +924,12 @@ }; if !self.skip_protoc_run { - let protoc = protoc_from_env(); - - let mut cmd = Command::new(protoc.clone()); - cmd.arg("--include_imports") - .arg("--include_source_info") - .arg("-o") - .arg(&file_descriptor_set_path); + let mut cmd = Command::new(&self.protoc_executable); + cmd.arg("--include_imports"); + if !self.skip_source_info { + cmd.arg("--include_source_info"); + } + cmd.arg("-o").arg(&file_descriptor_set_path); for include in includes { if include.as_ref().exists() { @@ -923,7 +965,7 @@ )), Err(err) => return Err(Error::new( err.kind(), - format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {:?}): {}", &protoc, err), + format!("failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: {}): {}", &self.protoc_executable.display(), err), )), Ok(output) => output, }; @@ -940,8 +982,9 @@ Error::new( e.kind(), format!( - "unable to open file_descriptor_set_path: {:?}, OS: {}", - &file_descriptor_set_path, e + "unable to open file_descriptor_set_path: {}, OS: {}", + file_descriptor_set_path.display(), + e ), ) })?; @@ -952,6 +995,42 @@ ) })?; + Ok(file_descriptor_set) + } + + /// Compile `.proto` files into Rust files during a Cargo build with additional code generator + /// configuration options. + /// + /// This method is like the `prost_build::compile_protos` function, with the added ability to + /// specify non-default code generation options. See that function for more information about + /// the arguments and generated outputs. + /// + /// The `protos` and `includes` arguments are ignored if `skip_protoc_run` is specified. + /// + /// # Example `build.rs` + /// + /// ```rust,no_run + /// # use std::io::Result; + /// fn main() -> Result<()> { + /// let mut prost_build = prost_build::Config::new(); + /// prost_build.btree_map(&["."]); + /// prost_build.compile_protos(&["src/frontend.proto", "src/backend.proto"], &["src"])?; + /// Ok(()) + /// } + /// ``` + pub fn compile_protos( + &mut self, + protos: &[impl AsRef], + includes: &[impl AsRef], + ) -> Result<()> { + // TODO: This should probably emit 'rerun-if-changed=PATH' directives for cargo, however + // according to [1] if any are output then those paths replace the default crate root, + // which is undesirable. Figure out how to do it in an additive way; perhaps gcc-rs has + // this figured out. + // [1]: http://doc.crates.io/build-script.html#outputs-of-the-build-script + + let file_descriptor_set = self.load_fds(protos, includes)?; + self.compile_fds(file_descriptor_set) } @@ -1015,7 +1094,7 @@ /// /// This is generally used when control over the output should not be managed by Prost, /// such as in a flow for a `protoc` code generating plugin. When compiling as part of a - /// `build.rs` file, instead use [`compile_protos()`]. + /// `build.rs` file, instead use [`Self::compile_protos()`]. pub fn generate( &mut self, requests: Vec<(Module, FileDescriptorProto)>, @@ -1023,10 +1102,10 @@ let mut modules = HashMap::new(); let mut packages = HashMap::new(); - let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1)) - .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?; + let message_graph = MessageGraph::new(requests.iter().map(|x| &x.1)); let extern_paths = ExternPaths::new(&self.extern_paths, self.prost_types) .map_err(|error| Error::new(ErrorKind::InvalidInput, error))?; + let mut context = Context::new(self, message_graph, extern_paths); for (request_module, request_fd) in requests { // Only record packages that have services @@ -1036,14 +1115,14 @@ let buf = modules .entry(request_module.clone()) .or_insert_with(String::new); - CodeGenerator::generate(self, &message_graph, &extern_paths, request_fd, buf); + CodeGenerator::generate(&mut context, request_fd, buf); if buf.is_empty() { // Did not generate any code, remove from list to avoid inclusion in include file or output file list modules.remove(&request_module); } } - if let Some(ref mut service_generator) = self.service_generator { + if let Some(service_generator) = context.service_generator_mut() { for (module, package) in packages { let buf = modules.get_mut(&module).unwrap(); service_generator.finalize_package(&package, buf); @@ -1072,6 +1151,26 @@ } } +/// Write a slice as the entire contents of a file. +/// +/// This function will create a file if it does not exist, +/// and will entirely replace its contents if it does. When +/// the contents is already correct, it doesn't touch to the file. +fn write_file_if_changed(path: &Path, content: &[u8]) -> std::io::Result<()> { + let previous_content = fs::read(path); + + if previous_content + .map(|previous_content| previous_content == content) + .unwrap_or(false) + { + trace!("unchanged: {}", path.display()); + Ok(()) + } else { + trace!("writing: {}", path.display()); + fs::write(path, content) + } +} + impl default::Default for Config { fn default() -> Config { Config { @@ -1092,9 +1191,11 @@ enable_type_names: false, type_name_domains: PathMap::default(), protoc_args: Vec::new(), + protoc_executable: protoc_from_env(), disable_comments: PathMap::default(), skip_debug: PathMap::default(), skip_protoc_run: false, + skip_source_info: false, include_file: None, prost_path: None, #[cfg(feature = "format")] @@ -1159,16 +1260,113 @@ if !protoc_include.exists() { panic!( - "PROTOC_INCLUDE environment variable points to non-existent directory ({:?})", - protoc_include + "PROTOC_INCLUDE environment variable points to non-existent directory ({})", + protoc_include.display() ); } if !protoc_include.is_dir() { panic!( - "PROTOC_INCLUDE environment variable points to a non-directory file ({:?})", - protoc_include + "PROTOC_INCLUDE environment variable points to a non-directory file ({})", + protoc_include.display() ); } Some(protoc_include) } + +#[cfg(test)] +mod tests { + use super::*; + + macro_rules! assert_starts_with { + ($left:expr, $right:expr) => { + match (&$left, &$right) { + (left_val, right_val) => { + if !(left_val.starts_with(right_val)) { + panic!( + "assertion 'starts_with` failed:\nleft: {}\nright: {}", + left_val, right_val + ) + } + } + } + }; + } + + #[test] + fn test_error_protoc_not_found() { + let mut config = Config::new(); + config.protoc_executable("path-does-not-exist"); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_eq!(err.to_string(), error_message_protoc_not_found()) + } + + #[test] + fn test_error_protoc_not_executable() { + let mut config = Config::new(); + config.protoc_executable("src/lib.rs"); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_starts_with!(err.to_string(), "failed to invoke protoc (hint: https://docs.rs/prost-build/#sourcing-protoc): (path: src/lib.rs): ") + } + + #[test] + fn test_error_incorrect_skip_protoc_run() { + let mut config = Config::new(); + config.skip_protoc_run(); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_eq!( + err.to_string(), + "file_descriptor_set_path is required with skip_protoc_run" + ) + } + + #[test] + fn test_error_protoc_failed() { + let mut config = Config::new(); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_starts_with!( + err.to_string(), + "protoc failed: You seem to have passed an empty string as one of the arguments to " + ) + } + + #[test] + fn test_error_non_existing_file_descriptor_set() { + let mut config = Config::new(); + config.skip_protoc_run(); + config.file_descriptor_set_path("path-does-not-exist"); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_starts_with!( + err.to_string(), + "unable to open file_descriptor_set_path: path-does-not-exist, OS: " + ) + } + + #[test] + fn test_error_text_incorrect_file_descriptor_set() { + let mut config = Config::new(); + config.skip_protoc_run(); + config.file_descriptor_set_path("src/lib.rs"); + + let err = config.load_fds(&[""], &[""]).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid FileDescriptorSet: failed to decode Protobuf message: unexpected end group tag" + ) + } + + #[test] + fn test_error_unset_out_dir() { + let mut config = Config::new(); + + let err = config + .compile_fds(FileDescriptorSet::default()) + .unwrap_err(); + assert_eq!(err.to_string(), "OUT_DIR environment variable is not set") + } +} diff -Nru rust-prost-build-0.12.6/src/context.rs rust-prost-build-0.13.5/src/context.rs --- rust-prost-build-0.12.6/src/context.rs 1970-01-01 00:00:00.000000000 +0000 +++ rust-prost-build-0.13.5/src/context.rs 2006-07-24 01:21:28.000000000 +0000 @@ -0,0 +1,266 @@ +use std::borrow::Cow; + +use prost_types::{ + field_descriptor_proto::{Label, Type}, + FieldDescriptorProto, +}; + +use crate::extern_paths::ExternPaths; +use crate::message_graph::MessageGraph; +use crate::{BytesType, Config, MapType, ServiceGenerator}; + +/// The context providing all the global information needed to generate code. +/// It also provides a more disciplined access to Config +/// and its mutable instance of ServiceGenerator. +/// +/// A `Context` is built once in the generation process and is reused by +/// `CodeGenerator` instances created to generate code for each input file. +pub struct Context<'a> { + config: &'a mut Config, + message_graph: MessageGraph, + extern_paths: ExternPaths, +} + +impl<'a> Context<'a> { + pub fn new( + config: &'a mut Config, + message_graph: MessageGraph, + extern_paths: ExternPaths, + ) -> Self { + Self { + config, + message_graph, + extern_paths, + } + } + + pub fn config(&self) -> &Config { + self.config + } + + pub fn service_generator_mut(&mut self) -> Option<&mut (dyn ServiceGenerator + 'static)> { + self.config.service_generator.as_deref_mut() + } + + pub fn prost_path(&self) -> &str { + self.config.prost_path.as_deref().unwrap_or("::prost") + } + + pub fn resolve_extern_ident(&self, pb_ident: &str) -> Option { + self.extern_paths.resolve_ident(pb_ident) + } + + /// Returns an iterator over the additional attributes configured + /// for the named type. + pub fn type_attributes(&self, fq_type_name: &str) -> impl Iterator { + self.config + .type_attributes + .get(fq_type_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named message. + pub fn message_attributes(&self, fq_message_name: &str) -> impl Iterator { + self.config + .message_attributes + .get(fq_message_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named enum. + pub fn enum_attributes(&self, fq_enum_name: &str) -> impl Iterator { + self.config + .enum_attributes + .get(fq_enum_name) + .map(|s| s.as_str()) + } + + /// Returns an iterator over the additional attributes configured + /// for the named message field. + pub fn field_attributes( + &self, + fq_message_name: &str, + field_name: &str, + ) -> impl Iterator { + self.config + .field_attributes + .get_field(fq_message_name, field_name) + .map(|s| s.as_str()) + } + + /// Returns the bytes type configured for the named message field. + pub(crate) fn bytes_type(&self, fq_message_name: &str, field_name: &str) -> BytesType { + self.config + .bytes_type + .get_first_field(fq_message_name, field_name) + .copied() + .unwrap_or_default() + } + + /// Returns the map type configured for the named message field. + pub(crate) fn map_type(&self, fq_message_name: &str, field_name: &str) -> MapType { + self.config + .map_type + .get_first_field(fq_message_name, field_name) + .copied() + .unwrap_or_default() + } + + /// Returns whether the Rust type for this message field needs to be `Box<_>`. + /// + /// This can be explicitly configured with `Config::boxed`, or necessary + /// to prevent an infinitely sized type definition in case when the type of + /// a non-repeated message field transitively contains the message itself. + pub fn should_box_message_field( + &self, + fq_message_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.should_box_impl(fq_message_name, None, field) + } + + /// Returns whether the Rust type for this field in the oneof needs to be `Box<_>`. + /// + /// This can be explicitly configured with `Config::boxed`, or necessary + /// to prevent an infinitely sized type definition in case when the type of + /// a non-repeated message field transitively contains the message itself. + pub fn should_box_oneof_field( + &self, + fq_message_name: &str, + oneof_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + self.should_box_impl(fq_message_name, Some(oneof_name), field) + } + + fn should_box_impl( + &self, + fq_message_name: &str, + oneof: Option<&str>, + field: &FieldDescriptorProto, + ) -> bool { + let repeated = field.label() == Label::Repeated; + let fd_type = field.r#type(); + if !repeated + && (fd_type == Type::Message || fd_type == Type::Group) + && self + .message_graph + .is_nested(field.type_name(), fq_message_name) + { + return true; + } + let config_path = match oneof { + None => Cow::Borrowed(fq_message_name), + Some(oneof_name) => Cow::Owned(format!("{fq_message_name}.{oneof_name}")), + }; + if self + .config + .boxed + .get_first_field(&config_path, field.name()) + .is_some() + { + if repeated { + println!( + "cargo:warning=\ + Field X is repeated and manually marked as boxed. \ + This is deprecated and support will be removed in a later release" + ); + } + return true; + } + false + } + + /// Returns `true` if this message can automatically derive Copy trait. + pub fn can_message_derive_copy(&self, fq_message_name: &str) -> bool { + assert_eq!(".", &fq_message_name[..1]); + self.message_graph + .get_message(fq_message_name) + .unwrap() + .field + .iter() + .all(|field| self.can_field_derive_copy(fq_message_name, field)) + } + + /// Returns `true` if the type of this message field allows deriving the Copy trait. + pub fn can_field_derive_copy( + &self, + fq_message_name: &str, + field: &FieldDescriptorProto, + ) -> bool { + assert_eq!(".", &fq_message_name[..1]); + + // repeated field cannot derive Copy + if field.label() == Label::Repeated { + false + } else if field.r#type() == Type::Message { + // nested and boxed messages cannot derive Copy + if self + .message_graph + .is_nested(field.type_name(), fq_message_name) + { + return false; + } + if self + .config + .boxed + .get_first_field(fq_message_name, field.name()) + .is_some() + { + false + } else { + self.can_message_derive_copy(field.type_name()) + } + } else { + matches!( + field.r#type(), + Type::Float + | Type::Double + | Type::Int32 + | Type::Int64 + | Type::Uint32 + | Type::Uint64 + | Type::Sint32 + | Type::Sint64 + | Type::Fixed32 + | Type::Fixed64 + | Type::Sfixed32 + | Type::Sfixed64 + | Type::Bool + | Type::Enum + ) + } + } + + pub fn should_disable_comments(&self, fq_message_name: &str, field_name: Option<&str>) -> bool { + if let Some(field_name) = field_name { + self.config + .disable_comments + .get_first_field(fq_message_name, field_name) + .is_some() + } else { + self.config + .disable_comments + .get(fq_message_name) + .next() + .is_some() + } + } + + /// Returns whether the named message should skip generating the `Debug` implementation. + pub fn should_skip_debug(&self, fq_message_name: &str) -> bool { + assert_eq!(b'.', fq_message_name.as_bytes()[0]); + self.config.skip_debug.get(fq_message_name).next().is_some() + } + + /// Returns the type name domain URL for the named message, + /// or an empty string if such is not configured. + pub fn type_name_domain(&self, fq_message_name: &str) -> &str { + self.config + .type_name_domains + .get_first(fq_message_name) + .map_or("", |name| name.as_str()) + } +} diff -Nru rust-prost-build-0.12.6/src/extern_paths.rs rust-prost-build-0.13.5/src/extern_paths.rs --- rust-prost-build-0.12.6/src/extern_paths.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/extern_paths.rs 2006-07-24 01:21:28.000000000 +0000 @@ -167,4 +167,34 @@ case(".google.protobuf.Duration", "::prost_types::Duration"); case(".google.protobuf.Empty", "()"); } + + #[test] + fn test_error_fully_qualified() { + let paths = [("foo".to_string(), "bar".to_string())]; + let err = ExternPaths::new(&paths, false).unwrap_err(); + assert_eq!( + err.to_string(), + "Protobuf paths must be fully qualified (begin with a leading '.'): foo" + ) + } + + #[test] + fn test_error_invalid_path() { + let paths = [(".foo.".to_string(), "bar".to_string())]; + let err = ExternPaths::new(&paths, false).unwrap_err(); + assert_eq!( + err.to_string(), + "invalid fully-qualified Protobuf path: .foo." + ) + } + + #[test] + fn test_error_duplicate() { + let paths = [ + (".foo".to_string(), "bar".to_string()), + (".foo".to_string(), "bar".to_string()), + ]; + let err = ExternPaths::new(&paths, false).unwrap_err(); + assert_eq!(err.to_string(), "duplicate extern Protobuf path: .foo") + } } diff -Nru rust-prost-build-0.12.6/src/fixtures/field_attributes/_expected_field_attributes_formatted.rs rust-prost-build-0.13.5/src/fixtures/field_attributes/_expected_field_attributes_formatted.rs --- rust-prost-build-0.12.6/src/fixtures/field_attributes/_expected_field_attributes_formatted.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/fixtures/field_attributes/_expected_field_attributes_formatted.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,5 +1,4 @@ // This file is @generated by prost-build. -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Container { #[prost(oneof = "container::Data", tags = "1, 2")] @@ -7,7 +6,6 @@ } /// Nested message and enum types in `Container`. pub mod container { - #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Data { #[prost(message, tag = "1")] @@ -16,18 +14,15 @@ Bar(super::Bar), } } -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Foo { #[prost(string, tag = "1")] pub foo: ::prost::alloc::string::String, } -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Bar { #[prost(message, optional, boxed, tag = "1")] pub qux: ::core::option::Option<::prost::alloc::boxed::Box>, } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct Qux {} diff -Nru rust-prost-build-0.12.6/src/fixtures/field_attributes/_expected_field_attributes.rs rust-prost-build-0.13.5/src/fixtures/field_attributes/_expected_field_attributes.rs --- rust-prost-build-0.12.6/src/fixtures/field_attributes/_expected_field_attributes.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/fixtures/field_attributes/_expected_field_attributes.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,5 +1,4 @@ // This file is @generated by prost-build. -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Container { #[prost(oneof="container::Data", tags="1, 2")] @@ -7,8 +6,7 @@ } /// Nested message and enum types in `Container`. pub mod container { - #[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Oneof)] + #[derive(Clone, PartialEq, ::prost::Oneof)] pub enum Data { #[prost(message, tag="1")] Foo(::prost::alloc::boxed::Box), @@ -16,19 +14,16 @@ Bar(super::Bar), } } -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Foo { #[prost(string, tag="1")] pub foo: ::prost::alloc::string::String, } -#[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Bar { #[prost(message, optional, boxed, tag="1")] pub qux: ::core::option::Option<::prost::alloc::boxed::Box>, } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] +#[derive(Clone, Copy, PartialEq, ::prost::Message)] pub struct Qux { } diff -Nru rust-prost-build-0.12.6/src/fixtures/helloworld/_expected_helloworld_formatted.rs rust-prost-build-0.13.5/src/fixtures/helloworld/_expected_helloworld_formatted.rs --- rust-prost-build-0.12.6/src/fixtures/helloworld/_expected_helloworld_formatted.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/fixtures/helloworld/_expected_helloworld_formatted.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,13 +1,13 @@ // This file is @generated by prost-build. #[derive(derive_builder::Builder)] -#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(custom_proto::Input)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Message { #[prost(string, tag = "1")] pub say: ::prost::alloc::string::String, } #[derive(derive_builder::Builder)] -#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(custom_proto::Output)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Response { #[prost(string, tag = "1")] @@ -28,9 +28,9 @@ /// (if the ProtoBuf definition does not change) and safe for programmatic use. pub fn as_str_name(&self) -> &'static str { match self { - ServingStatus::Unknown => "UNKNOWN", - ServingStatus::Serving => "SERVING", - ServingStatus::NotServing => "NOT_SERVING", + Self::Unknown => "UNKNOWN", + Self::Serving => "SERVING", + Self::NotServing => "NOT_SERVING", } } /// Creates an enum from field names used in the ProtoBuf definition. diff -Nru rust-prost-build-0.12.6/src/fixtures/helloworld/_expected_helloworld.rs rust-prost-build-0.13.5/src/fixtures/helloworld/_expected_helloworld.rs --- rust-prost-build-0.12.6/src/fixtures/helloworld/_expected_helloworld.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/fixtures/helloworld/_expected_helloworld.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,13 +1,13 @@ // This file is @generated by prost-build. #[derive(derive_builder::Builder)] -#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(custom_proto::Input)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Message { #[prost(string, tag="1")] pub say: ::prost::alloc::string::String, } #[derive(derive_builder::Builder)] -#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(custom_proto::Output)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct Response { #[prost(string, tag="1")] @@ -28,9 +28,9 @@ /// (if the ProtoBuf definition does not change) and safe for programmatic use. pub fn as_str_name(&self) -> &'static str { match self { - ServingStatus::Unknown => "UNKNOWN", - ServingStatus::Serving => "SERVING", - ServingStatus::NotServing => "NOT_SERVING", + Self::Unknown => "UNKNOWN", + Self::Serving => "SERVING", + Self::NotServing => "NOT_SERVING", } } /// Creates an enum from field names used in the ProtoBuf definition. diff -Nru rust-prost-build-0.12.6/src/lib.rs rust-prost-build-0.13.5/src/lib.rs --- rust-prost-build-0.12.6/src/lib.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/lib.rs 2006-07-24 01:21:28.000000000 +0000 @@ -1,4 +1,4 @@ -#![doc(html_root_url = "https://docs.rs/prost-build/0.12.6")] +#![doc(html_root_url = "https://docs.rs/prost-build/0.13.5")] #![allow(clippy::option_as_ref_deref, clippy::format_push_string)] //! `prost-build` compiles `.proto` files into Rust. @@ -8,23 +8,18 @@ //! //! ## Example //! -//! Let's create a small crate, `snazzy`, that defines a collection of +//! Let's create a small library crate, `snazzy`, that defines a collection of //! snazzy new items in a protobuf file. //! //! ```bash -//! $ cargo new snazzy && cd snazzy +//! $ cargo new --lib snazzy && cd snazzy //! ``` //! -//! First, add `prost-build`, `prost` and its public dependencies to `Cargo.toml` -//! (see [crates.io](https://crates.io/crates/prost) for the current versions): +//! First, add `prost-build` and `prost` as dependencies to `Cargo.toml`: //! -//! ```toml -//! [dependencies] -//! bytes = -//! prost = -//! -//! [build-dependencies] -//! prost-build = { version = } +//! ```bash +//! $ cargo add --build prost-build +//! $ cargo add prost //! ``` //! //! Next, add `src/items.proto` to the project: @@ -36,13 +31,16 @@ //! //! // A snazzy new shirt! //! message Shirt { +//! // Label sizes //! enum Size { //! SMALL = 0; //! MEDIUM = 1; //! LARGE = 2; //! } //! +//! // The base color //! string color = 1; +//! // The size as stated on the label //! Size size = 2; //! } //! ``` @@ -71,6 +69,7 @@ //! //! use snazzy::items; //! +//! /// Returns a large shirt of the specified color //! pub fn create_large_shirt(color: String) -> items::Shirt { //! let mut shirt = items::Shirt::default(); //! shirt.color = color; @@ -101,7 +100,14 @@ //! ## Sourcing `protoc` //! //! `prost-build` depends on the Protocol Buffers compiler, `protoc`, to parse `.proto` files into -//! a representation that can be transformed into Rust. If set, `prost-build` uses the `PROTOC` +//! a representation that can be transformed into Rust. +//! +//! The easiest way for `prost-build` to find `protoc` is to install it in your `PATH`. +//! This can be done by following the [`protoc` install instructions]. `prost-build` will search +//! the current path for `protoc` or `protoc.exe`. +//! +//! When `protoc` is installed in a different location, set `PROTOC` to the path of the executable. +//! If set, `prost-build` uses the `PROTOC` //! for locating `protoc`. For example, on a macOS system where Protobuf is installed //! with Homebrew, set the environment variables to: //! @@ -109,16 +115,14 @@ //! PROTOC=/usr/local/bin/protoc //! ``` //! -//! and in a typical Linux installation: -//! -//! ```bash -//! PROTOC=/usr/bin/protoc -//! ``` +//! Alternatively, the path to `protoc` execuatable can be explicitly set +//! via [`Config::protoc_executable()`]. //! -//! If no `PROTOC` environment variable is set then `prost-build` will search the -//! current path for `protoc` or `protoc.exe`. If `prost-build` can not find `protoc` +//! If `prost-build` can not find `protoc` //! via these methods the `compile_protos` method will fail. //! +//! [`protoc` install instructions]: https://github.com/protocolbuffers/protobuf#protocol-compiler-installation +//! //! ### Compiling `protoc` from source //! //! To compile `protoc` from source you can use the `protobuf-src` crate and @@ -143,6 +147,7 @@ pub(crate) use collections::{BytesType, MapType}; mod code_generator; +mod context; mod extern_paths; mod ident; mod message_graph; @@ -178,7 +183,7 @@ /// Finalizes the generation process. /// /// In case there's something that needs to be output at the end of the generation process, it - /// goes here. Similar to [`generate`](#method.generate), the output should be appended to + /// goes here. Similar to [`generate`](Self::generate), the output should be appended to /// `buf`. /// /// An example can be a module or other thing that needs to appear just once, not for each @@ -192,7 +197,7 @@ /// Finalizes the generation process for an entire protobuf package. /// - /// This differs from [`finalize`](#method.finalize) by where (and how often) it is called + /// This differs from [`finalize`](Self::finalize) by where (and how often) it is called /// during the service generator life cycle. This method is called once per protobuf package, /// making it ideal for grouping services within a single package spread across multiple /// `.proto` files. @@ -257,8 +262,6 @@ /// This function can be combined with a crate like [`protox`] which outputs a /// [`FileDescriptorSet`] and is a pure Rust implementation of `protoc`. /// -/// [`protox`]: https://github.com/andrewhickman/protox -/// /// # Example /// ```rust,no_run /// # use prost_types::FileDescriptorSet; @@ -269,6 +272,10 @@ /// prost_build::compile_fds(file_descriptor_set) /// } /// ``` +/// +/// [`protox`]: https://github.com/andrewhickman/protox +/// [1]: https://doc.rust-lang.org/std/macro.include.html +/// [2]: http://doc.crates.io/build-script.html#case-study-code-generation pub fn compile_fds(fds: FileDescriptorSet) -> Result<()> { Config::new().compile_fds(fds) } @@ -282,6 +289,32 @@ use super::*; + macro_rules! assert_eq_fixture_file { + ($expected_path:expr, $actual_path:expr) => {{ + let actual = std::fs::read_to_string($actual_path).unwrap(); + + // Normalizes windows and Linux-style EOL + let actual = actual.replace("\r\n", "\n"); + + assert_eq_fixture_contents!($expected_path, actual); + }}; + } + + macro_rules! assert_eq_fixture_contents { + ($expected_path:expr, $actual:expr) => {{ + let expected = std::fs::read_to_string($expected_path).unwrap(); + + // Normalizes windows and Linux-style EOL + let expected = expected.replace("\r\n", "\n"); + + if expected != $actual { + std::fs::write($expected_path, &$actual).unwrap(); + } + + assert_eq!(expected, $actual); + }}; + } + /// An example service generator that generates a trait with methods corresponding to the /// service methods. struct ServiceTraitGenerator; @@ -390,29 +423,43 @@ let _ = env_logger::try_init(); let tempdir = tempfile::tempdir().unwrap(); - Config::new() + let mut config = Config::new(); + config .out_dir(tempdir.path()) + // Add attributes to all messages and enums .message_attribute(".", "#[derive(derive_builder::Builder)]") - .enum_attribute(".", "#[some_enum_attr(u8)]") - .compile_protos( + .enum_attribute(".", "#[some_enum_attr(u8)]"); + + let fds = config + .load_fds( &["src/fixtures/helloworld/hello.proto"], &["src/fixtures/helloworld"], ) .unwrap(); - let out_file = tempdir.path().join("helloworld.rs"); - #[cfg(feature = "format")] - let expected_content = - read_all_content("src/fixtures/helloworld/_expected_helloworld_formatted.rs") - .replace("\r\n", "\n"); - #[cfg(not(feature = "format"))] - let expected_content = read_all_content("src/fixtures/helloworld/_expected_helloworld.rs") - .replace("\r\n", "\n"); - let content = read_all_content(out_file).replace("\r\n", "\n"); - assert_eq!( - expected_content, content, - "Unexpected content: \n{}", - content + // Add custom attributes to messages that are service inputs or outputs. + for file in &fds.file { + for service in &file.service { + for method in &service.method { + if let Some(input) = &method.input_type { + config.message_attribute(input, "#[derive(custom_proto::Input)]"); + } + if let Some(output) = &method.output_type { + config.message_attribute(output, "#[derive(custom_proto::Output)]"); + } + } + } + } + + config.compile_fds(fds).unwrap(); + + assert_eq_fixture_file!( + if cfg!(feature = "format") { + "src/fixtures/helloworld/_expected_helloworld_formatted.rs" + } else { + "src/fixtures/helloworld/_expected_helloworld.rs" + }, + tempdir.path().join("helloworld.rs") ); } @@ -444,12 +491,10 @@ assert!(!contents.is_empty()); } else { // The file wasn't generated so the result include file should not reference it - let expected = read_all_content("src/fixtures/imports_empty/_expected_include.rs"); - let actual = read_all_content(tempdir.path().join(Path::new(include_file))); - // Normalizes windows and Linux-style EOL - let expected = expected.replace("\r\n", "\n"); - let actual = actual.replace("\r\n", "\n"); - assert_eq!(expected, actual); + assert_eq_fixture_file!( + "src/fixtures/imports_empty/_expected_include.rs", + tempdir.path().join(Path::new(include_file)) + ); } } @@ -468,24 +513,13 @@ ) .unwrap(); - let out_file = tempdir.path().join("field_attributes.rs"); - - let content = read_all_content(out_file).replace("\r\n", "\n"); - - #[cfg(feature = "format")] - let expected_content = read_all_content( - "src/fixtures/field_attributes/_expected_field_attributes_formatted.rs", - ) - .replace("\r\n", "\n"); - #[cfg(not(feature = "format"))] - let expected_content = - read_all_content("src/fixtures/field_attributes/_expected_field_attributes.rs") - .replace("\r\n", "\n"); - - assert_eq!( - expected_content, content, - "Unexpected content: \n{}", - content + assert_eq_fixture_file!( + if cfg!(feature = "format") { + "src/fixtures/field_attributes/_expected_field_attributes_formatted.rs" + } else { + "src/fixtures/field_attributes/_expected_field_attributes.rs" + }, + tempdir.path().join("field_attributes.rs") ); } @@ -516,23 +550,13 @@ ) .unwrap(); - let expected = read_all_content("src/fixtures/alphabet/_expected_include.rs"); - let actual = read_all_content(tempdir.path().join(Path::new(include_file))); - // Normalizes windows and Linux-style EOL - let expected = expected.replace("\r\n", "\n"); - let actual = actual.replace("\r\n", "\n"); - - assert_eq!(expected, actual); + assert_eq_fixture_file!( + "src/fixtures/alphabet/_expected_include.rs", + tempdir.path().join(Path::new(include_file)) + ); } } - fn read_all_content(filepath: impl AsRef) -> String { - let mut f = File::open(filepath).unwrap(); - let mut content = String::new(); - f.read_to_string(&mut content).unwrap(); - content - } - #[test] fn write_includes() { let modules = [ @@ -555,9 +579,7 @@ .default_package_filename("_.default") .write_includes(modules.iter().collect(), &mut buf, None, &file_names) .unwrap(); - let expected = - read_all_content("src/fixtures/write_includes/_.includes.rs").replace("\r\n", "\n"); let actual = String::from_utf8(buf).unwrap(); - assert_eq!(expected, actual); + assert_eq_fixture_contents!("src/fixtures/write_includes/_.includes.rs", actual); } } diff -Nru rust-prost-build-0.12.6/src/message_graph.rs rust-prost-build-0.13.5/src/message_graph.rs --- rust-prost-build-0.12.6/src/message_graph.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/message_graph.rs 2006-07-24 01:21:28.000000000 +0000 @@ -4,7 +4,10 @@ use petgraph::graph::NodeIndex; use petgraph::Graph; -use prost_types::{field_descriptor_proto, DescriptorProto, FileDescriptorProto}; +use prost_types::{ + field_descriptor_proto::{Label, Type}, + DescriptorProto, FileDescriptorProto, +}; /// `MessageGraph` builds a graph of messages whose edges correspond to nesting. /// The goal is to recognize when message types are recursively nested, so @@ -12,15 +15,15 @@ pub struct MessageGraph { index: HashMap, graph: Graph, + messages: HashMap, } impl MessageGraph { - pub fn new<'a>( - files: impl Iterator, - ) -> Result { + pub(crate) fn new<'a>(files: impl Iterator) -> MessageGraph { let mut msg_graph = MessageGraph { index: HashMap::new(), graph: Graph::new(), + messages: HashMap::new(), }; for file in files { @@ -34,13 +37,14 @@ } } - Ok(msg_graph) + msg_graph } fn get_or_insert_index(&mut self, msg_name: String) -> NodeIndex { let MessageGraph { ref mut index, ref mut graph, + .. } = *self; assert_eq!(b'.', msg_name.as_bytes()[0]); *index @@ -58,19 +62,23 @@ let msg_index = self.get_or_insert_index(msg_name.clone()); for field in &msg.field { - if field.r#type() == field_descriptor_proto::Type::Message - && field.label() != field_descriptor_proto::Label::Repeated - { + if field.r#type() == Type::Message && field.label() != Label::Repeated { let field_index = self.get_or_insert_index(field.type_name.clone().unwrap()); self.graph.add_edge(msg_index, field_index, ()); } } + self.messages.insert(msg_name.clone(), msg.clone()); for msg in &msg.nested_type { self.add_message(&msg_name, msg); } } + /// Try get a message descriptor from current message graph + pub fn get_message(&self, message: &str) -> Option<&DescriptorProto> { + self.messages.get(message) + } + /// Returns true if message type `inner` is nested in message type `outer`. pub fn is_nested(&self, outer: &str, inner: &str) -> bool { let outer = match self.index.get(outer) { diff -Nru rust-prost-build-0.12.6/src/path.rs rust-prost-build-0.13.5/src/path.rs --- rust-prost-build-0.12.6/src/path.rs 2006-07-24 01:21:28.000000000 +0000 +++ rust-prost-build-0.13.5/src/path.rs 2006-07-24 01:21:28.000000000 +0000 @@ -3,7 +3,7 @@ use std::iter; /// Maps a fully-qualified Protobuf path to a value using path matchers. -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub(crate) struct PathMap { // insertion order might actually matter (to avoid warning about legacy-derive-helpers) // see: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#legacy-derive-helpers @@ -92,7 +92,7 @@ } } -impl<'a, T> std::iter::FusedIterator for Iter<'a, T> {} +impl std::iter::FusedIterator for Iter<'_, T> {} /// Given a fully-qualified path, returns a sequence of paths: /// - the path itself