Skip to main content

wekan_cli/commands/
cards.rs

1mod archive;
2mod bulk;
3mod copy;
4mod count;
5mod create;
6mod delete;
7mod export;
8mod find_by_custom_field;
9mod get;
10mod import_ics;
11mod labels;
12mod list;
13mod membership;
14mod restore;
15mod set_custom_field;
16mod update;
17
18#[cfg(test)]
19mod tests;
20
21use clap::{Args, Subcommand};
22
23use crate::{
24    client::WekanClientFactory,
25    command_result::{CardMembershipKind, CommandSuccess},
26    credentials::CredentialStore,
27    error::AppError,
28    input::ConfirmationProvider,
29};
30
31#[derive(Debug, Args)]
32pub struct CardArgs {
33    #[command(subcommand)]
34    pub command: CardCommand,
35}
36
37#[derive(Debug, Subcommand)]
38pub enum CardCommand {
39    /// Import iCalendar events as cards into a board list and swimlane.
40    ImportIcs(import_ics::ImportIcsArgs),
41    /// Export a native artifact to a file or validated raw stdout.
42    Export(export::ExportArgs),
43    /// Count active cards on a board or in a list.
44    Count(count::CountArgs),
45    /// Copy a card using Wekan's native copy operation.
46    Copy(copy::CopyArgs),
47    /// Create, delete, or merge labels on up to 500 cards per request.
48    Bulk(bulk::BulkArgs),
49    /// List the label IDs stored on a card.
50    Label(labels::LabelArgs),
51    /// List or merge individual card members.
52    Member(membership::MembershipArgs),
53    /// List or merge individual card assignees.
54    Assignee(membership::MembershipArgs),
55    /// List active cards in a board list or swimlane.
56    List(list::ListArgs),
57
58    /// Show a card by ID, optionally restricted to a board and list.
59    Get(get::GetArgs),
60
61    /// Assign an existing custom-field entry on an active card.
62    SetCustomField(set_custom_field::SetCustomFieldArgs),
63    /// Find active cards using Wekan's string custom-field query.
64    FindByCustomField(find_by_custom_field::FindByCustomFieldArgs),
65    /// Create a card in a board list.
66    Create(Box<create::CreateArgs>),
67
68    /// Update one or more editable card fields.
69    Update(Box<update::UpdateArgs>),
70
71    /// Archive an active card and its child cards.
72    Archive(archive::ArchiveArgs),
73
74    /// Restore an archived card and its child cards.
75    Restore(restore::RestoreArgs),
76
77    /// Permanently delete a card and its child resources.
78    Delete(delete::DeleteArgs),
79}
80
81pub(crate) async fn dispatch(
82    command: CardCommand,
83    client_factory: &WekanClientFactory,
84    credential_store: &dyn CredentialStore,
85    confirmation: &dyn ConfirmationProvider,
86) -> Result<CommandSuccess, AppError> {
87    match command {
88        CardCommand::ImportIcs(args) => {
89            import_ics::execute(args, client_factory, credential_store).await
90        }
91        CardCommand::Export(args) => export::execute(args, client_factory, credential_store).await,
92        CardCommand::Count(args) => count::execute(args, client_factory, credential_store).await,
93        CardCommand::Copy(args) => copy::execute(args, client_factory, credential_store).await,
94        CardCommand::Bulk(args) => {
95            bulk::dispatch(args.command, client_factory, credential_store, confirmation).await
96        }
97        CardCommand::Label(args) => {
98            labels::dispatch(args.command, client_factory, credential_store).await
99        }
100        CardCommand::Member(args) => {
101            membership::dispatch(
102                args.command,
103                CardMembershipKind::Member,
104                client_factory,
105                credential_store,
106            )
107            .await
108        }
109        CardCommand::Assignee(args) => {
110            membership::dispatch(
111                args.command,
112                CardMembershipKind::Assignee,
113                client_factory,
114                credential_store,
115            )
116            .await
117        }
118        CardCommand::List(args) => list::execute(args, client_factory, credential_store).await,
119        CardCommand::Get(args) => get::execute(args, client_factory, credential_store).await,
120        CardCommand::SetCustomField(args) => {
121            set_custom_field::execute(args, client_factory, credential_store).await
122        }
123        CardCommand::FindByCustomField(args) => {
124            find_by_custom_field::execute(args, client_factory, credential_store).await
125        }
126        CardCommand::Create(args) => create::execute(*args, client_factory, credential_store).await,
127        CardCommand::Update(args) => update::execute(*args, client_factory, credential_store).await,
128        CardCommand::Archive(args) => {
129            archive::execute(args, client_factory, credential_store).await
130        }
131        CardCommand::Restore(args) => {
132            restore::execute(args, client_factory, credential_store).await
133        }
134        CardCommand::Delete(args) => {
135            delete::execute(args, client_factory, credential_store, confirmation).await
136        }
137    }
138}
139
140fn non_empty(value: &str) -> Result<String, String> {
141    trimmed_non_empty(value)
142}
143
144fn trimmed_non_empty(value: &str) -> Result<String, String> {
145    let value = value.trim();
146    if value.is_empty() {
147        Err("value must not be empty or whitespace".to_owned())
148    } else {
149        Ok(value.to_owned())
150    }
151}
152
153fn date_input(value: &str) -> Result<crate::client::cards::CardDateInput, String> {
154    use crate::client::cards::CardDateInput;
155    Ok(match value.trim().parse::<serde_json::Number>() {
156        Ok(number) => CardDateInput::EpochMilliseconds(number),
157        Err(_) => CardDateInput::Text(value.to_owned()),
158    })
159}