what_core/server/
actions.rs1use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum ActionType {
10 Create,
12 Update,
14 Delete,
16 Navigate,
18 Refresh,
20 Toggle,
22 Custom(String),
24}
25
26impl From<&str> for ActionType {
27 fn from(s: &str) -> Self {
28 match s.to_lowercase().as_str() {
29 "create" | "new" | "add" => Self::Create,
30 "update" | "edit" | "save" => Self::Update,
31 "delete" | "remove" | "destroy" => Self::Delete,
32 "navigate" | "goto" | "redirect" => Self::Navigate,
33 "refresh" | "reload" => Self::Refresh,
34 "toggle" => Self::Toggle,
35 other => Self::Custom(other.to_string()),
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct ParsedAction {
43 pub action_type: ActionType,
45 pub collection: Option<String>,
47 pub id: Option<String>,
49 pub redirect: Option<String>,
51 pub target: Option<String>,
53 pub swap: SwapMode,
55 pub confirm: Option<String>,
57}
58
59#[derive(Debug, Clone, Default)]
61pub enum SwapMode {
62 #[default]
64 InnerHtml,
65 OuterHtml,
67 BeforeBegin,
69 AfterEnd,
71 BeforeEnd,
73 AfterBegin,
75 None,
77}
78
79impl From<&str> for SwapMode {
80 fn from(s: &str) -> Self {
81 match s.to_lowercase().as_str() {
82 "innerhtml" | "inner" => Self::InnerHtml,
83 "outerhtml" | "outer" => Self::OuterHtml,
84 "beforebegin" | "before" => Self::BeforeBegin,
85 "afterend" | "after" => Self::AfterEnd,
86 "beforeend" | "append" => Self::BeforeEnd,
87 "afterbegin" | "prepend" => Self::AfterBegin,
88 "none" | "false" => Self::None,
89 _ => Self::InnerHtml,
90 }
91 }
92}
93
94pub struct ActionHandler;
96
97impl ActionHandler {
98 pub fn parse_attributes(attrs: &HashMap<String, String>) -> ParsedAction {
100 let action_str = attrs
101 .get("w-action")
102 .map(|s| s.as_str())
103 .unwrap_or("create");
104 let action_type = ActionType::from(action_str);
105
106 ParsedAction {
107 action_type,
108 collection: attrs
109 .get("w-store")
110 .cloned()
111 .or_else(|| attrs.get("w-collection").cloned()),
112 id: attrs.get("w-id").cloned(),
113 redirect: attrs.get("w-redirect").cloned(),
114 target: attrs.get("w-target").cloned(),
115 swap: attrs
116 .get("w-swap")
117 .map(|s| SwapMode::from(s.as_str()))
118 .unwrap_or_default(),
119 confirm: attrs.get("w-confirm").cloned(),
120 }
121 }
122
123 pub fn generate_action_url(action: &ParsedAction) -> String {
125 let mut url = String::from("/w-action");
126
127 if let Some(ref collection) = action.collection {
128 url.push('/');
129 url.push_str(collection);
130
131 if let Some(ref id) = action.id {
132 url.push('/');
133 url.push_str(id);
134 }
135 }
136
137 let mut params = Vec::new();
139
140 match action.action_type {
141 ActionType::Create => params.push("w-action=create".to_string()),
142 ActionType::Update => params.push("w-action=update".to_string()),
143 ActionType::Delete => params.push("w-action=delete".to_string()),
144 ActionType::Custom(ref name) => params.push(format!("w-action={}", name)),
145 _ => {}
146 }
147
148 if let Some(ref redirect) = action.redirect {
149 params.push(format!("w-redirect={}", urlencoding::encode(redirect)));
150 }
151
152 if !params.is_empty() {
153 url.push('?');
154 url.push_str(¶ms.join("&"));
155 }
156
157 url
158 }
159}
160
161#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn test_parse_action_type() {
169 assert_eq!(ActionType::from("create"), ActionType::Create);
170 assert_eq!(ActionType::from("update"), ActionType::Update);
171 assert_eq!(ActionType::from("delete"), ActionType::Delete);
172 assert_eq!(
173 ActionType::from("custom_action"),
174 ActionType::Custom("custom_action".to_string())
175 );
176 }
177
178 #[test]
179 fn test_parse_attributes() {
180 let mut attrs = HashMap::new();
181 attrs.insert("w-action".to_string(), "create".to_string());
182 attrs.insert("w-store".to_string(), "posts".to_string());
183 attrs.insert("w-redirect".to_string(), "/posts".to_string());
184
185 let action = ActionHandler::parse_attributes(&attrs);
186
187 assert_eq!(action.action_type, ActionType::Create);
188 assert_eq!(action.collection, Some("posts".to_string()));
189 assert_eq!(action.redirect, Some("/posts".to_string()));
190 }
191
192 #[test]
193 fn test_generate_action_url() {
194 let action = ParsedAction {
195 action_type: ActionType::Create,
196 collection: Some("posts".to_string()),
197 id: None,
198 redirect: Some("/posts".to_string()),
199 target: None,
200 swap: SwapMode::InnerHtml,
201 confirm: None,
202 };
203
204 let url = ActionHandler::generate_action_url(&action);
205 assert!(url.starts_with("/w-action/posts"));
206 assert!(url.contains("w-action=create"));
207 }
208}