entrenar/sovereign/distribution/
sovereign.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use sha2::{Digest, Sha256};
6
7use super::{ComponentManifest, DistributionFormat, DistributionTier};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct SovereignDistribution {
12 pub name: String,
14 pub version: String,
16 pub tier: DistributionTier,
18 pub format: DistributionFormat,
20 pub components: Vec<ComponentManifest>,
22 pub checksum: String,
24 pub created_at: DateTime<Utc>,
26}
27
28impl SovereignDistribution {
29 pub fn new(
31 name: impl Into<String>,
32 version: impl Into<String>,
33 tier: DistributionTier,
34 format: DistributionFormat,
35 ) -> Self {
36 Self {
37 name: name.into(),
38 version: version.into(),
39 tier,
40 format,
41 components: Vec::new(),
42 checksum: String::new(),
43 created_at: Utc::now(),
44 }
45 }
46
47 pub fn core() -> Self {
49 let version = env!("CARGO_PKG_VERSION");
50 let mut dist = Self::new(
51 "entrenar-sovereign-core",
52 version,
53 DistributionTier::Core,
54 DistributionFormat::Tarball,
55 );
56
57 dist.components = vec![
58 ComponentManifest::entrenar_core(version),
59 ComponentManifest::trueno("0.2"),
60 ComponentManifest::aprender("0.1"),
61 ];
62
63 dist
64 }
65
66 pub fn standard() -> Self {
68 let version = env!("CARGO_PKG_VERSION");
69 let mut dist = Self::new(
70 "entrenar-sovereign-standard",
71 version,
72 DistributionTier::Standard,
73 DistributionFormat::Tarball,
74 );
75
76 dist.components = vec![
77 ComponentManifest::entrenar_core(version),
78 ComponentManifest::trueno("0.2"),
79 ComponentManifest::aprender("0.1"),
80 ComponentManifest::renacer("0.1"),
81 ComponentManifest::new("trueno-db", "0.1", "trueno-db"),
82 ComponentManifest::new("ruchy", "0.1", "ruchy"),
83 ];
84
85 dist
86 }
87
88 pub fn full() -> Self {
90 let version = env!("CARGO_PKG_VERSION");
91 let mut dist = Self::new(
92 "entrenar-sovereign-full",
93 version,
94 DistributionTier::Full,
95 DistributionFormat::Tarball,
96 );
97
98 dist.components = vec![
99 ComponentManifest::entrenar_core(version),
100 ComponentManifest::trueno("0.2").with_features(["gpu", "cuda"]),
101 ComponentManifest::aprender("0.1"),
102 ComponentManifest::renacer("0.1"),
103 ComponentManifest::new("trueno-db", "0.1", "trueno-db"),
104 ComponentManifest::new("ruchy", "0.1", "ruchy"),
105 ComponentManifest::new("entrenar-gpu", version, "entrenar-gpu")
106 .with_features(["cuda", "rocm"]),
107 ComponentManifest::new("entrenar-bench", version, "entrenar-bench"),
108 ComponentManifest::new("entrenar-inspect", version, "entrenar-inspect"),
109 ComponentManifest::new("entrenar-lora", version, "entrenar-lora"),
110 ComponentManifest::new("entrenar-shell", version, "entrenar-shell"),
111 ];
112
113 dist
114 }
115
116 pub fn with_format(mut self, format: DistributionFormat) -> Self {
118 self.format = format;
119 self
120 }
121
122 pub fn with_checksum(mut self, checksum: impl Into<String>) -> Self {
124 self.checksum = checksum.into();
125 self
126 }
127
128 pub fn compute_checksum(&mut self, data: &[u8]) {
130 let mut hasher = Sha256::new();
131 hasher.update(data);
132 self.checksum = format!("{:x}", hasher.finalize());
133 }
134
135 pub fn verify_checksum(&self, data: &[u8]) -> bool {
137 if self.checksum.is_empty() {
138 return false;
139 }
140
141 let mut hasher = Sha256::new();
142 hasher.update(data);
143 let computed = format!("{:x}", hasher.finalize());
144
145 computed == self.checksum
146 }
147
148 pub fn to_manifest_json(&self) -> String {
150 serde_json::to_string_pretty(self).unwrap_or_else(|_err| "{}".to_string())
151 }
152
153 pub fn from_manifest_json(json: &str) -> Result<Self, serde_json::Error> {
155 serde_json::from_str(json)
156 }
157
158 pub fn suggested_filename(&self) -> String {
160 format!("{}-{}.{}", self.name, self.version, self.format.extension())
161 }
162
163 pub fn component_count(&self) -> usize {
165 self.components.len()
166 }
167
168 pub fn has_component(&self, name: &str) -> bool {
170 self.components.iter().any(|c| c.name == name)
171 }
172}
173
174impl Default for SovereignDistribution {
175 fn default() -> Self {
176 Self::core()
177 }
178}