Skip to main content

winterbaume_networkfirewall/
views.rs

1//! Serde-compatible view types for NetworkFirewall state snapshots.
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6use winterbaume_core::{StateChangeNotifier, StateViewError, StatefulService};
7
8use crate::handlers::NetworkFirewallService;
9use crate::state::NetworkFirewallState;
10
11/// Serializable view of a subnet mapping.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct SubnetMappingView {
14    pub subnet_id: String,
15}
16
17/// Serializable view of a firewall.
18#[derive(Debug, Clone, Serialize, Deserialize, Default)]
19pub struct FirewallView {
20    pub firewall_name: String,
21    pub firewall_arn: String,
22    pub firewall_id: String,
23    pub firewall_policy_arn: String,
24    pub vpc_id: String,
25    #[serde(default)]
26    pub subnet_mappings: Vec<SubnetMappingView>,
27    pub delete_protection: bool,
28    #[serde(default)]
29    pub subnet_change_protection: bool,
30    #[serde(default)]
31    pub firewall_policy_change_protection: bool,
32    #[serde(default)]
33    pub availability_zone_change_protection: bool,
34    pub description: Option<String>,
35    #[serde(default)]
36    pub tags: Vec<(String, String)>,
37    /// Encryption configuration stored as `{"key_id": "...", "type": "..."}`.
38    #[serde(default)]
39    pub encryption_configuration: Option<serde_json::Value>,
40}
41
42/// Serializable view of a rule group.
43#[derive(Debug, Clone, Serialize, Deserialize, Default)]
44pub struct RuleGroupView {
45    pub rule_group_name: String,
46    pub rule_group_arn: String,
47    pub rule_group_id: String,
48    pub rule_group_type: String,
49    pub capacity: i32,
50    pub description: Option<String>,
51    #[serde(default)]
52    pub tags: Vec<(String, String)>,
53    pub rule_group_body: Option<serde_json::Value>,
54    pub rules: Option<String>,
55    /// Encryption configuration stored as `{"key_id": "...", "type": "..."}`.
56    #[serde(default)]
57    pub encryption_configuration: Option<serde_json::Value>,
58}
59
60/// Serializable view of a firewall policy.
61#[derive(Debug, Clone, Serialize, Deserialize, Default)]
62pub struct FirewallPolicyView {
63    pub firewall_policy_name: String,
64    pub firewall_policy_arn: String,
65    pub firewall_policy_id: String,
66    pub description: Option<String>,
67    #[serde(default)]
68    pub tags: Vec<(String, String)>,
69    pub firewall_policy_body: serde_json::Value,
70    /// Encryption configuration stored as `{"key_id": "...", "type": "..."}`.
71    #[serde(default)]
72    pub encryption_configuration: Option<serde_json::Value>,
73}
74
75/// Serializable view of a resource policy.
76#[derive(Debug, Clone, Serialize, Deserialize, Default)]
77pub struct ResourcePolicyView {
78    pub resource_arn: String,
79    pub policy: String,
80}
81
82/// Serializable view of a TLS inspection configuration.
83#[derive(Debug, Clone, Serialize, Deserialize, Default)]
84pub struct TlsInspectionConfigurationView {
85    pub name: String,
86    pub arn: String,
87    pub id: String,
88    pub description: Option<String>,
89    #[serde(default)]
90    pub tags: Vec<(String, String)>,
91    pub body: serde_json::Value,
92}
93
94/// Serializable view of a VPC endpoint association.
95#[derive(Debug, Clone, Serialize, Deserialize, Default)]
96pub struct VpcEndpointAssociationView {
97    pub vpc_endpoint_association_arn: String,
98    pub vpc_endpoint_association_id: String,
99    pub firewall_arn: String,
100    pub vpc_id: String,
101    pub subnet_id: String,
102    pub description: Option<String>,
103    #[serde(default)]
104    pub tags: Vec<(String, String)>,
105}
106
107/// Serializable view of an availability zone mapping.
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct AvailabilityZoneMappingView {
110    pub availability_zone: String,
111}
112
113/// Serializable view of a transit gateway attachment.
114#[derive(Debug, Clone, Serialize, Deserialize, Default)]
115pub struct TransitGatewayAttachmentView {
116    pub transit_gateway_attachment_id: String,
117    pub status: String,
118}
119
120/// Serializable view of a proxy.
121#[derive(Debug, Clone, Serialize, Deserialize, Default)]
122pub struct NfwProxyView {
123    pub proxy_name: String,
124    pub proxy_arn: String,
125    pub nat_gateway_id: String,
126    pub proxy_configuration_arn: Option<String>,
127    pub proxy_configuration_name: Option<String>,
128    pub proxy_state: String,
129    #[serde(default)]
130    pub tags: Vec<(String, String)>,
131    pub body: serde_json::Value,
132}
133
134/// Serializable view of a proxy configuration.
135#[derive(Debug, Clone, Serialize, Deserialize, Default)]
136pub struct NfwProxyConfigurationView {
137    pub proxy_configuration_name: String,
138    pub proxy_configuration_arn: String,
139    pub description: Option<String>,
140    #[serde(default)]
141    pub tags: Vec<(String, String)>,
142    pub body: serde_json::Value,
143}
144
145/// Serializable view of a proxy rule group.
146#[derive(Debug, Clone, Serialize, Deserialize, Default)]
147pub struct NfwProxyRuleGroupView {
148    pub proxy_rule_group_name: String,
149    pub proxy_rule_group_arn: String,
150    pub description: Option<String>,
151    #[serde(default)]
152    pub tags: Vec<(String, String)>,
153    pub body: serde_json::Value,
154}
155
156/// Serializable view of a flow operation.
157#[derive(Debug, Clone, Serialize, Deserialize, Default)]
158pub struct FlowOperationView {
159    pub flow_operation_id: String,
160    pub firewall_arn: String,
161    pub flow_operation_type: String,
162    pub flow_operation_status: String,
163    pub body: serde_json::Value,
164}
165
166/// Serializable view of an analysis report.
167#[derive(Debug, Clone, Serialize, Deserialize, Default)]
168pub struct AnalysisReportView {
169    pub analysis_report_id: String,
170    pub firewall_arn: String,
171    pub analysis_type: String,
172    pub status: String,
173}
174
175/// Serializable view of an encryption configuration.
176#[derive(Debug, Clone, Serialize, Deserialize, Default)]
177pub struct EncryptionConfigView {
178    pub key_id: Option<String>,
179    pub config_type: String,
180}
181
182/// Serializable view of the entire NetworkFirewall state for one account/region.
183#[derive(Debug, Clone, Serialize, Deserialize, Default)]
184pub struct NetworkFirewallStateView {
185    #[serde(default)]
186    pub firewalls: HashMap<String, FirewallView>,
187    /// Logging configurations stored as raw JSON strings, keyed by firewall ARN.
188    #[serde(default)]
189    pub logging_configs: HashMap<String, String>,
190    #[serde(default)]
191    pub rule_groups: HashMap<String, RuleGroupView>,
192    #[serde(default)]
193    pub firewall_policies: HashMap<String, FirewallPolicyView>,
194    #[serde(default)]
195    pub resource_policies: HashMap<String, ResourcePolicyView>,
196    #[serde(default)]
197    pub tls_inspection_configurations: HashMap<String, TlsInspectionConfigurationView>,
198    #[serde(default)]
199    pub vpc_endpoint_associations: HashMap<String, VpcEndpointAssociationView>,
200    #[serde(default)]
201    pub availability_zone_mappings: HashMap<String, Vec<AvailabilityZoneMappingView>>,
202    #[serde(default)]
203    pub transit_gateway_attachments: HashMap<String, TransitGatewayAttachmentView>,
204    #[serde(default)]
205    pub proxies: HashMap<String, NfwProxyView>,
206    #[serde(default)]
207    pub proxy_configurations: HashMap<String, NfwProxyConfigurationView>,
208    #[serde(default)]
209    pub proxy_rule_groups: HashMap<String, NfwProxyRuleGroupView>,
210    #[serde(default)]
211    pub flow_operations: HashMap<String, FlowOperationView>,
212    #[serde(default)]
213    pub analysis_reports: HashMap<String, AnalysisReportView>,
214    #[serde(default)]
215    pub encryption_configs: HashMap<String, EncryptionConfigView>,
216    #[serde(default)]
217    pub analysis_settings: HashMap<String, Vec<String>>,
218}
219
220// --- From internal types to view types ---
221
222impl From<&crate::types::Firewall> for FirewallView {
223    fn from(fw: &crate::types::Firewall) -> Self {
224        FirewallView {
225            firewall_name: fw.firewall_name.clone(),
226            firewall_arn: fw.firewall_arn.clone(),
227            firewall_id: fw.firewall_id.clone(),
228            firewall_policy_arn: fw.firewall_policy_arn.clone(),
229            vpc_id: fw.vpc_id.clone(),
230            subnet_mappings: fw
231                .subnet_mappings
232                .iter()
233                .map(|sm| SubnetMappingView {
234                    subnet_id: sm.subnet_id.clone(),
235                })
236                .collect(),
237            delete_protection: fw.delete_protection,
238            subnet_change_protection: fw.subnet_change_protection,
239            firewall_policy_change_protection: fw.firewall_policy_change_protection,
240            availability_zone_change_protection: fw.availability_zone_change_protection,
241            description: fw.description.clone(),
242            tags: fw.tags.clone(),
243            encryption_configuration: fw.encryption_configuration.clone(),
244        }
245    }
246}
247
248impl From<&crate::types::RuleGroup> for RuleGroupView {
249    fn from(rg: &crate::types::RuleGroup) -> Self {
250        RuleGroupView {
251            rule_group_name: rg.rule_group_name.clone(),
252            rule_group_arn: rg.rule_group_arn.clone(),
253            rule_group_id: rg.rule_group_id.clone(),
254            rule_group_type: rg.rule_group_type.clone(),
255            capacity: rg.capacity,
256            description: rg.description.clone(),
257            tags: rg.tags.clone(),
258            rule_group_body: rg.rule_group_body.clone(),
259            rules: rg.rules.clone(),
260            encryption_configuration: rg.encryption_configuration.clone(),
261        }
262    }
263}
264
265impl From<&crate::types::FirewallPolicy> for FirewallPolicyView {
266    fn from(fp: &crate::types::FirewallPolicy) -> Self {
267        FirewallPolicyView {
268            firewall_policy_name: fp.firewall_policy_name.clone(),
269            firewall_policy_arn: fp.firewall_policy_arn.clone(),
270            firewall_policy_id: fp.firewall_policy_id.clone(),
271            description: fp.description.clone(),
272            tags: fp.tags.clone(),
273            firewall_policy_body: fp.firewall_policy_body.clone(),
274            encryption_configuration: fp.encryption_configuration.clone(),
275        }
276    }
277}
278
279impl From<&crate::types::TlsInspectionConfiguration> for TlsInspectionConfigurationView {
280    fn from(tls: &crate::types::TlsInspectionConfiguration) -> Self {
281        TlsInspectionConfigurationView {
282            name: tls.name.clone(),
283            arn: tls.arn.clone(),
284            id: tls.id.clone(),
285            description: tls.description.clone(),
286            tags: tls.tags.clone(),
287            body: tls.body.clone(),
288        }
289    }
290}
291
292impl From<&crate::types::VpcEndpointAssociation> for VpcEndpointAssociationView {
293    fn from(assoc: &crate::types::VpcEndpointAssociation) -> Self {
294        VpcEndpointAssociationView {
295            vpc_endpoint_association_arn: assoc.vpc_endpoint_association_arn.clone(),
296            vpc_endpoint_association_id: assoc.vpc_endpoint_association_id.clone(),
297            firewall_arn: assoc.firewall_arn.clone(),
298            vpc_id: assoc.vpc_id.clone(),
299            subnet_id: assoc.subnet_id.clone(),
300            description: assoc.description.clone(),
301            tags: assoc.tags.clone(),
302        }
303    }
304}
305
306impl From<&NetworkFirewallState> for NetworkFirewallStateView {
307    fn from(state: &NetworkFirewallState) -> Self {
308        NetworkFirewallStateView {
309            firewalls: state
310                .firewalls
311                .iter()
312                .map(|(k, v)| (k.clone(), FirewallView::from(v)))
313                .collect(),
314            logging_configs: state
315                .logging_configs
316                .iter()
317                .map(|(k, v)| (k.clone(), serde_json::to_string(v).unwrap_or_default()))
318                .collect(),
319            rule_groups: state
320                .rule_groups
321                .iter()
322                .map(|(k, v)| (k.clone(), RuleGroupView::from(v)))
323                .collect(),
324            firewall_policies: state
325                .firewall_policies
326                .iter()
327                .map(|(k, v)| (k.clone(), FirewallPolicyView::from(v)))
328                .collect(),
329            resource_policies: state
330                .resource_policies
331                .iter()
332                .map(|(k, v)| {
333                    (
334                        k.clone(),
335                        ResourcePolicyView {
336                            resource_arn: v.resource_arn.clone(),
337                            policy: v.policy.clone(),
338                        },
339                    )
340                })
341                .collect(),
342            tls_inspection_configurations: state
343                .tls_inspection_configurations
344                .iter()
345                .map(|(k, v)| (k.clone(), TlsInspectionConfigurationView::from(v)))
346                .collect(),
347            vpc_endpoint_associations: state
348                .vpc_endpoint_associations
349                .iter()
350                .map(|(k, v)| (k.clone(), VpcEndpointAssociationView::from(v)))
351                .collect(),
352            availability_zone_mappings: state
353                .availability_zone_mappings
354                .iter()
355                .map(|(k, v)| {
356                    (
357                        k.clone(),
358                        v.iter()
359                            .map(|m| AvailabilityZoneMappingView {
360                                availability_zone: m.availability_zone.clone(),
361                            })
362                            .collect(),
363                    )
364                })
365                .collect(),
366            transit_gateway_attachments: state
367                .transit_gateway_attachments
368                .iter()
369                .map(|(k, v)| {
370                    (
371                        k.clone(),
372                        TransitGatewayAttachmentView {
373                            transit_gateway_attachment_id: v.transit_gateway_attachment_id.clone(),
374                            status: v.status.clone(),
375                        },
376                    )
377                })
378                .collect(),
379            proxies: state
380                .proxies
381                .iter()
382                .map(|(k, v)| {
383                    (
384                        k.clone(),
385                        NfwProxyView {
386                            proxy_name: v.proxy_name.clone(),
387                            proxy_arn: v.proxy_arn.clone(),
388                            nat_gateway_id: v.nat_gateway_id.clone(),
389                            proxy_configuration_arn: v.proxy_configuration_arn.clone(),
390                            proxy_configuration_name: v.proxy_configuration_name.clone(),
391                            proxy_state: v.proxy_state.clone(),
392                            tags: v.tags.clone(),
393                            body: v.body.clone(),
394                        },
395                    )
396                })
397                .collect(),
398            proxy_configurations: state
399                .proxy_configurations
400                .iter()
401                .map(|(k, v)| {
402                    (
403                        k.clone(),
404                        NfwProxyConfigurationView {
405                            proxy_configuration_name: v.proxy_configuration_name.clone(),
406                            proxy_configuration_arn: v.proxy_configuration_arn.clone(),
407                            description: v.description.clone(),
408                            tags: v.tags.clone(),
409                            body: v.body.clone(),
410                        },
411                    )
412                })
413                .collect(),
414            proxy_rule_groups: state
415                .proxy_rule_groups
416                .iter()
417                .map(|(k, v)| {
418                    (
419                        k.clone(),
420                        NfwProxyRuleGroupView {
421                            proxy_rule_group_name: v.proxy_rule_group_name.clone(),
422                            proxy_rule_group_arn: v.proxy_rule_group_arn.clone(),
423                            description: v.description.clone(),
424                            tags: v.tags.clone(),
425                            body: v.body.clone(),
426                        },
427                    )
428                })
429                .collect(),
430            flow_operations: state
431                .flow_operations
432                .iter()
433                .map(|(k, v)| {
434                    (
435                        k.clone(),
436                        FlowOperationView {
437                            flow_operation_id: v.flow_operation_id.clone(),
438                            firewall_arn: v.firewall_arn.clone(),
439                            flow_operation_type: v.flow_operation_type.clone(),
440                            flow_operation_status: v.flow_operation_status.clone(),
441                            body: v.body.clone(),
442                        },
443                    )
444                })
445                .collect(),
446            analysis_reports: state
447                .analysis_reports
448                .iter()
449                .map(|(k, v)| {
450                    (
451                        k.clone(),
452                        AnalysisReportView {
453                            analysis_report_id: v.analysis_report_id.clone(),
454                            firewall_arn: v.firewall_arn.clone(),
455                            analysis_type: v.analysis_type.clone(),
456                            status: v.status.clone(),
457                        },
458                    )
459                })
460                .collect(),
461            encryption_configs: state
462                .encryption_configs
463                .iter()
464                .map(|(k, v)| {
465                    (
466                        k.clone(),
467                        EncryptionConfigView {
468                            key_id: v.key_id.clone(),
469                            config_type: v.config_type.clone(),
470                        },
471                    )
472                })
473                .collect(),
474            analysis_settings: state.analysis_settings.clone(),
475        }
476    }
477}
478
479// --- From view types to internal types ---
480
481impl From<FirewallView> for crate::types::Firewall {
482    fn from(v: FirewallView) -> Self {
483        crate::types::Firewall {
484            firewall_name: v.firewall_name,
485            firewall_arn: v.firewall_arn,
486            firewall_id: v.firewall_id,
487            firewall_policy_arn: v.firewall_policy_arn,
488            vpc_id: v.vpc_id,
489            subnet_mappings: v
490                .subnet_mappings
491                .into_iter()
492                .map(|sm| crate::types::SubnetMapping {
493                    subnet_id: sm.subnet_id,
494                })
495                .collect(),
496            delete_protection: v.delete_protection,
497            subnet_change_protection: v.subnet_change_protection,
498            firewall_policy_change_protection: v.firewall_policy_change_protection,
499            availability_zone_change_protection: v.availability_zone_change_protection,
500            description: v.description,
501            tags: v.tags,
502            encryption_configuration: v.encryption_configuration,
503        }
504    }
505}
506
507impl From<RuleGroupView> for crate::types::RuleGroup {
508    fn from(v: RuleGroupView) -> Self {
509        crate::types::RuleGroup {
510            rule_group_name: v.rule_group_name,
511            rule_group_arn: v.rule_group_arn,
512            rule_group_id: v.rule_group_id,
513            rule_group_type: v.rule_group_type,
514            capacity: v.capacity,
515            description: v.description,
516            tags: v.tags,
517            rule_group_body: v.rule_group_body,
518            rules: v.rules,
519            encryption_configuration: v.encryption_configuration,
520        }
521    }
522}
523
524impl From<FirewallPolicyView> for crate::types::FirewallPolicy {
525    fn from(v: FirewallPolicyView) -> Self {
526        crate::types::FirewallPolicy {
527            firewall_policy_name: v.firewall_policy_name,
528            firewall_policy_arn: v.firewall_policy_arn,
529            firewall_policy_id: v.firewall_policy_id,
530            description: v.description,
531            tags: v.tags,
532            firewall_policy_body: v.firewall_policy_body,
533            encryption_configuration: v.encryption_configuration,
534        }
535    }
536}
537
538impl From<TlsInspectionConfigurationView> for crate::types::TlsInspectionConfiguration {
539    fn from(v: TlsInspectionConfigurationView) -> Self {
540        crate::types::TlsInspectionConfiguration {
541            name: v.name,
542            arn: v.arn,
543            id: v.id,
544            description: v.description,
545            tags: v.tags,
546            body: v.body,
547        }
548    }
549}
550
551impl From<VpcEndpointAssociationView> for crate::types::VpcEndpointAssociation {
552    fn from(v: VpcEndpointAssociationView) -> Self {
553        crate::types::VpcEndpointAssociation {
554            vpc_endpoint_association_arn: v.vpc_endpoint_association_arn,
555            vpc_endpoint_association_id: v.vpc_endpoint_association_id,
556            firewall_arn: v.firewall_arn,
557            vpc_id: v.vpc_id,
558            subnet_id: v.subnet_id,
559            description: v.description,
560            tags: v.tags,
561        }
562    }
563}
564
565impl From<NetworkFirewallStateView> for NetworkFirewallState {
566    fn from(view: NetworkFirewallStateView) -> Self {
567        NetworkFirewallState {
568            firewalls: view
569                .firewalls
570                .into_iter()
571                .map(|(k, v)| (k, crate::types::Firewall::from(v)))
572                .collect(),
573            logging_configs: view
574                .logging_configs
575                .into_iter()
576                .map(|(k, v)| {
577                    (
578                        k,
579                        serde_json::from_str(&v).unwrap_or(serde_json::Value::Null),
580                    )
581                })
582                .collect(),
583            rule_groups: view
584                .rule_groups
585                .into_iter()
586                .map(|(k, v)| (k, crate::types::RuleGroup::from(v)))
587                .collect(),
588            firewall_policies: view
589                .firewall_policies
590                .into_iter()
591                .map(|(k, v)| (k, crate::types::FirewallPolicy::from(v)))
592                .collect(),
593            resource_policies: view
594                .resource_policies
595                .into_iter()
596                .map(|(k, v)| {
597                    (
598                        k,
599                        crate::types::ResourcePolicy {
600                            resource_arn: v.resource_arn,
601                            policy: v.policy,
602                        },
603                    )
604                })
605                .collect(),
606            tls_inspection_configurations: view
607                .tls_inspection_configurations
608                .into_iter()
609                .map(|(k, v)| (k, crate::types::TlsInspectionConfiguration::from(v)))
610                .collect(),
611            vpc_endpoint_associations: view
612                .vpc_endpoint_associations
613                .into_iter()
614                .map(|(k, v)| (k, crate::types::VpcEndpointAssociation::from(v)))
615                .collect(),
616            availability_zone_mappings: view
617                .availability_zone_mappings
618                .into_iter()
619                .map(|(k, v)| {
620                    (
621                        k,
622                        v.into_iter()
623                            .map(|m| crate::types::AvailabilityZoneMapping {
624                                availability_zone: m.availability_zone,
625                            })
626                            .collect(),
627                    )
628                })
629                .collect(),
630            transit_gateway_attachments: view
631                .transit_gateway_attachments
632                .into_iter()
633                .map(|(k, v)| {
634                    (
635                        k,
636                        crate::types::TransitGatewayAttachment {
637                            transit_gateway_attachment_id: v.transit_gateway_attachment_id,
638                            status: v.status,
639                        },
640                    )
641                })
642                .collect(),
643            proxies: view
644                .proxies
645                .into_iter()
646                .map(|(k, v)| {
647                    (
648                        k,
649                        crate::types::NfwProxy {
650                            proxy_name: v.proxy_name,
651                            proxy_arn: v.proxy_arn,
652                            nat_gateway_id: v.nat_gateway_id,
653                            proxy_configuration_arn: v.proxy_configuration_arn,
654                            proxy_configuration_name: v.proxy_configuration_name,
655                            proxy_state: v.proxy_state,
656                            tags: v.tags,
657                            body: v.body,
658                        },
659                    )
660                })
661                .collect(),
662            proxy_configurations: view
663                .proxy_configurations
664                .into_iter()
665                .map(|(k, v)| {
666                    (
667                        k,
668                        crate::types::NfwProxyConfiguration {
669                            proxy_configuration_name: v.proxy_configuration_name,
670                            proxy_configuration_arn: v.proxy_configuration_arn,
671                            description: v.description,
672                            tags: v.tags,
673                            body: v.body,
674                        },
675                    )
676                })
677                .collect(),
678            proxy_rule_groups: view
679                .proxy_rule_groups
680                .into_iter()
681                .map(|(k, v)| {
682                    (
683                        k,
684                        crate::types::NfwProxyRuleGroup {
685                            proxy_rule_group_name: v.proxy_rule_group_name,
686                            proxy_rule_group_arn: v.proxy_rule_group_arn,
687                            description: v.description,
688                            tags: v.tags,
689                            body: v.body,
690                        },
691                    )
692                })
693                .collect(),
694            flow_operations: view
695                .flow_operations
696                .into_iter()
697                .map(|(k, v)| {
698                    (
699                        k,
700                        crate::types::FlowOperation {
701                            flow_operation_id: v.flow_operation_id,
702                            firewall_arn: v.firewall_arn,
703                            flow_operation_type: v.flow_operation_type,
704                            flow_operation_status: v.flow_operation_status,
705                            body: v.body,
706                        },
707                    )
708                })
709                .collect(),
710            analysis_reports: view
711                .analysis_reports
712                .into_iter()
713                .map(|(k, v)| {
714                    (
715                        k,
716                        crate::types::AnalysisReport {
717                            analysis_report_id: v.analysis_report_id,
718                            firewall_arn: v.firewall_arn,
719                            analysis_type: v.analysis_type,
720                            status: v.status,
721                        },
722                    )
723                })
724                .collect(),
725            encryption_configs: view
726                .encryption_configs
727                .into_iter()
728                .map(|(k, v)| {
729                    (
730                        k,
731                        crate::types::EncryptionConfig {
732                            key_id: v.key_id,
733                            config_type: v.config_type,
734                        },
735                    )
736                })
737                .collect(),
738            analysis_settings: view.analysis_settings,
739        }
740    }
741}
742
743// --- StatefulService implementation ---
744
745impl StatefulService for NetworkFirewallService {
746    type StateView = NetworkFirewallStateView;
747
748    async fn snapshot(&self, account_id: &str, region: &str) -> Self::StateView {
749        let state = self.state.get(account_id, region);
750        let guard = state.read().await;
751        NetworkFirewallStateView::from(&*guard)
752    }
753
754    async fn restore(
755        &self,
756        account_id: &str,
757        region: &str,
758        view: Self::StateView,
759    ) -> Result<(), StateViewError> {
760        let state = self.state.get(account_id, region);
761        {
762            let mut guard = state.write().await;
763            *guard = NetworkFirewallState::from(view);
764        }
765        self.notify_state_changed(account_id, region).await;
766        Ok(())
767    }
768
769    async fn merge(
770        &self,
771        account_id: &str,
772        region: &str,
773        view: Self::StateView,
774    ) -> Result<(), StateViewError> {
775        let state = self.state.get(account_id, region);
776        {
777            let mut guard = state.write().await;
778            for (arn, fw_view) in view.firewalls {
779                guard
780                    .firewalls
781                    .insert(arn, crate::types::Firewall::from(fw_view));
782            }
783            for (arn, cfg_str) in view.logging_configs {
784                if let Ok(v) = serde_json::from_str(&cfg_str) {
785                    guard.logging_configs.insert(arn, v);
786                }
787            }
788            for (arn, rg_view) in view.rule_groups {
789                guard
790                    .rule_groups
791                    .insert(arn, crate::types::RuleGroup::from(rg_view));
792            }
793            for (arn, fp_view) in view.firewall_policies {
794                guard
795                    .firewall_policies
796                    .insert(arn, crate::types::FirewallPolicy::from(fp_view));
797            }
798            for (arn, rp_view) in view.resource_policies {
799                guard.resource_policies.insert(
800                    arn,
801                    crate::types::ResourcePolicy {
802                        resource_arn: rp_view.resource_arn,
803                        policy: rp_view.policy,
804                    },
805                );
806            }
807            for (arn, tls_view) in view.tls_inspection_configurations {
808                guard.tls_inspection_configurations.insert(
809                    arn,
810                    crate::types::TlsInspectionConfiguration::from(tls_view),
811                );
812            }
813            for (arn, assoc_view) in view.vpc_endpoint_associations {
814                guard
815                    .vpc_endpoint_associations
816                    .insert(arn, crate::types::VpcEndpointAssociation::from(assoc_view));
817            }
818            for (arn, az_views) in view.availability_zone_mappings {
819                guard.availability_zone_mappings.insert(
820                    arn,
821                    az_views
822                        .into_iter()
823                        .map(|m| crate::types::AvailabilityZoneMapping {
824                            availability_zone: m.availability_zone,
825                        })
826                        .collect(),
827                );
828            }
829            for (id, att_view) in view.transit_gateway_attachments {
830                guard.transit_gateway_attachments.insert(
831                    id,
832                    crate::types::TransitGatewayAttachment {
833                        transit_gateway_attachment_id: att_view.transit_gateway_attachment_id,
834                        status: att_view.status,
835                    },
836                );
837            }
838            for (arn, proxy_view) in view.proxies {
839                guard.proxies.insert(
840                    arn,
841                    crate::types::NfwProxy {
842                        proxy_name: proxy_view.proxy_name,
843                        proxy_arn: proxy_view.proxy_arn,
844                        nat_gateway_id: proxy_view.nat_gateway_id,
845                        proxy_configuration_arn: proxy_view.proxy_configuration_arn,
846                        proxy_configuration_name: proxy_view.proxy_configuration_name,
847                        proxy_state: proxy_view.proxy_state,
848                        tags: proxy_view.tags,
849                        body: proxy_view.body,
850                    },
851                );
852            }
853            for (arn, config_view) in view.proxy_configurations {
854                guard.proxy_configurations.insert(
855                    arn,
856                    crate::types::NfwProxyConfiguration {
857                        proxy_configuration_name: config_view.proxy_configuration_name,
858                        proxy_configuration_arn: config_view.proxy_configuration_arn,
859                        description: config_view.description,
860                        tags: config_view.tags,
861                        body: config_view.body,
862                    },
863                );
864            }
865            for (arn, group_view) in view.proxy_rule_groups {
866                guard.proxy_rule_groups.insert(
867                    arn,
868                    crate::types::NfwProxyRuleGroup {
869                        proxy_rule_group_name: group_view.proxy_rule_group_name,
870                        proxy_rule_group_arn: group_view.proxy_rule_group_arn,
871                        description: group_view.description,
872                        tags: group_view.tags,
873                        body: group_view.body,
874                    },
875                );
876            }
877            for (id, op_view) in view.flow_operations {
878                guard.flow_operations.insert(
879                    id,
880                    crate::types::FlowOperation {
881                        flow_operation_id: op_view.flow_operation_id,
882                        firewall_arn: op_view.firewall_arn,
883                        flow_operation_type: op_view.flow_operation_type,
884                        flow_operation_status: op_view.flow_operation_status,
885                        body: op_view.body,
886                    },
887                );
888            }
889            for (id, report_view) in view.analysis_reports {
890                guard.analysis_reports.insert(
891                    id,
892                    crate::types::AnalysisReport {
893                        analysis_report_id: report_view.analysis_report_id,
894                        firewall_arn: report_view.firewall_arn,
895                        analysis_type: report_view.analysis_type,
896                        status: report_view.status,
897                    },
898                );
899            }
900            for (arn, enc_view) in view.encryption_configs {
901                guard.encryption_configs.insert(
902                    arn,
903                    crate::types::EncryptionConfig {
904                        key_id: enc_view.key_id,
905                        config_type: enc_view.config_type,
906                    },
907                );
908            }
909            for (arn, types) in view.analysis_settings {
910                guard.analysis_settings.insert(arn, types);
911            }
912        }
913        self.notify_state_changed(account_id, region).await;
914        Ok(())
915    }
916
917    fn notifier(&self) -> &StateChangeNotifier<Self::StateView> {
918        &self.notifier
919    }
920}