///|
/// Project configuration derived from user input
struct ProjectConfig {
project_name : String // e.g. "MyBouncingBall"
package_id : String // e.g. "mybouncingball"
display_name : String // e.g. "My Bouncing Ball"
theme_name : String // e.g. "Theme.MyBouncingBall"
module_name : String // e.g. "username/mybouncingball"
orientation : String // "landscape" or "portrait"
} derive(Show)
///|
/// Convert PascalCase name to lowercase package id
fn to_package_id(name : String) -> String {
let buf = StringBuilder::new()
for ch in name {
if ch >= 'A' && ch <= 'Z' {
buf.write_char((ch.to_int() + 32).unsafe_to_char())
} else {
buf.write_char(ch)
}
}
buf.to_string()
}
///|
/// Convert PascalCase to display name with spaces
fn to_display_name(name : String) -> String {
let buf = StringBuilder::new()
let chars = name.to_array()
for i, ch in chars {
if i > 0 && ch >= 'A' && ch <= 'Z' {
// Insert space before uppercase that follows lowercase
if chars[i - 1] >= 'a' && chars[i - 1] <= 'z' {
buf.write_char(' ')
}
}
buf.write_char(ch)
}
buf.to_string()
}
///|
/// Convert name to theme name
fn to_theme_name(name : String) -> String {
"Theme.\{name}"
}
///|
/// Validate that name is PascalCase (starts with uppercase, alphanumeric only)
fn is_valid_name(name : String) -> Bool {
if name.length() == 0 {
return false
}
let chars = name.to_array()
if chars[0] < 'A' || chars[0] > 'Z' {
return false
}
for ch in chars {
if not(
(ch >= 'A' && ch <= 'Z') ||
(ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= '9'),
) {
return false
}
}
true
}
///|
/// Derive all project names from the input name
fn derive_names(
name : String,
orientation : String,
module_owner : String,
) -> ProjectConfig {
let package_id = to_package_id(name)
{
project_name: name,
package_id,
display_name: to_display_name(name),
theme_name: to_theme_name(name),
module_name: "\{module_owner}/\{package_id}",
orientation,
}
}