Skip to content

Commit ec79c76

Browse files
authored
Refactoring structure (#16)
* refactoring commands * changed InputMode::Normal to InputMode::View App.error_message and is_error are now merged into a single field called error that is an Option<String> * show a banner "Error" in front of an error message
1 parent f0b8b13 commit ec79c76

File tree

7 files changed

+129
-115
lines changed

7 files changed

+129
-115
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "task_rustler"
3-
version = "0.3.1"
3+
version = "0.3.2"
44
authors = ["r366y"]
55
edition = "2021"
66

src/app.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ impl TaskList {
1717
}
1818
#[derive(Debug)]
1919
pub enum InputMode {
20-
Normal,
20+
View,
2121
Adding,
2222
EditingExisting,
2323
}
@@ -37,8 +37,7 @@ pub struct App {
3737
pub input_field: InputField,
3838
pub tasks_service: TasksService,
3939
pub show_popup: bool,
40-
pub error_message: String,
41-
pub is_error: bool,
40+
pub error: Option<String>,
4241
}
4342

4443
impl App {
@@ -48,12 +47,11 @@ impl App {
4847
input_title: String::new(),
4948
input_description: String::new(),
5049
input_date: String::new(),
51-
input_mode: InputMode::Normal,
50+
input_mode: InputMode::View,
5251
input_field: InputField::Title,
5352
tasks_service: TasksService::new(db_path),
5453
show_popup: false,
55-
error_message: String::new(),
56-
is_error: false,
54+
error: None,
5755
}
5856
}
5957

@@ -108,4 +106,12 @@ impl App {
108106
}
109107
}
110108
}
109+
110+
pub fn handle_char_input(&mut self, c: char) {
111+
match self.input_field {
112+
InputField::Title => self.input_title.push(c),
113+
InputField::Description => self.input_description.push(c),
114+
InputField::Date => self.input_date.push(c),
115+
}
116+
}
111117
}

src/command.rs

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
use crate::app::{App, InputField, InputMode};
22
use crate::date::{TaskDate, DATE_FORMAT};
33
use crate::task::Task;
4-
use anyhow::{Context, Result};
4+
use anyhow::{anyhow, Context, Result};
55

66
pub trait Command {
7-
fn execute(&mut self, app: &mut App) -> Result<()>;
7+
fn execute(&self, app: &mut App) -> Result<()>;
88
}
99

10-
pub struct EnterEditModeCommand;
10+
/// Enter in add command input mode
11+
pub struct EnterAddModeCommand;
1112

12-
impl Command for EnterEditModeCommand {
13-
fn execute(&mut self, app: &mut App) -> Result<()> {
13+
impl Command for EnterAddModeCommand {
14+
fn execute(&self, app: &mut App) -> Result<()> {
1415
app.input_mode = InputMode::Adding;
1516
app.input_field = InputField::Title;
1617
Ok(())
@@ -21,8 +22,11 @@ impl Command for EnterEditModeCommand {
2122
pub struct AddTaskCommand;
2223

2324
impl Command for AddTaskCommand {
24-
fn execute(&mut self, app: &mut App) -> Result<()> {
25+
fn execute(&self, app: &mut App) -> Result<()> {
2526
let mut t = Task::new();
27+
if app.input_title.is_empty() {
28+
return Err(anyhow!("You must insert at least a title for the task"));
29+
}
2630
if !app.input_date.is_empty() {
2731
t.date = TaskDate::try_from(app.input_date.drain(..).collect::<String>())
2832
.context("Invalid date format, use dd-mm-yyyy")?;
@@ -35,45 +39,12 @@ impl Command for AddTaskCommand {
3539
}
3640
}
3741

38-
/// Toggle completed for selected task status
39-
pub struct ToggleTaskStatusCommand;
40-
41-
impl Command for ToggleTaskStatusCommand {
42-
fn execute(&mut self, app: &mut App) -> Result<()> {
43-
if let Some(index) = app.task_list.state.selected() {
44-
let item = &mut app.task_list.items[index];
45-
item.completed = match item.completed {
46-
true => false,
47-
false => true,
48-
};
49-
let _ = app
50-
.tasks_service
51-
.toggle_task_status(item.id, item.completed);
52-
};
53-
Ok(())
54-
}
55-
}
56-
57-
/// Switch between priorities
58-
pub struct ToggleItemPriorityCommand;
59-
60-
impl Command for ToggleItemPriorityCommand {
61-
fn execute(&mut self, app: &mut App) -> Result<()> {
62-
if let Some(index) = app.task_list.state.selected() {
63-
let item = &mut app.task_list.items[index];
64-
item.priority = item.priority.next();
65-
app.tasks_service.change_priority(item.id, &item.priority);
66-
}
67-
Ok(())
68-
}
69-
}
70-
7142
/// Start editing a task, move cursor to Title input field
7243
/// and set InputMode equal to InputMode::EditingExisting
7344
pub struct StartEditingExistingTaskCommand;
7445

7546
impl Command for StartEditingExistingTaskCommand {
76-
fn execute(&mut self, app: &mut App) -> Result<()> {
47+
fn execute(&self, app: &mut App) -> Result<()> {
7748
if let Some(index) = app.task_list.state.selected() {
7849
app.input_title = app.task_list.items[index].title.clone();
7950
app.input_description = app.task_list.items[index].description.clone();
@@ -94,8 +65,11 @@ impl Command for StartEditingExistingTaskCommand {
9465
pub struct FinishEditingExistingTaskCommand;
9566

9667
impl Command for FinishEditingExistingTaskCommand {
97-
fn execute(&mut self, app: &mut App) -> Result<()> {
68+
fn execute(&self, app: &mut App) -> Result<()> {
9869
if let Some(index) = app.task_list.state.selected() {
70+
if app.input_title.is_empty() {
71+
return Err(anyhow!("You must insert at least a title for the task"));
72+
}
9973
if !app.input_date.is_empty() {
10074
app.task_list.items[index].date =
10175
TaskDate::try_from(app.input_date.drain(..).collect::<String>())
@@ -111,10 +85,44 @@ impl Command for FinishEditingExistingTaskCommand {
11185
}
11286
}
11387

88+
89+
/// Toggle completed for selected task status
90+
pub struct ToggleTaskStatusCommand;
91+
92+
impl Command for ToggleTaskStatusCommand {
93+
fn execute(&self, app: &mut App) -> Result<()> {
94+
if let Some(index) = app.task_list.state.selected() {
95+
let item = &mut app.task_list.items[index];
96+
item.completed = match item.completed {
97+
true => false,
98+
false => true,
99+
};
100+
let _ = app
101+
.tasks_service
102+
.toggle_task_status(item.id, item.completed);
103+
};
104+
Ok(())
105+
}
106+
}
107+
108+
/// Switch between priorities
109+
pub struct ToggleItemPriorityCommand;
110+
111+
impl Command for ToggleItemPriorityCommand {
112+
fn execute(&self, app: &mut App) -> Result<()> {
113+
if let Some(index) = app.task_list.state.selected() {
114+
let item = &mut app.task_list.items[index];
115+
item.priority = item.priority.next();
116+
app.tasks_service.change_priority(item.id, &item.priority);
117+
}
118+
Ok(())
119+
}
120+
}
121+
114122
pub struct DeleteTaskCommand;
115123

116124
impl Command for DeleteTaskCommand {
117-
fn execute(&mut self, app: &mut App) -> Result<()> {
125+
fn execute(&self, app: &mut App) -> Result<()> {
118126
if let Some(index) = app.task_list.state.selected() {
119127
app.tasks_service.delete_task(app.task_list.items[index].id);
120128
app.task_list.items.remove(index);
@@ -128,8 +136,8 @@ impl Command for DeleteTaskCommand {
128136
pub struct StopEditingCommand;
129137

130138
impl Command for StopEditingCommand {
131-
fn execute(&mut self, app: &mut App) -> Result<()> {
132-
app.input_mode = InputMode::Normal;
139+
fn execute(&self, app: &mut App) -> Result<()> {
140+
app.input_mode = InputMode::View;
133141
app.input_title.clear();
134142
app.input_description.clear();
135143
app.input_date.clear();

src/main.rs

Lines changed: 23 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use ratatui::crossterm::event::{Event, KeyCode};
33
use ratatui::Terminal;
44
use std::error::Error;
55
use std::io;
6-
use task_rustler::app::{App, InputField, InputMode};
6+
use task_rustler::app::{App, InputMode};
77
use task_rustler::command::*;
88
use task_rustler::ui;
99

@@ -33,7 +33,7 @@ fn run_app<B: ratatui::backend::Backend>(
3333
continue;
3434
}
3535
match app.input_mode {
36-
InputMode::Normal => match key.code {
36+
InputMode::View => match key.code {
3737
KeyCode::Char('q') => {
3838
return Ok(());
3939
}
@@ -45,9 +45,9 @@ fn run_app<B: ratatui::backend::Backend>(
4545
app.show_popup = false;
4646
}
4747
}
48-
_ => handle_key_event_normal_mode(key.code, &mut app),
48+
_ => handle_key_event_view_mode(key.code, &mut app),
4949
},
50-
InputMode::Adding => handle_key_event_editing_mode(key.code, &mut app),
50+
InputMode::Adding => handle_key_event_adding_mode(key.code, &mut app),
5151
InputMode::EditingExisting => {
5252
handle_key_event_editing_existing_mode(key.code, &mut app)
5353
}
@@ -56,10 +56,10 @@ fn run_app<B: ratatui::backend::Backend>(
5656
}
5757
}
5858

59-
fn handle_key_event_normal_mode(key: KeyCode, app: &mut App) {
59+
fn handle_key_event_view_mode(key: KeyCode, app: &mut App) {
6060
match key {
6161
KeyCode::Char('a') => {
62-
let _ = EnterEditModeCommand.execute(app);
62+
let _ = EnterAddModeCommand.execute(app);
6363
}
6464
KeyCode::Down => {
6565
app.select_next();
@@ -86,28 +86,16 @@ fn handle_key_event_normal_mode(key: KeyCode, app: &mut App) {
8686
}
8787
}
8888

89-
fn handle_key_event_editing_mode(key: KeyCode, app: &mut App) {
89+
fn handle_key_event_adding_mode(key: KeyCode, app: &mut App) {
9090
match key {
9191
KeyCode::Enter => {
92-
if !app.input_title.is_empty() {
93-
if let Err(e) = AddTaskCommand.execute(app) {
94-
app.is_error = true;
95-
app.error_message = e.to_string();
96-
} else {
97-
app.is_error = false;
98-
app.error_message.clear();
99-
}
100-
}
101-
if !app.is_error {
102-
app.input_mode = InputMode::Normal;
92+
handle_errors(AddTaskCommand, app);
93+
if app.error.is_none() {
94+
app.input_mode = InputMode::View;
10395
}
10496
}
10597
KeyCode::Tab => app.next_input_field(),
106-
KeyCode::Char(c) => match app.input_field {
107-
InputField::Title => app.input_title.push(c),
108-
InputField::Description => app.input_description.push(c),
109-
InputField::Date => app.input_date.push(c),
110-
},
98+
KeyCode::Char(c) => app.handle_char_input(c),
11199
KeyCode::Backspace => app.handle_backspace(),
112100
KeyCode::Esc => {
113101
let _ = StopEditingCommand.execute(app);
@@ -120,28 +108,24 @@ fn handle_key_event_editing_existing_mode(key: KeyCode, app: &mut App) {
120108
match key {
121109
KeyCode::Tab => app.next_input_field(),
122110
KeyCode::Enter => {
123-
if !app.input_title.is_empty() {
124-
if let Err(e) = FinishEditingExistingTaskCommand.execute(app) {
125-
app.is_error = true;
126-
app.error_message = e.to_string();
127-
} else {
128-
app.is_error = false;
129-
app.error_message.clear();
130-
}
131-
}
132-
if !app.is_error {
133-
app.input_mode = InputMode::Normal;
111+
handle_errors(FinishEditingExistingTaskCommand, app);
112+
if app.error.is_none() {
113+
app.input_mode = InputMode::View;
134114
}
135115
}
136-
KeyCode::Char(c) => match app.input_field {
137-
InputField::Title => app.input_title.push(c),
138-
InputField::Description => app.input_description.push(c),
139-
InputField::Date => app.input_date.push(c),
140-
},
116+
KeyCode::Char(c) => app.handle_char_input(c),
141117
KeyCode::Backspace => app.handle_backspace(),
142118
KeyCode::Esc => {
143119
let _ = StopEditingCommand.execute(app);
144120
}
145121
_ => {}
146122
}
147123
}
124+
125+
fn handle_errors<T: Command>(command:T, app: &mut App) {
126+
if let Err(e) = command.execute(app) {
127+
app.error= Some(e.to_string());
128+
} else {
129+
app.error = None;
130+
}
131+
}

0 commit comments

Comments
 (0)