cli/
cli.rs

1use clap::{arg, Args, Parser, Subcommand};
2use dotenv::dotenv;
3use rujira::{agile, api, Rujira};
4use serde_json::json;
5use tracing_subscriber::{fmt, EnvFilter};
6
7#[derive(Parser, Debug)]
8#[command(name = "itcast")]
9pub struct Cli {
10    #[command(subcommand)]
11    command: Commands,
12}
13
14#[derive(Debug, Subcommand)]
15enum Commands {
16    Project(ProjectArgs),
17    Issue(IssueArgs),
18    Board(BoardArgs),
19    Sprint(SprintArgs),
20    Myself,
21}
22
23#[derive(Debug, Args)]
24struct ProjectArgs {
25    #[command(subcommand)]
26    command: ProjectCommands,
27}
28#[derive(Debug, Args)]
29struct IssueArgs {
30    #[command(subcommand)]
31    command: IssueCommands,
32}
33#[derive(Debug, Args)]
34struct BoardArgs {
35    #[command(subcommand)]
36    command: BoardCommands,
37}
38#[derive(Debug, Args)]
39struct SprintArgs {
40    #[command(subcommand)]
41    command: SprintCommands,
42}
43#[derive(Debug, Subcommand)]
44enum ProjectCommands {
45    Create {
46        key: String,
47        #[arg(short, long)]
48        name: String,
49        #[arg(short, long, default_value_t = String::from("software"))]
50        r#type: String,
51        #[arg(short, long)]
52        lead: String,
53    },
54    Delete {
55        key: String,
56    },
57    Update {
58        key: String,
59        #[arg(short, long)]
60        expand: Option<String>,
61        #[arg(short, long)]
62        update: String,
63    },
64    List {
65        #[arg(short, long)]
66        include_archive: Option<bool>,
67        #[arg(short, long)]
68        browse_archive: Option<bool>,
69        #[arg(short, long)]
70        expand: Option<String>,
71        #[arg(short, long)]
72        recent: Option<u8>,
73    },
74}
75#[derive(Debug, Subcommand)]
76enum IssueCommands {
77    Create {
78        project: String,
79        #[arg(short, long)]
80        summary: String,
81        #[arg(short, long)]
82        description: String,
83        #[arg(short, long, default_value_t = String::from("Task"))]
84        r#type: String,
85    },
86    Edit {
87        key: String,
88        #[arg(short, long)]
89        sprint: String,
90    },
91    Get {
92        key: String,
93    },
94    Delete {
95        key: String,
96    },
97}
98#[derive(Debug, Subcommand)]
99enum BoardCommands {
100    Get { key: String },
101    List {},
102}
103#[derive(Debug, Subcommand)]
104enum SprintCommands {
105    List { board: String },
106    Get { key: String },
107    Issue { key: String, task: String },
108}
109
110#[tokio::main]
111async fn main() {
112    dotenv().ok();
113    fmt()
114        .with_env_filter(EnvFilter::from_default_env())
115        .compact()
116        .init();
117    let bot = Rujira::new().from_env_handler();
118    let args = Cli::parse();
119    match args.command {
120        Commands::Myself => {
121            let me = match crate::api::myself::get(bot).apply().await {
122                Ok(rs) => rs,
123                Err(e) => {
124                    eprintln!("{e}");
125                    panic!();
126                }
127            };
128            println!("{me:#?}");
129        }
130        Commands::Issue(s) => match s.command {
131            IssueCommands::Create {
132                project,
133                summary,
134                description,
135                r#type,
136            } => {
137                let fields = json!({
138                    "project": { "key": &project },
139                    "summary": &summary,
140                    "description": &description,
141                    "issuetype": { "name": &r#type },
142                });
143                let issue = crate::api::issue::create(bot.clone(), fields, false)
144                    .apply()
145                    .await
146                    .unwrap();
147                println!("{issue:#?}");
148            }
149            IssueCommands::Edit { key, sprint } => {
150                let payload = json!({
151                    "sprint": { "name": sprint }
152                });
153                let issue = crate::api::issue::edit(bot.clone(), &key, None, Some(payload), None)
154                    .apply()
155                    .await
156                    .unwrap();
157                println!("{issue:#?}");
158            }
159            IssueCommands::Get { key } => {
160                let issue = crate::api::issue::get(bot.clone(), &key, None, None, None, None)
161                    .apply()
162                    .await
163                    .unwrap();
164                println!("{issue:#?}");
165            }
166            _ => todo!(),
167        },
168        Commands::Project(s) => match s.command {
169            ProjectCommands::Update {
170                key,
171                update,
172                expand,
173            } => {
174                // ### Example
175                // ```
176                // cargo run --example cli -- project update T1 -u '{"name":"new"}'
177                // ```
178                let update = serde_json::from_str(&update).unwrap();
179                let project = api::project::update(bot.clone(), &key, update, expand.as_deref())
180                    .apply()
181                    .await
182                    .unwrap();
183                println!("{project:#?}");
184            }
185            ProjectCommands::List {
186                include_archive,
187                browse_archive,
188                expand,
189                recent,
190            } => {
191                let projects = api::project::list(
192                    bot.clone(),
193                    include_archive,
194                    browse_archive,
195                    // TOASK: Why it is as_deref working?
196                    expand.as_deref(),
197                    recent,
198                )
199                .apply()
200                .await
201                .unwrap();
202                println!("{projects:#?}");
203            }
204            ProjectCommands::Create {
205                key,
206                name,
207                r#type,
208                lead,
209            } => {
210                api::project::create(bot.clone(), &key, &name, &r#type, &lead)
211                    .apply()
212                    .await
213                    .unwrap();
214            }
215            ProjectCommands::Delete { key } => {
216                api::project::delete(bot.clone(), &key)
217                    .apply()
218                    .await
219                    .unwrap();
220            }
221        },
222        Commands::Board(s) => match s.command {
223            BoardCommands::List {} => {
224                let boards = agile::board::list(bot.clone(), None, None, None, None, None)
225                    .apply()
226                    .await
227                    .unwrap();
228                println!("{boards:#?}");
229            }
230            BoardCommands::Get { key } => {
231                let board = agile::board::get(bot.clone(), &key).apply().await.unwrap();
232                println!("{board:#?}");
233            }
234        },
235        Commands::Sprint(s) => match s.command {
236            SprintCommands::List { board } => {
237                let sprints = agile::sprint::list(bot.clone(), &board, None, None, None)
238                    .apply()
239                    .await
240                    .unwrap();
241                println!("{sprints:#?}");
242            }
243            SprintCommands::Get { key } => {
244                let sprint = agile::sprint::get(bot.clone(), &key).apply().await.unwrap();
245                println!("{sprint:#?}");
246            }
247            SprintCommands::Issue { key, task } => {
248                let payload = json!([task]);
249                let sprint = agile::sprint::issue(bot.clone(), &key, payload)
250                    .apply()
251                    .await
252                    .unwrap();
253                println!("{sprint:#?}");
254            }
255        },
256    }
257}