1use crate::Channel;
  2use client::ChannelId;
  3use collections::BTreeMap;
  4use rpc::proto;
  5use std::sync::Arc;
  6
  7#[derive(Default, Debug)]
  8pub struct ChannelIndex {
  9    channels_ordered: Vec<ChannelId>,
 10    channels_by_id: BTreeMap<ChannelId, Arc<Channel>>,
 11}
 12
 13impl ChannelIndex {
 14    pub const fn by_id(&self) -> &BTreeMap<ChannelId, Arc<Channel>> {
 15        &self.channels_by_id
 16    }
 17
 18    pub fn ordered_channels(&self) -> &[ChannelId] {
 19        &self.channels_ordered
 20    }
 21
 22    pub fn clear(&mut self) {
 23        self.channels_ordered.clear();
 24        self.channels_by_id.clear();
 25    }
 26
 27    /// Delete the given channels from this index.
 28    pub fn delete_channels(&mut self, channels: &[ChannelId]) {
 29        self.channels_by_id
 30            .retain(|channel_id, _| !channels.contains(channel_id));
 31        self.channels_ordered
 32            .retain(|channel_id| !channels.contains(channel_id));
 33    }
 34
 35    pub const fn bulk_insert(&mut self) -> ChannelPathsInsertGuard<'_> {
 36        ChannelPathsInsertGuard {
 37            channels_ordered: &mut self.channels_ordered,
 38            channels_by_id: &mut self.channels_by_id,
 39        }
 40    }
 41}
 42
 43/// A guard for ensuring that the paths index maintains its sort and uniqueness
 44/// invariants after a series of insertions
 45#[derive(Debug)]
 46pub struct ChannelPathsInsertGuard<'a> {
 47    channels_ordered: &'a mut Vec<ChannelId>,
 48    channels_by_id: &'a mut BTreeMap<ChannelId, Arc<Channel>>,
 49}
 50
 51impl ChannelPathsInsertGuard<'_> {
 52    pub fn insert(&mut self, channel_proto: proto::Channel) -> bool {
 53        let mut ret = false;
 54        let parent_path = channel_proto
 55            .parent_path
 56            .iter()
 57            .map(|cid| ChannelId(*cid))
 58            .collect();
 59        if let Some(existing_channel) = self.channels_by_id.get_mut(&ChannelId(channel_proto.id)) {
 60            let existing_channel = Arc::make_mut(existing_channel);
 61
 62            ret = existing_channel.visibility != channel_proto.visibility()
 63                || existing_channel.name != channel_proto.name
 64                || existing_channel.parent_path != parent_path
 65                || existing_channel.channel_order != channel_proto.channel_order;
 66
 67            existing_channel.visibility = channel_proto.visibility();
 68            existing_channel.name = channel_proto.name.into();
 69            existing_channel.parent_path = parent_path;
 70            existing_channel.channel_order = channel_proto.channel_order;
 71        } else {
 72            self.channels_by_id.insert(
 73                ChannelId(channel_proto.id),
 74                Arc::new(Channel {
 75                    id: ChannelId(channel_proto.id),
 76                    visibility: channel_proto.visibility(),
 77                    name: channel_proto.name.into(),
 78                    parent_path,
 79                    channel_order: channel_proto.channel_order,
 80                }),
 81            );
 82            self.insert_root(ChannelId(channel_proto.id));
 83        }
 84        ret
 85    }
 86
 87    fn insert_root(&mut self, channel_id: ChannelId) {
 88        self.channels_ordered.push(channel_id);
 89    }
 90}
 91
 92impl Drop for ChannelPathsInsertGuard<'_> {
 93    fn drop(&mut self) {
 94        self.channels_ordered.sort_by(|a, b| {
 95            let a = channel_path_sorting_key(*a, self.channels_by_id);
 96            let b = channel_path_sorting_key(*b, self.channels_by_id);
 97            a.cmp(b)
 98        });
 99        self.channels_ordered.dedup();
100    }
101}
102
103fn channel_path_sorting_key(
104    id: ChannelId,
105    channels_by_id: &BTreeMap<ChannelId, Arc<Channel>>,
106) -> impl Iterator<Item = (i32, ChannelId)> {
107    let (parent_path, order_and_id) =
108        channels_by_id
109            .get(&id)
110            .map_or((&[] as &[_], None), |channel| {
111                (
112                    channel.parent_path.as_slice(),
113                    Some((channel.channel_order, channel.id)),
114                )
115            });
116    parent_path
117        .iter()
118        .filter_map(|id| Some((channels_by_id.get(id)?.channel_order, *id)))
119        .chain(order_and_id)
120}