tonic_build add/use external crates like serde - rust

I'm trying to use or add an external create like serde, to the build.rs file that tonic_build will use to generate the .rs file from the .proto file.
// build.rs file
let proto_file = "src/hello/hello.proto";
tonic_build::configure()
.build_server(true)
.out_dir("./src/hello")
.type_attribute("HelloRequest", "#[derive(Deserialize, Serialize)]") // adding attributes
.compile(&[proto_file], &["."])
.unwrap_or_else(|e| panic!("protobuf compile error: {}", e));
I can see the attributes are added to the rust file, see below :
#[derive(Deserialize, Serialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct HelloRequest {
#[prost(string, tag="1")]
pub id: ::prost::alloc::string::String,
#[prost(string, tag="2")]
pub message: ::prost::alloc::string::String,
}
but the file doesn't add: use serde::{Serialize, Deserialize}; causing the build to fail. I tried using server_mod_attribute but failed. how can I add use serde::{Serialize, Deserialize} to the auto-generated .rs file using tonic_build?

you can use the full path to the library
.type_attribute("HelloRequest", "#[derive(serde::Deserialize, serde::Serialize)]")

Related

Cargo: How to include the entire directory or file in feature flags?

I'm working on a Rust project. I'm using Cargo feature flags for conditional compilation of the some code. There are cases where I have to include the entire file in the feature flags so doing so adding #[cfg(feature="my-flag")] over every function & use statement doesn't make much sense.
So to include the entire file in the feature flag I'm thinking to surround all the contents in the file a block & add the feature flag for the block.
#[cfg(feature="my-flag")]
{
use crate::access_control::{func1, func2};
use crate::models;
...
#[derive(Debug)]
pub enum MyEnum{..}
#[derive(Clone)]
pub Struct MyStruct{..}
pub fn my_func() {...}
fn my_func_internal() {...}
...
}
But I'm getting the error Syntax Error: expected an item after attributes
Also, there are also some cases where I want the entire directory to be included the feature flags. How should I go about it? Doing the adding feature flags for every file is one way. Does a better way exist?
As in #MarcusDunn's answer, the proper solution is to apply the attribute to the mod declaration:
// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;
However for the sake of completeness, I would point out that attributes can be applied to the item they're in instead of being applied to the following item. These are called "inner attributes" and are specified by adding a ! after the #:
#![cfg(feature="my-flag")] // Applies to the whole file
use crate::access_control::{func1, func2};
use crate::models;
#[derive(Debug)]
pub enum MyEnum {}
#[derive(Clone)]
pub struct MyStruct {}
pub fn my_func() {}
fn my_func_internal() {}
From https://doc.rust-lang.org/cargo/reference/features.html
// This conditionally includes a module which implements WEBP support.
#[cfg(feature = "webp")]
pub mod webp;
This could be an entire directory - or a single file, depends how you structure your modules.
You can use conditional compilation on modules. Perhaps something like this would work for your use case:
#[cfg(feature = "feat")]
use feat::S;
#[cfg(not(feature = "feat"))]
use no_feat::S;
mod feat {
pub const S: &str = "feat";
}
mod no_feat {
pub const S: &str = "no_feat";
}
fn main() {
println!("{}", S);
}
Running with cargo run:
no_feat
Running with cargo run --features feat:
feat
You can use the cfg-if crate:
cfg_if::cfg_if! {
if #[cfg(feature = "my-flag")] {
// ...
}
}

How to parse a string to case which doesn't match the type in 3rd party crate?

So this is some code from a 3rd party library:
#[derive(Debug, PartialEq, Eq, Hash, Clone, Serialize, Deserialize, Copy, Ord, PartialOrd)]
pub enum ViewingMetric {
RatingPercentage,
Rating
}
and what I would like is to parse a string like "rating_percentage" without being able to add #[serde(rename_all = "...")] into 3rd party code. Can I somehow specify the renaming during invocation of serde_json::from_str? Or must I add another 3rd party library which handles conversions between casings?
There is a guide on how to derive Serde for remote creates, in where you can customize whatever you need:
Would be something like:
#[derive(Serialize, Deserialize)]
#[serde(remote = "OtherCrate::ViewingMetric", rename_all = "snake_case")]
enum ViwingMetricSerde {
RatingPercentage,
Rating
}
Important, you would have to implement From/Into from your new type to the remote one From<ViwingMetricSerde> for ViwingMetric.
Then from your code, to actually get the original type:
#[derive(Serialize, Deserialize)]
pub struct MyStruct {
#[serde(with = "ViwingMetricSerde")]
metric: ViwingMetris
}

How can a submodule import a trait from the root in lib.rs?

I have a src/lib.rs that contains:
pub trait Compile {
fn from_source(src: &src) {
parser::parse(src);
}
}
And a src/compiler/interpreter.rs
use crate::Compile; // ERROR HERE - No Compile in the root
pub struct Interpreter;
impl Compile for Interpreter {}
I also have a src/compiler.rs
pub mod interpreter;
I want to be able to use the Compile trait within my interpreter impl however I cannot seem to figure out how import the trait. Any thoughts?
Its possible to do this in src/main.rs by doing:
mod lib;
use lib::Compile;
The way I solved this issue was in src/main.rs I used:
use {ROOT CRATE PACKAGE NAME}::Compile;
This must bring Compile into the crate scope so now I can just do: use crate::Compile inside my compiler sub module.

How can I dynamically define the struct for serde_json when the JSON structure is changed without recompiling?

For example, we add a structure that is used inside a *.rs script
#[derive(Serialize, Deserialize, Debug)]
struct Foo {
Output: OUTPUT,
input: INPUT,
logs: LOGS,
}
#[derive(Serialize, Deserialize, Debug)]
struct OUTPUT {
width: u32,
samplerate: u32,
}
#[derive(Serialize, Deserialize, Debug)]
struct INPUT {
fileinputuri: String,
}
#[derive(Serialize, Deserialize, Debug)]
struct LOGS {
statuslog: String,
}
This is referenced by
let var: Foo = serde_json::from_str(&contents).expect("error while reading json");
When I change the field width: u32, to n_width: u32, in the JSON we would change the struct to
#[derive(Serialize, Deserialize, Debug)]
struct OUTPUT {
n_width: u32,
samplerate: u32,
}
Rather than add this to the *.rs file and update it each time, could the struct be extracted to a configuration file and updated and loaded at function call time?
Are there any safety or performance implications for separating the struct from the *.rs file?
Is there a better anonymous/unnamed way that changes to the JSON could be made without needing to update the struct?
Per a comment below, using serde_json::Value is an option, but is this a safe method that can be used in place of a strongly typed structure? I'm worried about memory safety and security against manipulation by a malicious user that gains access to the JSON file.
pub enum Value {
Null,
Bool(bool),
Number(Number),
String(String),
Array(Vec<Value>),
Object(Map<String, Value>),
}
Assume that there is no further reference needed to the n_width field in this program, so no need to open the code by the Rust development team, only the JSON team would make the changes to the JSON file and the struct file.
If you want dynamically defined data validation, you should look into using something like JSON Schema.
If you need a mix of dynamic and static fields, this can be used along with serde(flatten) (as pointed out in Shepmaster's answer).
Assume that there is no further reference needed to the n_width field in this program, so no need to open the code by the Rust development team, only the JSON team would make the changes to the JSON file and the struct file.
If the Rust code doesn't care about the fields, then don't add them to the structs in the first place. Additional fields in the data that aren't included in the structure will be silently ignored by default.
If you need to bundle along extra data but never need to operate on it within the Rust code (such as in a proxy application), then review How can I use Serde's custom (de)serialization to update a subset of arbitrary input?

How to write a custom derive macro?

I'm trying to write my own derive mode macro in Rust, and the documentation on it is somewhat lacking in examples.
I have a struct like:
#[derive(MyMacroHere)]
struct Example {
id: i64,
value: Option<String>,
}
I want my macro to generate a method à la
fn set_fields(&mut self, id: i64, value: Option<String>) {
// ...
}
What are the basic steps to use the TokenStream trait to achieve something like that?
Create a crate for your procedural macros:
cargo new my_derive --lib
Edit the Cargo.toml to make it a procedural macro crate:
[lib]
proc-macro = true
Implement your procedural macro:
extern crate proc_macro;
use proc_macro::TokenStream;
#[proc_macro_derive(MyMacroHere)]
pub fn my_macro_here_derive(input: TokenStream) -> TokenStream {
// ...
}
Import the procedural macro and use it:
extern crate my_derive;
use my_derive::MyMacroHere;
#[derive(MyMacroHere)]
struct Example {
id: i64,
value: Option<String>,
}
The hard part is in implementation of the macro. Most people use the syn and quote crates to parse the incoming Rust code and then generate new code.
For example, syn's documentation starts with an example of a custom derive. You will parse the struct (or enum or union) and then handle the various ways of defining a struct (unit, tuple, named fields). You'll collect the information you need (type, maybe name), then you'll generate the appropriate code.
See also:
How to Write a Custom derive Macro
Documentation for proc_macro
Documentation for syn
Documentation for quote
Is it possible to add your own derivable traits, or are these fixed by the compiler?

Resources