{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Summoner.Config
( ConfigP (..)
, PartialConfig
, Config
, configT
, defaultConfig
, finalise
, loadFileConfig
) where
import Data.List (lookup)
import Generics.Deriving.Monoid (GMonoid, gmemptydefault)
import Generics.Deriving.Semigroup (GSemigroup, gsappenddefault)
import Toml (BiMap (..), Key, TomlCodec, (.=))
import Summoner.Decision (Decision (..))
import Summoner.GhcVer (GhcVer (..), parseGhcVer, showGhcVer)
import Summoner.License (LicenseName (..), parseLicenseName)
import Summoner.Settings (CustomPrelude (..), customPreludeT)
import Summoner.Source (Source, sourceT)
import qualified Toml
data Phase = Partial | Final
data ConfigP (p :: Phase) = Config
{ cOwner :: p :- Text
, cFullName :: p :- Text
, cEmail :: p :- Text
, cLicense :: p :- LicenseName
, cGhcVer :: p :- [GhcVer]
, cCabal :: Decision
, cStack :: Decision
, cGitHub :: Decision
, cTravis :: Decision
, cAppVey :: Decision
, cPrivate :: Decision
, cLib :: Decision
, cExe :: Decision
, cTest :: Decision
, cBench :: Decision
, cPrelude :: Last CustomPrelude
, cExtensions :: [Text]
, cWarnings :: [Text]
, cStylish :: Last Source
, cContributing :: Last Source
} deriving (Generic)
deriving instance
( GSemigroup (p :- Text)
, GSemigroup (p :- LicenseName)
, GSemigroup (p :- [GhcVer])
) => GSemigroup (ConfigP p)
deriving instance
( GMonoid (p :- Text)
, GMonoid (p :- LicenseName)
, GMonoid (p :- [GhcVer])
) => GMonoid (ConfigP p)
deriving instance
( Eq (p :- Text)
, Eq (p :- LicenseName)
, Eq (p :- [GhcVer])
) => Eq (ConfigP p)
deriving instance
( Show (p :- Text)
, Show (p :- LicenseName)
, Show (p :- [GhcVer])
) => Show (ConfigP p)
infixl 3 :-
type family phase :- field where
'Partial :- field = Last field
'Final :- field = field
type PartialConfig = ConfigP 'Partial
type Config = ConfigP 'Final
instance Semigroup PartialConfig where
(<>) = gsappenddefault
instance Monoid PartialConfig where
mempty = gmemptydefault
mappend = (<>)
defaultConfig :: PartialConfig
defaultConfig = Config
{ cOwner = Last (Just "kowainik")
, cFullName = Last (Just "Kowainik")
, cEmail = Last (Just "xrom.xkov@gmail.com")
, cLicense = Last (Just MIT)
, cGhcVer = Last (Just [])
, cCabal = Idk
, cStack = Idk
, cGitHub = Idk
, cTravis = Idk
, cAppVey = Idk
, cPrivate = Idk
, cLib = Idk
, cExe = Idk
, cTest = Idk
, cBench = Idk
, cPrelude = Last Nothing
, cExtensions = []
, cWarnings = []
, cStylish = Last Nothing
, cContributing = Last Nothing
}
configT :: TomlCodec PartialConfig
configT = Config
<$> lastT Toml.text "owner" .= cOwner
<*> lastT Toml.text "fullName" .= cFullName
<*> lastT Toml.text "email" .= cEmail
<*> lastT license "license" .= cLicense
<*> lastT ghcVerArr "ghcVersions" .= cGhcVer
<*> decision "cabal" .= cCabal
<*> decision "stack" .= cStack
<*> decision "github" .= cGitHub
<*> decision "travis" .= cTravis
<*> decision "appveyor" .= cAppVey
<*> decision "private" .= cPrivate
<*> decision "lib" .= cLib
<*> decision "exe" .= cExe
<*> decision "test" .= cTest
<*> decision "bench" .= cBench
<*> lastT preludeT "prelude" .= cPrelude
<*> textArr "extensions" .= cExtensions
<*> textArr "warnings" .= cWarnings
<*> lastT sourceT "stylish" .= cStylish
<*> lastT sourceT "contributing" .= cContributing
where
lastT :: (Key -> TomlCodec a) -> Key -> TomlCodec (Last a)
lastT codec = Toml.dimap getLast Last . Toml.dioptional . codec
_GhcVer :: BiMap GhcVer Toml.AnyValue
_GhcVer = Toml._TextBy showGhcVer parseGhcVer
ghcVerArr :: Key -> TomlCodec [GhcVer]
ghcVerArr = Toml.arrayOf _GhcVer
license :: Key -> TomlCodec LicenseName
license = Toml.mdimap show parseLicenseName . Toml.text
textArr :: Key -> TomlCodec [Text]
textArr = Toml.dimap Just maybeToMonoid . Toml.dioptional . Toml.arrayOf Toml._Text
decision :: Key -> TomlCodec Decision
decision = Toml.dimap fromDecision toDecision . Toml.dioptional . Toml.bool
decisionMaybe :: [(Decision, Maybe Bool)]
decisionMaybe = [ (Idk, Nothing)
, (Yes, Just True)
, (Nop, Just False)
]
fromDecision :: Decision -> Maybe Bool
fromDecision d = join $ lookup d decisionMaybe
toDecision :: Maybe Bool -> Decision
toDecision m = fromMaybe (error "Impossible") $ lookup m $ map swap decisionMaybe
preludeT :: Key -> TomlCodec CustomPrelude
preludeT = Toml.table customPreludeT
finalise :: PartialConfig -> Validation [Text] Config
finalise Config{..} = Config
<$> fin "owner" cOwner
<*> fin "fullName" cFullName
<*> fin "email" cEmail
<*> fin "license" cLicense
<*> fin "ghcVersions" cGhcVer
<*> pure cCabal
<*> pure cStack
<*> pure cGitHub
<*> pure cTravis
<*> pure cAppVey
<*> pure cPrivate
<*> pure cLib
<*> pure cExe
<*> pure cTest
<*> pure cBench
<*> pure cPrelude
<*> pure cExtensions
<*> pure cWarnings
<*> pure cStylish
<*> pure cContributing
where
fin name = maybe (Failure ["Missing field: " <> name]) Success . getLast
loadFileConfig :: MonadIO m => FilePath -> m PartialConfig
loadFileConfig = Toml.decodeFile configT