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