main.rs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. // SPDX-FileCopyrightText: 2021 - 2022 Samuel W. Flint <swflint@flintfam.org>
  2. //
  3. // SPDX-License-Identifier: GPL-3.0-or-later
  4. use clap::{Command, command, Arg, value_parser, ArgAction, ValueEnum, builder::PossibleValue};
  5. use clap_complete::{generate, Generator, Shell};
  6. use std::env;
  7. use std::io;
  8. mod lib;
  9. #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
  10. enum HumanBool {
  11. Yes,
  12. No
  13. }
  14. impl ValueEnum for HumanBool {
  15. fn value_variants<'a>() -> &'a [Self] {
  16. &[HumanBool::Yes, HumanBool::No]
  17. }
  18. fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
  19. Some(match self {
  20. HumanBool::Yes => PossibleValue::new("YES"),
  21. HumanBool::No => PossibleValue::new("NO")
  22. })
  23. }
  24. }
  25. impl std::fmt::Display for HumanBool {
  26. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
  27. self.to_possible_value()
  28. .expect("Values cannot be skipped")
  29. .get_name()
  30. .fmt(f)
  31. }
  32. }
  33. impl std::str::FromStr for HumanBool {
  34. type Err = String;
  35. fn from_str(s: &str) -> Result<Self, Self::Err> {
  36. for variant in Self::value_variants() {
  37. if variant.to_possible_value().unwrap().matches(s, false) {
  38. return Ok(*variant)
  39. }
  40. }
  41. Err(format!("Invalid Variant: {}", s))
  42. }
  43. }
  44. use crate::lib::{
  45. config::{
  46. find_config_file,
  47. read_configuration_file,
  48. write_configuration_file,
  49. Config
  50. },
  51. repository,
  52. action,
  53. group,
  54. repotype,
  55. run
  56. };
  57. fn build_cli() -> Command {
  58. command!()
  59. .propagate_version(true)
  60. .subcommand_required(true)
  61. .author("Samuel W. Flint <swflint@flintfam.org>")
  62. .after_help("License under the GNU GPL v3.0 or later (https://spdx.org/licenses/GPL-3.0-or-later.html)")
  63. .after_long_help("License under the GNU GPL v3.0 or later (https://spdx.org/licenses/GPL-3.0-or-later.html)")
  64. .about("Synchronize directories flexibly")
  65. .arg(Arg::new("config")
  66. .short('c')
  67. .long("config")
  68. .value_name("FILE")
  69. .help("Set a custom configuration file"))
  70. .subcommand(Command::new("run")
  71. .aliases(["sync", "rr"])
  72. .about("Run synchronization or command for repositories and groups.")
  73. .arg(Arg::new("repo")
  74. .action(ArgAction::Append)
  75. .value_name("REPO_OR_GROUP")
  76. .help("Name or names of repositories/groups to sync"))
  77. .arg(Arg::new("command")
  78. .short('C')
  79. .long("command")
  80. .value_name("COMMAND")
  81. .help("Run named COMMAND in each specified repository")))
  82. .subcommand(Command::new("repository")
  83. .about("Create and manage repositories")
  84. .visible_aliases(["repo", "r"])
  85. .subcommand_required(true)
  86. .subcommand(Command::new("list")
  87. .about("List repositories"))
  88. .subcommand(Command::new("register")
  89. .about("Register the current directory as a repository")
  90. .arg(Arg::new("type")
  91. .required(true)
  92. .value_name("TYPE")
  93. .help("Type of repository"))
  94. .arg(Arg::new("repo")
  95. .long("name")
  96. .short('n')
  97. .value_name("REPO")
  98. .help("Name of repository"))
  99. .arg(Arg::new("options")
  100. .action(ArgAction::Append)
  101. .value_name("OPTION=VALUE")
  102. .help("Type-specific options, in option=value form")))
  103. .subcommand(Command::new("config")
  104. .about("Configure repository")
  105. .arg(Arg::new("repo")
  106. .value_name("REPO")
  107. .required(true)
  108. .help("Name of repository to configure"))
  109. .arg(Arg::new("autocreate")
  110. .short('a')
  111. .long("autocreate")
  112. .value_name("YES/NO")
  113. .help("Set autocreation")
  114. .value_parser(value_parser!(HumanBool)))
  115. .arg(Arg::new("disable")
  116. .short('D')
  117. .long("disable")
  118. .value_name("YES/NO")
  119. .help("Disable repository")
  120. .value_parser(value_parser!(HumanBool)))
  121. .arg(Arg::new("options")
  122. .action(ArgAction::Append)
  123. .value_name("OPTION=VALUE")
  124. .help("Type-specific options, in option=value form")))
  125. .subcommand(Command::new("remove")
  126. .visible_aliases(["rm"])
  127. .about("Remove a repository")
  128. .arg(Arg::new("repo")
  129. .help("Name of repository")
  130. .value_name("REPO")
  131. .required(true)))
  132. .subcommand(Command::new("show")
  133. .visible_aliases(["describe"])
  134. .about("Show information about a repository")
  135. .arg(Arg::new("repo")
  136. .help("Name of repository")
  137. .value_name("REPO")
  138. .required(true))))
  139. .subcommand(Command::new("group")
  140. .about("Create and manage groups of repositories")
  141. .subcommand_required(true)
  142. .subcommand(Command::new("create")
  143. .about("Create a group")
  144. .arg(Arg::new("group")
  145. .help("Name of group")
  146. .required(true)
  147. .value_name("GROUP")))
  148. .subcommand(Command::new("delete")
  149. .visible_aliases(["drop"])
  150. .about("Delete a group.")
  151. .arg(Arg::new("group")
  152. .help("Name of group")
  153. .required(true)
  154. .value_name("GROUP")))
  155. .subcommand(Command::new("add")
  156. .about("Add a repo to a group")
  157. .arg(Arg::new("group")
  158. .help("Name of group")
  159. .required(true)
  160. .value_name("GROUP"))
  161. .arg(Arg::new("repo")
  162. .value_name("NAME")
  163. .required(true)
  164. .help("Name of repository"))
  165. )
  166. .subcommand(Command::new("act")
  167. .about("Add an action to a group")
  168. .arg(Arg::new("group")
  169. .help("Name of group")
  170. .required(true)
  171. .value_name("GROUP"))
  172. .arg(Arg::new("action")
  173. .help("Name of action")
  174. .required(true)
  175. .value_name("ACTION")))
  176. .subcommand(Command::new("remove")
  177. .about("Remove a repo from a group")
  178. .arg(Arg::new("group")
  179. .help("Name of group")
  180. .required(true)
  181. .value_name("GROUP"))
  182. .arg(Arg::new("action")
  183. .help("Name of action")
  184. .required(true)
  185. .value_name("ACTION")))
  186. .subcommand(Command::new("show")
  187. .about("Show information about a group")
  188. .arg(Arg::new("group")
  189. .help("Name of group")
  190. .required(true)
  191. .value_name("GROUP")))
  192. .subcommand(Command::new("list")
  193. .about("List known groups")))
  194. .subcommand(Command::new("type")
  195. .about("Create and manage repository types")
  196. .subcommand_required(true)
  197. .subcommand(Command::new("create")
  198. .about("Create a new repository type")
  199. .arg(Arg::new("type")
  200. .help("Name of type")
  201. .required(true)
  202. .value_name("TYPE"))
  203. .arg(Arg::new("description")
  204. .short('d')
  205. .long("description")
  206. .help("Description of repository type")
  207. .value_name("DESCRIPTION"))
  208. .arg(Arg::new("create")
  209. .short('c')
  210. .long("create")
  211. .help("Creation command")
  212. .value_name("COMMAND"))
  213. .arg(Arg::new("inward")
  214. .short('i')
  215. .long("inward")
  216. .help("Inward sync command")
  217. .value_name("COMMAND"))
  218. .arg(Arg::new("outward")
  219. .short('o')
  220. .long("outward")
  221. .help("Outward sync command")
  222. .value_name("COMMAND"))
  223. .arg(Arg::new("status")
  224. .short('s')
  225. .long("status")
  226. .help("Status command")
  227. .value_name("COMMAND"))
  228. .arg(Arg::new("pre_inward")
  229. .long("pre-inward")
  230. .help("Pre-inward command")
  231. .value_name("COMMAND"))
  232. .arg(Arg::new("post_inward")
  233. .long("post-inward")
  234. .help("Post-inward command")
  235. .value_name("COMMAND"))
  236. .arg(Arg::new("post_outward")
  237. .long("post-outward")
  238. .help("Post-outward command")
  239. .value_name("COMMAND"))
  240. )
  241. .subcommand(Command::new("config")
  242. .about("Configure a repository type")
  243. .arg(Arg::new("type")
  244. .help("Name of type")
  245. .required(true)
  246. .value_name("TYPE"))
  247. .arg(Arg::new("description")
  248. .short('d')
  249. .long("description")
  250. .help("Description of repository type")
  251. .value_name("DESCRIPTION"))
  252. .arg(Arg::new("create")
  253. .short('c')
  254. .long("create")
  255. .help("Creation command")
  256. .value_name("COMMAND"))
  257. .arg(Arg::new("inward")
  258. .short('i')
  259. .long("inward")
  260. .help("Inward sync command")
  261. .value_name("COMMAND"))
  262. .arg(Arg::new("outward")
  263. .short('o')
  264. .long("outward")
  265. .help("Outward sync command")
  266. .value_name("COMMAND"))
  267. .arg(Arg::new("status")
  268. .short('s')
  269. .long("status")
  270. .help("Status command")
  271. .value_name("COMMAND"))
  272. .arg(Arg::new("pre_inward")
  273. .long("pre-inward")
  274. .help("Pre-inward command")
  275. .value_name("COMMAND"))
  276. .arg(Arg::new("post_inward")
  277. .long("post-inward")
  278. .help("Post-inward command")
  279. .value_name("COMMAND"))
  280. .arg(Arg::new("post_outward")
  281. .long("post-outward")
  282. .help("Post-outward command")
  283. .value_name("COMMAND")))
  284. .subcommand(Command::new("command")
  285. .about("Manage commands in a repository type")
  286. .subcommand_required(true)
  287. .subcommand(Command::new("add")
  288. .about("Add a command to a repository type")
  289. .arg(Arg::new("type")
  290. .help("Name of type")
  291. .required(true)
  292. .value_name("TYPE"))
  293. .arg(Arg::new("name")
  294. .help("Name of command")
  295. .required(true)
  296. .value_name("NAME"))
  297. .arg(Arg::new("command")
  298. .help("Command")
  299. .required(true)
  300. .value_name("COMMAND")))
  301. .subcommand(Command::new("change")
  302. .about("Change a command in a repository type")
  303. .arg(Arg::new("type")
  304. .help("Name of type")
  305. .required(true)
  306. .value_name("TYPE"))
  307. .arg(Arg::new("name")
  308. .help("Name of command")
  309. .required(true)
  310. .value_name("NAME"))
  311. .arg(Arg::new("command")
  312. .help("Command")
  313. .required(true)
  314. .value_name("COMMAND")))
  315. .subcommand(Command::new("remove")
  316. .about("Remove a command from a repository type")
  317. .arg(Arg::new("type")
  318. .help("Name of type")
  319. .required(true)
  320. .value_name("TYPE"))
  321. .arg(Arg::new("name")
  322. .help("Name of command")
  323. .required(true)
  324. .value_name("NAME")))
  325. )
  326. .subcommand(Command::new("show")
  327. .about("Show information about a repository type")
  328. .arg(Arg::new("type")
  329. .help("Name of type")
  330. .required(true)
  331. .value_name("TYPE")))
  332. .subcommand(Command::new("list")
  333. .about("List known repository types")))
  334. .subcommand(Command::new("action")
  335. .about("Create and manage actions")
  336. .subcommand_required(true)
  337. .subcommand(Command::new("create")
  338. .about("Create a new action")
  339. .arg(Arg::new("action")
  340. .help("Name of action")
  341. .required(true)
  342. .value_name("ACTION"))
  343. .arg(Arg::new("command")
  344. .help("Command")
  345. .required(true)
  346. .value_name("COMMAND"))
  347. .arg(Arg::new("description")
  348. .help("Description of action")
  349. .long("description")
  350. .short('d')
  351. .value_name("DESCRIPTION")))
  352. .subcommand(Command::new("config")
  353. .about("Configure an action")
  354. .arg(Arg::new("action")
  355. .help("Name of action")
  356. .required(true)
  357. .value_name("ACTION"))
  358. .arg(Arg::new("disable")
  359. .short('D')
  360. .long("disable")
  361. .value_name("YES/NO")
  362. .help("Disable action")
  363. .value_parser(value_parser!(HumanBool)))
  364. .arg(Arg::new("command")
  365. .help("Command")
  366. .long("command")
  367. .short('c')
  368. .value_name("COMMAND"))
  369. .arg(Arg::new("description")
  370. .help("Description of action")
  371. .long("description")
  372. .short('d')
  373. .value_name("DESCRIPTION")))
  374. .subcommand(Command::new("show")
  375. .about("Show information about an action")
  376. .arg(Arg::new("action")
  377. .help("Name of action")
  378. .required(true)
  379. .value_name("ACTION")))
  380. .subcommand(Command::new("list")
  381. .about("List known actions")))
  382. .subcommand(Command::new("completion")
  383. .about("Generate completions for command.")
  384. .arg(Arg::new("shell")
  385. .value_name("SHELL")
  386. .help("Which shell to generate completions for")
  387. .required(true)
  388. .value_parser(value_parser!(Shell))))
  389. }
  390. fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
  391. generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
  392. }
  393. fn main() {
  394. let matches = build_cli().get_matches();
  395. let config_file = find_config_file(matches.get_one::<String>("config"));
  396. let mut configuration: Config = read_configuration_file(&config_file);
  397. if matches.get_one::<String>("config").is_some() {
  398. configuration.is_not_default = true;
  399. configuration.base_path = config_file.canonicalize().unwrap().parent().unwrap().to_path_buf();
  400. }
  401. match matches.subcommand() {
  402. Some(("completion", subm)) => {
  403. let mut cmd = build_cli();
  404. if let Some(generator) = subm.get_one::<Shell>("shell").copied() {
  405. print_completions(generator, &mut cmd);
  406. }
  407. }
  408. Some(("run", subm)) => {
  409. let repos: Vec<&str> = subm.get_many::<String>("repo")
  410. .expect("At least one repository/group must be specified.")
  411. .map(|s| s.as_str()).collect();
  412. if let Some(command) = subm.get_one::<String>("command") {
  413. run::run_with_command(&configuration, command, repos);
  414. } else {
  415. run::run(&configuration, repos);
  416. }
  417. }
  418. Some(("repository", subm)) => {
  419. match subm.subcommand() {
  420. Some(("register", subm)) => {
  421. let type_name = subm.get_one::<String>("type").expect("A type name must be provided").to_string();
  422. let location = match configuration.is_not_default {
  423. true => env::current_dir().unwrap().strip_prefix(&configuration.base_path).unwrap().to_path_buf(),
  424. _ => env::current_dir().unwrap()
  425. };
  426. let location_str = location.to_str().unwrap().to_string();
  427. let name = match subm.get_one::<String>("name") {
  428. Some(name) => name.to_string(),
  429. None => location.file_name().unwrap().to_str().unwrap().to_string()
  430. };
  431. let mut option_strings: Vec<String> = Vec::new();
  432. match subm.get_many::<String>("options") {
  433. Some(option) => {
  434. for string in option {
  435. option_strings.push(string.to_string())
  436. }
  437. }
  438. None => {}
  439. }
  440. repository::register(&mut configuration, &name, location_str, type_name, option_strings);
  441. }
  442. Some(("config", subm)) => {
  443. let repo = subm.get_one::<String>("repo").expect("A repository name must be provided").to_string();
  444. if let Some(options) = subm.get_many::<String>("options") {
  445. let mut option_strings: Vec<String> = Vec::new();
  446. for str_thing in options {
  447. option_strings.push(str_thing.to_string())
  448. }
  449. repository::update_options(&mut configuration, &repo, option_strings);
  450. }
  451. match subm.get_one::<HumanBool>("autocreate") {
  452. Some(HumanBool::Yes) => repository::update_autocreate(&mut configuration, &repo, true),
  453. Some(HumanBool::No) => repository::update_autocreate(&mut configuration, &repo, false),
  454. _ => {}
  455. }
  456. match subm.get_one::<HumanBool>("disable") {
  457. Some(HumanBool::Yes) => repository::update_disabled(&mut configuration, &repo, true),
  458. Some(HumanBool::No) => repository::update_disabled(&mut configuration, &repo, false),
  459. _ => {}
  460. }
  461. }
  462. Some(("remove", subm)) => {
  463. let repo = subm.get_one::<String>("repo").expect("A repository name must be provided").to_string();
  464. repository::remove_repo(&mut configuration, &repo);
  465. }
  466. Some(("list", _subm)) => {
  467. for key in configuration.repositories.keys() {
  468. println!(" - {}", key);
  469. }
  470. }
  471. Some(("show", subm)) => {
  472. let repo = subm.get_one::<String>("repo").expect("A repository name must be provided").to_string();
  473. let repository = configuration.repositories.get(&repo);
  474. match repository {
  475. Some(repository) => println!("{}", repository),
  476. None => eprintln!("No known repository named \"{}\".", repo)
  477. }
  478. }
  479. _ => {
  480. panic!("This should never happen...")
  481. }
  482. }
  483. }
  484. Some(("group", subm)) => {
  485. match subm.subcommand() {
  486. Some(("create", subm)) => {
  487. let group = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  488. group::add(&mut configuration, &group);
  489. }
  490. Some(("delete", subm)) => {
  491. let group = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  492. group::remove_group(&mut configuration, &group);
  493. }
  494. Some(("add", subm)) => {
  495. let group = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  496. let repo = subm.get_one::<String>("repo").expect("A repository name must be provided.").to_string();
  497. group::add_repo(&mut configuration, &group, &repo)
  498. }
  499. Some(("act", subm)) => {
  500. let group = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  501. let action = subm.get_one::<String>("action").expect("An action name must be provided").to_string();
  502. group::add_action(&mut configuration, &group, &action);
  503. }
  504. Some(("remove", subm)) => {
  505. let group = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  506. let repo = subm.get_one::<String>("repo").expect("A repository name must be provided.").to_string();
  507. group::remove_repo(&mut configuration, &group, &repo)
  508. }
  509. Some(("show", subm)) => {
  510. let group_name = subm.get_one::<String>("group").expect("A group name must be provided.").to_string();
  511. let group = configuration.groups.get(&group_name);
  512. match group {
  513. Some(group) => println!("{}", group),
  514. None => eprintln!("No known group named \"{}\".", group_name)
  515. }
  516. }
  517. Some(("list", _subm)) => {
  518. for key in configuration.groups.keys() {
  519. println!(" - {}", key);
  520. }
  521. }
  522. _ => {
  523. panic!("This should never happen...")
  524. }
  525. }
  526. }
  527. Some(("type", subm)) => {
  528. match subm.subcommand() {
  529. Some(("create", subm)) => {
  530. let tname = subm.get_one::<String>("type").expect("A type name must be provided").to_string();
  531. let temp_string = "".to_string();
  532. let description = subm.get_one::<String>("description").unwrap_or(&temp_string);
  533. let create = subm.get_one::<String>("create").unwrap_or(&temp_string);
  534. let inward = subm.get_one::<String>("inward").unwrap_or(&temp_string);
  535. let outward = subm.get_one::<String>("outward").unwrap_or(&temp_string);
  536. let status = subm.get_one::<String>("status").unwrap_or(&temp_string);
  537. let pre_inward = subm.get_one::<String>("pre_inward").unwrap_or(&temp_string);
  538. let post_inward = subm.get_one::<String>("post_inward").unwrap_or(&temp_string);
  539. let post_outward = subm.get_one::<String>("post_outward").unwrap_or(&temp_string);
  540. repotype::add(&mut configuration, &tname, &description, &create, &inward, &outward, &status, &pre_inward, &post_inward, &post_outward);
  541. }
  542. Some(("config", subm)) => {
  543. let tname = subm.get_one::<String>("type").expect("A type name must be provided").to_string();
  544. match subm.get_one::<String>("description") {
  545. Some(description) => repotype::update_description(&mut configuration, &tname, &description.to_string()),
  546. _ => {}
  547. }
  548. match subm.get_one::<String>("create") {
  549. Some(create) => repotype::update_create(&mut configuration, &tname, &create.to_string()),
  550. _ => {}
  551. }
  552. match subm.get_one::<String>("inward") {
  553. Some(inward) => repotype::update_inward(&mut configuration, &tname, &inward.to_string()),
  554. _ => {}
  555. }
  556. match subm.get_one::<String>("outward") {
  557. Some(outward) => repotype::update_outward(&mut configuration, &tname, &outward.to_string()),
  558. _ => {}
  559. }
  560. match subm.get_one::<String>("status") {
  561. Some(status) => repotype::update_status(&mut configuration, &tname, &status.to_string()),
  562. _ => {}
  563. }
  564. match subm.get_one::<String>("pre_inward") {
  565. Some(pre_inward) => repotype::update_pre_inward(&mut configuration, &tname, &pre_inward.to_string()),
  566. _ => {}
  567. }
  568. match subm.get_one::<String>("post_inward") {
  569. Some(post_inward) => repotype::update_post_inward(&mut configuration, &tname, &post_inward.to_string()),
  570. _ => {}
  571. }
  572. match subm.get_one::<String>("post_outward") {
  573. Some(post_outward) => repotype::update_post_outward(&mut configuration, &tname, &post_outward.to_string()),
  574. _ => {}
  575. }
  576. }
  577. Some(("command", subm)) => {
  578. match subm.subcommand() {
  579. Some(("add", subm)) => {
  580. let type_name = subm.get_one::<String>("type").expect("A type name is required").to_string();
  581. let name = subm.get_one::<String>("name").expect("A name is required").to_string();
  582. let command = subm.get_one::<String>("command").expect("A command is required").to_string();
  583. repotype::add_command(&mut configuration, &type_name, &name, &command);
  584. },
  585. Some(("change", subm)) => {
  586. let type_name = subm.get_one::<String>("type").expect("A type name is required").to_string();
  587. let name = subm.get_one::<String>("name").expect("A name is required").to_string();
  588. let command = subm.get_one::<String>("command").expect("A command is required").to_string();
  589. repotype::change_command(&mut configuration, &type_name, &name, &command);
  590. },
  591. Some(("remove", subm)) => {
  592. let type_name = subm.get_one::<String>("type").expect("A type name is required").to_string();
  593. let name = subm.get_one::<String>("name").expect("A name is required").to_string();
  594. repotype::remove_command(&mut configuration, &type_name, &name);
  595. },
  596. _ => panic!("Something has gone horribly wrong...")
  597. }
  598. }
  599. Some(("show", subm)) => {
  600. let tname = subm.get_one::<String>("type").expect("A type name is required").to_string();
  601. let repo_type = configuration.repo_types.get(&tname);
  602. match repo_type {
  603. Some(repo_type) => println!("{}", repo_type),
  604. None => eprintln!("No known repo type named \"{}\".", tname)
  605. }
  606. }
  607. Some(("list", _subm)) => {
  608. for key in configuration.repo_types.keys() {
  609. println!(" - {}", key);
  610. }
  611. }
  612. _ => {
  613. panic!("This should never happen...")
  614. }
  615. }
  616. }
  617. Some(("action", subm)) => {
  618. match subm.subcommand() {
  619. Some(("create", subm)) => {
  620. let name = subm.get_one::<String>("action").expect("An action name is required").to_string();
  621. let command = subm.get_one::<String>("command").expect("A command is required").to_string();
  622. let temp_string = "".to_string();
  623. let description = subm.get_one::<String>("Description").unwrap_or(&temp_string);
  624. action::add(&mut configuration, &name, &description, &command);
  625. }
  626. Some(("config", subm)) => {
  627. let name = subm.get_one::<String>("action").expect("An action name is required").to_string();
  628. match subm.get_one::<HumanBool>("disabled") {
  629. Some(HumanBool::Yes) => action::update_disabled(&mut configuration, &name, true),
  630. Some(HumanBool::No) => action::update_disabled(&mut configuration, &name, false),
  631. _ => {}
  632. }
  633. match subm.get_one::<String>("command") {
  634. Some(command) => action::update_command(&mut configuration, &name, &command.to_string()),
  635. _ => {}
  636. }
  637. match subm.get_one::<String>("description") {
  638. Some(description) => action::update_description(&mut configuration, &name, &description.to_string()),
  639. _ => {}
  640. }
  641. }
  642. Some(("show", subm)) => {
  643. let name = subm.get_one::<String>("action").expect("An action name is required").to_string();
  644. let action = configuration.actions.get(&name);
  645. match action {
  646. Some(action) => println!("{}", action),
  647. None => eprintln!("No known action named \"{}\".", name)
  648. }
  649. }
  650. Some(("list", _subm)) => {
  651. for key in configuration.actions.keys() {
  652. println!(" - {}", key);
  653. }
  654. }
  655. _ => {
  656. panic!("This should never happen...")
  657. }
  658. }
  659. }
  660. _ => {
  661. panic!("This should never happen...")
  662. }
  663. }
  664. match write_configuration_file(config_file, configuration) {
  665. Err(err) => panic!("Error writing configuration: {}.", err),
  666. _ => {}
  667. }
  668. }