1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use clap::Parser;

use crate::cmds::project::{ProjectListCliArgs, ProjectMetadataGetCliArgs};

use super::common::{validate_project_repo_path, GetArgs, ListArgs};

#[derive(Parser)]
pub struct ProjectCommand {
    #[clap(subcommand)]
    subcommand: ProjectSubcommand,
}

#[derive(Parser)]
enum ProjectSubcommand {
    #[clap(about = "Gather project information metadata")]
    Info(ProjectInfo),
    #[clap(about = "List project/repository tags")]
    Tags(ListProject),
}

#[derive(Parser)]
struct ProjectInfo {
    /// ID of the project
    #[clap(long, group = "id_or_repo")]
    pub id: Option<i64>,
    /// Path of the project in the format `OWNER/PROJECT_NAME`
    #[clap(long, group = "id_or_repo", value_name = "OWNER/PROJECT_NAME", value_parser=validate_project_repo_path)]
    pub repo: Option<String>,
    #[clap(flatten)]
    pub get_args: GetArgs,
}

#[derive(Parser)]
pub struct ListProject {
    #[clap(flatten)]
    pub list_args: ListArgs,
}

impl From<ProjectCommand> for ProjectOptions {
    fn from(options: ProjectCommand) -> Self {
        match options.subcommand {
            ProjectSubcommand::Info(options) => options.into(),
            ProjectSubcommand::Tags(options) => options.into(),
        }
    }
}

impl From<ProjectInfo> for ProjectOptions {
    fn from(options: ProjectInfo) -> Self {
        ProjectOptions::Info(
            ProjectMetadataGetCliArgs::builder()
                .id(options.id)
                .path(options.repo)
                .get_args(options.get_args.into())
                .build()
                .unwrap(),
        )
    }
}

impl From<ListProject> for ProjectOptions {
    fn from(options: ListProject) -> Self {
        ProjectOptions::Tags(
            ProjectListCliArgs::builder()
                .list_args(options.list_args.into())
                .tags(true)
                .build()
                .unwrap(),
        )
    }
}

pub enum ProjectOptions {
    Info(ProjectMetadataGetCliArgs),
    Tags(ProjectListCliArgs),
}

#[cfg(test)]
mod test {
    use crate::cli::{Args, Command};

    use super::*;

    #[test]
    fn test_project_cli_info() {
        let args = Args::parse_from(vec!["gr", "pj", "info", "--id", "1"]);
        let project_info = match args.command {
            Command::Project(ProjectCommand {
                subcommand: ProjectSubcommand::Info(options),
            }) => {
                assert_eq!(options.id, Some(1));
                options
            }
            _ => panic!("Expected ProjectCommand::Info"),
        };
        let options: ProjectOptions = project_info.into();
        match options {
            ProjectOptions::Info(options) => {
                assert_eq!(options.id, Some(1));
            }
            _ => panic!("Expected ProjectOptions::Info"),
        }
    }

    #[test]
    fn test_project_cli_list_tags() {
        let args = Args::parse_from(vec!["gr", "pj", "tags"]);
        let list_project = match args.command {
            Command::Project(ProjectCommand {
                subcommand: ProjectSubcommand::Tags(options),
            }) => options,
            _ => panic!("Expected ProjectCommand::Info"),
        };
        let options: ProjectOptions = list_project.into();
        match options {
            ProjectOptions::Tags(cli_args) => {
                assert!(cli_args.tags);
                assert!(!cli_args.stars);
            }
            _ => panic!("Expected ProjectOptions::Info"),
        }
    }
}