1#![allow(clippy::result_large_err)]
2
3use core::fmt;
4use std::{any::Any, collections::VecDeque, pin::Pin, sync::Arc};
5
6use zng_app::{event::app_local, update::UPDATES};
7use zng_clone_move::clmv;
8use zng_ext_config::RawConfigValue;
9use zng_task::{Progress, parking_lot::Mutex};
10use zng_txt::Txt;
11use zng_var::{ResponderVar, ResponseVar, Var, VarEq, VarValue, const_var, response_var, var};
12
13use crate::task::{SetupTask, SetupTaskError, SetupTaskType, TaskTypeId};
14
15pub struct SETUP;
23
24impl SETUP {
25 pub fn register_task_type<T: SetupTask>(&self) {
27 self.register_task_type_impl(SetupTaskType::new::<T>());
28 }
29 fn register_task_type_impl(&self, t: SetupTaskType) {
30 UPDATES.once_update("register_task_type", move || {
31 let mut sv = SETUP_SV.write();
32 let id = (t.task_type_id)();
33 if let Some(e) = sv.task_types.iter_mut().find(|t| (t.task_type_id)() == id) {
34 *e = t;
35 } else {
36 sv.task_types.push(t);
37 }
38 });
39 }
40
41 pub fn install(&self, config: InstallConfig, update: Option<UninstallConfig>) -> ResponseVar<Result<UninstallConfig, SetupError>> {
53 let (r, rsp) = response_var();
54 UPDATES.once_update("install", move || {
55 SETUP_SV.write().run(async move { install(config, update).await }, r);
56 });
57 rsp
58 }
59
60 pub fn prepare_install(
74 &self,
75 config: InstallConfig,
76 update: Option<UninstallConfig>,
77 ) -> ResponseVar<Result<PreparedInstallConfig, SetupError>> {
78 let (r, rsp) = response_var();
79 UPDATES.once_update("prepare_install", move || {
80 SETUP_SV.write().run(async move { prepare_install(config, update).await }, r);
81 });
82 rsp
83 }
84
85 pub fn cancel_prepared(&self, config: PreparedInstallConfig) -> ResponseVar<Result<(), SetupError>> {
92 let (r, rsp) = response_var();
93 UPDATES.once_update("cancel_prepared", move || {
94 SETUP_SV.write().run(async move { cancel_prepared(config).await }, r);
95 });
96 rsp
97 }
98
99 pub fn commit_install(&self, config: PreparedInstallConfig) -> ResponseVar<Result<UninstallConfig, SetupError>> {
111 let (r, rsp) = response_var();
112 UPDATES.once_update("commit_install", move || {
113 SETUP_SV.write().run(async move { commit_install(config).await }, r);
114 });
115 rsp
116 }
117
118 pub fn uninstall(&self, config: UninstallConfig) -> ResponseVar<Result<(), SetupError>> {
127 let (r, rsp) = response_var();
128 UPDATES.once_update("uninstall", move || {
129 SETUP_SV.write().run(async move { uninstall(config).await }, r);
130 });
131 rsp
132 }
133
134 pub fn validate_uninstall(&self, config: UninstallConfig) -> ResponseVar<Result<UninstallConfig, SetupError>> {
143 let (r, rsp) = response_var();
144 UPDATES.once_update("validate_uninstall", move || {
145 SETUP_SV.write().run(async move { validate_uninstall(config).await }, r);
146 });
147 rsp
148 }
149
150 pub fn status(&self) -> Var<SetupStatus> {
152 SETUP_SV.read().status.read_only()
153 }
154}
155
156type SetupOp = Pin<Box<dyn Future<Output = ()> + Send>>;
157
158struct Setup {
159 task_types: Vec<SetupTaskType>,
160 queue: Mutex<VecDeque<SetupOp>>, status: Var<SetupStatus>,
162 cancel: Var<bool>,
163}
164app_local! {
165 static SETUP_SV: Setup = Setup {
166 task_types: vec![
167 SetupTaskType::new::<crate::task::ExtractTar>(),
168 #[cfg(any(windows, target_os = "linux"))]
169 SetupTaskType::new::<crate::task::CreateShortcut>(),
170 #[cfg(windows)]
171 SetupTaskType::new::<crate::task::RegisterUninstaller>(),
172 ],
173 queue: Mutex::default(),
174 status: var(SetupStatus::Idle),
175 cancel: var(false),
176 };
177}
178
179#[derive(Default)]
183pub struct InstallConfig {
184 cfg: Vec<(SetupTaskType, Box<dyn Any + Send>)>,
185 tasks: Vec<(TaskTypeId, Txt)>,
186}
187impl InstallConfig {
188 pub fn new() -> Self {
190 Self::default()
191 }
192
193 pub fn push<T: SetupTask>(&mut self, name: impl Into<Txt>, config: T::InstallConfig) {
197 let t = SetupTaskType::new::<T>();
198 self.tasks.push(((t.task_type_id)(), name.into()));
199 self.cfg.push((t, Box::new(config)))
200 }
201
202 pub fn tasks(&self) -> &[(TaskTypeId, Txt)] {
204 &self.tasks
205 }
206
207 pub fn config<T: SetupTask>(&self, index: usize) -> Option<&T::InstallConfig> {
209 self.cfg.get(index)?.1.downcast_ref()
210 }
211}
212
213#[derive(Clone, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
218pub struct PreparedInstallConfig {
219 tasks: Vec<(TaskTypeId, Txt)>,
220 cfg: Vec<RawConfigValue>,
221}
222impl PreparedInstallConfig {
223 pub fn tasks(&self) -> &[(TaskTypeId, Txt)] {
225 &self.tasks
226 }
227}
228
229#[derive(Clone, PartialEq, Debug, serde::Serialize, serde::Deserialize)]
234pub struct UninstallConfig {
235 tasks: Vec<(TaskTypeId, Txt)>,
236 cfg: Vec<RawConfigValue>,
237}
238impl UninstallConfig {
239 pub fn tasks(&self) -> &[(TaskTypeId, Txt)] {
241 &self.tasks
242 }
243}
244
245#[cfg(feature = "save")]
246macro_rules! impl_save {
247 ($($Config:ident),+) => {$(
248
249#[cfg(feature = "save")]
250impl $Config {
251 pub fn save_blocking(&self, file: &std::path::Path) -> std::io::Result<()> {
255 save(self, file)
256 }
257
258 pub async fn save(self, file: std::path::PathBuf) -> std::io::Result<()> {
262 zng_task::wait(move || save(&self, &file)).await
263 }
264
265 pub fn load_blocking(file: &std::path::Path) -> std::io::Result<Self> {
269 load(file)
270 }
271
272 pub async fn load(file: std::path::PathBuf) -> std::io::Result<Self> {
276 zng_task::wait(move || load(&file)).await
277 }
278}
279 )+};
280}
281#[cfg(feature = "save")]
282impl_save! { UninstallConfig, PreparedInstallConfig }
283
284#[cfg(feature = "save")]
285fn save(config: &impl serde::Serialize, file: &std::path::Path) -> std::io::Result<()> {
286 let file = std::fs::File::create(file)?;
287 let mut zstd = zstd::Encoder::new(file, 22)?;
288 serde_json::to_writer(&mut zstd, config)?;
289 zstd.finish()?;
290 Ok(())
291}
292
293#[cfg(feature = "save")]
294fn load<T: serde::de::DeserializeOwned>(file: &std::path::Path) -> std::io::Result<T> {
295 let file = std::fs::File::open(file)?;
296 let zstd = zstd::Decoder::new(file)?;
298 let cfg = serde_json::from_reader(zstd)?;
299 Ok(cfg)
300}
301
302#[derive(Clone, PartialEq, Debug)]
304#[non_exhaustive]
305pub struct SetupError {
306 pub op_error: Option<SetupTaskError>,
310
311 pub task_errors: Vec<((usize, TaskTypeId, Txt), SetupTaskError)>,
315
316 pub state: SetupErrorState,
318}
319impl SetupError {
320 pub fn canceled() -> Self {
322 Self {
323 op_error: None,
324 task_errors: vec![],
325 state: SetupErrorState::Canceled,
326 }
327 }
328
329 pub fn task_errors(errors: Vec<((usize, TaskTypeId, Txt), SetupTaskError)>, state: SetupErrorState) -> Self {
335 Self {
336 op_error: None,
337 task_errors: errors,
338 state,
339 }
340 }
341
342 pub fn op_error(error: SetupTaskError, state: SetupErrorState) -> Self {
346 Self {
347 op_error: Some(error),
348 task_errors: vec![],
349 state,
350 }
351 }
352
353 pub fn corrupted_op_config(config_name: &'static str) -> Self {
355 #[derive(Debug)]
356 struct CorruptedOpConfig(&'static str);
357 impl fmt::Display for CorruptedOpConfig {
358 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
359 write!(f, "{} is corrupted", self.0)
360 }
361 }
362 impl std::error::Error for CorruptedOpConfig {}
363 Self::op_error(
364 SetupTaskError::CorruptedTaskData(Arc::new(CorruptedOpConfig(config_name))),
365 SetupErrorState::Canceled,
366 )
367 }
368}
369
370#[derive(Clone, PartialEq, Debug)]
372pub enum SetupErrorState {
373 Canceled,
378
379 PartialPrepareInstall,
385
386 PartialInstall {
396 data: UninstallConfig,
398 no_data: Vec<(usize, TaskTypeId, Txt)>,
404 },
405 PartialUninstall,
412}
413
414#[derive(Debug, PartialEq, Clone)]
416#[non_exhaustive]
417pub enum SetupStatus {
418 Idle,
420 PrepareInstall(SetupOpStatus),
422 CommitInstall(SetupOpStatus),
424 ValidateUninstall(SetupOpStatus),
426 Uninstall(SetupOpStatus),
428}
429impl SetupStatus {
430 pub fn is_idle(&self) -> bool {
432 match self {
433 SetupStatus::Idle => true,
434 SetupStatus::PrepareInstall(s)
435 | SetupStatus::CommitInstall(s)
436 | SetupStatus::ValidateUninstall(s)
437 | SetupStatus::Uninstall(s) => s.is_complete(),
438 }
439 }
440
441 pub fn op_status(&self) -> Option<&SetupOpStatus> {
443 match self {
444 SetupStatus::Idle => None,
445 SetupStatus::PrepareInstall(s)
446 | SetupStatus::CommitInstall(s)
447 | SetupStatus::ValidateUninstall(s)
448 | SetupStatus::Uninstall(s) => Some(s),
449 }
450 }
451}
452
453#[derive(Debug, PartialEq, Clone)]
455#[non_exhaustive]
456pub struct SetupOpStatus {
457 pub cancel: bool,
459 pub task: (TaskTypeId, Txt),
461 pub progress: (usize, usize),
463 pub task_progress: VarEq<Progress>,
465
466 pub errors: Vec<((usize, TaskTypeId, Txt), SetupTaskError)>,
470}
471impl SetupOpStatus {
472 pub fn is_complete(&self) -> bool {
476 self.progress.0 == self.progress.1.saturating_sub(1) && self.task_progress.with(|p| p.is_complete())
477 }
478
479 pub fn is_canceled(&self) -> bool {
483 self.cancel && self.is_complete()
484 }
485
486 pub fn is_corrupted(&self) -> bool {
490 !self.errors.is_empty() && !self.cancel && self.is_complete()
491 }
492}
493
494impl Setup {
495 fn run<R: VarValue>(
496 &mut self,
497 op: impl Future<Output = Result<R, SetupError>> + Send + 'static,
498 r: ResponderVar<Result<R, SetupError>>,
499 ) {
500 self.run_impl(Box::pin(async move {
501 let res = op.await;
502 r.respond(res);
503 }));
504 }
505 fn run_impl(&mut self, op: SetupOp) {
506 let q = self.queue.get_mut();
507 q.push_back(op);
508 if q.len() == 1 {
509 zng_task::spawn(async {
510 fn next_op() -> Option<SetupOp> {
511 let mut sv = SETUP_SV.write();
512 let op = sv.queue.get_mut().pop_front();
513 if op.is_some() {
514 sv.status.set(SetupStatus::Idle);
516 }
517 op
518 }
519 while let Some(op) = next_op() {
520 op.await;
521 }
522 });
523 }
524 }
525
526 fn task_type(&self, id: &TaskTypeId) -> Result<SetupTaskType, SetupTaskError> {
527 for t in &self.task_types {
528 if &(t.task_type_id)() == id {
529 return Ok(t.clone());
530 }
531 }
532 Err(SetupTaskError::UnknownType(id.clone()))
533 }
534}
535
536async fn install(config: InstallConfig, update: Option<UninstallConfig>) -> Result<UninstallConfig, SetupError> {
537 let config = prepare_install(config, update).await?;
538 if SETUP_SV.read().cancel.get() {
539 cancel_prepared(config).await?;
540 Err(SetupError::canceled())
541 } else {
542 commit_install(config).await
543 }
544}
545
546async fn prepare_install(config: InstallConfig, update: Option<UninstallConfig>) -> Result<PreparedInstallConfig, SetupError> {
547 let (status, cancel) = {
548 let sv = SETUP_SV.read();
549 (sv.status.clone(), sv.cancel.clone())
550 };
551
552 if let Some(u) = &update
553 && u.tasks.len() != u.cfg.len()
554 {
555 return Err(SetupError::corrupted_op_config("UninstallConfig"));
556 }
557
558 let tasks_len = config.tasks.len();
559 if tasks_len != config.cfg.len() {
560 return Err(SetupError::corrupted_op_config("InstallConfig"));
561 }
562 let mut prepared_cfg: Vec<RawConfigValue> = Vec::with_capacity(tasks_len);
563 let mut error = None;
564 for (i, (id, (task_ty, cfg))) in config.tasks.iter().zip(config.cfg).enumerate() {
565 let task_progress = var(Progress::indeterminate());
566 let task_progress_s = task_progress.read_only();
568 status.modify(clmv!(id, |a| {
569 match a.value_mut() {
570 SetupStatus::PrepareInstall(s) => {
571 s.task = id;
572 s.progress.0 = i;
573 s.task_progress = VarEq(task_progress_s);
574 }
575 _ => {
576 **a = SetupStatus::PrepareInstall(SetupOpStatus {
577 cancel: false,
578 task: id,
579 progress: (i, tasks_len),
580 task_progress: VarEq(task_progress_s),
581 errors: vec![],
582 });
583 }
584 }
585 }));
586
587 let mut uninstall_data = None;
589 if let Some(u) = &update {
590 if let Some(i) = u.cfg.len().checked_sub(i + 1)
592 && id == &u.tasks[i]
593 {
594 uninstall_data = Some(u.cfg[i].clone());
595 }
596
597 if uninstall_data.is_none() {
598 #[derive(Debug)]
599 struct TaskTypeMismatch;
600 impl fmt::Display for TaskTypeMismatch {
601 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
602 write!(f, "expected different task type")
603 }
604 }
605 impl std::error::Error for TaskTypeMismatch {}
606 error = Some((
607 (i, id.0.clone(), id.1.clone()),
608 SetupTaskError::CorruptedTaskData(Arc::new(TaskTypeMismatch)),
609 ));
610 break;
611 }
612 }
613
614 let r = (task_ty.prepare_install)(cfg, uninstall_data, task_progress.clone(), cancel.read_only()).await;
616 task_progress.set(Progress::complete());
617 match r {
618 Ok(r) => prepared_cfg.push(r),
619 Err(e) => {
620 error = Some(((i, id.0.clone(), id.1.clone()), e));
622 break;
623 }
624 }
625 if cancel.get() {
626 break;
627 }
628 }
629
630 let prepared_cfg = PreparedInstallConfig {
631 tasks: config.tasks,
632 cfg: prepared_cfg,
633 };
634
635 if let Some(e) = error {
636 status.modify(clmv!(e, |a| {
637 if let SetupStatus::PrepareInstall(s) = a.value_mut() {
639 s.errors.push(e);
640 s.task_progress = VarEq(const_var(Progress::indeterminate()));
642 s.cancel = true;
643 }
644 }));
645
646 if let Err(mut ce) = cancel_prepared(prepared_cfg).await {
648 ce.state = SetupErrorState::PartialPrepareInstall;
650 ce.task_errors.insert(0, e);
651 Err(ce)
652 } else {
653 Err(SetupError::task_errors(vec![e], SetupErrorState::Canceled))
654 }
655 } else if cancel.get() {
656 cancel_prepared(prepared_cfg).await?;
658 Err(SetupError::canceled())
659 } else {
660 status.modify(clmv!(|a| {
662 if let SetupStatus::PrepareInstall(s) = a.value_mut() {
663 s.task_progress = VarEq(const_var(Progress::complete()));
664 }
665 }));
666 Ok(prepared_cfg)
667 }
668}
669
670async fn cancel_prepared(config: PreparedInstallConfig) -> Result<(), SetupError> {
671 let status = SETUP_SV.read().status.clone();
672
673 let tasks_len = config.cfg.len();
674 if tasks_len > config.tasks.len() {
675 return Err(SetupError::corrupted_op_config("PreparedInstallConfig"));
676 }
677
678 let mut errors = vec![];
679
680 for (i, (id, cfg)) in config.tasks.into_iter().zip(config.cfg).enumerate() {
681 let task_progress = var(Progress::indeterminate());
682 let task_progress_s = task_progress.read_only();
684 status.modify(clmv!(id, |a| {
685 match a.value_mut() {
686 SetupStatus::PrepareInstall(s) => {
687 s.cancel = true;
688 s.task = id;
689 s.progress = (i, tasks_len);
690 s.task_progress = VarEq(task_progress_s);
691 }
692 _ => {
693 **a = SetupStatus::PrepareInstall(SetupOpStatus {
694 cancel: true,
695 task: id,
696 progress: (i, tasks_len),
697 task_progress: VarEq(task_progress_s),
698 errors: vec![],
699 });
700 }
701 }
702 }));
703
704 let task_ty = SETUP_SV.read().task_type(&id.0);
706 let error = match task_ty {
707 Ok(task_ty) => {
708 let r = (task_ty.cancel_install)(cfg, task_progress.clone()).await;
709 r.err()
710 }
711 Err(e) => Some(e),
712 };
713
714 if let Some(e) = error {
715 let e = ((i, id.0, id.1), e);
716 status.modify(clmv!(e, |a| {
718 if let SetupStatus::PrepareInstall(s) = a.value_mut() {
719 s.errors.push(e);
720 s.cancel = false;
721 s.task_progress = VarEq(const_var(Progress::complete()));
722 }
723 }));
724 errors.push(e);
725
726 } else {
728 task_progress.set(Progress::complete());
729 }
730 }
731
732 if errors.is_empty() {
733 Ok(())
734 } else {
735 Err(SetupError::task_errors(errors, SetupErrorState::PartialPrepareInstall))
736 }
737}
738
739async fn commit_install(config: PreparedInstallConfig) -> Result<UninstallConfig, SetupError> {
740 let status = SETUP_SV.read().status.clone();
741
742 let tasks_len = config.tasks.len();
743 if tasks_len != config.cfg.len() {
744 return Err(SetupError::corrupted_op_config("PreparedInstallConfig"));
745 }
746
747 let mut errors = vec![];
748 let mut uninstall_cfg = vec![];
749 let mut err_no_clean = vec![];
750
751 for (i, (id, cfg)) in config.tasks.iter().zip(config.cfg).enumerate() {
752 let task_progress = var(Progress::indeterminate());
753 let task_progress_s = task_progress.read_only();
755 status.modify(clmv!(id, |a| {
756 match a.value_mut() {
757 SetupStatus::CommitInstall(s) => {
758 s.task = id;
759 s.progress.0 = i;
760 s.task_progress = VarEq(task_progress_s);
761 }
762 _ => {
763 **a = SetupStatus::CommitInstall(SetupOpStatus {
764 cancel: false,
765 task: id,
766 progress: (i, tasks_len),
767 task_progress: VarEq(task_progress_s),
768 errors: vec![],
769 })
770 }
771 }
772 }));
773
774 let task_ty = SETUP_SV.read().task_type(&id.0);
776 let mut error = None;
777 match task_ty {
778 Ok(task_ty) => match (task_ty.install)(cfg, task_progress.clone()).await {
779 Ok(c) => {
780 uninstall_cfg.push(c);
781 }
782 Err(e) => {
783 error = Some(e.error);
784 if let Some(d) = e.clean_data {
785 uninstall_cfg.push(d);
786 } else {
787 err_no_clean.push((i, id.0.clone(), id.1.clone()));
788 }
789 }
790 },
791 Err(e) => error = Some(e),
792 };
793
794 if let Some(e) = error {
795 let e = ((i, id.0.clone(), id.1.clone()), e);
796 status.modify(clmv!(e, |a| {
798 if let SetupStatus::CommitInstall(s) = a.value_mut() {
799 s.errors.push(e);
800 s.cancel = false;
801 s.task_progress = VarEq(const_var(Progress::complete()));
802 }
803 }));
804 errors.push(e);
805
806 } else {
809 task_progress.set(Progress::complete());
810 }
811 }
812
813 let mut tasks = config.tasks;
814 tasks.reverse();
815 uninstall_cfg.reverse();
816 let data = UninstallConfig { tasks, cfg: uninstall_cfg };
817
818 if errors.is_empty() {
819 status.modify(clmv!(|a| {
821 if let SetupStatus::CommitInstall(s) = a.value_mut() {
822 s.task_progress = VarEq(const_var(Progress::complete()));
823 }
824 }));
825
826 Ok(data)
827 } else {
828 Err(SetupError::task_errors(
829 errors,
830 SetupErrorState::PartialInstall {
831 data,
832 no_data: err_no_clean,
833 },
834 ))
835 }
836}
837
838async fn uninstall(config: UninstallConfig) -> Result<(), SetupError> {
839 let config = validate_uninstall(config).await?;
840
841 let status = SETUP_SV.read().status.clone();
842
843 let tasks_len = config.tasks.len();
844 if tasks_len != config.cfg.len() {
845 return Err(SetupError::corrupted_op_config("UninstallConfig"));
846 }
847
848 let mut errors = vec![];
849
850 for (i, (id, cfg)) in config.tasks.into_iter().zip(config.cfg).enumerate() {
851 let task_progress = var(Progress::indeterminate());
852 let task_progress_s = task_progress.read_only();
854 status.modify(clmv!(id, |a| {
855 match a.value_mut() {
856 SetupStatus::Uninstall(s) => {
857 s.task = id;
858 s.progress.0 = i;
859 s.task_progress = VarEq(task_progress_s);
860 }
861 _ => {
862 **a = SetupStatus::Uninstall(SetupOpStatus {
863 cancel: false,
864 task: id,
865 progress: (i, tasks_len),
866 task_progress: VarEq(task_progress_s),
867 errors: vec![],
868 })
869 }
870 }
871 }));
872
873 let task_ty = SETUP_SV.read().task_type(&id.0);
875 let error = match task_ty {
876 Ok(task_ty) => (task_ty.uninstall)(cfg, task_progress.clone()).await.err(),
877 Err(e) => Some(e),
878 };
879
880 if let Some(e) = error {
881 let e = ((i, id.0.clone(), id.1.clone()), e);
882 status.modify(clmv!(e, |a| {
884 if let SetupStatus::Uninstall(s) = a.value_mut() {
885 s.errors.push(e);
886 s.cancel = false;
887 s.task_progress = VarEq(const_var(Progress::complete()));
888 }
889 }));
890 errors.push(e);
891
892 } else {
895 task_progress.set(Progress::complete());
896 }
897 }
898 if errors.is_empty() {
899 status.modify(clmv!(|a| {
901 if let SetupStatus::Uninstall(s) = a.value_mut() {
902 s.task_progress = VarEq(const_var(Progress::complete()));
903 }
904 }));
905 Ok(())
906 } else {
907 Err(SetupError::task_errors(errors, SetupErrorState::PartialUninstall))
908 }
909}
910
911async fn validate_uninstall(config: UninstallConfig) -> Result<UninstallConfig, SetupError> {
912 let (status, cancel) = {
913 let sv = SETUP_SV.read();
914 (sv.status.clone(), sv.cancel.clone())
915 };
916
917 let tasks_len = config.tasks.len();
918 if tasks_len != config.cfg.len() {
919 return Err(SetupError::corrupted_op_config("UninstallConfig"));
920 }
921
922 let UninstallConfig { tasks, mut cfg } = config;
923 let empty_cfg = RawConfigValue::serialize(()).unwrap();
924
925 let mut errors = vec![];
926
927 for (i, (id, cfg)) in tasks.iter().zip(cfg.iter_mut()).enumerate() {
928 let task_progress = var(Progress::indeterminate());
929 let task_progress_s = task_progress.read_only();
931 status.modify(clmv!(id, |a| {
932 match a.value_mut() {
933 SetupStatus::ValidateUninstall(s) => {
934 s.task = id;
935 s.progress.0 = i;
936 s.task_progress = VarEq(task_progress_s);
937 }
938 _ => {
939 **a = SetupStatus::ValidateUninstall(SetupOpStatus {
940 cancel: false,
941 task: id,
942 progress: (i, tasks_len),
943 task_progress: VarEq(task_progress_s),
944 errors: vec![],
945 })
946 }
947 }
948 }));
949
950 let task_ty = SETUP_SV.read().task_type(&id.0);
952 let error = match task_ty {
953 Ok(task_ty) => {
954 match (task_ty.validate_uninstall)(std::mem::replace(cfg, empty_cfg.clone()), task_progress.clone(), cancel.clone()).await {
955 Ok(c) => {
956 *cfg = c;
957 None
958 }
959 Err(e) => Some(e),
960 }
961 }
962 Err(e) => Some(e),
963 };
964
965 if let Some(e) = error {
966 let e = ((i, id.0.clone(), id.1.clone()), e);
967 status.modify(clmv!(e, |a| {
969 if let SetupStatus::ValidateUninstall(s) = a.value_mut() {
970 s.errors.push(e);
971 s.cancel = false;
972 s.task_progress = VarEq(const_var(Progress::complete()));
973 }
974 }));
975 errors.push(e);
976
977 } else {
980 task_progress.set(Progress::complete());
981 }
982
983 if cancel.get() {
984 break;
985 }
986 }
987
988 let canceled = cancel.get();
989 if errors.is_empty() && !canceled {
990 status.modify(clmv!(|a| {
992 if let SetupStatus::ValidateUninstall(s) = a.value_mut() {
993 s.task_progress = VarEq(const_var(Progress::complete()));
994 }
995 }));
996 Ok(UninstallConfig { tasks, cfg })
997 } else {
998 Err(SetupError::task_errors(errors, SetupErrorState::Canceled))
999 }
1000}