robinpath_modules/modules/
cookie_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("cookie.parse", |args, _| {
6 let cookie_str = args.first().map(|v| v.to_display_string()).unwrap_or_default();
7 let mut obj = indexmap::IndexMap::new();
8 for pair in cookie_str.split(';') {
9 let pair = pair.trim();
10 if pair.is_empty() {
11 continue;
12 }
13 if let Some((key, value)) = pair.split_once('=') {
14 obj.insert(
15 key.trim().to_string(),
16 Value::String(value.trim().to_string()),
17 );
18 }
19 }
20 Ok(Value::Object(obj))
21 });
22
23 rp.register_builtin("cookie.stringify", |args, _| {
25 let obj = args.first().cloned().unwrap_or(Value::Null);
26 match obj {
27 Value::Object(map) => {
28 let parts: Vec<String> = map
29 .iter()
30 .map(|(k, v)| format!("{}={}", k, v.to_display_string()))
31 .collect();
32 Ok(Value::String(parts.join("; ")))
33 }
34 _ => Err("cookie.stringify expects an object".to_string()),
35 }
36 });
37
38 rp.register_builtin("cookie.get", |args, _| {
40 let cookie_str = args.first().map(|v| v.to_display_string()).unwrap_or_default();
41 let name = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
42 for pair in cookie_str.split(';') {
43 let pair = pair.trim();
44 if let Some((key, value)) = pair.split_once('=') {
45 if key.trim() == name {
46 return Ok(Value::String(value.trim().to_string()));
47 }
48 }
49 }
50 Ok(Value::Null)
51 });
52
53 rp.register_builtin("cookie.set", |args, _| {
55 let cookie_str = args.first().map(|v| v.to_display_string()).unwrap_or_default();
56 let name = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
57 let value = args.get(2).map(|v| v.to_display_string()).unwrap_or_default();
58 let opts = args.get(3).cloned().unwrap_or(Value::Null);
59
60 let mut pairs: Vec<(String, String)> = Vec::new();
62 let mut found = false;
63 for pair in cookie_str.split(';') {
64 let pair = pair.trim();
65 if pair.is_empty() {
66 continue;
67 }
68 if let Some((key, val)) = pair.split_once('=') {
69 let key = key.trim().to_string();
70 if key == name {
71 pairs.push((key, value.clone()));
72 found = true;
73 } else {
74 pairs.push((key, val.trim().to_string()));
75 }
76 }
77 }
78 if !found {
79 pairs.push((name.clone(), value.clone()));
80 }
81
82 let base: Vec<String> = pairs.iter().map(|(k, v)| format!("{}={}", k, v)).collect();
84 let mut result = base.join("; ");
85
86 if let Value::Object(obj) = &opts {
88 if let Some(path) = obj.get("path") {
89 result.push_str(&format!("; Path={}", path.to_display_string()));
90 }
91 if let Some(domain) = obj.get("domain") {
92 result.push_str(&format!("; Domain={}", domain.to_display_string()));
93 }
94 if let Some(max_age) = obj.get("maxAge") {
95 result.push_str(&format!("; Max-Age={}", max_age.to_number() as i64));
96 }
97 if let Some(secure) = obj.get("secure") {
98 if secure.is_truthy() {
99 result.push_str("; Secure");
100 }
101 }
102 if let Some(http_only) = obj.get("httpOnly") {
103 if http_only.is_truthy() {
104 result.push_str("; HttpOnly");
105 }
106 }
107 if let Some(same_site) = obj.get("sameSite") {
108 result.push_str(&format!("; SameSite={}", same_site.to_display_string()));
109 }
110 }
111
112 Ok(Value::String(result))
113 });
114
115 rp.register_builtin("cookie.delete", |args, _| {
117 let cookie_str = args.first().map(|v| v.to_display_string()).unwrap_or_default();
118 let name = args.get(1).map(|v| v.to_display_string()).unwrap_or_default();
119 let mut pairs: Vec<String> = Vec::new();
120 for pair in cookie_str.split(';') {
121 let pair = pair.trim();
122 if pair.is_empty() {
123 continue;
124 }
125 if let Some((key, val)) = pair.split_once('=') {
126 if key.trim() != name {
127 pairs.push(format!("{}={}", key.trim(), val.trim()));
128 }
129 }
130 }
131 Ok(Value::String(pairs.join("; ")))
132 });
133
134 rp.register_builtin("cookie.isExpired", |args, _| {
136 let cookie_str = args.first().map(|v| v.to_display_string()).unwrap_or_default();
137 let lower = cookie_str.to_lowercase();
138
139 for pair in lower.split(';') {
141 let pair = pair.trim();
142 if let Some((key, val)) = pair.split_once('=') {
143 let key = key.trim();
144 let val = val.trim();
145 if key == "max-age" {
146 if let Ok(age) = val.parse::<i64>() {
147 if age <= 0 {
148 return Ok(Value::Bool(true));
149 }
150 }
151 }
152 if key == "expires" {
153 if val.contains("1970") || val.contains("Thu, 01 Jan 1970") {
155 return Ok(Value::Bool(true));
156 }
157 }
158 }
159 }
160
161 Ok(Value::Bool(false))
162 });
163}