use super::*;
pub(crate) fn parse_bucket_metadata_configuration(
bucket: &str,
body: &[u8],
) -> Result<BucketMetadataConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
if !xml.contains("<MetadataConfiguration") {
return Err(RuntimeError::InvalidBucketMetadataConfiguration(
"missing MetadataConfiguration root".to_string(),
));
}
let journal_xml = text_between(
&xml,
"<JournalTableConfiguration>",
"</JournalTableConfiguration>",
)
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataConfiguration(
"MetadataConfiguration must include JournalTableConfiguration".to_string(),
)
})?;
let record_expiration = parse_record_expiration(journal_xml)?;
let journal_encryption = parse_metadata_table_encryption(journal_xml)?;
let inventory_table_configuration = text_between(
&xml,
"<InventoryTableConfiguration>",
"</InventoryTableConfiguration>",
)
.map(parse_inventory_table_configuration)
.transpose()?;
Ok(BucketMetadataConfiguration {
bucket: bucket.to_string(),
journal_table_configuration: JournalTableConfiguration {
encryption_configuration: journal_encryption,
record_expiration,
},
inventory_table_configuration,
})
}
pub(crate) fn parse_journal_table_configuration(
body: &[u8],
) -> Result<JournalTableConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
if !xml.contains("<JournalTableConfiguration") {
return Err(RuntimeError::InvalidBucketMetadataConfiguration(
"missing JournalTableConfiguration root".to_string(),
));
}
let record_expiration = parse_record_expiration(&xml)?;
let encryption_configuration = parse_metadata_table_encryption(&xml)?;
Ok(JournalTableConfiguration {
encryption_configuration,
record_expiration,
})
}
pub(crate) fn parse_inventory_table_configuration_body(
body: &[u8],
) -> Result<InventoryTableConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
if !xml.contains("<InventoryTableConfiguration") {
return Err(RuntimeError::InvalidBucketMetadataConfiguration(
"missing InventoryTableConfiguration root".to_string(),
));
}
parse_inventory_table_configuration(&xml)
}
pub(crate) fn parse_bucket_metadata_table_configuration(
bucket: &str,
body: &[u8],
) -> Result<BucketMetadataTableConfiguration, RuntimeError> {
let xml = String::from_utf8_lossy(body);
if !xml.contains("<MetadataTableConfiguration") {
return Err(RuntimeError::InvalidBucketMetadataTableConfiguration(
"missing MetadataTableConfiguration root".to_string(),
));
}
let destination_xml = text_between(&xml, "<S3TablesDestination>", "</S3TablesDestination>")
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataTableConfiguration(
"MetadataTableConfiguration must include S3TablesDestination".to_string(),
)
})?;
let table_bucket_arn = text_between(destination_xml, "<TableBucketArn>", "</TableBucketArn>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataTableConfiguration(
"S3TablesDestination must include TableBucketArn".to_string(),
)
})?;
let table_name = text_between(destination_xml, "<TableName>", "</TableName>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataTableConfiguration(
"S3TablesDestination must include TableName".to_string(),
)
})?;
Ok(BucketMetadataTableConfiguration {
bucket: bucket.to_string(),
s3_tables_destination: S3TablesDestination {
table_bucket_arn: table_bucket_arn.to_string(),
table_name: table_name.to_string(),
},
})
}
fn parse_inventory_table_configuration(
xml: &str,
) -> Result<InventoryTableConfiguration, RuntimeError> {
let configuration_state = text_between(xml, "<ConfigurationState>", "</ConfigurationState>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataConfiguration(
"InventoryTableConfiguration must include ConfigurationState".to_string(),
)
})?;
Ok(InventoryTableConfiguration {
configuration_state: configuration_state.to_string(),
encryption_configuration: parse_metadata_table_encryption(xml)?,
})
}
fn parse_record_expiration(xml: &str) -> Result<RecordExpiration, RuntimeError> {
let record_expiration = text_between(xml, "<RecordExpiration>", "</RecordExpiration>")
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataConfiguration(
"JournalTableConfiguration must include RecordExpiration".to_string(),
)
})?;
let expiration = text_between(record_expiration, "<Expiration>", "</Expiration>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataConfiguration(
"RecordExpiration must include Expiration".to_string(),
)
})?;
let days = text_between(record_expiration, "<Days>", "</Days>")
.map(str::trim)
.filter(|value| !value.is_empty())
.map(|value| {
value.parse::<u32>().map_err(|_| {
RuntimeError::InvalidBucketMetadataConfiguration(format!(
"invalid RecordExpiration Days: {value}"
))
})
})
.transpose()?;
Ok(RecordExpiration {
expiration: expiration.to_string(),
days,
})
}
fn parse_metadata_table_encryption(
xml: &str,
) -> Result<Option<MetadataTableEncryptionConfiguration>, RuntimeError> {
let Some(encryption_xml) = text_between(
xml,
"<EncryptionConfiguration>",
"</EncryptionConfiguration>",
) else {
return Ok(None);
};
let sse_algorithm = text_between(encryption_xml, "<SseAlgorithm>", "</SseAlgorithm>")
.map(str::trim)
.filter(|value| !value.is_empty())
.ok_or_else(|| {
RuntimeError::InvalidBucketMetadataConfiguration(
"EncryptionConfiguration must include SseAlgorithm".to_string(),
)
})?;
let kms_key_arn = text_between(encryption_xml, "<KmsKeyArn>", "</KmsKeyArn>")
.map(str::trim)
.filter(|value| !value.is_empty())
.map(str::to_string);
Ok(Some(MetadataTableEncryptionConfiguration {
sse_algorithm: sse_algorithm.to_string(),
kms_key_arn,
}))
}