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}