game_manager_lib\utils/
series.rs

1//! Módulo para inferência de séries de jogos
2//!
3//! Baseado no nome do jogo, tenta identificar a série a que ele pertence.
4//! Utiliza uma lista conhecida de séries e heurísticas simples para identificar a série correta.
5
6use std::sync::OnceLock;
7
8/// Carrega e cacheia a lista de séries do JSON
9fn get_known_series() -> &'static Vec<String> {
10    static SERIES_CACHE: OnceLock<Vec<String>> = OnceLock::new();
11    SERIES_CACHE.get_or_init(|| {
12        let json_content = include_str!("../data/known_series.json");
13        let mut list: Vec<String> =
14            serde_json::from_str(json_content).expect("Erro ao carregar known_series.json");
15        // Ordena por tamanho (maior para menor) para prioridade correta
16        list.sort_by_key(|b| std::cmp::Reverse(b.len()));
17        list
18    })
19}
20
21fn normalize_name(name: &str) -> String {
22    name.to_lowercase()
23        .replace(['™', '®', '©', ':'], "")
24        .trim()
25        .to_string()
26}
27
28/// Tenta identificar a série baseada no nome do jogo
29pub fn infer_series(game_name: &str) -> Option<String> {
30    let normalized_target = normalize_name(game_name);
31    let known_list = get_known_series();
32
33    // 1. Busca na lista conhecida
34    for series in known_list {
35        let normalized_series = normalize_name(series);
36        if normalized_target.contains(&normalized_series) {
37            return Some(series.clone());
38        }
39    }
40
41    // 2. Heurística básica (se não achou na lista)
42    // Ex: "Horizon Zero Dawn" -> Tenta pegar "Horizon" se houver separador
43    let splitters = [":", " -", " –"];
44    for pattern in splitters {
45        if let Some(pos) = game_name.find(pattern) {
46            let base = game_name[..pos].trim();
47            if base.len() >= 3 {
48                return Some(base.to_string());
49            }
50        }
51    }
52
53    None
54}