koibumi_common/
boxes.rs

1//! Functions to prepare database connection for boxes.
2
3use std::{fmt, io};
4
5use log::{debug, error, info};
6
7use koibumi_node::db;
8
9use crate::{config::create_data_dir, param::Params};
10
11/// The default user ID bytes.
12pub const DEFAULT_USER_ID: &[u8] = b"default";
13const DEFAULT_USER_NAME: &str = "Default";
14
15/// A helper object for managing inbox/outbox.
16#[derive(Debug)]
17pub struct Boxes {
18    manager: koibumi_box::Manager,
19    user: koibumi_box::User,
20    unread_count: usize,
21    selected_identity_index: Option<usize>,
22    selected_contact_index: Option<usize>,
23}
24
25impl Boxes {
26    /// Returns the inbox/outbox manager.
27    pub fn manager(&self) -> &koibumi_box::Manager {
28        &self.manager
29    }
30
31    /// Returns the inbox/outbox manager as a mutable reference.
32    pub fn manager_mut(&mut self) -> &mut koibumi_box::Manager {
33        &mut self.manager
34    }
35
36    /// Returns the user object cached on this helper object.
37    pub fn user(&self) -> &koibumi_box::User {
38        &self.user
39    }
40
41    /// Returns the user object as a mutable reference, ceched on this helper object.
42    pub fn user_mut(&mut self) -> &mut koibumi_box::User {
43        &mut self.user
44    }
45
46    /// Returns the count of unread messages.
47    pub fn unread_count(&self) -> usize {
48        self.unread_count
49    }
50
51    /// Set the count of unread messages.
52    pub fn set_unread_count(&mut self, count: usize) {
53        self.unread_count = count;
54    }
55
56    /// Increments the count of unread messages.
57    pub fn increment_unread_count(&mut self) {
58        if self.unread_count == usize::MAX {
59            error!("unread_count overflow");
60            return;
61        }
62        self.unread_count += 1;
63    }
64
65    /// Decrements the count of unread messages.
66    pub fn decrement_unread_count(&mut self) {
67        if self.unread_count < 1 {
68            error!("unread_count underflow");
69            return;
70        }
71        self.unread_count -= 1;
72    }
73
74    /// Returns the index of the selected identity.
75    pub fn selected_identity_index(&self) -> Option<usize> {
76        self.selected_identity_index
77    }
78
79    /// Sets the index of the selected identity.
80    pub fn set_selected_identity_index(&mut self, value: Option<usize>) {
81        self.selected_identity_index = value
82    }
83
84    /// Returns the index of the selected contact.
85    pub fn selected_contact_index(&self) -> Option<usize> {
86        self.selected_contact_index
87    }
88
89    /// Sets the index of the selected contact.
90    pub fn set_selected_contact_index(&mut self, value: Option<usize>) {
91        self.selected_contact_index = value
92    }
93}
94
95/// An error which can be returned when operating on inbox/outbox.
96#[derive(Debug)]
97pub enum Error {
98    /// A standard I/O error was caught during operation on boxes.
99    /// The actual error caught is returned as a payload of this variant.
100    IoError(io::Error),
101    /// A SQLx error was caught during operation on boxes.
102    /// The actual error caught is returned as a payload of this variant.
103    SqlxError(sqlx::Error),
104    /// Indicates that an operation on boxes failed.
105    /// The actual error caught is returned as a payload of this variant.
106    BoxError(koibumi_box::Error),
107}
108
109impl fmt::Display for Error {
110    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
111        match self {
112            Self::IoError(err) => err.fmt(f),
113            Self::SqlxError(err) => err.fmt(f),
114            Self::BoxError(err) => err.fmt(f),
115        }
116    }
117}
118
119impl std::error::Error for Error {}
120
121impl From<io::Error> for Error {
122    fn from(err: io::Error) -> Self {
123        Self::IoError(err)
124    }
125}
126
127impl From<sqlx::Error> for Error {
128    fn from(err: sqlx::Error) -> Self {
129        Self::SqlxError(err)
130    }
131}
132
133impl From<koibumi_box::Error> for Error {
134    fn from(err: koibumi_box::Error) -> Self {
135        Self::BoxError(err)
136    }
137}
138
139/// Connects the database and add a user if not exists and returns a helper manager object.
140pub async fn prepare(params: &Params) -> Result<Boxes, Error> {
141    let mut path = create_data_dir(params)?;
142    path.push("box.db");
143    let pool = db::SqlitePool::connect_with(
144        sqlx::sqlite::SqliteConnectOptions::new()
145            .filename(path)
146            .create_if_missing(true),
147    )
148    .await?;
149    let manager = koibumi_box::Manager::new(pool).await?;
150
151    match manager.add_user(DEFAULT_USER_ID, DEFAULT_USER_NAME).await {
152        Ok(_) => {
153            info!("Default user added");
154        }
155        Err(koibumi_box::Error::AlreadyExists) => {
156            debug!("Default user already exists");
157        }
158        Err(err) => return Err(err.into()),
159    }
160
161    let user = manager.user(DEFAULT_USER_ID).await?;
162
163    /*
164    // DEBUG
165    for address in user.subscriptions() {
166        debug!("Subscription: {}", address);
167    }
168    */
169
170    Ok(Boxes {
171        manager,
172        user,
173        unread_count: 0,
174        selected_identity_index: None,
175        selected_contact_index: None,
176    })
177}