use fakecloud_aws::ec2query::{ec2_elem, ec2_list};
use fakecloud_core::service::{AwsRequest, AwsResponse, AwsServiceError};
use crate::service::Ec2Service;
use crate::service_helpers::{indexed_list, parse_filters};
const REGIONS: &[&str] = &[
"us-east-1",
"us-east-2",
"us-west-1",
"us-west-2",
"af-south-1",
"ap-east-1",
"ap-south-1",
"ap-south-2",
"ap-northeast-1",
"ap-northeast-2",
"ap-northeast-3",
"ap-southeast-1",
"ap-southeast-2",
"ap-southeast-3",
"ap-southeast-4",
"ca-central-1",
"eu-central-1",
"eu-central-2",
"eu-west-1",
"eu-west-2",
"eu-west-3",
"eu-north-1",
"eu-south-1",
"eu-south-2",
"me-south-1",
"me-central-1",
"sa-east-1",
];
pub(crate) fn describe_regions(
_svc: &Ec2Service,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let requested = indexed_list(&req.query_params, "RegionName");
let filters = parse_filters(&req.query_params);
let name_filter: Vec<String> = filters
.iter()
.filter(|f| f.name == "region-name")
.flat_map(|f| f.values.clone())
.collect();
let items: Vec<String> = REGIONS
.iter()
.filter(|r| requested.is_empty() || requested.iter().any(|x| x == *r))
.filter(|r| name_filter.is_empty() || name_filter.iter().any(|x| x == *r))
.map(|r| {
format!(
"{}{}{}",
ec2_elem("regionName", r),
ec2_elem("regionEndpoint", &format!("ec2.{r}.amazonaws.com")),
ec2_elem("optInStatus", "opt-in-not-required"),
)
})
.collect();
let body = ec2_list("regionInfo", &items);
Ok(Ec2Service::respond(
"DescribeRegions",
&req.request_id,
&body,
))
}
pub(crate) fn describe_availability_zones(
_svc: &Ec2Service,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let region = if req.region.is_empty() {
"us-east-1"
} else {
&req.region
};
let requested = indexed_list(&req.query_params, "ZoneName");
let items: Vec<String> = ["a", "b", "c"]
.iter()
.enumerate()
.map(|(i, suffix)| (format!("{region}{suffix}"), i + 1))
.filter(|(zone, _)| requested.is_empty() || requested.iter().any(|x| x == zone))
.map(|(zone, idx)| {
let short = region_short_code(region);
format!(
"{}{}{}{}{}",
ec2_elem("zoneName", &zone),
ec2_elem("zoneState", "available"),
ec2_elem("regionName", region),
ec2_elem("zoneId", &format!("{short}-az{idx}")),
ec2_elem("zoneType", "availability-zone"),
)
})
.collect();
let body = ec2_list("availabilityZoneInfo", &items);
Ok(Ec2Service::respond(
"DescribeAvailabilityZones",
&req.request_id,
&body,
))
}
pub(crate) fn describe_account_attributes(
_svc: &Ec2Service,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let attrs: &[(&str, &[&str])] = &[
("supported-platforms", &["VPC"]),
("default-vpc", &["vpc-00000000"]),
("max-instances", &["20"]),
("vpc-max-security-groups-per-interface", &["5"]),
("max-elastic-ips", &["5"]),
("vpc-max-elastic-ips", &["5"]),
];
let requested = indexed_list(&req.query_params, "AttributeName");
let items: Vec<String> = attrs
.iter()
.filter(|(name, _)| requested.is_empty() || requested.iter().any(|x| x == name))
.map(|(name, values)| {
let value_items: Vec<String> = values
.iter()
.map(|v| ec2_elem("attributeValue", v))
.collect();
format!(
"{}{}",
ec2_elem("attributeName", name),
ec2_list("attributeValueSet", &value_items),
)
})
.collect();
let body = ec2_list("accountAttributeSet", &items);
Ok(Ec2Service::respond(
"DescribeAccountAttributes",
&req.request_id,
&body,
))
}
fn region_short_code(region: &str) -> String {
let parts: Vec<&str> = region.split('-').collect();
if parts.len() < 3 {
return region.replace('-', "");
}
let first: String = parts[0].chars().take(2).collect();
let middle: String = parts[1].chars().take(1).collect();
format!("{first}{middle}{}", parts[2])
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn region_short_code_matches_aws_convention() {
assert_eq!(region_short_code("us-east-1"), "use1");
assert_eq!(region_short_code("ap-southeast-2"), "aps2");
assert_eq!(region_short_code("eu-central-1"), "euc1");
}
}