Decompress command

This commit is contained in:
2023-08-22 13:24:52 +02:00
parent 506636789c
commit 5905f62d66
6 changed files with 207 additions and 2 deletions

View File

@@ -63,6 +63,9 @@ public class LocalItemCreator : ItemCreatorBase<ILocalContentProvider>
try try
{ {
_logger.LogTrace("Trying to create element with path {Path}", path); _logger.LogTrace("Trying to create element with path {Path}", path);
var directory = Path.GetDirectoryName(path);
if (directory is { } && !Directory.Exists(directory)) Directory.CreateDirectory(directory);
await using (File.Create(path)) await using (File.Create(path))
{ {
} }

View File

@@ -21,12 +21,34 @@ public class CompressionUserCommandHandler : AggregatedUserCommandHandler
_markedItems = appState.SelectedTab.Map(t => t?.MarkedItems).Switch(); _markedItems = appState.SelectedTab.Map(t => t?.MarkedItems).Switch();
_selectedItem = appState.SelectedTab.Map(t => t?.CurrentSelectedItem).Switch(); _selectedItem = appState.SelectedTab.Map(t => t?.CurrentSelectedItem).Switch();
AddCommandHandler(new[] AddCommandHandler(new IUserCommandHandler[]
{ {
new TypeUserCommandHandler<CompressUserCommand>(Compress) new TypeUserCommandHandler<CompressUserCommand>(Compress),
new TypeUserCommandHandler<DecompressUserCommand>(Decompress)
}); });
} }
private Task Decompress()
{
_clipboardService.Clear();
_clipboardService.SetCommand<DecompressCommandFactory>();
if (_markedItems.Value is {Count: > 0} markedItems)
{
foreach (var markedItem in markedItems)
{
_clipboardService.AddContent(markedItem);
}
}
else if (_selectedItem.Value?.BaseItem?.FullName is { } fullname)
{
//TODO: check if file is decompressable
_clipboardService.AddContent(fullname);
}
return Task.CompletedTask;
}
private Task Compress() private Task Compress()
{ {
_clipboardService.Clear(); _clipboardService.Clear();

View File

@@ -0,0 +1,131 @@
using FileTime.Core.Command;
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using SharpCompress.Archives;
namespace FileTime.Tools.Compression;
public class DecompressCommand : CommandBase, IExecutableCommand, ITransportationCommand, IDisposable
{
private record ArchiveContext(IArchive Archive, string TargetContainerName);
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
private readonly List<ArchiveContext> _archives = new();
private readonly List<IDisposable> _disposables = new();
public IReadOnlyList<FullName> Sources { get; }
public FullName Target { get; }
public TransportMode TransportMode { get; }
internal DecompressCommand(
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory,
ICommandSchedulerNotifier commandSchedulerNotifier,
IReadOnlyCollection<FullName> sources,
TransportMode mode,
FullName targetFullName)
{
_timelessContentProvider = timelessContentProvider;
_contentAccessorFactory = contentAccessorFactory;
_commandSchedulerNotifier = commandSchedulerNotifier;
ArgumentNullException.ThrowIfNull(sources);
ArgumentNullException.ThrowIfNull(mode);
ArgumentNullException.ThrowIfNull(targetFullName);
Sources = new List<FullName>(sources).AsReadOnly();
TransportMode = mode;
Target = targetFullName;
}
public override Task<CanCommandRun> CanRun(PointInTime currentTime)
{
//TODO:
return Task.FromResult(CanCommandRun.True);
}
public override Task<PointInTime> SimulateCommand(PointInTime currentTime)
{
//TODO:
return Task.FromResult(currentTime);
}
public override void Cancel()
=> _cancellationTokenSource.Cancel();
public async Task Execute()
{
if (_archives.Count == 0) await OpenArchivesAsync();
var resolvedTarget = (IContainer) await _timelessContentProvider.GetItemByFullNameAsync(Target, PointInTime.Present);
var itemCreator = _contentAccessorFactory.GetItemCreator(resolvedTarget.Provider);
var contentWriterFactory = _contentAccessorFactory.GetContentWriterFactory(resolvedTarget.Provider);
foreach (var archiveContext in _archives)
{
await DecompressFile(archiveContext, resolvedTarget, contentWriterFactory, itemCreator);
}
}
private async Task DecompressFile(
ArchiveContext archiveContext,
IContainer resolvedTarget,
IContentWriterFactory contentWriterFactory,
IItemCreator itemCreator)
{
var archive = archiveContext.Archive;
foreach (var archiveEntry in archive.Entries)
{
var subPath = string.Join(Constants.SeparatorChar, archiveEntry.Key.Split('\\'));
var entryPath = Target.GetChild(subPath);
if (archiveEntry.IsDirectory)
{
await itemCreator.CreateContainerAsync(resolvedTarget.Provider, entryPath);
}
else
{
await itemCreator.CreateElementAsync(resolvedTarget.Provider, entryPath);
var newItem = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(entryPath, PointInTime.Present);
using var writer = await contentWriterFactory.CreateContentWriterAsync(newItem);
archiveEntry.WriteTo(writer.AsStream());
}
}
}
private async Task OpenArchivesAsync()
{
if (_archives.Count > 0) throw new InvalidOperationException("Archives are already open");
foreach (var source in Sources)
{
var targetElement = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(source, PointInTime.Present);
var contentReader = await _contentAccessorFactory.GetContentReaderFactory(targetElement.Provider).CreateContentReaderAsync(targetElement);
var contentReaderStream = contentReader.AsStream();
_disposables.Add(contentReader);
using var archive = ArchiveFactory.Open(contentReaderStream);
_archives.Add(new ArchiveContext(archive, GetFileName(source.GetName())));
}
}
public string GetFileName(string fullName)
{
var parts = fullName.Split('.');
var fileName = string.Join('.', parts[..^1]);
return string.IsNullOrEmpty(fileName) ? fullName : fileName;
}
public void Dispose()
{
foreach (var archive in _archives)
{
archive.Archive.Dispose();
}
}
}

View File

@@ -0,0 +1,32 @@
using FileTime.Core.Command;
using FileTime.Core.ContentAccess;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
namespace FileTime.Tools.Compression;
public class DecompressCommandFactory : ITransportationCommandFactory<DecompressCommand>
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
public DecompressCommandFactory(
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory,
ICommandSchedulerNotifier commandSchedulerNotifier)
{
_timelessContentProvider = timelessContentProvider;
_contentAccessorFactory = contentAccessorFactory;
_commandSchedulerNotifier = commandSchedulerNotifier;
}
public DecompressCommand GenerateCommand(IReadOnlyCollection<FullName> sources, TransportMode mode, FullName targetFullName)
=> new(
_timelessContentProvider,
_contentAccessorFactory,
_commandSchedulerNotifier,
sources,
mode,
targetFullName);
}

View File

@@ -0,0 +1,15 @@
using FileTime.App.Core.UserCommand;
namespace FileTime.Tools.Compression;
public class DecompressUserCommand : IIdentifiableUserCommand
{
public const string CommandName = "decompress";
public static readonly DecompressUserCommand Instance = new();
private DecompressUserCommand()
{
}
public string UserCommandID => CommandName;
public string Title => "Select for decompression";
}

View File

@@ -8,6 +8,7 @@ public class StartupHandler : IStartupHandler
public StartupHandler(IIdentifiableUserCommandService identifiableUserCommandService) public StartupHandler(IIdentifiableUserCommandService identifiableUserCommandService)
{ {
identifiableUserCommandService.AddIdentifiableUserCommand(CompressUserCommand.Instance); identifiableUserCommandService.AddIdentifiableUserCommand(CompressUserCommand.Instance);
identifiableUserCommandService.AddIdentifiableUserCommand(DecompressUserCommand.Instance);
} }
public Task InitAsync() => Task.CompletedTask; public Task InitAsync() => Task.CompletedTask;
} }
@@ -18,6 +19,7 @@ public static class Startup
{ {
services.AddSingleton<IStartupHandler, StartupHandler>(); services.AddSingleton<IStartupHandler, StartupHandler>();
services.AddSingleton<CompressCommandFactory>(); services.AddSingleton<CompressCommandFactory>();
services.AddSingleton<DecompressCommandFactory>();
services.AddSingleton<IUserCommandHandler, CompressionUserCommandHandler>(); services.AddSingleton<IUserCommandHandler, CompressionUserCommandHandler>();
return services; return services;
} }