mirror of
https://github.com/AThilenius/axum-connect.git
synced 2024-11-24 06:19:46 +00:00
Add auto-downloading of protoc, update readme
This commit is contained in:
parent
d92f5529c5
commit
7b1118dbee
7 changed files with 121 additions and 26 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -2,6 +2,7 @@
|
|||
"cSpell.words": [
|
||||
"bufbuild",
|
||||
"codegen",
|
||||
"DTLS",
|
||||
"impls",
|
||||
"pbjson",
|
||||
"prost",
|
||||
|
@ -9,7 +10,9 @@
|
|||
"protobuf",
|
||||
"protoc",
|
||||
"protos",
|
||||
"Roadmap",
|
||||
"serde",
|
||||
"SRTP",
|
||||
"Thilenius",
|
||||
"typecheck"
|
||||
]
|
||||
|
|
55
README.md
55
README.md
|
@ -34,24 +34,14 @@ _Prior knowledge with [Protobuf](https://github.com/protocolbuffers/protobuf)
|
|||
(both the IDL and it's use in RPC frameworks) and
|
||||
[Axum](https://github.com/tokio-rs/axum) are assumed._
|
||||
|
||||
## Dependencies 🙄
|
||||
## Dependencies 👀
|
||||
|
||||
Axum-connect uses [prost](https://github.com/tokio-rs/prost) for much of it's
|
||||
protobuf manipulation. Prost sopped shipping `protoc` so you'll need to install
|
||||
that manually, see the [grpc protoc install
|
||||
instruction](https://grpc.io/docs/protoc-installation/). Alternatively there are
|
||||
crates that ship pre-built protoc binaries.
|
||||
|
||||
```sh
|
||||
# On Debian systems
|
||||
sudo apt install protobuf-compiler
|
||||
```
|
||||
|
||||
You'll need 2 axum-connect crates, one for code-gen and one for runtime use.
|
||||
You'll need 2 `axum-connect` crates, one for code-gen and one for runtime use.
|
||||
Because of how prost works, you'll also need to add it to your own project.
|
||||
You'll obviously also need `axum` and `tokio`.
|
||||
|
||||
```sh
|
||||
# Note: axum-connect-build will fetch `protoc` for you.
|
||||
cargo add --build axum-connect-build
|
||||
cargo add axum-connect prost axum
|
||||
cargo add tokio --features full
|
||||
|
@ -88,10 +78,15 @@ Use the `axum_connect_codegen` crate to generate Rust code from the proto IDL.
|
|||
`build.rs`
|
||||
|
||||
```rust
|
||||
use axum_connect_build::axum_connect_codegen;
|
||||
use axum_connect_build::{axum_connect_codegen, AxumConnectGenSettings};
|
||||
|
||||
fn main() {
|
||||
axum_connect_codegen(&["proto"], &["proto/hello.proto"]).unwrap();
|
||||
// This helper will use `proto` as the import path, and globs all .proto
|
||||
// files in the `proto` directory. You can build an AxumConnectGenSettings
|
||||
// manually too, if you wish.
|
||||
let settings = AxumConnectGenSettings::from_directory_recursive("proto")
|
||||
.expect("failed to glob proto files");
|
||||
axum_connect_codegen(settings).unwrap();
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -170,7 +165,7 @@ API with end-to-end typed RPCs.
|
|||
- Possibly maybe-someday support BiDi streaming over WebRTC
|
||||
- This would require `connect-web` picking up support for the same
|
||||
- WebRTC streams because they are DTLS/SRTP and are resilient
|
||||
- Replace Prost
|
||||
- Replace Prost (with something custom and simpler)
|
||||
|
||||
## Non-goals
|
||||
|
||||
|
@ -184,6 +179,34 @@ API with end-to-end typed RPCs.
|
|||
- This is idiomatic Rust. Do one thing well, and leave the rest to other
|
||||
crates.
|
||||
|
||||
# Prost and Protobuf
|
||||
|
||||
## Protoc Version
|
||||
|
||||
The installed version of `protoc` can be configured in the
|
||||
`AxumConnectGenSettings` if you need/wish to do so. Setting the value to `None`
|
||||
will disable the download entirely.
|
||||
|
||||
## Reasoning
|
||||
|
||||
Prost stopped shipping `protoc` binaries (a decision I disagree with) so
|
||||
`axum-connect-build` internally uses
|
||||
[protoc-fetcher](https://crates.io/crates/protoc-fetcher) download and resolve a
|
||||
copy of `protoc`. This is far more turnkey than forcing every build environment
|
||||
(often Heroku and/or Docker) to have a recent `protoc` binary pre-installed.
|
||||
|
||||
Prost removed it in the name of security, but I fail to see how executing a
|
||||
hash-checked crate as part of your build is any less dangerous than executing a
|
||||
hash-checked binary as part of your build. Both get to run binary code on your
|
||||
build machine. This behavior can be disabled of you disagree, if you need to
|
||||
comply with corporate policy, or your build environment is offline.
|
||||
|
||||
I would someday like to replace all of it with a new 'lean and
|
||||
mean' protoc library for the Rust community. One with a built-in parser, that
|
||||
supports only the latest proto3 syntax as well as the canonical JSON
|
||||
serialization format and explicitly doesn't support many of the rarely used
|
||||
features. But that day is not today.
|
||||
|
||||
# Versioning
|
||||
|
||||
`axum-connect` and `axum-connect-build` versions are currently **not** kept in
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "axum-connect-build"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
authors = ["Alec Thilenius <alec@thilenius.com>"]
|
||||
edition = "2021"
|
||||
categories = [
|
||||
|
@ -22,5 +22,6 @@ proc-macro2 = "1.0.56"
|
|||
prost = "0.11.9"
|
||||
prost-build = "0.11.9"
|
||||
prost-reflect = "0.11.4"
|
||||
protoc-fetcher = "0.1.0"
|
||||
quote = "1.0.26"
|
||||
syn = "2.0.15"
|
||||
|
|
|
@ -8,18 +8,83 @@ use gen::AxumConnectServiceGenerator;
|
|||
|
||||
mod gen;
|
||||
|
||||
pub fn axum_connect_codegen(
|
||||
include: &[impl AsRef<Path>],
|
||||
inputs: &[impl AsRef<Path>],
|
||||
) -> anyhow::Result<()> {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AxumConnectGenSettings {
|
||||
pub includes: Vec<PathBuf>,
|
||||
pub inputs: Vec<PathBuf>,
|
||||
pub protoc_args: Vec<String>,
|
||||
pub protoc_version: Option<String>,
|
||||
}
|
||||
|
||||
impl Default for AxumConnectGenSettings {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
includes: Default::default(),
|
||||
inputs: Default::default(),
|
||||
protoc_args: Default::default(),
|
||||
protoc_version: Some("22.3".to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AxumConnectGenSettings {
|
||||
pub fn from_directory_recursive<P>(path: P) -> anyhow::Result<Self>
|
||||
where
|
||||
P: Into<PathBuf>,
|
||||
{
|
||||
let path = path.into();
|
||||
let mut settings = Self::default();
|
||||
settings.includes.push(path.clone());
|
||||
|
||||
// Recursively add all files that end in ".proto" to the inputs.
|
||||
let mut dirs = vec![path];
|
||||
while let Some(dir) = dirs.pop() {
|
||||
for entry in std::fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if path.is_dir() {
|
||||
dirs.push(path.clone());
|
||||
} else if path.extension().map(|ext| ext == "proto").unwrap_or(false) {
|
||||
settings.inputs.push(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(settings)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn axum_connect_codegen(settings: AxumConnectGenSettings) -> anyhow::Result<()> {
|
||||
// Fetch protoc
|
||||
if let Some(version) = &settings.protoc_version {
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let protoc_path = protoc_fetcher::protoc(version, Path::new(&out_dir))?;
|
||||
env::set_var("PROTOC", protoc_path);
|
||||
}
|
||||
|
||||
// Instruct cargo to re-run if any of the proto files change
|
||||
for input in &settings.inputs {
|
||||
println!("cargo:rerun-if-changed={}", input.display());
|
||||
}
|
||||
|
||||
let descriptor_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("proto_descriptor.bin");
|
||||
|
||||
let mut conf = prost_build::Config::new();
|
||||
|
||||
// Standard prost configuration
|
||||
conf.compile_well_known_types();
|
||||
conf.file_descriptor_set_path(&descriptor_path);
|
||||
conf.extern_path(".google.protobuf", "::pbjson_types");
|
||||
conf.service_generator(Box::new(AxumConnectServiceGenerator::new()));
|
||||
conf.compile_protos(inputs, include).unwrap();
|
||||
|
||||
// Arg configuration
|
||||
for arg in settings.protoc_args {
|
||||
conf.protoc_arg(arg);
|
||||
}
|
||||
|
||||
// File configuration
|
||||
conf.compile_protos(&settings.inputs, &settings.includes)
|
||||
.unwrap();
|
||||
|
||||
// Use pbjson to generate the Serde impls, and inline them with the Prost files.
|
||||
let descriptor_set = std::fs::read(descriptor_path)?;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use axum_connect_build::axum_connect_codegen;
|
||||
use axum_connect_build::{axum_connect_codegen, AxumConnectGenSettings};
|
||||
|
||||
fn main() {
|
||||
axum_connect_codegen(&["proto"], &["proto/hello.proto"]).unwrap();
|
||||
let settings = AxumConnectGenSettings::from_directory_recursive("proto")
|
||||
.expect("failed to glob proto files");
|
||||
axum_connect_codegen(settings).unwrap();
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ syntax = "proto3";
|
|||
|
||||
package hello;
|
||||
|
||||
message HelloRequest { string name = 1; }
|
||||
message HelloRequest { optional string name = 1; }
|
||||
|
||||
message HelloResponse { string message = 1; }
|
||||
|
||||
|
|
|
@ -30,7 +30,8 @@ async fn say_hello_success(Host(host): Host, request: HelloRequest) -> HelloResp
|
|||
HelloResponse {
|
||||
message: format!(
|
||||
"Hello {}! You're addressing the hostname: {}.",
|
||||
request.name, host
|
||||
request.name.unwrap_or_else(|| "unnamed".to_string()),
|
||||
host
|
||||
),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue