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