Repository: sharkdp/bat
File: src/line_range.rs (Rust)

Code before:
        let line_numbers: Vec<&str> = range_raw.split(':').collect();
        match line_numbers.len() {
            1 => {
                new_range.lower = line_numbers[0].parse()?;
                new_range.upper = new_range.lower;
                Ok(new_range)
            }
            2 => {
                new_range.lower = line_numbers[0].parse()?;
                let first_byte = line_numbers[1].bytes().next();

                new_range.upper = if first_byte == Some(b'+') {
                    let more_lines = &line_numbers[1][1..]
                        .parse()
                        .map_err(|_| "Invalid character after +")?;
                    new_range.lower + more_lines
                } else if first_byte == Some(b'-') {
                    // this will prevent values like "-+5" even though "+5" is valid integer
                    if line_numbers[1][1..].bytes().next() == Some(b'+') {
                        return Err("Invalid character after -".into());
                    }
                    let prior_lines = &line_numbers[1][1..]
                        .parse()
                        .map_err(|_| "Invalid character after -")?;
                    let prev_lower = new_range.lower;
                    new_range.lower -= prior_lines;
                    prev_lower
                } else {
                    line_numbers[1].parse()?
                };

                Ok(new_range)
            }
            _ => Err(
                "Line range contained more than one ':' character. Expected format: 'N' or 'N:M'"
                    .into(),
            ),
        }
    }

    pub(crate) fn is_inside(&self, line: usize) -> bool {
        line >= self.lower && line <= self.upper
    }
}

#[test]
fn test_parse_full() {
    let range = LineRange::from("40:50").expect("Shouldn't fail on test!");
    assert_eq!(40, range.lower);
    assert_eq!(50, range.upper);
}

Review comment (bug):
```suggestion
                    new_range.lower = new_range.lower.saturating_sub(prior_lines);
```

`saturating_sub` would be better. Otherwise `bat` would crash when overflow happens like `10:-100`

Code after:
        let line_numbers: Vec<&str> = range_raw.split(':').collect();
        match line_numbers.len() {
            1 => {
                new_range.lower = line_numbers[0].parse()?;
                new_range.upper = new_range.lower;
                Ok(new_range)
            }
            2 => {
                new_range.lower = line_numbers[0].parse()?;
                let first_byte = line_numbers[1].bytes().next();

                new_range.upper = if first_byte == Some(b'+') {
                    let more_lines = &line_numbers[1][1..]
                        .parse()
                        .map_err(|_| "Invalid character after +")?;
                    new_range.lower + more_lines
                } else if first_byte == Some(b'-') {
                    // this will prevent values like "-+5" even though "+5" is valid integer
                    if line_numbers[1][1..].bytes().next() == Some(b'+') {
                        return Err("Invalid character after -".into());
                    }
                    let prior_lines = &line_numbers[1][1..]
                        .parse()
                        .map_err(|_| "Invalid character after -")?;
                    let prev_lower = new_range.lower;
                    new_range.lower = new_range.lower.saturating_sub(*prior_lines);
                    prev_lower
                } else {
                    line_numbers[1].parse()?
                };

                Ok(new_range)
            }
            _ => Err(
                "Line range contained more than one ':' character. Expected format: 'N' or 'N:M'"
                    .into(),
            ),
        }
    }

    pub(crate) fn is_inside(&self, line: usize) -> bool {
        line >= self.lower && line <= self.upper
    }
}

#[test]
fn test_parse_full() {
    let range = LineRange::from("40:50").expect("Shouldn't fail on test!");
    assert_eq!(40, range.lower);
    assert_eq!(50, range.upper);
}