Skip to main content

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 crate::utils::text::{contains_word_boundary, normalize_for_matching};
7use std::sync::OnceLock;
8
9/// Carrega e cacheia a lista de séries do JSON
10fn get_known_series() -> &'static Vec<String> {
11    static SERIES_CACHE: OnceLock<Vec<String>> = OnceLock::new();
12    SERIES_CACHE.get_or_init(|| {
13        let json_content = include_str!("../data/known_series.json");
14        let mut list: Vec<String> =
15            serde_json::from_str(json_content).expect("Erro ao carregar known_series.json");
16        // Ordena por tamanho (maior para menor) para prioridade correta
17        list.sort_by_key(|b| std::cmp::Reverse(b.len()));
18        list
19    })
20}
21
22/// Tenta identificar a série baseada no nome do jogo
23pub fn infer_series(game_name: &str) -> Option<String> {
24    let normalized_target = normalize_for_matching(game_name);
25    let known_list = get_known_series();
26
27    // 1. Busca na lista conhecida
28    for series in known_list {
29        let normalized_series = normalize_for_matching(&series);
30        if contains_word_boundary(&normalized_target, &normalized_series) {
31            return Some(series.clone());
32        }
33    }
34
35    // 2. Heurística básica (se não achou na lista)
36    // Ex: "Horizon Zero Dawn" -> Tenta pegar "Horizon" se houver separador
37    let splitters = [":", " -", " –"];
38    for pattern in splitters {
39        if let Some(pos) = game_name.find(pattern) {
40            let base = game_name[..pos].trim();
41            if base.len() >= 3 {
42                return Some(base.to_string());
43            }
44        }
45    }
46
47    None
48}