Skip to content
This repository has been archived by the owner on Apr 19, 2024. It is now read-only.

Commit

Permalink
Reestructure core to support multiple app ids
Browse files Browse the repository at this point in the history
  • Loading branch information
edfloreshz committed Mar 11, 2024
1 parent f468408 commit ba10c2a
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 93 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ edition = "2021"
tokio = { version = "1.32.0", features = ["full"] }
serde = { version = "1.0.147", features = ["derive"] }
serde_json = "1.0.87"
diesel = { version = "2.0.2", features = ["sqlite", "chrono"] }
diesel = { version = "2.0.2", features = ["sqlite", "chrono", "r2d2"] }
chrono = { version = "0.4.19", features = ["serde"] }
anyhow = "1.0.66"
uuid = { version = "1.2.1", features = ["v4"] }
Expand Down
78 changes: 49 additions & 29 deletions core/src/service.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
use std::fmt::Display;
use std::sync::OnceLock;

use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, EnumString};

use crate::{
services::{
local::service::ComputerStorage, microsoft::service::MicrosoftService,
smart::Smart,
},
task_service::TodoProvider,
};
use serde::{Deserialize, Serialize};
use strum::IntoEnumIterator;
use strum_macros::{EnumIter, EnumString};

static APP_ID: OnceLock<&str> = OnceLock::new();

pub struct Services;

impl Services {
pub fn init(app_id: &'static str) {
APP_ID.get_or_init(|| app_id);
}
}

#[derive(
Debug,
Expand All @@ -24,13 +38,36 @@ use strum_macros::{EnumIter, EnumString};
Deserialize,
)]
pub enum Service {
Smart,
#[default]
Computer,
Microsoft,
Smart,
}

impl Service {
/// Finds the requested service and returns it.
/// After implementing the Service trait in your service
/// struct, register your service here.
pub fn get_service(&self) -> Box<dyn TodoProvider> {
if APP_ID.get().is_none() {
panic!("Must call Service::init before trying to get a service");
}

let app_id = APP_ID.get().unwrap().to_string();

match self {
Service::Smart => Box::new(Smart::new()),
Service::Computer => Box::new(ComputerStorage::new(app_id)),
Service::Microsoft => Box::new(MicrosoftService::new()),
}
}

/// Convenience method to get the list of services.
pub fn list() -> Vec<Self> {
Self::iter().collect()
}

/// Returns the icon for the service.
pub fn icon(&self) -> &str {
match self {
Service::Smart => "dialog-information-symbolic",
Expand All @@ -44,30 +81,13 @@ impl Service {
}
}

impl ToString for Service {
fn to_string(&self) -> String {
match self {
Service::Smart => "Smart lists".into(),
Service::Computer => "Computer".into(),
Service::Microsoft => "Microsoft To Do".into(),
}
}
}

impl Service {
/// Finds the requested service and returns it.
/// After implemeting the Service trait in your service
/// struct, register your service here.
pub fn get_service(&self) -> Box<dyn TodoProvider> {
match self {
Service::Smart => Box::new(Smart::new()),
Service::Computer => Box::new(ComputerStorage::new()),
Service::Microsoft => Box::new(MicrosoftService::new()),
}
}

/// Convenience method to get the list of services.
pub fn list() -> Vec<Self> {
Self::iter().collect()
impl Display for Service {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let str = match self {
Service::Smart => "Smart lists".to_string(),
Service::Computer => "Computer".to_string(),
Service::Microsoft => "Microsoft To Do".to_string(),
};
write!(f, "{}", str)
}
}
92 changes: 73 additions & 19 deletions core/src/services/local/database/mod.rs
Original file line number Diff line number Diff line change
@@ -1,35 +1,76 @@
pub mod models;

use anyhow::{anyhow, Context, Result};
use diesel::{Connection, SqliteConnection};
use anyhow::{anyhow, bail, Result};
use diesel::r2d2;
use diesel::r2d2::{ConnectionManager, PooledConnection};
use diesel::SqliteConnection;
use diesel_migrations::{
embed_migrations, EmbeddedMigrations, MigrationHarness,
};
use libset::Config;
use libset::{Config, FileType};

use crate::services::microsoft::service::APP_ID;
pub mod models;

pub type Pool = r2d2::Pool<r2d2::ConnectionManager<SqliteConnection>>;

pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("migrations");
pub const DATABASE_NAME: &str = "dev.edfloreshz.Done.db";

pub struct Database;
#[derive(Debug, Clone)]
pub struct Database {
application_id: String,
pool: Option<Pool>,
}

impl Database {
fn database_url() -> Result<String> {
let url = Config::new(APP_ID, 1, Some("database"))?
.path(DATABASE_NAME, libset::FileType::Plain)?;
Ok(url.display().to_string())
pub fn new(application_id: String) -> Result<Self> {
let database = Self {
application_id,
pool: None,
};

database
.ensure_database_exists()
.expect("Failed to ensure database exists.");

Ok(database)
}

pub fn establish_connection() -> Result<SqliteConnection> {
SqliteConnection::establish(Database::database_url()?.as_str())
.context("Error connecting to database")
pub fn database_url(&self) -> Result<String> {
let app_id = self.application_id.clone();
let url = Config::new(&app_id, 1, Some("database"))?
.path(&format!("{app_id}.db"), libset::FileType::Plain)?
.display()
.to_string();
Ok(url)
}

pub fn ensure_migrations_up_to_date() -> Result<()> {
let mut connection =
SqliteConnection::establish(Database::database_url()?.as_str())
.context("Error connecting to database")?;
pub fn establish_connection(
&mut self,
) -> Result<PooledConnection<ConnectionManager<SqliteConnection>>> {
if self.pool.is_none() {
let manager =
ConnectionManager::<SqliteConnection>::new(self.database_url()?);
let pool = Pool::builder()
.build(manager)
.expect("Failed to create pool");
self.pool = Some(pool);
}

let Some(pool) = &self.pool else {
bail!("Failed to get pool");
};

self
.ensure_migrations_up_to_date()
.expect("Failed to ensure migrations are up to date");

pool.get().map_err(|e| anyhow!(e))
}

pub fn ensure_migrations_up_to_date(&self) -> Result<()> {
let Some(pool) = &self.pool else {
bail!("Failed to get pool");
};

let mut connection = pool.get()?;
match connection.run_pending_migrations(MIGRATIONS) {
Ok(_) => Ok(()),
Err(err) => {
Expand All @@ -38,4 +79,17 @@ impl Database {
},
}
}

pub fn ensure_database_exists(&self) -> Result<()> {
let app_id = self.application_id.clone();
let database_config = Config::new(&app_id, 1, Some("database"))?;

let database_path =
database_config.path(&format!("{app_id}.db"), FileType::Plain)?;

if !database_path.exists() {
database_config.set_plain(&format!("{app_id}.db"), String::new())?;
}
Ok(())
}
}
Loading

0 comments on commit ba10c2a

Please sign in to comment.