koibumi-common-sync 0.0.0

Common code among GUI and daemon versions for Koibumi (sync version), an experimental Bitmessage client
Documentation
//! Functions to prepare database connection for boxes.

use std::{fmt, io};

use log::{debug, error, info};

use crate::{config::create_data_dir, param::Params};

/// The default user ID bytes.
pub const DEFAULT_USER_ID: &[u8] = b"default";
const DEFAULT_USER_NAME: &str = "Default";

/// A helper object for managing inbox/outbox.
#[derive(Debug)]
pub struct Boxes {
    manager: koibumi_box_sync::Manager,
    user: koibumi_box_sync::User,
    unread_count: usize,
    selected_identity_index: Option<usize>,
    selected_contact_index: Option<usize>,
}

impl Boxes {
    /// Returns the inbox/outbox manager.
    pub fn manager(&self) -> &koibumi_box_sync::Manager {
        &self.manager
    }

    /// Returns the inbox/outbox manager as a mutable reference.
    pub fn manager_mut(&mut self) -> &mut koibumi_box_sync::Manager {
        &mut self.manager
    }

    /// Returns the user object cached on this helper object.
    pub fn user(&self) -> &koibumi_box_sync::User {
        &self.user
    }

    /// Returns the user object as a mutable reference, ceched on this helper object.
    pub fn user_mut(&mut self) -> &mut koibumi_box_sync::User {
        &mut self.user
    }

    /// Returns the count of unread messages.
    pub fn unread_count(&self) -> usize {
        self.unread_count
    }

    /// Set the count of unread messages.
    pub fn set_unread_count(&mut self, count: usize) {
        self.unread_count = count;
    }

    /// Increments the count of unread messages.
    pub fn increment_unread_count(&mut self) {
        if self.unread_count == usize::MAX {
            error!("unread_count overflow");
            return;
        }
        self.unread_count += 1;
    }

    /// Decrements the count of unread messages.
    pub fn decrement_unread_count(&mut self) {
        if self.unread_count < 1 {
            error!("unread_count underflow");
            return;
        }
        self.unread_count -= 1;
    }

    /// Returns the index of the selected identity.
    pub fn selected_identity_index(&self) -> Option<usize> {
        self.selected_identity_index
    }

    /// Sets the index of the selected identity.
    pub fn set_selected_identity_index(&mut self, value: Option<usize>) {
        self.selected_identity_index = value
    }

    /// Returns the index of the selected contact.
    pub fn selected_contact_index(&self) -> Option<usize> {
        self.selected_contact_index
    }

    /// Sets the index of the selected contact.
    pub fn set_selected_contact_index(&mut self, value: Option<usize>) {
        self.selected_contact_index = value
    }
}

/// An error which can be returned when operating on inbox/outbox.
#[derive(Debug)]
pub enum Error {
    /// A standard I/O error was caught during operation on boxes.
    /// The actual error caught is returned as a payload of this variant.
    IoError(io::Error),
    /// A Rusqlite error was caught during operation on boxes.
    /// The actual error caught is returned as a payload of this variant.
    RusqliteError(rusqlite::Error),
    /// Indicates that an operation on boxes failed.
    /// The actual error caught is returned as a payload of this variant.
    BoxError(koibumi_box_sync::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::IoError(err) => err.fmt(f),
            Self::RusqliteError(err) => err.fmt(f),
            Self::BoxError(err) => err.fmt(f),
        }
    }
}

impl std::error::Error for Error {}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Self {
        Self::IoError(err)
    }
}

impl From<rusqlite::Error> for Error {
    fn from(err: rusqlite::Error) -> Self {
        Self::RusqliteError(err)
    }
}

impl From<koibumi_box_sync::Error> for Error {
    fn from(err: koibumi_box_sync::Error) -> Self {
        Self::BoxError(err)
    }
}

/// Connects the database and add a user if not exists and returns a helper manager object.
pub fn prepare(params: &Params) -> Result<Boxes, Error> {
    let mut path = create_data_dir(params)?;
    path.push("box.db");
    let conn = rusqlite::Connection::open(path)?;
    let manager = koibumi_box_sync::Manager::new(conn)?;

    match manager.add_user(DEFAULT_USER_ID, DEFAULT_USER_NAME) {
        Ok(_) => {
            info!("Default user added");
        }
        Err(koibumi_box_sync::Error::AlreadyExists) => {
            debug!("Default user already exists");
        }
        Err(err) => return Err(err.into()),
    }

    let user = manager.user(DEFAULT_USER_ID)?;

    /*
    // DEBUG
    for address in user.subscriptions() {
        debug!("Subscription: {}", address);
    }
    */

    Ok(Boxes {
        manager,
        user,
        unread_count: 0,
        selected_identity_index: None,
        selected_contact_index: None,
    })
}