ConsoleUI container size text

This commit is contained in:
2023-08-16 12:00:07 +02:00
parent e35702c8e6
commit cbbf7b3704
27 changed files with 191 additions and 46 deletions

View File

@@ -15,4 +15,11 @@
<ProjectReference Include="..\..\AppCommon\FileTime.App.Core.Abstraction\FileTime.App.Core.Abstraction.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="PropertyChanged.SourceGenerator" Version="1.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,8 @@
using System.Collections.ObjectModel;
namespace FileTime.Providers.Local;
public interface IRootDriveInfoService
{
ObservableCollection<RootDriveInfo> RootDriveInfos { get; set; }
}

View File

@@ -0,0 +1,57 @@
using System.ComponentModel;
using System.Drawing;
using System.Runtime.InteropServices;
using FileTime.Core.Models;
using PropertyChanged.SourceGenerator;
using IContainer = FileTime.Core.Models.IContainer;
namespace FileTime.Providers.Local;
public partial class RootDriveInfo
{
private readonly DriveInfo _driveInfo;
[Notify] private string _name;
[Notify] private string _fullName;
[Notify] private string? _label;
[Notify] private long _size = 0;
[Notify] private long _free = 0;
[Notify] private long _used = 0;
[Notify] public long UsedPercentage => Size == 0 ? 0 : Used * 100 / Size;
public FullName Path { get; }
public RootDriveInfo(DriveInfo driveInfo, IContainer container)
{
_driveInfo = driveInfo;
_name = container.Name;
_fullName = _name;
try
{
_fullName = container.FullName?.Path[(container.Provider.FullName!.Path.Length + 1)..] ?? _fullName;
}
catch
{
}
Path = container.FullName ?? throw new NullReferenceException($"Container does not have a {nameof(FullName)}");
Refresh();
}
private void Refresh()
{
Label = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? _driveInfo.VolumeLabel : null;
Size = _driveInfo.TotalSize;
Free = _driveInfo.AvailableFreeSpace;
Used = _driveInfo.TotalSize - _driveInfo.AvailableFreeSpace;
}
}

View File

@@ -6,6 +6,7 @@ using FileTime.Core.Enums;
using FileTime.Core.Models;
using FileTime.Core.Models.Extensions;
using FileTime.Core.Timeline;
using Microsoft.Extensions.DependencyInjection;
namespace FileTime.Providers.Local;
@@ -13,13 +14,18 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{
private readonly ITimelessContentProvider _timelessContentProvider;
private readonly bool _isCaseInsensitive;
private readonly Lazy<ObservableCollection<RootDriveInfo>> _rootDriveInfos;
public LocalContentProvider(ITimelessContentProvider timelessContentProvider)
public LocalContentProvider(
ITimelessContentProvider timelessContentProvider,
IServiceProvider serviceProvider)
: base(LocalContentProviderConstants.ContentProviderId, timelessContentProvider)
{
_timelessContentProvider = timelessContentProvider;
_isCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
_rootDriveInfos = new Lazy<ObservableCollection<RootDriveInfo>>(() => serviceProvider.GetRequiredService<IRootDriveInfoService>().RootDriveInfos);
SupportsContentStreams = true;
RefreshRootDirectories();
@@ -63,6 +69,16 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return rootDrive is not null;
}
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path)
{
var rootDriveInfos = _rootDriveInfos.Value;
var rootDriveInfo = rootDriveInfos.FirstOrDefault(d => path.Path.StartsWith(d.Path.Path));
if(rootDriveInfo is null) return null;
return new VolumeSizeInfo(rootDriveInfo.Size, rootDriveInfo.Free);
}
public override Task<IItem> GetItemByNativePathAsync(NativePath nativePath,
PointInTime pointInTime,
bool forceResolve = false,

View File

@@ -0,0 +1,79 @@
using System.Collections.ObjectModel;
using System.Runtime.InteropServices;
using FileTime.App.Core.Services;
using FileTime.Core.Models;
using ObservableComputations;
namespace FileTime.Providers.Local;
public class RootDriveInfoService : IRootDriveInfoService, IExitHandler
{
private readonly ILocalContentProvider _localContentProvider;
private readonly List<DriveInfo> _rootDrives = new();
private readonly OcConsumer _rootDriveInfosConsumer = new();
public ObservableCollection<RootDriveInfo> RootDriveInfos { get; set; }
public RootDriveInfoService(ILocalContentProvider localContentProvider)
{
_localContentProvider = localContentProvider;
InitRootDrives();
var rootDriveInfos = localContentProvider.Items.Selecting<AbsolutePath, (AbsolutePath Path, DriveInfo? Drive)>(
i => MatchRootDrive(i)
)
.Filtering(t => IsNotNull(t.Drive))
.Selecting(t => Resolve(t))
.Filtering(t => t.Item is IContainer)
.Selecting(t => new RootDriveInfo(t.Drive, (IContainer) t.Item!))
.Ordering(d => d.Name);
rootDriveInfos.For(_rootDriveInfosConsumer);
RootDriveInfos = rootDriveInfos;
void InitRootDrives()
{
var drives = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
? DriveInfo.GetDrives().Where(d => d.DriveType == DriveType.Fixed)
: DriveInfo.GetDrives().Where(d =>
d.DriveType == DriveType.Fixed
&& d.DriveFormat != "pstorefs"
&& d.DriveFormat != "bpf_fs"
&& d.DriveFormat != "tracefs"
&& !d.RootDirectory.FullName.StartsWith("/snap/"));
_rootDrives.Clear();
_rootDrives.AddRange(drives);
}
}
private static bool IsNotNull(object? obj) => obj is not null;
private static (IItem? Item, DriveInfo Drive) Resolve((AbsolutePath Path, DriveInfo? Drive) tuple)
{
var t = Task.Run(async () => await tuple.Path.ResolveAsyncSafe());
t.Wait();
return (Item: t.Result, Drive: tuple.Drive!);
}
private (AbsolutePath Path, DriveInfo? Drive) MatchRootDrive(AbsolutePath sourceItem)
{
var rootDrive = _rootDrives.FirstOrDefault(d =>
{
var containerPath = _localContentProvider.GetNativePath(sourceItem.Path).Path;
var drivePath = d.Name.TrimEnd(Path.DirectorySeparatorChar);
return containerPath == drivePath
|| (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && containerPath == "/" &&
d.Name == "/");
});
return (Path: sourceItem, Drive: rootDrive);
}
public Task ExitAsync(CancellationToken token = default)
{
_rootDriveInfosConsumer.Dispose();
return Task.CompletedTask;
}
}

View File

@@ -20,6 +20,7 @@ public static class Startup
serviceCollection.TryAddSingleton<IContentReaderFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentReaderFactory<ILocalContentProvider>>());
serviceCollection.TryAddSingleton<IContentWriterFactory<ILocalContentProvider>, LocalContentWriterFactory>();
serviceCollection.TryAddSingleton<IContentWriterFactory<LocalContentProvider>>(sp => sp.GetRequiredService<IContentWriterFactory<ILocalContentProvider>>());
serviceCollection.TryAddSingleton<IRootDriveInfoService, RootDriveInfoService>();
return serviceCollection;
}
}

View File

@@ -25,4 +25,5 @@ public class RemoteContentProvider : ContentProviderBase, IRemoteContentProvider
public override Task<byte[]?> GetContentAsync(IElement element, int? maxLength = null, CancellationToken cancellationToken = default) => throw new NotImplementedException();
public override bool CanHandlePath(NativePath path) => throw new NotImplementedException();
public override VolumeSizeInfo? GetVolumeSizeInfo(FullName path) => throw new NotImplementedException();
}