s3util-rs 1.4.0

Tools for managing Amazon S3 objects and buckets
Documentation
use anyhow::{Context, Result};
use tracing::info;

use s3util_rs::config::ClientConfig;
use s3util_rs::config::args::put_bucket_policy::PutBucketPolicyArgs;
use s3util_rs::storage::s3::api;

/// Runtime entry for `s3util put-bucket-policy s3://<BUCKET> <POLICY_FILE|->`
///
/// Reads the policy JSON from a file path or from stdin (`-`), then issues
/// `PutBucketPolicy`. The body is sent verbatim; s3util does not validate or
/// parse the JSON — S3 rejects invalid policies with `400 MalformedPolicy`.
/// Exits silently on success.
pub async fn run_put_bucket_policy(
    args: PutBucketPolicyArgs,
    client_config: ClientConfig,
) -> Result<()> {
    let bucket = args
        .bucket_name()
        .map_err(|e| anyhow::anyhow!("{}", e.trim_end()))?;

    let policy_arg = args
        .policy
        .as_deref()
        .ok_or_else(|| anyhow::anyhow!("policy source required"))?;

    let policy_body = if policy_arg == "-" {
        let mut buf = String::new();
        std::io::Read::read_to_string(&mut std::io::stdin(), &mut buf)?;
        buf
    } else {
        std::fs::read_to_string(policy_arg)
            .with_context(|| format!("reading policy from {policy_arg}"))?
    };

    let client = client_config.create_client().await;
    if args.dry_run {
        info!(bucket = %bucket, "[dry-run] would put bucket policy.");
        return Ok(());
    }
    api::put_bucket_policy(&client, &bucket, &policy_body).await?;
    info!(bucket = %bucket, "Bucket policy set.");
    Ok(())
}