1use std::path::PathBuf;
2
3use clap::{Parser, Subcommand};
4
5use crate::commands::{AddCommand, Command, InitCommand, RemoveCommand, SyncCommand};
6use crate::error::Result;
7
8#[derive(Parser)]
9#[command(version, about = "A simple dotfiles manager")]
10pub struct Cli {
11 #[command(subcommand)]
12 command: CliCommand,
13}
14
15#[derive(Subcommand)]
16enum CliCommand {
17 Init,
19 Add { path: PathBuf },
21 Remove { path: PathBuf },
23 Sync,
25}
26
27pub fn run() -> Result<()> {
28 let cli = Cli::parse();
29
30 match cli.command {
31 CliCommand::Init => InitCommand::new().execute(),
32 CliCommand::Add { path } => AddCommand::new(path).execute(),
33 CliCommand::Remove { path } => RemoveCommand::new(path).execute(),
34 CliCommand::Sync => SyncCommand::new().execute(),
35 }
36}
37
38#[cfg(test)]
39mod tests {
40 use super::*;
41 use clap::CommandFactory;
42
43 #[test]
44 fn verify_cli() {
45 Cli::command().debug_assert();
46 }
47
48 #[test]
49 fn parse_init() {
50 let cli = Cli::try_parse_from(["dot", "init"]).unwrap();
51 assert!(matches!(cli.command, CliCommand::Init));
52 }
53
54 #[test]
55 fn parse_add() {
56 let cli = Cli::try_parse_from(["dot", "add", "/path/file"]).unwrap();
57 assert!(
58 matches!(cli.command, CliCommand::Add { path } if path == PathBuf::from("/path/file"))
59 );
60 }
61
62 #[test]
63 fn parse_remove() {
64 let cli = Cli::try_parse_from(["dot", "remove", "myfile"]).unwrap();
65 assert!(
66 matches!(cli.command, CliCommand::Remove { path } if path == PathBuf::from("myfile"))
67 );
68 }
69
70 #[test]
71 fn parse_sync() {
72 let cli = Cli::try_parse_from(["dot", "sync"]).unwrap();
73 assert!(matches!(cli.command, CliCommand::Sync));
74 }
75
76 #[test]
77 fn add_requires_path() {
78 assert!(Cli::try_parse_from(["dot", "add"]).is_err());
79 }
80}