ferristseng / rust-ipfs-api

IPFS HTTP client in Rust
Apache License 2.0
247 stars 68 forks source link

remove 'static lifetime for add() argument #48

Open raffomania opened 4 years ago

raffomania commented 4 years ago

When using add, AFAIK the data I pass in has to be moved into the method because the argument requires a 'static lifetime. This makes the following impossible:

    let mut child = Command::new("cat")
        .arg("test.txt")
        .stdout(Stdio::piped())
        .spawn()?;

    let hash = ipfs.add(child.stdout?);
    child.wait()?; // error: child was moved in the previous line
    hash

What I want to do:

  1. directly pipe stdout into ipfs, without waiting
  2. then wait for the first command to finish and check if it returned status 0
  3. then return the ipfs hash

please correct me if I'm wrong, but with a 'static lifetime I think there's no way to do this.

ferristseng commented 4 years ago

Sorry for the late response. I think this might be a requirement for hyper. See this related issue: https://github.com/ferristseng/rust-multipart-rfc7578/issues/4

rand0m-cloud commented 2 years ago

The 'static requirement on data is extremely limited and makes the function useless because the usecase of adding compile time data to IPFS only exists in example code. I'm reverting to using the ipfs cli in my project because of this issue.

ferristseng commented 2 years ago

I believe this lifetime is a restriction of Actix/Hyper (example: https://docs.rs/hyper/0.14.16/hyper/struct.Body.html#method.wrap_stream).

What exactly are you trying to do?

ferristseng commented 2 years ago

please correct me if I'm wrong, but with a 'static lifetime I think there's no way to do this.

Added an example of how to do this: https://github.com/ferristseng/rust-ipfs-api/blob/35d282f4cfb2cf083abff5efa60c3c916f1d13fa/ipfs-api-examples/examples/add_file_piped_from_command.rs

raffomania commented 2 years ago

Thanks for following up! I'm not sure how you're able to call take without any arguments in that example, as the docs say it should take an u64 as a limit. Also, I don't understand how that example solves the lifetime problems I encountered, and at this point I don't remember the exact errors I found, so maybe I made an error unrelated to the lifetime constraints of add. I'll try to incorporate the example into my project and report back, however I'm not sure when I'll get around to that.

mrd0ll4r commented 1 year ago

Reviving this: I'm trying to cat data from IPFS and re-feed it to add with a different hash function, both via the HTTP API.

In the optimal case I don't want to read all of this into memory and then re-add it. Best case scenario would be to plumb the output of cat directly into add_async_with_options.

The slightly-worse-but-probably-works-for-now solution is to read all of it into memory and then add it, something like this:

let cat_resp = client
                    .cat(cid)
                    .map_ok(|chunk| chunk.to_vec())
                    .try_concat()
                    .await
                    .context("unable to cat file")?;

let sha1_resp = client.add_async_with_options(cat_resp.as_slice(),opts).await?;

But that does not work:

error[E0597]: `cat_resp` does not live long enough
   --> src/bin/test-api.rs:198:63
    |
198 |                 let sha1_resp = client.add_async_with_options(cat_resp.as_slice(),opts).await?;
    |                                 ------------------------------^^^^^^^^^^^^^^^^^^^------
    |                                 |                             |
    |                                 |                             borrowed value does not live long enough
    |                                 argument requires that `cat_resp` is borrowed for `'static`
199 |             }
    |             - `cat_resp` dropped here while still borrowed

For more information about this error, try `rustc --explain E0597`.

Edit: Looking a bit, I don't think the multipart::Form stuff needs a static lifetime:

impl<'a> Form<'a> {
    pub fn add_async_reader<F, R>(&mut self, name: F, read: R)
    where
        F: Display,
        R: 'a + AsyncRead + Send + Sync + Unpin,
    {
// ...
    }

// ...
}

Edit2: It does work with add_with_options (without AsyncRead), wrapping the Vec<u8> in a Cursor... hmm.