on master help sign in
Refactor InputDescription API into Input API
798b742Ethan P
src/bin/bat/app.rs+3−3
@@ -257,7 +257,7 @@ impl App {
257 let files: Option<Vec<&OsStr>> = self.matches.values_of_os("FILE").map(|vs| vs.collect());
257 let files: Option<Vec<&OsStr>> = self.matches.values_of_os("FILE").map(|vs| vs.collect());
258
258
259 if files.is_none() {
259 if files.is_none() {
260− let input = Input::stdin().with_name(filenames_or_none.next().unwrap_or(None));
260+ let input = Input::stdin().as_file(filenames_or_none.next().unwrap_or(None));
261 return Ok(vec![input]);
261 return Ok(vec![input]);
262 }
262 }
263 let files_or_none: Box<dyn Iterator<Item = _>> = match files {
263 let files_or_none: Box<dyn Iterator<Item = _>> = match files {
@@ -269,9 +269,9 @@ impl App {
269 for (filepath, provided_name) in files_or_none.zip(filenames_or_none) {
269 for (filepath, provided_name) in files_or_none.zip(filenames_or_none) {
270 if let Some(filepath) = filepath {
270 if let Some(filepath) = filepath {
271 if filepath.to_str().unwrap_or_default() == "-" {
271 if filepath.to_str().unwrap_or_default() == "-" {
272− file_input.push(Input::stdin().with_name(provided_name));
272+ file_input.push(Input::stdin().as_file(provided_name));
273 } else {
273 } else {
274− file_input.push(Input::ordinary_file(filepath).with_name(provided_name));
274+ file_input.push(Input::ordinary_file(filepath).as_file(provided_name));
275 }
275 }
276 }
276 }
277 }
277 }
src/bin/bat/main.rs−2
@@ -25,7 +25,6 @@ use assets::{assets_from_cache_or_binary, cache_dir, clear_assets, config_dir};
25use clap::crate_version;
25use clap::crate_version;
26use directories::PROJECT_DIRS;
26use directories::PROJECT_DIRS;
27
27
28−use bat::input::InputDescription;
29use bat::{
28use bat::{
30 assets::HighlightingAssets,
29 assets::HighlightingAssets,
31 config::Config,
30 config::Config,
@@ -124,7 +123,6 @@ pub fn list_languages(config: &Config) -> Result<()> {
124fn theme_preview_file<'a>() -> Input<'a> {
123fn theme_preview_file<'a>() -> Input<'a> {
125 Input::from_reader(Box::new(BufReader::new(THEME_PREVIEW_DATA)))
124 Input::from_reader(Box::new(BufReader::new(THEME_PREVIEW_DATA)))
126 .with_name(Some("theme.rs".as_ref()))
125 .with_name(Some("theme.rs".as_ref()))
127− .with_description(Some(InputDescription::new("")))
128}
126}
129
127
130pub fn list_themes(cfg: &Config) -> Result<()> {
128pub fn list_themes(cfg: &Config) -> Result<()> {
src/input.rs+49−36
@@ -10,39 +10,48 @@ use crate::error::*;
10/// This tells bat how to refer to the input.
10/// This tells bat how to refer to the input.
11#[derive(Clone)]
11#[derive(Clone)]
12pub struct InputDescription {
12pub struct InputDescription {
13− name: String,
13+ pub(crate) name: String,
14+
15+ /// The input title.
16+ /// This replaces the name if provided.
17+ title: Option<String>,
18+
19+ /// The input kind.
14 kind: Option<String>,
20 kind: Option<String>,
21+
22+ /// A summary description of the input.
23+ /// Defaults to "{kind} '{name}'"
15 summary: Option<String>,
24 summary: Option<String>,
16}
25}
17
26
18impl InputDescription {
27impl InputDescription {
19 /// Creates a description for an input.
28 /// Creates a description for an input.
20− ///
21− /// The name should describe where the input came from (e.g. "README.md")
22 pub fn new(name: impl Into<String>) -> Self {
29 pub fn new(name: impl Into<String>) -> Self {
23 InputDescription {
30 InputDescription {
24 name: name.into(),
31 name: name.into(),
32+ title: None,
25 kind: None,
33 kind: None,
26 summary: None,
34 summary: None,
27 }
35 }
28 }
36 }
29
37
30− /// A description for the type of input (e.g. "File")
31− pub fn with_kind(mut self, kind: Option<impl Into<String>>) -> Self {
38+ pub fn set_kind(&mut self, kind: Option<String>) -> () {
32− self.kind = kind.map(|kind| kind.into());
39+ self.kind = kind;
33− self
34 }
40 }
35
41
36− /// A summary description of the input.
42+ pub fn set_summary(&mut self, summary: Option<String>) -> () {
37− ///
43+ self.summary = summary;
38− /// Defaults to "{kind} '{name}'"
44+ }
45+
39− pub fn with_summary(mut self, summary: Option<impl Into<String>>) -> Self {
46+ pub fn set_title(&mut self, title: Option<String>) -> () {
40− self.summary = summary.map(|summary| summary.into());
47+ self.title = title;
41− self
42 }
48 }
43
49
44− pub fn name(&self) -> &String {
50+ pub fn title(&self) -> &String {
51+ match self.title.as_ref() {
52+ Some(ref title) => title,
45− &self.name
53+ None => &self.name,
54+ }
46 }
55 }
47
56
48 pub fn kind(&self) -> Option<&String> {
57 pub fn kind(&self) -> Option<&String> {
@@ -66,9 +75,7 @@ pub(crate) enum InputKind<'a> {
66impl<'a> InputKind<'a> {
75impl<'a> InputKind<'a> {
67 pub fn description(&self) -> InputDescription {
76 pub fn description(&self) -> InputDescription {
68 match self {
77 match self {
69− InputKind::OrdinaryFile(ref path) => {
70− InputDescription::new(path.to_string_lossy()).with_kind(Some("File"))
78+ InputKind::OrdinaryFile(ref path) => InputDescription::new(path.to_string_lossy()),
71− }
72 InputKind::StdIn => InputDescription::new("STDIN"),
79 InputKind::StdIn => InputDescription::new("STDIN"),
73 InputKind::CustomReader(_) => InputDescription::new("READER"),
80 InputKind::CustomReader(_) => InputDescription::new("READER"),
74 }
81 }
@@ -83,7 +90,7 @@ pub(crate) struct InputMetadata {
83pub struct Input<'a> {
90pub struct Input<'a> {
84 pub(crate) kind: InputKind<'a>,
91 pub(crate) kind: InputKind<'a>,
85 pub(crate) metadata: InputMetadata,
92 pub(crate) metadata: InputMetadata,
86− pub(crate) description: Option<InputDescription>,
93+ pub(crate) description: InputDescription,
87}
94}
88
95
89pub(crate) enum OpenedInputKind {
96pub(crate) enum OpenedInputKind {
@@ -101,26 +108,29 @@ pub(crate) struct OpenedInput<'a> {
101
108
102impl<'a> Input<'a> {
109impl<'a> Input<'a> {
103 pub fn ordinary_file(path: &OsStr) -> Self {
110 pub fn ordinary_file(path: &OsStr) -> Self {
111+ let kind = InputKind::OrdinaryFile(path.to_os_string());
104 Input {
112 Input {
105− kind: InputKind::OrdinaryFile(path.to_os_string()),
113+ description: kind.description(),
106 metadata: InputMetadata::default(),
114 metadata: InputMetadata::default(),
107− description: None,
115+ kind,
108 }
116 }
109 }
117 }
110
118
111 pub fn stdin() -> Self {
119 pub fn stdin() -> Self {
120+ let kind = InputKind::StdIn;
112 Input {
121 Input {
113− kind: InputKind::StdIn,
122+ description: kind.description(),
114 metadata: InputMetadata::default(),
123 metadata: InputMetadata::default(),
115− description: None,
124+ kind,
116 }
125 }
117 }
126 }
118
127
119 pub fn from_reader(reader: Box<dyn Read + 'a>) -> Self {
128 pub fn from_reader(reader: Box<dyn Read + 'a>) -> Self {
129+ let kind = InputKind::CustomReader(reader);
120 Input {
130 Input {
121− kind: InputKind::CustomReader(reader),
131+ description: kind.description(),
122 metadata: InputMetadata::default(),
132 metadata: InputMetadata::default(),
123− description: None,
133+ kind,
124 }
134 }
125 }
135 }
126
136
@@ -133,23 +143,26 @@ impl<'a> Input<'a> {
133 }
143 }
134
144
135 pub fn with_name(mut self, provided_name: Option<&OsStr>) -> Self {
145 pub fn with_name(mut self, provided_name: Option<&OsStr>) -> Self {
146+ match provided_name {
147+ Some(name) => self.description.name = name.to_string_lossy().to_string(),
148+ None => {}
149+ }
150+
136 self.metadata.user_provided_name = provided_name.map(|n| n.to_owned());
151 self.metadata.user_provided_name = provided_name.map(|n| n.to_owned());
137 self
152 self
138 }
153 }
139
154
140− pub fn with_description(mut self, description: Option<InputDescription>) -> Self {
155+ pub fn as_file(mut self, provided_name: Option<&OsStr>) -> Self {
141− self.description = description;
156+ self.description.kind = Some("File".to_owned());
142− self
157+ self.with_name(provided_name)
143 }
158 }
144
159
145− pub fn description(&self) -> InputDescription {
160+ pub fn description(&self) -> &InputDescription {
146− if let Some(ref description) = self.description {
161+ &self.description
147− description.clone()
162+ }
148− } else if let Some(ref name) = self.metadata.user_provided_name {
163+
149− InputDescription::new(name.to_string_lossy()).with_kind(Some("File"))
164+ pub fn description_mut(&mut self) -> &mut InputDescription {
150− } else {
151− self.kind.description()
165+ &mut self.description
152− }
153 }
166 }
154
167
155 pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
168 pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
src/pretty_printer.rs+84−12
@@ -9,7 +9,7 @@ use crate::{
9 config::{Config, VisibleLines},
9 config::{Config, VisibleLines},
10 controller::Controller,
10 controller::Controller,
11 error::Result,
11 error::Result,
12− input::Input,
12+ input::Input as BatInput,
13 line_range::{HighlightedLineRanges, LineRange, LineRanges},
13 line_range::{HighlightedLineRanges, LineRange, LineRanges},
14 style::{StyleComponent, StyleComponents},
14 style::{StyleComponent, StyleComponents},
15 SyntaxMapping, WrappingMode,
15 SyntaxMapping, WrappingMode,
@@ -61,10 +61,17 @@ impl<'a> PrettyPrinter<'a> {
61 self
61 self
62 }
62 }
63
63
64+ /// Adds multiple inputs which should be pretty-printed
65+ pub fn inputs(&mut self, inputs: impl IntoIterator<Item = Input<'a>>) -> &mut Self {
66+ for input in inputs {
67+ self.inputs.push(input);
68+ }
69+ self
70+ }
71+
64 /// Add a file which should be pretty-printed
72 /// Add a file which should be pretty-printed
65 pub fn input_file(&mut self, path: impl AsRef<OsStr>) -> &mut Self {
73 pub fn input_file(&mut self, path: impl AsRef<OsStr>) -> &mut Self {
66− self.inputs.push(Input::ordinary_file(path.as_ref()));
74+ self.input(Input::from_file(path).kind("File"))
67− self
68 }
75 }
69
76
70 /// Add multiple files which should be pretty-printed
77 /// Add multiple files which should be pretty-printed
@@ -73,22 +80,20 @@ impl<'a> PrettyPrinter<'a> {
73 I: IntoIterator<Item = P>,
80 I: IntoIterator<Item = P>,
74 P: AsRef<OsStr>,
81 P: AsRef<OsStr>,
75 {
82 {
76− for path in paths {
83+ self.inputs(paths.into_iter().map(Input::from_file))
77− self.inputs.push(Input::ordinary_file(path.as_ref()));
78− }
79− self
80 }
84 }
81
85
82 /// Add STDIN as an input
86 /// Add STDIN as an input
83 pub fn input_stdin(&mut self) -> &mut Self {
87 pub fn input_stdin(&mut self) -> &mut Self {
84− self.inputs.push(Input::stdin());
88+ self.inputs.push(Input::from_stdin());
85 self
89 self
86 }
90 }
87
91
88 /// Add STDIN as an input (with customized name)
92 /// Add STDIN as an input (with customized name)
93+ #[deprecated]
89 pub fn input_stdin_with_name(&mut self, name: impl AsRef<OsStr>) -> &mut Self {
94 pub fn input_stdin_with_name(&mut self, name: impl AsRef<OsStr>) -> &mut Self {
90 self.inputs
95 self.inputs
91− .push(Input::stdin().with_name(Some(name.as_ref())));
96+ .push(Input::from_stdin().name(name).kind("File"));
92 self
97 self
93 }
98 }
94
99
@@ -98,6 +103,8 @@ impl<'a> PrettyPrinter<'a> {
98 }
103 }
99
104
100 /// Add a byte string as an input (with customized name)
105 /// Add a byte string as an input (with customized name)
106+ #[deprecated]
107+ #[allow(deprecated)]
101 pub fn input_from_bytes_with_name(
108 pub fn input_from_bytes_with_name(
102 &mut self,
109 &mut self,
103 content: &'a [u8],
110 content: &'a [u8],
@@ -108,18 +115,19 @@ impl<'a> PrettyPrinter<'a> {
108
115
109 /// Add a custom reader as an input
116 /// Add a custom reader as an input
110 pub fn input_from_reader<R: Read + 'a>(&mut self, reader: R) -> &mut Self {
117 pub fn input_from_reader<R: Read + 'a>(&mut self, reader: R) -> &mut Self {
111− self.inputs.push(Input::from_reader(Box::new(reader)));
118+ self.inputs.push(Input::from_reader(reader));
112 self
119 self
113 }
120 }
114
121
115 /// Add a custom reader as an input (with customized name)
122 /// Add a custom reader as an input (with customized name)
123+ #[deprecated]
116 pub fn input_from_reader_with_name<R: Read + 'a>(
124 pub fn input_from_reader_with_name<R: Read + 'a>(
117 &mut self,
125 &mut self,
118 reader: R,
126 reader: R,
119 name: impl AsRef<OsStr>,
127 name: impl AsRef<OsStr>,
120 ) -> &mut Self {
128 ) -> &mut Self {
121 self.inputs
129 self.inputs
122− .push(Input::from_reader(Box::new(reader)).with_name(Some(name.as_ref())));
130+ .push(Input::from_reader(reader).name(name).kind("File"));
123 self
131 self
124 }
132 }
125
133
@@ -292,10 +300,74 @@ impl<'a> PrettyPrinter<'a> {
292 }
300 }
293 self.config.style_components = StyleComponents::new(&style_components);
301 self.config.style_components = StyleComponents::new(&style_components);
294
302
303+ // Collect the inputs to print
295 let mut inputs: Vec<Input> = vec![];
304 let mut inputs: Vec<Input> = vec![];
296 std::mem::swap(&mut inputs, &mut self.inputs);
305 std::mem::swap(&mut inputs, &mut self.inputs);
297
306
307+ // Run the controller
298 let controller = Controller::new(&self.config, &self.assets);
308 let controller = Controller::new(&self.config, &self.assets);
299− controller.run(inputs)
309+ controller.run(inputs.into_iter().map(|i| i.into()).collect())
310+ }
311+}
312+
313+pub struct Input<'a> {
314+ input: BatInput<'a>,
315+}
316+
317+impl<'a> Input<'a> {
318+ /// A new input from a reader.
319+ pub fn from_reader<R: Read + 'a>(reader: R) -> Self {
320+ BatInput::from_reader(Box::new(reader)).into()
321+ }
322+
323+ /// A new input from a file.
324+ pub fn from_file(path: impl AsRef<OsStr>) -> Self {
325+ BatInput::ordinary_file(path.as_ref()).into()
326+ }
327+
328+ /// A new input from bytes.
329+ pub fn from_bytes(bytes: &'a [u8]) -> Self {
330+ Input::from_reader(bytes).into()
331+ }
332+
333+ /// A new input from STDIN.
334+ pub fn from_stdin() -> Self {
335+ BatInput::stdin().into()
336+ }
337+
338+ /// The filename of the input.
339+ /// This affects syntax detection and changes the default header title.
340+ pub fn name(mut self, name: impl AsRef<OsStr>) -> Self {
341+ self.input = self.input.with_name(Some(name.as_ref()));
342+ self
343+ }
344+
345+ /// The description for the type of input (e.g. "File")
346+ /// This defaults to "File" for files, and nothing for other inputs.
347+ pub fn kind(mut self, kind: impl Into<String>) -> Self {
348+ let kind = kind.into();
349+ self.input
350+ .description_mut()
351+ .set_kind(if kind.is_empty() { None } else { Some(kind) });
352+ self
353+ }
354+
355+ /// The title for the input (e.g. "http://example.com/example.txt")
356+ /// This defaults to the file name.
357+ pub fn title(mut self, title: impl Into<String>) -> Self {
358+ self.input.description_mut().set_title(Some(title.into()));
359+ self
360+ }
361+}
362+
363+impl<'a> Into<Input<'a>> for BatInput<'a> {
364+ fn into(self) -> Input<'a> {
365+ Input { input: self }
366+ }
367+}
368+
369+impl<'a> Into<BatInput<'a>> for Input<'a> {
370+ fn into(self) -> BatInput<'a> {
371+ self.input
300 }
372 }
301}
373}
src/printer.rs+1−1
@@ -292,7 +292,7 @@ impl<'a> Printer for InteractivePrinter<'a> {
292 .kind()
292 .kind()
293 .map(|kind| format!("{}: ", kind))
293 .map(|kind| format!("{}: ", kind))
294 .unwrap_or("".into()),
294 .unwrap_or("".into()),
295− self.colors.filename.paint(description.name()),
295+ self.colors.filename.paint(description.title()),
296 mode
296 mode
297 )?;
297 )?;
298
298