Files
moli/moli-encoding/src/query.rs
T
2026-08-11 00:10:12 +08:00

80 lines
2.6 KiB
Rust

use std::borrow::Cow;
use encoding_rs::{EncoderResult, Encoding};
use crate::{
form_output_encoding,
percent::{append_percent_encoded_byte, append_percent_encoded_ncr},
};
pub fn encode_url_query_for_legacy_web<'a>(
input: &'a str,
encoding: &'static Encoding,
) -> Cow<'a, str> {
let encoding = form_output_encoding(encoding);
if encoding == encoding_rs::UTF_8 {
return Cow::Borrowed(input);
}
let Some(query_start) = query_delimiter_before_fragment(input) else {
return Cow::Borrowed(input);
};
let query_value_start = query_start + 1;
let fragment_start = input[query_value_start..]
.find('#')
.map(|offset| query_value_start + offset)
.unwrap_or(input.len());
let query = &input[query_value_start..fragment_start];
if query.is_empty() {
return Cow::Borrowed(input);
}
let mut output = String::with_capacity(input.len());
output.push_str(&input[..query_value_start]);
append_url_query(&mut output, query, encoding);
output.push_str(&input[fragment_start..]);
Cow::Owned(output)
}
fn query_delimiter_before_fragment(input: &str) -> Option<usize> {
input
.find('?')
.filter(|query_start| input[..*query_start].find('#').is_none())
}
fn append_url_query(output: &mut String, query: &str, encoding: &'static Encoding) {
let mut encoder = encoding.new_encoder();
let mut input = query;
let mut encoded = Vec::new();
loop {
let last = true;
let capacity = encoder
.max_buffer_length_from_utf8_without_replacement(input.len())
.unwrap_or(input.len())
.max(1);
if encoded.capacity() < capacity {
encoded.reserve(capacity - encoded.capacity());
}
let (result, read) =
encoder.encode_from_utf8_to_vec_without_replacement(input, &mut encoded, last);
append_url_query_bytes(output, &encoded);
encoded.clear();
input = &input[read..];
match result {
EncoderResult::InputEmpty => break,
EncoderResult::OutputFull => encoded.reserve(encoded.capacity().max(1)),
EncoderResult::Unmappable(ch) => append_percent_encoded_ncr(output, ch),
}
}
}
fn append_url_query_bytes(output: &mut String, input: &[u8]) {
for byte in input {
match *byte {
0x00..=0x1F | 0x7F | b' ' | b'"' | b'#' | b'\'' | b'<' | b'>' | 0x80..=0xFF => {
append_percent_encoded_byte(output, *byte);
}
byte => output.push(byte as char),
}
}
}