From eea1418c9010aec9f5923d3fd4e073625c4dcd09 Mon Sep 17 00:00:00 2001
From: Eduardo Trujillo <ed@chromabits.com>
Date: Sun, 16 Mar 2025 21:32:11 +0000
Subject: [PATCH] feat(rangeset): Add size methods and conversion from Vec

---
 src/rangeset.rs | 44 +++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/src/rangeset.rs b/src/rangeset.rs
index af804ff..49de772 100644
--- a/src/rangeset.rs
+++ b/src/rangeset.rs
@@ -1,9 +1,9 @@
-use std::collections::BTreeMap;
+use std::{collections::BTreeMap, convert::TryFrom};
 
 use thiserror::Error;
 
 /// Error type for [`RangeSet`]
-#[derive(Clone, Error, Debug, PartialEq)]
+#[derive(Clone, Error, Debug, PartialEq, Eq)]
 pub enum RangeSetError {
     /// Used when an invalid range is provided (e.g. End is before start).
     #[error("Invalid range provided")]
@@ -14,7 +14,7 @@ pub enum RangeSetError {
 }
 
 /// A set of non-overlapping ranges.
-#[derive(Clone, Debug)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub struct RangeSet<RK: Ord + Copy> {
     ranges: BTreeMap<RK, RK>, // Maps start -> end
 }
@@ -73,6 +73,16 @@ impl<RK: Ord + Copy> RangeSet<RK> {
     pub fn iter(&self) -> impl Iterator<Item = (RK, RK)> + '_ {
         self.ranges.iter().map(|(&s, &e)| (s, e))
     }
+
+    /// Returns the number of elements in the set.
+    pub fn len(&self) -> usize {
+        self.ranges.len()
+    }
+
+    /// Returns whether this set is empty.
+    pub fn is_empty(&self) -> bool {
+        self.ranges.is_empty()
+    }
 }
 
 impl<RK: Copy + Ord> Default for RangeSet<RK> {
@@ -81,8 +91,24 @@ impl<RK: Copy + Ord> Default for RangeSet<RK> {
     }
 }
 
+impl<RK: Copy + Ord> TryFrom<Vec<(RK, RK)>> for RangeSet<RK> {
+    type Error = RangeSetError;
+
+    fn try_from(value: Vec<(RK, RK)>) -> Result<Self, Self::Error> {
+        let mut range_set = RangeSet::new();
+
+        for range in value {
+            range_set.insert(range.0, range.1)?;
+        }
+
+        Ok(range_set)
+    }
+}
+
 #[cfg(test)]
 mod tests {
+    use std::convert::TryInto;
+
     use crate::rangeset::{RangeSet, RangeSetError};
 
     #[test]
@@ -107,4 +133,16 @@ mod tests {
         assert!(rs.remove(30, 40));
         assert!(!rs.remove(30, 40));
     }
+
+    #[test]
+    fn test_try_from_vec() {
+        let range_set: RangeSet<u32> = vec![(0, 10), (100, 150)].try_into().unwrap();
+
+        assert_eq!(range_set.len(), 2);
+
+        let invalid_result: Result<RangeSet<u32>, RangeSetError> =
+            vec![(10, 30), (11, 23)].try_into();
+
+        assert_eq!(invalid_result, Err(RangeSetError::RangeOverlap))
+    }
 }
-- 
GitLab