1use yog_ids::code_set;
5use yog_ids::kind;
6
7use crate::project::rust_code::{is_ident, is_keyword};
8
9pub(crate) fn valid_name(name: &str) -> bool {
11 yog_data::typed::is_res_key(name)
12}
13
14pub(crate) fn name_problem(name: &str) -> Option<String> {
16 (!valid_name(name)).then(|| {
17 format!("{name:?} isn't a valid name — identifiers joined by `.`, like screen.title")
18 })
19}
20
21pub(crate) fn valid_set_name(name: &str) -> bool {
23 is_ident(name) && !is_keyword(name)
24}
25
26const LANGUAGE_LEN: std::ops::RangeInclusive<usize> = 2..=3;
28const REGION_LEN: std::ops::RangeInclusive<usize> = 2..=4;
29const LOCALE_SEP: char = '_';
31
32pub(crate) fn valid_locale(locale: &str) -> bool {
34 let Some((lang, region)) = locale.split_once(LOCALE_SEP) else {
35 return false;
36 };
37 LANGUAGE_LEN.contains(&lang.len())
38 && lang.bytes().all(|b| b.is_ascii_lowercase())
39 && REGION_LEN.contains(®ion.len())
40 && region
41 .bytes()
42 .all(|b| b.is_ascii_lowercase() || b.is_ascii_digit())
43}
44
45pub(crate) fn accessor_name(name: &str) -> String {
48 let mut out = String::new();
49 for (n, part) in name.split(yog_ids::sep::DOT).enumerate() {
50 if n > 0 {
51 out.push_str(yog_ids::sep::WORD);
52 }
53 let mut prev_lower = false;
54 for c in part.chars() {
55 if c.is_ascii_uppercase() && prev_lower {
56 out.push_str(yog_ids::sep::WORD);
57 }
58 prev_lower = c.is_ascii_lowercase() || c.is_ascii_digit();
59 out.push(c.to_ascii_lowercase());
60 }
61 }
62 out
63}
64
65const NAME_SETS: [(&str, &str); 3] = [
67 (code_set::ITEMS, kind::ITEM),
68 (code_set::BLOCKS, kind::BLOCK),
69 (code_set::ENTITIES, kind::ENTITY),
70];
71
72pub(crate) fn default_key(set: &str, mod_id: &str, name: &str) -> String {
75 match NAME_SETS.iter().find(|(s, _)| *s == set) {
76 Some((_, kind)) => yog_ids::lang_key(kind, &yog_ids::join(mod_id, name)),
77 None => [mod_id, name].join(yog_ids::sep::DOT),
78 }
79}