Skip to content

Commit

Permalink
Merge pull request #4 from giuseppe-g-gelardi/async
Browse files Browse the repository at this point in the history
start add async features
  • Loading branch information
giuseppe-g-gelardi authored Dec 2, 2024
2 parents f52de5c + 783aa5f commit 318ce48
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 87 deletions.
10 changes: 8 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
[package]
name = "cargobase"
version = "0.1.0"
version = "0.1.1"
edition = "2021"

[dependencies]
serde = { version = "1.0.215", features = ["derive"] }
serde_json = { version = "1.0.132", features = ["raw_value"] }
serde_derive = "1.0.188"
base64 = "0.22.1"
tokio = { version = "1", features = ["full"] }
tokio = { version = "1", features = ["full"], optional = true}
uuid = {version ="1.11.0", features = ["v4"] }
thiserror = "2.0.3"
tempfile = "3.14.0"
Expand All @@ -17,3 +17,9 @@ tracing = "0.1"
tracing-subscriber = "0.3"
tracing-test = "0.2.5"

[features]
default = ["sync", "async"] # for development
# default = ["sync"]
sync = [] # synchronous features only
async = ["tokio"] # asynchronous features only
full = ["sync", "async"] # all features
65 changes: 0 additions & 65 deletions TestUpdateAndDelete.json

This file was deleted.

133 changes: 126 additions & 7 deletions src/cargobase/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ impl Database {
let name = name.to_string();
let file_name = format!("{name}.json");

// find a better way of logging this information for the end user
// -- they might not have tracing enabled

if std::path::Path::new(&file_name).exists() {
tracing::info!("Database already exists: {name}, loading database");

Expand All @@ -44,15 +41,42 @@ impl Database {
}
}

#[cfg(feature = "async")]
pub async fn new_async(name: &str) -> Self {
let name = name.to_string();
let file_name = format!("{name}.json");

if tokio::fs::metadata(&file_name).await.is_ok() {
tracing::info!("Database already exists: {name}, loading database");

// Load the database from the file
match Database::load_from_file_async(&file_name).await {
Ok(db) => return db,
Err(e) => {
tracing::error!("Failed to load database from file: {file_name}, error: {e}");
}
}
} else {
tracing::info!("Creating new database: {file_name}");
// Create an empty JSON file for the new database
if let Err(e) = tokio::fs::write(&file_name, "{}").await {
tracing::error!("Failed to create database file: {e}");
}
}

Database {
name,
file_name,
tables: Vec::new(),
}
}

pub fn drop_database(&self) -> Result<(), DatabaseError> {
if std::fs::remove_file(&self.file_name).is_err() {
tracing::error!(
"{}",
DatabaseError::DeleteError("Failed to delete database file".to_string())
);

// should we crash the program?
// return Err(DatabaseError::DeleteError("Failed to delete database file".to_string(),));
}

tracing::info!("Database `{}` dropped successfully", self.name);
Expand Down Expand Up @@ -98,10 +122,26 @@ impl Database {
Ok(())
}

#[cfg(feature = "async")]
pub(crate) async fn save_to_file_async(&self) -> Result<(), tokio::io::Error> {
let json_data = serde_json::to_string_pretty(&self)?;
tokio::fs::write(&self.file_name, json_data).await?;
tracing::info!("Database saved to file: {}", self.file_name);
Ok(())
}

pub(crate) fn load_from_file(file_name: &str) -> Result<Self, std::io::Error> {
let json_data = std::fs::read_to_string(file_name)?;
let db: Database = serde_json::from_str(&json_data)?;
tracing::info!("Database loaded from file: {}", file_name); // needed?
tracing::info!("Database loaded from file: {}", file_name);
Ok(db)
}

#[cfg(feature = "async")]
pub(crate) async fn load_from_file_async(file_name: &str) -> Result<Self, tokio::io::Error> {
let json_data = tokio::fs::read_to_string(file_name).await?;
let db: Database = serde_json::from_str(&json_data)?;
tracing::info!("Database loaded from file: {}", file_name);
Ok(db)
}

Expand Down Expand Up @@ -178,6 +218,9 @@ mod tests {
use crate::cargobase::setup_temp_db;
use crate::{Columns, Table};

#[cfg(feature = "async")]
use crate::cargobase::setup_temp_db_async;

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Default)]
struct TestData {
id: String,
Expand All @@ -196,6 +239,19 @@ mod tests {
assert_eq!(db.tables.len(), 1); // the setup_temp_db function adds a table
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_database_new_async() {
let db = setup_temp_db_async().await;

let db_name = &db.name.to_string();
let fnn = format!("{db_name}.json");

assert_eq!(db.name, db_name.to_string());
assert_eq!(db.file_name, fnn);
assert_eq!(db.tables.len(), 1); // the setup_temp_db function adds a table
}

#[test]
fn test_drop_database() {
let db = setup_temp_db();
Expand Down Expand Up @@ -271,4 +327,67 @@ mod tests {
// Ensure no tables were removed
assert_eq!(db.tables.len(), 1);
}

#[test]
fn test_save_to_file() {
let db = setup_temp_db();
let result = db.save_to_file();

assert!(result.is_ok());
assert!(std::path::Path::new(&db.file_name).exists());
}

#[test]
fn test_load_from_file() {
let db = setup_temp_db();
let loaded_db = Database::load_from_file(&db.file_name).expect("Failed to load database");

assert_eq!(db, loaded_db);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_save_to_file_async() {
use tempfile::NamedTempFile;

let temp_file = NamedTempFile::new().expect("Failed to create a temporary file");
let db_path = temp_file.path().to_str().unwrap().to_string();

let db = Database {
name: "test_db".to_string(),
file_name: db_path.clone(),
tables: vec![],
};

db.save_to_file_async()
.await
.expect("Failed to save database");
let loaded_db = Database::load_from_file(&db_path).expect("Failed to load database");
assert_eq!(db, loaded_db);
}

#[cfg(feature = "async")]
#[tokio::test]
async fn test_load_from_file_async() {
use tempfile::NamedTempFile;

let temp_file = NamedTempFile::new().expect("Failed to create a temporary file");
let db_path = temp_file.path().to_str().unwrap().to_string();

let db = Database {
name: "test_db".to_string(),
file_name: db_path.to_string(),
tables: vec![],
};

db.save_to_file_async()
.await
.expect("Failed to save database");

let loaded_db = Database::load_from_file_async(&db_path)
.await
.expect("Failed to load database");

assert_eq!(db, loaded_db);
}
}
7 changes: 7 additions & 0 deletions src/cargobase/errors/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,11 @@ pub enum DatabaseError {

#[error("")] // could expand to specify serialization/deserialization error
JSONError(#[from] serde_json::Error),

// #[error("IO error: `{0}`")]
// Io(#[from] std::io::Error),
//
// #[cfg(feature = "async")]
// #[error("Tokio IO error: `{0}`")]
// TokioIo(#[from] tokio::io::Error),
}
7 changes: 5 additions & 2 deletions src/cargobase/mod.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
pub mod columns;
pub mod database;
pub mod errors;
pub mod query;
pub mod row;
pub mod table;
pub mod util;
pub mod errors;
pub mod view;

pub use columns::{Column, Columns};
pub use database::Database;
pub use errors::errors::DatabaseError;
pub use query::Query;
pub use row::Row;
pub use table::Table;
pub use util::setup_temp_db;
pub use errors::errors::DatabaseError;
pub use view::View;

#[cfg(feature = "async")]
pub use util::setup_temp_db_async;
Loading

0 comments on commit 318ce48

Please sign in to comment.