Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - feat: generate SmartModules using SMDK #2630

651 changes: 640 additions & 11 deletions Cargo.lock

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions crates/smdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ dirs = "4.0.0"
thiserror = "1.0.20"
anyhow = { version = "1.0.38" }
cargo_metadata = "0.15.0"
cargo-generate = "0.16.0"
convert_case = "0.6.0"
include_dir = "0.7.2"
tempdir = "0.3.7"

fluvio = { path = "../fluvio", default-features = false }
fluvio-protocol = { path = "../fluvio-protocol", features=["record","api"] }
Expand Down
6 changes: 5 additions & 1 deletion crates/smdk/src/cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use clap::Parser;
use anyhow::Result;

use crate::build::BuildOpt;
use crate::generate::GenerateOpt;
use crate::test::TestOpt;
use crate::load::LoadOpt;

Expand All @@ -10,14 +11,17 @@ use crate::load::LoadOpt;
pub enum SmdkCommand {
/// Builds SmartModule into WASM
Build(BuildOpt),
/// Generates a new SmartModule Project
Generate(GenerateOpt),
Test(TestOpt),
Load(LoadOpt),
}

impl SmdkCommand {
pub(crate) fn process(self) -> Result<()> {
match self {
Self::Build(opt) => opt.process(),
SmdkCommand::Build(opt) => opt.process(),
SmdkCommand::Generate(opt) => opt.process(),
SmdkCommand::Test(opt) => opt.process(),
SmdkCommand::Load(opt) => opt.process(),
}
Expand Down
150 changes: 150 additions & 0 deletions crates/smdk/src/generate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use anyhow::{Error, Result};
use clap::Parser;
use cargo_generate::{GenerateArgs, TemplatePath, generate};
use include_dir::{Dir, include_dir};
use tempdir::TempDir;

static SMART_MODULE_TEMPLATE: Dir<'static> =
include_dir!("$CARGO_MANIFEST_DIR/../../smartmodule/cargo_template");

/// Generate new SmartModule project
#[derive(Debug, Parser)]
pub struct GenerateOpt {
/// SmartModule Project Name
name: String,
/// Template to generate project from.
///
/// Must be a GIT repository
#[clap(long)]
template: Option<String>,
}

/// Abstraction on different of template options available for generating a
/// new SmartModule project.
///
/// May hold a reference to a `TempDir` which should not be dropped before
/// accomplishing the project generation procedure.
struct Template {
template_path: TemplatePath,
_temp_dir: Option<TempDir>,
}

impl Template {
/// Extracts inlined directory contents into a temporary directory and
/// builds a `TemplatePath` instance with the `path` pointing to the temp
/// directory created.
///
/// Is important to hold the reference to the `_temp_dir` until generation
/// process is completed, otherwise the temp directory will be deleted
/// before reaching the generation process.
fn inline() -> Result<Self> {
sehz marked this conversation as resolved.
Show resolved Hide resolved
let temp_dir = TempDir::new("smartmodule_template")?;
let path = temp_dir.path().to_str().unwrap().to_string();
SMART_MODULE_TEMPLATE
.extract(&temp_dir)
.map_err(Error::from)?;
let template = Self {
template_path: TemplatePath {
git: None,
auto_path: None,
subfolder: None,
test: false,
branch: None,
tag: None,
path: Some(path),
favorite: None,
},
_temp_dir: Some(temp_dir),
};

Ok(template)
}

fn git(repo_uri: String) -> Result<Self> {
Ok(Self {
template_path: TemplatePath {
git: Some(repo_uri),
auto_path: None,
subfolder: None,
test: false,
branch: None,
tag: None,
path: None,
favorite: None,
},
_temp_dir: None,
})
}
}

impl GenerateOpt {
pub(crate) fn process(self) -> Result<()> {
println!("Generating new SmartModule project: {}", self.name);

let Template {
template_path,
_temp_dir,
} = if let Some(git_uri) = self.template {
Template::git(git_uri)?
} else {
Template::inline()?
};

let args = GenerateArgs {
template_path,
name: Some(self.name.clone()),
list_favorites: false,
force: false,
verbose: true,
template_values_file: None,
silent: false,
config: None,
vcs: None,
lib: false,
bin: false,
ssh_identity: None,
define: Vec::default(),
init: false,
destination: None,
force_git_init: false,
allow_commands: false,
overwrite: false,
other_args: None,
};

generate(args).map_err(Error::from)?;

Ok(())
}
}

#[cfg(test)]
mod test {
use std::fs::read_dir;

use super::Template;

#[test]
fn test_inline_template() {
let template = Template::inline().unwrap();

assert!(
template._temp_dir.is_some(),
"The temporary directory reference is not provided"
);

let temp_dir = template._temp_dir.unwrap();
let temp_dir = read_dir(temp_dir.path());
assert!(temp_dir.is_ok(), "The temporary directory doesn't exists");

let mut temp_dir = temp_dir.unwrap();
let smart_toml =
temp_dir.find(|entry| entry.as_ref().unwrap().file_name().eq("Smart.toml"));

assert!(
smart_toml.is_some(),
"Smart.toml from template is not included in temporary dir"
);
assert!(smart_toml.unwrap().is_ok());
}
}
1 change: 1 addition & 0 deletions crates/smdk/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
mod build;
mod cmd;
mod generate;
mod test;
mod load;
mod wasm;
Expand Down
9 changes: 7 additions & 2 deletions smartmodule/cargo_template/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ crate-type = ['cdylib']
fluvio-smartmodule = { {{smartmodule-version}} }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
{% if smartmodule-init %}
{% if smartmodule-type == "filter" %}
once_cell = "1.13.0"
{% endif %}
{% endif %}
sehz marked this conversation as resolved.
Show resolved Hide resolved


[profile.release]
[profile.release-lto]
inherits = "release"
lto = true
2 changes: 0 additions & 2 deletions smartmodule/cargo_template/Smart.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,5 @@ package_version = "0.1"
description = "{{project-description}}"
license = "Apache-2.0"



[cargo]
profile = "release"
1 change: 1 addition & 0 deletions smartmodule/cargo_template/cargo-generate.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type = "string"
prompt = "Which type of SmartModule would you like?"
choices = ["filter", "map", "filter-map", "array-map", "aggregate"]
default = "filter"

[placeholders.smartmodule-init]
type = "bool"
prompt = "Want to use SmartModule init?"
Expand Down
30 changes: 30 additions & 0 deletions smartmodule/cargo_template/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
{% if smartmodule-init %}
use fluvio_smartmodule::dataplane::smartmodule::{SmartModuleExtraParams, SmartModuleInitError};
{% if smartmodule-type == "filter" %}
use once_cell::sync::OnceCell;
use fluvio_smartmodule::eyre;
{% endif %}
{% endif %}
{% if smartmodule-type == "filter" %}
use fluvio_smartmodule::{smartmodule, Result, Record};

Expand Down Expand Up @@ -79,3 +86,26 @@ pub fn aggregate(accumulator: RecordData, current: &Record{% if smartmodule-para
#[derive(fluvio_smartmodule::SmartOpt, Default)]
pub struct SmartModuleOpt;
{% endif %}
{% if smartmodule-init %}
{% if smartmodule-type == "filter" %}
static CRITERIA: OnceCell<String> = OnceCell::new();

#[smartmodule(init)]
fn init(params: SmartModuleExtraParams) -> Result<()> {
// You can refer to the example SmartModules in Fluvio's GitHub Repository
// https://github.com/infinyon/fluvio/tree/master/smartmodule
if let Some(key) = params.get("key") {
CRITERIA.set(key.clone()).map_err(|err| eyre!("failed setting key: {:#?}", err))
} else {
Err(SmartModuleInitError::MissingParam("key".to_string()).into())
}
}
{% else %}
#[smartmodule(init)]
fn init(params: SmartModuleExtraParams) -> Result<()> {
// You can refer to the example SmartModules in Fluvio's GitHub Repository
// https://github.com/infinyon/fluvio/tree/master/smartmodule
todo!("Provide initialization logic for your SmartModule")
}
{% endif %}
{% endif %}