Skip to content

Commit 687756d

Browse files
committed
Introduce VFSException class
1 parent f610291 commit 687756d

File tree

16 files changed

+269
-91
lines changed

16 files changed

+269
-91
lines changed
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem')
2+
### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions').[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException')
3+
4+
## VFSException(string) Constructor
5+
6+
Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error.
7+
8+
```csharp
9+
public VFSException(string message);
10+
```
11+
#### Parameters
12+
13+
<a name='Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string).message'></a>
14+
15+
`message` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String')
16+
17+
The error message that explains the reason for the exception.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem')
2+
### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions').[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException')
3+
4+
## VFSException(string, Exception) Constructor
5+
6+
Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause
7+
of this exception.
8+
9+
```csharp
10+
public VFSException(string message, System.Exception innerException);
11+
```
12+
#### Parameters
13+
14+
<a name='Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string,System.Exception).message'></a>
15+
16+
`message` [System.String](https://docs.microsoft.com/en-us/dotnet/api/System.String 'System.String')
17+
18+
The error message that explains the reason for the exception.
19+
20+
<a name='Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string,System.Exception).innerException'></a>
21+
22+
`innerException` [System.Exception](https://docs.microsoft.com/en-us/dotnet/api/System.Exception 'System.Exception')
23+
24+
The exception that is the cause of the current exception, or a null reference if no inner
25+
exception is specified.

docs/api/VFSException.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#### [Atypical.VirtualFileSystem.Core](VirtualFileSystem.md 'VirtualFileSystem')
2+
### [Atypical.VirtualFileSystem.Core.Exceptions](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions 'Atypical.VirtualFileSystem.Core.Exceptions')
3+
4+
## VFSException Class
5+
6+
Exception thrown by the VFS.
7+
8+
```csharp
9+
public class VFSException : System.Exception
10+
```
11+
12+
Inheritance [System.Object](https://docs.microsoft.com/en-us/dotnet/api/System.Object 'System.Object') &#129106; [System.Exception](https://docs.microsoft.com/en-us/dotnet/api/System.Exception 'System.Exception') &#129106; VFSException
13+
14+
| Constructors | |
15+
| :--- | :--- |
16+
| [VFSException(string, Exception)](VFSException.VFSException(string,Exception).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string, System.Exception)') | Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause<br/>of this exception. |
17+
| [VFSException(string)](VFSException.VFSException(string).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string)') | Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error. |

docs/api/VirtualFileSystem.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@
200200
For example, the path of the node with the path "./temp/file.txt" is "./temp/file.txt".
201201
The path of the node with the path "./temp/" is "./temp/".
202202

203+
<a name='Atypical.VirtualFileSystem.Core.Exceptions'></a>
204+
205+
## Atypical.VirtualFileSystem.Core.Exceptions Namespace
206+
- **[VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException')** `Class` Exception thrown by the VFS.
207+
- **[VFSException(string, Exception)](VFSException.VFSException(string,Exception).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string, System.Exception)')** `Constructor` Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message and an inner exception that is the cause
208+
of this exception.
209+
- **[VFSException(string)](VFSException.VFSException(string).md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException.VFSException(string)')** `Constructor` Initializes a new instance of the [VFSException](VFSException.md 'Atypical.VirtualFileSystem.Core.Exceptions.VFSException') class with a message that describes the error.
210+
203211
<a name='Atypical.VirtualFileSystem.Core.Models'></a>
204212

205213
## Atypical.VirtualFileSystem.Core.Models Namespace

docs/links

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ T:Atypical.VirtualFileSystem.Core.Models.FileNode|FileNode.md|FileNode
7171
M:Atypical.VirtualFileSystem.Core.Models.RootNode.#ctor|RootNode.RootNode().md|RootNode()
7272
M:Atypical.VirtualFileSystem.Core.Models.RootNode.ToString|RootNode.ToString().md|ToString()
7373
T:Atypical.VirtualFileSystem.Core.Models.RootNode|RootNode.md|RootNode
74+
M:Atypical.VirtualFileSystem.Core.Exceptions.VFSException.#ctor(System.String)|VFSException.VFSException(string).md|VFSException(string)
75+
M:Atypical.VirtualFileSystem.Core.Exceptions.VFSException.#ctor(System.String,System.Exception)|VFSException.VFSException(string,Exception).md|VFSException(string, Exception)
76+
N:Atypical.VirtualFileSystem.Core.Exceptions|VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Exceptions|Atypical.VirtualFileSystem.Core.Exceptions
77+
T:Atypical.VirtualFileSystem.Core.Exceptions.VFSException|VFSException.md|VFSException
7478
N:Atypical.VirtualFileSystem.Core.Contracts|VirtualFileSystem.md#Atypical.VirtualFileSystem.Core.Contracts|Atypical.VirtualFileSystem.Core.Contracts
7579
T:Atypical.VirtualFileSystem.Core.Contracts.IDirectoryNode|IDirectoryNode.md|IDirectoryNode
7680
P:Atypical.VirtualFileSystem.Core.Contracts.IFileNode.Content|IFileNode.Content.md|Content

src/Atypical.VirtualFileSystem.Core/Abstractions/VFSPath.cs

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public abstract record VFSPath
2222
/// </summary>
2323
public static readonly Regex VFSPathRegex = new(VFSPathRegexPattern, RegexOptions.Compiled);
2424

25+
26+
2527
/// <summary>
2628
/// Creates a new instance of <see cref="VFSPath" />.
2729
/// </summary>
@@ -30,7 +32,9 @@ public abstract record VFSPath
3032
/// <exception cref="ArgumentException">Thrown when the path is invalid.</exception>
3133
public VFSPath(string path)
3234
{
33-
ArgumentNullException.ThrowIfNull(path);
35+
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
36+
if (path is null)
37+
ThrowArgumentHasInvalidFormat(string.Empty);
3438

3539
var vfsPath = CleanVFSPathInput(path);
3640

@@ -41,11 +45,11 @@ public VFSPath(string path)
4145
return;
4246
}
4347

44-
if (!VFSPathRegex.IsMatch(vfsPath))
45-
throw new ArgumentException($"The path '{path}' is invalid.", nameof(path));
46-
4748
if (vfsPath.Contains($".{DIRECTORY_SEPARATOR}") || vfsPath.Contains($"{DIRECTORY_SEPARATOR}."))
48-
throw new ArgumentException($"The path '{path}' contains relative path segments.", nameof(path));
49+
ThrowArgumentHasRelativePathSegment(vfsPath);
50+
51+
if (!VFSPathRegex.IsMatch(vfsPath))
52+
ThrowArgumentHasInvalidFormat(vfsPath);
4953

5054
Value = vfsPath;
5155

@@ -143,6 +147,9 @@ private string CleanVFSPathInput(string path)
143147
// if is root path, return it
144148
if (cleanPath is ROOT_PATH or "")
145149
return cleanPath;
150+
151+
// replace backslashes with forward slashes
152+
cleanPath = cleanPath.Replace('\\', '/');
146153

147154
// clean up the path - remove leading and trailing slashes
148155
cleanPath = cleanPath.TrimStart('/');
@@ -167,8 +174,7 @@ private string CleanVFSPathInput(string path)
167174
public VFSPath GetAbsoluteParentPath(int depthFromRoot)
168175
{
169176
if (depthFromRoot < 0)
170-
throw new ArgumentOutOfRangeException(nameof(depthFromRoot),
171-
"The depth from root must be greater than or equal to 0.");
177+
ThrowDepthFromRootMustBeGreaterThanOrEqualToZero(depthFromRoot);
172178

173179
if (IsRoot)
174180
return this;
@@ -184,4 +190,33 @@ public VFSPath GetAbsoluteParentPath(int depthFromRoot)
184190
/// </summary>
185191
/// <returns>A hash code for the current object.</returns>
186192
public override int GetHashCode() => Value.GetHashCode();
187-
}
193+
194+
[DoesNotReturn]
195+
private static void ThrowArgumentHasRelativePathSegment(string vfsPath)
196+
{
197+
var message = $"The path '{vfsPath}' contains a relative path segment.";
198+
throw new VFSException(message, new ArgumentException(message));
199+
}
200+
201+
[DoesNotReturn]
202+
private static void ThrowArgumentHasInvalidFormat(string vfsPath)
203+
{
204+
var message = vfsPath.Length > 0
205+
? $"The path '{vfsPath}' is invalid."
206+
: "An empty path is invalid.";
207+
208+
throw new VFSException(message, new ArgumentException(message));
209+
}
210+
211+
[DoesNotReturn]
212+
private static void ThrowDepthFromRootMustBeGreaterThanOrEqualToZero(int depthFromRoot)
213+
{
214+
var message = $"""
215+
The depth from root must be greater than or equal to 0.
216+
Actual value: {depthFromRoot}.
217+
""";
218+
219+
throw new VFSException(message);
220+
}
221+
}
222+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
2+
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions_005Cinvaliddirectory/@EntryIndexedValue">True</s:Boolean>
3+
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=exceptions_005Cinvalidpath/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright (c) 2022, Atypical Consulting SRL
2+
// All rights reserved.
3+
//
4+
// This source code is licensed under the BSD-style license found in the
5+
// LICENSE file in the root directory of this source tree.
6+
7+
namespace Atypical.VirtualFileSystem.Core.Exceptions;
8+
9+
/// <summary>
10+
/// Exception thrown by the VFS.
11+
/// </summary>
12+
public class VFSException : Exception
13+
{
14+
/// <summary>
15+
/// Initializes a new instance of the <see cref="VFSException"/> class with a message that describes the error.
16+
/// </summary>
17+
/// <param name="message">The error message that explains the reason for the exception.</param>
18+
public VFSException(string message)
19+
: base(message)
20+
{
21+
}
22+
23+
/// <summary>
24+
/// Initializes a new instance of the <see cref="VFSException"/> class with a message and an inner exception that is the cause
25+
/// of this exception.
26+
/// </summary>
27+
/// <param name="message">The error message that explains the reason for the exception.</param>
28+
/// <param name="innerException">
29+
/// The exception that is the cause of the current exception, or a null reference if no inner
30+
/// exception is specified.
31+
/// </param>
32+
public VFSException(string message, Exception innerException)
33+
: base(message, innerException)
34+
{
35+
}
36+
}

src/Atypical.VirtualFileSystem.Core/GlobalUsings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
global using System.Text.RegularExpressions;
1313
global using Atypical.VirtualFileSystem.Core.Abstractions;
1414
global using Atypical.VirtualFileSystem.Core.Contracts;
15+
global using Atypical.VirtualFileSystem.Core.Exceptions;
1516
global using Atypical.VirtualFileSystem.Core.Models;
1617
global using Atypical.VirtualFileSystem.Core.ValueObjects;
1718
global using static Atypical.VirtualFileSystem.Core.VFSConstants;

src/Atypical.VirtualFileSystem.Core/VFS.cs

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ private void AddToIndex(IVirtualFileSystemNode node)
3939
var added = Index.TryAdd(node.Path.Value, node);
4040

4141
if (!added)
42-
throw new InvalidOperationException($"The node '{node.Path}' already exists in the index.");
42+
ThrowVirtualNodeAlreadyExists(node);
4343

4444
if (node.Path.Parent is not null && !Index.ContainsKey(node.Path.Parent.Value))
4545
CreateDirectory(node.Path.Parent);
@@ -135,7 +135,7 @@ public bool TryGetDirectory(string path, out IDirectoryNode? directory)
135135
public IVirtualFileSystem CreateDirectory(VFSDirectoryPath directoryPath)
136136
{
137137
if (directoryPath.IsRoot)
138-
throw new ArgumentException("Cannot create root directory.", nameof(directoryPath));
138+
ThrowCannotCreateRootDirectory();
139139

140140
var directory = new DirectoryNode(directoryPath);
141141
AddToIndex(directory);
@@ -150,12 +150,12 @@ public IVirtualFileSystem CreateDirectory(string path)
150150
public IVirtualFileSystem DeleteDirectory(VFSDirectoryPath directoryPath)
151151
{
152152
if (directoryPath.IsRoot)
153-
throw new ArgumentException("Cannot delete root directory.", nameof(directoryPath));
153+
ThrowCannotDeleteRootDirectory();
154154

155155
// try to get the directory
156156
var found = TryGetDirectory(directoryPath, out _);
157157
if (!found)
158-
throw new KeyNotFoundException($"The directory '{directoryPath}' does not exist.");
158+
ThrowVirtualDirectoryNotFound(directoryPath);
159159

160160
// find the path and its children in the index
161161
var paths = Index.Keys
@@ -231,7 +231,7 @@ public IVirtualFileSystem DeleteFile(VFSFilePath filePath)
231231
// try to get the file
232232
var found = TryGetFile(filePath, out _);
233233
if (!found)
234-
throw new KeyNotFoundException($"The file '{filePath}' does not exist.");
234+
ThrowVirtualFileNotFound(filePath);
235235

236236
// remove the file from the index
237237
Index.Remove(filePath.Value);
@@ -252,4 +252,39 @@ public IEnumerable<IFileNode> FindFiles(Regex regexPattern)
252252
=> FindFiles().Where(f => regexPattern.IsMatch(f.Path.Value));
253253

254254
#endregion
255+
256+
[DoesNotReturn]
257+
private static void ThrowVirtualNodeAlreadyExists(IVirtualFileSystemNode node)
258+
{
259+
var message = $"The node '{node.Path}' already exists in the index.";
260+
throw new VFSException(message);
261+
}
262+
263+
[DoesNotReturn]
264+
private static void ThrowVirtualFileNotFound(VFSFilePath filePath)
265+
{
266+
var message = $"The file '{filePath}' does not exist in the index.";
267+
throw new VFSException(message);
268+
}
269+
270+
[DoesNotReturn]
271+
private static void ThrowVirtualDirectoryNotFound(VFSDirectoryPath directoryPath)
272+
{
273+
var message = $"The directory '{directoryPath}' does not exist in the index.";
274+
throw new VFSException(message);
275+
}
276+
277+
[DoesNotReturn]
278+
private static void ThrowCannotDeleteRootDirectory()
279+
{
280+
const string message = "Cannot delete the root directory.";
281+
throw new VFSException(message);
282+
}
283+
284+
[DoesNotReturn]
285+
private static void ThrowCannotCreateRootDirectory()
286+
{
287+
const string message = "Cannot create the root directory.";
288+
throw new VFSException(message);
289+
}
255290
}

0 commit comments

Comments
 (0)