wekan_cli/commands/
attachments.rs1mod delete;
2mod download;
3mod export;
4mod get;
5mod list;
6mod recovery;
7#[cfg(test)]
8mod tests;
9mod transfer;
10mod upload;
11
12use crate::{
13 client::WekanClientFactory, command_result::CommandSuccess, credentials::CredentialStore,
14 error::AppError, input::ConfirmationProvider,
15};
16use clap::{Args, Subcommand};
17
18#[derive(Debug, Args)]
19pub struct AttachmentArgs {
20 #[command(subcommand)]
21 pub command: AttachmentCommand,
22}
23
24#[derive(Debug, Subcommand)]
25pub enum AttachmentCommand {
26 Export(export::ExportArgs),
28 List(list::ListArgs),
30 Get(get::GetArgs),
32 Upload(upload::UploadArgs),
34 Download(download::DownloadArgs),
36 Copy(transfer::TransferArgs),
38 Move(transfer::TransferArgs),
40 Delete(delete::DeleteArgs),
42 ListDeleted(recovery::DeletedArgs),
44 Restore(recovery::RestoreArgs),
46 UploadBackground(upload::BackgroundArgs),
48 DownloadBackground(download::BackgroundArgs),
50}
51
52impl AttachmentCommand {
53 pub(crate) fn validate_input(&self) -> Result<(), AppError> {
54 match self {
55 Self::Upload(a) => {
56 a.source.name()?;
57 }
58 Self::UploadBackground(a) => {
59 a.source.name()?;
60 }
61 _ => {}
62 }
63 Ok(())
64 }
65 pub(crate) fn download_destination(&self) -> Option<&download::DestinationArgs> {
66 match self {
67 Self::Download(a) => Some(&a.destination),
68 Self::DownloadBackground(a) => Some(&a.destination),
69 _ => None,
70 }
71 }
72}
73
74pub(crate) async fn dispatch(
75 command: AttachmentCommand,
76 factory: &WekanClientFactory,
77 store: &dyn CredentialStore,
78 confirmation: &dyn ConfirmationProvider,
79) -> Result<CommandSuccess, AppError> {
80 match command {
81 AttachmentCommand::ListDeleted(a) => recovery::list(a, factory, store).await,
82 AttachmentCommand::Restore(a) => recovery::restore(a, factory, store).await,
83 AttachmentCommand::Export(args) => export::execute(args, factory, store).await,
84 AttachmentCommand::List(a) => list::execute(a, factory, store).await,
85 AttachmentCommand::Get(a) => get::execute(a, factory, store).await,
86 AttachmentCommand::Upload(a) => upload::execute(a, factory, store).await,
87 AttachmentCommand::UploadBackground(a) => upload::background(a, factory, store).await,
88 AttachmentCommand::Download(a) => {
89 download::execute(Some(a.attachment_id), None, a.destination, factory, store).await
90 }
91 AttachmentCommand::DownloadBackground(a) => {
92 download::execute(None, Some(a.board_id), a.destination, factory, store).await
93 }
94 AttachmentCommand::Copy(a) => transfer::execute(a, false, factory, store).await,
95 AttachmentCommand::Move(a) => transfer::execute(a, true, factory, store).await,
96 AttachmentCommand::Delete(a) => delete::execute(a, factory, store, confirmation).await,
97 }
98}
99
100fn identifier(value: &str) -> Result<String, String> {
101 let value = crate::client::trim_javascript_whitespace(value);
102 if value.is_empty() || matches!(value, "." | "..") {
103 Err("ID must be nonblank and cannot be . or ..".to_owned())
104 } else {
105 Ok(value.to_owned())
106 }
107}
108
109fn file_error(operation: &str, path: &std::path::Path, error: std::io::Error) -> AppError {
110 AppError::new(
111 crate::error::ErrorCode::FileIoError,
112 format!(
113 "could not {operation} attachment file `{}`: {}",
114 crate::input::escape_terminal_text(&path.display().to_string()),
115 crate::input::escape_terminal_text(&error.to_string())
116 ),
117 crate::exit_code::StableExitCode::Transport,
118 )
119}