use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex};
use crate::{ERR_POISONED_LOCK, Operation, OperationMetrics, Report};
#[derive(Debug)]
pub struct Session {
operations: Arc<Mutex<HashMap<String, Arc<Mutex<OperationMetrics>>>>>,
emit_stdout: bool,
emit_file: bool,
}
impl Session {
#[expect(
clippy::new_without_default,
reason = "to avoid ambiguity with the notion of a 'default session' that is not actually a default session"
)]
#[must_use]
pub fn new() -> Self {
Self {
operations: Arc::new(Mutex::new(HashMap::new())),
emit_stdout: true,
emit_file: true,
}
}
#[must_use]
pub fn no_stdout(mut self) -> Self {
self.emit_stdout = false;
self
}
#[must_use]
pub fn no_file(mut self) -> Self {
self.emit_file = false;
self
}
pub fn operation(&self, name: impl Into<String>) -> Operation {
let name = name.into();
let operation_data = {
let mut operations = self.operations.lock().expect(ERR_POISONED_LOCK);
Arc::clone(
operations
.entry(name.clone())
.or_insert_with(|| Arc::new(Mutex::new(OperationMetrics::default()))),
)
};
Operation::new(name, operation_data)
}
#[must_use]
pub fn to_report(&self) -> Report {
let operations = self.operations.lock().expect(ERR_POISONED_LOCK);
let operation_data: HashMap<String, OperationMetrics> = operations
.iter()
.map(|(name, data_ref)| {
(
name.clone(),
data_ref.lock().expect(ERR_POISONED_LOCK).clone(),
)
})
.collect();
Report::from_operation_data(&operation_data)
}
#[must_use]
pub fn is_empty(&self) -> bool {
let operations = self.operations.lock().expect(ERR_POISONED_LOCK);
operations.is_empty()
|| operations
.values()
.all(|op| op.lock().expect(ERR_POISONED_LOCK).total_iterations == 0)
}
}
impl Drop for Session {
fn drop(&mut self) {
if std::thread::panicking() {
return;
}
if !(self.emit_stdout || self.emit_file) {
return;
}
if self.is_empty() {
return;
}
let report = self.to_report();
if self.emit_stdout {
report.print_to_stdout();
}
if self.emit_file {
report.write_to_target();
}
}
}
impl fmt::Display for Session {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_report())
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use std::panic::{RefUnwindSafe, UnwindSafe};
use super::*;
static_assertions::assert_impl_all!(Session: Send, Sync);
static_assertions::assert_impl_all!(Session: UnwindSafe, RefUnwindSafe);
}