I stumbled across something a long while ago, fetching the environments variables from say env under linux (say on a pod via argos), anyway, point is I could fetch something like that:
rabbitmq__port=a
rabbitmq__password=b
rabbitmq__ssl__enabled=b
rabbitmq__hostname=c
rabbitmq__username=d
rabbitmq__virtualhost=e
rabbitmq__ssl__serverName=f
and wanted to convert it to json format, using the __ as a nestedness separator:
{
"rabbitmq": {
"port": "a",
"password": "b",
"ssl": {
"enabled": "b",
"serverName": "f"
},
"hostname": "c",
"username": "d",
"virtualhost": "e"
}
}
I've come up with the implementation below, and while it works, I found it a wee too verbose for what it is doing, and I'm trying to figure out something that would make it shorter and less convoluted and more idiomatic:
module Playground
open System
open System.Text.Json
open System.Collections.Generic
open FSharpPlus
module EnvJson =
[<Literal>]
let private EnvKeySeparator = "__"
[<Literal>]
let private EnvLineKeyValueSeparator = "="
let private addKeyTokensToJsonDict jsonDict (envValue: string) envKeyTokens =
let folder (state: IDictionary<string, obj>) (i, envKeyToken) =
if i = Array.length envKeyTokens - 1 then
state[envKeyToken] <- envValue
jsonDict
else if Dict.containsKey envKeyToken state then
state[envKeyToken] :?> IDictionary<string, obj>
else
let nestedState = Dictionary()
state[envKeyToken] <- nestedState
nestedState
Array.indexed envKeyTokens |> Array.fold folder jsonDict
let private addLineToJsonDict jsonDict envLine =
let envLineTokens =
String.trimWhiteSpaces envLine
|> String.split [ EnvLineKeyValueSeparator ]
|> Seq.toArray
let envLineKey, envLineValue =
Array.item 0 envLineTokens, Array.item 1 envLineTokens
envLineKey
|> String.split [ EnvKeySeparator ]
|> Seq.toArray
|> addKeyTokensToJsonDict jsonDict envLineValue
let ofLines options source =
JsonSerializer.Serialize(value = Seq.fold addLineToJsonDict (Dictionary()) source, options = options)
let ofText options source =
source
|> String.split [ Environment.NewLine ]
|> Seq.toArray
|> Array.filter (String.IsNullOrWhiteSpace >> not)
|> ofLines options
[<EntryPoint>]
let main _ =
"""
rabbitmq__port=a
rabbitmq__password=b
rabbitmq__ssl__enabled=b
rabbitmq__hostname=c
rabbitmq__username=d
rabbitmq__virtualhost=e
rabbitmq__ssl__serverName=f
"""
|> EnvJson.ofText JsonSerializerOptions.Default
|> printfn "%s"
```