1use std::sync::Arc;
16
17use serde::{Deserialize, Serialize};
18use typed_builder::TypedBuilder;
19
20use crate::ir::{Address, Span};
21
22#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
28#[serde(rename_all = "snake_case")]
29#[non_exhaustive]
30pub enum EdgeKind {
31 ExplicitDependsOn,
33 AttrRef,
35 ModuleInput,
37 TerragruntDependency,
40}
41
42impl EdgeKind {
43 #[must_use]
46 pub const fn as_str(self) -> &'static str {
47 match self {
48 Self::ExplicitDependsOn => "explicit_depends_on",
49 Self::AttrRef => "attr_ref",
50 Self::ModuleInput => "module_input",
51 Self::TerragruntDependency => "terragrunt_dependency",
52 }
53 }
54}
55
56#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, TypedBuilder)]
61#[non_exhaustive]
62#[serde(rename_all = "camelCase")]
63#[builder(field_defaults(setter(into)))]
64pub struct Edge {
65 pub from: Address,
67 pub to: Address,
69 pub kind: EdgeKind,
71 #[builder(default)]
75 pub attr: Option<Arc<str>>,
76 pub span: Span,
78}
79
80#[cfg(test)]
81#[allow(clippy::unwrap_used)]
82mod tests {
83 use std::sync::Arc;
84
85 use super::*;
86 use crate::ir::Span;
87
88 #[test]
89 fn test_edge_kind_string_form_is_stable() {
90 assert_eq!(EdgeKind::ExplicitDependsOn.as_str(), "explicit_depends_on");
91 assert_eq!(EdgeKind::AttrRef.as_str(), "attr_ref");
92 assert_eq!(EdgeKind::ModuleInput.as_str(), "module_input");
93 assert_eq!(
94 EdgeKind::TerragruntDependency.as_str(),
95 "terragrunt_dependency"
96 );
97 }
98
99 #[test]
100 fn test_should_build_minimal_edge() {
101 let e = Edge::builder()
102 .from(Address::new("aws_iam_role.r").unwrap())
103 .to(Address::new("aws_iam_policy.p").unwrap())
104 .kind(EdgeKind::AttrRef)
105 .attr(Some(Arc::<str>::from("policy")))
106 .span(Span::synthetic())
107 .build();
108 assert_eq!(e.from.as_str(), "aws_iam_role.r");
109 assert_eq!(e.kind, EdgeKind::AttrRef);
110 }
111
112 #[test]
113 fn test_should_serde_round_trip_edge() {
114 let e = Edge::builder()
115 .from(Address::new("aws_iam_role.r").unwrap())
116 .to(Address::new("aws_iam_policy.p").unwrap())
117 .kind(EdgeKind::AttrRef)
118 .span(Span::synthetic())
119 .build();
120 let json = serde_json::to_string(&e).unwrap();
121 let back: Edge = serde_json::from_str(&json).unwrap();
122 assert_eq!(e, back);
123 }
124}