reinhardt_core/parsers/
parser.rs1use crate::exception::{Error, Result};
2use async_trait::async_trait;
3use bytes::Bytes;
4use http::HeaderMap;
5use serde_json::Value;
6use std::collections::HashMap;
7
8pub type ParseError = Error;
9pub type ParseResult<T> = Result<T>;
10
11#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct MediaType {
14 pub main_type: String,
15 pub sub_type: String,
16 pub parameters: HashMap<String, String>,
17}
18
19impl MediaType {
20 pub fn new(main_type: impl Into<String>, sub_type: impl Into<String>) -> Self {
33 Self {
34 main_type: main_type.into(),
35 sub_type: sub_type.into(),
36 parameters: HashMap::new(),
37 }
38 }
39 pub fn with_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
51 self.parameters.insert(key.into(), value.into());
52 self
53 }
54 pub fn parse(content_type: &str) -> ParseResult<Self> {
67 let parts: Vec<&str> = content_type.split(';').collect();
68 if parts.is_empty() {
69 return Err(Error::Validation(content_type.to_string()));
70 }
71
72 let type_parts: Vec<&str> = parts[0].trim().split('/').collect();
73 if type_parts.len() != 2 {
74 return Err(Error::Validation(content_type.to_string()));
75 }
76
77 let mut media_type = MediaType::new(type_parts[0], type_parts[1]);
78
79 for part in parts.iter().skip(1) {
81 let param_parts: Vec<&str> = part.trim().splitn(2, '=').collect();
82 if param_parts.len() == 2 {
83 media_type.parameters.insert(
84 param_parts[0].trim().to_string(),
85 param_parts[1].trim().to_string(),
86 );
87 }
88 }
89
90 Ok(media_type)
91 }
92 pub fn matches(&self, pattern: &str) -> bool {
107 let parts: Vec<&str> = pattern.split('/').collect();
108 if parts.len() != 2 {
109 return false;
110 }
111
112 (parts[0] == "*" || parts[0] == self.main_type)
113 && (parts[1] == "*" || parts[1] == self.sub_type)
114 }
115}
116
117impl std::fmt::Display for MediaType {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 write!(f, "{}/{}", self.main_type, self.sub_type)?;
120 for (key, value) in &self.parameters {
121 write!(f, "; {}={}", key, value)?;
122 }
123 Ok(())
124 }
125}
126
127#[derive(Debug, Clone)]
129pub enum ParsedData {
130 Json(Value),
131 Xml(Value),
132 Yaml(Value),
133 Form(HashMap<String, String>),
134 MultiPart {
135 fields: HashMap<String, String>,
136 files: Vec<UploadedFile>,
137 },
138 File(UploadedFile),
139 MessagePack(Value),
140 Protobuf(Value),
141}
142
143#[derive(Debug, Clone)]
145pub struct UploadedFile {
146 pub name: String,
147 pub filename: Option<String>,
148 pub content_type: Option<String>,
149 pub size: usize,
150 pub data: Bytes,
151}
152
153impl UploadedFile {
154 pub fn new(name: String, data: Bytes) -> Self {
170 let size = data.len();
171 Self {
172 name,
173 filename: None,
174 content_type: None,
175 size,
176 data,
177 }
178 }
179 pub fn with_filename(mut self, filename: String) -> Self {
192 self.filename = Some(filename);
193 self
194 }
195 pub fn with_content_type(mut self, content_type: String) -> Self {
208 self.content_type = Some(content_type);
209 self
210 }
211}
212
213#[async_trait]
215pub trait Parser: Send + Sync {
216 fn media_types(&self) -> Vec<String>;
218
219 async fn parse(
221 &self,
222 content_type: Option<&str>,
223 body: Bytes,
224 headers: &HeaderMap,
225 ) -> ParseResult<ParsedData>;
226
227 fn can_parse(&self, content_type: Option<&str>) -> bool {
229 if let Some(ct) = content_type
230 && let Ok(media_type) = MediaType::parse(ct)
231 {
232 return self.media_types().iter().any(|mt| media_type.matches(mt));
233 }
234 false
235 }
236}
237
238#[derive(Default)]
240pub struct ParserRegistry {
241 parsers: Vec<Box<dyn Parser>>,
242}
243
244impl ParserRegistry {
245 pub fn new() -> Self {
255 Self::default()
256 }
257 pub fn register<P: Parser + 'static>(mut self, parser: P) -> Self {
271 self.parsers.push(Box::new(parser));
272 self
273 }
274 pub async fn parse(
296 &self,
297 content_type: Option<&str>,
298 body: Bytes,
299 headers: &HeaderMap,
300 ) -> ParseResult<ParsedData> {
301 for parser in &self.parsers {
302 if parser.can_parse(content_type) {
303 return parser.parse(content_type, body, headers).await;
304 }
305 }
306
307 Err(Error::Validation(
308 content_type.unwrap_or("none").to_string(),
309 ))
310 }
311}
312
313#[cfg(test)]
314mod tests {
315 use super::*;
316
317 #[test]
318 fn test_media_type_parse() {
319 let mt = MediaType::parse("application/json").unwrap();
320 assert_eq!(mt.main_type, "application");
321 assert_eq!(mt.sub_type, "json");
322
323 let mt = MediaType::parse("text/html; charset=utf-8").unwrap();
324 assert_eq!(mt.main_type, "text");
325 assert_eq!(mt.sub_type, "html");
326 assert_eq!(mt.parameters.get("charset"), Some(&"utf-8".to_string()));
327 }
328
329 #[test]
330 fn test_media_type_matches() {
331 let mt = MediaType::new("application", "json");
332 assert!(mt.matches("application/json"));
333 assert!(mt.matches("application/*"));
334 assert!(mt.matches("*/json"));
335 assert!(mt.matches("*/*"));
336 assert!(!mt.matches("text/html"));
337 }
338}