diff options
Diffstat (limited to 'crates/shirabe/src/io')
| -rw-r--r-- | crates/shirabe/src/io/buffer_io.rs | 4 | ||||
| -rw-r--r-- | crates/shirabe/src/io/console_io.rs | 44 | ||||
| -rw-r--r-- | crates/shirabe/src/io/io_interface.rs | 15 | ||||
| -rw-r--r-- | crates/shirabe/src/io/null_io.rs | 8 |
4 files changed, 44 insertions, 27 deletions
diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs index 491cd316..bbe1db9f 100644 --- a/crates/shirabe/src/io/buffer_io.rs +++ b/crates/shirabe/src/io/buffer_io.rs @@ -186,7 +186,7 @@ impl crate::io::IOInterfaceImmutable for BufferIO { self.inner .overwrite_error4(message, newline, size, verbosity) } - fn ask(&self, question: String, default: PhpMixed) -> PhpMixed { + fn ask(&self, question: String, default: PhpMixed) -> anyhow::Result<PhpMixed> { self.inner.ask(question, default) } fn ask_confirmation(&self, question: String, default: bool) -> bool { @@ -213,7 +213,7 @@ impl crate::io::IOInterfaceImmutable for BufferIO { attempts: PhpMixed, error_message: String, multiselect: bool, - ) -> PhpMixed { + ) -> anyhow::Result<PhpMixed> { self.inner.select( question, choices, diff --git a/crates/shirabe/src/io/console_io.rs b/crates/shirabe/src/io/console_io.rs index cf488855..83e84fd3 100644 --- a/crates/shirabe/src/io/console_io.rs +++ b/crates/shirabe/src/io/console_io.rs @@ -338,18 +338,17 @@ impl ConsoleIO { /// Delegates to `QuestionHelper::ask`. /// /// PHP: `$helper->ask($this->input, $this->getErrorOutput(), $question)`. - /// `QuestionHelper::ask` surfaces PHP exceptions; ConsoleIO does not catch them, so an - /// unrecoverable error here is a PHP fatal. The double `Result` is collapsed: the outer - /// `anyhow::Result` (fatal) and the inner `MissingInputException` (unhandled, hence also fatal - /// in PHP) both abort. - fn ask_question(&self, question: &impl QuestionInterface) -> PhpMixed { + /// `QuestionHelper::ask` surfaces PHP exceptions; ConsoleIO does not catch them, they + /// propagate to the caller. The double `Result` is flattened: the inner + /// `MissingInputException` is wrapped with its concrete type preserved so that + /// `Application`'s `instanceof ExceptionInterface` downcast still recognizes it. + fn ask_question(&self, question: &impl QuestionInterface) -> anyhow::Result<PhpMixed> { let error_output = self.get_error_output(); let mut question_helper = self.question_helper.borrow_mut(); let mut input = self.input.borrow_mut(); question_helper - .ask(&mut *input, error_output, question) - .expect("QuestionHelper::ask raised a fatal error") - .expect("QuestionHelper::ask returned no input") + .ask(&mut *input, error_output, question)? + .map_err(anyhow::Error::new) } } @@ -426,7 +425,7 @@ impl IOInterfaceImmutable for ConsoleIO { ); } - fn ask(&self, question: String, default: PhpMixed) -> PhpMixed { + fn ask(&self, question: String, default: PhpMixed) -> anyhow::Result<PhpMixed> { let sanitized_question = Self::sanitize(PhpMixed::String(question), true) .as_string() .unwrap_or("") @@ -441,6 +440,9 @@ impl IOInterfaceImmutable for ConsoleIO { self.ask_question(&question) } + // TODO(phase-c): ask_confirmation and ask_and_hide_answer still collapse ask_question + // errors with .expect() instead of propagating them; extending Result propagation to + // them is a further IOInterface signature change that has not been decided yet. fn ask_confirmation(&self, question: String, default: bool) -> bool { let sanitized = Self::sanitize(PhpMixed::String(question), true) .as_string() @@ -453,7 +455,9 @@ impl IOInterfaceImmutable for ConsoleIO { "/^no?$/i".to_string(), ); - let result = self.ask_question(&question); + let result = self + .ask_question(&question) + .expect("QuestionHelper::ask raised an error"); result.as_bool().unwrap_or(false) } @@ -499,7 +503,7 @@ impl IOInterfaceImmutable for ConsoleIO { .set_max_attempts(attempts) .map_err(|e| anyhow::anyhow!(e.0.message))?; - Ok(self.ask_question(&question)) + self.ask_question(&question) } fn ask_and_hide_answer(&self, question: String) -> Option<String> { @@ -513,7 +517,9 @@ impl IOInterfaceImmutable for ConsoleIO { .set_hidden(true) .expect("a freshly constructed question has no autocompleter"); - let result = self.ask_question(&question); + let result = self + .ask_question(&question) + .expect("QuestionHelper::ask raised an error"); result.as_string().map(|s| s.to_string()) } @@ -525,7 +531,7 @@ impl IOInterfaceImmutable for ConsoleIO { attempts: PhpMixed, error_message: String, multiselect: bool, - ) -> PhpMixed { + ) -> anyhow::Result<PhpMixed> { let sanitized_question = Self::sanitize(PhpMixed::String(question), true) .as_string() .unwrap_or("") @@ -563,7 +569,7 @@ impl IOInterfaceImmutable for ConsoleIO { question.set_error_message(error_message); question.set_multiselect(multiselect); - let result: PhpMixed = self.ask_question(&question); + let result: PhpMixed = self.ask_question(&question)?; // PHP: $isAssoc = (bool) \count(array_filter(array_keys($choices), 'is_string')); let choice_keys: Vec<String> = match &choices { @@ -574,7 +580,7 @@ impl IOInterfaceImmutable for ConsoleIO { let is_assoc = !choice_keys.is_empty() && choice_keys.iter().any(|k| k.parse::<i64>().is_err()); if is_assoc { - return result; + return Ok(result); } if !is_array(&result) { @@ -591,7 +597,9 @@ impl IOInterfaceImmutable for ConsoleIO { .collect(), _ => IndexMap::new(), }; - return PhpMixed::String(array_search(&result_str, &haystack).unwrap_or_default()); + return Ok(PhpMixed::String( + array_search(&result_str, &haystack).unwrap_or_default(), + )); } let mut results: Vec<String> = vec![]; @@ -611,7 +619,9 @@ impl IOInterfaceImmutable for ConsoleIO { } } - PhpMixed::List(results.into_iter().map(PhpMixed::String).collect()) + Ok(PhpMixed::List( + results.into_iter().map(PhpMixed::String).collect(), + )) } fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>> { diff --git a/crates/shirabe/src/io/io_interface.rs b/crates/shirabe/src/io/io_interface.rs index 33a2f017..b0271767 100644 --- a/crates/shirabe/src/io/io_interface.rs +++ b/crates/shirabe/src/io/io_interface.rs @@ -92,7 +92,10 @@ pub trait IOInterfaceImmutable: std::fmt::Debug { } fn overwrite_error4(&self, message: &str, newline: bool, size: Option<i64>, verbosity: i64); - fn ask(&self, question: String, default: PhpMixed) -> PhpMixed; + /// PHP: `@throws \RuntimeException If there is no data to read in the input stream`. + /// `QuestionHelper::ask` exceptions (validator errors, `MissingInputException`) propagate + /// to the caller, hence the `anyhow::Result` return type. + fn ask(&self, question: String, default: PhpMixed) -> anyhow::Result<PhpMixed>; fn ask_confirmation(&self, question: String, default: bool) -> bool; @@ -109,6 +112,10 @@ pub trait IOInterfaceImmutable: std::fmt::Debug { /// PHP `array $choices` may be a list (`PhpMixed::List`) or an associative /// array (`PhpMixed::Array`); list choices resolve to their index, while /// associative choices resolve to their key. + /// + /// PHP: `@throws \InvalidArgumentException` — the `ChoiceQuestion` validator error + /// propagates to the caller once `$attempts` is exhausted, hence the `anyhow::Result` + /// return type. fn select( &self, question: String, @@ -117,7 +124,7 @@ pub trait IOInterfaceImmutable: std::fmt::Debug { attempts: PhpMixed, error_message: String, multiselect: bool, - ) -> PhpMixed; + ) -> anyhow::Result<PhpMixed>; fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>>; @@ -278,7 +285,7 @@ impl IOInterfaceImmutable for std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { .overwrite_error4(message, newline, size, verbosity) } - fn ask(&self, question: String, default: PhpMixed) -> PhpMixed { + fn ask(&self, question: String, default: PhpMixed) -> anyhow::Result<PhpMixed> { self.borrow().ask(question, default) } @@ -309,7 +316,7 @@ impl IOInterfaceImmutable for std::rc::Rc<std::cell::RefCell<dyn IOInterface>> { attempts: PhpMixed, error_message: String, multiselect: bool, - ) -> PhpMixed { + ) -> anyhow::Result<PhpMixed> { self.borrow().select( question, choices, diff --git a/crates/shirabe/src/io/null_io.rs b/crates/shirabe/src/io/null_io.rs index 71a81408..6db473ae 100644 --- a/crates/shirabe/src/io/null_io.rs +++ b/crates/shirabe/src/io/null_io.rs @@ -65,8 +65,8 @@ impl IOInterfaceImmutable for NullIO { ) { } - fn ask(&self, _question: String, default: PhpMixed) -> PhpMixed { - default + fn ask(&self, _question: String, default: PhpMixed) -> anyhow::Result<PhpMixed> { + Ok(default) } fn ask_confirmation(&self, _question: String, default: bool) -> bool { @@ -95,8 +95,8 @@ impl IOInterfaceImmutable for NullIO { _attempts: PhpMixed, _error_message: String, _multiselect: bool, - ) -> PhpMixed { - default + ) -> anyhow::Result<PhpMixed> { + Ok(default) } fn get_authentications( |
