189 lines · 5.5 KB · Rust
RawDownload
pub mod inspect;pub mod preview;pub mod render; use anyhow::Result;use clap::{Args, Parser, Subcommand}; use crate::cli::inspect::InspectCommand; #[derive(Args, Debug, Clone, Default)]#[group(multiple = false)]pub struct TargetArg {    #[arg(global = true, long, help_heading = "Cargo Target Options")]    pub lib: bool,    #[arg(global = true, long, help_heading = "Cargo Target Options")]    pub example: Option<String>,} #[derive(Parser, Debug, Clone, Default)]pub struct CliArgs {    #[arg(global = true, short, long, help_heading = "Cargo Options")]    pub package: Option<String>,     #[arg(global = true, long, help_heading = "Cargo Options")]    pub features: Vec<String>,     #[command(flatten)]    pub target: TargetArg,     #[arg(trailing_var_arg = true, allow_hyphen_values = true)]    pub args: Vec<String>,} #[derive(Parser, Debug)]#[command(name = "ranim")]#[command(about = "A CLI tool for Ranim animation library")]#[command(version)]pub struct Cli {    #[command(subcommand)]    command: Commands,     #[command(flatten)]    pub args: CliArgs,} impl Cli {    pub fn run(self) -> Result<()> {        let args = self.args;         match self.command {            Commands::Preview { scene } => {                preview::preview_command(&args, &scene)?;            }            Commands::Output {                scenes,                buffer_count,            } => {                render::output_command(&args, &scenes, buffer_count)?;            }            Commands::Render {                scene,                buffer_count,            } => {                render::render_command(&args, &scene, buffer_count)?;            }            Commands::Inspect { command } => {                inspect::inspect_command(&args, command)?;            }        }         Ok(())    }} #[derive(Subcommand, Debug)]pub enum Commands {    /// Launch a preview app, watch the lib crate and rebuild it to dylib when it is changed    Preview { scene: Option<String> },    /// Render every `#[output(...)]` declared by the selected scenes    Output {        /// Optional scene names to render (if not provided, render all scenes)        #[arg(num_args = 0..)]        scenes: Vec<String>,         /// Number of GPU readback buffers (higher = more parallelism, more VRAM)        #[arg(long, default_value_t = 2)]        buffer_count: usize,    },    /// Render a single scene once with default output settings (`1920x1080`, 60 fps, mp4)    Render {        /// Scene name to render        scene: String,         /// Number of GPU readback buffers (higher = more parallelism, more VRAM)        #[arg(long, default_value_t = 2)]        buffer_count: usize,    },    /// Inspect scenes, animation trees, or evaluated frames without rendering    Inspect {        #[command(subcommand)]        command: InspectCommand,    },} #[cfg(test)]mod test {    use crate::{        Target,        cli::inspect::{FormatArg, InspectCommand},    };     use super::*;     #[test]    fn test_cli() {        let parse_args = |args: &[&str]| Cli::try_parse_from(args);        let cli = parse_args(&["ranim", "output", "-p", "package"]).unwrap();        let Commands::Output { scenes, .. } = &cli.command else {            unreachable!()        };        assert!(scenes.is_empty());        assert_eq!(cli.args.package, Some("package".to_string()));        assert!(!cli.args.target.lib);        assert!(cli.args.target.example.is_none());         let cli = parse_args(&["ranim", "render", "scene_name"]).unwrap();        let Commands::Render { scene, .. } = &cli.command else {            unreachable!()        };        assert_eq!(scene, "scene_name");         let cli = parse_args(&["ranim", "preview", "--lib"]).unwrap();        assert!(matches!(cli.command, Commands::Preview { scene: None }));        assert!(cli.args.package.is_none());        let TargetArg { lib, example } = cli.args.target.clone();        assert!(lib);        assert!(example.is_none());        assert_eq!(Target::from(cli.args.target.clone()), Target::Lib);         let cli = parse_args(&["ranim", "preview", "--example", "example"]).unwrap();        assert!(matches!(cli.command, Commands::Preview { scene: None }));        assert!(cli.args.package.is_none());        let TargetArg { lib, example } = cli.args.target.clone();        assert!(!lib);        assert_eq!(example, Some("example".to_string()));        assert_eq!(            Target::from(cli.args.target.clone()),            Target::Example("example".to_string())        );         let cli = parse_args(&["ranim", "inspect", "scenes", "--format", "json"]).unwrap();        assert!(matches!(            &cli.command,            Commands::Inspect {                command: InspectCommand::Scenes {                    format: FormatArg::Json                }            }        ));         let cli = parse_args(&[            "ranim",            "inspect",            "frame",            "basic",            "--at",            "2.0",            "--verbose",            "--example",            "example",        ])        .unwrap();        assert!(matches!(            &cli.command,            Commands::Inspect {                command: InspectCommand::Frame {                    scene,                    at: 2.0,                    format: FormatArg::Text,                    verbose: true,                }            } if scene == "basic"        ));        assert_eq!(            Target::from(cli.args.target.clone()),            Target::Example("example".to_string())        );    }}