Compression, fixes
This commit is contained in:
@@ -7,6 +7,7 @@ namespace FileTime.App.Core.Command
|
||||
AutoRefresh,
|
||||
ChangeTimelineMode,
|
||||
CloseTab,
|
||||
Compress,
|
||||
Copy,
|
||||
CopyHash,
|
||||
CopyPath,
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\Tools\FileTime.Tools.Compression\FileTime.Tools.Compression.csproj" />
|
||||
<ProjectReference Include="..\FileTime.App.Core\FileTime.App.Core.csproj" />
|
||||
<ProjectReference Include="..\..\Providers\FileTime.Providers.Local\FileTime.Providers.Local.csproj" />
|
||||
<ProjectReference Include="..\..\Providers\FileTime.Providers.Smb\FileTime.Providers.Smb.csproj" />
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
@@ -14,7 +15,7 @@ namespace FileTime.Core.Command
|
||||
|
||||
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
|
||||
|
||||
public IContainer? Target { get; set; }
|
||||
public AbsolutePath? Target { get; set; }
|
||||
|
||||
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
|
||||
|
||||
@@ -25,6 +26,9 @@ namespace FileTime.Core.Command
|
||||
|
||||
public string DisplayLabel { get; } = "Copy";
|
||||
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
||||
public bool TargetIsContainer => true;
|
||||
public List<InputElement> Inputs { get; } = new();
|
||||
public List<object>? InputResults { get; set; }
|
||||
|
||||
private async Task UpdateProgress()
|
||||
{
|
||||
@@ -85,7 +89,8 @@ namespace FileTime.Core.Command
|
||||
return (IContainer)(await newContainerDiff.AbsolutePath.ResolveAsync())!;
|
||||
};
|
||||
|
||||
await TraverseTree(Sources, Target, TransportMode.Value);
|
||||
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
|
||||
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
|
||||
|
||||
return startPoint.WithDifferences(newDiffs);
|
||||
}
|
||||
@@ -122,7 +127,8 @@ namespace FileTime.Core.Command
|
||||
}
|
||||
};
|
||||
|
||||
await TraverseTree(Sources, Target, TransportMode.Value);
|
||||
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
|
||||
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
|
||||
}
|
||||
|
||||
private async Task CalculateProgress()
|
||||
@@ -150,7 +156,8 @@ namespace FileTime.Core.Command
|
||||
}
|
||||
};
|
||||
|
||||
await TraverseTree(Sources, Target, TransportMode.Value);
|
||||
var resolvedTarget = (IContainer)(await Target.ResolveAsync())!;
|
||||
await TraverseTree(Sources, resolvedTarget, TransportMode.Value);
|
||||
_operationStatuses = operationStatuses;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Command
|
||||
@@ -5,7 +6,10 @@ namespace FileTime.Core.Command
|
||||
public interface ITransportationCommand : ICommand
|
||||
{
|
||||
IList<AbsolutePath> Sources { get; }
|
||||
IContainer? Target { get; set;}
|
||||
AbsolutePath? Target { get; set; }
|
||||
TransportMode? TransportMode { get; set; }
|
||||
bool TargetIsContainer { get; }
|
||||
List<InputElement> Inputs { get; }
|
||||
List<object>? InputResults { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
@@ -8,7 +9,7 @@ namespace FileTime.Core.Command
|
||||
{
|
||||
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
|
||||
|
||||
public IContainer? Target { get; set; }
|
||||
public AbsolutePath? Target { get; set; }
|
||||
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
|
||||
|
||||
public int Progress => 100;
|
||||
@@ -16,6 +17,10 @@ namespace FileTime.Core.Command
|
||||
public AsyncEventHandler ProgressChanged { get; } = new();
|
||||
public string DisplayLabel { get; } = "MoveCommand";
|
||||
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
||||
public bool TargetIsContainer => true;
|
||||
|
||||
public List<InputElement> Inputs { get; } = new();
|
||||
public List<object>? InputResults { get; set; }
|
||||
|
||||
public Task<CanCommandRun> CanRun(PointInTime startPoint)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace FileTime.Core.CommandHandlers
|
||||
{
|
||||
if (command is not CopyCommand copyCommand) return false;
|
||||
|
||||
return (copyCommand.Target?.Provider.SupportsContentStreams ?? false)
|
||||
return (copyCommand.Target?.ContentProvider.SupportsContentStreams ?? false)
|
||||
&& copyCommand.Sources.All(p => p.ContentProvider.SupportsContentStreams);
|
||||
}
|
||||
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace FileTime.Core.Interactions
|
||||
return new InputElement(label, InputType.Password, defaultValue);
|
||||
}
|
||||
|
||||
public static InputElement ForOptions(string label, List<object> defaultValue)
|
||||
public static InputElement ForOptions(string label, IEnumerable<object> defaultValue)
|
||||
{
|
||||
return new InputElement(label, InputType.Options, defaultValue);
|
||||
return new InputElement(label, InputType.Options, new List<object>(defaultValue));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
using System.Threading.Tasks;
|
||||
namespace FileTime.Core.Providers
|
||||
{
|
||||
public class ContentProviderStream : Stream
|
||||
{
|
||||
|
||||
private readonly IContentReader? _contentReader;
|
||||
private readonly IContentWriter? _contentWriter;
|
||||
public override bool CanRead => _contentReader == null;
|
||||
public override bool CanRead => _contentReader != null;
|
||||
|
||||
public override bool CanSeek => false;
|
||||
public override bool CanSeek => _contentReader != null;
|
||||
|
||||
public override bool CanWrite => _contentWriter == null;
|
||||
public override bool CanWrite => _contentWriter != null;
|
||||
|
||||
public override long Length => throw new NotImplementedException();
|
||||
|
||||
@@ -28,7 +26,8 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public override void Flush()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (_contentWriter == null) throw new NotSupportedException();
|
||||
Task.Run(async () => await _contentWriter.FlushAsync()).Wait();
|
||||
}
|
||||
|
||||
public override int Read(byte[] buffer, int offset, int count)
|
||||
@@ -45,7 +44,16 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public override long Seek(long offset, SeekOrigin origin)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (_contentReader == null) throw new NotSupportedException();
|
||||
|
||||
var newPosition = origin switch
|
||||
{
|
||||
SeekOrigin.Begin => offset,
|
||||
SeekOrigin.Current => _contentReader.Position ?? 0 + offset,
|
||||
_ => throw new NotSupportedException()
|
||||
};
|
||||
_contentReader.SetPosition(newPosition);
|
||||
return newPosition;
|
||||
}
|
||||
|
||||
public override void SetLength(long value)
|
||||
@@ -55,7 +63,14 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public override void Write(byte[] buffer, int offset, int count)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (_contentWriter == null) throw new NotSupportedException();
|
||||
var data = buffer;
|
||||
if (buffer.Length != count)
|
||||
{
|
||||
data = new byte[count];
|
||||
Array.Copy(buffer, data, count);
|
||||
}
|
||||
Task.Run(async () => await _contentWriter.WriteBytesAsync(data, offset)).Wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,9 @@ namespace FileTime.Core.Providers
|
||||
public interface IContentReader : IDisposable
|
||||
{
|
||||
int PreferredBufferSize { get; }
|
||||
long? Position { get; }
|
||||
|
||||
Task<byte[]> ReadBytesAsync(int bufferSize, int? offset = null);
|
||||
void SetPosition(long position);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ namespace FileTime.Core.Providers
|
||||
{
|
||||
int PreferredBufferSize { get; }
|
||||
|
||||
Task WriteBytesAsync(byte[] data);
|
||||
Task WriteBytesAsync(byte[] data, int? index = null);
|
||||
Task FlushAsync();
|
||||
}
|
||||
}
|
||||
@@ -151,11 +151,11 @@ namespace FileTime.Core.Timeline
|
||||
}
|
||||
finally
|
||||
{
|
||||
Task.Run(async () => await DisposeCommandThread(Thread.CurrentThread, commandToRun)).Wait();
|
||||
Task.Run(async () => await DisposeCommandThread(commandToRun)).Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task DisposeCommandThread(Thread thread, CommandTimeState? command)
|
||||
private async Task DisposeCommandThread(CommandTimeState? command)
|
||||
{
|
||||
await RunWithLockAsync(async () =>
|
||||
{
|
||||
@@ -170,7 +170,7 @@ namespace FileTime.Core.Timeline
|
||||
}
|
||||
}
|
||||
|
||||
var currentCommandRunner = _commandRunners.Find(r => r.Thread == thread);
|
||||
var currentCommandRunner = _commandRunners.Find(r => r.Command == command);
|
||||
if (currentCommandRunner != null) _commandRunners.Remove(currentCommandRunner);
|
||||
await UpdateReadOnlyCommands();
|
||||
StartCommandRunner();
|
||||
|
||||
@@ -45,6 +45,7 @@ namespace FileTime.Avalonia.Configuration
|
||||
new CommandBindingConfiguration(Commands.AutoRefresh, new KeyConfig(Key.R, shift: true)),
|
||||
new CommandBindingConfiguration(Commands.ChangeTimelineMode, new[] { Key.T, Key.M }),
|
||||
new CommandBindingConfiguration(Commands.CloseTab, Key.Q),
|
||||
new CommandBindingConfiguration(Commands.Compress, new[] { Key.Y, Key.C }),
|
||||
new CommandBindingConfiguration(Commands.Copy, new[] { Key.Y, Key.Y }),
|
||||
new CommandBindingConfiguration(Commands.CopyHash, new[] { Key.C, Key.H }),
|
||||
new CommandBindingConfiguration(Commands.CopyPath, new[] { Key.C, Key.P }),
|
||||
|
||||
@@ -18,6 +18,7 @@ using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using FileTime.Core.Timeline;
|
||||
using FileTime.Providers.Local;
|
||||
using FileTime.Tools.Compression.Command;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.Avalonia.Services
|
||||
@@ -66,14 +67,15 @@ namespace FileTime.Avalonia.Services
|
||||
{Commands.AutoRefresh, ToggleAutoRefresh},
|
||||
{Commands.ChangeTimelineMode, ChangeTimelineMode},
|
||||
{Commands.CloseTab, CloseTab},
|
||||
{Commands.Compress, Compress},
|
||||
{Commands.Copy, Copy},
|
||||
{Commands.CopyHash, CopyHash},
|
||||
{Commands.CopyPath, CopyPath},
|
||||
{Commands.CreateContainer, CreateContainer},
|
||||
{Commands.CreateElement, CreateElement},
|
||||
{Commands.Cut, Cut},
|
||||
{Commands.EnterRapidTravel, EnterRapidTravelMode},
|
||||
{Commands.Edit, Edit},
|
||||
{Commands.EnterRapidTravel, EnterRapidTravelMode},
|
||||
{Commands.GoToHome, GotToHome},
|
||||
{Commands.GoToPath, GoToContainer},
|
||||
{Commands.GoToProvider, GotToProvider},
|
||||
@@ -468,14 +470,37 @@ namespace FileTime.Avalonia.Services
|
||||
command.Sources.Add(item);
|
||||
}
|
||||
|
||||
_clipboard.Clear();
|
||||
|
||||
var currentLocation = _appState.SelectedTab.CurrentLocation.Container;
|
||||
command.Target = currentLocation is VirtualContainer virtualContainer
|
||||
|
||||
currentLocation =
|
||||
currentLocation is VirtualContainer virtualContainer
|
||||
? virtualContainer.BaseContainer
|
||||
: currentLocation;
|
||||
|
||||
if (!command.TargetIsContainer)
|
||||
{
|
||||
var handler = async (List<InputElementWrapper> inputs) =>
|
||||
{
|
||||
command.Target = AbsolutePath.FromParentAndChildName(currentLocation, inputs[0].Value, AbsolutePathType.Element);
|
||||
command.InputResults = inputs.Skip(1).Select(i => i.Option ?? i.Value).ToList();
|
||||
await AddCommand(command);
|
||||
};
|
||||
|
||||
_clipboard.Clear();
|
||||
var inputs = new List<InputElement>()
|
||||
{
|
||||
InputElement.ForText("Compressed element name")
|
||||
};
|
||||
inputs.AddRange(command.Inputs);
|
||||
|
||||
_dialogService.ReadInputs(inputs, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
command.Target = new AbsolutePath(currentLocation);
|
||||
await AddCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -659,7 +684,6 @@ namespace FileTime.Avalonia.Services
|
||||
|
||||
foreach (var timelineBlock in _appState.TimelineCommands)
|
||||
{
|
||||
|
||||
foreach (var command in timelineBlock.ParallelCommands)
|
||||
{
|
||||
if (command.IsSelected)
|
||||
@@ -881,10 +905,34 @@ namespace FileTime.Avalonia.Services
|
||||
|
||||
_dialogService.ReadInputs(new List<InputElement>()
|
||||
{
|
||||
InputElement.ForOptions("Hash function", Enum.GetValues<HashFunction>().Cast<object>().ToList())
|
||||
InputElement.ForOptions("Hash function", Enum.GetValues<HashFunction>().Cast<object>())
|
||||
}, handler);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task Compress()
|
||||
{
|
||||
_clipboard.Clear();
|
||||
_clipboard.SetCommand<CompressCommand>();
|
||||
|
||||
var currentSelectedItems = await _appState.SelectedTab.TabState.GetCurrentMarkedItems();
|
||||
if (currentSelectedItems.Count > 0)
|
||||
{
|
||||
foreach (var selectedItem in currentSelectedItems)
|
||||
{
|
||||
_clipboard.AddContent(selectedItem);
|
||||
}
|
||||
await _appState.SelectedTab.TabState.ClearCurrentMarkedItems();
|
||||
}
|
||||
else
|
||||
{
|
||||
var currentSelectedItem = _appState.SelectedTab.SelectedItem?.Item;
|
||||
if (currentSelectedItem != null)
|
||||
{
|
||||
_clipboard.AddContent(new AbsolutePath(currentSelectedItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Threading.Tasks;
|
||||
using FileTime.Core.Providers;
|
||||
|
||||
namespace FileTime.Providers.Local
|
||||
@@ -7,10 +6,10 @@ namespace FileTime.Providers.Local
|
||||
{
|
||||
private readonly FileStream _readerStream;
|
||||
private readonly BinaryReader _binaryReader;
|
||||
private bool disposed;
|
||||
private bool _disposed;
|
||||
|
||||
public int PreferredBufferSize => 1024 * 1024;
|
||||
private long? _bytesRead;
|
||||
public long? Position { get; private set; }
|
||||
|
||||
public LocalContentReader(FileStream readerStream)
|
||||
{
|
||||
@@ -24,10 +23,10 @@ namespace FileTime.Providers.Local
|
||||
|
||||
if (offset != null)
|
||||
{
|
||||
if (_bytesRead == null) _bytesRead = 0;
|
||||
if (Position == null) Position = 0;
|
||||
var buffer = new byte[max];
|
||||
var bytesRead = _binaryReader.Read(buffer, offset.Value, max);
|
||||
_bytesRead += bytesRead;
|
||||
Position += bytesRead;
|
||||
|
||||
if (buffer.Length != bytesRead)
|
||||
{
|
||||
@@ -41,6 +40,11 @@ namespace FileTime.Providers.Local
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPosition(long position)
|
||||
{
|
||||
Position = position;
|
||||
}
|
||||
|
||||
~LocalContentReader()
|
||||
{
|
||||
Dispose(false);
|
||||
@@ -54,7 +58,7 @@ namespace FileTime.Providers.Local
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!disposed)
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
@@ -62,7 +66,7 @@ namespace FileTime.Providers.Local
|
||||
_binaryReader.Dispose();
|
||||
}
|
||||
}
|
||||
disposed = true;
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,9 +15,16 @@ namespace FileTime.Providers.Local
|
||||
_binaryWriter = new BinaryWriter(_writerStream);
|
||||
}
|
||||
|
||||
public Task WriteBytesAsync(byte[] data)
|
||||
public Task WriteBytesAsync(byte[] data, int? index = null)
|
||||
{
|
||||
if (index != null)
|
||||
{
|
||||
_binaryWriter.Write(data, index.Value, data.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
_binaryWriter.Write(data);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ namespace FileTime.Providers.Smb
|
||||
private readonly object _fileHandle;
|
||||
private readonly ISMBClient _client;
|
||||
private bool _disposed;
|
||||
private long _bytesRead;
|
||||
private long _position;
|
||||
public long? Position => _position;
|
||||
|
||||
public int PreferredBufferSize => (int)_client.MaxReadSize;
|
||||
|
||||
@@ -25,7 +26,7 @@ namespace FileTime.Providers.Smb
|
||||
{
|
||||
var max = bufferSize > 0 && bufferSize < (int)_client.MaxReadSize ? bufferSize : (int)_client.MaxReadSize;
|
||||
|
||||
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, offset ?? _bytesRead, max);
|
||||
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, offset ?? _position, max);
|
||||
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
|
||||
{
|
||||
throw new Exception("Failed to read from file");
|
||||
@@ -35,11 +36,16 @@ namespace FileTime.Providers.Smb
|
||||
{
|
||||
return Task.FromResult(Array.Empty<byte>());
|
||||
}
|
||||
_bytesRead += data.Length;
|
||||
_position += data.Length;
|
||||
|
||||
return Task.FromResult(data);
|
||||
}
|
||||
|
||||
public void SetPosition(long position)
|
||||
{
|
||||
_position = position;
|
||||
}
|
||||
|
||||
~SmbContentReader()
|
||||
{
|
||||
Dispose(false);
|
||||
|
||||
@@ -26,14 +26,16 @@ namespace FileTime.Providers.Smb
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task WriteBytesAsync(byte[] data)
|
||||
public Task WriteBytesAsync(byte[] data, int? index = null)
|
||||
{
|
||||
var status = _smbFileStore.WriteFile(out int numberOfBytesWritten, _fileHandle, _writeOffset, data);
|
||||
var status = _smbFileStore.WriteFile(out int numberOfBytesWritten, _fileHandle, index ?? _writeOffset, data);
|
||||
if (status != NTStatus.STATUS_SUCCESS)
|
||||
{
|
||||
throw new Exception("Failed to write to file");
|
||||
}
|
||||
_writeOffset += numberOfBytesWritten;
|
||||
_writeOffset = index == null
|
||||
? _writeOffset + numberOfBytesWritten
|
||||
: index.Value + numberOfBytesWritten;
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Command;
|
||||
using FileTime.Core.Extensions;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using FileTime.Core.Timeline;
|
||||
using FileTime.Tools.Compression.Command.OperationHandlers;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace FileTime.Tools.Compression.Command
|
||||
{
|
||||
public class CompressCommand : IExecutableCommand
|
||||
public class CompressCommand : IExecutableCommand, ITransportationCommand
|
||||
{
|
||||
public IList<AbsolutePath> Sources { get; } = new List<AbsolutePath>();
|
||||
public AbsolutePath? Target { get; set; }
|
||||
public string DisplayLabel { get; } = "Compress";
|
||||
|
||||
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
||||
@@ -16,6 +23,18 @@ namespace FileTime.Tools.Compression.Command
|
||||
public int CurrentProgress { get; }
|
||||
|
||||
public AsyncEventHandler ProgressChanged { get; } = new AsyncEventHandler();
|
||||
public TransportMode? TransportMode { get; set; }
|
||||
public bool TargetIsContainer => false;
|
||||
public List<InputElement> Inputs { get; }
|
||||
public List<object>? InputResults { get; set; }
|
||||
|
||||
public CompressCommand()
|
||||
{
|
||||
Inputs = new List<InputElement>()
|
||||
{
|
||||
InputElement.ForOptions("Compression method", Enum.GetValues<Models.CompressionType>().Cast<object>())
|
||||
};
|
||||
}
|
||||
|
||||
public Task<CanCommandRun> CanRun(PointInTime startPoint)
|
||||
{
|
||||
@@ -25,12 +44,89 @@ namespace FileTime.Tools.Compression.Command
|
||||
|
||||
public Task<PointInTime> SimulateCommand(PointInTime startPoint)
|
||||
{
|
||||
//TODO: implement
|
||||
return Task.FromResult(startPoint.WithDifferences(new List<Difference>()));
|
||||
}
|
||||
|
||||
public Task Execute(TimeRunner timeRunner)
|
||||
public async Task Execute(TimeRunner timeRunner)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
|
||||
|
||||
var disposables = Enumerable.Empty<IDisposable>();
|
||||
|
||||
ICompressOperation? compressOperation = null;
|
||||
try
|
||||
{
|
||||
var compressionType =
|
||||
InputResults?.Count > 0 && InputResults[0] is Models.CompressionType compType
|
||||
? compType
|
||||
: Models.CompressionType.Zip;
|
||||
|
||||
compressOperation = compressionType switch
|
||||
{
|
||||
Models.CompressionType.Gzip => SharpCompress.Archives.GZip.GZipArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.GZip)))),
|
||||
Models.CompressionType.Zip => SharpCompress.Archives.Zip.ZipArchive.Create().Map(a => GetCompressOperation(a, a.SaveTo)),
|
||||
Models.CompressionType.Tar => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.None)))),
|
||||
Models.CompressionType.TarBz2 => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.BZip2)))),
|
||||
Models.CompressionType.TarLz => SharpCompress.Archives.Tar.TarArchive.Create().Map(a => GetCompressOperation(a, (s) => a.SaveTo(s, new SharpCompress.Writers.WriterOptions(CompressionType.LZip)))),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
|
||||
disposables = await TraverseTree(Sources, "", compressOperation);
|
||||
|
||||
var resolvedParent = (IContainer)(await Target.GetParent().ResolveAsync())!;
|
||||
var targetElement = await resolvedParent.CreateElementAsync(Target.GetName());
|
||||
|
||||
using var contentWriter = await targetElement.GetContentWriterAsync();
|
||||
using var contentWriterStream = new ContentProviderStream(contentWriter);
|
||||
compressOperation.SaveTo(contentWriterStream);
|
||||
|
||||
await contentWriterStream.FlushAsync();
|
||||
}
|
||||
finally
|
||||
{
|
||||
compressOperation?.Dispose();
|
||||
|
||||
foreach (var disposable in disposables)
|
||||
{
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static async Task<IEnumerable<IDisposable>> TraverseTree(
|
||||
IEnumerable<AbsolutePath> sources,
|
||||
string basePath,
|
||||
ICompressOperation operations)
|
||||
{
|
||||
var disposables = Enumerable.Empty<IDisposable>();
|
||||
foreach (var source in sources)
|
||||
{
|
||||
var item = await source.ResolveAsync();
|
||||
|
||||
if (item is IContainer container)
|
||||
{
|
||||
var items = await container.GetItems();
|
||||
if (items == null) continue;
|
||||
|
||||
var childItems = items.Select(i => new AbsolutePath(i)).ToList()!;
|
||||
var path = string.IsNullOrEmpty(basePath) ? container.Name : basePath + "\\" + container.Name;
|
||||
disposables = disposables.Concat(await TraverseTree(childItems, path, operations));
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
public static CompressOperation<TEntry, TVolume> GetCompressOperation<TEntry, TVolume>(AbstractWritableArchive<TEntry, TVolume> archive, Action<Stream> saveTo)
|
||||
where TEntry : IArchiveEntry
|
||||
where TVolume : IVolume
|
||||
{
|
||||
return new CompressOperation<TEntry, TVolume>(archive, saveTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Common;
|
||||
|
||||
namespace FileTime.Tools.Compression.Command.OperationHandlers
|
||||
{
|
||||
public class CompressOperation<TEntry, TVolume> : ICompressOperation
|
||||
where TEntry : IArchiveEntry
|
||||
where TVolume : IVolume
|
||||
{
|
||||
private readonly AbstractWritableArchive<TEntry, TVolume> _archive;
|
||||
private readonly Action<Stream> _saveTo;
|
||||
private bool _disposed;
|
||||
|
||||
public CompressOperation(AbstractWritableArchive<TEntry, TVolume> archive, Action<Stream> saveTo)
|
||||
{
|
||||
_archive = archive;
|
||||
_saveTo = saveTo;
|
||||
}
|
||||
public async Task<IEnumerable<IDisposable>> CompressElement(IElement element, string key)
|
||||
{
|
||||
if (element.Provider.SupportsContentStreams)
|
||||
{
|
||||
var contentReader = await element.GetContentReaderAsync();
|
||||
var contentReaderStream = new ContentProviderStream(contentReader);
|
||||
|
||||
_archive.AddEntry(key, contentReaderStream);
|
||||
|
||||
return new IDisposable[] { contentReader, contentReaderStream };
|
||||
}
|
||||
|
||||
return Enumerable.Empty<IDisposable>();
|
||||
}
|
||||
|
||||
public void SaveTo(Stream stream)
|
||||
{
|
||||
_saveTo(stream);
|
||||
}
|
||||
|
||||
~CompressOperation()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
private void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_archive.Dispose();
|
||||
}
|
||||
}
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Tools.Compression.Command.OperationHandlers
|
||||
{
|
||||
public interface ICompressOperation : IDisposable
|
||||
{
|
||||
Task<IEnumerable<IDisposable>> CompressElement(IElement element, string key);
|
||||
void SaveTo(Stream stream);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace FileTime.Tools.Compression.Models
|
||||
{
|
||||
public enum CompressionType
|
||||
{
|
||||
Gzip,
|
||||
Zip,
|
||||
Tar,
|
||||
TarBz2,
|
||||
TarLz
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user