theater_cli/commands/
create.rs1use anyhow::Result;
2use clap::Parser;
3use std::path::PathBuf;
4use tracing::debug;
5
6use crate::{error::CliError, output::formatters::ProjectCreated, templates, CommandContext};
7
8#[derive(Debug, Parser)]
9pub struct CreateArgs {
10 #[arg(required = true)]
12 pub name: String,
13
14 #[arg(short, long, default_value = "basic")]
16 pub template: String,
17
18 #[arg(short, long)]
20 pub output_dir: Option<PathBuf>,
21}
22
23pub async fn execute_async(args: &CreateArgs, ctx: &CommandContext) -> Result<(), CliError> {
25 debug!("Creating new actor project: {}", args.name);
26 debug!("Using template: {}", args.template);
27
28 return Err(CliError::not_implemented(
29 "create",
30 "Theater CLI \n\
31 The create command is not implemented yet \n\
32 Please create a new component with cargo component new <name> --lib \n\
33 Then, import theater:simple from wa.dev and add the necessary dependencies in your Cargo.toml \n\
34 I do apologize for this, working on documenting / implementing this command soon! \n\
35 In the meantime, please feel free to reach out to me at colinrozzi@gmail.com and I will absolutely walk you through how I create actors!"
36 ));
37
38 if !is_valid_project_name(&args.name) {
40 return Err(CliError::invalid_input(
41 "project_name",
42 &args.name,
43 "Project names must only contain alphanumeric characters, hyphens, and underscores",
44 ));
45 }
46
47 let output_dir = match &args.output_dir {
49 Some(dir) => dir.clone(),
50 None => std::env::current_dir()
51 .map_err(|e| CliError::file_operation_failed("get current directory", ".", e))?,
52 };
53
54 debug!("Output directory: {}", output_dir.display());
55
56 let templates_list = templates::available_templates();
58
59 if !templates_list.contains_key(&args.template) {
61 let available_templates: Vec<String> = templates_list.keys().cloned().collect();
62 return Err(CliError::template_not_found(
63 &args.template,
64 available_templates,
65 ));
66 }
67
68 let project_path = output_dir.join(&args.name);
70 templates::create_project(&args.template, &args.name, &output_dir).map_err(|e| {
71 CliError::file_operation_failed(
72 "create project",
73 project_path.display().to_string(),
74 std::io::Error::new(std::io::ErrorKind::Other, e),
75 )
76 })?;
77
78 let result = ProjectCreated {
80 name: args.name.clone(),
81 template: args.template.clone(),
82 path: project_path,
83 build_instructions: vec![
84 format!("cd {}", args.name),
85 "cargo build --target wasm32-unknown-unknown --release".to_string(),
86 "theater start manifest.toml".to_string(),
87 ],
88 };
89
90 ctx.output.output(&result, None)?;
91 Ok(())
92}
93
94fn is_valid_project_name(name: &str) -> bool {
95 !name.is_empty()
97 && name
98 .chars()
99 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
100}