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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
pub use crate::err::{Error, ErrorType, FriendlyError};
use crate::proc::Processor;
use crate::unit::content::process_content;
use crate::spec::tag::ns::Namespace;
pub use crate::cfg::Cfg;
use crate::err::debug_repr;

mod cfg;
mod err;
mod gen;
mod pattern;
#[macro_use]
mod proc;
mod spec;
mod tests;
mod unit;

/// Minifies a slice in-place and returns the new minified length.
/// Any original code after the end of the minified code is left intact.
///
/// # Arguments
///
/// * `code` - A mutable slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, Error, in_place};
///
/// let mut code = b"<p>  Hello, world!  </p>".to_vec();
/// let cfg = &Cfg {
///     minify_js: false,
///     minify_css: false,
/// };
/// match in_place(&mut code, cfg) {
///     Ok(minified_len) => assert_eq!(&code, b"<p>Hello, world!d!  </p>"),
///     Err(Error { error_type, position }) => {}
/// };
/// ```
pub fn in_place(code: &mut [u8], cfg: &Cfg) -> Result<usize, Error> {
    let mut proc = Processor::new(code);
    process_content(&mut proc, cfg, Namespace::Html, None, false)
        .and_then(|_| if !proc.at_end() {
            Err(ErrorType::UnexpectedClosingTag)
        } else {
            Ok(())
        })
        .map_err(|error_type| Error {
            error_type,
            position: proc.read_len(),
        })?;
    proc.finish()
}

/// Minifies a str in-place and returns the new minified length.
/// Any original code after the end of the minified code is left intact.
///
/// # Arguments
///
/// * `code` - A mutable str representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, Error, in_place_str};
///
/// let mut code = "<p>  Hello, world!  </p>".to_string();
/// let cfg = &Cfg {
///     minify_js: false,
///     minify_css: false,
/// };
/// match in_place_str(&mut code, cfg) {
///     Ok(minified_len) => assert_eq!(&code, "<p>Hello, world!d!  </p>"),
///     Err(Error { error_type, position }) => {}
/// };
/// ```
pub fn in_place_str<'s>(code: &'s mut str, cfg: &Cfg) -> Result<&'s str, Error> {
    let bytes = unsafe { code.as_bytes_mut() };
    match in_place(bytes, cfg) {
        Ok(min_len) => Ok(unsafe { std::str::from_utf8_unchecked(&bytes[..min_len]) }),
        Err(e) => Err(e),
    }
}

/// Minifies a Vec in-place, truncating it to the minified length.
///
/// # Arguments
///
/// * `code` - A slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, Error, truncate};
///
/// let mut code = b"<p>  Hello, world!  </p>".to_vec();
/// let cfg = &Cfg {
///     minify_js: false,
///     minify_css: false,
/// };
/// match truncate(&mut code, cfg) {
///     Ok(()) => assert_eq!(code, b"<p>Hello, world!".to_vec()),
///     Err(Error { error_type, position }) => {}
/// };
/// ```
pub fn truncate(code: &mut Vec<u8>, cfg: &Cfg) -> Result<(), Error> {
    match in_place(code, cfg) {
        Ok(written_len) => {
            code.truncate(written_len);
            Ok(())
        }
        Err(e) => Err(e),
    }
}

/// Copies a slice into a new Vec and minifies it, returning the Vec.
/// The resulting Vec will only contain minified code.
///
/// # Arguments
///
/// * `code` - A slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, Error, copy};
///
/// let mut code: &[u8] = b"<p>  Hello, world!  </p>";
/// let cfg = &Cfg {
///     minify_js: false,
///     minify_css: false,
/// };
/// match copy(&code, cfg) {
///     Ok(minified) => {
///         assert_eq!(code, b"<p>  Hello, world!  </p>");
///         assert_eq!(minified, b"<p>Hello, world!".to_vec());
///     }
///     Err(Error { error_type, position }) => {}
/// };
/// ```
pub fn copy(code: &[u8], cfg: &Cfg) -> Result<Vec<u8>, Error> {
    let mut copy = code.to_vec();
    match truncate(&mut copy, cfg) {
        Ok(()) => Ok(copy),
        Err(e) => Err(e),
    }
}

/// Minifies a slice in-place and returns the new minified length.
/// Any original code after the end of the minified code is left intact.
///
/// This function is identical to `in_place` except it returns a `FriendlyError` on error instead.
///
/// `FriendlyError` has a `code_context` field, which is a string of a visual representation of the
/// source, with line numbers and position markers to aid in debugging syntax.
///
/// # Arguments
///
/// * `code` - A mutable slice of bytes representing the source code to minify.
/// * `cfg` - Configuration object to adjust minification approach.
///
/// # Examples
///
/// ```
/// use minify_html::{Cfg, FriendlyError, with_friendly_error};
///
/// let mut code = b"<p></div>".to_vec();
/// let cfg = &Cfg {
///     minify_js: false,
///     minify_css: false,
/// };
/// match with_friendly_error(&mut code, cfg) {
///     Ok(minified_len) => {}
///     Err(FriendlyError { position, message, code_context }) => {
///         assert_eq!(position, 3);
///         assert_eq!(message, "Unexpected closing tag.");
///         assert_eq!(code_context, concat!(
///             "1|<p></div>\n",
///             ">|   ^ \n",
///         ));
///     }
/// };
/// ```
pub fn with_friendly_error(code: &mut [u8], cfg: &Cfg) -> Result<usize, FriendlyError> {
    in_place(code, cfg).map_err(|err| FriendlyError {
        position: err.position,
        message: err.error_type.message(),
        code_context: debug_repr(code, err.position as isize, -1),
    })
}