1mod buffer;
14mod commands;
15mod core;
16mod infrastructure;
17
18#[cfg(feature = "analyzer")]
19pub mod analyzer;
20
21#[cfg(feature = "lsp")]
22pub mod lsp;
23
24use commands::{handle_in_memory, process_mode_and_handle, process_mode_and_handle_with_options};
25use console::style;
26use core::{compiled_css::CompiledCssInMemory, config::ConfigInMemory};
27use indicatif::{ProgressBar, ProgressStyle};
28use infrastructure::{GrimoireCssDiagnostic, LightningCssOptimizer};
29use miette::GraphicalReportHandler;
30use std::path::Path;
31use std::time::{Duration, Instant};
32
33pub use core::{GrimoireCssError, color, component, config, spell::Spell};
34
35static GRIMM_CALM: &str = " |(• ε •)|";
36static GRIMM_HAPPY: &str = " ヽ(• ε •)ノ";
37static GRIMM_CURSED: &str = " |(x ~ x)|";
38static GRIMM_CASTING: [&str; 8] = [
39 GRIMM_CALM,
40 " (|¬ヘ¬)|",
41 " \\(°o°)/",
42 " (∩¬ロ¬)⊃━▪ ~",
43 " (∩¬ロ¬)⊃━▪ ~·",
44 " (∩¬ロ¬)⊃━▪ ~·•",
45 " (∩¬ロ¬)⊃━▪ ~·•●",
46 GRIMM_HAPPY,
47];
48
49pub fn start(mode: &str) -> Result<(), GrimoireCssError> {
79 let current_dir = std::env::current_dir()?;
80 let css_optimizer = LightningCssOptimizer::new(¤t_dir)?;
81
82 process_mode_and_handle(mode, ¤t_dir, &css_optimizer)
83}
84
85pub fn start_in_memory(
86 config: &ConfigInMemory,
87) -> Result<Vec<CompiledCssInMemory>, GrimoireCssError> {
88 let css_optimizer = LightningCssOptimizer::new_from(
89 config.browserslist_content.as_deref().unwrap_or_default(),
90 )?;
91
92 handle_in_memory(config, &css_optimizer)
93}
94
95#[cfg(feature = "analyzer")]
99pub fn start_in_memory_pretty(
100 config: &ConfigInMemory,
101) -> Result<Vec<CompiledCssInMemory>, GrimoireCssError> {
102 let css_optimizer = LightningCssOptimizer::new_from_with_printer_minify(
103 config.browserslist_content.as_deref().unwrap_or_default(),
104 false,
105 )?;
106
107 handle_in_memory(config, &css_optimizer)
108}
109
110pub fn get_logged_messages() -> Vec<String> {
114 buffer::read_messages()
115}
116
117pub fn start_as_cli(args: Vec<String>) -> Result<(), GrimoireCssError> {
153 let bin_name = args
154 .first()
155 .and_then(|s| Path::new(s).file_name())
156 .and_then(|n| n.to_str())
157 .unwrap_or("grimoire_css");
158
159 let help_text = || {
160 let usage = ["grimoire_css", "grim"]
161 .into_iter()
162 .map(|n| format!(" {n} <mode> [mode args]"))
163 .collect::<Vec<_>>()
164 .join("\n");
165
166 format!(
167 "Usage:\n{usage}\n\nModes:\n build\n init\n shorten\n fi\n\nUtilities:\n -h, --help Print help\n -V, --version Print version\n"
168 )
169 };
170
171 if args.get(1).is_some_and(|a| a == "--version" || a == "-V") {
172 println!("{bin_name} {}", env!("CARGO_PKG_VERSION"));
173 return Ok(());
174 }
175
176 if args
177 .get(1)
178 .is_some_and(|a| a == "--help" || a == "-h" || a == "help")
179 {
180 println!("{}", help_text());
181 return Ok(());
182 }
183
184 if args.get(1).is_some_and(|m| m == "fi") {
186 return commands::fi::run_fi_cli(args);
187 }
188
189 println!();
190
191 println!(
192 "{} Ritual initiated",
193 style(" Grimoire CSS ").white().on_color256(55).bright(),
194 );
195
196 if args.len() < 2 {
198 let message = format!(
199 "{} {} ",
200 style(" Cursed! ").white().on_red().bright(),
201 format_args!("No mode provided")
202 );
203
204 println!();
205 println!("{GRIMM_CURSED}");
206 println!();
207 println!("{message}");
208 println!();
209 println!("{}", help_text());
210
211 return Err(GrimoireCssError::InvalidInput(message));
212 }
213
214 println!();
215
216 let pb = ProgressBar::new_spinner();
217 pb.set_style(ProgressStyle::default_spinner().tick_strings(&GRIMM_CASTING));
218 pb.enable_steady_tick(Duration::from_millis(220));
219 pb.set_draw_target(indicatif::ProgressDrawTarget::stdout_with_hz(10));
220
221 let start_time = Instant::now();
222
223 let mode = args[1].as_str();
224
225 let cli_options = commands::CliOptions {
226 force_version_update: mode == "build" && args.iter().any(|a| a == "--force-version-update"),
227 };
228
229 let current_dir = std::env::current_dir()?;
231 let css_optimizer = LightningCssOptimizer::new(¤t_dir)?;
232
233 match process_mode_and_handle_with_options(mode, ¤t_dir, &css_optimizer, cli_options) {
234 Ok(_) => {
235 pb.finish_and_clear();
236
237 print!("\r\x1b[2K{GRIMM_HAPPY} Spells cast successfully.\n");
238
239 let duration = start_time.elapsed();
240
241 output_saved_messages();
242
243 println!();
244
245 println!(
246 "{}",
247 style(format!(
248 "{}",
249 style(format!(" Enchanted in {duration:.2?}! "))
250 .white()
251 .on_color256(55)
252 .bright(),
253 ))
254 );
255
256 println!();
257
258 Ok(())
259 }
260 Err(e) => {
261 pb.finish_and_clear();
262 print!("\r\x1b[2K{GRIMM_CURSED}\n");
263
264 println!();
265 println!("{}", style(" Cursed! ").white().on_red().bright());
266 println!();
267
268 let diagnostic: GrimoireCssDiagnostic = (&e).into();
269 let mut out = String::new();
270 GraphicalReportHandler::new()
271 .render_report(&mut out, &diagnostic)
272 .unwrap();
273 println!("{out}");
274
275 Err(e)
276 }
277 }
278}
279
280fn output_saved_messages() {
281 let messages = get_logged_messages();
282
283 if !messages.is_empty() {
284 println!();
285 for msg in &messages {
286 println!(" • {msg}");
287 }
288 }
289}