thot-local 0.10.0-intermediate

Local functionality for Thot data management and analysis software.
Documentation
use super::collections::users::Users;
use super::settings::user_settings::UserSettings;
use crate::result::{Error, Result, SettingsFileError, UsersError};
use settings_manager::prelude::{ListSetting, SystemSettings};
use thot_core::result::{Error as CoreError, ResourceError};
use thot_core::system::User;
use thot_core::types::ResourceId;
use validator;

// *************
// *** Users ***
// *************

/// Returns a user by the given id if it exists, otherwise returns an error.
pub fn user_by_id(id: &ResourceId) -> Result<User> {
    let mut users = load_users()?;
    let users: Vec<&User> = users
        .items()
        .iter()
        .filter(|user| &user.rid == id)
        .collect();

    match users.len() {
        0 => Err(Error::CoreError(CoreError::ResourceError(
            ResourceError::DoesNotExist(id.id.to_string()),
        ))),
        1 => Ok(users[0].clone()),
        _ => Err(Error::UsersError(UsersError::DuplicateEmail(
            id.id.to_string(),
        ))),
    }
}

/// Resturns a user by the given email if it exists, otherwise returns an error.
pub fn user_by_email(email: &str) -> Result<User> {
    let mut users = load_users()?;
    let users: Vec<&User> = users
        .items()
        .iter()
        .filter(|user| user.email == email)
        .collect();

    match users.len() {
        0 => Err(Error::CoreError(CoreError::ResourceError(
            ResourceError::DoesNotExist(email.to_string()),
        ))),
        1 => Ok(users[0].clone()),
        _ => Err(Error::UsersError(UsersError::DuplicateEmail(
            email.to_string(),
        ))),
    }
}

/// Adds a user to the system settings.
pub fn add_user(user: User) -> Result {
    // validate email
    if !validator::validate_email(&user.email) {
        return Err(Error::UsersError(UsersError::InvalidEmail(user.email)));
    }

    let mut users = load_users()?;

    // check if email already exists
    let user_count = user_count_by_email(&user.email, &users);
    if user_count > 0 {
        // same email already exists
        return Err(Error::UsersError(UsersError::DuplicateEmail(user.email)));
    }

    // add user
    users.push(user);
    save_users(users)
}

/// Delete a user by id.
pub fn delete_user(id: &ResourceId) -> Result {
    let mut users = load_users()?;
    let mut settings = load_user_settings()?;

    // remove user
    users.items().retain(|user| user.rid != *id);

    // unset as active user, if required
    if settings.active_user == Some(id.clone()) {
        settings.active_user = None;
        save_user_settings(settings)?;
    }

    save_users(users)
}

pub fn delete_user_by_email(email: &str) -> Result {
    let user = match user_by_email(email) {
        Err(_) => return Ok(()), // user does not exist
        Ok(u) => u,
    };

    let id = user.rid;
    delete_user(&id)
}

/// Set the user with the given id, updating all other fields.
pub fn update_user(user: User) -> Result {
    // validate email
    if !validator::validate_email(&user.email) {
        return Err(Error::UsersError(UsersError::InvalidEmail(user.email)));
    }

    let user_id = user.rid.clone();
    let mut users = load_users()?;

    // ensure valid user
    validate_user_by_id(&user_id, &users)?;

    // remove user
    users.items().retain(|u| u.rid != user_id);

    // replace with new user
    users.push(user);

    save_users(users)
}

pub fn set_active_user(id: &ResourceId) -> Result {
    // ensure valid users
    let users = load_users()?;
    validate_user_by_id(id, &users)?;

    // set active user
    let mut settings = load_user_settings()?;
    settings.active_user = Some(id.clone());
    save_user_settings(settings)
}

pub fn set_active_user_by_email(email: &str) -> Result {
    let user = user_by_email(email)?;
    let mut settings = load_user_settings()?;

    settings.active_user = Some(user.rid.into());
    save_user_settings(settings)
}

pub fn unset_active_user() -> Result {
    let mut settings = load_user_settings()?;

    settings.active_user = None;
    save_user_settings(settings)
}

// *************************
// *** private functions ***
// *************************

/// Returns the number of users with the given id.
fn user_count_by_id(id: &ResourceId, users: &Users) -> u32 {
    // ensure valid users
    let mut user_count = 0;
    for u in users.users.iter() {
        if u.rid == *id {
            user_count += 1;
        }
    }

    user_count
}

/// Verifies users are valid by id.
fn validate_user_by_id(id: &ResourceId, users: &Users) -> Result {
    let user_count = user_count_by_id(id, users);
    if user_count == 0 {
        return Err(Error::CoreError(CoreError::ResourceError(
            ResourceError::DoesNotExist(id.id.to_string()),
        )));
    } else if user_count > 1 {
        return Err(Error::CoreError(CoreError::ResourceError(
            ResourceError::DuplicateId(id.clone()),
        )));
    }

    // success
    Ok(())
}

/// Returns the number of users with the given email.
fn user_count_by_email(email: &str, users: &Users) -> usize {
    // ensure valid users
    users
        .users
        .iter()
        .filter(|user| user.email == email)
        .collect::<Vec<&User>>()
        .len()
}

fn load_user_settings() -> Result<UserSettings> {
    match UserSettings::load() {
        Ok(sets) => Ok(sets),
        Err(err) => {
            return Err(Error::SettingsFileError(SettingsFileError::CouldNotLoad(
                err,
            )))
        }
    }
}

fn save_user_settings(settings: UserSettings) -> Result {
    match settings.save() {
        Ok(_) => Ok(()),
        Err(err) => Err(Error::SettingsFileError(SettingsFileError::CouldNotSave(
            err,
        ))),
    }
}

fn load_users() -> Result<Users> {
    match Users::load() {
        Ok(users) => Ok(users),
        Err(err) => {
            return Err(Error::SettingsFileError(SettingsFileError::CouldNotLoad(
                err,
            )))
        }
    }
}

fn save_users(users: Users) -> Result {
    match users.save() {
        Ok(_) => Ok(()),
        Err(err) => Err(Error::SettingsFileError(SettingsFileError::CouldNotSave(
            err,
        ))),
    }
}

#[cfg(test)]
#[path = "./users_test.rs"]
mod users_test;