use clap::Args;
use crate::{
client::{
WekanClientFactory,
cards::{
CardCustomField, CardCustomFieldValue, CardDependency, CardDocument, CardLocation,
CardPoker, CardSticker, CardStickerHighlight, CardVote,
},
},
command_result::{
CardDetail, CardDetailCustomField, CardDetailCustomFieldValue, CardDetailDependency,
CardDetailLocation, CardDetailPoker, CardDetailSticker, CardDetailStickerHighlight,
CardDetailVote, CommandSuccess,
},
commands::{
authenticated::AuthenticatedContext, client_error::map_client_error_with_not_found,
},
credentials::CredentialStore,
error::AppError,
redaction::Redactor,
};
use crate::commands::transfer::identifier;
#[derive(Debug, Args)]
#[command(
after_help = "Examples:\n wekan card get card-id\n wekan card get card-id --board board-id --list list-id\n\nID-only lookup includes archived cards; scoped lookup returns active cards."
)]
pub struct GetArgs {
#[arg(long = "board", requires = "list_id", value_parser = identifier, help_heading = "Target")]
pub board_id: Option<String>,
#[arg(long = "list", requires = "board_id", value_parser = identifier, help_heading = "Target")]
pub list_id: Option<String>,
#[arg(value_parser = identifier, help_heading = "Target")]
pub card_id: String,
}
pub(super) async fn execute(
args: GetArgs,
client_factory: &WekanClientFactory,
credential_store: &dyn CredentialStore,
) -> Result<CommandSuccess, AppError> {
let context = AuthenticatedContext::load(client_factory, credential_store)?;
let redactor = Redactor::with_secret(context.record().token());
let card = match (&args.board_id, &args.list_id) {
(Some(board), Some(list)) => {
context
.client()
.card(board, list, &args.card_id, context.record().token())
.await
}
(None, None) => {
context
.client()
.card_by_id(&args.card_id, context.record().token())
.await
}
_ => unreachable!("parent selectors are paired by clap"),
}
.map_err(|error| {
map_client_error_with_not_found(
error,
&redactor,
"card lookup",
"the requested Wekan card was not found",
)
})?;
Ok(CommandSuccess::CardShown(Box::new(card_detail(card))))
}
pub(super) fn card_detail(card: CardDocument) -> CardDetail {
CardDetail {
scrum: card.scrum,
drawings: card.drawings,
custom_field_links: card.custom_field_links,
sync_last_source: card.sync_last_source,
email: card.email,
scrum_revision: card.scrum_revision,
parent_ids: card.parent_ids,
map_x: card.map_x,
map_y: card.map_y,
column_set_values: card.column_set_values,
card_kind: card.card_kind,
last_recurrence_at: card.last_recurrence_at,
sync_source_key: card.sync_source_key,
sync_label_ids: card.sync_label_ids,
sync_issue_state: card.sync_issue_state,
sync_closed_by_we_kan: card.sync_closed_by_we_kan,
sync_external_id: card.sync_external_id,
sync_source_type: card.sync_source_type,
attached_card_ids: card.attached_card_ids,
flow_start_at: card.flow_start_at,
flow_user_id: card.flow_user_id,
pomodoro_start_at: card.pomodoro_start_at,
pomodoro_phase: card.pomodoro_phase,
pomodoro_user_id: card.pomodoro_user_id,
show_list_on_card: card.show_list_on_card,
list_entered_at: card.list_entered_at,
recurrence_interval: card.recurrence_interval,
last_move_reason: card.last_move_reason,
flow_interruptions: card.flow_interruptions,
pomodoro_count: card.pomodoro_count,
pomodoro_work_minutes: card.pomodoro_work_minutes,
checklists: card.checklists,
card_id: card.card_id,
title: card.title,
watchers: card.watchers,
archived: card.archived,
archived_at: card.archived_at,
deleted_at: card.deleted_at,
deleted_by: card.deleted_by,
delete_batch_id: card.delete_batch_id,
parent_id: card.parent_id,
list_id: card.list_id,
swimlane_id: card.swimlane_id,
board_id: card.board_id,
cover_id: card.cover_id,
color: card.color,
created_at: card.created_at,
modified_at: card.modified_at,
custom_fields: card
.custom_fields
.into_iter()
.map(card_detail_custom_field)
.collect(),
date_last_activity: card.date_last_activity,
description: card.description,
requested_by: card.requested_by,
assigned_by: card.assigned_by,
label_ids: card.label_ids,
members: card.members,
assignees: card.assignees,
requesters: card.requesters,
assigners: card.assigners,
received_at: card.received_at,
start_at: card.start_at,
due_at: card.due_at,
end_at: card.end_at,
due_complete: card.due_complete,
stickers: card.stickers.into_iter().map(card_detail_sticker).collect(),
location_name: card.location_name,
location_address: card.location_address,
location_latitude: card.location_latitude,
location_longitude: card.location_longitude,
locations: card
.locations
.into_iter()
.map(card_detail_location)
.collect(),
spent_time: card.spent_time,
is_overtime: card.is_overtime,
user_id: card.user_id,
sort: card.sort,
subtask_sort: card.subtask_sort,
card_type: card.card_type,
linked_id: card.linked_id,
card_dependencies: card
.card_dependencies
.into_iter()
.map(card_detail_dependency)
.collect(),
vote: card.vote.map(card_detail_vote),
poker: card.poker.map(card_detail_poker),
target_id_gantt: card.target_id_gantt,
link_type_gantt: card.link_type_gantt,
link_id_gantt: card.link_id_gantt,
card_number: card.card_number,
show_activities: card.show_activities,
show_list_on_minicard: card.show_list_on_minicard,
show_checklist_at_minicard: card.show_checklist_at_minicard,
hide_finished_checklist_if_items_are_hidden: card
.hide_finished_checklist_if_items_are_hidden,
}
}
pub(super) fn card_detail_custom_field(field: CardCustomField) -> CardDetailCustomField {
CardDetailCustomField {
custom_field_id: field.custom_field_id,
value: field.value.map(card_detail_custom_field_value),
}
}
fn card_detail_custom_field_value(value: CardCustomFieldValue) -> CardDetailCustomFieldValue {
match value {
CardCustomFieldValue::String(value) => CardDetailCustomFieldValue::String(value),
CardCustomFieldValue::Number(value) => CardDetailCustomFieldValue::Number(value),
CardCustomFieldValue::Boolean(value) => CardDetailCustomFieldValue::Boolean(value),
CardCustomFieldValue::Strings(value) => CardDetailCustomFieldValue::Strings(value),
}
}
fn card_detail_sticker(sticker: CardSticker) -> CardDetailSticker {
CardDetailSticker {
icon: sticker.icon,
name: sticker.name,
highlight: sticker.highlight.map(card_detail_sticker_highlight),
position: sticker.position,
}
}
const fn card_detail_sticker_highlight(
highlight: CardStickerHighlight,
) -> CardDetailStickerHighlight {
match highlight {
CardStickerHighlight::Underline => CardDetailStickerHighlight::Underline,
CardStickerHighlight::Round => CardDetailStickerHighlight::Round,
}
}
fn card_detail_location(location: CardLocation) -> CardDetailLocation {
CardDetailLocation {
location_id: location.location_id,
name: location.name,
address: location.address,
latitude: location.latitude,
longitude: location.longitude,
}
}
fn card_detail_dependency(dependency: CardDependency) -> CardDetailDependency {
CardDetailDependency {
card_id: dependency.card_id,
dependency_type: dependency.dependency_type,
color: dependency.color,
icon: dependency.icon,
}
}
fn card_detail_vote(vote: CardVote) -> CardDetailVote {
CardDetailVote {
question: vote.question,
positive: vote.positive,
negative: vote.negative,
end: vote.end,
public: vote.public,
allow_non_board_members: vote.allow_non_board_members,
}
}
fn card_detail_poker(poker: CardPoker) -> CardDetailPoker {
CardDetailPoker {
question: poker.question,
one: poker.one,
two: poker.two,
three: poker.three,
five: poker.five,
eight: poker.eight,
thirteen: poker.thirteen,
twenty: poker.twenty,
forty: poker.forty,
one_hundred: poker.one_hundred,
unsure: poker.unsure,
end: poker.end,
allow_non_board_members: poker.allow_non_board_members,
estimation: poker.estimation,
}
}