Skip to content

Commit

Permalink
feat(cli,sdk): codegen command (#661)
Browse files Browse the repository at this point in the history
#### Motivation and context

Enable back `codegen` on current cli implementation.

#### Migration notes

None

### Checklist

- [x] The change come with new or modified tests
- [ ] Hard-to-understand functions have explanatory comments
- [ ] End-user documentation is updated to reflect the change
  • Loading branch information
michael-0acf4 authored Apr 9, 2024
1 parent 24a1993 commit 4dfeb55
Show file tree
Hide file tree
Showing 20 changed files with 347 additions and 119 deletions.
1 change: 1 addition & 0 deletions examples/deploy/deploy.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ def deploy_example_python(g: Graph):
dir=None,
prisma_migration=config_params,
disable_artifact_resolution=None,
codegen=None,
)


Expand Down
1 change: 1 addition & 0 deletions meta-cli/src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ impl Deploy {
auth: node.auth.clone(),
});
ServerStore::set_prefix(node_config.prefix);
ServerStore::set_codegen_flag(deploy.options.codegen);

Ok(Self {
config,
Expand Down
105 changes: 89 additions & 16 deletions meta-cli/src/cli/gen.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0

use std::collections::HashMap;
use std::{path::PathBuf, sync::Arc};

use super::{Action, ConfigArgs, NodeArgs};
use crate::cli::{Action, ConfigArgs, NodeArgs};
use crate::{
com::store::ServerStore,
config::Config,
Expand All @@ -13,36 +14,68 @@ use actix::Actor;
use actix_web::dev::ServerHandle;
use anyhow::{bail, Context, Result};
use async_trait::async_trait;
use clap::Parser;
use clap::{Parser, ValueEnum};
use common::typegraph::Typegraph;
use metagen::*;
use serde_json::json;

#[derive(ValueEnum, Debug, Clone)]
enum GeneratorOp {
/// missing module dependencies
Mod,
/// mdk materializer
Mdk,
}

#[derive(Parser, Debug)]
impl From<GeneratorOp> for String {
fn from(op: GeneratorOp) -> String {
match op {
GeneratorOp::Mod => "codegen",
GeneratorOp::Mdk => "mdk_rust",
}
.to_string()
}
}

#[derive(Parser, Debug, Clone)]
pub struct Gen {
#[command(flatten)]
node: NodeArgs,

#[clap(value_enum, default_value_t=GeneratorOp::Mod)]
generator: GeneratorOp,

/// Target typegate (cf config)
#[clap(short, long)]
pub target: Option<String>,

/// Metagen target to generate
#[clap(default_value = "main")]
gen_target: String,

/// Force load a typegraph file
#[clap(short, long)]
file: Option<PathBuf>,
}

#[async_trait]
impl Action for Gen {
async fn run(&self, args: ConfigArgs, server_handle: Option<ServerHandle>) -> Result<()> {
let dir = args.dir()?;
let config = Config::load_or_find(args.config, &dir)?;
let mut config = Config::load_or_find(args.config, &dir)?;

if let Some(file) = &self.file {
config.metagen = Some(dummy_gen_cfg(&self.generator, file));
}

let config = Arc::new(dbg!(config));
let Some(mgen_conf) = &config.metagen else {
anyhow::bail!(
"no metagen section defined in config found at {:?}",
config.path
);
};

let node_config = config.node(&self.node, self.target.as_deref().unwrap_or("dev"));
let node = node_config
.build(&dir)
Expand All @@ -55,18 +88,40 @@ impl Action for Gen {
typegate: Arc::new(node),
};

let files = metagen::generate_target(mgen_conf, &self.gen_target, resolver).await?;
let mut set = tokio::task::JoinSet::new();
for (path, file) in files {
set.spawn(async move {
tokio::fs::create_dir_all(path.parent().unwrap()).await?;
tokio::fs::write(path, file).await?;
Ok::<_, tokio::io::Error>(())
});
}
while let Some(res) = set.join_next().await {
res??;
}
match &self.generator {
GeneratorOp::Mod => {
let Some(file) = &self.file else {
anyhow::bail!("no file provided");
};

resolver
.resolve(GeneratorInputOrder::TypegraphFromPath {
path: file.to_owned(),
name: None,
})
.await?;

let responses = ServerStore::get_responses(file)
.context("invalid state, no response received")?;
for (_, res) in responses.iter() {
res.codegen()?
}
}
GeneratorOp::Mdk => {
let files = metagen::generate_target(mgen_conf, &self.gen_target, resolver).await?;
let mut set = tokio::task::JoinSet::new();
for (path, file) in files {
set.spawn(async move {
tokio::fs::create_dir_all(path.parent().unwrap()).await?;
tokio::fs::write(path, file).await?;
Ok::<_, tokio::io::Error>(())
});
}
while let Some(res) = set.join_next().await {
res??;
}
}
};

server_handle.unwrap().stop(true).await;

Expand Down Expand Up @@ -172,3 +227,21 @@ async fn load_tg_at(

Ok(tg)
}

fn dummy_gen_cfg(generator: &GeneratorOp, file: &PathBuf) -> metagen::Config {
let mut targets = HashMap::new();
targets.insert(
generator.clone().into(),
json!({
"typegraph_path": file,
"path": "./mats/gen",
"annotate_debug": false,
}),
);

let target = metagen::Target(targets);
let mut targets = HashMap::new();
targets.insert("main".to_string(), target);

metagen::Config { targets }
}
178 changes: 111 additions & 67 deletions meta-cli/src/codegen/deno.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub struct Codegen<'a> {
}

impl<'a> Codegen<'a> {
fn new<P>(tg: &'a Typegraph, path: P) -> Self
pub fn new<P>(tg: &'a Typegraph, path: P) -> Self
where
P: AsRef<Path>,
{
Expand Down Expand Up @@ -145,6 +145,18 @@ impl<'a> Codegen<'a> {
}
}

pub fn apply_codegen(self) -> Result<()> {
let modules = self.codegen()?;
for ModuleCode { path, code } in modules.iter() {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Error creating directory {}", parent.display()))?;
}
fs::write(path, code).with_context(|| format!("Error generating {}", path.display()))?
}
Ok(())
}

fn codegen(mut self) -> Result<Vec<ModuleCode>> {
let mut gen_list = vec![];

Expand Down Expand Up @@ -462,69 +474,101 @@ impl IntoJson for HashMap<String, Value> {
}
}

// #[cfg(test)]
// mod tests {
// use std::sync::Arc;

// use normpath::PathExt;
// use pathdiff::diff_paths;
// use tokio::sync::mpsc;

// use super::*;
// use crate::config::Config;
// use crate::deploy::actors::console::ConsoleActor;
// use crate::deploy::actors::loader::{LoadModule, LoaderActor, LoaderEvent};
// use crate::tests::utils::ensure_venv;
// use actix::prelude::*;

// #[actix::test(flavor = "multi_thread")]
// async fn codegen() -> Result<()> {
// crate::logger::init();
// ensure_venv()?;
// let test_folder = Path::new("./src/tests/typegraphs").normalize()?;
// std::env::set_current_dir(&test_folder)?;
// let tests = fs::read_dir(&test_folder).unwrap();
// let config = Config::default_in(".");
// let config = Arc::new(config);

// for typegraph_test in tests {
// let typegraph_test = typegraph_test.unwrap().path();
// let typegraph_test = diff_paths(&typegraph_test, &test_folder).unwrap();

// let console = ConsoleActor::new(Arc::clone(&config)).start();
// let (event_tx, event_rx) = mpsc::unbounded_channel();
// let loader = LoaderActor::new(Arc::clone(&config), console, event_tx, 1)
// .auto_stop()
// .start();
// loader.do_send(LoadModule(
// test_folder
// .join(&typegraph_test)
// .as_path()
// .to_owned()
// .into(),
// ));

// let mut event_rx = event_rx;
// // let tg = match event_rx.recv().await.unwrap() {
// // LoaderEvent::Typegraph(tg) => tg,
// // evt => bail!("unexpected loader evt: {evt:?}"),
// // };

// // TODO:
// // run typegraph! thenget serialized version

// // let module_codes = Codegen::new(&tg, &typegraph_test).codegen()?;
// // assert_eq!(module_codes.len(), 1);

// // let test_name = typegraph_test.to_string_lossy().to_string();
// // insta::assert_snapshot!(test_name, &module_codes[0].code);

// assert!(matches!(
// event_rx.recv().await,
// Some(LoaderEvent::Stopped(_))
// ));
// }

// Ok(())
// }
// }
#[cfg(test)]
mod tests {
use std::sync::Arc;

use futures::try_join;
use futures::FutureExt;
use normpath::PathExt;
use pathdiff::diff_paths;
use tokio::sync::mpsc;

use super::*;
use crate::com::server::init_server;
use crate::com::store::ServerStore;

use crate::com::store::Command;
use crate::config::Config;
use crate::deploy::actors::console::ConsoleActor;
use crate::deploy::actors::loader::{LoadModule, LoaderActor, LoaderEvent};
use crate::tests::utils::ensure_venv;
use actix::prelude::*;

#[actix::test(flavor = "multi_thread")]
async fn codegen() -> Result<()> {
crate::logger::init();
ensure_venv()?;
let test_folder = Path::new("./src/tests/typegraphs").normalize()?;
std::env::set_current_dir(&test_folder)?;
let tests = fs::read_dir(&test_folder).unwrap();
let config = Config::default_in(".");
let config = Arc::new(config);

for typegraph_test in tests {
let typegraph_test = typegraph_test.unwrap().path();
let typegraph_test = diff_paths(&typegraph_test, &test_folder).unwrap();

let console = ConsoleActor::new(Arc::clone(&config)).start();
let (event_tx, event_rx) = mpsc::unbounded_channel();
let loader = LoaderActor::new(Arc::clone(&config), console, event_tx, 1)
.auto_stop()
.start();

let server = init_server()?;
let server_handle = server.handle();

ServerStore::with(Some(Command::Serialize), Some(config.as_ref().clone()));
ServerStore::set_codegen_flag(true); // !

let test_scope = async {
loader.do_send(LoadModule(
test_folder
.join(&typegraph_test)
.as_path()
.to_owned()
.into(),
));

let mut event_rx = event_rx;
let tg = match event_rx.recv().await.unwrap() {
LoaderEvent::Typegraph(tg_infos) => {
let (_name, res) = tg_infos
.get_responses_or_fail()?
.as_ref()
.clone()
.into_iter()
.next()
.unwrap();
res.as_typegraph()? // !
}
evt => bail!("unexpected loader evt: {evt:?}"),
};
server_handle.stop(true).await;

let module_codes = Codegen::new(&tg, &typegraph_test).codegen()?;
assert_eq!(module_codes.len(), 1);

let test_name = typegraph_test.to_string_lossy().to_string();
insta::assert_snapshot!(test_name, &module_codes[0].code);

assert!(matches!(
event_rx.recv().await,
Some(LoaderEvent::Stopped(_))
));

Ok(())
};

match try_join!(server.map(|_| Ok(())), test_scope) {
Ok(((), ())) => {}
Err(e) => {
server_handle.stop(true).await;
panic!("{}", e)
}
};
}

Ok(())
}
}
Loading

0 comments on commit 4dfeb55

Please sign in to comment.