robinpath_modules/modules/
encode_mod.rs1use base64::{engine::general_purpose, Engine as _};
2use data_encoding::{BASE32, HEXLOWER};
3use robinpath::{RobinPath, Value};
4
5pub fn register(rp: &mut RobinPath) {
6 rp.register_builtin("encode.base64Encode", |args, _| {
8 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
9 Ok(Value::String(general_purpose::STANDARD.encode(s.as_bytes())))
10 });
11
12 rp.register_builtin("encode.base64Decode", |args, _| {
13 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
14 match general_purpose::STANDARD.decode(s.as_bytes()) {
15 Ok(bytes) => Ok(Value::String(String::from_utf8_lossy(&bytes).to_string())),
16 Err(e) => Err(format!("base64 decode error: {}", e)),
17 }
18 });
19
20 rp.register_builtin("encode.hexEncode", |args, _| {
22 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
23 Ok(Value::String(HEXLOWER.encode(s.as_bytes())))
24 });
25
26 rp.register_builtin("encode.hexDecode", |args, _| {
27 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
28 match HEXLOWER.decode(s.as_bytes()) {
29 Ok(bytes) => Ok(Value::String(String::from_utf8_lossy(&bytes).to_string())),
30 Err(e) => Err(format!("hex decode error: {}", e)),
31 }
32 });
33
34 rp.register_builtin("encode.urlEncode", |args, _| {
36 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
37 Ok(Value::String(url_encode(&s)))
38 });
39
40 rp.register_builtin("encode.urlDecode", |args, _| {
41 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
42 Ok(Value::String(url_decode(&s)))
43 });
44
45 rp.register_builtin("encode.htmlEncode", |args, _| {
47 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
48 Ok(Value::String(html_encode(&s)))
49 });
50
51 rp.register_builtin("encode.htmlDecode", |args, _| {
52 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
53 Ok(Value::String(html_decode(&s)))
54 });
55
56 rp.register_builtin("encode.base32Encode", |args, _| {
58 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
59 Ok(Value::String(BASE32.encode(s.as_bytes())))
60 });
61
62 rp.register_builtin("encode.base32Decode", |args, _| {
63 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
64 match BASE32.decode(s.as_bytes()) {
65 Ok(bytes) => Ok(Value::String(String::from_utf8_lossy(&bytes).to_string())),
66 Err(e) => Err(format!("base32 decode error: {}", e)),
67 }
68 });
69
70 rp.register_builtin("encode.binaryEncode", |args, _| {
72 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
73 let binary: Vec<String> = s.bytes().map(|b| format!("{:08b}", b)).collect();
74 Ok(Value::String(binary.join(" ")))
75 });
76
77 rp.register_builtin("encode.binaryDecode", |args, _| {
78 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
79 let bytes: Result<Vec<u8>, _> = s
80 .split_whitespace()
81 .map(|b| u8::from_str_radix(b, 2))
82 .collect();
83 match bytes {
84 Ok(b) => Ok(Value::String(String::from_utf8_lossy(&b).to_string())),
85 Err(e) => Err(format!("binary decode error: {}", e)),
86 }
87 });
88
89 rp.register_builtin("encode.rot13", |args, _| {
91 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
92 let result: String = s
93 .chars()
94 .map(|c| match c {
95 'a'..='m' | 'A'..='M' => (c as u8 + 13) as char,
96 'n'..='z' | 'N'..='Z' => (c as u8 - 13) as char,
97 _ => c,
98 })
99 .collect();
100 Ok(Value::String(result))
101 });
102
103 rp.register_builtin("encode.asciiToChar", |args, _| {
105 let n = args.first().map(|v| v.to_number() as u32).unwrap_or(0);
106 match char::from_u32(n) {
107 Some(c) => Ok(Value::String(c.to_string())),
108 None => Ok(Value::Null),
109 }
110 });
111
112 rp.register_builtin("encode.charToAscii", |args, _| {
113 let s = args.first().map(|v| v.to_display_string()).unwrap_or_default();
114 match s.chars().next() {
115 Some(c) => Ok(Value::Number(c as u32 as f64)),
116 None => Ok(Value::Null),
117 }
118 });
119}
120
121fn url_encode(s: &str) -> String {
122 let mut result = String::new();
123 for byte in s.bytes() {
124 match byte {
125 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'_' | b'.' | b'~' => {
126 result.push(byte as char);
127 }
128 _ => {
129 result.push_str(&format!("%{:02X}", byte));
130 }
131 }
132 }
133 result
134}
135
136fn url_decode(s: &str) -> String {
137 let mut result = Vec::new();
138 let bytes = s.as_bytes();
139 let mut i = 0;
140 while i < bytes.len() {
141 if bytes[i] == b'%' && i + 2 < bytes.len() {
142 if let Ok(byte) = u8::from_str_radix(
143 &String::from_utf8_lossy(&bytes[i + 1..i + 3]),
144 16,
145 ) {
146 result.push(byte);
147 i += 3;
148 continue;
149 }
150 }
151 if bytes[i] == b'+' {
152 result.push(b' ');
153 } else {
154 result.push(bytes[i]);
155 }
156 i += 1;
157 }
158 String::from_utf8_lossy(&result).to_string()
159}
160
161fn html_encode(s: &str) -> String {
162 s.replace('&', "&")
163 .replace('<', "<")
164 .replace('>', ">")
165 .replace('"', """)
166 .replace('\'', "'")
167}
168
169fn html_decode(s: &str) -> String {
170 s.replace("&", "&")
171 .replace("<", "<")
172 .replace(">", ">")
173 .replace(""", "\"")
174 .replace("'", "'")
175}