~skye/git/src/lib.rs

view raw


use clap::Parser;
use lazy_static::lazy_static;
use toml::Table;

use std::fs;

pub mod act;
pub mod com;
pub mod db;

/** rexport for all the macros */
pub mod macros {
    pub use crate::{
        dbg_elog, dbg_log, dbg_log_fmt, elog, encode_fmt, eputs, err_fmt,
        err_fmt_html, err_fmt_into, fatal, fmt, log, log_efmt, log_fmt,
        log_fmt_, mkrepopath, ok_fmt, page_fmt, safe_assert_eq, str_fmt,
        str_from,
    };
}

#[derive(Parser, Debug, Clone)]
#[command(version, about, long_about = None)]
pub struct Args {
    /* config file path */
    #[arg(short, long)]
    pub cfg: String,
}

/** assert_eq but just print the message, don't actually panic */
#[macro_export]
macro_rules! safe_assert_eq {
    ($x:expr, $y:expr) => {{
        if $x == $y {
            ()
        } else {
            elog!(
                "{}: left: {:?}, right: {:?}",
                "assert eq failed".red(),
                $x,
                $y
            );
        }
    }};
}

/** format! is too long for my k brain rot */
#[macro_export]
macro_rules! fmt {
    ($($t:tt)*) => {{
        format!($($t)*)
    }};
}

/** format a page */
#[macro_export]
macro_rules! page_fmt {
    { ($($t:tt)*), ($($b:tt)*) } => {{
        Html(format!(
            r#"
            <!DOCTYPE html>
            <html>
                <head>
                    <title>{title} :: {t}</title>
                    <meta charset="utf-8">
                    <link rel="stylesheet" type="text/css" href="/style.css">
                </head>
                <body>
                    <div id="head">
                        <b><a href="/">{title}</a></b>
                    </div>
                    <div id="page">
                        {b}
                    </div>
                    <div id="foot">
                        {version}
                    </div>
                </body>
            </html>
            "#,
            title = match CFG["title"].as_str() {
                Some(x) => x,
                None => "badboy git",
            },
            version = crate::com::cfg::VERSION,
            t = format!($($t)*),
            b = format!($($b)*)
        ))
    }};
}

/** shorhand for Err(format!) */
#[macro_export]
macro_rules! err_fmt {
    ($($t:tt)*) => {{
        Err(format!($($t)*))
    }};
}

/** shorthand for Err(format!.into()) */
#[macro_export]
macro_rules! err_fmt_into {
    ($($t:tt)*) => {{
        Err(format!($($t)*).into())
    }};
}

/** shorthand for Err(page_fmt!) */
#[macro_export]
macro_rules! err_fmt_html {
    [$g:expr, $($t:tt)*] => {{
        Err(page_fmt! {
            ($($t)*),
            (
                r#"
                <h1>error</h1>
                <p>{}</p>
                <p><a href="{}">go back<a></p>
                "#,
                format!($($t)*),
                $g
            )
        })

    }};
    ($($t:tt)*) => {{
        Err(page_fmt! {
            ($($t)*),
            (
                "<h1>error</h1>
                <p>{}</p>",
                format!($($t)*)
            )
        })
    }};
}

/** shorthand for Ok(format!()) */
#[macro_export]
macro_rules! ok_fmt {
    ($($t:tt)*) => {{
        Ok(format!($($t)*))
    }};
}

/** macro to format a fatal error.
 * basically a shorthand for panic(format!()) */
#[macro_export]
macro_rules! fatal {
    ($($t:tt)*) => {{
        use colored::Colorize;

        eputs!("{}: ", "fatal".red());
        elog!($($t)*);
        std::process::exit(-1);
    }};
}

/** $p: prefix */
#[macro_export]
macro_rules! log_fmt_ {
    ($p:expr, $($t:tt)*) => {{
        use colored::Colorize;
        format!(
            "{}: {}: {}",
            $p,
            format!("{}", now()).purple(),
            format!($($t)*)
        )
    }};
}

/** format a string for logging */
#[macro_export]
macro_rules! log_fmt {
    ($($t:tt)*) => {{
        log_fmt_!("log".blue(), $($t)*)
    }};
}

/** format a string for logging errors */
#[macro_export]
macro_rules! log_efmt {
    ($($t:tt)*) => {{
        log_fmt_!("err".red(), $($t)*)
    }};
}

/** log a string with a timestamp */
#[macro_export]
macro_rules! log {
    ($($t:tt)*) => {{
        println!("{}", log_fmt!($($t)*));
    }};
}

/** log but stderr */
#[macro_export]
macro_rules! elog {
    ($($t:tt)*) => {{
        eprintln!("{}", log_efmt!($($t)*));
    }};
}

/** formatter for the debug logger */
//#[macro_export]
//#[cfg(not(debug_assertions))]
//macro_rules! dbg_log_fmt {
//    ($x:expr, $($t:tt)*) => {{
//        String::new()
//    }};
//}
//#[cfg(debug_assertions)]
#[macro_export]
macro_rules! dbg_log_fmt {
    ($($t:tt)*) => {{
        log_fmt!("{}: {}", "dbg".purple(), format!($($t)*))
    }};
}

/** log but only in debug mode */
#[macro_export]
macro_rules! dbg_log {
    ($($t:tt)*) => {{
        println!("{}", dbg_log_fmt!("{}", format!($($t)*)));
    }};
}

/** log an error in debug mode */
#[macro_export]
macro_rules! dbg_elog {
    ($($t:tt)*) => {{
        eprintln!("{}", dbg_log_fmt!("dbg err: {}", format!($($t)*)));
    }};
}

/*
/** put a string without a newline */
#[macro_export]
macro_rules! puts {
    ($($t:tt)*) => {{
        use std::io::{self, Write};

        print!($($t)*);
        match io::stdout().flush() {
            Ok(_) => (),
            Err(e) => fatal!("failed to flush stdout: {e}")
        }
    }};
}
*/

/** puts but stderr */
#[macro_export]
macro_rules! eputs {
    ($($t:tt)*) => {{
        use std::io::{self, Write};

        eprint!($($t)*);
        match io::stderr().flush() {
            Ok(_) => (),
            Err(e) => {
                /* TODO: can't reuse fatal bc it leads to infinite recursion
                 * is there a better way? maybe an internal fn? */
                eprintln!(
                    "{}: failed to flush stderr: {}",
                    "fatal".red(),
                    e
                );
            }
        }
    }};
}

/** format! but it makes a &str */
#[macro_export]
macro_rules! str_fmt {
    ($($t:tt)*) => {
        format!($($t)*).as_str()
    }
}

/** encode a formatted string */
#[macro_export]
macro_rules! encode_fmt {
    ($($t:tt)*) => {{
        use html_escape::encode_text;
        format!("{}", encode_text(str_fmt!($($t)*)))
    }};
}

/** make a &str from x where x is a String: From<T> */
#[macro_export]
macro_rules! str_from {
    ($x:expr) => {{
        String::from($x).as_str()
    }};
}

/** get the current time and format it */
pub fn now() -> String {
    chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}

/*
#[derive(Clone, Debug)]
pub struct Cfg {
    pub title: String,
    pub git_addr: String,
    pub git_port: String,
}
*/

/*
 * i love global variables :)
 */
lazy_static! {
    /** args are global so they can be used by all the actions */
    pub static ref ARGS: Args = Args::parse();

    /** the configuration file structure */
    pub static ref CFG: Table = {
        /* file to read */
        let f = &ARGS.cfg;

        /* content of that file */
        let c = match fs::read_to_string(f) {
            Ok(x) => x,
            Err(e) => fatal!("failed to read config file: {e}"),
        };

        /* the resulting table */
        let t = match c.parse::<Table>() {
            Ok(x) => x,
            Err(e) => fatal!("failed to get config: {e}"),
        };

        /* err out if we don't have the required settings */
        {
            if !t.contains_key("title") {
                fatal!("configuration does not contain a title!!");
            }

            if !t.contains_key("git_addr") {
                fatal!("configuration does not contain a git addr!");
            }

            if !t.contains_key("git_port") {
                fatal!("configuration does not gontain a git port!");
            }
        }

        t
    };
}