on v0.14.0 help sign in
bat/src/controller.rs
157 lines 5.1 kB history raw
1use std::io::{self, Write};
3use crate::assets::HighlightingAssets;
4use crate::config::Config;
5use crate::error::*;
6use crate::input::{Input, InputReader, OpenedInput};
7use crate::line_range::{LineRanges, RangeCheckResult};
8use crate::output::OutputType;
9#[cfg(feature = "paging")]
10use crate::paging::PagingMode;
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, inputs: Vec<Input>) -> Result<bool> {
24 self.run_with_error_handler(inputs, default_error_handler)
25 }
27 pub fn run_with_error_handler(
28 &self,
29 inputs: Vec<Input>,
30 handle_error: impl Fn(&Error),
31 ) -> Result<bool> {
32 let mut output_type;
34 #[cfg(feature = "paging")]
35 {
36 use crate::input::InputKind;
37 use std::path::Path;
39 // Do not launch the pager if NONE of the input files exist
40 let mut paging_mode = self.config.paging_mode;
41 if self.config.paging_mode != PagingMode::Never {
42 let call_pager = inputs.iter().any(|ref input| {
43 if let InputKind::OrdinaryFile(ref path) = input.kind {
44 return Path::new(path).exists();
45 } else {
46 return true;
47 }
48 });
49 if !call_pager {
50 paging_mode = PagingMode::Never;
51 }
52 }
53 output_type = OutputType::from_mode(paging_mode, self.config.pager)?;
54 }
56 #[cfg(not(feature = "paging"))]
57 {
58 output_type = OutputType::stdout();
59 }
61 let writer = output_type.handle()?;
62 let mut no_errors: bool = true;
64 for input in inputs.into_iter() {
65 match input.open(io::stdin().lock()) {
66 Err(error) => {
67 handle_error(&error);
68 no_errors = false;
69 }
70 Ok(mut opened_input) => {
71 let mut printer: Box<dyn Printer> = if self.config.loop_through {
72 Box::new(SimplePrinter::new())
73 } else {
74 Box::new(InteractivePrinter::new(
75 &self.config,
76 &self.assets,
77 &mut opened_input,
78 ))
79 };
81 let result = self.print_file(&mut *printer, writer, &mut opened_input);
83 if let Err(error) = result {
84 handle_error(&error);
85 no_errors = false;
86 }
87 }
88 }
89 }
91 Ok(no_errors)
92 }
94 fn print_file<'a>(
95 &self,
96 printer: &mut dyn Printer,
97 writer: &mut dyn Write,
98 input: &mut OpenedInput,
99 ) -> Result<()> {
100 if !input.reader.first_line.is_empty() || self.config.style_components.header() {
101 printer.print_header(writer, input)?;
102 }
104 if !input.reader.first_line.is_empty() {
105 self.print_file_ranges(printer, writer, &mut input.reader, &self.config.line_ranges)?;
106 }
107 printer.print_footer(writer, input)?;
109 Ok(())
110 }
112 fn print_file_ranges(
113 &self,
114 printer: &mut dyn Printer,
115 writer: &mut dyn Write,
116 reader: &mut InputReader,
117 line_ranges: &LineRanges,
118 ) -> Result<()> {
119 let mut line_buffer = Vec::new();
120 let mut line_number: usize = 1;
122 let mut first_range: bool = true;
123 let mut mid_range: bool = false;
125 while reader.read_line(&mut line_buffer)? {
126 match line_ranges.check(line_number) {
127 RangeCheckResult::BeforeOrBetweenRanges => {
128 // Call the printer in case we need to call the syntax highlighter
129 // for this line. However, set `out_of_range` to `true`.
130 printer.print_line(true, writer, line_number, &line_buffer)?;
131 mid_range = false;
132 }
134 RangeCheckResult::InRange => {
135 if self.config.style_components.snip() {
136 if first_range {
137 first_range = false;
138 mid_range = true;
139 } else if !mid_range {
140 mid_range = true;
141 printer.print_snip(writer)?;
142 }
143 }
145 printer.print_line(false, writer, line_number, &line_buffer)?;
146 }
147 RangeCheckResult::AfterLastRange => {
148 break;
149 }
150 }
152 line_number += 1;
153 line_buffer.clear();
154 }
155 Ok(())
156 }