main.rs 5.5 KB

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