1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
#![forbid(unsafe_code)]
#![deny(missing_docs)]
#![warn(clippy::cast_lossless)]
#![warn(clippy::cast_possible_wrap)]
#![warn(clippy::cast_possible_truncation)]
//! A port of [lz-string](https://github.com/pieroxy/lz-string) to Rust.
//!
//!
//! # Example
//! ```rust
//! # // The demonstrated functions correspond with `LZString.compress` and `LZString.decompress` from the JS version.
//! # fn main() {
//! let data = "The quick brown fox jumps over the lazy dog";
//!
//! // Compress the data. This cannot fail.
//! let compressed_data = lz_str::compress(data);
//!
//! // Decompress the data.
//! // This may return `Option::None` if it fails.
//! // Make sure to do error-checking in a real application to prevent crashes!
//! let decompressed_data =
//! lz_str::decompress(compressed_data).expect("`compressed_data` is invalid");
//!
//! // The decompressed_data should be the same as data, except encoded as UTF16.
//! // We undo that here.
//! // In a real application,
//! // you will want to do error checking to prevent users from causing crashes with invalid data.
//! let decompressed_data =
//! String::from_utf16(&decompressed_data).expect("`decompressed_data` is not valid UTF16");
//!
//! assert!(data == decompressed_data);
//! # }
//! ```
//!
//! # Passing and Recieving Data
//! The original library uses invalid UTF16 strings to represent data.
//! To maintain compatability, this library uses a [`Vec`] of [`u16`]s instead of Rust strings where applicable.
//! The [`IntoWideIter`] trait exists to ease the passing of data into functions.
//! Most functions accept this generic parameter instead of a concrete type.
//! Look at this trait's documentation to see what types this trait is implemented for.
mod compress;
mod constants;
mod decompress;
pub use crate::compress::compress;
pub use crate::compress::compress_internal;
pub use crate::compress::compress_to_base64;
pub use crate::compress::compress_to_encoded_uri_component;
pub use crate::compress::compress_to_uint8_array;
pub use crate::compress::compress_to_utf16;
pub use crate::decompress::decompress;
pub use crate::decompress::decompress_from_base64;
pub use crate::decompress::decompress_from_encoded_uri_component;
pub use crate::decompress::decompress_from_uint8_array;
pub use crate::decompress::decompress_from_utf16;
pub use crate::decompress::decompress_internal;
/// A trait to make it easier to pass arguments to functions.
pub trait IntoWideIter {
/// The Iterator type
type Iter: Iterator<Item = u16>;
/// Convert this object into something that yields possibly invalid wide characters.
fn into_wide_iter(self) -> Self::Iter;
}
impl<'a> IntoWideIter for &'a str {
type Iter = std::str::EncodeUtf16<'a>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.encode_utf16()
}
}
impl<'a> IntoWideIter for &&'a str {
type Iter = std::str::EncodeUtf16<'a>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.encode_utf16()
}
}
impl<'a> IntoWideIter for &'a String {
type Iter = std::str::EncodeUtf16<'a>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.as_str().encode_utf16()
}
}
impl<'a> IntoWideIter for &'a [u16] {
type Iter = std::iter::Copied<std::slice::Iter<'a, u16>>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.iter().copied()
}
}
// TODO: Remove this in the next version.
// We do not benefit from taking ownership of the buffer.
impl IntoWideIter for Vec<u16> {
type Iter = std::vec::IntoIter<u16>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.into_iter()
}
}
impl<'a> IntoWideIter for &'a Vec<u16> {
type Iter = std::iter::Copied<std::slice::Iter<'a, u16>>;
#[inline]
fn into_wide_iter(self) -> Self::Iter {
self.iter().copied()
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn into_wide_iter_check() {
const DATA: &str = "test argument";
let expected: Vec<u16> = DATA.encode_utf16().collect();
fn check(arg: impl IntoWideIter, expected: &[u16]) {
let arg: Vec<u16> = arg.into_wide_iter().collect();
assert!(arg == expected);
}
{
let data: &str = DATA;
check(data, &expected);
}
{
let data: &&str = &DATA;
check(data, &expected);
}
// TODO: Should IntoWideIter be implemented for String?
// It's always better to pass an &str or an &String, so users should be forced to do that?
// {
// let data: String = DATA.into();
// check(data, &expected);
// }
{
let data: String = DATA.into();
let data: &String = &data;
check(data, &expected);
}
{
let data: Vec<u16> = DATA.encode_utf16().collect();
let data: &[u16] = &data;
check(data, &expected);
}
{
let data: Vec<u16> = DATA.encode_utf16().collect();
check(data, &expected);
}
{
let data: Vec<u16> = DATA.encode_utf16().collect();
let data: &Vec<u16> = &data;
check(data, &expected);
}
}
}