Skip to content

Commit

Permalink
feat(mdk): overridable templates (#842)
Browse files Browse the repository at this point in the history
<!--
Pull requests are squashed and merged using:
- their title as the commit message
- their description as the commit body

Having a good title and description is important for the users to get
readable changelog.
-->

<!-- 1. Explain WHAT the change is about -->

Solve
[MET-630](https://linear.app/metatypedev/issue/MET-630/gen-add-parameter-to-replace-static-sections)
- [x] Make templates in the _static_ sections overridable
  - [x] `mdk_rust`
  - [x] `mdk_python`
  - [x] `mdk_typescript`
- [x] Add a CLI tool to generate extract the default template


<!-- 3. Explain HOW users should update their code -->

#### Migration notes

No changes needed.

---

- [x] The change comes with new or modified tests
- [ ] Hard-to-understand functions have explanatory comments
- [ ] End-user documentation is updated to reflect the change
  • Loading branch information
Natoandro authored Sep 16, 2024
1 parent 70a4d49 commit d35f5c3
Show file tree
Hide file tree
Showing 32 changed files with 1,644 additions and 683 deletions.
41 changes: 41 additions & 0 deletions src/meta-cli/src/cli/gen.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright Metatype OÜ, licensed under the Mozilla Public License Version 2.0.
// SPDX-License-Identifier: MPL-2.0

use std::borrow::Cow;

use super::serialize::SerializeReportExt;
use crate::cli::{Action, ConfigArgs, NodeArgs};
use crate::config::PathOption;
Expand All @@ -11,6 +13,7 @@ use crate::{config::Config, deploy::actors::console::ConsoleActor};
use actix::Actor;
use clap::Parser;
use common::typegraph::Typegraph;
use futures_concurrency::future::FutureGroup;
use metagen::*;

#[derive(Parser, Debug, Clone)]
Expand Down Expand Up @@ -104,6 +107,37 @@ struct MetagenCtx {
dir: PathBuf,
}

async fn load_mdk_template(
default: &[(&'static str, &'static str)],
template_dir: Option<&std::path::Path>,
) -> anyhow::Result<MdkTemplate> {
let mut group = FutureGroup::new();
for (file_name, content) in default.iter() {
// TODO absolute path?
let override_path: Option<PathBuf> = template_dir.map(Into::into);
group.insert(Box::pin(async move {
let content = if let Some(override_path) = override_path {
let path = override_path.join(file_name);
if tokio::fs::try_exists(&path).await? {
Cow::Owned(tokio::fs::read_to_string(path).await?)
} else {
Cow::Borrowed(*content)
}
} else {
Cow::Borrowed(*content)
};
anyhow::Ok((*file_name, content))
}));
}

let mut entries = HashMap::new();
while let Some(res) = group.next().await {
let (file_name, content) = res?;
entries.insert(file_name, content);
}
Ok(MdkTemplate { entries })
}

impl InputResolver for MetagenCtx {
#[tracing::instrument]
async fn resolve(&self, order: GeneratorInputOrder) -> Result<GeneratorInputResolved> {
Expand All @@ -127,6 +161,13 @@ impl InputResolver for MetagenCtx {
let raw = load_tg_at(config, path, name.as_deref()).await?;
GeneratorInputResolved::TypegraphFromTypegate { raw }
}
GeneratorInputOrder::LoadMdkTemplate {
default,
override_path,
} => {
let template = load_mdk_template(default, override_path.as_deref()).await?;
GeneratorInputResolved::MdkTemplate { template }
}
})
}
}
Expand Down
52 changes: 52 additions & 0 deletions src/meta-cli/src/cli/mdk_template.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use async_trait::async_trait;
use clap::{Parser, ValueEnum};

use crate::interlude::*;

use super::{Action, ConfigArgs};

#[derive(ValueEnum, Debug, Clone)]
enum Template {
Rust,
Python,
Typescript,
}

#[derive(Parser, Debug)]
pub struct CreateMdkTemplate {
/// Target directory
#[clap(long)]
dir: String,

/// Template to create
#[clap(long)]
template: Template,
}

#[async_trait]
impl Action for CreateMdkTemplate {
#[tracing::instrument]
async fn run(&self, args: ConfigArgs) -> Result<()> {
let dir = args.dir()?.join(&self.dir);
tracing::info!("creating mdk template at {:?}", dir);

tokio::fs::create_dir_all(&dir)
.await
.context("failed to create target directory")?;

let template = match self.template {
Template::Rust => metagen::MDK_RUST_DEFAULT_TEMPLATE,
Template::Python => metagen::MDK_PYTHON_DEFAULT_TEMPLATE,
Template::Typescript => metagen::MDK_TYPESCRIPT_DEFAULT_TEMPLATE,
};

for (file_name, content) in template.iter() {
let path = dir.join(file_name);
tokio::fs::write(&path, content)
.await
.context("failed to write the template into the file")?;
}

Ok(())
}
}
3 changes: 3 additions & 0 deletions src/meta-cli/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) mod dev;
pub(crate) mod doctor;
pub(crate) mod gen;
pub(crate) mod list;
pub(crate) mod mdk_template;
pub(crate) mod new;
pub(crate) mod serialize;
pub(crate) mod typegate;
Expand Down Expand Up @@ -87,6 +88,8 @@ pub(crate) enum Commands {
Doctor(doctor::Doctor),
/// Create a new Metatype project
New(new::New),
/// Dump the default mdk template
MdkTemplate(mdk_template::CreateMdkTemplate),
/// Access a minimal deno CLI
Typegate(typegate::Typegate),
}
Expand Down
2 changes: 1 addition & 1 deletion src/metagen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ common.workspace = true
# logging
log.workspace = true

# encodding
# encoding
serde.workspace = true
serde_json.workspace = true
heck.workspace = true
Expand Down
2 changes: 2 additions & 0 deletions src/metagen/src/client_py/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ impl crate::Plugin for Generator {
{
GeneratorInputResolved::TypegraphFromTypegate { raw } => raw,
GeneratorInputResolved::TypegraphFromPath { raw } => raw,
_ => bail!("unexpected input type"),
};
let mut out = HashMap::new();
out.insert(
Expand Down Expand Up @@ -319,6 +320,7 @@ fn e2e() -> anyhow::Result<()> {
typegraph_path: None,
// NOTE: root will map to the test's tempdir
path: "./".into(),
template_dir: None,
},
})?,
}]
Expand Down
2 changes: 2 additions & 0 deletions src/metagen/src/client_rs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ impl crate::Plugin for Generator {
{
GeneratorInputResolved::TypegraphFromTypegate { raw } => raw,
GeneratorInputResolved::TypegraphFromPath { raw } => raw,
_ => bail!("unexpected input type"),
};
let mut out = HashMap::new();
out.insert(
Expand Down Expand Up @@ -463,6 +464,7 @@ fn e2e() -> anyhow::Result<()> {
typegraph_path: None,
// NOTE: root will map to the test's tempdir
path: "./".into(),
template_dir: None,
},
})?,
}]
Expand Down
2 changes: 2 additions & 0 deletions src/metagen/src/client_ts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ impl crate::Plugin for Generator {
{
GeneratorInputResolved::TypegraphFromTypegate { raw } => raw,
GeneratorInputResolved::TypegraphFromPath { raw } => raw,
_ => bail!("unexpected input type"),
};
let mut out = HashMap::new();
out.insert(
Expand Down Expand Up @@ -326,6 +327,7 @@ fn e2e() -> anyhow::Result<()> {
typegraph_path: None,
// NOTE: root will map to the test's tempdir
path: "./".into(),
template_dir: None,
},
})?,
}]
Expand Down
3 changes: 3 additions & 0 deletions src/metagen/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub struct MdkGeneratorConfigBase {
pub typegraph_path: Option<PathBuf>,
#[garde(skip)]
pub path: PathBuf,
// TODO validation??
#[garde(skip)]
pub template_dir: Option<PathBuf>,
}

fn either_typegraph_name_or_path(config: &MdkGeneratorConfigBase) -> garde::Result {
Expand Down
25 changes: 20 additions & 5 deletions src/metagen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,31 @@ use crate::interlude::*;

pub use config::*;
use futures_concurrency::future::FutureGroup;
pub use shared::MdkTemplate;

pub use mdk_python::DEFAULT_TEMPLATE as MDK_PYTHON_DEFAULT_TEMPLATE;
pub use mdk_rust::DEFAULT_TEMPLATE as MDK_RUST_DEFAULT_TEMPLATE;
pub use mdk_typescript::DEFAULT_TEMPLATE as MDK_TYPESCRIPT_DEFAULT_TEMPLATE;

/// This implements a command object pattern API for generator
/// implemntations to access the external world. See [InputResolver].
/// implementations to access the external world. See [InputResolver].
///
/// The rationale being that
/// - Ease of mocking for tests through [InputResolver] implemntaiton.
/// - Ease of mocking for tests through [InputResolver] implementation.
/// - Ease of translating to wasm API for any future user implemented generators.
#[derive(Debug)]
pub enum GeneratorInputOrder {
TypegraphFromTypegate { name: String },
TypegraphFromPath { path: PathBuf, name: Option<String> },
TypegraphFromTypegate {
name: String,
},
TypegraphFromPath {
path: PathBuf,
name: Option<String>,
},
LoadMdkTemplate {
default: &'static [(&'static str, &'static str)],
override_path: Option<PathBuf>,
},
}

/// Response types for the command object API implemented
Expand All @@ -66,6 +80,7 @@ pub enum GeneratorInputOrder {
pub enum GeneratorInputResolved {
TypegraphFromTypegate { raw: Box<Typegraph> },
TypegraphFromPath { raw: Box<Typegraph> },
MdkTemplate { template: MdkTemplate },
}

/// This type plays the "dispatcher" role to the command object
Expand Down Expand Up @@ -109,7 +124,7 @@ type PluginOutputResult = Result<Box<dyn Plugin>, anyhow::Error>;

#[derive(Clone)]
struct GeneratorRunner {
pub op: fn(&Path, serde_json::Value) -> PluginOutputResult,
op: fn(&Path, serde_json::Value) -> PluginOutputResult,
}

impl GeneratorRunner {
Expand Down
Loading

0 comments on commit d35f5c3

Please sign in to comment.