diff --git a/src/app.rs b/src/app.rs index 7b92c2db..54baa39f 100644 --- a/src/app.rs +++ b/src/app.rs @@ -148,8 +148,10 @@ impl App { .takes_value(true) .help("Print range of lines") .long_help( - "Print a specified range of lines from the files \ - --line-range 30-40 or --line-range 30-", + "Print a specified range or ranges of lines from the files. \ + For example: '--line-range 30:40' will print lines 30 to 40 \n\ + '--line-range :40' will print lines 1 to 40 \n\ + '--line-range 40:' will print lines 40 to the end of the file", ), ) .arg( @@ -280,7 +282,7 @@ impl App { term_width: Term::stdout().size().1 as usize, files, theme: self.matches.value_of("theme"), - line_range: get_ranges(self.matches.value_of("line-range")), + line_range: LineRange::from(self.matches.value_of("line-range")), }) } @@ -334,7 +336,7 @@ pub struct Config<'a> { pub term_width: usize, pub files: Vec>, pub theme: Option<&'a str>, - pub line_range: Option<(usize, usize)>, + pub line_range: Option, } fn is_truecolor_terminal() -> bool { @@ -343,28 +345,61 @@ fn is_truecolor_terminal() -> bool { .unwrap_or(false) } -fn get_ranges(value: Option<&str>) -> Option<(usize, usize)> { - match value { - None => None, - Some(str_range) => { - let mut new_range = (usize::min_value(), usize::max_value()); - if str_range.bytes().nth(0).expect("Something should be here!") == b':' { - new_range.1 = str_range[1..].parse().expect("This should be a number!"); - return Some(new_range); - } else if str_range.bytes().last().expect("There should be a last!") == b':' { - new_range.0 = str_range[..str_range.len() - 1] - .parse() - .expect("This should be a number!"); - return Some(new_range); - } +pub struct LineRange { + pub lower: usize, + pub upper: usize, +} - let line_numbers: Vec<&str> = str_range.split(':').collect(); - if line_numbers.len() == 2 { - new_range.0 = line_numbers[0].parse().expect("Should be a number!"); - new_range.1 = line_numbers[1].parse().expect("Should be a number!"); +impl LineRange { + pub fn from(value: Option<&str>) -> Option { + match value { + None => None, + Some(range_raw) => { + return LineRange::parse_range(range_raw).ok(); } + } + } + + pub fn parse_range(range_raw: &str) -> Result { + let mut new_range = LineRange{ + lower: usize::min_value(), + upper: usize::max_value(), + }; + + if range_raw.bytes().nth(0).ok_or("No first byte")? == b':' { + new_range.upper = range_raw[1..].parse()?; + return Ok(new_range); + } else if range_raw.bytes().last().ok_or("No last byte")? == b':' { + new_range.lower = range_raw[..range_raw.len() - 1].parse()?; + return Ok(new_range); + } - Some(new_range) + let line_numbers: Vec<&str> = range_raw.split(':').collect(); + if line_numbers.len() == 2 { + new_range.lower = line_numbers[0].parse()?; + new_range.upper = line_numbers[1].parse()?; } + Ok(new_range) } } + +#[test] +fn test_parse_line_range_full() { + let range = LineRange::from(Some("40:50")).expect("Shouldn't fail on test!"); + assert_eq!(40, range.lower); + assert_eq!(50, range.upper); +} + +#[test] +fn test_parse_line_range_partial_min() { + let range = LineRange::from(Some(":50")).expect("Shouldn't fail on test!"); + assert_eq!(usize::min_value(), range.lower); + assert_eq!(50, range.upper); +} + +#[test] +fn test_parse_line_range_partial_max() { + let range = LineRange::from(Some("40:")).expect("Shouldn't fail on test!"); + assert_eq!(40, range.lower); + assert_eq!(usize::max_value(), range.upper); +} diff --git a/src/features.rs b/src/features.rs index 1ef4a2c5..9eedb297 100644 --- a/src/features.rs +++ b/src/features.rs @@ -1,5 +1,5 @@ use ansi_term::Colour::Green; -use app::Config; +use app::{Config,LineRange}; use assets::HighlightingAssets; use diff::get_git_diff; use errors::*; @@ -85,16 +85,35 @@ fn print_file( filename: Option<&str>, ) -> Result<()> { let stdin = io::stdin(); // TODO: this is not always needed + { + let reader: Box = match filename { + None => Box::new(stdin.lock()), + Some(filename) => Box::new(BufReader::new(File::open(filename)?)), + }; + + let highlighter = HighlightLines::new(syntax, theme); + + printer.print_header(filename)?; + + match printer.config.line_range.as_ref() { + Some(range) => { + print_file_ranges(printer, reader, highlighter, range)?; + }, + None => { + print_file_no_ranges(printer, reader, highlighter)?; + } + } + printer.print_footer()?; + } + Ok(()) +} - let mut reader: Box = match filename { - None => Box::new(stdin.lock()), - Some(filename) => Box::new(BufReader::new(File::open(filename)?)), - }; - - let mut highlighter = HighlightLines::new(syntax, theme); - - printer.print_header(filename)?; - +fn print_file_ranges<'a>( + printer: &mut Printer, + mut reader: Box, + mut highlighter: HighlightLines, + ranges: &LineRange +) -> Result<()>{ let mut buffer = Vec::new(); while reader.read_until(b'\n', &mut buffer)? > 0 { @@ -102,24 +121,36 @@ fn print_file( let line = String::from_utf8_lossy(&buffer); let regions = highlighter.highlight(line.as_ref()); - if printer.config.line_range.is_some() { - if printer.line_number + 1 < printer.config.line_range.unwrap().0 { - // skip line - printer.line_number += 1; - } else if printer.line_number >= printer.config.line_range.unwrap().1 { - // no more lines in range - break; - } else { - printer.print_line(®ions)?; - } + if printer.line_number + 1 < ranges.lower { + // skip line + printer.line_number += 1; + } else if printer.line_number >= ranges.upper { + // no more lines in range + break; } else { printer.print_line(®ions)?; } } buffer.clear(); } + Ok(()) +} - printer.print_footer()?; +fn print_file_no_ranges<'a>( + printer: &mut Printer, + mut reader: Box, + mut highlighter: HighlightLines +) -> Result<()>{ + let mut buffer = Vec::new(); + + while reader.read_until(b'\n', &mut buffer)? > 0 { + { + let line = String::from_utf8_lossy(&buffer); + let regions = highlighter.highlight(line.as_ref()); + printer.print_line(®ions)?; + } + buffer.clear(); + } Ok(()) } diff --git a/src/main.rs b/src/main.rs index 93736cac..3d882b75 100644 --- a/src/main.rs +++ b/src/main.rs @@ -41,6 +41,7 @@ mod errors { Clap(::clap::Error); Io(::std::io::Error); SyntectError(::syntect::LoadingError); + ParseIntError(::std::num::ParseIntError); } }