aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/command/create_project_command.rs2
-rw-r--r--crates/shirabe/src/command/exec_command.rs2
-rw-r--r--crates/shirabe/src/command/init_command.rs6
-rw-r--r--crates/shirabe/src/command/package_discovery_trait.rs4
-rw-r--r--crates/shirabe/src/command/run_script_command.rs2
-rw-r--r--crates/shirabe/src/command/update_command.rs2
-rw-r--r--crates/shirabe/src/downloader/git_downloader.rs2
-rw-r--r--crates/shirabe/src/downloader/svn_downloader.rs2
-rw-r--r--crates/shirabe/src/io/buffer_io.rs4
-rw-r--r--crates/shirabe/src/io/console_io.rs44
-rw-r--r--crates/shirabe/src/io/io_interface.rs15
-rw-r--r--crates/shirabe/src/io/null_io.rs8
-rw-r--r--crates/shirabe/src/plugin/plugin_manager.rs2
-rw-r--r--crates/shirabe/src/util/auth_helper.rs6
-rw-r--r--crates/shirabe/src/util/forgejo.rs2
-rw-r--r--crates/shirabe/src/util/git.rs2
-rw-r--r--crates/shirabe/src/util/gitlab.rs2
-rw-r--r--crates/shirabe/src/util/perforce.rs12
-rw-r--r--crates/shirabe/src/util/svn.rs2
19 files changed, 71 insertions, 50 deletions
diff --git a/crates/shirabe/src/command/create_project_command.rs b/crates/shirabe/src/command/create_project_command.rs
index 68024b69..545ed533 100644
--- a/crates/shirabe/src/command/create_project_command.rs
+++ b/crates/shirabe/src/command/create_project_command.rs
@@ -173,7 +173,7 @@ impl Command for CreateProjectCommand {
);
input
.borrow_mut()
- .set_argument("directory", io.ask(prompt, PhpMixed::Null));
+ .set_argument("directory", io.ask(prompt, PhpMixed::Null)?);
}
let repository_opt = input.borrow().get_option("repository")?;
diff --git a/crates/shirabe/src/command/exec_command.rs b/crates/shirabe/src/command/exec_command.rs
index 057c9eb2..63017cd1 100644
--- a/crates/shirabe/src/command/exec_command.rs
+++ b/crates/shirabe/src/command/exec_command.rs
@@ -128,7 +128,7 @@ impl Command for ExecCommand {
PhpMixed::Int(1),
"Invalid binary name \"%s\"".to_string(),
false,
- );
+ )?;
if let Some(idx) = binary.as_int() {
input.borrow_mut().set_argument(
diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs
index 1ff4089a..4523d910 100644
--- a/crates/shirabe/src/command/init_command.rs
+++ b/crates/shirabe/src/command/init_command.rs
@@ -595,7 +595,7 @@ impl Command for InitCommand {
description_default
.map(PhpMixed::String)
.unwrap_or(PhpMixed::Null),
- );
+ )?;
input.borrow_mut().set_option("description", description);
let author = input
@@ -701,7 +701,7 @@ impl Command for InitCommand {
type_str
),
type_val,
- );
+ )?;
if type_value.as_string() == Some("") || matches!(type_value, PhpMixed::Bool(false)) {
type_value = PhpMixed::Null;
}
@@ -734,7 +734,7 @@ impl Command for InitCommand {
license.clone().unwrap_or_default()
),
license.map(PhpMixed::String).unwrap_or(PhpMixed::Null),
- );
+ )?;
let spdx = SpdxLicenses::new();
if !license.is_null()
&& !spdx.validate(license.as_string().unwrap_or(""))
diff --git a/crates/shirabe/src/command/package_discovery_trait.rs b/crates/shirabe/src/command/package_discovery_trait.rs
index 6fb83676..229d84e9 100644
--- a/crates/shirabe/src/command/package_discovery_trait.rs
+++ b/crates/shirabe/src/command/package_discovery_trait.rs
@@ -227,7 +227,7 @@ pub trait PackageDiscoveryTrait: BaseCommand {
let io = self.get_io();
loop {
- let package_input = io.ask("Search for a package: ".to_string(), PhpMixed::Null);
+ let package_input = io.ask("Search for a package: ".to_string(), PhpMixed::Null)?;
let mut package = match package_input.as_string() {
Some(s) => s.to_string(),
None => break,
@@ -694,7 +694,7 @@ pub trait PackageDiscoveryTrait: BaseCommand {
PhpMixed::Int(1),
"No package named \"%s\" is installed.".to_string(),
false,
- );
+ )?;
if let Some(idx_str) = result_mixed.as_string()
&& let Ok(idx) = idx_str.parse::<usize>()
&& let Some(selected) = similar.get(idx)
diff --git a/crates/shirabe/src/command/run_script_command.rs b/crates/shirabe/src/command/run_script_command.rs
index faa3a6dd..0887eb89 100644
--- a/crates/shirabe/src/command/run_script_command.rs
+++ b/crates/shirabe/src/command/run_script_command.rs
@@ -214,7 +214,7 @@ impl Command for RunScriptCommand {
PhpMixed::Int(1),
"Invalid script name \"%s\"".to_string(),
false,
- );
+ )?;
input.borrow_mut().set_argument("script", script)?;
diff --git a/crates/shirabe/src/command/update_command.rs b/crates/shirabe/src/command/update_command.rs
index 686e784d..5c0aae8a 100644
--- a/crates/shirabe/src/command/update_command.rs
+++ b/crates/shirabe/src/command/update_command.rs
@@ -715,7 +715,7 @@ impl UpdateCommand {
PhpMixed::Int(1),
"No package named \"%s\" is installed.".to_string(),
true,
- );
+ )?;
let packages: Vec<String> = match select_result {
PhpMixed::List(l) => l
.into_iter()
diff --git a/crates/shirabe/src/downloader/git_downloader.rs b/crates/shirabe/src/downloader/git_downloader.rs
index c2599d2b..fe725507 100644
--- a/crates/shirabe/src/downloader/git_downloader.rs
+++ b/crates/shirabe/src/downloader/git_downloader.rs
@@ -1265,7 +1265,7 @@ impl VcsDownloader for GitDownloader {
if update { "s," } else { "" }
),
PhpMixed::String("?".to_string()),
- )
+ )?
.as_string()
.map(|s| s.to_string());
let mut do_help = false;
diff --git a/crates/shirabe/src/downloader/svn_downloader.rs b/crates/shirabe/src/downloader/svn_downloader.rs
index e72bd40a..f5b7a833 100644
--- a/crates/shirabe/src/downloader/svn_downloader.rs
+++ b/crates/shirabe/src/downloader/svn_downloader.rs
@@ -308,7 +308,7 @@ impl VcsDownloader for SvnDownloader {
.ask(
" <info>Discard changes [y,n,v,?]?</info> ".to_string(),
PhpMixed::String("?".to_string()),
- )
+ )?
.as_string()
{
Some("y") => {
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(
diff --git a/crates/shirabe/src/plugin/plugin_manager.rs b/crates/shirabe/src/plugin/plugin_manager.rs
index 07b4438a..572bb3b6 100644
--- a/crates/shirabe/src/plugin/plugin_manager.rs
+++ b/crates/shirabe/src/plugin/plugin_manager.rs
@@ -875,7 +875,7 @@ impl PluginManager {
let answer = self.io.ask(
format!("Do you trust \"<fg=green;options=bold>{}</>\" to execute code and wish to enable it now? (writes \"allow-plugins\" to composer.json) [<comment>y,n,d,?</comment>] ", package),
PhpMixed::String(default.to_string()),
- );
+ )?;
let answer_str = answer.as_string().unwrap_or("");
match answer_str {
"y" | "n" | "d" => {
diff --git a/crates/shirabe/src/util/auth_helper.rs b/crates/shirabe/src/util/auth_helper.rs
index 0a2dbf86..8a621eb1 100644
--- a/crates/shirabe/src/util/auth_helper.rs
+++ b/crates/shirabe/src/util/auth_helper.rs
@@ -168,7 +168,7 @@ impl AuthHelper {
"After authorizing your token, confirm that you would like to retry the request"
.to_string(),
PhpMixed::Null,
- );
+ )?;
return Ok(PromptAuthResult {
retry: true,
@@ -420,7 +420,9 @@ impl AuthHelper {
true,
io_interface::NORMAL,
);
- let username = self.io.ask(" Username: ".to_string(), PhpMixed::Null);
+ let username = self
+ .io
+ .ask(" Username: ".to_string(), PhpMixed::Null)?;
let password = self.io.ask_and_hide_answer(" Password: ".to_string());
self.io.borrow_mut().set_authentication(
origin.to_string(),
diff --git a/crates/shirabe/src/util/forgejo.rs b/crates/shirabe/src/util/forgejo.rs
index 41d0dd3d..34d44b7b 100644
--- a/crates/shirabe/src/util/forgejo.rs
+++ b/crates/shirabe/src/util/forgejo.rs
@@ -80,7 +80,7 @@ impl Forgejo {
let username = self
.io
- .ask("Username: ".to_string(), shirabe_php_shim::PhpMixed::Null)
+ .ask("Username: ".to_string(), shirabe_php_shim::PhpMixed::Null)?
.as_string()
.map(|s| s.trim().to_string())
.unwrap_or_default();
diff --git a/crates/shirabe/src/util/git.rs b/crates/shirabe/src/util/git.rs
index 3e650fd1..1c3474ea 100644
--- a/crates/shirabe/src/util/git.rs
+++ b/crates/shirabe/src/util/git.rs
@@ -703,7 +703,7 @@ impl Git {
default_username
.map(PhpMixed::String)
.unwrap_or(PhpMixed::Null),
- )
+ )?
.as_string()
.map(|s| s.to_string()),
);
diff --git a/crates/shirabe/src/util/gitlab.rs b/crates/shirabe/src/util/gitlab.rs
index 485d4126..2b23cdbf 100644
--- a/crates/shirabe/src/util/gitlab.rs
+++ b/crates/shirabe/src/util/gitlab.rs
@@ -419,7 +419,7 @@ impl GitLab {
}
fn create_token(&mut self, scheme: &str, origin_url: &str) -> anyhow::Result<PhpMixed> {
- let username = match self.io.ask("Username: ".to_string(), PhpMixed::Null) {
+ let username = match self.io.ask("Username: ".to_string(), PhpMixed::Null)? {
PhpMixed::String(s) => s,
_ => String::new(),
};
diff --git a/crates/shirabe/src/util/perforce.rs b/crates/shirabe/src/util/perforce.rs
index 9cd804a4..e2591301 100644
--- a/crates/shirabe/src/util/perforce.rs
+++ b/crates/shirabe/src/util/perforce.rs
@@ -253,18 +253,18 @@ impl Perforce {
self.p4_user = user;
}
- pub fn query_p4_user(&mut self) {
+ pub fn query_p4_user(&mut self) -> anyhow::Result<()> {
let _ = self.get_user();
if strlen(&self.p4_user.clone().unwrap_or_default()) > 0 {
- return;
+ return Ok(());
}
self.p4_user = self.get_p4_variable("P4USER");
if strlen(&self.p4_user.clone().unwrap_or_default()) > 0 {
- return;
+ return Ok(());
}
self.p4_user = self
.io
- .ask("Enter P4 User:".to_string(), PhpMixed::Null)
+ .ask("Enter P4 User:".to_string(), PhpMixed::Null)?
.as_string()
.map(|s| s.to_string());
let command = if self.windows_flag {
@@ -280,6 +280,8 @@ impl Perforce {
)
};
self.execute_command(PhpMixed::String(command));
+
+ Ok(())
}
pub(crate) fn get_p4_variable(&mut self, name: &str) -> Option<String> {
@@ -554,7 +556,7 @@ impl Perforce {
}
pub fn p4_login(&mut self) -> anyhow::Result<()> {
- self.query_p4_user();
+ self.query_p4_user()?;
if !self.is_logged_in()? {
let password = self.query_p4_password();
if self.windows_flag {
diff --git a/crates/shirabe/src/util/svn.rs b/crates/shirabe/src/util/svn.rs
index 67501e5a..50315316 100644
--- a/crates/shirabe/src/util/svn.rs
+++ b/crates/shirabe/src/util/svn.rs
@@ -234,7 +234,7 @@ impl Svn {
self.credentials = Some(SvnCredentials {
username: self
.io
- .ask("Username: ".to_string(), PhpMixed::String("".to_string()))
+ .ask("Username: ".to_string(), PhpMixed::String("".to_string()))?
.as_string()
.unwrap_or("")
.to_string(),