Files
duckbrain-toolbox/packages/commands/src/Toolbox/Copier.php
T
2026-10-05 15:08:39 -03:00

102 lines
3.1 KiB
PHP

<?php
namespace Toolbox;
/**
* Copia (o enlaza) los archivos de un componente al proyecto respetando la
* regla de destino, el modo y el drift.
*/
final class Copier
{
public function __construct(private readonly FileSync $sync = new FileSync())
{
}
/**
* @param list<string> $files rutas relativas al paquete
* @param array<string, string> $expectedFiles rel -> hash previo (lock)
* @param array<string, string>|null $destinations
* @param array{force?:bool, backup?:bool, keepLocal?:bool, dryRun?:bool} $options
* @return array{files: array<string,string>, actions: array<string,string>, aborted: list<string>}
*/
public function install(
array $files,
string $sourceDir,
string $rootDir,
string $rootCore,
?array $destinations,
string $mode,
array $options = [],
array $expectedFiles = []
): array {
$installed = [];
$actions = [];
$aborted = [];
foreach ($files as $file) {
$relative = Destination::relative($file, $rootDir, $rootCore, $destinations);
$source = rtrim($sourceDir, '/') . '/' . ltrim($file, '/');
$target = rtrim($rootDir, '/') . '/' . $relative;
$expected = $expectedFiles[$relative] ?? null;
if (($options['dryRun'] ?? false)) {
$actions[$relative] = 'planned';
continue;
}
if ($mode === 'link') {
$action = $this->link($source, $target, $options);
} else {
$contents = file_get_contents($source);
if ($contents === false) {
throw new ToolboxException("No se pudo leer el archivo de origen: {$source}");
}
$action = $this->sync->write($target, $contents, $expected, $options);
}
$actions[$relative] = $action;
if ($action === FileSync::ABORTED) {
$aborted[] = $relative;
continue;
}
$installed[$relative] = is_file($source) ? Drift::hash($source) : '';
}
return ['files' => $installed, 'actions' => $actions, 'aborted' => $aborted];
}
/**
* @param array{force?:bool, keepLocal?:bool} $options
*/
private function link(string $source, string $target, array $options): string
{
if (is_link($target)) {
if (readlink($target) === $source) {
return FileSync::WRITTEN;
}
unlink($target);
} elseif (is_file($target)) {
if ($options['keepLocal'] ?? false) {
return FileSync::SKIPPED;
}
if (!($options['force'] ?? false)) {
return FileSync::ABORTED;
}
unlink($target);
}
if (!is_dir(dirname($target))) {
mkdir(dirname($target), 0777, true);
}
if (!symlink($source, $target)) {
throw new ToolboxException("No se pudo enlazar {$target} -> {$source}");
}
return FileSync::WRITTEN;
}
}