use super::write_new_artifact_toml;
use crate::config::{Config, IdStrategy};
use crate::diagnostic::{DiagnosticCode, DiagnosticResult, Diagnostics};
use crate::model::{
WorkItemContent, WorkItemMeta, WorkItemSpec, WorkItemStatus, WorkItemVerification,
};
use crate::schema::ArtifactSchema;
use crate::ui;
use crate::write::{WriteOp, create_dir_all, today};
use slug::slugify;
use std::path::Path;
pub(super) fn create(
config: &Config,
title: &str,
active: bool,
op: WriteOp,
) -> DiagnosticResult<Diagnostics> {
let work_dir = config.work_dir();
let display_work_dir = config.display_path(&work_dir);
create_dir_all(&work_dir, op, Some(&display_work_dir))?;
let date = today();
let slug = slugify(title);
let work_id = match config.work_item.id_strategy {
IdStrategy::Sequential => {
let id_prefix = format!("WI-{date}-");
let max_seq = find_max_sequence(&work_dir, &id_prefix);
format!("WI-{date}-{:03}", max_seq + 1)
}
IdStrategy::AuthorHash => {
let author_hash =
IdStrategy::get_author_hash().unwrap_or_else(IdStrategy::generate_random_suffix);
let id_prefix = format!("WI-{date}-{author_hash}-");
let max_seq = find_max_sequence(&work_dir, &id_prefix);
format!("WI-{date}-{author_hash}-{:03}", max_seq + 1)
}
IdStrategy::Random => {
let random_suffix = IdStrategy::generate_random_suffix();
format!("WI-{date}-{random_suffix}")
}
};
let mut filename = format!("{date}-{slug}.toml");
let mut work_path = work_dir.join(&filename);
let mut suffix = 1u32;
while !op.is_preview() && work_path.exists() {
filename = format!("{date}-{slug}-{suffix:03}.toml");
work_path = work_dir.join(&filename);
suffix += 1;
}
let (status, started) = if active {
(WorkItemStatus::Active, Some(date.clone()))
} else {
(WorkItemStatus::Queue, None)
};
let mut meta = WorkItemMeta::new(work_id.clone(), title, status);
meta.created = Some(date.clone());
meta.started = started;
let spec = WorkItemSpec {
govctl: meta,
content: WorkItemContent {
description:
"Describe the work to be done.\nWhat is the goal? What are the acceptance criteria?"
.to_string(),
..WorkItemContent::default()
},
verification: WorkItemVerification::default(),
};
write_new_artifact_toml(
config,
&work_path,
&spec,
ArtifactSchema::WorkItem,
DiagnosticCode::E0401WorkSchemaInvalid,
"work item",
op,
)?;
if !op.is_preview() {
let display_path = config.display_path(&work_path);
ui::created("work item", &display_path);
ui::sub_info(format!("ID: {work_id}"));
}
Ok(vec![])
}
fn find_max_sequence(work_dir: &Path, id_prefix: &str) -> u32 {
std::fs::read_dir(work_dir)
.into_iter()
.flatten()
.flatten()
.filter_map(|entry| {
let path = entry.path();
(path.extension()? == "toml").then_some(path)
})
.filter_map(|path| std::fs::read_to_string(&path).ok())
.filter_map(|content| {
content
.lines()
.find(|line| line.starts_with("id = \""))
.and_then(|line| line.strip_prefix("id = \""))
.and_then(|s| s.strip_suffix('"'))
.and_then(|id| id.strip_prefix(id_prefix))
.and_then(|seq_str| seq_str.parse::<u32>().ok())
})
.max()
.unwrap_or(0)
}