axum_test/multipart/
part.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
use anyhow::Context;
use bytes::Bytes;
use mime::Mime;
use std::fmt::Display;

///
/// For creating a section of a MultipartForm.
///
/// Use [`Part::text()`](crate::multipart::Part::text()) and [`Part::bytes()`](crate::multipart::Part::bytes()) for creating new instances.
/// Then attach them to a `MultipartForm` using [`MultipartForm::add_part()`](crate::multipart::MultipartForm::add_part()).
///
pub struct Part {
    pub(crate) bytes: Bytes,
    pub(crate) file_name: Option<String>,
    pub(crate) mime_type: Mime,
}

impl Part {
    /// Creates a new part of a multipart form, that will send text.
    ///
    /// The default mime type for this part will be `text/plain`,
    pub fn text<T>(text: T) -> Self
    where
        T: Display,
    {
        Self {
            bytes: text.to_string().into_bytes().into(),
            file_name: None,
            mime_type: mime::TEXT_PLAIN,
        }
    }

    /// Creates a new part of a multipart form, that will upload bytes.
    ///
    /// The default mime type for this part will be `application/octet-stream`,
    pub fn bytes<B>(bytes: B) -> Self
    where
        B: Into<Bytes>,
    {
        Self {
            bytes: bytes.into(),
            file_name: None,
            mime_type: mime::APPLICATION_OCTET_STREAM,
        }
    }

    /// Sets the file name for this part of a multipart form.
    ///
    /// By default there is no filename. This will set one.
    pub fn file_name<T>(mut self, file_name: T) -> Self
    where
        T: Display,
    {
        self.file_name = Some(file_name.to_string());
        self
    }

    /// Sets the mime type for this part of a multipart form.
    ///
    /// The default mime type is `text/plain` or `application/octet-stream`,
    /// depending on how this instance was created.
    /// This function will replace that.
    pub fn mime_type<M>(mut self, mime_type: M) -> Self
    where
        M: AsRef<str>,
    {
        let raw_mime_type = mime_type.as_ref();
        let parsed_mime_type = raw_mime_type
            .parse()
            .with_context(|| format!("Failed to parse '{raw_mime_type}' as a Mime type"))
            .unwrap();

        self.mime_type = parsed_mime_type;

        self
    }
}

#[cfg(test)]
mod test_text {
    use super::*;

    #[test]
    fn it_should_contain_text_given() {
        let part = Part::text("some_text");

        let output = String::from_utf8_lossy(&part.bytes);
        assert_eq!(output, "some_text");
    }

    #[test]
    fn it_should_use_mime_type_text() {
        let part = Part::text("some_text");
        assert_eq!(part.mime_type, mime::TEXT_PLAIN);
    }
}

#[cfg(test)]
mod test_byes {
    use super::*;

    #[test]
    fn it_should_contain_bytes_given() {
        let bytes = "some_text".as_bytes();
        let part = Part::bytes(bytes);

        let output = String::from_utf8_lossy(&part.bytes);
        assert_eq!(output, "some_text");
    }

    #[test]
    fn it_should_use_mime_type_octet_stream() {
        let bytes = "some_text".as_bytes();
        let part = Part::bytes(bytes);

        assert_eq!(part.mime_type, mime::APPLICATION_OCTET_STREAM);
    }
}

#[cfg(test)]
mod test_file_name {
    use super::*;

    #[test]
    fn it_should_use_file_name_given() {
        let mut part = Part::text("some_text");

        assert_eq!(part.file_name, None);
        part = part.file_name("my-text.txt");
        assert_eq!(part.file_name, Some("my-text.txt".to_string()));
    }
}

#[cfg(test)]
mod test_mime_type {
    use super::*;

    #[test]
    fn it_should_use_mime_type_set() {
        let mut part = Part::text("some_text");

        assert_eq!(part.mime_type, mime::TEXT_PLAIN);
        part = part.mime_type("application/json");
        assert_eq!(part.mime_type, mime::APPLICATION_JSON);
    }

    #[test]
    #[should_panic]
    fn it_should_error_if_invalid_mime_type() {
        let part = Part::text("some_text");
        part.mime_type("🦊");

        assert!(false);
    }
}