1pub mod c_common;
14pub mod c_cpp;
15pub mod go;
16pub mod java;
17pub mod javascript;
18pub mod json;
19pub mod rust;
20pub mod sql;
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub struct MinifyError {
24 pub message: String,
25}
26
27impl MinifyError {
28 pub(crate) fn new(s: impl Into<String>) -> Self {
29 MinifyError { message: s.into() }
30 }
31}
32
33#[derive(Debug, Clone, Default)]
34pub struct MinifyOptions {
35 pub keep_comments: bool,
40}
41
42#[derive(Debug, Clone, Default)]
45pub struct MinifyOutput {
46 pub body: String,
47 pub warnings: Vec<MinifyWarning>,
48}
49
50impl MinifyOutput {
51 pub(crate) fn body(s: impl Into<String>) -> Self {
52 MinifyOutput {
53 body: s.into(),
54 warnings: Vec::new(),
55 }
56 }
57}
58
59#[derive(Debug, Clone)]
62pub enum MinifyWarning {
63 LineCommentConverted,
67}
68
69pub fn is_supported(lang: &str) -> bool {
73 matches!(
74 lang.to_ascii_lowercase().as_str(),
75 "json"
76 | "jsonl"
77 | "rust"
78 | "rs"
79 | "c"
80 | "h"
81 | "cpp"
82 | "c++"
83 | "cc"
84 | "cxx"
85 | "hpp"
86 | "hxx"
87 | "java"
88 | "go"
89 | "js"
90 | "javascript"
91 | "ts"
92 | "typescript"
93 | "sql"
94 )
95}
96
97pub fn refusal_reason(lang: &str) -> Option<&'static str> {
100 match lang.to_ascii_lowercase().as_str() {
101 "python" | "py" => Some("Python uses significant whitespace; cannot be safely minified"),
102 "yaml" | "yml" => Some("YAML uses significant whitespace; cannot be safely minified"),
103 "makefile" | "make" | "mk" => Some(
104 "Makefile syntax is whitespace-sensitive (tabs are significant); cannot be safely minified",
105 ),
106 _ => None,
107 }
108}
109
110pub fn minify(lang: &str, source: &str, opts: &MinifyOptions) -> Result<MinifyOutput, MinifyError> {
113 match lang.to_ascii_lowercase().as_str() {
114 "json" => json::minify_json(source),
115 "jsonl" => json::minify_jsonl(source),
116 "rust" | "rs" => rust::minify(source, opts),
117 "c" | "h" => c_cpp::minify(source, opts, false),
118 "cpp" | "c++" | "cc" | "cxx" | "hpp" | "hxx" => c_cpp::minify(source, opts, true),
119 "java" => java::minify(source, opts),
120 "go" => go::minify(source, opts),
121 "js" | "javascript" | "ts" | "typescript" => javascript::minify(source, opts),
122 "sql" => sql::minify(source, opts),
123 other => Err(MinifyError::new(format!(
124 "no minifier registered for language `{}`",
125 other
126 ))),
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn dispatch_json_and_aliases() {
136 let opts = MinifyOptions::default();
137 assert!(minify("json", "{}", &opts).is_ok());
138 assert!(minify("JSON", "{}", &opts).is_ok());
139 assert!(minify("jsonl", "", &opts).is_ok());
140 assert!(minify("python", "x = 1", &opts).is_err());
141 }
142
143 #[test]
144 fn supported_check() {
145 assert!(is_supported("json"));
146 assert!(is_supported("JSONL"));
147 assert!(is_supported("rust"));
148 assert!(is_supported("rs"));
149 assert!(is_supported("c++"));
150 assert!(is_supported("ts"));
151 assert!(!is_supported("python"));
152 assert!(!is_supported(""));
153 }
154
155 #[test]
156 fn refused_languages() {
157 assert!(refusal_reason("python").is_some());
158 assert!(refusal_reason("PY").is_some());
159 assert!(refusal_reason("yaml").is_some());
160 assert!(refusal_reason("yml").is_some());
161 assert!(refusal_reason("makefile").is_some());
162 assert!(refusal_reason("rust").is_none());
163 assert!(refusal_reason("json").is_none());
164 }
165}