gioser
⎇ 90e5bd3c7307ac5cce67133f523687b9b1eff48c crates/ente-brain/src/loader.rs Rust · 71 siq'ikuna · 2.9 KB Ñawpa willay Pi imata llamkarqan ch'uya

prueba 90e5bd3c7 · Sergio ·

1//! Loader de Cards y Reglas desde archivos JSON.
2//!
3//! Sustituye al antiguo `kcl_loader.rs` (eliminado): la rama KCL invocaba
4//! un subprocess al CLI Go `kcl` que ningún target real tenía instalado y
5//! cuya validación duplicaba `EntityCard::validate()`. La fuente de verdad
6//! del shape de la Card es Rust + serde; en disco se guarda JSON crudo.
7//!
8//! Ergonomía de autoría futura (RON, Dhall, etc.) se añade como ramas
9//! adicionales aquí cuando duela escribir JSON a mano. Hoy: una sola rama.
10
11use crate::rules::Rule;
12use ente_card::EntityCard;
13use std::path::Path;
14use tracing::info;
15
16/// Carga una `EntityCard` desde un archivo JSON. Pasa por
17/// `EntityCard::validate()` antes de devolver — falla rápida.
18pub fn load_card_file(path: &Path) -> anyhow::Result<EntityCard> {
19    info!(path = %path.display(), "cargando Card desde JSON");
20    let raw = std::fs::read_to_string(path)?;
21    let card = extract_card_from_json(&raw)?;
22    card.validate()
23        .map_err(|e| anyhow::anyhow!("Card inválida ({}): {e}", path.display()))?;
24    Ok(card)
25}
26
27/// Extrae una `EntityCard` de JSON. Acepta:
28/// 1. Object directamente serializable como EntityCard.
29/// 2. Object dict con un único valor que sea EntityCard (compat con
30///    salidas de generadores que envuelven en `{"seed": {...}}`).
31pub fn extract_card_from_json(raw: &str) -> anyhow::Result<EntityCard> {
32    let v: serde_json::Value = serde_json::from_str(raw)?;
33    let direct_err = match serde_json::from_value::<EntityCard>(v.clone()) {
34        Ok(c) => return Ok(c),
35        Err(e) => e,
36    };
37    if let serde_json::Value::Object(map) = v {
38        for (_, vv) in map {
39            if let Ok(c) = serde_json::from_value::<EntityCard>(vv) {
40                return Ok(c);
41            }
42        }
43    }
44    // Propagamos el error del intento directo: es el caso típico (JSON top-level
45    // = EntityCard) y su mensaje apunta al campo concreto que rompió.
46    anyhow::bail!("JSON no contiene una EntityCard válida: {direct_err}")
47}
48
49/// Carga reglas desde un archivo JSON.
50pub fn load_rules_file(path: &Path) -> anyhow::Result<Vec<Rule>> {
51    info!(path = %path.display(), "cargando reglas desde JSON");
52    let raw = std::fs::read_to_string(path)?;
53    extract_rules_from_json(&raw)
54}
55
56/// Extrae un `Vec<Rule>` de JSON que puede ser:
57/// 1. Array directo: `[{...}, {...}]`
58/// 2. Object con un campo array: `{"rules": [...]}`
59pub fn extract_rules_from_json(raw: &str) -> anyhow::Result<Vec<Rule>> {
60    let v: serde_json::Value = serde_json::from_str(raw)?;
61    let arr = match v {
62        serde_json::Value::Array(_) => v,
63        serde_json::Value::Object(map) => map
64            .into_values()
65            .find(|x| x.is_array())
66            .ok_or_else(|| anyhow::anyhow!("JSON no contiene ningún array"))?,
67        _ => anyhow::bail!("JSON debe ser array o object con campo array"),
68    };
69    let rules: Vec<Rule> = serde_json::from_value(arr)?;
70    Ok(rules)
71}