1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5pub struct FormData {
6 fields: HashMap<String, FormValue>,
7}
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(untagged)]
11pub enum FormValue {
12 Text(String),
13 File(FileData),
14 Multiple(Vec<String>),
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct FileData {
19 pub name: String,
20 pub size: u64,
21 pub content_type: String,
22 pub data: Vec<u8>,
23}
24
25impl FormData {
26 pub fn new() -> Self {
27 Self::default()
28 }
29
30 pub fn set(&mut self, key: impl Into<String>, value: impl Into<String>) {
31 self.fields
32 .insert(key.into(), FormValue::Text(value.into()));
33 }
34
35 pub fn set_multiple(&mut self, key: impl Into<String>, values: Vec<String>) {
36 self.fields.insert(key.into(), FormValue::Multiple(values));
37 }
38
39 pub fn set_file(&mut self, key: impl Into<String>, file: FileData) {
40 self.fields.insert(key.into(), FormValue::File(file));
41 }
42
43 pub fn get(&self, key: &str) -> Option<&str> {
44 match self.fields.get(key) {
45 Some(FormValue::Text(s)) => Some(s),
46 _ => None,
47 }
48 }
49
50 pub fn get_all(&self, key: &str) -> Vec<&str> {
51 match self.fields.get(key) {
52 Some(FormValue::Text(s)) => vec![s],
53 Some(FormValue::Multiple(v)) => v.iter().map(|s| s.as_str()).collect(),
54 _ => vec![],
55 }
56 }
57
58 pub fn get_file(&self, key: &str) -> Option<&FileData> {
59 match self.fields.get(key) {
60 Some(FormValue::File(f)) => Some(f),
61 _ => None,
62 }
63 }
64
65 pub fn keys(&self) -> impl Iterator<Item = &String> {
66 self.fields.keys()
67 }
68
69 pub fn to_json(&self) -> serde_json::Value {
70 let mut map = serde_json::Map::new();
71 for (key, value) in &self.fields {
72 let json_value = match value {
73 FormValue::Text(s) => serde_json::Value::String(s.clone()),
74 FormValue::Multiple(v) => serde_json::Value::Array(
75 v.iter()
76 .map(|s| serde_json::Value::String(s.clone()))
77 .collect(),
78 ),
79 FormValue::File(f) => serde_json::json!({
80 "name": f.name,
81 "size": f.size,
82 "contentType": f.content_type,
83 }),
84 };
85 map.insert(key.clone(), json_value);
86 }
87 serde_json::Value::Object(map)
88 }
89}
90
91#[derive(Debug, Clone)]
92pub struct FormAction {
93 action_id: String,
94 method: FormMethod,
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq)]
98pub enum FormMethod {
99 Post,
100 Get,
101}
102
103impl FormAction {
104 pub fn new(action_id: impl Into<String>) -> Self {
105 Self {
106 action_id: action_id.into(),
107 method: FormMethod::Post,
108 }
109 }
110
111 pub fn with_method(mut self, method: FormMethod) -> Self {
112 self.method = method;
113 self
114 }
115
116 pub fn action_id(&self) -> &str {
117 &self.action_id
118 }
119
120 pub fn method(&self) -> FormMethod {
121 self.method
122 }
123
124 pub fn action_url(&self) -> String {
125 format!("/_actions/{}", self.action_id)
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_form_data_text() {
135 let mut form = FormData::new();
136 form.set("name", "John");
137 form.set("email", "john@example.com");
138
139 assert_eq!(form.get("name"), Some("John"));
140 assert_eq!(form.get("email"), Some("john@example.com"));
141 assert_eq!(form.get("missing"), None);
142 }
143
144 #[test]
145 fn test_form_data_multiple() {
146 let mut form = FormData::new();
147 form.set_multiple("tags", vec!["rust".to_string(), "web".to_string()]);
148
149 let tags = form.get_all("tags");
150 assert_eq!(tags.len(), 2);
151 assert!(tags.contains(&"rust"));
152 assert!(tags.contains(&"web"));
153 }
154
155 #[test]
156 fn test_form_data_file() {
157 let mut form = FormData::new();
158 form.set_file(
159 "avatar",
160 FileData {
161 name: "photo.jpg".to_string(),
162 size: 1024,
163 content_type: "image/jpeg".to_string(),
164 data: vec![1, 2, 3],
165 },
166 );
167
168 let file = form.get_file("avatar").unwrap();
169 assert_eq!(file.name, "photo.jpg");
170 assert_eq!(file.size, 1024);
171 }
172
173 #[test]
174 fn test_form_action() {
175 let action = FormAction::new("create-post");
176 assert_eq!(action.action_id(), "create-post");
177 assert_eq!(action.method(), FormMethod::Post);
178 assert_eq!(action.action_url(), "/_actions/create-post");
179 }
180
181 #[test]
182 fn test_form_data_to_json() {
183 let mut form = FormData::new();
184 form.set("name", "Test");
185
186 let json = form.to_json();
187 assert_eq!(json["name"], "Test");
188 }
189}