Skip to main content

ferro_cli/commands/
make_inertia.rs

1use console::style;
2use std::fs;
3use std::path::Path;
4
5use crate::templates;
6
7pub fn run(name: String) {
8    // Convert to page name (PascalCase with "Page" suffix)
9    let page_name = to_page_name(&name);
10
11    // Validate the resulting name
12    if !is_valid_component_name(&page_name) {
13        eprintln!(
14            "{} '{}' is not a valid page name",
15            style("Error:").red().bold(),
16            name
17        );
18        std::process::exit(1);
19    }
20
21    let pages_dir = Path::new("frontend/src/pages");
22    let page_file = pages_dir.join(format!("{page_name}.tsx"));
23
24    // Check if frontend/src/pages directory exists
25    if !pages_dir.exists() {
26        eprintln!(
27            "{} Pages directory not found at frontend/src/pages",
28            style("Error:").red().bold()
29        );
30        eprintln!(
31            "{}",
32            style("Make sure you're in a Ferro project root directory.").dim()
33        );
34        std::process::exit(1);
35    }
36
37    // Check if page file already exists
38    if page_file.exists() {
39        eprintln!(
40            "{} Page '{}' already exists at {}",
41            style("Info:").yellow().bold(),
42            page_name,
43            page_file.display()
44        );
45        std::process::exit(0);
46    }
47
48    // Generate page file content
49    let page_content = templates::inertia_page_template(&page_name);
50
51    // Write page file
52    if let Err(e) = fs::write(&page_file, page_content) {
53        eprintln!(
54            "{} Failed to write page file: {}",
55            style("Error:").red().bold(),
56            e
57        );
58        std::process::exit(1);
59    }
60    println!("{} Created {}", style("✓").green(), page_file.display());
61
62    println!();
63    println!(
64        "Page {} created successfully!",
65        style(&page_name).cyan().bold()
66    );
67    println!();
68    println!("Usage:");
69    println!("  {} Use the page in a controller:", style("1.").dim());
70    println!("     inertia_response!(\"{page_name}\", props)");
71    println!();
72}
73
74fn is_valid_component_name(name: &str) -> bool {
75    if name.is_empty() {
76        return false;
77    }
78
79    let mut chars = name.chars();
80
81    // First character must be uppercase letter
82    match chars.next() {
83        Some(c) if c.is_ascii_uppercase() => {}
84        _ => return false,
85    }
86
87    // Rest must be alphanumeric
88    chars.all(|c| c.is_alphanumeric())
89}
90
91fn to_pascal_case(s: &str) -> String {
92    let mut result = String::new();
93    let mut capitalize_next = true;
94
95    for c in s.chars() {
96        if c == '_' || c == '-' || c == ' ' {
97            capitalize_next = true;
98        } else if capitalize_next {
99            result.push(c.to_uppercase().next().unwrap());
100            capitalize_next = false;
101        } else {
102            result.push(c);
103        }
104    }
105    result
106}
107
108fn to_page_name(input: &str) -> String {
109    // Convert to PascalCase
110    let pascal = to_pascal_case(input);
111
112    // Append "Page" if not already present
113    if pascal.ends_with("Page") {
114        pascal
115    } else {
116        format!("{pascal}Page")
117    }
118}