read, colors and unpublish

This commit is contained in:
Christoph J. Scherr 2024-06-27 13:39:33 +02:00
parent 2edb979aef
commit 5bf0fcadeb
5 changed files with 80 additions and 17 deletions

11
Cargo.lock generated
View File

@ -348,6 +348,16 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" 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]] [[package]]
name = "console" name = "console"
version = "0.15.8" version = "0.15.8"
@ -618,6 +628,7 @@ name = "diesel-demo"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"colored",
"diesel", "diesel",
"dotenvy", "dotenvy",
"libpt", "libpt",

View File

@ -5,6 +5,7 @@ edition = "2021"
[dependencies] [dependencies]
anyhow.workspace = true anyhow.workspace = true
colored = "2.1.0"
diesel = { version = "2.2.1", features = ["serde_json", "sqlite", "uuid", "returning_clauses_for_sqlite_3_35"] } diesel = { version = "2.2.1", features = ["serde_json", "sqlite", "uuid", "returning_clauses_for_sqlite_3_35"] }
dotenvy = "0.15.7" dotenvy = "0.15.7"
libpt.workspace = true libpt.workspace = true

View File

@ -6,6 +6,7 @@ use self::schema::posts::dsl::*;
use std::io::Write; use std::io::Write;
use std::{env, io}; use std::{env, io};
use colored::Colorize;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::sqlite::SqliteConnection; use diesel::sqlite::SqliteConnection;
@ -37,7 +38,6 @@ pub fn load_relevant_posts(conn: &mut SqliteConnection) -> anyhow::Result<Vec<mo
// characters. Works well for the regular alphabet // characters. Works well for the regular alphabet
pub fn print_posts(posts_to_print: &Vec<models::Post>) { pub fn print_posts(posts_to_print: &Vec<models::Post>) {
if !posts_to_print.is_empty() { if !posts_to_print.is_empty() {
info!("{} posts are in the database", posts_to_print.len());
println!( println!(
"{: <12}| {: <30} | {: <40}[...] | {: <12} | {: <5}", "{: <12}| {: <30} | {: <40}[...] | {: <12} | {: <5}",
"id", "title", "body (truncated)", "body len", "is published?" "id", "title", "body (truncated)", "body len", "is published?"
@ -57,6 +57,7 @@ pub fn print_posts(posts_to_print: &Vec<models::Post>) {
post.published post.published
); );
} }
info!("total: {}", posts_to_print.len());
} else { } else {
warn!("Tried to display posts, but there are no posts stored in the database"); 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 stdin = io::stdin();
let mut stdout = io::stdout(); let mut stdout = io::stdout();
print!("> "); print!("{}", "> ".green().bold());
stdout.flush()?; stdout.flush()?;
stdin.read_line(buf)?; stdin.read_line(buf)?;
*buf = buf.trim().to_string(); *buf = buf.trim().to_string();

View File

@ -2,11 +2,24 @@ use diesel::SqliteConnection;
use diesel_demo::models::{Post, PostDraft}; use diesel_demo::models::{Post, PostDraft};
use libpt::log::{self, debug, error, trace, warn}; 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; use diesel_demo as lib;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
let _logger = log::Logger::builder() let _logger = log::Logger::builder()
.max_level(log::Level::DEBUG) .max_level(log::Level::INFO)
.display_level(false)
.show_time(false) .show_time(false)
.build(); .build();
debug!("logger initialized"); debug!("logger initialized");
@ -28,38 +41,62 @@ fn repl(conn: &mut SqliteConnection) -> anyhow::Result<()> {
lib::read_buf_interactive(&mut buf)?; lib::read_buf_interactive(&mut buf)?;
buf = buf.to_uppercase(); buf = buf.to_uppercase();
if buf.starts_with("HELP") { if buf.starts_with("HELP") {
println!( println!("{}", HELP_TEXT.bright_blue())
"\
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"
)
} else if buf.starts_with("EXIT") { } else if buf.starts_with("EXIT") {
break; 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::<diesel::result::Error>() {
if matches!(e, diesel::result::Error::NotFound) {
warn!("No post with id {id} exists");
continue;
}
}
};
} else if buf.starts_with("PUBLISH") { } else if buf.starts_with("PUBLISH") {
let id: i32 = match get_id(&buf) { let id: i32 = match get_id(&buf) {
Some(i) => i, Some(i) => i,
None => continue, 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::<diesel::result::Error>() {
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::<diesel::result::Error>() { if let Some(e) = e.downcast_ref::<diesel::result::Error>() {
if matches!(e, diesel::result::Error::NotFound) { if matches!(e, diesel::result::Error::NotFound) {
warn!("No post with id {id} exists"); warn!("No post with id {id} exists");
} }
} }
continue;
} else {
r.unwrap()
}; };
println!("{post}");
} else if buf.starts_with("DELETE") { } else if buf.starts_with("DELETE") {
let id: i32 = match get_id(&buf) { let id: i32 = match get_id(&buf) {
Some(i) => i, Some(i) => i,
None => continue, 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::<diesel::result::Error>() { if let Some(e) = e.downcast_ref::<diesel::result::Error>() {
if matches!(e, diesel::result::Error::NotFound) { if matches!(e, diesel::result::Error::NotFound) {
warn!("No post with id {id} exists"); warn!("No post with id {id} exists");
continue;
} }
} }
}; };

View File

@ -1,3 +1,4 @@
use std::fmt::Display;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use diesel::prelude::*; use diesel::prelude::*;
@ -22,14 +23,20 @@ pub struct Post {
} }
impl Post { impl Post {
pub fn publish(conn: &mut SqliteConnection, id: i32) -> anyhow::Result<()> { pub fn get(conn: &mut SqliteConnection, id: i32) -> anyhow::Result<Post> {
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}; use crate::schema::posts::dsl::{posts, published};
let post = diesel::update(posts.find(id)) let post = diesel::update(posts.find(id))
.set(published.eq(true)) .set(published.eq(publish))
.returning(Post::as_returning()) .returning(Post::as_returning())
.get_result(conn)?; .get_result(conn)?;
info!("updated post {}", post.id); info!("updated post {}: publish = {}", post.id, post.published);
Ok(()) Ok(())
} }
pub fn delete(conn: &mut SqliteConnection, id: i32) -> anyhow::Result<()> { 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)] #[derive(Insertable, Debug)]
#[diesel(table_name = crate::schema::posts)] #[diesel(table_name = crate::schema::posts)]
#[diesel(check_for_backend(diesel::sqlite::Sqlite))] // optional but improves generated compiler errors #[diesel(check_for_backend(diesel::sqlite::Sqlite))] // optional but improves generated compiler errors