1#[cfg(feature = "unstable-migration")]
4use std::collections::HashSet;
5
6use crate::prelude::*;
7
8#[cfg(feature = "full-dna-def")]
9use holochain_integrity_types::DnaModifiersBuilder;
10
11#[cfg(feature = "full-dna-def")]
12use crate::zome::ZomeError;
13#[cfg(feature = "full-dna-def")]
14use holo_hash::*;
15
16pub type IntegrityZomes = Vec<(ZomeName, IntegrityZomeDef)>;
18
19pub type CoordinatorZomes = Vec<(ZomeName, CoordinatorZomeDef)>;
21
22#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, SerializedBytes)]
30#[cfg_attr(feature = "full-dna-def", derive(derive_builder::Builder))]
31#[cfg_attr(feature = "full-dna-def", builder(public))]
32pub struct DnaDef {
33 #[cfg_attr(
35 feature = "full-dna-def",
36 builder(default = "\"Generated DnaDef\".to_string()")
37 )]
38 pub name: String,
39
40 pub modifiers: DnaModifiers,
44
45 pub integrity_zomes: IntegrityZomes,
47
48 pub coordinator_zomes: CoordinatorZomes,
51
52 #[cfg(feature = "unstable-migration")]
70 #[serde(default)]
71 #[cfg_attr(feature = "full-dna-def", builder(default))]
72 pub lineage: HashSet<DnaHash>,
73}
74
75#[cfg(feature = "full-dna-def")]
76#[derive(Serialize, Debug, PartialEq, Eq)]
77struct DnaDefHash<'a> {
79 modifiers: &'a DnaModifiers,
80 integrity_zomes: &'a IntegrityZomes,
81}
82
83#[cfg(feature = "test_utils")]
84impl DnaDef {
85 pub fn unique_from_zomes(
87 integrity: Vec<IntegrityZome>,
88 coordinator: Vec<CoordinatorZome>,
89 ) -> DnaDef {
90 let integrity = integrity.into_iter().map(|z| z.into_inner()).collect();
91 let coordinator = coordinator.into_iter().map(|z| z.into_inner()).collect();
92 DnaDefBuilder::default()
93 .integrity_zomes(integrity)
94 .coordinator_zomes(coordinator)
95 .random_network_seed()
96 .build()
97 .unwrap()
98 }
99}
100
101impl DnaDef {
102 #[cfg_attr(feature = "instrument", tracing::instrument(skip_all))]
104 pub fn all_zomes(&self) -> impl Iterator<Item = (&ZomeName, &ZomeDef)> {
105 self.integrity_zomes
106 .iter()
107 .map(|(n, def)| (n, def.as_any_zome_def()))
108 .chain(
109 self.coordinator_zomes
110 .iter()
111 .map(|(n, def)| (n, def.as_any_zome_def())),
112 )
113 }
114}
115
116#[cfg(feature = "full-dna-def")]
117impl DnaDef {
118 pub fn get_integrity_zome(&self, zome_name: &ZomeName) -> Result<IntegrityZome, ZomeError> {
120 self.integrity_zomes
121 .iter()
122 .find(|(name, _)| name == zome_name)
123 .cloned()
124 .map(|(name, def)| IntegrityZome::new(name, def))
125 .ok_or_else(|| {
126 tracing::error!(
127 "ZomeNotFound: {zome_name}. (get_integrity_zome) Existing zomes: integrity={:?}, coordinator={:?}",
128 self.integrity_zomes,
129 self.coordinator_zomes,
130 );
131 ZomeError::ZomeNotFound(format!("Integrity zome '{}' not found", &zome_name,))
132 })
133 }
134
135 #[cfg_attr(feature = "instrument", tracing::instrument(skip_all))]
137 pub fn is_integrity_zome(&self, zome_name: &ZomeName) -> bool {
138 self.integrity_zomes
139 .iter()
140 .any(|(name, _)| name == zome_name)
141 }
142
143 pub fn get_coordinator_zome(&self, zome_name: &ZomeName) -> Result<CoordinatorZome, ZomeError> {
145 self.coordinator_zomes
146 .iter()
147 .find(|(name, _)| name == zome_name)
148 .cloned()
149 .map(|(name, def)| CoordinatorZome::new(name, def))
150 .ok_or_else(|| {
151 tracing::error!(
152 "ZomeNotFound: {zome_name}. (get_coordinator_zome) Existing zomes: integrity={:?}, coordinator={:?}",
153 self.integrity_zomes,
154 self.coordinator_zomes,
155 );
156 ZomeError::ZomeNotFound(format!("Coordinator Zome '{}' not found", &zome_name,))
157 })
158 }
159
160 pub fn get_zome(&self, zome_name: &ZomeName) -> Result<Zome, ZomeError> {
162 self.integrity_zomes
163 .iter()
164 .find(|(name, _)| name == zome_name)
165 .cloned()
166 .map(|(name, def)| Zome::new(name, def.erase_type()))
167 .or_else(|| {
168 self.coordinator_zomes
169 .iter()
170 .find(|(name, _)| name == zome_name)
171 .cloned()
172 .map(|(name, def)| Zome::new(name, def.erase_type()))
173 })
174 .ok_or_else(|| {
175 tracing::error!(
176 "ZomeNotFound: {zome_name}. (get_zome) Existing zomes: integrity={:?}, coordinator={:?}",
177 self.integrity_zomes,
178 self.coordinator_zomes,
179 );
180 ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,))
181 })
182 }
183
184 pub fn get_all_coordinators(&self) -> Vec<CoordinatorZome> {
186 self.coordinator_zomes
187 .iter()
188 .cloned()
189 .map(|(name, def)| CoordinatorZome::new(name, def))
190 .collect()
191 }
192
193 pub fn get_wasm_zome(&self, zome_name: &ZomeName) -> Result<&WasmZome, ZomeError> {
195 self.all_zomes()
196 .find(|(name, _)| *name == zome_name)
197 .map(|(_, def)| def)
198 .ok_or_else(|| {
199 tracing::error!(
200 "ZomeNotFound: {zome_name}. (get_wasm_zome) Existing zomes: integrity={:?}, coordinator={:?}",
201 self.integrity_zomes,
202 self.coordinator_zomes,
203 );
204 ZomeError::ZomeNotFound(format!("Wasm zome '{}' not found", &zome_name,))
205 })
206 .and_then(|def| {
207 if let ZomeDef::Wasm(wasm_zome) = def {
208 Ok(wasm_zome)
209 } else {
210 Err(ZomeError::NonWasmZome(zome_name.clone()))
211 }
212 })
213 }
214
215 pub fn get_wasm_zome_hash(&self, zome_name: &ZomeName) -> Result<WasmHash, ZomeError> {
217 self.all_zomes()
218 .find(|(name, _)| *name == zome_name)
219 .map(|(_, def)| def)
220 .ok_or_else(|| {
221 tracing::error!(
222 "ZomeNotFound: {zome_name}. (get_wasm_zome_hash) Existing zomes: integrity={:?}, coordinator={:?}",
223 self.integrity_zomes,
224 self.coordinator_zomes,
225 );
226 ZomeError::ZomeNotFound(format!("Hash for wasm zome '{}' not found", &zome_name,))
227 })
228 .and_then(|def| match def {
229 ZomeDef::Wasm(wasm_zome) => Ok(wasm_zome.wasm_hash.clone()),
230 _ => Err(ZomeError::NonWasmZome(zome_name.clone())),
231 })
232 }
233
234 pub fn set_name(&self, name: String) -> Self {
236 let mut clone = self.clone();
237 clone.name = name;
238 clone
239 }
240
241 pub fn update_modifiers(&self, modifiers: DnaModifiersOpt) -> Self {
244 let mut clone = self.clone();
245 clone.modifiers = clone.modifiers.update(modifiers);
246 clone
247 }
248}
249
250#[cfg(feature = "full-dna-def")]
252pub fn random_network_seed() -> String {
253 nanoid::nanoid!()
254}
255
256#[cfg(feature = "full-dna-def")]
257impl DnaDefBuilder {
258 pub fn random_network_seed(&mut self) -> &mut Self {
260 self.modifiers = Some(
261 DnaModifiersBuilder::default()
262 .network_seed(random_network_seed())
263 .build()
264 .unwrap(),
265 );
266 self
267 }
268}
269
270#[cfg(feature = "full-dna-def")]
272pub type DnaDefHashed = HoloHashed<DnaDef>;
273
274#[cfg(feature = "full-dna-def")]
275impl HashableContent for DnaDef {
276 type HashType = holo_hash::hash_type::Dna;
277
278 fn hash_type(&self) -> Self::HashType {
279 holo_hash::hash_type::Dna::new()
280 }
281
282 fn hashable_content(&self) -> HashableContentBytes {
283 let hash = DnaDefHash {
284 modifiers: &self.modifiers,
285 integrity_zomes: &self.integrity_zomes,
286 };
287 HashableContentBytes::Content(
288 holochain_serialized_bytes::UnsafeBytes::from(
289 holochain_serialized_bytes::encode(&hash)
290 .expect("Could not serialize HashableContent"),
291 )
292 .into(),
293 )
294 }
295}
296
297#[cfg(test)]
298mod tests {
299
300 use super::*;
301 use holochain_serialized_bytes::prelude::*;
302
303 #[test]
304 fn test_update_modifiers() {
305 #[derive(Debug, Clone, Serialize, Deserialize, SerializedBytes)]
306 struct Props(u32);
307
308 let props = SerializedBytes::try_from(Props(42)).unwrap();
309
310 let mods = DnaModifiers {
311 network_seed: "seed".into(),
312 properties: ().try_into().unwrap(),
313 };
314
315 let opt = DnaModifiersOpt {
316 network_seed: None,
317 properties: Some(props.clone()),
318 };
319
320 let expected = DnaModifiers {
321 network_seed: "seed".into(),
322 properties: props.clone(),
323 };
324
325 assert_eq!(mods.update(opt), expected);
326 }
327}