mago_service/formatter/
mod.rs1use mago_feedback::create_progress_bar;
2use mago_feedback::remove_progress_bar;
3use mago_feedback::ProgressBarTheme;
4use mago_formatter::format;
5use mago_interner::ThreadedInterner;
6use mago_parser::parse_source;
7use mago_source::error::SourceError;
8use mago_source::SourceIdentifier;
9use mago_source::SourceManager;
10
11use crate::formatter::config::FormatterConfiguration;
12use crate::utils;
13
14pub mod config;
15
16#[derive(Debug)]
17pub struct FormatterService {
18 configuration: FormatterConfiguration,
19 interner: ThreadedInterner,
20 source_manager: SourceManager,
21}
22
23impl FormatterService {
24 pub fn new(
25 configuration: FormatterConfiguration,
26 interner: ThreadedInterner,
27 source_manager: SourceManager,
28 ) -> Self {
29 Self { configuration, interner, source_manager }
30 }
31
32 pub async fn run(&self, dry_run: bool) -> Result<usize, SourceError> {
34 self.process_sources(self.source_manager.user_defined_source_ids().collect(), dry_run).await
36 }
37
38 #[inline]
39 async fn process_sources(&self, source_ids: Vec<SourceIdentifier>, dry_run: bool) -> Result<usize, SourceError> {
40 let settings = self.configuration.get_settings();
41 let mut handles = Vec::with_capacity(source_ids.len());
42
43 let source_pb = create_progress_bar(source_ids.len(), "📂 Loading", ProgressBarTheme::Red);
44 let parse_pb = create_progress_bar(source_ids.len(), "🧩 Parsing", ProgressBarTheme::Blue);
45 let format_pb = create_progress_bar(source_ids.len(), "✨ Formatting", ProgressBarTheme::Magenta);
46 let write_pb = create_progress_bar(source_ids.len(), "🖊️ Writing", ProgressBarTheme::Green);
47
48 for source_id in source_ids.into_iter() {
49 handles.push(tokio::spawn({
50 let interner = self.interner.clone();
51 let manager = self.source_manager.clone();
52 let source_pb = source_pb.clone();
53 let parse_pb = parse_pb.clone();
54 let format_pb = format_pb.clone();
55 let write_pb = write_pb.clone();
56
57 async move {
58 let source = manager.load(&source_id)?;
60 source_pb.inc(1);
61
62 let (program, error) = parse_source(&interner, &source);
64 parse_pb.inc(1);
65
66 if let Some(error) = error {
67 let source_name = interner.lookup(&source.identifier.0);
68 mago_feedback::error!("skipping formatting for source '{}', {} ", source_name, error);
69
70 format_pb.inc(1);
71 write_pb.inc(1);
72
73 return Result::<bool, SourceError>::Ok(false);
74 }
75
76 let formatted = format(settings, &interner, &source, &program);
78 format_pb.inc(1);
79
80 let changed = utils::apply_changes(&interner, &manager, &source, formatted, dry_run)?;
82 write_pb.inc(1);
83
84 Result::<bool, SourceError>::Ok(changed)
85 }
86 }));
87 }
88
89 let mut changed = 0;
90 for handle in handles {
91 if handle.await.expect("failed to format files, this should never happen.")? {
92 changed += 1;
93 }
94 }
95
96 remove_progress_bar(source_pb);
97 remove_progress_bar(parse_pb);
98 remove_progress_bar(format_pb);
99 remove_progress_bar(write_pb);
100
101 Ok(changed)
102 }
103}