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#" {title} :: {t}
{b}
"#, 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#"

error

{}

go back

"#, format!($($t)*), $g ) }) }}; ($($t:tt)*) => {{ Err(page_fmt! { ($($t)*), ( "

error

{}

", 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 */ #[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::() { 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 }; }