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
//! 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)
    }

    #[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,
}