letting errors flow up

This commit is contained in:
Ben Morrison 2019-09-05 03:25:45 -04:00
parent 567a66d30f
commit fd2f770938
Signed by untrusted user: gbmor
GPG Key ID: 8F192E4720BB0DAC
4 changed files with 71 additions and 70 deletions

View File

@ -1,4 +1,7 @@
pub fn helper<T, V>(res: Result<T, V>) -> T
// This Result is used elsewhere, not in helper()
pub type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
pub fn helper<T, V>(res: std::result::Result<T, V>) -> T
where
V: std::fmt::Debug,
{
@ -17,7 +20,7 @@ mod tests {
#[test]
fn shouldnt_panic() {
let ok: Result<&str, &str> = Ok("okay");
let ok: std::result::Result<&str, &str> = Ok("okay");
let rhs = helper(ok);
assert_eq!("okay", rhs);
}
@ -25,7 +28,7 @@ mod tests {
#[test]
#[should_panic]
fn should_panic() {
let err: Result<&str, &str> = Err("oops");
let err: std::result::Result<&str, &str> = Err("oops");
helper(err);
}
}

View File

@ -4,13 +4,14 @@ use std::fs::File;
use chrono::offset::Utc;
use simplelog::*;
use crate::error;
use crate::user;
lazy_static! {
static ref FILE: String = format!("/tmp/clinte_{}.log", *user::NAME);
}
pub fn init() {
pub fn init() -> error::Result<()> {
// If the log file exists on startup,
// move and timestamp it so we get a
// fresh log file.
@ -19,7 +20,7 @@ pub fn init() {
let time = Utc::now().to_rfc3339();
new_file.push_str(".");
new_file.push_str(&time);
fs::rename(FILE.clone(), new_file).unwrap();
fs::rename(FILE.clone(), new_file)?;
}
CombinedLogger::init(vec![
@ -27,10 +28,12 @@ pub fn init() {
WriteLogger::new(
LevelFilter::Info,
Config::default(),
File::create(FILE.clone()).unwrap(),
File::create(FILE.clone())?,
),
])
.expect("Unable to initialize logging");
Ok(())
}
#[cfg(test)]
@ -43,7 +46,7 @@ mod tests {
fn init_logs() {
let blank = " ".bytes().collect::<Vec<u8>>();
fs::write(&*FILE, &blank).unwrap();
init();
init().unwrap();
info!("TEST LOG MESSAGE");
let logfile = fs::read_to_string(&*FILE).unwrap();

View File

@ -13,7 +13,7 @@ mod logging;
mod posts;
mod user;
fn main() {
fn main() -> error::Result<()> {
let arg_matches = clap::App::new("clinte")
.version(clap::crate_version!())
.author("Ben Morrison (gbmor)")
@ -24,7 +24,7 @@ fn main() {
.get_matches();
let start = time::Instant::now();
logging::init();
logging::init()?;
info!("clinte starting up!");
println!("clinte v{}", clap::crate_version!());
println!("a community notices system");
@ -36,24 +36,26 @@ fn main() {
if arg_matches.subcommand_matches("post").is_some() {
info!("New post...");
posts::create(&db);
posts::create(&db)?;
} else if arg_matches.subcommand_matches("update").is_some() {
let id: u32 = if let Some(val) = arg_matches.subcommand_matches("update_handler") {
val.value_of("id").unwrap().parse().unwrap()
val.value_of("id").unwrap().parse()?
} else {
0
};
info!("Updating post ...");
posts::update_handler(&db, id);
posts::update_handler(&db, id)?;
} else if arg_matches.subcommand_matches("delete").is_some() {
let id: u32 = if let Some(val) = arg_matches.subcommand_matches("update_handler") {
val.value_of("id").unwrap().parse().unwrap()
val.value_of("id").unwrap_or_else(|| "0").parse()?
} else {
0
};
info!("Deleting post");
posts::delete_handler(&db, id);
posts::delete_handler(&db, id)?;
}
posts::display(&db);
posts::display(&db)?;
Ok(())
}

View File

@ -1,17 +1,15 @@
use std::error::Error;
use std::io;
use rusqlite;
use crate::db;
use crate::ed;
use crate::error;
use crate::user;
type Result<T> = std::result::Result<T, Box<dyn Error>>;
// Executes the sql statement that inserts a new post
// Broken off for unit testing.
pub fn exec_new(stmt: &mut rusqlite::Statement, title: &str, body: &str) -> Result<()> {
pub fn exec_new(stmt: &mut rusqlite::Statement, title: &str, body: &str) -> error::Result<()> {
stmt.execute_named(&[
(":title", &title),
(":author", &*user::NAME),
@ -33,16 +31,15 @@ fn str_to_utf8(str: &str) -> String {
}
// First handler for creating a new post.
pub fn create(db: &db::Conn) {
pub fn create(db: &db::Conn) -> error::Result<()> {
let mut stmt = db
.conn
.prepare("INSERT INTO posts (title, author, body) VALUES (:title, :author, :body)")
.unwrap();
.prepare("INSERT INTO posts (title, author, body) VALUES (:title, :author, :body)")?;
println!();
println!("Title of the new post: ");
let mut title = String::new();
io::stdin().read_line(&mut title).unwrap();
io::stdin().read_line(&mut title)?;
let title = str_to_utf8(title.trim());
let title = if title.len() > 30 {
&title[..30]
@ -58,28 +55,27 @@ pub fn create(db: &db::Conn) {
&body
};
exec_new(&mut stmt, title, body).unwrap();
exec_new(&mut stmt, title, body)?;
println!();
Ok(())
}
// Shows the most recent posts.
pub fn display(db: &db::Conn) {
let mut stmt = db.conn.prepare("SELECT * FROM posts").unwrap();
let out = stmt
.query_map(rusqlite::NO_PARAMS, |row| {
let id: u32 = row.get(0)?;
let title: String = row.get(1)?;
let author: String = row.get(2)?;
let body: String = row.get(3)?;
Ok(db::Post {
id,
title,
author,
body,
})
pub fn display(db: &db::Conn) -> error::Result<()> {
let mut stmt = db.conn.prepare("SELECT * FROM posts")?;
let out = stmt.query_map(rusqlite::NO_PARAMS, |row| {
let id: u32 = row.get(0)?;
let title: String = row.get(1)?;
let author: String = row.get(2)?;
let body: String = row.get(3)?;
Ok(db::Post {
id,
title,
author,
body,
})
.unwrap();
})?;
let mut postvec = Vec::new();
out.for_each(|row| {
@ -96,38 +92,35 @@ pub fn display(db: &db::Conn) {
print!("{}", e);
}
}
Ok(())
}
// First handler to update posts.
pub fn update_handler(db: &db::Conn, id: u32) {
pub fn update_handler(db: &db::Conn, id: u32) -> error::Result<()> {
let id_num_in = if id == 0 {
println!();
println!("ID number of your post to edit?");
let mut id_num_in = String::new();
io::stdin().read_line(&mut id_num_in).unwrap();
id_num_in.trim().parse().unwrap()
io::stdin().read_line(&mut id_num_in)?;
id_num_in.trim().parse()?
} else {
id
};
let mut get_stmt = db
.conn
.prepare("SELECT * FROM posts WHERE id = :id")
.unwrap();
let mut get_stmt = db.conn.prepare("SELECT * FROM posts WHERE id = :id")?;
let row = get_stmt
.query_row_named(&[(":id", &id_num_in)], |row| {
let title: String = row.get(1).unwrap();
let author = row.get(2).unwrap();
let body = row.get(3).unwrap();
Ok(vec![title, author, body])
})
.unwrap();
let row = get_stmt.query_row_named(&[(":id", &id_num_in)], |row| {
let title: String = row.get(1)?;
let author = row.get(2)?;
let body = row.get(3)?;
Ok(vec![title, author, body])
})?;
if *user::NAME != row[1] {
println!();
println!("Username mismatch - can't update_handler post!");
return;
return Ok(());
}
let mut new_title = String::new();
@ -138,17 +131,18 @@ pub fn update_handler(db: &db::Conn, id: u32) {
println!("Title: {}\n\nBody: {}", row[0], row[2]);
println!();
println!("Enter new title:");
io::stdin().read_line(&mut new_title).unwrap();
io::stdin().read_line(&mut new_title)?;
println!();
println!("Enter new body:");
io::stdin().read_line(&mut new_body).unwrap();
io::stdin().read_line(&mut new_body)?;
println!();
update(&new_title, &new_body, id_num_in, &db).unwrap();
update(&new_title, &new_body, id_num_in, &db)?;
Ok(())
}
// Allows editing of posts - called by main::update
pub fn update(new_title: &str, new_body: &str, id_num_in: u32, db: &db::Conn) -> Result<()> {
pub fn update(new_title: &str, new_body: &str, id_num_in: u32, db: &db::Conn) -> error::Result<()> {
let new_title = new_title.trim();
let new_body = new_body.trim();
@ -164,20 +158,20 @@ pub fn update(new_title: &str, new_body: &str, id_num_in: u32, db: &db::Conn) ->
}
// Helper to just run a sql statement.
pub fn exec_stmt_no_params(stmt: &mut rusqlite::Statement) -> Result<()> {
pub fn exec_stmt_no_params(stmt: &mut rusqlite::Statement) -> error::Result<()> {
stmt.execute(rusqlite::NO_PARAMS)?;
Ok(())
}
// First handler to remove a post
pub fn delete_handler(db: &db::Conn, id: u32) {
pub fn delete_handler(db: &db::Conn, id: u32) -> error::Result<()> {
let id_num_in: u32 = if id == 0 {
println!();
println!("ID of the post to delete?");
let mut id_num_in = String::new();
io::stdin().read_line(&mut id_num_in).unwrap();
id_num_in.trim().parse().unwrap()
io::stdin().read_line(&mut id_num_in)?;
id_num_in.trim().parse()?
} else {
id
};
@ -185,21 +179,20 @@ pub fn delete_handler(db: &db::Conn, id: u32) {
let del_stmt = format!("DELETE FROM posts WHERE id = {}", id_num_in);
let get_stmt = format!("SELECT * FROM posts WHERE id = {}", id_num_in);
let mut get_stmt = db.conn.prepare(&get_stmt).unwrap();
let mut del_stmt = db.conn.prepare(&del_stmt).unwrap();
let mut get_stmt = db.conn.prepare(&get_stmt)?;
let mut del_stmt = db.conn.prepare(&del_stmt)?;
let user_in_post: String = get_stmt
.query_row(rusqlite::NO_PARAMS, |row| row.get(2))
.unwrap();
let user_in_post: String = get_stmt.query_row(rusqlite::NO_PARAMS, |row| row.get(2))?;
if *user::NAME != user_in_post {
println!();
println!("Users don't match. Can't delete!");
println!();
return;
return Ok(());
}
exec_stmt_no_params(&mut del_stmt).unwrap();
exec_stmt_no_params(&mut del_stmt)?;
Ok(())
}
#[cfg(test)]