1pub mod preview;
2pub mod render;
3
4use anyhow::Result;
5use clap::{Args, Parser, Subcommand};
6
7#[derive(Args, Debug, Clone, Default)]
8#[group(multiple = false)]
9pub struct TargetArg {
10 #[arg(global = true, long, help_heading = "Cargo Target Options")]
11 pub lib: bool,
12 #[arg(global = true, long, help_heading = "Cargo Target Options")]
13 pub example: Option<String>,
14}
15
16#[derive(Parser, Debug, Clone, Default)]
17pub struct CliArgs {
18 #[arg(global = true, short, long, help_heading = "Cargo Options")]
19 pub package: Option<String>,
20
21 #[command(flatten)]
22 pub target: TargetArg,
23
24 #[arg(trailing_var_arg = true, allow_hyphen_values = true)]
25 pub args: Vec<String>,
26}
27
28#[derive(Parser, Debug)]
29#[command(name = "ranim")]
30#[command(about = "A CLI tool for Ranim animation library")]
31#[command(version)]
32pub struct Cli {
33 #[command(subcommand)]
34 command: Commands,
35
36 #[command(flatten)]
37 pub args: CliArgs,
38}
39
40impl Cli {
41 pub fn run(self) -> Result<()> {
42 let args = self.args;
43
44 match self.command {
45 Commands::Preview { scene } => {
46 preview::preview_command(&args, &scene)?;
47 }
48 Commands::Render { scenes } => {
49 render::render_command(&args, &scenes)?;
50 }
51 }
52
53 Ok(())
54 }
55}
56
57#[derive(Subcommand, Debug)]
58pub enum Commands {
59 Preview { scene: Option<String> },
61 Render {
63 #[arg(num_args = 0..)]
65 scenes: Vec<String>,
66 },
67}
68
69#[cfg(test)]
70mod test {
71 use crate::Target;
72
73 use super::*;
74
75 #[test]
76 fn test_cli() {
77 let parse_args = |args: &[&str]| {
78 println!("parsing args {:?}", args);
79 let cli = Cli::try_parse_from(args);
80 println!("result: {:?}", cli);
81 cli
82 };
83 let cli = parse_args(&["ranim", "render", "-p", "package"]).unwrap();
84 let Commands::Render { scenes } = &cli.command else {
85 unreachable!()
86 };
87 assert!(scenes.is_empty());
88 assert_eq!(cli.args.package, Some("package".to_string()));
89 assert!(!cli.args.target.lib);
90 assert!(cli.args.target.example.is_none());
91
92 let cli = parse_args(&["ranim", "preview", "--lib"]).unwrap();
93 assert!(matches!(cli.command, Commands::Preview { scene: None }));
94 assert!(cli.args.package.is_none());
95 let TargetArg { lib, example } = cli.args.target.clone();
96 assert!(lib);
97 assert!(example.is_none());
98 assert_eq!(Target::from(cli.args.target.clone()), Target::Lib);
99
100 let cli = parse_args(&["ranim", "preview", "--example", "example"]).unwrap();
101 assert!(matches!(cli.command, Commands::Preview { scene: None }));
102 assert!(cli.args.package.is_none());
103 let TargetArg { lib, example } = cli.args.target.clone();
104 assert!(!lib);
105 assert_eq!(example, Some("example".to_string()));
106 assert_eq!(
107 Target::from(cli.args.target.clone()),
108 Target::Example("example".to_string())
109 );
110 }
111}