1use serde::Deserialize;
2use serde::Serialize;
3use serde_json::Value;
4use std::borrow::Cow;
5use std::cell::RefCell;
6use std::ops::Deref;
7use std::ops::DerefMut;
8use std::process::Stdio;
9use std::rc::Rc;
10use std::sync::atomic::AtomicU64;
11use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
12use tokio::process::{ChildStdin, ChildStdout, Command};
13use tokio::task::JoinHandle;
14
15pub mod ec2;
16
17pub mod s3;
18
19#[derive(Serialize)]
20struct Request<'a> {
21 js: &'a str,
22}
23
24#[derive(Deserialize)]
25struct Response {
26 json: Value,
27}
28
29struct AppInner {
30 stdin: ChildStdin,
31 stdout: ChildStdout,
32 handle: JoinHandle<()>,
33}
34
35#[derive(Clone)]
36pub struct App {
37 inner: Rc<RefCell<AppInner>>,
38 exprs: Rc<RefCell<Vec<String>>>,
39}
40
41impl App {
42 pub async fn new() -> Self {
43 let mut child = Command::new("node")
44 .arg("worker.js")
45 .stdin(Stdio::piped())
46 .stdout(Stdio::piped())
47 .spawn()
48 .expect("Failed to start child process");
49
50 let stdin = child.stdin.take().unwrap();
51 let stdout = child.stdout.take().unwrap();
52
53 let handle = tokio::task::spawn(async move {
54 child.wait().await.unwrap();
55 });
56
57 let me = App {
58 inner: Rc::new(RefCell::new(AppInner {
59 stdin,
60 stdout,
61 handle,
62 })),
63 exprs: Rc::default(),
64 };
65
66 me.request::<i32>(
67 r#"
68 app = new cdk.App();
69 0
70 "#,
71 )
72 .await;
73
74 me
75 }
76
77 pub async fn stack<S: Stack>(&mut self, stack: S) {
78 let mut layer = Layer {
79 app: self.clone(),
80 stack,
81 exprs: Rc::default(),
82 parent_exprs: self.exprs.clone(),
83 expr: None,
84 };
85 S::run(&mut layer);
86 }
87
88 async fn request<T>(&self, js: &str) -> T
89 where
90 T: for<'de> Deserialize<'de>,
91 {
92 let mut me = self.inner.borrow_mut();
93
94 let message = Request { js };
95 let mut bytes = serde_json::to_vec(&message).unwrap();
96 bytes.push(b'\n');
97 me.stdin.write_all(&bytes).await.unwrap();
98
99 let reader = BufReader::new(&mut me.stdout);
100 let mut lines = reader.lines();
101
102 let Ok(Some(line)) = lines.next_line().await else {
103 todo!()
104 };
105
106 let res: Response = serde_json::from_str(&line).unwrap();
107 serde_json::from_value(res.json).unwrap()
108 }
109
110 pub async fn run(&mut self) {
111 let exprs = self.exprs.borrow();
112
113 for expr in &*exprs {
114 self.request::<Value>(expr).await;
115 }
116 }
117}
118
119impl Drop for App {
120 fn drop(&mut self) {
121 self.inner.borrow_mut().handle.abort();
122 }
123}
124
125pub trait Stack: Sized {
126 fn run(me: &mut Layer<Self>);
127
128 fn name(&self) -> Cow<'static, str> {
129 let type_name = std::any::type_name::<Self>();
130 Cow::Borrowed(
131 type_name
132 .split('<')
133 .next()
134 .unwrap_or(type_name)
135 .split("::")
136 .last()
137 .unwrap_or(type_name),
138 )
139 }
140
141 fn setup(me: &mut Layer<Self>) {
142 let _ = me;
143 }
144
145 fn initialize(me: &mut Layer<Self>) {
146 let exprs = me.exprs.borrow().concat();
147 let js = format!(
148 r#"
149 class RustStack extends cdk.Stack {{
150 constructor(scope, id, props) {{
151 super(scope, id, props);
152 {}
153 }}
154 }}
155
156 new RustStack(app, '{}', {{}});
157
158 0
159 "#,
160 exprs,
161 me.stack.name()
162 );
163 me.parent_exprs.borrow_mut().push(js)
164 }
165
166 fn stack<T: Stack>(self, layer: &Layer<T>) -> Layer<Self> {
167 let mut layer = Layer {
168 app: layer.app.clone(),
169 stack: self,
170 exprs: Rc::default(),
171 parent_exprs: layer.exprs.clone(),
172 expr: None,
173 };
174 Self::setup(&mut layer);
175 layer
176 }
177}
178
179pub struct Layer<T: Stack> {
180 app: App,
181 stack: T,
182 exprs: Rc<RefCell<Vec<String>>>,
183 expr: Option<String>,
184 parent_exprs: Rc<RefCell<Vec<String>>>,
185}
186
187impl<T: Stack> Deref for Layer<T> {
188 type Target = T;
189
190 fn deref(&self) -> &Self::Target {
191 &self.stack
192 }
193}
194
195impl<T: Stack> DerefMut for Layer<T> {
196 fn deref_mut(&mut self) -> &mut Self::Target {
197 &mut self.stack
198 }
199}
200
201impl<T: Stack> Drop for Layer<T> {
202 fn drop(&mut self) {
203 T::initialize(self)
204 }
205}
206
207static COUNT: AtomicU64 = AtomicU64::new(0);