114 lines
3.4 KiB
Rust
114 lines
3.4 KiB
Rust
//! `milestone list|create|close` — manage milestones. Default action:
|
|
//! list.
|
|
|
|
use anyhow::{Context, Result};
|
|
use clap::{Args as ClapArgs, Subcommand};
|
|
use forgejo_api::structs::{
|
|
CreateMilestoneOption, EditMilestoneOption, IssueGetMilestonesListQuery,
|
|
};
|
|
use serde_json::{Value, json};
|
|
use time::OffsetDateTime;
|
|
use time::format_description::well_known::Rfc3339;
|
|
|
|
use crate::client::{Client, index};
|
|
use crate::verbs::{print_json, rfc3339};
|
|
|
|
#[derive(ClapArgs)]
|
|
pub struct Args {
|
|
#[command(subcommand)]
|
|
action: Option<Action>,
|
|
}
|
|
|
|
#[derive(Subcommand)]
|
|
enum Action {
|
|
/// List open milestones as JSON.
|
|
List,
|
|
/// Create a milestone, print {id,title}.
|
|
Create {
|
|
/// Milestone title.
|
|
#[arg(long)]
|
|
title: String,
|
|
/// Description.
|
|
#[arg(long)]
|
|
desc: Option<String>,
|
|
/// Due date YYYY-MM-DD.
|
|
#[arg(long)]
|
|
due: Option<String>,
|
|
},
|
|
/// Close a milestone by id.
|
|
Close {
|
|
/// Milestone id.
|
|
id: u64,
|
|
},
|
|
}
|
|
|
|
pub fn run(client: &Client, args: Args) -> Result<()> {
|
|
let (owner, name) = client.owner_repo()?;
|
|
match args.action.unwrap_or(Action::List) {
|
|
Action::List => {
|
|
let query = IssueGetMilestonesListQuery {
|
|
state: Some("open".to_owned()),
|
|
name: None,
|
|
};
|
|
let (_, milestones) = client
|
|
.api()
|
|
.issue_get_milestones_list(owner, name, query)
|
|
.page_size(50)
|
|
.send()?;
|
|
let trimmed: Vec<Value> = milestones
|
|
.iter()
|
|
.map(|m| {
|
|
json!({
|
|
"id": m.id,
|
|
"title": m.title,
|
|
"open_issues": m.open_issues,
|
|
"closed_issues": m.closed_issues,
|
|
"due_on": rfc3339(m.due_on),
|
|
"description": m.description,
|
|
})
|
|
})
|
|
.collect();
|
|
print_json(&Value::Array(trimmed))
|
|
}
|
|
Action::Create { title, desc, due } => {
|
|
let due_on = due
|
|
.filter(|s| !s.is_empty())
|
|
.map(|d| {
|
|
OffsetDateTime::parse(&format!("{d}T00:00:00Z"), &Rfc3339)
|
|
.with_context(|| format!("milestone create: bad --due date {d:?}"))
|
|
})
|
|
.transpose()?;
|
|
let payload = CreateMilestoneOption {
|
|
description: desc.filter(|s| !s.is_empty()),
|
|
due_on,
|
|
state: None,
|
|
title: Some(title),
|
|
};
|
|
let resp = client
|
|
.api()
|
|
.issue_create_milestone(owner, name, payload)
|
|
.send()?;
|
|
print_json(&json!({
|
|
"id": resp.id,
|
|
"title": resp.title,
|
|
}))
|
|
}
|
|
Action::Close { id } => {
|
|
let payload = EditMilestoneOption {
|
|
description: None,
|
|
due_on: None,
|
|
state: Some("closed".to_owned()),
|
|
title: None,
|
|
};
|
|
let resp = client
|
|
.api()
|
|
.issue_edit_milestone(owner, name, index(id)?, payload)
|
|
.send()?;
|
|
print_json(&json!({
|
|
"id": resp.id,
|
|
"title": resp.title,
|
|
"state": resp.state,
|
|
}))
|
|
}
|
|
}
|
|
}
|