ContentProvider refactor
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using System.Reflection.Metadata;
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Timeline;
|
||||
@@ -80,11 +81,14 @@ namespace FileTime.Core.Command.Delete
|
||||
if (_deleteContainer != null) await _deleteContainer.Invoke(container);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (container.AllowRecursiveDeletion)
|
||||
{
|
||||
foreach (var child in (await container.GetItems())!)
|
||||
{
|
||||
await TraverseTree(child);
|
||||
}
|
||||
}
|
||||
|
||||
if (_deleteContainer != null) await _deleteContainer.Invoke(container);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ namespace FileTime.Core.Models
|
||||
Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default);
|
||||
Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default);
|
||||
Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default);
|
||||
bool AllowRecursiveDeletion { get; }
|
||||
|
||||
Task RefreshAsync(CancellationToken token = default);
|
||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace FileTime.Core.Models
|
||||
|
||||
public bool IsDestroyed => BaseContainer.IsDestroyed;
|
||||
public bool IsExists => BaseContainer.IsExists;
|
||||
public bool AllowRecursiveDeletion => BaseContainer.AllowRecursiveDeletion;
|
||||
|
||||
private void RefreshAddBase(Func<object?, AsyncEventArgs, CancellationToken, Task> handler)
|
||||
{
|
||||
|
||||
@@ -3,9 +3,9 @@ using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Providers
|
||||
{
|
||||
public abstract class AbstractContainer<TProvider> : IContainer where TProvider : IContentProvider
|
||||
public abstract class AbstractContainer<TProvider> : IContainer where TProvider : class, IContentProvider
|
||||
{
|
||||
private readonly IContainer _parent;
|
||||
private readonly IContainer? _parent;
|
||||
private readonly List<Exception> _exceptions = new();
|
||||
private IReadOnlyList<IContainer>? _containers;
|
||||
private IReadOnlyList<IItem>? _items;
|
||||
@@ -39,13 +39,26 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public abstract bool IsExists { get; }
|
||||
|
||||
protected AbstractContainer(TProvider provider, IContainer parent, string name)
|
||||
public virtual bool AllowRecursiveDeletion { get; protected set; }
|
||||
|
||||
protected AbstractContainer(TProvider provider, IContainer parent, string name) : this(name)
|
||||
{
|
||||
_parent = parent;
|
||||
Provider = provider;
|
||||
FullName = parent.FullName == null ? name : parent.FullName + Constants.SeparatorChar + name;
|
||||
}
|
||||
|
||||
protected AbstractContainer(string name, string? fullName = null) : this(name)
|
||||
{
|
||||
Provider = this is TProvider provider ? provider : throw new ArgumentException($"This constructor is for {nameof(IContentProvider)}s only");
|
||||
FullName = fullName;
|
||||
}
|
||||
|
||||
private AbstractContainer(string name)
|
||||
{
|
||||
Name = name;
|
||||
FullName = parent.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||
Exceptions = _exceptions.AsReadOnly();
|
||||
Provider = null!;
|
||||
}
|
||||
|
||||
public virtual Task<bool> CanOpenAsync() => Task.FromResult(_exceptions.Count == 0);
|
||||
|
||||
122
src/Core/FileTime.Core/Providers/ContentProviderBase.cs
Normal file
122
src/Core/FileTime.Core/Providers/ContentProviderBase.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
|
||||
namespace FileTime.Core.Providers
|
||||
{
|
||||
public abstract class ContentProviderBase<T> : AbstractContainer<T>, IContentProvider
|
||||
where T : class, IContentProvider
|
||||
{
|
||||
private readonly object _initializationGuard = new();
|
||||
private bool _initialized;
|
||||
private bool _initializing;
|
||||
private IContainer? _parent;
|
||||
protected IReadOnlyList<IContainer>? RootContainers { get; private set; }
|
||||
public override bool IsExists => true;
|
||||
|
||||
protected ContentProviderBase(
|
||||
string name,
|
||||
string? fullName,
|
||||
string protocol,
|
||||
bool supportsContentStreams)
|
||||
: base(name, fullName)
|
||||
{
|
||||
Protocol = protocol;
|
||||
SupportsContentStreams = supportsContentStreams;
|
||||
|
||||
CanRename = false;
|
||||
CanDelete = SupportsDelete.False;
|
||||
}
|
||||
|
||||
public virtual bool SupportsContentStreams { get; }
|
||||
|
||||
public virtual string Protocol { get; }
|
||||
|
||||
public abstract Task<bool> CanHandlePath(string path);
|
||||
public override IContainer? GetParent() => _parent;
|
||||
|
||||
public void SetParent(IContainer parent) => _parent = parent;
|
||||
|
||||
protected async Task AddRootContainer(IContainer newRootContainer)
|
||||
{
|
||||
RootContainers =
|
||||
(await GetContainers())?.Append(newRootContainer).OrderBy(c => c.Name).ToList().AsReadOnly()
|
||||
?? new List<IContainer>() { newRootContainer }.AsReadOnly();
|
||||
}
|
||||
|
||||
protected async Task AddRootContainers(IEnumerable<IContainer> newRootContainers)
|
||||
{
|
||||
RootContainers =
|
||||
(await GetContainers())?.Concat(newRootContainers).OrderBy(c => c.Name).ToList().AsReadOnly()
|
||||
?? new List<IContainer>(newRootContainers).AsReadOnly();
|
||||
}
|
||||
|
||||
protected void SetRootContainers(IEnumerable<IContainer> newRootContainers)
|
||||
=> RootContainers = newRootContainers.OrderBy(c => c.Name).ToList().AsReadOnly();
|
||||
|
||||
public override async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
||||
{
|
||||
await InitIfNeeded();
|
||||
return RootContainers;
|
||||
}
|
||||
|
||||
public override async Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
|
||||
{
|
||||
await InitIfNeeded();
|
||||
return new List<IElement>();
|
||||
}
|
||||
|
||||
public override async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||
{
|
||||
await InitIfNeeded();
|
||||
return RootContainers;
|
||||
}
|
||||
|
||||
private async Task InitIfNeeded()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (_initializationGuard)
|
||||
{
|
||||
if (!_initializing)
|
||||
{
|
||||
_initializing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Task.Delay(1);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (_initialized) return;
|
||||
_initialized = true;
|
||||
await Init();
|
||||
IsLoaded = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_initializationGuard)
|
||||
{
|
||||
_initializing = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual Task Init() { return Task.CompletedTask; }
|
||||
|
||||
public override Task<IEnumerable<IItem>> RefreshItems(CancellationToken token = default) { throw new NotImplementedException($"{nameof(RefreshItems)} should not be called in {nameof(ContentProviderBase<T>)}."); }
|
||||
|
||||
public override Task<bool> IsExistsAsync(string name) => Task.FromResult(RootContainers?.Any(i => i.Name == name) ?? false);
|
||||
|
||||
public override async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||
public override Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||
public override Task Delete(bool hardDelete = false) => throw new NotSupportedException();
|
||||
public override Task Rename(string newName) => throw new NotSupportedException();
|
||||
public override Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||
|
||||
|
||||
public override void Unload() { }
|
||||
|
||||
public override void Destroy() { }
|
||||
}
|
||||
}
|
||||
@@ -7,9 +7,7 @@ namespace FileTime.Core.Providers
|
||||
bool SupportsContentStreams { get; }
|
||||
string Protocol { get; }
|
||||
|
||||
Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default);
|
||||
|
||||
bool CanHandlePath(string path);
|
||||
Task<bool> CanHandlePath(string path);
|
||||
|
||||
void SetParent(IContainer container);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace FileTime.Core.Providers
|
||||
|
||||
public bool IsDestroyed => false;
|
||||
public bool IsExists => true;
|
||||
public bool AllowRecursiveDeletion => false;
|
||||
|
||||
public TopContainer(IEnumerable<IContentProvider> contentProviders)
|
||||
{
|
||||
|
||||
@@ -34,6 +34,7 @@ namespace FileTime.Core.Timeline
|
||||
//FIXME: currently this can be different of the real items NativePath, should be fixed
|
||||
public string? NativePath => FullName;
|
||||
public bool IsExists => true;
|
||||
public bool AllowRecursiveDeletion => true;
|
||||
|
||||
public TimeContainer(string name, IContainer parent, IContentProvider contentProvider, IContentProvider virtualContentProvider, PointInTime pointInTime)
|
||||
{
|
||||
|
||||
@@ -1,115 +1,30 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
|
||||
namespace FileTime.Core.Timeline
|
||||
{
|
||||
public class TimeProvider : IContentProvider
|
||||
public class TimeProvider : ContentProviderBase<TimeProvider>
|
||||
{
|
||||
private readonly PointInTime _pointInTime;
|
||||
|
||||
public bool IsLoaded => true;
|
||||
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public string Name => "time";
|
||||
|
||||
public string? FullName => null;
|
||||
public string? NativePath => null;
|
||||
|
||||
public bool IsHidden => false;
|
||||
|
||||
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||
|
||||
public bool CanRename => false;
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
|
||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||
|
||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||
|
||||
public bool IsDestroyed => false;
|
||||
public bool SupportsContentStreams => false;
|
||||
|
||||
public string Protocol => "time2://";
|
||||
public bool IsExists => true;
|
||||
|
||||
public TimeProvider(PointInTime pointInTime)
|
||||
public TimeProvider(PointInTime pointInTime) : base("time", null, "time2://", false)
|
||||
{
|
||||
_pointInTime = pointInTime;
|
||||
}
|
||||
|
||||
public bool CanHandlePath(string path)
|
||||
public override Task<bool> CanHandlePath(string path)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||
|
||||
public Task<IContainer> CreateContainerAsync(string name)
|
||||
public override Task<IContainer> CreateContainerAsync(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IElement> CreateElementAsync(string name)
|
||||
public override Task<IElement> CreateElementAsync(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task Delete(bool hardDelete = false) => throw new NotSupportedException();
|
||||
|
||||
public Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public IContainer? GetParent() => null;
|
||||
|
||||
public Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> IsExistsAsync(string name)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task RefreshAsync(CancellationToken token = default) => Task.CompletedTask;
|
||||
|
||||
public Task Rename(string newName) => throw new NotSupportedException();
|
||||
|
||||
public void SetParent(IContainer container) { }
|
||||
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||
|
||||
public void Destroy() { }
|
||||
|
||||
public void Unload() { }
|
||||
|
||||
public Task<IContentReader> GetContentReaderAsync(IElement element)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public Task<IContentWriter> GetContentWriterAsync(IElement element)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -564,7 +564,7 @@ namespace FileTime.Avalonia.Services
|
||||
var path = inputs[0].Value;
|
||||
foreach (var contentProvider in _contentProviders)
|
||||
{
|
||||
if (contentProvider.CanHandlePath(path))
|
||||
if (await contentProvider.CanHandlePath(path))
|
||||
{
|
||||
var possibleContainer = await contentProvider.GetByPath(path);
|
||||
if (possibleContainer is IContainer container)
|
||||
|
||||
@@ -121,7 +121,7 @@ namespace FileTime.Avalonia.Services
|
||||
{
|
||||
foreach (var contentProvider in _contentProviders)
|
||||
{
|
||||
if (contentProvider.CanHandlePath(tab.Path))
|
||||
if (await contentProvider.CanHandlePath(tab.Path))
|
||||
{
|
||||
pathItem = await contentProvider.GetByPath(tab.Path, true);
|
||||
if (pathItem != null) break;
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace FileTime.Avalonia.ViewModels
|
||||
|
||||
private async Task<IContainer?> GetContainerForWindowsDrive(DriveInfo drive)
|
||||
{
|
||||
return (await LocalContentProvider.GetRootContainers()).FirstOrDefault(d => d.Name == drive.Name.TrimEnd(Path.DirectorySeparatorChar));
|
||||
return (await LocalContentProvider.GetContainers())!.FirstOrDefault(d => d.Name == drive.Name.TrimEnd(Path.DirectorySeparatorChar));
|
||||
}
|
||||
|
||||
private async Task<IContainer?> GetContainerForLinuxDrive(DriveInfo drive)
|
||||
|
||||
@@ -1,46 +1,17 @@
|
||||
using System.Threading.Tasks;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.Providers.Local
|
||||
{
|
||||
public class LocalContentProvider : IContentProvider
|
||||
public class LocalContentProvider : ContentProviderBase<LocalContentProvider>
|
||||
{
|
||||
private readonly ILogger<LocalContentProvider> _logger;
|
||||
private IContainer? _parent;
|
||||
|
||||
private readonly IReadOnlyList<IContainer> _rootContainers;
|
||||
private readonly IReadOnlyList<IItem>? _items;
|
||||
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
||||
|
||||
public string Name { get; } = "local";
|
||||
public string Protocol { get; } = "local://";
|
||||
|
||||
public string? FullName { get; }
|
||||
public string? NativePath => null;
|
||||
public bool IsHidden => false;
|
||||
public bool IsLoaded => true;
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public bool IsCaseInsensitive { get; }
|
||||
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||
public bool CanRename => false;
|
||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||
|
||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||
|
||||
public bool IsDestroyed => false;
|
||||
public bool SupportsContentStreams => true;
|
||||
public bool IsExists => true;
|
||||
|
||||
public LocalContentProvider(ILogger<LocalContentProvider> logger)
|
||||
: base("local", null, "local://", true)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
@@ -50,8 +21,7 @@ namespace FileTime.Providers.Local
|
||||
? new DirectoryInfo("/").GetDirectories()
|
||||
: Environment.GetLogicalDrives().Select(d => new DirectoryInfo(d));
|
||||
|
||||
_rootContainers = rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name).ToList().AsReadOnly();
|
||||
_items = _rootContainers.Cast<IItem>().ToList().AsReadOnly();
|
||||
SetRootContainers(rootDirectories.Select(d => new LocalFolder(d, this, this)).OrderBy(d => d.Name));
|
||||
}
|
||||
|
||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||
@@ -62,11 +32,11 @@ namespace FileTime.Providers.Local
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0]?.Length == 0) return this;
|
||||
|
||||
var normalizedRootContainerName = NormalizePath(pathParts[0]);
|
||||
var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == normalizedRootContainerName);
|
||||
var rootContainer = RootContainers?.FirstOrDefault(c => NormalizePath(c.Name) == normalizedRootContainerName);
|
||||
|
||||
if (rootContainer == null)
|
||||
{
|
||||
_logger.LogWarning("No root container found with name '{RootContainerName}'", path[0]);
|
||||
_logger.LogWarning("No root container found with name '{RootContainerName}'.", path[0]);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -74,52 +44,18 @@ namespace FileTime.Providers.Local
|
||||
return remainingPath.Length == 0 ? rootContainer : await rootContainer.GetByPath(remainingPath, acceptDeepestMatch);
|
||||
}
|
||||
|
||||
public async Task RefreshAsync(CancellationToken token = default) => await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty, token);
|
||||
|
||||
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
public Task<IContainer> CreateContainerAsync(string name) => throw new NotSupportedException();
|
||||
public Task<IElement> CreateElementAsync(string name) => throw new NotSupportedException();
|
||||
public Task<bool> IsExistsAsync(string name) => Task.FromResult(_rootContainers.Any(i => i.Name == name));
|
||||
|
||||
public Task Delete(bool hardDelete = false) => throw new NotSupportedException();
|
||||
public override Task<IContainer> CreateContainerAsync(string name) => throw new NotSupportedException();
|
||||
public override Task<IElement> CreateElementAsync(string name) => throw new NotSupportedException();
|
||||
|
||||
internal string NormalizePath(string path) => IsCaseInsensitive ? path.ToLower() : path;
|
||||
|
||||
public bool CanHandlePath(string path)
|
||||
public override async Task<bool> CanHandlePath(string path)
|
||||
{
|
||||
var normalizedPath = NormalizePath(path);
|
||||
Func<IContainer, bool> match = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? c => normalizedPath.StartsWith(NormalizePath(c.Name))
|
||||
: c => normalizedPath.StartsWith(NormalizePath("/" + c.Name));
|
||||
return _rootContainers.Any(match);
|
||||
}
|
||||
|
||||
public void SetParent(IContainer container) => _parent = container;
|
||||
public Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default) => Task.FromResult(_rootContainers);
|
||||
|
||||
public Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default) => Task.FromResult(_items);
|
||||
public Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IContainer>?)_rootContainers);
|
||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult(_elements);
|
||||
|
||||
public Task Rename(string newName) => throw new NotSupportedException();
|
||||
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
foreach (var c in _rootContainers)
|
||||
{
|
||||
c.Unload();
|
||||
}
|
||||
}
|
||||
|
||||
public void Unload()
|
||||
{
|
||||
foreach (var c in _rootContainers)
|
||||
{
|
||||
c.Unload();
|
||||
}
|
||||
return (await GetContainers())?.Any(match) ?? false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ namespace FileTime.Providers.Local
|
||||
NativePath = Directory.FullName;
|
||||
IsHidden = (Directory.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||
CanRename = true;
|
||||
AllowRecursiveDeletion = true;
|
||||
|
||||
//TODO: Linux soft delete
|
||||
SupportsDirectoryLevelSoftDelete = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
@@ -6,67 +5,28 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.Providers.Sftp
|
||||
{
|
||||
public class SftpContentProvider : IContentProvider
|
||||
public class SftpContentProvider : ContentProviderBase<SftpContentProvider>, IContainer
|
||||
{
|
||||
private IContainer? _parent;
|
||||
private readonly IInputInterface _inputInterface;
|
||||
private readonly List<IContainer> _rootContainers;
|
||||
private readonly IReadOnlyList<IContainer> _rootContainersReadOnly;
|
||||
private IReadOnlyList<IItem>? _items;
|
||||
private readonly IReadOnlyList<IElement> _elements = new List<IElement>().AsReadOnly();
|
||||
private readonly ILogger<SftpContentProvider> _logger;
|
||||
|
||||
public bool SupportsContentStreams => false;
|
||||
|
||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||
|
||||
public bool IsLoaded => true;
|
||||
|
||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||
|
||||
public AsyncEventHandler Refreshed { get; } = new AsyncEventHandler();
|
||||
|
||||
public string Name => "sftp";
|
||||
|
||||
public string? FullName => null;
|
||||
|
||||
public string? NativePath => null;
|
||||
|
||||
public bool IsHidden => false;
|
||||
|
||||
public bool IsDestroyed => false;
|
||||
|
||||
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||
|
||||
public bool CanRename => false;
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
|
||||
public string Protocol => "sftp://";
|
||||
public bool IsExists => true;
|
||||
|
||||
public SftpContentProvider(IInputInterface inputInterface, ILogger<SftpContentProvider> logger)
|
||||
: base("sftp", null, "sftp://", false)
|
||||
{
|
||||
_logger = logger;
|
||||
_rootContainers = new List<IContainer>();
|
||||
_items = new List<IItem>();
|
||||
_rootContainersReadOnly = _rootContainers.AsReadOnly();
|
||||
_inputInterface = inputInterface;
|
||||
}
|
||||
|
||||
public bool CanHandlePath(string path) => path.StartsWith("sftp://");
|
||||
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||
public override Task<bool> CanHandlePath(string path) => Task.FromResult(path.StartsWith("sftp://"));
|
||||
|
||||
public async Task<IContainer> CreateContainerAsync(string name)
|
||||
public override async Task<IContainer> CreateContainerAsync(string name)
|
||||
{
|
||||
var container = _rootContainers.Find(c => c.Name == name);
|
||||
var container = RootContainers?.FirstOrDefault(c => c.Name == name);
|
||||
|
||||
if (container == null)
|
||||
{
|
||||
container = new SftpServer(name, this, _inputInterface);
|
||||
_rootContainers.Add(container);
|
||||
_items = _rootContainers.OrderBy(c => c.Name).ToList().AsReadOnly();
|
||||
await AddRootContainer(container);
|
||||
}
|
||||
|
||||
await RefreshAsync();
|
||||
@@ -76,50 +36,9 @@ namespace FileTime.Providers.Sftp
|
||||
return container;
|
||||
}
|
||||
|
||||
public Task<IElement> CreateElementAsync(string name)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override Task<IElement> CreateElementAsync(string name) => throw new NotSupportedException();
|
||||
|
||||
public Task Delete(bool hardDelete = false)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public void Destroy() { }
|
||||
|
||||
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
||||
{
|
||||
await Init();
|
||||
return _rootContainersReadOnly;
|
||||
}
|
||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IElement>?)_elements);
|
||||
|
||||
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||
{
|
||||
await Init();
|
||||
return _items;
|
||||
}
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
public Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default) => Task.FromResult(_rootContainersReadOnly);
|
||||
|
||||
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 Task Rename(string newName) => throw new NotSupportedException();
|
||||
|
||||
public void SetParent(IContainer container) => _parent = container;
|
||||
|
||||
public void Unload() { }
|
||||
|
||||
private Task Init()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||
async Task<IItem?> IContainer.GetByPath(string path, bool acceptDeepestMatch)
|
||||
{
|
||||
if (path == null) return this;
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using AsyncEvent;
|
||||
using FileTime.Core.Interactions;
|
||||
using FileTime.Core.Models;
|
||||
using FileTime.Core.Providers;
|
||||
@@ -8,61 +6,28 @@ using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace FileTime.Providers.Smb
|
||||
{
|
||||
public class SmbContentProvider : IContentProvider
|
||||
public class SmbContentProvider : ContentProviderBase<SmbContentProvider>, IContainer
|
||||
{
|
||||
private readonly object _initializationGuard = new();
|
||||
private bool _initialized;
|
||||
private bool _initializing;
|
||||
private IContainer? _parent;
|
||||
private readonly IInputInterface _inputInterface;
|
||||
private readonly List<IContainer> _rootContainers;
|
||||
private readonly IReadOnlyList<IContainer> _rootContainersReadOnly;
|
||||
private IReadOnlyList<IItem> _items;
|
||||
private readonly IReadOnlyList<IElement> _elements = new List<IElement>().AsReadOnly();
|
||||
private readonly Persistence.PersistenceService _persistenceService;
|
||||
private readonly ILogger<SmbContentProvider> _logger;
|
||||
|
||||
public string Name { get; } = "smb";
|
||||
public string Protocol { get; } = "smb://";
|
||||
|
||||
public string? FullName => null;
|
||||
public string? NativePath => null;
|
||||
|
||||
public bool IsHidden => false;
|
||||
public bool IsLoaded => true;
|
||||
|
||||
public IContentProvider Provider => this;
|
||||
public SupportsDelete CanDelete => SupportsDelete.False;
|
||||
public bool CanRename => false;
|
||||
public IReadOnlyList<Exception> Exceptions { get; } = new List<Exception>().AsReadOnly();
|
||||
|
||||
public AsyncEventHandler Refreshed { get; } = new();
|
||||
|
||||
public bool SupportsDirectoryLevelSoftDelete => false;
|
||||
|
||||
public bool IsDestroyed => false;
|
||||
public bool SupportsContentStreams => true;
|
||||
public bool IsExists => true;
|
||||
|
||||
public SmbContentProvider(IInputInterface inputInterface, Persistence.PersistenceService persistenceService, ILogger<SmbContentProvider> logger)
|
||||
: base("smb", null, "smb://", true)
|
||||
{
|
||||
_rootContainers = new List<IContainer>();
|
||||
_items = new List<IItem>();
|
||||
_rootContainersReadOnly = _rootContainers.AsReadOnly();
|
||||
_inputInterface = inputInterface;
|
||||
_persistenceService = persistenceService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<IContainer> CreateContainerAsync(string name)
|
||||
public override async Task<IContainer> CreateContainerAsync(string name)
|
||||
{
|
||||
var container = _rootContainers.Find(c => c.Name == name);
|
||||
var container = RootContainers?.FirstOrDefault(c => c.Name == name);
|
||||
|
||||
if (container == null)
|
||||
{
|
||||
container = new SmbServer(name, this, _inputInterface);
|
||||
_rootContainers.Add(container);
|
||||
_items = _rootContainers.OrderBy(c => c.Name).ToList().AsReadOnly();
|
||||
await AddRootContainer(container);
|
||||
}
|
||||
|
||||
await RefreshAsync();
|
||||
@@ -72,17 +37,9 @@ namespace FileTime.Providers.Smb
|
||||
return container;
|
||||
}
|
||||
|
||||
public Task<IElement> CreateElementAsync(string name)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
public override Task<IElement> CreateElementAsync(string name) => throw new NotSupportedException();
|
||||
|
||||
public Task Delete(bool hardDelete = false)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public async Task<IItem?> GetByPath(string path, bool acceptDeepestMatch = false)
|
||||
async Task<IItem?> IContainer.GetByPath(string path, bool acceptDeepestMatch)
|
||||
{
|
||||
if (path == null) return this;
|
||||
|
||||
@@ -117,45 +74,13 @@ namespace FileTime.Providers.Smb
|
||||
}
|
||||
}
|
||||
|
||||
public IContainer? GetParent() => _parent;
|
||||
|
||||
public Task<IContainer> CloneAsync() => Task.FromResult((IContainer)this);
|
||||
|
||||
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 bool CanHandlePath(string path) => path.StartsWith("smb://") || path.StartsWith(@"\\");
|
||||
|
||||
public void SetParent(IContainer container) => _parent = container;
|
||||
public Task<IReadOnlyList<IContainer>> GetRootContainers(CancellationToken token = default) => Task.FromResult(_rootContainersReadOnly);
|
||||
|
||||
public async Task<IReadOnlyList<IItem>?> GetItems(CancellationToken token = default)
|
||||
{
|
||||
await Init();
|
||||
return _items;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<IContainer>?> GetContainers(CancellationToken token = default)
|
||||
{
|
||||
await Init();
|
||||
return _rootContainersReadOnly;
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<IElement>?> GetElements(CancellationToken token = default) => Task.FromResult((IReadOnlyList<IElement>?)_elements);
|
||||
|
||||
public Task Rename(string newName) => throw new NotSupportedException();
|
||||
public Task<bool> CanOpenAsync() => Task.FromResult(true);
|
||||
|
||||
public void Destroy() { }
|
||||
|
||||
public void Unload() { }
|
||||
public override Task<bool> CanHandlePath(string path) => Task.FromResult(path.StartsWith("smb://") || path.StartsWith(@"\\"));
|
||||
|
||||
public async Task SaveServers()
|
||||
{
|
||||
try
|
||||
{
|
||||
await _persistenceService.SaveServers(_rootContainers.OfType<SmbServer>());
|
||||
await _persistenceService.SaveServers(RootContainers?.OfType<SmbServer>() ?? Enumerable.Empty<SmbServer>());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -163,41 +88,10 @@ namespace FileTime.Providers.Smb
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Init()
|
||||
protected override async Task Init()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
lock (_initializationGuard)
|
||||
{
|
||||
if (!_initializing)
|
||||
{
|
||||
_initializing = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
await Task.Delay(1);
|
||||
}
|
||||
try
|
||||
{
|
||||
if (_initialized) return;
|
||||
if (_items.Count > 0) return;
|
||||
_initialized = true;
|
||||
|
||||
var servers = await _persistenceService.LoadServers();
|
||||
foreach (var server in servers)
|
||||
{
|
||||
var smbServer = new SmbServer(server.Path, this, _inputInterface, server.UserName, server.Password);
|
||||
_rootContainers.Add(smbServer);
|
||||
}
|
||||
_items = _rootContainers.OrderBy(c => c.Name).ToList().AsReadOnly();
|
||||
}
|
||||
finally
|
||||
{
|
||||
lock (_initializationGuard)
|
||||
{
|
||||
_initializing = false;
|
||||
}
|
||||
}
|
||||
SetRootContainers(servers.Select(s => new SmbServer(s.Path, this, _inputInterface, s.UserName, s.Password)));
|
||||
}
|
||||
|
||||
public static string GetNativePathSeparator() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "\\" : "/";
|
||||
|
||||
Reference in New Issue
Block a user