mc_repack_core/min/
mod.rs1use crate::{cfg, ext::KnownFmt};
2
3pub mod json;
5
6pub mod png;
8
9pub mod toml;
11
12pub mod nbt;
14
15pub mod ogg;
17
18pub mod jar;
20
21#[inline]
22const fn strip_bom(b: &[u8]) -> &[u8] {
23 if let [239, 187, 191, x @ ..] = b { x } else { b }
24}
25
26#[inline]
27fn brackets(b: &[u8]) -> Option<&[u8]> {
28 let i = b.iter().position(|&b| b == b'{' || b == b'[')?;
29 let endb = match b[i] {
30 b'{' => b'}',
31 b'[' => b']',
32 _ => { return None; }
33 };
34 let j = b.iter().rposition(|&b| b == endb)?;
35 Some(&b[i..=j])
36}
37
38#[derive(Clone)]
40pub enum Minifier {
41 #[cfg(feature = "png")] PNG,
43 JSON,
45 #[cfg(feature = "toml")] TOML,
47 #[cfg(feature = "nbt")] NBT,
49 #[cfg(feature = "ogg")] OGG,
51 #[cfg(feature = "jar")] JAR,
53 Hash,
55 Slash,
57 UnixLine
59}
60impl Minifier {
61 #[must_use]
63 pub fn by_extension(ftype: &str) -> Option<Self> {
64 Some(match ftype {
65 #[cfg(feature = "png")] "png" => Self::PNG,
66 "json" | "mcmeta" => Self::JSON,
67 #[cfg(feature = "toml")] "toml" => Self::TOML,
68 #[cfg(feature = "nbt")] "nbt" | "blueprint" => Self::NBT,
69 #[cfg(feature = "ogg")] "ogg" => Self::OGG,
70 #[cfg(feature = "jar")] "jar" => Self::JAR,
71 "cfg" | "obj" | "mtl" => Self::Hash,
72 "zs" | "js" | "fsh" | "vsh" => Self::Slash,
73 "mf" => Self::UnixLine,
74 _ => return None
75 })
76 }
77
78 pub const fn by_file_format(f: KnownFmt) -> Option<Self> {
80 Some(match f {
81 #[cfg(feature = "png")] KnownFmt::Png => Self::PNG,
82 KnownFmt::Json => Self::JSON,
83 #[cfg(feature = "toml")] KnownFmt::Toml => Self::TOML,
84 #[cfg(feature = "nbt")] KnownFmt::Nbt => Self::NBT,
85 #[cfg(feature = "ogg")] KnownFmt::Ogg => Self::OGG,
86 #[cfg(feature = "jar")] KnownFmt::Jar => Self::JAR,
87 KnownFmt::Cfg | KnownFmt::Obj | KnownFmt::Mtl => Self::Hash,
88 KnownFmt::Fsh | KnownFmt::Vsh | KnownFmt::Js | KnownFmt::Zs => Self::Slash,
89 KnownFmt::Mf => Self::UnixLine,
90 _ => return None
91 })
92 }
93
94 pub fn minify(&self, cfgmap: &cfg::ConfigMap, v: &[u8], vout: &mut Vec<u8>) -> Result_ {
98 match self {
99 #[cfg(feature = "png")] Self::PNG => cfgmap.fetch::<png::MinifierPNG>().minify(v, vout),
100 Self::JSON => cfgmap.fetch::<json::MinifierJSON>().minify(v, vout),
101 #[cfg(feature = "toml")] Self::TOML => cfgmap.fetch::<toml::MinifierTOML>().minify(strip_bom(v), vout),
102 #[cfg(feature = "nbt")] Self::NBT => cfgmap.fetch::<nbt::MinifierNBT>().minify(v, vout),
103 #[cfg(feature = "ogg")] Self::OGG => cfgmap.fetch::<ogg::MinifierOGG>().minify(v, vout),
104 #[cfg(feature = "jar")] Self::JAR => cfgmap.fetch::<jar::MinifierJAR>().minify(v, vout),
105 Self::Hash => remove_line_comments("#", v, vout),
106 Self::Slash => remove_line_comments("//", v, vout),
107 Self::UnixLine => unixify_lines(v, vout)
108 }
109 }
110
111 #[must_use]
113 pub const fn compress_min(&self) -> u16 {
114 match self {
115 #[cfg(feature = "png")] Self::PNG => 512,
116 Self::JSON => 64,
117 #[cfg(feature = "toml")] Self::TOML => 64,
118 #[cfg(feature = "nbt")] Self::NBT => 768,
119 _ => 24
120 }
121 }
122}
123
124type Result_ = anyhow::Result<()>;
125
126fn remove_line_comments(bs: &'static str, v: &[u8], vout: &mut Vec<u8>) -> Result_ {
127 let v = std::str::from_utf8(v)?;
128 for l in v.lines() {
129 let Some(ix) = l.as_bytes().iter().position(|&b| !b.is_ascii_whitespace()) else {
130 continue;
131 };
132 let l = &l[ix..];
133 let nix = l.find(bs);
134 let l = match nix {
135 Some(nix) if nix == ix => "",
136 Some(nix) => &l[..nix],
137 None => l
138 };
139 vout.extend_from_slice(l.trim_end().as_bytes());
140 vout.push(b'\n');
141 }
142 Ok(())
143}
144
145fn unixify_lines(v: &[u8], vout: &mut Vec<u8>) -> Result_ {
146 let v = std::str::from_utf8(v)?;
147 for l in v.lines() {
148 vout.extend_from_slice(l.trim_end().as_bytes());
149 vout.push(b'\n');
150 }
151 Ok(())
152}
153
154#[derive(Debug)]
156pub struct BracketsError;
157impl std::error::Error for BracketsError {}
158impl std::fmt::Display for BracketsError {
159 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 f.write_str("improper opening/closing brackets")
161 }
162}
163
164fn strip_string(s: &mut String) {
165 let Some(li) = s.bytes().position(|b| !b.is_ascii_whitespace()) else {
166 return;
167 };
168 *s = s.split_off(li);
169 let Some(ri) = s.bytes().rposition(|b| !b.is_ascii_whitespace()) else {
170 return;
171 };
172 s.truncate(ri + 1);
173}