Skip to main content

reinhardt_core/parsers/
form.rs

1use async_trait::async_trait;
2use bytes::Bytes;
3use http::HeaderMap;
4use std::collections::HashMap;
5
6use super::parser::{ParseError, ParseResult, ParsedData, Parser};
7
8/// Form parser for application/x-www-form-urlencoded content type
9#[derive(Debug, Clone, Default)]
10pub struct FormParser;
11
12impl FormParser {
13	/// Create a new FormParser.
14	///
15	/// # Examples
16	///
17	/// ```
18	/// use reinhardt_core::parsers::form::FormParser;
19	///
20	/// let parser = FormParser::new();
21	/// ```
22	pub fn new() -> Self {
23		Self
24	}
25}
26
27#[async_trait]
28impl Parser for FormParser {
29	fn media_types(&self) -> Vec<String> {
30		vec!["application/x-www-form-urlencoded".to_string()]
31	}
32
33	async fn parse(
34		&self,
35		_content_type: Option<&str>,
36		body: Bytes,
37		_headers: &HeaderMap,
38	) -> ParseResult<ParsedData> {
39		if body.is_empty() {
40			return Ok(ParsedData::Form(HashMap::new()));
41		}
42
43		match serde_urlencoded::from_bytes::<HashMap<String, String>>(&body) {
44			Ok(form_data) => Ok(ParsedData::Form(form_data)),
45			Err(e) => Err(ParseError::ParseError(format!("Invalid form data: {}", e))),
46		}
47	}
48}
49
50#[cfg(test)]
51mod tests {
52	use super::*;
53
54	#[tokio::test]
55	async fn test_form_parser_valid() {
56		let parser = FormParser::new();
57		let body = Bytes::from("name=test&value=123&active=true");
58		let headers = HeaderMap::new();
59
60		let result = parser
61			.parse(Some("application/x-www-form-urlencoded"), body, &headers)
62			.await
63			.unwrap();
64
65		match result {
66			ParsedData::Form(form) => {
67				assert_eq!(form.get("name"), Some(&"test".to_string()));
68				assert_eq!(form.get("value"), Some(&"123".to_string()));
69				assert_eq!(form.get("active"), Some(&"true".to_string()));
70			}
71			_ => panic!("Expected form data"),
72		}
73	}
74
75	#[tokio::test]
76	async fn test_form_parser_empty() {
77		let parser = FormParser::new();
78		let body = Bytes::new();
79		let headers = HeaderMap::new();
80
81		let result = parser
82			.parse(Some("application/x-www-form-urlencoded"), body, &headers)
83			.await
84			.unwrap();
85
86		match result {
87			ParsedData::Form(form) => {
88				assert!(form.is_empty());
89			}
90			_ => panic!("Expected form data"),
91		}
92	}
93
94	#[tokio::test]
95	async fn test_form_parser_url_encoded() {
96		let parser = FormParser::new();
97		let body = Bytes::from("message=Hello%20World&symbol=%26");
98		let headers = HeaderMap::new();
99
100		let result = parser
101			.parse(Some("application/x-www-form-urlencoded"), body, &headers)
102			.await
103			.unwrap();
104
105		match result {
106			ParsedData::Form(form) => {
107				assert_eq!(form.get("message"), Some(&"Hello World".to_string()));
108				assert_eq!(form.get("symbol"), Some(&"&".to_string()));
109			}
110			_ => panic!("Expected form data"),
111		}
112	}
113
114	#[test]
115	fn test_form_parser_media_types() {
116		let parser = FormParser::new();
117		let media_types = parser.media_types();
118
119		assert!(media_types.contains(&"application/x-www-form-urlencoded".to_string()));
120	}
121
122	// Tests from Django REST Framework
123
124	#[tokio::test]
125	async fn test_form_parse_drf() {
126		// DRF test: Make sure the form parsing works correctly
127		let parser = FormParser::new();
128		let body = Bytes::from("field1=abc&field2=defghijk");
129		let headers = HeaderMap::new();
130
131		let result = parser
132			.parse(Some("application/x-www-form-urlencoded"), body, &headers)
133			.await
134			.unwrap();
135
136		match result {
137			ParsedData::Form(form) => {
138				// Validate the form data can be used for validation
139				assert_eq!(form.get("field1"), Some(&"abc".to_string()));
140				assert_eq!(form.get("field2"), Some(&"defghijk".to_string()));
141
142				// Simulate form validation (field1 max_length=3, field2 any length)
143				let field1_valid = form.get("field1").map(|v| v.len() <= 3).unwrap_or(false);
144				assert!(field1_valid);
145			}
146			_ => panic!("Expected form data"),
147		}
148	}
149}