Skip to main content

miden_debug/
input.rs

1use std::{
2    borrow::Cow,
3    path::{Path, PathBuf},
4};
5
6use crate::linker::LibraryKind;
7
8#[derive(Debug, Clone)]
9pub enum InputFile {
10    Real(PathBuf),
11    Stdin(Box<[u8]>),
12}
13
14impl Default for InputFile {
15    fn default() -> Self {
16        Self::Stdin(Box::from([]))
17    }
18}
19
20impl InputFile {
21    pub fn file_name(&self) -> &str {
22        match self {
23            Self::Real(path) => {
24                path.file_name().and_then(|name| name.to_str()).unwrap_or("<noname>")
25            }
26            Self::Stdin(_) => "<noname>",
27        }
28    }
29
30    pub fn bytes(&self) -> Option<Cow<'_, [u8]>> {
31        match self {
32            Self::Real(path) => std::fs::read(path).ok().map(Cow::Owned),
33            Self::Stdin(bytes) => Some(Cow::Borrowed(bytes)),
34        }
35    }
36
37    pub fn library_kind(&self) -> Option<LibraryKind> {
38        match self {
39            Self::Real(path) if path.is_file() => {
40                if path.extension().and_then(|ext| ext.to_str()).is_some_and(|ext| ext == "masp") {
41                    return Some(LibraryKind::Masp);
42                }
43                let bytes = std::fs::read(path).ok()?;
44                if bytes.starts_with(b"MASP\0") {
45                    Some(LibraryKind::Masp)
46                } else {
47                    None
48                }
49            }
50            // Assume the path is a MASM project
51            Self::Real(_) => Some(LibraryKind::Masm),
52            Self::Stdin(bytes) if bytes.starts_with(b"MASP\0") => Some(LibraryKind::Masp),
53            // Assume the input is MASM text
54            Self::Stdin(_) => Some(LibraryKind::Masm),
55        }
56    }
57
58    /// Get an [InputFile] representing the contents of `path`.
59    ///
60    /// This function returns an error if the contents are not a valid supported file type.
61    #[cfg(feature = "std")]
62    pub fn from_path<P: AsRef<Path>>(path: P) -> Self {
63        let path = path.as_ref();
64        Self::Real(path.to_path_buf())
65    }
66
67    /// Get an [InputFile] representing the contents received from standard input.
68    ///
69    /// This function returns an error if the contents are not a valid supported file type.
70    #[cfg(feature = "std")]
71    pub fn from_stdin() -> Result<Self, std::io::Error> {
72        use std::io::Read;
73
74        let mut input = Vec::with_capacity(1024);
75        std::io::stdin().read_to_end(&mut input)?;
76        Ok(Self::Stdin(input.into_boxed_slice()))
77    }
78}
79
80#[cfg(feature = "std")]
81impl clap::builder::ValueParserFactory for InputFile {
82    type Parser = InputFileParser;
83
84    fn value_parser() -> Self::Parser {
85        InputFileParser
86    }
87}
88
89#[doc(hidden)]
90#[derive(Clone)]
91#[cfg(feature = "std")]
92pub struct InputFileParser;
93
94#[cfg(feature = "std")]
95impl clap::builder::TypedValueParser for InputFileParser {
96    type Value = InputFile;
97
98    fn parse_ref(
99        &self,
100        _cmd: &clap::Command,
101        _arg: Option<&clap::Arg>,
102        value: &std::ffi::OsStr,
103    ) -> Result<Self::Value, clap::error::Error> {
104        use clap::error::{Error, ErrorKind};
105
106        let input_file = match value.to_str() {
107            Some("-") => InputFile::from_stdin().map_err(|err| Error::raw(ErrorKind::Io, err))?,
108            Some(_) | None => InputFile::from_path(PathBuf::from(value)),
109        };
110
111        match &input_file {
112            InputFile::Real(path) => {
113                if !path.exists() {
114                    return Err(Error::raw(
115                        ErrorKind::ValueValidation,
116                        format!("invalid input '{}': file does not exist", path.display()),
117                    ));
118                }
119            }
120            InputFile::Stdin(_) => (),
121        }
122
123        Ok(input_file)
124    }
125}