@@ -3,14 +3,19 @@ module Generators.Exercise
3
3
open System
4
4
open System.IO
5
5
open System.Reflection
6
+ open Newtonsoft.Json
6
7
open Newtonsoft.Json .Linq
7
8
open Humanizer
8
9
open Serilog
9
10
open Formatting
10
11
open Rendering
12
+ open CanonicalData
13
+
14
+ let private exerciseNameFromType ( exerciseType : Type ) = exerciseType.Name.Kebaberize()
11
15
12
16
[<AbstractClass>]
13
- type Exercise () =
17
+ type GeneratorExercise () =
18
+
14
19
// Allow changes in canonical data
15
20
abstract member MapCanonicalData : CanonicalData -> CanonicalData
16
21
abstract member MapCanonicalDataCase : CanonicalDataCase -> CanonicalDataCase
@@ -56,7 +61,7 @@ type Exercise() =
56
61
abstract member UseFullMethodName : CanonicalDataCase -> bool
57
62
abstract member AdditionalNamespaces : string list
58
63
59
- member this.Name = this.GetType() .Name.Kebaberize ()
64
+ member this.Name = this.GetType() |> exerciseNameFromType
60
65
member this.TestModuleName = this.GetType() .Name.Pascalize() |> sprintf " %s Test"
61
66
member this.TestedModuleName = this.GetType() .Name.Pascalize()
62
67
@@ -66,8 +71,6 @@ type Exercise() =
66
71
Directory.CreateDirectory( Path.GetDirectoryName( testClassPath)) |> ignore
67
72
File.WriteAllText( testClassPath, contents)
68
73
69
- Log.Information( " Generated tests for {Exercise} exercise in {TestClassPath}" , this.Name, testClassPath);
70
-
71
74
member this.Regenerate ( canonicalData ) =
72
75
canonicalData
73
76
|> this.MapCanonicalData
@@ -233,23 +236,86 @@ type Exercise() =
233
236
default __.UseFullMethodName _ = false
234
237
235
238
default __.AdditionalNamespaces = []
239
+
240
+ type CustomExercise () =
241
+
242
+ member this.Name = this.GetType() .Name.Kebaberize()
243
+
244
+ type MissingDataExercise = { Name: string }
245
+
246
+ type UnimplementedExercise = { Name: string }
247
+
248
+ type Exercise =
249
+ | Generator of GeneratorExercise
250
+ | Custom of CustomExercise
251
+ | MissingData of MissingDataExercise
252
+ | Unimplemented of UnimplementedExercise
253
+
254
+ let exerciseName exercise =
255
+ match exercise with
256
+ | Generator generator -> generator.Name
257
+ | Custom custom -> custom.Name
258
+ | Unimplemented unimplemented -> unimplemented.Name
259
+ | MissingData missingData -> missingData.Name
260
+
261
+ type ConfigExercise = { Slug: string }
262
+
263
+ type Config = { Exercises: ConfigExercise [] }
264
+
265
+ let private exerciseNames =
266
+ let configFilePath = " ../config.json"
267
+ let configFileContents = File.ReadAllText configFilePath
268
+ let config = JsonConvert.DeserializeObject< Config>( configFileContents)
269
+
270
+ config.Exercises
271
+ |> Seq.map ( fun exercise -> exercise.Slug)
272
+ |> Seq.sort
273
+ |> Seq.toList
236
274
237
- let createExercises filteredExercise =
275
+ let private isConcreteType ( ty : Type ) = not ty.IsAbstract
238
276
239
- let isConcreteExercise ( exerciseType : Type ) =
240
- not exerciseType.IsAbstract && typeof< Exercise>. IsAssignableFrom( exerciseType)
277
+ let private isGeneratorExercise ( ty : Type ) = typeof< GeneratorExercise>. IsAssignableFrom( ty)
241
278
242
- let isFilteredExercise exercise ( exerciseType : Type ) =
243
- String.equals exercise exerciseType.Name ||
244
- String.equals exercise ( exerciseType.Name.Kebaberize())
279
+ let private isCustomExercise ( ty : Type ) = typeof< CustomExercise>. IsAssignableFrom( ty)
245
280
246
- let includeExercise ( exerciseType : Type ) =
247
- match filteredExercise with
248
- | None -> isConcreteExercise exerciseType
249
- | Some exercise -> isConcreteExercise exerciseType && isFilteredExercise exercise exerciseType
281
+ let private concreteAssemblyTypes =
282
+ Assembly.GetEntryAssembly() .GetTypes()
283
+ |> Array.filter isConcreteType
250
284
251
- let assemblyTypes = Assembly.GetEntryAssembly() .GetTypes()
285
+ let private exerciseTypeByName < 'T > exerciseType =
286
+ ( exerciseNameFromType exerciseType, Activator.CreateInstance( exerciseType) :?> 'T)
252
287
253
- seq { for exerciseType in assemblyTypes do
254
- if includeExercise exerciseType then
255
- yield Activator.CreateInstance( exerciseType) :?> Exercise }
288
+ let private exerciseTypesByName < 'T > predicate =
289
+ concreteAssemblyTypes
290
+ |> Array.filter predicate
291
+ |> Array.map exerciseTypeByName< 'T>
292
+ |> Map.ofArray
293
+
294
+ let private generatorExercises = exerciseTypesByName< GeneratorExercise> isGeneratorExercise
295
+
296
+ let private customExercises = exerciseTypesByName< CustomExercise> isCustomExercise
297
+
298
+ let private tryFindGeneratorExercise exerciseName =
299
+ generatorExercises
300
+ |> Map.tryFind exerciseName
301
+ |> Option.map Generator
302
+
303
+ let private tryFindCustomExercise exerciseName =
304
+ customExercises
305
+ |> Map.tryFind exerciseName
306
+ |> Option.map Custom
307
+
308
+ let private tryFindUnimplementedExercise options exerciseName =
309
+ match hasCanonicalData options exerciseName with
310
+ | true -> Unimplemented { Name = exerciseName } |> Some
311
+ | false -> None
312
+
313
+ let private createExercise options exerciseName =
314
+ tryFindGeneratorExercise exerciseName
315
+ |> Option.orElse ( tryFindCustomExercise exerciseName)
316
+ |> Option.orElse ( tryFindUnimplementedExercise options exerciseName)
317
+ |> Option.orElse ( MissingData { Name = exerciseName } |> Some)
318
+
319
+ let createExercises options =
320
+ exerciseNames
321
+ |> List.choose ( createExercise options)
0 commit comments