From 772bcff0ac553482781a3d97cb5395a58bab7286 Mon Sep 17 00:00:00 2001 From: elcharitas Date: Wed, 30 Apr 2025 19:13:26 +0100 Subject: [PATCH 1/3] feat(transformer): add FormData struct for handling multipart form data --- crates/shared/src/server/transformer.rs | 111 +++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/crates/shared/src/server/transformer.rs b/crates/shared/src/server/transformer.rs index 6500d64..f317ccf 100644 --- a/crates/shared/src/server/transformer.rs +++ b/crates/shared/src/server/transformer.rs @@ -287,9 +287,9 @@ impl<'b> Body<'b> { /// data: r#"------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name="file"; filename="example.txt"\r\nContent-Type: text/plain\r\n\r\nHello World\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--\r\n"#.to_string().into_bytes(), /// }; /// - /// let stream = body.form_data(); + /// let stream = body.to_multipart(); /// ``` - pub fn form_data(self) -> Result, multer::Error> { + pub fn to_multipart(self) -> Result, multer::Error> { if let Some(content_type) = self.content_type { let boundary = multer::parse_boundary( content_type @@ -333,3 +333,110 @@ impl<'a: 'b, 'b> Transformer<'a> for Body<'b> { Body { data, content_type } } } + +/// Represents a form data struct for handling multipart form data. +pub struct FormData { + fields: std::collections::HashMap, +} + +impl FormData { + /// Creates a new FormData instance from a multipart form. + /// + /// ### Arguments + /// + /// * `multipart` - The multipart form data. + /// + /// ### Returns + /// + /// * `FormData` - A new FormData instance. + pub async fn from_multipart<'f>(mut multipart: Multipart<'f>) -> Result { + let mut fields = std::collections::HashMap::new(); + + // Process all fields in the multipart form + while let Some(field) = multipart.next_field().await? { + let name = field.name().unwrap_or(""); + let name = name.to_string(); + if let Ok(value) = field.text().await { + fields.insert(name, value); + } + } + + Ok(FormData { fields }) + } + + /// Creates a new empty FormData instance. + pub fn new() -> Self { + FormData { + fields: std::collections::HashMap::new(), + } + } + + /// Retrieves the value associated with the specified field name from the form data. + /// + /// ### Arguments + /// + /// * `name` - The field name to search for. + /// + /// ### Returns + /// + /// * `Option` - The parsed value associated with the field name, if found and parseable. + /// + /// ### Examples + /// + /// ```rust ignore + /// let form_data = FormData::new(); + /// + /// let name: Option = form_data.get("name"); + /// let age: Option = form_data.get("age"); + /// ``` + pub fn get(&self, name: &str) -> Option { + self.fields + .get(name) + .and_then(|value| value.parse::().ok()) + } + + /// Sets a field value in the form data. + /// + /// ### Arguments + /// + /// * `name` - The field name. + /// * `value` - The field value. + pub fn set(&mut self, name: &str, value: T) { + self.fields.insert(name.to_string(), value.to_string()); + } + + /// Checks if the form data contains a field with the specified name. + /// + /// ### Arguments + /// + /// * `name` - The field name to check for. + /// + /// ### Returns + /// + /// * `bool` - True if the field exists, false otherwise. + pub fn has(&self, name: &str) -> bool { + self.fields.contains_key(name) + } + + /// Returns all field names in the form data. + /// + /// ### Returns + /// + /// * `Vec` - A vector of all field names. + pub fn field_names(&self) -> Vec { + self.fields.keys().cloned().collect() + } + + /// Helper method for the derive macro to set a field value by name. + /// + /// This is used by the FormData derive macro to set field values. + pub fn get_field_mut(&mut self, name: &str) -> Option<&mut String> { + self.fields.get_mut(name) + } +} + +impl Default for FormData { + fn default() -> Self { + Self::new() + } +} From 58c23e2e7d20943d494c387d0f24f863f50e0733 Mon Sep 17 00:00:00 2001 From: elcharitas Date: Wed, 30 Apr 2025 19:21:49 +0100 Subject: [PATCH 2/3] chore: cleanupo --- crates/shared/src/server/transformer.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/shared/src/server/transformer.rs b/crates/shared/src/server/transformer.rs index f317ccf..82cea98 100644 --- a/crates/shared/src/server/transformer.rs +++ b/crates/shared/src/server/transformer.rs @@ -73,7 +73,7 @@ pub struct Param<'a> { data: Vec<(&'a str, &'a str)>, } -impl<'a> Param<'a> { +impl Param<'_> { /// Retrieves the value associated with the specified `id` from the parameter data. /// /// ### Arguments @@ -145,7 +145,7 @@ pub struct Query<'q> { uri: &'q http::uri::Uri, } -impl<'q> Query<'q> { +impl Query<'_> { /// Retrieves the value associated with the specified `id` from the query parameters. /// /// ### Arguments From 3b66fbc9a04aeb5c06515184a16df22f5ee9e89f Mon Sep 17 00:00:00 2001 From: elcharitas Date: Sat, 3 May 2025 15:47:17 +0100 Subject: [PATCH 3/3] refactor: simplify generics and remove redundant lifetime annotations --- crates/shared/src/lib.rs | 2 +- crates/shared/src/server/context.rs | 1 - crates/shared/src/server/transformer.rs | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/shared/src/lib.rs b/crates/shared/src/lib.rs index 6e2f4c9..0b125aa 100644 --- a/crates/shared/src/lib.rs +++ b/crates/shared/src/lib.rs @@ -125,7 +125,7 @@ pub(crate) trait Middleware: Send + Sync { ) -> Pin + Send + 'a>>; } -impl<'b, T: NgynMiddleware + Send + 'b> Middleware for T { +impl Middleware for T { fn run<'a>( &'a self, cx: &'a mut NgynContext<'_>, diff --git a/crates/shared/src/server/context.rs b/crates/shared/src/server/context.rs index 2771922..8c2e289 100644 --- a/crates/shared/src/server/context.rs +++ b/crates/shared/src/server/context.rs @@ -18,7 +18,6 @@ impl NgynContextValue { } /// Represents the state of an application in Ngyn - pub trait AppState: Any + Send + Sync + 'static { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; diff --git a/crates/shared/src/server/transformer.rs b/crates/shared/src/server/transformer.rs index 82cea98..aaaa7e0 100644 --- a/crates/shared/src/server/transformer.rs +++ b/crates/shared/src/server/transformer.rs @@ -349,7 +349,7 @@ impl FormData { /// ### Returns /// /// * `FormData` - A new FormData instance. - pub async fn from_multipart<'f>(mut multipart: Multipart<'f>) -> Result { + pub async fn from_multipart(mut multipart: Multipart<'_>) -> Result { let mut fields = std::collections::HashMap::new(); // Process all fields in the multipart form