gioser
⎇ d270c5e67457195cd845f783c0b3b7bd66b9add0 crates/ente-brain/src/loader.rs Rust · 68 siq'ikuna · 2.7 KB Ñawpa willay Pi imata llamkarqan ch'uya

prueba d270c5e67 · 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    if let Ok(c) = serde_json::from_value::<EntityCard>(v.clone()) {
34        return Ok(c);
35    }
36    if let serde_json::Value::Object(map) = v {
37        for (_, vv) in map {
38            if let Ok(c) = serde_json::from_value::<EntityCard>(vv) {
39                return Ok(c);
40            }
41        }
42    }
43    anyhow::bail!("JSON no contiene una EntityCard válida")
44}
45
46/// Carga reglas desde un archivo JSON.
47pub fn load_rules_file(path: &Path) -> anyhow::Result<Vec<Rule>> {
48    info!(path = %path.display(), "cargando reglas desde JSON");
49    let raw = std::fs::read_to_string(path)?;
50    extract_rules_from_json(&raw)
51}
52
53/// Extrae un `Vec<Rule>` de JSON que puede ser:
54/// 1. Array directo: `[{...}, {...}]`
55/// 2. Object con un campo array: `{"rules": [...]}`
56pub fn extract_rules_from_json(raw: &str) -> anyhow::Result<Vec<Rule>> {
57    let v: serde_json::Value = serde_json::from_str(raw)?;
58    let arr = match v {
59        serde_json::Value::Array(_) => v,
60        serde_json::Value::Object(map) => map
61            .into_values()
62            .find(|x| x.is_array())
63            .ok_or_else(|| anyhow::anyhow!("JSON no contiene ningún array"))?,
64        _ => anyhow::bail!("JSON debe ser array o object con campo array"),
65    };
66    let rules: Vec<Rule> = serde_json::from_value(arr)?;
67    Ok(rules)
68}