Skip to main content

robinpath_modules/modules/
encode_mod.rs

1use base64::{engine::general_purpose, Engine as _};
2use data_encoding::{BASE32, HEXLOWER};
3use robinpath::{RobinPath, Value};
4
5pub fn register(rp: &mut RobinPath) {
6    // Base64
7    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    // Hex
21    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    // URL encode/decode
35    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    // HTML encode/decode
46    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    // Base32
57    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    // Binary
71    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    // ROT13
90    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    // ASCII conversions
104    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('&', "&amp;")
163        .replace('<', "&lt;")
164        .replace('>', "&gt;")
165        .replace('"', "&quot;")
166        .replace('\'', "&#39;")
167}
168
169fn html_decode(s: &str) -> String {
170    s.replace("&amp;", "&")
171        .replace("&lt;", "<")
172        .replace("&gt;", ">")
173        .replace("&quot;", "\"")
174        .replace("&#39;", "'")
175}