1use std::{
2 borrow::Cow,
3 path::{Path, PathBuf},
4};
5
6#[derive(Debug, Clone)]
7pub enum InputFile {
8 Real(PathBuf),
9 Stdin(Box<[u8]>),
10}
11
12impl Default for InputFile {
13 fn default() -> Self {
14 Self::Stdin(Box::from([]))
15 }
16}
17
18impl InputFile {
19 pub fn file_name(&self) -> &str {
20 match self {
21 Self::Real(path) => {
22 path.file_name().and_then(|name| name.to_str()).unwrap_or("<noname>")
23 }
24 Self::Stdin(_) => "<noname>",
25 }
26 }
27
28 pub fn bytes(&self) -> Option<Cow<'_, [u8]>> {
29 match self {
30 Self::Real(path) => std::fs::read(path).ok().map(Cow::Owned),
31 Self::Stdin(bytes) => Some(Cow::Borrowed(bytes)),
32 }
33 }
34
35 #[cfg(feature = "std")]
39 pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
40 let path = path.as_ref();
41 Self::Real(path.to_path_buf())
42 }
43
44 #[cfg(feature = "std")]
48 pub fn from_stdin() -> Result<Self, std::io::Error> {
49 use std::io::Read;
50
51 let mut input = Vec::with_capacity(1024);
52 std::io::stdin().read_to_end(&mut input)?;
53 Ok(Self::Stdin(input.into_boxed_slice()))
54 }
55}
56
57#[cfg(feature = "std")]
58impl clap::builder::ValueParserFactory for InputFile {
59 type Parser = InputFileParser;
60
61 fn value_parser() -> Self::Parser {
62 InputFileParser
63 }
64}
65
66#[doc(hidden)]
67#[derive(Clone)]
68#[cfg(feature = "std")]
69pub struct InputFileParser;
70
71#[cfg(feature = "std")]
72impl clap::builder::TypedValueParser for InputFileParser {
73 type Value = InputFile;
74
75 fn parse_ref(
76 &self,
77 _cmd: &clap::Command,
78 _arg: Option<&clap::Arg>,
79 value: &std::ffi::OsStr,
80 ) -> Result<Self::Value, clap::error::Error> {
81 use clap::error::{Error, ErrorKind};
82
83 let input_file = match value.to_str() {
84 Some("-") => InputFile::from_stdin().map_err(|err| Error::raw(ErrorKind::Io, err))?,
85 Some(_) | None => InputFile::from_path(PathBuf::from(value)),
86 };
87
88 match &input_file {
89 InputFile::Real(path) => {
90 if !path.exists() {
91 return Err(Error::raw(
92 ErrorKind::ValueValidation,
93 format!("invalid input '{}': file does not exist", path.display()),
94 ));
95 }
96 }
97 InputFile::Stdin(_) => (),
98 }
99
100 Ok(input_file)
101 }
102}