logo
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
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
use crate::form::{name::NameView, error::{Error, ErrorKind, Entity}};
use crate::http::{ContentType, RawStr};
use crate::{Request, Data};
use crate::fs::FileName;

/// A form field with a string value.
///
/// Rocket preprocesses all form fields into either [`ValueField`]s or
/// [`DataField`]s. All fields from url-encoded forms, and fields without
/// Content-Types from multipart forms, are preprocessed as a `ValueField`.
#[derive(Debug, Clone)]
pub struct ValueField<'r> {
    /// The (decoded) name of the form field.
    pub name: NameView<'r>,
    /// The (decoded) value of the form field.
    pub value: &'r str,
}

/// A multipart form field with an underlying data stream.
///
/// Rocket preprocesses all form fields into either [`ValueField`]s or
/// [`DataField`]s. Multipart form fields with a `Content-Type` are preprocessed
/// as a `DataField`. The underlying data is _not_ read into memory, but
/// instead, streamable from the contained [`Data`] structure.
pub struct DataField<'r, 'i> {
    /// The (decoded) name of the form field.
    pub name: NameView<'r>,
    /// The form fields's file name.
    pub file_name: Option<&'r FileName>,
    /// The form field's Content-Type, as submitted, which may or may not
    /// reflect on `data`.
    pub content_type: ContentType,
    /// The request in which the form field was submitted.
    pub request: &'r Request<'i>,
    /// The raw data stream.
    pub data: Data<'r>,
}

impl<'v> ValueField<'v> {
    /// Parse a field string, where both the key and value are assumed to be
    /// URL-decoded while preserving the `=` delimiter, into a `ValueField`.
    ///
    /// This implements 3.2, 3.3 of [section 5.1 of the WHATWG living standard].
    ///
    /// [section 5.1 of the WHATWG living standard]: https://url.spec.whatwg.org/#urlencoded-parsing
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::ValueField;
    ///
    /// let parsed = ValueField::parse("a cat=an A+ pet");
    /// assert_eq!(parsed.name, "a cat");
    /// assert_eq!(parsed.value, "an A+ pet");
    ///
    /// let parsed = ValueField::parse("a cat is an A+ pet");
    /// assert_eq!(parsed.name, "a cat is an A+ pet");
    /// assert_eq!(parsed.value, "");
    ///
    /// let parsed = ValueField::parse("cat.food=yum?");
    /// assert_eq!(parsed.name, "cat");
    /// assert_eq!(parsed.name.source(), "cat.food");
    /// assert_eq!(parsed.value, "yum?");
    /// ```
    pub fn parse(field: &'v str) -> Self {
        // WHATWG URL Living Standard 5.1 steps 3.2, 3.3.
        let (name, val) = RawStr::new(field).split_at_byte(b'=');
        ValueField::from((name.as_str(), val.as_str()))
    }

    /// Create a `ValueField` from a value, which is assumed to be URL-decoded.
    /// The field `name` will be empty.
    ///
    /// This is equivalent to `ValueField::from(("", value))`. To create a
    /// `ValueField` from both a `name` and a `value`, use
    /// `ValueField::from((name, value))`.
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::ValueField;
    ///
    /// let parsed = ValueField::from_value("A+=kitten");
    /// assert_eq!(parsed.name, "");
    /// assert_eq!(parsed.value, "A+=kitten");
    /// ```
    pub fn from_value(value: &'v str) -> Self {
        ValueField::from(("", value))
    }

    /// Shift the `name` of `self` and return `self` with the shfited `name`.
    ///
    /// See [`NameView::shift()`] for the details on name "shifting".
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::ValueField;
    ///
    /// let parsed = ValueField::parse("cat.food=yum?");
    /// assert_eq!(parsed.name, "cat");
    /// assert_eq!(parsed.name.source(), "cat.food");
    /// assert_eq!(parsed.name.key_lossy(), "cat");
    ///
    /// let shifted = parsed.shift();
    /// assert_eq!(shifted.name, "cat.food");
    /// assert_eq!(shifted.name.key_lossy(), "food");
    /// ```
    pub fn shift(mut self) -> Self {
        self.name.shift();
        self
    }

    /// Creates a complete unexpected value field [`Error`] from `self`.
    ///
    /// The error will have the following properties:
    ///   * `kind`: [`ErrorKind::Unexpected`]
    ///   * `name`: [`self.name.source()`](NameView::source())
    ///   * `value`: [`self.value`](ValueField::value)
    ///   * `entity`: [`Entity::ValueField`]
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::ValueField;
    /// use rocket::form::error::{ErrorKind, Entity};
    ///
    /// let field = ValueField::parse("cat.food=yum?");
    /// let error = field.unexpected();
    ///
    /// assert_eq!(error.name.as_ref().unwrap(), "cat.food");
    /// assert_eq!(error.value.as_ref().unwrap(), "yum?");
    /// assert_eq!(error.kind, ErrorKind::Unexpected);
    /// assert_eq!(error.entity, Entity::ValueField);
    /// ```
    pub fn unexpected(&self) -> Error<'v> {
        Error::from(ErrorKind::Unexpected)
            .with_name(self.name.source())
            .with_value(self.value)
            .with_entity(Entity::ValueField)
    }

    /// Creates a complete mising value field [`Error`] from `self`.
    ///
    /// The error will have the following properties:
    ///   * `kind`: [`ErrorKind::Missing`]
    ///   * `name`: [`self.name.source()`](NameView::source())
    ///   * `value`: [`self.value`](ValueField::value)
    ///   * `entity`: [`Entity::ValueField`]
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::ValueField;
    /// use rocket::form::error::{ErrorKind, Entity};
    ///
    /// let field = ValueField::parse("cat.food=yum?");
    /// let error = field.missing();
    ///
    /// assert_eq!(error.name.as_ref().unwrap(), "cat.food");
    /// assert_eq!(error.value.as_ref().unwrap(), "yum?");
    /// assert_eq!(error.kind, ErrorKind::Missing);
    /// assert_eq!(error.entity, Entity::ValueField);
    /// ```
    pub fn missing(&self) -> Error<'v> {
        Error::from(ErrorKind::Missing)
            .with_name(self.name.source())
            .with_value(self.value)
            .with_entity(Entity::ValueField)
    }
}

impl<'v> DataField<'v, '_> {
    /// Shift the `name` of `self` and return `self` with the shifted `name`.
    ///
    /// This is identical to [`ValueField::shift()`] but for `DataField`s. See
    /// [`NameView::shift()`] for the details on name "shifting".
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::DataField;
    ///
    /// fn push_data(field: DataField<'_, '_>) {
    ///     let shifted = field.shift();
    /// }
    /// ```
    pub fn shift(mut self) -> Self {
        self.name.shift();
        self
    }

    /// Creates a complete unexpected data field [`Error`] from `self`.
    ///
    /// The error will have the following properties:
    ///   * `kind`: [`ErrorKind::Unexpected`]
    ///   * `name`: [`self.name.source()`](NameView::source())
    ///   * `value`: `None`
    ///   * `entity`: [`Entity::DataField`]
    ///
    /// # Example
    ///
    /// ```rust
    /// use rocket::form::DataField;
    ///
    /// fn push_data(field: DataField<'_, '_>) {
    ///     let error = field.unexpected();
    /// }
    /// ```
    pub fn unexpected(&self) -> Error<'v> {
        Error::from(ErrorKind::Unexpected)
            .with_name(self.name.source())
            .with_entity(Entity::DataField)
    }
}

impl<'a> From<(&'a str, &'a str)> for ValueField<'a> {
    fn from((name, value): (&'a str, &'a str)) -> Self {
        ValueField { name: NameView::new(name), value }
    }
}

impl<'a, 'b> PartialEq<ValueField<'b>> for ValueField<'a> {
    fn eq(&self, other: &ValueField<'b>) -> bool {
        self.name == other.name && self.value == other.value
    }
}