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}