1use std::{fmt::Debug, marker::PhantomData};
2
3use futures::{StreamExt, TryStreamExt};
4use peace_cmd_ctx::{CmdCtxMpsf, CmdCtxMpsfFields, CmdCtxSpsf, CmdCtxTypes};
5use peace_cmd_model::CmdOutcome;
6use peace_cmd_rt::{CmdBlockWrapper, CmdExecution, CmdExecutionBuilder};
7use peace_flow_rt::Flow;
8use peace_item_model::ItemId;
9use peace_params::ParamsSpecs;
10use peace_profile_model::Profile;
11use peace_resource_rt::{
12 internal::StateDiffsMut,
13 resources::ts::SetUp,
14 states::{
15 ts::{CurrentStored, GoalStored},
16 StateDiffs,
17 },
18 type_reg::untagged::{BoxDtDisplay, TypeMap},
19 Resources,
20};
21use peace_rt_model::Error;
22
23use crate::cmd_blocks::{
24 DiffCmdBlock, DiffCmdBlockStatesTsExt, StatesCurrentReadCmdBlock, StatesDiscoverCmdBlock,
25 StatesGoalReadCmdBlock,
26};
27
28pub use self::{diff_info_spec::DiffInfoSpec, diff_state_spec::DiffStateSpec};
29
30mod diff_info_spec;
31mod diff_state_spec;
32
33pub struct DiffCmd<CmdCtxTypesT, Scope>(PhantomData<(CmdCtxTypesT, Scope)>);
34
35impl<CmdCtxTypesT, Scope> Debug for DiffCmd<CmdCtxTypesT, Scope> {
36 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37 f.debug_tuple("DiffCmd").field(&self.0).finish()
38 }
39}
40
41impl<'ctx, CmdCtxTypesT> DiffCmd<CmdCtxTypesT, CmdCtxSpsf<'ctx, CmdCtxTypesT>>
42where
43 CmdCtxTypesT: CmdCtxTypes,
44{
45 pub async fn diff_stored(
60 cmd_ctx: &mut CmdCtxSpsf<'ctx, CmdCtxTypesT>,
61 ) -> Result<
62 CmdOutcome<StateDiffs, <CmdCtxTypesT as CmdCtxTypes>::AppError>,
63 <CmdCtxTypesT as CmdCtxTypes>::AppError,
64 > {
65 Self::diff::<CurrentStored, GoalStored>(cmd_ctx).await
66 }
67
68 pub async fn diff<StatesTs0, StatesTs1>(
79 cmd_ctx: &mut CmdCtxSpsf<'ctx, CmdCtxTypesT>,
80 ) -> Result<
81 CmdOutcome<StateDiffs, <CmdCtxTypesT as CmdCtxTypes>::AppError>,
82 <CmdCtxTypesT as CmdCtxTypes>::AppError,
83 >
84 where
85 StatesTs0: Debug + DiffCmdBlockStatesTsExt + Send + Sync + Unpin + 'static,
86 StatesTs1: Debug + DiffCmdBlockStatesTsExt + Send + Sync + Unpin + 'static,
87 {
88 let mut cmd_execution_builder = CmdExecution::<StateDiffs, _>::builder();
89 cmd_execution_builder = Self::states_fetch_cmd_block_append(
90 cmd_execution_builder,
91 StatesTs0::diff_state_spec(),
92 );
93 cmd_execution_builder = Self::states_fetch_cmd_block_append(
94 cmd_execution_builder,
95 StatesTs1::diff_state_spec(),
96 );
97
98 cmd_execution_builder = cmd_execution_builder.with_cmd_block(CmdBlockWrapper::new(
99 DiffCmdBlock::<_, StatesTs0, StatesTs1>::new(),
100 |_state_diffs_ts0_and_ts1| StateDiffs::new(),
101 ));
102
103 #[cfg(feature = "output_progress")]
104 let cmd_execution_builder = cmd_execution_builder.with_progress_render_enabled(false);
105
106 cmd_execution_builder.build().exec(cmd_ctx).await
107 }
108
109 fn states_fetch_cmd_block_append(
110 cmd_execution_builder: CmdExecutionBuilder<'ctx, StateDiffs, CmdCtxTypesT>,
111 diff_state_spec: DiffStateSpec,
112 ) -> CmdExecutionBuilder<'ctx, StateDiffs, CmdCtxTypesT> {
113 match diff_state_spec {
114 DiffStateSpec::Current => cmd_execution_builder.with_cmd_block(CmdBlockWrapper::new(
115 StatesDiscoverCmdBlock::current(),
116 |_states_current_mut| StateDiffs::new(),
117 )),
118 DiffStateSpec::CurrentStored => cmd_execution_builder.with_cmd_block(
119 CmdBlockWrapper::new(StatesCurrentReadCmdBlock::new(), |_| StateDiffs::new()),
120 ),
121 DiffStateSpec::Goal => cmd_execution_builder
122 .with_cmd_block(CmdBlockWrapper::new(StatesDiscoverCmdBlock::goal(), |_| {
123 StateDiffs::new()
124 })),
125 DiffStateSpec::GoalStored => {
126 cmd_execution_builder
127 .with_cmd_block(CmdBlockWrapper::new(StatesGoalReadCmdBlock::new(), |_| {
128 StateDiffs::new()
129 }))
130 }
131 }
132 }
133}
134
135impl<'ctx, CmdCtxTypesT> DiffCmd<CmdCtxTypesT, CmdCtxMpsf<'ctx, CmdCtxTypesT>>
136where
137 CmdCtxTypesT: CmdCtxTypes,
138{
139 pub async fn diff_current_stored(
148 cmd_ctx: &mut CmdCtxMpsf<'ctx, CmdCtxTypesT>,
149 profile_a: &Profile,
150 profile_b: &Profile,
151 ) -> Result<StateDiffs, <CmdCtxTypesT as CmdCtxTypes>::AppError> {
152 let CmdCtxMpsfFields {
153 flow,
154 profiles,
155 profile_to_params_specs,
156 profile_to_states_current_stored,
157 resources,
158 ..
159 } = cmd_ctx.fields();
160
161 let params_specs = profile_to_params_specs
162 .get(profile_a)
163 .or_else(|| profile_to_params_specs.get(profile_b));
164 let params_specs = if let Some(params_specs) = params_specs {
165 params_specs
166 } else {
167 Err(Error::ParamsSpecsNotDefinedForDiff {
168 profile_a: profile_a.clone(),
169 profile_b: profile_b.clone(),
170 })?
171 };
172 let states_a = profile_to_states_current_stored
173 .get(profile_a)
174 .ok_or_else(|| {
175 let profile = profile_a.clone();
176 let profiles_in_scope = profiles.to_vec();
177 Error::ProfileNotInScope {
178 profile,
179 profiles_in_scope,
180 }
181 })?
182 .as_ref()
183 .ok_or_else(|| {
184 let profile = profile_a.clone();
185 Error::ProfileStatesCurrentNotDiscovered { profile }
186 })?;
187 let states_b = profile_to_states_current_stored
188 .get(profile_b)
189 .ok_or_else(|| {
190 let profile = profile_b.clone();
191 let profiles_in_scope = profiles.to_vec();
192 Error::ProfileNotInScope {
193 profile,
194 profiles_in_scope,
195 }
196 })?
197 .as_ref()
198 .ok_or_else(|| {
199 let profile = profile_b.clone();
200 Error::ProfileStatesCurrentNotDiscovered { profile }
201 })?;
202
203 Self::diff_any(flow, params_specs, resources, states_a, states_b).await
204 }
205}
206
207impl<CmdCtxTypesT, Scope> DiffCmd<CmdCtxTypesT, Scope>
208where
209 CmdCtxTypesT: CmdCtxTypes,
210{
211 pub async fn diff_any(
220 flow: &Flow<<CmdCtxTypesT as CmdCtxTypes>::AppError>,
221 params_specs: &ParamsSpecs,
222 resources: &Resources<SetUp>,
223 states_a: &TypeMap<ItemId, BoxDtDisplay>,
224 states_b: &TypeMap<ItemId, BoxDtDisplay>,
225 ) -> Result<StateDiffs, <CmdCtxTypesT as CmdCtxTypes>::AppError> {
226 let state_diffs = {
227 let state_diffs_mut = flow
228 .graph()
229 .stream()
230 .map(Result::<_, <CmdCtxTypesT as CmdCtxTypes>::AppError>::Ok)
231 .try_filter_map(|item| async move {
232 let state_diff_opt = item
233 .state_diff_exec(params_specs, resources, states_a, states_b)
234 .await?;
235
236 Ok(state_diff_opt.map(|state_diff| (item.id().clone(), state_diff)))
237 })
238 .try_collect::<StateDiffsMut>()
239 .await?;
240
241 StateDiffs::from(state_diffs_mut)
242 };
243
244 Ok(state_diffs)
245 }
246}
247
248impl<CmdCtxTypesT, Scope> Default for DiffCmd<CmdCtxTypesT, Scope> {
249 fn default() -> Self {
250 Self(PhantomData)
251 }
252}