{-# LANGUAGE DeriveAnyClass             #-}
{-# LANGUAGE DeriveGeneric              #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE DerivingVia                #-}
{-# LANGUAGE MultiWayIf                 #-}
{-# LANGUAGE OverloadedStrings          #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE TupleSections              #-}

module HS.Cfg.CfgFile where

import           Data.Default
import           Data.Text(Text)
import qualified Data.Text.IO         as T
import           Fmt
import           System.Directory
import           System.FilePath
import           System.IO
import           Text.Enum.Text


-- | enumeration for the @hs@ configuration files
data CfgFile
  = CF_managers
  | CF_mode
  | CF_compiler
  deriving stock    (CfgFile
CfgFile -> CfgFile -> Bounded CfgFile
forall a. a -> a -> Bounded a
maxBound :: CfgFile
$cmaxBound :: CfgFile
minBound :: CfgFile
$cminBound :: CfgFile
Bounded,Int -> CfgFile
CfgFile -> Int
CfgFile -> [CfgFile]
CfgFile -> CfgFile
CfgFile -> CfgFile -> [CfgFile]
CfgFile -> CfgFile -> CfgFile -> [CfgFile]
(CfgFile -> CfgFile)
-> (CfgFile -> CfgFile)
-> (Int -> CfgFile)
-> (CfgFile -> Int)
-> (CfgFile -> [CfgFile])
-> (CfgFile -> CfgFile -> [CfgFile])
-> (CfgFile -> CfgFile -> [CfgFile])
-> (CfgFile -> CfgFile -> CfgFile -> [CfgFile])
-> Enum CfgFile
forall a.
(a -> a)
-> (a -> a)
-> (Int -> a)
-> (a -> Int)
-> (a -> [a])
-> (a -> a -> [a])
-> (a -> a -> [a])
-> (a -> a -> a -> [a])
-> Enum a
enumFromThenTo :: CfgFile -> CfgFile -> CfgFile -> [CfgFile]
$cenumFromThenTo :: CfgFile -> CfgFile -> CfgFile -> [CfgFile]
enumFromTo :: CfgFile -> CfgFile -> [CfgFile]
$cenumFromTo :: CfgFile -> CfgFile -> [CfgFile]
enumFromThen :: CfgFile -> CfgFile -> [CfgFile]
$cenumFromThen :: CfgFile -> CfgFile -> [CfgFile]
enumFrom :: CfgFile -> [CfgFile]
$cenumFrom :: CfgFile -> [CfgFile]
fromEnum :: CfgFile -> Int
$cfromEnum :: CfgFile -> Int
toEnum :: Int -> CfgFile
$ctoEnum :: Int -> CfgFile
pred :: CfgFile -> CfgFile
$cpred :: CfgFile -> CfgFile
succ :: CfgFile -> CfgFile
$csucc :: CfgFile -> CfgFile
Enum,CfgFile -> CfgFile -> Bool
(CfgFile -> CfgFile -> Bool)
-> (CfgFile -> CfgFile -> Bool) -> Eq CfgFile
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CfgFile -> CfgFile -> Bool
$c/= :: CfgFile -> CfgFile -> Bool
== :: CfgFile -> CfgFile -> Bool
$c== :: CfgFile -> CfgFile -> Bool
Eq,Eq CfgFile
Eq CfgFile
-> (CfgFile -> CfgFile -> Ordering)
-> (CfgFile -> CfgFile -> Bool)
-> (CfgFile -> CfgFile -> Bool)
-> (CfgFile -> CfgFile -> Bool)
-> (CfgFile -> CfgFile -> Bool)
-> (CfgFile -> CfgFile -> CfgFile)
-> (CfgFile -> CfgFile -> CfgFile)
-> Ord CfgFile
CfgFile -> CfgFile -> Bool
CfgFile -> CfgFile -> Ordering
CfgFile -> CfgFile -> CfgFile
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: CfgFile -> CfgFile -> CfgFile
$cmin :: CfgFile -> CfgFile -> CfgFile
max :: CfgFile -> CfgFile -> CfgFile
$cmax :: CfgFile -> CfgFile -> CfgFile
>= :: CfgFile -> CfgFile -> Bool
$c>= :: CfgFile -> CfgFile -> Bool
> :: CfgFile -> CfgFile -> Bool
$c> :: CfgFile -> CfgFile -> Bool
<= :: CfgFile -> CfgFile -> Bool
$c<= :: CfgFile -> CfgFile -> Bool
< :: CfgFile -> CfgFile -> Bool
$c< :: CfgFile -> CfgFile -> Bool
compare :: CfgFile -> CfgFile -> Ordering
$ccompare :: CfgFile -> CfgFile -> Ordering
$cp1Ord :: Eq CfgFile
Ord,Int -> CfgFile -> ShowS
[CfgFile] -> ShowS
CfgFile -> String
(Int -> CfgFile -> ShowS)
-> (CfgFile -> String) -> ([CfgFile] -> ShowS) -> Show CfgFile
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CfgFile] -> ShowS
$cshowList :: [CfgFile] -> ShowS
show :: CfgFile -> String
$cshow :: CfgFile -> String
showsPrec :: Int -> CfgFile -> ShowS
$cshowsPrec :: Int -> CfgFile -> ShowS
Show)
  deriving anyclass (Bounded CfgFile
Enum CfgFile
Eq CfgFile
Ord CfgFile
Show CfgFile
TextParsable CfgFile
Buildable CfgFile
Int -> CfgFile -> Int
Text -> Possibly CfgFile
Buildable CfgFile
-> Bounded CfgFile
-> Enum CfgFile
-> Eq CfgFile
-> Ord CfgFile
-> Show CfgFile
-> TextParsable CfgFile
-> (CfgFile -> EnumTextConfig)
-> (CfgFile -> Text)
-> (CfgFile -> Builder)
-> (Text -> Possibly CfgFile)
-> (CfgFile -> ByteString)
-> (forall (m :: * -> *). MonadFail m => ByteString -> m CfgFile)
-> (Int -> CfgFile -> Int)
-> EnumText CfgFile
CfgFile -> ByteString
CfgFile -> Builder
CfgFile -> Text
CfgFile -> EnumTextConfig
forall e.
Buildable e
-> Bounded e
-> Enum e
-> Eq e
-> Ord e
-> Show e
-> TextParsable e
-> (e -> EnumTextConfig)
-> (e -> Text)
-> (e -> Builder)
-> (Text -> Possibly e)
-> (e -> ByteString)
-> (forall (m :: * -> *). MonadFail m => ByteString -> m e)
-> (Int -> e -> Int)
-> EnumText e
forall (m :: * -> *). MonadFail m => ByteString -> m CfgFile
hashWithSaltEnumText :: Int -> CfgFile -> Int
$chashWithSaltEnumText :: Int -> CfgFile -> Int
fromFieldEnumText_ :: ByteString -> m CfgFile
$cfromFieldEnumText_ :: forall (m :: * -> *). MonadFail m => ByteString -> m CfgFile
toFieldEnumText :: CfgFile -> ByteString
$ctoFieldEnumText :: CfgFile -> ByteString
parseEnumText :: Text -> Possibly CfgFile
$cparseEnumText :: Text -> Possibly CfgFile
buildEnumText :: CfgFile -> Builder
$cbuildEnumText :: CfgFile -> Builder
renderEnumText :: CfgFile -> Text
$crenderEnumText :: CfgFile -> Text
configEnumText :: CfgFile -> EnumTextConfig
$cconfigEnumText :: CfgFile -> EnumTextConfig
$cp7EnumText :: TextParsable CfgFile
$cp6EnumText :: Show CfgFile
$cp5EnumText :: Ord CfgFile
$cp4EnumText :: Eq CfgFile
$cp3EnumText :: Enum CfgFile
$cp2EnumText :: Bounded CfgFile
$cp1EnumText :: Buildable CfgFile
EnumText)
  deriving (CfgFile -> Builder
(CfgFile -> Builder) -> Buildable CfgFile
forall p. (p -> Builder) -> Buildable p
build :: CfgFile -> Builder
$cbuild :: CfgFile -> Builder
Buildable,Text -> Possibly CfgFile
(Text -> Possibly CfgFile) -> TextParsable CfgFile
forall a. (Text -> Possibly a) -> TextParsable a
parseText :: Text -> Possibly CfgFile
$cparseText :: Text -> Possibly CfgFile
TextParsable) via UsingEnumText CfgFile

-- | load a configuration file
load :: forall a . (Default a,TextParsable a) => CfgFile -> IO a
load :: CfgFile -> IO a
load CfgFile
cf = String -> IO a
rd (String -> IO a) -> IO String -> IO a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< CfgFile -> IO String
cfgFile CfgFile
cf
  where
    rd :: FilePath -> IO a
    rd :: String -> IO a
rd String
fp = do
      Bool
yup <- String -> IO Bool
doesFileExist String
fp
      if | Bool
yup       -> String -> Text -> IO a
prs String
fp (Text -> IO a) -> IO Text -> IO a
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< String -> IO Text
T.readFile String
fp
         | Bool
otherwise -> a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
forall a. Default a => a
def

    prs :: FilePath -> Text -> IO a
    prs :: String -> Text -> IO a
prs String
fp = (String -> IO a) -> (a -> IO a) -> Either String a -> IO a
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> String -> IO a
oops String
fp) a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String a -> IO a)
-> (Text -> Either String a) -> Text -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Either String a
forall a. TextParsable a => Text -> Possibly a
parseText

    oops :: FilePath -> String -> IO a
    oops :: String -> String -> IO a
oops String
fp String
msg = do
      Handle -> String -> IO ()
hPutStr Handle
stderr (String -> IO ()) -> String -> IO ()
forall a b. (a -> b) -> a -> b
$
        Builder
"hs: warning: failed to parse "Builder -> Builder -> String
forall b. FromBuilder b => Builder -> Builder -> b
+||String
fpString -> Builder -> Builder
forall a b. (Show a, FromBuilder b) => a -> Builder -> b
||+Builder
" with "Builder -> Builder -> Builder
forall b. FromBuilder b => Builder -> Builder -> b
+||String
msgString -> Builder -> Builder
forall a b. (Show a, FromBuilder b) => a -> Builder -> b
||+Builder
"; using default"
      a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return a
forall a. Default a => a
def

-- | save a configuration file
save :: Buildable a => CfgFile -> a -> IO ()
save :: CfgFile -> a -> IO ()
save CfgFile
cf a
x = (String -> Text -> IO ()) -> Text -> String -> IO ()
forall a b c. (a -> b -> c) -> b -> a -> c
flip String -> Text -> IO ()
T.writeFile (Builder -> Text
forall b. FromBuilder b => Builder -> b
fmt (Builder -> Text) -> Builder -> Text
forall a b. (a -> b) -> a -> b
$ a -> Builder
forall p. Buildable p => p -> Builder
build a
x) (String -> IO ()) -> IO String -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< CfgFile -> IO String
cfgFile CfgFile
cf

-- | generate the 'FilePath' for a 'CfgFile'
cfgFile :: CfgFile -> IO FilePath
cfgFile :: CfgFile -> IO String
cfgFile CfgFile
cf = (String -> ShowS
</> (Builder -> String
forall b. FromBuilder b => Builder -> b
fmt (Builder -> String) -> Builder -> String
forall a b. (a -> b) -> a -> b
$ CfgFile -> Builder
forall p. Buildable p => p -> Builder
build CfgFile
cf)) ShowS -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO String
dotHs

-- | generate the @hs@ configuration directory 'FilePath'
dotHs :: IO FilePath
dotHs :: IO String
dotHs = do
    String
dhs <- (String -> ShowS
</> String
".hs") ShowS -> IO String -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO String
getHomeDirectory
    String -> () -> String
forall a b. a -> b -> a
const String
dhs (() -> String) -> IO () -> IO String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Bool -> String -> IO ()
createDirectoryIfMissing Bool
True String
dhs