nomilo/src/form.rs

250 lines
7.8 KiB
Rust

use std::collections::HashMap;
use axum::extract::{Request, FromRequest};
use axum::response::{Response, IntoResponse};
use axum::http::StatusCode;
use axum::Form;
impl<S> FromRequest<S> for Node
where
S: Send + Sync,
{
type Rejection = Response;
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let Form(data): Form<Vec<(String, String)>> = 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<String, Node>),
Sequence(Sequence)
}
impl Node {
pub fn from_key_value(data: Vec<(String, String)>) -> Result<Node, FormError> {
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<Key> {
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::<usize>() {
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<Node>),
ExplicitIndex(HashMap<usize, Node>),
}
impl Sequence {
pub fn to_vec(self) -> Vec<Node> {
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);
}
}