mod api;
mod arguments;
mod commands;
mod config;
mod constants;
mod credentials;
mod error;
mod models;
mod picker;
mod utilities;
use api::client::ApiClient;
use api::client::V9ApiClient;
use arguments::Command::Auth;
use arguments::Command::Config;
use arguments::Command::Continue;
use arguments::Command::CreateProject;
use arguments::Command::CreateTag;
use arguments::Command::Current;
use arguments::Command::Delete;
use arguments::Command::DeleteProject;
use arguments::Command::DeleteTag;
use arguments::Command::Edit;
use arguments::Command::List;
use arguments::Command::Logout;
use arguments::Command::RenameProject;
use arguments::Command::RenameTag;
use arguments::Command::Running;
use arguments::Command::Start;
use arguments::Command::Stop;
use arguments::CommandLineArguments;
use arguments::ConfigSubCommand;
use commands::auth::AuthenticationCommand;
use commands::cont::ContinueCommand;
use commands::create_project::CreateProjectCommand;
use commands::create_tag::CreateTagCommand;
use commands::delete::DeleteCommand;
use commands::delete_project::DeleteProjectCommand;
use commands::delete_tag::DeleteTagCommand;
use commands::edit::EditCommand;
use commands::list::ListCommand;
use commands::rename_project::RenameProjectCommand;
use commands::rename_tag::RenameTagCommand;
use commands::running::RunningTimeEntryCommand;
use commands::start::StartCommand;
use commands::stop::{StopCommand, StopCommandOrigin};
use credentials::get_storage;
use credentials::Credentials;
use models::ResultWithDefaultError;
use std::io;
use structopt::StructOpt;
#[tokio::main]
async fn main() -> ResultWithDefaultError<()> {
let parsed_args = CommandLineArguments::from_args();
match execute_subcommand(parsed_args).await {
Ok(()) => Ok(()),
Err(error) => {
print!("{error}");
Ok(())
}
}
}
async fn execute_subcommand(args: CommandLineArguments) -> ResultWithDefaultError<()> {
let command = args.cmd;
let get_default_api_client = || get_api_client(args.proxy.clone());
let picker = picker::get_picker(args.fzf);
if let Some(directory) = args.directory {
if !directory.exists() {
return Err(Box::new(error::ArgumentError::DirectoryNotFound(directory)));
}
if !directory.is_dir() {
return Err(Box::new(error::ArgumentError::NotADirectory(directory)));
}
std::env::set_current_dir(directory).expect("Couldn't set current directory");
}
match command {
None => RunningTimeEntryCommand::execute(get_default_api_client()?).await?,
Some(subcommand) => match subcommand {
Stop => {
StopCommand::execute(&get_default_api_client()?, StopCommandOrigin::CommandLine)
.await?;
}
Continue { interactive } => {
let picker = if interactive { Some(picker) } else { None };
ContinueCommand::execute(get_default_api_client()?, picker).await?
}
List {
number,
json,
since,
until,
entity,
} => {
ListCommand::execute(
get_default_api_client()?,
number,
json,
since,
until,
entity,
)
.await?
}
Current | Running => {
RunningTimeEntryCommand::execute(get_default_api_client()?).await?
}
Start {
interactive,
billable,
description,
project,
tags,
task,
} => {
StartCommand::execute(
get_default_api_client()?,
picker,
description,
project,
tags,
billable,
interactive,
task,
)
.await?
}
CreateProject { name, color } => {
CreateProjectCommand::execute(get_default_api_client()?, name, color).await?
}
DeleteProject { name } => {
DeleteProjectCommand::execute(get_default_api_client()?, name).await?
}
RenameProject { old_name, new_name } => {
RenameProjectCommand::execute(get_default_api_client()?, old_name, new_name).await?
}
CreateTag { name } => {
CreateTagCommand::execute(get_default_api_client()?, name).await?
}
DeleteTag { name } => {
DeleteTagCommand::execute(get_default_api_client()?, name).await?
}
RenameTag { old_name, new_name } => {
RenameTagCommand::execute(get_default_api_client()?, old_name, new_name).await?
}
Edit {
id,
description,
project,
tags,
} => {
EditCommand::execute(get_default_api_client()?, id, description, project, tags)
.await?
}
Delete { id } => DeleteCommand::execute(get_default_api_client()?, id).await?,
Auth { api_token } => {
let api_token = match api_token {
Some(token) => token,
None => match rpassword::prompt_password("API token: ") {
Ok(token) => token,
Err(error) => return Err(Box::new(error)),
},
};
let credentials = Credentials { api_token };
let api_client = V9ApiClient::from_credentials(credentials, args.proxy)?;
AuthenticationCommand::execute(io::stdout(), api_client, get_storage()).await?
}
Logout => {
let storage = get_storage();
storage.clear()?;
println!("Successfully logged out.");
}
Config {
delete,
cmd,
edit,
path,
} => match cmd {
Some(config_command) => match config_command {
ConfigSubCommand::Init => {
config::init::ConfigInitCommand::execute(edit).await?;
}
ConfigSubCommand::Active => {
config::active::ConfigActiveCommand::execute().await?;
}
},
None => config::manage::ConfigManageCommand::execute(delete, edit, path).await?,
},
},
}
Ok(())
}
fn get_api_client(proxy: Option<String>) -> ResultWithDefaultError<impl ApiClient> {
let credentials_storage = get_storage();
match credentials_storage.read() {
Ok(credentials) => V9ApiClient::from_credentials(credentials, proxy),
Err(err) => Err(err),
}
}