use std::collections::BTreeMap;
use crate::bulk::{ArchiveRequest, IssuePatch};
use crate::error::{Result, TixError};
pub const ARCHIVE_OWNER: &str = "issue-archive-restore-01M3C4JF";
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveItemRequest {
pub number: u32,
pub selector: String,
pub operation: ArchiveOperation,
pub target_status: Option<String>,
pub reason: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchiveOperation {
Archive,
Restore,
}
impl ArchiveOperation {
pub fn service_fn(self) -> &'static str {
match self {
Self::Archive => "bulk_archive",
Self::Restore => "bulk_restore",
}
}
pub fn as_str(self) -> &'static str {
match self {
Self::Archive => "archive",
Self::Restore => "restore",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveBatchRequest {
pub operation: ArchiveOperation,
pub items: Vec<ArchiveItemRequest>,
}
impl ArchiveBatchRequest {
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn summary(&self) -> String {
format!(
"{} {} request for {} issue{}",
self.operation.service_fn(),
self.operation.as_str(),
self.items.len(),
if self.items.len() == 1 { "" } else { "s" }
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchiveProblem {
MissingTargetStatus,
UnknownIssue,
}
impl ArchiveProblem {
pub fn as_str(self) -> &'static str {
match self {
Self::MissingTargetStatus => "missing_target_status",
Self::UnknownIssue => "unknown_issue",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ArchiveDiagnostic {
pub problem: ArchiveProblem,
pub issue: Option<u32>,
pub message: String,
}
impl std::fmt::Display for ArchiveDiagnostic {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.issue {
Some(issue) => write!(f, "#{}: {}: {}", issue, self.problem.as_str(), self.message),
None => write!(f, "{}: {}", self.problem.as_str(), self.message),
}
}
}
pub fn adapt(
patch: &IssuePatch,
numbers: &[u32],
selectors: &BTreeMap<u32, String>,
) -> std::result::Result<ArchiveBatchRequest, ArchiveDiagnostic> {
let request = patch.archive.clone().ok_or_else(|| ArchiveDiagnostic {
problem: ArchiveProblem::UnknownIssue,
issue: None,
message: "this patch carries no archive request".to_string(),
})?;
Ok(build(request, numbers, selectors))
}
fn build(
request: ArchiveRequest,
numbers: &[u32],
selectors: &BTreeMap<u32, String>,
) -> ArchiveBatchRequest {
let operation = if request.archive {
ArchiveOperation::Archive
} else {
ArchiveOperation::Restore
};
let target_status = if request.archive {
None
} else {
request.target_status.clone()
};
let items = numbers
.iter()
.map(|number| ArchiveItemRequest {
number: *number,
selector: selectors
.get(number)
.cloned()
.unwrap_or_else(|| number.to_string()),
operation,
target_status: target_status.clone(),
reason: request.reason.clone(),
})
.collect();
ArchiveBatchRequest { operation, items }
}
pub fn refusal(request: &ArchiveBatchRequest) -> Option<ArchiveDiagnostic> {
if request.operation != ArchiveOperation::Restore {
return None;
}
if request
.items
.iter()
.all(|item| item.target_status.is_some())
{
return None;
}
Some(ArchiveDiagnostic {
problem: ArchiveProblem::MissingTargetStatus,
issue: None,
message: "restoring needs an explicit target status — there is no \\
\"restore to the previous status\", so name the column to restore \\
into"
.to_string(),
})
}
pub fn predicted_history_events(_patch: &IssuePatch) -> usize {
0
}
pub fn history_kind_for(_patch: &IssuePatch) -> Option<&'static str> {
None
}
pub fn apply_archive(plan: &crate::bulk::BatchPlan) -> Result<crate::bulk::plan::ItemDiagnostic> {
if plan.confirmation.is_none() || !plan.confirmation_is_valid() {
return Err(TixError::InvalidInput(
"a bulk apply needs a confirmation for exactly this plan — preview it \
first, and preview again if the selection changed"
.to_string(),
));
}
let request = plan
.patch
.archive
.clone()
.ok_or_else(|| TixError::InvalidInput("this plan carries no archive request".into()))?;
if !request.archive && request.target_status.is_none() {
return Err(TixError::InvalidInput(format!(
"{}: restoring needs an explicit target status — there is no \\
\"restore to the previous status\"",
ArchiveProblem::MissingTargetStatus.as_str()
)));
}
let operation = if request.archive {
"archive"
} else {
"restore"
};
Err(TixError::InvalidInput(format!(
"archive delegation is not available: the archive service is part of mission \
{ARCHIVE_OWNER}, which has not been implemented — this path will delegate \
to its `bulk_{operation}` once it lands, and it will not implement archiving \
itself"
)))
}
pub fn can_apply(_patch: &IssuePatch) -> bool {
false
}