Files
hammer/crates/hammer-core/src/swm.rs
T
sergioandClaude Opus 4.8 8bf1623044 Scaffold inicial: workspace Rust + SDDs completos
Arranque del proyecto hammer (distro AI-nativa: laboratorio funcional en el
sótano, terminal mutable clásica arriba, integración de IA programadora).

- Workspace Rust (compila, tests verdes): hammer-core, hammer-build,
  hammer-cli (bin `hammer`), hammerd.
- SDDs 00-10 + 6 ADRs en docs/ con toda la arquitectura.
- Esqueletos navegables mapeados a las fases del roadmap; Fase 0/1 listas
  para implementar el sandbox real.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 19:06:04 +00:00

122 lines
3.1 KiB
Rust

//! El manifiesto `.swm` (Software Mutación). Ver `docs/06-swm-format.md`.
//!
//! Es la unidad de intercambio: receta de transformación sobre fuente pública + ediciones de
//! config. NUNCA transporta binarios cocidos (salvo `FileDrop` con hash declarado).
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Swm {
pub swm_version: u32,
pub base: Base,
pub mutations: Vec<Mutation>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signature: Option<Signature>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Base {
pub distro_version: String,
#[serde(default)]
pub pins: std::collections::BTreeMap<String, String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum Mutation {
SourcePatch {
repo: String,
commit: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
patch: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
patch_url: Option<String>,
build: SwmBuild,
target_bin: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
expected_hash: Option<String>,
},
ConfigEdit {
file: String,
inline_diff: String,
},
InitRule {
action: String,
service: String,
command: String,
},
FileDrop {
path: String,
/// hash BLAKE3 del contenido, declarado para verificación.
content_hash: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
content_url: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SwmBuild {
#[serde(default = "default_compiler")]
pub compiler: String,
#[serde(default = "default_target")]
pub target: String,
#[serde(default = "default_link")]
pub link: String,
#[serde(default)]
pub flags: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Signature {
pub by: String,
pub alg: String,
pub sig: String,
}
fn default_compiler() -> String {
"zig-cc".into()
}
fn default_target() -> String {
"x86_64-linux-musl".into()
}
fn default_link() -> String {
"static".into()
}
impl Swm {
pub fn from_yaml(s: &str) -> crate::Result<Swm> {
serde_yaml::from_str(s).map_err(|e| crate::Error::Serde(e.to_string()))
}
pub fn to_yaml(&self) -> crate::Result<String> {
serde_yaml::to_string(self).map_err(|e| crate::Error::Serde(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_yaml() {
let yaml = r#"
swm_version: 1
base:
distro_version: "2026-06-06"
pins:
grep: "a1b2c3d"
mutations:
- type: config_edit
file: "/etc/network.conf"
inline_diff: |
- DHCP=yes
+ IP=192.168.1.100
"#;
let swm = Swm::from_yaml(yaml).expect("parse");
assert_eq!(swm.swm_version, 1);
assert_eq!(swm.mutations.len(), 1);
let back = swm.to_yaml().expect("serialize");
assert!(back.contains("config_edit"));
}
}