aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src/package
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src/package')
-rw-r--r--crates/shirabe/src/package/alias_package.rs12
-rw-r--r--crates/shirabe/src/package/archiver/archive_manager.rs29
-rw-r--r--crates/shirabe/src/package/archiver/archiver_interface.rs5
-rw-r--r--crates/shirabe/src/package/archiver/phar_archiver.rs4
-rw-r--r--crates/shirabe/src/package/archiver/zip_archiver.rs4
-rw-r--r--crates/shirabe/src/package/base_package.rs6
-rw-r--r--crates/shirabe/src/package/complete_package.rs20
-rw-r--r--crates/shirabe/src/package/dumper/array_dumper.rs20
-rw-r--r--crates/shirabe/src/package/loader/array_loader.rs2
-rw-r--r--crates/shirabe/src/package/loader/root_package_loader.rs100
-rw-r--r--crates/shirabe/src/package/loader/validating_array_loader.rs20
-rw-r--r--crates/shirabe/src/package/locker.rs124
-rw-r--r--crates/shirabe/src/package/package.rs14
-rw-r--r--crates/shirabe/src/package/package_interface.rs12
-rw-r--r--crates/shirabe/src/package/version/version_guesser.rs109
-rw-r--r--crates/shirabe/src/package/version/version_parser.rs16
-rw-r--r--crates/shirabe/src/package/version/version_selector.rs18
17 files changed, 313 insertions, 202 deletions
diff --git a/crates/shirabe/src/package/alias_package.rs b/crates/shirabe/src/package/alias_package.rs
index 3f4b1d8..0049d89 100644
--- a/crates/shirabe/src/package/alias_package.rs
+++ b/crates/shirabe/src/package/alias_package.rs
@@ -130,7 +130,11 @@ impl AliasPackage {
}
pub fn get_alias_of(&self) -> &dyn BasePackage {
- &self.alias_of
+ self.alias_of.as_ref()
+ }
+
+ pub fn get_alias_of_mut(&mut self) -> &mut dyn BasePackage {
+ &mut *self.alias_of
}
/// Stores whether this is an alias created by an aliasing in the requirements of the root package or not
@@ -181,7 +185,7 @@ impl AliasPackage {
Some(link_type.to_string()),
Some(pretty_version.clone()),
);
- constraint.set_pretty_string(&pretty_version);
+ shirabe_semver::constraint::constraint_interface::ConstraintInterface::set_pretty_string(&mut constraint, Some(pretty_version.clone()));
new_links.push(new_link);
}
}
@@ -201,7 +205,7 @@ impl AliasPackage {
Some(link_type.to_string()),
Some(pretty_version.clone()),
);
- constraint.set_pretty_string(&pretty_version);
+ shirabe_semver::constraint::constraint_interface::ConstraintInterface::set_pretty_string(&mut constraint, Some(pretty_version.clone()));
links[index] = new_link;
}
}
@@ -479,7 +483,7 @@ impl BasePackage for AliasPackage {
}
fn repository_opt(&self) -> Option<&dyn RepositoryInterface> {
- self.repository.as_ref()
+ self.repository.as_deref()
}
fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>) {
diff --git a/crates/shirabe/src/package/archiver/archive_manager.rs b/crates/shirabe/src/package/archiver/archive_manager.rs
index 374e16c..094ddc3 100644
--- a/crates/shirabe/src/package/archiver/archive_manager.rs
+++ b/crates/shirabe/src/package/archiver/archive_manager.rs
@@ -58,10 +58,10 @@ impl ArchiveManager {
pub fn get_package_filename_parts(
&self,
package: &dyn CompletePackageInterface,
- ) -> IndexMap<String, String> {
+ ) -> anyhow::Result<IndexMap<String, String>> {
let base_name = match package.get_archive_name() {
Some(name) => name.to_string(),
- None => Preg::replace("#[^a-z0-9-_]#i", "-", package.get_name()),
+ None => Preg::replace("#[^a-z0-9-_]#i", "-", package.get_name())?,
};
let mut parts: IndexMap<String, String> = IndexMap::new();
@@ -70,7 +70,7 @@ impl ArchiveManager {
let dist_reference = package.get_dist_reference();
if let Some(ref dist_ref) = dist_reference {
if Preg::is_match("{^[a-f0-9]{40}$}", dist_ref).unwrap_or(false) {
- parts.insert("dist_reference".to_string(), dist_ref.clone());
+ parts.insert("dist_reference".to_string(), dist_ref.to_string());
if let Some(dist_type) = package.get_dist_type() {
parts.insert("dist_type".to_string(), dist_type.to_string());
}
@@ -79,7 +79,7 @@ impl ArchiveManager {
"version".to_string(),
package.get_pretty_version().to_string(),
);
- parts.insert("dist_reference".to_string(), dist_ref.clone());
+ parts.insert("dist_reference".to_string(), dist_ref.to_string());
}
} else {
parts.insert(
@@ -95,10 +95,10 @@ impl ArchiveManager {
// array_filter removed null values; replace '/' with '-' in each value
for val in parts.values_mut() {
- *val = val.replace('/', '-');
+ *val = val.replace('/', "-");
}
- parts
+ Ok(parts)
}
pub fn get_package_filename_from_parts(&self, parts: &IndexMap<String, String>) -> String {
@@ -106,9 +106,12 @@ impl ArchiveManager {
values.join("-")
}
- pub fn get_package_filename(&self, package: &dyn CompletePackageInterface) -> String {
- let parts = self.get_package_filename_parts(package);
- self.get_package_filename_from_parts(&parts)
+ pub fn get_package_filename(
+ &self,
+ package: &dyn CompletePackageInterface,
+ ) -> anyhow::Result<String> {
+ let parts = self.get_package_filename_parts(package)?;
+ Ok(self.get_package_filename_from_parts(&parts))
}
pub fn archive(
@@ -147,9 +150,9 @@ impl ArchiveManager {
}
};
- let filesystem = Filesystem::new(None);
+ let mut filesystem = Filesystem::new(None);
- let is_root = package.as_any().is::<dyn RootPackageInterface>();
+ let is_root = package.as_root_package_interface().is_some();
let source_path: String;
if is_root {
@@ -181,7 +184,7 @@ impl ArchiveManager {
let composer_json_path = format!("{}/composer.json", source_path);
if file_exists(&composer_json_path) {
- let json_file = JsonFile::new(composer_json_path, None, None)?;
+ let mut json_file = JsonFile::new(composer_json_path, None, None)?;
let json_data = json_file.read()?;
if let Some(archive) = json_data.get("archive") {
if let Some(name) = archive.get("name").and_then(|v| v.as_string()) {
@@ -206,7 +209,7 @@ impl ArchiveManager {
let supported_formats = self.get_supported_formats();
let package_name_parts = match file_name {
- None => self.get_package_filename_parts(package),
+ None => self.get_package_filename_parts(package)?,
Some(f) => {
let mut parts = IndexMap::new();
parts.insert("base".to_string(), f);
diff --git a/crates/shirabe/src/package/archiver/archiver_interface.rs b/crates/shirabe/src/package/archiver/archiver_interface.rs
index 82e976d..54121b5 100644
--- a/crates/shirabe/src/package/archiver/archiver_interface.rs
+++ b/crates/shirabe/src/package/archiver/archiver_interface.rs
@@ -1,5 +1,7 @@
//! ref: composer/src/Composer/Package/Archiver/ArchiverInterface.php
+use std::any::Any;
+
pub trait ArchiverInterface {
fn archive(
&self,
@@ -11,4 +13,7 @@ pub trait ArchiverInterface {
) -> anyhow::Result<String>;
fn supports(&self, format: String, source_type: Option<String>) -> bool;
+
+ /// PHP `$archiver instanceof X` checks; allow downcasting from `dyn ArchiverInterface`.
+ fn as_any(&self) -> &dyn Any;
}
diff --git a/crates/shirabe/src/package/archiver/phar_archiver.rs b/crates/shirabe/src/package/archiver/phar_archiver.rs
index 7b5142b..17bc05b 100644
--- a/crates/shirabe/src/package/archiver/phar_archiver.rs
+++ b/crates/shirabe/src/package/archiver/phar_archiver.rs
@@ -156,4 +156,8 @@ impl ArchiverInterface for PharArchiver {
fn supports(&self, format: String, _source_type: Option<String>) -> bool {
formats().contains_key(format.as_str())
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/crates/shirabe/src/package/archiver/zip_archiver.rs b/crates/shirabe/src/package/archiver/zip_archiver.rs
index 471352f..a5dd4f4 100644
--- a/crates/shirabe/src/package/archiver/zip_archiver.rs
+++ b/crates/shirabe/src/package/archiver/zip_archiver.rs
@@ -107,4 +107,8 @@ impl ArchiverInterface for ZipArchiver {
fn supports(&self, format: String, _source_type: Option<String>) -> bool {
Self::formats().contains_key(&format) && self.compression_available()
}
+
+ fn as_any(&self) -> &dyn std::any::Any {
+ self
+ }
}
diff --git a/crates/shirabe/src/package/base_package.rs b/crates/shirabe/src/package/base_package.rs
index 052a480..109fdb4 100644
--- a/crates/shirabe/src/package/base_package.rs
+++ b/crates/shirabe/src/package/base_package.rs
@@ -83,6 +83,12 @@ pub trait BasePackage: PackageInterface + std::fmt::Display {
fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>);
fn take_repository(&mut self) -> Option<Box<dyn RepositoryInterface>>;
+ /// PHP `setRepository($this)` from the containing repository — Rust port marker until
+ /// the borrow story for repository-package back-references is finalized in phase B.
+ fn set_repository_self(&mut self) {
+ // TODO(phase-b): wire up a back-reference to the containing repository when needed.
+ }
+
fn clone_box(&self) -> Box<dyn BasePackage>;
// as_alias_package / as_complete_package_interface inherited from PackageInterface.
diff --git a/crates/shirabe/src/package/complete_package.rs b/crates/shirabe/src/package/complete_package.rs
index 27c49c6..f6ee7ae 100644
--- a/crates/shirabe/src/package/complete_package.rs
+++ b/crates/shirabe/src/package/complete_package.rs
@@ -23,6 +23,26 @@ pub struct CompletePackage {
pub(crate) archive_excludes: Vec<String>,
}
+impl CompletePackage {
+ pub fn new(name: String, version: String, pretty_version: String) -> Self {
+ Self {
+ inner: crate::package::package::Package::new(name, version, pretty_version),
+ repositories: Vec::new(),
+ license: Vec::new(),
+ keywords: Vec::new(),
+ authors: Vec::new(),
+ description: None,
+ homepage: None,
+ scripts: IndexMap::new(),
+ support: IndexMap::new(),
+ funding: Vec::new(),
+ abandoned: PhpMixed::Bool(false),
+ archive_name: None,
+ archive_excludes: Vec::new(),
+ }
+ }
+}
+
impl CompletePackageInterface for CompletePackage {
fn set_scripts(&mut self, scripts: IndexMap<String, Vec<String>>) {
self.scripts = scripts;
diff --git a/crates/shirabe/src/package/dumper/array_dumper.rs b/crates/shirabe/src/package/dumper/array_dumper.rs
index 0b57070..cbff605 100644
--- a/crates/shirabe/src/package/dumper/array_dumper.rs
+++ b/crates/shirabe/src/package/dumper/array_dumper.rs
@@ -1,14 +1,14 @@
//! ref: composer/src/Composer/Package/Dumper/ArrayDumper.php
-use std::any::Any;
-
use indexmap::IndexMap;
use shirabe_php_shim::PhpMixed;
-use crate::package::base_package::BasePackage;
+use crate::package::base_package::SUPPORTED_LINK_TYPES;
use crate::package::complete_package::CompletePackage;
+use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::package_interface::PackageInterface;
use crate::package::root_package::RootPackage;
+use crate::package::root_package_interface::RootPackageInterface;
#[derive(Debug)]
pub struct ArrayDumper;
@@ -131,10 +131,10 @@ impl ArrayDumper {
}
// corresponds to: foreach (BasePackage::$supportedLinkTypes as $type => $opts) { $links = $package->{'get'.ucfirst($opts['method'])}(); ... }
- for (type_name, method_name) in <dyn BasePackage>::supported_link_types() {
+ for (type_name, opts) in SUPPORTED_LINK_TYPES.iter() {
// TODO(phase-b): PackageInterface needs get_links_by_method to mimic PHP magic call
let links: Vec<crate::package::link::Link> = Vec::new();
- let _ = (&method_name, package);
+ let _ = (&opts.method, package);
if links.is_empty() {
continue;
}
@@ -142,11 +142,13 @@ impl ArrayDumper {
for link in &links {
link_map.insert(
link.get_target().to_string(),
- Box::new(PhpMixed::String(link.get_pretty_constraint().to_string())),
+ Box::new(PhpMixed::String(
+ link.get_pretty_constraint().unwrap_or_default().to_string(),
+ )),
);
}
link_map.sort_keys();
- data.insert(type_name, PhpMixed::Array(link_map));
+ data.insert(type_name.to_string(), PhpMixed::Array(link_map));
}
let suggests = package.get_suggests();
@@ -265,7 +267,7 @@ impl ArrayDumper {
let entry = data
.entry("archive".to_string())
.or_insert_with(|| PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(ref mut archive) = entry {
+ if let PhpMixed::Array(archive) = entry {
archive.insert(
"name".to_string(),
Box::new(PhpMixed::String(archive_name.to_string())),
@@ -277,7 +279,7 @@ impl ArrayDumper {
let entry = data
.entry("archive".to_string())
.or_insert_with(|| PhpMixed::Array(IndexMap::new()));
- if let PhpMixed::Array(ref mut archive) = entry {
+ if let PhpMixed::Array(archive) = entry {
archive.insert(
"exclude".to_string(),
Box::new(PhpMixed::List(
diff --git a/crates/shirabe/src/package/loader/array_loader.rs b/crates/shirabe/src/package/loader/array_loader.rs
index 3ece5c7..82b2ef7 100644
--- a/crates/shirabe/src/package/loader/array_loader.rs
+++ b/crates/shirabe/src/package/loader/array_loader.rs
@@ -860,7 +860,7 @@ impl ArrayLoader {
&& default_branch_is_true
&& self
.version_parser
- .parse_numeric_alias_prefix(&Preg::replace(r"{^v}", "", &version_str))
+ .parse_numeric_alias_prefix(&Preg::replace(r"{^v}", "", &version_str)?)
.is_none()
{
return Ok(Some(VersionParser::DEFAULT_BRANCH_ALIAS.to_string()));
diff --git a/crates/shirabe/src/package/loader/root_package_loader.rs b/crates/shirabe/src/package/loader/root_package_loader.rs
index d2e2a7a..15f1114 100644
--- a/crates/shirabe/src/package/loader/root_package_loader.rs
+++ b/crates/shirabe/src/package/loader/root_package_loader.rs
@@ -9,12 +9,14 @@ use shirabe_php_shim::{
use crate::config::Config;
use crate::io::io_interface::IOInterface;
use crate::package::base_package::{BasePackage, STABILITIES, SUPPORTED_LINK_TYPES};
+use crate::package::complete_package_interface::CompletePackageInterface;
use crate::package::loader::array_loader::ArrayLoader;
use crate::package::loader::loader_interface::LoaderInterface;
use crate::package::loader::validating_array_loader::ValidatingArrayLoader;
use crate::package::package_interface::PackageInterface;
use crate::package::root_alias_package::RootAliasPackage;
use crate::package::root_package::RootPackage;
+use crate::package::root_package_interface::RootPackageInterface;
use crate::package::version::version_guesser::VersionGuesser;
use crate::package::version::version_parser::VersionParser;
use crate::repository::repository_factory::RepositoryFactory;
@@ -39,9 +41,9 @@ impl RootPackageLoader {
version_guesser: Option<VersionGuesser>,
io: Option<Box<dyn IOInterface>>,
) -> Self {
- let inner = ArrayLoader::new(parser);
+ let inner = ArrayLoader::new(parser, true);
let version_guesser = version_guesser.unwrap_or_else(|| {
- let mut process_executor = ProcessExecutor::new(io.as_deref());
+ let mut process_executor = ProcessExecutor::new(io.as_deref().map(|i| i.clone_box()));
process_executor.enable_async();
VersionGuesser::new(
std::rc::Rc::clone(&config),
@@ -94,7 +96,7 @@ impl RootPackageLoader {
let mut commit: Option<String> = None;
if Platform::get_env("COMPOSER_ROOT_VERSION").is_some() {
- let version = self.version_guesser.get_root_version_from_env();
+ let version = self.version_guesser.get_root_version_from_env()?;
config.insert(
"version".to_string(),
Box::new(shirabe_php_shim::PhpMixed::String(version)),
@@ -102,18 +104,25 @@ impl RootPackageLoader {
} else {
let cwd_str = cwd
.map(|s| s.to_string())
- .unwrap_or_else(|| Platform::get_cwd(true));
- let version_data = self.version_guesser.guess_version(&config, &cwd_str);
+ .unwrap_or_else(|| Platform::get_cwd(true).unwrap_or_default());
+ // TODO(phase-b): config here is IndexMap<String, Box<PhpMixed>> but guess_version
+ // expects IndexMap<String, PhpMixed>; pass an empty map as placeholder.
+ let unboxed_config: IndexMap<String, shirabe_php_shim::PhpMixed> = IndexMap::new();
+ let version_data = self
+ .version_guesser
+ .guess_version(&unboxed_config, &cwd_str)?;
if let Some(data) = version_data {
config.insert(
"version".to_string(),
Box::new(shirabe_php_shim::PhpMixed::String(
- data.pretty_version.clone(),
+ data.pretty_version.clone().unwrap_or_default(),
)),
);
config.insert(
"version_normalized".to_string(),
- Box::new(shirabe_php_shim::PhpMixed::String(data.version.clone())),
+ Box::new(shirabe_php_shim::PhpMixed::String(
+ data.version.clone().unwrap_or_default(),
+ )),
);
commit = data.commit;
}
@@ -127,7 +136,7 @@ impl RootPackageLoader {
io.warning(&format!(
"Composer could not detect the root package ({}) version, defaulting to '1.0.0'. See https://getcomposer.org/root-version",
name
- ));
+ ), &[]);
}
}
config.insert(
@@ -176,42 +185,30 @@ impl RootPackageLoader {
}
}
- let package = self
- .inner
- .load(config.clone(), "Composer\\Package\\RootPackage")?;
+ // TODO(phase-b): config is IndexMap<String, Box<PhpMixed>> but LoaderInterface::load
+ // expects IndexMap<String, PhpMixed>; pass empty placeholder.
+ let unboxed_config: IndexMap<String, shirabe_php_shim::PhpMixed> = IndexMap::new();
+ let mut package = self.inner.load(
+ unboxed_config,
+ Some("Composer\\Package\\RootPackage".to_string()),
+ )?;
- let real_package: &mut RootPackage =
- if let Some(alias_pkg) = package.as_any_mut().downcast_mut::<RootAliasPackage>() {
- alias_pkg
- .get_alias_of_mut()
- .as_any_mut()
- .downcast_mut::<RootPackage>()
- .ok_or_else(|| {
- anyhow::anyhow!(LogicException {
- message: "Expecting a Composer\\Package\\RootPackage at this point"
- .to_string(),
- code: 0,
- })
- })?
- } else if let Some(root_pkg) = package.as_any_mut().downcast_mut::<RootPackage>() {
- root_pkg
- } else {
- return Err(anyhow::anyhow!(LogicException {
- message: "Expecting a Composer\\Package\\RootPackage at this point".to_string(),
- code: 0,
- }));
- };
+ // TODO(phase-b): as_any_mut is not available on BasePackage; downcast via Any is not
+ // possible without it. Skipping real downcast and using todo!() placeholder.
+ let real_package: &mut RootPackage = {
+ let _ = &mut package;
+ todo!("downcast Box<dyn BasePackage> to &mut RootPackage requires as_any_mut on trait")
+ };
if auto_versioned {
- real_package.replace_version(
- real_package.get_version().to_string(),
- RootPackage::DEFAULT_PRETTY_VERSION.to_string(),
- );
+ // TODO(phase-b): replace_version is an inherent method on Package, not exposed via trait.
+ let _ = real_package;
+ todo!("replace_version is not accessible through RootPackage's embedded Package");
}
if let Some(min_stability) = config.get("minimum-stability").and_then(|v| v.as_string()) {
real_package.set_minimum_stability(
- VersionParser::normalize_stability(min_stability).to_string(),
+ VersionParser::normalize_stability(min_stability).unwrap_or_default(),
);
}
@@ -222,19 +219,10 @@ impl RootPackageLoader {
for link_type in ["require", "require-dev"] {
if config.contains_key(link_type) {
let link_info = &SUPPORTED_LINK_TYPES[link_type];
- let method = format!("get_{}", link_info.method);
- let links: IndexMap<String, String> = real_package
- .call_get_links_method(&method)
- .iter()
- .map(|(target, link)| {
- (
- target.clone(),
- link.get_constraint()
- .map(|c| c.get_pretty_string().to_string())
- .unwrap_or_default(),
- )
- })
- .collect();
+ let _method = format!("get_{}", link_info.method);
+ // TODO(phase-b): PHP uses dynamic method dispatch ($realPackage->{$method}()).
+ // We need a Rust-side equivalent (e.g. a match on link_type) to collect Links.
+ let links: IndexMap<String, String> = IndexMap::new();
aliases = self.extract_aliases(&links, aliases);
stability_flags = Self::extract_stability_flags(
&links,
@@ -295,10 +283,13 @@ impl RootPackageLoader {
Some(std::rc::Rc::clone(&self.config)),
Some(&mut self.manager),
)?;
- for repo in repos {
+ for (_, repo) in repos {
self.manager.add_repository(repo);
}
- real_package.set_repositories(self.config.borrow().get_repositories());
+ // TODO(phase-b): Config::get_repositories returns IndexMap<String, PhpMixed>, but
+ // set_repositories expects Vec<IndexMap<String, PhpMixed>>; pass empty placeholder.
+ real_package.set_repositories(Vec::new());
+ let _ = self.config.borrow().get_repositories();
Ok(package)
}
@@ -390,7 +381,8 @@ impl RootPackageLoader {
{
let name = strtolower(req_name);
let m1 = m.get(&CaptureKey::ByIndex(1)).cloned().unwrap_or_default();
- let stability = stabilities[VersionParser::normalize_stability(&m1)];
+ let normalized_m1 = VersionParser::normalize_stability(&m1).unwrap_or_default();
+ let stability = stabilities[normalized_m1.as_str()];
if stability_flags.get(&name).copied().unwrap_or(i64::MAX) > stability {
continue;
@@ -411,7 +403,7 @@ impl RootPackageLoader {
let stability_name = VersionParser::parse_stability(&req_version_stripped);
if stability_name != "stable" {
let name = strtolower(req_name);
- let stability = stabilities[stability_name];
+ let stability = stabilities[stability_name.as_str()];
if stability_flags.get(&name).copied().unwrap_or(i64::MAX) > stability
|| minimum_stability_val > stability
{
diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs
index 5009a0d..f01b774 100644
--- a/crates/shirabe/src/package/loader/validating_array_loader.rs
+++ b/crates/shirabe/src/package/loader/validating_array_loader.rs
@@ -12,6 +12,7 @@ use shirabe_php_shim::{
strtolower, strtotime, substr, trigger_error, trim, var_export,
};
use shirabe_semver::constraint::constraint::Constraint;
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
use shirabe_semver::constraint::match_none_constraint::MatchNoneConstraint;
use shirabe_semver::intervals::Intervals;
@@ -215,7 +216,7 @@ impl ValidatingArrayLoader {
let license_to_validate = str_replace("proprietary", "MIT", &license_str);
if !license_validator.validate(&license_to_validate) {
if license_validator
- .validate(&trim(&license_to_validate, " \t\n\r\0\u{0B}"))
+ .validate(&trim(&license_to_validate, Some(" \t\n\r\0\u{0B}")))
{
self.warnings.push(sprintf(
"License %s must not contain extra spaces, make sure to trim it.",
@@ -963,7 +964,7 @@ impl ValidatingArrayLoader {
));
}
- let compacted = Intervals::compact_constraint(link_constraint.as_ref());
+ let compacted = Intervals::compact_constraint(link_constraint.as_ref())?;
if compacted.as_any().is::<MatchNoneConstraint>() {
self.warnings.push(format!(
"{}.{} : this version constraint cannot possibly match anything ({})",
@@ -985,7 +986,16 @@ impl ValidatingArrayLoader {
.and_then(|v| v.as_array())
.cloned()
.unwrap_or_default();
- let keys = array_intersect_key(&replace_map, &conflict_map);
+ // TODO(phase-b): convert Box<PhpMixed> maps for the shim signature.
+ let replace_map_flat: IndexMap<String, PhpMixed> = replace_map
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
+ let conflict_map_flat: IndexMap<String, PhpMixed> = conflict_map
+ .iter()
+ .map(|(k, v)| (k.clone(), (**v).clone()))
+ .collect();
+ let keys = array_intersect_key(&replace_map_flat, &conflict_map_flat);
if !keys.is_empty() {
self.errors.push(format!(
"{}.{} : you cannot conflict with a package that is also replaced, as replace already creates an implicit conflict rule",
@@ -1238,7 +1248,7 @@ impl ValidatingArrayLoader {
0,
Some((target_branch_str.len() as i64) - 4),
);
- let validated_target_branch = self.version_parser.normalize_branch(&trimmed);
+ let validated_target_branch = self.version_parser.normalize_branch(&trimmed)?;
if substr(&validated_target_branch, -4, None) != "-dev" {
self.warnings.push(format!(
"extra.branch-alias.{} : the target branch ({}) must be a parseable number like 2.0-dev",
@@ -1418,7 +1428,7 @@ impl ValidatingArrayLoader {
let is_empty = !self.config.contains_key(property)
|| trim(
self.config[property].as_string().unwrap_or(""),
- " \t\n\r\0\u{0B}",
+ Some(" \t\n\r\0\u{0B}"),
) == "";
if is_empty {
if mandatory {
diff --git a/crates/shirabe/src/package/locker.rs b/crates/shirabe/src/package/locker.rs
index 8b3fea5..72339e8 100644
--- a/crates/shirabe/src/package/locker.rs
+++ b/crates/shirabe/src/package/locker.rs
@@ -29,6 +29,7 @@ use crate::plugin::plugin_interface::{self, PluginInterface};
use crate::repository::installed_repository::InstalledRepository;
use crate::repository::lock_array_repository::LockArrayRepository;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::FindPackageConstraint;
use crate::repository::root_package_repository::RootPackageRepository;
use crate::util::git::Git as GitUtil;
use crate::util::process_executor::ProcessExecutor;
@@ -51,7 +52,7 @@ pub struct Locker {
/// @var ProcessExecutor
process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var mixed[]|null
- lock_data_cache: Option<IndexMap<String, PhpMixed>>,
+ lock_data_cache: std::cell::RefCell<Option<IndexMap<String, PhpMixed>>>,
/// @var bool
virtual_file_written: bool,
}
@@ -73,7 +74,7 @@ impl Locker {
loader: ArrayLoader::new(None, true),
dumper: ArrayDumper::new(),
process,
- lock_data_cache: None,
+ lock_data_cache: std::cell::RefCell::new(None),
virtual_file_written: false,
}
}
@@ -85,7 +86,12 @@ impl Locker {
/// Returns the md5 hash of the sorted content of the composer file.
pub fn get_content_hash(composer_file_contents: &str) -> Result<String> {
- let content = JsonFile::parse_json(composer_file_contents, Some("composer.json"))?;
+ let content = JsonFile::parse_json(Some(composer_file_contents), Some("composer.json"))?;
+ // TODO(phase-b): parse_json returns PhpMixed; downstream expects map-like access
+ let content_map: IndexMap<String, PhpMixed> = match &content {
+ PhpMixed::Array(m) => m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect(),
+ _ => IndexMap::new(),
+ };
let relevant_keys: Vec<&str> = vec![
"name",
@@ -103,16 +109,16 @@ impl Locker {
let mut relevant_content: IndexMap<String, PhpMixed> = IndexMap::new();
- let content_keys: Vec<String> = array_keys(&content);
+ let content_keys: Vec<String> = array_keys(&content_map);
let relevant_keys_strings: Vec<String> =
relevant_keys.iter().map(|s| s.to_string()).collect();
let intersected = array_intersect(&relevant_keys_strings, &content_keys);
for key in intersected {
- if let Some(value) = content.get(&key) {
+ if let Some(value) = content_map.get(&key) {
relevant_content.insert(key, value.clone());
}
}
- let platform_value = content.get("config").and_then(|v| match v {
+ let platform_value = content_map.get("config").and_then(|v| match v {
PhpMixed::Array(m) => m.get("platform").cloned(),
_ => None,
});
@@ -152,17 +158,21 @@ impl Locker {
}
/// Checks whether the lock file is still up to date with the current hash
- pub fn is_fresh(&self) -> Result<bool> {
+ pub fn is_fresh(&mut self) -> Result<bool> {
let lock = self.lock_file.read()?;
+ let lock_map: IndexMap<String, PhpMixed> = match lock {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
- let content_hash = lock.get("content-hash");
+ let content_hash = lock_map.get("content-hash");
if content_hash.is_some() && !shirabe_php_shim::empty(content_hash.unwrap()) {
// There is a content hash key, use that instead of the file hash
return Ok(self.content_hash == content_hash.unwrap().as_string().unwrap_or(""));
}
// BC support for old lock files without content-hash
- let lock_hash = lock.get("hash");
+ let lock_hash = lock_map.get("hash");
if lock_hash.is_some() && !shirabe_php_shim::empty(lock_hash.unwrap()) {
return Ok(self.hash == lock_hash.unwrap().as_string().unwrap_or(""));
}
@@ -174,7 +184,8 @@ impl Locker {
/// Searches and returns an array of locked packages, retrieved from registered repositories.
pub fn get_locked_repository(&mut self, with_dev_reqs: bool) -> Result<LockArrayRepository> {
let lock_data = self.get_lock_data()?;
- let mut packages = LockArrayRepository::new(vec![])?;
+ // TODO(phase-b): LockArrayRepository has no `new` constructor yet
+ let mut packages: LockArrayRepository = todo!("LockArrayRepository::new(vec![])");
let mut locked_packages = lock_data
.get("packages")
@@ -215,17 +226,12 @@ impl Locker {
let info_map: IndexMap<String, PhpMixed> =
m.iter().map(|(k, v)| (k.clone(), (**v).clone())).collect();
let package = self.loader.load(info_map, None)?;
- packages.add_package(package.clone())?;
- package_by_name.insert(package.get_name().to_string(), package.clone());
-
- // TODO(phase-b): `$package instanceof AliasPackage` downcast
- let package_as_alias: Option<&AliasPackage> = None;
- if let Some(alias) = package_as_alias {
- package_by_name.insert(
- alias.get_alias_of().get_name().to_string(),
- alias.get_alias_of(),
- );
- }
+ // TODO(phase-b): PHP shares the package between repository and map (Rc<dyn BasePackage>)
+ let _name = package.get_name().to_string();
+ let _ = (&mut packages, &mut package_by_name, package);
+ todo!(
+ "packages.add_package(package); package_by_name.insert(name, package); + AliasPackage downcast"
+ );
}
}
}
@@ -239,7 +245,8 @@ impl Locker {
.and_then(|v| v.as_string())
.unwrap_or("")
.to_string();
- if let Some(base_pkg) = package_by_name.get(&alias_pkg_name).cloned() {
+ // TODO(phase-b): Box<dyn BasePackage> is not Clone; PHP semantics need Rc<dyn BasePackage>
+ if let Some(base_pkg) = package_by_name.get(&alias_pkg_name) {
let mut alias_pkg = CompleteAliasPackage::new(
todo!("phase-b: downcast Box<BasePackage> to CompletePackage"),
m.get("alias_normalized")
@@ -251,7 +258,7 @@ impl Locker {
.unwrap_or("")
.to_string(),
);
- alias_pkg.set_root_package_alias(true);
+ // TODO(phase-b): set_root_package_alias missing on CompleteAliasPackage
let _ = base_pkg;
// TODO(phase-b): packages.add_package(Box::new(alias_pkg))
let _ = alias_pkg;
@@ -432,7 +439,7 @@ impl Locker {
/// @return array<string, mixed>
pub fn get_lock_data(&mut self) -> Result<IndexMap<String, PhpMixed>> {
- if let Some(cache) = self.lock_data_cache.clone() {
+ if let Some(cache) = self.lock_data_cache.borrow().clone() {
return Ok(cache);
}
@@ -444,8 +451,12 @@ impl Locker {
.into());
}
- let data = self.lock_file.read()?;
- self.lock_data_cache = Some(data.clone());
+ let data_php = self.lock_file.read()?;
+ let data: IndexMap<String, PhpMixed> = match data_php {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
+ *self.lock_data_cache.borrow_mut() = Some(data.clone());
Ok(data)
}
@@ -604,16 +615,21 @@ impl Locker {
} else {
None
};
- if !is_locked || Some(&lock) != current_data.as_ref() {
+ // TODO(phase-b): PhpMixed lacks PartialEq; PHP compares lock array with current data
+ let differs = current_data
+ .as_ref()
+ .map(|c| !std::ptr::eq(c as *const _, &lock as *const _))
+ .unwrap_or(true);
+ if !is_locked || differs {
if write {
self.lock_file.write(PhpMixed::Array(
lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect(),
))?;
- self.lock_data_cache = None;
+ *self.lock_data_cache.borrow_mut() = None;
self.virtual_file_written = false;
} else {
self.virtual_file_written = true;
- self.lock_data_cache = Some(JsonFile::parse_json(
+ let parsed = JsonFile::parse_json(
Some(&JsonFile::encode_with_indent(
&PhpMixed::Array(lock.into_iter().map(|(k, v)| (k, Box::new(v))).collect()),
shirabe_php_shim::JSON_UNESCAPED_SLASHES
@@ -622,7 +638,12 @@ impl Locker {
JsonFile::INDENT_DEFAULT,
)),
None,
- )?);
+ )?;
+ let parsed_map: IndexMap<String, PhpMixed> = match parsed {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
+ *self.lock_data_cache.borrow_mut() = Some(parsed_map);
}
return Ok(true);
@@ -644,7 +665,7 @@ impl Locker {
where
F: FnOnce(IndexMap<String, PhpMixed>) -> IndexMap<String, PhpMixed>,
{
- let contents = file_get_contents(&composer_json.get_path(), false, None);
+ let contents = file_get_contents(&composer_json.get_path());
let contents = match contents {
Some(s) => s,
None => {
@@ -660,7 +681,11 @@ impl Locker {
};
let lock_mtime = filemtime(&self.lock_file.get_path());
- let mut lock_data = self.lock_file.read()?;
+ let lock_data_php = self.lock_file.read()?;
+ let mut lock_data: IndexMap<String, PhpMixed> = match lock_data_php {
+ PhpMixed::Array(m) => m.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
lock_data.insert(
"content-hash".to_string(),
PhpMixed::String(Self::get_content_hash(&contents)?),
@@ -675,11 +700,13 @@ impl Locker {
.map(|(k, v)| (k, Box::new(v)))
.collect(),
))?;
- self.lock_data_cache = None;
+ *self.lock_data_cache.borrow_mut() = None;
self.virtual_file_written = false;
if let Some(mtime) = lock_mtime {
if is_int(&PhpMixed::Int(mtime)) {
- let _ = touch(&self.lock_file.get_path(), Some(mtime));
+ // TODO(phase-b): touch() in php-shim doesn't accept mtime; need touch2
+ let _ = mtime;
+ let _ = touch(&self.lock_file.get_path());
}
}
Ok(())
@@ -835,7 +862,12 @@ impl Locker {
let command = GitUtil::build_rev_list_command(&self.process, args);
let mut output = PhpMixed::Null;
if 0 == self.process.borrow_mut().execute(
- PhpMixed::String(command),
+ PhpMixed::List(
+ command
+ .into_iter()
+ .map(|s| Box::new(PhpMixed::String(s)))
+ .collect(),
+ ),
Some(&mut output),
path.as_deref(),
)? {
@@ -916,7 +948,7 @@ impl Locker {
let root_repo = RootPackageRepository::new(todo!("phase-b: clone root package"));
for set in &sets {
- let installed_repo = InstalledRepository::new(vec![/* set.repo, root_repo */])?;
+ let installed_repo = InstalledRepository::new(vec![/* set.repo, root_repo */]);
// PHP: call_user_func([$package, $set['method']])
// TODO(phase-b): dynamic method dispatch by name
@@ -925,13 +957,15 @@ impl Locker {
if PlatformRepository::is_platform_package(&link.get_target()) {
continue;
}
- if link.get_pretty_constraint().as_deref() == Some("self.version") {
+ if link.get_pretty_constraint().ok() == Some("self.version") {
continue;
}
if installed_repo
.find_packages_with_replacers_and_providers(
&link.get_target(),
- Some(link.get_constraint()),
+ Some(FindPackageConstraint::Constraint(
+ link.get_constraint().clone_box(),
+ )),
)
.is_empty()
{
@@ -939,7 +973,10 @@ impl Locker {
.find_packages_with_replacers_and_providers(&link.get_target(), None);
if !results.is_empty() {
- let provider = reset_first(&results).unwrap();
+ // TODO(phase-b): reset_first requires Clone on dyn BasePackage; PHP returns shared reference
+ let provider: &Box<dyn BasePackage> =
+ todo!("reset_first(&results) shared ref");
+ let _ = &results;
let mut description = provider.get_pretty_version().to_string();
if provider.get_name() != link.get_target() {
'outer: for (method, text) in [
@@ -959,7 +996,8 @@ impl Locker {
PhpMixed::String(
provider_link
.get_pretty_constraint()
- .unwrap_or_default(),
+ .unwrap_or_default()
+ .to_string(),
),
PhpMixed::String(format!(
"{} {}",
@@ -1012,7 +1050,7 @@ struct SetEntry {
// Suppress unused-import warnings for items kept for parity with the PHP source.
#[allow(dead_code)]
-const _USE_PARITY: () = {
+fn _use_parity() {
let _ = is_array;
- let _ = call_user_func;
-};
+ let _: PhpMixed = call_user_func("", &[]);
+}
diff --git a/crates/shirabe/src/package/package.rs b/crates/shirabe/src/package/package.rs
index 74286a0..b36de7a 100644
--- a/crates/shirabe/src/package/package.rs
+++ b/crates/shirabe/src/package/package.rs
@@ -463,9 +463,9 @@ impl Package {
url,
&self.name,
&self.version,
- r#ref.unwrap_or(""),
- r#type.unwrap_or(""),
- &self.pretty_version,
+ r#ref,
+ r#type,
+ Some(self.pretty_version.as_str()),
)
} else {
url.to_string()
@@ -479,9 +479,9 @@ impl Package {
&mirror.url,
&self.name,
&self.version,
- r#ref.unwrap_or(""),
- r#type.unwrap_or(""),
- &self.pretty_version,
+ r#ref,
+ r#type,
+ Some(self.pretty_version.as_str()),
)
} else if url_type == "source" && r#type == Some("git") {
ComposerMirror::process_git_url(
@@ -560,7 +560,7 @@ impl BasePackage for Package {
}
fn repository_opt(&self) -> Option<&dyn RepositoryInterface> {
- self.repository.as_ref()
+ self.repository.as_deref()
}
fn set_repository_box(&mut self, repository: Box<dyn RepositoryInterface>) {
diff --git a/crates/shirabe/src/package/package_interface.rs b/crates/shirabe/src/package/package_interface.rs
index beadc5c..e6ffbed 100644
--- a/crates/shirabe/src/package/package_interface.rs
+++ b/crates/shirabe/src/package/package_interface.rs
@@ -200,6 +200,18 @@ pub trait PackageInterface: std::fmt::Display + std::fmt::Debug {
/// @phpstan-return array<string, string>
fn get_suggests(&self) -> IndexMap<String, String>;
+ /// PHP helper that switches on the link kind (require/require-dev/conflict/etc.).
+ fn get_links_for_type(&self, link_type: &str) -> IndexMap<String, crate::package::link::Link> {
+ match link_type {
+ "require" => self.get_requires(),
+ "require-dev" => self.get_dev_requires(),
+ "conflict" => self.get_conflicts(),
+ "provide" => self.get_provides(),
+ "replace" => self.get_replaces(),
+ _ => IndexMap::new(),
+ }
+ }
+
/// Returns an associative array of autoloading rules
///
/// {"<type>": {"<namespace": "<directory>"}}
diff --git a/crates/shirabe/src/package/version/version_guesser.rs b/crates/shirabe/src/package/version/version_guesser.rs
index b366b20..c229c97 100644
--- a/crates/shirabe/src/package/version/version_guesser.rs
+++ b/crates/shirabe/src/package/version/version_guesser.rs
@@ -34,7 +34,7 @@ pub struct VersionGuesser {
process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
/// @var SemverVersionParser
- version_parser: SemverVersionParser,
+ version_parser: VersionParser,
/// @var IOInterface|null
io: Option<Box<dyn IOInterface>>,
@@ -54,7 +54,7 @@ impl VersionGuesser {
pub fn new(
config: std::rc::Rc<std::cell::RefCell<Config>>,
process: std::rc::Rc<std::cell::RefCell<ProcessExecutor>>,
- version_parser: SemverVersionParser,
+ version_parser: VersionParser,
io: Option<Box<dyn IOInterface>>,
) -> Self {
Self {
@@ -132,11 +132,14 @@ impl VersionGuesser {
&& Preg::is_match(r"{\.9{7}}", version_data.version.as_deref().unwrap_or(""))
.unwrap_or(false)
{
- version_data.pretty_version = Some(Preg::replace(
- r"{(\.9{7})+}",
- ".x",
- version_data.version.as_deref().unwrap_or(""),
- ));
+ version_data.pretty_version = Some(
+ Preg::replace(
+ r"{(\.9{7})+}",
+ ".x",
+ version_data.version.as_deref().unwrap_or(""),
+ )
+ .unwrap_or_default(),
+ );
}
let feature_non_empty = version_data
@@ -157,11 +160,14 @@ impl VersionGuesser {
)
.unwrap_or(false)
{
- version_data.feature_pretty_version = Some(Preg::replace(
- r"{(\.9{7})+}",
- ".x",
- version_data.feature_version.as_deref().unwrap_or(""),
- ));
+ version_data.feature_pretty_version = Some(
+ Preg::replace(
+ r"{(\.9{7})+}",
+ ".x",
+ version_data.feature_version.as_deref().unwrap_or(""),
+ )
+ .unwrap_or_default(),
+ );
}
version_data
@@ -228,7 +234,7 @@ impl VersionGuesser {
is_feature_branch = true;
is_detached = true;
} else {
- version = Some(self.version_parser.normalize_branch(&g1));
+ version = Some(self.version_parser.normalize_branch(&g1)?);
pretty_version = Some(format!("dev-{}", g1));
is_feature_branch = self.is_feature_branch(package_config, Some(&g1));
}
@@ -300,7 +306,12 @@ impl VersionGuesser {
Box::new(PhpMixed::String("-n1".to_string())),
Box::new(PhpMixed::String("HEAD".to_string())),
]),
- GitUtil::get_no_show_signature_flags(&self.process),
+ PhpMixed::List(
+ GitUtil::get_no_show_signature_flags(&self.process)
+ .into_iter()
+ .map(|s| Box::new(PhpMixed::String(s)))
+ .collect(),
+ ),
)
.as_list()
.map(|l| {
@@ -377,7 +388,7 @@ impl VersionGuesser {
Some(path.to_string()),
) {
let branch = trim(&output, None);
- let version = self.version_parser.normalize_branch(&branch);
+ let version = self.version_parser.normalize_branch(&branch)?;
let is_feature_branch = strpos(&version, "dev-") == Some(0);
if VersionParser::DEFAULT_BRANCH_ALIAS == version {
@@ -401,20 +412,15 @@ impl VersionGuesser {
}
// re-use the HgDriver to fetch branches (this properly includes bookmarks)
- let io = NullIO::new();
+ let _io = NullIO::new();
let mut repo_config: IndexMap<String, PhpMixed> = IndexMap::new();
repo_config.insert("url".to_string(), PhpMixed::String(path.to_string()));
- let mut driver = HgDriver::new(
- repo_config,
- // TODO(phase-b): NullIO -> Box<dyn IOInterface>
- Box::new(io),
- self.config.clone(),
- // TODO(phase-b): HttpDownloader::new signature
- todo!("HttpDownloader::new(io, config)"),
- std::rc::Rc::clone(&self.process),
+ // TODO(phase-b): HgDriver lacks a `new` constructor and HttpDownloader::new signature is unknown
+ let mut driver: HgDriver = todo!(
+ "HgDriver::new(repo_config, Box::new(io), self.config.clone(), HttpDownloader::new(io, config), Rc::clone(&self.process))"
);
let branches: Vec<String> =
- array_map(|k: &String| k.clone(), &array_keys(driver.get_branches()));
+ array_map(|k: &String| k.clone(), &array_keys(&driver.get_branches()?));
// try to find the best (nearest) version branch to assume this feature's version
let mut result = self.guess_feature_version(
@@ -486,7 +492,8 @@ impl VersionGuesser {
)
.is_some();
if !has_branch_alias || has_self_version {
- let branch = Preg::replace(r"{^dev-}", "", version.as_deref().unwrap_or(""));
+ let branch =
+ Preg::replace(r"{^dev-}", "", version.as_deref().unwrap_or("")).unwrap_or_default();
let mut length: i64 = PHP_INT_MAX;
// return directly, if branch is configured to be non-feature branch
@@ -518,7 +525,8 @@ impl VersionGuesser {
let result: Result<()> = (|| -> Result<()> {
let mut last_index: i64 = -1;
for (index, candidate) in branches.iter().enumerate() {
- let candidate_version = Preg::replace(r"{^remotes/\S+/}", "", candidate);
+ let candidate_version =
+ Preg::replace(r"{^remotes/\S+/}", "", candidate).unwrap_or_default();
// do not compare against itself or other feature branches
if candidate == &branch
@@ -537,23 +545,19 @@ impl VersionGuesser {
},
&scm_cmdline,
);
- let async_promise = self.process.borrow_mut().execute_async(&cmd_line, path);
- promises.push(async_promise.then(Box::new(
- move |process: Process| -> Result<()> {
- if !process.is_successful() {
- return Ok(());
- }
-
- let output = process.get_output();
- // overwrite existing if we have a shorter diff, or we have an equal diff and an index that comes later in the array (i.e. older version)
- // as newer versions typically have more commits, if the feature branch is based on a newer branch it should have a longer diff to the old version
- // but if it doesn't and they have equal diffs, then it probably is based on the old version
- // TODO(phase-b): closure captures need shared mutable state (last_index, length, version, pretty_version, promises)
- todo!(
- "mutate last_index/length/version/pretty_version and possibly cancel promises"
- );
- },
- )));
+ let async_promise = self.process.borrow_mut().execute_async(&cmd_line, path)?;
+ // TODO(phase-b): closure receives Process in PHP but PromiseInterface::then expects fn(Option<PhpMixed>) -> Option<PhpMixed>;
+ // closure captures need shared mutable state (last_index, length, version, pretty_version, promises)
+ promises.push(async_promise.then(
+ Some(Box::new(
+ move |_value: Option<PhpMixed>| -> Option<PhpMixed> {
+ todo!(
+ "mutate last_index/length/version/pretty_version and possibly cancel promises"
+ )
+ },
+ )),
+ None,
+ ));
}
self.process.borrow_mut().wait();
@@ -589,10 +593,13 @@ impl VersionGuesser {
non_feature_branches = implode("|", &names);
}
- !Preg::is_match(&format!(
- r"{{^({}|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}}",
- non_feature_branches,
- ), branch_name.unwrap_or("")).unwrap_or(false)
+ !Preg::is_match(
+ &format!(
+ r"{{^({}|master|main|latest|next|current|support|tip|trunk|default|develop|\d+\..+)$}}",
+ non_feature_branches,
+ ),
+ branch_name.unwrap_or(""),
+ )
.unwrap_or(false)
}
@@ -613,7 +620,7 @@ impl VersionGuesser {
Some(path.to_string()),
) {
let branch = trim(&output, None);
- version = Some(self.version_parser.normalize_branch(&branch));
+ version = Some(self.version_parser.normalize_branch(&branch)?);
pretty_version = Some(format!("dev-{}", branch));
}
@@ -691,7 +698,9 @@ impl VersionGuesser {
|| tags_path == *m2.as_ref().unwrap())
{
// we are in a branches path
- let version = self.version_parser.normalize_branch(m3.as_deref().unwrap());
+ let version = self
+ .version_parser
+ .normalize_branch(m3.as_deref().unwrap())?;
let pretty_version = format!("dev-{}", m3.as_ref().unwrap());
return Ok(Some(VersionData {
diff --git a/crates/shirabe/src/package/version/version_parser.rs b/crates/shirabe/src/package/version/version_parser.rs
index 84749a3..4286419 100644
--- a/crates/shirabe/src/package/version/version_parser.rs
+++ b/crates/shirabe/src/package/version/version_parser.rs
@@ -13,7 +13,7 @@ use crate::repository::platform_repository::PlatformRepository;
static CONSTRAINTS: LazyLock<Mutex<IndexMap<String, Arc<dyn ConstraintInterface + Send + Sync>>>> =
LazyLock::new(|| Mutex::new(IndexMap::new()));
-#[derive(Debug)]
+#[derive(Debug, Clone)]
pub struct VersionParser {
inner: SemverVersionParser,
}
@@ -24,13 +24,9 @@ impl VersionParser {
pub fn parse_constraints(
&self,
constraints: &str,
- ) -> anyhow::Result<Arc<dyn ConstraintInterface + Send + Sync>> {
- let mut cache = CONSTRAINTS.lock().unwrap();
- if !cache.contains_key(constraints) {
- let parsed = self.inner.parse_constraints(constraints)?;
- cache.insert(constraints.to_string(), Arc::from(parsed));
- }
- Ok(Arc::clone(cache.get(constraints).unwrap()))
+ ) -> anyhow::Result<Box<dyn ConstraintInterface>> {
+ // TODO(phase-b): re-introduce a memoization cache once trait objects are Send+Sync.
+ self.inner.parse_constraints(constraints)
}
pub fn parse_name_version_pairs(
@@ -92,6 +88,10 @@ impl VersionParser {
SemverVersionParser::parse_stability(version)
}
+ pub fn parse_numeric_alias_prefix(&self, branch: &str) -> Option<String> {
+ self.inner.parse_numeric_alias_prefix(branch)
+ }
+
pub fn is_upgrade(normalized_from: &str, normalized_to: &str) -> anyhow::Result<bool> {
if normalized_from == normalized_to {
return Ok(true);
diff --git a/crates/shirabe/src/package/version/version_selector.rs b/crates/shirabe/src/package/version/version_selector.rs
index 1df58ab..6496dea 100644
--- a/crates/shirabe/src/package/version/version_selector.rs
+++ b/crates/shirabe/src/package/version/version_selector.rs
@@ -23,6 +23,7 @@ use crate::package::loader::array_loader::ArrayLoader;
use crate::package::package_interface::PackageInterface;
use crate::package::version::version_parser::VersionParser;
use crate::repository::platform_repository::PlatformRepository;
+use crate::repository::repository_interface::RepositoryInterface;
use crate::repository::repository_set::RepositorySet;
#[derive(Debug)]
@@ -40,7 +41,8 @@ impl VersionSelector {
let mut platform_constraints: IndexMap<String, Vec<Box<dyn ConstraintInterface>>> =
IndexMap::new();
if let Some(platform_repo) = platform_repo {
- for package in platform_repo.get_packages() {
+ for package in <PlatformRepository as RepositoryInterface>::get_packages(platform_repo)
+ {
let constraint = Constraint::new("==", package.get_version());
platform_constraints
.entry(package.get_name().to_string())
@@ -88,9 +90,9 @@ impl VersionSelector {
};
let mut candidates = self.repository_set.find_packages(
&strtolower(package_name),
- constraint.as_deref(),
+ constraint.as_ref().map(|c| c.clone_box()),
repo_set_flags,
- )?;
+ );
let min_priority = *base_package::STABILITIES.get(preferred_stability).unwrap();
candidates.sort_by(|a, b| {
@@ -190,7 +192,7 @@ impl VersionSelector {
pkg.get_pretty_version(),
link.get_description(),
link.get_target(),
- link.get_pretty_constraint(),
+ link.get_pretty_constraint().unwrap_or_default(),
reason
),
true,
@@ -216,7 +218,7 @@ impl VersionSelector {
package = found_package;
} else {
package = if !candidates.is_empty() {
- Some(candidates.remove(0))
+ Some(candidates.remove(0).clone_package_box())
} else {
None
};
@@ -230,7 +232,7 @@ impl VersionSelector {
let package = if let Some(alias) = package.as_ref().as_any().downcast_ref::<AliasPackage>()
{
if alias.get_version() == VersionParser::DEFAULT_BRANCH_ALIAS {
- alias.get_alias_of()
+ alias.get_alias_of().clone_package_box()
} else {
package
}
@@ -266,9 +268,9 @@ impl VersionSelector {
);
}
- let loader = ArrayLoader::new(self.get_parser());
+ let loader = ArrayLoader::new(Some(self.get_parser().clone()), false);
let dumper = ArrayDumper::new();
- let extra = loader.get_branch_alias(&dumper.dump(package)?)?;
+ let extra = loader.get_branch_alias(&dumper.dump(package))?;
if let Some(extra) = extra {
if extra != VersionParser::DEFAULT_BRANCH_ALIAS {
let new_extra =