├── .gitignore ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── FSharpExtensions.sln ├── LICENSE ├── README.md ├── benchmarks ├── Benchmarks.CSharp │ ├── Benchmarks.CSharp.csproj │ ├── Program.cs │ ├── ScanfBenchmark.cs │ └── TryCatchBenchmark.cs ├── Benchmarks.FSharp │ ├── ActionSeq.fs │ ├── BenchmarkConfig.fs │ ├── BenchmarkRunner.fs │ ├── Benchmarks.FSharp.fsproj │ ├── CollectionsBuildersAllocations.fs │ ├── ComparisonOperatorBenchmark.fs │ ├── CustomBuildersVsLibraryBuilders.fs │ ├── EResultAllocationsBenchmark.fs │ ├── FSharpOptimizer.fs │ ├── FSharpOptimizerWithExperimentalPipe.fs │ ├── GSeq.fs │ ├── GenericEqualityBenchmark.fs │ ├── GenericEqualityObjectArrayBenchmark.fs │ ├── GenericTaskBuilder.fs │ ├── HttpVerbParsing.fs │ ├── IEquatableEqualityOperatorBenchmark.fs │ ├── MathematicalOperatorsBenchmarks.fs │ ├── NodeCode.fs │ ├── Program.fs │ ├── PushStream.fs │ ├── ScanfBenchmark.fs │ ├── TaskBuildersBenchmarks.fs │ └── VectorsBase64.fs └── Benchmarks.Lib │ ├── Benchmarks.Lib.csproj │ └── HttpVerbsWithLength.cs ├── global.json ├── src ├── En3Tho.Extensions.DependencyInjection.XUnit │ ├── En3Tho.Extensions.DependencyInjection.XUnit.csproj │ └── IServiceCollectionExtensions.cs ├── En3Tho.Extensions.DependencyInjection │ ├── AddAsOpenGeneric.cs │ ├── AddFromConfiguration.cs │ ├── AddHttpClient.cs │ ├── AddOrFail.cs │ ├── AddOrReplace.cs │ ├── AddOrReplaceOrFail.cs │ ├── AddScopedAsSelfAnd.cs │ ├── AddScopedFunc.cs │ ├── AddSingletonAsSelfAnd.cs │ ├── AddSingletonFunc.cs │ ├── AddTransientFunc.cs │ ├── ChangeLifetime.cs │ ├── Configuration.cs │ ├── Decoration.cs │ ├── En3Tho.Extensions.DependencyInjection.csproj │ ├── IServiceProviderExtensions.cs │ ├── IServiceScopeExtensions.cs │ ├── TryAddScopedAsSelfAnd.cs │ ├── TryAddScopedAsSelfAndOrFail.cs │ ├── TryAddScopedFunc.cs │ ├── TryAddScopedOrFailFunc.cs │ ├── TryAddSingletonAsSelfAnd.cs │ ├── TryAddSingletonAsSelfAndOrFail.cs │ ├── TryAddSingletonFunc.cs │ ├── TryAddSingletonOrFailFunc.cs │ ├── TryAddTransientFunc.cs │ ├── TryAddTransientOrFailFunc.cs │ ├── Update.cs │ └── Utility.cs ├── En3Tho.FSharp.ComputationExpressions │ ├── ArrayPoolBasedBuilders.fs │ ├── CodeBuilder.fs │ ├── CollectionBuilderExtensions.fs │ ├── En3Tho.FSharp.ComputationExpressions.fsproj │ ├── GSeqExtensions.fs │ ├── GenericCollectionCodeExtensions.fs │ ├── GenericTaskBuilder │ │ ├── Builder │ │ │ ├── GenericTaskBuilderBase.fs │ │ │ ├── GenericTaskBuilderCore.fs │ │ │ ├── GenericTaskBuilderExtensions.fs │ │ │ ├── GenericTaskBuilderExtensions2.fs │ │ │ └── ResumableCodeHelpers.fs │ │ ├── Data │ │ │ ├── SeqStateMachineData.fs │ │ │ ├── StateMachineData.fs │ │ │ ├── StateMachineDataWithState.fs │ │ │ ├── UnitStateMachineData.fs │ │ │ └── UnitStateMachineDataWithState.fs │ │ ├── GenericTaskBuilderInterfaces.fs │ │ ├── GenericTaskBuilderIntrinsics.fs │ │ └── Tasks │ │ │ ├── ActivityTasks.fs │ │ │ ├── AsyncIteratorMethodBuilderWrapper.fs │ │ │ ├── CancellableTasks.fs │ │ │ ├── ExceptionResultTasks.fs │ │ │ ├── ExceptionTask.fs │ │ │ ├── LazyTask.fs │ │ │ ├── MethodBuilderBehaviors.fs │ │ │ ├── RepeatableTask.fs │ │ │ ├── ResultTasks.fs │ │ │ ├── SemaphoreSlimTask.fs │ │ │ ├── SynchronizationContextTask.fs │ │ │ ├── TaskMethodBuilderWrappers.fs │ │ │ ├── TaskSeq.fs │ │ │ ├── Tasks.fs │ │ │ ├── ValueOptionTasks.fs │ │ │ └── ValueTaskMethodBuilderWrappers.fs │ ├── GenericUnitBuilderBase.fs │ ├── HttpBuilder │ │ ├── Content.fs │ │ ├── Extensions.fs │ │ ├── HeadersBuilders.fs │ │ ├── Serializers.fs │ │ └── Stages.fs │ ├── OptionBuilder.fs │ ├── ResultBuilder.fs │ ├── Tasks.fs │ ├── TryExprBuilder.fs │ └── ValueTaskExtensions.fs ├── En3Tho.FSharp.Extensions.Logging │ ├── En3Tho.FSharp.Extensions.Logging.fsproj │ └── ILoggerExtensions.fs ├── En3Tho.FSharp.Extensions │ ├── ActivePatterns.fs │ ├── Disposables.fs │ ├── En3Tho.FSharp.Extensions.fsproj │ ├── Experimental.fs │ ├── Extensions.fs │ ├── GSeq.fs │ ├── GSeqEnumerators.fs │ ├── InterfaceShortcuts.fs │ ├── Modules.fs │ ├── NumericsAndVectors.fs │ ├── Pointers.fs │ ├── Scanf.fs │ ├── Span.fs │ ├── Stackalloc.fs │ ├── String.fs │ └── TopLevel.fs ├── En3Tho.FSharp.Validation │ ├── AsyncMultiValidators.fs │ ├── BasicTypes.fs │ ├── CommonValidatedTypes.fs │ ├── En3Tho.FSharp.Validation.fsproj │ ├── Extensions.fs │ ├── JsonConverter.fs │ └── MultiValidators.fs └── En3Tho.FSharp.XUnitExtensions │ ├── En3Tho.FSharp.XUnitExtensions.fsproj │ └── Extensions.fs ├── tests ├── En3Tho.Extensions.DependencyInjection.Tests │ ├── AddFuncTests.fs │ ├── AddSingletonAsSelfAndInjectionTests.fs │ ├── DecorationTests.fs │ ├── En3Tho.Extensions.DependencyInjection.Tests.fsproj │ ├── Module2DependencyInjectionTest.fs │ ├── ModuleDependencyInjectionTest.fs │ ├── RootNamespaceDependencyInjectionTest.fs │ ├── ServiceCollectionExtensionsTests.fs │ └── UpdateTests.fs ├── En3Tho.FSharp.ComputationExpressions.Tests │ ├── ArrayPoolBasedBuilderTests.fs │ ├── CancellableTaskTests.fs │ ├── CodeBuilderTests.fs │ ├── ComputationExpressionBuildTests.fs │ ├── En3Tho.FSharp.ComputationExpressions.Tests.fsproj │ ├── GenericTaskBuilderTests.fs │ ├── HttpBuilderTests.fs │ ├── LazyTaskTests.fs │ ├── OptionsAndResults.fs │ ├── RepeatableTaskTests.fs │ ├── StringBuilderBuilderTests.fs │ ├── TaskAndCollectionsOverloadingTests.fs │ ├── TaskOptionAndResult.fs │ ├── TaskResultOverloadingTests.fs │ ├── Tasks.fs │ └── TryExprBuilderTests.fs ├── En3Tho.FSharp.Extensions.Tests │ ├── AsyncTests.fs │ ├── Byrefs.fs │ ├── DeferTests.fs │ ├── En3Tho.FSharp.Extensions.Tests.fsproj │ ├── FunkyPipesTests.fs │ ├── GSeq.fs │ ├── IEquatableOperatorTests.fs │ ├── ScanfTest.fs │ └── Vectors.fs └── En3Tho.FSharp.Validation.Tests │ ├── En3Tho.FSharp.Validation.Tests.fsproj │ ├── JsonSerialization.fs │ └── Validation.fs └── tools ├── CentralPackageManagementMigrationTool ├── CentralPackageManagementMigrationTool.fsproj ├── DotnetInfo.fs ├── MigrationTool.fs ├── Program.fs ├── Properties │ └── launchSettings.json ├── PropertiesAndExtensions.fs └── XmlNodeBuilder.fs └── ProjectUtilities ├── ActivePatternsCodeGen.fs ├── AddFunc.fs ├── Code.fs ├── ILoggerExtensionsCodeGen.fs ├── IntrinsicsCodeGen.fs ├── Program.fs ├── ProjectUtilities.fsproj ├── ServiceProviderCodeGen.fs ├── TryAddAsSelfAnd.fs └── WebApplicationsExtensionsCodeGen.fs /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | true 6 | 7 | 8 | 9 | enable 10 | enable 11 | default 12 | 13 | 14 | 15 | false 16 | $(OtherFlags) --test:GraphBasedChecking --test:ParallelOptimization --test:ParallelIlxGen 17 | 18 | 9, 19 | 0760, 20 | 96, 21 | 3535, 22 | 42, 23 | 77, 24 | 3391, 25 | 3513, 26 | 1204 27 | 28 | preview 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Directory.Build.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Igor Bagdamyan 5 | Copyright (c) 2024 Igor Bagdamyan 6 | https://github.com/En3Tho/FSharpExtensions 7 | https://github.com/En3Tho/FSharpExtensions/blob/main/LICENSE 8 | https://github.com/En3Tho/FSharpExtensions 9 | $(NugetVersion) 10 | $(NugetVersion) 11 | $(NugetVersion) 12 | true 13 | snupkg 14 | true 15 | true 16 | true 17 | 18 | 19 | 20 | 21 | all 22 | runtime; build; native; contentfiles; analyzers; buildtransitive 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | all 32 | 33 | 34 | runtime; build; native; contentfiles; analyzers; buildtransitive 35 | all 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Directory.Packages.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | all 13 | runtime; build; native; contentfiles; analyzers; buildtransitive 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Igor Bagdamyan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FSharpExtensions 2 | A set of extensions, computation expressions and other stuff I'm using 3 | -------------------------------------------------------------------------------- /benchmarks/Benchmarks.CSharp/Benchmarks.CSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /benchmarks/Benchmarks.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | // See https://aka.ms/new-console-template for more information 2 | 3 | using BenchmarkDotNet.Running; 4 | using Benchmarks.CSharp; 5 | 6 | BenchmarkRunner.Run(); -------------------------------------------------------------------------------- /benchmarks/Benchmarks.CSharp/ScanfBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using BenchmarkDotNet.Attributes; 3 | using BenchmarkDotNet.Jobs; 4 | using En3Tho.FSharp.Extensions; 5 | 6 | namespace Benchmarks.CSharp 7 | { 8 | [SimpleJob(RuntimeMoniker.Net60)] 9 | [MemoryDiagnoser] 10 | [DisassemblyDiagnoser] 11 | public class ScanfBenchmark 12 | { 13 | [Benchmark] 14 | public bool PrimitivesOnlyPreallocated() 15 | { 16 | return Scanf.scanf(Benchmarks.ScanfBenchmark.Assets.primitivesOnlyFmt, "123456 123456.123456"); 17 | } 18 | 19 | // [Benchmark] 20 | // public bool PrimitivesOnlyPreallocatedSpan() 21 | // { 22 | // return Scanf.scanfSpan(Benchmarks.FSharp.ScanfBenchmark.Assets.primitivesOnlyFmt, "123456 123456.123456".AsSpan()); 23 | // } 24 | } 25 | } -------------------------------------------------------------------------------- /benchmarks/Benchmarks.CSharp/TryCatchBenchmark.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace Benchmarks.CSharp; 4 | 5 | [MemoryDiagnoser] 6 | [DisassemblyDiagnoser] 7 | public class TryCatchBenchmark 8 | { 9 | private Action? Action; 10 | 11 | [GlobalSetup] 12 | public void GlobalSetup() 13 | { 14 | Action = null; 15 | } 16 | 17 | [Benchmark] 18 | public void RunTryCatch() 19 | { 20 | try 21 | { 22 | Action?.Invoke(this); 23 | } 24 | catch 25 | { 26 | } 27 | } 28 | 29 | [Benchmark] 30 | public void RunCheckThenTryCatch() 31 | { 32 | if (Action is {} action) 33 | { 34 | try 35 | { 36 | action(this); 37 | } 38 | catch 39 | { 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/BenchmarkRunner.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module Benchmarks.BenchmarkRunner 3 | 4 | open System.Runtime.InteropServices 5 | open BenchmarkDotNet.Configs 6 | open BenchmarkDotNet.Running 7 | 8 | type BenchmarkRunner with 9 | static member Run<'T1, 'T2>([] config: IConfig) = 10 | BenchmarkRunner.Run([| 11 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config) 12 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config) 13 | |]) 14 | 15 | static member Run<'T1, 'T2, 'T3>([] config: IConfig) = 16 | BenchmarkRunner.Run([| 17 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config) 18 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config) 19 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config) 20 | |]) 21 | 22 | static member Run<'T1, 'T2, 'T3, 'T4>([] config: IConfig) = 23 | BenchmarkRunner.Run([| 24 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config) 25 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config) 26 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config) 27 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config) 28 | |]) 29 | 30 | static member Run<'T1, 'T2, 'T3, 'T4, 'T5>([] config: IConfig) = 31 | BenchmarkRunner.Run([| 32 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config) 33 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config) 34 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config) 35 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config) 36 | BenchmarkConverter.TypeToBenchmarks(typeof<'T5>, config) 37 | |]) 38 | 39 | static member Run<'T1, 'T2, 'T3, 'T4, 'T5, 'T6>([] config: IConfig) = 40 | BenchmarkRunner.Run([| 41 | BenchmarkConverter.TypeToBenchmarks(typeof<'T1>, config) 42 | BenchmarkConverter.TypeToBenchmarks(typeof<'T2>, config) 43 | BenchmarkConverter.TypeToBenchmarks(typeof<'T3>, config) 44 | BenchmarkConverter.TypeToBenchmarks(typeof<'T4>, config) 45 | BenchmarkConverter.TypeToBenchmarks(typeof<'T5>, config) 46 | BenchmarkConverter.TypeToBenchmarks(typeof<'T6>, config) 47 | |]) -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/Benchmarks.FSharp.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | preview 6 | Benchmarks 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/CollectionsBuildersAllocations.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.CollectionsBuildersAllocations 2 | 3 | open BenchmarkDotNet.Attributes 4 | open BenchmarkDotNet.Jobs 5 | open En3Tho.FSharp.ComputationExpressions 6 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder 7 | 8 | [< 9 | MemoryDiagnoser; 10 | DisassemblyDiagnoser(filters = [||]); 11 | SimpleJob(RuntimeMoniker.Net60) 12 | >] 13 | type Benchmark() = 14 | 15 | member val Count = 10 with get, set 16 | 17 | member val Items = [ for i = 0 to 10 do i ] :> seq<_> 18 | 19 | [] 20 | member this.RunResizeArrayBuilder() = ResizeArray() { 21 | 1 22 | 2 23 | 3 24 | for i in this.Items do 25 | i 26 | let mutable i = this.Count 27 | while i > 0 do 28 | i 29 | i <- i - 1 30 | try 31 | i 32 | with e -> 33 | i 34 | try 35 | i 36 | finally 37 | () 38 | } 39 | 40 | [] 41 | member this.RunResizeArray() = 42 | let rsz = ResizeArray() 43 | 44 | rsz.Add(1) 45 | rsz.Add(2) 46 | rsz.Add(3) 47 | 48 | for i in this.Items do 49 | rsz.Add(i) 50 | let mutable i = this.Count 51 | while i > 0 do 52 | rsz.Add(i) 53 | i <- i - 1 54 | try 55 | rsz.Add(i) 56 | with e -> 57 | rsz.Add(i) 58 | try 59 | rsz.Add(i) 60 | finally 61 | () 62 | rsz -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/ComparisonOperatorBenchmark.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.ComparisonOperatorBenchmark 2 | 3 | open System 4 | open BenchmarkDotNet.Attributes 5 | 6 | // TODO: benchmark more operators to find allocating ones 7 | 8 | type [] CustomRecordType = { 9 | V1: string 10 | V2: int 11 | V3: int64 12 | } 13 | 14 | let inline compareReferences<'a when 'a: not struct> (left: 'a) (right: 'a) = 15 | match box left, box right with 16 | | null, null -> 0 17 | | _, null -> -1 18 | | null, _ -> 1 19 | | _ -> 0 20 | 21 | let inline compare<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) 22 | 23 | let inline compareArrays<'a when 'a :> IComparable<'a>> (left: 'a[]) (right: 'a[]) = 24 | match compareReferences left right with 25 | | 0 -> 26 | match left.Length.CompareTo(right.Length) with 27 | | 0 -> 28 | let rec go index = 29 | if uint index < uint left.Length then 30 | match left[index].CompareTo(right[index]) with 31 | | 0 -> 32 | go (index + 1) 33 | | result -> 34 | result 35 | else 36 | 0 37 | go 0 38 | | result -> 39 | result 40 | | result -> 41 | result 42 | 43 | let inline (<.) left right = compare left right = -1 44 | let inline (<..) left right = compareArrays left right = -1 45 | let inline (<=.) left right = compare left right <= 0 46 | let inline (<=..) left right = compareArrays left right <= 0 47 | 48 | 49 | let inline (>.) left right = compare left right = 1 50 | let inline (>=.) left right = compare left right >= 0 51 | 52 | [] 53 | type Benchmark() = 54 | 55 | let value1 = { 56 | V1 = "test" 57 | V2 = 10 58 | V3 = 10 59 | } 60 | 61 | let value2 = { 62 | V1 = "test" 63 | V2 = 10 64 | V3 = 11 65 | } 66 | 67 | let values1 = [| value1; value1; value1; value1; value1; |] 68 | let values2 = [| value1; value1; value1; value1; value2; |] 69 | 70 | [] 71 | member _.CompareCustomTypesUsingCustomOperator() = value1 <. value2 72 | 73 | [] 74 | member _.CompareCustomTypesUsingNativeOperator() = value1 < value2 75 | 76 | [] 77 | member _.CompareCustomArrayTypesUsingCustomOperator() = values1 <.. values2 78 | 79 | [] 80 | member _.CompareCustomArrayTypesUsingNativeOperator() = value1 < value2 81 | 82 | [] 83 | member _.CompareTimeSpanUsingCustomOperator() = TimeSpan() <. TimeSpan() 84 | 85 | [] 86 | member _.CompareTimeSpanUsingNativeOperator() = TimeSpan() < TimeSpan() -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/CustomBuildersVsLibraryBuilders.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.CustomBuildersVsLibraryBuilders 2 | 3 | open BenchmarkDotNet.Attributes 4 | open En3Tho.FSharp.ComputationExpressions 5 | open En3Tho.FSharp.ComputationExpressions.ArrayPoolBasedBuilders 6 | 7 | [< 8 | MemoryDiagnoser; 9 | Config(typeof<``Net 6, Pgo``>) 10 | >] 11 | type Benchmark() = 12 | 13 | [] 14 | member val Count = 100 with get, set 15 | 16 | [] 17 | member this.LibraryArray() = [| 18 | 1 19 | 2 20 | 3 21 | for i = 0 to this.Count do 22 | i 23 | let mutable i = this.Count 24 | while i > 0 do 25 | i 26 | i <- i - 1 27 | |] 28 | 29 | [] 30 | member this.LibraryList() = [ 31 | 1 32 | 2 33 | 3 34 | for i = 0 to this.Count do 35 | i 36 | let mutable i = this.Count 37 | while i > 0 do 38 | i 39 | i <- i - 1 40 | ] 41 | 42 | [] 43 | member this.CustomArray() = arr { 44 | 1 45 | 2 46 | 3 47 | for i = 0 to this.Count do 48 | i 49 | let mutable i = this.Count 50 | while i > 0 do 51 | i 52 | i <- i - 1 53 | } 54 | 55 | [] 56 | member this.CustomResizeArray() = rsz { 57 | 1 58 | 2 59 | 3 60 | for i = 0 to this.Count do 61 | i 62 | let mutable i = this.Count 63 | while i > 0 do 64 | i 65 | i <- i - 1 66 | } 67 | 68 | [] 69 | member this.CustomImmutableArray() = block { 70 | 1 71 | 2 72 | 3 73 | for i = 0 to this.Count do 74 | i 75 | let mutable i = this.Count 76 | while i > 0 do 77 | i 78 | i <- i - 1 79 | } -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/EResultAllocationsBenchmark.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.EResultAllocationsBenchmark 2 | 3 | open System 4 | open BenchmarkDotNet.Attributes 5 | open BenchmarkDotNet.Jobs 6 | open En3Tho.FSharp.ComputationExpressions.ResultBuilder 7 | 8 | 9 | [< 10 | MemoryDiagnoser; 11 | DisassemblyDiagnoser(filters = [||]); 12 | SimpleJob(RuntimeMoniker.Net60) 13 | >] 14 | type Benchmark() = 15 | 16 | let exnInstance = Exception() 17 | 18 | [] 19 | member _.EResultManual() = 20 | match Error exnInstance, Error exnInstance with 21 | | Ok q, Ok w -> Ok (q + w) 22 | | Error exn, _ 23 | | _, Error exn -> Error exn 24 | 25 | [] 26 | member _.EResultSimple() = eresult { 27 | let! q = Error exnInstance 28 | let! w = Error exnInstance 29 | return q + w 30 | } 31 | 32 | [] 33 | member _.EResultMultiManual2() = 34 | let q = Error exnInstance 35 | let w = Error exnInstance 36 | match q, w with 37 | | Ok q, Ok w -> 38 | Ok (q + w) 39 | | Error exn1, Error exn2 -> 40 | Error (AggregateException(exn1, exn2)) 41 | | Error exn, _ 42 | | _, Error exn -> 43 | Error (AggregateException(exn)) 44 | 45 | [] 46 | member _.EResultMulti2() = eresult { 47 | let! q = Error exnInstance 48 | and! w = Error exnInstance 49 | return q + w 50 | } 51 | 52 | [] 53 | member _.EResultMultiManual3() = 54 | let q = Error exnInstance 55 | let w = Error exnInstance 56 | let e = Error exnInstance 57 | match q, w, e with 58 | | Ok q, Ok w, Ok e -> Ok (q + w + e) 59 | | _ -> 60 | [ 61 | match q with Error exn -> exn | _ -> () 62 | match w with Error exn -> exn | _ -> () 63 | match e with Error exn -> exn | _ -> () 64 | ] |> AggregateException :> exn |> Error 65 | 66 | [] 67 | member _.EResultMulti3() = eresult { 68 | let! q = Error exnInstance 69 | and! w = Error exnInstance 70 | and! e = Error exnInstance 71 | return q + w + e 72 | } 73 | 74 | [] 75 | member _.EResultMulti4() = eresult { 76 | let! q = Error exnInstance 77 | and! w = Error exnInstance 78 | and! e = Error exnInstance 79 | and! r = Error exnInstance 80 | return q + w + e + r 81 | } 82 | 83 | [] 84 | member _.EResultMulti5() = eresult { 85 | let! q = Error exnInstance 86 | and! w = Error exnInstance 87 | and! e = Error exnInstance 88 | and! r = Error exnInstance 89 | and! t = Error exnInstance 90 | return q + w + e + r + t 91 | } 92 | 93 | [] 94 | member _.EResultMulti6() = eresult { 95 | let! q = Error exnInstance 96 | and! w = Error exnInstance 97 | and! e = Error exnInstance 98 | and! r = Error exnInstance 99 | and! t = Error exnInstance 100 | and! y = Error exnInstance 101 | return q + w + e + r + t + y 102 | } 103 | 104 | [] 105 | member _.EResultMulti7() = eresult { 106 | let! q = Error exnInstance 107 | and! w = Error exnInstance 108 | and! e = Error exnInstance 109 | and! r = Error exnInstance 110 | and! t = Error exnInstance 111 | and! y = Error exnInstance 112 | and! u = Error exnInstance 113 | return q + w + e + r + t + y + u 114 | } -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/GSeq.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.GSeq 2 | 3 | open BenchmarkDotNet.Jobs 4 | open En3Tho.FSharp.Extensions 5 | open BenchmarkDotNet.Attributes 6 | open System.Linq 7 | 8 | [] 9 | //[)>] 10 | [] 11 | type Benchmark() = 12 | 13 | [] 14 | member val Count = 0 with get, set 15 | 16 | member val Array = [||] with get, set 17 | 18 | [] 19 | member this.GlobalSetup() = 20 | this.Array <- Array.init this.Count id 21 | 22 | [] 23 | member this.IEnumerableFilterMapSkipFold() = 24 | this.Array 25 | .Where(fun x -> x % 2 <> 0) 26 | .Skip(5) 27 | .Select(fun x -> x + 15) 28 | .Aggregate(0, (fun x y -> x + y)) 29 | 30 | [] 31 | member this.SeqFilterMapSkipFold() = 32 | this.Array 33 | |> Seq.filter ^ fun x -> x % 2 <> 0 34 | |> Seq.skip 5 35 | |> Seq.map ^ fun x -> x + 15 36 | |> Seq.fold (fun x y -> x + y) 0 37 | 38 | [] 39 | member this.GSeqFilterMapSkipFold() = 40 | this.Array 41 | |> GSeq.ofArray 42 | |> GSeq.filter ^ fun x -> x % 2 <> 0 43 | |> GSeq.skip 5 44 | |> GSeq.map ^ fun x -> x + 15 45 | |> GSeq.fold 0 ^ fun x y -> x + y 46 | 47 | [] 48 | member this.ActionSeqFilterMapSkipFold() = 49 | let action = 50 | (ActionSeq.fold 0 ^ fun x y -> x + y // termination stage, not iteration 51 | |> ActionSeq.map ^ fun x -> x + 15 52 | |> ActionSeq.skip 5 53 | |> ActionSeq.filter ^ fun x -> x % 2 <> 0 54 | |> ActionSeq.fromArray) 55 | 56 | action.Invoke(this.Array) 57 | action.next.next.next.next.Result 58 | 59 | [] 60 | member this.For() = 61 | let mutable skip = 0 62 | let mutable result = 0 63 | let array = this.Array 64 | for i = 0 to array.Length - 1 do 65 | let x = array[i] 66 | if x % 2 <> 0 then 67 | if skip < 5 then 68 | skip <- skip + 1 69 | else 70 | let x = x + 15 71 | result <- result + x 72 | result -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/GenericEqualityBenchmark.fs: -------------------------------------------------------------------------------- 1 | 2 | module Benchmarks.GenericEqualityBenchmark 3 | 4 | open System 5 | open BenchmarkDotNet.Attributes 6 | 7 | type WootRecord = { 8 | Value1: string[] 9 | Value2: string[] 10 | Value3: string[] 11 | } 12 | 13 | let makeStringArray n = Array.init n (ignore >> Guid.NewGuid >> string) 14 | 15 | let makeWootRecord() : WootRecord = { 16 | Value1 = makeStringArray 5 17 | Value2 = makeStringArray 10 18 | Value3 = makeStringArray 5 19 | } 20 | 21 | let customStringArrayEquality (array1: string[]) (array2: string[]) = 22 | let mutable result = array1.Length = array2.Length 23 | let mutable i = 0 24 | while result && i < array1.Length do 25 | result <- array1[i].Equals(array2[i]) 26 | i <- i + 1 27 | result 28 | 29 | let customIEquatableArrayEquality<'a when 'a :> IEquatable<'a>> (array1: 'a[]) (array2: 'a[]) = 30 | let mutable result = array1.Length = array2.Length 31 | let mutable i = 0 32 | while result && i < array1.Length do 33 | result <- array1[i].Equals(array2[i]) 34 | i <- i + 1 35 | result 36 | 37 | let customWootRecordEquality (woot1: WootRecord) (woot2: WootRecord) = 38 | customStringArrayEquality woot1.Value1 woot2.Value1 39 | && customStringArrayEquality woot1.Value2 woot2.Value2 40 | && customStringArrayEquality woot1.Value3 woot2.Value3 41 | 42 | let customWootRecordIEquatableEquality (woot1: WootRecord) (woot2: WootRecord) = 43 | customIEquatableArrayEquality woot1.Value1 woot2.Value1 44 | && customIEquatableArrayEquality woot1.Value2 woot2.Value2 45 | && customIEquatableArrayEquality woot1.Value3 woot2.Value3 46 | 47 | [] 48 | type Benchmark() = 49 | 50 | let woot1 = makeWootRecord() 51 | let woot2 = makeWootRecord() 52 | let woot1Copy = { { woot1 with Value1 = woot2.Value1 } with Value1 = woot1.Value1 } 53 | 54 | let longStringArray = makeStringArray 1000 55 | let longStringArrayCopy = 56 | let result = Array.copy longStringArray 57 | result[result.Length - 1] <- Guid.NewGuid().ToString() 58 | result 59 | 60 | [] 61 | member x.TestAutoGeneratedEquality() = 62 | woot1 = woot2 63 | 64 | [] 65 | member x.TestManualEquality() = 66 | customWootRecordEquality woot1 woot2 67 | 68 | [] 69 | member x.TestIEquatableEquality() = 70 | customWootRecordIEquatableEquality woot1 woot2 71 | 72 | [] 73 | member x.TestAutoGeneratedEqualityCopy() = 74 | woot1 = woot1Copy 75 | 76 | [] 77 | member x.TestManualEqualityCopy() = 78 | customWootRecordEquality woot1 woot1Copy 79 | 80 | [] 81 | member x.TestIEquatableEqualityCopy() = 82 | customWootRecordIEquatableEquality woot1 woot1Copy 83 | 84 | // arrays only 85 | 86 | [] 87 | member x.TestAutoGeneratedArrayEquality() = 88 | woot1.Value1 = woot2.Value1 89 | 90 | [] 91 | member x.TestManualArrayEquality() = 92 | customStringArrayEquality woot1.Value1 woot2.Value1 93 | 94 | [] 95 | member x.TestIEquatableArrayEquality() = 96 | customIEquatableArrayEquality woot1.Value1 woot2.Value1 97 | 98 | [] 99 | member x.TestAutoGeneratedArrayEqualityCopy() = 100 | woot1.Value1 = woot1Copy.Value1 101 | 102 | [] 103 | member x.TestManualArrayEqualityCopy() = 104 | customStringArrayEquality woot1.Value1 woot1Copy.Value1 105 | 106 | [] 107 | member x.TestIEquatableArrayEqualityCopy() = 108 | customIEquatableArrayEquality woot1.Value1 woot1Copy.Value1 109 | 110 | [] 111 | member x.TestLongArrayAutoGenerated() = 112 | longStringArray = longStringArrayCopy 113 | 114 | [] 115 | member x.TestLongArrayManual() = 116 | customStringArrayEquality longStringArray longStringArrayCopy -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/GenericEqualityObjectArrayBenchmark.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.GenericEqualityObjectArrayBenchmark 2 | 3 | open System 4 | open BenchmarkDotNet.Attributes 5 | 6 | module FastEquality = 7 | type T = T with 8 | static member inline ($) (_, structEquatable: ^a when ^a :> IEquatable<^a> and ^a: struct) = fun (value: ^a) -> structEquatable.Equals(value) 9 | static member inline ($) (_, refEquatable: 'a when 'a: not struct) = fun (value: 'b when 'b: not struct) -> refEquatable.Equals(value) 10 | 11 | let inline (====) a b = (T $ a) b 12 | 13 | open FastEquality 14 | 15 | type [] AutoGeneratedNested = { 16 | Value1: string 17 | Value2: int 18 | } 19 | 20 | type [] AutoGenerated = { 21 | Value1: string 22 | Value2: int 23 | Value3: double 24 | Value4: AutoGeneratedNested 25 | } 26 | 27 | let makeAutoGeneratedNested value : AutoGeneratedNested = { 28 | Value1 = "Bla" 29 | Value2 = value 30 | } 31 | 32 | let makeAutoGenerated value : AutoGenerated = { 33 | Value1 = "Bla" 34 | Value2 = value 35 | Value3 = 10. 36 | Value4 = makeAutoGeneratedNested value 37 | } 38 | 39 | let inline manualEquality (value1: AutoGenerated) (value2: AutoGenerated) = 40 | value1.Value1.Equals(value2.Value1) 41 | && value1.Value2.Equals(value2.Value2) 42 | && value1.Value3.Equals(value2.Value3) 43 | && value1.Value4.Value1.Equals(value2.Value4.Value1) 44 | && value1.Value4.Value2.Equals(value2.Value4.Value2) 45 | 46 | let inline slowIEquatableEquality (value1: #IEquatable<'a>) (value2: #IEquatable<'a>) = value1.Equals(value2) 47 | let inline fastIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a) (value2: 'a) = value1.Equals(value2) 48 | 49 | let inline inrefIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a inref) (value2: 'a inref) = value1.Equals(value2) 50 | let inline byrefIEquatableEquality<'a when 'a :> IEquatable<'a>> (value1: 'a byref) (value2: 'a byref) = value1.Equals(value2) 51 | 52 | let defaultValue = makeAutoGenerated 100500 53 | let defaultValue2 = makeAutoGenerated 100500 54 | let array1 = Array.init 1000 makeAutoGenerated 55 | let array2 = Array.init 1000 makeAutoGenerated 56 | 57 | let list1 = List.init 1000 makeAutoGenerated 58 | let list2 = List.init 1000 makeAutoGenerated 59 | 60 | [] 61 | type Benchmark() = 62 | 63 | [] 64 | member x.TestAutoGeneratedEquality() = 65 | Array.forall2 (fun first second -> first = second) array1 array2 66 | 67 | [] 68 | member x.TestAutoGeneratedEqualityForArrayDirectly() = 69 | array1 = array2 70 | 71 | [] 72 | member x.TestAutoGeneratedEqualityUsingIEquatable() = 73 | Array.forall2 (fun first second -> fastIEquatableEquality first second) array1 array2 74 | 75 | [] 76 | member x.TestAutoGeneratedEqualityUsingSequenceEquals() = 77 | array1.AsSpan().SequenceEqual(array2) 78 | 79 | [] 80 | member x.TestAutoGeneratedEqualityUsingAnotherFasterOperator() = 81 | Array.forall2 (fun first second -> first ==== second) array1 array2 82 | 83 | [] 84 | member x.TestAutoGeneratedEqualityWithLists() = 85 | List.forall2 (fun first second -> first = second) list1 list2 86 | 87 | [] 88 | member x.TestAutoGeneratedEqualityWithListsDirectly() = 89 | list1 = list2 90 | 91 | [] 92 | member x.TestAutoGeneratedEqualityUsingIEquatableWithLists() = 93 | List.forall2 (fun first second -> fastIEquatableEquality first second) list1 list2 -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/GenericTaskBuilder.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.GenericTaskBuilder 2 | 3 | open System.Collections.Generic 4 | open System.Threading.Tasks 5 | open BenchmarkDotNet.Attributes 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | open En3Tho.FSharp.Extensions 8 | 9 | let task2 = En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.Native.TaskBuilder() 10 | 11 | type MapAsyncEnumerableEnumerator<'a, 'b>(source1: IAsyncEnumerable<'a>, map: 'a -> 'b) = 12 | let enumerator = source1.GetAsyncEnumerator() 13 | 14 | interface IAsyncEnumerator<'b> with 15 | member this.Current = enumerator.Current |> map 16 | member this.DisposeAsync() = enumerator.DisposeAsync() 17 | member this.MoveNextAsync() = enumerator.MoveNextAsync() 18 | 19 | module AsyncEnumerable = 20 | let map map source = { new IAsyncEnumerable<'b> with member _.GetAsyncEnumerator(_) = MapAsyncEnumerableEnumerator<'a, 'b>(source, map) } 21 | 22 | let init v = taskSeq { 23 | for i in 1 .. v do 24 | do! Task.Yield() 25 | yield i 26 | } 27 | 28 | [] 29 | [] 30 | type Benchmark() = 31 | 32 | [] 33 | member _.WaitTask() = task { 34 | do! Task.Yield() 35 | } 36 | 37 | [] 38 | member _.WaitTask2() = task2 { 39 | do! Task.Yield() 40 | } 41 | 42 | [] 43 | [)>] 44 | type AsyncSeqBenchmark() = 45 | 46 | [] 47 | member _.Direct() = task2 { 48 | for v in AsyncEnumerable.init 10 do 49 | string v |> ignore 50 | } 51 | 52 | [] 53 | member _.WihtoutGseqMap() = task2 { 54 | let values = 55 | AsyncEnumerable.init 10 56 | |> AsyncEnumerable.map string 57 | 58 | for _ in values do () 59 | } 60 | 61 | [] 62 | member _.WithGSeqMap() = task2 { // slower and allocates more for single map case. TODO: more? 63 | let values = 64 | AsyncEnumerable.init 10 65 | |> GSeq.withAsyncSeq (GSeq.map string) 66 | 67 | for _ in values do () 68 | } -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/HttpVerbParsing.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.HttpVerbParsing 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Text 6 | open BenchmarkDotNet.Attributes 7 | open Benchmarks.Lib 8 | 9 | module HttpChecker = 10 | 11 | let [] C = 67uy 12 | 13 | let [] P = 80uy 14 | let [] U = 85uy 15 | let [] A = 65uy 16 | 17 | let [] G = 71uy 18 | let [] H = 72uy 19 | 20 | let [] D = 68uy 21 | let [] O = 79uy 22 | let [] T = 84uy 23 | 24 | let [] NewLine = 10uy 25 | 26 | let connect = Encoding.UTF8.GetBytes("CONNECT") 27 | 28 | let post = Encoding.UTF8.GetBytes("POST") 29 | let put = Encoding.UTF8.GetBytes("PUT") 30 | let patch = Encoding.UTF8.GetBytes("PATCH") 31 | 32 | let get = Encoding.UTF8.GetBytes("GET") 33 | let head = Encoding.UTF8.GetBytes("HEAD") 34 | 35 | let delete = Encoding.UTF8.GetBytes("DELETE") 36 | let options = Encoding.UTF8.GetBytes("OPTIONS") 37 | let trace = Encoding.UTF8.GetBytes("TRACE") 38 | 39 | let getHttpVerbLength (span: ReadOnlySpan) = 40 | let mutable bytes = null 41 | match span[0] with 42 | | C -> bytes <- connect 43 | | G -> bytes <- get 44 | | P -> 45 | match span[1] with 46 | | O -> bytes <- post 47 | | U -> bytes <- put 48 | | A -> bytes <- patch 49 | | _ -> () 50 | | D -> bytes <- delete 51 | | H -> bytes <- head 52 | | O -> bytes <- options 53 | | T -> bytes <- trace 54 | | _ -> () 55 | if not (Object.ReferenceEquals(bytes, null)) && span.StartsWith(bytes) then 56 | bytes.Length 57 | else 58 | 0 59 | 60 | [] 61 | [] 62 | type Benchmark() = 63 | 64 | member val Bytes = null with get, set 65 | 66 | [] 67 | member this.GlobalSetup() = 68 | this.Bytes <- Encoding.UTF8.GetBytes("TRACE blabla") 69 | 70 | [] 71 | member this.GetHttpVerbLengthLoop() = 72 | let span = this.Bytes.AsSpan() 73 | let value = Unsafe.As(&Unsafe.AsRef(&span[0])) 74 | HttpVerbsWithLength.GetHttpVerbLengthWithLoop(value) 75 | 76 | [] 77 | member this.GetHttpVerbLengthIfElse() = 78 | let span = this.Bytes.AsSpan() 79 | let value = Unsafe.As(&Unsafe.AsRef(&span[0])) 80 | HttpVerbsAvx2.GetHttpVerbLengthIfElse(value) 81 | 82 | [] 83 | member this.GetHttpVerbLengthAvx2() = 84 | let span = this.Bytes.AsSpan() 85 | let value = Unsafe.As(&Unsafe.AsRef(&span[0])) 86 | HttpVerbsAvx2.GetHttpVerbLengthAvx2(value) 87 | 88 | [] 89 | member this.GetHttpVerbLengthAvx2_2() = 90 | let span = this.Bytes.AsSpan() 91 | let value = Unsafe.As(&Unsafe.AsRef(&span[0])) 92 | HttpVerbsAvx2.GetHttpVerbLengthAvx2_2(value) 93 | 94 | [] 95 | member this.GetHttpVerbLengthAvx2_3() = 96 | let span = this.Bytes.AsSpan() 97 | let value = Unsafe.As(&Unsafe.AsRef(&span[0])) 98 | HttpVerbsAvx2.GetHttpVerbLengthAvx2_3(value) 99 | 100 | [] 101 | member this.GetHttpVerbLengthArrays() = 102 | HttpChecker.getHttpVerbLength this.Bytes -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/IEquatableEqualityOperatorBenchmark.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.IEquatableEqualityOperatorBenchmark 2 | 3 | open BenchmarkDotNet.Attributes 4 | open En3Tho.FSharp.Extensions 5 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder 6 | 7 | type [] CustomRecordType = { 8 | V1: string 9 | V2: int 10 | V3: int64 11 | } 12 | 13 | [] 14 | type Benchmark() = 15 | 16 | let value1 = { 17 | V1 = "test" 18 | V2 = 10 19 | V3 = 10 20 | } 21 | 22 | let value2 = { 23 | V1 = "test" 24 | V2 = 10 25 | V3 = 11 26 | } 27 | 28 | let mkSeq1 count = 29 | Seq.init count (fun _ -> value1) 30 | 31 | let mkSeq2 count = 32 | let left = Seq.init (count - 1) (fun _ -> value1) 33 | let right = seq { value2 } 34 | Seq.append left right 35 | 36 | let array1 = mkSeq1 100 |> Seq.toArray 37 | let array2 = mkSeq2 100 |> Seq.toArray 38 | let list1 = mkSeq1 100 |> Seq.toList 39 | let list2 = mkSeq2 100 |> Seq.toList 40 | let sequence1 = array1 :> seq<_> 41 | let sequence2 = array2 :> seq<_> 42 | let resizeArray1 = ResizeArray() { yield! mkSeq1 100 } 43 | let resizeArray2 = ResizeArray() { yield! mkSeq2 100 } 44 | 45 | [] 46 | member _.TestValues() = value1 == value2 47 | 48 | [] 49 | member _.TestArrays() = array1 === array2 50 | 51 | [] 52 | member _.TestLists() = list1 === list2 53 | 54 | [] 55 | member _.TestSequences() = sequence1 === sequence2 56 | 57 | [] 58 | member _.TestResizeArrays() = resizeArray1 === resizeArray2 59 | 60 | [] 61 | member _.TestValuesUsingDefaultEqualityOperator() = value1 = value2 62 | 63 | [] 64 | member _.TestArraysUsingDefaultEqualityOperator() = array1 = array2 65 | 66 | [] 67 | member _.TestListsUsingDefaultEqualityOperator() = list1 = list2 68 | 69 | [] 70 | member _.TestSequencesUsingDefaultEqualityOperator() = sequence1 = sequence2 71 | 72 | [] 73 | member _.TestResizeArraysUsingDefaultEqualityOperator() = resizeArray1 = resizeArray2 -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/MathematicalOperatorsBenchmarks.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.MathematicalOperatorsBenchmarks 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open BenchmarkDotNet.Attributes 6 | 7 | [] 8 | type Benchmark() = 9 | 10 | [] 11 | member val IntValue1 = 0L with get, set 12 | member val TimeSpanValue1 = TimeSpan() with get, set 13 | member val DateTimeValue1 = DateTime() with get, set 14 | 15 | member val IntValue2 = 0L with get, set 16 | member val TimeSpanValue2 = TimeSpan() with get, set 17 | member val DateTimeValue2 = DateTime() with get, set 18 | 19 | member this.GlobalSetup() = 20 | this.TimeSpanValue1 <- TimeSpan.FromMinutes(this.IntValue1.f64) 21 | this.DateTimeValue1 <- DateTime(this.IntValue1) 22 | 23 | this.IntValue2 <- this.IntValue1 24 | this.TimeSpanValue2 <- TimeSpan.FromMinutes(this.IntValue1.f64) 25 | this.DateTimeValue2 <- DateTime(this.IntValue1) 26 | 27 | [] 28 | member this.TestInt() = this.IntValue1 - this.IntValue2 29 | 30 | [] 31 | member this.TimeSpan() = this.TimeSpanValue1 - this.TimeSpanValue2 32 | 33 | [] 34 | member this.DateTime() = this.DateTimeValue1 - this.DateTimeValue2 -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/NodeCode.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.NodeCode 2 | 3 | open System.Diagnostics 4 | open Benchmarks 5 | 6 | [] 7 | type NodeCode<'T> = Node of Async<'T> 8 | 9 | [] 10 | type NodeCodeBuilder() = 11 | 12 | static let zero = Node(async.Zero()) 13 | 14 | [] 15 | member _.Zero() : NodeCode = zero 16 | 17 | [] 18 | member _.Delay(f: unit -> NodeCode<'T>) = 19 | Node(async.Delay(fun () -> match f() with Node(p) -> p)) 20 | 21 | [] 22 | member _.Return(value) = Node(async.Return(value)) 23 | 24 | [] 25 | member _.ReturnFrom(computation: NodeCode<_>) = computation 26 | 27 | [] 28 | member _.Bind(Node(p): NodeCode<'a>, binder: 'a -> NodeCode<'b>) : NodeCode<'b> = 29 | Node(async.Bind(p, fun x -> match binder x with Node p -> p)) 30 | 31 | [] 32 | member _.TryWith(Node(p): NodeCode<'T>, binder: exn -> NodeCode<'T>) : NodeCode<'T> = 33 | Node(async.TryWith(p, fun ex -> match binder ex with Node p -> p)) 34 | 35 | [] 36 | member _.TryFinally(Node(p): NodeCode<'T>, binder: unit -> unit) : NodeCode<'T> = 37 | Node(async.TryFinally(p, binder)) 38 | 39 | [] 40 | member _.For(xs: 'T seq, binder: 'T -> NodeCode) : NodeCode = 41 | Node(async.For(xs, fun x -> match binder x with Node p -> p)) 42 | 43 | [] 44 | member _.Combine(Node(p1): NodeCode, Node(p2): NodeCode<'T>) : NodeCode<'T> = 45 | Node(async.Combine(p1, p2)) 46 | 47 | let node = NodeCodeBuilder() 48 | 49 | open BenchmarkDotNet.Attributes 50 | 51 | [] 52 | [] 53 | type ReturnBenchmark() = 54 | let value = 1 55 | 56 | let directReturned = node.Return(value) 57 | let ceReturned = node { return value } 58 | 59 | [] 60 | member _.DirectReturn() = 61 | node.Return(value) 62 | 63 | [] 64 | member _.CompExprReturn() = 65 | node { return value } 66 | 67 | [] 68 | member _.AwaitDirectReturned() = 69 | match directReturned with 70 | | Node computation -> 71 | Async.RunSynchronously(computation) |> ignore 72 | 73 | [] 74 | member _.AwaitCEReturned() = 75 | match ceReturned with 76 | | Node computation -> 77 | Async.RunSynchronously(computation) |> ignore -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/Program.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Runtime.InteropServices 3 | open BenchmarkDotNet.Configs 4 | open BenchmarkDotNet.Running 5 | open Benchmarks 6 | open Benchmarks.FSharp 7 | 8 | BenchmarkRunner.Run< 9 | // GenericEqualityBenchmark.Benchmark, 10 | // NodeCode.ReturnBenchmark, 11 | // FSharpOptimizer.Benchmark, 12 | // FSharpOptimizerWithExperimentalPipe.Benchmark, 13 | // HttpVerbParsing.Benchmark, 14 | GSeq.Benchmark 15 | >() |> ignore -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/ScanfBenchmark.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.ScanfBenchmark 2 | 3 | open System 4 | open BenchmarkDotNet.Attributes 5 | open BenchmarkDotNet.Jobs 6 | open En3Tho.FSharp.Extensions 7 | open En3Tho.FSharp.Extensions.Scanf 8 | 9 | module Assets = // for CSharp version of bench 10 | 11 | let value1 = ref "0" 12 | let value2 = ref 0 13 | let value3 = ref 0. 14 | let value4 = ref 0.f 15 | let value5 = ref '0' 16 | let value6 = ref false 17 | let value7 = ref 0u 18 | 19 | let manySmallValuesText = $"{value1.Value} {value2.Value} {value3.Value} {value4.Value} {value5.Value} {value6.Value} {value7.Value}" 20 | let manySmallValuesFmt : Printf.StringFormat<_> = $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}" 21 | 22 | let realisticFmt : Printf.StringFormat<_> = $"/authorize {value1} {value2}" 23 | 24 | let cmd = ref "" 25 | let userName = ref "" 26 | let userCode = ref 0 27 | let realisticCommandFmt : Printf.StringFormat<_> = $"/{cmd} {userName} {userCode}" 28 | 29 | let intRef = ref 0 30 | let floatRef = ref 0. 31 | let primitivesOnlyFmt : Printf.StringFormat<_> = $"{intRef} {floatRef}" 32 | 33 | open Assets 34 | 35 | [< 36 | MemoryDiagnoser; 37 | DisassemblyDiagnoser(filters = [||]); 38 | //SimpleJob(RuntimeMoniker.Net60) 39 | Config(typeof<``Net 6, Pgo``>) 40 | >] 41 | type Benchmark() = 42 | 43 | [] 44 | member _.ManySmallValues() = 45 | let value1 = ref "0" 46 | let value2 = ref 0 47 | let value3 = ref 0. 48 | let value4 = ref 0.f 49 | let value5 = ref '0' 50 | let value6 = ref false 51 | let value7 = ref 0u 52 | 53 | let text = $"{value1.Value} {value2.Value} {value3.Value} {value4.Value} {value5.Value} {value6.Value} {value7.Value}" 54 | scanf $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}" text 55 | 56 | [] 57 | member _.ManySmallValuesTextPreallocated() = 58 | scanf $"{value1} {value2} {value3} {value4} {value5} {value6} {value7}" manySmallValuesText 59 | 60 | [] 61 | member _.ManySmallValuesFullyPreallocated() = 62 | scanf manySmallValuesFmt manySmallValuesText 63 | 64 | [] 65 | member _.Realistic() = 66 | let value1 = ref "0" 67 | let value2 = ref 0 68 | scanf $"/authorize {value1} {value2}" "/authorize myText 123" 69 | 70 | [] 71 | member _.RealisticPreallocated() = 72 | scanf realisticFmt "/authorize myText 123" 73 | 74 | [] 75 | member _.RealisticCommand() = 76 | scanf $"/{cmd} {userName} {userCode}" "/authorize myText 123" 77 | 78 | [] 79 | member _.RealisticCommandPreallocated() = 80 | scanf realisticCommandFmt "/authorize myText 123" 81 | 82 | // [] 83 | // member _.RealisticCommandPreallocatedSpan() = 84 | // scanfSpan realisticCommandFmt ("/authorize myText 123".AsSpan()) 85 | 86 | [] 87 | member _.PrimitivesOnlyPreallocated() = 88 | scanf primitivesOnlyFmt "123456 123456.123456" 89 | 90 | // [] 91 | // member _.PrimitivesOnlyPreallocatedSpan() = 92 | // scanfSpan primitivesOnlyFmt ("123456 123456.123456".AsSpan()) -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/TaskBuildersBenchmarks.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.TaskBuildersBenchmarks 2 | 3 | open System.Threading.Tasks 4 | open BenchmarkDotNet.Attributes 5 | open BenchmarkDotNet.Jobs 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | 8 | [< 9 | MemoryDiagnoser; 10 | DisassemblyDiagnoser(filters = [||]); 11 | SimpleJob(RuntimeMoniker.Net60) 12 | >] 13 | type Benchmark() = 14 | let returnSomeValueAsTask x = Task.FromResult(x) 15 | let returnSomeValueAsValueTask x = ValueTask.FromResult(x) 16 | let returnSomeValueAsAsync x = async.Return(x) 17 | 18 | let runVTaskTaskAndAsyncAsTask() = task { 19 | let! x = returnSomeValueAsValueTask 5 20 | let! y = returnSomeValueAsTask 10 21 | let! z = returnSomeValueAsAsync 15 22 | return x + y 23 | } 24 | 25 | let runVTaskOnlyAsTask() = task { 26 | let! x = returnSomeValueAsValueTask 5 27 | let! y = returnSomeValueAsValueTask 10 28 | return x + y 29 | } 30 | 31 | let runVTaskOnlyAsVTask() = vtask { 32 | let! x = returnSomeValueAsValueTask 5 33 | let! y = returnSomeValueAsValueTask 10 34 | return x + y 35 | } 36 | 37 | let runVTaskAndTaskAsTask() = task { 38 | let! x = returnSomeValueAsValueTask 5 39 | let! y = returnSomeValueAsValueTask 10 40 | let! z = returnSomeValueAsTask 15 41 | return x + y + z 42 | } 43 | 44 | let runVTaskAndTaskAsVTask() = vtask { 45 | let! x = returnSomeValueAsValueTask 5 46 | let! y = returnSomeValueAsValueTask 10 47 | let! z = returnSomeValueAsTask 15 48 | return x + y + z 49 | } 50 | 51 | let runVTaskAndTaskAsUnitTask() = utask { 52 | let! x = returnSomeValueAsValueTask 5 53 | let! y = returnSomeValueAsValueTask 10 54 | let! z = returnSomeValueAsTask 15 55 | return () 56 | } 57 | 58 | let runVTaskAndTaskAsUnitVTask() = uvtask { 59 | let! x = returnSomeValueAsValueTask 5 60 | let! y = returnSomeValueAsValueTask 10 61 | let! z = returnSomeValueAsTask 15 62 | return () 63 | } 64 | 65 | [] 66 | member _.RunVTaskOnlyAsTask() = runVTaskOnlyAsTask().Result 67 | 68 | [] 69 | member _.RunVTaskOnlyAsVTask() = runVTaskOnlyAsVTask().Result 70 | 71 | [] 72 | member _.RunVTaskAndTaskAsTask() = runVTaskAndTaskAsTask().Result 73 | 74 | [] 75 | member _.RunVTaskAndTaskAsUnitTask() = runVTaskAndTaskAsUnitTask().GetAwaiter().GetResult() 76 | 77 | [] 78 | member _.RunVTaskAndTaskAsUnitVTask() = runVTaskAndTaskAsUnitVTask().GetAwaiter().GetResult() 79 | 80 | [] 81 | member _.RunVTaskTaskAndAsyncAsTask() = runVTaskTaskAndAsyncAsTask().Result -------------------------------------------------------------------------------- /benchmarks/Benchmarks.FSharp/VectorsBase64.fs: -------------------------------------------------------------------------------- 1 | module Benchmarks.VectorsBase64 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | open BenchmarkDotNet.Attributes 7 | open En3Tho.FSharp.Extensions 8 | 9 | [] 10 | [] 11 | type Benchmark() = 12 | 13 | member val Bytes = null with get, set 14 | 15 | [] 16 | member this.GlobalSetup() = 17 | this.Bytes <- Array.init 24 (fun _ -> 0xFFuy) 18 | 19 | member _.BytesToBase64VectorLookup1(source: ReadOnlySpan, destination: Span) = 20 | // Convert from 21 | // [ 00000000_66666666_55555555_44444444_00000000_33333333_22222222_11111111 ] 22 | // To 23 | // [ 00666666_00665555_00555544_00444444_00333333_00332222_00222211_00111111 ] 24 | 25 | // [ 00000000_33333333_22222222_11111111 ] 26 | // [ 00333333_00332222_00222211_00111111 ] 27 | 28 | 29 | // [ 00000000_00000000_00000000_00111111 ] // and 0x3F 30 | // [ 00000000_00000000_00000000_00111111 ] // shift 0 31 | 32 | // [ 00000000_00000000_00002222_11000000 ] // and 0xFC0 33 | // [ 00000000_00000000_00222211_00000000 ] // shift 2 34 | 35 | // [ 00000000_00000033_22220000_00000000 ] // and 0x3F000 36 | // [ 00000000_00332222_00000000_00000000 ] // shift 4 37 | 38 | // [ 00000000_33333300_00000000_00000000 ] // and 0xFC0000 39 | // [ 00333333_00000000_00000000_00000000 ] // shift 6 40 | 41 | // ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ 42 | let lookup0_31 = v256.Create(0x48_47_46_45_44_43_42_41UL, 0x50_4F_4E_4D_4C_4B_4A_49UL, 0x58_57_56_55_54_53_52_51UL, 0x66_65_64_63_62_61_5A_59UL).u8 43 | let lookup32_63 = v256.Create(0x6E_6D_6C_6B_6A_69_68_67UL, 0x76_75_74_73_72_71_70_6FUL, 0x33_32_31_30_7A_79_78_77UL, 0x2F_2B_39_38_37_36_35_34UL).u8 44 | let valuesU64 = MemoryMarshal.Cast<_, u64>(source) 45 | 46 | let mutable i = 0 47 | while i + 2 < valuesU64.Length do 48 | let v1 = valuesU64[i] 49 | let v2 = valuesU64[i + 1] 50 | let v3 = valuesU64[i + 2] 51 | 52 | let vec = v256.Create(v1, v2, v3, 0UL) 53 | 54 | // [ 00000000_66666666_55555555_44444444_00000000_33333333_22222222_11111111 ] 55 | let shuffled = v256.Shuffle(vec.u8, v256.Create(0xF0_05_04_03_F0_02_01_00UL, 0xF0_0B_0A_09_F0_08_07_06UL, 0xF0_11_10_0F_F0_0E_0D_0CUL, 0xF0_17_16_15_F0_14_13_12UL).u8).u32 56 | 57 | // [ 00666666_00665555_00555544_00444444_00333333_00332222_00222211_00111111 ] 58 | let vec6Bits = 59 | (shuffled &&& v256.Create(0x3Fu)) 60 | ||| ((shuffled &&& v256.Create(0xFC0u)) <<< 2) 61 | ||| ((shuffled &&& v256.Create(0x3F000u)) <<< 4) 62 | ||| ((shuffled &&& v256.Create(0xFC0000u)) <<< 6) 63 | 64 | let mask32_63 = v256.GreaterThan(vec6Bits.u8, v256.Create(31uy)) 65 | let mask0_31 = v256.Equals(mask32_63, v256.Zero) 66 | 67 | let byteChars0_31 = v256.Shuffle(lookup0_31, vec6Bits.u8 &&& mask0_31) 68 | let byteChars32_63 = v256.Shuffle(lookup32_63, vec6Bits.u8 &&& v256.Create(0x1Fuy) &&& mask32_63) 69 | let byteChars = (byteChars0_31 &&& mask0_31) ||| (byteChars32_63 &&& mask32_63) 70 | 71 | let chars1 = v256.Shuffle(byteChars, v256.Create(0xF0_03_F0_02_F0_01_F0_00UL, 0xF0_07_F0_06_F0_05_F0_04UL, 0xF0_0B_F0_0A_F0_09_F0_08UL, 0xF0_0F_F0_0E_F0_0D_F0_0CUL).u8) 72 | let chars2 = v256.Shuffle(byteChars, v256.Create(0xF0_13_F0_12_F0_11_F0_10UL, 0xF0_17_F0_16_F0_15_F0_14UL, 0xF0_1B_F0_1A_F0_19_F0_18UL, 0xF0_1F_F0_1E_F0_1D_F0_1CUL).u8) 73 | 74 | v256.StoreUnsafe(chars1.u16, &Unsafe.As<_, u16>(&destination[i * 32])) 75 | v256.StoreUnsafe(chars2.u16, &Unsafe.As<_, u16>(&destination[i * 32 + 16])) 76 | 77 | i <- i + 3 78 | 79 | [] 80 | member this.ConvertToBase64() = 81 | Convert.ToBase64String(this.Bytes) |> ignore 82 | 83 | [] 84 | member this.ConvertToBase64Naive() = 85 | let converted = Array.zeroCreate 32 86 | this.BytesToBase64VectorLookup1(this.Bytes, converted.AsSpan()) -------------------------------------------------------------------------------- /benchmarks/Benchmarks.Lib/Benchmarks.Lib.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.0", 4 | "rollForward": "latestMinor", 5 | "allowPrerelease": false 6 | } 7 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection.XUnit/En3Tho.Extensions.DependencyInjection.XUnit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8.0.1 5 | Small set of extensions based on XUnit.DependencyInjection library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection.XUnit/IServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Hosting; 3 | 4 | namespace En3Tho.Extensions.DependencyInjection.XUnit; 5 | 6 | public interface IXunitStartup 7 | { 8 | IHostBuilder? CreateHostBuilder() => null; 9 | void ConfigureHost(IHostBuilder hostBuilder) { } 10 | void ConfigureServices(IServiceCollection serviceCollection, HostBuilderContext hostBuilderContext) { } 11 | } 12 | 13 | public static class IServiceCollectionExtensions 14 | { 15 | public static IServiceCollection AddMockSingleton(this IServiceCollection collection) 16 | where TService : class 17 | { 18 | collection.AddSingleton>() 19 | .AddSingleton(services => services.GetRequiredService>().Object); 20 | return collection; 21 | } 22 | 23 | public static IServiceCollection AddMockScoped(this IServiceCollection collection) 24 | where TService : class 25 | { 26 | collection.AddScoped>() 27 | .AddScoped(services => services.GetRequiredService>().Object); 28 | return collection; 29 | } 30 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/AddFromConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Configuration; 2 | using Microsoft.Extensions.DependencyInjection; 3 | using Microsoft.Extensions.DependencyInjection.Extensions; 4 | 5 | namespace En3Tho.Extensions.DependencyInjection; 6 | 7 | public static partial class IServiceCollectionExtensions 8 | { 9 | public static IServiceCollection TryAddSingletonFromConfiguration(this IServiceCollection collection, string name) 10 | where TService : class 11 | { 12 | collection.TryAddSingleton(services => 13 | services.GetRequiredService() 14 | .GetSection(name) 15 | .Get()!); 16 | return collection; 17 | } 18 | 19 | public static IServiceCollection TryAddSingletonFromConfiguration(this IServiceCollection collection) 20 | where TService : class 21 | { 22 | var name = typeof(TService).Name; 23 | return collection.TryAddSingletonFromConfiguration(name); 24 | } 25 | 26 | public static IServiceCollection TryAddSingletonFromConfigurationOrFail(this IServiceCollection collection, string name) 27 | where TService : class 28 | { 29 | return collection.TryAddSingletonOrFail(services => 30 | services.GetRequiredService() 31 | .GetSection(name) 32 | .Get()!); 33 | } 34 | 35 | public static IServiceCollection TryAddSingletonFromConfigurationOrFail(this IServiceCollection collection) 36 | where TService : class 37 | { 38 | var name = typeof(TService).Name; 39 | return collection.TryAddSingletonFromConfigurationOrFail(name); 40 | } 41 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/AddHttpClient.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace En3Tho.Extensions.DependencyInjection; 4 | 5 | public static partial class IServiceCollectionExtensions 6 | { 7 | public static IServiceCollection AddHttpClient(this IServiceCollection collection, Uri uri) 8 | where TClient : class 9 | { 10 | collection.AddHttpClient(client => client.BaseAddress = uri); 11 | return collection; 12 | } 13 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/ChangeLifetime.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace En3Tho.Extensions.DependencyInjection; 4 | 5 | public static partial class IServiceCollectionExtensions 6 | { 7 | private static bool TryChangeLifetimeInternal(this IServiceCollection collection, ServiceLifetime newLifetime) 8 | { 9 | var changed = false; 10 | 11 | for (var i = 0; i < collection.Count; i++) 12 | { 13 | var descriptor = collection[i]; 14 | if (descriptor.ServiceType.Equals(typeof(TService)) 15 | && descriptor.TryChangeLifetime(newLifetime, out var newDescriptor)) 16 | { 17 | collection[i] = newDescriptor; 18 | changed = true; 19 | } 20 | } 21 | 22 | return changed; 23 | } 24 | 25 | public static IServiceCollection TryChangeLifetime(this IServiceCollection collection, 26 | ServiceLifetime newLifetime) 27 | { 28 | collection.TryChangeLifetimeInternal(newLifetime); 29 | return collection; 30 | } 31 | 32 | public static IServiceCollection TryChangeLifetimeOrFail(this IServiceCollection collection, 33 | ServiceLifetime newLifetime) 34 | { 35 | if (collection.TryChangeLifetimeInternal(newLifetime)) 36 | return collection; 37 | 38 | throw new InvalidOperationException("Service type is either not found in collection or is registered as single object instance"); 39 | } 40 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/Configuration.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace En3Tho.Extensions.DependencyInjection; 4 | 5 | public static partial class IServiceCollectionExtensions 6 | { 7 | public static IServiceCollection ConfigureByName(this IServiceCollection services) where TOptions : class 8 | => services.Configure(typeof(TOptions).Name, _ => {}); 9 | 10 | public static IServiceCollection ConfigureByName(this IServiceCollection services, Action configure) where TOptions : class 11 | => services.Configure(typeof(TOptions).Name, configure); 12 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/En3Tho.Extensions.DependencyInjection.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8.0.1 5 | Extensions for IServiceCollection 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/Update.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | 3 | namespace En3Tho.Extensions.DependencyInjection; 4 | 5 | public static partial class IServiceCollectionExtensions 6 | { 7 | public static IServiceCollection Update(this IServiceCollection collection, Func configureFactory) 8 | where TService : class 9 | { 10 | if (!collection.TryFindServiceDescriptor(out var possibleDescriptor)) 11 | throw new InvalidOperationException( 12 | $"Service {typeof(TService).FullName} was not found among registered services"); 13 | 14 | var (oldDescriptor, oldDescriptorIndex) = possibleDescriptor; 15 | 16 | ServiceDescriptor newDescriptor; 17 | 18 | switch (oldDescriptor) 19 | { 20 | // case of Add(x => instance) 21 | case { ImplementationFactory: { } factory }: 22 | var factoryFromFactory = (IServiceProvider serviceProvider) => 23 | { 24 | var oldImplementationInstance = factory(serviceProvider); 25 | return configureFactory((TService)oldImplementationInstance); 26 | }; 27 | 28 | newDescriptor = new(typeof(TService), factoryFromFactory, oldDescriptor.Lifetime); 29 | break; 30 | 31 | // case of Add(instance) 32 | case { ImplementationInstance: { } instance }: 33 | var factoryFromInstance = (IServiceProvider serviceProvider) => configureFactory((TService)instance); 34 | newDescriptor = new(typeof(TService), factoryFromInstance, oldDescriptor.Lifetime); 35 | break; 36 | 37 | // case of Add() 38 | default: 39 | var implementationFactory = ActivatorUtilities.CreateFactory(typeof(TService), Array.Empty()); 40 | var factoryFromServices = (IServiceProvider serviceProvider) => 41 | { 42 | var oldImplementationInstance = implementationFactory(serviceProvider, Array.Empty()); 43 | return configureFactory((TService)oldImplementationInstance); 44 | }; 45 | 46 | newDescriptor = new(typeof(TService), factoryFromServices, oldDescriptor.Lifetime); 47 | break; 48 | } 49 | 50 | collection[oldDescriptorIndex] = newDescriptor; 51 | 52 | return collection; 53 | } 54 | } -------------------------------------------------------------------------------- /src/En3Tho.Extensions.DependencyInjection/Utility.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics.CodeAnalysis; 2 | using Microsoft.Extensions.DependencyInjection; 3 | 4 | namespace En3Tho.Extensions.DependencyInjection; 5 | 6 | public static class ServiceDescriptorExtensions 7 | { 8 | public static bool TryChangeLifetime(this ServiceDescriptor descriptor, ServiceLifetime newLifetime, [NotNullWhen(true)] out ServiceDescriptor? result) 9 | { 10 | if (descriptor.Lifetime.Equals(newLifetime)) 11 | { 12 | result = descriptor; 13 | return true; 14 | } 15 | 16 | switch (descriptor) 17 | { 18 | case { ImplementationFactory: {} factory }: 19 | result = new(descriptor.ServiceType, factory, newLifetime); 20 | return true; 21 | 22 | case { ImplementationInstance: {} }: 23 | result = null; 24 | return false; 25 | 26 | case { ImplementationType: {} implementationType }: 27 | result = new(descriptor.ServiceType, descriptor.ImplementationType, newLifetime); 28 | return true; 29 | 30 | default: 31 | result = new(descriptor.ServiceType, descriptor.ServiceType, newLifetime); 32 | return true; 33 | } 34 | } 35 | } 36 | 37 | public static partial class IServiceCollectionExtensions 38 | { 39 | internal record struct ServiceDescriptorEnvelope(ServiceDescriptor Descriptor, int Index); 40 | 41 | internal static bool TryFindServiceDescriptor(this IServiceCollection collection, Type type, 42 | out ServiceDescriptorEnvelope serviceDescriptor) 43 | { 44 | for (var i = 0; i < collection.Count; i++) 45 | { 46 | var descriptor = collection[i]; 47 | if (descriptor.ServiceType == type) 48 | { 49 | serviceDescriptor = new ServiceDescriptorEnvelope(descriptor, i); 50 | return true; 51 | } 52 | } 53 | 54 | serviceDescriptor = default; 55 | return false; 56 | } 57 | 58 | internal static bool TryFindServiceDescriptor(this IServiceCollection collection, 59 | out ServiceDescriptorEnvelope serviceDescriptor) => 60 | collection.TryFindServiceDescriptor(typeof(TService), out serviceDescriptor); 61 | 62 | internal static void EnsureNotImplementedNoMoreThan(this IServiceCollection collection, Type serviceType, int times) 63 | { 64 | var descriptors = collection.Where(d => d.ServiceType == serviceType); 65 | if (descriptors.Count() > times) 66 | { 67 | var message = 68 | $"Service type {serviceType.FullName} is already implemented more than {times} times by {string.Join(", ", descriptors.Select(d => (d.ImplementationType ?? serviceType).FullName))}"; 69 | throw new InvalidOperationException(message); 70 | } 71 | } 72 | 73 | internal static void EnsureNotImplemented(this IServiceCollection collection, Type serviceType) => 74 | collection.EnsureNotImplementedNoMoreThan(serviceType, 0); 75 | 76 | 77 | internal static void EnsureImplementedOnceAtMaxAndRemoveIfImplemented(this IServiceCollection collection, 78 | Type serviceType) 79 | { 80 | var descriptors = collection.Where(d => d.ServiceType == serviceType).ToArray(); 81 | if (descriptors is { Length: > 1 }) 82 | { 83 | var message = 84 | $"Service type {serviceType.FullName} is already implemented by {string.Join(", ", descriptors.Select(d => (d.ImplementationType ?? serviceType).FullName))}"; 85 | throw new InvalidOperationException(message); 86 | } 87 | 88 | if (descriptors.Length == 1) 89 | collection.Remove(descriptors[0]); 90 | } 91 | } -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/En3Tho.FSharp.ComputationExpressions.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9.0.0 5 | A set of computation expressions for F# base library 6 | 7 | 8 | 9 | TRACE 10 | 11 | $(NoWarn), 12 | 3511 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GSeqExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions 2 | 3 | open System.Collections.Generic 4 | open En3Tho.FSharp.ComputationExpressions 5 | open En3Tho.FSharp.ComputationExpressions.Tasks 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.Low 7 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.High 8 | open En3Tho.FSharp.ComputationExpressions.ArrayPoolBasedBuilders 9 | open System.Runtime.CompilerServices 10 | 11 | module GSeqEnumerators = 12 | 13 | type [] AsyncEnumeratorCurrentValueProvider<'a>(wrapper: IAsyncEnumerator<'a>) = 14 | 15 | interface System.Collections.IEnumerator with 16 | member this.Current = wrapper.Current 17 | member this.MoveNext() = true 18 | member this.Reset() = () 19 | 20 | interface IEnumerator<'a> with 21 | member this.Current = wrapper.Current 22 | member this.Dispose() = () 23 | 24 | module GSeq = 25 | 26 | [] 27 | let inline toArray<'i, 'e when 'e: struct and 'e :> IEnumerator<'i>>(enumerator: 'e) = arr { 28 | let mutable enumerator = enumerator 29 | while enumerator.MoveNext() do 30 | enumerator.Current 31 | } 32 | 33 | [] 34 | let inline toResizeArray<'i, 'e when 'e: struct and 'e :> IEnumerator<'i>>(enumerator: 'e) = rsz { 35 | let mutable enumerator = enumerator 36 | while enumerator.MoveNext() do 37 | enumerator.Current 38 | } 39 | 40 | [] 41 | let inline toBlock<'i, 'e when 'e: struct and 'e :> IEnumerator<'i>>(enumerator: 'e) = block { 42 | let mutable enumerator = enumerator 43 | while enumerator.MoveNext() do 44 | enumerator.Current 45 | } 46 | 47 | open GSeqEnumerators 48 | 49 | // maybe AsyncEnumerable.withGSeq is better but I don't have AsyncEnumerable module now so whatever 50 | let inline withAsyncSeq<'a, 'i, 'e when 'e: struct and 'e :> IEnumerator<'i>> 51 | ([] gseqFactory: AsyncEnumeratorCurrentValueProvider<'a> -> 'e) (e: IAsyncEnumerable<'a>) = 52 | taskSeq { 53 | use enum = e.GetAsyncEnumerator() 54 | let mutable enumerator = gseqFactory (AsyncEnumeratorCurrentValueProvider(enum)) 55 | while! enum.MoveNextAsync() do 56 | if enumerator.MoveNext() then 57 | enumerator.Current 58 | } -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericCollectionCodeExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.ComponentModel 6 | open System.Runtime.CompilerServices 7 | open En3Tho.FSharp.Extensions 8 | 9 | type CollectionCode = UnitBuilderCode 10 | 11 | [] 12 | type UnitLikeCodeExtensions() = 13 | 14 | [] 15 | static member inline While(_, [] moveNext: unit -> bool, [] whileExpr: CollectionCode) : CollectionCode = 16 | fun () -> while moveNext() do (whileExpr()) 17 | 18 | [] 19 | static member inline Combine(_, [] first: CollectionCode, [] second) : CollectionCode = 20 | fun() -> 21 | first() 22 | second() 23 | 24 | [] 25 | static member inline TryFinally(_, [] tryExpr: CollectionCode, [] compensation: CollectionCode) = 26 | fun() -> 27 | try 28 | tryExpr() 29 | finally 30 | compensation() 31 | 32 | [] 33 | static member inline TryWith(_, [] tryExpr, [] compensation: exn -> CollectionCode) : CollectionCode = 34 | fun() -> 35 | try 36 | tryExpr() 37 | with e -> 38 | (compensation e)() 39 | 40 | [] 41 | static member inline Using(this, resource: #IDisposable, [] tryExpr: #IDisposable -> CollectionCode) : CollectionCode = 42 | this.TryFinally( 43 | (fun() -> (tryExpr(resource)())), 44 | (fun() -> if not (isNull (box resource)) then resource.Dispose())) 45 | 46 | 47 | [] 48 | static member inline For(this, values: 'a seq, [] forExpr: 'a -> CollectionCode) : CollectionCode = 49 | this.Using( 50 | values.GetEnumerator(), (fun e -> 51 | this.While((fun () -> e.MoveNext()), (fun () -> 52 | (forExpr e.Current)()) 53 | ) 54 | ) 55 | ) 56 | 57 | // This one is for GSeq mainly 58 | [] 59 | static member inline For<'T, 'TEnumerator when 'TEnumerator :> IEnumerator<'T>>(_, enumerator: 'TEnumerator, [] forExpr: 'T -> CollectionCode) : CollectionCode = 60 | fun () -> 61 | let mutable enumerator = enumerator 62 | while enumerator.MoveNext() do 63 | (forExpr enumerator.Current)() 64 | 65 | [] 66 | static member inline Delay(_, [] delay: unit -> CollectionCode) = 67 | fun () -> (delay())() -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Builder/GenericTaskBuilderBase.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | open Microsoft.FSharp.Core 5 | 6 | type GenericTaskBuilderWithStateReturnBase<'TState>(state: 'TState) = 7 | inherit GenericTaskBuilderReturnCore<'TState>(state) 8 | interface IBindExtensions 9 | interface IReturnExtensions 10 | 11 | type GenericTaskBuilderBase() = 12 | inherit GenericTaskBuilderWithStateReturnBase() 13 | 14 | type GenericTaskSeqBuilderWithStateBase<'TState>(state: 'TState) = 15 | inherit GenericTaskBuilderYieldCore<'TState>(state) 16 | interface IBindExtensions 17 | interface IYieldExtensions 18 | 19 | type GenericTaskBuilderYieldBase() = 20 | inherit GenericTaskSeqBuilderWithStateBase() -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Builder/GenericTaskBuilderExtensions2.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions 2 | 3 | // open System.Runtime.CompilerServices 4 | // open En3Tho.FSharp.ComputationExpressions.Tasks 5 | // open Microsoft.FSharp.Core 6 | // open Microsoft.FSharp.Core.CompilerServices 7 | // open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 8 | 9 | // this is quite painful with tasks actually, I'm not sure I want to expose this 10 | // module MultiBind = 11 | // 12 | // [] 13 | // type LowPriorityImpl() = 14 | // 15 | // [] 16 | // [] 17 | // static member inline Bind2(_: #IBindExtensions, task: ^TaskLike, task2: ^TaskLike2, [] continuation: 'TResult1 * 'TResult2 -> ResumableCode<'TData, 'TResult3>) 18 | // : ResumableCode<'TData, 'TResult3> = 19 | // ResumableCode<'TData, 'TResult3>(fun sm -> 20 | // let mutable ma = MultiAwaiterData() 21 | // 22 | // let awaiter1 = ResumableCodeHelpers.getAwaiter task 23 | // if not (ResumableCodeHelpers.isCompleted awaiter1) then 24 | // ma.AddAwaiter(awaiter1) 25 | // 26 | // let awaiter2 = ResumableCodeHelpers.getAwaiter task2 27 | // if not (ResumableCodeHelpers.isCompleted awaiter2) then 28 | // ma.AddAwaiter(awaiter2) 29 | // 30 | // ResumableCodeHelpers.Bind(ma, fun () -> 31 | // let res1 = ResumableCodeHelpers.getResult awaiter1 32 | // let res2 = ResumableCodeHelpers.getResult awaiter2 33 | // continuation (res1, res2) 34 | // ).Invoke(&sm)) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Data/StateMachineData.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open System.Runtime.CompilerServices 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 5 | 6 | [] 7 | type StateMachineData<'TMethodBuilder, 'TTask, 'TResult 8 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult>> = 9 | 10 | [] 11 | val mutable MethodBuilder: 'TMethodBuilder 12 | 13 | [] 14 | val mutable Result: 'TResult 15 | 16 | interface IStateMachineData, 'TResult> with 17 | 18 | member this.CheckCanContinueOrThrow() = true 19 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitOnCompleted(&awaiter, &stateMachine) 20 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 21 | member this.Finish(sm) = this.MethodBuilder.SetResult(this.Result) 22 | member this.SetException(``exception``: exn) = this.MethodBuilder.SetException(``exception``) 23 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 24 | member this.SetResult(result) = this.Result <- result 25 | 26 | type [] DefaultStateMachineDataInitializer<'TMethodBuilder, 'TTask, 'TResult 27 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult> 28 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder>> = 29 | 30 | interface IStateMachineDataInitializer, unit, 'TTask> with 31 | static member Initialize(sm, data, _) = 32 | data.MethodBuilder <- 'TMethodBuilder.Create() 33 | data.MethodBuilder.Start(&sm) 34 | data.MethodBuilder.Task 35 | 36 | // TODO: Do I even need this? 37 | [] 38 | type StateMachineRefDataBase<'TMethodBuilder, 'TTask, 'TResult 39 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult>>() = 40 | 41 | [] 42 | val mutable MethodBuilder: 'TMethodBuilder 43 | 44 | [] 45 | val mutable Result: 'TResult 46 | 47 | abstract member MoveNext: unit -> unit 48 | 49 | interface IAsyncStateMachine with 50 | member this.MoveNext() = this.MoveNext() 51 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 52 | 53 | interface IStateMachineData, 'TResult> with 54 | 55 | member this.CheckCanContinueOrThrow() = true 56 | member this.AwaitOnCompleted(awaiter, arg) = 57 | let mutable this = this 58 | this.MethodBuilder.AwaitOnCompleted(&awaiter, &this) 59 | member this.AwaitUnsafeOnCompleted(awaiter, arg) = 60 | let mutable this = this 61 | this.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &this) 62 | member this.Finish(sm) = 63 | this.MethodBuilder.SetResult(this.Result) 64 | member this.SetException(``exception``: exn) = this.MethodBuilder.SetException(``exception``) 65 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 66 | member this.SetResult(result) = this.Result <- result -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Data/StateMachineDataWithState.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | 5 | [] 6 | type StateMachineDataWithState<'TMethodBuilder, 'TStateCheck, 'TTask, 'TState, 'TResult 7 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult> 8 | and 'TStateCheck :> IStateCheck<'TState>> = 9 | 10 | [] 11 | val mutable MethodBuilder: 'TMethodBuilder 12 | 13 | [] 14 | val mutable State: 'TState 15 | 16 | [] 17 | val mutable Result: 'TResult 18 | 19 | interface IStateMachineData, 'TResult> with 20 | 21 | member this.CheckCanContinueOrThrow() = 22 | if 'TStateCheck.CanCheckState then 23 | 'TStateCheck.CheckState(&this.State) 24 | else 25 | true 26 | 27 | member this.Finish(sm) = 28 | if 'TStateCheck.CanProcessSuccess then 29 | 'TStateCheck.ProcessSuccess(&this.State) 30 | this.MethodBuilder.SetResult(this.Result) 31 | 32 | member this.SetException(``exception``: exn) = 33 | if 'TStateCheck.CanProcessException then 34 | 'TStateCheck.ProcessException(&this.State, ``exception``) 35 | this.MethodBuilder.SetException(``exception``) 36 | 37 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitOnCompleted(&awaiter, &stateMachine) 38 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 39 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 40 | member this.SetResult(result) = this.Result <- result 41 | 42 | interface IStateMachineDataWithState, 'TState> with 43 | member this.State = this.State 44 | 45 | type [] DefaultStateMachineDataWithStateInitializer<'TMethodBuilder, 'TTask, 'TState, 'TStateCheck, 'TResult 46 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult> 47 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder> 48 | and 'TStateCheck :> IStateCheck<'TState>> = 49 | 50 | interface IStateMachineDataInitializer, 'TState, 'TTask> with 51 | static member Initialize(sm, data, state) = 52 | data.State <- state 53 | data.MethodBuilder <- 'TMethodBuilder.Create() 54 | data.MethodBuilder.Start(&sm) 55 | data.MethodBuilder.Task -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Data/UnitStateMachineData.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | 5 | [] 6 | type UnitStateMachineData<'TMethodBuilder, 'TTask 7 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask>> = 8 | 9 | [] 10 | val mutable MethodBuilder: 'TMethodBuilder 11 | 12 | interface IStateMachineData, unit> with 13 | member this.CheckCanContinueOrThrow() = true 14 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitOnCompleted(&awaiter, &stateMachine) 15 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 16 | member this.Finish(sm) = this.MethodBuilder.SetResult() 17 | member this.SetException(``exception``: exn) = this.MethodBuilder.SetException(``exception``) 18 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 19 | member this.SetResult(_) = () 20 | 21 | type [] DefaultUnitStateMachineDataInitializer<'TMethodBuilder, 'TTask 22 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask> 23 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder>> = 24 | interface IStateMachineDataInitializer, unit, 'TTask> with 25 | static member Initialize(sm, data, _) = 26 | data.MethodBuilder <- 'TMethodBuilder.Create() 27 | data.MethodBuilder.Start(&sm) 28 | data.MethodBuilder.Task -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Data/UnitStateMachineDataWithState.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | 5 | [] 6 | type UnitStateMachineDataWithState<'TMethodBuilder, 'TStateCheck, 'TTask, 'TState 7 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask> 8 | and 'TStateCheck :> IStateCheck<'TState>> = 9 | 10 | [] 11 | val mutable MethodBuilder: 'TMethodBuilder 12 | 13 | [] 14 | val mutable State: 'TState 15 | 16 | interface IStateMachineData, unit> with 17 | 18 | member this.CheckCanContinueOrThrow() = 19 | if 'TStateCheck.CanCheckState then 20 | 'TStateCheck.CheckState(&this.State) 21 | else 22 | true 23 | 24 | member this.Finish(sm) = 25 | if 'TStateCheck.CanProcessSuccess then 26 | 'TStateCheck.ProcessSuccess(&this.State) 27 | this.MethodBuilder.SetResult() 28 | 29 | member this.SetException(``exception``: exn) = 30 | if 'TStateCheck.CanProcessException then 31 | 'TStateCheck.ProcessException(&this.State, ``exception``) 32 | this.MethodBuilder.SetException(``exception``) 33 | 34 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitOnCompleted(&awaiter, &stateMachine) 35 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 36 | member this.SetStateMachine(stateMachine) = this.MethodBuilder.SetStateMachine(stateMachine) 37 | member this.SetResult(_) = () 38 | 39 | interface IStateMachineDataWithState, 'TState> with 40 | member this.State = this.State 41 | 42 | type [] DefaultUnitStateMachineDataWithStateInitializer<'TMethodBuilder, 'TTask, 'TState, 'TStateCheck 43 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask> 44 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder> 45 | and 'TStateCheck :> IStateCheck<'TState>> = 46 | 47 | interface IStateMachineDataInitializer, 'TState, 'TTask> with 48 | static member Initialize(sm, data, state) = 49 | data.State <- state 50 | data.MethodBuilder <- 'TMethodBuilder.Create() 51 | data.MethodBuilder.Start(&sm) 52 | data.MethodBuilder.Task -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/GenericTaskBuilderIntrinsics.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Threading.Tasks 6 | 7 | type StateIntrinsic = struct end 8 | 9 | // these indicate default extensions for "straightforward" task builders 10 | type IBindExtensions = interface end 11 | type IYieldExtensions = interface end 12 | type IReturnExtensions = interface end 13 | 14 | module StateMachineCodes = 15 | let [] Finished = -1 16 | 17 | [] 18 | type TaskBindWrapper<'a>(task: Task<'a>) = 19 | member this.Task = task 20 | member inline this.GetAwaiter(): TaskAwaiter<'a> = this.Task.GetAwaiter() 21 | 22 | [] 23 | type internal FakeAwaiter = 24 | 25 | [] 26 | val mutable Continuation: Action 27 | 28 | interface ICriticalNotifyCompletion with 29 | member this.OnCompleted(continuation) = this.Continuation <- continuation 30 | member this.UnsafeOnCompleted(continuation) = this.Continuation <- continuation -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/ActivityTasks.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ActivityTask 2 | 3 | open System 4 | open System.Diagnostics 5 | open System.Runtime.CompilerServices 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 7 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 8 | open Microsoft.FSharp.Core 9 | 10 | [] 11 | type ActivityStateCheck = 12 | interface IStateCheck with 13 | static member CanCheckState = false 14 | static member CheckState(_) = 15 | raise (InvalidOperationException("Should not be called")) 16 | 17 | static member CanProcessException = true 18 | [] 19 | static member ProcessException(state, ``exception``) = 20 | match state with 21 | | null -> () 22 | | activity -> 23 | activity.AddException(``exception``).SetStatus(ActivityStatusCode.Error, ``exception``.Message).Dispose() 24 | // TODO: OpenTelemetry integration or a specialized state check? 25 | // activity.RecordException? 26 | 27 | static member CanProcessSuccess = true 28 | [] 29 | static member ProcessSuccess(state) = 30 | match state with 31 | | null -> () 32 | | activity -> 33 | activity.SetStatus(ActivityStatusCode.Ok).Dispose() 34 | 35 | type ActivityTaskBuilder(state) = 36 | inherit GenericTaskBuilderWithStateReturnBase(state) 37 | member inline this.Run([] code) = 38 | this.RunInternal>, ActivityStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) 39 | 40 | type ActivityValueTaskBuilder(state) = 41 | inherit GenericTaskBuilderWithStateReturnBase(state) 42 | member inline this.Run([] code) = 43 | this.RunInternal>, ActivityStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/AsyncIteratorMethodBuilderWrapper.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 2 | 3 | open System.Runtime.CompilerServices 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 5 | 6 | type [] AsyncIteratorMethodBuilderWrapper = 7 | val mutable private builder: AsyncIteratorMethodBuilder 8 | new(builder) = { builder = builder } 9 | 10 | static member Create() = AsyncIteratorMethodBuilderWrapper(AsyncIteratorMethodBuilder.Create()) 11 | 12 | member this.AwaitOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 13 | this.builder.AwaitOnCompleted(&awaiter, &stateMachine) 14 | member this.AwaitUnsafeOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 15 | this.builder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 16 | member this.Complete() = this.builder.Complete() 17 | member this.MoveNext(stateMachine: byref<#IAsyncStateMachine>) = this.builder.MoveNext(&stateMachine) 18 | member this.SetStateMachine(_) = () 19 | 20 | interface IAsyncIteratorMethodBuilder with 21 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.AwaitOnCompleted(&awaiter, &stateMachine) 22 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 23 | member this.Complete() = this.Complete() 24 | member this.MoveNext(stateMachine) = this.MoveNext(&stateMachine) 25 | member this.SetStateMachine(_) = this.SetStateMachine() 26 | 27 | interface IAsyncMethodBuilderCreator with 28 | static member Create() = AsyncIteratorMethodBuilderWrapper.Create() -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/CancellableTasks.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.CancellableTask 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Threading 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 7 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 8 | open Microsoft.FSharp.Core 9 | 10 | [] 11 | type CancellableStateCheck = 12 | interface IStateCheck with 13 | static member CanCheckState = true 14 | [] 15 | static member CheckState(state) = 16 | state.ThrowIfCancellationRequested() 17 | true 18 | 19 | static member CanProcessException = false 20 | static member ProcessException(_, _) = 21 | raise (InvalidOperationException("Should not be called")) 22 | 23 | static member CanProcessSuccess = false 24 | static member ProcessSuccess(_) = 25 | raise (InvalidOperationException("Should not be called")) 26 | 27 | type CancellableTaskBuilder(state) = 28 | inherit GenericTaskBuilderWithStateReturnBase(state) 29 | member inline this.Run([] code) = 30 | this.RunInternal>, CancellableStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) 31 | 32 | type CancellableValueTaskBuilder(state) = 33 | inherit GenericTaskBuilderWithStateReturnBase(state) 34 | member inline this.Run([] code) = 35 | this.RunInternal>, CancellableStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/ExceptionTask.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ExceptionTask 2 | 3 | open System.Threading.Tasks 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ExceptionResultTask 6 | open Microsoft.FSharp.Core.CompilerServices 7 | 8 | type ExceptionTaskBuilderBase() = 9 | inherit GenericTaskBuilderReturnCore() 10 | interface IBindExtensions 11 | 12 | member inline this.Return<'TData, 'TResult, 'TExn 13 | when 'TData :> IStateMachineData<'TData, Result<'TResult, 'TExn>> 14 | and 'TExn :> exn>(value: 'TResult) = 15 | ResumableCode<'TData, Result<'TResult, exn>>(fun sm -> 16 | sm.Data.SetResult(Ok value) 17 | true 18 | ) 19 | 20 | type ExceptionTaskBuilder() = 21 | inherit ExceptionTaskBuilderBase() 22 | member inline this.Run([] code) = 23 | this.RunInternal, ExnResultAsyncTaskMethodBuilderBehavior<_>>,_,_>,_,_,DefaultStateMachineDataInitializer<_,_,_>>(code) 24 | 25 | type ExceptionValueTaskBuilder() = 26 | inherit ExceptionTaskBuilderBase() 27 | member inline this.Run([] code) = 28 | this.RunInternal, ExnResultAsyncTaskMethodBuilderBehavior<_>>,_,_>,_,_,DefaultStateMachineDataInitializer<_,_,_>>(code) 29 | 30 | module Low = 31 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.Low 32 | type ExceptionTaskBuilderBase with 33 | [] 34 | member inline this.ReturnFrom(task: ^TaskLike) = 35 | this.Bind(task, (fun v -> this.Return v)) 36 | 37 | module High = 38 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.High 39 | type ExceptionTaskBuilderBase with 40 | member inline this.ReturnFrom(task: Task<'TResult>) = 41 | this.Bind(task, (fun v -> this.Return v)) 42 | 43 | member inline this.ReturnFrom(task: Async<'TResult>) = 44 | this.Bind(task, (fun v -> this.Return v)) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/MethodBuilderBehaviors.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 2 | 3 | open System.Runtime.CompilerServices 4 | 5 | type IAsyncValueTaskMethodBuilderBehavior<'TResult> = 6 | static abstract SetException: builder: byref> * ``exception``: exn -> unit 7 | static abstract SetResult: builder: byref> * result: 'TResult -> unit 8 | 9 | type IAsyncValueTaskMethodBuilderBehavior = 10 | static abstract SetException: builder: byref * ``exception``: exn -> unit 11 | static abstract SetResult: builder: byref -> unit 12 | 13 | type IAsyncTaskMethodBuilderBehavior<'TResult> = 14 | static abstract SetException: builder: byref> * ``exception``: exn -> unit 15 | static abstract SetResult: builder: byref> * result: 'TResult -> unit 16 | 17 | type IAsyncTaskMethodBuilderBehavior = 18 | static abstract SetException: builder: byref * ``exception``: exn -> unit 19 | static abstract SetResult: builder: byref -> unit 20 | 21 | [] 22 | type DefaultAsyncTaskMethodBuilderBehavior<'TResult> = 23 | interface IAsyncValueTaskMethodBuilderBehavior<'TResult> with 24 | 25 | [] 26 | static member SetException(builder, ``exception``) = builder.SetException(``exception``) 27 | 28 | [] 29 | static member SetResult(builder, result) = builder.SetResult(result) 30 | 31 | interface IAsyncTaskMethodBuilderBehavior<'TResult> with 32 | 33 | [] 34 | static member SetException(builder, ``exception``) = builder.SetException(``exception``) 35 | 36 | [] 37 | static member SetResult(builder, result) = builder.SetResult(result) 38 | 39 | [] 40 | type DefaultAsyncTaskMethodBuilderBehavior = 41 | interface IAsyncValueTaskMethodBuilderBehavior with 42 | 43 | [] 44 | static member SetException(builder, ``exception``) = builder.SetException(``exception``) 45 | 46 | [] 47 | static member SetResult(builder) = builder.SetResult() 48 | 49 | interface IAsyncTaskMethodBuilderBehavior with 50 | 51 | [] 52 | static member SetException(builder, ``exception``) = builder.SetException(``exception``) 53 | 54 | [] 55 | static member SetResult(builder) = builder.SetResult() -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/SemaphoreSlimTask.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SemaphoreSlimTask 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Threading 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 7 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 8 | open Microsoft.FSharp.Core 9 | 10 | [] 11 | type SemaphoreSlimStateCheck = 12 | interface IStateCheck with 13 | static member CanCheckState = false 14 | static member CheckState(_) = 15 | raise (InvalidOperationException("Should not be called")) 16 | 17 | static member CanProcessException = true 18 | [] 19 | static member ProcessException(state, _) = 20 | state.Release() |> ignore 21 | 22 | static member CanProcessSuccess = true 23 | [] 24 | static member ProcessSuccess(state) = 25 | state.Release() |> ignore 26 | 27 | type [] SemaphoreSlimTaskStateMachineDataInitializer<'TMethodBuilder, 'TTask, 'TResult 28 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult> 29 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder>> = 30 | 31 | interface IStateMachineDataInitializer, SemaphoreSlim, 'TTask> with 32 | static member Initialize(sm: byref<'a>, data, state) = 33 | data.MethodBuilder <- 'TMethodBuilder.Create() 34 | 35 | let mutable awaiter = state.WaitAsync().GetAwaiter() 36 | if awaiter.IsCompleted then 37 | data.MethodBuilder.Start(&sm) 38 | else 39 | data.MethodBuilder.AwaitUnsafeOnCompleted(&awaiter, &sm) 40 | 41 | data.MethodBuilder.Task 42 | 43 | type SemaphoreSlimTaskBuilder(state) = 44 | inherit GenericTaskBuilderWithStateReturnBase(state) 45 | member inline this.Run([] code) = 46 | this.RunInternal>, SemaphoreSlimStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) 47 | 48 | type SemaphoreSlimValueTaskBuilder(state) = 49 | inherit GenericTaskBuilderWithStateReturnBase(state) 50 | member inline this.Run([] code) = 51 | this.RunInternal>, SemaphoreSlimStateCheck,_,_,_>,_,_, DefaultStateMachineDataWithStateInitializer<_,_,_,_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/SynchronizationContextTask.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.SynchronizationContextTask 2 | 3 | open System 4 | open System.Threading 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 7 | open Microsoft.FSharp.Core 8 | 9 | type [] SyncContextTaskStateMachineDataInitializer<'TMethodBuilder, 'TTask, 'TResult 10 | when 'TMethodBuilder :> IAsyncMethodBuilder<'TTask, 'TResult> 11 | and 'TMethodBuilder :> IAsyncMethodBuilderCreator<'TMethodBuilder>> = 12 | 13 | interface IStateMachineDataInitializer, SynchronizationContext, 'TTask> with 14 | static member Initialize(sm: byref<'a>, data, state) = 15 | data.MethodBuilder <- 'TMethodBuilder.Create() 16 | 17 | let currentSyncContext = SynchronizationContext.Current 18 | if Object.ReferenceEquals(state, currentSyncContext) then 19 | data.MethodBuilder.Start(&sm) 20 | else 21 | // force state machine box creation with traditional async method builders 22 | let mutable fakeAwaiter = FakeAwaiter() 23 | data.MethodBuilder.AwaitUnsafeOnCompleted(&fakeAwaiter, &sm) 24 | state.Post((fun continuation -> 25 | (continuation :?> Action).Invoke() 26 | ), fakeAwaiter.Continuation) 27 | 28 | data.MethodBuilder.Task 29 | 30 | type SynchronizationContextTaskBuilder(state) = 31 | inherit GenericTaskBuilderWithStateReturnBase(state) 32 | member inline this.Run([] code) = 33 | this.RunInternal>,_,_>,_,_,SyncContextTaskStateMachineDataInitializer<_,_,_>>(code) 34 | 35 | type SynchronizationContextValueTaskBuilder(state) = 36 | inherit GenericTaskBuilderWithStateReturnBase(state) 37 | member inline this.Run([] code) = 38 | this.RunInternal>,_,_>,_,_,SyncContextTaskStateMachineDataInitializer<_,_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/TaskMethodBuilderWrappers.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 2 | 3 | open System.Runtime.CompilerServices 4 | open System.Threading.Tasks 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 6 | 7 | type [] AsyncTaskMethodBuilderWrapper<'TResult, 'TBehavior 8 | when 'TBehavior :> IAsyncTaskMethodBuilderBehavior<'TResult>> = 9 | 10 | val mutable private builder: AsyncTaskMethodBuilder<'TResult> 11 | new(builder) = { builder = builder } 12 | 13 | static member Create() = AsyncTaskMethodBuilderWrapper(AsyncTaskMethodBuilder<'TResult>.Create()) 14 | member this.SetException(``exception``) = 'TBehavior.SetException(&this.builder, ``exception``) 15 | member this.SetResult(data) = 'TBehavior.SetResult(&this.builder, data) 16 | 17 | member this.AwaitOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 18 | this.builder.AwaitOnCompleted(&awaiter, &stateMachine) 19 | member this.AwaitUnsafeOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 20 | this.builder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 21 | member this.SetStateMachine(stateMachine) = this.builder.SetStateMachine(stateMachine) 22 | member this.Start(stateMachine: byref<#IAsyncStateMachine>) = this.builder.Start(&stateMachine) 23 | member this.Task = this.builder.Task 24 | 25 | interface IAsyncMethodBuilderCreator> with 26 | static member Create() = AsyncTaskMethodBuilderWrapper<'TResult, 'TBehavior>.Create() 27 | 28 | interface IAsyncMethodBuilder, 'TResult> with 29 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.AwaitOnCompleted(&awaiter, &stateMachine) 30 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 31 | member this.SetException(``exception``) = this.SetException(``exception``) 32 | member this.SetResult(data) = this.SetResult(data) 33 | member this.SetStateMachine(stateMachine) = this.SetStateMachine(stateMachine) 34 | member this.Start(stateMachine: byref<#IAsyncStateMachine>) = this.Start(&stateMachine) 35 | member this.Task = this.Task 36 | 37 | type [] AsyncTaskMethodBuilderWrapper<'TBehavior 38 | when 'TBehavior :> IAsyncTaskMethodBuilderBehavior> = 39 | 40 | val mutable private builder: AsyncTaskMethodBuilder 41 | new(builder) = { builder = builder } 42 | 43 | member this.SetException(``exception``) = 'TBehavior.SetException(&this.builder, ``exception``) 44 | member this.SetResult() = 'TBehavior.SetResult(&this.builder) 45 | 46 | static member Create() = AsyncTaskMethodBuilderWrapper<'TBehavior>(AsyncTaskMethodBuilder.Create()) 47 | member this.AwaitOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 48 | this.builder.AwaitOnCompleted(&awaiter, &stateMachine) 49 | member this.AwaitUnsafeOnCompleted(awaiter: byref<#INotifyCompletion>, stateMachine: byref<#IAsyncStateMachine>) = 50 | this.builder.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 51 | member this.SetStateMachine(stateMachine) = this.builder.SetStateMachine(stateMachine) 52 | member this.Start(stateMachine: byref<#IAsyncStateMachine>) = this.builder.Start(&stateMachine) 53 | member this.Task = this.builder.Task 54 | 55 | interface IAsyncMethodBuilderCreator> with 56 | static member Create() = AsyncTaskMethodBuilderWrapper<'TBehavior>.Create() 57 | 58 | interface IAsyncMethodBuilder with 59 | member this.AwaitOnCompleted(awaiter, stateMachine) = this.AwaitOnCompleted(&awaiter, &stateMachine) 60 | member this.AwaitUnsafeOnCompleted(awaiter, stateMachine) = this.AwaitUnsafeOnCompleted(&awaiter, &stateMachine) 61 | member this.SetException(``exception``) = this.SetException(``exception``) 62 | member this.SetResult() = this.SetResult() 63 | member this.SetStateMachine(stateMachine) = this.SetStateMachine(stateMachine) 64 | member this.Start(stateMachine) = this.Start(&stateMachine) 65 | member this.Task = this.Task -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/TaskSeq.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.AsyncEnumerable 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 5 | 6 | type TaskSeq() = 7 | inherit GenericTaskBuilderYieldBase() 8 | member inline this.Run([] code) = 9 | this.RunInternal, _, _, DefaultSeqStateMachineDataInitializer<_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericTaskBuilder/Tasks/Tasks.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.Native 2 | 3 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder 4 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks 5 | 6 | type TaskBuilder() = 7 | inherit GenericTaskBuilderBase() 8 | member inline this.Run([] code) = 9 | this.RunInternal>,_,_>,_,_,DefaultStateMachineDataInitializer<_,_,_>>(code) 10 | 11 | type UnitTaskBuilder() = 12 | inherit GenericTaskBuilderBase() 13 | member inline this.Run([] code) = 14 | this.RunInternal,_>,_,_,DefaultUnitStateMachineDataInitializer<_,_>>(code) 15 | 16 | type ValueTaskBuilder() = 17 | inherit GenericTaskBuilderBase() 18 | member inline this.Run([] code) = 19 | this.RunInternal>,_,_>,_,_, DefaultStateMachineDataInitializer<_,_,_>>(code) 20 | 21 | type UnitValueTaskBuilder() = 22 | inherit GenericTaskBuilderBase() 23 | member inline this.Run([] code) = 24 | this.RunInternal,_>,_,_,DefaultUnitStateMachineDataInitializer<_,_>>(code) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/GenericUnitBuilderBase.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions 2 | 3 | open System 4 | open System.Collections.Generic 5 | 6 | [] 7 | module CodeDefinition = 8 | type UnitBuilderCode<'Builder> = 'Builder -> unit 9 | 10 | type UnitBuilderBase<'Builder>() = 11 | 12 | member inline _.While([] moveNext: unit -> bool, [] whileExpr: UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 13 | fun builder -> while moveNext() do (whileExpr(builder)) 14 | 15 | member inline _.Combine([] first: UnitBuilderCode<'Builder>, [] second: UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 16 | fun(builder) -> 17 | first(builder) 18 | second(builder) 19 | 20 | member inline _.TryFinally([] tryExpr: UnitBuilderCode<'Builder>, [] compensation: UnitBuilderCode<'Builder>) = 21 | fun (builder) -> 22 | try 23 | tryExpr(builder) 24 | finally 25 | compensation(builder) 26 | 27 | member inline _.TryWith([] tryExpr, [] compensation: exn -> UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 28 | fun (builder) -> 29 | try 30 | tryExpr() 31 | with e -> 32 | (compensation e)(builder) 33 | 34 | member inline this.Using(resource: #IDisposable, [] tryExpr: #IDisposable -> UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 35 | this.TryFinally( 36 | (fun (builder) -> (tryExpr(resource)(builder))), 37 | (fun (builder) -> if not (isNull (box resource)) then resource.Dispose())) 38 | 39 | member inline this.For(values: 'a seq, [] forExpr: 'a -> UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 40 | this.Using( 41 | values.GetEnumerator(), (fun e -> 42 | this.While((fun () -> e.MoveNext()), (fun (builder) -> 43 | (forExpr e.Current)(builder)) 44 | ) 45 | ) 46 | ) 47 | 48 | member inline this.For<'T, 'TEnumerator when 'TEnumerator :> IEnumerator<'T>>(enumerator: 'TEnumerator, [] forExpr: 'T -> UnitBuilderCode<'Builder>) : UnitBuilderCode<'Builder> = 49 | fun builder -> 50 | let mutable enumerator = enumerator 51 | while enumerator.MoveNext() do 52 | (forExpr enumerator.Current)(builder) 53 | 54 | member inline _.Zero() : UnitBuilderCode<'Builder> = fun _ -> () 55 | 56 | member inline _.Delay([] delay: unit -> UnitBuilderCode<'Builder>) = 57 | fun (builder) -> (delay())(builder) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/HttpBuilder/Content.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.HttpBuilder.Content 2 | 3 | open System.IO 4 | open System.Net.Http 5 | open System.Net.Http.Headers 6 | open System.Net.Mime 7 | open System.Text.Json 8 | 9 | // TODO: try to experiment with Memory / Memory based HttpContent? 10 | 11 | let inline getJsonMediaType() = 12 | MediaTypeHeaderValue(MediaTypeNames.Application.Json, CharSet = "utf-8") 13 | 14 | let inline getStringMediaType() = 15 | MediaTypeHeaderValue(MediaTypeNames.Text.Plain, CharSet = "utf-8") 16 | 17 | let inline getByteArrayOrStreamMediaType() = 18 | MediaTypeHeaderValue(MediaTypeNames.Application.Octet) 19 | 20 | let inline makeJsonContent<'a>(value: 'a, options: JsonSerializerOptions) = 21 | let content = ByteArrayContent(JsonSerializer.SerializeToUtf8Bytes<'a>(value, options)) 22 | content.Headers.ContentType <- getJsonMediaType() 23 | content 24 | 25 | let inline makeStringContent(value: string) = 26 | // String content has default utf8 content type anyway 27 | let content = StringContent(value) 28 | content 29 | 30 | let inline makeUtf8StringContent(value: byte[]) = 31 | let content = ByteArrayContent(value) 32 | content.Headers.ContentType <- getStringMediaType() 33 | content 34 | 35 | let inline makeByteArrayContent(value: byte[]) = 36 | // Byte array content and stream content do not have default content type. 37 | // I'm not sure I have to put content type manually. Maybe octet stream is default ? 38 | let content = ByteArrayContent(value) 39 | content.Headers.ContentType <- getByteArrayOrStreamMediaType() 40 | content 41 | 42 | let inline makeStreamContent(value: Stream) = 43 | let content = StreamContent(value) 44 | content.Headers.ContentType <- getByteArrayOrStreamMediaType() 45 | content 46 | 47 | let inline makeFormUrlEncodedContent values = 48 | let content = FormUrlEncodedContent(values) 49 | content -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/HttpBuilder/HeadersBuilders.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.HttpBuilder 2 | 3 | open System.Collections.Generic 4 | open System.ComponentModel 5 | open System.Net.Http.Headers 6 | open System.Runtime.CompilerServices 7 | 8 | type [] 9 | GetHeadersIntrinsic = struct end 10 | 11 | [] 12 | type ContentHeadersBuilderCode = delegate of HttpContentHeaders -> unit 13 | 14 | [] 15 | type ContentHeadersBuilder = 16 | 17 | member inline _.Bind(_: GetHeadersIntrinsic, [] builderCode: HttpContentHeaders -> ContentHeadersBuilderCode) : ContentHeadersBuilderCode = 18 | ContentHeadersBuilderCode(fun builder -> 19 | builderCode(builder).Invoke(builder)) 20 | 21 | member inline _.Combine([] first: ContentHeadersBuilderCode, [] second: ContentHeadersBuilderCode) : ContentHeadersBuilderCode = 22 | ContentHeadersBuilderCode(fun builder -> 23 | first.Invoke(builder) 24 | second.Invoke(builder) 25 | ) 26 | 27 | member inline _.Delay([] delay: unit -> ContentHeadersBuilderCode) = 28 | fun builder -> (delay()).Invoke(builder) 29 | 30 | member inline _.Yield(kvp: KeyValuePair) : ContentHeadersBuilderCode = 31 | ContentHeadersBuilderCode(fun headers -> 32 | headers.Add(kvp.Key, kvp.Value) 33 | ) 34 | 35 | member inline _.Yield(kvp: KeyValuePair>) : ContentHeadersBuilderCode = 36 | ContentHeadersBuilderCode(fun headers -> 37 | headers.Add(kvp.Key, kvp.Value) 38 | ) 39 | 40 | member inline this.Zero() : ContentHeadersBuilderCode = ContentHeadersBuilderCode(fun _ -> ()) 41 | 42 | member inline this.Run([] code: ContentHeadersBuilderCode) = 43 | ContentHeadersBuilderCode(fun builder -> code.Invoke(builder)) 44 | 45 | [] 46 | type RequestHeadersBuilderCode = delegate of HttpRequestHeaders -> unit 47 | 48 | [] 49 | type RequestHeadersBuilder = 50 | 51 | member inline _.Bind(_: GetHeadersIntrinsic, [] builderCode: HttpRequestHeaders -> RequestHeadersBuilderCode) : RequestHeadersBuilderCode = 52 | RequestHeadersBuilderCode(fun builder -> 53 | builderCode(builder).Invoke(builder)) 54 | 55 | member inline _.Combine([] first: RequestHeadersBuilderCode, [] second: RequestHeadersBuilderCode) : RequestHeadersBuilderCode = 56 | RequestHeadersBuilderCode(fun builder -> 57 | first.Invoke(builder) 58 | second.Invoke(builder) 59 | ) 60 | 61 | member inline _.Delay([] delay: unit -> RequestHeadersBuilderCode) = 62 | fun builder -> (delay()).Invoke(builder) 63 | 64 | member inline _.Yield(kvp: KeyValuePair) : RequestHeadersBuilderCode = 65 | RequestHeadersBuilderCode(fun headers -> 66 | headers.Add(kvp.Key, kvp.Value) 67 | ) 68 | 69 | member inline _.Yield(kvp: KeyValuePair>) : RequestHeadersBuilderCode = 70 | RequestHeadersBuilderCode(fun headers -> 71 | headers.Add(kvp.Key, kvp.Value) 72 | ) 73 | 74 | member inline _.Yield(authentication: AuthenticationHeaderValue) : RequestHeadersBuilderCode = 75 | RequestHeadersBuilderCode(fun headers -> 76 | headers.Authorization <- authentication 77 | ) 78 | 79 | member inline this.Zero() : RequestHeadersBuilderCode = RequestHeadersBuilderCode(fun _ -> ()) 80 | 81 | member inline this.Run([] code: RequestHeadersBuilderCode) = 82 | RequestHeadersBuilderCode(fun builder -> code.Invoke(builder)) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/HttpBuilder/Stages.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions.HttpBuilder 2 | 3 | open System.ComponentModel 4 | open System.Net.Http 5 | open System.Runtime.CompilerServices 6 | 7 | [] 8 | type HttpRequestMessageStage(client: HttpClient, request: HttpRequestMessage) = 9 | member _.Client = client 10 | member _.Request = request 11 | 12 | [] 13 | type HttpRequestMessageContentStage(client: HttpClient, request: HttpRequestMessage) = 14 | member _.Client = client 15 | member _.Request = request 16 | 17 | [] 18 | type HttpRequestMessageResponseStageCode = delegate of HttpRequestMessage -> unit 19 | 20 | [] 21 | type GetRequestIntrinsic = struct end 22 | type [] HttpRequestMessageResponseStage<'TResponseSerializer, 'TResult 23 | when 'TResponseSerializer :> IResponseSerializer<'TResult>> 24 | (client: HttpClient, request: HttpRequestMessage, serializer: 'TResponseSerializer) = 25 | member _.Client = client 26 | member _.Request = request 27 | member _.Serializer = serializer 28 | 29 | [] 30 | member this.Send() = 31 | let request = this.Request 32 | let client = this.Client 33 | let serializer = this.Serializer 34 | task { 35 | use request = request 36 | let! response = client.SendAsync(request) 37 | return! serializer.Serialize(response) 38 | } 39 | 40 | [] 41 | member this.GetAwaiter() = this.Send().GetAwaiter() 42 | 43 | member inline this.Bind(_: GetRequestIntrinsic, [] code: HttpRequestMessage -> HttpRequestMessageResponseStageCode) : HttpRequestMessageResponseStageCode = 44 | HttpRequestMessageResponseStageCode(fun request -> (code request).Invoke(request)) 45 | 46 | member inline this.Yield([] code: RequestHeadersBuilderCode) : HttpRequestMessageResponseStageCode = 47 | HttpRequestMessageResponseStageCode(fun request -> code.Invoke(request.Headers)) 48 | 49 | member inline this.Yield([] code: ContentHeadersBuilderCode) : HttpRequestMessageResponseStageCode = 50 | HttpRequestMessageResponseStageCode(fun request -> code.Invoke(request.Content.Headers)) 51 | 52 | member inline _.Combine([] first: HttpRequestMessageResponseStageCode, [] second: HttpRequestMessageResponseStageCode) : HttpRequestMessageResponseStageCode = 53 | HttpRequestMessageResponseStageCode(fun builder -> 54 | first.Invoke(builder) 55 | second.Invoke(builder) 56 | ) 57 | 58 | member inline _.Delay([] delay: unit -> HttpRequestMessageResponseStageCode) = 59 | fun builder -> (delay()).Invoke(builder) 60 | 61 | member inline this.Zero() : HttpRequestMessageResponseStageCode = HttpRequestMessageResponseStageCode(fun _ -> ()) 62 | 63 | member inline this.Run([] code: HttpRequestMessageResponseStageCode) = 64 | code.Invoke(this.Request) 65 | this -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/OptionBuilder.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.OptionBuilder 2 | 3 | open System 4 | 5 | type OptionCode<'T> = unit -> 'T voption 6 | 7 | type OptionBuilderBase() = 8 | 9 | member inline _.Zero() : OptionCode = 10 | fun() -> ValueSome () 11 | 12 | member inline _.Delay([] f: unit -> OptionCode<'T>) : OptionCode<'T> = 13 | fun () -> (f())() 14 | 15 | member inline _.Combine([] task1: OptionCode, [] task2: OptionCode<'T>) : OptionCode<'T> = 16 | fun () -> 17 | match task1() with 18 | | ValueNone -> ValueNone 19 | | ValueSome() -> task2() 20 | 21 | member inline _.Bind(res1: 'T1 option, [] task2: 'T1 -> OptionCode<'T>) : OptionCode<'T> = 22 | fun () -> 23 | match res1 with 24 | | None -> ValueNone 25 | | Some v -> (task2 v)() 26 | 27 | member inline _.Bind(res1: 'T1 voption, [] task2: 'T1 -> OptionCode<'T>) : OptionCode<'T> = 28 | fun () -> 29 | match res1 with 30 | | ValueNone -> ValueNone 31 | | ValueSome v -> (task2 v)() 32 | 33 | member inline _.Bind(res1: 'T1 Nullable, [] task2: 'T1 -> OptionCode<'T>) : OptionCode<'T> = 34 | fun () -> 35 | if res1.HasValue then 36 | (task2 res1.Value)() 37 | else 38 | ValueNone 39 | 40 | member inline _.TryWith([] body: OptionCode<'T>, [] catch: exn -> OptionCode<'T>) : OptionCode<'T> = 41 | fun () -> 42 | try 43 | body() 44 | with exn -> 45 | (catch exn)() 46 | 47 | member inline _.TryFinally([] body: OptionCode<'T>, [] compensation: unit -> unit) : OptionCode<'T> = 48 | fun () -> 49 | try 50 | body() 51 | finally 52 | compensation() 53 | 54 | member inline this.Using(disp: #IDisposable, [] body: #IDisposable -> OptionCode<'T>) : OptionCode<'T> = 55 | this.TryFinally( 56 | (fun () -> (body disp)()), 57 | (fun () -> if not (isNull (box disp)) then disp.Dispose())) 58 | 59 | member inline _.Return(value: 'T) : OptionCode<'T> = 60 | fun () -> 61 | ValueSome value 62 | 63 | member inline this.ReturnFrom(source: option<'T>) : OptionCode<'T> = 64 | fun () -> 65 | match source with Some x -> ValueSome x | None -> ValueNone 66 | 67 | member inline this.ReturnFrom(source: voption<'T>) : OptionCode<'T> = 68 | fun () -> 69 | source 70 | 71 | member inline this.ReturnFrom(source: Nullable<'T>) : OptionCode<'T> = 72 | fun () -> 73 | if source.HasValue then ValueSome source.Value else ValueNone 74 | 75 | type OptionBuilder() = 76 | inherit OptionBuilderBase() 77 | 78 | member inline _.Run([] code: OptionCode<'T>) : 'T option = 79 | match code() with 80 | | ValueNone -> None 81 | | ValueSome v -> Some v 82 | 83 | type ValueOptionBuilder() = 84 | inherit OptionBuilderBase() 85 | 86 | member inline _.Run([] code: OptionCode<'T>) : 'T voption = 87 | code() 88 | 89 | let option = OptionBuilder() 90 | let voption = ValueOptionBuilder() -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/TryExprBuilder.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.ComputationExpressions 2 | 3 | open System.Runtime.CompilerServices 4 | 5 | module TryExprBuilderImpl = 6 | 7 | type TryExprBuilderExpression<'a> = unit -> 'a 8 | 9 | type TryExprBuilderBase() = 10 | member inline _.Delay([] delay: unit -> TryExprBuilderExpression<'a>) = fun () -> (delay())() 11 | member inline _.Yield(value: 'a) : TryExprBuilderExpression<'a> = fun () -> value 12 | member inline _.Zero() : TryExprBuilderExpression = fun () -> () 13 | 14 | [] 15 | type TryExprBuilder() = 16 | inherit TryExprBuilderBase() 17 | member inline _.Run(expr: TryExprBuilderExpression<'a>) = 18 | try 19 | expr() 20 | with _ -> 21 | Unchecked.defaultof<'a> 22 | 23 | [] 24 | type TryExprBuilderToException() = 25 | inherit TryExprBuilderBase() 26 | member inline _.Run(expr: TryExprBuilderExpression) = 27 | try 28 | expr() 29 | null 30 | with e -> 31 | e 32 | 33 | [] 34 | type TryExprBuilderToResult() = 35 | inherit TryExprBuilderBase() 36 | member inline _.Run(expr: TryExprBuilderExpression<'a>) = 37 | try 38 | Ok(expr()) 39 | with e -> 40 | Error(e) 41 | 42 | [] 43 | type TryWithExprBuilder<'a>(value: 'a) = 44 | member _.Value = value 45 | member inline _.Delay([] delay: unit -> TryExprBuilderExpression<'a>) = fun () -> (delay())() 46 | member inline _.Yield(value: 'a) : TryExprBuilderExpression<'a> = fun () -> value 47 | member inline _.Zero() : TryExprBuilderExpression = fun () -> () 48 | member inline this.Run(expr: TryExprBuilderExpression<'a>) = 49 | try 50 | expr() 51 | with _ -> 52 | this.Value 53 | 54 | [] 55 | module TryExprBuilder = 56 | let try' = TryExprBuilderImpl.TryExprBuilder() 57 | let tryE = TryExprBuilderImpl.TryExprBuilderToException() 58 | let tryR = TryExprBuilderImpl.TryExprBuilderToResult() 59 | let tryWith value = TryExprBuilderImpl.TryWithExprBuilder(value) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.ComputationExpressions/ValueTaskExtensions.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions 2 | 3 | open En3Tho.FSharp.ComputationExpressions.Tasks 4 | open System.Threading.Tasks 5 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.Low 6 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Extensions.High 7 | 8 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ExceptionResultTask.TaskLikeLow 9 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ExceptionResultTask.Low 10 | open En3Tho.FSharp.ComputationExpressions.GenericTaskBuilder.Tasks.ExceptionResultTask.High 11 | 12 | module ValueTask = 13 | let inline map ([] mapper) (job: ValueTask<'a>) = 14 | if job.IsCompleted then 15 | ValueTask<_>(result = mapper job.Result) 16 | else vtask { 17 | let! result = job 18 | return mapper result 19 | } 20 | 21 | let inline bind ([] mapper) (job: ValueTask<'a>) = 22 | if job.IsCompleted then 23 | mapper job.Result 24 | else vtask { 25 | let! result = job 26 | return! mapper result 27 | } 28 | 29 | [] 30 | module TaskExtensions = 31 | type Task with 32 | member inline this.AsResult() = 33 | if this.IsCompletedSuccessfully then ValueTask<_>(result = Ok()) 34 | else 35 | exnResultValueTask { 36 | do! this 37 | return () 38 | } 39 | 40 | type Task<'a> with 41 | member inline this.AsResult() = 42 | if this.IsCompletedSuccessfully then ValueTask<_>(result = Ok this.Result) 43 | else 44 | exnResultValueTask { 45 | let! (v: 'a) = this 46 | return v 47 | } 48 | 49 | type ValueTask with 50 | member inline this.AsResult() = 51 | if 52 | this.IsCompletedSuccessfully then ValueTask<_>(result = Ok()) 53 | else 54 | exnResultValueTask { 55 | do! this 56 | return () 57 | } 58 | type ValueTask<'a> with 59 | member inline this.AsResult() = 60 | if 61 | this.IsCompletedSuccessfully then ValueTask<_>(result = Ok this.Result) 62 | else 63 | exnResultValueTask { 64 | return! this 65 | } -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions.Logging/En3Tho.FSharp.Extensions.Logging.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 8.0.1 5 | F# inline extensions for ILogger 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/Disposables.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions.Disposables 2 | 3 | open System 4 | open System.Threading.Tasks 5 | 6 | [] 7 | type ValueDisposable<'a>(value: 'a, dispose: 'a -> unit) = 8 | interface IDisposable with 9 | member this.Dispose() = dispose value 10 | 11 | [] 12 | type ValueDisposable2<'a, 'b>(value: 'a, value2: 'b, dispose: 'a -> 'b -> unit) = 13 | interface IDisposable with 14 | member this.Dispose() = dispose value value2 15 | 16 | [] 17 | type ValueDisposable3<'a, 'b, 'c>(value: 'a, value2: 'b, value3: 'c, dispose: 'a -> 'b -> 'c -> unit) = 18 | interface IDisposable with 19 | member this.Dispose() = dispose value value2 value3 20 | 21 | [] 22 | type UnitDisposable(dispose: unit -> unit) = 23 | interface IDisposable with 24 | member this.Dispose() = dispose() 25 | 26 | [] 27 | type UnitAsyncDisposable(dispose: unit -> ValueTask) = 28 | interface IAsyncDisposable with 29 | member this.DisposeAsync() = dispose() 30 | 31 | [] 32 | type ValueAsyncDisposable<'a>(value: 'a, dispose: 'a -> ValueTask) = 33 | interface IAsyncDisposable with 34 | member this.DisposeAsync() = dispose value 35 | 36 | [] 37 | type ValueAsyncDisposable2<'a, 'b>(value: 'a, value2: 'b, dispose: 'a -> 'b -> ValueTask) = 38 | interface IAsyncDisposable with 39 | member this.DisposeAsync() = dispose value value2 40 | 41 | [] 42 | type ValueAsyncDisposable3<'a, 'b, 'c>(value: 'a, value2: 'b, value3: 'c, dispose: 'a -> 'b -> 'c -> ValueTask) = 43 | interface IAsyncDisposable with 44 | member this.DisposeAsync() = dispose value value2 value3 -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/En3Tho.FSharp.Extensions.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9.0.0 5 | A set of extensions for F# base library 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/Experimental.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions.Experimental 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Linq 6 | open System.Runtime.InteropServices 7 | open En3Tho.FSharp.Extensions 8 | // some of the stuff will live here for a moment and then go to modules 9 | 10 | // module BitExtraction = ... 11 | 12 | // TODO: All operators based on new abstract statics? 13 | // + / - etc -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/Extensions.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module En3Tho.FSharp.Extensions.Extensions 3 | 4 | open System 5 | open System.Threading.Tasks 6 | 7 | let private unitTask = Task.FromResult() 8 | 9 | type Task with 10 | static member CompletedUnitTask = unitTask 11 | 12 | type ValueTask with 13 | static member inline FromTask(value) = ValueTask<_>(task = value) 14 | static member inline FromTask(value) = ValueTask(task = value) 15 | 16 | static member CompletedUnitTask = ValueTask() 17 | 18 | type Async<'a> with 19 | static member inline AwaitValueTask(valueTask: ValueTask) = 20 | if valueTask.IsCompletedSuccessfully then async.Zero() 21 | else valueTask.AsTask() |> Async.AwaitTask 22 | 23 | static member inline AwaitValueTask(valueTask: ValueTask<_>) = 24 | if valueTask.IsCompletedSuccessfully then valueTask.Result |> async.Return 25 | else valueTask.AsTask() |> Async.AwaitTask 26 | 27 | member this.AsResult() = 28 | async { 29 | try 30 | let! result = this 31 | return Ok result 32 | with e -> 33 | return Error e 34 | } 35 | 36 | type Exception with 37 | member this.IsOrContains<'a when 'a :> exn>() = 38 | match this with 39 | | :? 'a as result -> ValueSome result 40 | | :? AggregateException as exn -> 41 | let mutable result = ValueNone 42 | let enum = exn.InnerExceptions.GetEnumerator() 43 | 44 | while result.IsNone && enum.MoveNext() do 45 | result <- enum.Current.IsOrContains<'a>() 46 | 47 | match result with 48 | | ValueSome _ -> 49 | result 50 | | _ -> 51 | exn.InnerException.IsOrContains<'a>() 52 | | _ -> 53 | match this.InnerException with 54 | | null -> 55 | ValueNone 56 | | exn -> 57 | exn.IsOrContains<'a>() 58 | 59 | // Move this to functions when byrefs are available as generics 60 | type Span<'a> with 61 | member this.Advance(value) = 62 | if uint value >= uint this.Length then 63 | Span<'a>() 64 | else 65 | this.Slice(value, this.Length - value) 66 | 67 | type ReadOnlySpan<'a> with 68 | member this.Advance(value) = 69 | if uint value >= uint this.Length then 70 | ReadOnlySpan<'a>() 71 | else 72 | this.Slice(value, this.Length - value) 73 | 74 | // check if this is inlined, should be? 75 | member internal this.Self() = this 76 | 77 | member this.AsSpanUnsafe() = 78 | (# "" (this.Self()): Span<'a> #) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/InterfaceShortcuts.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions.InterfaceShortcuts 2 | 3 | open System 4 | 5 | module IEquatable = 6 | let inline equals<'a when 'a :> IEquatable<'a>> (left: 'a) (right: 'a) = left.Equals(right) 7 | 8 | module IComparable = 9 | let inline compareTo<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) 10 | let inline gt<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) > 0 11 | let inline gte<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) >= 0 12 | let inline lt<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) < 0 13 | let inline lte<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) <= 0 14 | let inline eq<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = left.CompareTo(right) = 0 15 | let inline min<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = if left.CompareTo(right) < 0 then left else right 16 | let inline max<'a when 'a :> IComparable<'a>> (left: 'a) (right: 'a) = if left.CompareTo(right) > 0 then left else right -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/Pointers.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module En3Tho.FSharp.Extensions.Pointers 3 | 4 | open System 5 | open Microsoft.FSharp.NativeInterop 6 | 7 | type voidptr with 8 | member inline this.iptr = ecast<_, iptr> this 9 | member inline this.uptr = ecast<_, uptr> this 10 | member inline this.As<'T when 'T: unmanaged>() = ucast<_, nativeptr<'T>> this 11 | member inline this.AsSpan<'T>(length) = Span<'T>(this, length) 12 | member inline this.AsReadOnlySpan<'T>(length) = ReadOnlySpan<'T>(this, length) 13 | 14 | type nativeptr<'T when 'T: unmanaged> with 15 | member inline this.voidptr = NativePtr.toVoidPtr this 16 | member inline this.iptr = NativePtr.toNativeInt this 17 | member inline this.uptr = this.iptr.uptr 18 | member inline this.byref = NativePtr.toByRef this 19 | member inline this.value with get() = NativePtr.read this and set value = NativePtr.write this value 20 | member inline this.As<'U when 'U: unmanaged>() = ucast<_, nativeptr<'U>> this 21 | member inline this.AsPPtr<'U when 'U: unmanaged>() = ucast<_, nativeptr>> this 22 | member inline this.AsSpan(length) = Span<'T>(this.voidptr, length) 23 | member inline this.AsReadOnlySpan(length) = ReadOnlySpan<'T>(this.voidptr, length) -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Extensions/Span.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.FSharp.Extensions 2 | 3 | open System 4 | open System.Runtime.CompilerServices 5 | open System.Runtime.InteropServices 6 | open Microsoft.FSharp.NativeInterop 7 | 8 | // TODO: add functions from String module 9 | // TODO: active patterns for Span and ReadOnlySpan 10 | 11 | module Span = 12 | let inline isEmpty (span: Span<_>) = span.Length = 0 13 | let inline isNotEmpty (span: Span<_>) = span.Length <> 0 14 | let inline slice start count (span: Span<_>) = span.Slice(start, count) 15 | let inline sliceFrom start (span: Span<_>) = span.Slice(start) 16 | let inline sliceTo index (span: Span<_>) = span.Slice(0, index) 17 | let inline fromPtr count (ptr: nativeptr<'a>) = Span<'a>(ptr |> NativePtr.toVoidPtr, count) 18 | let inline fromVoidPtr<'a when 'a: unmanaged> count ptr = Span<'a>(ptr, count) 19 | let inline fromArray (array: 'a[]): Span<_> = Span.op_Implicit(array) 20 | let inline fromArraySegment (array: 'a ArraySegment): Span<_> = Span.op_Implicit(array) 21 | let inline fromMemory (memory: Memory<_>) = memory.Span 22 | 23 | let inline (|Empty|_|) (str: Span<'a>) = 24 | str.IsEmpty 25 | 26 | let inline (|NotEmpty|_|) (str: Span<'a>) = 27 | str.IsEmpty |> not 28 | 29 | let inline stackalloc<'a when 'a: unmanaged> len = 30 | let ptr = NativePtr.stackalloc<'a> len 31 | Span<'a>(NativePtr.toVoidPtr ptr, len) 32 | 33 | let inline alloc<'a> len = 34 | Span<'a>(Array.zeroCreate<'a> len) 35 | 36 | let inline malloc<'a when 'a: unmanaged> len = 37 | let ptr = NativeMemory.Alloc(ecast<_, unativeint> (Unsafe.SizeOf<'a>()) * ecast<_, unativeint> len) 38 | Span<'a>(ptr, len) 39 | 40 | let inline mfree<'a when 'a: unmanaged> (span: Span<'a>) = 41 | NativeMemory.Free(Unsafe.AsPointer(&span.GetPinnableReference())) 42 | 43 | module ReadOnlySpan = 44 | let inline isEmpty (span: ReadOnlySpan<_>) = span.Length = 0 45 | let inline isNotEmpty (span: ReadOnlySpan<_>) = span.Length <> 0 46 | let inline slice start count (span: ReadOnlySpan<_>) = span.Slice(start, count) 47 | let inline sliceFrom start (span: ReadOnlySpan<_>) = span.Slice(start) 48 | let inline sliceTo index (span: ReadOnlySpan<_>) = span.Slice(0, index) 49 | let inline fromPtr count (ptr: nativeptr<'a>) = ReadOnlySpan<'a>(ptr |> NativePtr.toVoidPtr, count) 50 | let inline fromVoidPtr<'a when 'a: unmanaged> count ptr = ReadOnlySpan<'a>(ptr, count) 51 | let inline fromString (str: string): ReadOnlySpan = str.AsSpan() 52 | let inline fromSpan (span: Span<_>): ReadOnlySpan<_> = Span.op_Implicit(span) 53 | let inline fromArray (array: 'a[]): ReadOnlySpan<_> = ReadOnlySpan.op_Implicit(array) 54 | let inline fromArraySegment (array: 'a ArraySegment): ReadOnlySpan<_> = ReadOnlySpan.op_Implicit(array) 55 | let inline fromMemory (memory: ReadOnlyMemory<_>) = memory.Span 56 | 57 | let inline (|Empty|_|) (str: ReadOnlySpan<'a>) = 58 | str.IsEmpty 59 | 60 | let inline (|NotEmpty|_|) (str: ReadOnlySpan<'a>) = 61 | str.IsEmpty |> not -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Validation/En3Tho.FSharp.Validation.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9.0.0 5 | Small library for constructing validated types 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/En3Tho.FSharp.Validation/Extensions.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module En3Tho.FSharp.Validation.Extensions 3 | 4 | type NewCtorValidatorValidated<'value, 'validator when 'validator: (new: unit -> 'validator) and 'validator :> IValidator<'value>> with 5 | static member Try(value: 'value) = 6 | match NewCtorValidatorValidated<'value, 'validator>.Of(value) with 7 | | Ok result -> ValueSome result 8 | | _ -> ValueNone 9 | 10 | static member TryO(value: 'value) = 11 | match NewCtorValidatorValidated<'value, 'validator>.Of(value) with 12 | | Ok result -> Some result 13 | | _ -> None 14 | 15 | member inline this.MapTry([] map: 'value -> 'value) = 16 | match this.Map(map) with 17 | | Ok result -> ValueSome result 18 | | _ -> ValueNone 19 | 20 | member inline this.MapTryO([] map: 'value -> 'value) = 21 | match this.Map(map) with 22 | | Ok result -> Some result 23 | | _ -> None 24 | 25 | type InstanceValidatorValidated<'value, 'validator when 'validator :> IValidator<'value>> with 26 | static member Try(value: 'value, validator) = 27 | match InstanceValidatorValidated<'value, 'validator>.Of(value, validator) with 28 | | Ok result -> ValueSome result 29 | | _ -> ValueNone 30 | 31 | static member TryO(value: 'value, validator) = 32 | match InstanceValidatorValidated<'value, 'validator>.Of(value, validator) with 33 | | Ok result -> Some result 34 | | _ -> None 35 | 36 | member inline this.MapTry([] map: 'value -> 'value) = 37 | match this.Map(map) with 38 | | Ok result -> ValueSome result 39 | | _ -> ValueNone 40 | 41 | member inline this.MapTryO([] map: 'value -> 'value) = 42 | match this.Map(map) with 43 | | Ok result -> Some result 44 | | _ -> None -------------------------------------------------------------------------------- /src/En3Tho.FSharp.XUnitExtensions/En3Tho.FSharp.XUnitExtensions.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 8.0.1 5 | A set of extensions for Xunit Assert 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/En3Tho.FSharp.XUnitExtensions/Extensions.fs: -------------------------------------------------------------------------------- 1 | [] 2 | module En3Tho.FSharp.Xunit.Extensions 3 | 4 | open Xunit 5 | 6 | type Assert with 7 | static member IsValueSome<'a>(value: 'a voption) = 8 | if value.IsNone then Assert.Fail("Expected ValueSome, got ValueNone") 9 | static member IsValueSome<'a>(expected: 'a, value: 'a voption) = 10 | match value with ValueSome result -> Assert.Equal(expected, result) | _ -> () 11 | static member IsValueNone<'a>(value: 'a voption) = 12 | if value.IsSome then Assert.Fail($"Expected ValueNone, got ValueSome: {value.Value}") 13 | 14 | static member IsSome<'a>(value: 'a option) = 15 | if value.IsNone then Assert.Fail("Expected Some, got ValueNone") 16 | static member IsSome<'a>(expected: 'a, value: 'a option) = 17 | match value with Some result -> Assert.Equal(expected, result) | _ -> () 18 | static member IsNone<'a>(value: 'a option) = 19 | if value.IsSome then Assert.Fail($"Expected None, got Some: {value.Value}") 20 | 21 | static member IsOk<'a, 'b>(value: Result<'a, 'b>) = 22 | match value with Error result -> Assert.Fail($"Expected Ok, got Error: {result}") | _ -> () 23 | static member IsOk<'a, 'b>(expected: 'a, value: Result<'a, 'b>) = 24 | match value with | Ok result -> Assert.Equal(result, expected) | Error result -> Assert.Fail($"Expected Ok, got Error: {result}") 25 | 26 | static member IsError<'a, 'b>(value: Result<'a, 'b>) = 27 | match value with | Ok result -> Assert.Fail($"Expected Error, got Ok: {result}") | _ -> () 28 | static member IsError<'a, 'b>(expected: 'b, value: Result<'a, 'b>) = 29 | match value with | Error result -> Assert.Equal(result, expected) | Ok result -> Assert.Fail($"Expected Error, got Ok: {result}") 30 | 31 | static member IsErrorWithMessage<'a>(expected: string, value: Result<'a, exn>) = 32 | match value with 33 | | Error result -> Assert.Equal(expected, result.Message) 34 | | Ok result -> Assert.Fail($"Expected Error, got Ok: {result}") 35 | 36 | static member IsErrorOfType<'a, 'b when 'a :> exn>(value: Result<'b, exn>) = 37 | match value with 38 | | Error (:? 'a as _) -> () 39 | | Error result -> Assert.Fail($"Expected Error of {typeof<'b>.Name} but got Error: {result}") 40 | | Ok result -> Assert.Fail($"Expected Error, got Ok: {result}") -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/AddFuncTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.Extensions.DependencyInjection.Tests.AddFuncTests 2 | 3 | open En3Tho.Extensions.DependencyInjection.Tests 4 | open Microsoft.Extensions.DependencyInjection 5 | open En3Tho.Extensions.DependencyInjection 6 | open Xunit 7 | 8 | type Dependency1 = { 9 | Value: string 10 | } 11 | 12 | type Dependency2 = { 13 | Value: Dependency1 14 | } 15 | 16 | type Dependency3 = { 17 | Value1: string 18 | Value2: string 19 | } 20 | 21 | let getDep2 (dep1: Dependency1) : Dependency2 = { 22 | Value = { dep1 with Value = "Dep2"} 23 | } 24 | 25 | let getDep3 (dep1: Dependency1) (dep2: Dependency2) : Dependency3 = { 26 | Value1 = dep1.Value 27 | Value2 = dep2.Value.Value 28 | } 29 | 30 | [] 31 | let ``Test that registered func work properly`` () = 32 | let serviceProvider = 33 | ServiceCollection() 34 | .AddSingleton(implementationInstance = { Value = "Dep1"}) 35 | .AddSingletonFunc(getDep2) 36 | .AddSingletonFunc(getDep3) 37 | .BuildServiceProvider() 38 | 39 | let dep3 = serviceProvider.GetRequiredService() 40 | Assert.Equal(dep3, { Value1 = "Dep1"; Value2 = "Dep2" } 41 | ) -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/En3Tho.Extensions.DependencyInjection.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UnitTest 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/Module2DependencyInjectionTest.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.Extensions.DependencyInjection.Tests 2 | 3 | open Microsoft.Extensions.DependencyInjection 4 | open Xunit 5 | 6 | module Module2 = 7 | 8 | type Startup() = 9 | member this.ConfigureServices(services: IServiceCollection) = 10 | services 11 | .AddSingleton({ Dependency1.Value = "Module2" }) 12 | .AddSingleton() 13 | |> ignore 14 | 15 | type Test(dep: Dependency1, dep2: Dependency2) = 16 | 17 | [] 18 | let ``Test that DI works in tests``() = 19 | Assert.Equal(dep2.Value.Value, dep.Value) 20 | Assert.Equal(dep2.Value.Value, "Module2") 21 | 22 | module Module3 = 23 | 24 | type Startup() = 25 | member this.ConfigureServices(services: IServiceCollection) = 26 | services 27 | .AddSingleton({ Dependency1.Value = "Module3" }) 28 | .AddSingleton() 29 | |> ignore 30 | 31 | type Test(dep: Dependency1, dep2: Dependency2) = 32 | 33 | [] 34 | let ``Test that DI works in tests``() = 35 | Assert.Equal(dep2.Value.Value, dep.Value) 36 | Assert.Equal(dep2.Value.Value, "Module3") -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/ModuleDependencyInjectionTest.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.Extensions.DependencyInjection.Tests.Module 2 | 3 | open Microsoft.Extensions.DependencyInjection 4 | open Xunit 5 | 6 | type Startup() = 7 | member this.ConfigureServices(services: IServiceCollection) = 8 | services 9 | .AddSingleton({ Dependency1.Value = "Module" }) 10 | .AddSingleton() 11 | |> ignore 12 | 13 | type Test(dep: Dependency1, dep2: Dependency2) = 14 | 15 | [] 16 | let ``Test that DI works in tests``() = 17 | Assert.Equal(dep2.Value.Value, dep.Value) 18 | Assert.Equal(dep2.Value.Value, "Module") -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/RootNamespaceDependencyInjectionTest.fs: -------------------------------------------------------------------------------- 1 | namespace En3Tho.Extensions.DependencyInjection.Tests 2 | 3 | open Microsoft.Extensions.DependencyInjection 4 | open Xunit 5 | 6 | type Dependency1 = { 7 | Value: string 8 | } 9 | 10 | type Dependency2 = { 11 | Value: Dependency1 12 | } 13 | 14 | type Startup() = 15 | member this.ConfigureServices(services: IServiceCollection) = 16 | services 17 | .AddSingleton({ Dependency1.Value = "abc" }) 18 | .AddSingleton() 19 | |> ignore 20 | 21 | type Test(dep: Dependency1, dep2: Dependency2) = 22 | 23 | [] 24 | let ``Test that DI works in tests``() = 25 | Assert.Equal(dep2.Value.Value, dep.Value) 26 | Assert.Equal(dep2.Value.Value, "abc") -------------------------------------------------------------------------------- /tests/En3Tho.Extensions.DependencyInjection.Tests/UpdateTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.Extensions.DependencyInjection.Tests.ConfigureTests 2 | 3 | open En3Tho.Extensions.DependencyInjection 4 | open Microsoft.Extensions.DependencyInjection 5 | open Xunit 6 | 7 | type SomeDependency() = class end 8 | 9 | type SomeMutableType(dep: SomeDependency) = 10 | member _.Dep = dep 11 | member val X = 0 with get, set 12 | 13 | [] 14 | let ``Test that updating once works`` () = 15 | let serviceProvider = 16 | ServiceCollection() 17 | .AddSingleton() 18 | .AddSingleton() 19 | .Update(fun mutableType -> mutableType.X <- 10; mutableType) 20 | .BuildServiceProvider() 21 | let service = serviceProvider.GetRequiredService() 22 | Assert.Equal(10, service.X) 23 | 24 | [] 25 | let ``Test that updating multiple times works`` () = 26 | let serviceProvider = 27 | ServiceCollection() 28 | .AddSingleton() 29 | .AddSingleton() 30 | .Update(fun mutableType -> mutableType.X <- mutableType.X + 1; mutableType) 31 | .Update(fun mutableType -> mutableType.X <- mutableType.X + 2; mutableType) 32 | .Update(fun mutableType -> mutableType.X <- mutableType.X + 3; mutableType) 33 | .BuildServiceProvider() 34 | let service = serviceProvider.GetRequiredService() 35 | Assert.Equal(6, service.X) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/ArrayPoolBasedBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.ArrayPoolBasedBuilderTests 2 | 3 | open System 4 | open System.Collections.Generic 5 | open Xunit 6 | open En3Tho.FSharp.ComputationExpressions 7 | open En3Tho.FSharp.ComputationExpressions.ArrayPoolBasedBuilders 8 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder 9 | open System.Linq 10 | 11 | [] 12 | let ``Test that array pool based builders build effectively the same collection`` () = 13 | let count = Random.Shared.Next(5, 15) 14 | 15 | let libraryArray = [| 16 | 1 17 | 2 18 | 3 19 | for i = 0 to count do 20 | i 21 | let mutable i = count 22 | while i > 0 do 23 | i 24 | i <- i - 1 25 | |] 26 | 27 | let customArray = arr { 28 | 1 29 | 2 30 | 3 31 | for i = 0 to count do 32 | i 33 | let mutable i = count 34 | while i > 0 do 35 | i 36 | i <- i - 1 37 | } 38 | 39 | let customBlock = block { 40 | 1 41 | 2 42 | 3 43 | for i = 0 to count do 44 | i 45 | let mutable i = count 46 | while i > 0 do 47 | i 48 | i <- i - 1 49 | } 50 | 51 | let customResizeArray = rsz { 52 | 1 53 | 2 54 | 3 55 | for i = 0 to count do 56 | i 57 | let mutable i = count 58 | while i > 0 do 59 | i 60 | i <- i - 1 61 | } 62 | 63 | let customResizeArray2 = ResizeArray() { 64 | 1 65 | 2 66 | 3 67 | for i = 0 to count do 68 | i 69 | let mutable i = count 70 | while i > 0 do 71 | i 72 | i <- i - 1 73 | } 74 | 75 | let customLinkedList = LinkedList() { 76 | 1 77 | 2 78 | 3 79 | for i = 0 to count do 80 | i 81 | let mutable i = count 82 | while i > 0 do 83 | i 84 | i <- i - 1 85 | } 86 | 87 | Assert.True(libraryArray.SequenceEqual(customArray)) 88 | Assert.True(libraryArray.SequenceEqual(customBlock)) 89 | Assert.True(libraryArray.SequenceEqual(customResizeArray)) 90 | Assert.True(libraryArray.SequenceEqual(customResizeArray2)) 91 | Assert.True(libraryArray.SequenceEqual(customLinkedList)) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/CancellableTaskTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.CancellableTaskTests 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Threading 6 | open System.Threading.Tasks 7 | open Xunit 8 | open En3Tho.FSharp.ComputationExpressions.Tasks 9 | 10 | [] 11 | let ``test that cancellable task cancels in move next calls``() = task { 12 | use cts = CancellationTokenSource() 13 | cts.CancelAfter(5) 14 | 15 | let stateCancellableTask() = cancellableTask cts.Token { 16 | do! Task.Delay(50) 17 | do! Task.Delay(50) 18 | } 19 | 20 | let cancellable = stateCancellableTask() 21 | 22 | let f() = 23 | task { 24 | let! _ = cancellable 25 | () 26 | } :> Task 27 | 28 | return! Assert.ThrowsAsync(f) 29 | } 30 | 31 | [] 32 | let ``test that cancellable task propagates cancellation token correctly``() = task { 33 | use cts = CancellationTokenSource() 34 | 35 | let stateCancellableTask() = cancellableTask cts.Token { 36 | let! token = getState() 37 | do! Task.Delay(5, token) 38 | do! Task.Delay(5, token) 39 | } 40 | 41 | let cancellable = stateCancellableTask() 42 | 43 | cts.Cancel() 44 | 45 | let f() = 46 | task { 47 | let! _ = cancellable 48 | () 49 | } :> Task 50 | 51 | return! Assert.ThrowsAsync(f) 52 | } 53 | 54 | [] 55 | let ``Test that disposables will be disposed even if task is canceled``() = task { 56 | use cts = CancellationTokenSource() 57 | let messages = List() 58 | 59 | let cancellable = cancellableTask (cts.Token) { 60 | use _ = { new IDisposable with member _.Dispose() = messages.Add("Disposed") } // should be there 61 | cts.Cancel() 62 | use _ = { new IAsyncDisposable with 63 | member _.DisposeAsync() = 64 | uvtask { 65 | do! Task.Delay(50) 66 | messages.Add("DisposedAsync") 67 | } } // should be there 68 | try 69 | Console.WriteLine("Try") 70 | finally 71 | Console.WriteLine("Finally") 72 | } 73 | 74 | let f() = 75 | task { 76 | let! _ = cancellable 77 | () 78 | } :> Task 79 | 80 | let! _ = Assert.ThrowsAsync(f) 81 | Assert.Equal(2, messages.Count) 82 | Assert.Equal("DisposedAsync", messages[0]) 83 | Assert.Equal("Disposed", messages[1]) 84 | } 85 | 86 | [] 87 | let ``Test that disposables will be disposed even if task is canceled 2``() = task { 88 | use cts = CancellationTokenSource() 89 | let messages = List() 90 | 91 | let cancellable = cancellableTask (cts.Token) { 92 | cts.Cancel() 93 | use _ = { new IDisposable with member _.Dispose() = messages.Add("Disposed") } // should be there 94 | use _ = { new IAsyncDisposable with 95 | member _.DisposeAsync() = 96 | uvtask { 97 | do! Task.Delay(50) 98 | messages.Add("DisposedAsync") 99 | } } // should be there 100 | try 101 | Console.WriteLine("Try") 102 | finally 103 | Console.WriteLine("Finally") 104 | } 105 | 106 | let f() = 107 | task { 108 | let! _ = cancellable 109 | () 110 | } :> Task 111 | 112 | let! _ = Assert.ThrowsAsync(f) 113 | Assert.Equal(1, messages.Count) 114 | Assert.Equal("Disposed", messages[0]) 115 | } 116 | -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/CodeBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.CodeBuilderTests 2 | 3 | open System 4 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder 5 | open Xunit 6 | 7 | [] 8 | let ``test simple code``() = 9 | let code = code { 10 | "let x = 0" 11 | "Console.WriteLine(x + 1)" 12 | } 13 | 14 | let expected = String.concat Environment.NewLine [ 15 | "let x = 0" 16 | "Console.WriteLine(x + 1)" 17 | ] 18 | 19 | Assert.Equal(expected, code.ToString()) 20 | 21 | [] 22 | let ``test simple code with codeBlock``() = 23 | let code = code { 24 | codeBlock { 25 | "let x = 0" 26 | "Console.WriteLine(x + 1)" 27 | } 28 | } 29 | 30 | let expected = String.concat Environment.NewLine [ 31 | "let x = 0" 32 | "Console.WriteLine(x + 1)" 33 | ] 34 | 35 | Assert.Equal(expected, code.ToString()) 36 | 37 | [] 38 | let ``test simple code indent``() = 39 | let code = code { 40 | indent { 41 | "let x = 0" 42 | "Console.WriteLine(x + 1)" 43 | } 44 | } 45 | 46 | let expected = String.concat Environment.NewLine [ 47 | " let x = 0" 48 | " Console.WriteLine(x + 1)" 49 | ] 50 | 51 | Assert.Equal(expected, code.ToString()) 52 | 53 | [] 54 | let ``test simple code brace block``() = 55 | let code = code { 56 | braceBlock { 57 | "let x = 0" 58 | "Console.WriteLine(x + 1)" 59 | } 60 | } 61 | 62 | let expected = String.concat Environment.NewLine [ 63 | "{" 64 | " let x = 0" 65 | " Console.WriteLine(x + 1)" 66 | "}" 67 | ] 68 | 69 | Assert.Equal(expected, code.ToString()) 70 | 71 | [] 72 | let ``test simple code brace block indent``() = 73 | let code = code { 74 | braceBlock { 75 | indent { 76 | "let x = 0" 77 | "Console.WriteLine(x + 1)" 78 | } 79 | } 80 | } 81 | 82 | let expected = String.concat Environment.NewLine [ 83 | "{" 84 | " let x = 0" 85 | " Console.WriteLine(x + 1)" 86 | "}" 87 | ] 88 | 89 | Assert.Equal(expected, code.ToString()) 90 | 91 | [] 92 | let ``test simple code trim``() = 93 | let code = code { 94 | "let x = 0" 95 | "Console.WriteLine(x + 1)" 96 | } 97 | 98 | let expected = String.concat Environment.NewLine [ 99 | "let x = 0" 100 | "Console.WriteLine(x + 1)" 101 | ] 102 | 103 | code.TrimEnd() 104 | Assert.Equal(expected, code.ToString()) 105 | 106 | [] 107 | let ``test simple code trim2``() = 108 | let code = code { 109 | "let x = 0" 110 | "Console.WriteLine(x + 1)" 111 | "" 112 | "\n" 113 | "\r" 114 | "\t" 115 | } 116 | 117 | let expected = String.concat Environment.NewLine [ 118 | "let x = 0" 119 | "Console.WriteLine(x + 1)" 120 | ] 121 | 122 | code.TrimEnd() 123 | Assert.Equal(expected, code.ToString()) 124 | 125 | [] 126 | let ``test that code builder itself can accept new code``() = 127 | let codeB = code { 128 | "let x = 0" 129 | "Console.WriteLine(x + 1)" 130 | } 131 | 132 | codeB { 133 | "let y = 0" 134 | } |> ignore 135 | 136 | let expected = String.concat Environment.NewLine [ 137 | "let x = 0" 138 | "Console.WriteLine(x + 1)" 139 | "let y = 0" 140 | ] 141 | 142 | Assert.Equal(expected, codeB.ToString()) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/En3Tho.FSharp.ComputationExpressions.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UnitTest 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/HttpBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.HttpBuilderTests 2 | 3 | open System.IO 4 | open System.Net 5 | open System.Net.Http 6 | open System.Text.Json 7 | open System.Threading.Tasks 8 | open En3Tho.FSharp.Extensions 9 | open En3Tho.FSharp.ComputationExpressions.HttpBuilder 10 | open Xunit 11 | 12 | [] 13 | let ``test that http builder does the basic get request as string``() = task { 14 | let http = HttpClient() 15 | let! response = http.Get("https://google.com").AsString() 16 | Assert.NotEmpty(response) 17 | } 18 | 19 | [] 20 | let ``test that http builder does the basic get request as byte array``() = task { 21 | let http = HttpClient() 22 | let! response = http.Get("https://google.com").AsByteArray() 23 | Assert.NotEmpty(response) 24 | } 25 | 26 | [] 27 | let ``test that http builder does the basic get request as stream``() = task { 28 | let http = HttpClient() 29 | use! response = http.Get("https://google.com").AsStream() 30 | use reader = StreamReader(response) 31 | let! message = reader.ReadToEndAsync() 32 | Assert.NotEmpty(message) 33 | } 34 | 35 | module MockedTests = 36 | type FuncHandler(checkRequest) = 37 | inherit HttpMessageHandler() 38 | override this.SendAsync(request, _) = 39 | Task.FromResult(checkRequest request) 40 | 41 | let mkClient fn = HttpClient(FuncHandler(fn)) 42 | 43 | [] 44 | let ``test that http builder correctly passes all body headers``() = task { 45 | let http = mkClient ^ fun request -> 46 | Assert.True(request.Headers.Contains("TestRequestHeader1")) 47 | Assert.True(request.Headers.Contains("TestRequestHeader2")) 48 | Assert.True(request.Headers.Contains("TestRequestHeader3")) 49 | HttpResponseMessage(HttpStatusCode.OK) 50 | 51 | use! response = http.Get("https://google.com").AsResponse() { 52 | requestHeaders { 53 | "TestRequestHeader1" -- "Wow" 54 | "TestRequestHeader2" -- "Wow" 55 | "TestRequestHeader3" -- "Wow" 56 | } 57 | } 58 | Assert.Equal(response.StatusCode, HttpStatusCode.OK) 59 | } 60 | 61 | [] 62 | let ``test that http builder correctly passes all content headers``() = task { 63 | let http = mkClient ^ fun request -> 64 | Assert.True(request.Headers.Contains("TestRequestHeader1")) 65 | Assert.True(request.Headers.Contains("TestRequestHeader2")) 66 | Assert.True(request.Headers.Contains("TestRequestHeader3")) 67 | Assert.True(request.Content.Headers.Contains("TestContentHeader1")) 68 | Assert.True(request.Content.Headers.Contains("TestContentHeader2")) 69 | Assert.True(request.Content.Headers.Contains("TestContentHeader3")) 70 | HttpResponseMessage(HttpStatusCode.OK) 71 | 72 | let! response = http.Post("https://google.com").Json({| Value = 10 |}).AsResponse() { 73 | requestHeaders { 74 | "TestRequestHeader1" -- "Wow" 75 | "TestRequestHeader2" -- "Wow" 76 | "TestRequestHeader3" -- "Wow" 77 | } 78 | contentHeaders { 79 | "TestContentHeader1" -- "Wow" 80 | "TestContentHeader2" -- "Wow" 81 | "TestContentHeader3" -- "Wow" 82 | } 83 | } 84 | Assert.Equal(response.StatusCode, HttpStatusCode.OK) 85 | } 86 | 87 | [] 88 | let ``test that http builder correctly passes json body``() = task { 89 | let http = mkClient ^ fun request -> 90 | let body = JsonSerializer.Deserialize<{| Value: int |}>(request.Content.ReadAsStream()) 91 | Assert.Equal(body.Value, 10) 92 | HttpResponseMessage( 93 | HttpStatusCode.OK, 94 | Content = StringContent(JsonSerializer.Serialize({| body with Value = 15 |})) 95 | ) 96 | 97 | let! response = http.Post("https://google.com").Json({| Value = 10 |}).AsJson<{| Value: int |}>() 98 | Assert.Equal(response.Value, 15) 99 | } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/LazyTaskTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.LazyTaskTests 2 | 3 | open System.Threading.Tasks 4 | open Xunit 5 | open En3Tho.FSharp.ComputationExpressions.Tasks 6 | 7 | [] 8 | let ``test that lazy task doesn't start until awaited``() = task { 9 | let mutable counter = 0 10 | let lz = lazyTask { 11 | counter <- 1 12 | } 13 | Assert.Equal(0, counter) 14 | do! lz 15 | Assert.Equal(1, counter) 16 | 17 | do! lz 18 | Assert.Equal(1, counter) 19 | } 20 | 21 | [] 22 | let ``test that lazy task doesn't execute move next multiple times``() = task { 23 | let mutable counter = 0 24 | let lz = lazyTask { 25 | do! Task.Delay(100) 26 | counter <- counter + 1 27 | } 28 | 29 | do! Parallel.ForAsync(0, 100, (fun i _ -> uvtask { 30 | do! lz 31 | })) 32 | 33 | Assert.Equal(1, counter) 34 | } 35 | 36 | [] 37 | let ``test that lazy task correctly awaits multiple times``() = task { 38 | let mutable counter = 0 39 | let lz = lazyTask { 40 | do! Task.Delay(100) 41 | counter <- 1 42 | do! Task.Delay(100) 43 | counter <- 2 44 | } 45 | Assert.Equal(0, counter) 46 | do! lz 47 | Assert.Equal(2, counter) 48 | } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/RepeatableTaskTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.RepeatableTaskTests 2 | 3 | open System.Threading 4 | open System.Threading.Tasks 5 | open Xunit 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | 8 | // [] 9 | // let ``test that repeatable task doesn't start until awaited``() = task { 10 | // let mutable counter = 0 11 | // let lz = repeatableTask { 12 | // counter <- 1 13 | // } 14 | // Assert.Equal(0, counter) 15 | // do! lz 16 | // Assert.Equal(1, counter) 17 | // do! lz 18 | // Assert.Equal(2, counter) 19 | // } 20 | // 21 | // [] 22 | // let ``test that repeatable task repeats itself``() = task { 23 | // let mutable counter = 0 24 | // let lz = repeatableTask { 25 | // do! Task.Delay(1) 26 | // Interlocked.Increment(&counter) |> ignore 27 | // } 28 | // 29 | // do! Parallel.ForAsync(0, 100, (fun i _ -> uvtask { 30 | // do! lz 31 | // })) 32 | // 33 | // Assert.Equal(100, counter) 34 | // } 35 | // 36 | // [] 37 | // let ``test that repeatable task correctly awaits multiple times``() = task { 38 | // let mutable counter = 0 39 | // let lz = repeatableTask { 40 | // do! Task.Delay(1) 41 | // counter <- counter + 1 42 | // do! Task.Delay(1) 43 | // counter <- counter + 1 44 | // } 45 | // Assert.Equal(0, counter) 46 | // do! lz 47 | // Assert.Equal(2, counter) 48 | // do! lz 49 | // Assert.Equal(4, counter) 50 | // } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/StringBuilderBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.StringBuilderBuilderTests 2 | 3 | open System 4 | open System.Text 5 | open Xunit 6 | open En3Tho.FSharp.ComputationExpressions 7 | open En3Tho.FSharp.ComputationExpressions.SStringBuilderBuilder 8 | 9 | [] 10 | let ``Test string builder computation expression is working property`` () = 11 | let sb1 = StringBuilder() { 12 | 1 13 | 1. 14 | 1.f 15 | '1' 16 | "1" 17 | } 18 | 19 | let sb2 = 20 | StringBuilder() 21 | .Append(1) 22 | .Append(1.) 23 | .Append(1.f) 24 | .Append('1') 25 | .Append("1") 26 | Assert.Equal(sb1.ToString(), sb2.ToString()) 27 | 28 | let ``Test that string builder computation expression supports complex operations`` () = 29 | let sb1 = StringBuilder() { 30 | let mutable x = 0 31 | while x < 10 do 32 | x 33 | for i = 0 to x do 34 | i 35 | try 36 | x 37 | with e -> 38 | x 39 | x 40 | x 41 | x 42 | } 43 | 44 | let sb2 = StringBuilder() 45 | let mutable x = 0 46 | while x < 10 do 47 | sb2.Append(x) |> ignore 48 | for i = 0 to x do 49 | sb2.Append(i) |> ignore 50 | try 51 | sb2.Append(x) |> ignore 52 | with e -> 53 | sb2.Append(x) |> ignore 54 | sb2.Append(x) |> ignore 55 | sb2.Append(x) |> ignore 56 | sb2.Append(x) |> ignore 57 | 58 | Assert.Equal(sb1.ToString(), sb2.ToString()) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/TaskAndCollectionsOverloadingTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.TaskAndCollectionsOverloadingTests 2 | 3 | open System.Collections.Generic 4 | open System.ComponentModel 5 | open En3Tho.FSharp.Extensions 6 | open En3Tho.FSharp.ComputationExpressions 7 | open En3Tho.FSharp.ComputationExpressions.Tasks 8 | open En3Tho.FSharp.ComputationExpressions.ICollectionBuilder 9 | open En3Tho.FSharp.ComputationExpressions.SCollectionBuilder 10 | open En3Tho.FSharp.ComputationExpressions.IDictionaryBuilder 11 | open Xunit 12 | 13 | type CustomSCollection() = 14 | let mutable sum = 0 15 | member _.Sum = sum 16 | member _.Add(v: int) = sum <- sum + v 17 | 18 | [] 19 | member inline this.Run([] expr: CollectionCode) = expr(); this 20 | [] 21 | member inline this.Zero _ : CollectionCode = fun() -> () 22 | 23 | [] 24 | let ``should use both custom collection and exntask``() = exnTask { 25 | let value = CustomSCollection() { 26 | 1 27 | 2 28 | 3 29 | } 30 | 31 | Assert.Equal(1 + 2 + 3, value.Sum) 32 | return () 33 | } 34 | 35 | [] 36 | let ``should use both resize array and exntask``() = exnTask { 37 | let value = ResizeArray() { 38 | 1 39 | 2 40 | 3 41 | } 42 | 43 | Assert.Equal(1, value[0]) 44 | Assert.Equal(2, value[1]) 45 | Assert.Equal(3, value[2]) 46 | return () 47 | } 48 | 49 | [] 50 | let ``should use both dictionary and exntask``() = exnTask { 51 | let value = Dictionary() { 52 | 1 -- 1 53 | 2 -- 2 54 | 3 -- 3 55 | } 56 | 57 | Assert.Equal(1, value[1]) 58 | Assert.Equal(2, value[2]) 59 | Assert.Equal(3, value[3]) 60 | return! ``should use both resize array and exntask``() 61 | } 62 | 63 | [] 64 | let ``should return from exntask``() = exnTask { 65 | return 1 66 | } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/Tasks.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.Tasks 2 | 3 | open System 4 | open System.Threading.Tasks 5 | open Xunit 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | 8 | [] 9 | let ``Test array map does not throw with value task CE``() = vtask { 10 | let w = [| 1 |] |> Array.map (fun w -> w + 1) |> Array.head 11 | let! x = task { return 3 } 12 | let! y = vtask { return 4 } 13 | let finalResult = w + x + y + 3 14 | return Assert.Equal(11, finalResult) 15 | } 16 | 17 | [] 18 | let ``Test that non generic value task and task work properly with utask CE``() = utask { 19 | do! Task.CompletedTask 20 | do! ValueTask() 21 | } 22 | 23 | [] 24 | let ``Test that non generic value task and task work properly with uvtask CE``() = uvtask { 25 | do! Task.CompletedTask 26 | do! ValueTask() 27 | } 28 | 29 | [] 30 | let ``Test that async disposable works properly with uvtask CE``() = uvtask { 31 | use _ = { new IAsyncDisposable with member _.DisposeAsync() = ValueTask() } 32 | () 33 | } 34 | 35 | [] 36 | let ``Test that async disposable works properly with vtask CE``() = vtask { 37 | use _ = { new IAsyncDisposable with member _.DisposeAsync() = ValueTask() } 38 | return () 39 | } 40 | 41 | [] 42 | let ``Test that async disposable works properly with utask CE``() = utask { 43 | use _ = { new IAsyncDisposable with member _.DisposeAsync() = ValueTask() } 44 | () 45 | } 46 | 47 | [] 48 | let ``Test that dispose works with utask CE``() = 49 | Assert.ThrowsAsync(fun () -> task { return! utask { 50 | use _ = { new IDisposable with member _.Dispose() = raise (ObjectDisposedException("")) } 51 | () 52 | }}) 53 | 54 | [] 55 | let ``Test that dispose works with uvtask CE``() = 56 | Assert.ThrowsAsync(fun () -> task { return! uvtask { 57 | use _ = { new IDisposable with member _.Dispose() = raise (ObjectDisposedException("")) } 58 | () 59 | }}) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.ComputationExpressions.Tests/TryExprBuilderTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.ComputationExpressions.Tests.TryExprBuilderTests 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open En3Tho.FSharp.ComputationExpressions 6 | open En3Tho.FSharp.Xunit 7 | open Xunit 8 | 9 | [] 10 | let ``test that try expr works correctly on successful cases``() = 11 | let mutable x = 0 12 | let inc() = &x +<- 1; x 13 | 14 | Assert.Equal(1, try' { inc() }) 15 | Assert.Equal(2, tryWith(2) { inc() }) 16 | 17 | Assert.Equal(2, x) 18 | 19 | [] 20 | let ``test that try expr works correctly on failure cases``() = 21 | let mutable x = 0 22 | let inc() = &x +<- 1; failwith "test" 23 | 24 | Assert.Equal(0, try' { inc() }) 25 | Assert.Equal(2, tryWith(2) { inc() }) 26 | 27 | try' { x <- x + 1; failwith "boom" } 28 | 29 | Assert.Equal(3, x) 30 | 31 | [] 32 | let ``test that tryE expr works correctly on successful cases``() = 33 | let mutable x = 0 34 | let inc() = &x +<- 1; x 35 | 36 | Assert.Null(tryE { inc() |> ignore }) 37 | Assert.Equal(1, x) 38 | 39 | [] 40 | let ``test that tryE expr works correctly on failure cases``() = 41 | let mutable x = 0 42 | let inc() = &x +<- 1; failwith "test" 43 | 44 | Assert.NotNull(tryE { inc() |> ignore }) 45 | Assert.Equal(1, x) 46 | 47 | [] 48 | let ``test that tryR expr works correctly on successful cases``() = 49 | let mutable x = 0 50 | let inc() = &x +<- 1; x 51 | 52 | Assert.IsOk(tryR { inc() |> ignore }) 53 | Assert.Equal(1, x) 54 | 55 | [] 56 | let ``test that tryR expr works correctly on failure cases``() = 57 | let mutable x = 0 58 | let inc() = &x +<- 1; failwith "test" 59 | 60 | Assert.IsErrorOfType(tryR { inc() |> ignore }) 61 | Assert.Equal(1, x) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Extensions.Tests/AsyncTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.AsyncTests 2 | 3 | open System.IO 4 | open Xunit 5 | open En3Tho.FSharp.ComputationExpressions.Tasks 6 | open En3Tho.FSharp.Extensions 7 | 8 | [] 9 | let ``Test that streams are disposed asynchronously``() = vtask { 10 | do! File.OpenRead("") |> useAsync ^ fun stream -> vtask { 11 | do! new MemoryStream() |> useAsync ^ fun ms -> vtask { 12 | do! stream.CopyToAsync(ms) 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Extensions.Tests/Byrefs.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.Byrefs 2 | 3 | open Xunit 4 | 5 | open En3Tho.FSharp.Extensions 6 | 7 | let inc x = x + 1 8 | 9 | [] 10 | let ``Test |><- functionality``() = 11 | let mutable x = 1 12 | &x |><- inc 13 | Assert.Equal(2, x) 14 | 15 | [] 16 | let ``Test ??<- functionality``() = 17 | let mutable x: string = null 18 | &x ??<- "Hello" 19 | Assert.Equal("Hello", x) 20 | &x ??<- null 21 | Assert.Equal("Hello", x) 22 | 23 | [] 24 | let ``Test ???<- functionality``() = 25 | let mutable x: string = null 26 | &x ???<- fun() -> "Hello" 27 | Assert.Equal("Hello", x) 28 | &x ??<- null 29 | Assert.Equal("Hello", x) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Extensions.Tests/DeferTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.DeferTests 2 | 3 | open System.Threading.Tasks 4 | open Xunit 5 | open En3Tho.FSharp.Extensions 6 | open En3Tho.FSharp.ComputationExpressions.Tasks 7 | 8 | [] 9 | let ``Test that defer properly works with both disposable and iasyncdisposable`` () = task { 10 | let mutable disposeCounter = 0 11 | 12 | do! task { 13 | let dispose = fun() -> &disposeCounter +<- 1 14 | let disposev = fun _ -> &disposeCounter +<- 1 15 | 16 | use _ = defer dispose 17 | use _ = (fun() -> &disposeCounter +<- 1) |> defer 18 | use _ = defer (fun() -> &disposeCounter +<- 1) 19 | use _ = defer(1, disposev) 20 | 21 | use _ = defer(fun() -> &disposeCounter +<- 1) 22 | use _ = defer(1, fun _ -> &disposeCounter +<- 1) 23 | use _ = defer(1, fun _ -> &disposeCounter +<- 1) 24 | 25 | let disposeAsync = fun() -> &disposeCounter +<- 1; ValueTask() 26 | let disposevAsync = fun _ -> &disposeCounter +<- 1; ValueTask() 27 | 28 | use _ = Defer.defer disposeAsync 29 | use _ = (fun() -> &disposeCounter +<- 1; ValueTask()) |> defer 30 | use _ = defer (fun() -> &disposeCounter +<- 1; ValueTask()) 31 | use _ = defer(1, disposevAsync) 32 | use _ = defer(1, disposevAsync) 33 | () 34 | } 35 | 36 | Assert.Equal(12, disposeCounter) 37 | } 38 | 39 | [] 40 | let ``Test that defer properly works with both disposable and iasyncdisposable2`` () = vtask { 41 | let mutable disposeCounter = 0 42 | 43 | do! task { 44 | let dispose = fun() -> &disposeCounter +<- 1 45 | let disposev = fun _ -> &disposeCounter +<- 1 46 | 47 | use _ = defer dispose 48 | use _ = (fun() -> &disposeCounter +<- 1) |> defer 49 | use _ = defer (fun() -> &disposeCounter +<- 1) 50 | use _ = defer(1, disposev) 51 | 52 | use _ = defer (fun() -> &disposeCounter +<- 1) 53 | use _ = defer(1, fun _ -> &disposeCounter +<- 1) 54 | use _ = defer(1,fun _ -> &disposeCounter +<- 1) 55 | 56 | let disposeAsync = fun() -> &disposeCounter +<- 1; ValueTask() 57 | let disposevAsync = fun _ -> &disposeCounter +<- 1; ValueTask() 58 | 59 | use _ = Defer.defer disposeAsync 60 | use _ = (fun() -> &disposeCounter +<- 1; ValueTask()) |> defer 61 | use _ = defer (fun() -> &disposeCounter +<- 1; ValueTask()) 62 | use _ = defer(1, disposevAsync) 63 | use _ = defer(1, disposevAsync) 64 | () 65 | } 66 | 67 | Assert.Equal(12, disposeCounter) 68 | } -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Extensions.Tests/En3Tho.FSharp.Extensions.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UnitTest 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Extensions.Tests/FunkyPipesTests.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Extensions.Tests.FunkyPipesTests 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open Xunit 6 | 7 | let f1 (x: int) = x 8 | let f2 x y = x + y 9 | let f3 x y z = x + y + z 10 | 11 | let q1 = Func<_,_>(f1) 12 | let q2 = Func<_,_,_>(f2) 13 | let q3 = Func<_,_,_,_>(f3) 14 | 15 | [] 16 | let ``test that pipe one does not break usual type inference``() = 17 | 1 18 | |> fun x -> x + 1 19 | |> ignore 20 | 21 | let pipe1 (x: int) = 22 | x 23 | |> f1 24 | |> fun x -> x 25 | |> q1 26 | |> (f1 >> q1) 27 | |> fun result -> 28 | Assert.Equal(x, result) 29 | 30 | [] 31 | let ``test that pipe1 works as expected``() = 32 | pipe1 5 33 | 34 | let pipe2 x y = 35 | let z = 36 | (x, y) 37 | ||> f2 38 | (z, z) 39 | ||> q2 40 | |> fun result -> 41 | Assert.Equal((x + y) * 2, result) 42 | 43 | [] 44 | let ``test that pipe2 works as expected``() = 45 | pipe2 5 10 46 | 47 | let pipe3 x y z = 48 | let w = 49 | (x, y, z) 50 | |||> f3 51 | (w, w, w) 52 | |||> q3 53 | |> fun result -> 54 | Assert.Equal((x + y + z) * 3, result) 55 | 56 | [] 57 | let ``test that pipe3 works as expected``() = 58 | pipe3 5 10 15 59 | 60 | let composition (x: int) = 61 | x 62 | |> (f1 63 | >> q1 64 | >> (f1 >> q1) 65 | >> fun result -> 66 | Assert.Equal(x, result)) 67 | [] 68 | let ``test that composition works as expected``() = 69 | composition 5 70 | 71 | let invoke (x: int) = 72 | (q1 ^ x) + (f1 ^ x) 73 | |> fun result -> Assert.Equal(x * 2, result) 74 | 75 | [] 76 | let ``test that invoke works as expected``() = 77 | invoke 5 -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Validation.Tests/En3Tho.FSharp.Validation.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UnitTest 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Validation.Tests/JsonSerialization.fs: -------------------------------------------------------------------------------- 1 | module En3Tho.FSharp.Validation.Tests.JsonSerializationTests 2 | 3 | open System 4 | open System.Text.Json 5 | open En3Tho.FSharp.Validation 6 | open En3Tho.FSharp.Validation.CommonValidatedTypes 7 | open En3Tho.FSharp.Validation.Json 8 | open Xunit 9 | 10 | let makeJsonSerializerOptions() = 11 | JsonSerializerOptions() 12 | .AddValidatedConverters() 13 | 14 | let options = makeJsonSerializerOptions() 15 | 16 | type InstancePlainValue<'a> = InstanceValidatorValidated<'a, PlainValue.Validator<'a>> 17 | 18 | [] 19 | let ``test that new ctor validator validated values get serialized and deserialized``() = 20 | let plain10 = PlainValue.Make(10) 21 | let str = JsonSerializer.Serialize(plain10, options) 22 | let result = JsonSerializer.Deserialize>(str, options) 23 | Assert.Equal(plain10, result) 24 | 25 | [] 26 | let ``test that NonNegativeValue get serialized and deserialized``() = 27 | let plain10 = NonNegativeValue.Make(10) 28 | let str = JsonSerializer.Serialize(plain10, options) 29 | let result = JsonSerializer.Deserialize>(str, options) // should throw on negative case 30 | Assert.Equal(plain10, result) 31 | 32 | [] 33 | let ``test that NonNegativeValue should throw on deserialization on bad case``() = 34 | let str = "-15" 35 | let result = JsonSerializer.Deserialize>(str, options) 36 | Assert.Throws(fun () -> 37 | JsonSerializer.Deserialize>(str, options) |> ignore) 38 | 39 | [] 40 | let ``test that new ctor validator validated values get serialized as their underlying values``() = 41 | let plain10 = PlainValue.Make(10) 42 | let serializedPlain10 = JsonSerializer.Serialize(plain10, options) 43 | let serialized10 = JsonSerializer.Serialize(plain10.Value, options) 44 | Assert.Equal(serialized10, serializedPlain10) 45 | 46 | [] 47 | let ``test that instance validator validated values get serialized as their underlying values``() = 48 | let plain10 = InstancePlainValue.Make(10, PlainValue.Validator()) 49 | let serializedPlain10 = JsonSerializer.Serialize(plain10, options) 50 | let serialized10 = JsonSerializer.Serialize(plain10.Value, options) 51 | Assert.Equal(serialized10, serializedPlain10) 52 | 53 | [] 54 | let ``test that instance validator validated values get serialized but not deserialized``() = 55 | let plain10 = InstancePlainValue.Make(10, PlainValue.Validator()) 56 | let serializedPlain10 = JsonSerializer.Serialize(plain10, options) 57 | Assert.Throws(fun () -> 58 | JsonSerializer.Deserialize>(serializedPlain10, options) |> ignore) -------------------------------------------------------------------------------- /tests/En3Tho.FSharp.Validation.Tests/Validation.fs: -------------------------------------------------------------------------------- 1 | module Validation 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open En3Tho.FSharp.ComputationExpressions.ResultBuilder 6 | open En3Tho.FSharp.Validation.CommonValidatedTypes.NonEmptyString 7 | open En3Tho.FSharp.Validation.CommonValidatedTypes.NonNegativeValue 8 | open En3Tho.FSharp.Xunit 9 | open Xunit 10 | open En3Tho.FSharp.Validation.CommonValidatedTypes 11 | 12 | 13 | [] 14 | let ``Test that Validated's ToString method is printing original value`` () = 15 | 16 | let genericTest value = 17 | let validated = PlainValue.Make(value = value) 18 | Assert.Equal(value.ToString(), validated.ToString()) 19 | 20 | genericTest "abcd" 21 | genericTest 123 22 | genericTest 1234. 23 | genericTest 'c' 24 | 25 | let complexValue = {| Name = PlainValue.Make "123"; Age = PlainValue.Make(123) |} 26 | 27 | genericTest complexValue 28 | 29 | Assert.Equal($"{complexValue}", $"{PlainValue.Make(complexValue)}") 30 | 31 | [] 32 | let ``Test that Validated's generated`` () = 33 | 34 | let genericTest value = 35 | let validated = PlainValue.Make(value = value) 36 | Assert.Equal(value.ToString(), validated.ToString()) 37 | 38 | genericTest "abcd" 39 | genericTest 123 40 | genericTest 1234. 41 | genericTest 'c' 42 | 43 | let complexValue = {| Name = PlainValue.Make "123"; Age = PlainValue.Make(123) |} 44 | 45 | genericTest complexValue 46 | 47 | Assert.Equal($"{complexValue}", $"{PlainValue.Make(complexValue)}") 48 | 49 | 50 | type Age = NonNegativeValue 51 | type Name = NonEmptyString 52 | type Person = { 53 | Age: Age 54 | Name: Name 55 | } with 56 | static member Of(age, name) = exnresult { 57 | let! age = Age.Of(age) // would be good if "age" could be passed implicitly 58 | and! name = Name.Of(name) // would be good if "age" could be passed implicitly 59 | return { 60 | Age = age 61 | Name = name 62 | } 63 | } 64 | 65 | [] 66 | let ``Test that multivalidation works for successful case``() = 67 | let age = 1 68 | let name = "Bob" 69 | 70 | let person = exnresult { 71 | let! age = Age.Of(age) 72 | and! name = Name.Of(name) 73 | return { 74 | Age = age 75 | Name = name 76 | } 77 | } 78 | 79 | Assert.IsOk({ Age = Age.Make(age); Name = Name.Make(name) }, person) 80 | 81 | [] 82 | let ``Test that multivalidation works for unsuccessful case``() = 83 | let age = -1 84 | let name = "" 85 | 86 | let person = exnresult { 87 | let! age = Age.Of(age) 88 | and! name = Name.Of(name) 89 | return { 90 | Age = age 91 | Name = name 92 | } 93 | } 94 | 95 | Assert.IsError(person) 96 | match person with 97 | | Error (:? AggregateException as exn) -> 98 | let errors = exn.InnerExceptions |> Seq.toArray 99 | Assert.Equal(2, errors.Length) 100 | Assert.True(errors[0] :? ValueIsNegativeException) 101 | Assert.True(errors[1] :? StringIsEmptyException) 102 | | _ -> 103 | Assert.Fail("Impossible") -------------------------------------------------------------------------------- /tools/CentralPackageManagementMigrationTool/CentralPackageManagementMigrationTool.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | all 26 | runtime; build; native; contentfiles; analyzers; buildtransitive 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /tools/CentralPackageManagementMigrationTool/DotnetInfo.fs: -------------------------------------------------------------------------------- 1 | module CentralPackageManagementMigrationTool.DotnetInfo 2 | 3 | open System 4 | open System.Collections.Generic 5 | open System.Diagnostics 6 | open System.IO 7 | 8 | let [] DOTNET_CLI_UI_LANGUAGE = "DOTNET_CLI_UI_LANGUAGE" 9 | 10 | let readProcessOutput (startInfo: ProcessStartInfo) = 11 | use process' = Process.Start(startInfo) 12 | let lines = List(); 13 | process'.OutputDataReceived.AddHandler (fun _ e -> 14 | if String.IsNullOrWhiteSpace(e.Data) |> not then 15 | lines.Add(e.Data); 16 | ) 17 | process'.BeginOutputReadLine(); 18 | process'.WaitForExit() 19 | lines 20 | 21 | let parseCoreBasePath (lines: List) = 22 | let rec findBasePath i = 23 | let line = lines[i] 24 | match line.Split(":", 2, StringSplitOptions.TrimEntries) with 25 | | [| "Base Path"; sdkPath |] -> 26 | sdkPath 27 | | _ -> 28 | findBasePath(i + 1) 29 | 30 | findBasePath 0 31 | 32 | let getCoreBasePath (projectPath: string) = 33 | let originalCliLanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE); 34 | Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, "en-US") 35 | 36 | try 37 | let startInfo = ProcessStartInfo("dotnet", "--info", 38 | WorkingDirectory = Path.GetDirectoryName(projectPath), 39 | CreateNoWindow = true, 40 | UseShellExecute = false, 41 | RedirectStandardOutput = true, 42 | RedirectStandardError = true 43 | ) 44 | let lines = readProcessOutput startInfo 45 | parseCoreBasePath lines 46 | finally 47 | Environment.SetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE, originalCliLanguage); -------------------------------------------------------------------------------- /tools/CentralPackageManagementMigrationTool/Program.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open CentralPackageManagementMigrationTool 3 | 4 | let args = Environment.GetCommandLineArgs()[1] 5 | MigrationTool.run args -------------------------------------------------------------------------------- /tools/CentralPackageManagementMigrationTool/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "CPM": { 4 | "commandName": "Project", 5 | "commandLineArgs": "\"X:\\Sources\\En3Tho\\AspireApp1\\AspireApp1.sln\"", 6 | "environmentVariables": { 7 | } 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /tools/CentralPackageManagementMigrationTool/XmlNodeBuilder.fs: -------------------------------------------------------------------------------- 1 | module CentralPackageManagementMigrationTool.XmlNodeBuilder 2 | 3 | open System.Collections.Generic 4 | open System.Xml 5 | open En3Tho.FSharp.ComputationExpressions 6 | 7 | type XmlNodeBuilderBase(xmlElement: XmlElement) = 8 | member _.XmlElement = xmlElement 9 | 10 | member _.Add(other: XmlElement) = 11 | xmlElement.AppendChild(other) 12 | 13 | member _.Add(other: XmlElementBuilder) = 14 | xmlElement.AppendChild(other.XmlElement) 15 | 16 | member _.Add(other: XmlNodeBuilder) = 17 | xmlElement.AppendChild(other.XmlElement) 18 | 19 | and XmlElementBuilder(xmlElement: XmlElement) = 20 | inherit XmlNodeBuilderBase(xmlElement) 21 | 22 | member inline this.Zero() : CollectionCode = fun() -> () 23 | member inline this.Run([] code: CollectionCode) = 24 | code() 25 | 26 | and XmlNodeBuilder(doc: XmlDocument, name: string) = 27 | inherit XmlNodeBuilderBase(doc.CreateElement(name)) 28 | 29 | member this.Add(attr: KeyValuePair) = 30 | this.XmlElement.Attributes.Append(this.XmlElement.OwnerDocument.CreateAttribute(attr.Key, Value = attr.Value)) 31 | 32 | member this.Add(innerText: string) = 33 | this.XmlElement.InnerText <- innerText 34 | 35 | member inline this.Zero() : CollectionCode = fun() -> () 36 | member inline this.Run([] code: CollectionCode) = 37 | code() 38 | XmlElementBuilder(this.XmlElement) 39 | 40 | let xmlNode doc name = XmlNodeBuilder(doc, name) -------------------------------------------------------------------------------- /tools/ProjectUtilities/ActivePatternsCodeGen.fs: -------------------------------------------------------------------------------- 1 | module ProjectUtilities.ActivePatternsCodeGen 2 | 3 | open System 4 | open System.Reflection 5 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder 6 | 7 | let generatePropertyActivePatterns(type': Type) = 8 | match type'.Name with 9 | | name when name.Contains("<") || name.Contains(">") -> 10 | None 11 | | _ -> 12 | match type'.GetProperties(BindingFlags.Public ||| BindingFlags.Instance) with 13 | | [||] -> 14 | None 15 | | properties -> 16 | code { 17 | $"module {type'.Name} =" 18 | indent { 19 | for p in properties do 20 | $"let inline {p.Name} a = (^a: (member {p.Name}: {p.PropertyType}) a)" 21 | 22 | for p in properties do 23 | $"let inline (|{p.Name}|) a = (^a: (member {p.Name}: {p.PropertyType}) a)" 24 | } 25 | } |> Some -------------------------------------------------------------------------------- /tools/ProjectUtilities/AddFunc.fs: -------------------------------------------------------------------------------- 1 | module ProjectUtilities.AddFunc 2 | 3 | open System 4 | open En3Tho.FSharp.Extensions 5 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder 6 | 7 | let getMethodCodeForVerb dependenciesCount (verb: string) = 8 | 9 | let genericParameters = 10 | let dependencies = [| for index = 1 to dependenciesCount do $"TDependency{index}" |] 11 | String.Join(", ", dependencies) 12 | 13 | codeBlock { 14 | $"public static IServiceCollection {verb}Func(this IServiceCollection collection," 15 | 16 | indent { 17 | $"Func<{genericParameters}, TService> factory)" 18 | "where TService : class" 19 | for i = 1 to dependenciesCount do 20 | $"where TDependency{i} : notnull" 21 | } 22 | braceBlock { 23 | $"collection.{verb}(" 24 | indent { 25 | "serviceProvider => factory(" 26 | indent { 27 | for index = 1 to dependenciesCount - 1 do 28 | $"serviceProvider.GetRequiredService()," 29 | $"serviceProvider.GetRequiredService()" 30 | } 31 | } 32 | "));" 33 | "return collection;" 34 | } 35 | } 36 | 37 | let generateFileForVerb dependenciesCount verb = 38 | code { 39 | "// auto-generated" 40 | "using Microsoft.Extensions.DependencyInjection;" 41 | "using Microsoft.Extensions.DependencyInjection.Extensions;" 42 | "" 43 | "namespace En3Tho.Extensions.DependencyInjection;" 44 | "" 45 | "public static partial class IServiceCollectionExtensions" 46 | braceBlock { 47 | for i = 1 to dependenciesCount do 48 | getMethodCodeForVerb i verb 49 | "" 50 | trimEnd() 51 | } 52 | } 53 | 54 | let generateAllLifetimesAndVerbs() = seq { 55 | let lifetimes = [| "Singleton"; "Scoped"; "Transient" |] 56 | 57 | for lifetime in lifetimes do 58 | let verbs = [| $"Add{lifetime}"; $"TryAdd{lifetime}"; $"TryAdd{lifetime}OrFail" |] 59 | for verb in verbs do 60 | let funcArgsCount = 16 61 | let filePath = $"{verb}Func.cs" 62 | filePath, generateFileForVerb funcArgsCount verb 63 | } 64 | 65 | let generateFiles() = 66 | generateAllLifetimesAndVerbs() 67 | |> Seq.iter ^ fun (filePath, code) -> 68 | Code.writeToFile filePath code -------------------------------------------------------------------------------- /tools/ProjectUtilities/Code.fs: -------------------------------------------------------------------------------- 1 | module ProjectUtilities.Code 2 | 3 | open System 4 | open System.IO 5 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder.CodeBuilderImpl 6 | open En3Tho.FSharp.Extensions 7 | 8 | let writeToFile filePath (code: CodeBuilder) = 9 | let dirName = ".artifacts" 10 | if not ^ Directory.Exists(dirName) then Directory.CreateDirectory(dirName) |> ignore 11 | 12 | let text = code |> toString 13 | File.WriteAllText(Path.Combine(dirName, filePath), text) 14 | 15 | let writeToConsole (code: CodeBuilder) = 16 | code.Flush(Console.Out) 17 | Console.WriteLine() -------------------------------------------------------------------------------- /tools/ProjectUtilities/ILoggerExtensionsCodeGen.fs: -------------------------------------------------------------------------------- 1 | module ProjectUtilities.ILoggerExtensionsCodeGen 2 | 3 | open System 4 | open System.Reflection 5 | open En3Tho.FSharp.Extensions 6 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder 7 | open Microsoft.Extensions.Logging 8 | 9 | let renameParamNameIfException(paramName: string) = 10 | if paramName.Equals("exception") then "``exception``" else paramName 11 | 12 | let parametersToArgsString (parameters: ParameterInfo[]) = 13 | parameters 14 | |> Array.map ^ fun param -> $"{renameParamNameIfException param.Name}: {param.ParameterType.Name}" 15 | |> String.concat ", " 16 | 17 | let parametersToParamString (parameters: ParameterInfo[]) = 18 | parameters 19 | |> Array.map ^ fun param -> $"{renameParamNameIfException param.Name}" 20 | |> String.concat ", " 21 | 22 | let generateCodeForLogLevel (logLevel: LogLevel) (methods: MethodInfo[]) = codeBlock { 23 | for method in methods do 24 | let nonLoggerAndNonArgsParameters = 25 | method.GetParameters() 26 | |> Array.skip 1 27 | |> Array.where ^ fun param -> param.ParameterType.Equals(typeof) |> not 28 | 29 | let methodArgs = 30 | nonLoggerAndNonArgsParameters 31 | |> parametersToArgsString 32 | 33 | let methodParams = 34 | nonLoggerAndNonArgsParameters 35 | |> parametersToParamString 36 | 37 | for i = 1 to 16 do 38 | let genericArgs = Array.init i (fun idx -> $"arg{idx}: 'a{idx}") |> String.concat ", " 39 | let genericObjArray = Array.init i (fun idx -> $"arg{idx}") |> String.concat "; " 40 | $"member inline this.{method.Name}({methodArgs}, {genericArgs}) =" 41 | indent { 42 | $"if this.IsEnabled({nameof(LogLevel)}.{logLevel}) then" 43 | indent { 44 | $"let args: obj[] = [| {genericObjArray} |]" 45 | $"this.{method.Name}({methodParams}, args)" 46 | } 47 | } 48 | "" 49 | trimEnd() 50 | } 51 | 52 | let generateILoggerExtensionsCode() = 53 | let typeToReadFrom = typeof 54 | let methods = typeToReadFrom.GetMethods() 55 | code { 56 | "// auto-generated" 57 | "[]" 58 | "module Microsoft.Extensions.Logging.ILoggerExtensions" 59 | "" 60 | "open System" 61 | "" 62 | "type ILogger with" 63 | indent { 64 | for logLevel in Enum.GetValues() do 65 | let logLevelMethods = methods |> Array.where ^ fun method -> method.Name.Equals($"Log{logLevel}") 66 | generateCodeForLogLevel logLevel logLevelMethods 67 | "" 68 | } 69 | trimEnd() 70 | } 71 | 72 | let generateFiles() = 73 | generateILoggerExtensionsCode() 74 | |> Code.writeToFile "ILoggerExtensions.fs" -------------------------------------------------------------------------------- /tools/ProjectUtilities/Program.fs: -------------------------------------------------------------------------------- 1 | open System 2 | open System.Diagnostics 3 | 4 | open ProjectUtilities 5 | 6 | let go() = 7 | try 8 | let sw = Stopwatch.StartNew() 9 | 10 | // AddFunc.generateFiles() 11 | // ServiceProviderAndScopeCodeGen.generateFiles() 12 | // WebApplicationsExtensionsCodeGen.generateFiles() 13 | // ILoggerExtensionsCodeGen.generateFiles() 14 | IntrinsicsCodeGen.generateFiles "D:\Downloads\intel-intrinsics-3-6-9.xml" 15 | 16 | Console.WriteLine($"Elapsed: {sw.Elapsed}") 17 | with e -> 18 | Console.WriteLine(e.ToString()) 19 | go() -------------------------------------------------------------------------------- /tools/ProjectUtilities/ProjectUtilities.fsproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /tools/ProjectUtilities/WebApplicationsExtensionsCodeGen.fs: -------------------------------------------------------------------------------- 1 | module ProjectUtilities.WebApplicationsExtensionsCodeGen 2 | 3 | open En3Tho.FSharp.Extensions 4 | open En3Tho.FSharp.ComputationExpressions.CodeBuilder 5 | 6 | let generateCodeForHttpMethod httpMethod genericArgsCount = codeBlock { 7 | let args = 8 | [ 9 | for argIndex = 1 to genericArgsCount do 10 | $"'T{argIndex}" 11 | ] |> String.concat ", " 12 | 13 | $"member inline this.Map{httpMethod}(pattern, func: Func<{args}, 'TResult>) = this.Map{httpMethod}(pattern, handler = func)" 14 | } 15 | 16 | let generateWebApplicationExtensions() = code { 17 | "// auto-generated" 18 | "[]" 19 | "module En3Tho.FSharp.Extensions.AspNetCore.WebAppExtensions" 20 | "" 21 | "open System" 22 | "open Microsoft.AspNetCore.Builder" 23 | "" 24 | "type WebApplication with" 25 | indent { 26 | for httpMethod in ["Get"; "Post"; "Put"; "Delete"; "Patch"] do 27 | $"member inline this.Map{httpMethod}(pattern, func: Action) = this.Map{httpMethod}(pattern, handler = func)" 28 | $"member inline this.Map{httpMethod}(pattern, func: Func<'TResult>) = this.Map{httpMethod}(pattern, handler = func)" 29 | for genericArgsCount in 2 .. 16 do 30 | generateCodeForHttpMethod httpMethod genericArgsCount 31 | "" 32 | trimEnd() 33 | } 34 | } 35 | 36 | let generateFactoryCodeForHttpMethod httpMethod genericArgsCount = codeBlock { 37 | let args = 38 | [ 39 | for argIndex = 1 to genericArgsCount do 40 | $"'T{argIndex}" 41 | ] |> String.concat ", " 42 | 43 | $"static member {httpMethod}(route: string, handler: Func<{args}, 'TResult>) =" 44 | indent { 45 | $"{httpMethod}(route, handler :> Delegate)" 46 | } 47 | } 48 | 49 | let generateWebApplicationFactoryMembers() = code { 50 | "// auto-generated" 51 | "namespace En3Tho.FSharp.Extensions.AspNetCore" 52 | "" 53 | "open System" 54 | "" 55 | "[]" 56 | "type EndpointInfoFactory() =" 57 | "" 58 | indent { 59 | for httpMethod in ["Get"; "Post"; "Put"; "Delete"; "Patch"] do 60 | 61 | $"static member {httpMethod}(route: string, handler) =" 62 | indent { 63 | $"EndpointInfo(EndpointType.{httpMethod}, route, handler)" 64 | } 65 | $"static member {httpMethod}(route: string, handler: Action) =" 66 | indent { 67 | $"{httpMethod}(route, handler :> Delegate)" 68 | } 69 | $"static member {httpMethod}(route: string, handler: Func<'TResult>) =" 70 | indent { 71 | $"{httpMethod}(route, handler :> Delegate)" 72 | } 73 | for genericArgsCount in 2 .. 16 do 74 | generateFactoryCodeForHttpMethod httpMethod genericArgsCount 75 | "" 76 | trimEnd() 77 | } 78 | } 79 | 80 | let generateFiles() = 81 | generateWebApplicationExtensions() 82 | |> Code.writeToFile "WebApplicationExtensions.fs" 83 | 84 | generateWebApplicationFactoryMembers() 85 | |> Code.writeToFile "EndpointInfoFactory.fs" --------------------------------------------------------------------------------