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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use crate::{
    topic::{InterestLevel, Subscriptions, Topic},
    Gossip, PriorityMap, Subscription,
};
use keynesis::{key::ed25519, passport::block::Time};
use std::net::SocketAddr;

pub struct Profile {
    subscriptions: PriorityMap<InterestLevel, Topic>,
    gossip: Gossip,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Proximity {
    priority: usize,
    proximity: usize,
}

impl Profile {
    pub fn new(address: SocketAddr, id: &ed25519::SecretKey) -> Self {
        let gossip = Gossip::new(address, id, Subscriptions::new().as_slice());

        Self {
            gossip,
            subscriptions: PriorityMap::new(Subscriptions::MAX_NUM_SUBSCRIPTIONS),
        }
    }

    pub fn from_gossip(gossip: Gossip) -> Self {
        let mut subscriptions = PriorityMap::new(Subscriptions::MAX_NUM_SUBSCRIPTIONS);

        for subscription in gossip.subscriptions() {
            let interest_level = subscription.interest_level();
            let topic = subscription.topic();
            subscriptions.put(interest_level, topic);
        }

        Self {
            gossip,
            subscriptions,
        }
    }

    pub(crate) fn clear_subscriptions(&mut self) {
        self.subscriptions.clear();
    }

    pub(crate) fn subscriptions_mut(&mut self) -> &mut PriorityMap<InterestLevel, Topic> {
        &mut self.subscriptions
    }

    pub(crate) fn unsubscribe(&mut self, topic: &Topic) {
        self.subscriptions.remove(topic);
    }

    pub fn gossip(&self) -> &Gossip {
        &self.gossip
    }

    pub(crate) fn commit_gossip(&mut self, id: &ed25519::SecretKey) -> &Gossip {
        let subscriptions = self.subscriptions();

        self.gossip = Gossip::new(self.address(), id, subscriptions.as_slice());

        &self.gossip
    }

    pub fn id(&self) -> ed25519::PublicKey {
        self.gossip.id()
    }

    pub fn last_update(&self) -> Time {
        self.gossip.time()
    }

    pub fn address(&self) -> SocketAddr {
        self.gossip.address()
    }

    pub fn subscriptions(&self) -> Subscriptions {
        let mut subscriptions = Subscriptions::new();
        for (interest_level, topic) in self.subscriptions.iter() {
            let sub = Subscription::new(*topic, *interest_level);
            subscriptions
                .push(sub.as_slice())
                .expect("We are already limiting the number of internal subscriptions");
        }
        subscriptions
    }

    pub fn proximity_to(&self, to: &Self) -> Proximity {
        let mut priority_score = 0;
        let mut proximity_score = 0;
        for (interest_level, topic) in self.subscriptions.iter() {
            if let Some((to, _)) = to.subscriptions.get(topic) {
                proximity_score += 1;
                priority_score += interest_level.priority_score(*to);
            }
        }
        Proximity {
            proximity: proximity_score,
            priority: priority_score,
        }
    }
}

impl PartialOrd<Self> for Proximity {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for Proximity {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        use std::cmp::Ordering::{Equal, Greater, Less};
        if self.priority > other.priority {
            Greater
        } else if self.priority < other.priority {
            Less
        } else if self.proximity > other.proximity {
            Greater
        } else if self.proximity < other.proximity {
            Less
        } else {
            Equal
        }
    }
}

impl From<Gossip> for Profile {
    fn from(gossip: Gossip) -> Self {
        Self::from_gossip(gossip)
    }
}