use std::time::Duration;
use super::zome;
use crate::prelude::*;
#[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)>;
pub type NetworkSeed = String;
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[cfg_attr(feature = "full-dna-def", derive(derive_builder::Builder))]
pub struct DnaModifiers {
pub network_seed: NetworkSeed,
#[cfg_attr(feature = "full-dna-def", builder(default = "().try_into().unwrap()"))]
pub properties: SerializedBytes,
#[cfg_attr(feature = "full-dna-def", builder(default = "Timestamp::now()"))]
pub origin_time: Timestamp,
#[cfg_attr(feature = "full-dna-def", builder(default = "standard_quantum_time()"))]
#[cfg_attr(feature = "full-dna-def", serde(default = "standard_quantum_time"))]
pub quantum_time: Duration,
}
#[cfg(feature = "full-dna-def")]
const fn standard_quantum_time() -> Duration {
kitsune_p2p_dht::spacetime::STANDARD_QUANTUM_TIME
}
impl DnaModifiers {
pub fn update(mut self, modifiers: DnaModifiersOpt) -> DnaModifiers {
self.network_seed = modifiers.network_seed.unwrap_or(self.network_seed);
self.properties = modifiers.properties.unwrap_or(self.properties);
self.origin_time = modifiers.origin_time.unwrap_or(self.origin_time);
self.quantum_time = modifiers.quantum_time.unwrap_or(self.quantum_time);
self
}
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct DnaModifiersOpt<P = SerializedBytes> {
pub network_seed: Option<NetworkSeed>,
pub properties: Option<P>,
pub origin_time: Option<Timestamp>,
pub quantum_time: Option<Duration>,
}
impl<P: TryInto<SerializedBytes, Error = E>, E: Into<SerializedBytesError>> Default
for DnaModifiersOpt<P>
{
fn default() -> Self {
Self::none()
}
}
impl<P: TryInto<SerializedBytes, Error = E>, E: Into<SerializedBytesError>> DnaModifiersOpt<P> {
pub fn none() -> Self {
Self {
network_seed: None,
properties: None,
origin_time: None,
quantum_time: None,
}
}
pub fn serialized(self) -> Result<DnaModifiersOpt<SerializedBytes>, E> {
let Self {
network_seed,
properties,
origin_time,
quantum_time,
} = self;
let properties = if let Some(p) = properties {
Some(p.try_into()?)
} else {
None
};
Ok(DnaModifiersOpt {
network_seed,
properties,
origin_time,
quantum_time,
})
}
pub fn with_network_seed(mut self, network_seed: NetworkSeed) -> Self {
self.network_seed = Some(network_seed);
self
}
pub fn with_properties(mut self, properties: P) -> Self {
self.properties = Some(properties);
self
}
pub fn with_origin_time(mut self, origin_time: Timestamp) -> Self {
self.origin_time = Some(origin_time);
self
}
pub fn with_quantum_time(mut self, quantum_time: Duration) -> Self {
self.quantum_time = Some(quantum_time);
self
}
pub fn has_some_option_set(&self) -> bool {
self.network_seed.is_some() || self.properties.is_some() || self.origin_time.is_some()
}
}
#[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> {
name: &'a String,
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 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 {
name: &self.name,
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(Duration::from_secs(60)),
};
let expected = DnaModifiers {
network_seed: "seed".into(),
properties: props.clone(),
origin_time: now,
quantum_time: Duration::from_secs(60),
};
assert_eq!(mods.update(opt), expected);
}
}