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