diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-05-17 11:52:08 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-05-17 11:52:20 +0900 |
| commit | 93a7671c98a9f022d757781f8fe583a2d55df07b (patch) | |
| tree | 66ad0cef7ac58823262280a6bf94961c1d73f92a /crates/shirabe/src/command | |
| parent | 35690acf83fa4473311a18e970ecd8156e1e6ac0 (diff) | |
| download | php-shirabe-93a7671c98a9f022d757781f8fe583a2d55df07b.tar.gz php-shirabe-93a7671c98a9f022d757781f8fe583a2d55df07b.tar.zst php-shirabe-93a7671c98a9f022d757781f8fe583a2d55df07b.zip | |
refactor(shirabe): convert PHP abstract classes to Rust traits
PHP abstract classes are represented as traits to better align with
Rust's type system.
Diffstat (limited to 'crates/shirabe/src/command')
| -rw-r--r-- | crates/shirabe/src/command/base_command.rs | 63 | ||||
| -rw-r--r-- | crates/shirabe/src/command/base_config_command.rs | 65 | ||||
| -rw-r--r-- | crates/shirabe/src/command/base_dependency_command.rs | 45 |
3 files changed, 82 insertions, 91 deletions
diff --git a/crates/shirabe/src/command/base_command.rs b/crates/shirabe/src/command/base_command.rs index 77777ef..0324737 100644 --- a/crates/shirabe/src/command/base_command.rs +++ b/crates/shirabe/src/command/base_command.rs @@ -34,19 +34,17 @@ use crate::plugin::pre_command_run_event::PreCommandRunEvent; use crate::util::platform::Platform; /// Base class for Composer commands -#[derive(Debug)] -pub struct BaseCommand { - inner: Command, - /// @var Composer|null - composer: Option<Composer>, - /// @var IOInterface - io: Option<Box<dyn IOInterface>>, -} +pub trait BaseCommand { + fn inner(&self) -> &Command; + fn inner_mut(&mut self) -> &mut Command; + fn composer(&self) -> Option<&Composer>; + fn composer_mut(&mut self) -> Option<&mut Composer>; + fn io(&self) -> Option<&dyn IOInterface>; + fn io_mut(&mut self) -> Option<&mut dyn IOInterface>; -impl BaseCommand { /// Gets the application instance for this command. pub fn get_application(&self) -> Result<Application> { - let application = self.inner.get_application(); + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast from generic Symfony Application let application_as_composer: Option<Application> = application; if application_as_composer.is_none() { @@ -85,12 +83,13 @@ impl BaseCommand { disable_plugins: Option<bool>, disable_scripts: Option<bool>, ) -> Result<Composer> { - if self.composer.is_none() { - let application = self.inner.get_application(); + if self.composer().is_none() { + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast let application_as_composer: Option<Application> = application; if let Some(app) = application_as_composer { - self.composer = Some(app.get_composer(true, disable_plugins, disable_scripts)?); + *self.composer_mut() = + Some(app.get_composer(true, disable_plugins, disable_scripts)?); // PHP: assert($this->composer instanceof Composer) — Rust types guarantee this } else { return Err(RuntimeException { @@ -103,7 +102,7 @@ impl BaseCommand { } } - Ok(self.composer.clone().unwrap()) + Ok(self.composer().clone().unwrap()) } /// Retrieves the default Composer\Composer instance or null @@ -112,27 +111,27 @@ impl BaseCommand { disable_plugins: Option<bool>, disable_scripts: Option<bool>, ) -> Option<Composer> { - if self.composer.is_none() { - let application = self.inner.get_application(); + if self.composer().is_none() { + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast let application_as_composer: Option<Application> = application; if let Some(app) = application_as_composer { - self.composer = app + *self.composer_mut() = app .get_composer(false, disable_plugins, disable_scripts) .ok(); } } - self.composer.clone() + self.composer().clone() } pub fn set_composer(&mut self, composer: Composer) { - self.composer = Some(composer); + *self.composer_mut() = Some(composer); } /// Removes the cached composer instance pub fn reset_composer(&mut self) -> Result<()> { - self.composer = None; + *self.composer_mut() = None; self.get_application()?.reset_composer(); Ok(()) } @@ -143,29 +142,29 @@ impl BaseCommand { } pub fn get_io(&mut self) -> &dyn IOInterface { - if self.io.is_none() { - let application = self.inner.get_application(); + if self.io().is_none() { + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast let application_as_composer: Option<Application> = application; if let Some(app) = application_as_composer { - self.io = Some(app.get_io()); + *self.io_mut() = Some(app.get_io()); } else { - self.io = Some(Box::new(NullIO::new())); + *self.io_mut() = Some(Box::new(NullIO::new())); } } - &**self.io.as_ref().unwrap() + &**self.io().as_ref().unwrap() } pub fn set_io(&mut self, io: Box<dyn IOInterface>) { - self.io = Some(io); + *self.io_mut() = Some(io); } /// @inheritdoc /// /// Backport suggested values definition from symfony/console 6.1+ pub fn complete(&self, input: &CompletionInput, suggestions: &mut CompletionSuggestions) { - let definition = self.inner.get_definition(); + let definition = self.inner().get_definition(); let name = input.get_completion_name().to_string(); if CompletionInput::TYPE_OPTION_VALUE == input.get_completion_type() && definition.has_option(&name) @@ -191,7 +190,7 @@ impl BaseCommand { return; } } - self.inner.complete(input, suggestions); + self.inner().complete(input, suggestions); } /// @inheritDoc @@ -206,7 +205,7 @@ impl BaseCommand { let mut disable_scripts = input.has_parameter_option(PhpMixed::String("--no-scripts".to_string())); - let application = self.inner.get_application(); + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast let application_as_composer: Option<&Application> = None; if let Some(app) = application_as_composer { @@ -245,7 +244,7 @@ impl BaseCommand { let pre_command_run_event = PreCommandRunEvent::new( PluginEvents::PRE_COMMAND_RUN.to_string(), Box::new(input), - self.inner.get_name().to_string(), + self.inner().get_name().to_string(), ); composer.get_event_dispatcher().dispatch( pre_command_run_event.get_name(), @@ -340,7 +339,7 @@ impl BaseCommand { } } - self.inner.initialize(input, output) + self.inner().initialize(input, output) } /// Calls {@see Factory::create()} with the given arguments, taking into account flags and default states for disabling scripts and plugins @@ -357,7 +356,7 @@ impl BaseCommand { let mut disable_scripts = disable_scripts == Some(true) || input.has_parameter_option(PhpMixed::String("--no-scripts".to_string())); - let application = self.inner.get_application(); + let application = self.inner().get_application(); // TODO(phase-b): `$application instanceof Application` downcast let application_as_composer: Option<&Application> = None; if let Some(app) = application_as_composer { diff --git a/crates/shirabe/src/command/base_config_command.rs b/crates/shirabe/src/command/base_config_command.rs index 74929da..85bcb68 100644 --- a/crates/shirabe/src/command/base_config_command.rs +++ b/crates/shirabe/src/command/base_config_command.rs @@ -12,16 +12,15 @@ use shirabe_external_packages::symfony::console::input::input_interface::InputIn use shirabe_external_packages::symfony::console::output::output_interface::OutputInterface; use shirabe_php_shim::{PhpMixed, chmod, touch}; -#[derive(Debug)] -pub struct BaseConfigCommand { - inner: BaseCommand, - pub(crate) config: Option<Config>, - pub(crate) config_file: Option<JsonFile>, - pub(crate) config_source: Option<JsonConfigSource>, -} +pub trait BaseConfigCommand: BaseCommand { + fn config(&self) -> Option<&Config>; + fn config_mut(&mut self) -> Option<&mut Config>; + fn config_file(&self) -> Option<&JsonFile>; + fn config_file_mut(&mut self) -> Option<&mut JsonFile>; + fn config_source(&self) -> Option<&JsonConfigSource>; + fn config_source_mut(&mut self) -> Option<&mut JsonConfigSource>; -impl BaseConfigCommand { - pub fn initialize( + fn initialize( &mut self, input: &dyn InputInterface, output: &dyn OutputInterface, @@ -33,8 +32,8 @@ impl BaseConfigCommand { } let io = self.inner.get_io(); - self.config = Some(Factory::create_config(io)?); - let config = self.config.as_mut().unwrap(); + *self.config_mut() = Some(Factory::create_config(io)?); + let config = self.config().as_mut().unwrap(); // When using --global flag, set baseDir to home directory for correct absolute path resolution if input.get_option("global").as_bool() { @@ -53,29 +52,33 @@ impl BaseConfigCommand { std::fs::write(&config_file, "{\n}\n")?; } - let config = self.config.as_ref().unwrap(); - self.config_file = Some(JsonFile::new(config_file.clone(), None, Some(io))); - self.config_source = Some(JsonConfigSource::new(self.config_file.as_ref().unwrap())); + let config = self.config().as_ref().unwrap(); + *self.config_file_mut() = Some(JsonFile::new(config_file.clone(), None, Some(io))); + *self.config_source_mut() = + Some(JsonConfigSource::new(self.config_file().as_ref().unwrap())); // Initialize the global file if it's not there, ignoring any warnings or notices - if input.get_option("global").as_bool() && !self.config_file.as_ref().unwrap().exists() { - let path = self.config_file.as_ref().unwrap().get_path().to_string(); + if input.get_option("global").as_bool() && !self.config_file().as_ref().unwrap().exists() { + let path = self.config_file().as_ref().unwrap().get_path().to_string(); touch(&path); - self.config_file.as_mut().unwrap().write(PhpMixed::Array({ - let mut m = IndexMap::new(); - m.insert( - "config".to_string(), - Box::new(PhpMixed::Array(IndexMap::new())), - ); - m - }))?; + self.config_file_mut() + .as_mut() + .unwrap() + .write(PhpMixed::Array({ + let mut m = IndexMap::new(); + m.insert( + "config".to_string(), + Box::new(PhpMixed::Array(IndexMap::new())), + ); + m + }))?; let _ = Silencer::call(|| { chmod(&path, 0o600); Ok(()) }); } - if !self.config_file.as_ref().unwrap().exists() { + if !self.config_file().as_ref().unwrap().exists() { return Err(anyhow::anyhow!( "File \"{}\" cannot be found in the current directory", config_file @@ -86,11 +89,7 @@ impl BaseConfigCommand { } /// Get the local composer.json, global config.json, or the file passed by the user - pub(crate) fn get_composer_config_file( - &self, - input: &dyn InputInterface, - config: &Config, - ) -> String { + fn get_composer_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { if input.get_option("global").as_bool() { format!("{}/config.json", config.get("home")) } else { @@ -104,11 +103,7 @@ impl BaseConfigCommand { /// Get the local auth.json or global auth.json, or if the user passed in a file to use, /// the corresponding auth.json - pub(crate) fn get_auth_config_file( - &self, - input: &dyn InputInterface, - config: &Config, - ) -> String { + fn get_auth_config_file(&self, input: &dyn InputInterface, config: &Config) -> String { if input.get_option("global").as_bool() { format!("{}/auth.json", config.get("home")) } else { diff --git a/crates/shirabe/src/command/base_dependency_command.rs b/crates/shirabe/src/command/base_dependency_command.rs index 4ea779a..62b97ea 100644 --- a/crates/shirabe/src/command/base_dependency_command.rs +++ b/crates/shirabe/src/command/base_dependency_command.rs @@ -24,44 +24,41 @@ use crate::repository::repository_interface::{FindPackageConstraint, RepositoryI use crate::repository::root_package_repository::RootPackageRepository; use crate::util::package_info::PackageInfo; -#[derive(Debug)] -pub struct BaseDependencyCommand { - inner: BaseCommand, - pub(crate) colors: Vec<String>, -} +pub trait BaseDependencyCommand: BaseCommand { + const ARGUMENT_PACKAGE: &'static str = "package"; + const ARGUMENT_CONSTRAINT: &'static str = "version"; + const OPTION_RECURSIVE: &'static str = "recursive"; + const OPTION_TREE: &'static str = "tree"; -impl BaseDependencyCommand { - pub const ARGUMENT_PACKAGE: &'static str = "package"; - pub const ARGUMENT_CONSTRAINT: &'static str = "version"; - pub const OPTION_RECURSIVE: &'static str = "recursive"; - pub const OPTION_TREE: &'static str = "tree"; + fn colors(&self) -> &[String]; + fn colors_mut(&mut self) -> &mut [String]; - pub fn set_name(&mut self, name: &str) -> &mut Self { + fn set_name(&mut self, name: &str) -> &mut Self { self.inner.set_name(name); self } - pub fn set_aliases(&mut self, aliases: Vec<String>) -> &mut Self { + fn set_aliases(&mut self, aliases: Vec<String>) -> &mut Self { self.inner.set_aliases(aliases); self } - pub fn set_description(&mut self, description: &str) -> &mut Self { + fn set_description(&mut self, description: &str) -> &mut Self { self.inner.set_description(description); self } - pub fn set_definition(&mut self, definition: Vec<shirabe_php_shim::PhpMixed>) -> &mut Self { + fn set_definition(&mut self, definition: Vec<shirabe_php_shim::PhpMixed>) -> &mut Self { self.inner.set_definition(definition); self } - pub fn set_help(&mut self, help: &str) -> &mut Self { + fn set_help(&mut self, help: &str) -> &mut Self { self.inner.set_help(help); self } - pub(crate) fn do_execute( + fn do_execute( &mut self, input: &dyn InputInterface, output: &dyn OutputInterface, @@ -309,7 +306,7 @@ impl BaseDependencyCommand { Ok(r#return) } - pub(crate) fn print_table(&self, output: &dyn OutputInterface, results: Vec<DependentsEntry>) { + fn print_table(&self, output: &dyn OutputInterface, results: Vec<DependentsEntry>) { let mut table: Vec<Vec<String>> = vec![]; let mut doubles: IndexMap<String, bool> = IndexMap::new(); let mut results = results; @@ -362,28 +359,28 @@ impl BaseDependencyCommand { self.inner.render_table(table, output); } - pub(crate) fn init_styles(&mut self, output: &dyn OutputInterface) { - self.colors = vec![ + fn init_styles(&mut self, output: &dyn OutputInterface) { + *self.colors_mut() = vec![ "green".to_string(), "yellow".to_string(), "cyan".to_string(), "magenta".to_string(), "blue".to_string(), ]; - for color in &self.colors { + for color in &self.colors() { let style = OutputFormatterStyle::new(color.clone()); output.get_formatter().set_style(color, style); } } - pub(crate) fn print_tree(&self, results: &[DependentsEntry], prefix: &str, level: i64) { + fn print_tree(&self, results: &[DependentsEntry], prefix: &str, level: i64) { let count = results.len() as i64; let mut idx: i64 = 0; - let colors_len = self.colors.len() as i64; + let colors_len = self.colors().len() as i64; for result in results { let DependentsEntry(package, link, children) = result; - let color = &self.colors[(level % colors_len) as usize]; - let prev_color = &self.colors[((level - 1) % colors_len) as usize]; + let color = &self.colors()[(level % colors_len) as usize]; + let prev_color = &self.colors()[((level - 1) % colors_len) as usize]; idx += 1; let is_last = idx == count; let version_text = |
