kanban-cli 0.3.0

Command-line interface for the kanban project management tool
use clap::{Args, Parser, Subcommand};
use uuid::Uuid;

#[cfg(has_git_commit)]
const VERSION: &str = concat!(
    env!("CARGO_PKG_VERSION"),
    "\ncommit: ",
    env!("GIT_COMMIT_HASH")
);

#[cfg(not(has_git_commit))]
const VERSION: &str = env!("CARGO_PKG_VERSION");

#[derive(Parser)]
#[command(name = "kanban")]
#[command(about = "A terminal-based kanban board", long_about = None)]
#[command(version = VERSION, arg_required_else_help = false)]
pub struct Cli {
    /// Path to kanban data file (or set KANBAN_FILE env var)
    #[arg(value_name = "FILE", env = "KANBAN_FILE")]
    pub file: Option<String>,

    #[command(subcommand)]
    pub command: Option<Commands>,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Board operations
    Board(BoardCommand),
    /// Column operations
    Column(ColumnCommand),
    /// Card operations
    Card(CardCommand),
    /// Sprint operations
    Sprint(SprintCommand),
    /// Export board data
    Export(ExportArgs),
    /// Import board data
    Import(ImportArgs),
    /// Generate shell completions
    Completions {
        #[arg(value_enum)]
        shell: clap_complete::Shell,
    },
}

// Board commands
#[derive(Args)]
pub struct BoardCommand {
    #[command(subcommand)]
    pub action: BoardAction,
}

#[derive(Subcommand)]
pub enum BoardAction {
    /// Create a new board
    Create {
        #[arg(long)]
        name: String,
        #[arg(long)]
        card_prefix: Option<String>,
    },
    /// List all boards
    List {
        #[arg(long)]
        page: Option<u32>,
        #[arg(long)]
        page_size: Option<u32>,
    },
    /// Get a specific board by ID
    Get {
        /// Board ID
        id: Uuid,
    },
    /// Update a board
    Update(BoardUpdateArgs),
    /// Delete a board by ID
    Delete {
        /// Board ID
        id: Uuid,
    },
}

#[derive(Args)]
pub struct BoardUpdateArgs {
    /// Board ID to update
    pub id: Uuid,
    #[arg(long)]
    pub name: Option<String>,
    #[arg(long)]
    pub description: Option<String>,
    #[arg(long)]
    pub sprint_prefix: Option<String>,
    #[arg(long)]
    pub card_prefix: Option<String>,
}

// Column commands
#[derive(Args)]
pub struct ColumnCommand {
    #[command(subcommand)]
    pub action: ColumnAction,
}

#[derive(Subcommand)]
pub enum ColumnAction {
    /// Create a new column
    Create {
        #[arg(long)]
        board_id: Uuid,
        #[arg(long)]
        name: String,
        #[arg(long)]
        position: Option<i32>,
    },
    /// List columns for a board
    List {
        #[arg(long)]
        board_id: Uuid,
        #[arg(long)]
        page: Option<u32>,
        #[arg(long)]
        page_size: Option<u32>,
    },
    /// Get a specific column by ID
    Get {
        /// Column ID
        id: Uuid,
    },
    /// Update a column
    Update(ColumnUpdateArgs),
    /// Delete a column by ID
    Delete {
        /// Column ID
        id: Uuid,
    },
    /// Reorder a column by ID
    Reorder {
        /// Column ID
        id: Uuid,
        #[arg(long)]
        position: i32,
    },
}

#[derive(Args)]
pub struct ColumnUpdateArgs {
    /// Column ID to update
    pub id: Uuid,
    #[arg(long)]
    pub name: Option<String>,
    #[arg(long)]
    pub position: Option<i32>,
    #[arg(long)]
    pub wip_limit: Option<u32>,
    #[arg(long)]
    pub clear_wip_limit: bool,
}

// Card commands
#[derive(Args)]
pub struct CardCommand {
    #[command(subcommand)]
    pub action: CardAction,
}

#[derive(Subcommand)]
pub enum CardAction {
    /// Create a new card
    Create(CardCreateArgs),
    /// List cards with optional filters
    List(CardListArgs),
    /// Get a specific card by ID or identifier (e.g. KAN-5)
    Get {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Update a card
    Update(CardUpdateArgs),
    /// Move a card to another column
    Move {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
        #[arg(long)]
        column_id: Uuid,
        #[arg(long)]
        position: Option<i32>,
    },
    /// Archive a card by ID or identifier (e.g. KAN-5)
    Archive {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Restore an archived card by ID or identifier (e.g. KAN-5)
    Restore {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
        #[arg(long)]
        column_id: Option<Uuid>,
    },
    /// Permanently delete an archived card by ID or identifier (e.g. KAN-5)
    Delete {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Assign a card to a sprint
    AssignSprint {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
        #[arg(long)]
        sprint_id: Uuid,
    },
    /// Unassign a card from its sprint
    UnassignSprint {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Get the branch name for a card
    BranchName {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Get the git checkout command for a card
    GitCheckout {
        /// Card UUID or identifier like KAN-5 or 5
        id: String,
    },
    /// Archive multiple cards
    BulkArchive {
        #[arg(long, value_delimiter = ',')]
        ids: Vec<Uuid>,
    },
    /// Move multiple cards to a column
    BulkMove {
        #[arg(long, value_delimiter = ',')]
        ids: Vec<Uuid>,
        #[arg(long)]
        column_id: Uuid,
    },
    /// Assign multiple cards to a sprint
    BulkAssignSprint {
        #[arg(long, value_delimiter = ',')]
        ids: Vec<Uuid>,
        #[arg(long)]
        sprint_id: Uuid,
    },
}

#[derive(Args)]
pub struct CardCreateArgs {
    #[arg(long)]
    pub board_id: Uuid,
    #[arg(long)]
    pub column_id: Uuid,
    #[arg(long)]
    pub title: String,
    #[arg(long)]
    pub description: Option<String>,
    #[arg(long)]
    pub priority: Option<String>,
    #[arg(long)]
    pub points: Option<u8>,
    #[arg(long)]
    pub due_date: Option<String>,
}

#[derive(Args)]
pub struct CardListArgs {
    #[arg(long)]
    pub board_id: Option<Uuid>,
    #[arg(long)]
    pub column_id: Option<Uuid>,
    #[arg(long)]
    pub sprint_id: Option<Uuid>,
    #[arg(long)]
    pub status: Option<String>,
    #[arg(long)]
    pub archived: bool,
    #[arg(long)]
    pub page: Option<u32>,
    #[arg(long)]
    pub page_size: Option<u32>,
}

#[derive(Args)]
pub struct CardUpdateArgs {
    /// Card UUID or identifier like KAN-5 or 5
    pub id: String,
    #[arg(long)]
    pub title: Option<String>,
    #[arg(long)]
    pub description: Option<String>,
    #[arg(long)]
    pub priority: Option<String>,
    #[arg(long)]
    pub status: Option<String>,
    #[arg(long)]
    pub points: Option<u8>,
    #[arg(long)]
    pub due_date: Option<String>,
    #[arg(long)]
    pub clear_due_date: bool,
}

// Sprint commands
#[derive(Args)]
pub struct SprintCommand {
    #[command(subcommand)]
    pub action: SprintAction,
}

#[derive(Subcommand)]
pub enum SprintAction {
    /// Create a new sprint
    Create {
        #[arg(long)]
        board_id: Uuid,
        #[arg(long)]
        prefix: Option<String>,
        #[arg(long)]
        name: Option<String>,
    },
    /// List sprints for a board
    List {
        #[arg(long)]
        board_id: Uuid,
        #[arg(long)]
        page: Option<u32>,
        #[arg(long)]
        page_size: Option<u32>,
    },
    /// Get a specific sprint by ID
    Get {
        /// Sprint ID
        id: Uuid,
    },
    /// Update a sprint
    Update(SprintUpdateArgs),
    /// Activate a sprint by ID
    Activate {
        /// Sprint ID
        id: Uuid,
        #[arg(long)]
        duration_days: Option<i32>,
    },
    /// Complete a sprint by ID
    Complete {
        /// Sprint ID
        id: Uuid,
    },
    /// Cancel a sprint by ID
    Cancel {
        /// Sprint ID
        id: Uuid,
    },
    /// Delete a sprint by ID
    Delete {
        /// Sprint ID
        id: Uuid,
    },
}

#[derive(Args)]
pub struct SprintUpdateArgs {
    /// Sprint ID to update
    pub id: Uuid,
    #[arg(long)]
    pub name: Option<String>,
    #[arg(long)]
    pub prefix: Option<String>,
    #[arg(long)]
    pub card_prefix: Option<String>,
    #[arg(long)]
    pub start_date: Option<String>,
    #[arg(long)]
    pub end_date: Option<String>,
    #[arg(long)]
    pub clear_start_date: bool,
    #[arg(long)]
    pub clear_end_date: bool,
}

// Export/Import commands
#[derive(Args)]
pub struct ExportArgs {
    #[arg(long)]
    pub board_id: Option<Uuid>,
}

#[derive(Args)]
pub struct ImportArgs {
    #[arg(long)]
    pub file: String,
}