diff --git a/Cargo.lock b/Cargo.lock index b0725e5..ea09365 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -348,6 +348,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "console" version = "0.15.8" @@ -618,6 +628,7 @@ name = "diesel-demo" version = "0.1.0" dependencies = [ "anyhow", + "colored", "diesel", "dotenvy", "libpt", diff --git a/members/diesel-demo/Cargo.toml b/members/diesel-demo/Cargo.toml index 5424625..9e896ec 100644 --- a/members/diesel-demo/Cargo.toml +++ b/members/diesel-demo/Cargo.toml @@ -5,6 +5,7 @@ edition = "2021" [dependencies] anyhow.workspace = true +colored = "2.1.0" diesel = { version = "2.2.1", features = ["serde_json", "sqlite", "uuid", "returning_clauses_for_sqlite_3_35"] } dotenvy = "0.15.7" libpt.workspace = true diff --git a/members/diesel-demo/src/lib.rs b/members/diesel-demo/src/lib.rs index 728c5d0..66fb2c9 100644 --- a/members/diesel-demo/src/lib.rs +++ b/members/diesel-demo/src/lib.rs @@ -6,6 +6,7 @@ use self::schema::posts::dsl::*; use std::io::Write; use std::{env, io}; +use colored::Colorize; use diesel::prelude::*; use diesel::sqlite::SqliteConnection; @@ -37,7 +38,6 @@ pub fn load_relevant_posts(conn: &mut SqliteConnection) -> anyhow::Result) { if !posts_to_print.is_empty() { - info!("{} posts are in the database", posts_to_print.len()); println!( "{: <12}| {: <30} | {: <40}[...] | {: <12} | {: <5}", "id", "title", "body (truncated)", "body len", "is published?" @@ -57,6 +57,7 @@ pub fn print_posts(posts_to_print: &Vec) { post.published ); } + info!("total: {}", posts_to_print.len()); } else { warn!("Tried to display posts, but there are no posts stored in the database"); } @@ -68,7 +69,7 @@ pub fn read_buf_interactive(buf: &mut String) -> anyhow::Result<()> { let stdin = io::stdin(); let mut stdout = io::stdout(); - print!("> "); + print!("{}", "> ".green().bold()); stdout.flush()?; stdin.read_line(buf)?; *buf = buf.trim().to_string(); diff --git a/members/diesel-demo/src/main.rs b/members/diesel-demo/src/main.rs index ecf370f..a73b0d0 100644 --- a/members/diesel-demo/src/main.rs +++ b/members/diesel-demo/src/main.rs @@ -2,11 +2,24 @@ use diesel::SqliteConnection; use diesel_demo::models::{Post, PostDraft}; use libpt::log::{self, debug, error, trace, warn}; +const HELP_TEXT: &str = "\ + help - show this menu\n\ + exit - exit the application\n\ + list - list all posts\n\ + publish [id] - publish the post with the id [id]\n\ + unpublish [id] - make the post with the id [id] a draft\n\ + delete [id] - delete the post with the id [id]\n\ + read [id] - display the post with the id [id]\n\ + new - create a new post"; + +use colored::*; + use diesel_demo as lib; fn main() -> anyhow::Result<()> { let _logger = log::Logger::builder() - .max_level(log::Level::DEBUG) + .max_level(log::Level::INFO) + .display_level(false) .show_time(false) .build(); debug!("logger initialized"); @@ -28,38 +41,62 @@ fn repl(conn: &mut SqliteConnection) -> anyhow::Result<()> { lib::read_buf_interactive(&mut buf)?; buf = buf.to_uppercase(); if buf.starts_with("HELP") { - println!( - "\ - help - show this menu\n\ - exit - exit the application\n\ - list - list all posts\n\ - publish [id] - delete the post with the id [id]\n\ - delete [id] - delete the post with the id [id]\n\ - new - create a new post" - ) + println!("{}", HELP_TEXT.bright_blue()) } else if buf.starts_with("EXIT") { break; + } else if buf.starts_with("UNPUBLISH") { + let id: i32 = match get_id(&buf) { + Some(i) => i, + None => continue, + }; + if let Err(e) = Post::publish(conn, id, false) { + if let Some(e) = e.downcast_ref::() { + if matches!(e, diesel::result::Error::NotFound) { + warn!("No post with id {id} exists"); + continue; + } + } + }; } else if buf.starts_with("PUBLISH") { let id: i32 = match get_id(&buf) { Some(i) => i, None => continue, }; - if let Err(e) = Post::publish(conn, id){ + if let Err(e) = Post::publish(conn, id, true) { + if let Some(e) = e.downcast_ref::() { + if matches!(e, diesel::result::Error::NotFound) { + warn!("No post with id {id} exists"); + continue; + } + } + }; + } else if buf.starts_with("READ") { + let id: i32 = match get_id(&buf) { + Some(i) => i, + None => continue, + }; + let r = Post::get(conn, id); + let post: Post = if let Err(e) = r { if let Some(e) = e.downcast_ref::() { if matches!(e, diesel::result::Error::NotFound) { warn!("No post with id {id} exists"); } } + continue; + } else { + r.unwrap() }; + println!("{post}"); } else if buf.starts_with("DELETE") { let id: i32 = match get_id(&buf) { Some(i) => i, None => continue, }; - if let Err(e) = Post::delete(conn, id){ + if let Err(e) = Post::delete(conn, id) { if let Some(e) = e.downcast_ref::() { if matches!(e, diesel::result::Error::NotFound) { warn!("No post with id {id} exists"); + continue; } } }; diff --git a/members/diesel-demo/src/models.rs b/members/diesel-demo/src/models.rs index 88e2a90..d1beaf6 100644 --- a/members/diesel-demo/src/models.rs +++ b/members/diesel-demo/src/models.rs @@ -1,3 +1,4 @@ +use std::fmt::Display; use std::io::{self, Read, Write}; use diesel::prelude::*; @@ -22,14 +23,20 @@ pub struct Post { } impl Post { - pub fn publish(conn: &mut SqliteConnection, id: i32) -> anyhow::Result<()> { + pub fn get(conn: &mut SqliteConnection, id: i32) -> anyhow::Result { + use crate::schema::posts::dsl::posts; + + Ok(posts.find(id).select(Post::as_select()).first(conn)?) + } + + pub fn publish(conn: &mut SqliteConnection, id: i32, publish: bool) -> anyhow::Result<()> { use crate::schema::posts::dsl::{posts, published}; let post = diesel::update(posts.find(id)) - .set(published.eq(true)) + .set(published.eq(publish)) .returning(Post::as_returning()) .get_result(conn)?; - info!("updated post {}", post.id); + info!("updated post {}: publish = {}", post.id, post.published); Ok(()) } pub fn delete(conn: &mut SqliteConnection, id: i32) -> anyhow::Result<()> { @@ -43,6 +50,12 @@ impl Post { } } +impl Display for Post { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "\n{:<60} | published: {:<5}\n{:=^140}\n\n{}", self.title, self.published, "", self.body) + } +} + #[derive(Insertable, Debug)] #[diesel(table_name = crate::schema::posts)] #[diesel(check_for_backend(diesel::sqlite::Sqlite))] // optional but improves generated compiler errors