Skip to main content

qubit_progress/reporter/impls/
logger_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 crate::{
11    model::ProgressEvent,
12    reporter::ProgressReporter,
13};
14
15/// Progress reporter that emits progress events through the `log` crate.
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct LoggerProgressReporter {
18    /// Log target used for emitted records.
19    target: String,
20    /// Log level used for emitted records.
21    level: log::Level,
22}
23
24impl LoggerProgressReporter {
25    /// Creates a logger reporter at [`log::Level::Info`].
26    ///
27    /// # Parameters
28    ///
29    /// * `target` - Log target used for emitted records.
30    ///
31    /// # Returns
32    ///
33    /// A logger-backed progress reporter.
34    #[inline]
35    pub fn new(target: &str) -> Self {
36        Self {
37            target: target.to_owned(),
38            level: log::Level::Info,
39        }
40    }
41
42    /// Returns a copy configured with a log level.
43    ///
44    /// # Parameters
45    ///
46    /// * `level` - Log level used for emitted records.
47    ///
48    /// # Returns
49    ///
50    /// This reporter configured with `level`.
51    #[inline]
52    pub const fn with_level(mut self, level: log::Level) -> Self {
53        self.level = level;
54        self
55    }
56
57    /// Returns the log target.
58    ///
59    /// # Returns
60    ///
61    /// The target used for emitted log records.
62    #[inline]
63    pub fn target(&self) -> &str {
64        self.target.as_str()
65    }
66
67    /// Returns the log level.
68    ///
69    /// # Returns
70    ///
71    /// The level used for emitted log records.
72    #[inline]
73    pub const fn level(&self) -> log::Level {
74        self.level
75    }
76
77    /// Emits one message through the `log` crate.
78    ///
79    /// # Parameters
80    ///
81    /// * `message` - Preformatted progress message.
82    #[inline]
83    fn log_line(&self, message: &str) {
84        log::log!(target: self.target.as_str(), self.level, "{message}");
85    }
86}
87
88impl Default for LoggerProgressReporter {
89    /// Creates a logger reporter with the default target.
90    ///
91    /// # Returns
92    ///
93    /// A logger-backed reporter at [`log::Level::Info`].
94    #[inline]
95    fn default() -> Self {
96        Self::new("qubit_progress")
97    }
98}
99
100impl ProgressReporter for LoggerProgressReporter {
101    /// Logs one progress event.
102    ///
103    /// # Parameters
104    ///
105    /// * `event` - Progress event to log.
106    #[inline]
107    fn report(&self, event: &ProgressEvent) {
108        self.log_line(&format_event(event));
109    }
110}
111
112/// Formats one progress event for log output.
113///
114/// # Parameters
115///
116/// * `event` - Event to format.
117///
118/// # Returns
119///
120/// A single-line log message.
121fn format_event(event: &ProgressEvent) -> String {
122    let counters = event.counters();
123    let progress = match counters.progress_percent() {
124        Some(percent) => format!("{percent:.2}%"),
125        None => "unknown".to_owned(),
126    };
127    match event.stage() {
128        Some(stage) => format!(
129            "progress phase={}, stage={}, completed={}, total={:?}, active={}, failed={}, progress={}",
130            event.phase(),
131            stage.name(),
132            counters.completed_count(),
133            counters.total_count(),
134            counters.active_count(),
135            counters.failed_count(),
136            progress,
137        ),
138        None => format!(
139            "progress phase={}, completed={}, total={:?}, active={}, failed={}, progress={}",
140            event.phase(),
141            counters.completed_count(),
142            counters.total_count(),
143            counters.active_count(),
144            counters.failed_count(),
145            progress,
146        ),
147    }
148}