diff --git a/src/assets.rs b/src/assets.rs index 55bf3ef0..f6b10b3a 100644 --- a/src/assets.rs +++ b/src/assets.rs @@ -188,11 +188,11 @@ impl HighlightingAssets { pub(crate) fn get_syntax( &self, language: Option<&str>, - file: &Input, + input: &Input, reader: &mut InputReader, mapping: &SyntaxMapping, ) -> &SyntaxReference { - let syntax = match (language, file) { + let syntax = match (language, input) { (Some(language), _) => self.syntax_set.find_syntax_by_token(language), (None, Input::Ordinary(ofile)) => { let path = Path::new(ofile.provided_path()); @@ -282,12 +282,11 @@ mod tests { } let input = Input::Ordinary(OrdinaryFile::from_path(file_path.as_os_str())); - let syntax = self.assets.get_syntax( - None, - &input, - &mut input.get_reader(io::stdin().lock()).unwrap(), - &self.syntax_mapping, - ); + let stdin = io::stdin(); + let mut reader = input.get_reader(stdin.lock()).unwrap(); + let syntax = self + .assets + .get_syntax(None, &input, &mut reader, &self.syntax_mapping); syntax.name.clone() } diff --git a/src/controller.rs b/src/controller.rs index f9cf78ac..8d6b7fe0 100644 --- a/src/controller.rs +++ b/src/controller.rs @@ -5,7 +5,7 @@ use crate::config::Config; #[cfg(feature = "paging")] use crate::config::PagingMode; use crate::errors::*; -use crate::input::{Input, InputReader}; +use crate::input::{Input, InputDescription, InputReader}; use crate::line_range::{LineRanges, RangeCheckResult}; use crate::output::OutputType; use crate::printer::{InteractivePrinter, Printer, SimplePrinter}; @@ -61,6 +61,8 @@ impl<'b> Controller<'b> { let mut no_errors: bool = true; for input in inputs.into_iter() { + let description = input.description(); + match input.get_reader(io::stdin().lock()) { Err(error) => { handle_error(&error); @@ -69,7 +71,7 @@ impl<'b> Controller<'b> { Ok(mut reader) => { let result = if self.config.loop_through { let mut printer = SimplePrinter::new(); - self.print_file(reader, &mut printer, writer, &input) + self.print_file(reader, &mut printer, writer, &description) } else { let mut printer = InteractivePrinter::new( &self.config, @@ -77,7 +79,7 @@ impl<'b> Controller<'b> { &input, &mut reader, ); - self.print_file(reader, &mut printer, writer, &input) + self.print_file(reader, &mut printer, writer, &description) }; if let Err(error) = result { @@ -96,10 +98,10 @@ impl<'b> Controller<'b> { reader: InputReader, printer: &mut P, writer: &mut dyn Write, - input: &Input, + input_description: &InputDescription, ) -> Result<()> { if !reader.first_line.is_empty() || self.config.style_components.header() { - printer.print_header(writer, input)?; + printer.print_header(writer, input_description)?; } if !reader.first_line.is_empty() { diff --git a/src/input.rs b/src/input.rs index 7b2c371f..7b464824 100644 --- a/src/input.rs +++ b/src/input.rs @@ -7,6 +7,104 @@ use content_inspector::{self, ContentType}; use crate::errors::*; const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs"); +#[derive(Debug, Clone, PartialEq)] +pub struct OrdinaryFile { + path: OsString, + user_provided_path: Option, +} + +impl OrdinaryFile { + pub fn from_path(path: &OsStr) -> OrdinaryFile { + OrdinaryFile { + path: path.to_os_string(), + user_provided_path: None, + } + } + + pub fn set_provided_path(&mut self, user_provided_path: &OsStr) { + self.user_provided_path = Some(user_provided_path.to_os_string()); + } + + pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr { + self.user_provided_path + .as_ref() + .unwrap_or_else(|| &self.path) + } +} + +#[derive(Debug, Clone)] +pub struct InputDescription { + pub full: String, + pub prefix: String, + pub name: String, +} + +pub enum Input { + StdIn(Option), + Ordinary(OrdinaryFile), + FromReader(Box, Option), + ThemePreviewFile, +} + +impl Input { + pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result> { + match self { + Input::StdIn(_) => Ok(InputReader::new(stdin)), + Input::Ordinary(ofile) => { + let file = File::open(&ofile.path) + .map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?; + + if file.metadata()?.is_dir() { + return Err( + format!("'{}' is a directory.", ofile.path.to_string_lossy()).into(), + ); + } + + Ok(InputReader::new(BufReader::new(file))) + } + Input::ThemePreviewFile => Ok(InputReader::new(THEME_PREVIEW_FILE)), + Input::FromReader(_, _) => unimplemented!(), //Ok(InputReader::new(BufReader::new(reader))), + } + } + + pub(crate) fn description(&self) -> InputDescription { + match self { + Input::Ordinary(ofile) => InputDescription { + full: format!("file '{}'", &ofile.provided_path().to_string_lossy()), + prefix: "File: ".to_owned(), + name: ofile.provided_path().to_string_lossy().into_owned(), + }, + Input::StdIn(Some(name)) => InputDescription { + full: format!( + "STDIN (with name '{}')", + name.to_string_lossy().into_owned() + ), + prefix: "File: ".to_owned(), + name: name.to_string_lossy().into_owned(), + }, + Input::StdIn(None) => InputDescription { + full: "STDIN".to_owned(), + prefix: "".to_owned(), + name: "STDIN".to_owned(), + }, + Input::ThemePreviewFile => InputDescription { + full: "".to_owned(), + prefix: "".to_owned(), + name: "".to_owned(), + }, + Input::FromReader(_, Some(name)) => InputDescription { + full: format!("file '{}'", name.to_string_lossy()), + prefix: "File: ".to_owned(), + name: name.to_string_lossy().into_owned(), + }, + Input::FromReader(_, None) => InputDescription { + full: "reader".to_owned(), + prefix: "".to_owned(), + name: "READER".into(), + }, + } + } +} pub struct InputReader<'a> { inner: Box, @@ -52,60 +150,6 @@ impl<'a> InputReader<'a> { } } -#[derive(Debug, Clone, PartialEq)] -pub struct OrdinaryFile { - path: OsString, - user_provided_path: Option, -} - -impl OrdinaryFile { - pub fn from_path(path: &OsStr) -> OrdinaryFile { - OrdinaryFile { - path: path.to_os_string(), - user_provided_path: None, - } - } - - pub fn set_provided_path(&mut self, user_provided_path: &OsStr) { - self.user_provided_path = Some(user_provided_path.to_os_string()); - } - - pub(crate) fn provided_path<'a>(&'a self) -> &'a OsStr { - self.user_provided_path - .as_ref() - .unwrap_or_else(|| &self.path) - } -} - -pub enum Input { - StdIn(Option), - Ordinary(OrdinaryFile), - FromReader(Box, Option), - ThemePreviewFile, -} - -impl Input { - pub(crate) fn get_reader<'a, R: BufRead + 'a>(&self, stdin: R) -> Result> { - match self { - Input::StdIn(_) => Ok(InputReader::new(stdin)), - Input::Ordinary(ofile) => { - let file = File::open(&ofile.path) - .map_err(|e| format!("'{}': {}", ofile.path.to_string_lossy(), e))?; - - if file.metadata()?.is_dir() { - return Err( - format!("'{}' is a directory.", ofile.path.to_string_lossy()).into(), - ); - } - - Ok(InputReader::new(BufReader::new(file))) - } - Input::ThemePreviewFile => Ok(InputReader::new(THEME_PREVIEW_FILE)), - Input::FromReader(_, _) => unimplemented!(), //Ok(InputReader::new(BufReader::new(reader))), - } - } -} - #[test] fn basic() { let content = b"#!/bin/bash\necho hello"; diff --git a/src/pretty_printer.rs b/src/pretty_printer.rs index a03b0e3b..5b58526f 100644 --- a/src/pretty_printer.rs +++ b/src/pretty_printer.rs @@ -1,4 +1,5 @@ use std::ffi::OsStr; +use std::io::Read; use crate::{ config::{ @@ -52,6 +53,18 @@ impl<'a> PrettyPrinter<'a> { self } + /// Add STDIN as an input + pub fn input_stdin(&mut self) -> &mut Self { + self.inputs.push(Input::StdIn(None)); + self + } + + /// Add STDIN as an input + pub fn input_reader(&mut self, reader: impl Read) -> &mut Self { + //self.inputs.push(Input::FromReader(Box::new(reader), None)); + self + } + /// Specify the syntax file which should be used (default: auto-detect) pub fn language(&mut self, language: &'a str) -> &mut Self { self.config.language = Some(language); diff --git a/src/printer.rs b/src/printer.rs index b61c8063..0fc914f2 100644 --- a/src/printer.rs +++ b/src/printer.rs @@ -27,14 +27,14 @@ use crate::decorations::{Decoration, GridBorderDecoration, LineNumberDecoration} #[cfg(feature = "git")] use crate::diff::{get_git_diff, LineChanges}; use crate::errors::*; -use crate::input::{Input, InputReader}; +use crate::input::{Input, InputDescription, InputReader}; use crate::line_range::RangeCheckResult; use crate::preprocessor::{expand_tabs, replace_nonprintable}; use crate::terminal::{as_terminal_escaped, to_ansi_color}; use crate::wrap::WrappingMode; pub trait Printer { - fn print_header(&mut self, handle: &mut dyn Write, file: &Input) -> Result<()>; + fn print_header(&mut self, handle: &mut dyn Write, input: &InputDescription) -> Result<()>; fn print_footer(&mut self, handle: &mut dyn Write) -> Result<()>; fn print_snip(&mut self, handle: &mut dyn Write) -> Result<()>; @@ -57,7 +57,7 @@ impl SimplePrinter { } impl Printer for SimplePrinter { - fn print_header(&mut self, _handle: &mut dyn Write, _file: &Input) -> Result<()> { + fn print_header(&mut self, _handle: &mut dyn Write, input: &InputDescription) -> Result<()> { Ok(()) } @@ -101,7 +101,7 @@ impl<'a> InteractivePrinter<'a> { pub fn new( config: &'a Config, assets: &'a HighlightingAssets, - file: &Input, + input: &Input, reader: &mut InputReader, ) -> Self { let theme = assets.get_theme(&config.theme); @@ -160,14 +160,14 @@ impl<'a> InteractivePrinter<'a> { #[cfg(feature = "git")] { if config.style_components.changes() { - if let Input::Ordinary(ofile) = file { + if let Input::Ordinary(ofile) = input { line_changes = get_git_diff(ofile.provided_path()); } } } // Determine the type of syntax for highlighting - let syntax = assets.get_syntax(config.language, file, reader, &config.syntax_mapping); + let syntax = assets.get_syntax(config.language, input, reader, &config.syntax_mapping); Some(HighlightLines::new(syntax, theme)) }; @@ -230,32 +230,20 @@ impl<'a> InteractivePrinter<'a> { } impl<'a> Printer for InteractivePrinter<'a> { - fn print_header(&mut self, handle: &mut dyn Write, file: &Input) -> Result<()> { + fn print_header( + &mut self, + handle: &mut dyn Write, + description: &InputDescription, + ) -> Result<()> { if !self.config.style_components.header() { if Some(ContentType::BINARY) == self.content_type && !self.config.show_nonprintable { - let input = match file { - Input::Ordinary(ofile) => { - format!("file '{}'", &ofile.provided_path().to_string_lossy()) - } - Input::StdIn(Some(name)) => format!( - "STDIN (with name '{}')", - name.to_string_lossy().into_owned() - ), - Input::StdIn(None) => "STDIN".to_owned(), - Input::ThemePreviewFile => "".to_owned(), - Input::FromReader(_, Some(name)) => { - format!("file '{}'", name.to_string_lossy()) - } - Input::FromReader(_, None) => "READER".to_owned(), - }; - writeln!( handle, "{}: Binary content from {} will not be printed to the terminal \ (but will be present if the output of 'bat' is piped). You can use 'bat -A' \ to show the binary file contents.", Yellow.paint("[bat warning]"), - input + description.full, )?; } else { if self.config.style_components.grid() { @@ -280,20 +268,6 @@ impl<'a> Printer for InteractivePrinter<'a> { write!(handle, "{}", " ".repeat(self.panel_width))?; } - let (prefix, name) = match file { - Input::Ordinary(ofile) => ( - "File: ", - Cow::from(ofile.provided_path().to_string_lossy().to_owned()), - ), - Input::StdIn(Some(name)) => ("File: ", Cow::from(name.to_string_lossy().to_owned())), - Input::StdIn(None) => ("File: ", Cow::from("STDIN".to_owned())), - Input::ThemePreviewFile => ("", Cow::from("")), - Input::FromReader(_, Some(name)) => { - ("File: ", Cow::from(name.to_string_lossy().to_owned())) - } - Input::FromReader(_, None) => ("File: ", Cow::from("READER".to_owned())), - }; - let mode = match self.content_type { Some(ContentType::BINARY) => " ", Some(ContentType::UTF_16LE) => " ", @@ -305,8 +279,8 @@ impl<'a> Printer for InteractivePrinter<'a> { writeln!( handle, "{}{}{}", - prefix, - self.colors.filename.paint(name), + description.prefix, + self.colors.filename.paint(&description.name), mode )?;