@@ -8,18 +8,55 @@ use crate::error::*;
88
99const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
1010
11−/// A description for an Input source.
11+/// A description of an Input source.
1212/// This tells bat how to refer to the input.
13−#[derive(Debug, Clone)]
14−pub(crate) struct InputDescription {
15− /// The name of the input used when printing warnings.
16− pub full: String,
13+#[derive(Clone)]
14+pub struct InputDescription {
15+ name: String,
16+ kind: Option<String>,
17+ summary: Option<String>,
18+}
19+
20+impl InputDescription {
21+ /// Creates a description for an input.
22+ ///
23+ /// The name should uniquely describes where the input came from (e.g. "README.md")
24+ pub fn new(name: impl Into<String>) -> Self {
25+ InputDescription {
26+ name: name.into(),
27+ kind: None,
28+ summary: None,
29+ }
30+ }
1731
18− /// The prefix string for the input when displaying it in the header.
19− pub prefix: String,
32+ /// A description for the type of input (e.g. "File")
33+ pub fn with_kind(mut self, kind: Option<impl Into<String>>) -> Self {
34+ self.kind = kind.map(|kind| kind.into());
35+ self
36+ }
37+
38+ /// A summary description of the input.
39+ ///
40+ /// Defaults to "{kind} '{name}'"
41+ pub fn with_summary(mut self, summary: Option<impl Into<String>>) -> Self {
42+ self.summary = summary.map(|summary| summary.into());
43+ self
44+ }
45+
46+ pub fn name(&self) -> &String {
47+ &self.name
48+ }
2049
21− /// The name of the input.
22− pub name: String,
50+ pub fn kind(&self) -> Option<&String> {
51+ self.kind.as_ref()
52+ }
53+
54+ pub fn summary(&self) -> String {
55+ self.summary.clone().unwrap_or_else(|| match &self.kind {
56+ None => self.name.clone(),
57+ Some(kind) => format!("{} '{}'", kind.to_lowercase(), self.name),
58+ })
59+ }
2360}
2461
2562pub(crate) enum InputKind<'a> {
@@ -29,6 +66,19 @@ pub(crate) enum InputKind<'a> {
2966 CustomReader(Box<dyn Read + 'a>),
3067}
3168
69+impl<'a> InputKind<'a> {
70+ pub fn description(&self) -> InputDescription {
71+ match self {
72+ InputKind::OrdinaryFile(ref path) => {
73+ InputDescription::new(path.to_string_lossy()).with_kind(Some("File"))
74+ }
75+ InputKind::StdIn => InputDescription::new("STDIN"),
76+ InputKind::ThemePreviewFile => InputDescription::new(""),
77+ InputKind::CustomReader(_) => InputDescription::new("READER"),
78+ }
79+ }
80+}
81+
3282#[derive(Clone, Default)]
3383pub(crate) struct InputMetadata {
3484 pub(crate) user_provided_name: Option<OsString>,
@@ -37,6 +87,7 @@ pub(crate) struct InputMetadata {
3787pub struct Input<'a> {
3888 pub(crate) kind: InputKind<'a>,
3989 pub(crate) metadata: InputMetadata,
90+ pub(crate) description: Option<InputDescription>,
4091}
4192
4293pub(crate) enum OpenedInputKind {
@@ -59,6 +110,7 @@ pub(crate) struct OpenedInput<'a> {
59110 pub(crate) kind: OpenedInputKind,
60111 pub(crate) metadata: InputMetadata,
61112 pub(crate) reader: InputReader<'a>,
113+ pub(crate) description: InputDescription,
62114}
63115
64116impl<'a> Input<'a> {
@@ -66,6 +118,7 @@ impl<'a> Input<'a> {
66118 Input {
67119 kind: InputKind::OrdinaryFile(path.to_os_string()),
68120 metadata: InputMetadata::default(),
121+ description: None,
69122 }
70123 }
71124
@@ -73,6 +126,7 @@ impl<'a> Input<'a> {
73126 Input {
74127 kind: InputKind::StdIn,
75128 metadata: InputMetadata::default(),
129+ description: None,
76130 }
77131 }
78132
@@ -80,6 +134,7 @@ impl<'a> Input<'a> {
80134 Input {
81135 kind: InputKind::ThemePreviewFile,
82136 metadata: InputMetadata::default(),
137+ description: None,
83138 }
84139 }
85140
@@ -87,6 +142,7 @@ impl<'a> Input<'a> {
87142 Input {
88143 kind: InputKind::CustomReader(reader),
89144 metadata: InputMetadata::default(),
145+ description: None,
90146 }
91147 }
92148
@@ -103,15 +159,33 @@ impl<'a> Input<'a> {
103159 self
104160 }
105161
162+ pub fn with_description(mut self, description: Option<InputDescription>) -> Self {
163+ self.description = description;
164+ self
165+ }
166+
167+ pub fn description(&self) -> InputDescription {
168+ if let Some(ref description) = self.description {
169+ description.clone()
170+ } else if let Some(ref name) = self.metadata.user_provided_name {
171+ InputDescription::new(name.to_string_lossy()).with_kind(Some("File"))
172+ } else {
173+ self.kind.description()
174+ }
175+ }
176+
106177 pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
178+ let description = self.description().clone();
107179 match self.kind {
108180 InputKind::StdIn => Ok(OpenedInput {
109181 kind: OpenedInputKind::StdIn,
182+ description,
110183 metadata: self.metadata,
111184 reader: InputReader::new(stdin),
112185 }),
113186 InputKind::OrdinaryFile(path) => Ok(OpenedInput {
114187 kind: OpenedInputKind::OrdinaryFile(path.clone()),
188+ description,
115189 metadata: self.metadata,
116190 reader: {
117191 let file = File::open(&path)
@@ -124,10 +198,12 @@ impl<'a> Input<'a> {
124198 }),
125199 InputKind::ThemePreviewFile => Ok(OpenedInput {
126200 kind: OpenedInputKind::ThemePreviewFile,
201+ description,
127202 metadata: self.metadata,
128203 reader: InputReader::new(THEME_PREVIEW_FILE),
129204 }),
130205 InputKind::CustomReader(reader) => Ok(OpenedInput {
206+ description,
131207 kind: OpenedInputKind::CustomReader,
132208 metadata: self.metadata,
133209 reader: InputReader::new(BufReader::new(reader)),
@@ -136,41 +212,6 @@ impl<'a> Input<'a> {
136212 }
137213}
138214
139−impl<'a> OpenedInput<'a> {
140− pub fn description(&self) -> InputDescription {
141− if let Some(ref name) = self.metadata.user_provided_name {
142− InputDescription {
143− full: format!("file '{}'", name.to_string_lossy()),
144− prefix: "File: ".to_owned(),
145− name: name.to_string_lossy().into_owned(),
146− }
147− } else {
148− match self.kind {
149− OpenedInputKind::OrdinaryFile(ref path) => InputDescription {
150− full: format!("file '{}'", path.to_string_lossy()),
151− prefix: "File: ".to_owned(),
152− name: path.to_string_lossy().into_owned(),
153− },
154− OpenedInputKind::StdIn => InputDescription {
155− full: "STDIN".to_owned(),
156− prefix: "".to_owned(),
157− name: "STDIN".to_owned(),
158− },
159− OpenedInputKind::ThemePreviewFile => InputDescription {
160− full: "".to_owned(),
161− prefix: "".to_owned(),
162− name: "".to_owned(),
163− },
164− OpenedInputKind::CustomReader => InputDescription {
165− full: "reader".to_owned(),
166− prefix: "".to_owned(),
167− name: "READER".into(),
168− },
169− }
170− }
171− }
172−}
173−
174215pub(crate) struct InputReader<'a> {
175216 inner: Box<dyn BufRead + 'a>,
176217 pub(crate) first_line: Vec<u8>,