use clap::{Parser, ValueEnum};
#[derive(Debug, Clone, ValueEnum)]
pub enum PackageManager {
Npm,
Bun,
}
impl PackageManager {
pub fn as_str(&self) -> &str {
match self {
PackageManager::Npm => "npm",
PackageManager::Bun => "bun",
}
}
}
#[derive(Parser, Debug)]
#[command(
name = "create-lamdera-app",
version,
about = "A CLI tool to scaffold Lamdera applications",
long_about = "Create Lamdera App - Scaffold a full-stack Lamdera application with Tailwind CSS, authentication, i18n, dark mode, and testing support."
)]
pub struct Args {
#[arg(short, long)]
pub name: Option<String>,
#[arg(long)]
pub github: Option<bool>,
#[arg(long, conflicts_with = "github")]
pub no_github: bool,
#[arg(long)]
pub public: bool,
#[arg(long)]
pub skip_install: bool,
#[arg(long, value_enum, default_value = "npm")]
pub package_manager: PackageManager,
#[arg(long)]
pub bun: bool,
#[arg(long)]
pub simple: bool,
#[arg(long)]
pub init: bool,
#[arg(long)]
pub install_precommit: bool,
}
impl Args {
pub fn get_package_manager(&self) -> PackageManager {
if self.bun {
PackageManager::Bun
} else {
self.package_manager.clone()
}
}
pub fn should_create_github(&self) -> Option<bool> {
if self.no_github {
Some(false)
} else {
self.github
}
}
pub fn validate(&self) -> Result<(), String> {
if let Some(ref name) = self.name {
validate_project_name(name)?;
}
Ok(())
}
}
pub fn validate_project_name(name: &str) -> Result<(), String> {
if name.is_empty() {
return Err("Project name cannot be empty".to_string());
}
if !name
.chars()
.all(|c| c.is_alphanumeric() || c == '-' || c == '_')
{
return Err(format!(
"Project name '{}' contains invalid characters. Use only letters, numbers, hyphens, and underscores.",
name
));
}
if name.chars().next().map_or(false, |c| c.is_numeric()) {
return Err(format!("Project name '{}' cannot start with a number", name));
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_project_names() {
assert!(validate_project_name("my-app").is_ok());
assert!(validate_project_name("my_app").is_ok());
assert!(validate_project_name("MyApp").is_ok());
assert!(validate_project_name("app123").is_ok());
}
#[test]
fn test_invalid_project_names() {
assert!(validate_project_name("").is_err());
assert!(validate_project_name("123app").is_err());
assert!(validate_project_name("my app").is_err());
assert!(validate_project_name("my@app").is_err());
}
}