prueba
This commit is contained in:
@@ -1,10 +1,18 @@
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
# rule.k — Triplet [Sujeto + Evento + Acción(Objeto)]. La gramática del
|
# rule.k — REFERENCE ONLY. NOT LOADED.
|
||||||
# Cerebro del fractal. Cada regla es una sinapsis: cuando ocurre `when`,
|
|
||||||
# el motor ejecuta `then` para todos los Entes que cumplen `scope`.
|
|
||||||
#
|
#
|
||||||
# El motor en Rust las indexa por discriminante de EventKind para lookup
|
# La gramática autoritativa de Rule vive en Rust:
|
||||||
|
# crates/ente-brain/src/rules.rs
|
||||||
|
# El loader (crates/ente-brain/src/loader.rs) sólo acepta JSON / JSONL.
|
||||||
|
#
|
||||||
|
# Conservado como notas de diseño humano-legibles del shape Rule:
|
||||||
|
# Triplet [Sujeto + Evento + Acción(Objeto)]. Cada regla es una sinapsis:
|
||||||
|
# cuando ocurre `when`, el motor ejecuta `then` para los Entes que cumplen
|
||||||
|
# `scope`. El motor las indexa por discriminante de EventKind para lookup
|
||||||
# en O(1). Las reglas son inmutables tras carga (Arc<Rule>).
|
# en O(1). Las reglas son inmutables tras carga (Arc<Rule>).
|
||||||
|
#
|
||||||
|
# Si cambias el shape en Rust, sincroniza este archivo a mano (o
|
||||||
|
# reemplázalo por JSON Schema generado vía `schemars`).
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
schema Rule:
|
schema Rule:
|
||||||
|
|||||||
@@ -97,3 +97,76 @@ pub fn extract_rules_from_json(raw: &str) -> anyhow::Result<Vec<Rule>> {
|
|||||||
}
|
}
|
||||||
Ok(rules)
|
Ok(rules)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::introspect::append_rule_jsonl;
|
||||||
|
use crate::rules::{Action, EventKind, EventPattern, LogLevel, Rule, Scope};
|
||||||
|
use ulid::Ulid;
|
||||||
|
|
||||||
|
fn sample_rule() -> Rule {
|
||||||
|
Rule {
|
||||||
|
id: Ulid::new(),
|
||||||
|
priority: 5,
|
||||||
|
when: EventPattern::Single { kind: EventKind::EnteSpawned },
|
||||||
|
then: vec![Action::Log {
|
||||||
|
level: LogLevel::Info,
|
||||||
|
message: "test".into(),
|
||||||
|
}],
|
||||||
|
scope: Scope::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rules_from_array() {
|
||||||
|
let r = sample_rule();
|
||||||
|
let raw = format!("[{}]", serde_json::to_string(&r).unwrap());
|
||||||
|
let parsed = extract_rules_from_json(&raw).expect("array parse");
|
||||||
|
assert_eq!(parsed.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rules_from_object_with_array() {
|
||||||
|
let r = sample_rule();
|
||||||
|
let raw = format!(r#"{{"rules":[{}]}}"#, serde_json::to_string(&r).unwrap());
|
||||||
|
let parsed = extract_rules_from_json(&raw).expect("object parse");
|
||||||
|
assert_eq!(parsed.len(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn rules_from_jsonl_with_comments_and_blanks() {
|
||||||
|
let r1 = sample_rule();
|
||||||
|
let r2 = sample_rule();
|
||||||
|
let raw = format!(
|
||||||
|
"# header comment\n\n{}\n# inline comment\n{}\n\n",
|
||||||
|
serde_json::to_string(&r1).unwrap(),
|
||||||
|
serde_json::to_string(&r2).unwrap()
|
||||||
|
);
|
||||||
|
let parsed = extract_rules_from_json(&raw).expect("jsonl parse");
|
||||||
|
assert_eq!(parsed.len(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn append_rule_jsonl_roundtrip() {
|
||||||
|
let dir = tempdir_unique();
|
||||||
|
let path = dir.join("rules.jsonl");
|
||||||
|
let r1 = sample_rule();
|
||||||
|
let r2 = sample_rule();
|
||||||
|
append_rule_jsonl(&path, &r1).expect("append 1");
|
||||||
|
append_rule_jsonl(&path, &r2).expect("append 2");
|
||||||
|
let raw = std::fs::read_to_string(&path).expect("read back");
|
||||||
|
let parsed = extract_rules_from_json(&raw).expect("roundtrip parse");
|
||||||
|
assert_eq!(parsed.len(), 2);
|
||||||
|
assert_eq!(parsed[0].id, r1.id);
|
||||||
|
assert_eq!(parsed[1].id, r2.id);
|
||||||
|
let _ = std::fs::remove_dir_all(&dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn tempdir_unique() -> std::path::PathBuf {
|
||||||
|
let base = std::env::temp_dir();
|
||||||
|
let p = base.join(format!("ente-brain-loader-{}", Ulid::new()));
|
||||||
|
std::fs::create_dir_all(&p).unwrap();
|
||||||
|
p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
# ============================================================================
|
# ============================================================================
|
||||||
# card.k — Genética del Ente. Esquema KCL para EntityCard.
|
# card.k — REFERENCE ONLY. NOT LOADED.
|
||||||
#
|
#
|
||||||
# Esta es la gramática autoritativa: cualquier Card que se cargue al fractal
|
# La validación canónica de EntityCard vive en Rust:
|
||||||
# debe pasar la validación de este esquema. El boot de PID 1 acepta JSON que
|
# crates/ente-card/src/lib.rs :: EntityCard::validate()
|
||||||
# cumple este shape (KCL exporta JSON tras validar `check:`).
|
# El loader (crates/ente-brain/src/loader.rs) sólo acepta JSON.
|
||||||
#
|
#
|
||||||
# Para validar manualmente:
|
# Este archivo se conserva como notas de diseño legibles para humanos sobre
|
||||||
# kcl run examples/my-card.k --schema schema/card.k
|
# las invariantes que `validate()` debe garantizar. Si modificas el shape
|
||||||
#
|
# en Rust, sincroniza este archivo a mano (o reemplázalo por JSON Schema
|
||||||
# Cada `check:` es invariante de fractal. Romperlo = Card inválida = no boot.
|
# generado vía `schemars`).
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
# ---------- Identidad ----------
|
# ---------- Identidad ----------
|
||||||
|
|||||||
Reference in New Issue
Block a user