use std::{
fmt::{self, Debug},
marker::PhantomData,
ops::{Deref, DerefMut},
};
use fn_graph::{DataAccess, DataAccessDyn, TypeIds};
use peace_cfg::{async_trait, ItemSpec, ItemSpecId, OpCheckStatus, OpCtx, TryFnSpec};
use peace_data::Data;
use peace_resources::{
resources::ts::{
Empty, SetUp, WithStatesCurrent, WithStatesCurrentAndDesired, WithStatesCurrentDiffs,
WithStatesSavedAndDesired,
},
states::{self, States, StatesCurrent, StatesDesired},
type_reg::untagged::BoxDtDisplay,
Resources,
};
use serde::{de::DeserializeOwned, Serialize};
use crate::{
outcomes::{ItemEnsure, ItemEnsureBoxed, ItemEnsurePartial, ItemEnsurePartialBoxed},
ItemSpecRt, StatesTypeRegs,
};
#[allow(clippy::type_complexity)]
pub struct ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>(
IS,
PhantomData<(
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
)>,
);
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug
+ ItemSpec<
State = State,
StateDiff = StateDiff,
StateCurrentFnSpec = StateCurrentFnSpec,
StateDesiredFnSpec = StateDesiredFnSpec,
StateDiffFnSpec = StateDiffFnSpec,
EnsureOpSpec = EnsureOpSpec,
CleanOpSpec = CleanOpSpec,
> + Send
+ Sync,
E: Debug
+ Send
+ Sync
+ std::error::Error
+ From<<IS as ItemSpec>::Error>
+ From<crate::Error>
+ 'static,
State: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateDiff: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateCurrentFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDesiredFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDiffFnSpec: Debug
+ peace_cfg::StateDiffFnSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
EnsureOpSpec: Debug
+ peace_cfg::EnsureOpSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
CleanOpSpec: Debug
+ peace_cfg::CleanOpSpec<Error = <IS as ItemSpec>::Error, State = State>
+ Send
+ Sync,
{
async fn state_current_try_exec<ResourcesTs>(
&self,
resources: &Resources<ResourcesTs>,
) -> Result<Option<State>, E> {
let state_current = {
let data = <<StateCurrentFnSpec as peace_cfg::TryFnSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<StateCurrentFnSpec as TryFnSpec>::try_exec(data).await?
};
Ok(state_current)
}
async fn state_current_exec<ResourcesTs>(
&self,
resources: &Resources<ResourcesTs>,
) -> Result<State, E> {
let state_current = {
let data = <<StateCurrentFnSpec as peace_cfg::TryFnSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<StateCurrentFnSpec as TryFnSpec>::exec(data).await?
};
Ok(state_current)
}
async fn state_desired_try_exec(
&self,
resources: &Resources<SetUp>,
) -> Result<Option<State>, E> {
let data = <<StateDesiredFnSpec as peace_cfg::TryFnSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
let state_desired = <StateDesiredFnSpec as peace_cfg::TryFnSpec>::try_exec(data).await?;
Ok(state_desired)
}
async fn state_desired_exec(&self, resources: &Resources<SetUp>) -> Result<State, E> {
let data = <<StateDesiredFnSpec as peace_cfg::TryFnSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
let state_desired = <StateDesiredFnSpec as peace_cfg::TryFnSpec>::exec(data).await?;
Ok(state_desired)
}
async fn state_diff_exec<ResourcesTs, StatesTs>(
&self,
resources: &Resources<ResourcesTs>,
) -> Result<StateDiff, E>
where
StatesTs: Debug + Send + Sync + 'static,
{
let state_diff: StateDiff = {
let item_spec_id = <IS as ItemSpec>::id(self);
let states_base = resources.borrow::<States<StatesTs>>();
let state_base = states_base.get::<State, _>(item_spec_id);
let states_desired = resources.borrow::<StatesDesired>();
let state_desired = states_desired.get::<State, _>(item_spec_id);
if let (Some(state_base), Some(state_desired)) = (state_base, state_desired) {
self.state_diff_exec_with(resources, state_base, state_desired)
.await?
} else {
panic!(
"`ItemSpecWrapper::state_diff_exec<{StatesTs}>` must be called after \
`States<{StatesTs}>` and `StatesDesired` are populated, e.g. using `StatesSavedReadCmd` and \
`StatesDesiredDiscoverCmd`.",
StatesTs = std::any::type_name::<StatesTs>()
);
}
};
Ok(state_diff)
}
async fn state_diff_exec_with<ResourcesTs>(
&self,
resources: &Resources<ResourcesTs>,
state_base: &State,
state_desired: &State,
) -> Result<StateDiff, E> {
let state_diff: StateDiff = {
let data = <<StateDiffFnSpec as peace_cfg::StateDiffFnSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<StateDiffFnSpec as peace_cfg::StateDiffFnSpec>::exec(data, state_base, state_desired)
.await
.map_err(Into::<E>::into)?
};
Ok(state_diff)
}
async fn ensure_op_check<ResourcesTs>(
&self,
resources: &Resources<ResourcesTs>,
state_current: &State,
state_desired: &State,
state_diff: &StateDiff,
) -> Result<OpCheckStatus, E> {
let data = <<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<EnsureOpSpec as peace_cfg::EnsureOpSpec>::check(
data,
state_current,
state_desired,
state_diff,
)
.await
.map_err(Into::<E>::into)
}
async fn ensure_op_exec_dry<ResourcesTs>(
&self,
op_ctx: OpCtx<'_>,
resources: &Resources<ResourcesTs>,
state_current: &State,
state_desired: &State,
state_diff: &StateDiff,
) -> Result<State, E> {
let data = <<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<EnsureOpSpec as peace_cfg::EnsureOpSpec>::exec_dry(
op_ctx,
data,
state_current,
state_desired,
state_diff,
)
.await
.map_err(Into::<E>::into)
}
async fn ensure_op_exec<ResourcesTs>(
&self,
op_ctx: OpCtx<'_>,
resources: &Resources<ResourcesTs>,
state_current: &State,
state_desired: &State,
state_diff: &StateDiff,
) -> Result<State, E> {
let data = <<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
<EnsureOpSpec as peace_cfg::EnsureOpSpec>::exec(
op_ctx,
data,
state_current,
state_desired,
state_diff,
)
.await
.map_err(Into::<E>::into)
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> Debug
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> Deref
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
{
type Target = IS;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> DerefMut
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> From<IS>
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug
+ ItemSpec<
State = State,
StateDiff = StateDiff,
StateCurrentFnSpec = StateCurrentFnSpec,
StateDesiredFnSpec = StateDesiredFnSpec,
StateDiffFnSpec = StateDiffFnSpec,
EnsureOpSpec = EnsureOpSpec,
CleanOpSpec = CleanOpSpec,
> + Send
+ Sync,
E: Debug + Send + Sync + std::error::Error + From<<IS as ItemSpec>::Error> + 'static,
State: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateDiff: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateCurrentFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDesiredFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDiffFnSpec: Debug
+ peace_cfg::StateDiffFnSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
EnsureOpSpec: Debug
+ peace_cfg::EnsureOpSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
CleanOpSpec: Debug
+ peace_cfg::CleanOpSpec<Error = <IS as ItemSpec>::Error, State = State>
+ Send
+ Sync,
{
fn from(item_spec: IS) -> Self {
Self(item_spec, PhantomData)
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> DataAccess
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug
+ ItemSpec<
State = State,
StateDiff = StateDiff,
StateCurrentFnSpec = StateCurrentFnSpec,
StateDesiredFnSpec = StateDesiredFnSpec,
StateDiffFnSpec = StateDiffFnSpec,
EnsureOpSpec = EnsureOpSpec,
CleanOpSpec = CleanOpSpec,
> + Send
+ Sync,
E: Debug + Send + Sync + std::error::Error + From<<IS as ItemSpec>::Error> + 'static,
State: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateDiff: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateCurrentFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDesiredFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDiffFnSpec: Debug
+ peace_cfg::StateDiffFnSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
EnsureOpSpec: Debug
+ peace_cfg::EnsureOpSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
CleanOpSpec: Debug
+ peace_cfg::CleanOpSpec<Error = <IS as ItemSpec>::Error, State = State>
+ Send
+ Sync,
{
fn borrows() -> TypeIds {
<<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as DataAccess>::borrows()
}
fn borrow_muts() -> TypeIds {
<<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as DataAccess>::borrow_muts()
}
}
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> DataAccessDyn
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug
+ ItemSpec<
State = State,
StateDiff = StateDiff,
StateCurrentFnSpec = StateCurrentFnSpec,
StateDesiredFnSpec = StateDesiredFnSpec,
StateDiffFnSpec = StateDiffFnSpec,
EnsureOpSpec = EnsureOpSpec,
CleanOpSpec = CleanOpSpec,
> + Send
+ Sync,
E: Debug + Send + Sync + std::error::Error + From<<IS as ItemSpec>::Error> + 'static,
State: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateDiff: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateCurrentFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDesiredFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDiffFnSpec: Debug
+ peace_cfg::StateDiffFnSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
EnsureOpSpec: Debug
+ peace_cfg::EnsureOpSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
CleanOpSpec: Debug
+ peace_cfg::CleanOpSpec<Error = <IS as ItemSpec>::Error, State = State>
+ Send
+ Sync,
{
fn borrows(&self) -> TypeIds {
<<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as DataAccess>::borrows()
}
fn borrow_muts(&self) -> TypeIds {
<<EnsureOpSpec as peace_cfg::EnsureOpSpec>::Data<'_> as DataAccess>::borrow_muts()
}
}
#[async_trait(?Send)]
impl<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
> ItemSpecRt<E>
for ItemSpecWrapper<
IS,
E,
State,
StateDiff,
StateCurrentFnSpec,
StateDesiredFnSpec,
StateDiffFnSpec,
EnsureOpSpec,
CleanOpSpec,
>
where
IS: Debug
+ ItemSpec<
State = State,
StateDiff = StateDiff,
StateCurrentFnSpec = StateCurrentFnSpec,
StateDesiredFnSpec = StateDesiredFnSpec,
StateDiffFnSpec = StateDiffFnSpec,
EnsureOpSpec = EnsureOpSpec,
CleanOpSpec = CleanOpSpec,
> + Send
+ Sync,
E: Debug
+ Send
+ Sync
+ std::error::Error
+ From<<IS as ItemSpec>::Error>
+ From<crate::Error>
+ 'static,
State: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateDiff: Clone + Debug + fmt::Display + Serialize + DeserializeOwned + Send + Sync + 'static,
StateCurrentFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDesiredFnSpec:
Debug + TryFnSpec<Error = <IS as ItemSpec>::Error, Output = State> + Send + Sync,
StateDiffFnSpec: Debug
+ peace_cfg::StateDiffFnSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
EnsureOpSpec: Debug
+ peace_cfg::EnsureOpSpec<
Error = <IS as ItemSpec>::Error,
State = State,
StateDiff = StateDiff,
> + Send
+ Sync,
CleanOpSpec: Debug
+ peace_cfg::CleanOpSpec<Error = <IS as ItemSpec>::Error, State = State>
+ Send
+ Sync,
{
fn id(&self) -> &ItemSpecId {
<IS as ItemSpec>::id(self)
}
async fn setup(&self, resources: &mut Resources<Empty>) -> Result<(), E> {
<IS as ItemSpec>::setup(self, resources)
.await
.map_err(Into::<E>::into)
}
fn state_register(&self, states_type_regs: &mut StatesTypeRegs) {
states_type_regs
.states_current_type_reg_mut()
.register::<State>(<IS as ItemSpec>::id(self).clone());
states_type_regs
.states_desired_type_reg_mut()
.register::<State>(<IS as ItemSpec>::id(self).clone());
}
async fn state_current_try_exec(
&self,
resources: &Resources<SetUp>,
) -> Result<Option<BoxDtDisplay>, E> {
self.state_current_try_exec(resources)
.await
.map(|state_current| state_current.map(BoxDtDisplay::new))
.map_err(Into::<E>::into)
}
async fn state_current_exec(&self, resources: &Resources<SetUp>) -> Result<BoxDtDisplay, E> {
self.state_current_exec(resources)
.await
.map(BoxDtDisplay::new)
.map_err(Into::<E>::into)
}
async fn state_ensured_exec(
&self,
resources: &Resources<WithStatesCurrentDiffs>,
) -> Result<BoxDtDisplay, E> {
self.state_current_exec(resources)
.await
.map(BoxDtDisplay::new)
.map_err(Into::<E>::into)
}
async fn state_cleaned_try_exec(
&self,
resources: &Resources<WithStatesCurrent>,
) -> Result<Option<BoxDtDisplay>, E> {
self.state_current_try_exec(resources)
.await
.map(|state_current| state_current.map(BoxDtDisplay::new))
.map_err(Into::<E>::into)
}
async fn state_desired_try_exec(
&self,
resources: &Resources<SetUp>,
) -> Result<Option<BoxDtDisplay>, E> {
self.state_desired_try_exec(resources)
.await
.map(|state_desired| state_desired.map(BoxDtDisplay::new))
.map_err(Into::<E>::into)
}
async fn state_desired_exec(&self, resources: &Resources<SetUp>) -> Result<BoxDtDisplay, E> {
self.state_desired_exec(resources)
.await
.map(BoxDtDisplay::new)
.map_err(Into::<E>::into)
}
async fn state_diff_exec_with_states_saved(
&self,
resources: &Resources<WithStatesSavedAndDesired>,
) -> Result<BoxDtDisplay, E> {
self.state_diff_exec::<_, states::ts::Saved>(resources)
.await
.map(BoxDtDisplay::new)
.map_err(Into::<E>::into)
}
async fn state_diff_exec_with_states_current(
&self,
resources: &Resources<WithStatesCurrentAndDesired>,
) -> Result<BoxDtDisplay, E> {
self.state_diff_exec::<_, states::ts::Current>(resources)
.await
.map(BoxDtDisplay::new)
.map_err(Into::<E>::into)
}
async fn ensure_prepare(
&self,
resources: &Resources<SetUp>,
) -> Result<ItemEnsureBoxed, (E, ItemEnsurePartialBoxed)> {
let mut item_ensure_partial = ItemEnsurePartial::<State, StateDiff>::new();
match self.state_current_exec(resources).await {
Ok(state_current) => item_ensure_partial.state_current = Some(state_current),
Err(error) => return Err((error, item_ensure_partial.into())),
}
match self.state_desired_exec(resources).await {
Ok(state_desired) => item_ensure_partial.state_desired = Some(state_desired),
Err(error) => return Err((error, item_ensure_partial.into())),
}
match self
.state_diff_exec_with(
resources,
item_ensure_partial
.state_current
.as_ref()
.expect("unreachable: This is set just above."),
item_ensure_partial
.state_desired
.as_ref()
.expect("unreachable: This is set just above."),
)
.await
{
Ok(state_diff) => item_ensure_partial.state_diff = Some(state_diff),
Err(error) => return Err((error, item_ensure_partial.into())),
}
let (Some(state_current), Some(state_desired), Some(state_diff)) = (
item_ensure_partial.state_current.as_ref(),
item_ensure_partial.state_desired.as_ref(),
item_ensure_partial.state_diff.as_ref(),
) else {
unreachable!("These are set just above.");
};
match self
.ensure_op_check(resources, state_current, state_desired, state_diff)
.await
{
Ok(op_check_status) => item_ensure_partial.op_check_status = Some(op_check_status),
Err(error) => return Err((error, item_ensure_partial.into())),
}
Ok(ItemEnsure::try_from((item_ensure_partial, None))
.expect("unreachable: All the fields are set above.")
.into())
}
async fn ensure_exec_dry(
&self,
op_ctx: OpCtx<'_>,
resources: &Resources<SetUp>,
item_ensure_boxed: &mut ItemEnsureBoxed,
) -> Result<(), E> {
let Some(item_ensure) =
item_ensure_boxed.as_data_type_mut().downcast_mut::<ItemEnsure<State, StateDiff>>() else {
panic!("Failed to downcast `ItemEnsureBoxed` to `{concrete_type}`.\n\
This is a bug in the Peace framework.",
concrete_type = std::any::type_name::<ItemEnsure<State, StateDiff>>())
};
let ItemEnsure {
state_saved: _,
state_current,
state_desired,
state_diff,
op_check_status,
state_ensured,
} = item_ensure;
match op_check_status {
#[cfg(not(feature = "output_progress"))]
OpCheckStatus::ExecRequired => {
let state_ensured_dry = self
.ensure_op_exec_dry(op_ctx, resources, state_current, state_desired, state_diff)
.await?;
*state_ensured = Some(state_ensured_dry);
}
#[cfg(feature = "output_progress")]
OpCheckStatus::ExecRequired { progress_limit: _ } => {
let state_ensured_dry = self
.ensure_op_exec_dry(op_ctx, resources, state_current, state_desired, state_diff)
.await?;
*state_ensured = Some(state_ensured_dry);
}
OpCheckStatus::ExecNotRequired => {}
}
Ok(())
}
async fn ensure_exec(
&self,
op_ctx: OpCtx<'_>,
resources: &Resources<SetUp>,
item_ensure_boxed: &mut ItemEnsureBoxed,
) -> Result<(), E> {
let Some(item_ensure) =
item_ensure_boxed.as_data_type_mut().downcast_mut::<ItemEnsure<State, StateDiff>>() else {
panic!("Failed to downcast `ItemEnsureBoxed` to `{concrete_type}`.\n\
This is a bug in the Peace framework.",
concrete_type = std::any::type_name::<ItemEnsure<State, StateDiff>>())
};
let ItemEnsure {
state_saved: _,
state_current,
state_desired,
state_diff,
op_check_status,
state_ensured,
} = item_ensure;
match op_check_status {
#[cfg(not(feature = "output_progress"))]
OpCheckStatus::ExecRequired => {
let state_ensured_next = self
.ensure_op_exec(op_ctx, resources, state_current, state_desired, state_diff)
.await?;
*state_ensured = Some(state_ensured_next);
}
#[cfg(feature = "output_progress")]
OpCheckStatus::ExecRequired { progress_limit: _ } => {
let state_ensured_next = self
.ensure_op_exec(op_ctx, resources, state_current, state_desired, state_diff)
.await?;
*state_ensured = Some(state_ensured_next);
}
OpCheckStatus::ExecNotRequired => {}
}
Ok(())
}
async fn clean_op_check(
&self,
resources: &Resources<WithStatesCurrent>,
) -> Result<OpCheckStatus, E> {
let op_check_status = {
let data = <<CleanOpSpec as peace_cfg::CleanOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
let item_spec_id = <IS as ItemSpec>::id(self);
let states = resources.borrow::<StatesCurrent>();
let state = states.get::<State, _>(item_spec_id);
if let Some(state) = state {
<CleanOpSpec as peace_cfg::CleanOpSpec>::check(data, state).await?
} else {
panic!(
"`ItemSpecWrapper::clean_op_check` must only be called with `StatesCurrent`, `StatesDesired`, and \
`StateDiffs` populated using `DiffCmd`."
);
}
};
Ok(op_check_status)
}
async fn clean_op_exec_dry(&self, resources: &Resources<WithStatesCurrent>) -> Result<(), E> {
let data = <<CleanOpSpec as peace_cfg::CleanOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
let item_spec_id = <IS as ItemSpec>::id(self);
let states = resources.borrow::<StatesCurrent>();
let state = states.get::<State, _>(item_spec_id);
if let Some(state) = state {
<CleanOpSpec as peace_cfg::CleanOpSpec>::exec_dry(data, state).await?;
} else {
panic!(
"`ItemSpecWrapper::clean_op_exec_dry` must only be called with `StatesCurrent` populated using `StatesCurrentDiscoverCmd`."
);
}
Ok(())
}
async fn clean_op_exec(&self, resources: &Resources<WithStatesCurrent>) -> Result<(), E> {
let data = <<CleanOpSpec as peace_cfg::CleanOpSpec>::Data<'_> as Data>::borrow(
self.id(),
resources,
);
let item_spec_id = <IS as ItemSpec>::id(self);
let states = resources.borrow::<StatesCurrent>();
let state = states.get::<State, _>(item_spec_id);
if let Some(state) = state {
<CleanOpSpec as peace_cfg::CleanOpSpec>::exec(data, state).await?;
} else {
panic!(
"`ItemSpecWrapper::clean_op_exec` must only be called with `StatesCurrent` populated using `StatesCurrentDiscoverCmd`."
);
}
Ok(())
}
}