{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}

module TOML.Value (
  Value (..),
  renderValue,
  Table,
) where

import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Time (Day, LocalTime, TimeOfDay, TimeZone)

type Table = Map Text Value

data Value
  = Table Table
  | Array [Value]
  | String Text
  | Integer Integer
  | Float Double -- TOML spec specifies this must be a double precision float: https://github.com/toml-lang/toml/issues/538
  | Boolean Bool
  | OffsetDateTime (LocalTime, TimeZone)
  | LocalDateTime LocalTime
  | LocalDate Day
  | LocalTime TimeOfDay
  deriving (Int -> Value -> ShowS
[Value] -> ShowS
Value -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Value] -> ShowS
$cshowList :: [Value] -> ShowS
show :: Value -> String
$cshow :: Value -> String
showsPrec :: Int -> Value -> ShowS
$cshowsPrec :: Int -> Value -> ShowS
Show, Value -> Value -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Value -> Value -> Bool
$c/= :: Value -> Value -> Bool
== :: Value -> Value -> Bool
$c== :: Value -> Value -> Bool
Eq)

-- | Render a Value in pseudo-JSON format.
renderValue :: Value -> Text
renderValue :: Value -> Text
renderValue = \case
  Table Table
kvs -> Text
"{" forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
Text.intercalate Text
", " (forall a b. (a -> b) -> [a] -> [b]
map forall {a}. Show a => (a, Value) -> Text
renderKeyValue forall a b. (a -> b) -> a -> b
$ forall k a. Map k a -> [(k, a)]
Map.toList Table
kvs) forall a. Semigroup a => a -> a -> a
<> Text
"}"
  Array [Value]
vs -> Text
"[" forall a. Semigroup a => a -> a -> a
<> Text -> [Text] -> Text
Text.intercalate Text
", " (forall a b. (a -> b) -> [a] -> [b]
map Value -> Text
renderValue [Value]
vs) forall a. Semigroup a => a -> a -> a
<> Text
"]"
  String Text
s -> forall a. Show a => a -> Text
showT Text
s
  Integer Integer
x -> forall a. Show a => a -> Text
showT Integer
x
  Float Double
x -> forall a. Show a => a -> Text
showT Double
x
  Boolean Bool
b -> if Bool
b then Text
"true" else Text
"false"
  OffsetDateTime (LocalTime, TimeZone)
x -> forall a. Show a => a -> Text
showT (LocalTime, TimeZone)
x
  LocalDateTime LocalTime
x -> forall a. Show a => a -> Text
showT LocalTime
x
  LocalDate Day
x -> forall a. Show a => a -> Text
showT Day
x
  LocalTime TimeOfDay
x -> forall a. Show a => a -> Text
showT TimeOfDay
x
  where
    renderKeyValue :: (a, Value) -> Text
renderKeyValue (a
k, Value
v) = forall a. Show a => a -> Text
showT a
k forall a. Semigroup a => a -> a -> a
<> Text
": " forall a. Semigroup a => a -> a -> a
<> Value -> Text
renderValue Value
v

    showT :: (Show a) => a -> Text
    showT :: forall a. Show a => a -> Text
showT = String -> Text
Text.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show