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}