servo-url 0.1.0-rc2

A component of the servo web-engine.
Documentation
/* 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)
}