cdk-builder 0.1.0

Bindings to the AWS CDK
Documentation
use serde::Deserialize;
use serde::Serialize;
use serde_json::Value;
use std::borrow::Cow;
use std::cell::RefCell;
use std::ops::Deref;
use std::ops::DerefMut;
use std::process::Stdio;
use std::rc::Rc;
use std::sync::atomic::AtomicU64;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::{ChildStdin, ChildStdout, Command};
use tokio::task::JoinHandle;

pub mod ec2;

pub mod s3;

#[derive(Serialize)]
struct Request<'a> {
    js: &'a str,
}

#[derive(Deserialize)]
struct Response {
    json: Value,
}

struct AppInner {
    stdin: ChildStdin,
    stdout: ChildStdout,
    handle: JoinHandle<()>,
}

#[derive(Clone)]
pub struct App {
    inner: Rc<RefCell<AppInner>>,
    exprs: Rc<RefCell<Vec<String>>>,
}

impl App {
    pub async fn new() -> Self {
        let mut child = Command::new("node")
            .arg("worker.js")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .spawn()
            .expect("Failed to start child process");

        let stdin = child.stdin.take().unwrap();
        let stdout = child.stdout.take().unwrap();

        let handle = tokio::task::spawn(async move {
            child.wait().await.unwrap();
        });

        let me = App {
            inner: Rc::new(RefCell::new(AppInner {
                stdin,
                stdout,
                handle,
            })),
            exprs: Rc::default(),
        };

        me.request::<i32>(
            r#"
                app = new cdk.App();
                0
            "#,
        )
        .await;

        me
    }

    pub async fn stack<S: Stack>(&mut self, stack: S) {
        let mut layer = Layer {
            app: self.clone(),
            stack,
            exprs: Rc::default(),
            parent_exprs: self.exprs.clone(),
            expr: None,
        };
        S::run(&mut layer);
    }

    async fn request<T>(&self, js: &str) -> T
    where
        T: for<'de> Deserialize<'de>,
    {
        let mut me = self.inner.borrow_mut();

        let message = Request { js };
        let mut bytes = serde_json::to_vec(&message).unwrap();
        bytes.push(b'\n');
        me.stdin.write_all(&bytes).await.unwrap();

        let reader = BufReader::new(&mut me.stdout);
        let mut lines = reader.lines();

        let Ok(Some(line)) = lines.next_line().await else {
            todo!()
        };

        let res: Response = serde_json::from_str(&line).unwrap();
        serde_json::from_value(res.json).unwrap()
    }

    pub async fn run(&mut self) {
        let exprs = self.exprs.borrow();

        for expr in &*exprs {
            self.request::<Value>(expr).await;
        }
    }
}

impl Drop for App {
    fn drop(&mut self) {
        self.inner.borrow_mut().handle.abort();
    }
}

pub trait Stack: Sized {
    fn run(me: &mut Layer<Self>);

    fn name(&self) -> Cow<'static, str> {
        let type_name = std::any::type_name::<Self>();
        Cow::Borrowed(
            type_name
                .split('<')
                .next()
                .unwrap_or(type_name)
                .split("::")
                .last()
                .unwrap_or(type_name),
        )
    }

    fn setup(me: &mut Layer<Self>) {
        let _ = me;
    }

    fn initialize(me: &mut Layer<Self>) {
        let exprs = me.exprs.borrow().concat();
        let js = format!(
            r#"
                class RustStack extends cdk.Stack {{
                    constructor(scope, id, props) {{
                        super(scope, id, props);
                        {}
                    }}
                }}

                new RustStack(app, '{}', {{}});

                0
            "#,
            exprs,
            me.stack.name()
        );
        me.parent_exprs.borrow_mut().push(js)
    }

    fn stack<T: Stack>(self, layer: &Layer<T>) -> Layer<Self> {
        let mut layer = Layer {
            app: layer.app.clone(),
            stack: self,
            exprs: Rc::default(),
            parent_exprs: layer.exprs.clone(),
            expr: None,
        };
        Self::setup(&mut layer);
        layer
    }
}

pub struct Layer<T: Stack> {
    app: App,
    stack: T,
    exprs: Rc<RefCell<Vec<String>>>,
    expr: Option<String>,
    parent_exprs: Rc<RefCell<Vec<String>>>,
}

impl<T: Stack> Deref for Layer<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.stack
    }
}

impl<T: Stack> DerefMut for Layer<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.stack
    }
}

impl<T: Stack> Drop for Layer<T> {
    fn drop(&mut self) {
        T::initialize(self)
    }
}

static COUNT: AtomicU64 = AtomicU64::new(0);