Skip to main content

alien_core/
network_ids.rs

1//! Normalization at AWS network input and checkpoint boundaries.
2//!
3//! An unset CloudFormation CommaDelimitedList resolves to `[""]`. Terraform
4//! callers can supply the same value explicitly. Empty identifiers mean absence.
5
6use serde::{Deserialize, Deserializer};
7
8/// Deserialize network IDs, trimming whitespace and removing empty entries.
9pub fn deserialize_ids<'de, D: Deserializer<'de>>(
10    deserializer: D,
11) -> Result<Vec<String>, D::Error> {
12    let ids = Vec::<String>::deserialize(deserializer)?;
13    Ok(ids
14        .into_iter()
15        .map(|id| id.trim().to_string())
16        .filter(|id| !id.is_empty())
17        .collect())
18}
19
20/// Deserialize an optional network ID; blank values mean no ID was supplied.
21pub fn deserialize_optional_id<'de, D: Deserializer<'de>>(
22    deserializer: D,
23) -> Result<Option<String>, D::Error> {
24    let id = Option::<String>::deserialize(deserializer)?;
25    Ok(id
26        .map(|id| id.trim().to_string())
27        .filter(|id| !id.is_empty()))
28}
29
30#[cfg(test)]
31mod tests {
32    use crate::{import::data::AwsNetworkImportData, NetworkSettings};
33    use serde_json::json;
34
35    #[test]
36    fn setup_network_settings_normalize_all_aws_id_lists_and_round_trip() {
37        for (ids, expected) in [
38            (json!([]), json!([])),
39            (json!([""]), json!([])),
40            (json!(["  "]), json!([])),
41            (json!(["", " id-a ", "id-b", ""]), json!(["id-a", "id-b"])),
42        ] {
43            let value = json!({"type": "byo-vpc-aws", "vpc_id": "vpc-test", "public_subnet_ids": ids, "private_subnet_ids": ids, "security_group_ids": ids});
44            let parsed: NetworkSettings = serde_json::from_value(value).unwrap();
45            let serialized = serde_json::to_value(&parsed).unwrap();
46            for key in [
47                "public_subnet_ids",
48                "private_subnet_ids",
49                "security_group_ids",
50            ] {
51                assert_eq!(serialized[key], expected);
52            }
53            assert_eq!(
54                serde_json::from_value::<NetworkSettings>(serialized).unwrap(),
55                parsed
56            );
57        }
58    }
59
60    #[test]
61    fn network_import_normalizes_flat_and_zonal_ids_and_optional_security_group() {
62        for group in [json!(null), json!(""), json!("  "), json!(" sg-real ")] {
63            let data: AwsNetworkImportData = serde_json::from_value(json!({
64                "vpcId": "vpc-test", "publicSubnetIds": ["", "  "],
65                "privateSubnetIds": ["", " subnet-private "], "securityGroupId": group,
66                "availabilityZones": [], "isByoVpc": "true",
67                "subnetsByFailureDomain": {"us-east-1a": {"publicSubnetIds": [""], "privateSubnetIds": [" subnet-private ", ""]}}
68            })).unwrap();
69            assert!(data.public_subnet_ids.is_empty());
70            assert_eq!(data.private_subnet_ids, ["subnet-private"]);
71            assert_eq!(
72                data.security_group_id.as_deref(),
73                if group == " sg-real " {
74                    Some("sg-real")
75                } else {
76                    None
77                }
78            );
79            assert!(data.subnets_by_failure_domain["us-east-1a"]
80                .public_subnet_ids
81                .is_empty());
82            assert_eq!(
83                data.subnets_by_failure_domain["us-east-1a"].private_subnet_ids,
84                ["subnet-private"]
85            );
86        }
87    }
88}