diff --git a/.changes/android-kotlin-keyword-as-ident.md b/.changes/android-kotlin-keyword-as-ident.md new file mode 100644 index 00000000..1ebcf519 --- /dev/null +++ b/.changes/android-kotlin-keyword-as-ident.md @@ -0,0 +1,5 @@ +--- +"cargo-mobile2": "patch" +--- + +On Android, allows using Kotlin keywords as identifiers and escape them in templates. diff --git a/src/config/app/identifier.rs b/src/config/app/identifier.rs index f6f2abad..f02e3ba0 100644 --- a/src/config/app/identifier.rs +++ b/src/config/app/identifier.rs @@ -3,9 +3,9 @@ use std::error::Error; use std::fmt; static RESERVED_PACKAGE_NAMES: [&str; 2] = ["kotlin", "java"]; -static RESERVED_KEYWORDS: [&str; 63] = [ +// https://docs.oracle.com/javase/tutorial/java/nutsandbolts/_keywords.html +static RESERVED_JAVA_KEYWORDS: [&str; 53] = [ "abstract", - "as", "assert", "boolean", "break", @@ -27,21 +27,17 @@ static RESERVED_KEYWORDS: [&str; 63] = [ "finally", "float", "for", - "fun", "goto", "if", "implements", "import", "instanceof", - "in", "int", "interface", - "is", "long", "native", "new", "null", - "object", "package", "private", "protected", @@ -59,13 +55,8 @@ static RESERVED_KEYWORDS: [&str; 63] = [ "transient", "true", "try", - "typealias", - "typeof", - "val", - "var", "void", "volatile", - "when", "while", ]; @@ -129,7 +120,7 @@ pub fn check_identifier_syntax(identifier_name: &str) -> Result<(), IdentifierEr if label.is_empty() { return Err(IdentifierError::EmptyLabel); } - if RESERVED_KEYWORDS.contains(&label) { + if RESERVED_JAVA_KEYWORDS.contains(&label) { return Err(IdentifierError::ReservedKeyword { keyword: label.to_owned(), }); diff --git a/src/config/app/mod.rs b/src/config/app/mod.rs index c7eec654..25564abf 100644 --- a/src/config/app/mod.rs +++ b/src/config/app/mod.rs @@ -222,6 +222,20 @@ impl App { .join(".") } + pub fn android_identifier_escape_kotlin_keyword(&self) -> String { + self.reverse_identifier() + .split('.') + .map(|s| { + if crate::reserved_names::KOTLIN_ONLY_KEYWORDS.contains(&s) { + format!("`{}`", s) + } else { + s.to_string() + } + }) + .collect::>() + .join(".") + } + pub fn manifest_path(&self) -> PathBuf { self.root_dir().join("Cargo.toml") } diff --git a/src/lib.rs b/src/lib.rs index d87b6ac5..7e1a5851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ pub mod init; pub mod opts; pub mod os; mod project; -mod reserved_names; +pub mod reserved_names; pub mod target; mod templating; pub mod update; diff --git a/src/reserved_names/mod.rs b/src/reserved_names/mod.rs index c7d5d67c..de63ed39 100644 --- a/src/reserved_names/mod.rs +++ b/src/reserved_names/mod.rs @@ -19,6 +19,19 @@ pub static WINDOWS: &[&str] = &[ pub static ARTIFACTS: &[&str] = &["deps", "examples", "build", "incremental"]; +pub static KOTLIN_ONLY_KEYWORDS: &[&str] = &[ + "as", + "fun", + "in", + "is", + "object", + "typealias", + "typeof", + "val", + "var", + "when", +]; + pub fn in_keywords(s: impl AsRef) -> bool { KEYWORDS.contains(&s.as_ref()) } diff --git a/src/templating/init.rs b/src/templating/init.rs index b903b29f..fe7eff7c 100644 --- a/src/templating/init.rs +++ b/src/templating/init.rs @@ -6,6 +6,7 @@ use crate::{ Bicycle, EscapeFn, HelperDef, JsonMap, }, config::{app, Config}, + reserved_names::KOTLIN_ONLY_KEYWORDS, util::{self, Git}, }; use std::collections::HashMap; @@ -122,6 +123,28 @@ fn reverse_domain_snake_case( .map_err(Into::into) } +fn escape_kotlin_keyword( + helper: &Helper, + _: &Handlebars, + _: &Context, + _: &mut RenderContext, + out: &mut dyn Output, +) -> HelperResult { + let escaped_result = get_str(helper) + .split('.') + .map(|s| { + if KOTLIN_ONLY_KEYWORDS.contains(&s) { + format!("`{}`", s) + } else { + s.to_string() + } + }) + .collect::>() + .join("."); + + out.write(&escaped_result).map_err(Into::into) +} + fn app_root(ctx: &Context) -> Result<&str, RenderErrorReason> { let app_root = ctx .data() @@ -219,6 +242,7 @@ pub fn init(config: Option<&Config>) -> Bicycle { "reverse-domain-snake-case", Box::new(reverse_domain_snake_case), ); + helpers.insert("escape-kotlin-keyword", Box::new(escape_kotlin_keyword)); helpers.insert("dot-to-slash", Box::new(dot_to_slash)); if config.is_some() { // don't mix these up or very bad things will happen to all of us diff --git a/templates/apps/wry/gen/android/app/src/main/kotlin/{{dot-to-slash (reverse-domain app.domain)}}/{{snake-case app.name}}/MainActivity.kt.hbs b/templates/apps/wry/gen/android/app/src/main/kotlin/{{dot-to-slash (reverse-domain app.domain)}}/{{snake-case app.name}}/MainActivity.kt.hbs index b8138b9e..b202442e 100644 --- a/templates/apps/wry/gen/android/app/src/main/kotlin/{{dot-to-slash (reverse-domain app.domain)}}/{{snake-case app.name}}/MainActivity.kt.hbs +++ b/templates/apps/wry/gen/android/app/src/main/kotlin/{{dot-to-slash (reverse-domain app.domain)}}/{{snake-case app.name}}/MainActivity.kt.hbs @@ -1,3 +1,3 @@ -package {{reverse-domain app.identifier}} +package {{escape-kotlin-keyword (reverse-domain app.identifier)}} class MainActivity : WryActivity()