use super::zome;
use crate::prelude::*;
#[cfg(feature = "full-dna-def")]
use holochain_integrity_types::info::DnaModifiersBuilder;
#[cfg(feature = "full-dna-def")]
use crate::zome::error::ZomeError;
#[cfg(feature = "full-dna-def")]
use holo_hash::*;
#[cfg(feature = "full-dna-def")]
use kitsune_p2p_dht::spacetime::Dimension;
pub type IntegrityZomes = Vec<(ZomeName, zome::IntegrityZomeDef)>;
pub type CoordinatorZomes = Vec<(ZomeName, zome::CoordinatorZomeDef)>;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, SerializedBytes)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "full-dna-def", derive(derive_builder::Builder))]
#[cfg_attr(feature = "full-dna-def", builder(public))]
pub struct DnaDef {
#[cfg_attr(
feature = "full-dna-def",
builder(default = "\"Generated DnaDef\".to_string()")
)]
pub name: String,
pub modifiers: DnaModifiers,
pub integrity_zomes: IntegrityZomes,
pub coordinator_zomes: CoordinatorZomes,
}
#[derive(Serialize, Debug, PartialEq, Eq)]
struct DnaDefHash<'a> {
modifiers: &'a DnaModifiers,
integrity_zomes: &'a IntegrityZomes,
}
#[cfg(feature = "test_utils")]
impl DnaDef {
pub fn unique_from_zomes(
integrity: Vec<IntegrityZome>,
coordinator: Vec<CoordinatorZome>,
) -> DnaDef {
let integrity = integrity.into_iter().map(|z| z.into_inner()).collect();
let coordinator = coordinator.into_iter().map(|z| z.into_inner()).collect();
DnaDefBuilder::default()
.integrity_zomes(integrity)
.coordinator_zomes(coordinator)
.random_network_seed()
.build()
.unwrap()
}
}
impl DnaDef {
pub fn all_zomes(&self) -> impl Iterator<Item = (&ZomeName, &zome::ZomeDef)> {
self.integrity_zomes
.iter()
.map(|(n, def)| (n, def.as_any_zome_def()))
.chain(
self.coordinator_zomes
.iter()
.map(|(n, def)| (n, def.as_any_zome_def())),
)
}
}
#[cfg(feature = "full-dna-def")]
impl DnaDef {
pub fn get_integrity_zome(
&self,
zome_name: &ZomeName,
) -> Result<zome::IntegrityZome, ZomeError> {
self.integrity_zomes
.iter()
.find(|(name, _)| name == zome_name)
.cloned()
.map(|(name, def)| IntegrityZome::new(name, def))
.ok_or_else(|| ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,)))
}
pub fn is_integrity_zome(&self, zome_name: &ZomeName) -> bool {
self.integrity_zomes
.iter()
.any(|(name, _)| name == zome_name)
}
pub fn get_coordinator_zome(
&self,
zome_name: &ZomeName,
) -> Result<zome::CoordinatorZome, ZomeError> {
self.coordinator_zomes
.iter()
.find(|(name, _)| name == zome_name)
.cloned()
.map(|(name, def)| CoordinatorZome::new(name, def))
.ok_or_else(|| ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,)))
}
pub fn get_zome(&self, zome_name: &ZomeName) -> Result<zome::Zome, ZomeError> {
self.integrity_zomes
.iter()
.find(|(name, _)| name == zome_name)
.cloned()
.map(|(name, def)| Zome::new(name, def.erase_type()))
.or_else(|| {
self.coordinator_zomes
.iter()
.find(|(name, _)| name == zome_name)
.cloned()
.map(|(name, def)| Zome::new(name, def.erase_type()))
})
.ok_or_else(|| ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,)))
}
pub fn get_all_coordinators(&self) -> Vec<zome::CoordinatorZome> {
self.coordinator_zomes
.iter()
.cloned()
.map(|(name, def)| CoordinatorZome::new(name, def))
.collect()
}
pub fn get_wasm_zome(&self, zome_name: &ZomeName) -> Result<&zome::WasmZome, ZomeError> {
self.all_zomes()
.find(|(name, _)| *name == zome_name)
.map(|(_, def)| def)
.ok_or_else(|| ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,)))
.and_then(|def| {
if let ZomeDef::Wasm(wasm_zome) = def {
Ok(wasm_zome)
} else {
Err(ZomeError::NonWasmZome(zome_name.clone()))
}
})
}
pub fn get_wasm_zome_hash(&self, zome_name: &ZomeName) -> Result<WasmHash, ZomeError> {
self.all_zomes()
.find(|(name, _)| *name == zome_name)
.map(|(_, def)| def)
.ok_or_else(|| ZomeError::ZomeNotFound(format!("Zome '{}' not found", &zome_name,)))
.and_then(|def| match def {
ZomeDef::Wasm(wasm_zome) => Ok(wasm_zome.wasm_hash.clone()),
_ => Err(ZomeError::NonWasmZome(zome_name.clone())),
})
}
pub fn set_name(&self, name: String) -> Self {
let mut clone = self.clone();
clone.name = name;
clone
}
pub fn update_modifiers(&self, dna_modifiers: DnaModifiersOpt) -> Self {
let mut clone = self.clone();
clone.modifiers = clone.modifiers.update(dna_modifiers);
clone
}
pub fn topology(&self, cutoff: std::time::Duration) -> kitsune_p2p_dht::spacetime::Topology {
kitsune_p2p_dht::spacetime::Topology {
space: Dimension::standard_space(),
time: Dimension::time(self.modifiers.quantum_time),
time_origin: self.modifiers.origin_time,
time_cutoff: cutoff,
}
}
}
#[cfg(feature = "full-dna-def")]
pub fn random_network_seed() -> String {
nanoid::nanoid!()
}
#[cfg(feature = "full-dna-def")]
impl DnaDefBuilder {
pub fn random_network_seed(&mut self) -> &mut Self {
self.modifiers = Some(
DnaModifiersBuilder::default()
.network_seed(random_network_seed())
.build()
.unwrap(),
);
self
}
}
#[cfg(feature = "full-dna-def")]
pub type DnaDefHashed = HoloHashed<DnaDef>;
#[cfg(feature = "full-dna-def")]
impl HashableContent for DnaDef {
type HashType = holo_hash::hash_type::Dna;
fn hash_type(&self) -> Self::HashType {
holo_hash::hash_type::Dna::new()
}
fn hashable_content(&self) -> HashableContentBytes {
let hash = DnaDefHash {
modifiers: &self.modifiers,
integrity_zomes: &self.integrity_zomes,
};
HashableContentBytes::Content(
holochain_serialized_bytes::UnsafeBytes::from(
holochain_serialized_bytes::encode(&hash)
.expect("Could not serialize HashableContent"),
)
.into(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use holochain_serialized_bytes::prelude::*;
use kitsune_p2p_dht::spacetime::STANDARD_QUANTUM_TIME;
#[test]
fn test_update_modifiers() {
#[derive(Debug, Clone, Serialize, Deserialize, SerializedBytes)]
struct Props(u32);
let props = SerializedBytes::try_from(Props(42)).unwrap();
let now = Timestamp::now();
let mods = DnaModifiers {
network_seed: "seed".into(),
properties: ().try_into().unwrap(),
origin_time: Timestamp::HOLOCHAIN_EPOCH,
quantum_time: STANDARD_QUANTUM_TIME,
};
let opt = DnaModifiersOpt {
network_seed: None,
properties: Some(props.clone()),
origin_time: Some(now),
quantum_time: Some(core::time::Duration::from_secs(60)),
};
let expected = DnaModifiers {
network_seed: "seed".into(),
properties: props.clone(),
origin_time: now,
quantum_time: core::time::Duration::from_secs(60),
};
assert_eq!(mods.update(opt), expected);
}
}