4

I've recently started learning Rust, and I'm currently trying to create a small API; I've made my own struct for an API Response and enum for an Error:

// API Response Structure
pub struct ApiResponse {
    pub body: JsonValue,
    pub status: Status,
}

// Enum for my "custom errors"
#[derive(Debug, Snafu, Clone, Serialize)]
pub enum Errors {
    #[snafu(display("Unable to retrieve the specified entry."))]
    NotFound,

    #[snafu(display("Internal Server Error, unable to process the data."))]
    ISE,
}

Then I've implemented the Responder<'_> trait for my API Response struct:

impl<'r> Responder<'r> for ApiResponse {
    fn respond_to(self, req: &Request) -> Result<Response<'r>, Status> {
        Response::build_from(self.body.respond_to(req).unwrap())
            .status(self.status)
            .header(ContentType::JSON)
            .ok()
    }
}

However, when I try to use the mentioned, it would seem as I cannot have my function return a Result<JsonValue, Errors>. I'm not quite sure about this issue, nor am I that experienced with Rust, so any documentation/pointers will be greatly appreciated.

Here's the function which returns the Response type.

#[put("/users/<user_id>", format = "json", data = "<new_user>")]
pub fn update_user(
    conn: DbConnection,
    user_id: i32,
    new_user: Json<NewUser>,
) -> Result<JsonValue, ApiResponse> {

    match users::table.find(user_id).load::<User>(&*conn) {
        Ok(result) => {
            if result.len() < 1 {
                let response = ApiResponse {
                    body: json!({
                        "message": Errors::NotFound
                    }),
                    status: Status::NotFound
                };

                return Err(response)
            }
        },

        Err(e) => {
            let response = ApiResponse {
                body: json!({
                    "message": Errors::ISE
                }),
                status: Status::InternalServerError
            };

            return Err(response);
        },
    }

    match diesel::update(users::table.filter(id.eq(user_id)))
        .set((
            user_name.eq(new_user.user_name.to_string()),
            age.eq(new_user.age),
            gender.eq(new_user.gender.to_string()),
        ))
        .get_result::<User>(&*conn)
    {
        Ok(result) => Ok(json!(result)),
        Err(_) => {
            let res = ApiResponse {
                body: json!({
                    "message": Errors::ISE
                }),
                status: Status::InternalServerError
            };

            return Err(res);
        },
    }
}

Side-note: Please keep in mind that I'm still a beginner with Rust, and my error handling / code in general is not the best.


Edit: I forgot to include the error stack:

error[E0277]: the trait bound `std::result::Result<rocket_contrib::json::JsonValue, api_cont::ApiResponse>: rocket::response::Responder<'_>` is not satisfied
   --> src/routes.rs:72:6
    |
72  | ) -> Result<JsonValue, ApiResponse> {
    |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `rocket::response::Responder<'_>` is not implemented for `std::result::Result<rocket_contrib::json::JsonValue, api_cont::ApiResponse>`
    | 
   ::: /home/kenneth/.cargo/registry/src/github.com-1ecc6299db9ec823/rocket-0.4.4/src/handler.rs:202:20
    |
202 |     pub fn from<T: Responder<'r>>(req: &Request, responder: T) -> Outcome<'r> {
    |                    ------------- required by this bound in `rocket::handler::<impl rocket::Outcome<rocket::Response<'r>, rocket::http::Status, rocket::Data>>::from`
    |
    = help: the following implementations were found:
              <std::result::Result<R, E> as rocket::response::Responder<'r>>
              <std::result::Result<R, E> as rocket::response::Responder<'r>>
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
kernel_dev
  • 143
  • 1
  • 8
  • You are using rocket_contrib::json right? – peterulb Mar 26 '20 at 10:05
  • Yes, I am using `rocket_contrib::json`. – kernel_dev Mar 30 '20 at 04:46
  • Did you import your trait implementation on the place of usage? If yes, can you produce a gist which we can copy and execute? – peterulb Mar 30 '20 at 08:09
  • I'm fairly certain that I have. However, you can check for yourself via my GitHub repository: https://github.com/SketchyLxve/Rust-Projects I might’ve been a bit misleading with the code shown above, the implementation is in the same file as my `ApiResponse` struct and my `Errors` enum. – kernel_dev Mar 30 '20 at 08:54
  • Hey Sketchy, may I know what version of Rocket were you using? I'm having similar errors which did not exist before. These are my dependencies: ```toml [dependencies] rocket = "0.5.0-rc.1" serde = { version = "1.0.127" , features = ["derive"] } [dependencies.rocket_contrib] version = "0.4.10" default-features = false features = ["json"] ``` – MikeTheSapien Aug 03 '21 at 02:18
  • Hello, @mike: Sadly, I do not have the repository available any longer, I’ve deleted it quite awhile ago. Nor do I have the source code available to me. I apologise for the inconvenience! I hope you resolve your issue. – kernel_dev Oct 09 '21 at 05:03
  • No worries of course! But thanks for your response! I fixed my bug by mostly copying from [rocket](rocket.rs) and dumping my old code. It seems my problem had arisen due to rocket changing from an unstable toolchain into a stable one. – MikeTheSapien Oct 09 '21 at 12:43

1 Answers1

5

I've run into this error as well. The following is mentioned in the API Docs:

A type implementing Responder should implement the Debug trait when possible. This is because the Responder implementation for Result requires its Err type to implement Debug. Therefore, a type implementing Debug can more easily be composed.

That means you must implement Debug, as you use ApiResponse as an Err type.

#[derive(Debug)]
pub struct ApiResponse {
    pub body: JsonValue,
    pub status: Status,
}
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
peterulb
  • 2,869
  • 13
  • 20
  • Awesome, I totally forgot about having to derive debug. Thank you for the answer! It solved my issue. Greatly appreciate it. – kernel_dev Mar 30 '20 at 12:13