gioser

tawasuyu/brahman

Rust 570TOML 215Markdown 68JSON 198269 Sutichaykuna

⎇ 94ea0eaa539cf93d58167ea1687b5a242b196573 crates/runtime/sandokan-core/src/intent.rs Rust · 112 siq'ikuna · 3.6 KB Ñawpa willay Pi imata llamkarqan ch'uya

feat(sandokan): CLI de prueba + fix wire serialization 94ea0eaa5 · sergio ·

1//! Qué orquestar: la intención de ejecución y su contexto.
2
3use brahman_card::Card;
4use serde::{Deserialize, Serialize};
5use std::time::{Duration, SystemTime};
6use ulid::Ulid;
7
8/// Nivel de aislamiento pedido para una encarnación.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
10pub enum IsolationLevel {
11    /// Sin sandbox — mismo namespace que el orquestador.
12    None,
13    /// Namespaces estándar (pid/mount/net/...) según `Card.soma`.
14    #[default]
15    Standard,
16    /// Namespaces + rootfs aislado (`pivot_root` + OverlayFS).
17    Sealed,
18}
19
20/// Contexto de ejecución: ajustes sobre cómo encarnar la Card.
21#[derive(Debug, Clone, Default, Serialize, Deserialize)]
22pub struct ExecContext {
23    /// Aislamiento pedido. `None` = derivar de `Card.soma`.
24    pub isolation: Option<IsolationLevel>,
25    /// Variables de entorno adicionales (sobre las del Card).
26    pub env: Vec<(String, String)>,
27    /// Time-to-live opcional: si se setea, el orquestador detiene la
28    /// entidad al vencer.
29    pub ttl: Option<Duration>,
30}
31
32/// Una intención de ejecución: la `Card` a encarnar + su contexto.
33#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct Intent {
35    /// La Card se serializa vía `WireCard` (proyección postcard-friendly):
36    /// el campo `extensions` de `Card` usa `#[serde(flatten)]`, que no es
37    /// compatible con formatos no auto-descriptivos como postcard.
38    #[serde(with = "card_wire")]
39    pub card: Card,
40    #[serde(default)]
41    pub context: ExecContext,
42}
43
44/// Serde adapter: `Card` ↔ `WireCard` en el límite de serialización.
45/// Las `extensions` locales de la Card se descartan al cruzar el wire.
46mod card_wire {
47    use brahman_card::{Card, WireCard};
48    use serde::{Deserialize, Deserializer, Serialize, Serializer};
49
50    pub fn serialize<S: Serializer>(card: &Card, s: S) -> Result<S::Ok, S::Error> {
51        WireCard::from(card.clone()).serialize(s)
52    }
53
54    pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Card, D::Error> {
55        Ok(Card::from(WireCard::deserialize(d)?))
56    }
57}
58
59impl Intent {
60    /// Intención mínima: encarnar una Card con el contexto por defecto.
61    pub fn new(card: Card) -> Self {
62        Self { card, context: ExecContext::default() }
63    }
64
65    /// Builder: fija el nivel de aislamiento.
66    pub fn with_isolation(mut self, level: IsolationLevel) -> Self {
67        self.context.isolation = Some(level);
68        self
69    }
70
71    /// Builder: fija un TTL.
72    pub fn with_ttl(mut self, ttl: Duration) -> Self {
73        self.context.ttl = Some(ttl);
74        self
75    }
76
77    /// El id de la Card que esta intención encarna.
78    pub fn card_id(&self) -> Ulid {
79        self.card.id
80    }
81}
82
83/// Referencia a una entidad encarnada por el orquestador.
84#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
85pub struct ExecHandle {
86    /// Id de la Card encarnada (identidad estable en el fractal).
87    pub card_id: Ulid,
88    /// Label humano-legible (copiado de `Card.label`).
89    pub label: String,
90    /// Cuándo arrancó.
91    pub started_at: SystemTime,
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn isolation_default_is_standard() {
100        assert_eq!(IsolationLevel::default(), IsolationLevel::Standard);
101    }
102
103    #[test]
104    fn intent_builders_compose() {
105        let card = Card::new("demo");
106        let intent = Intent::new(card)
107            .with_isolation(IsolationLevel::Sealed)
108            .with_ttl(Duration::from_secs(30));
109        assert_eq!(intent.context.isolation, Some(IsolationLevel::Sealed));
110        assert_eq!(intent.context.ttl, Some(Duration::from_secs(30)));
111    }
112}