aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-11-04 01:32:15 +0100
committersalmonik <salmonik@tuta.io>2026-11-04 01:32:15 +0100
commit2872b5cd79f1cd6555a6b53690ee38e5d847528b (patch)
treeaf2f31fe99dd06944fb6ca3cc63444a37a7923a0
parentautocomplete: key results by extension filter and reject the placeholder (diff)
downloadsenator-sperma-2872b5cd79f1cd6555a6b53690ee38e5d847528b.tar.gz
senator-sperma-2872b5cd79f1cd6555a6b53690ee38e5d847528b.zip
web: percent-encode UTF-8 query bytes
-rw-r--r--src/commands/web.rs29
1 files changed, 22 insertions, 7 deletions
diff --git a/src/commands/web.rs b/src/commands/web.rs
index f8b8667..fc44d7b 100644
--- a/src/commands/web.rs
+++ b/src/commands/web.rs
@@ -111,15 +111,30 @@ fn url_decode(s: &str) -> String {
111} 111}
112 112
113fn urlencoding(s: &str) -> String { 113fn urlencoding(s: &str) -> String {
114 s.chars() 114 let mut out = String::with_capacity(s.len());
115 .map(|c| match c { 115 for byte in s.as_bytes() {
116 'A'..='Z' | 'a'..='z' | '0'..='9' | '_' | '-' | '.' | '~' => c.to_string(), 116 match byte {
117 ' ' => "+".to_string(), 117 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'_' | b'-' | b'.' | b'~' => {
118 _ => format!("%{:02X}", c as u8), 118 out.push(*byte as char)
119 }) 119 }
120 .collect() 120 b' ' => out.push('+'),
121 _ => out.push_str(&format!("%{byte:02X}")),
122 }
123 }
124 out
121} 125}
122 126
123pub fn commands() -> Vec<poise::Command<Data, Error>> { 127pub fn commands() -> Vec<poise::Command<Data, Error>> {
124 vec![web()] 128 vec![web()]
125} 129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn encodes_utf8_bytes_not_chars() {
137 assert_eq!(urlencoding("ż"), "%C5%BC");
138 assert_eq!(urlencoding("a b"), "a+b");
139 }
140}