module Data.HOCON
  ( Config(..)
  , getNode
  , hasPath
  , getString
  , getNumber
  , getList
  , getBool
  , isNull
  , getConfig
  , pretty
  )
where

import Data.List (find)
import Data.List.Split (splitOn)
import Data.Map (Map)
import Data.Maybe (isJust)
import Data.String.Utils (join)

data Config =
  HOCONNode (Map String Config) |
  HOCONString String |
  HOCONNumber Double |
  HOCONList [Config] |
  HOCONBool Bool |
  HOCONNull
  deriving (Int -> Config -> ShowS
[Config] -> ShowS
Config -> String
(Int -> Config -> ShowS)
-> (Config -> String) -> ([Config] -> ShowS) -> Show Config
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Config] -> ShowS
$cshowList :: [Config] -> ShowS
show :: Config -> String
$cshow :: Config -> String
showsPrec :: Int -> Config -> ShowS
$cshowsPrec :: Int -> Config -> ShowS
Show, Config -> Config -> Bool
(Config -> Config -> Bool)
-> (Config -> Config -> Bool) -> Eq Config
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Config -> Config -> Bool
$c/= :: Config -> Config -> Bool
== :: Config -> Config -> Bool
$c== :: Config -> Config -> Bool
Eq)

isNull :: Config -> Bool
isNull :: Config -> Bool
isNull Config
HOCONNull = Bool
True
isNull Config
_         = Bool
False

getNode :: String -> Config -> Maybe (Map String Config)
getNode :: String -> Config -> Maybe (Map String Config)
getNode String
key Config
conf = do
  Config
conf' <- String -> Config -> Maybe Config
getConfig String
key Config
conf
  case Config
conf' of
    HOCONNode Map String Config
nodes -> Map String Config -> Maybe (Map String Config)
forall (m :: * -> *) a. Monad m => a -> m a
return Map String Config
nodes
    Config
_               -> Maybe (Map String Config)
forall a. Maybe a
Nothing

getConfig :: String -> Config -> Maybe Config
getConfig :: String -> Config -> Maybe Config
getConfig String
key = [String] -> Config -> Maybe Config
go (String -> String -> [String]
forall a. Eq a => [a] -> [a] -> [[a]]
splitOn String
"." String
key)
 where
  go :: [String] -> Config -> Maybe Config
go [String
k] Config
conf = do
    Map String Config
nodes      <- Config -> Maybe (Map String Config)
getNodes Config
conf
    (String, Config)
nestedConf <- ((String, Config) -> Bool)
-> Map String Config -> Maybe (String, Config)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
k) (String -> Bool)
-> ((String, Config) -> String) -> (String, Config) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, Config) -> String
forall a b. (a, b) -> a
fst) Map String Config
nodes
    Config -> Maybe Config
forall (m :: * -> *) a. Monad m => a -> m a
return (Config -> Maybe Config) -> Config -> Maybe Config
forall a b. (a -> b) -> a -> b
$ (String, Config) -> Config
forall a b. (a, b) -> b
snd (String, Config)
nestedConf
  go (String
k : [String]
ks) Config
conf = do
    Map String Config
nodes      <- Config -> Maybe (Map String Config)
getNodes Config
conf
    (String, Config)
nestedConf <- ((String, Config) -> Bool)
-> Map String Config -> Maybe (String, Config)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((String -> String -> Bool
forall a. Eq a => a -> a -> Bool
== String
k) (String -> Bool)
-> ((String, Config) -> String) -> (String, Config) -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String, Config) -> String
forall a b. (a, b) -> a
fst) Map String Config
nodes
    [String] -> Config -> Maybe Config
go [String]
ks ((String, Config) -> Config
forall a b. (a, b) -> b
snd (String, Config)
nestedConf)

getNumber :: String -> Config -> Maybe Double
getNumber :: String -> Config -> Maybe Double
getNumber String
key Config
conf = do
  Config
conf' <- String -> Config -> Maybe Config
getConfig String
key Config
conf
  case Config
conf' of
    HOCONNumber Double
n -> Double -> Maybe Double
forall (m :: * -> *) a. Monad m => a -> m a
return Double
n
    Config
_             -> Maybe Double
forall a. Maybe a
Nothing

getString :: String -> Config -> Maybe String
getString :: String -> Config -> Maybe String
getString String
key Config
conf = do
  Config
conf' <- String -> Config -> Maybe Config
getConfig String
key Config
conf
  case Config
conf' of
    HOCONString String
s -> String -> Maybe String
forall (m :: * -> *) a. Monad m => a -> m a
return String
s
    Config
_             -> Maybe String
forall a. Maybe a
Nothing

getList :: String -> Config -> Maybe [Config]
getList :: String -> Config -> Maybe [Config]
getList String
key Config
conf = do
  Config
conf' <- String -> Config -> Maybe Config
getConfig String
key Config
conf
  case Config
conf' of
    HOCONList [Config]
l -> [Config] -> Maybe [Config]
forall (m :: * -> *) a. Monad m => a -> m a
return [Config]
l
    Config
_           -> Maybe [Config]
forall a. Maybe a
Nothing

getBool :: String -> Config -> Maybe Bool
getBool :: String -> Config -> Maybe Bool
getBool String
key Config
conf = do
  Config
conf' <- String -> Config -> Maybe Config
getConfig String
key Config
conf
  case Config
conf' of
    HOCONBool Bool
b -> Bool -> Maybe Bool
forall (m :: * -> *) a. Monad m => a -> m a
return Bool
b
    Config
_           -> Maybe Bool
forall a. Maybe a
Nothing

hasPath :: String -> Config -> Bool
hasPath :: String -> Config -> Bool
hasPath String
key = Maybe Config -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Config -> Bool)
-> (Config -> Maybe Config) -> Config -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Config -> Maybe Config
getConfig String
key

getNodes :: Config -> Maybe [(String, Config)]
getNodes :: Config -> Maybe (Map String Config)
getNodes (HOCONNode Map String Config
nodes) = Map String Config -> Maybe (Map String Config)
forall a. a -> Maybe a
Just Map String Config
nodes
getNodes Config
_                 = Maybe (Map String Config)
forall a. Maybe a
Nothing

pretty :: Config -> String
pretty :: Config -> String
pretty Config
HOCONNull           = String
"null"
pretty (HOCONNumber Double
n    ) = Double -> String
forall a. Show a => a -> String
show Double
n
pretty (HOCONString String
s    ) = String
"\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
s String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\""
pretty (HOCONBool   Bool
b    ) = if Bool
b then String
"true" else String
"false"
pretty (HOCONList   [Config]
xs   ) = String
"[" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
join String
"," ([String] -> String)
-> ([Config] -> [String]) -> [Config] -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Config -> String) -> [Config] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map Config -> String
pretty ([Config] -> String) -> [Config] -> String
forall a b. (a -> b) -> a -> b
$ [Config]
xs) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"]"
pretty (HOCONNode   Map String Config
nodes) = String
"{" String -> ShowS
forall a. [a] -> [a] -> [a]
++ (String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
join String
"," ([String] -> String)
-> (Map String Config -> [String]) -> Map String Config -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((String, Config) -> String) -> Map String Config -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (\(String
k, Config
node) -> String
"\"" String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
k String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"\":" String -> ShowS
forall a. [a] -> [a] -> [a]
++ Config -> String
pretty Config
node) (Map String Config -> String) -> Map String Config -> String
forall a b. (a -> b) -> a -> b
$ Map String Config
nodes) String -> ShowS
forall a. [a] -> [a] -> [a]
++ String
"}"