Add loop protection
This commit is contained in:
@ -106,3 +106,86 @@ pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod loop_detection {
|
||||
use crate::models::{Member, MemberID};
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct LoopStack<'a> {
|
||||
curr: MemberID,
|
||||
prev: Option<&'a LoopStack<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> LoopStack<'a> {
|
||||
pub fn contains(&self, id: MemberID) -> bool {
|
||||
if let Some(ls) = &self.prev {
|
||||
if ls.contains(id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
self.curr == id
|
||||
}
|
||||
}
|
||||
|
||||
fn detect_loop_control(
|
||||
members: &HashMap<MemberID, &&Member>,
|
||||
curr_stack: &LoopStack,
|
||||
next: Option<MemberID>,
|
||||
) -> bool {
|
||||
match next {
|
||||
None => false,
|
||||
Some(id) => {
|
||||
if curr_stack.contains(id) {
|
||||
log::debug!("Loop detected! {:?}", curr_stack);
|
||||
return true;
|
||||
}
|
||||
|
||||
detect_loop_recurse(
|
||||
members,
|
||||
&LoopStack {
|
||||
curr: id,
|
||||
prev: Some(curr_stack),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Recurse loop detection
|
||||
fn detect_loop_recurse(members: &HashMap<MemberID, &&Member>, curr_stack: &LoopStack) -> bool {
|
||||
let member = match members.get(&curr_stack.curr) {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
log::warn!("Member {:?} not found in the tree for loop detection, this should never happen!", curr_stack.curr);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
detect_loop_control(members, curr_stack, member.mother())
|
||||
|| detect_loop_control(members, curr_stack, member.father())
|
||||
}
|
||||
|
||||
/// Detect loops in members hierarchy
|
||||
pub fn detect_loop(members: &[&Member]) -> bool {
|
||||
let mut map = HashMap::new();
|
||||
for m in members {
|
||||
map.insert(m.id(), m);
|
||||
}
|
||||
|
||||
for m in members {
|
||||
if detect_loop_recurse(
|
||||
&map,
|
||||
&LoopStack {
|
||||
curr: m.id(),
|
||||
prev: None,
|
||||
},
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user