-
Notifications
You must be signed in to change notification settings - Fork 0
/
Infrastructure.fs
134 lines (100 loc) · 4.33 KB
/
Infrastructure.fs
1
2
3
4
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
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/// Module containing various items and helper functions used throughout the project.
[<AutoOpen; CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
module NikonTheThird.Krystallizer.Infrastructure
open FSharp.Control
open FSharp.Quotations.Patterns
open Npgsql
open Serilog
open System
open System.Data.Common
open System.IO
open System.Reflection
open System.Text.Json
open System.Text.Json.Serialization
open System.Threading.Tasks
/// DirectoryInfo pointing to the path of the currently executing assembly.
/// This is used to resolve relative file and folder paths.
let executingAssemblyDirectoryInfo =
let executingAssembly = Assembly.GetExecutingAssembly ()
(FileInfo executingAssembly.Location).DirectoryName
|> DirectoryInfo
/// Contains the date and time the program was started.
/// This is used to generate file names.
let programStartDateTime = DateTime.Now
/// JSON serializer options set up to handle F# types.
let jsonSerializerOptions =
JsonSerializerOptions (PropertyNamingPolicy = JsonNamingPolicy.CamelCase)
do jsonSerializerOptions.Converters.Add (
JsonFSharpConverter (JsonUnionEncoding.Default ||| JsonUnionEncoding.UnwrapFieldlessTags)
)
/// Additional operations for async computation expressions.
type [<AbstractClass; Sealed>] Async private () =
/// Like Async.AwaitTask, but for ValueTask.
static member inline AwaitValueTask (task : ValueTask<_>) =
task.AsTask ()
|> Async.AwaitTask
/// Waits for the given task to resolve and calls the mapping
/// on its result. The mapping returns another task.
static member inline Bind mapping task =
async.Bind (task, mapping)
/// Waits for the given task to resolve and calls the mapping
/// on its result.
static member inline Map mapping =
Async.Bind (mapping >> async.Return)
/// Extensions for the async computation expression.
type AsyncBuilder with
/// A version of for that accepts an asynchronous sequence.
member inline _.For (sequence, body) = async {
let! sequence = sequence
for element in sequence do
do! body element
}
/// Extensions for the asyncSeq computation expression.
type AsyncSeq.AsyncSeqBuilder with
/// A version of while that accepts an asynchronous guard.
member inline _.While (guard, body) = asyncSeq {
let! guardResult' = guard ()
let mutable guardResult = guardResult'
while guardResult do
yield! body
let! guardResult' = guard ()
do guardResult <- guardResult'
}
/// Extensions for the database column reader.
type DbDataReader with
/// Read the given column index as a byte array.
member inline this.GetByteArray index =
this.GetValue index |> unbox<byte array>
/// Read the given column index as an optional int32.
member inline this.GetInt32Option index =
match this.GetValue index with
| value when Convert.IsDBNull value -> ValueNone
| value -> unbox<int32> value |> ValueSome
/// Extensions for the SQL command builder.
type NpgsqlCommand with
/// Adds a typed parameter to the command, which avoids boxing.
member inline this.AddTypedParameter<'T> (name, value) =
NpgsqlParameter<'T> (
ParameterName = name,
TypedValue = value
)
|> this.Parameters.Add
|> ignore
/// Adds a typed optional parameter to the command, which avoids boxing.
member inline this.AddTypedOptionParameter<'T> (name, value) =
match value with
| ValueSome value -> this.AddTypedParameter<'T> (name, value)
| ValueNone -> this.Parameters.AddWithValue (name, DBNull.Value) |> ignore
/// Extensions for value options.
[<RequireQualifiedAccess; CompilationRepresentation (CompilationRepresentationFlags.ModuleSuffix)>]
module ValueOption =
/// Converts the given value option to a regular option.
let toOption = function
| ValueSome value -> Some value
| ValueNone -> None
/// Active pattern to convert a value option to a regular option inside a match.
let (|AsOption|) = ValueOption.toOption
/// Returns a Serilog logger for the given module property expression.
let getModuleLogger = function
| PropertyGet (_, propertyInfo, _) -> Log.ForContext propertyInfo.DeclaringType
| _ -> raise (InvalidOperationException "Invalid logger expression")