nunc 0.2.0

WIP: time tracking application
Documentation
use std::path::PathBuf;

use conciliator::Claw;

use clap::{
	CommandFactory,
	Subcommand,
	Parser
};
use clap_complete::Shell;

use crate::Nunc;
use crate::error::Result;

macro_rules! ret_errmsg {
	($($args:tt)*) => {
		return Err($crate::error::Error::Message(format!($($args)*)))
	};
}

pub mod arg;
pub mod cmd;
use cmd::*;

#[derive(Parser)]
pub struct Cli {
	#[clap(short, long)]
	config: Option<PathBuf>,

	#[clap(long)]
	generate_completions: Option<Shell>,

	#[clap(subcommand)]
	cmd: Option<Cmd>

}

#[derive(Subcommand)]
enum Cmd {
	/// Creating new entries with start & stop
	#[clap(flatten)]
	Log(log::Cmd),

	/// View entries & metrics
	#[clap(flatten)]
	Review(review::Cmd),

	/// Manage the database & tree: create, show, edit & remove
	#[clap(flatten)]
	Manage(manage::Cmd),

	/// View and change timezones
	#[clap(subcommand)]
	Zone(zone::Cmd),
}

impl Cli {
	#[cfg(not(test))]
	pub fn run(self, con: &Claw) -> Result<()> {
		let nunc = match &self.config {
			Some(path) => Nunc::load_with_config(path)?,
			None => Nunc::load()?
		};

		if let Some(shell) = self.generate_completions {
			clap_complete::generate(
				shell,
				&mut Self::command(),
				"nunc",
				&mut std::io::stdout()
			);
			return Ok(());
		}

		match self.cmd {
			Some(cmd) => cmd.run(con, &nunc),
			None => Ok(())
		}
	}
}
#[cfg(test)]
impl Cli {
	fn test_run(self, nunc: &Nunc, con: &Claw) -> Result<()> {
		match self.cmd {
			Some(cmd) => cmd.run(con, nunc),
			None => Ok(())
		}
	}

}

#[cfg(test)]
pub struct Tester {
	nunc: Nunc,
	con: Claw
}

#[cfg(test)]
impl Tester {
	pub fn init() -> Result<Self> {
		Ok(Self {
			nunc: Nunc::testing()?,
			con: conciliator::init()
		})
	}
	pub fn run<'s>(&self, args: impl IntoIterator<Item=&'s str>) -> Result<()> {
		use conciliator::{
			Color,
			Conciliator,
			Paint
		};
		let mut cmd_line = self.con.line(..);
		cmd_line.tag(Color::Iota, "TEST")
			.push("nunc_test");
		let args_tee = args.into_iter().inspect(|arg| {
			cmd_line.push(" ").push(arg);
		});
		let cli = Cli::try_parse_from(
			["nunc_test"].into_iter().chain(args_tee)
		).unwrap();
		cmd_line.print();
		cli.test_run(&self.nunc, &self.con).inspect_err(|err| {
			self.con.error(err);
		})
	}

}

impl Cmd {
	fn run(self, con: &Claw, nunc: &Nunc) -> Result<()> {
		match self {
			Cmd::Log(cmd) => cmd.run(con, nunc),
			Cmd::Review(cmd) => cmd.run(con, nunc),
			Cmd::Manage(cmd) => cmd.run(con, nunc),
			Cmd::Zone(cmd) => cmd.run(con, nunc),
		}
	}
}

#[test]
fn clap() {
	Cli::command().debug_assert();
}

#[test]
fn empty_dummy() -> Result<()> {
	Tester::init()?.run([""; 0])
}