@@ -8,18 +8,55 @@ use crate::error::*;
9const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
9const THEME_PREVIEW_FILE: &[u8] = include_bytes!("../assets/theme_preview.rs");
11−/// A description for an Input source.
11+/// A description of an Input source.
12/// This tells bat how to refer to the input.
12/// This tells bat how to refer to the input.
13−#[derive(Debug, Clone)]
13+#[derive(Clone)]
14−pub(crate) struct InputDescription {
14+pub struct InputDescription {
15− /// The name of the input used when printing warnings.
15+ name: String,
16+ kind: Option<String>,
17+ summary: Option<String>,
20+impl InputDescription {
21+ /// Creates a description for an input.
23+ /// The name should uniquely describes where the input came from (e.g. "README.md")
16− pub full: String,
24+ pub fn new(name: impl Into<String>) -> Self {
18− /// The prefix string for the input when displaying it in the header.
32+ /// A description for the type of input (e.g. "File")
19− pub prefix: String,
33+ pub fn with_kind(mut self, kind: Option<impl Into<String>>) -> Self {
34+ self.kind = kind.map(|kind| kind.into());
38+ /// A summary description of the input.
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());
46+ pub fn name(&self) -> &String {
21− /// The name of the input.
50+ pub fn kind(&self) -> Option<&String> {
22− pub name: String,
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),
25pub(crate) enum InputKind<'a> {
62pub(crate) enum InputKind<'a> {
@@ -29,6 +66,19 @@ pub(crate) enum InputKind<'a> {
29 CustomReader(Box<dyn Read + 'a>),
66 CustomReader(Box<dyn Read + 'a>),
69+impl<'a> InputKind<'a> {
70+ pub fn description(&self) -> InputDescription {
72+ InputKind::OrdinaryFile(ref path) => {
73+ InputDescription::new(path.to_string_lossy()).with_kind(Some("File"))
75+ InputKind::StdIn => InputDescription::new("STDIN"),
76+ InputKind::ThemePreviewFile => InputDescription::new(""),
77+ InputKind::CustomReader(_) => InputDescription::new("READER"),
32#[derive(Clone, Default)]
82#[derive(Clone, Default)]
33pub(crate) struct InputMetadata {
83pub(crate) struct InputMetadata {
34 pub(crate) user_provided_name: Option<OsString>,
84 pub(crate) user_provided_name: Option<OsString>,
@@ -37,6 +87,7 @@ pub(crate) struct InputMetadata {
37pub struct Input<'a> {
87pub struct Input<'a> {
38 pub(crate) kind: InputKind<'a>,
88 pub(crate) kind: InputKind<'a>,
39 pub(crate) metadata: InputMetadata,
89 pub(crate) metadata: InputMetadata,
90+ pub(crate) description: Option<InputDescription>,
42pub(crate) enum OpenedInputKind {
93pub(crate) enum OpenedInputKind {
@@ -59,6 +110,7 @@ pub(crate) struct OpenedInput<'a> {
59 pub(crate) kind: OpenedInputKind,
110 pub(crate) kind: OpenedInputKind,
60 pub(crate) metadata: InputMetadata,
111 pub(crate) metadata: InputMetadata,
61 pub(crate) reader: InputReader<'a>,
112 pub(crate) reader: InputReader<'a>,
113+ pub(crate) description: InputDescription,
64impl<'a> Input<'a> {
116impl<'a> Input<'a> {
@@ -66,6 +118,7 @@ impl<'a> Input<'a> {
67 kind: InputKind::OrdinaryFile(path.to_os_string()),
119 kind: InputKind::OrdinaryFile(path.to_os_string()),
68 metadata: InputMetadata::default(),
120 metadata: InputMetadata::default(),
@@ -73,6 +126,7 @@ impl<'a> Input<'a> {
74 kind: InputKind::StdIn,
127 kind: InputKind::StdIn,
75 metadata: InputMetadata::default(),
128 metadata: InputMetadata::default(),
@@ -80,6 +134,7 @@ impl<'a> Input<'a> {
81 kind: InputKind::ThemePreviewFile,
135 kind: InputKind::ThemePreviewFile,
82 metadata: InputMetadata::default(),
136 metadata: InputMetadata::default(),
@@ -87,6 +142,7 @@ impl<'a> Input<'a> {
88 kind: InputKind::CustomReader(reader),
143 kind: InputKind::CustomReader(reader),
89 metadata: InputMetadata::default(),
144 metadata: InputMetadata::default(),
@@ -103,15 +159,33 @@ impl<'a> Input<'a> {
162+ pub fn with_description(mut self, description: Option<InputDescription>) -> Self {
163+ self.description = description;
167+ pub fn description(&self) -> InputDescription {
168+ if let Some(ref description) = self.description {
170+ } else if let Some(ref name) = self.metadata.user_provided_name {
171+ InputDescription::new(name.to_string_lossy()).with_kind(Some("File"))
173+ self.kind.description()
106 pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
177 pub(crate) fn open<R: BufRead + 'a>(self, stdin: R) -> Result<OpenedInput<'a>> {
178+ let description = self.description().clone();
107 match self.kind {
179 match self.kind {
108 InputKind::StdIn => Ok(OpenedInput {
180 InputKind::StdIn => Ok(OpenedInput {
109 kind: OpenedInputKind::StdIn,
181 kind: OpenedInputKind::StdIn,
110 metadata: self.metadata,
183 metadata: self.metadata,
111 reader: InputReader::new(stdin),
184 reader: InputReader::new(stdin),
113 InputKind::OrdinaryFile(path) => Ok(OpenedInput {
186 InputKind::OrdinaryFile(path) => Ok(OpenedInput {
114 kind: OpenedInputKind::OrdinaryFile(path.clone()),
187 kind: OpenedInputKind::OrdinaryFile(path.clone()),
115 metadata: self.metadata,
189 metadata: self.metadata,
116 reader: {
190 reader: {
117 let file = File::open(&path)
191 let file = File::open(&path)
@@ -124,10 +198,12 @@ impl<'a> Input<'a> {
125 InputKind::ThemePreviewFile => Ok(OpenedInput {
199 InputKind::ThemePreviewFile => Ok(OpenedInput {
126 kind: OpenedInputKind::ThemePreviewFile,
200 kind: OpenedInputKind::ThemePreviewFile,
127 metadata: self.metadata,
202 metadata: self.metadata,
128 reader: InputReader::new(THEME_PREVIEW_FILE),
203 reader: InputReader::new(THEME_PREVIEW_FILE),
130 InputKind::CustomReader(reader) => Ok(OpenedInput {
205 InputKind::CustomReader(reader) => Ok(OpenedInput {
131 kind: OpenedInputKind::CustomReader,
207 kind: OpenedInputKind::CustomReader,
132 metadata: self.metadata,
208 metadata: self.metadata,
133 reader: InputReader::new(BufReader::new(reader)),
209 reader: InputReader::new(BufReader::new(reader)),
@@ -136,41 +212,6 @@ impl<'a> Input<'a> {
139−impl<'a> OpenedInput<'a> {
140− pub fn description(&self) -> InputDescription {
141− if let Some(ref name) = self.metadata.user_provided_name {
143− full: format!("file '{}'", name.to_string_lossy()),
144− prefix: "File: ".to_owned(),
145− name: name.to_string_lossy().into_owned(),
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(),
154− OpenedInputKind::StdIn => InputDescription {
155− full: "STDIN".to_owned(),
156− prefix: "".to_owned(),
157− name: "STDIN".to_owned(),
159− OpenedInputKind::ThemePreviewFile => InputDescription {
160− full: "".to_owned(),
161− prefix: "".to_owned(),
162− name: "".to_owned(),
164− OpenedInputKind::CustomReader => InputDescription {
165− full: "reader".to_owned(),
166− prefix: "".to_owned(),
167− name: "READER".into(),
174pub(crate) struct InputReader<'a> {
215pub(crate) struct InputReader<'a> {
175 inner: Box<dyn BufRead + 'a>,
216 inner: Box<dyn BufRead + 'a>,
176 pub(crate) first_line: Vec<u8>,
217 pub(crate) first_line: Vec<u8>,