Skip to main content

qubit_progress/reporter/impls/
writer_progress_reporter.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026 Haixing Hu.
4 *
5 *    SPDX-License-Identifier: Apache-2.0
6 *
7 *    Licensed under the Apache License, Version 2.0.
8 *
9 ******************************************************************************/
10use std::{
11    io::Write,
12    sync::{
13        Arc,
14        Mutex,
15    },
16};
17
18use super::format::format_duration;
19use crate::{
20    model::ProgressEvent,
21    reporter::ProgressReporter,
22};
23
24/// Progress reporter that writes human-readable events to a writer.
25///
26/// # Type Parameters
27///
28/// * `W` - Writer receiving formatted progress events.
29#[derive(Debug)]
30pub struct WriterProgressReporter<W> {
31    /// Shared writer receiving progress lines.
32    writer: Arc<Mutex<W>>,
33}
34
35impl<W> WriterProgressReporter<W> {
36    /// Creates a reporter from a shared writer.
37    ///
38    /// # Parameters
39    ///
40    /// * `writer` - Shared writer receiving progress output.
41    ///
42    /// # Returns
43    ///
44    /// A writer-backed progress reporter.
45    #[inline]
46    pub fn new(writer: Arc<Mutex<W>>) -> Self {
47        Self { writer }
48    }
49
50    /// Creates a reporter from an owned writer.
51    ///
52    /// # Parameters
53    ///
54    /// * `writer` - Owned writer receiving progress output.
55    ///
56    /// # Returns
57    ///
58    /// A writer-backed progress reporter.
59    #[inline]
60    pub fn from_writer(writer: W) -> Self {
61        Self::new(Arc::new(Mutex::new(writer)))
62    }
63
64    /// Returns the shared writer used by this reporter.
65    ///
66    /// # Returns
67    ///
68    /// A shared reference to the writer mutex.
69    #[inline]
70    pub const fn writer(&self) -> &Arc<Mutex<W>> {
71        &self.writer
72    }
73}
74
75impl<W> ProgressReporter for WriterProgressReporter<W>
76where
77    W: Write + Send,
78{
79    /// Writes one progress event as a single human-readable line.
80    ///
81    /// # Parameters
82    ///
83    /// * `event` - Progress event to format and write.
84    ///
85    /// # Panics
86    ///
87    /// Panics when the writer mutex is poisoned or writing to the configured
88    /// writer fails.
89    fn report(&self, event: &ProgressEvent) {
90        let mut writer = self
91            .writer
92            .lock()
93            .unwrap_or_else(std::sync::PoisonError::into_inner);
94        writeln!(writer, "{}", format_event(event)).expect("progress reporter should write event");
95    }
96}
97
98/// Formats one progress event.
99///
100/// # Parameters
101///
102/// * `event` - Event to format.
103///
104/// # Returns
105///
106/// A compact human-readable line.
107fn format_event(event: &ProgressEvent) -> String {
108    let counters = event.counters();
109    let progress = match (counters.completed_count(), counters.total_count()) {
110        (completed, Some(total)) => format!(
111            "{completed}/{total} ({:.2}%)",
112            counters.progress_percent().unwrap_or(100.0)
113        ),
114        (completed, None) => format!("{completed} completed"),
115    };
116    let active = counters.active_count();
117    let failed = counters.failed_count();
118    let elapsed = format_duration(event.elapsed());
119    match event.stage() {
120        Some(stage) => format!(
121            "{} [{}] {progress}, active {active}, failed {failed}, elapsed {elapsed}",
122            event.phase(),
123            stage.name(),
124        ),
125        None => format!(
126            "{} {progress}, active {active}, failed {failed}, elapsed {elapsed}",
127            event.phase(),
128        ),
129    }
130}