diff --git a/README.md b/README.md
index e473966..73dcd07 100644
--- a/README.md
+++ b/README.md
@@ -152,9 +152,9 @@ if (!fs.IsReadOnly)
## Supported Versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Abstractions/README.md b/src/Ramstack.FileSystem.Abstractions/README.md
index fd7558e..ddf2a0f 100644
--- a/src/Ramstack.FileSystem.Abstractions/README.md
+++ b/src/Ramstack.FileSystem.Abstractions/README.md
@@ -1,4 +1,6 @@
-# Ramstack.FileSystem
+# Ramstack.FileSystem.Abstractions
+[](https://nuget.org/packages/Ramstack.FileSystem.Abstractions)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides a virtual file system abstraction.
@@ -158,9 +160,9 @@ if (!fs.IsReadOnly)
## Supported Versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Abstractions/VirtualPath.cs b/src/Ramstack.FileSystem.Abstractions/VirtualPath.cs
index ef964e0..5a938aa 100644
--- a/src/Ramstack.FileSystem.Abstractions/VirtualPath.cs
+++ b/src/Ramstack.FileSystem.Abstractions/VirtualPath.cs
@@ -12,6 +12,8 @@ namespace Ramstack.FileSystem;
/// For compatibility across different implementations of
/// and operating systems, directory separators are unified to use both
/// backslashes and forward slashes ("/" and "\").
+///
+///
/// This approach will be reviewed once a better solution is found.
///
///
@@ -221,6 +223,14 @@ public static bool IsNormalized(ReadOnlySpan path)
///
public static string Normalize(string path)
{
+ // Short-circuit optimization:
+ // Many paths are already normalized except for a missing leading slash.
+ // It's faster and more memory-efficient to first check/add the leading slash
+ // before performing full normalization.
+
+ if (!path.StartsWith('/'))
+ path = $"/{path}";
+
if (IsNormalized(path))
return path;
diff --git a/src/Ramstack.FileSystem.Adapters/README.md b/src/Ramstack.FileSystem.Adapters/README.md
index 83c0818..3a5cd9a 100644
--- a/src/Ramstack.FileSystem.Adapters/README.md
+++ b/src/Ramstack.FileSystem.Adapters/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Adapters
+[](https://nuget.org/packages/Ramstack.FileSystem.Adapters)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` for integrating with `Microsoft.Extensions.FileProviders`.
@@ -46,9 +48,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Amazon/AccessControl.cs b/src/Ramstack.FileSystem.Amazon/AccessControl.cs
index cfdb0ff..aa830a9 100644
--- a/src/Ramstack.FileSystem.Amazon/AccessControl.cs
+++ b/src/Ramstack.FileSystem.Amazon/AccessControl.cs
@@ -1,4 +1,4 @@
-namespace Ramstack.FileSystem.Amazon;
+namespace Ramstack.FileSystem.Amazon;
///
/// An enumeration of all possible CannedACLs that can be used
diff --git a/src/Ramstack.FileSystem.Amazon/README.md b/src/Ramstack.FileSystem.Amazon/README.md
index 1238550..637e7cc 100644
--- a/src/Ramstack.FileSystem.Amazon/README.md
+++ b/src/Ramstack.FileSystem.Amazon/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Amazon
+[](https://nuget.org/packages/Ramstack.FileSystem.Amazon)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` using Amazon S3 storage.
@@ -57,9 +59,9 @@ AmazonS3FileSystem fs = new AmazonS3FileSystem(
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Amazon/S3Directory.cs b/src/Ramstack.FileSystem.Amazon/S3Directory.cs
index 7d4aae9..517e6a5 100644
--- a/src/Ramstack.FileSystem.Amazon/S3Directory.cs
+++ b/src/Ramstack.FileSystem.Amazon/S3Directory.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using Amazon.S3.Model;
@@ -180,8 +181,8 @@ protected override async IAsyncEnumerable GetFileNodesCoreAsync(str
foreach (var obj in response.S3Objects)
{
- var directoryPath = VirtualPath.GetDirectoryName(
- VirtualPath.Join("/", obj.Key));
+ var path = VirtualPath.Normalize(obj.Key);
+ var directoryPath = VirtualPath.GetDirectoryName(path);
while (directoryPath.Length != 0 && directories.Add(directoryPath))
{
@@ -193,13 +194,13 @@ protected override async IAsyncEnumerable GetFileNodesCoreAsync(str
// unnecessary memory allocation, we process them directly.
//
if (IsMatched(directoryPath.AsSpan(FullName.Length), patterns, excludes))
- yield return new S3Directory(_fs, VirtualPath.Normalize(directoryPath));
+ yield return new S3Directory(_fs, directoryPath);
directoryPath = VirtualPath.GetDirectoryName(directoryPath);
}
if (IsMatched(obj.Key.AsSpan(request.Prefix.Length), patterns, excludes))
- yield return CreateVirtualFile(obj);
+ yield return CreateVirtualFile(obj, path);
}
request.ContinuationToken = response.NextContinuationToken;
@@ -276,7 +277,7 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
foreach (var obj in response.S3Objects)
{
var directoryPath = VirtualPath.GetDirectoryName(
- VirtualPath.Join("/", obj.Key));
+ VirtualPath.Normalize(obj.Key));
while (directoryPath.Length != 0 && directories.Add(directoryPath))
{
@@ -288,7 +289,7 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
// unnecessary memory allocation, we process them directly.
//
if (IsMatched(directoryPath.AsSpan(FullName.Length), patterns, excludes))
- yield return new S3Directory(_fs, VirtualPath.Normalize(directoryPath));
+ yield return new S3Directory(_fs, directoryPath);
directoryPath = VirtualPath.GetDirectoryName(directoryPath);
}
@@ -303,10 +304,11 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
/// Creates a instance based on the specified object.
///
/// The representing the file.
+ /// The normalized name of the object.
///
/// A new instance representing the file.
///
- private S3File CreateVirtualFile(S3Object obj)
+ private S3File CreateVirtualFile(S3Object obj, string? normalizedName = null)
{
var properties = VirtualNodeProperties
.CreateFileProperties(
@@ -315,7 +317,7 @@ private S3File CreateVirtualFile(S3Object obj)
lastWriteTime: obj.LastModified,
length: obj.Size);
- var path = VirtualPath.Normalize(obj.Key);
+ var path = normalizedName ?? VirtualPath.Normalize(obj.Key);
return new S3File(_fs, path, properties);
}
@@ -363,6 +365,9 @@ private static bool IsMatched(scoped ReadOnlySpan path, string[] patterns,
/// GetPrefix("/sub/folder") // returns "sub/folder/"
///
///
- private static string GetPrefix(string path) =>
- path == "/" ? "" : $"{path[1..]}/";
+ private static string GetPrefix(string path)
+ {
+ Debug.Assert(VirtualPath.IsNormalized(path));
+ return path == "/" ? "" : $"{path[1..]}/";
+ }
}
diff --git a/src/Ramstack.FileSystem.Amazon/Utilities/NullSynchronizationContext.cs b/src/Ramstack.FileSystem.Amazon/Utilities/NullSynchronizationContext.cs
index adedfa2..b7404bc 100644
--- a/src/Ramstack.FileSystem.Amazon/Utilities/NullSynchronizationContext.cs
+++ b/src/Ramstack.FileSystem.Amazon/Utilities/NullSynchronizationContext.cs
@@ -1,4 +1,4 @@
-namespace Ramstack.FileSystem.Amazon.Utilities;
+namespace Ramstack.FileSystem.Amazon.Utilities;
///
/// Provides a mechanism to temporarily set the to .
diff --git a/src/Ramstack.FileSystem.Azure/AzureDirectory.cs b/src/Ramstack.FileSystem.Azure/AzureDirectory.cs
index db9d180..f14a92c 100644
--- a/src/Ramstack.FileSystem.Azure/AzureDirectory.cs
+++ b/src/Ramstack.FileSystem.Azure/AzureDirectory.cs
@@ -1,4 +1,5 @@
using System.Collections.ObjectModel;
+using System.Diagnostics;
using System.Runtime.CompilerServices;
using Azure;
@@ -168,8 +169,8 @@ protected override async IAsyncEnumerable GetFileNodesCoreAsync(str
{
foreach (var blob in page.Values)
{
- var directoryPath = VirtualPath.GetDirectoryName(
- VirtualPath.Join("/", blob.Name));
+ var path = VirtualPath.Normalize(blob.Name);
+ var directoryPath = VirtualPath.GetDirectoryName(path);
while (directoryPath.Length != 0 && directories.Add(directoryPath))
{
@@ -181,13 +182,13 @@ protected override async IAsyncEnumerable GetFileNodesCoreAsync(str
// unnecessary memory allocation, we process them directly.
//
if (IsMatched(directoryPath.AsSpan(FullName.Length), patterns, excludes))
- yield return new AzureDirectory(_fs, VirtualPath.Normalize(directoryPath));
+ yield return new AzureDirectory(_fs, directoryPath);
directoryPath = VirtualPath.GetDirectoryName(directoryPath);
}
if (IsMatched(blob.Name.AsSpan(prefix.Length), patterns, excludes))
- yield return CreateVirtualFile(blob);
+ yield return CreateVirtualFile(blob, path);
}
}
}
@@ -246,7 +247,7 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
foreach (var blob in page.Values)
{
var directoryPath = VirtualPath.GetDirectoryName(
- VirtualPath.Join("/", blob.Name));
+ VirtualPath.Normalize(blob.Name));
while (directoryPath.Length != 0 && directories.Add(directoryPath))
{
@@ -258,7 +259,7 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
// unnecessary memory allocation, we process them directly.
//
if (IsMatched(directoryPath.AsSpan(FullName.Length), patterns, excludes))
- yield return new AzureDirectory(_fs, VirtualPath.Normalize(directoryPath));
+ yield return new AzureDirectory(_fs, directoryPath);
directoryPath = VirtualPath.GetDirectoryName(directoryPath);
}
@@ -270,10 +271,11 @@ protected override async IAsyncEnumerable GetDirectoriesCoreAs
/// Creates a instance based on the specified blob item.
///
/// The representing the file.
+ /// The normalized name of the blob.
///
/// A new instance representing the file.
///
- private AzureFile CreateVirtualFile(BlobItem blob)
+ private AzureFile CreateVirtualFile(BlobItem blob, string? normalizedPath = null)
{
var info = blob.Properties;
var properties = VirtualNodeProperties.CreateFileProperties(
@@ -282,7 +284,7 @@ private AzureFile CreateVirtualFile(BlobItem blob)
lastWriteTime: info.LastModified.GetValueOrDefault(),
length: info.ContentLength.GetValueOrDefault(defaultValue: -1));
- var path = VirtualPath.Normalize(blob.Name);
+ var path = normalizedPath ?? VirtualPath.Normalize(blob.Name);
return new AzureFile(_fs, path, properties);
}
@@ -329,6 +331,9 @@ private static bool IsMatched(scoped ReadOnlySpan path, string[] patterns,
/// GetPrefix("/sub/folder") // returns "sub/folder/"
///
///
- private static string GetPrefix(string path) =>
- path == "/" ? "" : $"{path[1..]}/";
+ private static string GetPrefix(string path)
+ {
+ Debug.Assert(VirtualPath.IsNormalized(path));
+ return path == "/" ? "" : $"{path[1..]}/";
+ }
}
diff --git a/src/Ramstack.FileSystem.Azure/AzureFileSystem.cs b/src/Ramstack.FileSystem.Azure/AzureFileSystem.cs
index 2860e38..a756947 100644
--- a/src/Ramstack.FileSystem.Azure/AzureFileSystem.cs
+++ b/src/Ramstack.FileSystem.Azure/AzureFileSystem.cs
@@ -158,7 +158,7 @@ void IDisposable.Dispose()
///
internal BlobClient CreateBlobClient(string path)
{
- Debug.Assert(path == VirtualPath.Normalize(path));
+ Debug.Assert(VirtualPath.IsNormalized(path));
return AzureClient.GetBlobClient(path[1..]);
}
}
diff --git a/src/Ramstack.FileSystem.Azure/README.md b/src/Ramstack.FileSystem.Azure/README.md
index 1601788..727d9a6 100644
--- a/src/Ramstack.FileSystem.Azure/README.md
+++ b/src/Ramstack.FileSystem.Azure/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Azure
+[](https://nuget.org/packages/Ramstack.FileSystem.Azure)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` based on Azure Blob Storage.
@@ -49,9 +51,9 @@ AzureFileSystem fs = new AzureFileSystem(connectionString, containerName: "stora
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs b/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs
index 5331169..4622791 100644
--- a/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs
+++ b/src/Ramstack.FileSystem.Composite/CompositeDirectory.cs
@@ -46,12 +46,46 @@ protected override async IAsyncEnumerable GetFileNodesCoreAsync([En
if (node is NotFoundFile or NotFoundDirectory)
continue;
- if (!set.Add(node.FullName))
+ if (set.Add(node.FullName))
+ yield return node is VirtualFile file
+ ? new CompositeFile(_fs, node.FullName, file)
+ : new CompositeDirectory(_fs, node.FullName);
+ }
+ }
+ }
+
+ ///
+ protected override async IAsyncEnumerable GetFilesCoreAsync([EnumeratorCancellation] CancellationToken cancellationToken)
+ {
+ var set = new HashSet();
+
+ foreach (var fs in _fs.InternalFileSystems)
+ {
+ await foreach (var file in fs.GetFilesAsync(FullName, cancellationToken).ConfigureAwait(false))
+ {
+ if (file is NotFoundFile)
+ continue;
+
+ if (set.Add(file.FullName))
+ yield return new CompositeFile(_fs, file.FullName, file);
+ }
+ }
+ }
+
+ ///
+ protected override async IAsyncEnumerable GetDirectoriesCoreAsync([EnumeratorCancellation] CancellationToken cancellationToken)
+ {
+ var set = new HashSet();
+
+ foreach (var fs in _fs.InternalFileSystems)
+ {
+ await foreach (var directory in fs.GetDirectoriesAsync(FullName, cancellationToken).ConfigureAwait(false))
+ {
+ if (directory is NotFoundDirectory)
continue;
- yield return node is VirtualFile file
- ? new CompositeFile(_fs, node.FullName, file)
- : new CompositeDirectory(_fs, node.FullName);
+ if (set.Add(directory.FullName))
+ yield return new CompositeDirectory(_fs, directory.FullName);
}
}
}
diff --git a/src/Ramstack.FileSystem.Composite/CompositeFileSystem.Helpers.cs b/src/Ramstack.FileSystem.Composite/CompositeFileSystem.Helpers.cs
index 9b4bc16..2bc0a52 100644
--- a/src/Ramstack.FileSystem.Composite/CompositeFileSystem.Helpers.cs
+++ b/src/Ramstack.FileSystem.Composite/CompositeFileSystem.Helpers.cs
@@ -1,4 +1,4 @@
-using Ramstack.FileSystem.Null;
+using Ramstack.FileSystem.Null;
namespace Ramstack.FileSystem.Composite;
diff --git a/src/Ramstack.FileSystem.Composite/README.md b/src/Ramstack.FileSystem.Composite/README.md
index 24d7f06..371cfdf 100644
--- a/src/Ramstack.FileSystem.Composite/README.md
+++ b/src/Ramstack.FileSystem.Composite/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Composite
+[](https://nuget.org/packages/Ramstack.FileSystem.Composite)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` that combines multiple file systems into a single composite file system.
@@ -40,9 +42,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Globbing/README.md b/src/Ramstack.FileSystem.Globbing/README.md
index e161265..9d30996 100644
--- a/src/Ramstack.FileSystem.Globbing/README.md
+++ b/src/Ramstack.FileSystem.Globbing/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Globbing
+[](https://nuget.org/packages/Ramstack.FileSystem.Globbing)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` that applies glob-based filtering rules to determine
which files and directories of the underlying file system to include or exclude.
@@ -45,9 +47,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Google/GcsDirectory.cs b/src/Ramstack.FileSystem.Google/GcsDirectory.cs
index 80e8866..a66c20f 100644
--- a/src/Ramstack.FileSystem.Google/GcsDirectory.cs
+++ b/src/Ramstack.FileSystem.Google/GcsDirectory.cs
@@ -1,4 +1,5 @@
-using System.Net;
+using System.Diagnostics;
+using System.Net;
using System.Runtime.CompilerServices;
using Google;
@@ -306,6 +307,9 @@ private static bool IsMatched(scoped ReadOnlySpan path, string[] patterns,
/// GetPrefix("/sub/folder") // returns "sub/folder/"
///
///
- private static string GetPrefix(string path) =>
- path == "/" ? "" : $"{path[1..]}/";
+ private static string GetPrefix(string path)
+ {
+ Debug.Assert(VirtualPath.IsNormalized(path));
+ return path == "/" ? "" : $"{path[1..]}/";
+ }
}
diff --git a/src/Ramstack.FileSystem.Google/GcsFile.cs b/src/Ramstack.FileSystem.Google/GcsFile.cs
index 3c5ce08..fd69cdd 100644
--- a/src/Ramstack.FileSystem.Google/GcsFile.cs
+++ b/src/Ramstack.FileSystem.Google/GcsFile.cs
@@ -1,4 +1,4 @@
-using System.Net;
+using System.Net;
using Google;
using Google.Cloud.Storage.V1;
diff --git a/src/Ramstack.FileSystem.Google/GcsWriteStream.cs b/src/Ramstack.FileSystem.Google/GcsWriteStream.cs
index 2d4d064..99c2591 100644
--- a/src/Ramstack.FileSystem.Google/GcsWriteStream.cs
+++ b/src/Ramstack.FileSystem.Google/GcsWriteStream.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
namespace Ramstack.FileSystem.Google;
@@ -56,8 +56,11 @@ public GcsWriteStream(GoogleFileSystem fs, string objectName)
}
///
- public override int Read(byte[] array, int offset, int count) =>
- _stream.Read(array, offset, count);
+ public override int Read(byte[] array, int offset, int count)
+ {
+ Error_NotSupported();
+ return 0;
+ }
///
public override int Read(Span buffer)
@@ -80,6 +83,8 @@ public override void Write(ReadOnlySpan buffer)
catch
{
_disposed = true;
+ _stream.Close();
+
throw;
}
}
@@ -89,15 +94,17 @@ public override Task WriteAsync(byte[] buffer, int offset, int count, Cancellati
WriteAsync(buffer.AsMemory(offset, count), cancellationToken).AsTask();
///
- public override ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
+ public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default)
{
try
{
- return _stream.WriteAsync(buffer, cancellationToken);
+ await _stream.WriteAsync(buffer, cancellationToken).ConfigureAwait(false);
}
catch
{
_disposed = true;
+ _stream.Close();
+
throw;
}
}
@@ -114,12 +121,13 @@ public override void SetLength(long value) =>
Error_NotSupported();
///
- public override void Flush() =>
- _stream.Flush();
+ public override void Flush()
+ {
+ }
///
public override Task FlushAsync(CancellationToken cancellationToken) =>
- _stream.FlushAsync(cancellationToken);
+ Task.CompletedTask;
///
protected override void Dispose(bool disposing)
diff --git a/src/Ramstack.FileSystem.Google/GoogleFileSystem.cs b/src/Ramstack.FileSystem.Google/GoogleFileSystem.cs
index 6530197..bb69257 100644
--- a/src/Ramstack.FileSystem.Google/GoogleFileSystem.cs
+++ b/src/Ramstack.FileSystem.Google/GoogleFileSystem.cs
@@ -1,4 +1,4 @@
-using Google;
+using Google;
using Google.Cloud.Storage.V1;
using Google.Apis.Auth.OAuth2;
diff --git a/src/Ramstack.FileSystem.Google/README.md b/src/Ramstack.FileSystem.Google/README.md
index c6bd6a0..a7aff67 100644
--- a/src/Ramstack.FileSystem.Google/README.md
+++ b/src/Ramstack.FileSystem.Google/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Google
+[](https://nuget.org/packages/Ramstack.FileSystem.Google)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` using Google Cloud Storage.
@@ -52,9 +54,9 @@ GoogleFileSystem fs = new GoogleFileSystem(client, bucketName: "my-bucket")
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Physical/PhysicalFileSystem.cs b/src/Ramstack.FileSystem.Physical/PhysicalFileSystem.cs
index 0bf02c0..cadf419 100644
--- a/src/Ramstack.FileSystem.Physical/PhysicalFileSystem.cs
+++ b/src/Ramstack.FileSystem.Physical/PhysicalFileSystem.cs
@@ -63,7 +63,7 @@ void IDisposable.Dispose()
///
private string GetPhysicalPath(string path)
{
- Debug.Assert(path == VirtualPath.Normalize(path));
+ Debug.Assert(VirtualPath.IsNormalized(path));
return Path.Join(_root, path);
}
}
diff --git a/src/Ramstack.FileSystem.Physical/README.md b/src/Ramstack.FileSystem.Physical/README.md
index 3c4f4c7..4f56c65 100644
--- a/src/Ramstack.FileSystem.Physical/README.md
+++ b/src/Ramstack.FileSystem.Physical/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Physical
+[](https://nuget.org/packages/Ramstack.FileSystem.Physical)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` based on local file system.
@@ -47,9 +49,9 @@ PhysicalFileSystem fs = new PhysicalFileSystem(@"C:\path\to\directory")
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Prefixed/README.md b/src/Ramstack.FileSystem.Prefixed/README.md
index 64fbcf5..cdb5b11 100644
--- a/src/Ramstack.FileSystem.Prefixed/README.md
+++ b/src/Ramstack.FileSystem.Prefixed/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Prefixed
+[](https://nuget.org/packages/Ramstack.FileSystem.Prefixed)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` that adds a specified prefix to the file paths within the underlying file system.
@@ -44,9 +46,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/public/assets"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Readonly/README.md b/src/Ramstack.FileSystem.Readonly/README.md
index 8da8a9b..475a6bc 100644
--- a/src/Ramstack.FileSystem.Readonly/README.md
+++ b/src/Ramstack.FileSystem.Readonly/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Readonly
+[](https://nuget.org/packages/Ramstack.FileSystem.Readonly)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` that wraps the underlying file system, preventing any destructive operations.
@@ -41,9 +43,9 @@ await fs.DeleteFileAsync("/hello.txt");
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Sub/README.md b/src/Ramstack.FileSystem.Sub/README.md
index 079af32..35cbff2 100644
--- a/src/Ramstack.FileSystem.Sub/README.md
+++ b/src/Ramstack.FileSystem.Sub/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Sub
+[](https://nuget.org/packages/Ramstack.FileSystem.Sub)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` that wraps an underlying file system for managing files under a specific subpath.
@@ -41,9 +43,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/src/Ramstack.FileSystem.Zip/README.md b/src/Ramstack.FileSystem.Zip/README.md
index fcbb6d5..44c96fd 100644
--- a/src/Ramstack.FileSystem.Zip/README.md
+++ b/src/Ramstack.FileSystem.Zip/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Zip
+[](https://nuget.org/packages/Ramstack.FileSystem.Zip)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides an implementation of `Ramstack.FileSystem` based on ZIP archives.
@@ -40,9 +42,9 @@ await foreach (VirtualFile file in fs.GetFilesAsync("/"))
## Supported versions
-| | Version |
-|------|------------|
-| .NET | 6, 7, 8, 9 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions
diff --git a/tests/Ramstack.FileSystem.Abstractions.Tests/VirtualPathTests.cs b/tests/Ramstack.FileSystem.Abstractions.Tests/VirtualPathTests.cs
index b02ea29..8ccae68 100644
--- a/tests/Ramstack.FileSystem.Abstractions.Tests/VirtualPathTests.cs
+++ b/tests/Ramstack.FileSystem.Abstractions.Tests/VirtualPathTests.cs
@@ -116,9 +116,33 @@ public bool IsNormalized(string path) =>
public void Normalize(string path, string expected)
{
foreach (var p in GetPathVariations(path))
- Assert.That(VirtualPath.Normalize(p),Is.EqualTo(expected));
+ Assert.That(VirtualPath.Normalize(p), Is.EqualTo(expected));
}
+ [TestCase("/", "/", "//")]
+ [TestCase("", "/", "/")]
+ [TestCase("/", "", "/")]
+ [TestCase("a", "b", "a/b")]
+ [TestCase("a/", "b", "a/b")]
+ [TestCase("a", "/b", "a/b")]
+ [TestCase("a/", "/b", "a//b")]
+ [TestCase("a", "", "a")]
+ [TestCase("", "a", "a")]
+ [TestCase("/a", "/b", "/a/b")]
+ [TestCase("a", "/b", "a/b")]
+ [TestCase("/a/", "/b/", "/a//b/")]
+
+ [TestCase("a\\", "b", "a\\b")]
+ [TestCase("a", "\\b", "a\\b")]
+ [TestCase("a\\", "\\b", "a\\\\b")]
+ [TestCase("a\\", "/b", "a\\/b")]
+ [TestCase("a/", "\\b", "a/\\b")]
+
+ [TestCase("a/", "/", "a//")]
+ [TestCase("/", "/a", "//a")]
+ public void Join(string a, string b, string expected) =>
+ Assert.That(VirtualPath.Join(a, b), Is.EqualTo(expected));
+
private static string[] GetPathVariations(string path) =>
[path, path.Replace('/', '\\')];
}
diff --git a/tests/Ramstack.FileSystem.Amazon.Tests/ReadonlyAmazonFileSystemTests.cs b/tests/Ramstack.FileSystem.Amazon.Tests/ReadonlyAmazonFileSystemTests.cs
index efade71..32e2b58 100644
--- a/tests/Ramstack.FileSystem.Amazon.Tests/ReadonlyAmazonFileSystemTests.cs
+++ b/tests/Ramstack.FileSystem.Amazon.Tests/ReadonlyAmazonFileSystemTests.cs
@@ -1,4 +1,4 @@
-using Amazon;
+using Amazon;
using Amazon.Runtime;
using Amazon.S3;
diff --git a/tests/Ramstack.FileSystem.Amazon.Tests/WritableAmazonFileSystemTests.cs b/tests/Ramstack.FileSystem.Amazon.Tests/WritableAmazonFileSystemTests.cs
index f009143..6d4d567 100644
--- a/tests/Ramstack.FileSystem.Amazon.Tests/WritableAmazonFileSystemTests.cs
+++ b/tests/Ramstack.FileSystem.Amazon.Tests/WritableAmazonFileSystemTests.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using Amazon;
using Amazon.Runtime;
diff --git a/tests/Ramstack.FileSystem.Composite.Tests/CompositionHelperTests.cs b/tests/Ramstack.FileSystem.Composite.Tests/CompositionHelperTests.cs
index b883e4e..905cb75 100644
--- a/tests/Ramstack.FileSystem.Composite.Tests/CompositionHelperTests.cs
+++ b/tests/Ramstack.FileSystem.Composite.Tests/CompositionHelperTests.cs
@@ -1,4 +1,4 @@
-using Ramstack.FileSystem.Null;
+using Ramstack.FileSystem.Null;
namespace Ramstack.FileSystem.Composite;
diff --git a/tests/Ramstack.FileSystem.Composite.Tests/TestFileSystem.cs b/tests/Ramstack.FileSystem.Composite.Tests/TestFileSystem.cs
index 0d9ca98..5ff2180 100644
--- a/tests/Ramstack.FileSystem.Composite.Tests/TestFileSystem.cs
+++ b/tests/Ramstack.FileSystem.Composite.Tests/TestFileSystem.cs
@@ -1,4 +1,4 @@
-namespace Ramstack.FileSystem.Composite;
+namespace Ramstack.FileSystem.Composite;
internal sealed class TestFileSystem : IVirtualFileSystem
{
diff --git a/tests/Ramstack.FileSystem.Google.Tests/ReadonlyGoogleFileSystemTests.cs b/tests/Ramstack.FileSystem.Google.Tests/ReadonlyGoogleFileSystemTests.cs
index fc6c802..261e911 100644
--- a/tests/Ramstack.FileSystem.Google.Tests/ReadonlyGoogleFileSystemTests.cs
+++ b/tests/Ramstack.FileSystem.Google.Tests/ReadonlyGoogleFileSystemTests.cs
@@ -1,4 +1,4 @@
-using Google.Apis.Auth.OAuth2;
+using Google.Apis.Auth.OAuth2;
using Google.Cloud.Storage.V1;
using Ramstack.FileSystem.Specification.Tests;
diff --git a/tests/Ramstack.FileSystem.Google.Tests/WritableGoogleFileSystemTests.cs b/tests/Ramstack.FileSystem.Google.Tests/WritableGoogleFileSystemTests.cs
index 11f08cf..00280ea 100644
--- a/tests/Ramstack.FileSystem.Google.Tests/WritableGoogleFileSystemTests.cs
+++ b/tests/Ramstack.FileSystem.Google.Tests/WritableGoogleFileSystemTests.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using Google.Apis.Auth.OAuth2;
using Google.Cloud.Storage.V1;
diff --git a/tests/Ramstack.FileSystem.Prefixed.Tests/PrefixedFileSystemTests.cs b/tests/Ramstack.FileSystem.Prefixed.Tests/PrefixedFileSystemTests.cs
index 3b240b7..d60adb0 100644
--- a/tests/Ramstack.FileSystem.Prefixed.Tests/PrefixedFileSystemTests.cs
+++ b/tests/Ramstack.FileSystem.Prefixed.Tests/PrefixedFileSystemTests.cs
@@ -27,7 +27,7 @@ await fs.FileExistsAsync("/ea5fd219.txt"),
public async Task Directory_Delete_ArtificialDirectory_ThrowsException()
{
using var storage = new TempFileStorage();
- using var fs = new PrefixedFileSystem("/bin/apps/myapp1", new PhysicalFileSystem(storage.Root));;
+ using var fs = new PrefixedFileSystem("/bin/apps/myapp1", new PhysicalFileSystem(storage.Root));
await Assert.ThatAsync(
async () => await fs.DeleteDirectoryAsync("/"),
diff --git a/tests/Ramstack.FileSystem.Specification.Tests/README.md b/tests/Ramstack.FileSystem.Specification.Tests/README.md
index c0869c6..f84b3c8 100644
--- a/tests/Ramstack.FileSystem.Specification.Tests/README.md
+++ b/tests/Ramstack.FileSystem.Specification.Tests/README.md
@@ -1,4 +1,6 @@
# Ramstack.FileSystem.Specification.Tests
+[](https://nuget.org/packages/Ramstack.FileSystem.Specification.Tests)
+[](https://github.com/rameel/ramstack.filesystem/blob/main/LICENSE)
Provides a suite of `NUnit` tests to validate specifications for `Ramstack.FileSystem`.
@@ -43,9 +45,9 @@ public class PhysicalFileSystemSpecificationTests : VirtualFileSystemSpecificati
## Supported Versions
-| | Version |
-|------|---------|
-| .NET | 6, 7, 8 |
+| | Version |
+|------|----------------|
+| .NET | 6, 7, 8, 9, 10 |
## Contributions