Fix crash with empty fullname path

This commit is contained in:
2023-02-22 22:13:38 +01:00
parent 28bc479ee4
commit 3dccaa5243
8 changed files with 46 additions and 37 deletions

View File

@@ -128,7 +128,7 @@ public class TabPersistenceService : ITabPersistenceService
if (tab.Path == null) continue; if (tab.Path == null) continue;
IContainer? container = null; IContainer? container = null;
var path = new FullName(tab.Path); var path = FullName.CreateSafe(tab.Path);
while (true) while (true)
{ {
try try

View File

@@ -67,7 +67,7 @@ public abstract partial class ItemViewModel : IItemViewModel
IsSelected = itemViewModelType is ItemViewModelType.Main IsSelected = itemViewModelType is ItemViewModelType.Main
? parentTab.CurrentSelectedItem.Select(EqualsTo) ? parentTab.CurrentSelectedItem.Select(EqualsTo)
: Observable.Return(IsInDeespestPath()); : Observable.Return(IsInDeepestPath());
IsAlternative = sourceCollection.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0); IsAlternative = sourceCollection.Select(c => c?.Index().FirstOrDefault(i => EqualsTo(i.Value)).Key % 2 == 0);
@@ -92,7 +92,7 @@ public abstract partial class ItemViewModel : IItemViewModel
return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path; return BaseItem?.FullName?.Path is string path && path == itemViewModel?.BaseItem?.FullName?.Path;
} }
private bool IsInDeespestPath() private bool IsInDeepestPath()
{ {
if (_parentTab?.Tab?.LastDeepestSelectedPath is null if (_parentTab?.Tab?.LastDeepestSelectedPath is null
|| BaseItem?.FullName is null) || BaseItem?.FullName is null)
@@ -102,7 +102,7 @@ public abstract partial class ItemViewModel : IItemViewModel
var ownFullName = BaseItem.FullName; var ownFullName = BaseItem.FullName;
var deepestPath = _parentTab.Tab.LastDeepestSelectedPath; var deepestPath = _parentTab.Tab.LastDeepestSelectedPath;
var commonPath = new FullName(PathHelper.GetCommonPath(ownFullName.Path, deepestPath.Path)); var commonPath = FullName.CreateSafe(PathHelper.GetCommonPath(ownFullName.Path, deepestPath.Path));
return commonPath.Path == ownFullName.Path; return commonPath.Path == ownFullName.Path;
} }

View File

@@ -7,11 +7,18 @@ public record FullName(string Path)
var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar); var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar);
return pathParts.Length switch return pathParts.Length switch
{ {
> 1 => new(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))), > 1 => CreateSafe(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))),
_ => null _ => null
}; };
} }
public static FullName? CreateSafe(string? path)
{
if (string.IsNullOrWhiteSpace(path))
return null;
return new(path);
}
public string GetName() public string GetName()
=> Path.Split(Constants.SeparatorChar).Last(); => Path.Split(Constants.SeparatorChar).Last();

View File

@@ -55,7 +55,7 @@ public abstract class ContentProviderBase : IContentProvider
protected ContentProviderBase(string name) protected ContentProviderBase(string name)
{ {
DisplayName = Name = name; DisplayName = Name = name;
FullName = new FullName(name); FullName = FullName.CreateSafe(name);
Extensions = new ExtensionCollection(); Extensions = new ExtensionCollection();
_extensions = Extensions.AsReadOnly(); _extensions = Extensions.AsReadOnly();
} }

View File

@@ -1,10 +1,8 @@
using System.Collections.ObjectModel;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using DynamicData; using DynamicData;
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Services;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Core.Models; namespace FileTime.Core.Models;
@@ -28,7 +26,7 @@ public record Container(
ReadOnlyExtensionCollection Extensions, ReadOnlyExtensionCollection Extensions,
IObservable<IObservable<IChangeSet<AbsolutePath, string>>?> Items) : IContainer IObservable<IObservable<IChangeSet<AbsolutePath, string>>?> Items) : IContainer
{ {
BehaviorSubject<bool> IsLoading { get; } = new BehaviorSubject<bool>(false); BehaviorSubject<bool> IsLoading { get; } = new(false);
IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable(); IObservable<bool> IContainer.IsLoading => IsLoading.AsObservable();
public AbsolutePathType Type => AbsolutePathType.Container; public AbsolutePathType Type => AbsolutePathType.Container;
} }

View File

@@ -1,7 +1,5 @@
using System.Collections.ObjectModel;
using FileTime.Core.ContentAccess; using FileTime.Core.ContentAccess;
using FileTime.Core.Enums; using FileTime.Core.Enums;
using FileTime.Core.Services;
using FileTime.Core.Timeline; using FileTime.Core.Timeline;
namespace FileTime.Core.Models; namespace FileTime.Core.Models;

View File

@@ -37,7 +37,7 @@ public class Tab : ITab
{ {
if (_currentSelectedItemCached is not null) if (_currentSelectedItemCached is not null)
{ {
LastDeepestSelectedPath = new FullName(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, _currentSelectedItemCached.Path.Path)); LastDeepestSelectedPath = FullName.CreateSafe(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, _currentSelectedItemCached.Path.Path));
} }
}) })
.Publish(null) .Publish(null)
@@ -57,7 +57,7 @@ public class Tab : ITab
.Where(c => c is null) .Where(c => c is null)
.Select(_ => (IObservable<IChangeSet<IItem, string>>?)null) .Select(_ => (IObservable<IChangeSet<IItem, string>>?)null)
) )
.Publish((IObservable<IChangeSet<IItem, string>>?)null) .Publish(null)
.RefCount(); .RefCount();
CurrentSelectedItem = CurrentSelectedItem =
@@ -96,7 +96,7 @@ public class Tab : ITab
_currentLocation.OnNext(currentLocation); _currentLocation.OnNext(currentLocation);
} }
private AbsolutePath? GetSelectedItemByItems(IEnumerable<IItem> items) private AbsolutePath? GetSelectedItemByItems(IReadOnlyCollection<IItem> items)
{ {
if (!items.Any()) return null; if (!items.Any()) return null;
@@ -109,7 +109,7 @@ public class Tab : ITab
{ {
var itemNameToSelect = LastDeepestSelectedPath.Path var itemNameToSelect = LastDeepestSelectedPath.Path
.Split(Constants.SeparatorChar) .Split(Constants.SeparatorChar)
.Skip((parentPath?.Split(Constants.SeparatorChar).Length) ?? 0) .Skip(parentPath.Split(Constants.SeparatorChar).Length)
.FirstOrDefault(); .FirstOrDefault();
var itemToSelect = items.FirstOrDefault(i => i.FullName?.GetName() == itemNameToSelect); var itemToSelect = items.FirstOrDefault(i => i.FullName?.GetName() == itemNameToSelect);
@@ -121,7 +121,7 @@ public class Tab : ITab
} }
} }
LastDeepestSelectedPath = new FullName(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, newSelectedItem.Path.Path)); LastDeepestSelectedPath = FullName.CreateSafe(PathHelper.GetLongerPath(LastDeepestSelectedPath?.Path, newSelectedItem.Path.Path));
return newSelectedItem; return newSelectedItem;
} }

View File

@@ -76,11 +76,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{ {
if ((path?.Length ?? 0) == 0) if ((path?.Length ?? 0) == 0)
{ {
return Task.FromResult((IItem) this); return Task.FromResult((IItem)this);
} }
else if (Directory.Exists(path)) else if (Directory.Exists(path))
{ {
return Task.FromResult((IItem) DirectoryToContainer( return Task.FromResult((IItem)DirectoryToContainer(
new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar),
pointInTime, pointInTime,
!itemInitializationSettings.SkipChildInitialization) !itemInitializationSettings.SkipChildInitialization)
@@ -88,7 +88,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
} }
else if (File.Exists(path)) else if (File.Exists(path))
{ {
return Task.FromResult((IItem) FileToElement(new FileInfo(path), pointInTime)); return Task.FromResult((IItem)FileToElement(new FileInfo(path), pointInTime));
} }
var type = forceResolvePathType switch var type = forceResolvePathType switch
@@ -120,10 +120,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
return forceResolvePathType switch return forceResolvePathType switch
{ {
AbsolutePathType.Container => Task.FromResult( AbsolutePathType.Container => Task.FromResult(
(IItem) CreateEmptyContainer( (IItem)CreateEmptyContainer(
nativePath, nativePath,
pointInTime, pointInTime,
Observable.Return(new List<Exception>() {innerException}) Observable.Return(new List<Exception>() { innerException })
) )
), ),
AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)), AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)),
@@ -142,11 +142,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var fullName = GetFullName(nativePath); var fullName = GetFullName(nativePath);
var parentFullName = fullName.GetParent(); var parentFullName = fullName.GetParent();
var parent = new AbsolutePath( var parent =
_timelessContentProvider, parentFullName is null
pointInTime, ? null
parentFullName ?? new FullName(""), : new AbsolutePath(
AbsolutePathType.Container); _timelessContentProvider,
pointInTime,
parentFullName,
AbsolutePathType.Container);
return new Container( return new Container(
name, name,
@@ -208,11 +211,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
{ {
var fullName = GetFullName(directoryInfo.FullName); var fullName = GetFullName(directoryInfo.FullName);
var parentFullName = fullName.GetParent(); var parentFullName = fullName.GetParent();
var parent = new AbsolutePath( var parent =
_timelessContentProvider, parentFullName is null
pointInTime, ? null
parentFullName ?? new FullName(""), : new AbsolutePath(
AbsolutePathType.Container); _timelessContentProvider,
pointInTime,
parentFullName,
AbsolutePathType.Container);
var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>()); var exceptions = new BehaviorSubject<IEnumerable<Exception>>(Enumerable.Empty<Exception>());
return new Container( return new Container(
@@ -247,14 +253,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var items = GetItemsByContainer(directoryInfo, pointInTime); var items = GetItemsByContainer(directoryInfo, pointInTime);
var result = new SourceCache<AbsolutePath, string>(i => i.Path.Path); var result = new SourceCache<AbsolutePath, string>(i => i.Path.Path);
if (items.Count == 0) return (IObservable<IChangeSet<AbsolutePath, string>>?) result.Connect().StartWithEmpty(); if (items.Count == 0) return (IObservable<IChangeSet<AbsolutePath, string>>?)result.Connect().StartWithEmpty();
result.AddOrUpdate(items); result.AddOrUpdate(items);
return (IObservable<IChangeSet<AbsolutePath, string>>?) result.Connect(); return (IObservable<IChangeSet<AbsolutePath, string>>?)result.Connect();
} }
catch (Exception e) catch (Exception e)
{ {
exceptions.OnNext(new List<Exception> {e}); exceptions.OnNext(new List<Exception> { e });
} }
return null; return null;
@@ -299,10 +305,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path); private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path);
private FullName GetFullName(string nativePath) => private FullName GetFullName(string nativePath) =>
new((Name + Constants.SeparatorChar + FullName.CreateSafe((Name + Constants.SeparatorChar +
string.Join(Constants.SeparatorChar, string.Join(Constants.SeparatorChar,
nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar))) nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar)))
.TrimEnd(Constants.SeparatorChar)); .TrimEnd(Constants.SeparatorChar))!;
public override NativePath GetNativePath(FullName fullName) public override NativePath GetNativePath(FullName fullName)
{ {
@@ -328,7 +334,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo
var size = maxLength ?? realFileSize switch var size = maxLength ?? realFileSize switch
{ {
> int.MaxValue => int.MaxValue, > int.MaxValue => int.MaxValue,
_ => (int) realFileSize _ => (int)realFileSize
}; };
var buffer = new byte[size]; var buffer = new byte[size];
await reader.ReadAsync(buffer.AsMemory(0, size), cancellationToken); await reader.ReadAsync(buffer.AsMemory(0, size), cancellationToken);