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