/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::Cow; use encoding_rs::{EncoderResult, Encoding, UTF_8}; /// This is equivalent to [Encoding::encode], except nonmappable code points are handled /// according to the url specification, which expects nonmappable code points to be wrapped in `%26%23` and /// `%3B` (see [percent encode after encoding](https://url.spec.whatwg.org/#string-percent-encode-after-encoding)). pub fn encode_as_url_query_string<'a>( mut string: &'a str, encoding: &'static Encoding, ) -> Cow<'a, [u8]> { let output_encoding = encoding.output_encoding(); if output_encoding == UTF_8 { return Cow::Borrowed(string.as_bytes()); } let bytes = string.as_bytes(); let valid_up_to = if output_encoding == encoding_rs::ISO_2022_JP { Encoding::iso_2022_jp_ascii_valid_up_to(bytes) } else { Encoding::ascii_valid_up_to(bytes) }; if valid_up_to == bytes.len() { // All the bytes are already correctly encoded - we don't need to do anything! return Cow::Borrowed(bytes); } let mut encoder = encoding.new_encoder(); let mut output = Vec::with_capacity( encoder .max_buffer_length_from_utf8_if_no_unmappables(string.len()) .expect("string size would overflow `usize`"), ); loop { match encoder.encode_from_utf8_to_vec_without_replacement(string, &mut output, true) { (EncoderResult::InputEmpty, _) => break, (EncoderResult::OutputFull, consumed) => { output.reserve( encoder .max_buffer_length_from_utf8_if_no_unmappables(string.len()) .expect("string size would overflow `usize`"), ); string = &string[consumed..]; }, (EncoderResult::Unmappable(character), consumed) => { use std::io::Write; write!(&mut output, "%26%23{}%3B", character as u32).unwrap(); string = &string[consumed..]; }, }; } Cow::Owned(output) }