StreamCopyCommandHandler, Async rename
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
using FileTime.App.Core.Clipboard;
|
using FileTime.App.Core.Clipboard;
|
||||||
using FileTime.Core.Command;
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.CommandHandlers;
|
||||||
using FileTime.Core.Providers;
|
using FileTime.Core.Providers;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
using FileTime.Providers.Local;
|
using FileTime.Providers.Local;
|
||||||
@@ -21,7 +22,23 @@ namespace FileTime.App.Core
|
|||||||
.AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"))
|
.AddSingleton<IContentProvider, LocalContentProvider>(sp => sp.GetService<LocalContentProvider>() ?? throw new Exception($"No {nameof(LocalContentProvider)} instance found"))
|
||||||
.AddSingleton<IContentProvider, SmbContentProvider>()
|
.AddSingleton<IContentProvider, SmbContentProvider>()
|
||||||
.AddSingleton<CommandExecutor>()
|
.AddSingleton<CommandExecutor>()
|
||||||
.AddSingleton<TimeRunner>();
|
.AddSingleton<TimeRunner>()
|
||||||
|
.RegisterCommandHandlers();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IServiceCollection RegisterCommandHandlers(this IServiceCollection serviceCollection)
|
||||||
|
{
|
||||||
|
var commandHandlers = new List<Type>()
|
||||||
|
{
|
||||||
|
typeof(StreamCopyCommandHandler)
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var commandHandler in commandHandlers)
|
||||||
|
{
|
||||||
|
serviceCollection.AddTransient(typeof(ICommandHandler), commandHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
return serviceCollection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,13 +148,13 @@ namespace FileTime.ConsoleUI.App
|
|||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(newContainerName))
|
if (!string.IsNullOrWhiteSpace(newContainerName))
|
||||||
{
|
{
|
||||||
await currentLocation.CreateContainer(newContainerName);
|
await currentLocation.CreateContainerAsync(newContainerName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async Task Validator(string newPath)
|
async Task Validator(string newPath)
|
||||||
{
|
{
|
||||||
if (await currentLocation.IsExists(newPath))
|
if (await currentLocation.IsExistsAsync(newPath))
|
||||||
{
|
{
|
||||||
_coloredConsoleRenderer.ForegroundColor = _styles.ErrorColor;
|
_coloredConsoleRenderer.ForegroundColor = _styles.ErrorColor;
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ namespace FileTime.ConsoleUI.App
|
|||||||
{
|
{
|
||||||
IList<AbsolutePath>? itemsToDelete = null;
|
IList<AbsolutePath>? itemsToDelete = null;
|
||||||
|
|
||||||
var currentSelectedItems = (await _tabStates[_selectedTab!].GetCurrentMarkedItems()).Select(p => p.Resolve()).ToList();
|
var currentSelectedItems = (await _tabStates[_selectedTab!].GetCurrentMarkedItems()).Select(p => p.ResolveAsync()).ToList();
|
||||||
var currentSelectedItem = await _selectedTab?.GetCurrentSelectedItem();
|
var currentSelectedItem = await _selectedTab?.GetCurrentSelectedItem();
|
||||||
if (currentSelectedItems.Count > 0)
|
if (currentSelectedItems.Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
9
src/Core/FileTime.Core/Command/CanHandleCommand.cs
Normal file
9
src/Core/FileTime.Core/Command/CanHandleCommand.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Core.Command
|
||||||
|
{
|
||||||
|
public enum CanHandleCommand
|
||||||
|
{
|
||||||
|
CanHandle,
|
||||||
|
Fallback,
|
||||||
|
CanNotHandle
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace FileTime.Core.Command
|
namespace FileTime.Core.Command
|
||||||
{
|
{
|
||||||
public class CommandExecutor
|
public class CommandExecutor
|
||||||
{
|
{
|
||||||
private readonly List<ICommandHandler> _commandHandlers;
|
private readonly List<ICommandHandler> _commandHandlers;
|
||||||
|
private readonly ILogger<CommandExecutor> _logger;
|
||||||
|
|
||||||
public CommandExecutor(IEnumerable<ICommandHandler> commandHandlers)
|
public CommandExecutor(
|
||||||
|
IEnumerable<ICommandHandler> commandHandlers,
|
||||||
|
ILogger<CommandExecutor> logger)
|
||||||
{
|
{
|
||||||
_commandHandlers = commandHandlers.ToList();
|
_commandHandlers = commandHandlers.ToList();
|
||||||
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteCommandAsync(ICommand command, TimeRunner timeRunner)
|
public async Task ExecuteCommandAsync(ICommand command, TimeRunner timeRunner)
|
||||||
@@ -19,7 +24,15 @@ namespace FileTime.Core.Command
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _commandHandlers.Find(c => c.CanHandle(command))?.ExecuteAsync(command, timeRunner);
|
var commandHandler = _commandHandlers.Find(c => c.CanHandle(command));
|
||||||
|
if (commandHandler != null)
|
||||||
|
{
|
||||||
|
await commandHandler.ExecuteAsync(command, timeRunner);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogError("No command handler for command {Command}", command.GetType().Name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
_copyOperation = (_, to, _, _) =>
|
_copyOperation = (_, to, _, _) =>
|
||||||
{
|
{
|
||||||
var target = to.GetParentAsAbsolutePath().Resolve();
|
var target = to.GetParent().ResolveAsync();
|
||||||
newDiffs.Add(new Difference(
|
newDiffs.Add(new Difference(
|
||||||
target is IElement
|
target is IElement
|
||||||
? DifferenceItemType.Element
|
? DifferenceItemType.Element
|
||||||
@@ -79,12 +79,12 @@ namespace FileTime.Core.Command
|
|||||||
var newContainerDiff = new Difference(
|
var newContainerDiff = new Difference(
|
||||||
DifferenceItemType.Container,
|
DifferenceItemType.Container,
|
||||||
DifferenceActionType.Create,
|
DifferenceActionType.Create,
|
||||||
AbsolutePath.FromParentAndChildName(target, name)
|
AbsolutePath.FromParentAndChildName(target, name, AbsolutePathType.Container)
|
||||||
);
|
);
|
||||||
|
|
||||||
newDiffs.Add(newContainerDiff);
|
newDiffs.Add(newContainerDiff);
|
||||||
|
|
||||||
return (IContainer)(await newContainerDiff.AbsolutePath.Resolve())!;
|
return (IContainer)(await newContainerDiff.AbsolutePath.ResolveAsync())!;
|
||||||
};
|
};
|
||||||
|
|
||||||
await TraverseTree(Sources, Target, TransportMode.Value);
|
await TraverseTree(Sources, Target, TransportMode.Value);
|
||||||
@@ -110,7 +110,7 @@ namespace FileTime.Core.Command
|
|||||||
await UpdateProgress();
|
await UpdateProgress();
|
||||||
};
|
};
|
||||||
|
|
||||||
_createContainer = async (IContainer target, string name) => await target.CreateContainer(name);
|
_createContainer = async (IContainer target, string name) => await target.CreateContainerAsync(name);
|
||||||
_containerCopyDone = async (path) =>
|
_containerCopyDone = async (path) =>
|
||||||
{
|
{
|
||||||
foreach (var item in _operationStatuses[path])
|
foreach (var item in _operationStatuses[path])
|
||||||
@@ -137,7 +137,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
_copyOperation = async (from, to, _, _) =>
|
_copyOperation = async (from, to, _, _) =>
|
||||||
{
|
{
|
||||||
var parentPath = to.GetParentAsAbsolutePath();
|
var parentPath = to.GetParent();
|
||||||
List<OperationProgress> operationsByFolder;
|
List<OperationProgress> operationsByFolder;
|
||||||
if (operationStatuses.ContainsKey(parentPath))
|
if (operationStatuses.ContainsKey(parentPath))
|
||||||
{
|
{
|
||||||
@@ -145,7 +145,7 @@ namespace FileTime.Core.Command
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var resolvedFrom = await from.Resolve();
|
var resolvedFrom = await from.ResolveAsync();
|
||||||
operationsByFolder = new List<OperationProgress>();
|
operationsByFolder = new List<OperationProgress>();
|
||||||
operationStatuses.Add(parentPath, operationsByFolder);
|
operationStatuses.Add(parentPath, operationsByFolder);
|
||||||
operationsByFolder.Add(new OperationProgress(from.Path, resolvedFrom is IElement element ? await element.GetElementSize() : 0L));
|
operationsByFolder.Add(new OperationProgress(from.Path, resolvedFrom is IElement element ? await element.GetElementSize() : 0L));
|
||||||
@@ -166,7 +166,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
foreach (var source in sources)
|
foreach (var source in sources)
|
||||||
{
|
{
|
||||||
var item = await source.Resolve();
|
var item = await source.ResolveAsync();
|
||||||
|
|
||||||
if (item is IContainer container)
|
if (item is IContainer container)
|
||||||
{
|
{
|
||||||
@@ -182,7 +182,7 @@ namespace FileTime.Core.Command
|
|||||||
{
|
{
|
||||||
var targetName = element.Name;
|
var targetName = element.Name;
|
||||||
|
|
||||||
var targetNameExists = await target.IsExists(targetName);
|
var targetNameExists = await target.IsExistsAsync(targetName);
|
||||||
if (transportMode == Command.TransportMode.Merge)
|
if (transportMode == Command.TransportMode.Merge)
|
||||||
{
|
{
|
||||||
for (var i = 0; targetNameExists; i++)
|
for (var i = 0; targetNameExists; i++)
|
||||||
@@ -197,7 +197,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
OperationProgress? operation = null;
|
OperationProgress? operation = null;
|
||||||
var targetFolderPath = new AbsolutePath(target);
|
var targetFolderPath = new AbsolutePath(target);
|
||||||
var targetElementPath = AbsolutePath.FromParentAndChildName(target, targetName);
|
var targetElementPath = AbsolutePath.FromParentAndChildName(target, targetName, AbsolutePathType.Element);
|
||||||
|
|
||||||
foreach(var asd in _operationStatuses.Keys)
|
foreach(var asd in _operationStatuses.Keys)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -26,10 +26,10 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
public async Task Execute(TimeRunner timeRunner)
|
public async Task Execute(TimeRunner timeRunner)
|
||||||
{
|
{
|
||||||
var possibleContainer = await Container.Resolve();
|
var possibleContainer = await Container.ResolveAsync();
|
||||||
if (possibleContainer is IContainer container)
|
if (possibleContainer is IContainer container)
|
||||||
{
|
{
|
||||||
await container.CreateContainer(NewContainerName);
|
await container.CreateContainerAsync(NewContainerName);
|
||||||
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
|
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
|
||||||
}
|
}
|
||||||
//TODO: else
|
//TODO: else
|
||||||
@@ -39,18 +39,18 @@ namespace FileTime.Core.Command
|
|||||||
{
|
{
|
||||||
var newDifferences = new List<Difference>()
|
var newDifferences = new List<Difference>()
|
||||||
{
|
{
|
||||||
new Difference(DifferenceItemType.Container, DifferenceActionType.Create, new AbsolutePath(Container.ContentProvider, Container.Path + Constants.SeparatorChar + NewContainerName, Container.VirtualContentProvider))
|
new Difference(DifferenceItemType.Container, DifferenceActionType.Create, Container.GetChild(NewContainerName, AbsolutePathType.Container))
|
||||||
};
|
};
|
||||||
return Task.FromResult(startPoint.WithDifferences(newDifferences));
|
return Task.FromResult(startPoint.WithDifferences(newDifferences));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
|
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
|
||||||
{
|
{
|
||||||
var resolvedContainer = await Container.Resolve();
|
var resolvedContainer = await Container.ResolveAsync();
|
||||||
if (resolvedContainer == null) return CanCommandRun.Forceable;
|
if (resolvedContainer == null) return CanCommandRun.Forceable;
|
||||||
|
|
||||||
if (resolvedContainer is not IContainer container
|
if (resolvedContainer is not IContainer container
|
||||||
|| await container.IsExists(NewContainerName))
|
|| await container.IsExistsAsync(NewContainerName))
|
||||||
{
|
{
|
||||||
return CanCommandRun.False;
|
return CanCommandRun.False;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,10 +24,10 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
public async Task Execute(TimeRunner timeRunner)
|
public async Task Execute(TimeRunner timeRunner)
|
||||||
{
|
{
|
||||||
var possibleContainer = await Container.Resolve();
|
var possibleContainer = await Container.ResolveAsync();
|
||||||
if (possibleContainer is IContainer container)
|
if (possibleContainer is IContainer container)
|
||||||
{
|
{
|
||||||
await container.CreateElement(NewElementName);
|
await container.CreateElementAsync(NewElementName);
|
||||||
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
|
await timeRunner.RefreshContainer.InvokeAsync(this, new AbsolutePath(container));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,18 +36,18 @@ namespace FileTime.Core.Command
|
|||||||
{
|
{
|
||||||
var newDifferences = new List<Difference>()
|
var newDifferences = new List<Difference>()
|
||||||
{
|
{
|
||||||
new Difference(DifferenceItemType.Element, DifferenceActionType.Create, new AbsolutePath(Container.ContentProvider, Container.Path + Constants.SeparatorChar + NewElementName, Container.VirtualContentProvider))
|
new Difference(DifferenceItemType.Element, DifferenceActionType.Create, Container.GetChild(NewElementName, AbsolutePathType.Element))
|
||||||
};
|
};
|
||||||
return Task.FromResult(startPoint.WithDifferences(newDifferences));
|
return Task.FromResult(startPoint.WithDifferences(newDifferences));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
|
public async Task<CanCommandRun> CanRun(PointInTime startPoint)
|
||||||
{
|
{
|
||||||
var resolvedContainer = Container.Resolve();
|
var resolvedContainer = Container.ResolveAsync();
|
||||||
if (resolvedContainer == null) return CanCommandRun.Forceable;
|
if (resolvedContainer == null) return CanCommandRun.Forceable;
|
||||||
|
|
||||||
if (resolvedContainer is not IContainer container
|
if (resolvedContainer is not IContainer container
|
||||||
|| await container.IsExists(NewElementName))
|
|| await container.IsExistsAsync(NewElementName))
|
||||||
{
|
{
|
||||||
return CanCommandRun.False;
|
return CanCommandRun.False;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
foreach (var item in ItemsToDelete)
|
foreach (var item in ItemsToDelete)
|
||||||
{
|
{
|
||||||
await TraverseTree((await item.Resolve())!);
|
await TraverseTree((await item.ResolveAsync())!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return startPoint.WithDifferences(newDifferences);
|
return startPoint.WithDifferences(newDifferences);
|
||||||
@@ -64,7 +64,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
foreach (var item in ItemsToDelete)
|
foreach (var item in ItemsToDelete)
|
||||||
{
|
{
|
||||||
await TraverseTree((await item.Resolve())!);
|
await TraverseTree((await item.ResolveAsync())!);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,7 +97,7 @@ namespace FileTime.Core.Command
|
|||||||
var result = CanCommandRun.True;
|
var result = CanCommandRun.True;
|
||||||
foreach (var itemPath in ItemsToDelete)
|
foreach (var itemPath in ItemsToDelete)
|
||||||
{
|
{
|
||||||
var resolvedItem = await itemPath.Resolve();
|
var resolvedItem = await itemPath.ResolveAsync();
|
||||||
if (resolvedItem != null
|
if (resolvedItem != null
|
||||||
&& (
|
&& (
|
||||||
resolvedItem.CanDelete == SupportsDelete.False
|
resolvedItem.CanDelete == SupportsDelete.False
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
public async Task Execute(TimeRunner timeRunner)
|
public async Task Execute(TimeRunner timeRunner)
|
||||||
{
|
{
|
||||||
var itemToRename = await Source.Resolve();
|
var itemToRename = await Source.ResolveAsync();
|
||||||
if (itemToRename != null)
|
if (itemToRename != null)
|
||||||
{
|
{
|
||||||
await itemToRename.Rename(Target);
|
await itemToRename.Rename(Target);
|
||||||
@@ -34,7 +34,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
|
public async Task<PointInTime> SimulateCommand(PointInTime startPoint)
|
||||||
{
|
{
|
||||||
var item = await Source.Resolve();
|
var item = await Source.ResolveAsync();
|
||||||
if (item == null) throw new FileNotFoundException();
|
if (item == null) throw new FileNotFoundException();
|
||||||
var newDifferences = new List<Difference>()
|
var newDifferences = new List<Difference>()
|
||||||
{
|
{
|
||||||
@@ -50,7 +50,7 @@ namespace FileTime.Core.Command
|
|||||||
|
|
||||||
public Task<CanCommandRun> CanRun(PointInTime startPoint)
|
public Task<CanCommandRun> CanRun(PointInTime startPoint)
|
||||||
{
|
{
|
||||||
return Task.FromResult(Source.Resolve() != null ? CanCommandRun.True : CanCommandRun.False);
|
return Task.FromResult(Source.ResolveAsync() != null ? CanCommandRun.True : CanCommandRun.False);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.CommandHandlers
|
||||||
|
{
|
||||||
|
public class StreamCopyCommandHandler : ICommandHandler
|
||||||
|
{
|
||||||
|
public bool CanHandle(object command)
|
||||||
|
{
|
||||||
|
if (command is not CopyCommand copyCommand) return false;
|
||||||
|
|
||||||
|
return (copyCommand.Target?.Provider.SupportsContentStreams ?? false)
|
||||||
|
&& copyCommand.Sources.All(p => p.ContentProvider.SupportsContentStreams);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(object command, TimeRunner timeRunner)
|
||||||
|
{
|
||||||
|
if (command is not CopyCommand copyCommand) throw new ArgumentException($"Can not execute command of type '{command.GetType()}'.");
|
||||||
|
|
||||||
|
await copyCommand.Execute(CopyElement, timeRunner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, OperationProgress? operationProgress, CopyCommandContext copyCommandContext)
|
||||||
|
{
|
||||||
|
var parent = (IContainer?)(await targetPath.GetParent().ResolveAsync())!;
|
||||||
|
var elementName = targetPath.GetName();
|
||||||
|
if (!await parent.IsExistsAsync(elementName))
|
||||||
|
{
|
||||||
|
await parent.CreateElementAsync(elementName);
|
||||||
|
}
|
||||||
|
|
||||||
|
var source = (IElement?)(await sourcePath.ResolveAsync())!;
|
||||||
|
var target = (IElement?)(await targetPath.ResolveAsync())!;
|
||||||
|
|
||||||
|
using var reader = await source.GetContentReaderAsync();
|
||||||
|
using var writer = await target.GetContentWriterAsync();
|
||||||
|
|
||||||
|
byte[] dataRead;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
dataRead = await reader.ReadBytesAsync(writer.PreferredBufferSize);
|
||||||
|
await writer.WriteBytesAsync(dataRead);
|
||||||
|
await writer.FlushAsync();
|
||||||
|
if (operationProgress != null) operationProgress.Progress += dataRead.LongLength;
|
||||||
|
await copyCommandContext.UpdateProgress();
|
||||||
|
}
|
||||||
|
while (dataRead.Length > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,19 +9,22 @@ namespace FileTime.Core.Models
|
|||||||
public IContentProvider? VirtualContentProvider { get; }
|
public IContentProvider? VirtualContentProvider { get; }
|
||||||
|
|
||||||
public string Path { get; }
|
public string Path { get; }
|
||||||
|
public AbsolutePathType Type { get; }
|
||||||
|
|
||||||
public AbsolutePath(AbsolutePath from)
|
public AbsolutePath(AbsolutePath from)
|
||||||
{
|
{
|
||||||
ContentProvider = from.ContentProvider;
|
ContentProvider = from.ContentProvider;
|
||||||
Path = from.Path;
|
Path = from.Path;
|
||||||
VirtualContentProvider = from.VirtualContentProvider;
|
VirtualContentProvider = from.VirtualContentProvider;
|
||||||
|
Type = from.Type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbsolutePath(IContentProvider contentProvider, string path, IContentProvider? virtualContentProvider)
|
public AbsolutePath(IContentProvider contentProvider, string path, AbsolutePathType type, IContentProvider? virtualContentProvider)
|
||||||
{
|
{
|
||||||
ContentProvider = contentProvider;
|
ContentProvider = contentProvider;
|
||||||
Path = path;
|
Path = path;
|
||||||
VirtualContentProvider = virtualContentProvider;
|
VirtualContentProvider = virtualContentProvider;
|
||||||
|
Type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbsolutePath(IItem item)
|
public AbsolutePath(IItem item)
|
||||||
@@ -43,33 +46,39 @@ namespace FileTime.Core.Models
|
|||||||
ContentProvider = item.Provider;
|
ContentProvider = item.Provider;
|
||||||
Path = item.FullName!;
|
Path = item.FullName!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Type = item switch
|
||||||
|
{
|
||||||
|
IContainer => AbsolutePathType.Container,
|
||||||
|
IElement => AbsolutePathType.Element,
|
||||||
|
_ => AbsolutePathType.Unknown
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AbsolutePath FromParentAndChildName(IContainer parent, string childName)
|
public static AbsolutePath FromParentAndChildName(IContainer parent, string childName, AbsolutePathType childType)
|
||||||
{
|
{
|
||||||
IContentProvider? contentProvider;
|
var contentProvider = parent.Provider;
|
||||||
IContentProvider? virtualContentProvider;
|
var path = parent.FullName! + Constants.SeparatorChar + childName;
|
||||||
string? path;
|
|
||||||
|
|
||||||
if (parent is TimeContainer timeContainer)
|
var virtualContentProvider = parent switch
|
||||||
{
|
{
|
||||||
contentProvider = timeContainer.Provider;
|
TimeContainer timeContainer => timeContainer.VirtualProvider,
|
||||||
virtualContentProvider = timeContainer.VirtualProvider;
|
_ => null
|
||||||
path = timeContainer.FullName! + Constants.SeparatorChar + childName;
|
};
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
contentProvider = parent.Provider;
|
|
||||||
path = parent.FullName! + Constants.SeparatorChar + childName;
|
|
||||||
virtualContentProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new AbsolutePath(contentProvider, path, virtualContentProvider);
|
return new AbsolutePath(contentProvider, path, childType, virtualContentProvider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IItem?> Resolve()
|
public AbsolutePath GetChild(string childName, AbsolutePathType childType)
|
||||||
{
|
{
|
||||||
var result = VirtualContentProvider != null && (await VirtualContentProvider.IsExists(Path))
|
var path = Path + Constants.SeparatorChar + childName;
|
||||||
|
|
||||||
|
return new AbsolutePath(ContentProvider, path, childType, VirtualContentProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IItem?> ResolveAsync()
|
||||||
|
{
|
||||||
|
var result = VirtualContentProvider != null && (await VirtualContentProvider.IsExistsAsync(Path))
|
||||||
? await VirtualContentProvider.GetByPath(Path)
|
? await VirtualContentProvider.GetByPath(Path)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
@@ -78,13 +87,13 @@ namespace FileTime.Core.Models
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetParent()
|
public string GetParentPath()
|
||||||
{
|
{
|
||||||
var pathParts = Path.Split(Constants.SeparatorChar);
|
var pathParts = Path.Split(Constants.SeparatorChar);
|
||||||
return string.Join(Constants.SeparatorChar, pathParts[..^1]);
|
return string.Join(Constants.SeparatorChar, pathParts[..^1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbsolutePath GetParentAsAbsolutePath() => new(ContentProvider, GetParent(), VirtualContentProvider);
|
public AbsolutePath GetParent() => new(ContentProvider, GetParentPath(), AbsolutePathType.Container, VirtualContentProvider);
|
||||||
|
|
||||||
public string GetName() => Path.Split(Constants.SeparatorChar).Last();
|
public string GetName() => Path.Split(Constants.SeparatorChar).Last();
|
||||||
|
|
||||||
|
|||||||
9
src/Core/FileTime.Core/Models/AbsolutePathType.cs
Normal file
9
src/Core/FileTime.Core/Models/AbsolutePathType.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Core.Models
|
||||||
|
{
|
||||||
|
public enum AbsolutePathType
|
||||||
|
{
|
||||||
|
Unknown,
|
||||||
|
Container,
|
||||||
|
Element
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,13 +30,13 @@ namespace FileTime.Core.Models
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Task<IContainer> CreateContainer(string name);
|
Task<IContainer> CreateContainerAsync(string name);
|
||||||
Task<IElement> CreateElement(string name);
|
Task<IElement> CreateElementAsync(string name);
|
||||||
|
|
||||||
Task<bool> IsExists(string name);
|
Task<bool> IsExistsAsync(string name);
|
||||||
|
|
||||||
Task<IContainer> Clone();
|
Task<IContainer> CloneAsync();
|
||||||
Task<bool> CanOpen();
|
Task<bool> CanOpenAsync();
|
||||||
void Unload();
|
void Unload();
|
||||||
|
|
||||||
bool IsLoaded { get; }
|
bool IsLoaded { get; }
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using FileTime.Core.Providers;
|
||||||
|
|
||||||
namespace FileTime.Core.Models
|
namespace FileTime.Core.Models
|
||||||
{
|
{
|
||||||
public interface IElement : IItem
|
public interface IElement : IItem
|
||||||
@@ -6,5 +8,8 @@ namespace FileTime.Core.Models
|
|||||||
string GetPrimaryAttributeText();
|
string GetPrimaryAttributeText();
|
||||||
Task<string> GetContent(CancellationToken token = default);
|
Task<string> GetContent(CancellationToken token = default);
|
||||||
Task<long> GetElementSize(CancellationToken token = default);
|
Task<long> GetElementSize(CancellationToken token = default);
|
||||||
|
|
||||||
|
Task<IContentReader> GetContentReaderAsync();
|
||||||
|
Task<IContentWriter> GetContentWriterAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,9 +139,9 @@ namespace FileTime.Core.Models
|
|||||||
: baseContainer;
|
: baseContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IContainer> CreateContainer(string name) => await BaseContainer.CreateContainer(name);
|
public async Task<IContainer> CreateContainerAsync(string name) => await BaseContainer.CreateContainerAsync(name);
|
||||||
public async Task<IElement> CreateElement(string name) => await BaseContainer.CreateElement(name);
|
public async Task<IElement> CreateElementAsync(string name) => await BaseContainer.CreateElementAsync(name);
|
||||||
public async Task<bool> IsExists(string name) => await BaseContainer.IsExists(name);
|
public async Task<bool> IsExistsAsync(string name) => await BaseContainer.IsExistsAsync(name);
|
||||||
|
|
||||||
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@@ -157,10 +157,10 @@ namespace FileTime.Core.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task Delete(bool hardDelete = false) => await BaseContainer.Delete();
|
public async Task Delete(bool hardDelete = false) => await BaseContainer.Delete();
|
||||||
public async Task<IContainer> Clone()
|
public async Task<IContainer> CloneAsync()
|
||||||
{
|
{
|
||||||
return new VirtualContainer(
|
return new VirtualContainer(
|
||||||
await BaseContainer.Clone(),
|
await BaseContainer.CloneAsync(),
|
||||||
_containerTransformators,
|
_containerTransformators,
|
||||||
_elementTransformators,
|
_elementTransformators,
|
||||||
IsPermanent,
|
IsPermanent,
|
||||||
@@ -170,7 +170,7 @@ namespace FileTime.Core.Models
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async Task Rename(string newName) => await BaseContainer.Rename(newName);
|
public async Task Rename(string newName) => await BaseContainer.Rename(newName);
|
||||||
public async Task<bool> CanOpen() => await BaseContainer.CanOpen();
|
public async Task<bool> CanOpenAsync() => await BaseContainer.CanOpenAsync();
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace FileTime.Core.Providers
|
|||||||
{
|
{
|
||||||
public interface IContentProvider : IContainer
|
public interface IContentProvider : IContainer
|
||||||
{
|
{
|
||||||
|
bool SupportsContentStreams { get; }
|
||||||
|
|
||||||
Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default);
|
Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default);
|
||||||
|
|
||||||
bool CanHandlePath(string path);
|
bool CanHandlePath(string path);
|
||||||
|
|||||||
9
src/Core/FileTime.Core/Providers/IContentReader.cs
Normal file
9
src/Core/FileTime.Core/Providers/IContentReader.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Core.Providers
|
||||||
|
{
|
||||||
|
public interface IContentReader : IDisposable
|
||||||
|
{
|
||||||
|
int PreferredBufferSize { get; }
|
||||||
|
|
||||||
|
Task<byte[]> ReadBytesAsync(int bufferSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/Core/FileTime.Core/Providers/IContentWriter.cs
Normal file
10
src/Core/FileTime.Core/Providers/IContentWriter.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace FileTime.Core.Providers
|
||||||
|
{
|
||||||
|
public interface IContentWriter : IDisposable
|
||||||
|
{
|
||||||
|
int PreferredBufferSize { get; }
|
||||||
|
|
||||||
|
Task WriteBytesAsync(byte[] data);
|
||||||
|
Task FlushAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,9 +48,9 @@ namespace FileTime.Core.Providers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name) => throw new NotImplementedException();
|
public Task<IContainer> CreateContainerAsync(string name) => throw new NotImplementedException();
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name) => throw new NotImplementedException();
|
public Task<IElement> CreateElementAsync(string name) => throw new NotImplementedException();
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false) => throw new NotImplementedException();
|
public Task Delete(bool hardDelete = false) => throw new NotImplementedException();
|
||||||
|
|
||||||
@@ -58,7 +58,7 @@ namespace FileTime.Core.Providers
|
|||||||
|
|
||||||
public IContainer? GetParent() => null;
|
public IContainer? GetParent() => null;
|
||||||
|
|
||||||
public Task<bool> IsExists(string name) => throw new NotImplementedException();
|
public Task<bool> IsExistsAsync(string name) => throw new NotImplementedException();
|
||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
@@ -66,11 +66,11 @@ namespace FileTime.Core.Providers
|
|||||||
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers);
|
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult(_containers);
|
||||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
|
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
|
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() { }
|
public void Destroy() { }
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ namespace FileTime.Core.Timeline
|
|||||||
return new Difference(
|
return new Difference(
|
||||||
Type,
|
Type,
|
||||||
Action,
|
Action,
|
||||||
new AbsolutePath(AbsolutePath.ContentProvider, AbsolutePath.Path, virtualContentProvider)
|
new AbsolutePath(AbsolutePath.ContentProvider, AbsolutePath.Path, AbsolutePath.Type, virtualContentProvider)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ namespace FileTime.Core.Timeline
|
|||||||
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IContainer> Clone() => new TimeContainer(Name, await _parent!.Clone(), Provider, VirtualProvider, _pointInTime);
|
public async Task<IContainer> CloneAsync() => new TimeContainer(Name, await _parent!.CloneAsync(), Provider, VirtualProvider, _pointInTime);
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name) => Task.FromResult((IContainer)new TimeContainer(name, this, Provider, VirtualProvider, _pointInTime));
|
public Task<IContainer> CreateContainerAsync(string name) => Task.FromResult((IContainer)new TimeContainer(name, this, Provider, VirtualProvider, _pointInTime));
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name) => Task.FromResult((IElement)new TimeElement(name, this, Provider, VirtualProvider));
|
public Task<IElement> CreateElementAsync(string name) => Task.FromResult((IElement)new TimeElement(name, this, Provider, VirtualProvider));
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
public Task Delete(bool hardDelete = false) => Task.CompletedTask;
|
||||||
|
|
||||||
@@ -106,7 +106,7 @@ namespace FileTime.Core.Timeline
|
|||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
|
public async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
|
||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ namespace FileTime.Core.Timeline
|
|||||||
if (elementDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{elementDiff}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Element}.");
|
if (elementDiff.Type != DifferenceItemType.Container) throw new ArgumentException($"{elementDiff}'s {nameof(Difference.Type)} property is not {DifferenceItemType.Element}.");
|
||||||
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
|
return new TimeElement(elementDiff.Name, this, Provider, elementDiff.AbsolutePath.VirtualContentProvider ?? elementDiff.AbsolutePath.ContentProvider);
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() => IsDestroyed = true;
|
public void Destroy() => IsDestroyed = true;
|
||||||
public void Unload() { }
|
public void Unload() { }
|
||||||
|
|||||||
@@ -48,5 +48,9 @@ namespace FileTime.Core.Timeline
|
|||||||
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
||||||
|
|
||||||
public void Destroy() => IsDestroyed = true;
|
public void Destroy() => IsDestroyed = true;
|
||||||
|
|
||||||
|
public Task<IContentReader> GetContentReaderAsync() => throw new NotSupportedException();
|
||||||
|
|
||||||
|
public Task<IContentWriter> GetContentWriterAsync() => throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -30,6 +30,7 @@ namespace FileTime.Core.Timeline
|
|||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
|
public bool SupportsContentStreams => false;
|
||||||
|
|
||||||
public TimeProvider(PointInTime pointInTime)
|
public TimeProvider(PointInTime pointInTime)
|
||||||
{
|
{
|
||||||
@@ -41,14 +42,14 @@ namespace FileTime.Core.Timeline
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name)
|
public Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name)
|
public Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -82,7 +83,7 @@ namespace FileTime.Core.Timeline
|
|||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> IsExists(string name)
|
public Task<bool> IsExistsAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -92,10 +93,20 @@ namespace FileTime.Core.Timeline
|
|||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
|
|
||||||
public void SetParent(IContainer container) { }
|
public void SetParent(IContainer container) { }
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() { }
|
public void Destroy() { }
|
||||||
|
|
||||||
public void Unload() { }
|
public void Unload() { }
|
||||||
|
|
||||||
|
public Task<IContentReader> GetContentReaderAsync(IElement element)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IContentWriter> GetContentWriterAsync(IElement element)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +139,10 @@ namespace FileTime.Core.Timeline
|
|||||||
_commandExecutor.ExecuteCommandAsync(commandToRun.Command, this).Wait();
|
_commandExecutor.ExecuteCommandAsync(commandToRun.Command, this).Wait();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogError(e, "Error while running command: {CommandType} ({Command}).", commandToRun?.Command.GetType().Name, commandToRun?.Command.DisplayLabel);
|
||||||
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
DisposeCommandThread(Thread.CurrentThread, commandToRun).Wait();
|
DisposeCommandThread(Thread.CurrentThread, commandToRun).Wait();
|
||||||
@@ -151,11 +155,11 @@ namespace FileTime.Core.Timeline
|
|||||||
{
|
{
|
||||||
if (command != null)
|
if (command != null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Command finished running: {0}", command.Command.DisplayLabel);
|
_logger.LogDebug("Command finished running: {Command}", command.Command.DisplayLabel);
|
||||||
_commandsToRun[0].Remove(command);
|
_commandsToRun[0].Remove(command);
|
||||||
if (_commandsToRun[0].Commands.Count == 0)
|
if (_commandsToRun[0].Commands.Count == 0)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Removing empty command array. {0} batch left.", _commandsToRun.Count - 1);
|
_logger.LogDebug("Removing empty command array. {RemainingBatchNumber} batch left.", _commandsToRun.Count - 1);
|
||||||
_commandsToRun.RemoveAt(0);
|
_commandsToRun.RemoveAt(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
var currentLocation = await Tab.GetCurrentLocation();
|
var currentLocation = await Tab.GetCurrentLocation();
|
||||||
if (currentLocation != null)
|
if (currentLocation != null)
|
||||||
{
|
{
|
||||||
await currentLocation.CreateContainer(name);
|
await currentLocation.CreateContainerAsync(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -376,7 +376,7 @@ namespace FileTime.Avalonia.Application
|
|||||||
var currentLocation = await Tab.GetCurrentLocation();
|
var currentLocation = await Tab.GetCurrentLocation();
|
||||||
if (currentLocation != null)
|
if (currentLocation != null)
|
||||||
{
|
{
|
||||||
await currentLocation.CreateElement(name);
|
await currentLocation.CreateElementAsync(name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ namespace FileTime.Avalonia.Services
|
|||||||
}
|
}
|
||||||
else if (tabContainer == null)
|
else if (tabContainer == null)
|
||||||
{
|
{
|
||||||
var newContainer = await _appState.SelectedTab.CurrentLocation.Container.Clone();
|
var newContainer = await _appState.SelectedTab.CurrentLocation.Container.CloneAsync();
|
||||||
|
|
||||||
var newTab = new Tab();
|
var newTab = new Tab();
|
||||||
await newTab.Init(newContainer);
|
await newTab.Init(newContainer);
|
||||||
@@ -351,7 +351,7 @@ namespace FileTime.Avalonia.Services
|
|||||||
//FIXME: check 'is Container'
|
//FIXME: check 'is Container'
|
||||||
if (currentSelectedItems.Count == 1)
|
if (currentSelectedItems.Count == 1)
|
||||||
{
|
{
|
||||||
if ((await currentSelectedItems[0].Resolve()) is IContainer container
|
if ((await currentSelectedItems[0].ResolveAsync()) is IContainer container
|
||||||
&& (await container.GetItems())?.Count > 0)
|
&& (await container.GetItems())?.Count > 0)
|
||||||
{
|
{
|
||||||
askForDelete = true;
|
askForDelete = true;
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
using FileTime.Core.Command;
|
|
||||||
using FileTime.Core.Models;
|
|
||||||
using FileTime.Core.Timeline;
|
|
||||||
|
|
||||||
namespace FileTime.Providers.Local.CommandHandlers
|
|
||||||
{
|
|
||||||
public class CopyCommandHandler : ICommandHandler
|
|
||||||
{
|
|
||||||
public bool CanHandle(object command)
|
|
||||||
{
|
|
||||||
if (command is not CopyCommand copyCommand) return false;
|
|
||||||
|
|
||||||
if (copyCommand.Target != null && copyCommand.Target is not LocalFolder) return false;
|
|
||||||
|
|
||||||
if (copyCommand.Sources.Any(s => s.ContentProvider is not LocalContentProvider)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ExecuteAsync(object command, TimeRunner timeRunner)
|
|
||||||
{
|
|
||||||
if (command is not CopyCommand copyCommand) throw new ArgumentException($"Can not execute command of type '{command.GetType()}'.");
|
|
||||||
|
|
||||||
await copyCommand.Execute(CopyElement, timeRunner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async Task CopyElement(AbsolutePath sourcePath, AbsolutePath targetPath, OperationProgress? operationProgress, CopyCommandContext copyCommandContext)
|
|
||||||
{
|
|
||||||
using var sourceStream = File.OpenRead(sourcePath.Path);
|
|
||||||
using var sourceReader = new BinaryReader(sourceStream);
|
|
||||||
|
|
||||||
using var targetStream = File.OpenWrite(targetPath.Path);
|
|
||||||
using var targetWriter = new BinaryWriter(targetStream);
|
|
||||||
|
|
||||||
const int bufferSize = 1024 * 1024;
|
|
||||||
byte[] dataRead;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
dataRead = sourceReader.ReadBytes(bufferSize);
|
|
||||||
targetWriter.Write(dataRead);
|
|
||||||
targetWriter.Flush();
|
|
||||||
if (operationProgress != null) operationProgress.Progress += dataRead.LongLength;
|
|
||||||
await copyCommandContext.UpdateProgress();
|
|
||||||
}
|
|
||||||
while (dataRead.Length > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using AsyncEvent;
|
using AsyncEvent;
|
||||||
@@ -35,6 +36,7 @@ namespace FileTime.Providers.Local
|
|||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
|
public bool SupportsContentStreams => true;
|
||||||
|
|
||||||
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
||||||
{
|
{
|
||||||
@@ -64,7 +66,7 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
if (rootContainer == null)
|
if (rootContainer == null)
|
||||||
{
|
{
|
||||||
_logger.LogWarning("No root container found with name '{0}'", path[0]);
|
_logger.LogWarning("No root container found with name '{RootContainerName}'", path[0]);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,12 +76,12 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
public Task<IContainer> CreateContainer(string name) => throw new NotSupportedException();
|
public Task<IContainer> CreateContainerAsync(string name) => throw new NotSupportedException();
|
||||||
public Task<IElement> CreateElement(string name) => throw new NotSupportedException();
|
public Task<IElement> CreateElementAsync(string name) => throw new NotSupportedException();
|
||||||
public Task<bool> IsExists(string name) => Task.FromResult(_rootContainers.Any(i => i.Name == name));
|
public Task<bool> IsExistsAsync(string name) => Task.FromResult(_rootContainers.Any(i => i.Name == name));
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false) => throw new NotSupportedException();
|
public Task Delete(bool hardDelete = false) => throw new NotSupportedException();
|
||||||
|
|
||||||
@@ -102,7 +104,7 @@ namespace FileTime.Providers.Local
|
|||||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
|
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
|
|||||||
50
src/Providers/FileTime.Providers.Local/LocalContentReader.cs
Normal file
50
src/Providers/FileTime.Providers.Local/LocalContentReader.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
using FileTime.Core.Providers;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Local
|
||||||
|
{
|
||||||
|
public class LocalContentReader : IContentReader
|
||||||
|
{
|
||||||
|
private readonly FileStream _readerStream;
|
||||||
|
private readonly BinaryReader _binaryReader;
|
||||||
|
private bool disposed;
|
||||||
|
|
||||||
|
public int PreferredBufferSize => 1024 * 1024;
|
||||||
|
|
||||||
|
public LocalContentReader(FileStream readerStream)
|
||||||
|
{
|
||||||
|
_readerStream = readerStream;
|
||||||
|
_binaryReader = new BinaryReader(_readerStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<byte[]> ReadBytesAsync(int bufferSize)
|
||||||
|
{
|
||||||
|
var max = bufferSize > 0 && bufferSize < PreferredBufferSize ? bufferSize : PreferredBufferSize;
|
||||||
|
|
||||||
|
return Task.FromResult(_binaryReader.ReadBytes(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
~LocalContentReader()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_readerStream.Dispose();
|
||||||
|
_binaryReader.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Providers/FileTime.Providers.Local/LocalContentWriter.cs
Normal file
54
src/Providers/FileTime.Providers.Local/LocalContentWriter.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using FileTime.Core.Providers;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Local
|
||||||
|
{
|
||||||
|
public class LocalContentWriter : IContentWriter
|
||||||
|
{
|
||||||
|
private readonly FileStream _writerStream;
|
||||||
|
private readonly BinaryWriter _binaryWriter;
|
||||||
|
private bool disposed;
|
||||||
|
public int PreferredBufferSize => 1024 * 1024;
|
||||||
|
|
||||||
|
public LocalContentWriter(FileStream writerStream)
|
||||||
|
{
|
||||||
|
_writerStream = writerStream;
|
||||||
|
_binaryWriter = new BinaryWriter(_writerStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WriteBytesAsync(byte[] data)
|
||||||
|
{
|
||||||
|
_binaryWriter.Write(data);
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task FlushAsync()
|
||||||
|
{
|
||||||
|
_binaryWriter.Flush();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
~LocalContentWriter()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_writerStream.Dispose();
|
||||||
|
_binaryWriter.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,5 +88,10 @@ namespace FileTime.Providers.Local
|
|||||||
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(File.Length);
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(File.Length);
|
||||||
|
|
||||||
public void Destroy() => IsDestroyed = true;
|
public void Destroy() => IsDestroyed = true;
|
||||||
|
|
||||||
|
public Task<IContentReader> GetContentReaderAsync() =>
|
||||||
|
Task.FromResult((IContentReader)new LocalContentReader(System.IO.File.OpenRead(File.FullName)));
|
||||||
|
public Task<IContentWriter> GetContentWriterAsync() =>
|
||||||
|
Task.FromResult((IContentWriter)new LocalContentWriter(System.IO.File.OpenWrite(File.FullName)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +60,7 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent));
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)new LocalFolder(Directory, Provider, _parent));
|
||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default)
|
public async Task RefreshAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
@@ -131,7 +131,7 @@ namespace FileTime.Providers.Local
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
public async Task<IContainer> CreateContainer(string name)
|
public async Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
Directory.CreateSubdirectory(name);
|
Directory.CreateSubdirectory(name);
|
||||||
await RefreshAsync();
|
await RefreshAsync();
|
||||||
@@ -139,7 +139,7 @@ namespace FileTime.Providers.Local
|
|||||||
return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!;
|
return _containers!.FirstOrDefault(c => Provider.NormalizePath(c.Name) == Provider.NormalizePath(name))!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IElement> CreateElement(string name)
|
public async Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
using (File.Create(Path.Combine(Directory.FullName, name))) { }
|
using (File.Create(Path.Combine(Directory.FullName, name))) { }
|
||||||
await RefreshAsync();
|
await RefreshAsync();
|
||||||
@@ -147,7 +147,7 @@ namespace FileTime.Providers.Local
|
|||||||
return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!;
|
return _elements!.FirstOrDefault(e => Provider.NormalizePath(e.Name) == Provider.NormalizePath(name))!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false;
|
public async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => Provider.NormalizePath(i.Name) == Provider.NormalizePath(name)) ?? false;
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false)
|
public Task Delete(bool hardDelete = false)
|
||||||
{
|
{
|
||||||
@@ -185,7 +185,7 @@ namespace FileTime.Providers.Local
|
|||||||
+ ((Directory.Attributes & FileAttributes.System) == FileAttributes.System ? "s" : "-");
|
+ ((Directory.Attributes & FileAttributes.System) == FileAttributes.System ? "s" : "-");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy()
|
public void Destroy()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
using FileTime.Providers.Local.CommandHandlers;
|
|
||||||
|
|
||||||
namespace FileTime.Providers.Local
|
namespace FileTime.Providers.Local
|
||||||
{
|
{
|
||||||
public static class Startup
|
public static class Startup
|
||||||
{
|
{
|
||||||
public static Type[] GetCommandHandlers()
|
public static Type[] GetCommandHandlers()
|
||||||
{
|
{
|
||||||
return new Type[]{
|
return Array.Empty<Type>();
|
||||||
typeof(CopyCommandHandler)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ namespace FileTime.Providers.Smb
|
|||||||
{
|
{
|
||||||
public class SmbContentProvider : IContentProvider
|
public class SmbContentProvider : IContentProvider
|
||||||
{
|
{
|
||||||
private readonly object _initializationGuard = new object();
|
private readonly object _initializationGuard = new();
|
||||||
private bool _initialized;
|
private bool _initialized;
|
||||||
private bool _initializing;
|
private bool _initializing;
|
||||||
private IContainer? _parent;
|
private IContainer? _parent;
|
||||||
@@ -39,6 +39,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||||
|
|
||||||
public bool IsDestroyed => false;
|
public bool IsDestroyed => false;
|
||||||
|
public bool SupportsContentStreams => true;
|
||||||
|
|
||||||
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
|
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
|
||||||
{
|
{
|
||||||
@@ -50,7 +51,7 @@ namespace FileTime.Providers.Smb
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IContainer> CreateContainer(string name)
|
public async Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
var fullName = "\\\\" + name;
|
var fullName = "\\\\" + name;
|
||||||
var container = _rootContainers.Find(c => c.Name == name);
|
var container = _rootContainers.Find(c => c.Name == name);
|
||||||
@@ -69,7 +70,7 @@ namespace FileTime.Providers.Smb
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name)
|
public Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
@@ -98,9 +99,9 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public async Task<bool> IsExists(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
|
public async Task<bool> IsExistsAsync(string name) => (await GetItems())?.Any(i => i.Name == name) ?? false;
|
||||||
|
|
||||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
|
|
||||||
@@ -124,7 +125,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IElement>?)_elements);
|
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IElement>?)_elements);
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() { }
|
public void Destroy() { }
|
||||||
|
|
||||||
|
|||||||
67
src/Providers/FileTime.Providers.Smb/SmbContentReader.cs
Normal file
67
src/Providers/FileTime.Providers.Smb/SmbContentReader.cs
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
using FileTime.Core.Providers;
|
||||||
|
using SMBLibrary;
|
||||||
|
using SMBLibrary.Client;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Smb
|
||||||
|
{
|
||||||
|
public class SmbContentReader : IContentReader
|
||||||
|
{
|
||||||
|
private readonly ISMBFileStore _smbFileStore;
|
||||||
|
private readonly object _fileHandle;
|
||||||
|
private readonly ISMBClient _client;
|
||||||
|
private bool disposed;
|
||||||
|
private long _bytesRead;
|
||||||
|
|
||||||
|
public int PreferredBufferSize => (int)_client.MaxReadSize;
|
||||||
|
|
||||||
|
public SmbContentReader(ISMBFileStore smbFileStore, object fileHandle, ISMBClient client)
|
||||||
|
{
|
||||||
|
_smbFileStore = smbFileStore;
|
||||||
|
_fileHandle = fileHandle;
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<byte[]> ReadBytesAsync(int bufferSize)
|
||||||
|
{
|
||||||
|
var max = bufferSize > 0 && bufferSize < (int)_client.MaxReadSize ? bufferSize : (int)_client.MaxReadSize;
|
||||||
|
|
||||||
|
var status = _smbFileStore.ReadFile(out byte[] data, _fileHandle, _bytesRead, max);
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_END_OF_FILE)
|
||||||
|
{
|
||||||
|
throw new Exception("Failed to read from file");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status == NTStatus.STATUS_END_OF_FILE || data.Length == 0)
|
||||||
|
{
|
||||||
|
return Task.FromResult(Array.Empty<byte>());
|
||||||
|
}
|
||||||
|
_bytesRead += data.Length;
|
||||||
|
|
||||||
|
return Task.FromResult(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
~SmbContentReader()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!disposed)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_smbFileStore.CloseFile(_fileHandle);
|
||||||
|
_smbFileStore.Disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
disposed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Providers/FileTime.Providers.Smb/SmbContentWriter.cs
Normal file
24
src/Providers/FileTime.Providers.Smb/SmbContentWriter.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using FileTime.Core.Providers;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Smb
|
||||||
|
{
|
||||||
|
public class SmbContentWriter : IContentWriter
|
||||||
|
{
|
||||||
|
public int PreferredBufferSize => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task FlushAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task WriteBytesAsync(byte[] data)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
using FileTime.Core.Models;
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Providers;
|
using FileTime.Core.Providers;
|
||||||
|
using SMBLibrary;
|
||||||
|
|
||||||
namespace FileTime.Providers.Smb
|
namespace FileTime.Providers.Smb
|
||||||
{
|
{
|
||||||
public class SmbFile : IElement
|
public class SmbFile : IElement
|
||||||
{
|
{
|
||||||
|
private readonly IContainer _parent;
|
||||||
|
private readonly SmbClientContext _smbClientContext;
|
||||||
|
private readonly SmbShare _smbShare;
|
||||||
|
|
||||||
public bool IsSpecial => false;
|
public bool IsSpecial => false;
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
@@ -17,11 +22,10 @@ namespace FileTime.Providers.Smb
|
|||||||
public bool CanRename => true;
|
public bool CanRename => true;
|
||||||
|
|
||||||
public IContentProvider Provider { get; }
|
public IContentProvider Provider { get; }
|
||||||
private IContainer _parent;
|
|
||||||
|
|
||||||
public bool IsDestroyed { get; private set; }
|
public bool IsDestroyed { get; private set; }
|
||||||
|
|
||||||
public SmbFile(string name, SmbContentProvider provider, IContainer parent)
|
public SmbFile(string name, SmbContentProvider provider, SmbShare smbShare, IContainer parent, SmbClientContext smbClientContext)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
FullName = parent.FullName + Constants.SeparatorChar + Name;
|
FullName = parent.FullName + Constants.SeparatorChar + Name;
|
||||||
@@ -29,6 +33,8 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
Provider = provider;
|
Provider = provider;
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
|
_smbClientContext = smbClientContext;
|
||||||
|
_smbShare = smbShare;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Delete(bool hardDelete = false)
|
public Task Delete(bool hardDelete = false)
|
||||||
@@ -50,5 +56,35 @@ namespace FileTime.Providers.Smb
|
|||||||
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
public Task<long> GetElementSize(CancellationToken token = default) => Task.FromResult(-1L);
|
||||||
|
|
||||||
public void Destroy() => IsDestroyed = true;
|
public void Destroy() => IsDestroyed = true;
|
||||||
|
|
||||||
|
public async Task<IContentReader> GetContentReaderAsync()
|
||||||
|
{
|
||||||
|
return await _smbClientContext.RunWithSmbClientAsync(client =>
|
||||||
|
{
|
||||||
|
NTStatus status = NTStatus.STATUS_DATA_ERROR;
|
||||||
|
var fileStore = _smbShare.TreeConnect(client, out status);
|
||||||
|
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception($"Could not open file {NativePath} for read.");
|
||||||
|
}
|
||||||
|
|
||||||
|
var path = NativePath!;
|
||||||
|
path = path[(_parent.NativePath!.Length + 1)..];
|
||||||
|
status = fileStore.CreateFile(out object fileHandle, out FileStatus fileStatus, path, AccessMask.GENERIC_READ | AccessMask.SYNCHRONIZE, SMBLibrary.FileAttributes.Normal, ShareAccess.Read, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_ALERT, null);
|
||||||
|
|
||||||
|
if (status != NTStatus.STATUS_SUCCESS)
|
||||||
|
{
|
||||||
|
throw new Exception($"Could not open file {NativePath} for read.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SmbContentReader(fileStore, fileHandle, client);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<IContentWriter> GetContentWriterAsync()
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,6 @@ namespace FileTime.Providers.Smb
|
|||||||
private IReadOnlyList<IItem>? _items;
|
private IReadOnlyList<IItem>? _items;
|
||||||
private IReadOnlyList<IContainer>? _containers;
|
private IReadOnlyList<IContainer>? _containers;
|
||||||
private IReadOnlyList<IElement>? _elements;
|
private IReadOnlyList<IElement>? _elements;
|
||||||
private readonly SmbShare _smbShare;
|
|
||||||
private readonly IContainer? _parent;
|
private readonly IContainer? _parent;
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
@@ -22,6 +21,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public SmbContentProvider Provider { get; }
|
public SmbContentProvider Provider { get; }
|
||||||
IContentProvider IItem.Provider => Provider;
|
IContentProvider IItem.Provider => Provider;
|
||||||
|
public SmbShare SmbShare { get; }
|
||||||
public SupportsDelete CanDelete => SupportsDelete.True;
|
public SupportsDelete CanDelete => SupportsDelete.True;
|
||||||
public bool CanRename => true;
|
public bool CanRename => true;
|
||||||
|
|
||||||
@@ -35,7 +35,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
|
public SmbFolder(string name, SmbContentProvider contentProvider, SmbShare smbShare, IContainer parent)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
_smbShare = smbShare;
|
SmbShare = smbShare;
|
||||||
|
|
||||||
Name = name;
|
Name = name;
|
||||||
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||||
@@ -43,21 +43,21 @@ namespace FileTime.Providers.Smb
|
|||||||
Provider = contentProvider;
|
Provider = contentProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name)
|
public Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name)
|
public Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
public Task<bool> IsExists(string name)
|
public Task<bool> IsExistsAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var path = FullName![(_smbShare.FullName!.Length + 1)..];
|
var path = FullName![(SmbShare.FullName!.Length + 1)..];
|
||||||
(containers, elements) = await _smbShare.ListFolder(this, _smbShare.Name, path, token);
|
(containers, elements) = await SmbShare.ListFolder(this, path, token);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ namespace FileTime.Providers.Smb
|
|||||||
if (_elements == null) await RefreshAsync(token);
|
if (_elements == null) await RefreshAsync(token);
|
||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() => IsDestroyed = true;
|
public void Destroy() => IsDestroyed = true;
|
||||||
|
|
||||||
|
|||||||
@@ -69,12 +69,12 @@ namespace FileTime.Providers.Smb
|
|||||||
return Task.FromResult(_elements);
|
return Task.FromResult(_elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name)
|
public Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name)
|
public Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public IContainer? GetParent() => Provider;
|
public IContainer? GetParent() => Provider;
|
||||||
|
|
||||||
public Task<bool> IsExists(string name)
|
public Task<bool> IsExistsAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -120,7 +120,7 @@ namespace FileTime.Providers.Smb
|
|||||||
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
private void DisposeSmbClient()
|
private void DisposeSmbClient()
|
||||||
{
|
{
|
||||||
@@ -211,7 +211,7 @@ namespace FileTime.Providers.Smb
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() { }
|
public void Destroy() { }
|
||||||
|
|
||||||
|
|||||||
@@ -61,12 +61,12 @@ namespace FileTime.Providers.Smb
|
|||||||
return _elements;
|
return _elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> CreateContainer(string name)
|
public Task<IContainer> CreateContainerAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IElement> CreateElement(string name)
|
public Task<IElement> CreateElementAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -78,9 +78,9 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
public Task<bool> IsExists(string name)
|
public Task<bool> IsExistsAsync(string name)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
@@ -92,7 +92,7 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
(containers, elements) = await ListFolder(this, Name, string.Empty, token);
|
(containers, elements) = await ListFolder(this, string.Empty, token);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
|
|
||||||
@@ -111,14 +111,15 @@ namespace FileTime.Providers.Smb
|
|||||||
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName, CancellationToken token = default)
|
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string folderName, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
return await _smbClientContext.RunWithSmbClientAsync(client =>
|
return await _smbClientContext.RunWithSmbClientAsync(client =>
|
||||||
{
|
{
|
||||||
var containers = new List<IContainer>();
|
var containers = new List<IContainer>();
|
||||||
var elements = new List<IElement>();
|
var elements = new List<IElement>();
|
||||||
NTStatus status = NTStatus.STATUS_DATA_ERROR;
|
var status = NTStatus.STATUS_DATA_ERROR;
|
||||||
ISMBFileStore fileStore = client.TreeConnect(shareName, out status);
|
var fileStore = TreeConnect(client, out status);
|
||||||
|
|
||||||
if (status == NTStatus.STATUS_SUCCESS)
|
if (status == NTStatus.STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
||||||
@@ -137,7 +138,7 @@ namespace FileTime.Providers.Smb
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
|
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, this, parent, _smbClientContext));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,8 +152,13 @@ namespace FileTime.Providers.Smb
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal ISMBFileStore TreeConnect(ISMBClient client, out NTStatus status)
|
||||||
|
{
|
||||||
|
return client.TreeConnect(Name, out status);
|
||||||
|
}
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
public Task<bool> CanOpen() => Task.FromResult(true);
|
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||||
|
|
||||||
public void Destroy() { }
|
public void Destroy() { }
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ namespace FileTime.Tools.Compression.Command
|
|||||||
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
public IReadOnlyList<string> CanRunMessages { get; } = new List<string>().AsReadOnly();
|
||||||
|
|
||||||
public int Progress { get; }
|
public int Progress { get; }
|
||||||
|
public int CurrentProgress { get; }
|
||||||
|
|
||||||
public AsyncEventHandler ProgressChanged { get; } = new AsyncEventHandler();
|
public AsyncEventHandler ProgressChanged { get; } = new AsyncEventHandler();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user