//! `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, } #[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, /// Due date YYYY-MM-DD. #[arg(long)] due: Option, }, /// 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 = 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, })) } } }