Skip to main content

studiole_command/services/
progress_writer_factory.rs

1//! DI-managed factory that owns a shared [`MultiProgress`] and produces [`ProgressWriter`] handles.
2use crate::prelude::*;
3use indicatif::MultiProgress;
4use tracing_subscriber::fmt::MakeWriter;
5
6/// Factory owning a shared [`MultiProgress`] for coordinating progress bars and log output.
7///
8/// - Implements [`FromServices`] for automatic DI construction
9/// - Implements [`MakeWriter`] so it can be passed to [`LoggerBuilder::with_writer`]
10/// - Registered by `with_commands()`; resolved by both [`CliProgress`] and the logger
11#[derive(Clone)]
12pub struct ProgressWriterFactory {
13    multi: MultiProgress,
14}
15
16impl ProgressWriterFactory {
17    /// Clone of the inner [`MultiProgress`] handle.
18    ///
19    /// - Used by [`CliProgress::new`] to attach bars to the shared group
20    #[must_use]
21    pub fn multi(&self) -> MultiProgress {
22        self.multi.clone()
23    }
24}
25
26impl FromServices for ProgressWriterFactory {
27    type Error = ResolveError;
28
29    fn from_services(_services: &ServiceProvider) -> Result<Self, Report<Self::Error>> {
30        Ok(Self {
31            multi: MultiProgress::new(),
32        })
33    }
34}
35
36impl<'a> MakeWriter<'a> for ProgressWriterFactory {
37    type Writer = ProgressWriter<'a>;
38    fn make_writer(&'a self) -> Self::Writer {
39        ProgressWriter::new(&self.multi)
40    }
41}
42
43#[cfg(all(test, feature = "server"))]
44mod tests {
45    use super::*;
46    use std::io::Write;
47
48    /// `make_writer().write` returns the byte count.
49    #[test]
50    fn progress_writer_factory_make_writer_write() {
51        // Arrange
52        let factory = ProgressWriterFactory {
53            multi: MultiProgress::with_draw_target(indicatif::ProgressDrawTarget::hidden()),
54        };
55        let mut writer = factory.make_writer();
56        // Act
57        let count = writer.write(b"x").expect("write should succeed");
58        // Assert
59        assert_eq!(count, 1);
60    }
61
62    /// `FromServices` constructs without error.
63    #[test]
64    fn progress_writer_factory_from_services() {
65        // Arrange
66        let services = ServiceBuilder::new().build();
67        // Act
68        let factory = ProgressWriterFactory::from_services(&services);
69        // Assert
70        assert!(factory.is_ok());
71    }
72}