<commit_before>module Generators.Templates

open System.IO
open System.Collections.Generic
open System.Reflection
open FSharp.Reflection
open DotLiquid
open DotLiquid.FileSystems
open Rendering

type OutputFilter() =
    static member Format (input: string) = Obj.render input

    static member Indent (input: string) = String.indent 1 input

let private fileSystem = LocalFileSystem(Path.GetFullPath("./Templates"))
Template.RegisterFilter(OutputFilter().GetType())
Template.FileSystem <- fileSystem :> DotLiquid.FileSystems.IFileSystem

let private registrations = Dictionary<_,_>()
let rec private registerTypeTree templateDataType =
    if registrations.ContainsKey templateDataType then ()
    elif FSharpType.IsRecord templateDataType then
        let properties = templateDataType.GetProperties(BindingFlags.Instance ||| BindingFlags.Public)
        Template.RegisterSafeType(templateDataType, [| for p in properties -> p.Name |])
        registrations.[templateDataType] <- true
        for p in properties do registerTypeTree p.PropertyType
    elif templateDataType.IsGenericType then
        let t = templateDataType.GetGenericTypeDefinition()
        if t = typedefof<seq<_>> || t = typedefof<list<_>> then
            registrations.[templateDataType] <- true
            registerTypeTree (templateDataType.GetGenericArguments().[0])
        elif t = typedefof<IDictionary<_,_>> || t = typedefof<Map<_,_>> then
            registrations.[templateDataType] <- true
            registerTypeTree (templateDataType.GetGenericArguments().[0])
            registerTypeTree (templateDataType.GetGenericArguments().[1])
        elif t = typedefof<option<_>> then
            Template.RegisterSafeType(templateDataType, [|"Value"; "IsSome"; "IsNone";|])
            registrations.[templateDataType] <- true
            registerTypeTree (templateDataType.GetGenericArguments().[0])
        elif templateDataType.IsArray then          
            registrations.[templateDataType] <- true
            registerTypeTree (templateDataType.GetElementType())
    else 
        let properties = templateDataType.GetProperties(BindingFlags.Instance ||| BindingFlags.Public)
        Template.RegisterSafeType(templateDataType, [| for p in properties -> p.Name |])
        registrations.[templateDataType] <- true
        for p in properties do registerTypeTree p.PropertyType

let private hashFromData (data: obj) =
    match FSharpType.IsRecord (data.GetType()) with
    | true  -> Hash.FromAnonymousObject(data)
    | false -> Hash.FromDictionary(data :?> IDictionary<string, obj>)

let renderTemplate name data =
    let template = sprintf "{%% include \"%s\" %%}" name
    data.GetType() |> registerTypeTree

    let parsedTemplate = Template.Parse template
    let hash = hashFromData data
    parsedTemplate.Render(hash)<commit_msg>generators: Replace generic type registration with simpler, explicit type registration<commit_after>module Generators.Templates

open System.IO
open System.Collections.Generic
open FSharp.Reflection
open DotLiquid
open DotLiquid.FileSystems
open Rendering

type OutputFilter() =
    static member Indent (input: string) = String.indent 1 input

let private initTemplate() = 
    Template.FileSystem <- LocalFileSystem(Path.GetFullPath("./Templates"))

    Template.RegisterFilter(OutputFilter().GetType()) |> ignore

    Template.RegisterSafeType(typeof<TestMethodBodyAssert>, [| "Sut"; "Expected" |])
    Template.RegisterSafeType(typeof<TestMethodBody>, [| "Arrange"; "Assert" |])
    Template.RegisterSafeType(typeof<TestMethod>, [| "Skip"; "Name"; "Body" |])
    Template.RegisterSafeType(typeof<TestFile>, [| "Version"; "TestModuleName"; "TestedModuleName"; "Namespaces"; "Setup"; "Methods" |])

let private hashFromData (data: obj) =
    match FSharpType.IsRecord (data.GetType()) with
    | true  -> Hash.FromAnonymousObject(data)
    | false -> Hash.FromDictionary(data :?> IDictionary<string, obj>)

let renderTemplate =
    initTemplate()

    fun (name: string) (data: obj) ->
        let template = sprintf "{%% include \"%s\" %%}" name
        let parsedTemplate = Template.Parse template
        let hashedData = hashFromData data
        parsedTemplate.Render hashedData
{'fraction_non_alphanumeric': 0.10718520275077069, 'fraction_numerical': 0.0014228124258951862, 'mean_word_length': 4.045454545454546, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 32, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1037334', 'n_tokens_mistral': 1128, 'n_tokens_neox': 1119, 'n_words': 275}