aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe-external-packages/src/symfony/console/descriptor
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-08-09 11:19:03 +0900
committernsfisis <nsfisis@gmail.com>2026-08-09 11:19:03 +0900
commit6051927c8fa32cfffa102d2a170c5a6cf747a1b9 (patch)
tree3fe6769c90cb03ca8f1b9b825e38ad459182361e /crates/shirabe-external-packages/src/symfony/console/descriptor
parente3e8806aec771e482899ed3470e920f7b291fa95 (diff)
downloadphp-shirabe-6051927c8fa32cfffa102d2a170c5a6cf747a1b9.tar.gz
php-shirabe-6051927c8fa32cfffa102d2a170c5a6cf747a1b9.tar.zst
php-shirabe-6051927c8fa32cfffa102d2a170c5a6cf747a1b9.zip
refactor(symfony-console): extract symfony/console into the shirabe-symfony-console crate
Move `Symfony\Component\Console` out of shirabe-external-packages and into its own crate, so the path is `shirabe_symfony_console::application::Application` instead of `shirabe_external_packages::symfony::console::application::Application`. The `delegate_to_inner!` and `delegate_command_trait_impls_to_inner!` macros move with it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe-external-packages/src/symfony/console/descriptor')
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs189
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor.rs99
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor_interface.rs29
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs404
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs380
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs594
-rw-r--r--crates/shirabe-external-packages/src/symfony/console/descriptor/xml_descriptor.rs405
7 files changed, 0 insertions, 2100 deletions
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs
deleted file mode 100644
index 62fc9933..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs
+++ /dev/null
@@ -1,189 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/ApplicationDescription.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::exception::command_not_found_exception::CommandNotFoundException;
-use indexmap::IndexMap;
-use shirabe_php_shim::PhpMixed;
-
-/// @internal
-#[derive(Debug)]
-pub struct ApplicationDescription {
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- namespace: Option<String>,
- show_hidden: bool,
-
- /// @var array
- /// Each namespace entry is `['id' => string, 'commands' => string[]]`.
- namespaces: Option<IndexMap<String, IndexMap<String, PhpMixed>>>,
-
- /// @var array<string, Command>
- commands: Option<IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>>,
-
- /// @var array<string, Command>
- aliases: Option<IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>>,
-}
-
-impl ApplicationDescription {
- pub const GLOBAL_NAMESPACE: &'static str = "_global";
-
- pub fn new(
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- namespace: Option<String>,
- show_hidden: bool,
- ) -> Self {
- ApplicationDescription {
- application,
- namespace,
- show_hidden,
- namespaces: None,
- commands: None,
- aliases: None,
- }
- }
-
- pub fn get_namespaces(&mut self) -> IndexMap<String, IndexMap<String, PhpMixed>> {
- if self.namespaces.is_none() {
- self.inspect_application();
- }
-
- self.namespaces.clone().unwrap()
- }
-
- pub fn get_commands(
- &mut self,
- ) -> &IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>> {
- if self.commands.is_none() {
- self.inspect_application();
- }
-
- self.commands.as_ref().unwrap()
- }
-
- /// @throws CommandNotFoundException
- pub fn get_command(
- &self,
- name: &str,
- ) -> anyhow::Result<std::rc::Rc<std::cell::RefCell<dyn Command>>> {
- let in_commands = self
- .commands
- .as_ref()
- .map(|c| c.contains_key(name))
- .unwrap_or(false);
- let in_aliases = self
- .aliases
- .as_ref()
- .map(|a| a.contains_key(name))
- .unwrap_or(false);
- if !in_commands && !in_aliases {
- return Err(CommandNotFoundException::new(
- format!("Command \"{}\" does not exist.", name),
- vec![],
- 0,
- )
- .into());
- }
-
- Ok(self
- .commands
- .as_ref()
- .and_then(|c| c.get(name))
- .cloned()
- .unwrap_or_else(|| self.aliases.as_ref().unwrap().get(name).unwrap().clone()))
- }
-
- fn inspect_application(&mut self) {
- self.commands = Some(IndexMap::new());
- self.namespaces = Some(IndexMap::new());
-
- let namespace_filter = match &self.namespace {
- Some(ns) if !ns.is_empty() => {
- Some(self.application.borrow_mut().find_namespace(ns).unwrap())
- }
- _ => None,
- };
- let all = self
- .application
- .borrow_mut()
- .all(namespace_filter.as_deref())
- .unwrap();
- for (namespace, commands) in self.sort_commands(all) {
- let mut names: Vec<String> = vec![];
-
- for (name, command) in commands {
- let command_name = command.borrow().get_name();
- let is_hidden = command.borrow().is_hidden();
- if command_name.is_none()
- || command_name.as_deref() == Some("")
- || (!self.show_hidden && is_hidden)
- {
- continue;
- }
-
- if command_name.as_deref() == Some(name.as_str()) {
- self.commands
- .as_mut()
- .unwrap()
- .insert(name.clone(), command);
- } else {
- self.aliases
- .get_or_insert_with(IndexMap::new)
- .insert(name.clone(), command);
- }
-
- names.push(name);
- }
-
- let mut entry: IndexMap<String, PhpMixed> = IndexMap::new();
- entry.insert("id".to_string(), PhpMixed::String(namespace.clone()));
- entry.insert(
- "commands".to_string(),
- PhpMixed::List(names.into_iter().map(PhpMixed::String).collect()),
- );
- self.namespaces.as_mut().unwrap().insert(namespace, entry);
- }
- }
-
- fn sort_commands(
- &self,
- commands: IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>,
- ) -> IndexMap<String, IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>> {
- let mut namespaced_commands: IndexMap<
- String,
- IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>,
- > = IndexMap::new();
- let mut global_commands: IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>> =
- IndexMap::new();
- let mut sorted_commands: IndexMap<
- String,
- IndexMap<String, std::rc::Rc<std::cell::RefCell<dyn Command>>>,
- > = IndexMap::new();
- for (name, command) in commands {
- let key = self.application.borrow().extract_namespace(&name, Some(1));
- if ["", Self::GLOBAL_NAMESPACE].contains(&key.as_str()) {
- global_commands.insert(name, command);
- } else {
- namespaced_commands
- .entry(key)
- .or_default()
- .insert(name, command);
- }
- }
-
- if !global_commands.is_empty() {
- global_commands.sort_keys();
- sorted_commands.insert(Self::GLOBAL_NAMESPACE.to_string(), global_commands);
- }
-
- if !namespaced_commands.is_empty() {
- // ksort($namespacedCommands, \SORT_STRING)
- namespaced_commands.sort_keys();
- for (key, mut commands_set) in namespaced_commands {
- commands_set.sort_keys();
- sorted_commands.insert(key, commands_set);
- }
- }
-
- sorted_commands
- }
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor.rs
deleted file mode 100644
index 5055b49b..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/Descriptor.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::descriptor::descriptor_interface::{
- DescribableObject, DescriptorInterface,
-};
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_php_shim::PhpMixed;
-
-/// @internal
-pub trait Descriptor: DescriptorInterface {
- fn output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>;
-
- fn set_output(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>);
-
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.set_output(output);
-
- // PHP dispatches via `$object instanceof ...`; the explicit `DescribableObject` enum makes
- // that dispatch a `match`.
- match object {
- DescribableObject::InputArgument(argument) => {
- self.describe_input_argument(&argument, options)?;
- }
- DescribableObject::InputOption(option) => {
- self.describe_input_option(&option, options)?;
- }
- DescribableObject::InputDefinition(definition) => {
- self.describe_input_definition(&definition, options)?;
- }
- DescribableObject::Command(command) => {
- self.describe_command(&*command.borrow(), options)?;
- }
- DescribableObject::Application(application) => {
- self.describe_application(application, options)?;
- }
- }
-
- Ok(())
- }
-
- /// Writes content to output.
- fn write(&self, content: &str, decorated: bool) {
- self.output().borrow().write(
- &[content.to_string()],
- false,
- if decorated {
- crate::symfony::console::output::output_interface::OUTPUT_NORMAL
- } else {
- crate::symfony::console::output::output_interface::OUTPUT_RAW
- },
- );
- }
-
- /// Describes an InputArgument instance.
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-
- /// Describes an InputOption instance.
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-
- /// Describes an InputDefinition instance.
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-
- /// Describes a Command instance.
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-
- /// Describes an Application instance.
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor_interface.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor_interface.rs
deleted file mode 100644
index be0e9ff7..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/descriptor_interface.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/DescriptorInterface.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_php_shim::PhpMixed;
-
-/// The set of objects the descriptors know how to describe.
-pub enum DescribableObject {
- InputArgument(InputArgument),
- InputOption(InputOption),
- InputDefinition(InputDefinition),
- Command(std::rc::Rc<std::cell::RefCell<dyn Command>>),
- Application(std::rc::Rc<std::cell::RefCell<dyn Application>>),
-}
-
-/// Descriptor interface.
-pub trait DescriptorInterface {
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()>;
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs
deleted file mode 100644
index 3232e6fa..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/json_descriptor.rs
+++ /dev/null
@@ -1,404 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/JsonDescriptor.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::descriptor::application_description::ApplicationDescription;
-use crate::symfony::console::descriptor::descriptor::Descriptor;
-use crate::symfony::console::descriptor::descriptor_interface::{
- DescribableObject, DescriptorInterface,
-};
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_pcre::preg::Preg;
-use shirabe_php_shim::{PhpMixed, php_regex};
-
-/// JSON descriptor.
-///
-/// @internal
-#[derive(Debug, Default)]
-pub struct JsonDescriptor {
- output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>,
-}
-
-impl JsonDescriptor {
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_data(self.get_input_argument_data(argument)?, &options)?;
- Ok(())
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_data(self.get_input_option_data(option, false)?, &options)?;
- if option.is_negatable() {
- self.write_data(self.get_input_option_data(option, true)?, &options)?;
- }
- Ok(())
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_data(self.get_input_definition_data(definition)?, &options)?;
- Ok(())
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let short = matches!(options.get("short"), Some(PhpMixed::Bool(true)));
- self.write_data(self.get_command_data(command, short)?, &options)?;
- Ok(())
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let described_namespace = match options.get("namespace") {
- Some(PhpMixed::String(s)) => Some(s.clone()),
- _ => None,
- };
- let mut description =
- ApplicationDescription::new(application.clone(), described_namespace.clone(), true);
- let mut commands: Vec<PhpMixed> = vec![];
-
- let short = matches!(options.get("short"), Some(PhpMixed::Bool(true)));
- for command in description.get_commands().values() {
- let command = command.borrow();
- commands.push(PhpMixed::Array(
- self.get_command_data(&*command, short)?
- .into_iter()
- .collect(),
- ));
- }
-
- let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
- if "UNKNOWN" != application.borrow().get_name() {
- let mut application_data: IndexMap<String, PhpMixed> = IndexMap::new();
- application_data.insert(
- "name".to_string(),
- PhpMixed::String(application.borrow().get_name()),
- );
- if "UNKNOWN" != application.borrow().get_version() {
- application_data.insert(
- "version".to_string(),
- PhpMixed::String(application.borrow().get_version()),
- );
- }
- data.insert("application".to_string(), PhpMixed::Array(application_data));
- }
-
- data.insert("commands".to_string(), PhpMixed::List(commands));
-
- if let Some(described_namespace) = described_namespace {
- data.insert(
- "namespace".to_string(),
- PhpMixed::String(described_namespace),
- );
- } else {
- data.insert(
- "namespaces".to_string(),
- PhpMixed::List(
- description
- .get_namespaces()
- .into_values()
- .map(|ns| PhpMixed::Array(ns.into_iter().collect()))
- .collect(),
- ),
- );
- }
-
- self.write_data(data, &options)?;
- Ok(())
- }
-
- /// Writes data as json.
- fn write_data(
- &self,
- data: IndexMap<String, PhpMixed>,
- options: &IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let flags = match options.get("json_encoding") {
- Some(PhpMixed::Int(f)) => *f,
- _ => 0,
- };
-
- self.write(
- &shirabe_php_shim::json_encode_ex(&PhpMixed::Array(data.into_iter().collect()), flags)
- .unwrap_or_default(),
- false,
- );
- Ok(())
- }
-
- fn get_input_argument_data(
- &self,
- argument: &InputArgument,
- ) -> anyhow::Result<IndexMap<String, PhpMixed>> {
- let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
- data.insert(
- "name".to_string(),
- PhpMixed::String(argument.get_name().to_string()),
- );
- data.insert(
- "is_required".to_string(),
- PhpMixed::Bool(argument.is_required()),
- );
- data.insert("is_array".to_string(), PhpMixed::Bool(argument.is_array()));
- data.insert(
- "description".to_string(),
- PhpMixed::String(Preg::replace(
- php_regex!("/\\s*[\\r\\n]\\s*/"),
- " ",
- argument.get_description(),
- )),
- );
- data.insert(
- "default".to_string(),
- if matches!(argument.get_default(), PhpMixed::Float(f) if f.is_infinite() && *f > 0.0) {
- PhpMixed::String("INF".to_string())
- } else {
- argument.get_default().clone()
- },
- );
- Ok(data)
- }
-
- fn get_input_option_data(
- &self,
- option: &InputOption,
- negated: bool,
- ) -> anyhow::Result<IndexMap<String, PhpMixed>> {
- let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
- if negated {
- data.insert(
- "name".to_string(),
- PhpMixed::String(format!("--no-{}", option.get_name())),
- );
- data.insert("shortcut".to_string(), PhpMixed::String(String::new()));
- data.insert("accept_value".to_string(), PhpMixed::Bool(false));
- data.insert("is_value_required".to_string(), PhpMixed::Bool(false));
- data.insert("is_multiple".to_string(), PhpMixed::Bool(false));
- data.insert(
- "description".to_string(),
- PhpMixed::String(format!("Negate the \"--{}\" option", option.get_name())),
- );
- data.insert("default".to_string(), PhpMixed::Bool(false));
- } else {
- data.insert(
- "name".to_string(),
- PhpMixed::String(format!("--{}", option.get_name())),
- );
- data.insert(
- "shortcut".to_string(),
- PhpMixed::String(if let Some(shortcut) = option.get_shortcut() {
- format!("-{}", shirabe_php_shim::str_replace("|", "|-", shortcut))
- } else {
- String::new()
- }),
- );
- data.insert(
- "accept_value".to_string(),
- PhpMixed::Bool(option.accept_value()),
- );
- data.insert(
- "is_value_required".to_string(),
- PhpMixed::Bool(option.is_value_required()),
- );
- data.insert("is_multiple".to_string(), PhpMixed::Bool(option.is_array()));
- data.insert(
- "description".to_string(),
- PhpMixed::String(Preg::replace(
- php_regex!("/\\s*[\\r\\n]\\s*/"),
- " ",
- option.get_description(),
- )),
- );
- data.insert(
- "default".to_string(),
- if matches!(option.get_default(), PhpMixed::Float(f) if f.is_infinite() && *f > 0.0)
- {
- PhpMixed::String("INF".to_string())
- } else {
- option.get_default().clone()
- },
- );
- }
- Ok(data)
- }
-
- fn get_input_definition_data(
- &self,
- definition: &InputDefinition,
- ) -> anyhow::Result<IndexMap<String, PhpMixed>> {
- let mut input_arguments: IndexMap<String, PhpMixed> = IndexMap::new();
- for (name, argument) in definition.get_arguments() {
- input_arguments.insert(
- name.clone(),
- PhpMixed::Array(
- self.get_input_argument_data(argument)?
- .into_iter()
- .collect(),
- ),
- );
- }
-
- let mut input_options: IndexMap<String, PhpMixed> = IndexMap::new();
- for (name, option) in definition.get_options() {
- input_options.insert(
- name.clone(),
- PhpMixed::Array(
- self.get_input_option_data(option, false)?
- .into_iter()
- .collect(),
- ),
- );
- if option.is_negatable() {
- input_options.insert(
- format!("no-{}", name),
- PhpMixed::Array(
- self.get_input_option_data(option, true)?
- .into_iter()
- .collect(),
- ),
- );
- }
- }
-
- let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
- data.insert("arguments".to_string(), PhpMixed::Array(input_arguments));
- data.insert("options".to_string(), PhpMixed::Array(input_options));
- Ok(data)
- }
-
- fn get_command_data(
- &self,
- command: &dyn Command,
- short: bool,
- ) -> anyhow::Result<IndexMap<String, PhpMixed>> {
- let mut data: IndexMap<String, PhpMixed> = IndexMap::new();
- data.insert(
- "name".to_string(),
- match command.get_name() {
- Some(name) => PhpMixed::String(name),
- None => PhpMixed::Null,
- },
- );
- data.insert(
- "description".to_string(),
- PhpMixed::String(command.get_description()),
- );
-
- if short {
- data.insert(
- "usage".to_string(),
- PhpMixed::List(
- command
- .get_aliases()
- .into_iter()
- .map(PhpMixed::String)
- .collect(),
- ),
- );
- } else {
- command.merge_application_definition(false);
-
- let mut usage = vec![PhpMixed::String(command.get_synopsis(false))];
- usage.extend(command.get_usages().into_iter().map(PhpMixed::String));
- usage.extend(command.get_aliases().into_iter().map(PhpMixed::String));
- data.insert("usage".to_string(), PhpMixed::List(usage));
- data.insert(
- "help".to_string(),
- PhpMixed::String(command.get_processed_help()),
- );
- data.insert(
- "definition".to_string(),
- PhpMixed::Array(
- self.get_input_definition_data(&command.get_definition())?
- .into_iter()
- .collect(),
- ),
- );
- }
-
- data.insert("hidden".to_string(), PhpMixed::Bool(command.is_hidden()));
-
- Ok(data)
- }
-}
-
-impl DescriptorInterface for JsonDescriptor {
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- Descriptor::describe(self, output, object, options)
- }
-}
-
-impl Descriptor for JsonDescriptor {
- fn output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> {
- self.output.clone().unwrap()
- }
-
- fn set_output(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) {
- self.output = Some(output);
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- JsonDescriptor::describe_input_argument(self, argument, options)
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- JsonDescriptor::describe_input_option(self, option, options)
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- JsonDescriptor::describe_input_definition(self, definition, options)
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- JsonDescriptor::describe_command(self, command, options)
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- JsonDescriptor::describe_application(self, application, options)
- }
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs
deleted file mode 100644
index 48514cfe..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/markdown_descriptor.rs
+++ /dev/null
@@ -1,380 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/MarkdownDescriptor.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::descriptor::application_description::ApplicationDescription;
-use crate::symfony::console::descriptor::descriptor::Descriptor;
-use crate::symfony::console::descriptor::descriptor_interface::{
- DescribableObject, DescriptorInterface,
-};
-use crate::symfony::console::helper::helper::Helper;
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_pcre::preg::Preg;
-use shirabe_php_shim::PhpMixed;
-
-/// Markdown descriptor.
-///
-/// @internal
-#[derive(Debug, Default)]
-pub struct MarkdownDescriptor {
- output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>,
-}
-
-impl MarkdownDescriptor {
- pub fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let decorated = output.borrow().is_decorated();
- output.borrow().set_decorated(false);
-
- Descriptor::describe(self, output.clone(), object, options)?;
-
- output.borrow().set_decorated(decorated);
- Ok(())
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let name = if !argument.get_name().is_empty() {
- argument.get_name().to_string()
- } else {
- "<none>".to_string()
- };
- self.write(
- &format!(
- "#### `{}`\n\n{}* Is required: {}\n* Is array: {}\n* Default: `{}`",
- name,
- if !argument.get_description().is_empty() {
- format!(
- "{}\n\n",
- Preg::replace("/\\s*[\\r\\n]\\s*/", "\n", argument.get_description())
- )
- } else {
- String::new()
- },
- if argument.is_required() { "yes" } else { "no" },
- if argument.is_array() { "yes" } else { "no" },
- shirabe_php_shim::str_replace(
- "\n",
- "",
- &shirabe_php_shim::var_export(argument.get_default(), true),
- ),
- ),
- true,
- );
- Ok(())
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let mut name = format!("--{}", option.get_name());
- if option.is_negatable() {
- name += &format!("|--no-{}", option.get_name());
- }
- if let Some(shortcut) = option.get_shortcut() {
- name += &format!("|-{}", shirabe_php_shim::str_replace("|", "|-", shortcut));
- }
-
- self.write(
- &format!(
- "#### `{}`\n\n{}* Accept value: {}\n* Is value required: {}\n* Is multiple: {}\n* Is negatable: {}\n* Default: `{}`",
- name,
- if !option.get_description().is_empty() {
- format!(
- "{}\n\n",
- Preg::replace("/\\s*[\\r\\n]\\s*/", "\n", option.get_description())
- )
- } else {
- String::new()
- },
- if option.accept_value() { "yes" } else { "no" },
- if option.is_value_required() { "yes" } else { "no" },
- if option.is_array() { "yes" } else { "no" },
- if option.is_negatable() { "yes" } else { "no" },
- shirabe_php_shim::str_replace(
- "\n",
- "",
- &shirabe_php_shim::var_export(option.get_default(), true),
- ),
- ),
- true,
- );
- Ok(())
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let show_arguments = !definition.get_arguments().is_empty();
- if show_arguments {
- self.write("### Arguments", true);
- for argument in definition.get_arguments().values() {
- self.write("\n\n", true);
- // describeInputArgument returns null; the guarded write never runs.
- self.describe_input_argument(argument, IndexMap::new())?;
- }
- }
-
- if !definition.get_options().is_empty() {
- if show_arguments {
- self.write("\n\n", true);
- }
-
- self.write("### Options", true);
- for option in definition.get_options().values() {
- self.write("\n\n", true);
- // describeInputOption returns null; the guarded write never runs.
- self.describe_input_option(option, IndexMap::new())?;
- }
- }
- Ok(())
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- if matches!(options.get("short"), Some(PhpMixed::Bool(true))) {
- self.write(
- &format!(
- "`{}`\n{}\n\n{}### Usage\n\n{}",
- command.get_name().unwrap_or_default(),
- shirabe_php_shim::str_repeat(
- "-",
- (Helper::width(command.get_name().as_deref().unwrap_or("")) + 2) as usize
- ),
- if !command.get_description().is_empty() {
- format!("{}\n\n", command.get_description())
- } else {
- String::new()
- },
- command
- .get_aliases()
- .iter()
- .fold(String::new(), |carry, usage| {
- format!("{}* `{}`\n", carry, usage)
- }),
- ),
- true,
- );
-
- return Ok(());
- }
-
- command.merge_application_definition(false);
-
- let mut usages = vec![command.get_synopsis(false)];
- usages.extend(command.get_aliases());
- usages.extend(command.get_usages());
- self.write(
- &format!(
- "`{}`\n{}\n\n{}### Usage\n\n{}",
- command.get_name().unwrap_or_default(),
- shirabe_php_shim::str_repeat(
- "-",
- (Helper::width(command.get_name().as_deref().unwrap_or("")) + 2) as usize
- ),
- if !command.get_description().is_empty() {
- format!("{}\n\n", command.get_description())
- } else {
- String::new()
- },
- usages.iter().fold(String::new(), |carry, usage| {
- format!("{}* `{}`\n", carry, usage)
- }),
- ),
- true,
- );
-
- let help = command.get_processed_help();
- if !help.is_empty() {
- self.write("\n", true);
- self.write(&help, true);
- }
-
- let definition = command.get_definition().clone();
- if !definition.get_options().is_empty() || !definition.get_arguments().is_empty() {
- self.write("\n\n", true);
- self.describe_input_definition(&definition, IndexMap::new())?;
- }
- Ok(())
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let described_namespace = match options.get("namespace") {
- Some(PhpMixed::String(s)) => Some(s.clone()),
- _ => None,
- };
- let mut description =
- ApplicationDescription::new(application.clone(), described_namespace, false);
- let title = self.get_application_title(&*application.borrow());
-
- self.write(
- &format!(
- "{}\n{}",
- title,
- shirabe_php_shim::str_repeat("=", Helper::width(&title) as usize)
- ),
- true,
- );
-
- for namespace in description.get_namespaces().values() {
- let namespace_id = match namespace.get("id") {
- Some(PhpMixed::String(s)) => s.clone(),
- _ => String::new(),
- };
- if ApplicationDescription::GLOBAL_NAMESPACE != namespace_id {
- self.write("\n\n", true);
- self.write(&format!("**{}:**", namespace_id), true);
- }
-
- self.write("\n\n", true);
- let command_names: Vec<String> = match namespace.get("commands") {
- Some(PhpMixed::List(names)) => names
- .iter()
- .filter_map(|n| n.as_string().map(|s| s.to_string()))
- .collect(),
- _ => vec![],
- };
- self.write(
- &command_names
- .iter()
- .map(|command_name| {
- Ok(format!(
- "* [`{}`](#{})",
- command_name.clone(),
- shirabe_php_shim::str_replace(
- ":",
- "",
- &description
- .get_command(command_name)?
- .borrow()
- .get_name()
- .unwrap_or_default(),
- ),
- ))
- })
- .collect::<anyhow::Result<Vec<String>>>()?
- .join("\n"),
- true,
- );
- }
-
- for command in description.get_commands().values() {
- let command = command.borrow();
- self.write("\n\n", true);
- // describeCommand returns null; the guarded write never runs.
- self.describe_command(&*command, options.clone())?;
- }
- Ok(())
- }
-
- fn get_application_title(&self, application: &dyn Application) -> String {
- if "UNKNOWN" != application.get_name() {
- if "UNKNOWN" != application.get_version() {
- return format!("{} {}", application.get_name(), application.get_version());
- }
-
- return application.get_name();
- }
-
- "Console Tool".to_string()
- }
-}
-
-impl DescriptorInterface for MarkdownDescriptor {
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe(self, output, object, options)
- }
-}
-
-impl Descriptor for MarkdownDescriptor {
- fn output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> {
- self.output.clone().unwrap()
- }
-
- fn set_output(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) {
- self.output = Some(output);
- }
-
- /// {@inheritdoc}
- fn write(&self, content: &str, decorated: bool) {
- // PHP overrides write() only to flip the default of $decorated to true;
- // it still delegates to parent::write.
- let _ = decorated;
- self.output().borrow().write(
- &[content.to_string()],
- false,
- if decorated {
- crate::symfony::console::output::output_interface::OUTPUT_NORMAL
- } else {
- crate::symfony::console::output::output_interface::OUTPUT_RAW
- },
- );
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe_input_argument(self, argument, options)
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe_input_option(self, option, options)
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe_input_definition(self, definition, options)
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe_command(self, command, options)
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- MarkdownDescriptor::describe_application(self, application, options)
- }
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs
deleted file mode 100644
index 147fe73f..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/text_descriptor.rs
+++ /dev/null
@@ -1,594 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/TextDescriptor.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::descriptor::application_description::ApplicationDescription;
-use crate::symfony::console::descriptor::descriptor::Descriptor;
-use crate::symfony::console::descriptor::descriptor_interface::{
- DescribableObject, DescriptorInterface,
-};
-use crate::symfony::console::formatter::output_formatter::OutputFormatter;
-use crate::symfony::console::helper::helper::Helper;
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_pcre::preg::Preg;
-use shirabe_php_shim::PhpMixed;
-
-/// Text descriptor.
-///
-/// @internal
-#[derive(Debug, Default)]
-pub struct TextDescriptor {
- output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>,
-}
-
-/// Models PHP's `array<Command|string>` passed to `getColumnWidth`.
-/// `PhpMixed` cannot hold console types, so a dedicated enum is used.
-#[derive(Debug)]
-enum CommandOrString {
- Command(std::rc::Rc<std::cell::RefCell<dyn Command>>),
- String(String),
-}
-
-impl TextDescriptor {
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let default = if !argument.get_default().is_null()
- && (!matches!(
- argument.get_default(),
- PhpMixed::List(_) | PhpMixed::Array(_)
- ) || shirabe_php_shim::count(argument.get_default()) != 0)
- {
- format!(
- "<comment> [default: {}]</comment>",
- self.format_default_value(argument.get_default())?
- )
- } else {
- String::new()
- };
-
- let total_width = match options.get("total_width") {
- Some(PhpMixed::Int(w)) => *w,
- _ => Helper::width(argument.get_name()),
- };
- let spacing_width = total_width - shirabe_php_shim::strlen(argument.get_name());
-
- self.write_text(
- &format!(
- " <info>{}</info> {}{}{}",
- argument.get_name(),
- shirabe_php_shim::str_repeat(" ", spacing_width as usize),
- // + 4 = 2 spaces before <info>, 2 spaces after </info>
- Preg::replace(
- "/\\s*[\\r\\n]\\s*/",
- &format!(
- "\n{}",
- shirabe_php_shim::str_repeat(" ", (total_width + 4) as usize)
- ),
- argument.get_description(),
- ),
- default,
- ),
- &options,
- );
- Ok(())
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let default = if option.accept_value()
- && !option.get_default().is_null()
- && (!matches!(option.get_default(), PhpMixed::List(_) | PhpMixed::Array(_))
- || shirabe_php_shim::count(option.get_default()) != 0)
- {
- format!(
- "<comment> [default: {}]</comment>",
- self.format_default_value(option.get_default())?
- )
- } else {
- String::new()
- };
-
- let mut value = String::new();
- if option.accept_value() {
- value = format!("={}", shirabe_php_shim::strtoupper(option.get_name()));
-
- if option.is_value_optional() {
- value = format!("[{}]", value);
- }
- }
-
- let total_width = match options.get("total_width") {
- Some(PhpMixed::Int(w)) => *w,
- _ => self.calculate_total_width_for_options(&[option]),
- };
- let synopsis = format!(
- "{}{}",
- if option.get_shortcut().is_some() {
- format!("-{}, ", option.get_shortcut().unwrap())
- } else {
- " ".to_string()
- },
- if option.is_negatable() {
- format!("--{0}|--no-{0}", option.get_name().to_string())
- } else {
- format!("--{0}{1}", option.get_name(), value)
- }
- );
-
- let spacing_width = total_width - Helper::width(&synopsis);
-
- self.write_text(
- &format!(
- " <info>{}</info> {}{}{}{}",
- synopsis,
- shirabe_php_shim::str_repeat(" ", spacing_width as usize),
- // + 4 = 2 spaces before <info>, 2 spaces after </info>
- Preg::replace(
- "/\\s*[\\r\\n]\\s*/",
- &format!(
- "\n{}",
- shirabe_php_shim::str_repeat(" ", (total_width + 4) as usize)
- ),
- option.get_description(),
- ),
- default,
- if option.is_array() {
- "<comment> (multiple values allowed)</comment>".to_string()
- } else {
- String::new()
- },
- ),
- &options,
- );
- Ok(())
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let mut total_width = self.calculate_total_width_for_options(
- &definition
- .get_options()
- .values()
- .map(|o| o.as_ref())
- .collect::<Vec<_>>(),
- );
- for argument in definition.get_arguments().values() {
- total_width = std::cmp::max(total_width, Helper::width(argument.get_name()));
- }
-
- if !definition.get_arguments().is_empty() {
- self.write_text("<comment>Arguments:</comment>", &options);
- self.write_text("\n", &IndexMap::new());
- for argument in definition.get_arguments().values() {
- let mut merged = options.clone();
- merged.insert("total_width".to_string(), PhpMixed::Int(total_width));
- self.describe_input_argument(argument, merged)?;
- self.write_text("\n", &IndexMap::new());
- }
- }
-
- if !definition.get_arguments().is_empty() && !definition.get_options().is_empty() {
- self.write_text("\n", &IndexMap::new());
- }
-
- if !definition.get_options().is_empty() {
- let mut later_options: Vec<&InputOption> = vec![];
-
- self.write_text("<comment>Options:</comment>", &options);
- for option in definition.get_options().values() {
- if shirabe_php_shim::strlen(option.get_shortcut().unwrap_or("")) > 1 {
- later_options.push(option.as_ref());
- continue;
- }
- self.write_text("\n", &IndexMap::new());
- let mut merged = options.clone();
- merged.insert("total_width".to_string(), PhpMixed::Int(total_width));
- self.describe_input_option(option, merged)?;
- }
- for option in later_options {
- self.write_text("\n", &IndexMap::new());
- let mut merged = options.clone();
- merged.insert("total_width".to_string(), PhpMixed::Int(total_width));
- self.describe_input_option(option, merged)?;
- }
- }
- Ok(())
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- command.merge_application_definition(false);
-
- let description = command.get_description();
- if !description.is_empty() {
- self.write_text("<comment>Description:</comment>", &options);
- self.write_text("\n", &IndexMap::new());
- self.write_text(&format!(" {}", description), &IndexMap::new());
- self.write_text("\n\n", &IndexMap::new());
- }
-
- self.write_text("<comment>Usage:</comment>", &options);
- let mut usages = vec![command.get_synopsis(true)];
- usages.extend(command.get_aliases());
- usages.extend(command.get_usages());
- for usage in usages {
- self.write_text("\n", &IndexMap::new());
- self.write_text(&format!(" {}", OutputFormatter::escape(&usage)?), &options);
- }
- self.write_text("\n", &IndexMap::new());
-
- let definition = command.get_definition().clone();
- if !definition.get_options().is_empty() || !definition.get_arguments().is_empty() {
- self.write_text("\n", &IndexMap::new());
- self.describe_input_definition(&definition, options.clone())?;
- self.write_text("\n", &IndexMap::new());
- }
-
- let help = command.get_processed_help();
- if !help.is_empty() && help != description {
- self.write_text("\n", &IndexMap::new());
- self.write_text("<comment>Help:</comment>", &options);
- self.write_text("\n", &IndexMap::new());
- self.write_text(
- &format!(" {}", shirabe_php_shim::str_replace("\n", "\n ", &help)),
- &options,
- );
- self.write_text("\n", &IndexMap::new());
- }
- Ok(())
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let described_namespace = match options.get("namespace") {
- Some(PhpMixed::String(s)) => Some(s.clone()),
- _ => None,
- };
- let mut description =
- ApplicationDescription::new(application.clone(), described_namespace.clone(), false);
-
- if matches!(options.get("raw_text"), Some(v) if shirabe_php_shim::php_truthy(v)) {
- let width = self.get_column_width(
- &description
- .get_commands()
- .values()
- .map(|c| CommandOrString::Command(c.clone()))
- .collect::<Vec<_>>(),
- );
-
- let command_list: Vec<_> = description.get_commands().values().cloned().collect();
- for command in &command_list {
- let command = command.borrow();
- self.write_text(
- &format!(
- "{:<w$} {}",
- command.get_name().unwrap_or_default(),
- command.get_description(),
- w = width as usize,
- ),
- &options,
- );
- self.write_text("\n", &IndexMap::new());
- }
- } else {
- let help = application.borrow().get_help();
- if !help.is_empty() {
- self.write_text(&format!("{}\n\n", help), &options);
- }
-
- self.write_text("<comment>Usage:</comment>\n", &options);
- self.write_text(" command [options] [arguments]\n\n", &options);
-
- let app_definition = application.borrow_mut().get_definition();
- let options_only: Vec<std::rc::Rc<InputOption>> = app_definition
- .borrow()
- .get_options()
- .values()
- .cloned()
- .collect();
- let definition = InputDefinition::from_options(options_only)?;
- self.describe_input_definition(&definition, options.clone())?;
-
- self.write_text("\n", &IndexMap::new());
- self.write_text("\n", &IndexMap::new());
-
- let mut commands = description.get_commands().clone();
- let namespaces = description.get_namespaces();
- if described_namespace.is_some() && !namespaces.is_empty() {
- // make sure all alias commands are included when describing a specific namespace
- let described_namespace_info = namespaces.values().next().unwrap();
- if let Some(PhpMixed::List(names)) = described_namespace_info.get("commands") {
- let names: Vec<String> = names
- .iter()
- .filter_map(|n| n.as_string().map(|s| s.to_string()))
- .collect();
- for name in names {
- let command = description.get_command(&name)?;
- commands.insert(name, command);
- }
- }
- }
-
- // calculate max. width based on available commands per namespace
- let width = self.get_column_width(&{
- let command_keys: Vec<String> = commands.keys().cloned().collect();
- let mut merged: Vec<CommandOrString> = vec![];
- for namespace in namespaces.values() {
- if let Some(PhpMixed::List(ns_commands)) = namespace.get("commands") {
- for c in ns_commands {
- if let PhpMixed::String(name) = c
- && command_keys.contains(name)
- {
- merged.push(CommandOrString::String(name.clone()));
- }
- }
- }
- }
- merged
- });
-
- if let Some(ref described_namespace) = described_namespace {
- self.write_text(
- &format!(
- "<comment>Available commands for the \"{}\" namespace:</comment>",
- described_namespace.clone(),
- ),
- &options,
- );
- } else {
- self.write_text("<comment>Available commands:</comment>", &options);
- }
-
- for namespace in namespaces.values() {
- let ns_commands: Vec<String> = match namespace.get("commands") {
- Some(PhpMixed::List(names)) => names
- .iter()
- .filter_map(|n| match n {
- PhpMixed::String(name) if commands.contains_key(name) => {
- Some(name.clone())
- }
- _ => None,
- })
- .collect(),
- _ => vec![],
- };
-
- if ns_commands.is_empty() {
- continue;
- }
-
- let namespace_id = match namespace.get("id") {
- Some(PhpMixed::String(s)) => s.clone(),
- _ => String::new(),
- };
-
- if described_namespace.is_none()
- && ApplicationDescription::GLOBAL_NAMESPACE != namespace_id
- {
- self.write_text("\n", &IndexMap::new());
- self.write_text(&format!(" <comment>{}</comment>", namespace_id), &options);
- }
-
- for name in ns_commands {
- self.write_text("\n", &IndexMap::new());
- let spacing_width = width - Helper::width(&name);
- let command = commands.get(&name).unwrap().clone();
- let command = command.borrow();
- let command_aliases = if command.get_name().as_deref() == Some(name.as_str()) {
- self.get_command_aliases_text(&*command)
- } else {
- String::new()
- };
- self.write_text(
- &format!(
- " <info>{}</info>{}{}{}",
- name.clone(),
- shirabe_php_shim::str_repeat(" ", spacing_width as usize),
- command_aliases,
- command.get_description(),
- ),
- &options,
- );
- }
- }
-
- self.write_text("\n", &IndexMap::new());
- }
- Ok(())
- }
-
- fn write_text(&self, content: &str, options: &IndexMap<String, PhpMixed>) {
- let raw_text =
- matches!(options.get("raw_text"), Some(v) if shirabe_php_shim::php_truthy(v));
- let content = if raw_text {
- shirabe_php_shim::strip_tags(content)
- } else {
- content.to_string()
- };
- let decorated = match options.get("raw_output") {
- Some(v) => !shirabe_php_shim::php_truthy(v),
- None => true,
- };
- self.write(&content, decorated);
- }
-
- /// Formats command aliases to show them in the command description.
- fn get_command_aliases_text(&self, command: &dyn Command) -> String {
- let mut text = String::new();
- let aliases = command.get_aliases();
-
- if !aliases.is_empty() {
- text = format!("[{}] ", aliases.join("|"));
- }
-
- text
- }
-
- /// Formats input option/argument default value.
- fn format_default_value(&self, default: &PhpMixed) -> anyhow::Result<String> {
- if matches!(default, PhpMixed::Float(f) if f.is_infinite() && *f > 0.0) {
- return Ok("INF".to_string());
- }
-
- let default = match default {
- PhpMixed::String(s) => PhpMixed::String(OutputFormatter::escape(s)?),
- PhpMixed::Array(arr) => {
- let mut arr = arr.clone();
- for (_key, value) in arr.iter_mut() {
- if let PhpMixed::String(s) = &*value {
- *value = PhpMixed::String(OutputFormatter::escape(s)?);
- }
- }
- PhpMixed::Array(arr)
- }
- PhpMixed::List(list) => {
- let mut list = list.clone();
- for value in list.iter_mut() {
- if let PhpMixed::String(s) = &*value {
- *value = PhpMixed::String(OutputFormatter::escape(s)?);
- }
- }
- PhpMixed::List(list)
- }
- other => other.clone(),
- };
-
- Ok(shirabe_php_shim::str_replace(
- "\\\\",
- "\\",
- &shirabe_php_shim::json_encode_ex(
- &default,
- shirabe_php_shim::JSON_UNESCAPED_SLASHES | shirabe_php_shim::JSON_UNESCAPED_UNICODE,
- )
- .unwrap_or_default(),
- ))
- }
-
- fn get_column_width(&self, commands: &[CommandOrString]) -> i64 {
- let mut widths: Vec<i64> = vec![];
-
- for command in commands {
- // case $command instanceof Command
- match command {
- CommandOrString::Command(command) => {
- let command = command.borrow();
- widths.push(Helper::width(command.get_name().as_deref().unwrap_or("")));
- for alias in command.get_aliases() {
- widths.push(Helper::width(&alias));
- }
- }
- CommandOrString::String(s) => {
- widths.push(Helper::width(s));
- }
- }
- }
-
- if !widths.is_empty() {
- widths.into_iter().max().unwrap() + 2
- } else {
- 0
- }
- }
-
- fn calculate_total_width_for_options(&self, options: &[&InputOption]) -> i64 {
- let mut total_width: i64 = 0;
- for option in options {
- // "-" + shortcut + ", --" + name
- let mut name_length = 1
- + Helper::width(option.get_shortcut().unwrap_or("")).max(1)
- + 4
- + Helper::width(option.get_name());
- if option.is_negatable() {
- name_length += 6 + Helper::width(option.get_name()); // |--no- + name
- } else if option.accept_value() {
- let mut value_length = 1 + Helper::width(option.get_name()); // = + value
- value_length += if option.is_value_optional() { 2 } else { 0 }; // [ + ]
-
- name_length += value_length;
- }
- total_width = std::cmp::max(total_width, name_length);
- }
-
- total_width
- }
-}
-
-impl DescriptorInterface for TextDescriptor {
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- Descriptor::describe(self, output, object, options)
- }
-}
-
-impl Descriptor for TextDescriptor {
- fn output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> {
- self.output.clone().unwrap()
- }
-
- fn set_output(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) {
- self.output = Some(output);
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- TextDescriptor::describe_input_argument(self, argument, options)
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- TextDescriptor::describe_input_option(self, option, options)
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- TextDescriptor::describe_input_definition(self, definition, options)
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- TextDescriptor::describe_command(self, command, options)
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- TextDescriptor::describe_application(self, application, options)
- }
-}
diff --git a/crates/shirabe-external-packages/src/symfony/console/descriptor/xml_descriptor.rs b/crates/shirabe-external-packages/src/symfony/console/descriptor/xml_descriptor.rs
deleted file mode 100644
index c2770a43..00000000
--- a/crates/shirabe-external-packages/src/symfony/console/descriptor/xml_descriptor.rs
+++ /dev/null
@@ -1,405 +0,0 @@
-//! ref: composer/vendor/symfony/console/Descriptor/XmlDescriptor.php
-
-use crate::symfony::console::application::Application;
-use crate::symfony::console::command::command::Command;
-use crate::symfony::console::descriptor::application_description::ApplicationDescription;
-use crate::symfony::console::descriptor::descriptor::Descriptor;
-use crate::symfony::console::descriptor::descriptor_interface::{
- DescribableObject, DescriptorInterface,
-};
-use crate::symfony::console::input::input_argument::InputArgument;
-use crate::symfony::console::input::input_definition::InputDefinition;
-use crate::symfony::console::input::input_option::InputOption;
-use crate::symfony::console::output::output_interface::OutputInterface;
-use indexmap::IndexMap;
-use shirabe_php_shim::{DOMDocument, DOMNode, PhpMixed};
-
-/// XML descriptor.
-///
-/// @internal
-#[derive(Debug, Default)]
-pub struct XmlDescriptor {
- output: Option<std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>>,
-}
-
-impl XmlDescriptor {
- pub fn get_input_definition_document(&self, definition: &InputDefinition) -> DOMDocument {
- let dom = DOMDocument::new("1.0", "UTF-8");
- let definition_xml = dom.append_child(dom.create_element("definition"));
-
- let arguments_xml = definition_xml.append_child(dom.create_element("arguments"));
- for argument in definition.get_arguments().values() {
- let argument_xml = self.get_input_argument_document(argument);
- self.append_document(&arguments_xml, &argument_xml.as_node());
- }
-
- let options_xml = definition_xml.append_child(dom.create_element("options"));
- for option in definition.get_options().values() {
- let option_xml = self.get_input_option_document(option);
- self.append_document(&options_xml, &option_xml.as_node());
- }
-
- dom
- }
-
- pub fn get_command_document(&self, command: &dyn Command, short: bool) -> DOMDocument {
- let dom = DOMDocument::new("1.0", "UTF-8");
- let command_xml = dom.append_child(dom.create_element("command"));
-
- let name = command.get_name().unwrap_or_default();
- command_xml.set_attribute("id", &name);
- command_xml.set_attribute("name", &name);
- command_xml.set_attribute("hidden", if command.is_hidden() { "1" } else { "0" });
-
- let usages_xml = command_xml.append_child(dom.create_element("usages"));
-
- let description_xml = command_xml.append_child(dom.create_element("description"));
- description_xml.append_child(dom.create_text_node(&shirabe_php_shim::str_replace(
- "\n",
- "\n ",
- &command.get_description(),
- )));
-
- if short {
- for usage in command.get_aliases() {
- usages_xml.append_child(dom.create_element_with_value("usage", &usage));
- }
- } else {
- command.merge_application_definition(false);
-
- let mut usages = vec![command.get_synopsis(false)];
- usages.extend(command.get_aliases());
- usages.extend(command.get_usages());
- for usage in usages {
- usages_xml.append_child(dom.create_element_with_value("usage", &usage));
- }
-
- let help_xml = command_xml.append_child(dom.create_element("help"));
- help_xml.append_child(dom.create_text_node(&shirabe_php_shim::str_replace(
- "\n",
- "\n ",
- &command.get_processed_help(),
- )));
-
- let command_definition = command.get_definition().clone();
- let definition_xml = self.get_input_definition_document(&command_definition);
- let definition_node = definition_xml
- .get_elements_by_tag_name("definition")
- .item(0)
- .expect("input definition document always contains a <definition> element");
- self.append_document(&command_xml, &definition_node);
- }
-
- dom
- }
-
- pub fn get_application_document(
- &self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- namespace: Option<String>,
- short: bool,
- ) -> DOMDocument {
- let dom = DOMDocument::new("1.0", "UTF-8");
- let root_xml = dom.append_child(dom.create_element("symfony"));
-
- let app_name = application.borrow().get_name();
- if app_name != "UNKNOWN" {
- root_xml.set_attribute("name", &app_name);
- let app_version = application.borrow().get_version();
- if app_version != "UNKNOWN" {
- root_xml.set_attribute("version", &app_version);
- }
- }
-
- let commands_xml = root_xml.append_child(dom.create_element("commands"));
-
- let mut description =
- ApplicationDescription::new(application.clone(), namespace.clone(), true);
-
- if let Some(ref namespace) = namespace {
- commands_xml.set_attribute("namespace", namespace);
- }
-
- for command in description.get_commands().values() {
- let command = command.borrow();
- let command_xml = self.get_command_document(&*command, short);
- self.append_document(&commands_xml, &command_xml.as_node());
- }
-
- if namespace.is_none() {
- let namespaces_xml = root_xml.append_child(dom.create_element("namespaces"));
-
- let namespaces = description.get_namespaces();
- for namespace_description in namespaces.values() {
- let namespace_array_xml =
- namespaces_xml.append_child(dom.create_element("namespace"));
- let id = match namespace_description.get("id") {
- Some(PhpMixed::String(s)) => s.as_str(),
- _ => "",
- };
- namespace_array_xml.set_attribute("id", id);
-
- if let Some(PhpMixed::List(names)) = namespace_description.get("commands") {
- for name in names {
- let command_xml =
- namespace_array_xml.append_child(dom.create_element("command"));
- command_xml
- .append_child(dom.create_text_node(name.as_string().unwrap_or("")));
- }
- }
- }
- }
-
- dom
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_document(self.get_input_argument_document(argument));
- Ok(())
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_document(self.get_input_option_document(option));
- Ok(())
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- _options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- self.write_document(self.get_input_definition_document(definition));
- Ok(())
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let short = matches!(options.get("short"), Some(PhpMixed::Bool(true)));
- self.write_document(self.get_command_document(command, short));
- Ok(())
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- let namespace = match options.get("namespace") {
- Some(PhpMixed::String(s)) => Some(s.clone()),
- _ => None,
- };
- let short = matches!(options.get("short"), Some(PhpMixed::Bool(true)));
- self.write_document(self.get_application_document(application, namespace, short));
- Ok(())
- }
-
- /// Appends document children to parent node.
- fn append_document(&self, parent_node: &DOMNode, imported_parent: &DOMNode) {
- for child_node in imported_parent.child_nodes() {
- parent_node.append_child(parent_node.owner_document().import_node(&child_node, true));
- }
- }
-
- /// Writes DOM document.
- fn write_document(&self, dom: DOMDocument) {
- dom.set_format_output(true);
- let mut buf = Vec::new();
- dom.save_xml(&mut buf)
- .expect("serializing XML to an in-memory buffer cannot fail");
- let xml = String::from_utf8(buf).expect("DOM serialization yields valid UTF-8");
- self.write(&xml, false);
- }
-
- fn get_input_argument_document(&self, argument: &InputArgument) -> DOMDocument {
- let dom = DOMDocument::new("1.0", "UTF-8");
-
- let object_xml = dom.append_child(dom.create_element("argument"));
- object_xml.set_attribute("name", argument.get_name());
- object_xml.set_attribute(
- "is_required",
- if argument.is_required() { "1" } else { "0" },
- );
- object_xml.set_attribute("is_array", if argument.is_array() { "1" } else { "0" });
- let description_xml = object_xml.append_child(dom.create_element("description"));
- description_xml.append_child(dom.create_text_node(argument.get_description()));
-
- let defaults_xml = object_xml.append_child(dom.create_element("defaults"));
- let defaults: Vec<String> = match argument.get_default() {
- PhpMixed::List(_) | PhpMixed::Array(_) => {
- self.default_values_as_strings(argument.get_default())
- }
- PhpMixed::Bool(_) => vec![shirabe_php_shim::var_export(argument.get_default(), true)],
- d if shirabe_php_shim::php_truthy(d) => {
- vec![shirabe_php_shim::php_to_string(argument.get_default())]
- }
- _ => vec![],
- };
- for default in defaults {
- let default_xml = defaults_xml.append_child(dom.create_element("default"));
- default_xml.append_child(dom.create_text_node(&default));
- }
-
- dom
- }
-
- fn get_input_option_document(&self, option: &InputOption) -> DOMDocument {
- let dom = DOMDocument::new("1.0", "UTF-8");
-
- let object_xml = dom.append_child(dom.create_element("option"));
- object_xml.set_attribute("name", &format!("--{}", option.get_name()));
- let pos = shirabe_php_shim::strpos(option.get_shortcut().unwrap_or(""), "|");
- if let Some(pos) = pos {
- object_xml.set_attribute(
- "shortcut",
- &format!(
- "-{}",
- shirabe_php_shim::substr(option.get_shortcut().unwrap(), 0, Some(pos as i64))
- ),
- );
- object_xml.set_attribute(
- "shortcuts",
- &format!(
- "-{}",
- shirabe_php_shim::str_replace("|", "|-", option.get_shortcut().unwrap())
- ),
- );
- } else {
- object_xml.set_attribute(
- "shortcut",
- &match option.get_shortcut() {
- Some(s) => format!("-{}", s),
- None => String::new(),
- },
- );
- }
- object_xml.set_attribute(
- "accept_value",
- if option.accept_value() { "1" } else { "0" },
- );
- object_xml.set_attribute(
- "is_value_required",
- if option.is_value_required() { "1" } else { "0" },
- );
- object_xml.set_attribute("is_multiple", if option.is_array() { "1" } else { "0" });
- let description_xml = object_xml.append_child(dom.create_element("description"));
- description_xml.append_child(dom.create_text_node(option.get_description()));
-
- if option.accept_value() {
- let defaults: Vec<String> = match option.get_default() {
- PhpMixed::List(_) | PhpMixed::Array(_) => {
- self.default_values_as_strings(option.get_default())
- }
- PhpMixed::Bool(_) => vec![shirabe_php_shim::var_export(option.get_default(), true)],
- d if shirabe_php_shim::php_truthy(d) => {
- vec![shirabe_php_shim::php_to_string(option.get_default())]
- }
- _ => vec![],
- };
- let defaults_xml = object_xml.append_child(dom.create_element("defaults"));
-
- if !defaults.is_empty() {
- for default in defaults {
- let default_xml = defaults_xml.append_child(dom.create_element("default"));
- default_xml.append_child(dom.create_text_node(&default));
- }
- }
- }
-
- if option.is_negatable() {
- let object_xml = dom.append_child(dom.create_element("option"));
- object_xml.set_attribute("name", &format!("--no-{}", option.get_name()));
- object_xml.set_attribute("shortcut", "");
- object_xml.set_attribute("accept_value", "0");
- object_xml.set_attribute("is_value_required", "0");
- object_xml.set_attribute("is_multiple", "0");
- let description_xml = object_xml.append_child(dom.create_element("description"));
- description_xml.append_child(
- dom.create_text_node(&format!("Negate the \"--{}\" option", option.get_name())),
- );
- }
-
- dom
- }
-
- /// Helper used by the default-value branches of getInputArgumentDocument /
- /// getInputOptionDocument when the default is an array (returns it verbatim).
- fn default_values_as_strings(&self, default: &PhpMixed) -> Vec<String> {
- match default {
- PhpMixed::List(list) => list.iter().map(shirabe_php_shim::php_to_string).collect(),
- PhpMixed::Array(arr) => arr.values().map(shirabe_php_shim::php_to_string).collect(),
- _ => vec![],
- }
- }
-}
-
-impl DescriptorInterface for XmlDescriptor {
- fn describe(
- &mut self,
- output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>,
- object: DescribableObject,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- Descriptor::describe(self, output, object, options)
- }
-}
-
-impl Descriptor for XmlDescriptor {
- fn output(&self) -> std::rc::Rc<std::cell::RefCell<dyn OutputInterface>> {
- self.output.clone().unwrap()
- }
-
- fn set_output(&mut self, output: std::rc::Rc<std::cell::RefCell<dyn OutputInterface>>) {
- self.output = Some(output);
- }
-
- fn describe_input_argument(
- &mut self,
- argument: &InputArgument,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- XmlDescriptor::describe_input_argument(self, argument, options)
- }
-
- fn describe_input_option(
- &mut self,
- option: &InputOption,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- XmlDescriptor::describe_input_option(self, option, options)
- }
-
- fn describe_input_definition(
- &mut self,
- definition: &InputDefinition,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- XmlDescriptor::describe_input_definition(self, definition, options)
- }
-
- fn describe_command(
- &mut self,
- command: &dyn Command,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- XmlDescriptor::describe_command(self, command, options)
- }
-
- fn describe_application(
- &mut self,
- application: std::rc::Rc<std::cell::RefCell<dyn Application>>,
- options: IndexMap<String, PhpMixed>,
- ) -> anyhow::Result<()> {
- XmlDescriptor::describe_application(self, application, options)
- }
-}