Skip to content

Commit

Permalink
Extracted structs into their own modules and refactored to be DRY.
Browse files Browse the repository at this point in the history
  • Loading branch information
gitkeeper committed Apr 22, 2024
1 parent b1ad0e9 commit 77d4f24
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 160 deletions.
8 changes: 5 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- None
- Extracted the task struct from [lib.rs](src/lib.rs) to [task.rs](src/task.rs).
- Extracted available commands from [lib.rs](src/lib.rs) to [command.rs](src/command.rs).
- The database is now passed around as a reference instead of instantiating it in every command.

### Deprecated

- None

### Removed

- Removed the error module for now and simplified error handling in general by introducing a
- Removed the `error.rs` module for now and simplified error handling in general by introducing a
custom Result and Error type for the whole library.

### Fixed

- None
- All `use` statements now use {} for multiple imports from the same module more consistently.

### Security

Expand Down
37 changes: 18 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
# Pilum

[![Crates.io](https://img.shields.io/crates/v/pilum?style=flat-square)](https://crates.io/gitkeeper/pilum)
[![Crates.io](https://img.shields.io/crates/d/pilum?style=flat-square)](https://crates.io/gitkeeper/pilum)
[![License](https://img.shields.io/badge/license-Apache%202.0-blue?style=flat-square)](LICENSE-APACHE.md)
[![License](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE-MIT.md)
[![Build Status](https://img.shields.io/github/actions/workflow/status/gitkeeper/pilum/rust.yml?branch=main&style=flat-square)](https://github.com/gitkeeper/pilum/actions/workflows/rust.yml?query=branch%3Amain)
[![Coverage Status](https://img.shields.io/coveralls/github/gitkeeper/pilum/main?style=flat-square)](https://coveralls.io/github/gitkeeper/pilum?branch=main)
[![Contributors](https://img.shields.io/github/contributors/gitkeeper/pilum?style=flat-square)](https://github.com/gitkeeper/pilum/graphs/contributors)

## About

Pilum is a sophisticated task manager with a CLI and a GUI written in Rust.

Pilum serves as a convenient and easy-to-use task management tool, operated via the command line
and a graphical interface. It keeps track of your to-do tasks, enabling operations like adding,
removing and altering tasks as per your requirements. Pilum is equipped with a wide range of
commands for sophisticated task manipulations.
Pilum serves as a convenient and easy-to-use task management tool, operated via the command line and
a graphical interface. It keeps track of your to-do tasks, enabling operations like adding, removing
and altering tasks as per your requirements. Pilum is equipped with a wide range of commands for
sophisticated task manipulations.

Essentially, Pilum functions as a list organizer. You can feed details, along with their
respective parameters, and the program neatly structures and displays it. By integrating
deadlines and recurring tasks, it becomes a comprehensive to-do manager. Further refinement
is achieved by incorporating elements like priorities, tags, project groups and more, making
Pilum a fully-fledged task organization program.
Essentially, Pilum functions as a list organizer. You can feed details, along with their respective
parameters, and the program neatly structures and displays it. By integrating deadlines and
recurring tasks, it becomes a comprehensive to-do manager. Further refinement is achieved by
incorporating elements like priorities, tags, project groups and more, making Pilum a fully-fledged
task organization program.

## Installation

You must have [Rust](https://www.rust-lang.org/) installed on your machine. To install Rust, it's
recommended to follow [Rust's installation instructions](https://www.rust-lang.org/tools/install)
for your .
For the time being, you must have [Rust](https://www.rust-lang.org/) installed on your machine. To install Rust, it's
recommended to follow [Rust's installation instructions](https://www.rust-lang.org/tools/install) for your respective operating system.

Afterward, you can install this package by running `cargo install pilum`.
Afterward, you can install Pilum by running `cargo install pilum`.

## Usage

Expand All @@ -39,7 +35,7 @@ Pilum's command-line interface is invoked with the following pattern:

The following commands are available as of now:

- `pilum add <task name>`
- `pilum add <task name>...`
- `pilum all`

## Development
Expand All @@ -52,7 +48,8 @@ application you may run `cargo bench`.
If you work on new features, you should also add the proper documentation. Please run `cargo doc`
before you create a pull request and especially before you publish. To release a new version,
update the version number in [Cargo.toml](Cargo.toml), and then run `cargo publish`, which will
upload the package to [crates.io](https://crates.io).
upload the package to [crates.io](https://crates.io). The project adheres to the
[semantic versioning](https://semver.org/) standard.

## Contributing

Expand All @@ -63,4 +60,6 @@ to adhere to the [code of conduct](CODE_OF_CONDUCT.md).
## License

This package is available as open source and dual-licensed under the terms of
[Apache 2.0](LICENSE-APACHE.md) or [MIT](LICENSE-MIT.md).
[Apache 2.0](LICENSE-APACHE.md) and [MIT](LICENSE-MIT.md). The user or developer choosing to use,
distribute, or modify this software can opt to do so under either of these licenses, adhering to the
terms and conditions defined by the license they choose.
9 changes: 5 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
//! executing the corresponding commands. It uses the `Commands` enum to define
//! the available commands for the application.
//!
use crate::*;

use crate::database::Database;
use crate::{command::*, Result};
use clap::{Parser, Subcommand};

/// The `Commands` enum defines the available commands for the application.
Expand Down Expand Up @@ -73,10 +73,11 @@ impl Cli {
///
pub async fn run() -> Result<()> {
let args = Cli::parse();
let db = Database::initialize().await?;

match args.command {
Some(Commands::Add { names }) => add_task(names).await?,
Some(Commands::All) => list_all_tasks().await?,
Some(Commands::Add { names }) => add_task(&db, names).await?,
Some(Commands::All) => list_all_tasks(&db).await?,
None => exit_no_subcommand(),
_ => exit_unknown_subcommand(),
}
Expand Down
100 changes: 100 additions & 0 deletions src/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//! The `command` module contains functions that implement the command-line
//! interface for the application.
//!
//! Public functions:
//! - The public `add_task` function adds a new pending task to the task list.
//! - The public `list_all_tasks` function lists all the tasks in the task list.
//!
//! Private functions:
//! - The private `next_task_number` function gets the next task number to be used.
//!
use crate::{task::Task, Result};
use surrealdb::{engine::local::Db, Surreal};

/// Adds a new pending task to the task list.
///
/// The `add_task` function takes a `name` parameter, which is the name of the task
/// to be added. It then initializes the SurrealDB database and prints a message
/// indicating that the task has been created.
///
/// The function is asynchronous because it calls the `Database::new` method, which
/// is asynchronous.
///
/// # Parameters
/// - `name`: The name of the task to be added.
///
/// # Panics
/// The function will panic if the database connection fails.
///
/// # Errors
/// The function will return an error if the database connection fails.
///
pub async fn add_task(db: &Surreal<Db>, names: Vec<String>) -> Result<()> {
for name in names {
let number = next_task_number(db).await?;
let created: Vec<Task> = db.create("tasks").content(Task::new(number, name)).await?;
let task = created.first().ok_or("Failed to add task.")?;

println!("Created task {}: {}", task.number(), task.name());
}

Ok(())
}

/// Lists all the tasks in the task list.
///
/// The `list_all_tasks` function initializes the SurrealDB database and queries
/// the database for all the tasks. It then prints the task number and name for
/// each task found in the database.
///
/// # Parameters
///
/// There are no parameters for this function.
///
/// # Returns
///
/// The function returns `Ok(())` if the operation is successful.
///
/// # Errors
///
/// The function will return an error if the database query fails.
///
pub async fn list_all_tasks(db: &Surreal<Db>) -> Result<()> {
let mut response = db.query("SELECT * FROM tasks ORDER BY number ASC").await?;
let tasks: Vec<Task> = response.take(0)?;

if tasks.is_empty() {
println!("No tasks found.");
} else {
for task in tasks {
println!("{}: {}", task.number(), task.name());
}
}

Ok(())
}

/// Gets the next task number.
///
/// The `next_task_number` function takes a reference to the SurrealDB database
/// and returns the next task number to be used. It queries the database for the
/// existing tasks, finds the maximum task number, and increments it by one to
/// get the next task number.
///
/// # Parameters
///
/// - `db`: A reference to the SurrealDB database.
///
/// # Returns
///
/// The function returns the next task number to be used.
///
/// # Errors
///
/// The function will return an error if the database query fails.
///
async fn next_task_number(db: &Surreal<Db>) -> Result<i64> {
let tasks: Vec<Task> = db.select("tasks").await?;
let next_number = tasks.iter().map(|t| t.number()).max().unwrap_or(0) + 1;
Ok(next_number)
}
50 changes: 28 additions & 22 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
//! uses the `dirs` crate to get the home directory and appends the default
//! directory to it.
//!
use super::Result;
use crate::Result;

use std::path::PathBuf;
use surrealdb::engine::local::{Db, RocksDb};
use surrealdb::Surreal;
use surrealdb::{
engine::local::{Db, RocksDb},
Surreal,
};

/// The `Database` struct provides a way to interact with the SurrealDB database.
/// It does not hold any data itself, but provides methods for initializing and
Expand Down Expand Up @@ -56,10 +58,10 @@ impl Database {
/// database directory using `Database::cleanup()`.
///
pub async fn initialize() -> Result<Surreal<Db>> {
let mut endpoint = Self::namespace_production().join(Self::DATABASE);
let mut endpoint = Self::namespace_production()?.join(Self::DATABASE);

if std::env::var("PILUM_MODE").is_ok_and(|m| m == "test") {
endpoint = Self::namespace_test().join(Self::DATABASE);
endpoint = Self::namespace_test()?.join(Self::DATABASE);
}

Self::connect(endpoint).await
Expand Down Expand Up @@ -88,37 +90,41 @@ impl Database {
/// This method is used to remove the database directory. This function
/// deletes the whole application directory in production!
///
pub fn cleanup_production() -> std::io::Result<()> {
if Self::namespace_production().exists() {
std::fs::remove_dir_all(Self::namespace_production())?
}
Ok(())
pub fn cleanup_production() -> Result<()> {
Self::cleanup(Self::namespace_production()?)
}

/// Removes the test database directory.
///
/// This method is used to remove the test database directory. It is typically
/// used after running the tests to clean up the test database.
///
pub fn cleanup_test() -> std::io::Result<()> {
if Self::namespace_test().exists() {
std::fs::remove_dir_all(Self::namespace_test())?
pub fn cleanup_test() -> Result<()> {
Self::cleanup(Self::namespace_test()?)
}

/// Removes the directory at the given path.
///
/// This method is used to remove a directory. It checks if the directory
/// exists and then deletes it.
///
fn cleanup(path: PathBuf) -> Result<()> {
if path.exists() {
std::fs::remove_dir_all(&path)?
}
Ok(())
}

/// Returns the path where the production database resides in.
fn namespace_production() -> PathBuf {
dirs::home_dir()
.unwrap()
.join(format!(".{}", Self::NAMESPACE))
fn namespace_production() -> Result<PathBuf> {
let home_dir = dirs::home_dir().ok_or("Failed to get home directory.")?;
Ok(home_dir.join(format!(".{}", Self::NAMESPACE)))
}

/// Returns the path where the test database resides in.
fn namespace_test() -> PathBuf {
std::env::current_dir()
.unwrap()
.join("tmp")
.join(Self::NAMESPACE)
fn namespace_test() -> Result<PathBuf> {
let current_dir = std::env::current_dir()
.map_err(|e| format!("Failed to get current directory: {}", e))?;
Ok(current_dir.join("tmp").join(Self::NAMESPACE))
}
}
Loading

0 comments on commit 77d4f24

Please sign in to comment.