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

Return key #49

Merged
merged 9 commits into from
Feb 28, 2024
271 changes: 97 additions & 174 deletions Cargo.lock

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[package]
name = "notabena"
version = "0.1.0"
version = "0.1.1"
edition = "2021"
authors = [ "ThatFrogDev", "MrSerge01" ]
authors = [ "ThatFrogDev" ]
license-file = "LICENSE.md"
description = "A note-taking app for the command line. GUI support will be added (see README)."
homepage = "https://github.com/thatfrogdev/notabena"
Expand All @@ -14,9 +14,9 @@ categories = [ "command-line-utilities", "gui", "text-processing" ]
[dependencies]
async-std = "1.12.0"
directories = "5.0.1"
chrono = "0.4.33"
chrono = "0.4.34"
dialoguer = "0.11.0"
inquire = "0.6.2"
crossterm = "0.27.0"
rusqlite = { version = "0.30.0", features = ["bundled"] }
rusqlite = { version = "0.31.0", features = ["bundled"] }
termimad = "0.29.1"
tempfile = "3.10.0"
3 changes: 2 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ mod api;
mod note;
mod prompts;
mod return_to_main;
mod tests;
mod utilities;

use crate::{
note::Note,
prompts::{multiselect::multiselect, select::select},
return_to_main::return_to_main,
utilities::{cursor_to_origin::cursor_to_origin, truncate_note::truncate_note},
/* return_to_main::return_to_main, */
};
use async_std::path::PathBuf;
use directories::BaseDirs;
Expand Down
62 changes: 27 additions & 35 deletions src/note.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
api, multiselect,
prompts::{confirm::confirm, input::input, select::select},
truncate_note,
return_to_main, truncate_note,
utilities::{cursor_to_origin::cursor_to_origin, display::display},
};
use async_std::path::PathBuf;
Expand All @@ -19,38 +19,32 @@ pub struct Note {
impl Note {
pub fn create(db_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
cursor_to_origin()?;
println!(
"If you're done inputting a field, you can press Enter twice to continue or save, or Alt/Option-Q to return to the main menu.\r"
);
let mut inputted_note = Note {
id: api::get_notes(db_file)?.len(),
name: input("Name:", "".to_string()),
content: input("Content:", "".to_string()),
name: input("Name:", "".to_string())?,
content: input("Content:", "".to_string())?,
created: format!("{}", Local::now().format("%A %e %B, %H:%M")),
};

cursor_to_origin()?;
println!("This is the note you're about to create:");
display(&mut inputted_note)?;

match confirm("Do you want to save this note?") {
true => {
Connection::open(db_file)?.execute(
"INSERT INTO saved_notes (id, name, content, created) VALUES (?1, ?2, ?3, ?4);",
params![
&inputted_note.id,
&inputted_note.name,
&inputted_note.content,
&inputted_note.created
],
)?;

cursor_to_origin()?;
println!("Note created successfully.");
Ok(())
}
false => {
cursor_to_origin()?;
Ok(())
}
}
Connection::open(db_file)?.execute(
"INSERT INTO saved_notes (id, name, content, created) VALUES (?1, ?2, ?3, ?4);",
params![
&inputted_note.id,
&inputted_note.name,
&inputted_note.content,
&inputted_note.created
],
)?;
cursor_to_origin()?;
println!("Note created successfully.");
Ok(())
}

pub fn show(db_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -74,15 +68,18 @@ impl Note {

pub fn edit(db_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
cursor_to_origin()?;
println!(
"If you're done editing a field, you can press Enter twice to continue or save, or Alt/Option-Q to return to the main menu.\r"
);
let saved_notes = api::get_notes(db_file)?;
let mut options: Vec<String> = Vec::new();
truncate_note(&mut options, db_file)?;
let selection = select("Select the note that you want to edit:", &options);
let selected_note = &saved_notes[selection];
let updated_note = Note {
id: selected_note.id.clone(),
name: input("Name:", selected_note.name.clone()),
content: input("Content:", selected_note.content.clone()),
name: input("Name:", selected_note.name.clone())?,
content: input("Content:", selected_note.content.clone())?,
created: selected_note.created.clone(),
};

Expand All @@ -91,15 +88,10 @@ impl Note {
println!("You can't edit notes, because there are none.");
}

match confirm("Are you sure that you want to edit this note?") {
true => {
cursor_to_origin()?;
api::save_note(&updated_note, db_file)?; // why the fuck whas this line not here yet
println!("Note updated successfully.");
Ok(())
}
false => Ok(()),
}
cursor_to_origin()?;
api::save_note(&updated_note, db_file)?; // why the fuck whas this line not here yet
println!("Note updated successfully.");
Ok(())
}

pub fn delete(db_file: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
Expand Down
19 changes: 17 additions & 2 deletions src/prompts/input.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use crate::return_to_main::{continue_event, return_event};
use crate::{main, return_to_main};
use dialoguer::{theme::ColorfulTheme, Input};

pub fn input(prompt: &str, initial_text: String) -> String {
match initial_text.as_str() {
pub fn input(prompt: &str, initial_text: String) -> Result<String, Box<dyn std::error::Error>> {
let result = match initial_text.as_str() {
"" => Input::with_theme(&ColorfulTheme::default())
.with_prompt(prompt)
.interact_text()
Expand All @@ -11,5 +13,18 @@ pub fn input(prompt: &str, initial_text: String) -> String {
.with_initial_text(initial_text)
.interact_text()
.unwrap(),
};

match return_to_main() {
Ok(value) => {
if value == return_event() {
main()?;
} else if value == continue_event() {
return Ok(result);
}
}
_ => return Ok(result),
}

Ok(result)
}
37 changes: 26 additions & 11 deletions src/return_to_main.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,38 @@
use crossterm::event;
use crossterm::event::{Event, KeyCode, KeyEvent};
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyEventState, KeyModifiers};
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::time::Duration;

// TODO: make it not bitch
pub fn return_to_main() -> Result<(), Box<dyn std::error::Error>> {
// very hacky way to make these values public
pub fn return_event() -> KeyEvent {
KeyEvent {
code: KeyCode::Char('q'),
modifiers: KeyModifiers::ALT,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}
}

pub fn continue_event() -> KeyEvent {
KeyEvent {
code: KeyCode::Enter,
modifiers: KeyModifiers::NONE,
kind: KeyEventKind::Press,
state: KeyEventState::NONE,
}
}

pub fn return_to_main() -> Result<KeyEvent, Box<dyn std::error::Error>> {
enable_raw_mode()?;

loop {
if event::poll(Duration::from_millis(1000))? {
if let Event::Key(event) = event::read()? {
match event {
KeyEvent {
code: KeyCode::Char('r'),
modifiers: event::KeyModifiers::CONTROL,
kind: _,
state: _,
} => return Ok(()),
_ => (),
if event == return_event() {
return Ok(return_event());
}
if event == continue_event() {
return Ok(continue_event());
}
}
}
Expand Down
72 changes: 72 additions & 0 deletions src/tests/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#[cfg(test)]
use crate::{api, note::Note};
use async_std::path::PathBuf;
use chrono::prelude::Local;
use tempfile::tempdir;

#[test]
fn test_db_init() -> Result<(), Box<dyn std::error::Error>> {
let dir = tempdir()?;
let data_directory = PathBuf::from(dir.path());
let db_file = data_directory.join("notes.db");
assert!(api::init_db(&data_directory, &db_file).is_ok());
Ok(())
}

#[test]
fn test_save_note() -> Result<(), Box<dyn std::error::Error>> {
let dir = tempdir()?;
let data_directory = PathBuf::from(dir.path());
let db_file: PathBuf = data_directory.join("notes.db");
api::init_db(&data_directory, &db_file)?;

let note = Note {
id: 0,
name: "Test".to_string(),
content: "Test".to_string(),
created: format!("{}", Local::now().format("%A %e %B, %H:%M")),
};

assert!(api::save_note(&note, &db_file).is_ok());
Ok(())
}

#[test]
fn test_delete_notes() {
let dir = tempdir().unwrap();
let data_directory = PathBuf::from(dir.path());
let db_file = data_directory.join("notes.db");
api::init_db(&data_directory, &db_file).unwrap();

let note = Note {
id: 0,
name: "Test".to_string(),
content: "Test".to_string(),
created: format!("{}", Local::now().format("%A %e %B, %H:%M")),
};

api::save_note(&note, &db_file).unwrap();
let notes = api::get_notes(&db_file).unwrap();
let ids: Vec<usize> = notes.iter().map(|note| note.id).collect();
api::delete_notes(ids, &db_file).unwrap();
assert!(api::get_notes(&db_file).unwrap().is_empty());
}

#[test]
fn test_get_notes() -> Result<(), Box<dyn std::error::Error>> {
let dir = tempdir()?;
let data_directory = PathBuf::from(dir.path());
let db_file = data_directory.join("notes.db");
api::init_db(&data_directory, &db_file)?;

let note = Note {
id: 0,
name: "Test".to_string(),
content: "Test".to_string(),
created: format!("{}", Local::now().format("%A %e %B, %H:%M")),
};

api::save_note(&note, &db_file)?;
assert!(!api::get_notes(&db_file)?.is_empty());
Ok(())
}
1 change: 1 addition & 0 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod api;