Skip to content

Commit

Permalink
0.1.4 (#3)
Browse files Browse the repository at this point in the history
* Fix typo

* Add error message concatenation and formatting to facilitate best practice error chaining.
  • Loading branch information
tvandinther authored Mar 16, 2022
1 parent 242feee commit bb89448
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 3 deletions.
37 changes: 37 additions & 0 deletions Fallible.Tests/ErrorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,41 @@ public void ToString_ContainsStackTrace()
}

#endregion

#region Message Tests

[Fact]
public void Format_CorrectlyFormatsMessage()
{
const string expected = "test :test: test";
var error = new Error("test");

error.Format("test :{0}: test", error.Message);

Assert.Equal(expected, error.Message);
}

[Fact]
public void AdditionOperator_CorrectlyPrependsString_WhenStringOnLHS()
{
const string expected = "test: appended";
var error = new Error("test");

error += ": appended";

Assert.Equal(expected, error.Message);
}

[Fact]
public void AdditionOperator_CorrectlyAppendsString_WhenStringOnRHS()
{
const string expected = "prepended: test";
var error = new Error("test");

error = "prepended: " + error;

Assert.Equal(expected, error.Message);
}

#endregion
}
18 changes: 17 additions & 1 deletion Fallible/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Fallible;

public class Error : IEquatable<Error>
{
public readonly string Message;
public string Message { get; private set; }
public readonly string StackTrace;
private readonly string _callingFilePath;
private readonly string _callingMemberName;
Expand All @@ -22,7 +22,23 @@ public Error(string message, [CallerFilePath] string callingFilePath = "",
}

public static implicit operator bool(Error? error) => error is not default(Error);
public static Error operator +(string message, Error error)
{
error.Message = string.Concat(message, error.Message);
return error;
}

public static Error operator +(Error error, string message)
{
error.Message = string.Concat(error.Message, message);
return error;
}

public void Format(string format, params object[] args)
{
Message = string.Format(format, args);
}

public bool Equals(Error? other)
{
if (ReferenceEquals(null, other)) return false;
Expand Down
2 changes: 1 addition & 1 deletion Fallible/Fallible.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageVersion>0.1.3</PackageVersion>
<PackageVersion>0.1.4</PackageVersion>
<Title>Fallible</Title>
<Authors>Tom van Dinther</Authors>
<Description>An idiomatic way to explicitly define, propagate and handle error states in C#. This library is inspired by Go's errors.</Description>
Expand Down
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ public Fallible<int> GetValue(int arg)

#### Returning `void`

Fallible includes a `void` type that can be used to return *void* from a method. It does not have an accessible constructor and can only be created by using the `Fallible.Return` property.
Fallible includes a `Void` type that can be used to return *void* from a method. It does not have an accessible constructor and can only be created by using the `Fallible.Return` property.

```c#
public Fallible<Void> DoSomething()
Expand Down Expand Up @@ -133,6 +133,46 @@ For example, `DateTime.Parse` can throw two exceptions: `FormatException` and `A
var (result, error) = Fallible.Try(() => DateTime.Parse("1/1/2019"));
```

### Chaining error messages

When dealing with an `Error` object, often you may want to pass the error up the call stack. As it is passed up the call stack, the level of abstraction is increased which can give increasing context to the error message. To facilitate this best practice, the `Error` object allows string concatenation with itself.

```c#
Fallible<User> GetUserFromDB(UserId id)
{
if (!databaseIsConnected) return new Error("Database is not connected");
...
}

Fallible<User> FindUserById(UserId id)
{
var (user, error) = GetUserFromDB(id);
if (error) return "Could not find user: " + error;

return user;
}

var (user, error) = FindUserById(id);
if (error)
{
Console.WriteLine(error); // "Could not find user: Database is not connected"
}
```

Messages can also be appended by putting the string on the right hand side of the `+` operator.

```c#
return error + ": Could not find user";
```

#### Error message formatting

To ensure that error messages are not accidentally overwritten, directly setting the `Error.Message` property is not possible. If appending or prepending from the error message is not suitable, you can use the `Error.Format` method to format the message more comprehensively. This method functions exactly like the `string.Format` method and uses that in its implementation.

```c#
return error.Format("Could not find user: {0}: Aborting...", error.Message);
```

## Final Notes

If you are using this library in your project, it does not mean that you can not use exceptions. Exceptions are still an effective way of quickly returning up the call stack when the application is in a serious erroneous state. This usage would be similar to `panic()` in Go. I hope you enjoy using this library and find it an enjoyable addition to the C# coding experience.
Expand Down

0 comments on commit bb89448

Please sign in to comment.