Skip to main content

wekan_cli/commands/
cards.rs

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