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