Browse compressed file content

This commit is contained in:
2023-09-05 00:13:24 +02:00
parent 3a29991948
commit b85c19407e
19 changed files with 406 additions and 14 deletions

View File

@@ -0,0 +1,172 @@
using FileTime.Core.Command;
using FileTime.Core.ContentAccess;
using FileTime.Core.Enums;
using FileTime.Core.Interactions;
using FileTime.Core.Models;
using FileTime.Core.Timeline;
using FileTime.Tools.Extensions;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompressCompressionType = SharpCompress.Common.CompressionType;
namespace FileTime.Tools.Compression.Compress;
public class CompressCommand : CommandBase, IExecutableCommand, ITransportationCommand, IRequireInputCommand
{
private readonly IList<IInputElement> _inputs;
private readonly IUserCommunicationService _userCommunicationService;
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly IContentAccessorFactory _contentAccessorFactory;
private readonly ICommandSchedulerNotifier _commandSchedulerNotifier;
private readonly OptionsInputElement<CompressionType> _compressionType;
private readonly CancellationTokenSource _cancellationTokenSource = new();
private readonly TextInputElement _targetFileName;
public IReadOnlyList<FullName> Sources { get; }
public FullName Target { get; }
public TransportMode TransportMode { get; }
internal CompressCommand(
IUserCommunicationService userCommunicationService,
ITimelessContentProvider timelessContentProvider,
IContentAccessorFactory contentAccessorFactory,
ICommandSchedulerNotifier commandSchedulerNotifier,
IReadOnlyCollection<FullName> sources,
TransportMode mode,
FullName targetFullName)
{
_userCommunicationService = userCommunicationService;
_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;
_targetFileName = new TextInputElement("File name");
_compressionType = new OptionsInputElement<CompressionType>(
"CompressionMethod",
Enum.GetValues<CompressionType>()
.Select(t => new OptionElement<CompressionType>(t.ToString(), t))
)
{
Value = CompressionType.Zip
};
_inputs = new List<IInputElement>
{
_targetFileName,
_compressionType
};
}
public override Task<CanCommandRun> CanRun(PointInTime currentTime)
{
//TODO:
return Task.FromResult(CanCommandRun.True);
}
public override Task<PointInTime> SimulateCommand(PointInTime currentTime)
{
var differences = new List<Difference>
{
new(DifferenceActionType.Create, new AbsolutePath(_timelessContentProvider, currentTime, Target, AbsolutePathType.Element))
};
return Task.FromResult(currentTime.WithDifferences(differences));
}
public override void Cancel()
=> _cancellationTokenSource.Cancel();
public async Task Execute()
{
var disposables = Enumerable.Empty<IDisposable>();
ICompressOperation? compressOperation = null;
try
{
var compressionType = _compressionType.Value;
compressOperation = compressionType switch
{
CompressionType.Gzip => SharpCompress.Archives.GZip.GZipArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(SharpCompressCompressionType.GZip)))),
CompressionType.Zip => SharpCompress.Archives.Zip.ZipArchive.Create().Map(a => GetCompressOperation(a, a.SaveTo)),
CompressionType.Tar => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(SharpCompressCompressionType.None)))),
CompressionType.TarBz2 => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(SharpCompressCompressionType.BZip2)))),
CompressionType.TarLz => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(SharpCompressCompressionType.LZip)))),
_ => throw new NotImplementedException()
};
disposables = await TraverseTree(Sources, "", compressOperation);
var resolvedParent = await _timelessContentProvider.GetItemByFullNameAsync(
Target,
PointInTime.Present
);
var newItemName = Target.GetChild(_targetFileName.Value!);
await _contentAccessorFactory.GetItemCreator(resolvedParent.Provider).CreateElementAsync(resolvedParent.Provider, newItemName);
var targetElement = (IElement) await _timelessContentProvider.GetItemByFullNameAsync(newItemName, PointInTime.Present);
using var contentWriter = await _contentAccessorFactory.GetContentWriterFactory(resolvedParent.Provider).CreateContentWriterAsync(targetElement);
await using var contentWriterStream = contentWriter.AsStream();
compressOperation.SaveTo(contentWriterStream);
await contentWriterStream.FlushAsync(_cancellationTokenSource.Token);
}
finally
{
compressOperation?.Dispose();
foreach (var disposable in disposables)
{
disposable.Dispose();
}
}
await _commandSchedulerNotifier.RefreshContainer(Target);
}
private async Task<IEnumerable<IDisposable>> TraverseTree(
IEnumerable<FullName> sources,
string basePath,
ICompressOperation operations,
CancellationToken cancellationToken = default)
{
var disposables = Enumerable.Empty<IDisposable>();
foreach (var source in sources)
{
var item = await _timelessContentProvider.GetItemByFullNameAsync(
source,
PointInTime.Present
);
if (item is IContainer container)
{
var items = container.Items;
var childItems = items.Select(i => i.Path).ToList();
var path = string.IsNullOrEmpty(basePath) ? container.Name : basePath + "\\" + container.Name;
disposables = disposables.Concat(await TraverseTree(childItems, path, operations, cancellationToken));
}
else if (item is IElement element)
{
var path = string.IsNullOrEmpty(basePath) ? element.Name : basePath + "\\" + element.Name;
disposables = disposables.Concat(await operations.CompressElement(element, path));
}
}
return disposables;
}
private CompressOperation<TEntry, TVolume> GetCompressOperation<TEntry, TVolume>(AbstractWritableArchive<TEntry, TVolume> archive, Action<Stream> saveTo)
where TEntry : IArchiveEntry where TVolume : IVolume
=> new(_contentAccessorFactory, archive, saveTo);
public async Task ReadInputs()
=> await _userCommunicationService.ReadInputs(_inputs);
}

View File

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

View File

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