game_manager_lib/utils/
text.rs1const EDITION_SUFFIXES: &[&str] = &[
6 "Collector's Edition",
7 "Collectors Edition",
8 "Complete Edition",
9 "Game of the Year Edition",
10 "Definitive Edition",
11 "Special Edition",
12 "Deluxe Edition",
13 "Premium Edition",
14 "Enhanced Edition",
15 "GOTY Edition",
16 "GOTY",
17 "Ultimate Edition",
18];
19
20pub fn strip_trademark_symbols(name: &str) -> String {
23 name.chars()
24 .filter(|c| !matches!(c, '™' | '®' | '©'))
25 .collect::<String>()
26 .split_whitespace()
27 .collect::<Vec<_>>()
28 .join(" ")
29}
30
31pub fn normalize_for_matching(name: &str) -> String {
34 strip_trademark_symbols(name)
35 .to_lowercase()
36 .replace(':', "")
37 .trim()
38 .to_string()
39}
40
41pub fn contains_word_boundary(haystack: &str, needle: &str) -> bool {
45 let h = haystack.as_bytes();
46 let n = needle.as_bytes();
47 if n.is_empty() || n.len() > h.len() {
48 return false;
49 }
50 for start in 0..=(h.len() - n.len()) {
51 if &h[start..start + n.len()] == n {
52 let before_ok = start == 0 || !(h[start - 1] as char).is_alphanumeric();
53 let end = start + n.len();
54 let after_ok = end == h.len() || !(h[end] as char).is_alphanumeric();
55 if before_ok && after_ok {
56 return true;
57 }
58 }
59 }
60 false
61}
62
63pub fn is_likely_non_base_game(name: &str) -> bool {
68 let lower = name.to_lowercase();
69 if name.trim().len() <= 2 {
70 return true;
71 }
72
73 let boundary_keywords = ["demo", "trial"];
75 if boundary_keywords
76 .iter()
77 .any(|kw| contains_word_boundary(&lower, kw))
78 {
79 return true;
80 }
81
82 let substring_keywords = [
84 "season pass",
85 "dlc",
86 "add-on",
87 "addon",
88 "expansion",
89 "pre-order",
90 "preorder",
91 "pre order",
92 "starter pack",
93 "pack",
94 "soundtrack",
95 "artbook",
96 "art book",
97 "playtest",
98 "goodie",
99 ];
100 substring_keywords.iter().any(|kw| lower.contains(kw))
101}
102
103pub fn strip_edition_suffix(name: &str) -> String {
106 let trimmed = name.trim();
107 for suffix in EDITION_SUFFIXES {
108 if let Some(rest) = trimmed.to_lowercase().strip_suffix(&suffix.to_lowercase()) {
109 return trimmed[..rest.len()]
110 .trim_end_matches(['-', ':', ',', ' '])
111 .trim()
112 .to_string();
113 }
114 }
115 trimmed.to_string()
116}