How to parse multipart forms using abonander/multipart with Rocket?

Rocket's primary abstraction for data is the FromData trait. Given the POST data and the request, you can construct a given type:

pub trait FromData<'a>: Sized {
    type Error;
    type Owned: Borrow<Self::Borrowed>;
    type Borrowed: ?Sized;
    fn transform(
        request: &Request, 
        data: Data
    ) -> Transform<Outcome<Self::Owned, Self::Error>>;
    fn from_data(
        request: &Request, 
        outcome: Transformed<'a, Self>
    ) -> Outcome<Self, Self::Error>;
}

Then, it's just a matter of reading the API for multipart and inserting tab A into slot B:

#![feature(proc_macro_hygiene, decl_macro)]

use multipart::server::Multipart; // 0.16.1, default-features = false, features = ["server"]
use rocket::{
    data::{Data, FromData, Outcome, Transform, Transformed},
    post, routes, Request,
}; // 0.4.2
use std::io::Read;

#[post("/", data = "<upload>")]
fn index(upload: DummyMultipart) -> String {
    format!("I read this: {:?}", upload)
}

#[derive(Debug)]
struct DummyMultipart {
    alpha: String,
    one: i32,
    file: Vec<u8>,
}

// All of the errors in these functions should be reported
impl<'a> FromData<'a> for DummyMultipart {
    type Owned = Vec<u8>;
    type Borrowed = [u8];
    type Error = ();

    fn transform(_request: &Request, data: Data) -> Transform<Outcome<Self::Owned, Self::Error>> {
        let mut d = Vec::new();
        data.stream_to(&mut d).expect("Unable to read");

        Transform::Owned(Outcome::Success(d))
    }

    fn from_data(request: &Request, outcome: Transformed<'a, Self>) -> Outcome<Self, Self::Error> {
        let d = outcome.owned()?;

        let ct = request
            .headers()
            .get_one("Content-Type")
            .expect("no content-type");
        let idx = ct.find("boundary=").expect("no boundary");
        let boundary = &ct[(idx + "boundary=".len())..];

        let mut mp = Multipart::with_body(&d[..], boundary);

        // Custom implementation parts

        let mut alpha = None;
        let mut one = None;
        let mut file = None;

        mp.foreach_entry(|mut entry| match &*entry.headers.name {
            "alpha" => {
                let mut t = String::new();
                entry.data.read_to_string(&mut t).expect("not text");
                alpha = Some(t);
            }
            "one" => {
                let mut t = String::new();
                entry.data.read_to_string(&mut t).expect("not text");
                let n = t.parse().expect("not number");
                one = Some(n);
            }
            "file" => {
                let mut d = Vec::new();
                entry.data.read_to_end(&mut d).expect("not file");
                file = Some(d);
            }
            other => panic!("No known key {}", other),
        })
        .expect("Unable to iterate");

        let v = DummyMultipart {
            alpha: alpha.expect("alpha not set"),
            one: one.expect("one not set"),
            file: file.expect("file not set"),
        };

        // End custom

        Outcome::Success(v)
    }
}

fn main() {
    rocket::ignite().mount("/", routes![index]).launch();
}

I've never used either of these APIs for real, so there's no guarantee that this is a good implementation. In fact, all the panicking on error definitely means it's suboptimal. A production usage would handle all of those cleanly.

However, it does work:

%curl -X POST -F alpha=omega -F one=2 -F file=@hello http://localhost:8000/
I read this: DummyMultipart { alpha: "omega", one: 2, file: [104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, 10] }

An advanced implementation would allow for some abstraction between the user-specific data and the generic multipart aspects. Something like Multipart<MyForm> would be nice.

The author of Rocket points out that this solution allows a malicious end user to POST an infinitely sized file, which would cause the machine to run out of memory. Depending on the intended use, you may wish to establish some kind of cap on the number of bytes read, potentially writing to the filesystem at some breakpoint.