WIP: Refactor data types
This commit is contained in:
parent
9b4341ccf1
commit
c999764a28
172
src/app.rs
172
src/app.rs
@ -1,20 +1,26 @@
|
||||
use indexmap::IndexMap;
|
||||
use int_enum::IntEnum;
|
||||
// use indexmap::IndexMap;
|
||||
// use int_enum::IntEnum;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp::min;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
#[repr(usize)]
|
||||
#[derive(
|
||||
Deserialize, Serialize, Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, IntEnum,
|
||||
)]
|
||||
pub enum TaskStatus {
|
||||
Todo = 0,
|
||||
InProgress = 1,
|
||||
Done = 2,
|
||||
Ideas = 3,
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Column {
|
||||
pub name: String,
|
||||
pub selected_task_idx: usize,
|
||||
pub tasks: Vec<Task>,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
pub fn get_selected_task(&self) -> Option<&Task> {
|
||||
self.tasks.get(self.selected_task_idx)
|
||||
}
|
||||
|
||||
pub fn get_selected_task_mut(&mut self) -> Option<&mut Task> {
|
||||
self.tasks.get_mut(self.selected_task_idx)
|
||||
}
|
||||
}
|
||||
|
||||
// #[derive(Deserialize, Serialize, Debug, Clone, Copy)]
|
||||
@ -37,7 +43,8 @@ impl Default for Task {
|
||||
#[derive(Deserialize, Serialize, Debug)]
|
||||
pub struct Project {
|
||||
pub name: String,
|
||||
pub tasks_per_column: IndexMap<TaskStatus, Vec<Task>>,
|
||||
pub selected_column_idx: usize,
|
||||
pub columns: Vec<Column>
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@ -52,12 +59,8 @@ impl Project {
|
||||
pub fn new(name: &str) -> Self {
|
||||
Project {
|
||||
name: name.to_owned(),
|
||||
tasks_per_column: IndexMap::from([
|
||||
(TaskStatus::Done, vec![]),
|
||||
(TaskStatus::Todo, vec![]),
|
||||
(TaskStatus::InProgress, vec![]),
|
||||
(TaskStatus::Ideas, vec![]),
|
||||
]),
|
||||
columns: vec![],
|
||||
selected_column_idx: 0,
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,137 +73,112 @@ impl Project {
|
||||
Self::load_from_json(&json)
|
||||
}
|
||||
|
||||
pub fn add_task(&mut self, status: TaskStatus, task: Task) {
|
||||
self.tasks_per_column.entry(status).or_default().push(task);
|
||||
}
|
||||
// pub fn add_task(&mut self, status: Column, task: Task) {
|
||||
// self.tasks_per_column.entry(status).or_default().push(task);
|
||||
// }
|
||||
|
||||
pub fn save(&self) {
|
||||
let json = serde_json::to_string_pretty(&self).unwrap();
|
||||
std::fs::write("kanban-tui.json", json).unwrap();
|
||||
let _json = serde_json::to_string_pretty(&self).unwrap();
|
||||
// std::fs::write("kanban-tui.json", json).unwrap();
|
||||
}
|
||||
|
||||
pub fn get_selected_column(&self) -> &Column {
|
||||
&self.columns[self.selected_column_idx]
|
||||
}
|
||||
|
||||
pub fn get_selected_column_mut(&mut self) -> &mut Column {
|
||||
&mut self.columns[self.selected_column_idx]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Default for Project {
|
||||
fn default() -> Self {
|
||||
Project {
|
||||
name: String::new(),
|
||||
tasks_per_column: IndexMap::new(),
|
||||
columns: vec![],
|
||||
selected_column_idx: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AppState {
|
||||
pub selected_column: usize,
|
||||
pub selected_task: [usize; 4],
|
||||
pub project: Project,
|
||||
pub quit: bool,
|
||||
pub columns: Vec<Column>,
|
||||
pub popup_text: Option<String>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
pub fn new(project: Project) -> Self {
|
||||
AppState {
|
||||
selected_column: 0,
|
||||
selected_task: [0, 0, 0, 0],
|
||||
quit: false,
|
||||
popup_text: None,
|
||||
project,
|
||||
columns: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
fn selected_task_idx(&self) -> usize {
|
||||
self.selected_task[self.selected_column]
|
||||
}
|
||||
|
||||
fn selected_task_idx_mut(&mut self) -> &mut usize {
|
||||
&mut self.selected_task[self.selected_column]
|
||||
}
|
||||
|
||||
pub fn get_tasks_in_active_column(&self) -> &[Task] {
|
||||
let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone();
|
||||
self.project.tasks_per_column.get(&column).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_tasks_in_active_column_mut(&mut self) -> &mut Vec<Task> {
|
||||
let column: TaskStatus = TaskStatus::from_int(self.selected_column).unwrap().clone();
|
||||
self.project.tasks_per_column.get_mut(&column).unwrap()
|
||||
}
|
||||
|
||||
pub fn get_selected_task(&self) -> Option<&Task> {
|
||||
let tasks = self.get_tasks_in_active_column();
|
||||
tasks.get(self.selected_task_idx())
|
||||
}
|
||||
|
||||
pub fn select_previous_task(&mut self) {
|
||||
*self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1)
|
||||
let task_idx = &mut self.project.get_selected_column_mut().selected_task_idx;
|
||||
*task_idx = task_idx.saturating_sub(1)
|
||||
}
|
||||
|
||||
pub fn select_next_task(&mut self) {
|
||||
let tasks = self.get_tasks_in_active_column();
|
||||
if tasks.len() > 0 {
|
||||
let mins = min(self.selected_task_idx() + 1, tasks.len() - 1);
|
||||
*self.selected_task_idx_mut() = mins;
|
||||
}
|
||||
let column = &mut self.project.get_selected_column_mut();
|
||||
let task_idx = &mut column.selected_task_idx;
|
||||
*task_idx = min(*task_idx, column.tasks.len() - 1)
|
||||
}
|
||||
|
||||
pub fn select_previous_column(&mut self) {
|
||||
self.selected_column = self.selected_column.saturating_sub(1);
|
||||
pub fn select_previous_column(&mut self) -> &Column {
|
||||
self.project.selected_column_idx = self.project.selected_column_idx.saturating_sub(1);
|
||||
&self.project.columns[self.project.selected_column_idx]
|
||||
}
|
||||
|
||||
pub fn select_next_column(&mut self) {
|
||||
self.selected_column = min(
|
||||
self.selected_column + 1,
|
||||
self.project.tasks_per_column.len() - 1,
|
||||
)
|
||||
pub fn select_next_column(&mut self) -> &Column {
|
||||
self.project.selected_column_idx = min(
|
||||
self.project.selected_column_idx + 1,
|
||||
self.project.columns.len() - 1,
|
||||
);
|
||||
&self.project.columns[self.project.selected_column_idx]
|
||||
}
|
||||
|
||||
pub fn move_task_previous_column(&mut self) {
|
||||
let tasks = self.get_tasks_in_active_column();
|
||||
let task_idx = self.selected_task_idx();
|
||||
if self.selected_column > 0 && tasks.len() > 0 && task_idx.clone() < tasks.len() {
|
||||
let task = self.get_tasks_in_active_column_mut().remove(task_idx);
|
||||
*self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1);
|
||||
self.select_previous_column();
|
||||
let target_tasks = self.get_tasks_in_active_column_mut();
|
||||
target_tasks.push(task);
|
||||
*self.selected_task_idx_mut() = target_tasks.len() - 1;
|
||||
self.project.save();
|
||||
let column = self.project.get_selected_column_mut();
|
||||
if let Some(task) = column.get_selected_task_mut() {
|
||||
if self.project.selected_column_idx > 0 {
|
||||
column.tasks.remove(column.selected_task_idx);
|
||||
self.select_previous_column().tasks.push(*task);
|
||||
self.project.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_task_next_column(&mut self) {
|
||||
let tasks = self.get_tasks_in_active_column();
|
||||
let task_idx = self.selected_task_idx();
|
||||
if self.selected_column < self.project.tasks_per_column.len()
|
||||
&& tasks.len() > 0
|
||||
&& task_idx < tasks.len()
|
||||
{
|
||||
let task = self.get_tasks_in_active_column_mut().remove(task_idx);
|
||||
*self.selected_task_idx_mut() = self.selected_task_idx().saturating_sub(1);
|
||||
self.select_next_column();
|
||||
let target_tasks = self.get_tasks_in_active_column_mut();
|
||||
target_tasks.push(task);
|
||||
*self.selected_task_idx_mut() = target_tasks.len() - 1;
|
||||
self.project.save();
|
||||
let column = self.project.get_selected_column();
|
||||
if let Some(task) = column.get_selected_task() {
|
||||
if self.project.selected_column_idx < self.project.columns.len() {
|
||||
column.tasks.remove(column.selected_task_idx);
|
||||
self.select_next_column().tasks.push(*task);
|
||||
self.project.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_task_up(&mut self) {
|
||||
let task_idx = self.selected_task_idx();
|
||||
if task_idx > 0 {
|
||||
let tasks = self.get_tasks_in_active_column_mut();
|
||||
tasks.swap(task_idx, task_idx - 1);
|
||||
*self.selected_task_idx_mut() = task_idx - 1;
|
||||
let column = self.project.get_selected_column();
|
||||
if column.selected_task_idx > 0 {
|
||||
column.tasks.swap(column.selected_task_idx, column.selected_task_idx - 1);
|
||||
column.selected_task_idx = column.selected_task_idx - 1;
|
||||
self.project.save();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn move_task_down(&mut self) {
|
||||
let task_idx = self.selected_task_idx();
|
||||
let tasks = self.get_tasks_in_active_column_mut();
|
||||
if task_idx < tasks.len() - 1 {
|
||||
tasks.swap(task_idx, task_idx + 1);
|
||||
*self.selected_task_idx_mut() = task_idx + 1;
|
||||
let column = self.project.get_selected_column();
|
||||
if column.selected_task_idx < column.tasks.len() - 1 {
|
||||
column.tasks.swap(column.selected_task_idx, column.selected_task_idx + 1);
|
||||
column.selected_task_idx = column.selected_task_idx + 1;
|
||||
self.project.save();
|
||||
}
|
||||
}
|
||||
|
@ -7,13 +7,13 @@ pub fn handle_input(state: &mut AppState) -> Result<(), std::io::Error> {
|
||||
match key.code {
|
||||
KeyCode::Char('q') => state.quit = true,
|
||||
KeyCode::Char('h') |
|
||||
KeyCode::Left => state.select_previous_column(),
|
||||
KeyCode::Left => { state.select_previous_column(); },
|
||||
KeyCode::Char('j') |
|
||||
KeyCode::Down => state.select_next_task(),
|
||||
KeyCode::Char('k') |
|
||||
KeyCode::Up => state.select_previous_task(),
|
||||
KeyCode::Char('l') |
|
||||
KeyCode::Right => state.select_next_column(),
|
||||
KeyCode::Right => { state.select_next_column(); },
|
||||
KeyCode::Char('<') |
|
||||
KeyCode::Char('H') => state.move_task_previous_column(),
|
||||
KeyCode::Char('>') |
|
||||
|
18
src/ui.rs
18
src/ui.rs
@ -11,20 +11,22 @@ fn draw_tasks<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
||||
.direction(Direction::Horizontal)
|
||||
.constraints(
|
||||
vec![
|
||||
Constraint::Percentage(100 / state.project.tasks_per_column.len() as u16);
|
||||
state.project.tasks_per_column.len()
|
||||
Constraint::Percentage(100 / state.project.columns.len() as u16);
|
||||
state.project.columns.len()
|
||||
]
|
||||
.as_ref(),
|
||||
)
|
||||
.split(*area);
|
||||
|
||||
for (i, (status, tasks)) in state.project.tasks_per_column.iter().enumerate() {
|
||||
let items: Vec<ListItem> = tasks
|
||||
for (i, column) in state.project.columns.iter().enumerate() {
|
||||
let items: Vec<ListItem> = column.tasks
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(j, task)| {
|
||||
let mut style = Style::default();
|
||||
if i == state.selected_column && j == state.selected_task[state.selected_column] {
|
||||
let col_idx = state.project.selected_column_idx;
|
||||
let task_idx = state.project.get_selected_column().selected_task_idx;
|
||||
if i == col_idx && j == task_idx {
|
||||
style = style.fg(Color::White).add_modifier(Modifier::BOLD);
|
||||
} else {
|
||||
style = style.fg(Color::White);
|
||||
@ -35,10 +37,10 @@ fn draw_tasks<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
||||
})
|
||||
.collect();
|
||||
let mut style = Style::default();
|
||||
if i == state.selected_column {
|
||||
if i == state.project.selected_column_idx {
|
||||
style = style.fg(Color::Green);
|
||||
};
|
||||
let mut s = Span::raw(format!("{:?}", status));
|
||||
let mut s = Span::raw(format!("{:?}", column.name));
|
||||
s.style = Style::default()
|
||||
.add_modifier(Modifier::BOLD | Modifier::ITALIC | Modifier::UNDERLINED)
|
||||
.fg(Color::White);
|
||||
@ -50,7 +52,7 @@ fn draw_tasks<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
||||
|
||||
fn draw_task_info<B: Backend>(f: &mut Frame<B>, area: &Rect, state: &AppState) {
|
||||
let block = Block::default().title("TASK INFO").borders(Borders::ALL);
|
||||
if let Some(task) = state.get_selected_task() {
|
||||
if let Some(task) = state.project.get_selected_column().get_selected_task() {
|
||||
let p = Paragraph::new(task.description.as_str())
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: true });
|
||||
|
Loading…
x
Reference in New Issue
Block a user