diff --git a/crates/rules/src/roles_builder.rs b/crates/rules/src/roles_builder.rs index 2b198af9..e4d2bac9 100644 --- a/crates/rules/src/roles_builder.rs +++ b/crates/rules/src/roles_builder.rs @@ -39,6 +39,8 @@ impl RoleBuilder { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum RoleBuilderValidation { + /// e.g. tried to remove a factor source which was not found. + FactorSourceNotFound, ForeverInvalid(ForeverInvalidReason), NotYetValid(NotYetValidReason), } @@ -147,6 +149,38 @@ pub trait BuilderOfFactorsInRole: Sized + private::UncheckedBuilderOfFactorsInRo self.validate() } + fn override_contains_factor_source(&self, factor_source: &FactorSource) -> bool { + self.override_factors().contains(&factor_source) + } + + fn threshold_contains_factor_source(&self, factor_source: &FactorSource) -> bool { + self.threshold_factors().contains(&factor_source) + } + + /// # Throws + /// Throws an error if the specified `factor_source` was not in any of the two lists. + fn remove_factor_source(&mut self, factor_source: &FactorSource) -> RoleBuilderMutateResult; + + fn override_contains_factor_source_of_kind( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.override_factors() + .into_iter() + .find(|f| f.factor_source_kind() == factor_source_kind) + .is_some() + } + + fn threshold_contains_factor_source_of_kind( + &self, + factor_source_kind: FactorSourceKind, + ) -> bool { + self.threshold_factors() + .into_iter() + .find(|f| f.factor_source_kind() == factor_source_kind) + .is_some() + } + /// If Ok => self is mutated /// If Err(NotYetValid) => self is mutated /// If Err(ForeverInvalid) => self is not mutated @@ -161,7 +195,8 @@ pub trait BuilderOfFactorsInRole: Sized + private::UncheckedBuilderOfFactorsInRo Ok(()) | Err(RoleBuilderValidation::NotYetValid(_)) => { self.unchecked_add_factor_source_to_list(factor_source, factor_list_kind); } - Err(RoleBuilderValidation::ForeverInvalid(_)) => {} + Err(RoleBuilderValidation::ForeverInvalid(_)) + | Err(RoleBuilderValidation::FactorSourceNotFound) => {} } validation } @@ -328,6 +363,33 @@ impl BuilderOfFactorsInRole for RoleBuilder { || self.threshold_contains_factor_source_of_kind(factor_source_kind) } + /// Lowers the threshold if the deleted factor source is in the threshold list + /// and if after removal of `factor_source` `self.threshold > self.threshold_factors.len()` + fn remove_factor_source(&mut self, factor_source: &FactorSource) -> RoleBuilderMutateResult { + if !self.contains_factor_source(factor_source) { + return RoleBuilderMutateResult::Err(RoleBuilderValidation::FactorSourceNotFound); + } + let remove = |xs: &mut Vec| { + let index = xs + .iter() + .position(|f| f == factor_source) + .expect("Called remove of non existing FactorSource, this is a programmer error, should have checked if it exists before calling remove."); + xs.remove(index); + }; + + if self.override_contains_factor_source(factor_source) { + remove(&mut self.override_factors) + } + if self.threshold_contains_factor_source(factor_source) { + remove(&mut self.threshold_factors); + let threshold_factors_len = self.threshold_factors.len() as u8; + if self.threshold > threshold_factors_len { + self.set_threshold(threshold_factors_len)?; + } + } + Ok(()) + } + fn validation_for_addition_of_factor_source_of_kind_to_list_for_primary( &self, factor_source_kind: FactorSourceKind, @@ -482,34 +544,6 @@ impl RoleBuilder { } impl RoleBuilder { - fn override_contains_factor_source(&self, factor_source: &FactorSource) -> bool { - self.override_factors.contains(factor_source) - } - - fn threshold_contains_factor_source(&self, factor_source: &FactorSource) -> bool { - self.threshold_factors.contains(factor_source) - } - - fn override_contains_factor_source_of_kind( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.override_factors - .iter() - .find(|f| f.factor_source_kind() == factor_source_kind) - .is_some() - } - - fn threshold_contains_factor_source_of_kind( - &self, - factor_source_kind: FactorSourceKind, - ) -> bool { - self.threshold_factors - .iter() - .find(|f| f.factor_source_kind() == factor_source_kind) - .is_some() - } - fn factor_sources_not_of_kind_to_list_of_kind( &self, factor_source_kind: FactorSourceKind, @@ -1401,7 +1435,7 @@ mod tests { } #[cfg(test)] - mod set_threshold_suite { + mod threshold_suite { use super::*; fn sample() -> FactorSource { @@ -1420,6 +1454,60 @@ mod tests { FactorListKind::Threshold } + #[test] + fn remove_lowers_threshold_from_1_to_0() { + let mut sut = make(); + let fs = sample(); + sut.add_factor_source_to_list(fs.clone(), list()).unwrap(); + sut.set_threshold(1).unwrap(); + assert_eq!(sut.threshold, 1); + sut.remove_factor_source(&fs).unwrap(); + assert_eq!(sut.threshold, 0); + } + + #[test] + fn remove_lowers_threshold_from_3_to_1() { + let mut sut = make(); + let fs0 = sample(); + let fs1 = sample_other(); + sut.add_factor_source_to_list(fs0.clone(), list()).unwrap(); + sut.add_factor_source_to_list(fs1.clone(), list()).unwrap(); + sut.add_factor_source_to_list(FactorSource::sample_arculus_other(), list()) + .unwrap(); + sut.set_threshold(3).unwrap(); + assert_eq!(sut.threshold, 3); + sut.remove_factor_source(&fs0).unwrap(); + sut.remove_factor_source(&fs1).unwrap(); + assert_eq!(sut.threshold, 1); + } + + #[test] + fn remove_from_override_does_not_change_threshold() { + let mut sut = make(); + sut.add_factor_source_to_list(sample(), list()).unwrap(); + sut.add_factor_source_to_list(sample_other(), list()) + .unwrap(); + let fs = FactorSource::sample_arculus_other(); + sut.add_factor_source_to_list(fs.clone(), FactorListKind::Override) + .unwrap(); + sut.set_threshold(2).unwrap(); + assert_eq!(sut.threshold, 2); + sut.remove_factor_source(&fs).unwrap(); + assert_eq!(sut.threshold, 2); + + let built = sut.build().unwrap(); + assert_eq!( + built, + RoleWithFactors { + built: PhantomData, + role: role(), + threshold: 2, + threshold_factors: vec![sample(), sample_other()], + override_factors: Vec::new(), + } + ); + } + #[test] fn one_factor_then_set_threshold_to_one_is_ok() { // Arrange