-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDatabaseFileSystem.cs
More file actions
120 lines (100 loc) · 4.09 KB
/
Copy pathDatabaseFileSystem.cs
File metadata and controls
120 lines (100 loc) · 4.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
using System.Runtime.CompilerServices;
using SquidStd.Database.Abstractions.Interfaces.Data;
using SquidStd.Vfs.Abstractions;
using SquidStd.Vfs.Abstractions.Data;
using SquidStd.Vfs.Abstractions.Interfaces;
using SquidStd.Vfs.Database.Data.Entities;
namespace SquidStd.Vfs.Database.Services;
/// <summary>
/// A <see cref="IVirtualFileSystem" /> implementation that stores files as rows in a relational
/// database via the generic <see cref="IDataAccess{TEntity}" /> abstraction (FreeSql).
/// </summary>
public sealed class DatabaseFileSystem : IVirtualFileSystem
{
private readonly IDataAccess<VfsFileEntity> _data;
/// <summary>
/// Initializes the database filesystem.
/// </summary>
/// <param name="dataAccess">The data access for <see cref="VfsFileEntity" /> rows.</param>
public DatabaseFileSystem(IDataAccess<VfsFileEntity> dataAccess)
{
_data = dataAccess;
}
/// <inheritdoc />
public async ValueTask<bool> ExistsAsync(string path, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(path);
return await _data.ExistsAsync(e => e.Path == path, cancellationToken).ConfigureAwait(false);
}
/// <inheritdoc />
public async ValueTask<byte[]?> ReadAllBytesAsync(string path, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(path);
var rows = await _data.QueryAsync(e => e.Path == path, cancellationToken).ConfigureAwait(false);
return rows.Count > 0 ? rows[0].Content : null;
}
/// <inheritdoc />
public async ValueTask WriteAllBytesAsync(
string path,
ReadOnlyMemory<byte> data,
CancellationToken cancellationToken = default
)
{
ArgumentException.ThrowIfNullOrEmpty(path);
var rows = await _data.QueryAsync(e => e.Path == path, cancellationToken).ConfigureAwait(false);
var existing = rows.Count > 0 ? rows[0] : null;
var bytes = data.ToArray();
if (existing is not null)
{
existing.Content = bytes;
existing.Size = bytes.Length;
await _data.UpdateAsync(existing, cancellationToken).ConfigureAwait(false);
}
else
{
await _data.InsertAsync(
new VfsFileEntity { Path = path, Content = bytes, Size = bytes.Length },
cancellationToken
).ConfigureAwait(false);
}
}
/// <inheritdoc />
public async Task<Stream> OpenReadAsync(string path, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(path);
var bytes = await ReadAllBytesAsync(path, cancellationToken).ConfigureAwait(false)
?? throw new FileNotFoundException($"No file at '{path}'.", path);
return new MemoryStream(bytes, writable: false);
}
/// <inheritdoc />
public Task<Stream> OpenWriteAsync(string path, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(path);
return Task.FromResult<Stream>(
new DeferredWriteStream(
(bytes, ct) => WriteAllBytesAsync(path, bytes, ct),
cancellationToken
)
);
}
/// <inheritdoc />
public async ValueTask<bool> DeleteAsync(string path, CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrEmpty(path);
return await _data.BulkDeleteAsync(e => e.Path == path, cancellationToken).ConfigureAwait(false) > 0;
}
/// <inheritdoc />
public async IAsyncEnumerable<VfsEntry> ListAsync(
string? prefix = null,
[EnumeratorCancellation] CancellationToken cancellationToken = default
)
{
var rows = string.IsNullOrEmpty(prefix)
? await _data.QueryAsync(cancellationToken: cancellationToken).ConfigureAwait(false)
: await _data.QueryAsync(e => e.Path.StartsWith(prefix), cancellationToken).ConfigureAwait(false);
foreach (var e in rows)
{
yield return new VfsEntry(e.Path, e.Size, e.Updated);
}
}
}