use crate::connections::db_connection; use crate::constants::FAMILY_INVITATION_CODE_LEN; use crate::manual_schema::families_memberships; use crate::models::{ Family, FamilyID, FamilyMembership, Membership, NewFamily, NewMembership, UserID, }; use crate::schema::{families, memberships}; use crate::services::{members_service, users_service}; use crate::utils::string_utils::rand_str; use crate::utils::time_utils::time; use diesel::prelude::*; /// Create a new family, with an initial administrator pub async fn create(name: &str, user_id: UserID) -> anyhow::Result { let family = db_connection::execute(|conn| { let res: Family = diesel::insert_into(families::table) .values(&NewFamily { name: name.trim(), invitation_code: rand_str(FAMILY_INVITATION_CODE_LEN), time_create: time() as i64, }) .get_result(conn)?; Ok(res) })?; add_member(family.id(), user_id, true).await?; Ok(family) } /// Add a member to a family pub async fn add_member( family_id: FamilyID, user_id: UserID, admin: bool, ) -> anyhow::Result { db_connection::execute(|conn| { let res = diesel::insert_into(memberships::table) .values(&NewMembership { user_id: user_id.0, family_id: family_id.0, time_create: time() as i64, is_admin: admin, }) .get_result(conn)?; Ok(res) }) } /// Find a family by id pub async fn get_by_id(id: FamilyID) -> anyhow::Result { db_connection::execute(|conn| { families::table .filter(families::dsl::id.eq(id.0)) .first(conn) }) } /// Find a family by invitation code pub async fn get_by_invitation_code(code: &str) -> anyhow::Result { db_connection::execute(|conn| { families::table .filter(families::dsl::invitation_code.eq(code)) .first(conn) }) } /// Check if a given user is member of a family or not pub async fn is_member(family_id: FamilyID, user_id: UserID) -> anyhow::Result { db_connection::execute(|conn| { memberships::table .filter(memberships::dsl::family_id.eq(family_id.0)) .filter(memberships::dsl::user_id.eq(user_id.0)) .count() .get_result(conn) }) .map(|c: i64| c > 0) } /// Get the memberships of a user, with family info pub async fn get_user_family_memberships(user_id: UserID) -> anyhow::Result> { db_connection::execute(|conn| { families_memberships::table .filter(families_memberships::dsl::user_id.eq(user_id.0)) .get_results(conn) }) } /// Get the memberships of a user, without family info pub async fn get_user_memberships(user_id: UserID) -> anyhow::Result> { db_connection::execute(|conn| { memberships::table .filter(memberships::dsl::user_id.eq(user_id.0)) .get_results(conn) }) } /// Get information about a membership of a user pub async fn get_membership(family_id: FamilyID, user_id: UserID) -> anyhow::Result { db_connection::execute(|conn| { memberships::table .filter(memberships::dsl::user_id.eq(user_id.0)) .filter(memberships::dsl::family_id.eq(family_id.0)) .get_result(conn) }) } /// Update a membership to a family pub async fn update_membership(membership: &Membership) -> anyhow::Result<()> { db_connection::execute(|conn| { diesel::update( memberships::dsl::memberships.filter( memberships::dsl::user_id .eq(membership.user_id().0) .and(memberships::dsl::family_id.eq(membership.family_id().0)), ), ) .set(memberships::dsl::is_admin.eq(membership.is_admin)) .execute(conn) })?; Ok(()) } #[derive(serde::Serialize)] pub struct FamilyMember { #[serde(flatten)] membership: Membership, user_name: String, user_mail: String, } /// Get information about the users of a family pub async fn get_memberships_of_family(family_id: FamilyID) -> anyhow::Result> { let memberships = db_connection::execute(|conn| { memberships::table .filter(memberships::dsl::family_id.eq(family_id.0)) .get_results::(conn) })?; let mut out = Vec::with_capacity(memberships.len()); for m in memberships { let user = users_service::get_by_id(m.user_id()).await?; out.push(FamilyMember { user_name: user.name, user_mail: user.email, membership: m, }) } Ok(out) } /// Get information about a membership of a user, joined with family information pub async fn get_family_membership( family_id: FamilyID, user_id: UserID, ) -> anyhow::Result { db_connection::execute(|conn| { families_memberships::table .filter(families_memberships::dsl::user_id.eq(user_id.0)) .filter(families_memberships::dsl::family_id.eq(family_id.0)) .get_result(conn) }) } /// Update a family pub async fn update_family(family: &Family) -> anyhow::Result<()> { db_connection::execute(|conn| { diesel::update(families::dsl::families.filter(families::dsl::id.eq(family.id().0))) .set(( families::dsl::name.eq(family.name.clone()), families::dsl::invitation_code.eq(family.invitation_code.clone()), )) .execute(conn) })?; Ok(()) } /// Delete a family pub async fn delete_family(family_id: FamilyID) -> anyhow::Result<()> { // TODO : delete couples // Remove all family members members_service::delete_all_family(family_id).await?; // Remove all memberships db_connection::execute(|conn| { diesel::delete( memberships::dsl::memberships.filter(memberships::dsl::family_id.eq(family_id.0)), ) .execute(conn) })?; // Remove the family itself db_connection::execute(|conn| { diesel::delete(families::dsl::families.filter(families::dsl::id.eq(family_id.0))) .execute(conn) })?; Ok(()) } /// Remove a membership to a family pub async fn remove_membership(family_id: FamilyID, user_id: UserID) -> anyhow::Result<()> { let family = get_family_membership(family_id, user_id).await?; if family.is_admin && family.count_admins == 1 { // We need to delete the whole family delete_family(family_id).await } else { // Remove the single membership db_connection::execute(|conn| { diesel::delete( memberships::dsl::memberships.filter( memberships::dsl::user_id .eq(user_id.0) .and(memberships::dsl::family_id.eq(family_id.0)), ), ) .execute(conn) })?; Ok(()) } } /// Remove all memberships of user pub async fn remove_all_user_membership(user_id: UserID) -> anyhow::Result<()> { for m in get_user_memberships(user_id).await? { remove_membership(m.family_id(), user_id).await?; } Ok(()) }