diff options
Diffstat (limited to 'crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs')
| -rw-r--r-- | crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs | 185 |
1 files changed, 185 insertions, 0 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 new file mode 100644 index 0000000..3454b78 --- /dev/null +++ b/crates/shirabe-external-packages/src/symfony/console/descriptor/application_description.rs @@ -0,0 +1,185 @@ +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; +use std::cell::RefCell; +use std::rc::Rc; + +/// @internal +#[derive(Debug)] +pub struct ApplicationDescription { + application: Rc<RefCell<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, Rc<RefCell<dyn Command>>>>, + + /// @var array<string, Command> + aliases: Option<IndexMap<String, Rc<RefCell<dyn Command>>>>, +} + +impl ApplicationDescription { + pub const GLOBAL_NAMESPACE: &'static str = "_global"; + + pub fn new( + application: Rc<RefCell<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() + } + + /// @return Command[] + pub fn get_commands(&mut self) -> &IndexMap<String, Rc<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<Rc<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(|n| Box::new(PhpMixed::String(n))) + .collect(), + ), + ); + self.namespaces.as_mut().unwrap().insert(namespace, entry); + } + } + + fn sort_commands( + &self, + commands: IndexMap<String, Rc<RefCell<dyn Command>>>, + ) -> IndexMap<String, IndexMap<String, Rc<RefCell<dyn Command>>>> { + let mut namespaced_commands: IndexMap<String, IndexMap<String, Rc<RefCell<dyn Command>>>> = + IndexMap::new(); + let mut global_commands: IndexMap<String, Rc<RefCell<dyn Command>>> = IndexMap::new(); + let mut sorted_commands: IndexMap<String, IndexMap<String, Rc<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_insert_with(IndexMap::new) + .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 + } +} |
