aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
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
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')
-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
-rw-r--r--crates/shirabe/src/dependency_resolver/rule.rs98
-rw-r--r--crates/shirabe/src/downloader/archive_downloader.rs118
-rw-r--r--crates/shirabe/src/downloader/vcs_downloader.rs313
-rw-r--r--crates/shirabe/src/io/base_io.rs57
-rw-r--r--crates/shirabe/src/package/archiver/base_exclude_filter.rs43
-rw-r--r--crates/shirabe/src/package/base_package.rs170
-rw-r--r--crates/shirabe/src/repository/vcs/vcs_driver.rs119
10 files changed, 402 insertions, 689 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 =
diff --git a/crates/shirabe/src/dependency_resolver/rule.rs b/crates/shirabe/src/dependency_resolver/rule.rs
index 1ba1dd7..bc2c68e 100644
--- a/crates/shirabe/src/dependency_resolver/rule.rs
+++ b/crates/shirabe/src/dependency_resolver/rule.rs
@@ -39,19 +39,7 @@ pub enum ReasonData {
},
}
-/// @phpstan-type ReasonData Link|BasePackage|string|int|array{packageName: string, constraint: ConstraintInterface}|array{package: BasePackage}
-#[derive(Debug)]
-pub struct Rule {
- /// @var int
- pub(crate) bitfield: i64,
- /// @var Request
- pub(crate) request: Option<Request>,
- /// @var Link|BasePackage|ConstraintInterface|string
- /// @phpstan-var ReasonData
- pub(crate) reason_data: ReasonData,
-}
-
-impl Rule {
+pub trait Rule: std::fmt::Display {
// reason constants and // their reason data contents
pub const RULE_ROOT_REQUIRE: i64 = 2;
pub const RULE_FIXED: i64 = 3;
@@ -67,11 +55,24 @@ impl Rule {
const BITFIELD_REASON: i64 = 8;
const BITFIELD_DISABLED: i64 = 16;
+ fn bitfield(&self) -> i64;
+ fn bitfield_mut(&mut self) -> &mut i64;
+ fn request(&self) -> Option<&Request>;
+ fn request_mut(&mut self) -> Option<&mut Request>;
+ fn reason_data(&self) -> Option<&ReasonData>;
+ fn reason_data_mut(&mut self) -> Option<&mut ReasonData>;
+
+ fn get_literals(&self) -> Vec<i64>;
+ fn get_hash(&self) -> PhpMixed;
+ fn to_string(&self) -> String;
+ fn equals(&self, rule: &dyn Rule) -> bool;
+ fn is_assertion(&self) -> bool;
+
/// @param self::RULE_* $reason A RULE_* constant describing the reason for generating this rule
/// @param mixed $reasonData
///
/// @phpstan-param ReasonData $reasonData
- pub fn new(reason: i64, reason_data: ReasonData) -> Self {
+ fn new(reason: i64, reason_data: ReasonData) -> Self {
let bitfield = (0i64 << Self::BITFIELD_DISABLED)
| (reason << Self::BITFIELD_REASON)
| (255i64 << Self::BITFIELD_TYPE);
@@ -83,16 +84,16 @@ impl Rule {
}
/// @return self::RULE_*
- pub fn get_reason(&self) -> i64 {
+ fn get_reason(&self) -> i64 {
(self.bitfield & (255 << Self::BITFIELD_REASON)) >> Self::BITFIELD_REASON
}
/// @phpstan-return ReasonData
- pub fn get_reason_data(&self) -> &ReasonData {
+ fn get_reason_data(&self) -> &ReasonData {
&self.reason_data
}
- pub fn get_required_package(&self) -> Option<String> {
+ fn get_required_package(&self) -> Option<String> {
match self.get_reason() {
r if r == Self::RULE_ROOT_REQUIRE => match self.get_reason_data() {
ReasonData::RootRequire { package_name, .. } => Some(package_name.clone()),
@@ -111,33 +112,33 @@ impl Rule {
}
/// @param RuleSet::TYPE_* $type
- pub fn set_type(&mut self, r#type: i64) {
+ fn set_type(&mut self, r#type: i64) {
self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_TYPE))
| ((255 & r#type) << Self::BITFIELD_TYPE);
}
- pub fn get_type(&self) -> i64 {
+ fn get_type(&self) -> i64 {
(self.bitfield & (255 << Self::BITFIELD_TYPE)) >> Self::BITFIELD_TYPE
}
- pub fn disable(&mut self) {
+ fn disable(&mut self) {
self.bitfield = (self.bitfield & !(255i64 << Self::BITFIELD_DISABLED))
| (1i64 << Self::BITFIELD_DISABLED);
}
- pub fn enable(&mut self) {
+ fn enable(&mut self) {
self.bitfield &= !(255i64 << Self::BITFIELD_DISABLED);
}
- pub fn is_disabled(&self) -> bool {
+ fn is_disabled(&self) -> bool {
0 != ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED)
}
- pub fn is_enabled(&self) -> bool {
+ fn is_enabled(&self) -> bool {
0 == ((self.bitfield & (255 << Self::BITFIELD_DISABLED)) >> Self::BITFIELD_DISABLED)
}
- pub fn is_caused_by_lock(
+ fn is_caused_by_lock(
&self,
_repository_set: &RepositorySet,
request: &Request,
@@ -205,7 +206,7 @@ impl Rule {
}
/// @internal
- pub fn get_source_package(&self, pool: &Pool) -> Result<Box<BasePackage>> {
+ fn get_source_package(&self, pool: &Pool) -> Result<Box<BasePackage>> {
let literals = self.get_literals();
match self.get_reason() {
@@ -244,14 +245,14 @@ impl Rule {
/// @param BasePackage[] $installedMap
/// @param array<Rule[]> $learnedPool
- pub fn get_pretty_string(
+ fn get_pretty_string(
&self,
repository_set: &RepositorySet,
request: &Request,
pool: &mut Pool,
is_verbose: bool,
installed_map: IndexMap<i64, Box<BasePackage>>,
- _learned_pool: IndexMap<i64, Vec<Box<dyn RuleTrait>>>,
+ _learned_pool: IndexMap<i64, Vec<Box<dyn Rule>>>,
) -> String {
let mut literals = self.get_literals();
@@ -621,7 +622,7 @@ impl Rule {
}
/// @param array<int|BasePackage> $literalsOrPackages An array containing packages or literals
- pub(crate) fn format_packages_unique(
+ fn format_packages_unique(
&self,
pool: &Pool,
literals_or_packages: Vec<Box<BasePackage>>,
@@ -677,44 +678,3 @@ impl Rule {
package
}
}
-
-/// PHP abstract methods on Rule — concrete subclasses must implement.
-pub trait RuleTrait: std::any::Any {
- /// @return list<int>
- fn get_literals(&self) -> Vec<i64>;
-
- /// @return int|string
- fn get_hash(&self) -> PhpMixed;
-
- fn to_string(&self) -> String;
-
- fn equals(&self, rule: &dyn RuleTrait) -> bool;
-
- fn is_assertion(&self) -> bool;
-}
-
-// TODO(phase-b): abstract method dispatch — currently Rule has stubs that subclasses override.
-impl Rule {
- pub fn get_literals(&self) -> Vec<i64> {
- todo!("abstract: implemented by subclass")
- }
-
- pub fn get_hash(&self) -> PhpMixed {
- todo!("abstract: implemented by subclass")
- }
-
- pub fn equals(&self, _rule: &Rule) -> bool {
- todo!("abstract: implemented by subclass")
- }
-
- pub fn is_assertion(&self) -> bool {
- todo!("abstract: implemented by subclass")
- }
-}
-
-impl std::fmt::Display for Rule {
- fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- // TODO(phase-b): abstract; subclasses provide __toString
- todo!("abstract: implemented by subclass")
- }
-}
diff --git a/crates/shirabe/src/downloader/archive_downloader.rs b/crates/shirabe/src/downloader/archive_downloader.rs
index 59704e3..03edffe 100644
--- a/crates/shirabe/src/downloader/archive_downloader.rs
+++ b/crates/shirabe/src/downloader/archive_downloader.rs
@@ -1,9 +1,5 @@
//! ref: composer/src/Composer/Downloader/ArchiveDownloader.php
-use crate::dependency_resolver::operation::install_operation::InstallOperation;
-use crate::downloader::file_downloader::FileDownloader;
-use crate::package::package_interface::PackageInterface;
-use crate::util::platform::Platform;
use anyhow::Result;
use indexmap::IndexMap;
use shirabe_external_packages::react::promise::promise_interface::PromiseInterface;
@@ -12,64 +8,84 @@ use shirabe_php_shim::{
DIRECTORY_SEPARATOR, RuntimeException, bin2hex, file_exists, is_dir, random_bytes, realpath,
};
-#[derive(Debug)]
-pub struct ArchiveDownloader {
- pub(crate) inner: FileDownloader,
- pub(crate) cleanup_executed: IndexMap<String, bool>,
-}
+use crate::dependency_resolver::operation::install_operation::InstallOperation;
+use crate::downloader::file_downloader::FileDownloader;
+use crate::package::package_interface::PackageInterface;
+use crate::util::platform::Platform;
+
+pub trait ArchiveDownloader {
+ fn inner(&self) -> &FileDownloader;
+ fn inner_mut(&mut self) -> &mut FileDownloader;
+ fn cleanup_executed(&self) -> &IndexMap<String, bool>;
+ fn cleanup_executed_mut(&mut self) -> &mut IndexMap<String, bool>;
+
+ fn extract(
+ &self,
+ package: &dyn PackageInterface,
+ file: &str,
+ path: &str,
+ ) -> Result<Box<dyn PromiseInterface>>;
-impl ArchiveDownloader {
- pub fn prepare(
+ fn prepare(
&mut self,
r#type: &str,
package: &dyn PackageInterface,
path: &str,
prev_package: Option<&dyn PackageInterface>,
) -> Result<Box<dyn PromiseInterface>> {
- self.cleanup_executed.remove(package.get_name());
-
- self.inner.prepare(r#type, package, path, prev_package)
+ self.cleanup_executed_mut().remove(package.get_name());
+ self.inner_mut()
+ .prepare(r#type, package, path, prev_package)
}
- pub fn cleanup(
+ fn cleanup(
&mut self,
r#type: &str,
package: &dyn PackageInterface,
path: &str,
prev_package: Option<&dyn PackageInterface>,
) -> Result<Box<dyn PromiseInterface>> {
- self.cleanup_executed
+ self.cleanup_executed_mut()
.insert(package.get_name().to_string(), true);
-
- self.inner.cleanup(r#type, package, path, prev_package)
+ self.inner_mut()
+ .cleanup(r#type, package, path, prev_package)
}
- pub fn install(
+ /// @inheritDoc
+ ///
+ /// @throws \RuntimeException
+ /// @throws \UnexpectedValueException
+ fn install(
&mut self,
package: &dyn PackageInterface,
path: &str,
output: bool,
) -> Result<Box<dyn PromiseInterface>> {
if output {
- self.inner.io.write_error(&format!(
+ self.inner().io.write_error(&format!(
" - {}{}",
InstallOperation::format(package, false),
self.get_install_operation_appendix(package, path)
));
}
- let vendor_dir = self.inner.config.get("vendor-dir");
+ let vendor_dir = self.inner().config.get("vendor-dir");
// clean up the target directory, unless it contains the vendor dir, as the vendor dir contains
// the archive to be extracted. This is the case when installing with create-project in the current directory
// but in that case we ensure the directory is empty already in ProjectInstaller so no need to empty it here.
- if !self.inner.filesystem.normalize_path(&vendor_dir).contains(
- &self
- .inner
- .filesystem
- .normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR)),
- ) {
- self.inner.filesystem.empty_directory(path);
+ if !self
+ .inner()
+ .filesystem
+ .normalize_path(&vendor_dir)
+ .contains(
+ &self
+ .inner()
+ .filesystem
+ .normalize_path(&format!("{}{}", path, DIRECTORY_SEPARATOR)),
+ )
+ {
+ self.inner_mut().filesystem.empty_directory(path);
}
let temporary_dir;
@@ -80,33 +96,34 @@ impl ArchiveDownloader {
}
}
- self.inner.add_cleanup_path(package, &temporary_dir);
+ self.inner_mut().add_cleanup_path(package, &temporary_dir);
// avoid cleaning up $path if installing in "." for eg create-project as we can not
// delete the directory we are currently in on windows
if !is_dir(path) || realpath(path) != Platform::get_cwd() {
- self.inner.add_cleanup_path(package, path);
+ self.inner_mut().add_cleanup_path(package, path);
}
- self.inner
+ self.inner_mut()
.filesystem
.ensure_directory_exists(&temporary_dir);
- let file_name = self.inner.get_file_name(package, path);
+ let file_name = self.inner().get_file_name(package, path);
- let filesystem = &self.inner.filesystem;
+ let filesystem = &self.inner().filesystem;
let cleanup = move || {
// remove cache if the file was corrupted
- self.inner.clear_last_cache_write(package);
+ self.inner_mut().clear_last_cache_write(package);
// clean up
filesystem.remove_directory(&temporary_dir);
if is_dir(path) && realpath(path) != Platform::get_cwd() {
filesystem.remove_directory(path);
}
- self.inner.remove_cleanup_path(package, &temporary_dir);
+ self.inner_mut()
+ .remove_cleanup_path(package, &temporary_dir);
let realpath_result = realpath(path);
if let Some(realpath_val) = realpath_result {
- self.inner.remove_cleanup_path(package, &realpath_val);
+ self.inner_mut().remove_cleanup_path(package, &realpath_val);
}
};
@@ -155,7 +172,10 @@ impl ArchiveDownloader {
code: 0,
}.into());
}
- rename_recursively.as_ref().unwrap()(&file, &format!("{}/{}", to, file_basename))?;
+ rename_recursively.as_ref().unwrap()(
+ &file,
+ &format!("{}/{}", to, file_basename),
+ )?;
} else {
filesystem.rename(&file, &format!("{}/{}", to, file_basename));
}
@@ -179,7 +199,9 @@ impl ArchiveDownloader {
}
let content_dir = get_folder_content(&temporary_dir);
- let single_dir_at_top_level = content_dir.len() == 1 && is_dir(&content_dir[0].to_string_lossy().to_string());
+ let single_dir_at_top_level =
+ content_dir.len() == 1
+ && is_dir(&content_dir[0].to_string_lossy().to_string());
if rename_as_one {
// if the target $path is clear, we can rename the whole package in one go instead of looping over the contents
@@ -204,8 +226,8 @@ impl ArchiveDownloader {
Ok(promise.then(
Box::new(move || -> Result<()> {
- self.inner.remove_cleanup_path(package, &temporary_dir);
- self.inner.remove_cleanup_path(package, path);
+ self.inner_mut().remove_cleanup_path(package, &temporary_dir);
+ self.inner_mut().remove_cleanup_path(package, path);
Ok(())
}),
None,
@@ -218,20 +240,8 @@ impl ArchiveDownloader {
))
}
- pub fn get_install_operation_appendix(
- &self,
- _package: &dyn PackageInterface,
- _path: &str,
- ) -> &str {
+ /// @inheritDoc
+ fn get_install_operation_appendix(&self, _package: &dyn PackageInterface, _path: &str) -> &str {
": Extracting archive"
}
-
- pub(crate) fn extract(
- &self,
- _package: &dyn PackageInterface,
- _file: &str,
- _path: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- todo!()
- }
}
diff --git a/crates/shirabe/src/downloader/vcs_downloader.rs b/crates/shirabe/src/downloader/vcs_downloader.rs
index 37954d2..b76c242 100644
--- a/crates/shirabe/src/downloader/vcs_downloader.rs
+++ b/crates/shirabe/src/downloader/vcs_downloader.rs
@@ -23,40 +23,58 @@ use crate::package::version::version_parser::VersionParser;
use crate::util::filesystem::Filesystem;
use crate::util::process_executor::ProcessExecutor;
-#[derive(Debug)]
-pub struct VcsDownloader {
- pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
- pub(crate) process: ProcessExecutor,
- pub(crate) filesystem: Filesystem,
- /// @var array<string, true>
- pub(crate) has_cleaned_changes: IndexMap<String, bool>,
-}
+pub trait VcsDownloader:
+ DownloaderInterface + ChangeReportInterface + VcsCapableDownloaderInterface
+{
+ fn io(&self) -> &dyn IOInterface;
+ fn io_mut(&mut self) -> &mut dyn IOInterface;
+ fn config(&self) -> &Config;
+ fn config_mut(&mut self) -> &mut Config;
+ fn process(&self) -> &ProcessExecutor;
+ fn process_mut(&mut self) -> &mut ProcessExecutor;
+ fn filesystem(&self) -> &Filesystem;
+ fn filesystem_mut(&mut self) -> &mut Filesystem;
+ fn has_cleaned_changes(&self) -> &IndexMap<String, bool>;
+ fn has_cleaned_changes_mut(&mut self) -> &mut IndexMap<String, bool>;
-impl VcsDownloader {
- pub fn new(
- io: Box<dyn IOInterface>,
- config: Config,
- process: Option<ProcessExecutor>,
- fs: Option<Filesystem>,
- ) -> Self {
- // TODO(phase-b): ProcessExecutor::new takes &dyn IOInterface; Filesystem::new takes ProcessExecutor
- let process = process.unwrap_or_else(|| ProcessExecutor::new(&*io));
- let filesystem = fs.unwrap_or_else(|| Filesystem::new(&process));
- Self {
- io,
- config,
- process,
- filesystem,
- has_cleaned_changes: IndexMap::new(),
- }
- }
+ /// Downloads data needed to run an install/update later
+ fn do_download(
+ &mut self,
+ package: &dyn PackageInterface,
+ path: &str,
+ url: &str,
+ prev_package: Option<&dyn PackageInterface>,
+ ) -> Result<Box<dyn PromiseInterface>>;
+
+ /// Downloads specific package into specific folder.
+ fn do_install(
+ &mut self,
+ package: &dyn PackageInterface,
+ path: &str,
+ url: &str,
+ ) -> Result<Box<dyn PromiseInterface>>;
+
+ /// Updates specific package in specific folder from initial to target version.
+ fn do_update(
+ &mut self,
+ initial: &dyn PackageInterface,
+ target: &dyn PackageInterface,
+ path: &str,
+ url: &str,
+ ) -> Result<Box<dyn PromiseInterface>>;
+
+ /// Fetches the commit logs between two commits
+ fn get_commit_logs(&self, from_reference: &str, to_reference: &str, path: &str) -> String;
- pub fn get_installation_source(&self) -> String {
+ /// Checks if VCS metadata repository has been initialized
+ /// repository example: .git|.svn|.hg
+ fn has_metadata_repository(&self, path: &str) -> bool;
+
+ fn get_installation_source(&self) -> String {
"source".to_string()
}
- pub fn download(
+ fn download(
&mut self,
package: &dyn PackageInterface,
path: &str,
@@ -88,8 +106,8 @@ impl VcsDownloader {
if is_phpunit_exception {
return Err(e);
}
- if self.io.is_debug() {
- self.io.write_error(
+ if self.io().is_debug() {
+ self.io_mut().write_error(
PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
true,
IOInterface::NORMAL,
@@ -100,7 +118,7 @@ impl VcsDownloader {
.collect(),
)) > 0
{
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(" Failed, trying the next URL".to_string()),
true,
IOInterface::NORMAL,
@@ -121,7 +139,7 @@ impl VcsDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- pub fn prepare(
+ fn prepare(
&mut self,
r#type: &str,
package: &dyn PackageInterface,
@@ -130,10 +148,10 @@ impl VcsDownloader {
) -> Result<Box<dyn PromiseInterface>> {
if r#type == "update" {
self.clean_changes(prev_package.unwrap(), path, true)?;
- self.has_cleaned_changes
+ self.has_cleaned_changes_mut()
.insert(prev_package.unwrap().get_unique_name(), true);
} else if r#type == "install" {
- self.filesystem.empty_directory(path);
+ self.filesystem_mut().empty_directory(path);
} else if r#type == "uninstall" {
self.clean_changes(package, path, false)?;
}
@@ -141,7 +159,7 @@ impl VcsDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- pub fn cleanup(
+ fn cleanup(
&mut self,
r#type: &str,
_package: &dyn PackageInterface,
@@ -150,18 +168,21 @@ impl VcsDownloader {
) -> Result<Box<dyn PromiseInterface>> {
if r#type == "update"
&& prev_package
- .map(|p| self.has_cleaned_changes.contains_key(&p.get_unique_name()))
+ .map(|p| {
+ self.has_cleaned_changes()
+ .contains_key(&p.get_unique_name())
+ })
.unwrap_or(false)
{
self.reapply_changes(path);
- self.has_cleaned_changes
+ self.has_cleaned_changes_mut()
.shift_remove(&prev_package.unwrap().get_unique_name());
}
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- pub fn install(
+ fn install(
&mut self,
package: &dyn PackageInterface,
path: &str,
@@ -177,7 +198,7 @@ impl VcsDownloader {
.into());
}
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(format!(
" - {}: ",
InstallOperation::format(package, false)
@@ -199,8 +220,8 @@ impl VcsDownloader {
if is_phpunit_exception {
return Err(e);
}
- if self.io.is_debug() {
- self.io.write_error(
+ if self.io().is_debug() {
+ self.io_mut().write_error(
PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
true,
IOInterface::NORMAL,
@@ -211,7 +232,7 @@ impl VcsDownloader {
.collect(),
)) > 0
{
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(" Failed, trying the next URL".to_string()),
true,
IOInterface::NORMAL,
@@ -232,7 +253,7 @@ impl VcsDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- pub fn update(
+ fn update(
&mut self,
initial: &dyn PackageInterface,
target: &dyn PackageInterface,
@@ -249,7 +270,7 @@ impl VcsDownloader {
.into());
}
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(format!(
" - {}: ",
UpdateOperation::format(initial, target, false),
@@ -277,8 +298,8 @@ impl VcsDownloader {
if is_phpunit_exception {
return Err(e);
}
- if self.io.is_debug() {
- self.io.write_error(
+ if self.io().is_debug() {
+ self.io_mut().write_error(
PhpMixed::String(format!("Failed: [{}] {}", get_class(&e), e,)),
true,
IOInterface::NORMAL,
@@ -289,7 +310,7 @@ impl VcsDownloader {
.collect(),
)) > 0
{
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(" Failed, trying the next URL".to_string()),
true,
IOInterface::NORMAL,
@@ -302,7 +323,7 @@ impl VcsDownloader {
// print the commit logs if in verbose mode and VCS metadata is present
// because in case of missing metadata code would trigger another exception
- if exception.is_none() && self.io.is_verbose() && self.has_metadata_repository(path) {
+ if exception.is_none() && self.io().is_verbose() && self.has_metadata_repository(path) {
let mut message = "Pulling in changes:";
let mut logs = self.get_commit_logs(
initial.get_source_reference().unwrap_or(""),
@@ -329,12 +350,12 @@ impl VcsDownloader {
// escape angle brackets for proper output in the console
logs = str_replace("<", "\\<", &logs);
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(format!(" {}", message)),
true,
IOInterface::NORMAL,
);
- self.io
+ self.io_mut()
.write_error(PhpMixed::String(logs), true, IOInterface::NORMAL);
}
}
@@ -348,12 +369,12 @@ impl VcsDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- pub fn remove(
+ fn remove(
&mut self,
package: &dyn PackageInterface,
path: &str,
) -> Result<Box<dyn PromiseInterface>> {
- self.io.write_error(
+ self.io_mut().write_error(
PhpMixed::String(format!(
" - {}",
UninstallOperation::format(package, false)
@@ -362,7 +383,7 @@ impl VcsDownloader {
IOInterface::NORMAL,
);
- let promise = self.filesystem.remove_directory_async(path);
+ let promise = self.filesystem_mut().remove_directory_async(path);
let path = path.to_string();
Ok(
@@ -380,9 +401,9 @@ impl VcsDownloader {
)
}
- pub fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> {
+ fn get_vcs_reference(&self, package: &dyn PackageInterface, path: &str) -> Option<String> {
let parser = VersionParser::new();
- let guesser = VersionGuesser::new(&self.config, &self.process, &parser, &*self.io);
+ let guesser = VersionGuesser::new(self.config(), self.process(), &parser, self.io());
let dumper = ArrayDumper::new();
let package_config = dumper.dump(package);
@@ -398,12 +419,9 @@ impl VcsDownloader {
/// Prompt the user to check if changes should be stashed/removed or the operation aborted
///
- /// @param bool $update if true (update) the changes can be stashed and reapplied after an update,
- /// if false (remove) the changes should be assumed to be lost if the operation is not aborted
- ///
- /// @throws \RuntimeException in case the operation must be aborted
- /// @phpstan-return PromiseInterface<void|null>
- pub(crate) fn clean_changes(
+ /// @param bool $update if true (update) the changes can be stashed and reapplied after an update,
+ /// if false (remove) the changes should be assumed to be lost if the operation is not aborted
+ fn clean_changes(
&self,
package: &dyn PackageInterface,
path: &str,
@@ -421,90 +439,10 @@ impl VcsDownloader {
Ok(shirabe_external_packages::react::promise::resolve(None))
}
- /// Reapply previously stashes changes if applicable, only called after an update (regardless if successful or not)
- ///
- /// @throws \RuntimeException in case the operation must be aborted or the patch does not apply cleanly
- pub(crate) fn reapply_changes(&self, _path: &str) {}
-
- /// Downloads data needed to run an install/update later
- ///
- /// @param PackageInterface $package package instance
- /// @param string $path download path
- /// @param string $url package url
- /// @param PackageInterface|null $prevPackage previous package (in case of an update)
- /// @phpstan-return PromiseInterface<void|null>
- // TODO(phase-b): abstract; overridden by concrete subclasses (GitDownloader, SvnDownloader, ...)
- pub(crate) fn do_download(
- &mut self,
- _package: &dyn PackageInterface,
- _path: &str,
- _url: &str,
- _prev_package: Option<&dyn PackageInterface>,
- ) -> Result<Box<dyn PromiseInterface>> {
- todo!("abstract: implemented by subclass")
- }
-
- /// Downloads specific package into specific folder.
- ///
- /// @param PackageInterface $package package instance
- /// @param string $path download path
- /// @param string $url package url
- /// @phpstan-return PromiseInterface<void|null>
- // TODO(phase-b): abstract; overridden by concrete subclasses
- pub(crate) fn do_install(
- &mut self,
- _package: &dyn PackageInterface,
- _path: &str,
- _url: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- todo!("abstract: implemented by subclass")
- }
-
- /// Updates specific package in specific folder from initial to target version.
- ///
- /// @param PackageInterface $initial initial package
- /// @param PackageInterface $target updated package
- /// @param string $path download path
- /// @param string $url package url
- /// @phpstan-return PromiseInterface<void|null>
- // TODO(phase-b): abstract; overridden by concrete subclasses
- pub(crate) fn do_update(
- &mut self,
- _initial: &dyn PackageInterface,
- _target: &dyn PackageInterface,
- _path: &str,
- _url: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- todo!("abstract: implemented by subclass")
- }
-
- /// Fetches the commit logs between two commits
- ///
- /// @param string $fromReference the source reference
- /// @param string $toReference the target reference
- /// @param string $path the package path
- // TODO(phase-b): abstract; overridden by concrete subclasses
- pub(crate) fn get_commit_logs(
- &self,
- _from_reference: &str,
- _to_reference: &str,
- _path: &str,
- ) -> String {
- todo!("abstract: implemented by subclass")
- }
-
- /// Checks if VCS metadata repository has been initialized
- /// repository example: .git|.svn|.hg
- // TODO(phase-b): abstract; overridden by concrete subclasses
- pub(crate) fn has_metadata_repository(&self, _path: &str) -> bool {
- todo!("abstract: implemented by subclass")
- }
+ /// Reapply previously stashed changes if applicable, only called after an update (regardless if successful or not)
+ fn reapply_changes(&self, _path: &str) {}
- /// @param string[] $urls
- ///
- /// @return string[]
fn prepare_urls(&self, mut urls: Vec<String>) -> Vec<String> {
- // PHP: foreach ($urls as $index => $url) — mutates in place
for index in 0..urls.len() {
let mut url = urls[index].clone();
if Filesystem::is_local_path(&url) {
@@ -532,91 +470,4 @@ impl VcsDownloader {
urls
}
-
- // TODO(phase-b): get_local_changes belongs to ChangeReportInterface, implemented by subclasses
- pub(crate) fn get_local_changes(
- &self,
- _package: &dyn PackageInterface,
- _path: String,
- ) -> Option<String> {
- todo!("abstract: implemented by ChangeReportInterface subclasses")
- }
-}
-
-impl DownloaderInterface for VcsDownloader {
- fn get_installation_source(&self) -> String {
- VcsDownloader::get_installation_source(self)
- }
-
- fn download(
- &self,
- _package: &dyn PackageInterface,
- _path: &str,
- _prev_package: Option<&dyn PackageInterface>,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): download mutates state; trait method takes &self
- todo!("download requires &mut self")
- }
-
- fn prepare(
- &self,
- _type: &str,
- _package: &dyn PackageInterface,
- _path: &str,
- _prev_package: Option<&dyn PackageInterface>,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): prepare mutates state; trait method takes &self
- todo!("prepare requires &mut self")
- }
-
- fn install(
- &self,
- _package: &dyn PackageInterface,
- _path: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): install mutates state; trait method takes &self
- todo!("install requires &mut self")
- }
-
- fn update(
- &self,
- _initial: &dyn PackageInterface,
- _target: &dyn PackageInterface,
- _path: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): update mutates state; trait method takes &self
- todo!("update requires &mut self")
- }
-
- fn remove(
- &self,
- _package: &dyn PackageInterface,
- _path: &str,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): remove mutates state; trait method takes &self
- todo!("remove requires &mut self")
- }
-
- fn cleanup(
- &self,
- _type: &str,
- _package: &dyn PackageInterface,
- _path: &str,
- _prev_package: Option<&dyn PackageInterface>,
- ) -> Result<Box<dyn PromiseInterface>> {
- // TODO(phase-b): cleanup mutates state; trait method takes &self
- todo!("cleanup requires &mut self")
- }
-}
-
-impl ChangeReportInterface for VcsDownloader {
- fn get_local_changes(&self, package: &dyn PackageInterface, path: String) -> Option<String> {
- VcsDownloader::get_local_changes(self, package, path)
- }
-}
-
-impl VcsCapableDownloaderInterface for VcsDownloader {
- fn get_vcs_reference(&self, package: &dyn PackageInterface, path: String) -> Option<String> {
- VcsDownloader::get_vcs_reference(self, package, &path)
- }
}
diff --git a/crates/shirabe/src/io/base_io.rs b/crates/shirabe/src/io/base_io.rs
index 58d1e40..ddccd98 100644
--- a/crates/shirabe/src/io/base_io.rs
+++ b/crates/shirabe/src/io/base_io.rs
@@ -12,26 +12,27 @@ use shirabe_php_shim::{
UnexpectedValueException, array_merge, in_array, json_encode_ex,
};
-#[derive(Debug)]
-pub struct BaseIO {
- pub(crate) authentications: IndexMap<String, IndexMap<String, Option<String>>>,
-}
+// TODO(phase-b): default implementations in a subtrait cannot override supertrait methods in Rust;
+// write/write_error etc. from IOInterface are called through the supertrait and must be provided
+// by concrete types implementing both BaseIO and IOInterface.
+pub trait BaseIO: IOInterface {
+ fn authentications(&self) -> &IndexMap<String, IndexMap<String, Option<String>>>;
+ fn authentications_mut(&mut self) -> &mut IndexMap<String, IndexMap<String, Option<String>>>;
-impl BaseIO {
- pub fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>> {
- self.authentications.clone()
+ fn get_authentications(&self) -> IndexMap<String, IndexMap<String, Option<String>>> {
+ self.authentications().clone()
}
- pub fn reset_authentications(&mut self) {
- self.authentications = IndexMap::new();
+ fn reset_authentications(&mut self) {
+ *self.authentications_mut() = IndexMap::new();
}
- pub fn has_authentication(&self, repository_name: &str) -> bool {
- self.authentications.contains_key(repository_name)
+ fn has_authentication(&self, repository_name: &str) -> bool {
+ self.authentications().contains_key(repository_name)
}
- pub fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>> {
- if let Some(auth) = self.authentications.get(repository_name) {
+ fn get_authentication(&self, repository_name: &str) -> IndexMap<String, Option<String>> {
+ if let Some(auth) = self.authentications().get(repository_name) {
return auth.clone();
}
let mut result = IndexMap::new();
@@ -40,7 +41,7 @@ impl BaseIO {
result
}
- pub fn set_authentication(
+ fn set_authentication(
&mut self,
repository_name: String,
username: String,
@@ -49,18 +50,18 @@ impl BaseIO {
let mut auth = IndexMap::new();
auth.insert("username".to_string(), Some(username));
auth.insert("password".to_string(), password);
- self.authentications.insert(repository_name, auth);
+ self.authentications_mut().insert(repository_name, auth);
}
- pub fn write_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
+ fn write_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
self.write(messages, newline, verbosity);
}
- pub fn write_error_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
+ fn write_error_raw(&self, messages: PhpMixed, newline: bool, verbosity: i64) {
self.write_error(messages, newline, verbosity);
}
- pub(crate) fn check_and_set_authentication(
+ fn check_and_set_authentication(
&mut self,
repository_name: String,
username: String,
@@ -85,7 +86,7 @@ impl BaseIO {
self.set_authentication(repository_name, username, password);
}
- pub fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()> {
+ fn load_configuration(&mut self, config: &mut Config) -> anyhow::Result<()> {
let bitbucket_oauth = config.get("bitbucket-oauth");
let github_oauth = config.get("github-oauth");
let gitlab_oauth = config.get("gitlab-oauth");
@@ -383,7 +384,7 @@ impl BaseIO {
Ok(())
}
- pub fn emergency(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn emergency(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::EMERGENCY.to_string()),
message,
@@ -391,7 +392,7 @@ impl BaseIO {
);
}
- pub fn alert(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn alert(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::ALERT.to_string()),
message,
@@ -399,7 +400,7 @@ impl BaseIO {
);
}
- pub fn critical(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn critical(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::CRITICAL.to_string()),
message,
@@ -407,7 +408,7 @@ impl BaseIO {
);
}
- pub fn error(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn error(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::ERROR.to_string()),
message,
@@ -415,7 +416,7 @@ impl BaseIO {
);
}
- pub fn warning(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn warning(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::WARNING.to_string()),
message,
@@ -423,7 +424,7 @@ impl BaseIO {
);
}
- pub fn notice(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn notice(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::NOTICE.to_string()),
message,
@@ -431,7 +432,7 @@ impl BaseIO {
);
}
- pub fn info(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn info(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::INFO.to_string()),
message,
@@ -439,7 +440,7 @@ impl BaseIO {
);
}
- pub fn debug(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
+ fn debug(&mut self, message: PhpMixed, context: IndexMap<String, Box<PhpMixed>>) {
self.log(
PhpMixed::String(LogLevel::DEBUG.to_string()),
message,
@@ -447,7 +448,7 @@ impl BaseIO {
);
}
- pub fn log(
+ fn log(
&mut self,
level: PhpMixed,
message: PhpMixed,
diff --git a/crates/shirabe/src/package/archiver/base_exclude_filter.rs b/crates/shirabe/src/package/archiver/base_exclude_filter.rs
index f20af20..6522d79 100644
--- a/crates/shirabe/src/package/archiver/base_exclude_filter.rs
+++ b/crates/shirabe/src/package/archiver/base_exclude_filter.rs
@@ -3,22 +3,16 @@
use shirabe_external_packages::composer::pcre::preg::Preg;
use shirabe_external_packages::symfony::component::finder::glob::Glob;
-#[derive(Debug)]
-pub struct BaseExcludeFilter {
- pub(crate) source_path: String,
- pub(crate) exclude_patterns: Vec<(String, bool, bool)>,
-}
-
-impl BaseExcludeFilter {
- pub fn new(source_path: String) -> Self {
- Self {
- source_path,
- exclude_patterns: vec![],
- }
- }
+pub trait BaseExcludeFilter {
+ fn source_path(&self) -> &str;
+ fn exclude_patterns(&self) -> &[(String, bool, bool)];
+ fn exclude_patterns_mut(&mut self) -> &mut Vec<(String, bool, bool)>;
- pub fn filter(&self, relative_path: &str, mut exclude: bool) -> bool {
- for (pattern, negate, strip_leading_slash) in &self.exclude_patterns {
+ /// Checks the given path against all exclude patterns in this filter
+ ///
+ /// Negated patterns overwrite exclude decisions of previous filters.
+ fn filter(&self, relative_path: &str, mut exclude: bool) -> bool {
+ for (pattern, negate, strip_leading_slash) in self.exclude_patterns() {
let path = if *strip_leading_slash {
&relative_path[1..]
} else {
@@ -36,30 +30,33 @@ impl BaseExcludeFilter {
exclude
}
- pub fn parse_lines<F>(&self, lines: Vec<String>, line_parser: F) -> Vec<(String, bool, bool)>
+ /// Processes a file containing exclude rules of different formats per line
+ fn parse_lines<F>(&self, lines: Vec<String>, line_parser: F) -> Vec<(String, bool, bool)>
where
F: Fn(&str) -> Option<(String, bool, bool)>,
{
lines
.into_iter()
.filter_map(|line| {
- let line = line.trim();
+ let line = line.trim().to_string();
if line.is_empty() || line.starts_with('#') {
return None;
}
- line_parser(line)
+ line_parser(&line)
})
.collect()
}
- pub fn generate_patterns(&self, rules: Vec<String>) -> Vec<(String, bool, bool)> {
+ /// Generates a set of exclude patterns for filter() from gitignore rules
+ fn generate_patterns(&self, rules: Vec<String>) -> Vec<(String, bool, bool)> {
rules
.into_iter()
.map(|rule| self.generate_pattern(&rule))
.collect()
}
- pub fn generate_pattern(&self, rule: &str) -> (String, bool, bool) {
+ /// Generates an exclude pattern for filter() from a gitignore rule
+ fn generate_pattern(&self, rule: &str) -> (String, bool, bool) {
let mut negate = false;
let mut pattern = String::new();
@@ -82,6 +79,10 @@ impl BaseExcludeFilter {
let glob_regex = Glob::to_regex(rule);
let rule_regex = &glob_regex[2..glob_regex.len() - 2];
- (format!("{}{}(?=$|/)", pattern, rule_regex), negate, false)
+ (
+ format!("{{{}{}(?=$|/)}}", pattern, rule_regex),
+ negate,
+ false,
+ )
}
}
diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs
index 4cbd11c..eb6cc90 100644
--- a/crates/shirabe/src/package/base_package.rs
+++ b/crates/shirabe/src/package/base_package.rs
@@ -58,55 +58,43 @@ pub static SUPPORTED_LINK_TYPES: LazyLock<IndexMap<&'static str, SupportedLinkTy
pub static STABILITIES: LazyLock<IndexMap<&'static str, i64>> = LazyLock::new(|| {
let mut m = IndexMap::new();
- m.insert("stable", BasePackage::STABILITY_STABLE);
- m.insert("RC", BasePackage::STABILITY_RC);
- m.insert("beta", BasePackage::STABILITY_BETA);
- m.insert("alpha", BasePackage::STABILITY_ALPHA);
- m.insert("dev", BasePackage::STABILITY_DEV);
+ m.insert("stable", 0i64);
+ m.insert("RC", 5i64);
+ m.insert("beta", 10i64);
+ m.insert("alpha", 15i64);
+ m.insert("dev", 20i64);
m
});
-#[derive(Debug)]
-pub struct BasePackage {
- pub id: i64,
- pub(crate) name: String,
- pub(crate) pretty_name: String,
- pub(crate) repository: Option<Box<dyn RepositoryInterface>>,
-}
-
-impl std::fmt::Display for BasePackage {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.get_unique_name())
- }
-}
+pub trait BasePackage: PackageInterface + std::fmt::Display {
+ const STABILITY_STABLE: i64 = 0;
+ const STABILITY_RC: i64 = 5;
+ const STABILITY_BETA: i64 = 10;
+ const STABILITY_ALPHA: i64 = 15;
+ const STABILITY_DEV: i64 = 20;
-impl BasePackage {
- pub const STABILITY_STABLE: i64 = 0;
- pub const STABILITY_RC: i64 = 5;
- pub const STABILITY_BETA: i64 = 10;
- pub const STABILITY_ALPHA: i64 = 15;
- pub const STABILITY_DEV: i64 = 20;
+ fn id(&self) -> i64;
+ fn id_mut(&mut self) -> &mut i64;
+ fn name(&self) -> &str;
+ fn name_mut(&mut self) -> &mut String;
+ fn pretty_name(&self) -> &str;
+ fn pretty_name_mut(&mut self) -> &mut String;
+ fn repository_opt(&self) -> Option<&dyn RepositoryInterface>;
+ fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>);
+ fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>>;
- pub fn new(name: String) -> Self {
- let pretty_name = name.clone();
- let name = name.to_lowercase();
- Self {
- id: -1,
- name,
- pretty_name,
- repository: None,
- }
- }
+ fn as_any(&self) -> &dyn std::any::Any;
+ fn clone_box(&self) -> Box<dyn BasePackage>;
- pub fn get_name(&self) -> &str {
- &self.name
+ fn get_name(&self) -> &str {
+ self.name()
}
- pub fn get_pretty_name(&self) -> &str {
- &self.pretty_name
+ fn get_pretty_name(&self) -> &str {
+ self.pretty_name()
}
- pub fn get_names(&self, provides: bool) -> Vec<String> {
+ fn get_names(&self, provides: bool) -> Vec<String> {
let mut names: IndexMap<String, bool> = IndexMap::new();
names.insert(self.get_name().to_string(), true);
@@ -123,19 +111,16 @@ impl BasePackage {
names.into_keys().collect()
}
- pub fn set_id(&mut self, id: i64) {
- self.id = id;
+ fn set_id(&mut self, id: i64) {
+ *self.id_mut() = id;
}
- pub fn get_id(&self) -> i64 {
- self.id
+ fn get_id(&self) -> i64 {
+ self.id()
}
- pub fn set_repository(
- &mut self,
- repository: Box<dyn RepositoryInterface>,
- ) -> anyhow::Result<()> {
- if let Some(ref existing) = self.repository {
+ fn set_repository(&mut self, repository: Box<dyn RepositoryInterface>) -> anyhow::Result<()> {
+ if let Some(existing) = self.repository_opt() {
// TODO(phase-b): proper reference identity check before raising error
return Err(anyhow::anyhow!(LogicException {
message: format!(
@@ -147,40 +132,35 @@ impl BasePackage {
code: 0,
}));
}
- self.repository = Some(repository);
+ self.set_repository_box(repository);
Ok(())
}
- pub fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
- self.repository.as_deref()
+ fn get_repository(&self) -> Option<&dyn RepositoryInterface> {
+ self.repository_opt()
}
- pub fn is_platform(&self) -> bool {
- self.repository
- .as_ref()
+ fn is_platform(&self) -> bool {
+ self.repository_opt()
.and_then(|r| r.as_any().downcast_ref::<PlatformRepository>())
.is_some()
}
- pub fn get_unique_name(&self) -> String {
+ fn get_unique_name(&self) -> String {
format!("{}-{}", self.get_name(), self.get_version())
}
- pub fn equals(&self, _package: &dyn PackageInterface) -> bool {
+ fn equals(&self, _package: &dyn PackageInterface) -> bool {
// TODO(phase-b): implement via reference identity (requires Rc/Arc)
// PHP uses === which is reference equality; unwraps AliasPackage on both sides
todo!("equals requires reference identity which needs Rc/Arc")
}
- pub fn get_pretty_string(&self) -> String {
+ fn get_pretty_string(&self) -> String {
format!("{} {}", self.get_pretty_name(), self.get_pretty_version())
}
- pub fn get_full_pretty_version(
- &self,
- truncate: bool,
- display_mode: i64,
- ) -> anyhow::Result<String> {
+ fn get_full_pretty_version(&self, truncate: bool, display_mode: i64) -> anyhow::Result<String> {
const DISPLAY_SOURCE_REF_IF_DEV: i64 = PackageInterface::DISPLAY_SOURCE_REF_IF_DEV;
const DISPLAY_SOURCE_REF: i64 = PackageInterface::DISPLAY_SOURCE_REF;
const DISPLAY_DIST_REF: i64 = PackageInterface::DISPLAY_DIST_REF;
@@ -224,75 +204,33 @@ impl BasePackage {
Ok(format!("{} {}", self.get_pretty_version(), reference))
}
- pub fn get_stability_priority(&self) -> i64 {
+ fn get_stability_priority(&self) -> i64 {
*STABILITIES
.get(self.get_stability())
.unwrap_or(&Self::STABILITY_STABLE)
}
- pub fn php_clone(&mut self) {
- self.repository = None;
- self.id = -1;
+ fn php_clone(&mut self) {
+ self.take_repository();
+ *self.id_mut() = -1;
}
- pub fn package_name_to_regexp(allow_pattern: &str, wrap: &str) -> String {
+ fn package_name_to_regexp(allow_pattern: &str, wrap: &str) -> String
+ where
+ Self: Sized,
+ {
let cleaned = preg_quote(allow_pattern, None).replace("\\*", ".*");
wrap.replace("%s", &cleaned)
}
- pub fn package_names_to_regexp(package_names: &[String], wrap: &str) -> String {
+ fn package_names_to_regexp(package_names: &[String], wrap: &str) -> String
+ where
+ Self: Sized,
+ {
let patterns: Vec<String> = package_names
.iter()
.map(|name| Self::package_name_to_regexp(name, "%s"))
.collect();
wrap.replace("%s", &patterns.join("|"))
}
-
- // Methods below are defined in Package/CompletePackage subclasses in PHP.
- // Called via $this polymorphism from BasePackage methods.
- // TODO(phase-b): resolve via trait dispatch or field access in concrete types.
-
- pub fn get_provides(&self) -> IndexMap<String, Link> {
- todo!("defined in Package subclass")
- }
-
- pub fn get_replaces(&self) -> IndexMap<String, Link> {
- todo!("defined in Package subclass")
- }
-
- pub fn get_version(&self) -> &str {
- todo!("defined in Package subclass")
- }
-
- pub fn get_pretty_version(&self) -> &str {
- todo!("defined in Package subclass")
- }
-
- pub fn is_dev(&self) -> bool {
- todo!("defined in Package subclass")
- }
-
- pub fn get_source_type(&self) -> Option<&str> {
- todo!("defined in Package subclass")
- }
-
- pub fn get_source_reference(&self) -> Option<&str> {
- todo!("defined in Package subclass")
- }
-
- pub fn get_dist_reference(&self) -> Option<&str> {
- todo!("defined in Package subclass")
- }
-
- pub fn get_stability(&self) -> &str {
- todo!("defined in Package subclass")
- }
-
- pub fn as_any(&self) -> &dyn std::any::Any {
- self
- }
-
- pub fn clone_box(&self) -> Box<BasePackage> {
- todo!("clone_box needs resolution in Phase B")
- }
}
diff --git a/crates/shirabe/src/repository/vcs/vcs_driver.rs b/crates/shirabe/src/repository/vcs/vcs_driver.rs
index a16930f..3195822 100644
--- a/crates/shirabe/src/repository/vcs/vcs_driver.rs
+++ b/crates/shirabe/src/repository/vcs/vcs_driver.rs
@@ -11,72 +11,48 @@ use crate::config::Config;
use crate::downloader::transport_exception::TransportException;
use crate::io::io_interface::IOInterface;
use crate::json::json_file::JsonFile;
+use crate::repository::vcs::vcs_driver_interface::VcsDriverInterface;
use crate::util::filesystem::Filesystem;
use crate::util::http::response::Response;
use crate::util::http_downloader::HttpDownloader;
use crate::util::process_executor::ProcessExecutor;
-#[derive(Debug)]
-pub struct VcsDriver {
- pub(crate) url: String,
- pub(crate) origin_url: String,
- pub(crate) repo_config: IndexMap<String, PhpMixed>,
- pub(crate) io: Box<dyn IOInterface>,
- pub(crate) config: Config,
- pub(crate) process: ProcessExecutor,
- pub(crate) http_downloader: HttpDownloader,
- pub(crate) info_cache: IndexMap<String, Option<IndexMap<String, PhpMixed>>>,
- pub(crate) cache: Option<Cache>,
-}
-
-impl VcsDriver {
- pub fn new(
- mut repo_config: IndexMap<String, PhpMixed>,
- io: Box<dyn IOInterface>,
- config: Config,
- http_downloader: HttpDownloader,
- process: ProcessExecutor,
- ) -> Self {
- if let Some(PhpMixed::String(url)) = repo_config.get("url").cloned() {
- if Filesystem::is_local_path(&url) {
- let platform_path = Filesystem::get_platform_path(&url);
- repo_config.insert("url".to_string(), PhpMixed::String(platform_path));
- }
- }
-
- let url = repo_config
- .get("url")
- .and_then(|v| v.as_string())
- .unwrap_or("")
- .to_string();
-
- Self {
- origin_url: url.clone(),
- url,
- repo_config,
- io,
- config,
- http_downloader,
- process,
- info_cache: IndexMap::new(),
- cache: None,
- }
- }
+// TODO(phase-b): the constructor is `final` in PHP; concrete implementations must replicate the
+// initialization logic (local-path normalization etc.) from the original new() body.
+pub trait VcsDriver: VcsDriverInterface {
+ fn url(&self) -> &str;
+ fn url_mut(&mut self) -> &mut String;
+ fn origin_url(&self) -> &str;
+ fn origin_url_mut(&mut self) -> &mut String;
+ fn repo_config(&self) -> &IndexMap<String, PhpMixed>;
+ fn repo_config_mut(&mut self) -> &mut IndexMap<String, PhpMixed>;
+ fn io(&self) -> &dyn IOInterface;
+ fn io_mut(&mut self) -> &mut dyn IOInterface;
+ fn config(&self) -> &Config;
+ fn config_mut(&mut self) -> &mut Config;
+ fn process(&self) -> &ProcessExecutor;
+ fn process_mut(&mut self) -> &mut ProcessExecutor;
+ fn http_downloader(&self) -> &HttpDownloader;
+ fn http_downloader_mut(&mut self) -> &mut HttpDownloader;
+ fn info_cache(&self) -> &IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
+ fn info_cache_mut(&mut self) -> &mut IndexMap<String, Option<IndexMap<String, PhpMixed>>>;
+ fn cache(&self) -> Option<&Cache>;
+ fn cache_mut(&mut self) -> Option<&mut Cache>;
- pub(crate) fn should_cache(&self, identifier: &str) -> bool {
- self.cache.is_some() && Preg::is_match("{^[a-f0-9]{40}$}iD", identifier).unwrap_or(false)
+ fn should_cache(&self, identifier: &str) -> bool {
+ self.cache().is_some() && Preg::is_match("{^[a-f0-9]{40}$}iD", identifier).unwrap_or(false)
}
- pub fn get_composer_information(
+ fn get_composer_information(
&mut self,
identifier: &str,
) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> {
- if !self.info_cache.contains_key(identifier) {
+ if !self.info_cache().contains_key(identifier) {
if self.should_cache(identifier) {
- if let Some(res) = self.cache.as_ref().and_then(|c| c.read(identifier)) {
+ if let Some(res) = self.cache().and_then(|c| c.read(identifier)) {
let parsed = JsonFile::parse_json(&res, None)?;
- self.info_cache.insert(identifier.to_string(), parsed);
- return Ok(self.info_cache.get(identifier).and_then(|v| v.clone()));
+ self.info_cache_mut().insert(identifier.to_string(), parsed);
+ return Ok(self.info_cache().get(identifier).and_then(|v| v.clone()));
}
}
@@ -88,17 +64,18 @@ impl VcsDriver {
composer_map,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
);
- self.cache.as_ref().map(|c| c.write(identifier, &encoded));
+ self.cache().map(|c| c.write(identifier, &encoded));
}
}
- self.info_cache.insert(identifier.to_string(), composer);
+ self.info_cache_mut()
+ .insert(identifier.to_string(), composer);
}
- Ok(self.info_cache.get(identifier).and_then(|v| v.clone()))
+ Ok(self.info_cache().get(identifier).and_then(|v| v.clone()))
}
- pub(crate) fn get_base_composer_information(
+ fn get_base_composer_information(
&mut self,
identifier: &str,
) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>> {
@@ -137,44 +114,28 @@ impl VcsDriver {
Ok(Some(composer))
}
- pub fn has_composer_file(&mut self, identifier: &str) -> bool {
+ fn has_composer_file(&mut self, identifier: &str) -> bool {
match self.get_composer_information(identifier) {
Ok(Some(_)) => true,
_ => false,
}
}
- pub(crate) fn get_scheme(&self) -> &str {
+ fn get_scheme(&self) -> &str {
if extension_loaded("openssl") {
return "https";
}
"http"
}
- pub(crate) fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> {
+ fn get_contents(&self, url: &str) -> anyhow::Result<Response, TransportException> {
let options = self
- .repo_config
+ .repo_config()
.get("options")
.cloned()
.unwrap_or(PhpMixed::Array(IndexMap::new()));
- self.http_downloader.get(url, &options)
+ self.http_downloader().get(url, &options)
}
- pub fn cleanup(&self) {}
-
- // abstract methods to be implemented by subclasses (via VcsDriverInterface trait)
- pub(crate) fn get_file_content(
- &self,
- file: &str,
- identifier: &str,
- ) -> anyhow::Result<Option<String>> {
- todo!()
- }
-
- pub(crate) fn get_change_date(
- &self,
- identifier: &str,
- ) -> anyhow::Result<Option<chrono::DateTime<chrono::Utc>>> {
- todo!()
- }
+ fn cleanup(&self) {}
}