yash_builtin/break/
syntax.rs1use crate::common::syntax::Mode;
20use crate::common::syntax::parse_arguments;
21use std::num::NonZeroUsize;
22use std::num::ParseIntError;
23use thiserror::Error;
24use yash_env::Env;
25use yash_env::semantics::Field;
26use yash_env::source::pretty::Report;
27use yash_env::source::pretty::ReportType;
28use yash_env::source::pretty::Snippet;
29
30#[derive(Clone, Debug, Eq, Error, PartialEq)]
32#[non_exhaustive]
33pub enum Error {
34 #[error(transparent)]
36 CommonError(#[from] crate::common::syntax::ParseError<'static>),
37
38 #[error("too many operands")]
40 TooManyOperands(Vec<Field>),
41
42 #[error("invalid numeric operand")]
44 InvalidNumber(Field, ParseIntError),
45}
46
47impl Error {
48 #[must_use]
50 pub fn to_report(&self) -> Report<'_> {
51 let (location, label) = match self {
52 Self::CommonError(e) => return e.to_report(),
53 Self::TooManyOperands(operands) => (
54 &operands[1].origin,
55 format!("{}: redundant operand", operands[1].value),
56 ),
57 Self::InvalidNumber(operand, e) => {
58 (&operand.origin, format!("{}: {}", operand.value, e))
59 }
60 };
61
62 let mut report = Report::new();
63 report.r#type = ReportType::Error;
64 report.title = self.to_string().into();
65 report.snippets = Snippet::with_primary_span(location, label.into());
66 report
67 }
68}
69
70impl<'a> From<&'a Error> for Report<'a> {
71 #[inline]
72 fn from(error: &'a Error) -> Self {
73 error.to_report()
74 }
75}
76
77pub type Result = std::result::Result<NonZeroUsize, Error>;
81
82pub fn parse<S>(env: &Env<S>, args: Vec<Field>) -> Result {
84 let (_options, mut operands) = parse_arguments(&[], Mode::with_env(env), args)?;
85
86 if operands.len() > 1 {
87 return Err(Error::TooManyOperands(operands));
88 }
89
90 match operands.pop() {
91 None => Ok(NonZeroUsize::new(1).unwrap()),
92
93 Some(field) => field
94 .value
95 .parse()
96 .map_err(|e| Error::InvalidNumber(field, e)),
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103 use assert_matches::assert_matches;
104 use std::num::IntErrorKind;
105
106 #[test]
107 fn default_count() {
108 let env = Env::new_virtual();
109 let result = parse(&env, vec![]);
110 assert_eq!(result, Ok(NonZeroUsize::new(1).unwrap()));
111 }
112
113 #[test]
114 fn valid_counts() {
115 let env = Env::new_virtual();
116 let args = Field::dummies(["1"]);
117 let result = parse(&env, args);
118 assert_eq!(result, Ok(NonZeroUsize::new(1).unwrap()));
119
120 let args = Field::dummies(["2"]);
121 let result = parse(&env, args);
122 assert_eq!(result, Ok(NonZeroUsize::new(2).unwrap()));
123 }
124
125 #[test]
126 fn too_many_operands() {
127 let env = Env::new_virtual();
128 let args = Field::dummies(["1", "2"]);
129 let result = parse(&env, args.clone());
130 assert_eq!(result, Err(Error::TooManyOperands(args)));
131 }
132
133 #[test]
134 fn non_positive_integer() {
135 let env = Env::new_virtual();
136 let arg = Field::dummy("0");
137 let result = parse(&env, vec![arg.clone()]);
138 assert_matches!(result, Err(Error::InvalidNumber(field, error)) => {
139 assert_eq!(field, arg);
140 assert_eq!(error.kind(), &IntErrorKind::Zero);
141 });
142 }
143}