CopyCommand WIP
This commit is contained in:
@@ -1,3 +0,0 @@
|
|||||||
namespace FileTime.App.Core.Models;
|
|
||||||
|
|
||||||
public record FileExtension(long? Size);
|
|
||||||
@@ -50,4 +50,7 @@ public class AbsolutePath
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AbsolutePath GetChild(string childName, AbsolutePathType type)
|
||||||
|
=> new (TimelessProvider, PointInTime, Path.GetChild(childName), type);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
namespace FileTime.Core.Models.Extensions;
|
||||||
|
|
||||||
|
public record FileExtension(long? Size);
|
||||||
27
src/Core/FileTime.Core.Command/Copy/CalculateStrategy.cs
Normal file
27
src/Core/FileTime.Core.Command/Copy/CalculateStrategy.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Models.Extensions;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public class CalculateStrategy : ICopyStrategy
|
||||||
|
{
|
||||||
|
private readonly List<OperationProgress> _operationStatuses;
|
||||||
|
public IReadOnlyList<OperationProgress> OperationStatuses { get; }
|
||||||
|
|
||||||
|
public CalculateStrategy()
|
||||||
|
{
|
||||||
|
_operationStatuses = new();
|
||||||
|
OperationStatuses = _operationStatuses.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ContainerCopyDoneAsync(AbsolutePath path) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public async Task CopyAsync(AbsolutePath from, AbsolutePath to, CopyCommandContext context)
|
||||||
|
{
|
||||||
|
var resolvedFrom = await from.ResolveAsync();
|
||||||
|
_operationStatuses.Add(new OperationProgress(from.Path.Path, (resolvedFrom as IElement)?.GetExtension<FileExtension>()?.Size ?? 0L));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CreateContainerAsync(IContainer target, string name, PointInTime currentTime) => Task.CompletedTask;
|
||||||
|
}
|
||||||
@@ -1,16 +1,140 @@
|
|||||||
|
using FileTime.Core.Enums;
|
||||||
|
using FileTime.Core.Extensions;
|
||||||
|
using FileTime.Core.Models;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
namespace FileTime.Core.Command.Copy;
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
public class CopyCommand : ITransportationCommand
|
public class CopyCommand : ITransportationCommand
|
||||||
{
|
{
|
||||||
public Task<CanCommandRun> CanRun(PointInTime currentTime)
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
|
||||||
|
private readonly List<OperationProgress> _operationProgresses = new();
|
||||||
|
|
||||||
|
public IList<FullName> Sources { get; } = new List<FullName>();
|
||||||
|
|
||||||
|
public FullName? Target { get; set; }
|
||||||
|
|
||||||
|
public TransportMode? TransportMode { get; set; } = Command.TransportMode.Merge;
|
||||||
|
public OperationProgress? CurrentOperationProgress { get; private set; }
|
||||||
|
|
||||||
|
public CopyCommand(ITimelessContentProvider timelessContentProvider)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
_timelessContentProvider = timelessContentProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<PointInTime> SimulateCommand(PointInTime currentTime)
|
public Task<CanCommandRun> CanRun(PointInTime currentTime)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
//TODO:
|
||||||
|
return Task.FromResult(CanCommandRun.True);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<PointInTime> SimulateCommand(PointInTime currentTime)
|
||||||
|
{
|
||||||
|
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
|
||||||
|
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
|
||||||
|
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
|
||||||
|
|
||||||
|
var simulateOperation = new SimulateStrategy(_timelessContentProvider);
|
||||||
|
var resolvedTarget = await _timelessContentProvider.GetItemByFullNameAsync(Target, currentTime);
|
||||||
|
|
||||||
|
await TraverseTree(
|
||||||
|
currentTime,
|
||||||
|
Sources,
|
||||||
|
new AbsolutePath(_timelessContentProvider, resolvedTarget),
|
||||||
|
TransportMode.Value,
|
||||||
|
simulateOperation);
|
||||||
|
|
||||||
|
return currentTime.WithDifferences(simulateOperation.NewDiffs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteAsync(CopyFunc copy)
|
||||||
|
{
|
||||||
|
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
|
||||||
|
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
|
||||||
|
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
|
||||||
|
|
||||||
|
var currentTime = PointInTime.Present;
|
||||||
|
|
||||||
|
await CalculateProgressAsync(currentTime);
|
||||||
|
|
||||||
|
var copyOperation = new CopyStrategy(copy, new CopyStrategyParam(_operationProgresses));
|
||||||
|
|
||||||
|
var resolvedTarget = await _timelessContentProvider.GetItemByFullNameAsync(Target, currentTime);
|
||||||
|
|
||||||
|
await TraverseTree(
|
||||||
|
currentTime,
|
||||||
|
Sources,
|
||||||
|
new AbsolutePath(_timelessContentProvider, resolvedTarget),
|
||||||
|
TransportMode.Value,
|
||||||
|
copyOperation);
|
||||||
|
//await TimeRunner.RefreshContainer.InvokeAsync(this, Target);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CalculateProgressAsync(PointInTime currentTime)
|
||||||
|
{
|
||||||
|
if (Sources == null) throw new ArgumentException(nameof(Sources) + " can not be null");
|
||||||
|
if (Target == null) throw new ArgumentException(nameof(Target) + " can not be null");
|
||||||
|
if (TransportMode == null) throw new ArgumentException(nameof(TransportMode) + " can not be null");
|
||||||
|
|
||||||
|
var calculateOperation = new CalculateStrategy();
|
||||||
|
var resolvedTarget = await _timelessContentProvider.GetItemByFullNameAsync(Target, currentTime);
|
||||||
|
|
||||||
|
await TraverseTree(
|
||||||
|
currentTime,
|
||||||
|
Sources,
|
||||||
|
new AbsolutePath(_timelessContentProvider, resolvedTarget),
|
||||||
|
TransportMode.Value,
|
||||||
|
calculateOperation);
|
||||||
|
|
||||||
|
_operationProgresses.Clear();
|
||||||
|
_operationProgresses.AddRange(calculateOperation.OperationStatuses);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TraverseTree(
|
||||||
|
PointInTime curretnTime,
|
||||||
|
IEnumerable<FullName> sources,
|
||||||
|
AbsolutePath target,
|
||||||
|
TransportMode transportMode,
|
||||||
|
ICopyStrategy copyOperation)
|
||||||
|
{
|
||||||
|
var resolvedTarget = ((IContainer)await target.ResolveAsync()) ?? throw new Exception();
|
||||||
|
|
||||||
|
foreach (var source in sources)
|
||||||
|
{
|
||||||
|
var item = await _timelessContentProvider.GetItemByFullNameAsync(source, curretnTime);
|
||||||
|
|
||||||
|
if (item is IContainer container)
|
||||||
|
{
|
||||||
|
if (!((await resolvedTarget.Items.GetItemsAsync())?.Any(i => i.Path.GetName() == item.Name) ?? false))
|
||||||
|
{
|
||||||
|
await copyOperation.CreateContainerAsync(resolvedTarget, container.Name, container.PointInTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
var children = await container.Items.GetItemsAsync();
|
||||||
|
if (children is null) continue;
|
||||||
|
|
||||||
|
await TraverseTree(curretnTime, children.Select(c => c.Path).ToList(), target.GetChild(item.Name, AbsolutePathType.Container), transportMode, copyOperation);
|
||||||
|
await copyOperation.ContainerCopyDoneAsync(new AbsolutePath(_timelessContentProvider, container));
|
||||||
|
}
|
||||||
|
else if (item is IElement element)
|
||||||
|
{
|
||||||
|
var newElementName = await Helper.GetNewNameAsync(resolvedTarget, element.Name, transportMode);
|
||||||
|
if (newElementName == null) continue;
|
||||||
|
|
||||||
|
var newElementPath = target.GetChild(newElementName, AbsolutePathType.Element);
|
||||||
|
|
||||||
|
var currentProgress = _operationProgresses.Find(o => o.Key == element.FullName!.Path);
|
||||||
|
CurrentOperationProgress = currentProgress;
|
||||||
|
|
||||||
|
await copyOperation.CopyAsync(new AbsolutePath(_timelessContentProvider, element), newElementPath, new CopyCommandContext(UpdateProgress, currentProgress));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Task UpdateProgress()
|
||||||
|
{
|
||||||
|
//TODO
|
||||||
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
16
src/Core/FileTime.Core.Command/Copy/CopyCommandContext.cs
Normal file
16
src/Core/FileTime.Core.Command/Copy/CopyCommandContext.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public class CopyCommandContext
|
||||||
|
{
|
||||||
|
private readonly Func<Task> _updateProgress;
|
||||||
|
|
||||||
|
public CopyCommandContext(Func<Task> updateProgress, OperationProgress? currentProgress)
|
||||||
|
{
|
||||||
|
_updateProgress = updateProgress;
|
||||||
|
CurrentProgress = currentProgress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OperationProgress? CurrentProgress { get; }
|
||||||
|
|
||||||
|
public async Task UpdateProgress() => await _updateProgress.Invoke();
|
||||||
|
}
|
||||||
5
src/Core/FileTime.Core.Command/Copy/CopyFunc.cs
Normal file
5
src/Core/FileTime.Core.Command/Copy/CopyFunc.cs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public delegate Task CopyFunc(AbsolutePath from, AbsolutePath to, CopyCommandContext context);
|
||||||
37
src/Core/FileTime.Core.Command/Copy/CopyStrategy.cs
Normal file
37
src/Core/FileTime.Core.Command/Copy/CopyStrategy.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public class CopyStrategy : ICopyStrategy
|
||||||
|
{
|
||||||
|
private readonly CopyFunc _copy;
|
||||||
|
private readonly CopyStrategyParam _copyStrategyParam;
|
||||||
|
|
||||||
|
public CopyStrategy(CopyFunc copy, CopyStrategyParam copyStrategyParam)
|
||||||
|
{
|
||||||
|
_copy = copy;
|
||||||
|
_copyStrategyParam = copyStrategyParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ContainerCopyDoneAsync(AbsolutePath containerPath)
|
||||||
|
{
|
||||||
|
foreach (var item in _copyStrategyParam.OperationProgresses.FindAll(o => o.Key.StartsWith(containerPath.Path.Path)))
|
||||||
|
{
|
||||||
|
item.SetProgress(item.TotalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
await _copyStrategyParam.RefreshContainerAsync(containerPath.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CopyAsync(AbsolutePath from, AbsolutePath to, CopyCommandContext context)
|
||||||
|
{
|
||||||
|
await _copy(from, to, context);
|
||||||
|
context.CurrentProgress?.SetProgress(context.CurrentProgress.TotalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CreateContainerAsync(IContainer target, string name, PointInTime currentTime)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
src/Core/FileTime.Core.Command/Copy/CopyStrategyParam.cs
Normal file
17
src/Core/FileTime.Core.Command/Copy/CopyStrategyParam.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public class CopyStrategyParam
|
||||||
|
{
|
||||||
|
private readonly Func<FullName, Task> _refreshContainer;
|
||||||
|
public List<OperationProgress> OperationProgresses { get; }
|
||||||
|
|
||||||
|
public CopyStrategyParam(List<OperationProgress> operationProgresses, Func<FullName, Task> refreshContainer)
|
||||||
|
{
|
||||||
|
OperationProgresses = operationProgresses;
|
||||||
|
_refreshContainer = refreshContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task RefreshContainerAsync(FullName containerName) => await _refreshContainer(containerName);
|
||||||
|
}
|
||||||
11
src/Core/FileTime.Core.Command/Copy/ICopyStrategy.cs
Normal file
11
src/Core/FileTime.Core.Command/Copy/ICopyStrategy.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public interface ICopyStrategy
|
||||||
|
{
|
||||||
|
Task CreateContainerAsync(IContainer target, string name, PointInTime currentTime);
|
||||||
|
Task ContainerCopyDoneAsync(AbsolutePath containerPath);
|
||||||
|
Task CopyAsync(AbsolutePath from, AbsolutePath to, CopyCommandContext context);
|
||||||
|
}
|
||||||
39
src/Core/FileTime.Core.Command/Copy/SimulateStrategy.cs
Normal file
39
src/Core/FileTime.Core.Command/Copy/SimulateStrategy.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using FileTime.Core.Models;
|
||||||
|
using FileTime.Core.Timeline;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command.Copy;
|
||||||
|
|
||||||
|
public class SimulateStrategy : ICopyStrategy
|
||||||
|
{
|
||||||
|
private readonly List<Difference> _newDiffs;
|
||||||
|
private readonly ITimelessContentProvider _timelessContentProvider;
|
||||||
|
|
||||||
|
public IReadOnlyList<Difference> NewDiffs { get; }
|
||||||
|
|
||||||
|
public SimulateStrategy(ITimelessContentProvider timelessContentProvider)
|
||||||
|
{
|
||||||
|
_timelessContentProvider = timelessContentProvider;
|
||||||
|
_newDiffs = new();
|
||||||
|
NewDiffs = _newDiffs.AsReadOnly();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task CreateContainerAsync(IContainer target, string name, PointInTime currentTime)
|
||||||
|
{
|
||||||
|
var newContainerDiff = new Difference(
|
||||||
|
DifferenceActionType.Create,
|
||||||
|
new AbsolutePath(_timelessContentProvider, currentTime, target.FullName!.GetChild(name), Enums.AbsolutePathType.Container)
|
||||||
|
);
|
||||||
|
|
||||||
|
_newDiffs.Add(newContainerDiff);
|
||||||
|
|
||||||
|
return Task.FromResult((IContainer)null!);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task ContainerCopyDoneAsync(AbsolutePath path) => Task.CompletedTask;
|
||||||
|
|
||||||
|
public Task CopyAsync(AbsolutePath from, AbsolutePath to, CopyCommandContext context)
|
||||||
|
{
|
||||||
|
_newDiffs.Add(new Difference(DifferenceActionType.Create, to));
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
src/Core/FileTime.Core.Command/Helper.cs
Normal file
28
src/Core/FileTime.Core.Command/Helper.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using FileTime.Core.Extensions;
|
||||||
|
using FileTime.Core.Models;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command;
|
||||||
|
|
||||||
|
public static class Helper
|
||||||
|
{
|
||||||
|
public static async Task<string?> GetNewNameAsync(IContainer resolvedTarget, string name, TransportMode transportMode)
|
||||||
|
{
|
||||||
|
var items = await resolvedTarget.Items.GetItemsAsync() ?? throw new NullReferenceException();
|
||||||
|
var newName = name;
|
||||||
|
var targetNameExists = resolvedTarget != null && items.Any(i => i.Path.GetName() == newName);
|
||||||
|
if (transportMode == TransportMode.Merge && resolvedTarget != null)
|
||||||
|
{
|
||||||
|
for (var i = 0; targetNameExists; i++)
|
||||||
|
{
|
||||||
|
newName = name + (i == 0 ? "_" : $"_{i}");
|
||||||
|
targetNameExists = resolvedTarget != null && items.Any(i => i.Path.GetName() == newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (transportMode == TransportMode.Skip && targetNameExists)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newName;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/Core/FileTime.Core.Command/OperationProgress.cs
Normal file
24
src/Core/FileTime.Core.Command/OperationProgress.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Reactive.Subjects;
|
||||||
|
|
||||||
|
namespace FileTime.Core.Command;
|
||||||
|
|
||||||
|
public class OperationProgress
|
||||||
|
{
|
||||||
|
private readonly BehaviorSubject<long> _currentProgress = new(0);
|
||||||
|
public string Key { get; }
|
||||||
|
public IObservable<long> Progress { get; }
|
||||||
|
public long TotalCount { get; }
|
||||||
|
public IObservable<bool> IsDone { get; }
|
||||||
|
|
||||||
|
public OperationProgress(string key, long totalCount)
|
||||||
|
{
|
||||||
|
Key = key;
|
||||||
|
TotalCount = totalCount;
|
||||||
|
|
||||||
|
Progress = _currentProgress.AsObservable();
|
||||||
|
IsDone = Progress.Select(p => p >= TotalCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetProgress(long progress) => _currentProgress.OnNext(progress);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user