on 37b3b87 help sign in
bat/src/controller.rs
149 lines 5.2 kB history raw
1use std::io::{self, Write};
2use std::path::Path;
4use crate::assets::HighlightingAssets;
5use crate::config::{Config, PagingMode};
6use crate::errors::*;
7use crate::inputfile::{InputFile, InputFileReader};
8use crate::line_range::{LineRanges, RangeCheckResult};
9use crate::output::OutputType;
10use crate::printer::{InteractivePrinter, Printer, SimplePrinter};
12pub struct Controller<'a> {
13 config: &'a Config<'a>,
14 assets: &'a HighlightingAssets,
17impl<'b> Controller<'b> {
18 pub fn new<'a>(config: &'a Config, assets: &'a HighlightingAssets) -> Controller<'a> {
19 Controller { config, assets }
20 }
22 pub fn run(&self) -> Result<bool> {
23 self.run_with_error_handler(default_error_handler)
24 }
26 pub fn run_with_error_handler(&self, handle_error: impl Fn(&Error)) -> Result<bool> {
27 // Do not launch the pager if NONE of the input files exist
28 let mut paging_mode = self.config.paging_mode;
29 if self.config.paging_mode != PagingMode::Never {
30 let call_pager = self.config.files.iter().any(|file| {
31 if let InputFile::Ordinary(path) = file {
32 return Path::new(path).exists();
33 } else {
34 return true;
35 }
36 });
37 if !call_pager {
38 paging_mode = PagingMode::Never;
39 }
40 }
42 let mut output_type = OutputType::from_mode(paging_mode, self.config.pager)?;
43 let writer = output_type.handle()?;
44 let mut no_errors: bool = true;
46 let stdin = io::stdin();
48 let filenames: Box<dyn Iterator<Item = _>> = match self.config.filenames {
49 Some(ref filenames) => Box::new(filenames.into_iter().map(|name| Some(*name))),
50 None => Box::new(std::iter::repeat(None)),
51 };
53 for (input_file, file_name) in self.config.files.iter().zip(filenames) {
54 match input_file.get_reader(&stdin) {
55 Err(error) => {
56 handle_error(&error);
57 no_errors = false;
58 }
59 Ok(mut reader) => {
60 let result = if self.config.loop_through {
61 let mut printer = SimplePrinter::new();
62 self.print_file(reader, &mut printer, writer, *input_file, file_name)
63 } else {
64 let mut printer = InteractivePrinter::new(
65 &self.config,
66 &self.assets,
67 *input_file,
68 &mut reader,
69 );
70 self.print_file(reader, &mut printer, writer, *input_file, file_name)
71 };
73 if let Err(error) = result {
74 handle_error(&error);
75 no_errors = false;
76 }
77 }
78 }
79 }
81 Ok(no_errors)
82 }
84 fn print_file<'a, P: Printer>(
85 &self,
86 reader: InputFileReader,
87 printer: &mut P,
88 writer: &mut dyn Write,
89 input_file: InputFile<'a>,
90 file_name: Option<&str>,
91 ) -> Result<()> {
92 if !reader.first_line.is_empty() || self.config.style_components.header() {
93 printer.print_header(writer, input_file, file_name)?;
94 }
96 if !reader.first_line.is_empty() {
97 self.print_file_ranges(printer, writer, reader, &self.config.line_ranges)?;
98 }
99 printer.print_footer(writer)?;
101 Ok(())
102 }
104 fn print_file_ranges<P: Printer>(
105 &self,
106 printer: &mut P,
107 writer: &mut dyn Write,
108 mut reader: InputFileReader,
109 line_ranges: &LineRanges,
110 ) -> Result<()> {
111 let mut line_buffer = Vec::new();
112 let mut line_number: usize = 1;
114 let mut first_range: bool = true;
115 let mut mid_range: bool = false;
117 while reader.read_line(&mut line_buffer)? {
118 match line_ranges.check(line_number) {
119 RangeCheckResult::BeforeOrBetweenRanges => {
120 // Call the printer in case we need to call the syntax highlighter
121 // for this line. However, set `out_of_range` to `true`.
122 printer.print_line(true, writer, line_number, &line_buffer)?;
123 mid_range = false;
124 }
126 RangeCheckResult::InRange => {
127 if self.config.style_components.snip() {
128 if first_range {
129 first_range = false;
130 mid_range = true;
131 } else if !mid_range {
132 mid_range = true;
133 printer.print_snip(writer)?;
134 }
135 }
137 printer.print_line(false, writer, line_number, &line_buffer)?;
138 }
139 RangeCheckResult::AfterLastRange => {
140 break;
141 }
142 }
144 line_number += 1;
145 line_buffer.clear();
146 }
147 Ok(())
148 }