`](/api/Rascal.Result-1.html)
+- [`Prelude`](/api/Rascal.Prelude.html)
+- [`Error`](/api/Rascal.Error.html)
diff --git a/docs/diagnostics/index.md b/docs/diagnostics/index.md
new file mode 100644
index 0000000..e797424
--- /dev/null
+++ b/docs/diagnostics/index.md
@@ -0,0 +1 @@
+The main Rascal package has a built-in suite of analyzers and code fixes. This section describes the various diagnostics which may be produced by these analyzers.
diff --git a/docs/diagnostics/rascal001.md b/docs/diagnostics/rascal001.md
new file mode 100644
index 0000000..2f82235
--- /dev/null
+++ b/docs/diagnostics/rascal001.md
@@ -0,0 +1,31 @@
+# Use 'Map' instead of 'Then(x => Ok(...))'
+
+
+
+
+Id: RASCAL001
+
+Severity: warning
+
+Has code fix: yes
+
+
+
+
+
+## Description
+
+*RASCAL001* is reported when [`Ok`](/api/Rascal.Prelude.html#Rascal_Prelude_Ok__1___0_) or any other form of result construction is used as the immediate return from the lambda inside a [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) call. Because [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) chains results, this is equivalent to a much simpler [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) call. The warning can be resolved by replacing the [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) call with a [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) which maps to the expression inside the [`Ok`](/api/Rascal.Prelude.html#Rascal_Prelude_Ok__1___0_).
+
+
+
+### Example
+
+```cs
+var a = Ok(2);
+
+var b = a.Then(x => Ok(F(x))); // RASCAL001
+
+// Fix:
+var b = a.Map(x => F(x));
+```
diff --git a/docs/diagnostics/rascal002.md b/docs/diagnostics/rascal002.md
new file mode 100644
index 0000000..f17366a
--- /dev/null
+++ b/docs/diagnostics/rascal002.md
@@ -0,0 +1,31 @@
+# Use Then instead of 'Map(...).Unnest()'
+
+
+
+
+Id: RASCAL002
+
+Severity: warning
+
+Has code fix: yes
+
+
+
+
+
+## Description
+
+*RASCAL002* is reported when [`Unnest`](/api/Rascal.ResultExtensions.html#Rascal_ResultExtensions_Unnest__1_Rascal_Result_Rascal_Result___0___) is called immediately after a [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) call. This operation is equivalent to calling [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) with the same mapping function, which improves clarity and performance. The warning can be resolved by removing the [`Unnest`](/api/Rascal.ResultExtensions.html#Rascal_ResultExtensions_Unnest__1_Rascal_Result_Rascal_Result___0___) call and replacing the [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) call with a [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__).
+
+
+
+### Example
+
+```cs
+var a = Ok(2);
+
+var b = a.Map(x => F(x)).Unnest(); // RASCAL002
+
+// Fix:
+var b = a.Then(x => F(x));
+```
diff --git a/docs/diagnostics/rascal003.md b/docs/diagnostics/rascal003.md
new file mode 100644
index 0000000..9f6a9a9
--- /dev/null
+++ b/docs/diagnostics/rascal003.md
@@ -0,0 +1,31 @@
+# Unnecessary 'Map' call with identity function
+
+
+
+
+Id: RASCAL003
+
+Severity: warning
+
+Has code fix: yes
+
+
+
+
+
+## Description
+
+*RASCAL003* is reported when [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) is used with an *identity function*, i.e. a lambda in the form `x => x`. Because [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) transforms the ok value of a result, applying an identity function onto the value does nothing and returns the same result as the input, and the call is completely useless. The warning can be resolved by removing the call altogether.
+
+
+
+### Example
+
+```cs
+var a = Ok(2);
+
+var b = a.Map(x => x); // RASCAL003
+
+// Fix:
+var b = a;
+```
diff --git a/docs/diagnostics/rascal004.md b/docs/diagnostics/rascal004.md
new file mode 100644
index 0000000..bda1e15
--- /dev/null
+++ b/docs/diagnostics/rascal004.md
@@ -0,0 +1,30 @@
+# 'To' called with same type as result
+
+
+
+
+Id: RASCAL004
+
+Severity: warning
+
+Has code fix: no
+
+
+
+
+
+## Description
+
+*RASCAL004* is reported when [`To`](/api/Rascal.Result-1.html#Rascal_Result_1_To__1_Rascal_Error_) is called with the same type as that of the result it is called on. Because `.To()` is equivalent to an `is T` pattern, calling `.To` on a `Result` where both `T`s are the same type will always succeed if the result is ok, and the call is redundant. The warning can be resolved by removing the call altogether.
+
+
+
+### Example
+
+```cs
+// Types added for clarity
+
+Result a = Ok(2);
+
+Result b = a.To(); // RASCAL004
+```
diff --git a/docs/diagnostics/rascal005.md b/docs/diagnostics/rascal005.md
new file mode 100644
index 0000000..0a854df
--- /dev/null
+++ b/docs/diagnostics/rascal005.md
@@ -0,0 +1,42 @@
+# 'To' called with impossible type
+
+
+
+
+Id: RASCAL005
+
+Severity: warning
+
+Has code fix: no
+
+
+
+
+
+## Description
+
+*RASCAL005* is reported when [`To`](/api/Rascal.Result-1.html#Rascal_Result_1_To__1_Rascal_Error_) is called with a type which is incompatible with the type of the result. This mainly applies to class types which do not inherit each other, and structs which aren't the same type. Interfaces, type parameters, and `object` may always succeed regardless of which type they are casted to/from.
+
+
+
+### Example
+
+```cs
+// Types added for clarity
+
+Result a = Ok(2);
+
+Result b = a.To(); // RASCAL005
+```
+
+```cs
+// Types added for clarity
+
+Result a = Ok(new B());
+
+Result b = a.To(); // RASCAL005
+
+class A;
+class B : A;
+class C : A;
+```
diff --git a/docs/diagnostics/rascal006.md b/docs/diagnostics/rascal006.md
new file mode 100644
index 0000000..f74555f
--- /dev/null
+++ b/docs/diagnostics/rascal006.md
@@ -0,0 +1,40 @@
+# Use 'GetValueOr' instead of 'Match(x => x, ...)'
+
+
+
+
+Id: RASCAL006
+
+Severity: warning
+
+Has code fix: yes
+
+
+
+
+
+## Description
+
+*RASCAL006* is reported when [`Match`](/api/Rascal.Result-1.html#Rascal_Result_1_Match__1_System_Func__0___0__System_Func_Rascal_Error___0__) is called with an *identity function* as its first argument, i.e. a lambda with the form `x => x`. Such a call is equivalent to [`GetValueOr`](/api/Rascal.Result-1.html#Rascal_Result_1_GetValueOr_System_Func_Rascal_Error__0__), with the remaining argument being a function which returns a value based on the result's error. Note that [`GetValueOr`](/api/Rascal.Result-1.html#Rascal_Result_1_GetValueOr_System_Func_Rascal_Error__0__) has three different overloads which are suitable depending on whether the result's error is needed when retrieving the value. The warning can be resolved by removing the first argument and replacing the call to [`Match`](/api/Rascal.Result-1.html#Rascal_Result_1_Match__1_System_Func__0___0__System_Func_Rascal_Error___0__) with [`GetValueOr`](/api/Rascal.Result-1.html#Rascal_Result_1_GetValueOr_System_Func_Rascal_Error__0__).
+
+
+
+### Example
+
+```cs
+var a = Ok(2);
+
+var b = a.Match(x => x, e => 0); // RASCAL006
+
+// Fix:
+var b = a.DefaultOr(0);
+```
+
+```cs
+var a = Ok(2);
+
+var b = a.Match(x => x, e => F(e)); // RASCAL006
+
+// Fix:
+var b = a.DefaultOr(e => F(e));
+```
diff --git a/docs/diagnostics/rascal007.md b/docs/diagnostics/rascal007.md
new file mode 100644
index 0000000..ea108a9
--- /dev/null
+++ b/docs/diagnostics/rascal007.md
@@ -0,0 +1,21 @@
+# Missing symbol required for analysis
+
+
+
+
+Id: RASCAL007
+
+Severity: warning
+
+Has code fix: no
+
+
+
+
+
+## Description
+
+*RASCAL007* is reported if any type or member which is required by the analysis suite to perform analysis is found to be missing. The most likely cause of this is referencing the analysis assembly without referencing the core Rascal assembly, or referencing a higher version of the analysis assembly than that of the core assembly. This may also be the result of a bug in the analysis suite.
+
+> [!IMPORTANT]
+> If *RASCAL007* occurs, all analyzers from the analysis suite will completely stop working until the warning is resolved.
diff --git a/docs/diagnostics/toc.yml b/docs/diagnostics/toc.yml
new file mode 100644
index 0000000..24af9c8
--- /dev/null
+++ b/docs/diagnostics/toc.yml
@@ -0,0 +1,14 @@
+- name: RASCAL001
+ href: rascal001.md
+- name: RASCAL002
+ href: rascal002.md
+- name: RASCAL003
+ href: rascal003.md
+- name: RASCAL004
+ href: rascal004.md
+- name: RASCAL005
+ href: rascal005.md
+- name: RASCAL006
+ href: rascal006.md
+- name: RASCAL007
+ href: rascal007.md
diff --git a/docs/docfx.json b/docs/docfx.json
new file mode 100644
index 0000000..54cee53
--- /dev/null
+++ b/docs/docfx.json
@@ -0,0 +1,53 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "src": "../src",
+ "files": [
+ "Rascal/Rascal.csproj"
+ ]
+ }
+ ],
+ "dest": "api"
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "**/*.{md,yml}"
+ ],
+ "exclude": [
+ "apidocs/**.md",
+ "_site/**"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "files": [
+ "apidocs/**.md"
+ ]
+ }
+ ],
+ "output": "_site",
+ "template": [
+ "default",
+ "modern"
+ ],
+ "globalMetadata": {
+ "_appName": "Rascal Docs",
+ "_appTitle": "Rascal Docs",
+ "_enableSearch": false,
+ "pdf": false
+ }
+ }
+}
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000..a61659b
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,163 @@
+---
+_layout: landing
+---
+
+# Rascal
+
+Rascal is a simple yet powerful [result type](https://www.youtube.com/watch?v=srQt1NAHYC0&t=1018s) implementation for C#, containing a variety of utilities and standard functions for working with result types and integrating them into the rest of C#.
+
+Rascal is first and foremost an aggregate of the result types I personally find myself implementing in a majority of my own projects, and a competetor other result libraries second. As such, this library implements some things I think other result implementations are lacking, while omitting some features other libraries do implement.
+
+Additionally, Rascal comes with a suite of analyzers and code fixes to help you write better and more reliable code using the library. The documentation for these analyzers can be found in the [diagnostics documentation](/diagnostics/index.html).
+
+
+
+## Installation
+
+# [.NET CLI](#tab/cli)
+
+Run in a terminal in the root of your project:
+
+```ps1
+dotnet add package Rascal --prerelease
+```
+
+# [Package manager console](#tab/pm)
+
+Run from the Visual Studio Package Manager console:
+
+```ps1
+NuGet\Install-Package Rascal -IncludePrerelease
+```
+
+# [Script environment](#tab/repl)
+
+In environments such as [C# REPL](https://github.com/waf/CSharpRepl) or [RoslynPad](https://roslynpad.net), enter:
+
+```cs
+#r "nuget: Rascal"
+```
+
+If you wish to install a specific version of the package, specify the package version:
+
+```cs
+#r "nuget: Rascal, 1.0.1-pre"
+```
+
+# [`PackageReference`](#tab/csproj)
+
+Add under an `ItemGroup` node in your project file:
+
+```xml
+
+```
+
+Obviously, replace `1.0.1-pre` with the actual package version you want.
+
+---
+
+
+
+## Quick start
+
+After installing the package, create a file called `Usings.cs` and add the following:
+
+```cs
+global using static Rascal.Prelude;
+```
+
+[`Prelude`](/api/Rascal.Prelude.html) includes a variety of utility functions which you can now access from anywhere in your project.
+
+Now, let's pretend you have an ASP.NET Core app with the following method in a service:
+
+```cs
+public Task GetUser(int userId)
+{
+ var user = db.Users.FirstOrDefaultAsync(user => user.Id == userId);
+
+ return user;
+}
+```
+
+... you can replace it with:
+
+```cs
+public Task> GetUser(int userId) => TryAsync(async () =>
+{
+ var user = await db.Users.FirstOrDefaultAsync(user => user.Id == userId);
+
+ if (user is null) return new NotFoundError($"User with ID {userId} does not exist.");
+
+ return user;
+});
+```
+
+This code will handle catching any exceptions thrown by [`FirstOrDefaultAsync`](https://learn.microsoft.com/en-us/dotnet/api/system.data.entity.queryableextensions.firstordefaultasync) and will return a [`NotFoundError`](/api/Rascal.Errors.NotFoundError.html) if the user isn't found. Now you can use this method as such:
+
+# [Minimal API](#tab/minimal)
+
+```cs
+app.MapGet("/users/{id}", async (int id, IUserService userService) =>
+{
+ var userResult = await userService.GetUser(id);
+
+ return userResult.Match(
+ user => Results.Ok(user),
+ error => error switch
+ {
+ NotFoundError => Results.NotFound(),
+ _ => Results.Problem(detail: error.Message, statusCode: 500)
+ }
+ );
+});
+```
+
+# [Controller](#tab/controller)
+
+```cs
+public sealed class UsersController(IUserService userService) : ControllerBase
+{
+ [HttpGet("/users/{id}")]
+ public async Task Get(int id)
+ {
+ var user = await userService.GetUser(id);
+
+ return user.Match(
+ user => this.Ok(user),
+ IActionResult (error) => error switch
+ {
+ NotFoundError => this.NotFound(),
+ _ => this.Problem(detail: error.Message, statusCode: 500)
+ }
+ );
+ }
+}
+```
+
+---
+
+
+
+### More samples
+
+A plethora of additional code samples are available in the [samples](/samples/index.html) section of the documentation.
+
+
+
+### Explore the API
+
+Once you're ready to dive into the library, feel free to refer to the [API documentation](/api/index.html) for an in-depth look into each of the methods provided by the library. You can of course also explore the API through intellisense in your IDE of choice.
+
+
+
+## Other great libraries
+
+Some libraries Rascal takes inspiration from:
+
+- Rust's [std::result](https://doc.rust-lang.org/std/result)
+- Haskell's [Data.Maybe](https://hackage.haskell.org/package/base-4.19.0.0/docs/Data-Maybe.html) and [Control.Monad](https://hackage.haskell.org/package/base-4.19.0.0/docs/Control-Monad.html)
+- [Remora.Results](https://github.com/Remora/Remora.Results)
+- [Pidgin](https://github.com/benjamin-hodgson/Pidgin)
+- [SuperLinq](https://github.com/viceroypenguin/SuperLinq)
+- [HonkSharp](https://github.com/asc-community/HonkSharp)
+- [error-or](https://github.com/amantinband/error-or)
diff --git a/docs/samples/index.md b/docs/samples/index.md
new file mode 100644
index 0000000..9c30202
--- /dev/null
+++ b/docs/samples/index.md
@@ -0,0 +1,64 @@
+This article contains a variety of code samples demonstrating common usages for various parts of the Rascal library. All the samples source code can be found [here](https://github.com/thinker227/Rascal/tree/main/samples).
+
+
+
+## Creating results
+
+Results in Rascal can be created in a variety of ways, the two most common of which are through the [`Ok`](/api/Rascal.Prelude.html#Rascal_Prelude_Ok__1___0_) and [`Err`](/api/Rascal.Prelude.html#Rascal_Prelude_Err__1_Rascal_Error_) methods defined in the prelude, or through implicitly converting ok values or errors into results.
+
+[!code-csharp[](../../samples/Construction.csx#L7-L13)]
+
+
+
+## Mapping
+
+"Mapping" refers to taking a result containing some value some type (`T`) and *mapping* said value to a new value of some other type (`TNew`). The principal method of mapping is the aptly named [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__).
+
+[!code-csharp[](../../samples/Map.csx#L6-L14)]
+
+
+
+Another operation, commonly referred to as "bind" or "chaining", exists, which looks quite similar to mapping, the only difference being that the lambda you supply to the method returns a *new* result rather than a plain value. The principal method of chaining is [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___), which can be read as "a, then b, then c".
+
+[!code-csharp[](../../samples/Then.csx#L6-L19)]
+
+
+
+[`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) together make up the core of the [`Result`](/api/Rascal.Result-1.html) type, allowing for chaining multiple operations on a single result. In functional terms, these are what makes [`Result`](/api/Rascal.Result-1.html) a functor and monad respectively (although not an applicative).
+
+
+
+> [!TIP]
+> The aliases [`Select`](/api/Rascal.Result-1.html#Rascal_Result_1_Select__1_System_Func__0___0__) and [`SelectMany`](/api/Rascal.Result-1.html#Rascal_Result_1_SelectMany__1_System_Func__0_Rascal_Result___0___) are available for [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) respectively. These exist to supply support for *query expressions* as an alternative to method chaining. Query syntax can in specific situations be more readable than the method chaining alternative, although in *most* scenarios, method chaning is better. [`Select`](/api/Rascal.Result-1.html#Rascal_Result_1_Select__1_System_Func__0___0__) and [`SelectMany`](/api/Rascal.Result-1.html#Rascal_Result_1_SelectMany__1_System_Func__0_Rascal_Result___0___) should ***not*** be used outside query syntax.
+
+
+
+### Combine
+
+[`Combine`](/api/Rascal.Result-1.html#Rascal_Result_1_Combine__1_Rascal_Result___0__) is an addition to [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___) which streamlines the specific case where you have two results and want to *combine* them into a single result only if both results are ok.
+
+[!code-csharp[](../../samples/Combine.csx#L6-L15)]
+
+
+
+## Validation
+
+Rascal supports a simple way of validating the value of a result, returning an error in case the validation fails.
+
+[!code-csharp[](../../samples/Validation.csx#L8-L24)]
+
+
+
+## Exception handling
+
+One of the major kinks of adapting C# into a more functional style (such as using results) is the already existing standard of using exceptions for error-handling. Exceptions have *many* flaws, and result types explicitly exist to provide a better alternative to exceptions, but Rascal nontheless provides a way to interoperate with traditional exception-based error handling.
+
+The [`Try`](/api/Rascal.Prelude.html#Rascal_Prelude_Try__1_System_Func___0__) method in the prelude is the premiere exception-handling method, which runs another function inside a `try`-`catch` block, and returns an [`ExceptionError`](/api/Rascal.Errors.ExceptionError.html) in case an exception is thrown.
+
+[!code-csharp[](../../samples/Try.csx#L6-L11)]
+
+
+
+`Try` variants also exist for [`Map`](/api/Rascal.Result-1.html#Rascal_Result_1_Map__1_System_Func__0___0__) and [`Then`](/api/Rascal.Result-1.html#Rascal_Result_1_Then__1_System_Func__0_Rascal_Result___0___), namely [`TryMap`](/api/Rascal.Result-1.html#Rascal_Result_1_TryMap__1_System_Func__0___0__) and [`ThenTry`](/api/Rascal.Result-1.html#Rascal_Result_1_ThenTry__1_System_Func__0_Rascal_Result___0___).
+
+[!code-csharp[](../../samples/TryMap.csx#L6-L11)]
diff --git a/docs/samples/toc.yml b/docs/samples/toc.yml
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/docs/samples/toc.yml
@@ -0,0 +1 @@
+[]
diff --git a/docs/toc.yml b/docs/toc.yml
new file mode 100644
index 0000000..c378383
--- /dev/null
+++ b/docs/toc.yml
@@ -0,0 +1,13 @@
+- name: Samples
+ href: samples/
+ homepage: samples/index.md
+- name: API
+ href: api/
+ homepage: api/index.md
+- name: Diagnostics
+ href: diagnostics/
+ homepage: diagnostics/index.md
+- name: Github
+ href: https://github.com/thinker227/Rascal
+- name: Nuget
+ href: https://www.nuget.org/packages/Rascal
diff --git a/samples/.vscode/launch.json b/samples/.vscode/launch.json
new file mode 100644
index 0000000..305245b
--- /dev/null
+++ b/samples/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": ".NET Script Debug",
+ "type": "coreclr",
+ "request": "launch",
+ "program": "${env:HOME}/.dotnet/tools/dotnet-script",
+ "args": ["${file}"],
+ "windows": {
+ "program": "${env:USERPROFILE}/.dotnet/tools/dotnet-script.exe",
+ },
+ "cwd": "${workspaceFolder}",
+ "stopAtEntry": false,
+ }
+ ]
+}
\ No newline at end of file
diff --git a/samples/Combine.csx b/samples/Combine.csx
new file mode 100644
index 0000000..3dbd10e
--- /dev/null
+++ b/samples/Combine.csx
@@ -0,0 +1,19 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using static Rascal.Prelude;
+
+// Read console input and assert that it isn't null.
+var name = Console.ReadLine().NotNull();
+
+// Read console input, assert that it isn't null, then try parse it into an int.
+var age = Console.ReadLine().NotNull()
+ .Then(str => ParseR(str));
+
+// Combine the name and age results together, then map them into a person.
+var person = name.Combine(age)
+ .Map(v => new Person(v.first, v.second));
+
+Console.WriteLine(person);
+
+record Person(string Name, int Age);
diff --git a/samples/Construction.csx b/samples/Construction.csx
new file mode 100644
index 0000000..bbad9fa
--- /dev/null
+++ b/samples/Construction.csx
@@ -0,0 +1,15 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using Rascal.Errors;
+using static Rascal.Prelude;
+
+// You can create a result either through explicit Ok/Error functions...
+var explicitOk = Ok(new Person("Melody", 27));
+var explicitError = Err("Could not find person");
+
+// ... or through implicit conversions...
+Result implicitOk = new Person("Edwin", 32);
+Result implicitError = new StringError("Failed to find person");
+
+record Person(string Name, int Age);
diff --git a/samples/Map.csx b/samples/Map.csx
new file mode 100644
index 0000000..eaa15e7
--- /dev/null
+++ b/samples/Map.csx
@@ -0,0 +1,18 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using static Rascal.Prelude;
+
+var name = "Raymond";
+
+// Read console input and try parse it into an int.
+// If the input cannot be parsed, the result will be an error.
+var age = ParseR(Console.ReadLine()!);
+
+// Map the age to a new person.
+// If the age is an error, the person will also be an error.
+var person = age.Map(x => new Person(name, x));
+
+Console.WriteLine(person);
+
+record Person(string Name, int Age);
diff --git a/samples/Then.csx b/samples/Then.csx
new file mode 100644
index 0000000..e801721
--- /dev/null
+++ b/samples/Then.csx
@@ -0,0 +1,23 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using static Rascal.Prelude;
+
+// Read console input and assert that it isn't null.
+// If the input is null, the value will be an error.
+var name = Console.ReadLine().NotNull();
+
+// Chain an operation on the name which will only execute if the name is ok.
+var person = name.Then(n =>
+{
+ // Read console input, assert that it isn't null, then try parse it into an int.
+ var age = Console.ReadLine().NotNull()
+ .Then(str => ParseR(str));
+
+ // Map the age into a new person.
+ return age.Map(a => new Person(n, a));
+});
+
+Console.WriteLine(person);
+
+record Person(string Name, int Age);
diff --git a/samples/Try.csx b/samples/Try.csx
new file mode 100644
index 0000000..a67db88
--- /dev/null
+++ b/samples/Try.csx
@@ -0,0 +1,14 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using static Rascal.Prelude;
+
+// Try read console input and use the input to read the specified file.
+// If an exception is thrown, the exception will be returned as an error.
+var text = Try(() =>
+{
+ var path = Console.ReadLine()!;
+ return File.ReadAllText(path);
+});
+
+Console.WriteLine(text);
diff --git a/samples/TryMap.csx b/samples/TryMap.csx
new file mode 100644
index 0000000..bcbb803
--- /dev/null
+++ b/samples/TryMap.csx
@@ -0,0 +1,13 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using Rascal;
+using static Rascal.Prelude;
+
+// Read console input and assert that it isn't null.
+var path = Console.ReadLine().NotNull();
+
+// Try to map the input by reading a file specified by the input.
+// If ReadAllText throws an exception, the exception will be returned as an error.
+var text = path.TryMap(p => File.ReadAllText(p));
+
+Console.WriteLine(text);
diff --git a/samples/Validation.csx b/samples/Validation.csx
new file mode 100644
index 0000000..a79d587
--- /dev/null
+++ b/samples/Validation.csx
@@ -0,0 +1,28 @@
+#r "../src/Rascal/bin/Release/net8.0/Rascal.dll"
+
+using System.Text.RegularExpressions;
+using Rascal;
+using Rascal.Errors;
+using static Rascal.Prelude;
+
+// Read console input, assert that it isn't null, and validate that it matches the regex.
+var name = Console.ReadLine().NotNull()
+ .Validate(
+ str => Regex.IsMatch(str, "[A-Z][a-z]*"),
+ _ => "Name can only contain characters a-z and has to start with a capital letter.");
+
+var person = name.Then(n =>
+{
+ // Read console input, assert that it isn't null, try parse it into an int, then validate that it is greater than 0.
+ var age = Console.ReadLine().NotNull()
+ .Then(str => ParseR(str))
+ .Validate(
+ x => x > 0,
+ _ => "Age has to be greater than 0.");
+
+ return age.Map(a => new Person(n, a));
+});
+
+Console.WriteLine(person);
+
+record Person(string Name, int Age);
diff --git a/samples/omnisharp.json b/samples/omnisharp.json
new file mode 100644
index 0000000..3789428
--- /dev/null
+++ b/samples/omnisharp.json
@@ -0,0 +1,6 @@
+{
+ "script": {
+ "enableScriptNuGetReferences": true,
+ "defaultTargetFramework": "net8.0"
+ }
+}
diff --git a/src/Rascal.Analysis/Diagnostics.cs b/src/Rascal.Analysis/Diagnostics.cs
index b01d84c..474b739 100644
--- a/src/Rascal.Analysis/Diagnostics.cs
+++ b/src/Rascal.Analysis/Diagnostics.cs
@@ -2,11 +2,14 @@ namespace Rascal.Analysis;
public static class Diagnostics
{
+ private const string CorrectnessCategory = "RascalCorrectness";
+ private const string AnalysisCategory = "RascalAnalysis";
+
public static DiagnosticDescriptor UseMap { get; } = new(
"RASCAL001",
"Use 'Map' instead of 'Then(x => Ok(...))'",
- "Use 'Map' instead of calling 'Ok' directly inside 'Then'",
- "Correctness",
+ "Use 'Map' instead of calling 'Ok' directly inside 'Then'.",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
"Calling 'Ok' directly inside a 'Then' call is equivalent to calling 'Map'. " +
@@ -14,9 +17,9 @@ public static class Diagnostics
public static DiagnosticDescriptor UseThen { get; } = new(
"RASCAL002",
- "Use Then instead of 'Map(...).Unnest()'",
- "Use 'Then' instead of calling 'Unnest' directly after 'Map'",
- "Correctness",
+ "Use 'Then' instead of 'Map(...).Unnest()'",
+ "Use 'Then' instead of calling 'Unnest' directly after 'Map'.",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
"Calling 'Unnest' directly after a 'Map' call is equivalent to calling 'Then'. " +
@@ -26,8 +29,8 @@ public static class Diagnostics
"RASCAL003",
"Unnecessary 'Map' call with identity function",
"This call maps {0} to itself. " +
- "The call can be safely removed because it doesn't do anything",
- "Correctness",
+ "The call can be safely removed because it doesn't do anything.",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
"Calling 'Map' with an identity function returns the same result as the input. " +
@@ -38,17 +41,18 @@ public static class Diagnostics
"'To' called with same type as result",
"This call converts '{0}' to itself and will always succeed. " +
"Remove this call to 'To' as it doesn't do anything.",
- "Correctness",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
- "Calling 'To' with the same type as that of the result will always succeed.");
+ "Calling 'To' with the same type as that of the result will always succeed. " +
+ "Remove the call as it doesn't do anything.");
public static DiagnosticDescriptor ToImpossibleType { get; } = new(
"RASCAL005",
"'To' called with impossible type",
"This call tries to convert '{0}' to '{1}', but no value of type '{0}' can be of type '{1}'. " +
"The conversion will always fail.",
- "Correctness",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
"Calling 'To' with a type which no value of the type of the result permits will always fail.");
@@ -58,18 +62,20 @@ public static class Diagnostics
"Use 'GetValueOr' instead of 'Match(x => x, ...)'",
"This call matches {0} using an identity function. " +
"Use 'GetValueOr' instead to reduce allocations.",
- "Correctness",
+ CorrectnessCategory,
DiagnosticSeverity.Warning,
true,
- "Calling 'Match' with an identity function for the 'ifOk' parameter is equivalent to 'DefaultOr'.");
+ "Calling 'Match' with an identity function for the 'ifOk' parameter is equivalent to calling 'GetValueOr'. " +
+ "Replace this call with 'GetValueOr'.");
public static DiagnosticDescriptor MissingSymbol { get; } = new(
"RASCAL007",
"Missing symbol required for analysis",
"Cannot find type or member '{0}' which is required for analysis. " +
"No analysis will be performed. " +
- "Verify that the version of the analyzer package matches that of the library, or report this as a bug.",
- "Analysis",
+ "Verify that the library is referenced and that the version of the analyzer assembly matches that of the library. " +
+ "Alternatively, this may be a bug and should be reported as such.",
+ AnalysisCategory,
DiagnosticSeverity.Warning,
true);
}
diff --git a/src/Rascal/Rascal.csproj b/src/Rascal/Rascal.csproj
index 6293443..c40e86e 100644
--- a/src/Rascal/Rascal.csproj
+++ b/src/Rascal/Rascal.csproj
@@ -24,7 +24,7 @@
thinker227
thinker227 2023
LICENSE.txt
- README.md
+ package-readme.md
true
https://github.com/thinker227/Rascal
git
@@ -32,7 +32,7 @@
-
+
diff --git a/src/Rascal/ResultExtensions.cs b/src/Rascal/ResultExtensions.cs
index d83c3ea..cb23a2e 100644
--- a/src/Rascal/ResultExtensions.cs
+++ b/src/Rascal/ResultExtensions.cs
@@ -207,4 +207,23 @@ public static async Task> CatchCancellation(this ValueTask task,
}
}
#endif
+
+#if NET7_0_OR_GREATER // Support for generic math
+
+ // These are just extension variants of their corresponding Prelude methods
+ // since extension methods in statically imported classes as not accessible.
+
+ ///
+ [Pure]
+ public static Result Parse(this string? s, IFormatProvider? provider = null, Error? error = null)
+ where T : IParsable =>
+ Prelude.ParseR(s, provider, error);
+
+ ///
+ [Pure]
+ public static Result Parse(this ReadOnlySpan s, IFormatProvider? provider = null, Error? error = null)
+ where T : ISpanParsable =>
+ Prelude.ParseR(s, provider, error);
+
+#endif
}
diff --git a/src/Rascal/package-readme.md b/src/Rascal/package-readme.md
new file mode 100644
index 0000000..7badc63
--- /dev/null
+++ b/src/Rascal/package-readme.md
@@ -0,0 +1,5 @@
+Rascal is a simple yet powerful [result type](https://www.youtube.com/watch?v=srQt1NAHYC0&t=1018s) implementation for C#, containing a variety of utilities and standard functions for working with result types and integrating them into the rest of C#.
+
+Rascal is first and foremost an aggregate of the result types I personally find myself implementing in a majority of my own projects, and a competetor other result libraries second. As such, this library implements some things I think other result implementations are lacking, while omitting some features other libraries do implement.
+
+**TODO**: Link to the Github Pages docfx documentation.