FileSystem 0.5.1
dotnet add package FileSystem --version 0.5.1
NuGet\Install-Package FileSystem -Version 0.5.1
<PackageReference Include="FileSystem" Version="0.5.1" />
paket add FileSystem --version 0.5.1
#r "nuget: FileSystem, 0.5.1"
// Install FileSystem as a Cake Addin #addin nuget:?package=FileSystem&version=0.5.1 // Install FileSystem as a Cake Tool #tool nuget:?package=FileSystem&version=0.5.1
FileSystem
An async FileSystem abstraction, with decoration and in-memory/virtual support.
Installation
This package is available on NuGet.
PM> install-package FileSystem
Usage
public class Configuration
{
private readonly IFileSystem _fileSystem;
public Configuration(IFileSystem fileSystem)
{
_fileSystem = fileSystem;
}
public async Task Load()
{
using (var stream = await _fileSystem.ReadFile("config.json"))
{
//...
}
}
public async Task Save()
{
await _fileSystem.WriteFile("config.json", async stream => {
await stream.Write(/* ... */);
});
}
}
//usage:
var config = new Configuration(new PhysicalFileSystem());
Logging
As I didn't want to take a dependency on any particular logging library, there is no out of the box implementation. However, implementing your own only takes a few lines of code, making use of the EventingFileSystem
class. For example, logging everything as Debug with Serilog:
public class LoggingFileSystem : EventingFileSystem
{
public LoggingFileSystem(IFileSystem inner) : base(inner)
{
HandleEvent = message =>
{
Log.Debug($"{message}: {{@event}}", message);
return Task.CompletedTask;
};
}
}
Each event emitted by the EventingFileSystem
has a reasonable .ToString
implementation, so you can just write Console.WriteLine(message.ToString())
if you wish.
Decoration
For ease of implementing, FileSystem supplies a FileSystemDecorator
class, which implements all IFileSystem
methods as virtual calls to an inner IFileSystem
.
For example, an encrypting filesystem could be implemented by just overriding a few methods:
public class EncryptingFileSystem : FileSystemDecorator
{
private readonly ICrypto _crypto;
public EncryptingFileSystem(IFileSystem inner, ICrypto crypto) : base(inner)
{
_crypto = crypto;
}
public override async Task<Stream> ReadFile(string path)
{
return await _crypto.DecryptStream(await base.ReadFile(path));
}
public override async Task WriteFile(string path, Func<Stream, Task> write)
{
await base.WriteFile(path, async stream =>
{
using (var encrypted = await _crypto.Encrypt(async cryptoStream => await write(cryptoStream)))
{
await encrypted.CopyToAsync(stream);
}
});
}
public override Task AppendFile(string path, Func<Stream, Task> write)
{
throw new NotSupportedException("You cannot append to an encrypted file. Try reading, and the writing the whole file.");
}
}
Testing
The easiest way of testing code using an IFileSystem
dependency is to use the InMemoryFileSystem
, which will behave the same as the physical file system.
var fileSystem = new InMemoryFileSystem();
var sut = new TestClass(fileSystem);
sut.Execute();
fileSystem
.ReadLines("./the/file.txt")
.ShouldBe(new[] { "first", "second", "third" });
Alternatly, if you want to assert on something written to a stream, e.g. on a .AppendFile()
call, you can do it manually (this example using NSubstitute):
var ms = new MemoryStream();
var fileSystem = Substitute.For<IFileSystem>();
fileSystem
.AppendFile("wat", Arg.Do<Func<Stream, Task>>(func => func(ms).Wait()))
.Returns(Task.CompletedTask);
fileSystem.AppendFile("wat", async stream => {
await stream.WriteAsync(new byte[] { 1, 2, 3 }, 0, 3);
});
ms.ToArray().ShouldBe(new byte[] { 1, 2, 3});
Or if you want to capture many streams, you can use the provided StreamCapture
class:
var streams = new StreamCapture();
var fileSystem = Substitute.For<IFileSystem>();
fileSystem
.AppendFile("wat", Arg.Do<Func<Stream, Task>>(streams.Capture))
.Returns(Task.CompletedTask);
fileSystem.AppendFile("wat", async stream => {
await stream.WriteAsync(new byte[] { 1, 2, 3 }, 0, 3);
});
streams.Last.ToArray().ShouldBe(new byte[] { 1, 2, 3});
To do
- Caching FileSystem
- Read caching I guess
- pluggable caching strategies
- Commitable FileSystem (call
.Commit()
to flush writes to disk.) - Case(In)Sensitive FileSystem?
- Not sure how this would work
- ReadFile would ListFiles first, then find the right mapping perhaps?
- S3FileSystem
- Separate package I guess
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
.NET Framework | net451 is compatible. net452 was computed. net46 was computed. net461 is compatible. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETFramework 4.5.1
- No dependencies.
-
.NETFramework 4.6.1
- No dependencies.
-
.NETStandard 2.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.