Skip to main content

gen_core/
region.rs

1use std::num::ParseIntError;
2
3use thiserror::Error;
4
5#[derive(Debug, PartialEq)]
6pub struct Region {
7    pub name: String,
8    pub start: i64,
9    pub end: i64,
10}
11
12#[derive(Debug, Error, PartialEq)]
13pub enum RegionParseError {
14    #[error("Region start is less than region end")]
15    InvalidRange,
16    #[error("Parsing error: Region name not present")]
17    NoName,
18    #[error("Parsing error: Region coordinates not present")]
19    NoCoordinates,
20    #[error("Parsing error: Start coordinate not present")]
21    NoStartCoordinate,
22    #[error("Parsing error: End coordinate not present")]
23    NoEndCoordinate,
24    #[error("Parsing error: Invalid coordinate: {0}")]
25    InvalidCoordinate(#[from] ParseIntError),
26}
27
28impl Region {
29    pub fn parse(region_string: &str) -> Result<Self, RegionParseError> {
30        // Example input: "chr1:100-200"
31        let mut parts = region_string.split(':');
32        let name = parts.next().ok_or(RegionParseError::NoName)?.to_string();
33        let interval = parts.next().ok_or(RegionParseError::NoCoordinates)?;
34        let mut bounds = interval.split('-');
35        let start = bounds
36            .next()
37            .ok_or(RegionParseError::NoStartCoordinate)?
38            .parse::<i64>()
39            .map_err(RegionParseError::InvalidCoordinate)?;
40        let end = bounds
41            .next()
42            .ok_or(RegionParseError::NoEndCoordinate)?
43            .parse::<i64>()
44            .map_err(RegionParseError::InvalidCoordinate)?;
45        if start > end {
46            return Err(RegionParseError::InvalidRange);
47        }
48
49        Ok(Self { name, start, end })
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56
57    #[test]
58    fn test_parse_valid_region() {
59        let region = Region::parse("chr1:100-200");
60        assert_eq!(
61            region,
62            Ok(Region {
63                name: "chr1".to_string(),
64                start: 100,
65                end: 200,
66            })
67        );
68    }
69
70    #[test]
71    fn test_parse_invalid_format() {
72        let region = Region::parse("chr1-100-200");
73        assert_eq!(region, Err(RegionParseError::NoCoordinates));
74    }
75
76    #[test]
77    fn test_parse_start_greater_than_end() {
78        let region = Region::parse("chr1:300-200");
79        assert_eq!(region, Err(RegionParseError::InvalidRange));
80    }
81
82    #[test]
83    fn test_region_applies_to_array_slice() {
84        let string = "foobarbaz";
85        let region = Region::parse("chr1:3-5").unwrap();
86        let slice = &string[region.start as usize..region.end as usize];
87        assert_eq!(slice, "ba");
88    }
89}