aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/command
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-17 11:52:08 +0900
committernsfisis <nsfisis@gmail.com>2026-05-17 11:52:20 +0900
commit93a7671c98a9f022d757781f8fe583a2d55df07b (patch)
tree66ad0cef7ac58823262280a6bf94961c1d73f92a /crates/shirabe/src/command
parent35690acf83fa4473311a18e970ecd8156e1e6ac0 (diff)
downloadphp-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.rs63
-rw-r--r--crates/shirabe/src/command/base_config_command.rs65
-rw-r--r--crates/shirabe/src/command/base_dependency_command.rs45
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 =