🏡 index : ~doyle/shorty.git

use std::os::unix::fs::MetadataExt;

use bytes::BytesMut;
use futures_util::{future::Either, stream};
use rusoto_core::ByteStream;
use rusoto_s3::{PutObjectRequest, S3};
use tokio::{fs::File, io::AsyncReadExt};
use tokio_util::io::ReaderStream;
use uuid::Uuid;

#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
    let mut args = std::env::args();
    let path = args
        .nth(1)
        .filter(|v| v != "-")
        .map(std::path::PathBuf::from);

    let load_file_and_meta = async {
        if let Some(path) = &path {
            let extension = path.extension().and_then(|v| v.to_str());
            let mime = mime_guess::from_path(path).first_raw();
            let file = File::open(path).await?;
            let size = file.metadata().await?.size() as usize;
            let stream = Either::Left(ReaderStream::new(file));
            Ok((stream, extension, mime, size))
        } else {
            let mut buf = BytesMut::new();
            while tokio::io::stdin().read_buf(&mut buf).await? != 0 {}
            let buf = buf.freeze();
            let len = buf.len();
            let (extension, mime) = infer::get(&buf)
                .map(|v| (v.extension(), v.mime_type()))
                .unzip();
            let stream = Either::Right(stream::once(async move { Ok(buf) }));
            Ok((stream, extension, mime, len))
        }
    };

    let build_client = async {
        let config = shorty::Config::load().await?;
        let client = shorty::s3_client(
            config.s3.key,
            config.s3.secret,
            config.s3.endpoint,
            config.s3.tls,
        )
        .await?;

        Ok::<_, anyhow::Error>((config.s3.bucket, client))
    };

    let ((bucket, s3), (stream, extension, mime, size)) =
        tokio::try_join!(build_client, load_file_and_meta)?;

    let id = Uuid::new_v4();

    let key = if let Some(ext) = extension {
        format!("u/{id}.{ext}")
    } else {
        format!("u/{id}")
    };

    println!("https://{bucket}/{key}");

    s3.put_object(PutObjectRequest {
        key,
        content_type: mime.map(|v| v.to_string()),
        bucket,
        body: Some(ByteStream::new_with_size(stream, size)),
        ..PutObjectRequest::default()
    })
    .await?;

    Ok(())
}