Fix crash with empty fullname path
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user