From 3dccaa524391ad21222cbd2f5fbc6207c287aa7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=81d=C3=A1m=20Kov=C3=A1cs?= Date: Wed, 22 Feb 2023 22:13:38 +0100 Subject: [PATCH] Fix crash with empty fullname path --- .../Persistence/TabPersistenceService.cs | 2 +- .../ViewModels/ItemViewModel.cs | 6 +-- .../Models/FullName.cs | 9 +++- .../ContentProviderBase.cs | 2 +- src/Core/FileTime.Core.Models/Container.cs | 4 +- src/Core/FileTime.Core.Models/Element.cs | 2 - src/Core/FileTime.Core.Services/Tab.cs | 10 ++-- .../LocalContentProvider.cs | 48 +++++++++++-------- 8 files changed, 46 insertions(+), 37 deletions(-) diff --git a/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs b/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs index 322504a..4c9e9fe 100644 --- a/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs +++ b/src/AppCommon/FileTime.App.Core/Services/Persistence/TabPersistenceService.cs @@ -128,7 +128,7 @@ public class TabPersistenceService : ITabPersistenceService if (tab.Path == null) continue; IContainer? container = null; - var path = new FullName(tab.Path); + var path = FullName.CreateSafe(tab.Path); while (true) { try diff --git a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs index d8e07b8..11263b2 100644 --- a/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs +++ b/src/AppCommon/FileTime.App.Core/ViewModels/ItemViewModel.cs @@ -67,7 +67,7 @@ public abstract partial class ItemViewModel : IItemViewModel IsSelected = itemViewModelType is ItemViewModelType.Main ? parentTab.CurrentSelectedItem.Select(EqualsTo) - : Observable.Return(IsInDeespestPath()); + : Observable.Return(IsInDeepestPath()); 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; } - private bool IsInDeespestPath() + private bool IsInDeepestPath() { if (_parentTab?.Tab?.LastDeepestSelectedPath is null || BaseItem?.FullName is null) @@ -102,7 +102,7 @@ public abstract partial class ItemViewModel : IItemViewModel var ownFullName = BaseItem.FullName; 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; } diff --git a/src/Core/FileTime.Core.Abstraction/Models/FullName.cs b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs index a1fdc9e..71a6fd5 100644 --- a/src/Core/FileTime.Core.Abstraction/Models/FullName.cs +++ b/src/Core/FileTime.Core.Abstraction/Models/FullName.cs @@ -7,11 +7,18 @@ public record FullName(string Path) var pathParts = Path.TrimEnd(Constants.SeparatorChar).Split(Constants.SeparatorChar); return pathParts.Length switch { - > 1 => new(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))), + > 1 => CreateSafe(string.Join(Constants.SeparatorChar, pathParts.SkipLast(1))), _ => null }; } + public static FullName? CreateSafe(string? path) + { + if (string.IsNullOrWhiteSpace(path)) + return null; + return new(path); + } + public string GetName() => Path.Split(Constants.SeparatorChar).Last(); diff --git a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs index 8dd8582..836a399 100644 --- a/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs +++ b/src/Core/FileTime.Core.ContentAccess/ContentProviderBase.cs @@ -55,7 +55,7 @@ public abstract class ContentProviderBase : IContentProvider protected ContentProviderBase(string name) { DisplayName = Name = name; - FullName = new FullName(name); + FullName = FullName.CreateSafe(name); Extensions = new ExtensionCollection(); _extensions = Extensions.AsReadOnly(); } diff --git a/src/Core/FileTime.Core.Models/Container.cs b/src/Core/FileTime.Core.Models/Container.cs index 49beda4..6210e7a 100644 --- a/src/Core/FileTime.Core.Models/Container.cs +++ b/src/Core/FileTime.Core.Models/Container.cs @@ -1,10 +1,8 @@ -using System.Collections.ObjectModel; using System.Reactive.Linq; using System.Reactive.Subjects; using DynamicData; using FileTime.Core.ContentAccess; using FileTime.Core.Enums; -using FileTime.Core.Services; using FileTime.Core.Timeline; namespace FileTime.Core.Models; @@ -28,7 +26,7 @@ public record Container( ReadOnlyExtensionCollection Extensions, IObservable>?> Items) : IContainer { - BehaviorSubject IsLoading { get; } = new BehaviorSubject(false); + BehaviorSubject IsLoading { get; } = new(false); IObservable IContainer.IsLoading => IsLoading.AsObservable(); public AbsolutePathType Type => AbsolutePathType.Container; } \ No newline at end of file diff --git a/src/Core/FileTime.Core.Models/Element.cs b/src/Core/FileTime.Core.Models/Element.cs index e1fb12e..36b0710 100644 --- a/src/Core/FileTime.Core.Models/Element.cs +++ b/src/Core/FileTime.Core.Models/Element.cs @@ -1,7 +1,5 @@ -using System.Collections.ObjectModel; using FileTime.Core.ContentAccess; using FileTime.Core.Enums; -using FileTime.Core.Services; using FileTime.Core.Timeline; namespace FileTime.Core.Models; diff --git a/src/Core/FileTime.Core.Services/Tab.cs b/src/Core/FileTime.Core.Services/Tab.cs index 26a0592..17bcd63 100644 --- a/src/Core/FileTime.Core.Services/Tab.cs +++ b/src/Core/FileTime.Core.Services/Tab.cs @@ -37,7 +37,7 @@ public class Tab : ITab { 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) @@ -57,7 +57,7 @@ public class Tab : ITab .Where(c => c is null) .Select(_ => (IObservable>?)null) ) - .Publish((IObservable>?)null) + .Publish(null) .RefCount(); CurrentSelectedItem = @@ -96,7 +96,7 @@ public class Tab : ITab _currentLocation.OnNext(currentLocation); } - private AbsolutePath? GetSelectedItemByItems(IEnumerable items) + private AbsolutePath? GetSelectedItemByItems(IReadOnlyCollection items) { if (!items.Any()) return null; @@ -109,7 +109,7 @@ public class Tab : ITab { var itemNameToSelect = LastDeepestSelectedPath.Path .Split(Constants.SeparatorChar) - .Skip((parentPath?.Split(Constants.SeparatorChar).Length) ?? 0) + .Skip(parentPath.Split(Constants.SeparatorChar).Length) .FirstOrDefault(); 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; } diff --git a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs index 3483937..9e7237e 100644 --- a/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs +++ b/src/Providers/FileTime.Providers.Local/LocalContentProvider.cs @@ -76,11 +76,11 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo { if ((path?.Length ?? 0) == 0) { - return Task.FromResult((IItem) this); + return Task.FromResult((IItem)this); } else if (Directory.Exists(path)) { - return Task.FromResult((IItem) DirectoryToContainer( + return Task.FromResult((IItem)DirectoryToContainer( new DirectoryInfo(path!.TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar), pointInTime, !itemInitializationSettings.SkipChildInitialization) @@ -88,7 +88,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo } 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 @@ -120,10 +120,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo return forceResolvePathType switch { AbsolutePathType.Container => Task.FromResult( - (IItem) CreateEmptyContainer( + (IItem)CreateEmptyContainer( nativePath, pointInTime, - Observable.Return(new List() {innerException}) + Observable.Return(new List() { innerException }) ) ), AbsolutePathType.Element => Task.FromResult(CreateEmptyElement(nativePath)), @@ -142,11 +142,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo var fullName = GetFullName(nativePath); var parentFullName = fullName.GetParent(); - var parent = new AbsolutePath( - _timelessContentProvider, - pointInTime, - parentFullName ?? new FullName(""), - AbsolutePathType.Container); + var parent = + parentFullName is null + ? null + : new AbsolutePath( + _timelessContentProvider, + pointInTime, + parentFullName, + AbsolutePathType.Container); return new Container( name, @@ -208,11 +211,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo { var fullName = GetFullName(directoryInfo.FullName); var parentFullName = fullName.GetParent(); - var parent = new AbsolutePath( - _timelessContentProvider, - pointInTime, - parentFullName ?? new FullName(""), - AbsolutePathType.Container); + var parent = + parentFullName is null + ? null + : new AbsolutePath( + _timelessContentProvider, + pointInTime, + parentFullName, + AbsolutePathType.Container); var exceptions = new BehaviorSubject>(Enumerable.Empty()); return new Container( @@ -247,14 +253,14 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo var items = GetItemsByContainer(directoryInfo, pointInTime); var result = new SourceCache(i => i.Path.Path); - if (items.Count == 0) return (IObservable>?) result.Connect().StartWithEmpty(); + if (items.Count == 0) return (IObservable>?)result.Connect().StartWithEmpty(); result.AddOrUpdate(items); - return (IObservable>?) result.Connect(); + return (IObservable>?)result.Connect(); } catch (Exception e) { - exceptions.OnNext(new List {e}); + exceptions.OnNext(new List { e }); } return null; @@ -299,10 +305,10 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo private FullName GetFullName(NativePath nativePath) => GetFullName(nativePath.Path); private FullName GetFullName(string nativePath) => - new((Name + Constants.SeparatorChar + + FullName.CreateSafe((Name + Constants.SeparatorChar + string.Join(Constants.SeparatorChar, nativePath.TrimStart(Constants.SeparatorChar).Split(Path.DirectorySeparatorChar))) - .TrimEnd(Constants.SeparatorChar)); + .TrimEnd(Constants.SeparatorChar))!; public override NativePath GetNativePath(FullName fullName) { @@ -328,7 +334,7 @@ public sealed partial class LocalContentProvider : ContentProviderBase, ILocalCo var size = maxLength ?? realFileSize switch { > int.MaxValue => int.MaxValue, - _ => (int) realFileSize + _ => (int)realFileSize }; var buffer = new byte[size]; await reader.ReadAsync(buffer.AsMemory(0, size), cancellationToken);