main.rs 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. use std::{process::exit, str::FromStr};
  2. use clap::{Parser, Subcommand};
  3. use log::{error, info};
  4. use simplelog::{ColorChoice, TermLogger, TerminalMode};
  5. use url::Url;
  6. use darkfi::{
  7. rpc::client::RpcClient,
  8. util::cli::{get_log_config, get_log_level},
  9. Result,
  10. };
  11. mod drawdown;
  12. mod filter;
  13. mod primitives;
  14. mod rpc;
  15. mod util;
  16. mod view;
  17. use drawdown::{drawdown, to_naivedate};
  18. use primitives::{task_from_cli, State, TaskEvent};
  19. use util::{desc_in_editor, due_as_timestamp};
  20. use view::{comments_as_string, print_task_info, print_task_list};
  21. const DEFAULT_PATH: &str = "~/tau_exported_tasks";
  22. #[derive(Parser)]
  23. #[clap(name = "tau", version)]
  24. struct Args {
  25. #[clap(short, parse(from_occurrences))]
  26. /// Increase verbosity (-vvv supported)
  27. verbose: u8,
  28. #[clap(short, long, default_value = "tcp://127.0.0.1:12055")]
  29. /// taud JSON-RPC endpoint
  30. endpoint: Url,
  31. /// Search filters (zero or more)
  32. filters: Vec<String>,
  33. #[clap(subcommand)]
  34. command: Option<TauSubcommand>,
  35. }
  36. #[derive(Subcommand)]
  37. enum TauSubcommand {
  38. /// Add a new task
  39. Add { values: Vec<String> },
  40. /// Update/Edit an existing task by ID
  41. Update {
  42. /// Task ID
  43. task_id: u64,
  44. /// Values (ex: project:blockchain)
  45. values: Vec<String>,
  46. },
  47. /// Set or Get task state
  48. State {
  49. /// Task ID
  50. task_id: u64,
  51. /// Set task state
  52. state: Option<String>,
  53. },
  54. /// Set or Get comment for a task
  55. Comment {
  56. /// Task ID
  57. task_id: u64,
  58. /// Comment content
  59. content: Vec<String>,
  60. },
  61. /// Get task info by ID
  62. Info { task_id: u64 },
  63. /// Switch workspace
  64. Switch {
  65. /// Tau workspace
  66. workspace: String,
  67. },
  68. /// Import tasks from a specified directory.
  69. Import { path: Option<String> },
  70. /// Export tasks to a specified directory.
  71. Export { path: Option<String> },
  72. /// Drawdown.
  73. Log { month: String, owner: String },
  74. }
  75. pub struct Tau {
  76. pub rpc_client: RpcClient,
  77. }
  78. #[async_std::main]
  79. async fn main() -> Result<()> {
  80. let args = Args::parse();
  81. let log_level = get_log_level(args.verbose.into());
  82. let log_config = get_log_config();
  83. TermLogger::init(log_level, log_config, TerminalMode::Mixed, ColorChoice::Auto)?;
  84. let rpc_client = RpcClient::new(args.endpoint).await?;
  85. let tau = Tau { rpc_client };
  86. // Parse subcommands
  87. match args.command {
  88. Some(sc) => match sc {
  89. TauSubcommand::Add { values } => {
  90. let mut task = task_from_cli(values)?;
  91. if task.title.is_empty() {
  92. error!("Please provide a title for the task.");
  93. exit(1);
  94. };
  95. if task.desc.is_none() {
  96. task.desc = desc_in_editor()?;
  97. };
  98. return tau.add(task).await
  99. }
  100. TauSubcommand::Update { task_id, values } => {
  101. let task = task_from_cli(values)?;
  102. tau.update(task_id, task).await
  103. }
  104. TauSubcommand::State { task_id, state } => match state {
  105. Some(state) => {
  106. let state = state.trim().to_lowercase();
  107. if let Ok(st) = State::from_str(&state) {
  108. tau.set_state(task_id, &st).await
  109. } else {
  110. error!("State can only be one of the following: open start stop pause",);
  111. Ok(())
  112. }
  113. }
  114. None => {
  115. let task = tau.get_task_by_id(task_id).await?;
  116. let state = State::from_str(&task.state)?;
  117. println!("Task {}: {}", task_id, state);
  118. Ok(())
  119. }
  120. },
  121. TauSubcommand::Comment { task_id, content } => {
  122. if content.is_empty() {
  123. let task = tau.get_task_by_id(task_id).await?;
  124. let comments = comments_as_string(task.comments);
  125. println!("Comments {}:\n{}", task_id, comments);
  126. Ok(())
  127. } else {
  128. tau.set_comment(task_id, &content.join(" ")).await
  129. }
  130. }
  131. TauSubcommand::Info { task_id } => {
  132. let task = tau.get_task_by_id(task_id).await?;
  133. print_task_info(task)
  134. }
  135. TauSubcommand::Switch { workspace } => {
  136. tau.switch_ws(workspace).await?;
  137. Ok(())
  138. }
  139. TauSubcommand::Export { path } => {
  140. let path = path.unwrap_or(DEFAULT_PATH.into());
  141. let res = tau.export_to(path.clone()).await?;
  142. if res {
  143. info!("Exported to {}", path);
  144. } else {
  145. error!("Error exporting to {}", path);
  146. }
  147. Ok(())
  148. }
  149. TauSubcommand::Import { path } => {
  150. let path = path.unwrap_or(DEFAULT_PATH.into());
  151. let res = tau.import_from(path.clone()).await?;
  152. if res {
  153. info!("Imported from {}", path);
  154. } else {
  155. error!("Error importing from {}", path);
  156. }
  157. Ok(())
  158. }
  159. TauSubcommand::Log { month, owner } => {
  160. let ts = to_naivedate(month.clone())?.and_hms(12, 0, 0).timestamp();
  161. let tasks = tau.get_stop_tasks(ts).await?;
  162. drawdown(month, tasks, owner)?;
  163. Ok(())
  164. }
  165. },
  166. None => {
  167. let task_ids = tau.get_ids().await?;
  168. let mut tasks = vec![];
  169. for id in task_ids {
  170. tasks.push(tau.get_task_by_id(id).await?);
  171. }
  172. print_task_list(tasks, args.filters)?;
  173. Ok(())
  174. }
  175. }?;
  176. tau.close_connection().await
  177. }