0

Context

I'm building a small Rust CLI to automate some tasks in Gitlab. I'm using reqwest to send my HTTP requests to the Gitlab API. I'm currently refactoring my code, and now I would like to remove this duplicated code snippet.

fn main() {
    // ... some code
    let create_branch_response = gitlab::create_branch(&client, &args)
        .expect("An error ocurred while processing your request to create a new branch");
    match create_branch_response.status() {
        StatusCode::OK => (),
        StatusCode::BAD_REQUEST => {
            let json_response = create_branch_response
                .json::<gitlab::GitlabError>()
                .expect("An unkown error happened while creating your new branch!");
            panic!(
                "A validation error ocurred while creating your new branch: {}",
                json_response.message
            );
        }
        _ => panic!("An unexpected error ocurred while creating your new branch"),
    }
    let new_branch = create_branch_response
        .json::<gitlab::Branch>()
        .expect("An error ocurred while reading the response");
    println!("New branch {} created!", new_branch.name);
    println!("URL: {}", new_branch.web_url);
    // Some other code
    let create_pr_response = gitlab::create_pr(&client, &args)
        .expect("An error ocurred while processing your request to create a merge request");
    match create_pr_response.status() {
        StatusCode::OK => (),
        StatusCode::BAD_REQUEST => {
            let json_response = create_pr_response
                .json::<gitlab::GitlabError>()
                .expect("An unkown error happened while creating your new merge request!");
            panic!(
                "A validation error ocurred while creating your new merge request: {}",
                json_response.message
            );
        }
        _ => panic!("An unexpected error ocurred while creating your merge request"),
    }
    let new_pr = create_pr_response
        .json::<gitlab::MergeRequest>()
        .expect("An error ocurred while reading the merge request response");
    println!("New pull request \"{}\" created!", new_pr.title);
    println!("URL: {}", new_pr.web_url);
}

My goal is to handle the response's status code on a separate function:

pub fn handle_response_status(status: StatusCode, resource: String, response: &Response) {
    match status {
        StatusCode::OK => (),
        StatusCode::BAD_REQUEST => {
            let json_response = response.json::<gitlab::GitlabError>().expect(
                format!(
                    "An unkown error happened while creating your new {}!",
                    resource
                )
                .as_str(),
            );
            panic!(
                "A validation error ocurred while creating your new {}: {}",
                resource, json_response.message
            );
        }
        _ => panic!(
            "An unexpected error ocurred while creating your {}",
            resource
        ),
    }
}

This code does not work due to the json method call which deserializes my response body into a desired struct.

error[E0507]: cannot move out of `*response` which is behind a shared reference
  --> src/util.rs:22:33
   |
22 |             let json_response = response.json::<gitlab::GitlabError>().expect(
   |                                 ^^^^^^^^^-----------------------------
   |                                 |        |
   |                                 |        `*response` moved due to this method call
   |                                 move occurs because `*response` has type `reqwest::blocking::Response`, which does not implement the `Copy` trait

What I tried

I already tried the following (sorry, I'm still new to rust).

let json_response = response.clone().json::<gitlab::GitlabError>().expect(
                format!(
                    "An unkown error happened while creating your new {}!",
                    resource
                )
                .as_str(),
            );

But I get the same error.

error[E0507]: cannot move out of a shared reference
   --> src/util.rs:12:33
    |
12  |             let json_response = response.clone().json::<gitlab::GitlabError>().expect(
    |                                 ^^^^^^^^^^^^^^^^^-----------------------------
    |                                 |                |
    |                                 |                value moved due to this method call
    |                                 move occurs because value has type `reqwest::blocking::Response`, which does not implement the `Copy` trait
    |

I also tried receiving the response as text and then deserializing with serde_json, but calling create_branch_response.json() also causes a move.

If you are interested on reading the whole code for better context, here's a branch with these (broken) changes: https://github.com/Je12emy/shears-cli/tree/util_module. Any feedback is appreciated

Jeremy
  • 1,447
  • 20
  • 40
  • Why do you need to do this "without moving"? Your original code consumed `Response`s, why can't `handle_response_status` take an owned `Response` too? – Cerberus May 20 '23 at 19:18
  • After handling the error status code on the `handle_response_status` I deserialize the data to print out some information. But you are right, I could do this inside the function, take ownership and not deal with this. I have updated the code snippet to reflect my intent – Jeremy May 20 '23 at 19:28

1 Answers1

0

The reason you're getting a error[E0507]: cannot move out of a shared reference is because you are actually cloning the reference instead of the actual struct (Response). Rust will implicitly deref the ref for function call for some case (see Implicit Deref Coercions with Functions and Methods), but not for your case since reqwest::blocking::Response does not implement Clone.

See this example

To solve your borrowing problem, I recommend you to get the bytes value from request, create a BufReader, and then use serde_json::from_reader (reqwest internally use this). Then you can parse the json multiple times.

Andra
  • 1,282
  • 2
  • 11
  • 34