use std::collections::HashMap; use axum::extract::{Request, FromRequest}; use axum::response::{Response, IntoResponse}; use axum::http::StatusCode; use axum::Form; impl FromRequest for Node where S: Send + Sync, { type Rejection = Response; async fn from_request(req: Request, state: &S) -> Result { let Form(data): Form> = Form::from_request(req, state) .await .map_err(IntoResponse::into_response)?; let node = Node::from_key_value(data) .map_err(|_| StatusCode::UNPROCESSABLE_ENTITY.into_response())?; Ok(node) } } #[derive(Debug)] pub enum FormError { MismatchedType } #[derive(Debug, PartialEq, Eq)] pub enum Node { Value(String), Map(HashMap), Sequence(Sequence) } impl Node { pub fn from_key_value(data: Vec<(String, String)>) -> Result { let mut form = Node::Map(HashMap::new()); for (key, value) in data { // Consider empty value not filled and remove them if value.is_empty() { continue; } let path = Self::parse_key(&key); let mut parent_node = &mut form; for window in path.windows(2) { let parent_key = &window[0]; let child_key = &window[1]; parent_node = match (parent_node, parent_key) { (&mut Node::Map(ref mut map), Key::Attribute(key)) => { map.entry(key.clone()).or_insert_with(|| child_key.new_node()) }, (&mut Node::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => { list.push(child_key.new_node()); list.last_mut().unwrap() }, (&mut Node::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => { list.entry(*index).or_insert_with(|| child_key.new_node()) }, _ => { return Err(FormError::MismatchedType); } }; } let last_key = path.last().unwrap(); match (parent_node, last_key) { (&mut Node::Map(ref mut map), Key::Attribute(key)) => { map.insert(key.clone(), Node::Value(value)); }, (&mut Node::Sequence(Sequence::ImplicitIndex(ref mut list)), Key::ImplicitIndex) => { list.push(Node::Value(value)) }, (&mut Node::Sequence(Sequence::ExplicitIndex(ref mut list)), Key::ExplicitIndex(index)) => { list.insert(*index, Node::Value(value)); }, _ => { return Err(FormError::MismatchedType); } } } Ok(form) } pub fn parse_key(key: &str) -> Vec { let keys = if let Some((head, tail)) = key.split_once('[') { let mut keys = vec![head]; keys.extend(tail.trim_end_matches(']').split("][")); keys } else { vec![key] }; keys.iter().map(|key| { if key.is_empty() { Key::ImplicitIndex } else if let Ok(index) = key.parse::() { Key::ExplicitIndex(index) } else { Key::Attribute(key.to_string()) } }).collect() } pub fn into_json_value(self) -> serde_json::Value { match self { Node::Value(value) => serde_json::Value::String(value), Node::Map(map) => { let map = map.into_iter() .map(|(key, node)| (key, node.into_json_value())) .collect(); serde_json::Value::Object(map) }, Node::Sequence(list) => { let array = list.to_vec() .into_iter() .map(|node| node.into_json_value()) .collect(); serde_json::Value::Array(array) } } } } #[derive(Debug, PartialEq, Eq)] pub enum Sequence { ImplicitIndex(Vec), ExplicitIndex(HashMap), } impl Sequence { pub fn to_vec(self) -> Vec { match self { Sequence::ImplicitIndex(list) => list, Sequence::ExplicitIndex(map) => { let mut key_values: Vec<(usize, Node)> = map.into_iter().collect(); key_values.sort_by_key(|(k, _)| *k); key_values.into_iter().map(|(_, v)| v).collect() } } } } #[derive(Debug, PartialEq, Eq)] pub enum Key { ExplicitIndex(usize), ImplicitIndex, Attribute(String), } impl Key { pub fn new_node(&self) -> Node { match self { Key::ExplicitIndex(_) => Node::Sequence(Sequence::ExplicitIndex(HashMap::new())), Key::ImplicitIndex => Node::Sequence(Sequence::ImplicitIndex(Vec::new())), Key::Attribute(_) => Node::Map(HashMap::new()), } } } #[cfg(test)] mod tests { use super::*; use serde_json::json; #[test] pub fn test_parse_key() { let key = "records[0][addresses][][address]".to_string(); let parsed_key = vec![ Key::Attribute("records".to_string()), Key::ExplicitIndex(0), Key::Attribute("addresses".to_string()), Key::ImplicitIndex, Key::Attribute("address".to_string()), ]; assert_eq!(Node::parse_key(&key), parsed_key); } #[test] pub fn test_parse_key_value() { let form_data = vec![ ("records[0][addresses][][address]".to_string(), "123".to_string()), ("records[0][addresses][][address]".to_string(), "abc".to_string()), ]; let mut address1 = HashMap::new(); address1.insert("address".to_string(), Node::Value("123".to_string())); let mut address2 = HashMap::new(); address2.insert("address".to_string(), Node::Value("abc".to_string())); let addresses = vec![Node::Map(address1), Node::Map(address2)]; let mut record = HashMap::new(); record.insert("addresses".to_string(), Node::Sequence(Sequence::ImplicitIndex(addresses))); let mut record_list = HashMap::new(); record_list.insert(0, Node::Map(record)); let mut form = HashMap::new(); form.insert("records".to_string(), Node::Sequence(Sequence::ExplicitIndex(record_list))); let parsed_form = Node::Map(form); assert_eq!(Node::from_key_value(form_data).unwrap(), parsed_form); } #[test] pub fn test_json_value() { let mut address1 = HashMap::new(); address1.insert("address".to_string(), Node::Value("123".to_string())); let mut address2 = HashMap::new(); address2.insert("address".to_string(), Node::Value("abc".to_string())); let addresses = vec![Node::Map(address1), Node::Map(address2)]; let mut record = HashMap::new(); record.insert("addresses".to_string(), Node::Sequence(Sequence::ImplicitIndex(addresses))); let mut record_list = HashMap::new(); record_list.insert(0, Node::Map(record)); let mut form = HashMap::new(); form.insert("records".to_string(), Node::Sequence(Sequence::ExplicitIndex(record_list))); let parsed_form = Node::Map(form); let json_value = json!({ "records": [ { "addresses": [ { "address": "123" }, { "address": "abc" }, ] } ] }); assert_eq!(parsed_form.into_json_value(), json_value); } }