Handlebars helpers within Rocket - rust

I am trying to register a helper for handlebars the ultimate goal is to be able to have a helper that calculates/prints the length of a Vec. But I already fail to get the samples from the documentation into my program. I managed to do a 'minimal' example that shows the same error as my rocket page.
#[macro_use]
extern crate handlebars;
extern crate rocket;
extern crate rocket_contrib;
use handlebars::{Context, Handlebars, Helper, HelperResult, JsonRender, Output, RenderContext};
use rocket_contrib::templates::Template;
handlebars_helper!(hex: |v: i64| format!("0x{:x}", v));
fn wow_helper(
h: &Helper,
_: &Handlebars,
_: &Context,
_: &mut RenderContext,
out: &mut Output,
) -> HelperResult {
if let Some(param) = h.param(0) {
out.write("<b><i>")?;
out.write(&param.value().render())?;
out.write("</b></i>")?;
}
Ok(())
}
fn main() {
rocket::ignite()
.attach(Template::custom(|engines| {
engines
.handlebars
.register_helper("mlenb", Box::new(wow_helper));
}))
.launch();
println!("Hello, world!");
}
with the dependencies in cargo.toml:
[dependencies]
handlebars="3.3.0"
rocket="0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["diesel_sqlite_pool", "handlebars_templates", "serve"]
The error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:184:43
|
162 | / fn wow_helper(
163 | | h: &handlebars::Helper,
164 | | _: &handlebars::Handlebars,
165 | | _: &handlebars::Context,
... |
175 | | Ok(())
176 | | }
| |_- found signature of `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7> fn(&'r handlebars::Helper<'s, 't0>, &'t1 handlebars::Handlebars<'t2>, &'t3 handlebars::Context, &'t4 mut handlebars::RenderContext<'t5, 't6>, &'t7 mut (dyn handlebars::Output + 't7)) -> _`
...
184 | .register_helper("mlenb", Box::new(wow_helper));
| ^^^^^^^^^^^^^^^^^^^^ expected signature of `for<'r, 'reg, 'rc, 's, 't0> fn(&'r rocket_contrib::templates::handlebars::Helper<'reg, 'rc>, &'reg rocket_contrib::templates::handlebars::Handlebars, &'rc rocket_contrib::templates::handlebars::Context, &'s mut rocket_contrib::templates::handlebars::RenderContext<'reg>, &'t0 mut (dyn rocket_contrib::templates::handlebars::Output + 't0)) -> _`
|
= note: required because of the requirements on the impl of `rocket_contrib::templates::handlebars::HelperDef` for `for<'r, 's, 't0, 't1, 't2, 't3, 't4, 't5, 't6, 't7> fn(&'r handlebars::Helper<'s, 't0>, &'t1 handlebars::Handlebars<'t2>, &'t3 handlebars::Context, &'t4 mut handlebars::RenderContext<'t5, 't6>, &'t7 mut (dyn handlebars::Output + 't7)) -> std::result::Result<(), handlebars::RenderError> {wow_helper}`
= note: required for the cast to the object type `dyn rocket_contrib::templates::handlebars::HelperDef`

Don't need to use crate handlebars in cargo.toml with rocket crate.
its works well like that:
in cargo.toml only
[dependencies]
rocket = "0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["handlebars_templates", "serves"]
and in the main.rs use:
use rocket_contrib :: templates :: {Template, handlebars};
use handlebars :: {Helper, Handlebars, Context, RenderContext, Output, HelperResult, RenderError};
So there will be no problems with versioning the handlebars!

Thanks to #jebrosen:mozilla.org in the rocket matrix channel the problem got resolved.
Problem:
I used incompatible versions of handlebars. The rocket library uses version 1.0 while I used Version 3.0 in the Cargo.toml.
Solution:
Change the Cargo.toml to:
[dependencies]
handlebars="1.0"
rocket="0.4.5"
[dependencies.rocket_contrib]
version = "0.4.5"
default-features = false
features = ["diesel_sqlite_pool", "handlebars_templates", "serve"]
Careful a rebuild requires a cargo clean otherwise the changes don't get built.
Solution for the initial Problem:
I did not manage to use the macro handlebars_helper! to create the length helper but instead created it using the function syntax.
fn mlen_helper_fun(
h: &handlebars::Helper<'_, '_>,
_: &handlebars::Handlebars,
_: &handlebars::Context,
_: &mut handlebars::RenderContext<'_>,
out: &mut dyn handlebars::Output,
) -> handlebars::HelperResult {
if let Some(param) = h.param(0) {
if let serde_json::value::Value::Array(arr) = param.value() {
out.write(&format!("{}", arr.len()))?;
Ok(())
} else {
Err(handlebars::RenderError::new("Not a array"))
}
} else {
Err(handlebars::RenderError::new(
"No parameter given at least one required",
))
}
}
This can then be used with:
.attach(Template::custom(|engines| {
engines
.handlebars
.register_helper("length", Box::new(mlen_helper_fun));
}))

Related

Error while rocket implementation for creating a basic API

I have been programming in Rust for a month or so and now I wanted to try out API's using Rocket in rust. I tried to implement the below code but I got the error the trait bound \Json<Test>: Responder<'_, '_> is not satisfied . Not quite sure what I did wrong I tried to find out a reason for it but couldn't get any. I am referring to this video and this guide, according to which I did everything correct.
Also side note: I use extern crate rocket but it still does not recognize Build, routes, etc. Any solution to this as well?
#![feature(proc_macro_hygiene, decl_macro)]
#[macro_use]
extern crate rocket;
use rocket::*;
use rocket_contrib::json::Json;
use serde_json::json;
struct Test{
name: String,
}
#[get("/")]
fn hello_world() -> Json<Test> {
let test: Test = Test { name: String::from("Test54") };
Json(test)
}
#[launch]
fn rocket() -> Rocket<Build> {
rocket::build()
.mount(
"/",
routes![hello_world]
)
}
cargo.toml
[package]
name = "backend"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "lib"
path = "src/lib.rs"
[[bin]]
name = "backend"
path = "src/bin.rs"
[dependencies]
rocket = "0.5.0-rc.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
[dependencies.rocket_contrib]
version = "0.4"
default-features = false
features = ["json"]
error log
error[E0277]: the trait bound `Json<Test>: Responder<'_, '_>` is not satisfied
--> src/bin.rs:19:21
|
19 | fn hello_world() -> Json<Test> {
| ^^^^^^^^^^ the trait `Responder<'_, '_>` is not implemented for `Json<Test>`
|
= help: the following other types implement trait `Responder<'r, 'o>`:
<&'o [u8] as Responder<'r, 'o>>
<&'o str as Responder<'r, 'o>>
<() as Responder<'r, 'static>>
<(ContentType, R) as Responder<'r, 'o>>
<(Status, R) as Responder<'r, 'o>>
188 | pub fn from<R: Responder<'r, 'o>>(req: &'r Request<'_>, responder: R) -> Outcome<'r> {
| ^^^^^^^^^^^^^^^^^ required by this bound in `route::handler::<impl Outcome<rocket::Response<'o>, Status, rocket::Data<'o>>>::fr
om`
For more information about this error, try `rustc --explain E0277`.
rocket_contrib::json::Json only implements rocket::response::Responder if the T it contains implements serde::ser::Serialize (due to this). Currently, your Teststruct does not implement Serialize, so Json<Test> does not implement Responder; this is what the compiler is complaining about.
You can easily implement Serialize for Test by deriving it:
use serde::Serialize;
#[derive(Serialize)]
struct Test {
name: String,
}

How should I use dependencies of dependencies?

I'm working on project using warp. Warp includes the bytes cargo as a dependency and I want to use the Bytes struct that gets passed into the callback. Here's an example:
use warp::Filter;
fn process(data: bytes::Bytes) -> impl warp::reply::Reply {
println!("{:?}", data);
"processed!"
}
#[tokio::main]
async fn main() {
let process = warp::path("process")
.and(warp::post())
.and(warp::body::bytes())
.map(process);
warp::serve(process)
.run(([127, 0, 0, 1], 3030))
.await;
}
Here's the dependencies in my Cargo.toml:
[dependencies]
bytes = "*"
tokio = { version = "0.2", features = ["full"] }
warp = "0.2"
This setup gives me this error:
error[E0631]: type mismatch in function arguments
--> src/main.rs:13:14
|
3 | fn process(data: bytes::Bytes) -> impl warp::reply::Reply {
| --------------------------------------------------------- found signature of `fn(bytes::bytes::Bytes) -> _`
...
13 | .map(process);
| ^^^^^^^ expected signature of `fn(bytes::bytes::Bytes) -> _`
|
= note: required because of the requirements on the impl of `warp::generic::Func<(bytes::bytes::Bytes,)>` for `fn(bytes::bytes::Bytes) -> impl warp::reply::Reply {process}`
Not a big fan of this error message because it's telling me that it's looking for the signature fn(bytes::bytes::Bytes) -> _ and it found fn(bytes::bytes::Bytes) -> _ which pretty much look exactly the same to me, but after some searching I realized it's because there's two different versions of Bytes getting pulled in, so I adjust my Cargo.toml like this:
bytes = "0.5.6"
So I get the same version of bytes that warp is pulling in.
Is this the conventional way to deal with this issue?

Rust trait not satisfied

I'm new to rust and tried searching in stackoverflow as well as reading the serde documentation
https://docs.serde.rs/serde/trait.Serialize.html and https://serde.rs/impl-serialize.html, but I was a bit lost.
I would like to use Tera to generate html and the struct I'm passing it does not have the trait serde::ser::Serialize implemented and I tried to implement it but it doesn't appear quite right.
Cargo.toml dependencies
serde = "1.0.115"
serde_derive = "1.0.115"
serde-xml-rs = "0.4.0"
tera = "0.7.2"
main.rs
extern crate tera;
#[macro_use]
extern crate serde_derive;
extern crate serde;
use tera::Context;
use serde::ser::{Serialize, SerializeStruct, Serializer};
#[derive(Serialize, Debug)]
struct Person {
firstname: String,
lastname: String,
age: i32,
}
#[derive(Debug)]
struct Attendees {
people: Vec<Person>,
updatedOn: String,
updatedBy: String,
}
impl Serialize for Attendees {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut s = serializer.serialize_struct("Person", 3)?;
s.serialize_field("people", &self.people)?;
s.serialize_field("updatedOn", &self.updatedOn)?;
s.serialize_field("updatedBy", &self.updatedBy)?;
s.end()
}
}
fn main() {
let mut context = Context::new();
let mut peeps: Vec<Person> = Vec::new();
let mut attendees = Attendees {
people: peeps,
updatedOn: String::from("today"),
updatedBy: String::from("someone"),
};
context.add("attendees", &attendees);
}
compiler says:
mytest % cargo run
Compiling mytest v0.1.0 (/home/mike/mytest)
error[E0277]: the trait bound `Attendees: serde::ser::Serialize` is not satisfied
--> src/main.rs:44:29
|
44 | context.add("attendees", &attendees);
| ^^^^^^^^^^ the trait `serde::ser::Serialize` is not implemented for `Attendees`
error: aborting due to previous error
I am clearly missing something... Can anyone please help?
The trait you implemented and the trait that the error is referring are not the same, because they refer to two different versions of serde.
[[package]]
name = "tera"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c37e2aaa53871f9c3722a20f8951fea0afd366955e11542a58feb71997c6d769"
dependencies = [
"chrono",
"error-chain",
"glob",
"humansize",
"lazy_static 0.2.11",
"pest",
"regex",
"serde 0.9.15",
"serde_json",
"slug",
"url",
]
tera 0.7.2 is not using the version 1.0.* of serde, but 0.9.*.
You may use a more recent of tera, or use a compatible serde version in your Cargo.toml:
[dependencies]
serde = "0.9.15"
serde_derive = "0.9.15"

the trait `serde::Deserialize<'_>` is not implemented for `diesel_geography::types::GeogPoint`

I'm trying to use Diesel and diesel_geography to read from a PostGIS database using Rust.
Here's the error I'm getting:
error[E0277]: the trait bound `diesel_geography::types::GeogPoint: serde::Serialize` is not satisfied
--> src/models.rs:11:5
|
11 | pub coordinates: GeogPoint,
| ^^^ the trait `serde::Serialize` is not implemented for `diesel_geography::types::GeogPoint`
|
= note: required by `serde::ser::SerializeStruct::serialize_field`
error[E0277]: the trait bound `diesel_geography::types::GeogPoint: serde::Deserialize<'_>` is not satisfied
--> src/models.rs:11:5
|
11 | pub coordinates: GeogPoint,
| ^^^ the trait `serde::Deserialize<'_>` is not implemented for `diesel_geography::types::GeogPoint`
|
= note: required by `serde::de::SeqAccess::next_element`
error[E0277]: the trait bound `diesel_geography::types::GeogPoint: serde::Deserialize<'_>` is not satisfied
--> src/models.rs:11:5
|
11 | pub coordinates: GeogPoint,
| ^^^ the trait `serde::Deserialize<'_>` is not implemented for `diesel_geography::types::GeogPoint`
|
= note: required by `serde::de::MapAccess::next_value`
Looking around, I found that a similar error happens when there are several versions of serde used as dependency, this can be checked using cargo tree -d. I've tried and serde does not appear as a duplicate dependency.
This is my code so far:
Cargo.toml
[package]
name = "123"
version = "0.1.0"
authors = ["ASD"]
edition = "2018"
[dependencies]
diesel = { version = "1.4.2", features = ["postgres"] }
serde = { version = "1.0", features = ["derive"] }
serde_json="1.0"
dotenv = "0.14.1"
diesel-geography = "0.2.0"
schema.rs
table! {
use diesel::sql_types::*;
use diesel_geography::sql_types::*;
users (email) {
email -> Varchar,
password -> Varchar,
coordinates -> Geography
}
}
models.rs
use diesel_geography::types::*;
use crate::schema::users;
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize, Queryable, Insertable)]
#[table_name = "users"]
pub struct User {
pub email: String,
pub password: String,
pub coordinates: GeogPoint
}
main.rs
extern crate serde;
extern crate dotenv;
#[macro_use] extern crate diesel;
//#[macro_use] extern crate serde_derive;
mod models;
mod schema;
use diesel::PgConnection;
use dotenv::dotenv;
use std::env;
use diesel::prelude::*;
fn main() {
dotenv().ok();
let database_url = env::var("DATABASE_URL")
.expect("DATABASE_URL must be set");
let connection = PgConnection::establish(&database_url)
.expect(&format!("Error connecting to {}", database_url));
use crate::schema::users::dsl::*;
use crate::models::User;
let results = users
.limit(5)
.load::<User>(&connection)
.expect("Error loading users");
println!("Displaying {} users", results.len());
for user in results {
println!("{}", user.email);
println!("----------\n");
println!("{}", user.password);
}
}
Serde is an optional dependency of diesel-geography. You need to enable the feature:
[dependencies]
diesel-geography = { version = "0.2.0", features = ["serde"] }

Multiple actix-web client requests - expected struct actix_web::Error found ()

I have this code:
extern crate actix;
extern crate actix_web;
extern crate env_logger;
extern crate futures; // 0.1.25
extern crate tokio; // 0.1.17
use futures::future::ok as fut_ok;
use futures::Future;
use tokio::runtime::Builder;
use std::sync::{Arc, Mutex};
extern crate serde_json;
type Error = ();
fn questions_data(
val: i32,
) -> Box<Future<Item = serde_json::Value, Error = actix_web::error::Error>> {
let f = std::fs::read_to_string("auth_token").unwrap();
let token = f.trim();
use actix_web::{client, HttpMessage};
use std::time::Duration;
Box::new(
client::ClientRequest::get(
"https://jsonplaceholder.typicode.com/todos/".to_owned() + &val.to_string(),
)
.header(
actix_web::http::header::AUTHORIZATION,
"Bearer ".to_owned() + token,
)
.finish()
.unwrap()
.send()
.timeout(Duration::from_secs(30))
.map_err(actix_web::error::Error::from) // <- convert SendRequestError to an Error
.and_then(|resp| {
resp.body().limit(67_108_864).from_err().and_then(|body| {
let resp: serde_json::Value = serde_json::from_slice(&body).unwrap();
fut_ok(resp)
})
}),
)
}
fn main() {
let num_workers = 8;
let mut core = Builder::new().core_threads(num_workers).build().unwrap();
let results = Arc::new(Mutex::new(Vec::new()));
for n in 1..100 {
let res = results.clone();
core.spawn(questions_data(n).map(move |n| {
res.lock().unwrap().push(n);
}));
}
core.shutdown_on_idle().wait().unwrap();
let data = results.lock().unwrap();
println!("{:?}", *data);
}
[dependencies]
futures = "0.1.25"
tokio-core = "0.1.17"
futures-util = "0.2.1"
tokio = "0.1.11"
rand = "0.6.0"
actix-web = "0.7.14"
actix = "0.7.6"
env_logger = "0.6.0"
serde_json = "1.0.33"
I get error when I cargo run it:
error[E0271]: type mismatch resolving `<std::boxed::Box<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>> as futures::Future>::Error == ()`
--> src/main.rs:52:14
|
52 | core.spawn(Box::new(questions_data(n).map(move |n| {
| ^^^^^ expected struct `actix_web::Error`, found ()
|
= note: expected type `actix_web::Error`
found type `()`
error[E0277]: `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>` cannot be sent between threads safely
--> src/main.rs:52:14
|
52 | core.spawn(Box::new(questions_data(n).map(move |n| {
| ^^^^^ `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>` cannot be sent between threads safely
|
= help: the trait `std::marker::Send` is not implemented for `dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>`
= note: required because it appears within the type `std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>`
= note: required because it appears within the type `futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>>`
= note: required because it appears within the type `std::boxed::Box<futures::Map<std::boxed::Box<dyn futures::Future<Item=serde_json::Value, Error=actix_web::Error>>, [closure#src/main.rs:52:51: 54:10 res:_]>>`
Similar code, without running actix-web client, works https://play.rust-lang.org/?version=stable&mode=debug&edition=2015&gist=e81185a73fcb40a3426875573c78accd
EDIT:
Maybe something like map_err(|_| ()) but don’t know how to apply that:
https://users.rust-lang.org/t/type-error-when-combining-streams/21619
Type mismatch resolving the error type when forwarding messages from a futures channel to a WebSocket Sink
As rustc says there are two errors here:
First is about error types mismatch. It could be fixed with map_err as you already noticed:
questions_data(n)
.map(move |n| {
res.lock().unwrap().push(n);
})
.map_err(|e| println!("an error occurred: {}", e))
Second (about Send marker) is somewhat more confusing. Basically it means that tokio::Runtime::spawn want its argument future to be also Send + 'static because tokio could move it to another thread. But your future cannot be send between threads safely because it contains non-sendable type (dyn futures::Stream<Item=bytes::bytes::Bytes, Error=actix_web::Error> + 'static) inside actix_web::client::ClientRequest type.
Probably the easiest way to fix the second error is to use actix::spawn function instead tokio::Runtime::spawn method: it requires only 'static marker for its argument.
After these changes your program compiles at least.

Resources