mod authoring;
mod module;
use clap::{Args, Parser, Subcommand, ValueEnum};
#[derive(Debug, Parser)]
#[command(
name = "lenso",
version,
about = "Author, validate, resolve, and run Lenso applications and Modules",
propagate_version = true
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Debug, Subcommand)]
enum Command {
Add(authoring::AddArgs),
Check(authoring::CheckArgs),
Resolve(authoring::ResolveArgs),
Run(authoring::RunArgs),
Compose {
#[command(subcommand)]
command: authoring::ComposeCommand,
},
App {
#[command(subcommand)]
command: authoring::AppCommand,
},
Module {
#[command(subcommand)]
command: ModuleCommand,
},
}
#[derive(Debug, Subcommand)]
enum ModuleCommand {
Create(ModuleCreateArgs),
Dev(ModuleDevArgs),
Check(ModuleCheckArgs),
Verify(ModuleVerifyArgs),
}
#[derive(Debug, Args, Clone)]
struct ModuleCreateArgs {
module_id: String,
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long, value_enum, default_value_t = ModuleRuntimeArg::Rust)]
runtime: ModuleRuntimeArg,
#[arg(long, value_enum, default_value_t = ModuleRecipeArg::Stateless)]
recipe: ModuleRecipeArg,
#[arg(long)]
dir: Option<std::path::PathBuf>,
#[arg(long)]
no_install: bool,
#[arg(long)]
capability: Option<String>,
#[arg(long)]
dry_run: bool,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum ModuleRuntimeArg {
Rust,
Bun,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, ValueEnum)]
enum ModuleRecipeArg {
Stateless,
Stateful,
WebConsole,
ManagedWork,
}
#[derive(Debug, Args, Clone)]
struct ModuleDevArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long, default_value = "lenso.json")]
project: std::path::PathBuf,
#[arg(long = "bun-bin", default_value = "bun")]
bun_bin: String,
}
#[derive(Debug, Args, Clone)]
struct ModuleCheckArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long, default_value = "lenso.json")]
project: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[derive(Debug, Args, Clone)]
struct ModuleVerifyArgs {
#[arg(long)]
repo_root: Option<std::path::PathBuf>,
#[arg(long, default_value = "lenso.json")]
project: std::path::PathBuf,
#[arg(long = "module")]
module_key: Option<String>,
#[arg(long, default_value = "lenso.module.verify.json")]
manifest: std::path::PathBuf,
#[arg(long, default_value = ".lenso/module-verification.json")]
output: std::path::PathBuf,
#[arg(long)]
json: bool,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let cli = Cli::parse();
match cli.command {
Command::Add(args) => authoring::add(&args)?,
Command::Check(args) => authoring::check(&args)?,
Command::Resolve(args) => authoring::resolve(&args)?,
Command::Run(args) => authoring::run(&args).await?,
Command::Compose { command } => authoring::compose(command).await?,
Command::App { command } => authoring::app(command)?,
Command::Module { command } => match command {
ModuleCommand::Create(args) => {
module::create_module(&module::ModuleCreateOptions {
capability: args.capability,
dir: args.dir,
dry_run: args.dry_run,
module_id: args.module_id,
no_install: args.no_install,
repo_root: args.repo_root,
recipe: match args.recipe {
ModuleRecipeArg::Stateless => module::ModuleRecipe::Stateless,
ModuleRecipeArg::Stateful => module::ModuleRecipe::Stateful,
ModuleRecipeArg::WebConsole => module::ModuleRecipe::WebConsole,
ModuleRecipeArg::ManagedWork => module::ModuleRecipe::ManagedWork,
},
runtime: match args.runtime {
ModuleRuntimeArg::Rust => module::ModuleRuntime::Rust,
ModuleRuntimeArg::Bun => module::ModuleRuntime::Bun,
},
})?;
}
ModuleCommand::Dev(args) => {
authoring::dev_module(args.repo_root.as_deref(), &args.project, &args.bun_bin)
.await?;
}
ModuleCommand::Check(args) => {
authoring::check_module(&authoring::ModuleCheckOptions {
json: args.json,
project: args.project,
repo_root: args.repo_root,
})?;
}
ModuleCommand::Verify(args) => {
authoring::verify_module(authoring::ModuleVerifyOptions {
json: args.json,
manifest: args.manifest,
module_key: args.module_key,
output: args.output,
project: args.project,
repo_root: args.repo_root,
})?;
}
},
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn command_tree_is_valid() {
Cli::command().debug_assert();
}
#[test]
fn removed_framework_commands_are_not_public() {
let command = Cli::command();
let names = command
.get_subcommands()
.map(clap::Command::get_name)
.collect::<Vec<_>>();
assert_eq!(
names,
["add", "check", "resolve", "run", "compose", "app", "module"]
);
let module = command
.get_subcommands()
.find(|subcommand| subcommand.get_name() == "module")
.expect("module command");
let module_names = module
.get_subcommands()
.map(clap::Command::get_name)
.collect::<Vec<_>>();
assert_eq!(module_names, ["create", "dev", "check", "verify"]);
}
}