use rustack_s3_model::{
error::S3Error,
input::{
CreateBucketInput, DeleteBucketInput, GetBucketLocationInput, HeadBucketInput,
ListBucketsInput,
},
output::{CreateBucketOutput, GetBucketLocationOutput, HeadBucketOutput, ListBucketsOutput},
types::{Bucket, BucketLocationConstraint, LocationType, Owner},
};
use tracing::debug;
use crate::{
error::S3ServiceError, provider::RustackS3, state::object::Owner as InternalOwner,
validation::validate_bucket_name,
};
pub(crate) fn to_model_owner(owner: &InternalOwner) -> Owner {
Owner {
display_name: Some(owner.display_name.clone()),
id: Some(owner.id.clone()),
}
}
#[allow(clippy::unused_async)]
impl RustackS3 {
pub async fn handle_create_bucket(
&self,
input: CreateBucketInput,
) -> Result<CreateBucketOutput, S3Error> {
let bucket_name = input.bucket;
validate_bucket_name(&bucket_name).map_err(S3ServiceError::into_s3_error)?;
let region = input
.create_bucket_configuration
.and_then(|c| c.location_constraint)
.map_or_else(
|| self.config.default_region.clone(),
|lc: BucketLocationConstraint| lc.as_str().to_owned(),
);
let owner = InternalOwner::default();
let object_lock_enabled = input.object_lock_enabled_for_bucket.unwrap_or(false);
self.state
.create_bucket(bucket_name.clone(), region, owner)
.map_err(S3ServiceError::into_s3_error)?;
if object_lock_enabled {
if let Ok(bucket) = self.state.get_bucket(&bucket_name) {
*bucket.object_lock_enabled.write() = true;
bucket.enable_versioning();
}
}
debug!(bucket = %bucket_name, "create_bucket completed");
Ok(CreateBucketOutput {
bucket_arn: None,
location: Some(format!("/{bucket_name}")),
})
}
pub async fn handle_delete_bucket(&self, input: DeleteBucketInput) -> Result<(), S3Error> {
let bucket_name = input.bucket;
self.cors_index.delete_rules(&bucket_name);
self.storage.delete_bucket_data(&bucket_name);
self.state
.delete_bucket(&bucket_name)
.map_err(S3ServiceError::into_s3_error)?;
debug!(bucket = %bucket_name, "delete_bucket completed");
Ok(())
}
pub async fn handle_head_bucket(
&self,
input: HeadBucketInput,
) -> Result<HeadBucketOutput, S3Error> {
let bucket_name = input.bucket;
let bucket = self
.state
.get_bucket(&bucket_name)
.map_err(S3ServiceError::into_s3_error)?;
Ok(HeadBucketOutput {
access_point_alias: None,
bucket_arn: None,
bucket_location_name: Some(bucket.region.clone()),
bucket_location_type: Some(LocationType::from("Region")),
bucket_region: Some(bucket.region.clone()),
})
}
pub async fn handle_list_buckets(
&self,
_input: ListBucketsInput,
) -> Result<ListBucketsOutput, S3Error> {
let bucket_list = self.state.list_buckets();
let buckets: Vec<Bucket> = bucket_list
.into_iter()
.map(|(name, creation_date)| Bucket {
bucket_arn: None,
name: Some(name),
creation_date: Some(creation_date),
bucket_region: None,
})
.collect();
let owner = to_model_owner(&InternalOwner::default());
Ok(ListBucketsOutput {
buckets,
continuation_token: None,
owner: Some(owner),
prefix: None,
})
}
pub async fn handle_get_bucket_location(
&self,
input: GetBucketLocationInput,
) -> Result<GetBucketLocationOutput, S3Error> {
let bucket_name = input.bucket;
let bucket = self
.state
.get_bucket(&bucket_name)
.map_err(S3ServiceError::into_s3_error)?;
let location_constraint = if bucket.region == "us-east-1" {
None
} else {
Some(BucketLocationConstraint::from(bucket.region.as_str()))
};
Ok(GetBucketLocationOutput {
location_constraint,
})
}
}