first commit.
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
<?php
|
||||
|
||||
namespace Toolbox;
|
||||
|
||||
final class Manifest
|
||||
{
|
||||
/**
|
||||
* @param array<string, Component> $components
|
||||
* @param list<string> $default
|
||||
*/
|
||||
private function __construct(
|
||||
public readonly string $name,
|
||||
public readonly string $version,
|
||||
public readonly string $description,
|
||||
private readonly array $components,
|
||||
private readonly ?array $default,
|
||||
public readonly ?string $coreRequires,
|
||||
public readonly ?string $coreTested,
|
||||
public readonly ?string $php,
|
||||
public readonly array $postInstall,
|
||||
) {
|
||||
}
|
||||
|
||||
public static function fromFile(string $path): self
|
||||
{
|
||||
if (!is_file($path)) {
|
||||
throw new ToolboxException("No se encontró el manifiesto: {$path}");
|
||||
}
|
||||
|
||||
$data = json_decode((string) file_get_contents($path), true);
|
||||
if (!is_array($data)) {
|
||||
throw new ToolboxException("Manifiesto JSON inválido: {$path}");
|
||||
}
|
||||
|
||||
return self::fromArray($data, $path);
|
||||
}
|
||||
|
||||
public static function fromArray(array $data, string $origin = 'duckbrain.json'): self
|
||||
{
|
||||
$name = $data['name'] ?? null;
|
||||
$version = $data['version'] ?? null;
|
||||
|
||||
if (!is_string($name) || $name === '') {
|
||||
throw new ToolboxException("El manifiesto '{$origin}' no declara 'name'");
|
||||
}
|
||||
|
||||
if (!is_string($version) || $version === '') {
|
||||
throw new ToolboxException("El manifiesto '{$origin}' no declara 'version'");
|
||||
}
|
||||
|
||||
Semver::parse($version);
|
||||
|
||||
if (!isset($data['components']) || !is_array($data['components']) || $data['components'] === []) {
|
||||
throw new ToolboxException("El manifiesto '{$origin}' no declara 'components'");
|
||||
}
|
||||
|
||||
$components = [];
|
||||
foreach ($data['components'] as $componentName => $componentData) {
|
||||
if (!is_string($componentName) || !is_array($componentData)) {
|
||||
throw new ToolboxException("Componente inválido en '{$origin}'");
|
||||
}
|
||||
$components[$componentName] = Component::fromArray($componentName, $componentData);
|
||||
}
|
||||
|
||||
$default = null;
|
||||
if (array_key_exists('default', $data)) {
|
||||
if (!is_array($data['default'])) {
|
||||
throw new ToolboxException("El manifiesto '{$origin}' tiene 'default' inválido");
|
||||
}
|
||||
$default = array_values($data['default']);
|
||||
foreach ($default as $componentName) {
|
||||
if (!is_string($componentName) || !isset($components[$componentName])) {
|
||||
throw new ToolboxException("'default' referencia un componente inexistente en '{$origin}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$postInstall = [];
|
||||
foreach (($data['post-install'] ?? []) as $command) {
|
||||
if (!is_string($command)) {
|
||||
throw new ToolboxException("'post-install' inválido en '{$origin}'");
|
||||
}
|
||||
$postInstall[] = $command;
|
||||
}
|
||||
|
||||
return new self(
|
||||
name: $name,
|
||||
version: $version,
|
||||
description: is_string($data['description'] ?? null) ? $data['description'] : '',
|
||||
components: $components,
|
||||
default: $default,
|
||||
coreRequires: $data['core']['requires'] ?? null,
|
||||
coreTested: $data['core']['tested'] ?? null,
|
||||
php: is_string($data['php'] ?? null) ? $data['php'] : null,
|
||||
postInstall: $postInstall,
|
||||
);
|
||||
}
|
||||
|
||||
/** @return array<string, Component> */
|
||||
public function components(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
public function hasComponent(string $name): bool
|
||||
{
|
||||
return isset($this->components[$name]);
|
||||
}
|
||||
|
||||
public function component(string $name): Component
|
||||
{
|
||||
if (!isset($this->components[$name])) {
|
||||
throw new ToolboxException("El paquete '{$this->name}' no tiene el componente '{$name}'");
|
||||
}
|
||||
|
||||
return $this->components[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expande una petición (componente o conjunto por defecto) a nombres.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function expand(?string $component): array
|
||||
{
|
||||
if ($component !== null) {
|
||||
if (!$this->hasComponent($component)) {
|
||||
throw new ToolboxException("El paquete '{$this->name}' no tiene el componente '{$component}'");
|
||||
}
|
||||
|
||||
return [$component];
|
||||
}
|
||||
|
||||
if ($this->default === null) {
|
||||
throw new ToolboxException(
|
||||
"El paquete '{$this->name}' no declara 'default'; indica un componente explícito"
|
||||
);
|
||||
}
|
||||
|
||||
return $this->default;
|
||||
}
|
||||
|
||||
public function coreRequirement(?string $component = null): ?string
|
||||
{
|
||||
if ($component !== null) {
|
||||
return $this->component($component)->coreRequires ?? $this->coreRequires;
|
||||
}
|
||||
|
||||
return $this->coreRequires;
|
||||
}
|
||||
|
||||
public function testedCore(?string $component = null): ?string
|
||||
{
|
||||
if ($component !== null) {
|
||||
return $this->component($component)->coreTested ?? $this->coreTested;
|
||||
}
|
||||
|
||||
return $this->coreTested;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user