From 98920b840c0089b51ab4a5a88f9c2d50426f7d6f Mon Sep 17 00:00:00 2001 From: rameel Date: Tue, 5 Aug 2025 02:10:57 +0500 Subject: [PATCH 1/3] Add VirtualNodeProperties.None to represent a node with no data --- .../VirtualNodeProperties.cs | 8 +++++++- .../VirtualDirectoryAdapter.cs | 4 ++-- src/Ramstack.FileSystem.Amazon/S3Directory.cs | 7 ++----- src/Ramstack.FileSystem.Azure/AzureDirectory.cs | 7 ++----- src/Ramstack.FileSystem.Composite/CompositeDirectory.cs | 7 ++----- src/Ramstack.FileSystem.Prefixed/PrefixedFileSystem.cs | 7 ++----- src/Ramstack.FileSystem.Zip/ZipDirectory.cs | 9 +++------ 7 files changed, 20 insertions(+), 29 deletions(-) diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualNodeProperties.cs b/src/Ramstack.FileSystem.Abstractions/VirtualNodeProperties.cs index f991e7a..503f9c5 100644 --- a/src/Ramstack.FileSystem.Abstractions/VirtualNodeProperties.cs +++ b/src/Ramstack.FileSystem.Abstractions/VirtualNodeProperties.cs @@ -9,11 +9,17 @@ namespace Ramstack.FileSystem; public sealed class VirtualNodeProperties { /// - /// Gets an instance of that represents a node with no data or an unavailable state. + /// Gets an instance of that represents a node with an unavailable state. /// public static VirtualNodeProperties Unavailable { get; } = new VirtualNodeProperties(default, default, default, -1); + /// + /// Gets an instance of that represents a node with no data. + /// + public static VirtualNodeProperties None { get; } = + new VirtualNodeProperties(default, default, default, 0); + /// /// Gets the time when the current file or directory was created. /// diff --git a/src/Ramstack.FileSystem.Adapters/VirtualDirectoryAdapter.cs b/src/Ramstack.FileSystem.Adapters/VirtualDirectoryAdapter.cs index f59abb3..672610c 100644 --- a/src/Ramstack.FileSystem.Adapters/VirtualDirectoryAdapter.cs +++ b/src/Ramstack.FileSystem.Adapters/VirtualDirectoryAdapter.cs @@ -46,8 +46,8 @@ protected override void RefreshCore() protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) { var properties = _file?.Exists ?? _directory!.Exists - ? VirtualNodeProperties.CreateDirectoryProperties(default, default, default) - : null; + ? VirtualNodeProperties.None + : VirtualNodeProperties.Unavailable; return new ValueTask(properties); } diff --git a/src/Ramstack.FileSystem.Amazon/S3Directory.cs b/src/Ramstack.FileSystem.Amazon/S3Directory.cs index 0207d57..b2f52c4 100644 --- a/src/Ramstack.FileSystem.Amazon/S3Directory.cs +++ b/src/Ramstack.FileSystem.Amazon/S3Directory.cs @@ -28,11 +28,8 @@ public S3Directory(AmazonS3FileSystem fileSystem, string path) : base(path) } /// - protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) - { - var properties = VirtualNodeProperties.CreateDirectoryProperties(default, default, default); - return new ValueTask(properties); - } + protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) => + new ValueTask(VirtualNodeProperties.None); /// protected override ValueTask CreateCoreAsync(CancellationToken cancellationToken) => diff --git a/src/Ramstack.FileSystem.Azure/AzureDirectory.cs b/src/Ramstack.FileSystem.Azure/AzureDirectory.cs index 13736bf..334382e 100644 --- a/src/Ramstack.FileSystem.Azure/AzureDirectory.cs +++ b/src/Ramstack.FileSystem.Azure/AzureDirectory.cs @@ -27,11 +27,8 @@ public AzureDirectory(AzureFileSystem fileSystem, string path) : base(path) => _fs = fileSystem; /// - protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) - { - var properties = VirtualNodeProperties.CreateDirectoryProperties(default, default, default); - return new ValueTask(properties); - } + protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) => + new ValueTask(VirtualNodeProperties.None); /// protected override ValueTask ExistsCoreAsync(CancellationToken cancellationToken) => diff --git a/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs b/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs index cc36bfe..5331169 100644 --- a/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs +++ b/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs @@ -23,11 +23,8 @@ public CompositeDirectory(CompositeFileSystem fileSystem, string path) : base(pa _fs = fileSystem; /// - protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) - { - var properties = VirtualNodeProperties.CreateDirectoryProperties(default, default, default); - return new ValueTask(properties); - } + protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) => + new ValueTask(VirtualNodeProperties.None); /// protected override ValueTask CreateCoreAsync(CancellationToken cancellationToken) => diff --git a/src/Ramstack.FileSystem.Prefixed/PrefixedFileSystem.cs b/src/Ramstack.FileSystem.Prefixed/PrefixedFileSystem.cs index 6f1a4b2..291073a 100644 --- a/src/Ramstack.FileSystem.Prefixed/PrefixedFileSystem.cs +++ b/src/Ramstack.FileSystem.Prefixed/PrefixedFileSystem.cs @@ -130,11 +130,8 @@ public ArtificialDirectory(PrefixedFileSystem fileSystem, string path, VirtualDi (_fs, _directory) = (fileSystem, directory); /// - protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) - { - var properties = VirtualNodeProperties.CreateDirectoryProperties(default, default, default); - return new ValueTask(properties); - } + protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) => + new ValueTask(VirtualNodeProperties.None); /// protected override ValueTask CreateCoreAsync(CancellationToken cancellationToken) => diff --git a/src/Ramstack.FileSystem.Zip/ZipDirectory.cs b/src/Ramstack.FileSystem.Zip/ZipDirectory.cs index 09fd93f..4603915 100644 --- a/src/Ramstack.FileSystem.Zip/ZipDirectory.cs +++ b/src/Ramstack.FileSystem.Zip/ZipDirectory.cs @@ -20,16 +20,13 @@ internal sealed class ZipDirectory : VirtualDirectory /// Initializes a new instance of the class. /// /// The file system associated with this directory. - /// The path of directory. + /// The path of the directory. public ZipDirectory(ZipFileSystem fileSystem, string path) : base(path) => _fileSystem = fileSystem; /// - protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) - { - var properties = VirtualNodeProperties.CreateDirectoryProperties(default, default, default); - return new ValueTask(properties); - } + protected override ValueTask GetPropertiesCoreAsync(CancellationToken cancellationToken) => + new ValueTask(VirtualNodeProperties.None); /// protected override ValueTask ExistsCoreAsync(CancellationToken cancellationToken) => From aee41b535bb0437862800c9d139bdd5875e8e247 Mon Sep 17 00:00:00 2001 From: rameel Date: Tue, 5 Aug 2025 02:12:00 +0500 Subject: [PATCH 2/3] Clean up and formatting --- .../VirtualNode.cs | 2 +- src/Ramstack.FileSystem.Amazon/S3Directory.cs | 2 +- src/Ramstack.FileSystem.Amazon/S3UploadStream.cs | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualNode.cs b/src/Ramstack.FileSystem.Abstractions/VirtualNode.cs index d8d7bc6..62e7610 100644 --- a/src/Ramstack.FileSystem.Abstractions/VirtualNode.cs +++ b/src/Ramstack.FileSystem.Abstractions/VirtualNode.cs @@ -41,7 +41,7 @@ public abstract class VirtualNode /// The full path of the file or directory. protected VirtualNode(string path) { - Debug.Assert(path == VirtualPath.Normalize(path)); + Debug.Assert(VirtualPath.IsNormalized(path)); FullName = path; } diff --git a/src/Ramstack.FileSystem.Amazon/S3Directory.cs b/src/Ramstack.FileSystem.Amazon/S3Directory.cs index b2f52c4..2e1641a 100644 --- a/src/Ramstack.FileSystem.Amazon/S3Directory.cs +++ b/src/Ramstack.FileSystem.Amazon/S3Directory.cs @@ -24,7 +24,7 @@ internal sealed class S3Directory : VirtualDirectory public S3Directory(AmazonS3FileSystem fileSystem, string path) : base(path) { _fs = fileSystem; - _prefix = FullName == "/" ? "" : $"{FullName[1..]}/"; + _prefix = path == "/" ? "" : $"{path[1..]}/"; } /// diff --git a/src/Ramstack.FileSystem.Amazon/S3UploadStream.cs b/src/Ramstack.FileSystem.Amazon/S3UploadStream.cs index 6dc5362..0c5eaa4 100644 --- a/src/Ramstack.FileSystem.Amazon/S3UploadStream.cs +++ b/src/Ramstack.FileSystem.Amazon/S3UploadStream.cs @@ -91,13 +91,6 @@ public override int Read(byte[] buffer, int offset, int count) return 0; } - /// - public override long Seek(long offset, SeekOrigin origin) - { - Error_NotSupported(); - return 0; - } - /// public override void Write(byte[] buffer, int offset, int count) => Write(buffer.AsSpan(offset, count)); @@ -139,6 +132,13 @@ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, Cancella } } + /// + public override long Seek(long offset, SeekOrigin origin) + { + Error_NotSupported(); + return 0; + } + /// public override void SetLength(long value) => Error_NotSupported(); From da1d4bc35d2d861df34dd7fc8140b595f396b9ab Mon Sep 17 00:00:00 2001 From: rameel Date: Tue, 5 Aug 2025 02:14:10 +0500 Subject: [PATCH 3/3] Use AnyAsync instead of CountAsync for better performance --- .../VirtualFileSystemSpecificationTests.cs | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/Ramstack.FileSystem.Specification.Tests/VirtualFileSystemSpecificationTests.cs b/tests/Ramstack.FileSystem.Specification.Tests/VirtualFileSystemSpecificationTests.cs index 4f0e6b5..7aa8dac 100644 --- a/tests/Ramstack.FileSystem.Specification.Tests/VirtualFileSystemSpecificationTests.cs +++ b/tests/Ramstack.FileSystem.Specification.Tests/VirtualFileSystemSpecificationTests.cs @@ -65,8 +65,8 @@ public async Task FileTree_VirtualNode_FileSystem_MatchesIssuingFileSystemRefere using var fs = GetFileSystem(); Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var node in fs.GetFileNodesAsync("/", "**")) { @@ -85,8 +85,8 @@ public async Task Exists_ReturnsTrue_For_ExistingFile() using var fs = GetFileSystem(); Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var node in fs.GetFileNodesAsync("/", "**")) { @@ -114,8 +114,8 @@ public async Task File_OpenRead_ReturnsReadableStream() using var fs = GetFileSystem(); Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -148,8 +148,8 @@ public async Task File_OpenWrite_ReturnsWritableStream() return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -205,8 +205,8 @@ public async Task File_Write_OverwritesContent_When_OverwriteIsTrue() return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -233,8 +233,8 @@ public async Task File_Write_ThrowsException_For_ExistingFile_When_OverwriteIsFa return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -324,8 +324,8 @@ public async Task File_Readonly_OpenWrite_ThrowsException() return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -362,8 +362,8 @@ public async Task File_Readonly_Write_ThrowsException() return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) { @@ -395,8 +395,8 @@ public async Task File_Readonly_Delete_ThrowsException() return; Assert.That( - await fs.GetFilesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetFilesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var file in fs.GetFilesAsync("/", "**")) Assert.That(() => file.DeleteAsync(), Throws.Exception); @@ -705,8 +705,8 @@ public async Task Directory_Create_For_ExistingDirectory() return; Assert.That( - await fs.GetDirectoriesAsync("/", "**").CountAsync(), - Is.Not.Zero); + await fs.GetDirectoriesAsync("/", "**").AnyAsync(), + Is.True); await foreach (var directory in fs.GetDirectoriesAsync("/", "**")) await directory.CreateAsync();