subxt 0.50.0

Interact with Substrate based chains on the Polkadot Network
Documentation
use std::fmt::Display;

/// A map that associates ranges of keys with values.
#[derive(Debug, Clone)]
pub struct RangeMap<K, V> {
    // (range_start, range_ended, value). This is
    // guaranteed to be sorted and have non-overlapping ranges.
    mapping: Vec<(K, K, V)>,
}

impl<K: Clone + Copy + Display + PartialOrd + Ord, V> RangeMap<K, V> {
    /// Build an empty [`RangeMap`] as a placeholder.
    pub fn empty() -> Self {
        RangeMap {
            mapping: Vec::new(),
        }
    }

    /// Build a [`RangeMap`].
    pub fn builder() -> RangeMapBuilder<K, V> {
        RangeMapBuilder {
            mapping: Vec::new(),
        }
    }

    /// Return the value whose key is within the range, or None if not found.
    pub fn get(&self, key: K) -> Option<&V> {
        let idx = self
            .mapping
            .binary_search_by_key(&key, |&(start, end, _)| {
                if key >= start && key < end {
                    key
                } else {
                    start
                }
            })
            .ok()?;

        self.mapping.get(idx).map(|(_, _, val)| val)
    }
}

/// A builder for constructing a [`RangeMap`]. Use [``RangeMap::builder()`] to create one.
#[derive(Debug, Clone)]
pub struct RangeMapBuilder<K, V> {
    mapping: Vec<(K, K, V)>,
}

impl<K: Clone + Copy + Display + PartialOrd + Ord, V> RangeMapBuilder<K, V> {
    /// Try to add a range, mapping block numbers to a spec version.
    ///
    /// Returns an error if the range is empty or overlaps with an existing range.
    pub fn try_add_range(
        &mut self,
        start: K,
        end: K,
        val: V,
    ) -> Result<&mut Self, RangeMapError<K>> {
        let (start, end) = if start < end {
            (start, end)
        } else {
            (end, start)
        };

        if start == end {
            return Err(RangeMapError::EmptyRange(start));
        }

        if let Some(&(s, e, _)) = self.mapping.iter().find(|&&(s, e, _)| start < e && end > s) {
            return Err(RangeMapError::OverlappingRanges {
                proposed: (start, end),
                existing: (s, e),
            });
        }

        self.mapping.push((start, end, val));
        Ok(self)
    }

    /// Add a range of blocks with the given spec version.
    ///
    /// # Panics
    ///
    /// This method will panic if the range is empty or overlaps with an existing range.
    pub fn add_range(mut self, start: K, end: K, val: V) -> Self {
        if let Err(e) = self.try_add_range(start, end, val) {
            panic!("{e}")
        }
        self
    }

    /// Finish adding ranges and build the [`RangeMap`].
    pub fn build(mut self) -> RangeMap<K, V> {
        self.mapping.sort_by_key(|&(start, _, _)| start);
        RangeMap {
            mapping: self.mapping,
        }
    }
}

/// An error that can occur when calling [`RangeMapBuilder::try_add_range()`].
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum RangeMapError<K: Display> {
    /// An error indicating that the proposed block range is empty.
    #[error("Block range cannot be empty: start and end values must be different, but got {} for both", .0)]
    EmptyRange(K),
    /// An error indicating that the proposed block range overlaps with an existing one.
    #[error("Overlapping block ranges are not allowed: proposed range is {}..{}, but we already have {}..{}", proposed.0, proposed.1, existing.0, existing.1)]
    OverlappingRanges {
        /// The range being proposed / added.
        proposed: (K, K),
        /// The existing range which overlaps.
        existing: (K, K),
    },
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_rangemap_get() {
        let spec_version = RangeMap::builder()
            .add_range(0, 100, 1)
            .add_range(100, 200, 2)
            .add_range(200, 300, 3)
            .build();

        assert_eq!(spec_version.get(0), Some(&1));
        assert_eq!(spec_version.get(50), Some(&1));
        assert_eq!(spec_version.get(100), Some(&2));
        assert_eq!(spec_version.get(150), Some(&2));
        assert_eq!(spec_version.get(200), Some(&3));
        assert_eq!(spec_version.get(250), Some(&3));
        assert_eq!(spec_version.get(300), None);
    }

    #[test]
    fn test_rangemap_set() {
        let mut spec_version = RangeMap::builder()
            .add_range(0, 100, 1)
            .add_range(200, 300, 3);

        assert_eq!(
            spec_version.try_add_range(99, 130, 2).unwrap_err(),
            RangeMapError::OverlappingRanges {
                proposed: (99, 130),
                existing: (0, 100),
            }
        );
        assert_eq!(
            spec_version.try_add_range(170, 201, 2).unwrap_err(),
            RangeMapError::OverlappingRanges {
                proposed: (170, 201),
                existing: (200, 300),
            }
        );
    }
}