~gbmor/clinte

841e6a34a49261d2681c8a4d2c1dc176ba4e3b35 — Ben Morrison 1 year, 2 months ago 8ecc294
removed panics and refactored error handling

Using a helper function to handle fatal errors
  error::helper()
Displays the simplified message if an error condition
occurs. Displays both the simplified and the raw error
message if -v verbose logging is enabled.
6 files changed, 61 insertions(+), 89 deletions(-)

M src/db.rs
M src/ed.rs
M src/error.rs
M src/logging.rs
M src/main.rs
M src/posts.rs
M src/db.rs => src/db.rs +17 -12
@@ 1,6 1,7 @@
use std::time;

use crate::conf;
use crate::error;

const DB_PATH: &str = "/usr/local/clinte/clinte.db";



@@ 25,24 26,28 @@ impl Conn {
            log::info!("Connecting to database");
        }

        let conn = rusqlite::Connection::open_with_flags(
            path,
            rusqlite::OpenFlags::SQLITE_OPEN_FULL_MUTEX
                | rusqlite::OpenFlags::SQLITE_OPEN_CREATE
                | rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE,
        )
        .expect("Could not connect to DB");
        let conn = error::helper(
            rusqlite::Connection::open_with_flags(
                path,
                rusqlite::OpenFlags::SQLITE_OPEN_FULL_MUTEX
                    | rusqlite::OpenFlags::SQLITE_OPEN_CREATE
                    | rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE,
            ),
            "Could not connect to DB",
        );

        conn.execute(
            "CREATE TABLE IF NOT EXISTS posts (
        error::helper(
            conn.execute(
                "CREATE TABLE IF NOT EXISTS posts (
            id INTEGER PRIMARY KEY NOT NULL,
            title TEXT NOT NULL,
            author TEXT NOT NULL,
            body TEXT NOT NULL
        )",
            rusqlite::NO_PARAMS,
        )
        .expect("Could not initialize DB");
                rusqlite::NO_PARAMS,
            ),
            "Could not initialize DB",
        );

        if *conf::DEBUG {
            log::info!(

M src/ed.rs => src/ed.rs +9 -12
@@ 4,22 4,15 @@ use std::process;

use chrono::prelude::*;

use crate::conf;
use crate::error;
use crate::user;

fn create_tmp_file<'a>() -> Result<String, &'a str> {
fn create_tmp_file<'a>() -> Result<String, std::io::Error> {
    let the_time = Utc::now().to_rfc3339();
    let file_name = format!("/tmp/clinte_ed_{}_{}", *user::NAME, the_time);
    match fs::write(&file_name, "") {
        Ok(_) => Ok(file_name),
        Err(err) => {
            log::warn!("Couldn't create tempfile");
            if *conf::DEBUG {
                log::warn!("--> {:?}", err);
            }
            Err("Unable to create temp file")
        }
        Err(err) => Err(err),
    }
}



@@ 40,7 33,7 @@ pub fn call() -> String {
        }
    };

    let tmp_loc = error::helper(create_tmp_file());
    let tmp_loc = error::helper(create_tmp_file(), "Couldn't create tempfile");

    error::helper(
        process::Command::new(editor)


@@ 48,9 41,13 @@ pub fn call() -> String {
            .stdin(process::Stdio::inherit())
            .stdout(process::Stdio::inherit())
            .output(),
        "Couldn't call editor",
    );

    let body = error::helper(fs::read_to_string(&tmp_loc));
    error::helper(fs::remove_file(tmp_loc));
    let body = error::helper(
        fs::read_to_string(&tmp_loc),
        "Couldn't read message from disk",
    );
    error::helper(fs::remove_file(tmp_loc), "Couldn't remove temporary file");
    body
}

M src/error.rs => src/error.rs +9 -11
@@ 1,15 1,20 @@
use crate::conf;

// 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
pub fn helper<T, V>(res: std::result::Result<T, V>, simplified_message: &str) -> T
where
    V: std::fmt::Debug,
{
    match res {
        Ok(val) => val,
        Err(err) => {
            log::error!("{:?}", err);
            panic!("{:?}", err);
            log::error!("{}", simplified_message);
            if *conf::DEBUG {
                log::error!("--> {:?}", err);
            }
            std::process::exit(1);
        }
    }
}


@@ 21,14 26,7 @@ mod tests {
    #[test]
    fn shouldnt_panic() {
        let ok: std::result::Result<&str, &str> = Ok("okay");
        let rhs = helper(ok);
        let rhs = helper(ok, "okay");
        assert_eq!("okay", rhs);
    }

    #[test]
    #[should_panic]
    fn should_panic() {
        let err: std::result::Result<&str, &str> = Err("oops");
        helper(err);
    }
}

M src/logging.rs => src/logging.rs +1 -9
@@ 2,20 2,12 @@ use std::fs::OpenOptions;

use simplelog::*;

use crate::conf;
use crate::error;
use crate::user;

pub fn checked_init() {
    let logfile = format!("/tmp/clinte_{}.log", *user::NAME);

    if let Err(e) = init(&logfile) {
        log::error!("Couldn't initialize logging. Exiting.");
        if *conf::DEBUG {
            log::error!("--> {}", e);
        }
        std::process::exit(1);
    }
    error::helper(init(&logfile), "Couldn't initialize logging");
}

fn init(path: &str) -> error::Result<()> {

M src/main.rs => src/main.rs +22 -45
@@ 29,63 29,40 @@ fn main() {

    if arg_matches.subcommand_matches("post").is_some() {
        log::info!("New post...");
        if let Err(e) = posts::create(&db) {
            log::error!("Error creating new post");
            if *conf::DEBUG {
                log::error!("--> {}", e);
            }
            std::process::exit(1);
        }
        error::helper(posts::create(&db), "Error creating new post");
    } else if arg_matches.subcommand_matches("update").is_some() {
        let id: u32 = if let Some(val) = arg_matches.subcommand_matches("update_handler") {
            match val.value_of("id").unwrap_or_else(|| "0").parse() {
                Ok(n) => n,
                Err(e) => {
                    log::error!("Couldn't parse ID");
                    if *conf::DEBUG {
                        log::error!("--> {}", e);
                    }
                    std::process::exit(1);
                }
            }
            error::helper(
                val.value_of("id").unwrap_or_else(|| "0").parse(),
                "Couldn't parse ID",
            )
        } else {
            0
        };

        log::info!("Updating post ...");
        if let Err(e) = posts::update_handler(&db, id) {
            log::error!("Error updating post {}", id);
            if *conf::DEBUG {
                log::error!("--> {}", e);
            }
            std::process::exit(1);
        }

        error::helper(
            posts::update_handler(&db, id),
            format!("Error updating post {}", id).as_ref(),
        );
    } else if arg_matches.subcommand_matches("delete").is_some() {
        let id: u32 = if let Some(val) = arg_matches.subcommand_matches("update_handler") {
            match val.value_of("id").unwrap_or_else(|| "0").parse() {
                Ok(n) => n,
                Err(_) => {
                    log::error!("Couldn't parse ID");
                    std::process::exit(1);
                }
            }
            error::helper(
                val.value_of("id").unwrap_or_else(|| "0").parse(),
                "Couldn't parse ID",
            )
        } else {
            0
        };

        log::info!("Deleting post");
        if let Err(e) = posts::delete_handler(&db, id) {
            log::error!("Error deleting post {}", id);
            if *conf::DEBUG {
                log::error!("--> {}", e);
            }
            std::process::exit(1);
        }
    }

    if let Err(e) = posts::display(&db) {
        log::error!("Error displaying posts");
        if *conf::DEBUG {
            log::error!("--> {}", e);
        }
        std::process::exit(1);
        error::helper(
            posts::delete_handler(&db, id),
            format!("Error deleting post {}", id).as_ref(),
        );
    }

    error::helper(posts::display(&db), "Error displaying posts");
}

M src/posts.rs => src/posts.rs +3 -0
@@ 38,8 38,10 @@ pub fn create(db: &db::Conn) -> error::Result<()> {

    println!();
    println!("Title of the new post: ");

    let mut title = String::new();
    io::stdin().read_line(&mut title)?;

    let title = str_to_utf8(title.trim());
    let title = if title.len() > 30 {
        &title[..30]


@@ 48,6 50,7 @@ pub fn create(db: &db::Conn) -> error::Result<()> {
    };

    println!();

    let body_raw = str_to_utf8(&ed::call());
    let body = if body_raw.len() > 500 {
        &body_raw[..500]