packages feed

salak-toml-0.3.4.1: src/Salak/Toml.hs

{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE GADTs             #-}
{-# LANGUAGE NoOverloadedLists #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TupleSections     #-}
-- |
-- Module:      Salak.Toml
-- Copyright:   (c) 2019 Daniel YU
-- License:     MIT
-- Maintainer:  leptonyu@gmail.com
-- Stability:   experimental
-- Portability: portable
--
-- Toml support for "Salak".
--
module Salak.Toml(
    TOML(..)
  , loadToml
  , runSalakWithToml
  ) where

import           Control.Exception   (Exception, throwIO)
import qualified Data.HashMap.Strict as HM
import           Data.List           (foldl')
import qualified Data.List.NonEmpty  as N
import           Data.Text           (Text)
import qualified Data.Text.IO        as IO
import           Data.Time
import           Salak
import           Salak.Internal
import qualified Salak.Trie          as TR
import           Toml                hiding (Key, TOML, Value)
import qualified Toml                as T


runSalakWithToml :: (MonadCatch m, MonadIO m) => FilePath -> RunSalakT m a -> m a
runSalakWithToml name = runSalakWith name TOML

-- | TOML notation for `loadToml`
data TOML = TOML

instance HasLoad TOML where
  loaders _ = (, loadToml) <$> ["toml", "tml"]

{-# INLINE toSs #-}
toSs :: T.Key -> Keys
toSs (T.Key ps) = fromKeys $ toS <$> N.toList ps

{-# INLINE toS #-}
toS :: Piece -> Key
toS = KT . unPiece

loadTOML :: Int -> T.TOML -> TraceSource -> TraceSource
loadTOML i T.TOML{..}
  = foldPairs       tomlPairs
  . foldTables      tomlTables
  . foldTableArrays tomlTableArrays
  where
    {-# INLINE foldToml #-}
    foldToml = flip . HM.foldlWithKey'
    {-# INLINE foldPairs #-}
    foldPairs       = foldToml (\s k v -> TR.modify' (insertAnyValue v) (toSs k) s)
    {-# INLINE foldTableArrays #-}
    foldTableArrays = foldToml (\s _ v -> foldArray (loadTOML i) (N.toList v) s)
    {-# INLINE foldTables #-}
    foldTables      = foldToml (\s _ v -> go v s)
    {-# INLINE go #-}
    go (Leaf   k   toml)  = TR.modify' (loadTOML i toml)  (toSs k)
    go (Branch k v tomap) = TR.modify' (foldTables tomap) (toSs k) . maybe id (loadTOML i) v
    {-# INLINE insertAnyValue #-}
    insertAnyValue :: AnyValue -> TraceSource -> TraceSource
    insertAnyValue (AnyValue (Array   b))             = foldArray (insertAnyValue . AnyValue) b
    insertAnyValue (AnyValue (Bool    b))             = setVal i (VB  b)
    insertAnyValue (AnyValue (Integer b))             = setVal i (VI $ fromIntegral b)
    insertAnyValue (AnyValue (Double  b))             = setVal i (VI $ realToFrac   b)
    insertAnyValue (AnyValue (Text    b))             = setVal i (VT  b)
    insertAnyValue (AnyValue (Local   b))             = setVal i (VLT b)
    insertAnyValue (AnyValue (Day     b))             = setVal i (VD  b)
    insertAnyValue (AnyValue (Hours   b))             = setVal i (VH  b)
    insertAnyValue (AnyValue (Zoned   b))             = setVal i (VU  $ zonedTimeToUTC b)
    {-# INLINE foldArray #-}
    foldArray :: (a -> TraceSource -> TraceSource) -> [a] -> TraceSource -> TraceSource
    foldArray f = flip (foldl' (\t (j,a) -> TR.modify (KI j) (f a) t)) . zip [0..]

newtype TomlException = TomlException Text deriving Show

instance Exception TomlException

-- | Load Toml
loadToml :: FilePath -> LoadSalak ()
loadToml file = loadTrie True file $ \i -> do
  re <- T.parse <$> IO.readFile file
  case re of
      Left  (T.ParseException e) -> throwIO (TomlException e)
      Right a                    -> return  (loadTOML i a TR.empty)