use crate::store::{IntoStorageReport, StorageError};
use amethystate_core::failure::one_line;
use amethystate_core::path::{StorePath, StorePathError};
use error_stack::Report;
use std::fmt;
use std::sync::Arc;
#[derive(Debug)]
pub enum RunStep {
NothingProvided {
under: Arc<str>,
wanted: &'static str,
on_offer: Arc<str>,
},
WillNotRead {
under: Arc<str>,
entry: Arc<str>,
wanted: &'static str,
why: Report<StorageError>,
},
WillNotEncode {
under: Arc<str>,
entry: Arc<str>,
wanted: &'static str,
why: Report<StorageError>,
},
NotAPath(StorePathError),
Refused(crate::MigrationError),
Store(Report<StorageError>),
}
impl RunStep {
pub fn from_store(under: &StorePath, entry: &str, why: Report<StorageError>) -> Self {
match crate::store::will_not_read(&why) {
true => Self::WillNotRead {
under: Arc::from(under.to_string()),
entry: Arc::from(entry),
wanted: "",
why,
},
false => Self::Store(why),
}
}
pub fn reading<T>(under: &StorePath, entry: &str, why: Report<StorageError>) -> Self {
match Self::from_store(under, entry, why) {
Self::WillNotRead {
under, entry, why, ..
} => Self::WillNotRead {
under,
entry,
wanted: std::any::type_name::<T>(),
why,
},
other => other,
}
}
pub fn writing<T>(under: &StorePath, entry: &str, why: Report<StorageError>) -> Self {
match crate::store::will_not_read(&why) {
true => Self::WillNotEncode {
under: Arc::from(under.to_string()),
entry: Arc::from(entry),
wanted: std::any::type_name::<T>(),
why,
},
false => Self::Store(why),
}
}
}
impl fmt::Display for RunStep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::NothingProvided {
under,
wanted,
on_offer,
} => write!(
f,
"the step under `{under}` asked for a {wanted} and nothing provided one; {on_offer}"
),
Self::WillNotRead {
under,
entry,
wanted,
why,
} => write!(
f,
"`{entry}` under `{under}` will not read as a {wanted}: {}",
one_line(why)
),
Self::WillNotEncode {
under,
entry,
wanted,
why,
} => write!(
f,
"the {wanted} the step wrote to `{entry}` under `{under}` will not encode: {}",
one_line(why)
),
Self::NotAPath(why) => write!(f, "the step named no path to work at: {why}"),
Self::Refused(why) => write!(f, "the step turned the data down: {why}"),
Self::Store(why) => write!(f, "{}", one_line(why)),
}
}
}
impl std::error::Error for RunStep {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::NotAPath(why) => Some(why),
Self::Refused(why) => Some(why),
Self::Store(why) | Self::WillNotRead { why, .. } | Self::WillNotEncode { why, .. } => {
Some(why.current_context())
}
Self::NothingProvided { .. } => None,
}
}
}
impl From<StorePathError> for RunStep {
fn from(why: StorePathError) -> Self {
Self::NotAPath(why)
}
}
impl From<Report<StorageError>> for RunStep {
fn from(why: Report<StorageError>) -> Self {
Self::Store(why)
}
}
impl From<crate::MigrationError> for RunStep {
fn from(why: crate::MigrationError) -> Self {
Self::Refused(why)
}
}
impl From<RunStep> for Report<StorageError> {
fn from(why: RunStep) -> Self {
match why {
RunStep::Store(report)
| RunStep::WillNotRead { why: report, .. }
| RunStep::WillNotEncode { why: report, .. } => report,
RunStep::NotAPath(why) => Report::new(why).change_context(StorageError::Path),
RunStep::Refused(why) => why.into_report(),
RunStep::NothingProvided {
under,
wanted,
on_offer,
} => Report::new(StorageError::Migrate)
.attach(crate::store::facts::Migrating(under.to_string()))
.attach(format!("no value provided for {wanted}"))
.attach(on_offer.to_string())
.attach("StoreBuilder::provide hands a value to every migration step"),
}
}
}
pub type StepResult<T> = Result<T, RunStep>;