fn generate_next_id(roadmap: &crate::models::roadmap::Roadmap) -> String {
let mut max_num = 0u32;
for item in &roadmap.roadmap {
if let Some(num_str) = item.id.split('-').next_back() {
if let Ok(num) = num_str.parse::<u32>() {
max_num = max_num.max(num);
}
}
}
format!("PMAT-{:03}", max_num + 1)
}
fn find_item_fuzzy(
service: &RoadmapService,
id: &str,
) -> Result<crate::models::roadmap::RoadmapItem> {
if let Ok(Some(item)) = service.find_item(id) {
return Ok(item);
}
let roadmap = service.load()?;
let id_lower = id.to_lowercase();
for item in &roadmap.roadmap {
if item.id.to_lowercase() == id_lower {
return Ok(item.clone());
}
}
let mut matches: Vec<_> = roadmap
.roadmap
.iter()
.filter(|item| item.id.to_lowercase().contains(&id_lower))
.collect();
match matches.len() {
0 => anyhow::bail!(
"Ticket '{}' not found. Use 'pmat work list' to see available tickets.",
id
),
1 => Ok(matches.pop().expect("verified 1 element exists").clone()),
_ => {
let match_ids: Vec<_> = matches.iter().map(|i| i.id.as_str()).collect();
anyhow::bail!(
"Ambiguous ID '{}'. Multiple matches: {}. Please be more specific.",
id,
match_ids.join(", ")
)
}
}
}
fn extract_line_from_yaml_error(error: &str) -> Option<usize> {
if let Some(pos) = error.find("at line ") {
let rest = error.get(pos + 8..).unwrap_or_default();
if let Some(end) = rest.find(' ') {
return rest.get(..end).unwrap_or_default().parse().ok();
}
}
None
}
include!("ticket_validate_migrate.rs");
include!("ticket_crud.rs");
include!("ticket_annotate.rs");
include!("ticket_annotate_output.rs");
include!("ticket_score.rs");
#[cfg(all(test, feature = "broken-tests"))]
#[path = "work_handlers_tests.rs"]
mod tests;