1use std::{fmt, io};
4
5use log::{debug, error, info};
6
7use koibumi_node::db;
8
9use crate::{config::create_data_dir, param::Params};
10
11pub const DEFAULT_USER_ID: &[u8] = b"default";
13const DEFAULT_USER_NAME: &str = "Default";
14
15#[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 pub fn manager(&self) -> &koibumi_box::Manager {
28 &self.manager
29 }
30
31 pub fn manager_mut(&mut self) -> &mut koibumi_box::Manager {
33 &mut self.manager
34 }
35
36 pub fn user(&self) -> &koibumi_box::User {
38 &self.user
39 }
40
41 pub fn user_mut(&mut self) -> &mut koibumi_box::User {
43 &mut self.user
44 }
45
46 pub fn unread_count(&self) -> usize {
48 self.unread_count
49 }
50
51 pub fn set_unread_count(&mut self, count: usize) {
53 self.unread_count = count;
54 }
55
56 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 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 pub fn selected_identity_index(&self) -> Option<usize> {
76 self.selected_identity_index
77 }
78
79 pub fn set_selected_identity_index(&mut self, value: Option<usize>) {
81 self.selected_identity_index = value
82 }
83
84 pub fn selected_contact_index(&self) -> Option<usize> {
86 self.selected_contact_index
87 }
88
89 pub fn set_selected_contact_index(&mut self, value: Option<usize>) {
91 self.selected_contact_index = value
92 }
93}
94
95#[derive(Debug)]
97pub enum Error {
98 IoError(io::Error),
101 SqlxError(sqlx::Error),
104 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
139pub 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 Ok(Boxes {
171 manager,
172 user,
173 unread_count: 0,
174 selected_identity_index: None,
175 selected_contact_index: None,
176 })
177}