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…
⎇ 8bf1623044075274682e4351a0f8e120047c2bed
crates/hammer-core/src/swm.rs
History
Who touched what
raw
Scaffold inicial: workspace Rust + SDDs completos
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}