1use std::borrow::{Borrow, BorrowMut};
2use std::cell::RefCell;
3use std::collections::HashMap;
4use std::rc::Rc;
5
6use tf_bindgen_schema::document::{Meta, Metadata, ProviderConfig, Terraform};
7use tf_bindgen_schema::Document;
8
9use crate::{L1Construct, Path, Provider, Scope};
10
11#[derive(Clone)]
16pub struct Stack {
17 inner: Rc<InnerStack>,
18}
19
20struct InnerStack {
21 name: String,
22 provider: RefCell<Vec<Rc<dyn Provider>>>,
23 resources: RefCell<Vec<Rc<dyn L1Construct>>>,
24 data_sources: RefCell<Vec<Rc<dyn L1Construct>>>,
25}
26
27impl Stack {
28 pub fn new(name: impl Into<String>) -> Rc<Self> {
29 Rc::new(Self {
30 inner: Rc::new(InnerStack {
31 name: name.into(),
32 provider: RefCell::new(Vec::new()),
33 resources: RefCell::new(Vec::new()),
34 data_sources: RefCell::new(Vec::new()),
35 }),
36 })
37 }
38
39 pub fn name(&self) -> &str {
41 &self.inner.name
42 }
43
44 pub fn add_provider(&self, provider: Rc<dyn Provider>) {
46 self.inner.provider.borrow_mut().push(provider)
47 }
48
49 pub fn add_resource(&self, resource: Rc<dyn L1Construct>) {
51 self.inner.resources.borrow_mut().push(resource)
52 }
53
54 pub fn add_data_source(&self, data_source: Rc<dyn L1Construct>) {
56 self.inner.data_sources.borrow_mut().push(data_source)
57 }
58
59 pub fn to_document(&self) -> Document {
61 let mut document = Document {
62 meta: Meta {
63 metadata: Metadata {
64 backend: "local".to_string(),
65 stack_name: self.name().to_string(),
66 version: tf_bindgen_schema::VERSION.to_string(),
67 },
68 outputs: HashMap::default(),
69 },
70 terraform: Terraform {
71 required_providers: HashMap::default(),
72 },
73 provider: HashMap::default(),
74 resource: HashMap::default(),
75 data: HashMap::default(),
76 };
77 for provider in self.inner.provider.borrow().iter() {
78 let path = provider.path();
79 let name = path.name();
80 let local_name = name.split('/').last().unwrap();
81 let (version, schema) = provider.to_schema();
82 let config = ProviderConfig {
83 source: name.to_string(),
84 version,
85 };
86 document
87 .terraform
88 .required_providers
89 .insert(local_name.to_string(), config);
90 if !document.provider.borrow().contains_key(local_name) {
91 document
92 .provider
93 .borrow_mut()
94 .insert(local_name.to_string(), Vec::new());
95 }
96 document
97 .provider
98 .borrow_mut()
99 .get_mut(local_name)
100 .unwrap()
101 .push(schema);
102 }
103 for resource in self.inner.resources.borrow().iter() {
104 let path = resource.path();
105 let key = path.id();
106 let (ty, schema) = resource.to_schema();
107 if !document.resource.contains_key(&ty) {
108 document.resource.insert(ty.clone(), HashMap::new());
109 }
110 if document
111 .resource
112 .get_mut(&ty)
113 .unwrap()
114 .insert(key, schema)
115 .is_some()
116 {
117 panic!("resource '{path}' already exists");
118 }
119 }
120 for data_source in self.inner.data_sources.borrow().iter() {
121 let path = data_source.path();
122 let key = path.id();
123 let (ty, schema) = data_source.to_schema();
124 if !document.data.contains_key(&ty) {
125 document.data.insert(ty.clone(), HashMap::new());
126 }
127 if document
128 .data
129 .get_mut(&ty)
130 .unwrap()
131 .insert(key, schema)
132 .is_some()
133 {
134 panic!("data source '{path}' already exists");
135 }
136 }
137 document
138 }
139}
140
141impl Scope for Stack {
142 fn stack(&self) -> Stack {
144 self.clone()
145 }
146
147 fn path(&self) -> crate::Path {
148 Path::from(self.name().to_string())
149 }
150}