aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-15 00:50:28 +0900
committernsfisis <nsfisis@gmail.com>2026-05-15 02:37:37 +0900
commit145a501f142da5bca765e4af7083e52d2f151f87 (patch)
tree771f9d0de0ac37aa583dd51e35bafa5bbd5ab8e0 /crates/shirabe/src
parentfe4c11f23399c3fe90838c228d94746268b51b2d (diff)
downloadphp-shirabe-145a501f142da5bca765e4af7083e52d2f151f87.tar.gz
php-shirabe-145a501f142da5bca765e4af7083e52d2f151f87.tar.zst
php-shirabe-145a501f142da5bca765e4af7083e52d2f151f87.zip
feat(port): port PackageRepository.php
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/repository/advisory_provider_interface.rs2
-rw-r--r--crates/shirabe/src/repository/package_repository.rs150
2 files changed, 151 insertions, 1 deletions
diff --git a/crates/shirabe/src/repository/advisory_provider_interface.rs b/crates/shirabe/src/repository/advisory_provider_interface.rs
index 2d98ef0..d0aaceb 100644
--- a/crates/shirabe/src/repository/advisory_provider_interface.rs
+++ b/crates/shirabe/src/repository/advisory_provider_interface.rs
@@ -20,5 +20,5 @@ pub struct SecurityAdvisoryResult {
pub trait AdvisoryProviderInterface {
fn has_security_advisories(&self) -> bool;
- fn get_security_advisories(&self, package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, allow_partial_advisories: bool) -> SecurityAdvisoryResult;
+ fn get_security_advisories(&self, package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>, allow_partial_advisories: bool) -> anyhow::Result<SecurityAdvisoryResult>;
}
diff --git a/crates/shirabe/src/repository/package_repository.rs b/crates/shirabe/src/repository/package_repository.rs
index d0867b7..7bdf95f 100644
--- a/crates/shirabe/src/repository/package_repository.rs
+++ b/crates/shirabe/src/repository/package_repository.rs
@@ -1 +1,151 @@
//! ref: composer/src/Composer/Repository/PackageRepository.php
+
+use crate::advisory::partial_security_advisory::PartialSecurityAdvisory;
+use crate::advisory::security_advisory::SecurityAdvisory;
+use crate::package::loader::array_loader::ArrayLoader;
+use crate::package::loader::validating_array_loader::ValidatingArrayLoader;
+use crate::package::version::version_parser::VersionParser;
+use crate::repository::advisory_provider_interface::{
+ AdvisoryProviderInterface, PartialOrSecurityAdvisory, SecurityAdvisoryResult,
+};
+use crate::repository::array_repository::ArrayRepository;
+use crate::repository::invalid_repository_exception::InvalidRepositoryException;
+use indexmap::IndexMap;
+use shirabe_external_packages::composer::pcre::preg::Preg;
+use shirabe_php_shim::{Exception, PhpMixed};
+use shirabe_semver::constraint::constraint_interface::ConstraintInterface;
+
+#[derive(Debug)]
+pub struct PackageRepository {
+ inner: ArrayRepository,
+ config: Vec<PhpMixed>,
+ security_advisories: IndexMap<String, PhpMixed>,
+}
+
+impl PackageRepository {
+ pub fn new(config: IndexMap<String, PhpMixed>) -> Self {
+ let package = config.get("package").cloned().unwrap_or(PhpMixed::Null);
+ let config_list: Vec<PhpMixed> = match package {
+ PhpMixed::List(list) => list.into_iter().map(|p| *p).collect(),
+ other => vec![other],
+ };
+
+ let security_advisories = match config
+ .get("security-advisories")
+ .cloned()
+ .unwrap_or(PhpMixed::Array(IndexMap::new()))
+ {
+ PhpMixed::Array(map) => map.into_iter().map(|(k, v)| (k, *v)).collect(),
+ _ => IndexMap::new(),
+ };
+
+ Self {
+ inner: ArrayRepository::new(),
+ config: config_list,
+ security_advisories,
+ }
+ }
+
+ pub fn initialize(&mut self) -> anyhow::Result<Result<(), InvalidRepositoryException>> {
+ self.inner.initialize()?;
+
+ let loader = ValidatingArrayLoader::new(ArrayLoader::new(None, true), true);
+ for package in &self.config {
+ let package = match loader.load(package) {
+ Ok(p) => p,
+ Err(e) => {
+ let msg = format!(
+ "A repository of type \"package\" contains an invalid package definition: {}\n\nInvalid package definition:\n{}",
+ e,
+ shirabe_php_shim::json_encode(package).unwrap_or_default()
+ );
+ return Ok(Err(InvalidRepositoryException(Exception {
+ message: msg,
+ code: 0,
+ })));
+ }
+ };
+ self.inner.add_package(package)?;
+ }
+ Ok(Ok(()))
+ }
+
+ pub fn get_repo_name(&self) -> String {
+ Preg::replace(r"^array ", "package ", &self.inner.get_repo_name())
+ }
+}
+
+impl AdvisoryProviderInterface for PackageRepository {
+ fn has_security_advisories(&self) -> bool {
+ !self.security_advisories.is_empty()
+ }
+
+ fn get_security_advisories(
+ &self,
+ package_constraint_map: IndexMap<String, Box<dyn ConstraintInterface>>,
+ allow_partial_advisories: bool,
+ ) -> anyhow::Result<SecurityAdvisoryResult> {
+ let parser = VersionParser::new();
+
+ let mut advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = IndexMap::new();
+ for (package_name, package_advisories) in &self.security_advisories {
+ if package_constraint_map.contains_key(package_name.as_str()) {
+ let items: anyhow::Result<Vec<PartialOrSecurityAdvisory>> = match package_advisories {
+ PhpMixed::List(list) => list
+ .iter()
+ .filter_map(|data| {
+ let data_map = match data.as_ref() {
+ PhpMixed::Array(m) => m
+ .iter()
+ .map(|(k, v)| (k.clone(), *v.clone()))
+ .collect::<IndexMap<String, PhpMixed>>(),
+ _ => return Ok(None),
+ };
+ let advisory_any =
+ PartialSecurityAdvisory::create(package_name, &data_map, &parser)
+ .ok()?;
+ let advisory =
+ if let Ok(full) = advisory_any.downcast::<SecurityAdvisory>() {
+ PartialOrSecurityAdvisory::Full(*full)
+ } else if let Ok(partial) =
+ advisory_any.downcast::<PartialSecurityAdvisory>()
+ {
+ PartialOrSecurityAdvisory::Partial(*partial)
+ } else {
+ return Ok(None);
+ };
+ if !allow_partial_advisories
+ && matches!(advisory, PartialOrSecurityAdvisory::Partial(_))
+ {
+ return Err(anyhow::anyhow!(RuntimeException { message: format!("Advisory for {} could not be loaded as a full advisory from {}\n{}", package_name, self.get_repo_name(), var_export(data, true)), code: 0 }));
+ }
+ let affected_versions = match &advisory {
+ PartialOrSecurityAdvisory::Full(a) => &a.affected_versions,
+ PartialOrSecurityAdvisory::Partial(a) => &a.affected_versions,
+ };
+ if !affected_versions
+ .matches(package_constraint_map[package_name.as_str()].as_ref())
+ {
+ return Ok(None);
+ }
+ Ok(Some(advisory))
+ })
+ .collect(),
+ _ => vec![],
+ };
+ advisories.insert(package_name.clone(), items?);
+ }
+ }
+
+ let names_found: Vec<String> = advisories.keys().cloned().collect();
+ let advisories: IndexMap<String, Vec<PartialOrSecurityAdvisory>> = advisories
+ .into_iter()
+ .filter(|(_, adv)| !adv.is_empty())
+ .collect();
+
+ Ok(SecurityAdvisoryResult {
+ names_found,
+ advisories,
+ })
+ }
+}