@ -9,6 +9,8 @@
use std ::collections ::HashSet ;
use serde ::Serialize ;
use crate ::graph ::TicketGraph ;
use crate ::store ::MigrateReport ;
use crate ::ticket ::{ Status , Ticket , TicketType } ;
@ -23,15 +25,29 @@ const COL_PRI: usize = 5;
const COL_TYPE : usize = 9 ;
/// Width of the status column ("in_progress" = 11 chars, +2 padding).
const COL_STATUS : usize = 13 ;
/// Width of each label in the full ticket view ("D ependencies:" = 13 + 1 space).
/// Width of each label in the diff view ("d ependencies:" = 13 + 1 space).
const LABEL_WIDTH : usize = 14 ;
// ── Internal helpers ──────────────────────────────────────────────────────────
/// Ticket metadata serialised into the TOML frontmatter block for display.
///
/// Mirrors the on-disk `MarkdownFrontmatter` in `store.rs` but adds `id` as
/// the first field so that human-readable output is self-contained.
#[ derive(Serialize) ]
struct DisplayFrontmatter < ' a > {
id : & ' a str ,
title : & ' a str ,
priority : u8 ,
status : & ' a Status ,
ticket_type : & ' a TicketType ,
dependencies : & ' a [ String ] ,
}
/// Return the canonical display string for a [`Status`] variant.
///
/// The strings match the serde serialisation: `"todo"`, `"in_progress"`,
/// `"done"`.
/// `"done"` , etc .
fn status_str ( status : & Status ) -> & ' static str {
match status {
Status ::Todo = > "todo" ,
@ -58,37 +74,34 @@ fn ticket_type_str(ticket_type: &TicketType) -> &'static str {
// ── Public formatting functions ───────────────────────────────────────────────
/// Format a single ticket as a human-readable key– value table .
/// Format a single ticket as a TOML-frontmatter markdown document .
///
/// Each field is displayed on its own line with a label padded to
/// [`LABEL_WIDTH`] characters. An empty `Dependencies` field is rendered as
/// an empty string rather than being omitted .
/// The output mirrors the `.md` file format used on disk, with `id` added as
/// the first frontmatter key so the output is self-contained. The ticket body
/// follows the closing `+++` delimiter .
///
/// ```text
/// ID: a3f9c2
/// Title: Fix login bug
/// Body: Users cannot log in with email addresses containing +
/// Priority: 8
/// Status: in_progress
/// Type: bug
/// Dependencies: b7d41e, c9e823
/// +++
/// id = "a3f9c2"
/// title = "Fix login bug"
/// priority = 8
/// status = "in_progress"
/// ticket_type = "bug"
/// dependencies = ["b7d41e", "c9e823"]
/// +++
/// Users cannot log in with email addresses containing +
/// ```
pub fn format_ticket ( ticket : & Ticket ) -> String {
let deps = ticket . dependencies . join ( ", " ) ;
[
format! ( "{:<LABEL_WIDTH$}{}" , "ID:" , ticket . id ) ,
format! ( "{:<LABEL_WIDTH$}{}" , "Title:" , ticket . title ) ,
format! ( "{:<LABEL_WIDTH$}{}" , "Body:" , ticket . body ) ,
format! ( "{:<LABEL_WIDTH$}{}" , "Priority:" , ticket . priority ) ,
format! ( "{:<LABEL_WIDTH$}{}" , "Status:" , status_str ( & ticket . status ) ) ,
format! (
"{:<LABEL_WIDTH$}{}" ,
"Type:" ,
ticket_type_str ( & ticket . ticket_type )
) ,
format! ( "{:<LABEL_WIDTH$}{}" , "Dependencies:" , deps ) ,
]
. join ( "\n" )
let fm = DisplayFrontmatter {
id : & ticket . id ,
title : & ticket . title ,
priority : ticket . priority ,
status : & ticket . status ,
ticket_type : & ticket . ticket_type ,
dependencies : & ticket . dependencies ,
} ;
let toml_str = toml ::to_string ( & fm ) . expect ( "frontmatter serialisation must not fail" ) ;
format! ( "+++\n{toml_str}+++\n{}" , ticket . body )
}
/// Print a full tabular representation of a single ticket to stdout.