Skip to content

Commit 958c04f

Browse files
committed
Complete rewrite with more methods, async support etc.
1 parent 981048a commit 958c04f

File tree

119 files changed

+69867
-717
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+69867
-717
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
🔐 Setup Instructions
2+
Tag your release Push a tag like v1.0.0 to trigger the workflow:
3+
4+
bash
5+
git tag v1.0.0
6+
git push origin v1.0.0
7+
Add your NuGet API key
8+
9+
Go to your GitHub repo → Settings → Secrets and variables → Actions
10+
11+
Add a new secret named NUGET_API_KEY with your key from nuget.org
12+
13+
Ensure your .csproj includes:
14+
15+
xml
16+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
17+
<PublishRepositoryUrl>true</PublishRepositoryUrl>
18+
<EmbedUntrackedSources>true</EmbedUntrackedSources>
19+
You now have a fully automated, tag-driven release pipeline that builds, tests, packs, and publishes your NuGet package with full Source Link and XML doc support. Want to add prerelease support or auto-versioning from Git tags next? I’ve got templates for that too.

.github/workflows/publish.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Publish NuGet Package
2+
3+
on:
4+
push:
5+
tags:
6+
- 'v*' # Triggers on version tags like v1.0.0
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: 🧾 Checkout code
14+
uses: actions/checkout@v4
15+
16+
- name: 🛠 Setup .NET
17+
uses: actions/setup-dotnet@v4
18+
with:
19+
dotnet-version: '8.x'
20+
21+
- name: 🔍 Restore dependencies
22+
run: dotnet restore
23+
24+
- name: 🧪 Run tests
25+
run: dotnet test --configuration Release --no-build
26+
27+
- name: 📦 Pack NuGet package
28+
run: dotnet pack --configuration Release --no-build --output ./nupkg
29+
30+
- name: 🚀 Publish to NuGet.org
31+
run: dotnet nuget push ./nupkg/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
32+
Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,61 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 17
4-
VisualStudioVersion = 17.0.31903.59
5-
MinimumVisualStudioVersion = 10.0.40219.1
6-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8456212E-F453-483A-8E27-7494722AE10F}"
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions", "src\AStar.Dev.Functional.Extensions\AStar.Dev.Functional.Extensions.csproj", "{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23}"
74
EndProject
8-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C168EA0-2CFB-490A-8E3A-BE08F456DD3F}"
5+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions.Tests.Unit", "test\AStar.Dev.Functional.Extensions.Tests.Unit\AStar.Dev.Functional.Extensions.Tests.Unit.csproj", "{CD5D55C2-C6D1-4E28-93FD-1A60118403CE}"
96
EndProject
10-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions", "src\AStar.Dev.Functional.Extensions\AStar.Dev.Functional.Extensions.csproj", "{3FC5E2DB-6C2C-4D72-8498-39A121722334}"
7+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{267BE110-D583-4557-80D5-0D9E80A05542}"
118
EndProject
12-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions.Tests.Unit", "test\AStar.Dev.Functional.Extensions.Tests.Unit\AStar.Dev.Functional.Extensions.Tests.Unit.csproj", "{F61660A7-160E-4654-94A5-F8D5D75FFBD6}"
13-
EndProject
14-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{419B9C55-6F41-4ED8-B87C-855B23E01C41}"
9+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{2C3EC910-77D3-475C-A9EE-E8F9F9C8C43C}"
1510
ProjectSection(SolutionItems) = preProject
16-
.github\dependabot.yml = .github\dependabot.yml
11+
.github\workflows\publish.yml = .github\workflows\publish.yml
1712
EndProjectSection
1813
EndProject
19-
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{2BB3AD42-1BED-4792-8E37-E87783AE949C}"
20-
ProjectSection(SolutionItems) = preProject
21-
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
22-
EndProjectSection
14+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.ConsoleSample", "samples\AStar.Dev.ConsoleSample\AStar.Dev.ConsoleSample.csproj", "{16513013-C9A4-4707-9D79-87A2F5BBCF5C}"
15+
EndProject
16+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.SampleApi", "samples\AStar.Dev.SampleApi\AStar.Dev.SampleApi.csproj", "{2C6E2B2F-2312-499D-A885-405FB25CFD85}"
17+
EndProject
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.SampleBlazor", "samples\AStar.Dev.SampleBlazor\AStar.Dev.SampleBlazor.csproj", "{B4A9B8A7-7548-4C0E-AA7E-36299B21240F}"
19+
EndProject
20+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{E8A4624A-D322-4CF1-A3FB-EC7E013CEC89}"
21+
EndProject
22+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D1A87C44-FB04-49D8-8C34-C6CD33944612}"
23+
EndProject
24+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{0A5D570A-ACDC-429C-A9EA-9E8D9DA7774C}"
2325
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
2729
Release|Any CPU = Release|Any CPU
2830
EndGlobalSection
29-
GlobalSection(SolutionProperties) = preSolution
30-
HideSolutionNode = FALSE
31+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
32+
{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
33+
{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23}.Debug|Any CPU.Build.0 = Debug|Any CPU
34+
{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23}.Release|Any CPU.ActiveCfg = Release|Any CPU
35+
{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23}.Release|Any CPU.Build.0 = Release|Any CPU
36+
{CD5D55C2-C6D1-4E28-93FD-1A60118403CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
37+
{CD5D55C2-C6D1-4E28-93FD-1A60118403CE}.Debug|Any CPU.Build.0 = Debug|Any CPU
38+
{CD5D55C2-C6D1-4E28-93FD-1A60118403CE}.Release|Any CPU.ActiveCfg = Release|Any CPU
39+
{CD5D55C2-C6D1-4E28-93FD-1A60118403CE}.Release|Any CPU.Build.0 = Release|Any CPU
40+
{16513013-C9A4-4707-9D79-87A2F5BBCF5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41+
{16513013-C9A4-4707-9D79-87A2F5BBCF5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
42+
{16513013-C9A4-4707-9D79-87A2F5BBCF5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
43+
{16513013-C9A4-4707-9D79-87A2F5BBCF5C}.Release|Any CPU.Build.0 = Release|Any CPU
44+
{2C6E2B2F-2312-499D-A885-405FB25CFD85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{2C6E2B2F-2312-499D-A885-405FB25CFD85}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{2C6E2B2F-2312-499D-A885-405FB25CFD85}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{2C6E2B2F-2312-499D-A885-405FB25CFD85}.Release|Any CPU.Build.0 = Release|Any CPU
48+
{B4A9B8A7-7548-4C0E-AA7E-36299B21240F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49+
{B4A9B8A7-7548-4C0E-AA7E-36299B21240F}.Debug|Any CPU.Build.0 = Debug|Any CPU
50+
{B4A9B8A7-7548-4C0E-AA7E-36299B21240F}.Release|Any CPU.ActiveCfg = Release|Any CPU
51+
{B4A9B8A7-7548-4C0E-AA7E-36299B21240F}.Release|Any CPU.Build.0 = Release|Any CPU
3152
EndGlobalSection
3253
GlobalSection(NestedProjects) = preSolution
33-
{3FC5E2DB-6C2C-4D72-8498-39A121722334} = {8456212E-F453-483A-8E27-7494722AE10F}
34-
{F61660A7-160E-4654-94A5-F8D5D75FFBD6} = {3C168EA0-2CFB-490A-8E3A-BE08F456DD3F}
35-
{2BB3AD42-1BED-4792-8E37-E87783AE949C} = {419B9C55-6F41-4ED8-B87C-855B23E01C41}
36-
EndGlobalSection
37-
GlobalSection(ProjectConfigurationPlatforms) = postSolution
38-
{3FC5E2DB-6C2C-4D72-8498-39A121722334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
39-
{3FC5E2DB-6C2C-4D72-8498-39A121722334}.Debug|Any CPU.Build.0 = Debug|Any CPU
40-
{3FC5E2DB-6C2C-4D72-8498-39A121722334}.Release|Any CPU.ActiveCfg = Release|Any CPU
41-
{3FC5E2DB-6C2C-4D72-8498-39A121722334}.Release|Any CPU.Build.0 = Release|Any CPU
42-
{F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
43-
{F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Debug|Any CPU.Build.0 = Debug|Any CPU
44-
{F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Release|Any CPU.ActiveCfg = Release|Any CPU
45-
{F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Release|Any CPU.Build.0 = Release|Any CPU
54+
{2C3EC910-77D3-475C-A9EE-E8F9F9C8C43C} = {267BE110-D583-4557-80D5-0D9E80A05542}
55+
{16513013-C9A4-4707-9D79-87A2F5BBCF5C} = {E8A4624A-D322-4CF1-A3FB-EC7E013CEC89}
56+
{2C6E2B2F-2312-499D-A885-405FB25CFD85} = {E8A4624A-D322-4CF1-A3FB-EC7E013CEC89}
57+
{B4A9B8A7-7548-4C0E-AA7E-36299B21240F} = {E8A4624A-D322-4CF1-A3FB-EC7E013CEC89}
58+
{46743FC5-9AAD-46A0-BD96-B2EEA6CE9E23} = {D1A87C44-FB04-49D8-8C34-C6CD33944612}
59+
{CD5D55C2-C6D1-4E28-93FD-1A60118403CE} = {0A5D570A-ACDC-429C-A9EA-9E8D9DA7774C}
4660
EndGlobalSection
4761
EndGlobal
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<LangVersion>latest</LangVersion>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\AStar.Dev.Functional.Extensions\AStar.Dev.Functional.Extensions.csproj"/>
13+
</ItemGroup>
14+
15+
</Project>
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
using AStar.Dev.Functional.Extensions;
2+
3+
Console.WriteLine("🔎 FunctionalResults Sample");
4+
5+
// Example: Option<T>
6+
var maybeName = GetUserInput();
7+
8+
var greeting = maybeName.Match(
9+
name => $"Hello, {name}!",
10+
() => "Hello, mysterious stranger.");
11+
12+
Console.WriteLine(greeting);
13+
14+
// Example: Result<T, E>
15+
var result = Divide(10, 0);
16+
17+
var message = result.Match(
18+
val => $"Quotient: {val}",
19+
err => $"Error: {err}");
20+
21+
Console.WriteLine(message);
22+
23+
// Example: Try<T>
24+
var risky = Try<int>.Run(() => int.Parse("not-an-int"));
25+
26+
var parsed = risky.Match(
27+
ok => $"Parsed int: {ok}",
28+
ex => $"Caught exception: {ex.Message}");
29+
30+
Console.WriteLine(parsed);
31+
32+
var numbers = new List<int> { 1, 2, 3, 4, 5 };
33+
34+
var found = numbers.FirstOrNone(n => n == 3); // returns Option<int>.Some
35+
var notFound = numbers.FirstOrNone(n => n == 10); // returns Option<int>.None
36+
37+
Console.WriteLine(found); // Output: Some(3)
38+
Console.WriteLine(notFound); // Output: None
39+
40+
var result2 = await TryFetchUsernameAsync()
41+
.MapAsync(name => name.Trim())
42+
.BindAsync(ValidateAsync)
43+
.MatchAsync(
44+
valid => Task.FromResult($"Welcome, {valid}!"),
45+
() => Task.FromResult("No valid user found.")
46+
);
47+
48+
Console.WriteLine(result2); // Output: Welcome, Jason!
49+
Option.Some("Jason");
50+
Option.None<string>();
51+
var (isSome, value) = Option.Some("hello");
52+
53+
return;
54+
55+
Option<string> GetUserInput()
56+
{
57+
Console.Write("Enter your name (or leave blank): ");
58+
var input = Console.ReadLine();
59+
60+
return string.IsNullOrWhiteSpace(input) ? Option.None<string>() : input;
61+
}
62+
63+
Result<int, string> Divide(int numerator, int denominator)
64+
{
65+
return denominator == 0
66+
? new Result<int, string>.Error("Division by zero")
67+
: new Result<int, string>.Ok(numerator / denominator);
68+
}
69+
70+
static Task<Option<string>> TryFetchUsernameAsync()
71+
{
72+
return Task.FromResult<Option<string>>(new Option<string>.Some(" Jason "));
73+
}
74+
75+
static Task<Option<string>> ValidateAsync(string name)
76+
{
77+
return Task.FromResult(name.Trim() == "Jason"
78+
? new Option<string>.Some(name.Trim())
79+
: Option.None<string>());
80+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<LangVersion>latest</LangVersion>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.6"/>
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\AStar.Dev.Functional.Extensions\AStar.Dev.Functional.Extensions.csproj"/>
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using AStar.Dev.Functional.Extensions;
2+
3+
var app = WebApplication.Create();
4+
app.MapGet("/", () => "Sample API up and running!");
5+
6+
app.MapGet("/greet", (string? name) =>
7+
{
8+
#pragma warning disable CS8604 // Possible null reference argument. That is the point...
9+
Option<string> maybeName = name;
10+
#pragma warning restore CS8604 // Possible null reference argument.
11+
12+
return maybeName.Match(
13+
some => $"Hello, {some} 👋",
14+
() => "Hello, anonymous 👻");
15+
});
16+
17+
app.MapGet("/divide", (int a, int b) =>
18+
{
19+
Result<int, string> result = b == 0
20+
? new Result<int, string>.Error("Division by zero")
21+
: new Result<int, string>.Ok(a / b);
22+
23+
return result.Match(
24+
ok => Results.Ok(new { Result = ok }),
25+
err => Results.BadRequest(new { Error = err }));
26+
});
27+
28+
app.MapGet("/parse", (string input) =>
29+
{
30+
var parsed = Try<int>.Run(() => int.Parse(input));
31+
32+
return parsed.Match(
33+
ok => Results.Ok(new { Parsed = ok }),
34+
ex => Results.BadRequest(new { Error = ex.Message }));
35+
});
36+
37+
app.Run();
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"profiles": {
4+
"http": {
5+
"commandName": "Project",
6+
"dotnetRunMessages": true,
7+
"launchBrowser": false,
8+
"applicationUrl": "http://localhost:5195",
9+
"environmentVariables": {
10+
"ASPNETCORE_ENVIRONMENT": "Development"
11+
}
12+
},
13+
"https": {
14+
"commandName": "Project",
15+
"dotnetRunMessages": true,
16+
"launchBrowser": false,
17+
"applicationUrl": "https://localhost:7127;http://localhost:5195",
18+
"environmentVariables": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
}
21+
}
22+
}
23+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@SampleApi_HostAddress = http://localhost:5195
2+
3+
GET {{SampleApi_HostAddress}}/weatherforecast/
4+
Accept: application/json
5+
6+
###
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}

0 commit comments

Comments
 (0)