use clap::Args;
use crate::wiki::{ref_list::RefCntKind, req::ReqId, Wiki, WikiError};
#[derive(Args, Debug, Clone)]
pub struct ReleaseParameter {
#[arg(index = 1, required = true)]
pub req_folder: std::path::PathBuf,
#[arg(long, alias = "branch-name", required = false, default_value = "main")]
pub branch: String,
#[arg(long, alias = "tag", required = true)]
pub release_tag: String,
#[arg(long)]
pub wiki_url_prefix: Option<String>,
#[arg(long, aliases = ["release-file", "out-file", "checklist-file"])]
pub report_file: Option<std::path::PathBuf>,
#[arg(long)]
pub checklist: bool,
#[arg(long, alias = "repo")]
pub repo_name: Option<String>,
}
pub fn release(param: &ReleaseParameter) -> Result<(), ReleaseError> {
let wiki = Wiki::try_from(¶m.req_folder)?;
let high_reqs = wiki.high_lvl_reqs();
let head = if param.checklist {
"Requirements requiring *manual* verification for"
} else {
"*Active* requirements in"
};
let report = format!(
"**{} release {}:**\n\n{}",
head,
param.release_tag,
release_list(
&wiki,
¶m.wiki_url_prefix,
¶m.branch,
param.repo_name.as_deref(),
high_reqs.iter(),
0,
param.checklist,
)
);
match ¶m.report_file {
Some(filepath) => {
let mut report_file = filepath.clone();
report_file.set_extension("md");
std::fs::write(report_file, report)
.map_err(|_| logid::pipe!(ReleaseError::WritingReport))?;
}
None => println!("{report}"),
}
Ok(())
}
fn release_list<'a>(
wiki: &'a Wiki,
wiki_url_prefix: &Option<String>,
branch: &str,
repo: Option<&str>,
req_ids: impl Iterator<Item = &'a ReqId>,
indent: usize,
checklist: bool,
) -> String {
let mut list = String::new();
req_ids.for_each(|req_id| {
let mut sub_indent = indent;
if let Some(req) = wiki.req(req_id) {
if !checklist {
sub_indent += 2; }
if let Some(entry) = req.ref_list.iter().find(|entry| {
entry.proj_line.branch_name.as_str() == branch
&& entry.proj_line.repo_name.as_ref().map(|s| s.as_str()) == repo
}) {
if !entry.is_deprecated
&& (entry.is_manual || (!checklist && entry.ref_cnt != RefCntKind::Untraced))
{
let wiki_link = match &wiki_url_prefix {
Some(prefix) => {
let file_link = req
.filepath
.file_stem()
.map_or("bad-file".to_string(), |f| {
f.to_string_lossy().to_string()
});
let file_link = file_link
.split_whitespace()
.collect::<Vec<&str>>()
.join("-")
.to_lowercase();
format!(
" \n{}See in wiki: {}{}{}",
" ".repeat(sub_indent),
prefix,
if prefix.ends_with('/') { "" } else { "/" },
file_link
)
}
None => String::new(),
};
list.push_str(&format!(
"{}- {}{}: {}{}\n",
" ".repeat(indent),
if checklist { "[ ] " } else { "" },
req_id,
req.head.title,
wiki_link,
));
}
}
}
if let Some(subs) = wiki.sub_reqs(req_id) {
let mut ordered_subs: Vec<&String> = subs.iter().collect();
ordered_subs.sort();
list.push_str(&release_list(
wiki,
wiki_url_prefix,
branch,
repo,
ordered_subs.iter().copied(),
sub_indent,
checklist,
));
}
});
list
}
#[derive(Debug, thiserror::Error, logid::ErrLogId)]
pub enum ReleaseError {
#[error("Failed to parse the wiki.")]
WikiSetup,
#[error("Could not write report to given file.")]
WritingReport,
}
impl From<WikiError> for ReleaseError {
fn from(_value: WikiError) -> Self {
ReleaseError::WikiSetup
}
}