Skip to content

Commit 58b06dc

Browse files
committed
Now regenerating client bindings as well
1 parent f6f4bb4 commit 58b06dc

1 file changed

Lines changed: 106 additions & 8 deletions

File tree

  • crates/cli/src/subcommands

crates/cli/src/subcommands/dev.rs

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use crate::common_args;
21
use crate::config::Config;
2+
use crate::generate::Language;
33
use crate::subcommands::init;
44
use crate::util::{
55
add_auth_header_opt, database_identity, detect_module_language, get_auth_header, get_login_token_or_log_in,
66
spacetime_reverse_dns, ResponseExt,
77
};
8+
use crate::{common_args, generate};
89
use crate::{publish, tasks};
910
use anyhow::Context;
1011
use clap::{Arg, ArgAction, ArgMatches, Command};
@@ -29,7 +30,7 @@ use tokio::task::JoinHandle;
2930

3031
pub fn cli() -> Command {
3132
Command::new("dev")
32-
.about("Start development mode with auto-rebuild and publish")
33+
.about("Start development mode with auto-regenerate client module bindings, auto-rebuild, and auto-publish on file changes.")
3334
.arg(
3435
Arg::new("database")
3536
.long("database")
@@ -42,6 +43,29 @@ pub fn cli() -> Command {
4243
.default_value(".")
4344
.help("The path to the project directory"),
4445
)
46+
.arg(
47+
Arg::new("module-bindings-path")
48+
.long("module-bindings-path")
49+
.value_parser(clap::value_parser!(PathBuf))
50+
.default_value("src/module_bindings")
51+
.help("The path to the module bindings directory relative to the project directory, defaults to `<project-path>/src/module_bindings`"),
52+
)
53+
// NOTE: All server templates must have their server code in `spacetimedb/` directory
54+
// This is not a requirement in general, but is a requirement for all templates
55+
// i.e. `spacetime dev` is valid on non-templates.
56+
.arg(
57+
Arg::new("module-project-path")
58+
.long("module-project-path")
59+
.value_parser(clap::value_parser!(PathBuf))
60+
.default_value("spacetimedb")
61+
.help("The path to the SpacetimeDB server module project relative to the project directory, defaults to `<project-path>/spacetimedb`"),
62+
)
63+
.arg(
64+
Arg::new("client-lang")
65+
.long("client-lang")
66+
.value_parser(clap::value_parser!(Language))
67+
.help("The programming language for the generated client module bindings (e.g., typescript, csharp, python). If not specified, it will be detected from the project."),
68+
)
4569
.arg(
4670
Arg::new("local")
4771
.long("local")
@@ -65,7 +89,10 @@ struct DatabaseRow {
6589

6690
pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::Error> {
6791
let project_path = args.get_one::<PathBuf>("project-path").unwrap();
92+
let spacetimedb_project_path = args.get_one::<PathBuf>("module-project-path").unwrap();
93+
let module_bindings_path = args.get_one::<PathBuf>("module-bindings-path").unwrap();
6894
let use_local = args.get_flag("local");
95+
let client_language = args.get_one::<Language>("client-lang");
6996
let force = args.get_flag("force");
7097

7198
let server = if let Some(s) = args.get_one::<String>("server") {
@@ -76,7 +103,18 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
76103
Some("maincloud")
77104
};
78105

79-
let mut spacetimedb_dir = project_path.join("spacetimedb");
106+
if module_bindings_path.is_absolute() {
107+
anyhow::bail!("Module bindings path must be a relative path");
108+
}
109+
let mut module_bindings_dir = project_path.join(module_bindings_path);
110+
111+
if spacetimedb_project_path.is_absolute() {
112+
anyhow::bail!("SpacetimeDB project path must be a relative path");
113+
}
114+
let mut spacetimedb_dir = project_path.join(spacetimedb_project_path);
115+
116+
// Check if we are in a SpacetimeDB project directory
117+
80118
if !spacetimedb_dir.exists() || !spacetimedb_dir.is_dir() {
81119
println!("{}", "No SpacetimeDB project found in current directory.".yellow());
82120
let should_init = Confirm::new()
@@ -95,7 +133,8 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
95133
let canonical_created_path = created_project_path
96134
.canonicalize()
97135
.context("Failed to canonicalize created project path")?;
98-
spacetimedb_dir = canonical_created_path.join("spacetimedb");
136+
spacetimedb_dir = canonical_created_path.join(spacetimedb_project_path);
137+
module_bindings_dir = canonical_created_path.join(module_bindings_path);
99138

100139
if !spacetimedb_dir.exists() {
101140
anyhow::bail!("Project initialization did not create spacetimedb directory");
@@ -105,6 +144,21 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
105144
}
106145
}
107146

147+
if !module_bindings_dir.exists() {
148+
// Create the module bindings directory if it doesn't exist
149+
std::fs::create_dir_all(&module_bindings_dir).with_context(|| {
150+
format!(
151+
"Failed to create module bindings path {}",
152+
module_bindings_dir.display()
153+
)
154+
})?;
155+
} else if !module_bindings_dir.is_dir() {
156+
anyhow::bail!(
157+
"Module bindings path {} exists but is not a directory.",
158+
module_bindings_path.display()
159+
);
160+
}
161+
108162
let use_local = if use_local {
109163
true
110164
} else if config.spacetimedb_token().is_some() {
@@ -160,7 +214,16 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
160214
println!("{}", "Press Ctrl+C to stop".dimmed());
161215
println!();
162216

163-
build_and_publish(&config, &spacetimedb_dir, &database_name, server, use_local).await?;
217+
generate_build_and_publish(
218+
&config,
219+
&spacetimedb_dir,
220+
&module_bindings_dir,
221+
&database_name,
222+
client_language,
223+
server,
224+
use_local,
225+
)
226+
.await?;
164227

165228
let db_identity = database_identity(&config, &database_name, server).await?;
166229
let _log_handle = start_log_stream(config.clone(), db_identity.to_hex().to_string(), server).await?;
@@ -196,7 +259,17 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
196259
}
197260

198261
println!("\n{}", "File change detected, rebuilding...".yellow());
199-
match build_and_publish(&config, &spacetimedb_dir, &database_name, server, use_local).await {
262+
match generate_build_and_publish(
263+
&config,
264+
&spacetimedb_dir,
265+
&module_bindings_dir,
266+
&database_name,
267+
client_language,
268+
server,
269+
use_local,
270+
)
271+
.await
272+
{
200273
Ok(_) => {}
201274
Err(e) => {
202275
eprintln!("{} {}", "Error:".red().bold(), e);
@@ -207,20 +280,45 @@ pub async fn exec(mut config: Config, args: &ArgMatches) -> Result<(), anyhow::E
207280
}
208281
}
209282

210-
async fn build_and_publish(
283+
async fn generate_build_and_publish(
211284
config: &Config,
212285
project_path: &Path,
286+
module_bindings_path: &Path,
213287
database_name: &str,
288+
client_language: Option<&Language>,
214289
server: Option<&str>,
215290
_use_local: bool,
216291
) -> Result<(), anyhow::Error> {
217-
detect_module_language(project_path)?;
292+
let module_language = detect_module_language(project_path)?;
293+
let client_language = client_language.unwrap_or_else(|| match module_language {
294+
crate::util::ModuleLanguage::Rust => &Language::Rust,
295+
crate::util::ModuleLanguage::Csharp => &Language::Csharp,
296+
crate::util::ModuleLanguage::Javascript => &Language::TypeScript,
297+
});
298+
let client_language_str = match client_language {
299+
Language::Rust => "rust",
300+
Language::Csharp => "csharp",
301+
Language::TypeScript => "typescript",
302+
Language::UnrealCpp => "unrealcpp",
303+
};
218304

219305
println!("{}", "Building...".cyan());
220306
let (_path_to_program, _host_type) =
221307
tasks::build(project_path, Some(Path::new("src")), false).context("Failed to build project")?;
222308
println!("{}", "Build complete!".green());
223309

310+
println!("{}", "Generating module bindings...".cyan());
311+
let generate_args = generate::cli().get_matches_from(vec![
312+
"generate",
313+
"--lang",
314+
client_language_str,
315+
"--project-path",
316+
project_path.to_str().unwrap(),
317+
"--out-dir",
318+
module_bindings_path.to_str().unwrap(),
319+
]);
320+
generate::exec(config.clone(), &generate_args).await?;
321+
224322
println!("{}", "Publishing...".cyan());
225323

226324
let project_path_str = project_path.to_str().unwrap();

0 commit comments

Comments
 (0)