cot_cli/
handlers.rs

1use std::path::PathBuf;
2
3use anyhow::Context;
4use clap::CommandFactory;
5
6use crate::args::{
7    Cli, CompletionsArgs, ManpagesArgs, MigrationListArgs, MigrationMakeArgs, ProjectNewArgs,
8};
9use crate::migration_generator::{MigrationGeneratorOptions, list_migrations, make_migrations};
10use crate::new_project::{CotSource, new_project};
11
12pub fn handle_new_project(
13    ProjectNewArgs { path, name, source }: ProjectNewArgs,
14) -> anyhow::Result<()> {
15    let project_name = match name {
16        None => {
17            let dir_name = path
18                .file_name()
19                .with_context(|| format!("file name not present: {}", path.display()))?;
20            dir_name.to_string_lossy().into_owned()
21        }
22        Some(name) => name,
23    };
24
25    let cot_source = if source.use_git {
26        CotSource::Git
27    } else if let Some(path) = &source.cot_path {
28        CotSource::Path(path)
29    } else {
30        CotSource::PublishedCrate
31    };
32    new_project(&path, &project_name, &cot_source).with_context(|| "unable to create project")
33}
34
35pub fn handle_migration_list(MigrationListArgs { path }: MigrationListArgs) -> anyhow::Result<()> {
36    let path = path.unwrap_or(PathBuf::from("."));
37    let migrations = list_migrations(&path).with_context(|| "unable to list migrations")?;
38    for (app_name, migs) in migrations {
39        for mig in migs {
40            println!("{app_name}\t{mig}");
41        }
42    }
43
44    Ok(())
45}
46
47pub fn handle_migration_make(
48    MigrationMakeArgs {
49        path,
50        app_name,
51        output_dir,
52    }: MigrationMakeArgs,
53) -> anyhow::Result<()> {
54    let path = path.unwrap_or(PathBuf::from("."));
55    let options = MigrationGeneratorOptions {
56        app_name,
57        output_dir,
58    };
59    make_migrations(&path, options).with_context(|| "unable to create migrations")
60}
61
62pub fn handle_cli_manpages(
63    ManpagesArgs { output_dir, create }: ManpagesArgs,
64) -> anyhow::Result<()> {
65    let output_dir = output_dir.unwrap_or(PathBuf::from("."));
66    if create {
67        std::fs::create_dir_all(&output_dir).context("unable to create output directory")?;
68    }
69    clap_mangen::generate_to(Cli::command(), output_dir)
70        .context("unable to generate manpages in output directory")
71}
72
73#[expect(clippy::unnecessary_wraps)] // return Result<()> for consistency
74pub fn handle_cli_completions(CompletionsArgs { shell }: CompletionsArgs) -> anyhow::Result<()> {
75    generate_completions(shell, &mut std::io::stdout());
76
77    Ok(())
78}
79
80fn generate_completions(shell: clap_complete::Shell, writer: &mut impl std::io::Write) {
81    clap_complete::generate(shell, &mut Cli::command(), "cot", writer);
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87    use crate::args::CotSourceArgs;
88
89    #[test]
90    fn new_project_wrong_directory() {
91        let temp_dir = tempfile::tempdir().unwrap();
92        let args = ProjectNewArgs {
93            path: temp_dir.path().to_path_buf(),
94            name: None,
95            source: CotSourceArgs {
96                use_git: false,
97                cot_path: None,
98            },
99        };
100
101        let result = handle_new_project(args);
102
103        assert!(result.is_err());
104    }
105
106    #[test]
107    fn migration_list_wrong_directory() {
108        let args = MigrationListArgs {
109            path: Some(PathBuf::from("nonexistent")),
110        };
111
112        let result = handle_migration_list(args);
113
114        assert!(result.is_err());
115    }
116
117    #[test]
118    fn migration_make_wrong_directory() {
119        let args = MigrationMakeArgs {
120            path: Some(PathBuf::from("nonexistent")),
121            app_name: None,
122            output_dir: None,
123        };
124
125        let result = handle_migration_make(args);
126
127        assert!(result.is_err());
128    }
129
130    #[test]
131    fn generate_manpages() {
132        let temp_dir = tempfile::tempdir().unwrap();
133        let args = ManpagesArgs {
134            output_dir: Some(temp_dir.path().to_path_buf()),
135            create: true,
136        };
137
138        let result = handle_cli_manpages(args);
139
140        assert!(result.is_ok());
141        assert!(temp_dir.path().join("cot.1").exists());
142    }
143
144    #[test]
145    fn generate_completions_shell() {
146        let mut output = Vec::new();
147
148        generate_completions(clap_complete::Shell::Bash, &mut output);
149
150        assert!(!output.is_empty());
151    }
152}