Skip to content

Commit b7e0935

Browse files
authored
.NET 8 (#26)
* Update target frameworks and package versions * Collection expressions * Update FSharp.Core version to 8.0.100 * IUtf8SpanFormattable for Option<T> * Updating dependencies * IUtf8SpanFormattable for Result and NumericOption
1 parent e5bb3d1 commit b7e0935

19 files changed

+355
-97
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ on:
99
- "**.csproj"
1010

1111
env:
12-
DOTNET_VERSION: "7.0.203" # The .NET SDK version to use
12+
DOTNET_VERSION: "8.0.100" # The .NET SDK version to use
1313

1414
jobs:
1515
build-and-test:

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"dotnet.defaultSolution": "src/RustyOptions.sln"
3+
}

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ For performance and convenience:
168168
- Supports nullable type annotations.
169169
- Supports serialization and deserialization with `System.Text.Json`.
170170
- `IEquatable<T>` and `IComparable<T>` allow `Option` and `Result` types to be easily compared and sorted.
171-
- `IFormattable` and `ISpanFormattable` allow `Option` and `Result` to efficiently format their content.
171+
- `IFormattable`, `ISpanFormattable`, and `IUtf8SpanFormattable` allow `Option` and `Result` to efficiently format their content.
172172
- `Option` and `Result` can be efficiently converted to `ReadOnlySpan<T>` or `IEnumerable<T>` for easier interop with existing code.
173173
- Convenient extension methods for working with dictionaries (`GetValueOrNone`), collections (`FirstOrNone`), enums (`Option.ParseEnum`) and more.
174174
- Supports explicit conversion to and from the F# Option, ValueOption, and Result types for easy interop.

src/RustyOptions.FSharp.Tests/RustyOptions.FSharp.Tests.fsproj

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
55
<GenerateDocumentationFile>false</GenerateDocumentationFile>
66
<IsPackable>false</IsPackable>
77
<ReleaseVersion>0.8.0</ReleaseVersion>
@@ -13,17 +13,17 @@
1313
</ItemGroup>
1414

1515
<ItemGroup>
16-
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
17-
<PackageReference Include="xunit" Version="2.4.2" />
18-
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
16+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
17+
<PackageReference Include="xunit" Version="2.6.2" />
18+
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.4">
1919
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2020
<PrivateAssets>all</PrivateAssets>
2121
</PackageReference>
22-
<PackageReference Include="coverlet.collector" Version="3.2.0">
22+
<PackageReference Include="coverlet.collector" Version="6.0.0">
2323
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2424
<PrivateAssets>all</PrivateAssets>
2525
</PackageReference>
26-
<PackageReference Include="coverlet.msbuild" Version="3.2.0">
26+
<PackageReference Include="coverlet.msbuild" Version="6.0.0">
2727
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
2828
<PrivateAssets>all</PrivateAssets>
2929
</PackageReference>
@@ -37,6 +37,10 @@
3737
<PackageReference Update="FSharp.Core" Version="7.0.0" />
3838
</ItemGroup>
3939

40+
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
41+
<PackageReference Update="FSharp.Core" Version="8.0.100" />
42+
</ItemGroup>
43+
4044
<ItemGroup>
4145
<ProjectReference Include="..\RustyOptions.FSharp\RustyOptions.FSharp.fsproj" />
4246
<ProjectReference Include="..\RustyOptions\RustyOptions.csproj" />

src/RustyOptions.FSharp/RustyOptions.FSharp.fsproj

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>net7.0;net6.0</TargetFrameworks>
4+
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
55
<GenerateDocumentationFile>true</GenerateDocumentationFile>
66
<AssemblyName>RustyOptions.FSharp</AssemblyName>
7-
<Version>0.9.0</Version>
8-
<ReleaseVersion>0.9.0</ReleaseVersion>
7+
<Version>0.10.0</Version>
8+
<ReleaseVersion>0.10.0</ReleaseVersion>
99
<PackageId>RustyOptions.FSharp</PackageId>
1010
<Authors>Joel Mueller</Authors>
1111
<Company></Company>
@@ -35,4 +35,8 @@
3535
<ItemGroup Condition=" '$(TargetFramework)' == 'net7.0' ">
3636
<PackageReference Update="FSharp.Core" Version="7.0.0" />
3737
</ItemGroup>
38+
39+
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
40+
<PackageReference Update="FSharp.Core" Version="8.0.100" />
41+
</ItemGroup>
3842
</Project>

src/RustyOptions.Tests/NumericOptionTests.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -280,18 +280,41 @@ public void CanFormatToSpan()
280280
Span<char> buffer = stackalloc char[255];
281281

282282
Assert.True(someInt.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
283-
Assert.True(buffer[..written].SequenceEqual("Some(4200)"));
283+
Assert.Equal("Some(4200)", buffer[..written]);
284284

285285
Assert.True(noneInt.TryFormat(buffer, out written, "", CultureInfo.InvariantCulture));
286-
Assert.True(buffer[..written].SequenceEqual("None"));
286+
Assert.Equal("None", buffer[..written]);
287287

288288
Assert.True(someInt.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
289-
Assert.True(buffer[..written].SequenceEqual("Some(4,200.00)"));
289+
Assert.Equal("Some(4,200.00)", buffer[..written]);
290290

291291
Assert.False(someInt.TryFormat(Span<char>.Empty, out written, "", null));
292292
Assert.False(noneInt.TryFormat(Span<char>.Empty, out written, "", null));
293293
}
294294

295+
#if NET8_0_OR_GREATER
296+
[Fact]
297+
public void CanFormatToUtf8Span()
298+
{
299+
var someInt = Some(4200);
300+
var noneInt = None<int>();
301+
302+
Span<byte> buffer = stackalloc byte[255];
303+
304+
Assert.True(someInt.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
305+
Assert.Equal("Some(4200)"u8, buffer[..written]);
306+
307+
Assert.True(noneInt.TryFormat(buffer, out written, "", CultureInfo.InvariantCulture));
308+
Assert.Equal("None"u8, buffer[..written]);
309+
310+
Assert.True(someInt.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
311+
Assert.Equal("Some(4,200.00)"u8, buffer[..written]);
312+
313+
Assert.False(someInt.TryFormat(Span<byte>.Empty, out written, "", null));
314+
Assert.False(noneInt.TryFormat(Span<byte>.Empty, out written, "", null));
315+
}
316+
#endif
317+
295318
[Fact]
296319
public void CanFlatten()
297320
{
@@ -420,9 +443,9 @@ public void CanGetValues()
420443
var options = Enumerable.Range(1, 10)
421444
.Select(x => x % 2 == 0 ? Some(x) : None<int>());
422445

423-
var values = options.Values().ToArray();
446+
ReadOnlySpan<int> values = options.Values().ToArray();
424447

425-
Assert.Equal(new[] { 2, 4, 6, 8, 10 }, values);
448+
Assert.Equal([2, 4, 6, 8, 10], values);
426449
}
427450

428451
[Fact]

src/RustyOptions.Tests/OptionCollectionTests.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,9 @@ public void CanGetValues()
7878
var options = Enumerable.Range(1, 10)
7979
.Select(x => (x & 1) == 0 ? Some(x) : None<int>());
8080

81-
var values = options.Values().ToArray();
81+
ReadOnlySpan<int> values = options.Values().ToArray();
8282

83-
Assert.Equal(new[] { 2, 4, 6, 8, 10 }, values);
83+
Assert.Equal([2, 4, 6, 8, 10], values);
8484
}
8585

8686
[Fact]
@@ -108,9 +108,9 @@ public void CanGetFirstMatch()
108108
{
109109
static bool IsEven(int x) => (x & 1) == 0;
110110

111-
var empty = Array.Empty<int>();
112-
var notEmpty = new[] { 3, 5, 6, 7, 8, 9 };
113-
var noMatches = new[] { 1, 3, 5, 7 };
111+
int[] empty = [];
112+
int[] notEmpty = [3, 5, 6, 7, 8, 9];
113+
int[] noMatches = [1, 3, 5, 7];
114114

115115
Assert.Equal(None<int>(), empty.FirstOrNone(IsEven));
116116
Assert.Equal(Some(6), notEmpty.FirstOrNone(IsEven));
@@ -200,12 +200,12 @@ public void CanGetSingleMatch()
200200
{
201201
static bool IsEven(int x) => (x & 1) == 0;
202202

203-
var empty = Array.Empty<int>();
204-
var singleWithMatch = new[] { 4 };
205-
var singleNoMatch = new[] { 3 };
206-
var manyWithMatch = new[] { 3, 4, 5 };
207-
var manyNoMatch = new[] { 3, 5 };
208-
var manyWithManyMatches = new[] { 2, 3, 4, 5, 6 };
203+
int[] empty = [];
204+
int[] singleWithMatch = [4];
205+
int[] singleNoMatch = [3];
206+
int[] manyWithMatch = [3, 4, 5];
207+
int[] manyNoMatch = [3, 5];
208+
int[] manyWithManyMatches = [2, 3, 4, 5, 6];
209209

210210
Assert.Equal(None<int>(), empty.SingleOrNone(IsEven));
211211
Assert.Equal(Some(4), singleWithMatch.SingleOrNone(IsEven));

src/RustyOptions.Tests/OptionTests.cs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,26 +271,57 @@ public void CanFormatToSpan()
271271
Span<char> buffer = stackalloc char[255];
272272

273273
Assert.True(someInt.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
274-
Assert.True(buffer[..written].SequenceEqual("Some(4200)"));
274+
Assert.Equal("Some(4200)", buffer[..written]);
275275

276276
Assert.True(noneInt.TryFormat(buffer, out written, "", CultureInfo.InvariantCulture));
277-
Assert.True(buffer[..written].SequenceEqual("None"));
277+
Assert.Equal("None", buffer[..written]);
278278

279279
Assert.True(someInt.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
280-
Assert.True(buffer[..written].SequenceEqual("Some(4,200.00)"));
280+
Assert.Equal("Some(4,200.00)", buffer[..written]);
281281

282282
var notSpanFormattable = Some(new NotSpanFormattable { Value = 4200 });
283283
Assert.True(notSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
284-
Assert.True(buffer[..written].SequenceEqual("Some(4,200.00)"));
284+
Assert.Equal("Some(4,200.00)", buffer[..written]);
285285

286286
var notFormattable = Some(new NotFormattable { Value = 4200 });
287287
Assert.True(notFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
288-
Assert.True(buffer[..written].SequenceEqual("Some(4200)"));
288+
Assert.Equal("Some(4200)", buffer[..written]);
289289

290290
Assert.False(someInt.TryFormat(Span<char>.Empty, out written, "", null));
291291
Assert.False(notFormattable.TryFormat(Span<char>.Empty, out written, "", null));
292292
}
293293

294+
#if NET8_0_OR_GREATER
295+
[Fact]
296+
public void CanFormatToUtf8Span()
297+
{
298+
var someInt = Some(4200);
299+
var noneInt = None<int>();
300+
301+
Span<byte> buffer = stackalloc byte[255];
302+
303+
Assert.True(someInt.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
304+
Assert.Equal("Some(4200)"u8, buffer[..written]);
305+
306+
Assert.True(noneInt.TryFormat(buffer, out written, "", CultureInfo.InvariantCulture));
307+
Assert.Equal("None"u8, buffer[..written]);
308+
309+
Assert.True(someInt.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
310+
Assert.Equal("Some(4,200.00)"u8, buffer[..written]);
311+
312+
var notSpanFormattable = Some(new NotSpanFormattable { Value = 4200 });
313+
Assert.True(notSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
314+
Assert.Equal("Some(4,200.00)"u8, buffer[..written]);
315+
316+
var notFormattable = Some(new NotFormattable { Value = 4200 });
317+
Assert.True(notFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
318+
Assert.Equal("Some(4200)"u8, buffer[..written]);
319+
320+
Assert.False(someInt.TryFormat(Span<byte>.Empty, out written, "", null));
321+
Assert.False(notFormattable.TryFormat(Span<byte>.Empty, out written, "", null));
322+
}
323+
#endif
324+
294325
[Fact]
295326
public void CanFlatten()
296327
{
@@ -426,7 +457,7 @@ public void CanCompare()
426457
Assert.False(n > n);
427458
#pragma warning restore CS1718 // Comparison made to same variable
428459

429-
var items = new[] { d, b, n, c, a };
460+
Option<int>[] items = [d, b, n, c, a];
430461
Array.Sort(items);
431462
Assert.Equal(new[] { a, b, c, d, n }, items);
432463
}
@@ -455,7 +486,9 @@ private sealed class NotFormattable
455486
[InlineData("foo", true, null)]
456487
[InlineData("9", false, ConsoleColor.Blue)]
457488
[InlineData("797", false, (ConsoleColor)797)]
489+
#pragma warning disable xUnit1012 // Null should only be used for nullable parameters
458490
[InlineData(null, true, null)]
491+
#pragma warning restore xUnit1012 // Null should only be used for nullable parameters
459492
public void CanParseEnums(string name, bool ignoreCase, ConsoleColor? expected)
460493
{
461494
Assert.Equal(Option.Create(expected), Option.ParseEnum<ConsoleColor>(name, ignoreCase));

src/RustyOptions.Tests/ResultTests.cs

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,16 @@ public void CanMatch()
9292
}
9393

9494
[Fact]
95-
public void CanMatchWithSideEffects()
96-
{
95+
public void CanMatchWithSideEffects()
96+
{
9797
var ok = Ok(42);
98-
var err = Err<int>("oops");
99-
100-
int output = 0;
101-
ok.Match(x => output += x, e => { if (e == "oops") { output -= 1; } });
102-
Assert.Equal(42, output);
103-
err.Match(x => output += x, e => { if (e == "oops") { output -= 1; } });
104-
Assert.Equal(41, output);
98+
var err = Err<int>("oops");
99+
100+
int output = 0;
101+
ok.Match(x => output += x, e => { if (e == "oops") { output -= 1; } });
102+
Assert.Equal(42, output);
103+
err.Match(x => output += x, e => { if (e == "oops") { output -= 1; } });
104+
Assert.Equal(41, output);
105105
}
106106

107107
[Fact]
@@ -242,26 +242,62 @@ public void CanFormatToSpan()
242242
Span<char> buffer = stackalloc char[255];
243243

244244
Assert.True(ok.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
245-
Assert.True(buffer[..written].SequenceEqual("Ok(4200)"));
245+
Assert.Equal("Ok(4200)", buffer[..written]);
246+
247+
Assert.True(err.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
248+
Assert.Equal("Err(-9,600.00)", buffer[..written]);
249+
250+
Assert.True(ok.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
251+
Assert.Equal("Ok(4,200.00)", buffer[..written]);
252+
253+
Assert.True(okNotSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
254+
Assert.Equal("Ok(4,200.00)", buffer[..written]);
255+
256+
Assert.True(okNotFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
257+
Assert.Equal("Ok(4200)", buffer[..written]);
258+
259+
Assert.True(errNotSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
260+
Assert.Equal("Err(-1.00)", buffer[..written]);
261+
262+
Assert.True(errNotFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
263+
Assert.Equal("Err(-1)", buffer[..written]);
264+
}
265+
266+
#if NET8_0_OR_GREATER
267+
[Fact]
268+
public void CanFormatToUtf8Span()
269+
{
270+
var ok = Ok(4200);
271+
var err = Err<int, int>(-9600);
272+
var okNotSpanFormattable = Ok(new NotSpanFormattable { Value = 4200 });
273+
var okNotFormattable = Ok(new NotFormattable { Value = 4200 });
274+
var errNotSpanFormattable = Err<int, NotSpanFormattable>(new NotSpanFormattable { Value = -1 });
275+
var errNotFormattable = Err<int, NotFormattable>(new NotFormattable { Value = -1 });
276+
277+
Span<byte> buffer = stackalloc byte[255];
278+
279+
Assert.True(ok.TryFormat(buffer, out int written, "", CultureInfo.InvariantCulture));
280+
Assert.Equal("Ok(4200)"u8, buffer[..written]);
246281

247282
Assert.True(err.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
248-
Assert.True(buffer[..written].SequenceEqual("Err(-9,600.00)"));
283+
Assert.Equal("Err(-9,600.00)"u8, buffer[..written]);
249284

250285
Assert.True(ok.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
251-
Assert.True(buffer[..written].SequenceEqual("Ok(4,200.00)"));
286+
Assert.Equal("Ok(4,200.00)"u8, buffer[..written]);
252287

253288
Assert.True(okNotSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
254-
Assert.True(buffer[..written].SequenceEqual("Ok(4,200.00)"));
289+
Assert.Equal("Ok(4,200.00)"u8, buffer[..written]);
255290

256291
Assert.True(okNotFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
257-
Assert.True(buffer[..written].SequenceEqual("Ok(4200)"));
292+
Assert.Equal("Ok(4200)"u8, buffer[..written]);
258293

259294
Assert.True(errNotSpanFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
260-
Assert.True(buffer[..written].SequenceEqual("Err(-1.00)"));
295+
Assert.Equal("Err(-1.00)"u8, buffer[..written]);
261296

262297
Assert.True(errNotFormattable.TryFormat(buffer, out written, "n2", CultureInfo.InvariantCulture));
263-
Assert.True(buffer[..written].SequenceEqual("Err(-1)"));
298+
Assert.Equal("Err(-1)"u8, buffer[..written]);
264299
}
300+
#endif
265301

266302
[Fact]
267303
public void CanCompare()

0 commit comments

Comments
 (0)