minify_html_onepass/lib.rs
1pub use crate::cfg::Cfg;
2use crate::err::debug_repr;
3pub use crate::err::Error;
4pub use crate::err::ErrorType;
5pub use crate::err::FriendlyError;
6use crate::proc::Processor;
7use crate::unit::content::process_content;
8use minify_html_common::spec::tag::ns::Namespace;
9
10mod cfg;
11mod err;
12#[macro_use]
13mod proc;
14#[cfg(test)]
15mod tests;
16mod unit;
17
18/// Minifies a slice in-place and returns the new minified length.
19/// Any original code after the end of the minified code is left intact.
20///
21/// # Arguments
22///
23/// * `code` - A mutable slice of bytes representing the source code to minify.
24/// * `cfg` - Configuration object to adjust minification approach.
25///
26/// # Examples
27///
28/// ```
29/// use minify_html_onepass::{Cfg, Error, in_place};
30///
31/// let mut code = b"<p> Hello, world! </p>".to_vec();
32/// let cfg = &Cfg {
33/// minify_js: false,
34/// minify_css: false,
35/// };
36/// match in_place(&mut code, cfg) {
37/// Ok(minified_len) => assert_eq!(&code, b"<p>Hello, world!d! </p>"),
38/// Err(Error { error_type, position }) => {}
39/// };
40/// ```
41pub fn in_place(code: &mut [u8], cfg: &Cfg) -> Result<usize, Error> {
42 let mut proc = Processor::new(code);
43 process_content(&mut proc, cfg, Namespace::Html, None, false)
44 .and_then(|_| {
45 if !proc.at_end() {
46 Err(ErrorType::UnexpectedClosingTag)
47 } else {
48 Ok(())
49 }
50 })
51 .map_err(|error_type| Error {
52 error_type,
53 position: proc.read_len(),
54 })?;
55 proc.finish()
56}
57
58/// Minifies a str in-place and returns the new minified str.
59/// Any original code after the end of the minified code is left intact.
60///
61/// # Arguments
62///
63/// * `code` - A mutable str representing the source code to minify.
64/// * `cfg` - Configuration object to adjust minification approach.
65///
66/// # Examples
67///
68/// ```
69/// use minify_html_onepass::{Cfg, Error, in_place_str};
70///
71/// let mut code = "<p> Hello, world! </p>".to_string();
72/// let cfg = &Cfg {
73/// minify_js: false,
74/// minify_css: false,
75/// };
76/// match in_place_str(&mut code, cfg) {
77/// Ok(minified_len) => assert_eq!(&code, "<p>Hello, world!d! </p>"),
78/// Err(Error { error_type, position }) => {}
79/// };
80/// ```
81pub fn in_place_str<'s>(code: &'s mut str, cfg: &Cfg) -> Result<&'s str, Error> {
82 let bytes = unsafe { code.as_bytes_mut() };
83 match in_place(bytes, cfg) {
84 Ok(min_len) => Ok(unsafe { std::str::from_utf8_unchecked(&bytes[..min_len]) }),
85 Err(e) => Err(e),
86 }
87}
88
89/// Minifies a Vec in-place, truncating it to the minified length.
90///
91/// # Arguments
92///
93/// * `code` - A slice of bytes representing the source code to minify.
94/// * `cfg` - Configuration object to adjust minification approach.
95///
96/// # Examples
97///
98/// ```
99/// use minify_html_onepass::{Cfg, Error, truncate};
100///
101/// let mut code = b"<p> Hello, world! </p>".to_vec();
102/// let cfg = &Cfg {
103/// minify_js: false,
104/// minify_css: false,
105/// };
106/// match truncate(&mut code, cfg) {
107/// Ok(()) => assert_eq!(code, b"<p>Hello, world!".to_vec()),
108/// Err(Error { error_type, position }) => {}
109/// };
110/// ```
111pub fn truncate(code: &mut Vec<u8>, cfg: &Cfg) -> Result<(), Error> {
112 match in_place(code, cfg) {
113 Ok(written_len) => {
114 code.truncate(written_len);
115 Ok(())
116 }
117 Err(e) => Err(e),
118 }
119}
120
121/// Copies a slice into a new Vec and minifies it, returning the Vec.
122/// The resulting Vec will only contain minified code.
123///
124/// # Arguments
125///
126/// * `code` - A slice of bytes representing the source code to minify.
127/// * `cfg` - Configuration object to adjust minification approach.
128///
129/// # Examples
130///
131/// ```
132/// use minify_html_onepass::{Cfg, Error, copy};
133///
134/// let mut code: &[u8] = b"<p> Hello, world! </p>";
135/// let cfg = &Cfg {
136/// minify_js: false,
137/// minify_css: false,
138/// };
139/// match copy(&code, cfg) {
140/// Ok(minified) => {
141/// assert_eq!(code, b"<p> Hello, world! </p>");
142/// assert_eq!(minified, b"<p>Hello, world!".to_vec());
143/// }
144/// Err(Error { error_type, position }) => {}
145/// };
146/// ```
147pub fn copy(code: &[u8], cfg: &Cfg) -> Result<Vec<u8>, Error> {
148 let mut copy = code.to_vec();
149 match truncate(&mut copy, cfg) {
150 Ok(()) => Ok(copy),
151 Err(e) => Err(e),
152 }
153}
154
155/// Minifies a slice in-place and returns the new minified length.
156/// Any original code after the end of the minified code is left intact.
157///
158/// This function is identical to `in_place` except it returns a `FriendlyError` on error instead.
159///
160/// `FriendlyError` has a `code_context` field, which is a string of a visual representation of the
161/// source, with line numbers and position markers to aid in debugging syntax.
162///
163/// # Arguments
164///
165/// * `code` - A mutable slice of bytes representing the source code to minify.
166/// * `cfg` - Configuration object to adjust minification approach.
167///
168/// # Examples
169///
170/// ```
171/// use minify_html_onepass::{Cfg, FriendlyError, with_friendly_error};
172///
173/// let mut code = b"<p></div>".to_vec();
174/// let cfg = &Cfg {
175/// minify_js: false,
176/// minify_css: false,
177/// };
178/// match with_friendly_error(&mut code, cfg) {
179/// Ok(minified_len) => {}
180/// Err(FriendlyError { position, message, code_context }) => {
181/// assert_eq!(position, 3);
182/// assert_eq!(message, "Unexpected closing tag.");
183/// assert_eq!(code_context, concat!(
184/// "1|<p></div>\n",
185/// ">| ^ \n",
186/// ));
187/// }
188/// };
189/// ```
190pub fn with_friendly_error(code: &mut [u8], cfg: &Cfg) -> Result<usize, FriendlyError> {
191 in_place(code, cfg).map_err(|err| FriendlyError {
192 position: err.position,
193 message: err.error_type.message(),
194 code_context: debug_repr(code, err.position as isize, -1),
195 })
196}