diff options
Diffstat (limited to 'third_party/rust/urlencoding')
-rw-r--r-- | third_party/rust/urlencoding/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | third_party/rust/urlencoding/Cargo.toml | 10 | ||||
-rw-r--r-- | third_party/rust/urlencoding/LICENSE | 19 | ||||
-rw-r--r-- | third_party/rust/urlencoding/README.md | 50 | ||||
-rw-r--r-- | third_party/rust/urlencoding/src/lib.rs | 170 |
5 files changed, 250 insertions, 0 deletions
diff --git a/third_party/rust/urlencoding/.cargo-checksum.json b/third_party/rust/urlencoding/.cargo-checksum.json new file mode 100644 index 0000000000..e5c1ef9e42 --- /dev/null +++ b/third_party/rust/urlencoding/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"81153f958c5a2f4b4aabf0b69cff507c0755f40af1c17860583e0528c48bebe2","LICENSE":"5fe71cd9f72a6009711249fff7cb5d4d075cf9288a5eb52e125d5f8ad9ed8ce7","README.md":"f96070e07fc534f4b591919399304d7464f20892ec2ba0704c8e4a714595d561","src/lib.rs":"08de888c4e799701b755cb865d6da8faf3a1268755264170ac8ac7b4480682a5"},"package":"3df3561629a8bb4c57e5a2e4c43348d9e29c7c29d9b1c4c1f47166deca8f37ed"}
\ No newline at end of file diff --git a/third_party/rust/urlencoding/Cargo.toml b/third_party/rust/urlencoding/Cargo.toml new file mode 100644 index 0000000000..a1ca1995fc --- /dev/null +++ b/third_party/rust/urlencoding/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "urlencoding" +version = "1.0.0" +authors = ["Bertram Truong <b@bertramtruong.com>"] +license = "MIT" +description = "A Rust library for doing URL percentage encoding." +repository = "https://github.com/bt/rust_urlencoding" +keywords = ["url", "encoding", "urlencoding"] + +[dependencies] diff --git a/third_party/rust/urlencoding/LICENSE b/third_party/rust/urlencoding/LICENSE new file mode 100644 index 0000000000..fb9900014e --- /dev/null +++ b/third_party/rust/urlencoding/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2016 Bertram Truong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/third_party/rust/urlencoding/README.md b/third_party/rust/urlencoding/README.md new file mode 100644 index 0000000000..5088cf1298 --- /dev/null +++ b/third_party/rust/urlencoding/README.md @@ -0,0 +1,50 @@ +# urlencoding + +[![Latest Version](https://img.shields.io/crates/v/urlencoding.svg)](https://crates.io/crates/urlencoding) + +A Rust library for doing URL percentage encoding. + +Installation +============ + +This crate can be downloaded through Cargo. To do so, add the following line to your `Cargo.toml` file, under `dependencies`: + +```toml +urlencoding = "1.0.0" +``` + +Usage +===== + +To encode a string, do the following: + +```rust +extern crate urlencoding; + +use urlencoding::encode; + +fn main() { + let encoded = encode("This string will be URL encoded."); + println!("{}", encoded); + // This%20string%20will%20be%20URL%20encoded. +} +``` + +To decode a string, it's only slightly different: + +```rust +extern crate urlencoding; + +use urlencoding::decode; + +fn main() { + let decoded = decode("%F0%9F%91%BE%20Exterminate%21"); + println!("{}", decoded.unwrap()); + // 👾 Exterminate! +} +``` + +License +======= + +This project is licensed under the MIT license, Copyright (c) 2017 Bertram Truong. For more information see the `LICENSE` file. diff --git a/third_party/rust/urlencoding/src/lib.rs b/third_party/rust/urlencoding/src/lib.rs new file mode 100644 index 0000000000..6808a395b1 --- /dev/null +++ b/third_party/rust/urlencoding/src/lib.rs @@ -0,0 +1,170 @@ +use std::str; +use std::string::FromUtf8Error; + +pub fn encode(data: &str) -> String { + let mut escaped = String::new(); + for b in data.as_bytes().iter() { + match *b as char { + // Accepted characters + 'A'...'Z' | 'a'...'z' | '0'...'9' | '-' | '_' | '.' | '~' => escaped.push(*b as char), + + // Everything else is percent-encoded + b => escaped.push_str(format!("%{:02X}", b as u32).as_str()), + }; + } + return escaped; +} + +pub fn decode(data: &str) -> Result<String, FromUrlEncodingError> { + validate_urlencoded_str(data)?; + let mut unescaped_bytes: Vec<u8> = Vec::new(); + let mut bytes = data.bytes(); + // If validate_urlencoded_str returned Ok, then we know + // every '%' is followed by 2 hex characters + while let Some(b) = bytes.next() { + match b as char { + '%' => { + let bytes_to_decode = &[bytes.next().unwrap(), bytes.next().unwrap()]; + let hex_str = str::from_utf8(bytes_to_decode).unwrap(); + unescaped_bytes.push(u8::from_str_radix(hex_str, 16).unwrap()); + }, + _ => { + // Assume whoever did the encoding intended what we got + unescaped_bytes.push(b); + } + } + } + String::from_utf8(unescaped_bytes).or_else(|e| Err(FromUrlEncodingError::Utf8CharacterError { + error: e, + })) +} + +// Validates every '%' character is followed by exactly 2 hex +// digits. +fn validate_urlencoded_str(data: &str) -> Result<(), FromUrlEncodingError> { + let mut iter = data.char_indices(); + while let Some((idx, chr)) = iter.next() { + match chr { + '%' => { + validate_percent_encoding(&mut iter, idx)?; + }, + _ => continue, + } + } + Ok(()) +} + +// Validates the next two characters returned by the provided iterator are +// hexadecimal digits. +fn validate_percent_encoding(iter: &mut str::CharIndices, current_idx: usize) -> Result<(), FromUrlEncodingError> { + for _ in 0..2 { + match iter.next() { + // Only hex digits are valid + Some((_, c)) if c.is_digit(16) => { + continue + }, + Some((i, c)) => return Err(FromUrlEncodingError::UriCharacterError { + character: c, + index: i, + }), + // We got a '%' without 2 characters after it, so mark the '%' as bad + None => return Err(FromUrlEncodingError::UriCharacterError { + character: '%', + index: current_idx, + }), + } + } + Ok(()) +} + +#[derive(Debug)] +pub enum FromUrlEncodingError { + UriCharacterError { character: char, index: usize }, + Utf8CharacterError { error: FromUtf8Error }, +} + +#[cfg(test)] +mod tests { + use super::encode; + use super::decode; + use super::FromUrlEncodingError; + + #[test] + fn it_encodes_successfully() { + let expected = "this%20that"; + assert_eq!(expected, encode("this that")); + } + + #[test] + fn it_encodes_successfully_emoji() { + let emoji_string = "👾 Exterminate!"; + let expected = "%F0%9F%91%BE%20Exterminate%21"; + assert_eq!(expected, encode(emoji_string)); + } + + #[test] + fn it_decodes_successfully() { + let expected = String::from("this that"); + let encoded = "this%20that"; + assert_eq!(expected, decode(encoded).unwrap()); + } + + #[test] + fn it_decodes_successfully_emoji() { + let expected = String::from("👾 Exterminate!"); + let encoded = "%F0%9F%91%BE%20Exterminate%21"; + assert_eq!(expected, decode(encoded).unwrap()); + } + + #[test] + fn it_decodes_unsuccessfully_emoji() { + let bad_encoded_string = "👾 Exterminate!"; + + assert_eq!(bad_encoded_string, decode(bad_encoded_string).unwrap()); + } + + #[test] + fn it_decodes_unsuccessfuly_bad_percent_01() { + let bad_encoded_string = "this%2that"; + let expected_idx = 6; + let expected_char = 't'; + + match decode(bad_encoded_string).unwrap_err() { + FromUrlEncodingError::UriCharacterError { index: i, character: c } => { + assert_eq!(expected_idx, i); + assert_eq!(expected_char, c) + }, + _ => panic!() + } + } + + #[test] + fn it_decodes_unsuccessfuly_bad_percent_02() { + let bad_encoded_string = "this%20that%"; + let expected_idx = 11; + let expected_char = '%'; + + match decode(bad_encoded_string).unwrap_err() { + FromUrlEncodingError::UriCharacterError { index: i, character: c } => { + assert_eq!(expected_idx, i); + assert_eq!(expected_char, c) + }, + _ => panic!() + } + } + + #[test] + fn it_decodes_unsuccessfuly_bad_percent_03() { + let bad_encoded_string = "this%20that%2"; + let expected_idx = 11; + let expected_char = '%'; + + match decode(bad_encoded_string).unwrap_err() { + FromUrlEncodingError::UriCharacterError { index: i, character: c } => { + assert_eq!(expected_idx, i); + assert_eq!(expected_char, c) + }, + _ => panic!() + } + } +} |