bobashare_admin/cli/
create.rs1use std::path::PathBuf;
2
3use anyhow::{anyhow, Context};
4use bobashare::{generate_randomized_id, storage::file::FileBackend};
5use chrono::TimeDelta;
6use clap::{Args, Subcommand};
7use tokio::{
8 fs::File,
9 io::{self, AsyncWriteExt},
10};
11use tracing::{event, instrument, Level};
12
13#[derive(Debug, Clone, Args)]
14pub(crate) struct CreateUpload {
15 #[clap(short, long, value_parser)]
19 expiry: Option<u16>,
20 #[clap(short, long, value_parser)]
21 source_file: PathBuf,
22
23 #[clap(subcommand)]
24 name: NameOptions,
25}
26#[derive(Debug, Clone, Subcommand)]
27pub(crate) enum NameOptions {
28 Random {
30 #[clap(short, long, default_value_t = 8)]
32 length: u16,
33 },
34 Name {
36 #[clap(short, long)]
37 name: String,
38 },
39}
40
41#[instrument(skip(backend))]
42pub(crate) async fn create_upload(backend: FileBackend, args: CreateUpload) -> anyhow::Result<()> {
43 let expiry = args.expiry.map(|e| TimeDelta::try_days(e.into()).unwrap());
44 let name = match args.name {
45 NameOptions::Name { name } => name,
47 NameOptions::Random { length } => generate_randomized_id(length.into()),
48 };
49
50 let filename = args
51 .source_file
52 .file_name()
53 .ok_or_else(|| anyhow!("invalid filename for source file"))?
54 .to_string_lossy()
55 .to_string();
56 let mut file = File::open(&args.source_file)
57 .await
58 .with_context(|| format!("error opening file at {:?}", &args.source_file))?;
59 let mimetype = mime_guess::from_path(&args.source_file).first_or_octet_stream();
60
61 let mut upload = backend
62 .create_upload(name, filename, mimetype, expiry, None)
63 .await?;
64
65 println!("{:?}", upload.metadata);
66
67 let copied = io::copy(&mut file, &mut upload.file).await?;
68
69 event!(Level::DEBUG, "Wrote {} bytes to the upload file", copied);
70 upload.file.flush().await?;
71 upload.flush().await?;
72
73 Ok(())
74}