use custom Task type
This commit is contained in:
parent
01566042b3
commit
5450884bb0
6 changed files with 79 additions and 100 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -214,6 +214,12 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dotenvy"
|
||||
version = "0.15.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||
|
||||
[[package]]
|
||||
name = "dsl_auto_type"
|
||||
version = "0.1.3"
|
||||
|
@ -737,6 +743,7 @@ dependencies = [
|
|||
"color-eyre",
|
||||
"crossterm",
|
||||
"diesel",
|
||||
"dotenvy",
|
||||
"ratatui",
|
||||
]
|
||||
|
||||
|
|
|
@ -12,3 +12,4 @@ ratatui = "0.29.0"
|
|||
color-eyre = "0.6.3"
|
||||
|
||||
diesel = { version = "2.2", features = ["sqlite", "returning_clauses_for_sqlite_3_35"] }
|
||||
dotenvy = "0.15"
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
rust-toolchain
|
||||
cargo-generate
|
||||
diesel-cli
|
||||
sqlite
|
||||
|
||||
just
|
||||
lazysql
|
||||
|
|
14
src/lib.rs
Normal file
14
src/lib.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
pub mod models;
|
||||
pub mod schema;
|
||||
|
||||
use diesel::prelude::*;
|
||||
use dotenvy::dotenv;
|
||||
use std::env;
|
||||
|
||||
pub fn establish_connection() -> SqliteConnection {
|
||||
dotenv().ok();
|
||||
|
||||
let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
|
||||
SqliteConnection::establish(&database_url)
|
||||
.unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
|
||||
}
|
146
src/main.rs
146
src/main.rs
|
@ -1,4 +1,5 @@
|
|||
use color_eyre::Result;
|
||||
use diesel::prelude::*;
|
||||
use ratatui::{
|
||||
DefaultTerminal,
|
||||
buffer::Buffer,
|
||||
|
@ -15,6 +16,8 @@ use ratatui::{
|
|||
StatefulWidget, Widget, Wrap,
|
||||
},
|
||||
};
|
||||
use todo::establish_connection;
|
||||
use todo::models::*;
|
||||
|
||||
const TODO_HEADER_STYLE: Style = Style::new().fg(SLATE.c100).bg(BLUE.c800);
|
||||
const NORMAL_ROW_BG: Color = SLATE.c950;
|
||||
|
@ -26,91 +29,32 @@ const COMPLETED_TEXT_FG_COLOR: Color = GREEN.c500;
|
|||
fn main() -> Result<()> {
|
||||
color_eyre::install()?;
|
||||
let terminal = ratatui::init();
|
||||
let app_result = App::default().run(terminal);
|
||||
let app = App::default().load_db();
|
||||
let app_result = app.run(terminal);
|
||||
ratatui::restore();
|
||||
app_result
|
||||
}
|
||||
|
||||
struct App {
|
||||
should_exit: bool,
|
||||
todo_list: TodoList,
|
||||
tasks: TaskList,
|
||||
sqlite: SqliteConnection,
|
||||
}
|
||||
|
||||
struct TodoList {
|
||||
items: Vec<TodoItem>,
|
||||
struct TaskList {
|
||||
items: Vec<Task>,
|
||||
state: ListState,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TodoItem {
|
||||
todo: String,
|
||||
info: String,
|
||||
status: Status,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum Status {
|
||||
Todo,
|
||||
Completed,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
should_exit: false,
|
||||
todo_list: TodoList::from_iter([
|
||||
(
|
||||
Status::Todo,
|
||||
"Rewrite everything with Rust!",
|
||||
"I can't hold my inner voice. He tells me to rewrite the complete universe with Rust",
|
||||
),
|
||||
(
|
||||
Status::Completed,
|
||||
"Rewrite all of your tui apps with Ratatui",
|
||||
"Yes, you heard that right. Go and replace your tui with Ratatui.",
|
||||
),
|
||||
(
|
||||
Status::Todo,
|
||||
"Pet your cat",
|
||||
"Minnak loves to be pet by you! Don't forget to pet and give some treats!",
|
||||
),
|
||||
(
|
||||
Status::Todo,
|
||||
"Walk with your dog",
|
||||
"Max is bored, go walk with him!",
|
||||
),
|
||||
(
|
||||
Status::Completed,
|
||||
"Pay the bills",
|
||||
"Pay the train subscription!!!",
|
||||
),
|
||||
(
|
||||
Status::Completed,
|
||||
"Refactor list example",
|
||||
"If you see this info that means I completed this task!",
|
||||
),
|
||||
]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(Status, &'static str, &'static str)> for TodoList {
|
||||
fn from_iter<I: IntoIterator<Item = (Status, &'static str, &'static str)>>(iter: I) -> Self {
|
||||
let items = iter
|
||||
.into_iter()
|
||||
.map(|(status, todo, info)| TodoItem::new(status, todo, info))
|
||||
.collect();
|
||||
let state = ListState::default();
|
||||
Self { items, state }
|
||||
}
|
||||
}
|
||||
|
||||
impl TodoItem {
|
||||
fn new(status: Status, todo: &str, info: &str) -> Self {
|
||||
Self {
|
||||
status,
|
||||
todo: todo.to_string(),
|
||||
info: info.to_string(),
|
||||
tasks: TaskList {
|
||||
items: Vec::new(),
|
||||
state: ListState::default(),
|
||||
},
|
||||
sqlite: establish_connection(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,6 +70,17 @@ impl App {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn load_db(mut self) -> Self {
|
||||
use todo::schema::tasks::dsl::*;
|
||||
|
||||
self.tasks.items = tasks
|
||||
.select(Task::as_select())
|
||||
.load(&mut self.sqlite)
|
||||
.expect("Error loading posts");
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
fn handle_key(&mut self, key: KeyEvent) {
|
||||
if key.kind != KeyEventKind::Press {
|
||||
return;
|
||||
|
@ -143,32 +98,32 @@ impl App {
|
|||
}
|
||||
|
||||
fn select_none(&mut self) {
|
||||
self.todo_list.state.select(None);
|
||||
self.tasks.state.select(None);
|
||||
}
|
||||
|
||||
fn select_next(&mut self) {
|
||||
self.todo_list.state.select_next();
|
||||
self.tasks.state.select_next();
|
||||
}
|
||||
fn select_previous(&mut self) {
|
||||
self.todo_list.state.select_previous();
|
||||
self.tasks.state.select_previous();
|
||||
}
|
||||
|
||||
fn select_first(&mut self) {
|
||||
self.todo_list.state.select_first();
|
||||
self.tasks.state.select_first();
|
||||
}
|
||||
|
||||
fn select_last(&mut self) {
|
||||
self.todo_list.state.select_last();
|
||||
self.tasks.state.select_last();
|
||||
}
|
||||
|
||||
/// Changes the status of the selected list item
|
||||
fn toggle_status(&mut self) {
|
||||
if let Some(i) = self.todo_list.state.selected() {
|
||||
self.todo_list.items[i].status = match self.todo_list.items[i].status {
|
||||
Status::Completed => Status::Todo,
|
||||
Status::Todo => Status::Completed,
|
||||
}
|
||||
}
|
||||
// if let Some(i) = self.tasks.state.selected() {
|
||||
// self.tasks.items[i].status = match self.tasks.items[i].status {
|
||||
// Status::Completed => Status::Todo,
|
||||
// Status::Todo => Status::Completed,
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -216,13 +171,14 @@ impl App {
|
|||
|
||||
// Iterate through all elements in the `items` and stylize them.
|
||||
let items: Vec<ListItem> = self
|
||||
.todo_list
|
||||
.tasks
|
||||
.items
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, todo_item)| {
|
||||
.map(|(i, task)| {
|
||||
let color = alternate_colors(i);
|
||||
ListItem::from(todo_item).bg(color)
|
||||
let item = item_from_task(task);
|
||||
item.bg(color)
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -235,16 +191,13 @@ impl App {
|
|||
|
||||
// We need to disambiguate this trait method as both `Widget` and `StatefulWidget` share the
|
||||
// same method name `render`.
|
||||
StatefulWidget::render(list, area, buf, &mut self.todo_list.state);
|
||||
StatefulWidget::render(list, area, buf, &mut self.tasks.state);
|
||||
}
|
||||
|
||||
fn render_selected_item(&self, area: Rect, buf: &mut Buffer) {
|
||||
// We get the info depending on the item's state.
|
||||
let info = if let Some(i) = self.todo_list.state.selected() {
|
||||
match self.todo_list.items[i].status {
|
||||
Status::Completed => format!("✓ DONE: {}", self.todo_list.items[i].info),
|
||||
Status::Todo => format!("☐ TODO: {}", self.todo_list.items[i].info),
|
||||
}
|
||||
let info = if let Some(i) = self.tasks.state.selected() {
|
||||
format!("☐ TODO: {}", self.tasks.items[i].title)
|
||||
} else {
|
||||
"Nothing selected...".to_string()
|
||||
};
|
||||
|
@ -275,14 +228,7 @@ const fn alternate_colors(i: usize) -> Color {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&TodoItem> for ListItem<'_> {
|
||||
fn from(value: &TodoItem) -> Self {
|
||||
let line = match value.status {
|
||||
Status::Todo => Line::styled(format!(" ☐ {}", value.todo), TEXT_FG_COLOR),
|
||||
Status::Completed => {
|
||||
Line::styled(format!(" ✓ {}", value.todo), COMPLETED_TEXT_FG_COLOR)
|
||||
}
|
||||
};
|
||||
ListItem::new(line)
|
||||
}
|
||||
fn item_from_task(task: &Task) -> ListItem<'_> {
|
||||
let line = Line::styled(format!(" ☐ {}", task.title), TEXT_FG_COLOR);
|
||||
ListItem::new(line)
|
||||
}
|
||||
|
|
10
src/models.rs
Normal file
10
src/models.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Queryable, Selectable)]
|
||||
#[diesel(table_name = crate::schema::tasks)]
|
||||
#[diesel(check_for_backend(diesel::sqlite::Sqlite))]
|
||||
pub struct Task {
|
||||
pub id: i32,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue