tawasuyu

sergio/takana

> Una distribución Linux construida con un laboratorio funcional y hermético en el > sótano, y una terminal mutable, clásica y anárquica en el piso de arriba — diseñada > desde el suelo para que una IA programadora entienda, modifique y co…

TOML 1572Shell 203Rust 109Markdown 993728 Definitions

⎇ 8bf1623044075274682e4351a0f8e120047c2bed crates/hammer-core/src/swm.rs Rust · 121 lines · 3.1 KB History Who touched what raw

Scaffold inicial: workspace Rust + SDDs completos 8bf162304 · sergio ·

1//! El manifiesto `.swm` (Software Mutación). Ver `docs/06-swm-format.md`.
2//!
3//! Es la unidad de intercambio: receta de transformación sobre fuente pública + ediciones de
4//! config. NUNCA transporta binarios cocidos (salvo `FileDrop` con hash declarado).
5
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Swm {
10    pub swm_version: u32,
11    pub base: Base,
12    pub mutations: Vec<Mutation>,
13    #[serde(default, skip_serializing_if = "Option::is_none")]
14    pub signature: Option<Signature>,
15}
16
17#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct Base {
19    pub distro_version: String,
20    #[serde(default)]
21    pub pins: std::collections::BTreeMap<String, String>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(tag = "type", rename_all = "snake_case")]
26pub enum Mutation {
27    SourcePatch {
28        repo: String,
29        commit: String,
30        #[serde(default, skip_serializing_if = "Option::is_none")]
31        patch: Option<String>,
32        #[serde(default, skip_serializing_if = "Option::is_none")]
33        patch_url: Option<String>,
34        build: SwmBuild,
35        target_bin: String,
36        #[serde(default, skip_serializing_if = "Option::is_none")]
37        expected_hash: Option<String>,
38    },
39    ConfigEdit {
40        file: String,
41        inline_diff: String,
42    },
43    InitRule {
44        action: String,
45        service: String,
46        command: String,
47    },
48    FileDrop {
49        path: String,
50        /// hash BLAKE3 del contenido, declarado para verificación.
51        content_hash: String,
52        #[serde(default, skip_serializing_if = "Option::is_none")]
53        content_url: Option<String>,
54    },
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct SwmBuild {
59    #[serde(default = "default_compiler")]
60    pub compiler: String,
61    #[serde(default = "default_target")]
62    pub target: String,
63    #[serde(default = "default_link")]
64    pub link: String,
65    #[serde(default)]
66    pub flags: Vec<String>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct Signature {
71    pub by: String,
72    pub alg: String,
73    pub sig: String,
74}
75
76fn default_compiler() -> String {
77    "zig-cc".into()
78}
79fn default_target() -> String {
80    "x86_64-linux-musl".into()
81}
82fn default_link() -> String {
83    "static".into()
84}
85
86impl Swm {
87    pub fn from_yaml(s: &str) -> crate::Result<Swm> {
88        serde_yaml::from_str(s).map_err(|e| crate::Error::Serde(e.to_string()))
89    }
90
91    pub fn to_yaml(&self) -> crate::Result<String> {
92        serde_yaml::to_string(self).map_err(|e| crate::Error::Serde(e.to_string()))
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn roundtrip_yaml() {
102        let yaml = r#"
103swm_version: 1
104base:
105  distro_version: "2026-06-06"
106  pins:
107    grep: "a1b2c3d"
108mutations:
109  - type: config_edit
110    file: "/etc/network.conf"
111    inline_diff: |
112      - DHCP=yes
113      + IP=192.168.1.100
114"#;
115        let swm = Swm::from_yaml(yaml).expect("parse");
116        assert_eq!(swm.swm_version, 1);
117        assert_eq!(swm.mutations.len(), 1);
118        let back = swm.to_yaml().expect("serialize");
119        assert!(back.contains("config_edit"));
120    }
121}