yash_builtin/source/
syntax.rs1use super::Command;
20use crate::common::report::report_error;
21use crate::common::syntax::Mode;
22use crate::common::syntax::ParseError;
23use crate::common::syntax::parse_arguments;
24use thiserror::Error;
25use yash_env::Env;
26use yash_env::semantics::Field;
27use yash_env::source::pretty::{Report, ReportType};
28use yash_env::system::{Fcntl, Isatty, Write};
29
30#[derive(Clone, Debug, Eq, Error, PartialEq)]
32#[non_exhaustive]
33pub enum Error {
34 #[error(transparent)]
36 CommonError(#[from] ParseError<'static>),
37
38 #[error("missing file operand")]
40 MissingFile,
41}
42
43impl Error {
44 #[must_use]
46 pub fn to_report(&self) -> Report<'_> {
47 match self {
48 Self::CommonError(e) => e.to_report(),
49 Self::MissingFile => {
50 let mut report = Report::new();
51 report.r#type = ReportType::Error;
52 report.title = "missing file operand".into();
53 report
54 }
55 }
56 }
57
58 #[inline(always)]
60 pub async fn report<S>(&self, env: &mut Env<S>) -> crate::Result
61 where
62 S: Fcntl + Isatty + Write,
63 {
64 report_error(env, self).await
65 }
66}
67
68impl<'a> From<&'a Error> for Report<'a> {
69 #[inline]
70 fn from(error: &'a Error) -> Self {
71 error.to_report()
72 }
73}
74
75pub fn parse<S>(env: &Env<S>, args: Vec<Field>) -> Result<Command, Error> {
77 let mode = Mode::with_env(env);
78 let (_options, mut operands) = parse_arguments(&[], mode, args)?;
79 if operands.is_empty() {
80 return Err(Error::MissingFile);
81 }
82 let file = operands.remove(0);
83 let params = operands;
84 Ok(Command { file, params })
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 #[test]
92 fn file_only() {
93 let env = Env::new_virtual();
94 let args = vec![Field::dummy("foo")];
95 assert_eq!(
96 parse(&env, args),
97 Ok(Command {
98 file: Field::dummy("foo"),
99 params: vec![],
100 })
101 );
102 }
103
104 #[test]
105 fn file_and_parameters() {
106 let env = Env::new_virtual();
107 let args = Field::dummies(["my/file", "foo", "bar"]);
108 assert_eq!(
109 parse(&env, args),
110 Ok(Command {
111 file: Field::dummy("my/file"),
112 params: Field::dummies(["foo", "bar"]),
113 })
114 );
115 }
116
117 #[test]
118 fn no_file() {
119 let env = Env::new_virtual();
120 let args = vec![];
121 assert_eq!(parse(&env, args), Err(Error::MissingFile));
122 }
123
124 #[test]
125 fn unknown_short_option() {
126 let env = Env::new_virtual();
127 let args = Field::dummies(["-@", "foo"]);
128 assert_eq!(
129 parse(&env, args),
130 Err(Error::CommonError(ParseError::UnknownShortOption(
131 '@',
132 Field::dummy("-@"),
133 ))),
134 );
135 }
136}