rocket_community/form/
field.rs

1use crate::form::{
2    error::{Entity, Error, ErrorKind},
3    name::NameView,
4};
5use crate::fs::FileName;
6use crate::http::{ContentType, RawStr};
7use crate::{Data, Request};
8
9/// A form field with a string value.
10///
11/// Rocket preprocesses all form fields into either [`ValueField`]s or
12/// [`DataField`]s. All fields from url-encoded forms, and fields without
13/// Content-Types from multipart forms, are preprocessed as a `ValueField`.
14#[derive(Debug, Clone)]
15pub struct ValueField<'r> {
16    /// The (decoded) name of the form field.
17    pub name: NameView<'r>,
18    /// The (decoded) value of the form field.
19    pub value: &'r str,
20}
21
22/// A multipart form field with an underlying data stream.
23///
24/// Rocket preprocesses all form fields into either [`ValueField`]s or
25/// [`DataField`]s. Multipart form fields with a `Content-Type` are preprocessed
26/// as a `DataField`. The underlying data is _not_ read into memory, but
27/// instead, streamable from the contained [`Data`] structure.
28pub struct DataField<'r, 'i> {
29    /// The (decoded) name of the form field.
30    pub name: NameView<'r>,
31    /// The form fields's file name.
32    pub file_name: Option<&'r FileName>,
33    /// The form field's Content-Type, as submitted, which may or may not
34    /// reflect on `data`.
35    pub content_type: ContentType,
36    /// The request in which the form field was submitted.
37    pub request: &'r Request<'i>,
38    /// The raw data stream.
39    pub data: Data<'r>,
40}
41
42impl<'v> ValueField<'v> {
43    /// Parse a field string, where both the key and value are assumed to be
44    /// URL-decoded while preserving the `=` delimiter, into a `ValueField`.
45    ///
46    /// This implements 3.2, 3.3 of [section 5.1 of the WHATWG living standard].
47    ///
48    /// [section 5.1 of the WHATWG living standard]: https://url.spec.whatwg.org/#urlencoded-parsing
49    ///
50    /// # Example
51    ///
52    /// ```rust
53    /// # extern crate rocket_community as rocket;
54    /// use rocket::form::ValueField;
55    ///
56    /// let parsed = ValueField::parse("a cat=an A+ pet");
57    /// assert_eq!(parsed.name, "a cat");
58    /// assert_eq!(parsed.value, "an A+ pet");
59    ///
60    /// let parsed = ValueField::parse("a cat is an A+ pet");
61    /// assert_eq!(parsed.name, "a cat is an A+ pet");
62    /// assert_eq!(parsed.value, "");
63    ///
64    /// let parsed = ValueField::parse("cat.food=yum?");
65    /// assert_eq!(parsed.name, "cat");
66    /// assert_eq!(parsed.name.source(), "cat.food");
67    /// assert_eq!(parsed.value, "yum?");
68    /// ```
69    pub fn parse(field: &'v str) -> Self {
70        // WHATWG URL Living Standard 5.1 steps 3.2, 3.3.
71        let (name, val) = RawStr::new(field).split_at_byte(b'=');
72        ValueField::from((name.as_str(), val.as_str()))
73    }
74
75    /// Create a `ValueField` from a value, which is assumed to be URL-decoded.
76    /// The field `name` will be empty.
77    ///
78    /// This is equivalent to `ValueField::from(("", value))`. To create a
79    /// `ValueField` from both a `name` and a `value`, use
80    /// `ValueField::from((name, value))`.
81    ///
82    /// # Example
83    ///
84    /// ```rust
85    /// # extern crate rocket_community as rocket;
86    /// use rocket::form::ValueField;
87    ///
88    /// let parsed = ValueField::from_value("A+=kitten");
89    /// assert_eq!(parsed.name, "");
90    /// assert_eq!(parsed.value, "A+=kitten");
91    /// ```
92    pub fn from_value(value: &'v str) -> Self {
93        ValueField::from(("", value))
94    }
95
96    /// Shift the `name` of `self` and return `self` with the shifted `name`.
97    ///
98    /// See [`NameView::shift()`] for the details on name "shifting".
99    ///
100    /// # Example
101    ///
102    /// ```rust
103    /// # extern crate rocket_community as rocket;
104    /// use rocket::form::ValueField;
105    ///
106    /// let parsed = ValueField::parse("cat.food=yum?");
107    /// assert_eq!(parsed.name, "cat");
108    /// assert_eq!(parsed.name.source(), "cat.food");
109    /// assert_eq!(parsed.name.key_lossy(), "cat");
110    ///
111    /// let shifted = parsed.shift();
112    /// assert_eq!(shifted.name, "cat.food");
113    /// assert_eq!(shifted.name.key_lossy(), "food");
114    /// ```
115    pub fn shift(mut self) -> Self {
116        self.name.shift();
117        self
118    }
119
120    /// Creates a complete unexpected value field [`Error`] from `self`.
121    ///
122    /// The error will have the following properties:
123    ///   * `kind`: [`ErrorKind::Unexpected`]
124    ///   * `name`: [`self.name.source()`](NameView::source())
125    ///   * `value`: [`self.value`](ValueField::value)
126    ///   * `entity`: [`Entity::ValueField`]
127    ///
128    /// # Example
129    ///
130    /// ```rust
131    /// # extern crate rocket_community as rocket;
132    /// use rocket::form::ValueField;
133    /// use rocket::form::error::{ErrorKind, Entity};
134    ///
135    /// let field = ValueField::parse("cat.food=yum?");
136    /// let error = field.unexpected();
137    ///
138    /// assert_eq!(error.name.as_ref().unwrap(), "cat.food");
139    /// assert_eq!(error.value.as_ref().unwrap(), "yum?");
140    /// assert_eq!(error.kind, ErrorKind::Unexpected);
141    /// assert_eq!(error.entity, Entity::ValueField);
142    /// ```
143    pub fn unexpected(&self) -> Error<'v> {
144        Error::from(ErrorKind::Unexpected)
145            .with_name(self.name.source())
146            .with_value(self.value)
147            .with_entity(Entity::ValueField)
148    }
149
150    /// Creates a complete missing value field [`Error`] from `self`.
151    ///
152    /// The error will have the following properties:
153    ///   * `kind`: [`ErrorKind::Missing`]
154    ///   * `name`: [`self.name.source()`](NameView::source())
155    ///   * `value`: [`self.value`](ValueField::value)
156    ///   * `entity`: [`Entity::ValueField`]
157    ///
158    /// # Example
159    ///
160    /// ```rust
161    /// # extern crate rocket_community as rocket;
162    /// use rocket::form::ValueField;
163    /// use rocket::form::error::{ErrorKind, Entity};
164    ///
165    /// let field = ValueField::parse("cat.food=yum?");
166    /// let error = field.missing();
167    ///
168    /// assert_eq!(error.name.as_ref().unwrap(), "cat.food");
169    /// assert_eq!(error.value.as_ref().unwrap(), "yum?");
170    /// assert_eq!(error.kind, ErrorKind::Missing);
171    /// assert_eq!(error.entity, Entity::ValueField);
172    /// ```
173    pub fn missing(&self) -> Error<'v> {
174        Error::from(ErrorKind::Missing)
175            .with_name(self.name.source())
176            .with_value(self.value)
177            .with_entity(Entity::ValueField)
178    }
179}
180
181impl<'v> DataField<'v, '_> {
182    /// Shift the `name` of `self` and return `self` with the shifted `name`.
183    ///
184    /// This is identical to [`ValueField::shift()`] but for `DataField`s. See
185    /// [`NameView::shift()`] for the details on name "shifting".
186    ///
187    /// # Example
188    ///
189    /// ```rust
190    /// # extern crate rocket_community as rocket;
191    /// use rocket::form::DataField;
192    ///
193    /// fn push_data(field: DataField<'_, '_>) {
194    ///     let shifted = field.shift();
195    /// }
196    /// ```
197    pub fn shift(mut self) -> Self {
198        self.name.shift();
199        self
200    }
201
202    /// Creates a complete unexpected data field [`Error`] from `self`.
203    ///
204    /// The error will have the following properties:
205    ///   * `kind`: [`ErrorKind::Unexpected`]
206    ///   * `name`: [`self.name.source()`](NameView::source())
207    ///   * `value`: `None`
208    ///   * `entity`: [`Entity::DataField`]
209    ///
210    /// # Example
211    ///
212    /// ```rust
213    /// # extern crate rocket_community as rocket;
214    /// use rocket::form::DataField;
215    ///
216    /// fn push_data(field: DataField<'_, '_>) {
217    ///     let error = field.unexpected();
218    /// }
219    /// ```
220    pub fn unexpected(&self) -> Error<'v> {
221        Error::from(ErrorKind::Unexpected)
222            .with_name(self.name.source())
223            .with_entity(Entity::DataField)
224    }
225}
226
227impl<'a> From<(&'a str, &'a str)> for ValueField<'a> {
228    fn from((name, value): (&'a str, &'a str)) -> Self {
229        ValueField {
230            name: NameView::new(name),
231            value,
232        }
233    }
234}
235
236impl<'a, 'b> PartialEq<ValueField<'b>> for ValueField<'a> {
237    fn eq(&self, other: &ValueField<'b>) -> bool {
238        self.name == other.name && self.value == other.value
239    }
240}