1mod buffer;
14mod commands;
15mod core;
16mod infrastructure;
17
18#[cfg(feature = "analyzer")]
19pub mod analyzer;
20
21pub mod transmutator;
22
23#[cfg(feature = "lsp")]
24pub mod lsp;
25
26#[cfg(feature = "mcp")]
27pub mod mcp;
28
29use commands::{handle_in_memory, process_mode_and_handle, process_mode_and_handle_with_options};
30use console::style;
31use core::{compiled_css::CompiledCssInMemory, config::ConfigInMemory};
32use indicatif::{ProgressBar, ProgressStyle};
33use infrastructure::{GrimoireCssDiagnostic, LightningCssOptimizer};
34use miette::GraphicalReportHandler;
35use std::path::Path;
36use std::time::{Duration, Instant};
37
38pub use core::{GrimoireCssError, color, component, config, spell::Spell};
39
40static GRIMM_CALM: &str = " |(• ε •)|";
41static GRIMM_HAPPY: &str = " ヽ(• ε •)ノ";
42static GRIMM_CURSED: &str = " |(x ~ x)|";
43static GRIMM_CASTING: [&str; 8] = [
44 GRIMM_CALM,
45 " (|¬ヘ¬)|",
46 " \\(°o°)/",
47 " (∩¬ロ¬)⊃━▪ ~",
48 " (∩¬ロ¬)⊃━▪ ~·",
49 " (∩¬ロ¬)⊃━▪ ~·•",
50 " (∩¬ロ¬)⊃━▪ ~·•●",
51 GRIMM_HAPPY,
52];
53
54pub fn start(mode: &str) -> Result<(), GrimoireCssError> {
84 let current_dir = std::env::current_dir()?;
85 let css_optimizer = LightningCssOptimizer::new(¤t_dir)?;
86
87 process_mode_and_handle(mode, ¤t_dir, &css_optimizer)
88}
89
90pub fn build(root: &Path) -> Result<(), GrimoireCssError> {
92 build_with_options(root, false)
93}
94
95pub fn build_with_options(root: &Path, force_version_update: bool) -> Result<(), GrimoireCssError> {
97 let css_optimizer = LightningCssOptimizer::new(root)?;
98 process_mode_and_handle_with_options(
99 "build",
100 root,
101 &css_optimizer,
102 commands::CliOptions {
103 force_version_update,
104 },
105 )
106}
107
108pub fn init(root: &Path) -> Result<(), GrimoireCssError> {
110 commands::init_project(root, "init").map(|_| ())
111}
112
113pub fn shorten(root: &Path) -> Result<(), GrimoireCssError> {
115 commands::shorten_project(root)
116}
117
118pub fn start_in_memory(
119 config: &ConfigInMemory,
120) -> Result<Vec<CompiledCssInMemory>, GrimoireCssError> {
121 let css_optimizer = LightningCssOptimizer::new_from(
122 config.browserslist_content.as_deref().unwrap_or_default(),
123 )?;
124
125 handle_in_memory(config, &css_optimizer)
126}
127
128#[cfg(feature = "analyzer")]
132pub fn start_in_memory_pretty(
133 config: &ConfigInMemory,
134) -> Result<Vec<CompiledCssInMemory>, GrimoireCssError> {
135 let css_optimizer = LightningCssOptimizer::new_from_with_printer_minify(
136 config.browserslist_content.as_deref().unwrap_or_default(),
137 false,
138 )?;
139
140 handle_in_memory(config, &css_optimizer)
141}
142
143pub fn get_logged_messages() -> Vec<String> {
147 buffer::read_messages()
148}
149
150pub fn start_as_cli(args: Vec<String>) -> Result<(), GrimoireCssError> {
186 let bin_name = args
187 .first()
188 .and_then(|s| Path::new(s).file_name())
189 .and_then(|n| n.to_str())
190 .unwrap_or("grimoire_css");
191
192 let help_text = || {
193 let usage = ["grimoire_css", "grim"]
194 .into_iter()
195 .map(|n| format!(" {n} <mode> [mode args]"))
196 .collect::<Vec<_>>()
197 .join("\n");
198
199 format!(
200 "Usage:\n{usage}\n\nModes:\n build\n init\n shorten\n transmute\n fi\n\nUtilities:\n -h, --help Print help\n -V, --version Print version\n"
201 )
202 };
203
204 if args.get(1).is_some_and(|a| a == "--version" || a == "-V") {
205 println!("{bin_name} {}", env!("CARGO_PKG_VERSION"));
206 return Ok(());
207 }
208
209 if args
210 .get(1)
211 .is_some_and(|a| a == "--help" || a == "-h" || a == "help")
212 {
213 println!("{}", help_text());
214 return Ok(());
215 }
216
217 if let Some(result) = commands::process_machine_readable_mode(&args) {
218 return result;
219 }
220
221 println!();
222
223 println!(
224 "{} Ritual initiated",
225 style(" Grimoire CSS ").white().on_color256(55).bright(),
226 );
227
228 if args.len() < 2 {
230 let message = format!(
231 "{} {} ",
232 style(" Cursed! ").white().on_red().bright(),
233 format_args!("No mode provided")
234 );
235
236 println!();
237 println!("{GRIMM_CURSED}");
238 println!();
239 println!("{message}");
240 println!();
241 println!("{}", help_text());
242
243 return Err(GrimoireCssError::InvalidInput(message));
244 }
245
246 println!();
247
248 let pb = ProgressBar::new_spinner();
249 pb.set_style(ProgressStyle::default_spinner().tick_strings(&GRIMM_CASTING));
250 pb.enable_steady_tick(Duration::from_millis(220));
251 pb.set_draw_target(indicatif::ProgressDrawTarget::stdout_with_hz(10));
252
253 let start_time = Instant::now();
254
255 let mode = args[1].as_str();
256
257 let cli_options = commands::CliOptions {
258 force_version_update: mode == "build" && args.iter().any(|a| a == "--force-version-update"),
259 };
260
261 let current_dir = std::env::current_dir()?;
263 let css_optimizer = LightningCssOptimizer::new(¤t_dir)?;
264
265 match process_mode_and_handle_with_options(mode, ¤t_dir, &css_optimizer, cli_options) {
266 Ok(_) => {
267 pb.finish_and_clear();
268
269 print!("\r\x1b[2K{GRIMM_HAPPY} Spells cast successfully.\n");
270
271 let duration = start_time.elapsed();
272
273 output_saved_messages();
274
275 println!();
276
277 println!(
278 "{}",
279 style(format!(
280 "{}",
281 style(format!(" Enchanted in {duration:.2?}! "))
282 .white()
283 .on_color256(55)
284 .bright(),
285 ))
286 );
287
288 println!();
289
290 Ok(())
291 }
292 Err(e) => {
293 pb.finish_and_clear();
294 print!("\r\x1b[2K{GRIMM_CURSED}\n");
295
296 println!();
297 println!("{}", style(" Cursed! ").white().on_red().bright());
298 println!();
299
300 let diagnostic: GrimoireCssDiagnostic = (&e).into();
301 let mut out = String::new();
302 GraphicalReportHandler::new()
303 .render_report(&mut out, &diagnostic)
304 .unwrap();
305 println!("{out}");
306
307 Err(e)
308 }
309 }
310}
311
312fn output_saved_messages() {
313 let messages = get_logged_messages();
314
315 if !messages.is_empty() {
316 println!();
317 for msg in &messages {
318 println!(" • {msg}");
319 }
320 }
321}