Skip to content

Commit

Permalink
refactor: sqlite db pool connect and error handle
Browse files Browse the repository at this point in the history
  • Loading branch information
D-lyw committed May 28, 2024
1 parent 56c63e2 commit 2a25c06
Show file tree
Hide file tree
Showing 11 changed files with 180 additions and 93 deletions.
10 changes: 6 additions & 4 deletions src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ tauri-plugin-store = { git = "https://github.com/tauri-apps/plugins-workspace",
sea-orm = { version = "0.12", features = [ "sqlx-sqlite", "runtime-tokio-rustls", "macros" ] }
tokio = { version = "1", features = ["full"] }
anyhow = "1.0.83"
thiserror = "1.0.61"
dotenv = "0.15.0"
chrono = '0.4.38'
tauri-plugin-authenticator = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "v1" }
tracing = "0.1"
tracing-subscriber = "0.3.18"
sqlx = "0.7.4"

[features]
# This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!!
Expand Down
Binary file modified src-tauri/db.sqlite
Binary file not shown.
37 changes: 22 additions & 15 deletions src-tauri/src/commands/todo.rs
Original file line number Diff line number Diff line change
@@ -1,32 +1,39 @@
use entity::todos;
use sea_orm::DbErr;
use sea_orm::DbConn;
use tauri::State;

use crate::service::{Mutation, Query};
use crate::{
error::AppError,
service::{Mutation, Query},
};

#[tauri::command]
pub async fn add_todo_item(title: String) -> Result<bool, String> {
let result = Mutation::create_todo_item(title).await;
pub async fn add_todo_item(db: State<'_, DbConn>, title: String) -> Result<bool, String> {
let result = Mutation::create_todo_item(db.inner(), title).await;
Ok(result.is_ok())
}

#[tauri::command]
pub async fn query_list_by_page(page: u64) -> Result<Vec<todos::Model>, String> {
match Query::query_todo_list(page).await {
pub async fn query_list_by_page(
db: State<'_, DbConn>,
page: u64,
) -> Result<Vec<todos::Model>, String> {
match Query::query_todo_list(db.inner(), page).await {
Ok(result) => Ok(result),
Err(err) => Err(err.to_string()),
}
}

#[tauri::command]
pub async fn delete_item_by_id(id: i32) -> Result<bool, String> {
let result = Mutation::delete_item_by_id(id).await;
Ok(result.is_ok())
pub async fn delete_item_by_id(db: State<'_, DbConn>, id: i32) -> Result<bool, AppError> {
Mutation::delete_item_by_id(&db, id).await
}

#[tauri::command]
pub async fn switch_item_status(id: i32, is_done: bool) -> Result<bool, String> {
match Mutation::switch_item_status(id, is_done).await {
Ok(result) => Ok(result),
Err(err) => Err(err.to_string()),
}
}
pub async fn switch_item_status(
db: State<'_, DbConn>,
id: i32,
is_done: bool,
) -> Result<bool, AppError> {
Mutation::switch_item_status(db.inner(), id, is_done).await
}
36 changes: 36 additions & 0 deletions src-tauri/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use sea_orm::DbErr;
use serde::Serialize;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum AppError {
#[error("The item with a specified id is not found")]
RowNotFound,
#[error("Call command parameter is not valid")]
ParamsError,
#[error(transparent)]
DatabaseError(DbErr),
#[error(transparent)]
Unexpected(anyhow::Error),
}

impl From<anyhow::Error> for AppError {
fn from(err: anyhow::Error) -> Self {
AppError::Unexpected(err)
}
}

impl From<DbErr> for AppError {
fn from(err: DbErr) -> Self {
AppError::DatabaseError(err)
}
}

impl Serialize for AppError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_string().as_ref())
}
}
16 changes: 6 additions & 10 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,23 @@
mod commands;
mod service;
mod utils;
mod setup;
mod error;
pub use commands::todo;
use std::{env, sync::OnceLock};
pub use utils::*;

// use anyhow::Result;
use dotenv::dotenv;
use tauri::{
CustomMenuItem, Menu, MenuEntry, MenuItem, Submenu, SystemTray, SystemTrayMenu,
SystemTrayMenuItem,
CustomMenuItem, Manager, Menu, MenuEntry, MenuItem, Submenu, SystemTray, SystemTrayMenu, SystemTrayMenuItem
};

pub static APP: OnceLock<tauri::AppHandle> = OnceLock::new();

#[tokio::main]
async fn main() {
tauri::async_runtime::set(tokio::runtime::Handle::current());

dotenv().ok();

let quit = CustomMenuItem::new("quit".to_string(), "Quit");
Expand Down Expand Up @@ -49,13 +51,7 @@ async fn main() {
todo::delete_item_by_id,
todo::switch_item_status
])
.setup(|app| {
APP.set(app.handle())
.unwrap_or_else(|_| panic!("Failed to initialize APP"));

init_db();
Ok(())
})
.setup(setup::setup)
.run(tauri::generate_context!())
.expect("error while running tauri application");
}
26 changes: 12 additions & 14 deletions src-tauri/src/service/mutation.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,36 @@
use chrono::SecondsFormat;
use entity::todos;
use sea_orm::{ActiveModelTrait, ActiveValue::NotSet, DbErr, EntityTrait, Set};
use sea_orm::{ActiveModelTrait, ActiveValue::NotSet, DbConn, EntityTrait, Set};

use crate::establish_connection;
use crate::error::AppError;

pub struct Mutation;

impl Mutation {
pub async fn create_todo_item(title: String) -> Result<todos::ActiveModel, DbErr> {
let db = establish_connection().await?;
todos::ActiveModel {
pub async fn create_todo_item(db: &DbConn,title: String) -> Result<todos::ActiveModel, AppError> {
let result = todos::ActiveModel {
id: NotSet,
title: Set(title),
datetime: Set(chrono::Utc::now().to_rfc3339_opts(SecondsFormat::Millis, true)),
..Default::default()
}
.save(&db)
.await
.save(db)
.await?;
Ok(result)
}

pub async fn delete_item_by_id(id: i32) -> Result<bool, DbErr> {
let db = establish_connection().await?;
todos::Entity::delete_by_id(id).exec(&db).await?;
pub async fn delete_item_by_id(db: &DbConn, id: i32) -> Result<bool, AppError> {
todos::Entity::delete_by_id(id).exec(db).await?;
Ok(true)
}

pub async fn switch_item_status(id: i32, is_done: bool) -> Result<bool, DbErr> {
let db = establish_connection().await?;
let item = todos::Entity::find_by_id(id).one(&db).await?;
pub async fn switch_item_status(db: &DbConn, id: i32, is_done: bool) -> Result<bool, AppError> {
let item = todos::Entity::find_by_id(id).one(db).await?;
match item {
Some(item) => {
let mut item: todos::ActiveModel = item.into();
item.done = Set(Some(is_done));
item.save(&db).await?;
item.save(db).await?;
Ok(true)
}
None => Ok(false),
Expand Down
10 changes: 4 additions & 6 deletions src-tauri/src/service/query.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
use entity::todos;
use sea_orm::{entity::*, query::*, DbErr};

use crate::establish_connection;
use sea_orm::{entity::*, query::*, DbConn};

use crate::error::AppError;

pub struct Query ;

impl Query {
pub async fn query_todo_list(page_num: u64 ) -> Result<Vec<todos::Model>, DbErr> {
let db = establish_connection().await?;
let todo_pages = todos::Entity::find().order_by_desc(todos::Column::Id).paginate(&db, 100);
pub async fn query_todo_list(db: &DbConn, page_num: u64 ) -> Result<Vec<todos::Model>, AppError> {
let todo_pages = todos::Entity::find().order_by_desc(todos::Column::Id).paginate(db, 100);

let list = todo_pages.fetch_page(page_num - 1).await?;
Ok(list)
Expand Down
46 changes: 46 additions & 0 deletions src-tauri/src/setup.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::env;

use migration::{Migrator, MigratorTrait};
use sea_orm::{DatabaseConnection, SqlxSqliteConnector};
use sqlx::SqlitePool;
use tauri::{api::path::app_data_dir, App, Config, Manager};

pub fn setup(app: &mut App) -> Result<(), Box<dyn std::error::Error>> {
let handler = app.handle();
tokio::spawn(async move {
let db_conn = get_database_pool(&handler.config()).await;
handler.manage(db_conn);
});

Ok(())
}

pub async fn get_database_pool(config: &Config) -> DatabaseConnection {
let database_url = get_db_path(config);

let pool = SqlitePool::connect(&database_url)
.await
.expect("Failed to connect database");

let db_conn = SqlxSqliteConnector::from_sqlx_sqlite_pool(pool);

Migrator::up(&db_conn, None)
.await
.expect("Failed to migrate");

db_conn
}

pub fn get_db_path(config: &Config) -> String {
if cfg!(debug_assertions) {
env::var("DATABASE_URL").expect("DATABASE_URL must be set")
} else {
let app_data_dir = app_data_dir(config).expect("DATABASE_URL must be");
std::fs::create_dir_all(&app_data_dir).unwrap();

format!(
"sqlite://{}?mode=rwc",
app_data_dir.join("db.sqlite").to_string_lossy()
)
}
}
88 changes: 45 additions & 43 deletions src-tauri/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,51 @@ use std::path::Path;

use migration::Migrator;
use migration::MigratorTrait;
use sea_orm::{Database, DbConn, DbErr};
use sea_orm::{Database, DatabaseConnection, DbConn, DbErr, SqlxSqliteConnector};
use sqlx::SqlitePool;
use tauri::App;

use crate::APP;

pub async fn establish_connection() -> Result<DbConn, DbErr> {
let database_url = format!("sqlite://{}?mode=rwc", get_db_path());

let db = Database::connect(database_url).await?;

// TODO: refactor only migrate database once
Migrator::up(&db, None).await?;

Ok(db)
}

pub fn init_db() {
let db_path = get_db_path();
let db_dir = Path::new(&db_path).parent().unwrap();
if !db_dir.exists() {
create_dir_all(db_dir).unwrap();
}
if !Path::new(&db_path).exists() {
File::create(&db_path).unwrap();
}
// let db = Database::connect(database_url).await?;

// Migrator::up(&db, None).await?;
}

pub fn get_db_path() -> String {
// development environment path
if cfg!(debug_assertions) {
env::var("DATABASE_URL").expect("DATABASE_URL must be set")
} else {
let data_dir = APP
.get()
.expect("fail")
.path_resolver()
.app_data_dir()
.expect("fail");
let db_path = data_dir.join("db.sqlite");
println!("{}", db_path.to_string_lossy());

db_path.to_str().unwrap().to_string()
}
}
// pub async fn establish_connection() -> Result<DbConn, DbErr> {
// let database_url = format!("sqlite://{}?mode=rwc", get_db_path());

// let db = Database::connect(database_url).await?;

// // TODO: refactor only migrate database once
// Migrator::up(&db, None).await?;

// Ok(db)
// }

// pub fn init_db() {
// let db_path = get_db_path();
// let db_dir = Path::new(&db_path).parent().unwrap();
// if !db_dir.exists() {
// create_dir_all(db_dir).unwrap();
// }
// if !Path::new(&db_path).exists() {
// File::create(&db_path).unwrap();
// }
// // let db = Database::connect(database_url).await?;

// // Migrator::up(&db, None).await?;
// }

// pub fn get_db_path() -> String {
// // development environment path
// if cfg!(debug_assertions) {
// env::var("DATABASE_URL").expect("DATABASE_URL must be set")
// } else {
// let data_dir = APP
// .get()
// .expect("fail")
// .path_resolver()
// .app_data_dir()
// .expect("fail");
// let db_path = data_dir.join("db.sqlite");
// println!("{}", db_path.to_string_lossy());

// db_path.to_str().unwrap().to_string()
// }
// }
Loading

0 comments on commit 2a25c06

Please sign in to comment.