summaryrefslogtreecommitdiffstats
path: root/vendor/minifier/src/json
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/minifier/src/json')
-rw-r--r--vendor/minifier/src/json/json_minifier.rs50
-rw-r--r--vendor/minifier/src/json/mod.rs114
-rw-r--r--vendor/minifier/src/json/read/byte_to_char.rs132
-rw-r--r--vendor/minifier/src/json/read/internal_buffer.rs44
-rw-r--r--vendor/minifier/src/json/read/internal_reader.rs63
-rw-r--r--vendor/minifier/src/json/read/json_read.rs106
-rw-r--r--vendor/minifier/src/json/string.rs100
7 files changed, 609 insertions, 0 deletions
diff --git a/vendor/minifier/src/json/json_minifier.rs b/vendor/minifier/src/json/json_minifier.rs
new file mode 100644
index 000000000..ad9fae2ce
--- /dev/null
+++ b/vendor/minifier/src/json/json_minifier.rs
@@ -0,0 +1,50 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+#[derive(Debug, Clone)]
+pub struct JsonMinifier {
+ pub is_string: bool,
+ pub escaped_quotation: u8,
+}
+
+impl Default for JsonMinifier {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl JsonMinifier {
+ pub fn new() -> Self {
+ JsonMinifier {
+ is_string: false,
+ escaped_quotation: 0,
+ }
+ }
+}
+
+#[inline]
+pub fn keep_element(minifier: &mut JsonMinifier, item1: &char, item2: Option<&char>) -> bool {
+ let remove_element =
+ item1.is_ascii_control() || is_whitespace_outside_string(minifier, item1, item2);
+ !remove_element
+}
+
+#[inline]
+fn is_whitespace_outside_string(
+ minifier: &mut JsonMinifier,
+ item1: &char,
+ item2: Option<&char>,
+) -> bool {
+ if !minifier.is_string && item1.eq(&'"') {
+ minifier.is_string = true;
+ } else if minifier.is_string {
+ if item1.eq(&'\\') && item2.eq(&Some(&'"')) {
+ minifier.escaped_quotation = 4;
+ }
+ if minifier.escaped_quotation > 0 {
+ minifier.escaped_quotation -= 1;
+ } else if item1.eq(&'"') {
+ minifier.is_string = false;
+ }
+ }
+ !minifier.is_string && item1.is_whitespace()
+}
diff --git a/vendor/minifier/src/json/mod.rs b/vendor/minifier/src/json/mod.rs
new file mode 100644
index 000000000..44c8a1c92
--- /dev/null
+++ b/vendor/minifier/src/json/mod.rs
@@ -0,0 +1,114 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::json::{
+ json_minifier::{keep_element, JsonMinifier},
+ read::json_read::JsonRead,
+ string::JsonMultiFilter,
+};
+use std::fmt;
+use std::io::{self, Read};
+
+mod read {
+ mod byte_to_char;
+ mod internal_buffer;
+ mod internal_reader;
+ pub mod json_read;
+}
+
+mod json_minifier;
+mod string;
+
+type JsonMethod = fn(&mut JsonMinifier, &char, Option<&char>) -> bool;
+
+/// Minifies a given String by JSON minification rules
+///
+/// # Example
+///
+/// ```rust
+/// use minifier::json::minify;
+///
+/// let json = r#"
+/// {
+/// "test": "test",
+/// "test2": 2
+/// }
+/// "#.into();
+/// let json_minified = minify(json);
+/// assert_eq!(&json_minified.to_string(), r#"{"test":"test","test2":2}"#);
+/// ```
+#[inline]
+pub fn minify(json: &str) -> Minified<'_> {
+ Minified(JsonMultiFilter::new(json.chars(), keep_element))
+}
+
+#[derive(Debug)]
+pub struct Minified<'a>(JsonMultiFilter<'a, JsonMethod>);
+
+impl<'a> Minified<'a> {
+ pub fn write<W: io::Write>(self, w: W) -> io::Result<()> {
+ self.0.write(w)
+ }
+}
+
+impl<'a> fmt::Display for Minified<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+/// Minifies a given Read by JSON minification rules
+///
+/// # Example
+///
+/// ```rust
+/// extern crate minifier;
+/// use std::fs::File;
+/// use std::io::Read;
+/// use minifier::json::minify_from_read;
+///
+/// fn main() {
+/// let mut html_minified = String::new();
+/// let mut file = File::open("tests/files/test.json").expect("file not found");
+/// minify_from_read(file).read_to_string(&mut html_minified);
+/// }
+/// ```
+#[inline]
+pub fn minify_from_read<R: Read>(json: R) -> JsonRead<JsonMethod, R> {
+ JsonRead::new(json, keep_element)
+}
+
+#[test]
+fn removal_from_read() {
+ use std::fs::File;
+
+ let input = File::open("tests/files/test.json").expect("file not found");
+ let expected: String = "{\"test\":\"\\\" test2\",\"test2\":\"\",\"test3\":\" \"}".into();
+ let mut actual = String::new();
+ minify_from_read(input)
+ .read_to_string(&mut actual)
+ .expect("error at read");
+ assert_eq!(actual, expected);
+}
+
+#[test]
+fn removal_of_control_characters() {
+ let input = "\n".into();
+ let expected: String = "".into();
+ let actual = minify(input);
+ assert_eq!(actual.to_string(), expected);
+}
+
+#[test]
+fn removal_of_whitespace_outside_of_tags() {
+ let input = r#"
+ {
+ "test": "\" test2",
+ "test2": "",
+ "test3": " "
+ }
+ "#
+ .into();
+ let expected: String = "{\"test\":\"\\\" test2\",\"test2\":\"\",\"test3\":\" \"}".into();
+ let actual = minify(input);
+ assert_eq!(actual.to_string(), expected);
+}
diff --git a/vendor/minifier/src/json/read/byte_to_char.rs b/vendor/minifier/src/json/read/byte_to_char.rs
new file mode 100644
index 000000000..d3618b9cb
--- /dev/null
+++ b/vendor/minifier/src/json/read/byte_to_char.rs
@@ -0,0 +1,132 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::json::read::internal_reader::InternalReader;
+use std::{
+ error, fmt,
+ io::{Error, Read},
+ str::from_utf8,
+};
+
+pub struct ByteToChar<R> {
+ iter: InternalReader<R>,
+}
+
+impl<R: Read> ByteToChar<R> {
+ #[inline]
+ pub fn new(read: R, buffer_size: usize) -> Result<Self, Error> {
+ Ok(ByteToChar {
+ iter: InternalReader::new(read, buffer_size)?,
+ })
+ }
+
+ fn get_next(&mut self) -> Result<Option<u8>, CharsError> {
+ match self.iter.next() {
+ None => Ok(None),
+ Some(item) => match item {
+ Ok(item) => Ok(Some(item)),
+ Err(err) => Err(CharsError::Other(err)),
+ },
+ }
+ }
+}
+
+impl<R: Read + fmt::Debug> fmt::Debug for ByteToChar<R> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Filter").field("iter", &self.iter).finish()
+ }
+}
+
+impl<R: Read> Iterator for ByteToChar<R> {
+ type Item = Result<char, CharsError>;
+
+ fn next(&mut self) -> Option<Result<char, CharsError>> {
+ let first_byte = match self.get_next() {
+ Err(err) => return Some(Err(err)),
+ Ok(item) => match item {
+ Some(item) => item,
+ None => return None,
+ },
+ };
+
+ let width = utf8_char_width(first_byte);
+ if width == 1 {
+ return Some(Ok(first_byte as char));
+ }
+ if width == 0 {
+ return Some(Err(CharsError::NotUtf8));
+ }
+ let mut buf = [first_byte, 0, 0, 0];
+ {
+ let mut start = 1;
+ while start < width {
+ let byte = match self.get_next() {
+ Err(err) => return Some(Err(err)),
+ Ok(item) => match item {
+ Some(item) => item,
+ None => return Some(Err(CharsError::NotUtf8)),
+ },
+ };
+ buf[start] = byte;
+ start += 1;
+ }
+ }
+ Some(match from_utf8(&buf[..width]).ok() {
+ Some(s) => Ok(s.chars().next().unwrap()),
+ None => Err(CharsError::NotUtf8),
+ })
+ }
+}
+
+fn utf8_char_width(b: u8) -> usize {
+ UTF8_CHAR_WIDTH[b as usize] as usize
+}
+
+// https://tools.ietf.org/html/rfc3629
+static UTF8_CHAR_WIDTH: [u8; 256] = [
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, // 0x1F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, // 0x3F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, // 0x5F
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, // 0x7F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // 0x9F
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, // 0xBF
+ 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, // 0xDF
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xEF
+ 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xFF
+];
+
+/// An enumeration of possible errors that can be generated from the `Chars`
+/// adapter.
+#[derive(Debug)]
+pub enum CharsError {
+ /// Variant representing that the underlying stream was read successfully
+ /// but it did not contain valid utf8 data.
+ NotUtf8,
+
+ /// Variant representing that an I/O error occurred.
+ Other(Error),
+}
+
+impl error::Error for CharsError {
+ fn cause(&self) -> Option<&dyn error::Error> {
+ match *self {
+ CharsError::NotUtf8 => None,
+ CharsError::Other(ref e) => e.source(),
+ }
+ }
+}
+
+impl fmt::Display for CharsError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match *self {
+ CharsError::NotUtf8 => "byte stream did not contain valid utf8".fmt(f),
+ CharsError::Other(ref e) => e.fmt(f),
+ }
+ }
+}
diff --git a/vendor/minifier/src/json/read/internal_buffer.rs b/vendor/minifier/src/json/read/internal_buffer.rs
new file mode 100644
index 000000000..90eebcd73
--- /dev/null
+++ b/vendor/minifier/src/json/read/internal_buffer.rs
@@ -0,0 +1,44 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+const ARRAY_DEFAULT: u8 = 0;
+
+#[derive(Debug)]
+pub struct Buffer {
+ buffer: Vec<u8>,
+ read_pos: usize,
+ buffer_size: usize,
+ data_size: usize,
+}
+
+impl Buffer {
+ pub fn new(size: usize) -> Buffer {
+ Buffer {
+ buffer: vec![ARRAY_DEFAULT; size],
+ read_pos: 0,
+ buffer_size: size,
+ data_size: 0,
+ }
+ }
+
+ pub fn as_mut(&mut self) -> &mut [u8] {
+ self.buffer.as_mut()
+ }
+
+ pub fn update_metadata(&mut self, size: usize) {
+ self.read_pos = 0;
+ self.data_size = size;
+ }
+
+ pub fn next(&mut self) -> Option<u8> {
+ if self.read_pos >= self.data_size {
+ return None;
+ }
+ let item = self.buffer.get(self.read_pos);
+ self.read_pos += 1;
+ item.copied()
+ }
+
+ pub fn cont(&self) -> bool {
+ self.data_size == self.buffer_size
+ }
+}
diff --git a/vendor/minifier/src/json/read/internal_reader.rs b/vendor/minifier/src/json/read/internal_reader.rs
new file mode 100644
index 000000000..45f178f08
--- /dev/null
+++ b/vendor/minifier/src/json/read/internal_reader.rs
@@ -0,0 +1,63 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use super::internal_buffer::Buffer;
+use std::{
+ fmt,
+ io::{Error, Read},
+};
+
+pub struct InternalReader<R> {
+ read: R,
+ buffer_size: usize,
+ buffer: Buffer,
+}
+
+impl<R: Read> InternalReader<R> {
+ pub fn new(mut read: R, buffer_size: usize) -> Result<Self, Error> {
+ let mut buffer = Buffer::new(buffer_size);
+ InternalReader::read_data(&mut read, &mut buffer)?;
+ Ok(InternalReader {
+ read,
+ buffer_size,
+ buffer,
+ })
+ }
+
+ fn read_data(read: &mut R, buffer: &mut Buffer) -> Result<(), Error> {
+ let size = read.read(buffer.as_mut())?;
+ buffer.update_metadata(size);
+ Ok(())
+ }
+}
+
+impl<R: Read + fmt::Debug> fmt::Debug for InternalReader<R> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("JsonReader")
+ .field("read", &self.read)
+ .field("buffer_size", &self.buffer_size)
+ .field("buffer", &self.buffer)
+ .finish()
+ }
+}
+
+impl<R: Read> Iterator for InternalReader<R> {
+ type Item = Result<u8, Error>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Result<u8, Error>> {
+ if self.buffer_size == 0 {
+ return None;
+ }
+ loop {
+ if let Some(item) = self.buffer.next() {
+ return Some(Ok(item));
+ } else if self.buffer.cont() {
+ if let Err(err) = InternalReader::read_data(&mut self.read, &mut self.buffer) {
+ return Some(Err(err));
+ };
+ } else {
+ return None;
+ }
+ }
+ }
+}
diff --git a/vendor/minifier/src/json/read/json_read.rs b/vendor/minifier/src/json/read/json_read.rs
new file mode 100644
index 000000000..338db13c8
--- /dev/null
+++ b/vendor/minifier/src/json/read/json_read.rs
@@ -0,0 +1,106 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::json::{
+ json_minifier::JsonMinifier,
+ read::byte_to_char::{ByteToChar, CharsError},
+};
+use std::{
+ fmt,
+ io::{Error, ErrorKind, Read},
+ vec::IntoIter,
+};
+
+pub struct JsonRead<P, R> {
+ minifier: JsonMinifier,
+ read: Option<R>,
+ iter: Option<ByteToChar<R>>,
+ predicate: P,
+ initialized: bool,
+ item_iter: Option<IntoIter<u8>>,
+ item1: Option<char>,
+}
+
+impl<P, R: Read> JsonRead<P, R> {
+ #[inline]
+ pub fn new(read: R, predicate: P) -> Self {
+ JsonRead {
+ minifier: JsonMinifier::default(),
+ read: Some(read),
+ iter: None,
+ predicate,
+ initialized: false,
+ item_iter: None,
+ item1: None,
+ }
+ }
+
+ fn get_next(&mut self) -> Result<Option<char>, CharsError> {
+ match self.iter.as_mut().unwrap().next() {
+ None => Ok(None),
+ Some(item) => match item {
+ Ok(item) => Ok(Some(item)),
+ Err(err) => Err(err),
+ },
+ }
+ }
+
+ fn add_char_to_buffer(&mut self, buf: &mut [u8], buf_pos: &mut usize) {
+ if let Some(ref mut iter) = self.item_iter {
+ while *buf_pos < buf.len() {
+ if let Some(byte) = iter.next() {
+ buf[*buf_pos] = byte;
+ *buf_pos += 1;
+ } else {
+ break;
+ }
+ }
+ }
+ }
+}
+
+impl<P, R: Read + fmt::Debug> fmt::Debug for JsonRead<P, R> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Filter")
+ .field("iter", &self.iter)
+ .field("initialized", &self.initialized)
+ .finish()
+ }
+}
+
+impl<P, R> Read for JsonRead<P, R>
+where
+ R: Read,
+ P: FnMut(&mut JsonMinifier, &char, Option<&char>) -> bool,
+{
+ fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
+ let mut buf_pos: usize = 0;
+
+ if buf.is_empty() {
+ return Ok(0);
+ }
+
+ if !self.initialized {
+ self.iter = Some(ByteToChar::new(self.read.take().unwrap(), buf.len())?);
+ self.item1 = self.get_next()?;
+ self.initialized = true;
+ }
+
+ while let Some(item) = self.item1.take() {
+ self.item1 = self.get_next()?;
+ if (self.predicate)(&mut self.minifier, &item, self.item1.as_ref()) {
+ self.item_iter = Some(item.to_string().into_bytes().into_iter());
+ self.add_char_to_buffer(buf, &mut buf_pos);
+ }
+ if buf_pos >= buf.len() {
+ break;
+ }
+ }
+ Ok(buf_pos)
+ }
+}
+
+impl From<CharsError> for Error {
+ fn from(_: CharsError) -> Self {
+ Error::from(ErrorKind::InvalidData)
+ }
+}
diff --git a/vendor/minifier/src/json/string.rs b/vendor/minifier/src/json/string.rs
new file mode 100644
index 000000000..071bf0012
--- /dev/null
+++ b/vendor/minifier/src/json/string.rs
@@ -0,0 +1,100 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::json::json_minifier::JsonMinifier;
+
+use std::fmt;
+use std::str::Chars;
+
+#[derive(Clone)]
+pub struct JsonMultiFilter<'a, P: Clone> {
+ minifier: JsonMinifier,
+ iter: Chars<'a>,
+ predicate: P,
+ initialized: bool,
+ item1: Option<<Chars<'a> as Iterator>::Item>,
+}
+
+impl<'a, P: Clone> JsonMultiFilter<'a, P> {
+ #[inline]
+ pub fn new(iter: Chars<'a>, predicate: P) -> Self {
+ JsonMultiFilter {
+ minifier: JsonMinifier::default(),
+ iter,
+ predicate,
+ initialized: false,
+ item1: None,
+ }
+ }
+}
+
+impl<'a, P: Clone> fmt::Debug for JsonMultiFilter<'a, P> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Filter")
+ .field("minifier", &self.minifier)
+ .field("iter", &self.iter)
+ .field("initialized", &self.initialized)
+ .finish()
+ }
+}
+
+impl<'a, P: Clone> Iterator for JsonMultiFilter<'a, P>
+where
+ P: FnMut(
+ &mut JsonMinifier,
+ &<Chars<'a> as Iterator>::Item,
+ Option<&<Chars<'a> as Iterator>::Item>,
+ ) -> bool,
+{
+ type Item = <Chars<'a> as Iterator>::Item;
+
+ #[inline]
+ fn next(&mut self) -> Option<<Chars<'a> as Iterator>::Item> {
+ if !self.initialized {
+ self.item1 = self.iter.next();
+ self.initialized = true;
+ }
+
+ while let Some(item) = self.item1.take() {
+ self.item1 = self.iter.next();
+ if (self.predicate)(&mut self.minifier, &item, self.item1.as_ref()) {
+ return Some(item);
+ }
+ }
+ None
+ }
+}
+
+impl<'a, P> JsonMultiFilter<'a, P>
+where
+ P: FnMut(
+ &mut JsonMinifier,
+ &<Chars<'a> as Iterator>::Item,
+ Option<&<Chars<'a> as Iterator>::Item>,
+ ) -> bool
+ + Clone,
+{
+ pub(super) fn write<W: std::io::Write>(self, mut w: W) -> std::io::Result<()> {
+ for token in self {
+ write!(w, "{}", token)?;
+ }
+ Ok(())
+ }
+}
+
+impl<'a, P> fmt::Display for JsonMultiFilter<'a, P>
+where
+ P: FnMut(
+ &mut JsonMinifier,
+ &<Chars<'a> as Iterator>::Item,
+ Option<&<Chars<'a> as Iterator>::Item>,
+ ) -> bool
+ + Clone,
+{
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let s = (*self).clone();
+ for token in s {
+ write!(f, "{}", token)?;
+ }
+ Ok(())
+ }
+}