Skip to main content

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