use akv_cli::{ErrorKind, ResultExt};
use azure_identity::DeveloperToolsCredential;
use azure_storage_blob::BlobServiceClient;
use clap::{Parser, Subcommand};
use futures::TryStreamExt;
use std::{env, path::PathBuf};
use tokio::{
fs,
io::{self, AsyncWriteExt},
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotazure::load()?;
let args = Args::parse();
let endpoint = env::var("AZURE_STORAGE_SERVICE_ENDPOINT")
.with_context(ErrorKind::Other, "$AZURE_STORAGE_SERVICE_ENDPOINT required")?;
let credential = DeveloperToolsCredential::new(None)?;
let client = BlobServiceClient::new(&endpoint, Some(credential), None)?
.blob_container_client("examples");
if args.list_args().is_some() {
let mut pager = client.list_blobs(None)?;
while let Some(blob) = pager.try_next().await? {
let blob_name = blob.name.and_then(|n| n.content);
let blob_name = blob_name.as_deref().unwrap_or("(unknown)");
let content_type = blob.properties.and_then(|p| p.content_type);
let content_type = content_type.as_deref().unwrap_or("(unknown)");
println!("{blob_name} ({content_type})");
}
} else if let Some(Commands::Read(args)) = args.command {
let content = client
.blob_client(&args.name)
.download(None)
.await?
.into_body()
.collect()
.await?;
match args.output {
Some(path) => fs::write(path, content).await?,
None => io::stdout().write_all(content.as_ref()).await?,
};
}
Ok(())
}
#[derive(Debug, Parser)]
#[command(args_conflicts_with_subcommands = true)]
struct Args {
#[command(subcommand)]
command: Option<Commands>,
#[command(flatten)]
list: ListArgs,
}
impl Args {
fn list_args(&self) -> Option<&ListArgs> {
match self.command {
Some(Commands::List(ref args)) => Some(args),
Some(_) => None,
None => Some(&self.list),
}
}
}
#[derive(Debug, Subcommand)]
enum Commands {
List(ListArgs),
Read(ReadArgs),
}
#[derive(Debug, clap::Args)]
struct ListArgs;
#[derive(Debug, clap::Args)]
struct ReadArgs {
#[arg(short = 'n', long)]
name: String,
#[arg(short = 'o', long)]
output: Option<PathBuf>,
}