use eyre::{Context, bail};
use std::{
fs,
path::{Path, PathBuf},
};
const MAIN_RS: &str = include_str!("node/main-template.rs");
const TALKER_RS: &str = include_str!("talker/main-template.rs");
const LISTENER_RS: &str = include_str!("listener/main-template.rs");
const VERSION: &str = env!("CARGO_PKG_VERSION");
pub fn create(args: crate::CommandNew, use_path_deps: bool) -> eyre::Result<()> {
let crate::CommandNew {
kind,
lang: _,
name,
path,
} = args;
match kind {
crate::Kind::Node => create_custom_node(name, path, use_path_deps, MAIN_RS),
crate::Kind::Dataflow => create_dataflow(name, path, use_path_deps),
}
}
fn create_dataflow(
name: String,
path: Option<PathBuf>,
use_path_deps: bool,
) -> Result<(), eyre::ErrReport> {
const DATAFLOW_YML: &str = include_str!("dataflow-template.yml");
const WORKSPACE_CARGO_TOML: &str = include_str!("Cargo-template.toml");
if name.contains('/') {
bail!("dataflow name must not contain `/` separators");
}
if !name.is_ascii() {
bail!("dataflow name must be ASCII");
}
let root = path.as_deref().unwrap_or_else(|| Path::new(&name));
fs::create_dir(root)
.with_context(|| format!("failed to create directory `{}`", root.display()))?;
let dataflow_yml = DATAFLOW_YML.replace("___name___", &name);
let dataflow_yml_path = root.join("dataflow.yml");
fs::write(&dataflow_yml_path, dataflow_yml)
.with_context(|| format!("failed to write `{}`", dataflow_yml_path.display()))?;
let cargo_toml = WORKSPACE_CARGO_TOML.replace("___name___", &name);
let cargo_toml_path = root.join("Cargo.toml");
fs::write(&cargo_toml_path, cargo_toml)
.with_context(|| format!("failed to write `{}`", cargo_toml_path.display()))?;
create_custom_node(
"talker_1".into(),
Some(root.join("talker_1")),
use_path_deps,
TALKER_RS,
)?;
create_custom_node(
"talker_2".into(),
Some(root.join("talker_2")),
use_path_deps,
TALKER_RS,
)?;
create_custom_node(
"listener_1".into(),
Some(root.join("listener_1")),
use_path_deps,
LISTENER_RS,
)?;
println!(
"Created new Rust dataflow at `{name}` at {}",
Path::new(".").join(root).display()
);
Ok(())
}
fn create_custom_node(
name: String,
path: Option<PathBuf>,
use_path_deps: bool,
template_scripts: &str,
) -> Result<(), eyre::ErrReport> {
const CARGO_TOML: &str = include_str!("node/Cargo-template.toml");
if name.contains('/') {
bail!("node name must not contain `/` separators");
}
if !name.is_ascii() {
bail!("node name must be ASCII");
}
let root = path.as_deref().unwrap_or_else(|| Path::new(&name));
fs::create_dir(root)
.with_context(|| format!("failed to create directory `{}`", root.display()))?;
let src = root.join("src");
fs::create_dir(&src)
.with_context(|| format!("failed to create directory `{}`", src.display()))?;
let dep = if use_path_deps {
r#"dora-node-api = { path = "../../apis/rust/node" }"#.to_string()
} else {
format!(r#"dora-node-api = "{VERSION}""#)
};
let cargo_toml = CARGO_TOML
.replace("___name___", &name)
.replace("dora-node-api = {}", &dep);
let cargo_toml_path = root.join("Cargo.toml");
fs::write(&cargo_toml_path, cargo_toml)
.with_context(|| format!("failed to write `{}`", cargo_toml_path.display()))?;
let main_rs_path = src.join("main.rs");
fs::write(&main_rs_path, template_scripts)
.with_context(|| format!("failed to write `{}`", main_rs_path.display()))?;
println!(
"Created new Rust custom node `{name}` at {}",
Path::new(".").join(root).display()
);
Ok(())
}