use crate::connections::db_connection; use crate::models::{FamilyID, Member, MemberID, NewMember}; use crate::schema::members; use crate::utils::time_utils::time; use diesel::prelude::*; use diesel::RunQueryDsl; /// Create a new family member pub async fn create(family_id: FamilyID) -> anyhow::Result { db_connection::execute(|conn| { let res: Member = diesel::insert_into(members::table) .values(&NewMember { family_id: family_id.0, time_create: time() as i64, time_update: time() as i64, }) .get_result(conn)?; Ok(res) }) } /// Get the information of a member pub async fn get_by_id(id: MemberID) -> anyhow::Result { db_connection::execute(|conn| members::table.filter(members::dsl::id.eq(id.0)).first(conn)) } /// Get all the members of a family pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result> { db_connection::execute(|conn| { members::table .filter(members::dsl::family_id.eq(id.0)) .get_results(conn) }) } /// Check whether a member with a given id exists or not pub async fn exists(family_id: FamilyID, member_id: MemberID) -> anyhow::Result { db_connection::execute(|conn| { let count: i64 = members::table .filter( members::id .eq(member_id.0) .and(members::family_id.eq(family_id.0)), ) .count() .get_result(conn)?; Ok(count != 0) }) } /// Update the information of a member pub async fn update(member: &mut Member) -> anyhow::Result<()> { member.time_update = time() as i64; db_connection::execute(|conn| { diesel::update(members::dsl::members.filter(members::dsl::id.eq(member.id().0))) .set(( members::dsl::first_name.eq(member.first_name.clone()), members::dsl::last_name.eq(member.last_name.clone()), members::dsl::birth_last_name.eq(member.birth_last_name.clone()), members::dsl::photo_id.eq(member.photo_id().map(|p| p.0)), members::dsl::email.eq(member.email.clone()), members::dsl::phone.eq(member.phone.clone()), members::dsl::address.eq(member.address.clone()), members::dsl::city.eq(member.city.clone()), members::dsl::postal_code.eq(member.postal_code.clone()), members::dsl::country.eq(member.country.clone()), members::dsl::sex.eq(member.sex().map(|s| s.to_str().to_string())), members::dsl::time_update.eq(member.time_update), members::dsl::mother.eq(member.mother().map(|m| m.0)), members::dsl::father.eq(member.father().map(|m| m.0)), members::dsl::birth_year.eq(member.birth_year), members::dsl::birth_month.eq(member.birth_month), members::dsl::birth_day.eq(member.birth_day), members::dsl::death_year.eq(member.death_year), members::dsl::death_month.eq(member.death_month), members::dsl::death_day.eq(member.death_day), members::dsl::note.eq(member.note.clone()), )) .execute(conn) })?; Ok(()) } /// Delete a member pub async fn delete(member: &Member) -> anyhow::Result<()> { // TODO : remove associated couple // TODO : remove user photo // Remove the member db_connection::execute(|conn| { diesel::delete(members::dsl::members.filter(members::dsl::id.eq(member.id().0))) .execute(conn) })?; Ok(()) } /// Delete all the members of a family pub async fn delete_all_family(family_id: FamilyID) -> anyhow::Result<()> { for m in get_all_of_family(family_id).await? { delete(&m).await?; } 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, curr_stack: &LoopStack, next: Option, ) -> 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, 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 } }