Skip to main content

miden_debug/
input.rs

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    /// Get an [InputFile] representing the contents of `path`.
36    ///
37    /// This function returns an error if the contents are not a valid supported file type.
38    pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
39        let path = path.as_ref();
40        Self::Real(path.to_path_buf())
41    }
42
43    /// Get an [InputFile] representing the contents received from standard input.
44    ///
45    /// This function returns an error if the contents are not a valid supported file type.
46    pub fn from_stdin() -> Result<Self, std::io::Error> {
47        use std::io::Read;
48
49        let mut input = Vec::with_capacity(1024);
50        std::io::stdin().read_to_end(&mut input)?;
51        Ok(Self::Stdin(input.into_boxed_slice()))
52    }
53}
54
55#[cfg(feature = "std")]
56impl clap::builder::ValueParserFactory for InputFile {
57    type Parser = InputFileParser;
58
59    fn value_parser() -> Self::Parser {
60        InputFileParser
61    }
62}
63
64#[doc(hidden)]
65#[derive(Clone)]
66#[cfg(feature = "std")]
67pub struct InputFileParser;
68
69#[cfg(feature = "std")]
70impl clap::builder::TypedValueParser for InputFileParser {
71    type Value = InputFile;
72
73    fn parse_ref(
74        &self,
75        _cmd: &clap::Command,
76        _arg: Option<&clap::Arg>,
77        value: &std::ffi::OsStr,
78    ) -> Result<Self::Value, clap::error::Error> {
79        use clap::error::{Error, ErrorKind};
80
81        let input_file = match value.to_str() {
82            Some("-") => InputFile::from_stdin().map_err(|err| Error::raw(ErrorKind::Io, err))?,
83            Some(_) | None => InputFile::from_path(PathBuf::from(value)),
84        };
85
86        match &input_file {
87            InputFile::Real(path) => {
88                if !path.exists() {
89                    return Err(Error::raw(
90                        ErrorKind::ValueValidation,
91                        format!("invalid input '{}': file does not exist", path.display()),
92                    ));
93                }
94            }
95            InputFile::Stdin(_) => (),
96        }
97
98        Ok(input_file)
99    }
100}