poldercast 1.1.2

Peer to Peer topology management
Documentation
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)
    }
}