Skip to main content

wekan_cli/commands/
cards.rs

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