use std::path::PathBuf;
use crate::error::{Error, Result};
use crate::types::ExtensionName;
#[derive(Debug, Default)]
pub struct LoadReport {
loaded: Vec<ExtensionName>,
failed: Vec<(PathBuf, Error)>,
}
impl LoadReport {
pub(crate) fn record_loaded(&mut self, name: ExtensionName) {
self.loaded.push(name);
}
pub(crate) fn record_failed(&mut self, dir: PathBuf, error: Error) {
self.failed.push((dir, error));
}
#[must_use]
pub fn loaded(&self) -> &[ExtensionName] {
&self.loaded
}
#[must_use]
pub fn failed(&self) -> &[(PathBuf, Error)] {
&self.failed
}
#[must_use]
pub const fn is_clean(&self) -> bool {
self.failed.is_empty()
}
}
#[derive(Debug)]
pub struct Dispatch {
name: ExtensionName,
result: Result<Option<serde_json::Value>>,
}
impl Dispatch {
pub(crate) const fn new(
name: ExtensionName,
result: Result<Option<serde_json::Value>>,
) -> Self {
Self { name, result }
}
#[must_use]
pub const fn name(&self) -> &ExtensionName {
&self.name
}
#[must_use = "check the handler's outcome rather than discarding it"]
pub const fn result(&self) -> &Result<Option<serde_json::Value>> {
&self.result
}
pub fn into_result(self) -> Result<Option<serde_json::Value>> {
self.result
}
}
#[cfg(test)]
mod tests {
#![expect(
clippy::unwrap_used,
reason = "tests unwrap known-valid fixtures; a panic is the intended failure signal"
)]
use std::path::PathBuf;
use serde_json::json;
use super::{Dispatch, LoadReport};
use crate::error::Error;
use crate::types::ExtensionName;
#[test]
fn is_clean_on_an_empty_report() {
let report = LoadReport::default();
assert!(report.is_clean());
assert!(report.loaded().is_empty());
assert!(report.failed().is_empty());
}
#[test]
fn record_failed_flips_is_clean() {
let mut report = LoadReport::default();
report.record_failed(
PathBuf::from("/extensions/broken"),
Error::ManifestVariable {
name: "MISSING".to_owned(),
},
);
assert!(!report.is_clean());
assert_eq!(report.failed().len(), 1);
assert_eq!(report.failed()[0].0, PathBuf::from("/extensions/broken"));
}
#[test]
fn dispatch_into_result_round_trips_an_ok_value() {
let name = ExtensionName::new("journal-indexer").unwrap();
let dispatch = Dispatch::new(name, Ok(Some(json!(1))));
assert_eq!(dispatch.into_result().unwrap(), Some(json!(1)));
}
#[test]
fn result_on_an_err_exposes_the_error_message() {
let name = ExtensionName::new("journal-indexer").unwrap();
let dispatch = Dispatch::new(
name,
Err(Error::ManifestVariable {
name: "MISSING".to_owned(),
}),
);
let err = dispatch.result().as_ref().unwrap_err();
assert!(err.to_string().contains("MISSING"), "{err}");
}
}