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 Self::Real(_) => Some(LibraryKind::Masm),
52 Self::Stdin(bytes) if bytes.starts_with(b"MASP\0") => Some(LibraryKind::Masp),
53 Self::Stdin(_) => Some(LibraryKind::Masm),
55 }
56 }
57
58 #[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 #[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}