Skip to content

Commit

Permalink
Refactor packages.
Browse files Browse the repository at this point in the history
Packages are now treated as individual instances of a Package class, similar to how Composer does it internally, which will help us to extend and format any output from Composer itself.
  • Loading branch information
bennothommo committed Apr 9, 2024
1 parent 4e46213 commit 803a9ef
Show file tree
Hide file tree
Showing 10 changed files with 661 additions and 70 deletions.
40 changes: 15 additions & 25 deletions src/Commands/Search.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace Winter\Packager\Commands;

use Winter\Packager\Exceptions\CommandException;
use Winter\Packager\Package\Package;
use Winter\Packager\Package\Collection;

/**
* Search command.
Expand Down Expand Up @@ -34,11 +36,6 @@ class Search extends BaseCommand
*/
public ?string $limitTo = null;

/**
* @var array<int, mixed> The results returned from the query.
*/
public array $results = [];

/**
* Command handler.
*/
Expand Down Expand Up @@ -69,9 +66,20 @@ public function execute()
throw new CommandException(implode(PHP_EOL, $output['output']));
}

$this->results = json_decode(implode(PHP_EOL, $output['output']), true);
$results = json_decode(implode(PHP_EOL, $output['output']), true);
$packages = [];

return $this;
foreach ($results as $result) {
[$namespace, $name] = preg_split('/\//', $result['name'], 2);

$packages[] = new Package(
$namespace,
$name,
$result['description'] ?? ''
);
}

return new Collection($packages);
}

/**
Expand All @@ -90,24 +98,6 @@ public function requiresWorkDir(): bool
return false;
}

/**
* Returns the list of results found.
*
* @return array<int, mixed>
*/
public function getResults(): array
{
return $this->results;
}

/**
* Returns the number of results found.
*/
public function count(): int
{
return count($this->results);
}

/**
* @inheritDoc
*/
Expand Down
101 changes: 100 additions & 1 deletion src/Commands/Show.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

namespace Winter\Packager\Commands;

use Winter\Packager\Enums\VersionStatus;
use Winter\Packager\Exceptions\CommandException;
use Winter\Packager\Package\Collection;
use Winter\Packager\Package\DetailedPackage;
use Winter\Packager\Package\Package;
use Winter\Packager\Package\VersionedPackage;

/**
* Show command.
Expand Down Expand Up @@ -98,7 +103,91 @@ public function execute()
}
}

return json_decode(implode(PHP_EOL, $output['output']), true);
$results = json_decode(implode(PHP_EOL, $output['output']), true);
$packages = [];

if (is_null($this->package) && in_array($this->mode, ['installed', 'locked', 'platform', 'path', 'outdated', 'direct'])) {
// Convert packages in simple lists to a package collection
$key = (!in_array($this->mode, ['locked', 'platform'])) ? 'installed' : $this->mode;
$results = $results[$key];

foreach ($results as $result) {
[$namespace, $name] = $this->nameSplit($result['name']);

if (isset($result['version'])) {
$packages[] = new VersionedPackage(
$namespace,
$name,
$result['description'] ?? '',
$result['version'],
$result['latest'] ?? '',
VersionStatus::tryFrom($result['latest-status'] ?? '') ?? VersionStatus::UP_TO_DATE
);
} else {
$packages[] = new Package(
$namespace,
$name,
$result['description'] ?? '',
);
}
}

return new Collection($packages);
} elseif (is_null($this->package) && $this->mode === 'available') {
// Convert entire available package list into a package collection
foreach ($results['available'] as $result) {
[$namespace, $name] = $this->nameSplit($result['name']);

$packages[] = new Package(
$namespace,
$name,
$result['description'] ?? '',
);
}

return new Collection($packages);
} elseif ($this->mode === 'self') {
$result = $results;
[$namespace, $name] = $this->nameSplit($result['name']);

// Return the current package
return new DetailedPackage(
$namespace,
$name,
$result['description'] ?? '',
$result['type'] ?? 'library',
$result['keywords'] ?? [],
$result['homepage'] ?? '',
$result['authors'] ?? [],
$result['licenses'] ?? [],
$result['support'] ?? [],
$result['funding'] ?? [],
$result['requires'] ?? [],
$result['devRequires'] ?? [],
$result['extras'] ?? [],
);
} elseif (!is_null($this->package)) {
$result = $results;
[$namespace, $name] = $this->nameSplit($result['name']);

return new DetailedPackage(
$namespace,
$name,
$result['description'] ?? '',
$result['type'] ?? 'library',
$result['keywords'] ?? [],
$result['homepage'] ?? '',
$result['authors'] ?? [],
$result['licenses'] ?? [],
$result['support'] ?? [],
$result['funding'] ?? [],
$result['requires'] ?? [],
$result['devRequires'] ?? [],
$result['extras'] ?? [],
);
}

return null;
}

/**
Expand Down Expand Up @@ -140,4 +229,14 @@ public function arguments(): array

return $arguments;
}

/**
* Split package name from namespace.
*
* @return string[]
*/
protected function nameSplit(string $name): array
{
return preg_split('/\//', $name, 2);
}
}
10 changes: 10 additions & 0 deletions src/Enums/VersionStatus.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Winter\Packager\Enums;

enum VersionStatus: string
{
case UP_TO_DATE = 'up-to-date';
case SEMVER_UPDATE = 'semver-safe-update';
case MAJOR_UPDATE = 'update-possible';
}
189 changes: 189 additions & 0 deletions src/Package/Collection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<?php

namespace Winter\Packager\Package;

/**
* Package collection.
*
* Collections contain one or more packages from a given result set, and can be used to filter and traverse the results.
*
* @author Ben Thomson <[email protected]>
* @since 0.3.0
* @implements \ArrayAccess<int|string, Package>
* @implements \Iterator<int, Package>
*/
class Collection implements \ArrayAccess, \Iterator, \Countable
{
/**
* @var array<int, Package> The packages contained in the collection.
*/
protected array $items = [];

/**
* Present position in the collection.
*/
protected int $position = 0;

/**
* Constructor.
*
* @param Package[]|Package $items
*/
public function __construct(...$items)
{
foreach ($items as $item) {
if ($item instanceof Package) {
$this->items[] = $item;
} elseif (is_array($item)) {
foreach ($item as $subItem) {
if ($subItem instanceof Package) {
$this->items[] = $subItem;
}
}
}
}
}

/**
* Adds a package to this collection.
*/
protected function add(Package $package): void
{
$this->items[] = $package;

uasort($this->items, function (Package $a, Package $b) {
return $a->getPackageName() <=> $b->getPackageName();
});
}

/**
* Gets the count of packages in this collection.
*/
public function count(): int
{
return count($this->items);
}

public function rewind(): void
{
$this->position = 0;
}

public function current(): mixed
{
return $this->items[$this->position];
}

public function key(): int
{
return $this->position;
}

public function next(): void
{
++$this->position;
}

public function valid(): bool
{
return isset($this->items[$this->position]);
}

/**
* Gets a package at a given index.
*
* This does not reset the internal pointer of the collection.
*
* If no package is found at the given index, `null` is returned.
*/
public function get(int $index): ?Package
{
return $this->items[$index] ?? null;
}

/**
* Finds a given package in the collection.
*/
public function find(string $namespace, string $name = '', ?string $version = null): ?Package
{
if (empty($name) && strpos($namespace, '/') !== false) {
[$namespace, $name] = explode('/', $namespace, 2);
}

foreach ($this->items as $item) {
if ($item->getNamespace() === $namespace && $item->getName() === $name) {
if (is_null($version) || ($item instanceof VersionedPackage && $item->getVersion() === $version)) {
return $item;
}
}
}

return null;
}

/**
* Checks if a given offset exists.
*
* You may either provide an integer key to retrieve by index, or a string key in the format `namespace/name` to
* find a particular package.
*/
public function offsetExists(mixed $offset): bool
{
if (is_int($offset)) {
return isset($this->items[$offset]);
}

if (is_string($offset)) {
[$namespace, $name] = explode('/', $offset, 2);
return !is_null($this->find($namespace, $name));
}
}

/**
* Gets a package at a given offset.
*
* You may either provide an integer key to retrieve by index, or a string key in the format `namespace/name` to
* find a particular package.
*/
public function offsetGet(mixed $offset): ?Package
{
if (is_int($offset)) {
return $this->get($offset);
}

if (is_string($offset)) {
[$namespace, $name] = explode('/', $offset, 2);
return $this->find($namespace, $name);
}
}

/**
* Sets a package at a given offset.
*
* This method is not supported.
*/
public function offsetSet(mixed $offset, mixed $value): void
{
throw new \RuntimeException('You cannot set values in a package collection.');
}

/**
* Unsets a package at a given offset.
*
* This method is not supported.
*/
public function offsetUnset(mixed $offset): void
{
throw new \RuntimeException('You cannot unset values in a package collection.');
}

/**
* Retrieve the collection as an array.
*
* @return array<int, \Winter\Packager\Package\Package>
*/
public function toArray()
{
return $this->items;
}
}
Loading

0 comments on commit 803a9ef

Please sign in to comment.