aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-symfony-console/src/input/input_definition.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-19 23:45:37 +0900
committernsfisis <nsfisis@gmail.com>2026-08-19 23:45:37 +0900
commit0800c90a7ec0bc7a3d4c13063dfe6d2298a557bc (patch)
treeba5602549ce20b8d71136423e85dffc25deafff4 /crates/shirabe-symfony-console/src/input/input_definition.rs
parent6ed3a85dd636366d194c9810fd777db7f25e263f (diff)
downloadphp-shirabe-0800c90a7ec0bc7a3d4c13063dfe6d2298a557bc.tar.gz
php-shirabe-0800c90a7ec0bc7a3d4c13063dfe6d2298a557bc.tar.zst
php-shirabe-0800c90a7ec0bc7a3d4c13063dfe6d2298a557bc.zip
fix(input): thread a typed InputValue through the input layer
Options and arguments were stored and passed as PhpMixed even though Symfony only ever puts a string, a bool, a list of strings or null in one. get_option already narrowed to InputOptionValue at the boundary; this widens that enum into InputValue and pushes it through InputInterface, InputOption/InputArgument defaults, the Input storage, ArgvInput/ArrayInput/StringInput/CompletionInput, Command::add_option and add_argument, and the Composer-side wrappers. Two neighbouring string|int unions get types of their own: InputDefinition::{get_argument,has_argument} take an ArgumentName, and ArrayInput keys its parameters by ParameterName. has_parameter_option and get_parameter_option take the values they look for as &[&str], which is what PHP's `(array) $values` cast produced anyway. Two behaviours change along the way. Input::set_option on a negated option now negates with PHP's loose bool cast rather than treating a non-bool as false, matching `!$value`. ArrayInput::parse now resolves an integer key to an argument position instead of looking up an argument literally named "0". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-symfony-console/src/input/input_definition.rs')
-rw-r--r--crates/shirabe-symfony-console/src/input/input_definition.rs53
1 files changed, 34 insertions, 19 deletions
diff --git a/crates/shirabe-symfony-console/src/input/input_definition.rs b/crates/shirabe-symfony-console/src/input/input_definition.rs
index 8d116bc1..35fa4e52 100644
--- a/crates/shirabe-symfony-console/src/input/input_definition.rs
+++ b/crates/shirabe-symfony-console/src/input/input_definition.rs
@@ -4,8 +4,8 @@ use crate::exception::InvalidArgumentException;
use crate::exception::LogicException;
use crate::input::InputArgument;
use crate::input::InputOption;
+use crate::input::InputValue;
use indexmap::IndexMap;
-use shirabe_php_shim::PhpMixed;
/// A InputDefinition represents a set of valid command line arguments and options.
///
@@ -154,40 +154,32 @@ impl InputDefinition {
}
/// Returns an InputArgument by name or by position.
- pub fn get_argument(&self, name: &PhpMixed) -> anyhow::Result<std::rc::Rc<InputArgument>> {
+ pub fn get_argument(&self, name: &ArgumentName) -> anyhow::Result<std::rc::Rc<InputArgument>> {
if !self.has_argument(name) {
return Err(InvalidArgumentException::new(format!(
"The \"{}\" argument does not exist.",
- name.clone()
+ name
))
.into());
}
match name {
- PhpMixed::Int(index) => {
+ ArgumentName::Position(index) => {
let arguments: Vec<std::rc::Rc<InputArgument>> =
self.arguments.values().cloned().collect();
Ok(std::rc::Rc::clone(&arguments[*index as usize]))
}
- _ => {
- let key = shirabe_php_shim::php_to_string(name);
- Ok(std::rc::Rc::clone(&self.arguments[&key]))
- }
+ ArgumentName::Name(name) => Ok(std::rc::Rc::clone(&self.arguments[name])),
}
}
/// Returns true if an InputArgument object exists by name or position.
- pub fn has_argument(&self, name: &PhpMixed) -> bool {
+ pub fn has_argument(&self, name: &ArgumentName) -> bool {
match name {
- PhpMixed::Int(index) => {
- let arguments: Vec<std::rc::Rc<InputArgument>> =
- self.arguments.values().cloned().collect();
- *index >= 0 && (*index as usize) < arguments.len()
- }
- _ => {
- let key = shirabe_php_shim::php_to_string(name);
- self.arguments.contains_key(&key)
+ ArgumentName::Position(index) => {
+ *index >= 0 && (*index as usize) < self.arguments.len()
}
+ ArgumentName::Name(name) => self.arguments.contains_key(name),
}
}
@@ -210,7 +202,7 @@ impl InputDefinition {
self.required_count
}
- pub fn get_argument_defaults(&self) -> IndexMap<String, PhpMixed> {
+ pub fn get_argument_defaults(&self) -> IndexMap<String, InputValue> {
let mut values = IndexMap::new();
for argument in self.arguments.values() {
values.insert(
@@ -346,7 +338,7 @@ impl InputDefinition {
self.get_option(&self.shortcut_to_name(shortcut)?)
}
- pub fn get_option_defaults(&self) -> IndexMap<String, PhpMixed> {
+ pub fn get_option_defaults(&self) -> IndexMap<String, InputValue> {
let mut values = IndexMap::new();
for option in self.options.values() {
values.insert(option.get_name().to_string(), option.get_default().clone());
@@ -448,3 +440,26 @@ impl InputDefinition {
format!("{}{}", shirabe_php_shim::implode(" ", &elements), tail)
}
}
+
+/// The `string|int` selector [`InputDefinition::get_argument`] and
+/// [`InputDefinition::has_argument`] accept.
+#[derive(Debug, Clone)]
+pub enum ArgumentName {
+ Name(String),
+ Position(i64),
+}
+
+impl ArgumentName {
+ pub fn of(name: &str) -> Self {
+ Self::Name(name.to_string())
+ }
+}
+
+impl std::fmt::Display for ArgumentName {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Self::Name(name) => write!(f, "{}", name),
+ Self::Position(index) => write!(f, "{}", index),
+ }
+ }
+}