Skip to content

Commit ae6d927

Browse files
Merge pull request #18 from rohas-dev/fix/rust-codegen
fix(rust): enhance Rust code generation with improved error handling, dynamic directory creation, and support for WebSocket handlers and middlewares
2 parents a9af5f3 + 1176d3f commit ae6d927

20 files changed

Lines changed: 906 additions & 50 deletions

File tree

crates/rohas-codegen/src/config.rs

Lines changed: 118 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::fs;
44
use std::path::{Path, PathBuf};
55

66
pub fn generate_package_json(_schema: &Schema, output_dir: &Path) -> Result<()> {
7-
let project_root = get_project_root(output_dir);
7+
let project_root = get_project_root(output_dir)?;
88
let project_name = extract_project_name(&project_root);
99

1010
let content = format!(
@@ -45,7 +45,7 @@ pub fn generate_package_json(_schema: &Schema, output_dir: &Path) -> Result<()>
4545
}
4646

4747
pub fn generate_tsconfig_json(_schema: &Schema, output_dir: &Path) -> Result<()> {
48-
let project_root = get_project_root(output_dir);
48+
let project_root = get_project_root(output_dir)?;
4949
let content = r#"{
5050
"compilerOptions": {
5151
"target": "ES2022",
@@ -88,7 +88,7 @@ pub fn generate_tsconfig_json(_schema: &Schema, output_dir: &Path) -> Result<()>
8888
}
8989

9090
pub fn generate_requirements_txt(_schema: &Schema, output_dir: &Path) -> Result<()> {
91-
let project_root = get_project_root(output_dir);
91+
let project_root = get_project_root(output_dir)?;
9292
let content = r#"# Python dependencies for Rohas project
9393
# Add your project-specific dependencies here
9494
@@ -102,7 +102,7 @@ typing-extensions>=4.0.0
102102
}
103103

104104
pub fn generate_pyproject_toml(_schema: &Schema, output_dir: &Path) -> Result<()> {
105-
let project_root = get_project_root(output_dir);
105+
let project_root = get_project_root(output_dir)?;
106106
let project_name = extract_project_name(&project_root);
107107

108108
let content = format!(
@@ -146,7 +146,7 @@ target-version = "py39"
146146
}
147147

148148
pub fn generate_cargo_toml(_schema: &Schema, output_dir: &Path) -> Result<()> {
149-
let project_root = get_project_root(output_dir);
149+
let project_root = get_project_root(output_dir)?;
150150
let project_name = extract_project_name(&project_root);
151151

152152
let lib_name = project_name.replace('-', "_");
@@ -183,7 +183,28 @@ tokio-test = "0.4"
183183
}
184184

185185
pub fn generate_gitignore(_schema: &Schema, output_dir: &Path) -> Result<()> {
186-
let project_root = get_project_root(output_dir);
186+
let project_root = get_project_root(output_dir)
187+
.map_err(|e| crate::error::CodegenError::GenerationFailed(format!(
188+
"Failed to get project root from output_dir {}: {}",
189+
output_dir.display(),
190+
e
191+
)))?;
192+
193+
let gitignore_path = project_root.join(".gitignore");
194+
195+
if let Some(parent) = gitignore_path.parent() {
196+
fs::create_dir_all(parent).map_err(|e| {
197+
crate::error::CodegenError::Io(std::io::Error::new(
198+
e.kind(),
199+
format!(
200+
"Failed to create parent directory {} for .gitignore: {}",
201+
parent.display(),
202+
e
203+
)
204+
))
205+
})?;
206+
}
207+
187208
let content = r#"# Dependencies
188209
node_modules/
189210
__pycache__/
@@ -238,12 +259,22 @@ coverage/
238259
src/generated/
239260
"#;
240261

241-
fs::write(project_root.join(".gitignore"), content)?;
262+
fs::write(&gitignore_path, content)
263+
.map_err(|e| crate::error::CodegenError::Io(std::io::Error::new(
264+
e.kind(),
265+
format!("Failed to write .gitignore to {}: {}", gitignore_path.display(), e)
266+
)))?;
242267
Ok(())
243268
}
244269

245270
pub fn generate_editorconfig(_schema: &Schema, output_dir: &Path) -> Result<()> {
246-
let project_root = get_project_root(output_dir);
271+
let project_root = get_project_root(output_dir)?;
272+
let editorconfig_path = project_root.join(".editorconfig");
273+
274+
if let Some(parent) = editorconfig_path.parent() {
275+
fs::create_dir_all(parent)?;
276+
}
277+
247278
let content = r#"# EditorConfig is awesome: https://EditorConfig.org
248279
249280
root = true
@@ -270,12 +301,21 @@ indent_size = 2
270301
trim_trailing_whitespace = false
271302
"#;
272303

273-
fs::write(project_root.join(".editorconfig"), content)?;
304+
fs::write(&editorconfig_path, content)
305+
.map_err(|e| crate::error::CodegenError::Io(std::io::Error::new(
306+
e.kind(),
307+
format!("Failed to write .editorconfig to {}: {}", editorconfig_path.display(), e)
308+
)))?;
274309
Ok(())
275310
}
276311

277312
pub fn generate_readme(schema: &Schema, output_dir: &Path) -> Result<()> {
278-
let project_root = get_project_root(output_dir);
313+
let project_root = get_project_root(output_dir)
314+
.map_err(|e| crate::error::CodegenError::GenerationFailed(format!(
315+
"Failed to get project root from output_dir {} in generate_readme: {}",
316+
output_dir.display(),
317+
e
318+
)))?;
279319
let project_name = extract_project_name(&project_root);
280320
let has_apis = !schema.apis.is_empty();
281321
let has_events = !schema.events.is_empty();
@@ -400,22 +440,31 @@ MIT
400440
);
401441

402442
let readme_path = project_root.join("README.md");
443+
444+
if let Some(parent) = readme_path.parent() {
445+
fs::create_dir_all(parent)?;
446+
}
447+
403448
if !readme_path.exists() {
404-
fs::write(readme_path, content)?;
449+
fs::write(&readme_path, content)
450+
.map_err(|e| crate::error::CodegenError::Io(std::io::Error::new(
451+
e.kind(),
452+
format!("Failed to write README.md to {}: {}", readme_path.display(), e)
453+
)))?;
405454
}
406455

407456
Ok(())
408457
}
409458

410459
pub fn generate_nvmrc(_schema: &Schema, output_dir: &Path) -> Result<()> {
411-
let project_root = get_project_root(output_dir);
460+
let project_root = get_project_root(output_dir)?;
412461
let content = "18.0.0\n";
413462
fs::write(project_root.join(".nvmrc"), content)?;
414463
Ok(())
415464
}
416465

417466
pub fn generate_prettierrc(_schema: &Schema, output_dir: &Path) -> Result<()> {
418-
let project_root = get_project_root(output_dir);
467+
let project_root = get_project_root(output_dir)?;
419468
let content = r#"{
420469
"semi": true,
421470
"trailingComma": "es5",
@@ -432,7 +481,7 @@ pub fn generate_prettierrc(_schema: &Schema, output_dir: &Path) -> Result<()> {
432481
}
433482

434483
pub fn generate_prettierignore(_schema: &Schema, output_dir: &Path) -> Result<()> {
435-
let project_root = get_project_root(output_dir);
484+
let project_root = get_project_root(output_dir)?;
436485
let content = r#"node_modules/
437486
dist/
438487
build/
@@ -447,7 +496,7 @@ src/generated/
447496
}
448497

449498
pub fn generate_rspack_config(_schema: &Schema, output_dir: &Path) -> Result<()> {
450-
let project_root = get_project_root(output_dir);
499+
let project_root = get_project_root(output_dir)?;
451500
let content = r#"const path = require('path');
452501
const fs = require('fs');
453502
@@ -548,12 +597,63 @@ module.exports = {
548597
Ok(())
549598
}
550599

551-
fn get_project_root(output_dir: &Path) -> PathBuf {
552-
if output_dir.file_name().and_then(|s| s.to_str()) == Some("src") {
553-
output_dir.parent().unwrap_or(output_dir).to_path_buf()
600+
fn get_project_root(output_dir: &Path) -> Result<PathBuf> {
601+
let project_root = if output_dir.file_name().and_then(|s| s.to_str()) == Some("src") {
602+
match output_dir.parent() {
603+
Some(parent) => {
604+
let parent_path = parent.to_path_buf();
605+
if parent_path.as_os_str().is_empty() || parent_path == Path::new("/") {
606+
output_dir.to_path_buf()
607+
} else {
608+
parent_path
609+
}
610+
}
611+
None => {
612+
return Err(crate::error::CodegenError::GenerationFailed(format!(
613+
"Cannot determine project root from output_dir: {}",
614+
output_dir.display()
615+
)));
616+
}
617+
}
554618
} else {
555619
output_dir.to_path_buf()
620+
};
621+
match fs::metadata(&project_root) {
622+
Ok(metadata) => {
623+
if !metadata.is_dir() {
624+
return Err(crate::error::CodegenError::GenerationFailed(format!(
625+
"Project root path exists but is not a directory: {}",
626+
project_root.display()
627+
)));
628+
}
629+
}
630+
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
631+
fs::create_dir_all(&project_root).map_err(|e| {
632+
crate::error::CodegenError::Io(std::io::Error::new(
633+
e.kind(),
634+
format!(
635+
"Failed to create project root directory {} (from output_dir {}): {}",
636+
project_root.display(),
637+
output_dir.display(),
638+
e
639+
)
640+
))
641+
})?;
642+
}
643+
Err(e) => {
644+
return Err(crate::error::CodegenError::Io(std::io::Error::new(
645+
e.kind(),
646+
format!(
647+
"Failed to check project root directory {} (from output_dir {}): {}",
648+
project_root.display(),
649+
output_dir.display(),
650+
e
651+
)
652+
)));
653+
}
556654
}
655+
656+
Ok(project_root)
557657
}
558658

559659
fn extract_project_name(project_root: &Path) -> String {

crates/rohas-codegen/src/generator.rs

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ impl Generator {
2121
output_dir.display()
2222
);
2323

24+
if let Some(parent) = output_dir.parent() {
25+
fs::create_dir_all(parent)?;
26+
}
27+
fs::create_dir_all(output_dir)?;
28+
2429
self.create_directory_structure(output_dir)?;
2530

2631
self.generate_common_configs(schema, output_dir)?;
@@ -64,10 +69,43 @@ impl Generator {
6469
}
6570

6671
fn generate_common_configs(&self, schema: &Schema, output_dir: &Path) -> Result<()> {
72+
use tracing::error;
6773
info!("Generating common configuration files");
68-
config::generate_gitignore(schema, output_dir)?;
69-
config::generate_editorconfig(schema, output_dir)?;
70-
config::generate_readme(schema, output_dir)?;
74+
info!("Output directory: {}", output_dir.display());
75+
76+
info!("Generating .gitignore...");
77+
config::generate_gitignore(schema, output_dir)
78+
.map_err(|e| {
79+
error!("Failed to generate .gitignore: {}", e);
80+
crate::error::CodegenError::GenerationFailed(format!(
81+
"Failed to generate .gitignore: {}",
82+
e
83+
))
84+
})?;
85+
info!("Generated .gitignore successfully");
86+
87+
info!("Generating .editorconfig...");
88+
config::generate_editorconfig(schema, output_dir)
89+
.map_err(|e| {
90+
error!("Failed to generate .editorconfig: {}", e);
91+
crate::error::CodegenError::GenerationFailed(format!(
92+
"Failed to generate .editorconfig: {}",
93+
e
94+
))
95+
})?;
96+
info!("Generated .editorconfig successfully");
97+
98+
info!("Generating README.md...");
99+
config::generate_readme(schema, output_dir)
100+
.map_err(|e| {
101+
error!("Failed to generate README.md: {}", e);
102+
crate::error::CodegenError::GenerationFailed(format!(
103+
"Failed to generate README.md: {}",
104+
e
105+
))
106+
})?;
107+
info!("Generated README.md successfully");
108+
71109
Ok(())
72110
}
73111

@@ -112,14 +150,33 @@ impl Generator {
112150
}
113151

114152
fn generate_rust(&self, schema: &Schema, output_dir: &Path) -> Result<()> {
153+
use tracing::error;
154+
155+
info!("Generating Rust code...");
156+
info!("Generating state...");
115157
rust::generate_state(output_dir)?;
158+
info!("Generating models...");
116159
rust::generate_models(schema, output_dir)?;
160+
info!("Generating DTOs...");
117161
rust::generate_dtos(schema, output_dir)?;
162+
info!("Generating APIs...");
118163
rust::generate_apis(schema, output_dir)?;
164+
info!("Generating events...");
119165
rust::generate_events(schema, output_dir)?;
166+
info!("Generating crons...");
120167
rust::generate_crons(schema, output_dir)?;
121-
rust::generate_websockets(schema, output_dir)?;
168+
info!("Generating websockets...");
169+
rust::generate_websockets(schema, output_dir)
170+
.map_err(|e| {
171+
error!("Failed to generate websockets: {}", e);
172+
crate::error::CodegenError::GenerationFailed(format!(
173+
"Failed to generate websockets: {}",
174+
e
175+
))
176+
})?;
177+
info!("Generating middlewares...");
122178
rust::generate_middlewares(schema, output_dir)?;
179+
info!("Generating lib.rs...");
123180
rust::generate_lib_rs(schema, output_dir)?;
124181

125182
info!("Generating Rust configuration files");

0 commit comments

Comments
 (0)