I am getting a compiler error trying to use the log crate in a package in a workspace. The other crates in the workspace are using logging without problem.
Cargo.toml:
[dependencies]
log = "^0"
rocket = "^0"
[dependencies.uuid]
version = "^0"
features = ["v4"]
lib.rs:
#![feature(proc_macro_hygiene, decl_macro)]
use rocket::{
Request,
Data,
Response
};
use rocket::request::{
self,
FromRequest,
Outcome
};
use rocket::fairing::{
Fairing,
Info,
Kind
};
use uuid::Uuid;
use std::fmt;
use log;
pub struct LoggerFairing {
service_name: &'static str
}
impl LoggerFairing {
pub fn new(service_name: &'static str) -> Self {
LoggerFairing {
service_name
}
}
}
impl Fairing for LoggerFairing {
fn info(&self) -> Info {
Info {
name: self.service_name,
kind: Kind::Request | Kind::Response
}
}
fn on_request(&self, req: &mut Request, _: &Data) {
let ip_addr = req.client_ip().map(|addr| addr.to_string())
.unwrap_or("IP Address unknown".to_string());
let method = req.method();
let url = req.uri();
let request_id = get_request_id(req);
log::info!("request {:?} from {}: {} {}", request_id, ip_addr, method, url);
}
fn on_response(&self, req: &Request, res: &mut Response) {
let request_id = get_request_id(req);
let status = res.status();
log::info!("request {:?} responded with {}", request_id, status);
}
}
fn get_request_id<'t, 'r>(req: &'t Request<'r>) -> Option<RequestId<'t>> {
match req.guard::<RequestId>() {
Outcome::Success(request_id) => Some(request_id),
_ => None
}
}
pub struct RequestId<'t> {
pub id: &'t Uuid
}
impl<'t, 'r> FromRequest<'t, 'r> for RequestId<'t> {
type Error = ();
fn from_request(req: &'t Request<'r>) -> request::Outcome<Self, Self::Error> {
let id = req.local_cache(|| Uuid::new_v4());
request::Outcome::Success(RequestId {
id
})
}
}
impl<'t> fmt::Display for RequestId<'t> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
The error message:
error: cannot find macro `log` in this scope
--> utils\logging\src\lib.rs:62:9
|
62 | log::info!("request {:?} from {}: {} {}", request_id, ip_addr, method, url);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: cannot find macro `log` in this scope
--> utils\logging\src\lib.rs:71:9
|
71 | log::info!("request {:?} responded with {}", request_id, status);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to 2 previous errors
error: could not compile `logging`.
I've used several variations of the use log statement and how I'm calling the info! macro, but they all cause the same error message. I've tried specifying exact versions in Cargo.toml.
I'm stumped. This is exactly how I'm using log in other crates.
Specifying the exact version of the log crate (0.4.11) in Cargo.toml fixes this problem.
I'm assuming there is something funny happening when Cargo tries to resolve the dependencies.
Related
I'm trying to make it possible to register an actix route automatically. To do so I found the crate Inventory which seems to answer my need.
I have the following code:
#[get("/hello2/{name}")]
async fn greet2(req: HttpRequest) -> String {
let name = req.match_info().get("name").unwrap_or("World");
let a = format!("Hello2 {}!", &name);
a
}
pub struct TestStruct {
test: fn(HttpRequest) -> dyn Future<Output = String>
}
inventory::collect!(TestStruct);
inventory::submit! {
let mut a = TestStruct {
test: <greet2 as HttpServiceFactory>::register
};
a.test.push(greet2::register::greet2);
a
}
#[actix_web::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(move || {
let mut app = App::new();
for route in inventory::iter::<AutomaticHttpService> {
app = app.service(route);
}
app
})
.bind(("127.0.0.1", 8080))?
.workers(2)
.run()
.await
}
My code does not compile as I cannot access the requested function pointer and I cannot use the HttpServiceFactory trait as it is not declared in the same file. I also tried to use the std::any::Any type and then cast to HttpServiceFactory (maybe using unsafe) but without success because its size was not known at compile time.
Would you have any clues to unblock my situation?
I want to add some error handler in the rust function, so I define the rust function response like this so that I could return the error message if encount the recoverable error:
pub fn add_bill_book() -> Result<BillBook,String> {
return Err("failed".parse().unwrap())
}
and box the result to return to client like this;
fn main() {
let result = add_bill_book();
box_rest_response(result);
}
but when I compile the project, shows error like this:
➜ rust-learn git:(multiple-statement) ✗ cargo build
Compiling rust-learn v0.1.0 (/Users/xiaoqiangjiang/source/reddwarf/backend/rust-learn)
error[E0277]: the trait bound `Result<BillBook, std::string::String>: std::default::Default` is not satisfied
--> src/main.rs:8:23
|
8 | box_rest_response(result);
| ----------------- ^^^^^^ the trait `std::default::Default` is not implemented for `Result<BillBook, std::string::String>`
| |
| required by a bound introduced by this call
|
note: required by a bound in `box_rest_response`
--> src/main.rs:11:87
|
11 | pub fn box_rest_response<T>(data: T) -> content::RawJson<String> where T: Serialize + Default {
| ^^^^^^^ required by this bound in `box_rest_response`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `rust-learn` due to previous error
what should I do to fix it? I think it is hard to implement the default trait for all type of Result. This is the full minimal reproduce example:
use rocket::response::content;
use rust_wheel::model::response::api_response::ApiResponse;
use serde::Serialize;
use serde::Deserialize;
fn main() {
let result = add_bill_book();
box_rest_response(result);
}
pub fn box_rest_response<T>(data: T) -> content::RawJson<String> where T: Serialize + Default {
let res = ApiResponse {
result: data,
..Default::default()
};
let response_json = serde_json::to_string(&res).unwrap();
return content::RawJson(response_json);
}
#[derive(Debug,Serialize,Deserialize,Default,Clone)]
pub struct BillBook {
pub id: i64,
pub created_time: i64,
pub updated_time: i64,
pub deleted: i32,
pub creator: i64,
pub bill_book_template_id: i32,
pub remark: Option<String>,
pub contents: Option<String>,
}
pub fn add_bill_book() -> Result<BillBook,String> {
return Err("failed".parse().unwrap())
}
and this is the Cargo.toml dependencies:
[package]
name = "rust-learn"
version = "0.1.0"
edition = "2018"
[dependencies]
rocket = { version = "=0.5.0-rc.2", features = ["json"] }
serde = { version = "1.0.64", features = ["derive"] }
serde_json = "1.0.64"
serde_derive = "1.0"
# database
diesel = { version = "1.4.7", features = ["postgres","serde_json"] }
dotenv = "0.15.0"
jsonwebtoken = "7"
chrono = "0.4"
config = "0.11"
ring = "0.16.20"
md5 = "0.7.0"
data-encoding = "2.3.2"
# reddwarf public component
rust_wheel = { git = "https://github.com/jiangxiaoqiang/rust_wheel.git" }
I tried this way to solve this problem:
return match contents {
Ok(_) => {
box_rest_response(contents.unwrap())
},
Err(_) => {
box_rest_response(contents.unwrap_err())
}
}
but the new problem is that I have to match the result in many places, is it possible to optimized it? I tried to add a new function in public lib like this:
pub fn box_rest_result<T,E>(result: Result<T,E>) -> content::RawJson<String> where T: Serialize + Default, E: std::default::Default{
return match result {
Ok(_) => {
box_rest_response(result.unwrap())
},
Err(_) => {
box_rest_response(result.unwrap_err())
}
}
}
still facing the same problem.
Result does not implement Default because there is no one-fits-all default value.
You can create a newtype struct and implement Default on it, as in How do I implement a trait I don't own for a type I don't own?.
Note that match with unwrap() is a very bad idea, since match can already extract the value:
match contents {
Ok(v) => box_rest_response(v),
Err(e) => box_rest_response(e),
}
I know the issue is that I have two Result types from different libraries but can't find how to fix it.
[dependencies]
crossterm = "0.23"
time = "0.3.9"
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["blocking", "json"] }
use time::Instant;
use std::collections::HashMap;
use crossterm::{
event::{self, Event, KeyCode, KeyEvent},
Result,
};
pub fn read_char() -> Result<char> {
loop {
if let Event::Key(KeyEvent {
code: KeyCode::Char(c),
..
}) = event::read()?
{
return Ok(c);
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let instant = Instant::now();
let response = reqwest::blocking::get("https://httpbin.org/ip")?
.json::<HashMap<String, String>>()?;
let duration = instant.elapsed();
println!("ns = {:?}, response: {:#?}, ", duration.whole_nanoseconds(), response);
// Any key to continue
println!("Press any key to continue:");
println!("{:?}", read_char());
Ok(())
}
Gives the error:
error[E0107]: this type alias takes 1 generic argument but 2 generic arguments were supplied
--> src\main.rs:20:14
|
20 | fn main() -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^ -------------------------- help: remove this generic argument
| |
| expected 1 generic argument
How do I fix this? I have searched but am likely looking for incorrect terms e.g. namespace alias and core::Result error[E0107] is not really helping.
I have tried this without success:
fn main() -> core::Result<(), Box<dyn std::error::Error>> {
You have crossterm ::Result in scope, so you would have to disambiguate the result you want to return, otherwise it just thinks you want to return the crossterm type:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
...
Ok(())
}
I'm trying to write a Rocket / Juniper / Rust based GraphQL Server using PickleDB - an in-memory key/value store.
The pickle db is created / loaded at the start and given to rocket to manage:
fn rocket() -> Rocket {
let pickle_path = var_os(String::from("PICKLE_PATH")).unwrap_or(OsString::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Bin;
let pickle_db: PickleDb = match Path::new(&pickle_path).exists() {
false => PickleDb::new(pickle_path, pickle_db_dump_policy, pickle_serialization_method),
true => PickleDb::load(pickle_path, pickle_db_dump_policy, pickle_serialization_method).unwrap(),
};
rocket::ignite()
.manage(Schema::new(Query, Mutation))
.manage(pickle_db)
.mount(
"/",
routes![graphiql, get_graphql_handler, post_graphql_handler],
)
}
And I want to retrieve the PickleDb instance from the Rocket State in my Guard:
pub struct Context {
pickle_db: PickleDb,
}
impl juniper::Context for Context {}
impl<'a, 'r> FromRequest<'a, 'r> for Context {
type Error = ();
fn from_request(_request: &'a Request<'r>) -> request::Outcome<Context, ()> {
let pickle_db = _request.guard::<State<PickleDb>>()?.inner();
Outcome::Success(Context { pickle_db })
}
}
This does not work because the State only gives me a reference:
26 | Outcome::Success(Context { pickle_db })
| ^^^^^^^^^ expected struct `pickledb::pickledb::PickleDb`, found `&pickledb::pickledb::PickleDb`
When I change my Context struct to contain a reference I get lifetime issues which I'm not yet familiar with:
15 | pickle_db: &PickleDb,
| ^ expected named lifetime parameter
I tried using 'static which does make rust quite unhappy and I tried to use the request lifetime (?) 'r of the FromRequest, but that does not really work either...
How do I get this to work? As I'm quite new in rust, is this the right way to do things?
I finally have a solution, although the need for unsafe indicates it is sub-optimal :)
#![allow(unsafe_code)]
use pickledb::{PickleDb, PickleDbDumpPolicy, SerializationMethod};
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::env;
use std::path::Path;
use std::time::Duration;
pub static mut PICKLE_DB: Option<PickleDb> = None;
pub fn cache_init() {
let pickle_path = env::var(String::from("PICKLE_PATH")).unwrap_or(String::from("pickle.db"));
let pickle_db_dump_policy = PickleDbDumpPolicy::PeriodicDump(Duration::from_secs(120));
let pickle_serialization_method = SerializationMethod::Json;
let pickle_db = match Path::new(&pickle_path).exists() {
false => PickleDb::new(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
),
true => PickleDb::load(
pickle_path,
pickle_db_dump_policy,
pickle_serialization_method,
)
.unwrap(),
};
unsafe {
PICKLE_DB = Some(pickle_db);
}
}
pub fn cache_get<V>(key: &str) -> Option<V>
where
V: DeserializeOwned + std::fmt::Debug,
{
unsafe {
let pickle_db = PICKLE_DB
.as_ref()
.expect("cache uninitialized - call cache_init()");
pickle_db.get::<V>(key)
}
}
pub fn cache_set<V>(key: &str, value: &V) -> Result<(), pickledb::error::Error>
where
V: Serialize,
{
unsafe {
let pickle_db = PICKLE_DB
.as_mut()
.expect("cache uninitialized - call cache_init()");
pickle_db.set::<V>(key, value)?;
Ok(())
}
}
This can be simply imported and used as expected, but I think I'll run into issues when the load gets to high...
This question already has answers here:
Is there any way to return a reference to a variable created in a function?
(5 answers)
Closed 5 years ago.
I'm trying to write a wrapper around serde_json & Rocket's FromData to strongly type some of the JSON I exchange with a server.
I can't compile the following code:
extern crate serde_json;
extern crate rocket;
extern crate serde;
use serde::ser::Error;
use serde_json::Value;
use rocket::data::DataStream;
use rocket::outcome::IntoOutcome;
use std::io::Read;
static NULL: Value = serde_json::Value::Null;
pub struct ResponseJSON<'v> {
success: bool,
http_code: u16,
data: &'v serde_json::Value,
}
impl<'v> ResponseJSON<'v> {
pub fn ok() -> ResponseJSON<'v> {
ResponseJSON {
success: true,
http_code: 200,
data: &NULL,
}
}
pub fn http_code(mut self, code: u16) -> ResponseJSON<'v> {
self.http_code = code;
self
}
pub fn data(mut self, ref_data: &'v serde_json::Value) -> ResponseJSON<'v> {
self.data = ref_data;
self
}
pub fn from_serde_value(json: &'v serde_json::Value) -> ResponseJSON<'v> {
if !json["success"].is_null() {
ResponseJSON::ok()
.http_code(json["http_code"].as_u64().unwrap() as u16)
.data(json.get("data").unwrap_or(&NULL))
} else {
ResponseJSON::ok()
.data(json.pointer("").unwrap())
}
}
}
impl<'v> rocket::data::FromData for ResponseJSON<'v> {
type Error = serde_json::Error;
fn from_data(request: &rocket::Request, data: rocket::Data) -> rocket::data::Outcome<Self, serde_json::Error> {
if !request.content_type().map_or(false, |ct| ct.is_json()) {
println!("Content-Type is not JSON.");
return rocket::Outcome::Forward(data);
}
let data_from_reader = data.open().take(1<<20);
let value = serde_json::from_reader(data_from_reader);
let unwraped_value : Value = if value.is_ok() { value.unwrap() } else { Value::Null };
if !unwraped_value.is_null() {
Ok(ResponseJSON::from_serde_value(&unwraped_value)).into_outcome()
} else {
Err(serde_json::Error::custom("Unable to create JSON from reader")).into_outcome()
}
}
}
fn main() {
println!("it runs!");
}
The compiler's error:
Compiling tests v0.1.0 (file:///Users/bgbahoue/Projects.nosync/tests)
error: `unwraped_value` does not live long enough
--> src/main.rs:64:48
|
64 | Ok(ResponseJSON::from_serde_value(&unwraped_value)).into_outcome()
| ^^^^^^^^^^^^^^ does not live long enough
...
68 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'v as defined on the body at 53:114...
--> src/main.rs:53:115
|
53 | fn from_data(request: &rocket::Request, data: rocket::Data) -> rocket::data::Outcome<Self, serde_json::Error> {
| ___________________________________________________________________________________________________________________^
54 | | if !request.content_type().map_or(false, |ct| ct.is_json()) {
55 | | println!("Content-Type is not JSON.");
56 | | return rocket::Outcome::Forward(data);
... |
67 | | }
68 | | }
| |_____^
error: aborting due to previous error
Since data_from_reader, value and unwraped_value come from data I thought the compiler could infer that it had the same lifetime but apparently it's not the case. Is there any way I could state that or do something that would work in such a case ?
serde_json::from_reader:
pub fn from_reader<R, T>(rdr: R) -> Result<T>
where
R: Read,
T: DeserializeOwned,
rocket::data::Data::open:
fn open(self) -> DataStream
rocket::data::DataStream::take:
fn take(self, limit: u64) -> Take<Self>
As per #LukasKalbertodt 's comment above, it does work when ResponseJSON owns the serde_json::Value
Revised code (pasted as is, even though there are better ways to chain some parts of the code)
#![allow(dead_code)]
extern crate serde_json;
extern crate rocket;
extern crate serde;
use serde::ser::Error;
use serde_json::Value;
use rocket::outcome::IntoOutcome;
use std::io::Read;
static NULL: Value = serde_json::Value::Null;
pub struct ResponseJSON { // <-- changed to remove the lifetime parameter
success: bool,
http_code: u16,
data: serde_json::Value, // <- changed to remove the '&'
}
impl ResponseJSON {
pub fn ok() -> ResponseJSON {
ResponseJSON {
success: true,
http_code: 200,
data: Value::Null,
}
}
pub fn http_code(mut self, code: u16) -> ResponseJSON {
self.http_code = code;
self
}
pub fn data(mut self, data: serde_json::Value) -> ResponseJSON { // <- changed to remove the '&'
self.data = data;
self
}
pub fn from_serde_value(json: serde_json::Value) -> ResponseJSON { // <- changed to remove the reference & lifetime parameter
if !json["success"].is_null() {
ResponseJSON::ok()
.http_code(json["http_code"].as_u64().unwrap() as u16)
.data(json.get("data").unwrap_or(&NULL).clone())
} else {
ResponseJSON::ok()
.data(json.pointer("").unwrap().clone())
}
}
}
impl rocket::data::FromData for ResponseJSON {
type Error = serde_json::Error;
fn from_data(request: &rocket::Request, data: rocket::Data) -> rocket::data::Outcome<Self, serde_json::Error> {
if !request.content_type().map_or(false, |ct| ct.is_json()) {
println!("Content-Type is not JSON.");
return rocket::Outcome::Forward(data);
}
let data_from_reader = data.open().take(1<<20);
let value = serde_json::from_reader(data_from_reader);
let unwraped_value : Value = if value.is_ok() { value.unwrap() } else { Value::Null };
if !unwraped_value.is_null() {
Ok(ResponseJSON::from_serde_value(unwraped_value)).into_outcome() // <- changed to remove the '&' in front of `unwraped_value`
} else {
Err(serde_json::Error::custom("Unable to create JSON from reader")).into_outcome()
}
}
}
fn main() {
println!("it compiles & runs");
}
cargo run output
Compiling tests v0.1.0 (file:///Users/bgbahoue/Projects.nosync/tests)
Finished dev [unoptimized + debuginfo] target(s) in 1.28 secs
Running `target/debug/tests`
it compiles & runs
My take is that in that case the ownership (lifetime ?) of the input parameter's data is passed to data_from_reader to value to unwraped_value to the temp ResponseJSON to the returned rocket::data::Outcome; so it seems ok.
With references, the temp ResponseJSON didn't outlive the function end, since it outlived the serde_json::Value from which it was created i.e. unwraped_value lifetime i.e. the function's end; hence the compiler issue.
Not 100% sure of my explaination though, would love your thoughts about that