| Age | Commit message (Collapse) | Author |
|
The capture groups were discarded at 162 of the preg_match call sites,
which only tested the Option. They now call preg_is_match, which lets the
regex engine skip capture tracking.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Every call site but one passed offset 0. The remaining one, the UTF-8
chunking loop in Application, slices the subject instead: its pattern has
no anchor or lookaround, so matching a suffix is equivalent to starting
the search at that offset.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Preg had shed everything it owned: after the last few rounds its methods
were one-line forwards to the shim's preg_*(), differing only in a
default argument or a wrapper the caller unwrapped anyway. The 460 call
sites now name the shim function, and shirabe-pcre is gone from the
workspace along with its LICENSE entry.
The forwards expand as they read: isMatch becomes
preg_match2(.., 0).is_some() (is_none() where PHP negates it), isMatch3
and match3 drop the .is_some(), matchAll counts through
preg_match_all2(..).occurrence_count(), and replace4/replace5 spell out
the limit and count arguments preg_replace2 takes. Callbacks are the one
place the shapes differ: preg_replace_callback carries an error out of
the callback, so the fourteen infallible closures wrap their result in
Ok() and expect() it back.
Config::process() is the fifteenth, and it drops the `error` cell it
captured to smuggle a failure past a closure that could only return a
String. The `?` in the closure now carries it, which is what the PHP
does -- a throw from the callback leaves preg_replace_callback at the
failing match rather than running the remaining replacements and
reporting the last error.
The module doc that explained why composer/pcre's exceptions and
*StrictGroups() variants have no counterpart moves to the shim's preg
module, where the functions it describes live.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
PregMatches keyed both forms of a capture group through CaptureKey, so
every read built one: a usize wrapped in an enum, or worse, a String
allocated to name a group that regex::Captures can look up from a &str.
It now mirrors regex::Captures instead -- get() takes the group number,
name() the group name -- and the enum drops out of the type entirely.
That is 285 call sites across 59 files, and the named ones carry most of
the win: `matches.get(&CaptureKey::ByName("host".to_string()))` reads as
`matches.name("host")`. ProcessExecutor loses a `user_key` binding that
existed only to build the key once.
CaptureKey stays as the key type of PregMatchesAll and
PregMatchesAllWithOffsets, where numbered and named entries share one
IndexMap and a key type is the point. Five files still name it.
Also retargets the two preg_match_all comments that described the
occurrence count through `matches[&CaptureKey::ByIndex(0)].len()`, an
Index impl these types no longer carry.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Preg::match4 and Preg::replace_callback gave callers a
PregMatchedGroups: an IndexMap rebuilt from the match with an owned
String per group, plus a second String for a named group's name key.
That is the copy PregMatches shed when it started wrapping
regex::Captures, reinstated one layer up -- and nearly every regex call
in the tree goes through Preg rather than the shim's preg_* directly, so
almost nothing saw the borrow.
PregMatchedGroups existed only to drop the null (unmatched) groups the
old PregMatches held as Option<String> values. PregMatches::get reports
a non-participating group as None on its own, so the two read alike and
the type collapses into it. Call sites still reach groups through
get(&CaptureKey::ByIndex(N)); what changes is that the value arrives as
a &str borrowed from the subject, which the signatures now carry as a
lifetime.
Three places needed the borrow reckoned with rather than a mechanical
rewrite: PhpFileCleaner::clean and Problem::get_messages read their
groups out before mutating what the match borrows, and
Git::get_authentication_failure names the lifetime of its url argument,
which the result borrows instead of self.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
`Composer\Pcre\Preg` fills `$matches` through a by-ref parameter, and the
port mirrored that with a `&mut` (or `Option<&mut>`) out-param plus a
bool or count return. Callers had to declare an empty map one line ahead
of the call, and the type never said the map is only meaningful when the
call matched. Return the matches instead:
- match3/match4/is_match3/is_match4 -> Option<PregMatchedGroups>
- is_match_named -> Option<PregNamedGroups>
- match_all2/is_match_all -> PregMatchesAll
- is_match_all_with_offsets3 -> PregMatchesAllWithOffsets
Nothing is lost: the bool is `Option::is_some()`, and the occurrence
count is the length of any one column of a PREG_PATTERN_ORDER map, now
spelled `PregMatchesAll::occurrence_count()`. is_match() still answers
the bool question directly for callers that want no groups.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The five IndexMap shapes that the preg_* functions and Preg fill in are
now distinct types generated by preg_match_map!, so a matches map no
longer interchanges with any other map of the same key and value type.
Index<usize> is kept alongside Index<&Q> because call sites such as
config_command and event_dispatcher reach for a group by its position in
the map rather than by its capture key.
|
|
preg_split2()'s limit was always -1 and its flags were always either 0 or
PREG_SPLIT_DELIM_CAPTURE alone, so both arguments are gone: the shim now
exposes preg_split() and preg_split_delim_capture() over a shared
preg_split_impl(). That leaves Preg::split()/split4() as bare
pass-throughs, so callers use the shim functions directly and the wrappers
are dropped along with the now-unreferenced PREG_SPLIT_* constants.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The binary called itself Composer everywhere: the application name, the
logo, --version, about, and every warning that talks about the running
program. Prompts to file a bug also pointed at Composer's issue tracker.
Add SHIRABE_VERSION and SHIRABE_RELEASE_DATE next to the Composer version
constants and report those, naming the Composer version this port tracks
alongside them. Composer::VERSION and getVersion() are untouched, so the
composer platform package, composer-runtime-api and the HTTP User-Agent
keep the value plugins and package repositories expect.
build.rs stamps the release date with the UTC date of the HEAD commit,
the way Composer's Compiler fills in @release_date@ when building the
phar. It now also fails the build when git cannot be read, instead of
letting COMPOSER_DEV_WARNING_TIME fall back to the tagged-release value
and suppress the outdated-build warning forever.
Messages about the Composer ecosystem keep their wording. Two of them are
pinned by upstream installer fixtures (Rule's "cannot be modified by
Composer" and SolverProblemsException's "you can run Composer with") and
stay as they are so those fixtures can keep being used verbatim.
The e2e list comparison against upstream Composer now skips the banner,
which cannot match by design, and compares everything below it as before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Move `Composer\Pcre` out of shirabe-external-packages and into its own
crate, so the path is `shirabe_pcre::preg::Preg` instead of
`shirabe_external_packages::composer::pcre::preg::Preg`.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Ported exceptions were flat structs reached with `downcast_ref`, so
Composer's `catch (\RuntimeException $e)` only matched the exact leaf
type and `get_class($e)` had nothing to report. Each exception now
embeds an instance of the class it extends and travels inside an
`AnyThrowable`; `Catch::catch`/`catch_mut` walk that chain, and
`PhpClass::php_class_name` yields the PHP FQCN.
Dropping the `std::error::Error` impls from the exception types leaves
`AnyThrowable` as the only route into an `anyhow::Error`, so the walk
cannot be bypassed. A `no_exception_downcast` linter catches the
`downcast::<X>()` calls that would now silently answer `None`.
Three sites change behavior as a result: the `TransportException`
exit-code override reaches `MaxFileSizeExceededException`, the
`catch (\LogicException)` in findSimilar() reaches its subclasses, and
rendered exception titles carry the real class name rather than a
guess. `get_class_err()` is no longer a `todo!()`, which re-enables
FilesystemRepositoryTest::testCorruptedRepositoryFile.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Several call sites coerced PhpMixed to bool via `.as_bool()` (which
only matches a literal Bool variant) where the corresponding PHP code
does a plain `(bool)` cast or truthy check (isset()/array_key_exists()
+ implicit bool conversion). This silently dropped truthy non-bool
values (e.g. String("true"), String("1"), Int(1)) to their unwrap_or
default instead of PHP's actual truthy result. Switched these sites to
PhpMixed::to_bool(), which implements PHP's full truthy-cast rules.
|
|
port
extract_stability_flags ported PHP's `isset($stabilityFlags[$name]) &&
$stabilityFlags[$name] > $stability` as `unwrap_or(i64::MAX) > stability`,
so the check always short-circuited to "already more unstable" and no flag
(e.g. from an explicit `*@dev` requirement) was ever recorded. Fixed using
Option::is_some_and, a direct translation of PHP's isset() && ... check.
Also fixes tests/common/test_case.rs's shared installation_manager()
helper, which built a real InstallationManager::new instead of the
__new_mock constructor (mirroring PHP's FactoryMock::createInstallationManager()),
so it always had zero installers registered and wrote install-path: null
into fixture installed.json files.
Un-ignores test_reinstall_command, test_locally_modified_packages_from_source/
_from_dist, and test_package_still_present_error_when_no_install_flag_used —
the first three were already passing (their #[ignore] reasons were stale),
the last is fixed by the test_case.rs change above.
Updates the #[ignore] reasons on installer_test.rs's three fixture-driven
integration tests to reflect their current state: the install pipeline now
runs end-to-end, but the ~189-fixture installer/ set still hits several
independent, unrelated bugs/gaps that need case-by-case triage rather than
a single fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
regex::Regex::clone() does not share the underlying meta engine's
search-cache pool, so every fresh clone pays a ~10us warmup cost on
its first use. Two changes together eliminate this across nearly all
preg_* call sites:
- A php_regex! macro resolves PHP-style patterns to a per-call-site
&'static regex::Regex (via regex-macro's LazyLock), applied at the
majority of call sites throughout the codebase.
- Call sites still passing dynamic pattern strings go through
PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out
Arc::clone()s instead of cloning the Regex itself.
PregPattern::resolve() returns a ResolvedPattern enum (Arc or
'static reference) rather than an owned Regex, so neither path ever
clones the Regex proper.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
|
|
Port 11 categories of previously-ignored Composer tests now reachable with
the mockall crate: DownloadManager, VCS/Perforce/File downloaders,
VersionSelector, PlatformRepository, Auditor, installer/FilesystemRepository,
RootPackageLoader, util auth/http, commands, and Cache.
Extract test seams additively on concrete structs as *Interface traits
(Runtime, HhvmDetector, VersionGuesser, RepositorySet, Perforce,
BinaryInstaller) plus mock-field seams (Cache, Filesystem); consumers take
trait objects. Mocks are defined locally in the test crates via
mockall::mock!, since automock-generated mocks are cfg(test)-gated and
invisible across the integration-test boundary.
dataProviders are ported in full; tests blocked by unported shims stay
#[ignore] with documented reasons rather than reduced or weakened.
Fix product bugs surfaced by the ports:
- util/github: use the exception code, not the HTTP status, for 401/403
- advisory: serialize empty audit maps as [] to match PHP json_encode
- repository/filesystem and downloader/file: fix RefCell double-borrow panics
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
Add create_installed_json/create_composer_lock test helpers. Port command (8),
repository path/forgejo/perforce/vcs (11), and fossil/hg/download_manager (13)
tests. Fix production porting bugs: root_package_loader/forgejo_url/version_bumper
regex delimiters, repository_manager create_repository_by_class, array_loader
isset, licenses_command RefCell borrow; implement disk_free_space and
touch2/touch3 via libc.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
The PCRE delimiter `(?<!^|as|[=>< ,]) *(?<!-)[, ](?!-) *(?!,|as|$)` used to
split AND-constraints relies on look-around, which the regex crate cannot
compile (parse_constraints panicked). Reproduce its semantics in a hand-written
`split_and_constraints` scanner shared by VersionParser and RootPackageLoader.
Also model `method_exists` for the class-name form (shirabe runs no dumped
Composer ClassLoader) and un-ignore the InstalledVersions tests, serialized via
`#[serial]` since they share global static state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
The List and Array variants of PhpMixed boxed their elements
unnecessarily. Store PhpMixed values directly and update all callers
accordingly.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
|
|
|
The Preg methods panic on PCRE failure (per the file header rationale),
so their anyhow::Result wrappers never carried an Err.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Rust's type system already distinguishes participating from
non-participating capture groups via Option, so the *StrictGroups
methods add no safety here. Remove them and switch callers to the
plain variants.
|
|
Resolve the remaining todo!()/placeholder sites in RootPackageLoader::load
so the root package loads with real data:
- Unbox config at use sites and change load()'s parameter from
IndexMap<String, Box<PhpMixed>> to IndexMap<String, PhpMixed>, matching
ArrayLoader::load / VersionGuesser::guess_version and dropping the
redundant box/unbox round-trip at the factory call site.
- Expose replace_version through CompletePackage/RootPackage inherent
delegation and a RootPackageHandle method (PHP Package::replaceVersion,
inherited), and apply it for the auto-versioned default.
- Collect require/require-dev links via getter dispatch and build the
pretty-string map feeding extractAliases/StabilityFlags/References.
Also reconcile the package repositories type with Config: change
CompletePackageInterface::{get,set}_repositories from
Vec<IndexMap<String, PhpMixed>> to IndexMap<String, PhpMixed> (PHP's
array<int|string, mixed>), wire setRepositories(config.getRepositories()),
and dump repositories verbatim as a keyed array (matching PHP ArrayDumper)
instead of forcing a list.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
|
|
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
|
PHP packages have reference semantics, so introduce shared-ownership
handles over an AnyPackage enum (PackageInterfaceHandle and friends)
and replace Box<dyn PackageInterface> throughout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Convert Pool to Rc<RefCell<Pool>> so Solver, Decisions, and
RuleSetGenerator share it, resolving the todo!() placeholders that
blocked the dependency resolver (Phase C shared ownership).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Model PHP's `Composer extends PartialComposer` as a PartialOrFullComposer
enum and merge partial_composer.rs into composer.rs. Introduce
ComposerHandle / PartialComposerHandle (plus their Weak variants) so the
graph can be shared, and build it at once with Rc::new_cyclic in the
factory to resolve the back-reference cycles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
|
|
|
|
|
|