1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
//! A DNA gamut is a representation of all DNAs available in a given context.

use super::DnaVersionSpec;
use crate::prelude::*;
use std::collections::{hash_map, HashMap, HashSet};

/// Representation of all DNAs and Cells available in a given context.
/// When given a DnaVersionSpec, a particular DNA can be selected from this
/// gamut.
///
/// Moreover, each DNA hash has associated with it a list of Agents. Each agent
/// represents a Cell which exists on the conductor, using that DNA and agent
/// pair. A DNA with no agents listed is simply registered but does not exist
/// in any Cell.
///
/// NB: since our DnaVersionSpec is currently very simplistic, so is the gamut.
/// As our versioning becomes more expressive, so will this type. For instance,
/// if we introduce semver, the gamut will include versions of DNAs as well.
///
/// This type basically exists as an abstract adapter between the conductor's
/// DNA store and the app installation process. Without needing to know exactly
/// what we will need from the DNA store, we can define what questions we will
/// need to ask of it through this type.
pub struct DnaGamut(HashMap<DnaHash, HashSet<AgentPubKey>>);

/// We don't have any notion of DNA versioning other than the hash, but this is
/// a placeholder to indicate the need for it in the future and to start using
/// it in public interfaces.
pub struct DnaVersion;

impl DnaGamut {
    /// Constructor. Restructure a list of CellIds into the proper format.
    pub fn new<I: IntoIterator<Item = CellId>>(cells: I) -> Self {
        let mut map: HashMap<DnaHash, HashSet<AgentPubKey>> = HashMap::new();
        for cell in cells {
            let (dna, agent) = cell.into_dna_and_agent();
            match map.entry(dna) {
                hash_map::Entry::Occupied(mut e) => {
                    e.get_mut().insert(agent);
                }
                hash_map::Entry::Vacant(e) => {
                    e.insert(vec![agent].into_iter().collect());
                }
            }
        }
        Self(map)
    }

    #[deprecated = "Stop using the placeholder"]
    #[allow(missing_docs)]
    pub fn placeholder() -> Self {
        Self::new(std::iter::empty())
    }

    /// Given a version spec, return the best-matching DNA in the gamut
    pub fn resolve_dna(&self, spec: DnaVersionSpec) -> DnaResolution {
        for hash in spec.dna_hashes() {
            if self.0.contains_key(hash.as_ref()) {
                return DnaResolution::Match(hash.clone(), DnaVersion);
            }
        }
        DnaResolution::NoMatch
    }

    /// Given a version spec, return the best-matching CellId
    // TODO: use DPKI to filter Cells which belong to Agents that are not
    //       associated with the provided agent
    pub fn resolve_cell(&self, spec: DnaVersionSpec, _agent: &AgentPubKey) -> CellResolution {
        for hash in spec.dna_hashes() {
            if let Some(agent) = self
                .0
                .get(hash.as_ref())
                // TODO: this is where an agent check could go, but for now we
                //       just return the first one available
                .map(|agents| agents.iter().next())
                .unwrap_or(None)
            {
                return CellResolution::Match(
                    CellId::new(hash.clone().into(), agent.clone()),
                    DnaVersion,
                );
            }
        }
        CellResolution::NoMatch
    }
}

/// Possible results of DNA resolution
pub enum DnaResolution {
    /// A match was found within the gamut
    Match(DnaHashB64, DnaVersion),
    /// No match was found
    NoMatch,
    /// Multiple matches were found, or other scenario that requires user
    /// intervention for resolution (TODO, placeholder)
    Conflict,
}

/// Possible results of Cell resolution
pub enum CellResolution {
    /// A match was found within the gamut
    Match(CellId, DnaVersion),
    /// No match was found
    NoMatch,
    /// Multiple matches were found, or other scenario that requires user
    /// intervention for resolution (TODO, placeholder)
    Conflict,
}