packages feed

hinit-0.2.1: src/Hinit/Template/Config.hs

{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TupleSections #-}

module Hinit.Template.Config where

import Control.Applicative
import Data.String.Interpolate
import Data.Text (Text, pack, unpack)
import Data.Tuple.Optics
import GHC.Generics
import Optics.Operators
import System.FilePath.Glob
  ( Pattern,
    compile,
    decompile,
    simplify,
  )
import Toml
import qualified Toml as T

data TemplateConfig = TemplateConfig
  { desc :: Maybe Text,
    tags :: [Text],
    ignores :: [Pattern],
    options :: [Option],
    optionals :: [OptionalIgnores]
  }
  deriving (Show, Eq, Generic)

pToTomlVal :: Pattern -> AnyValue
pToTomlVal p = AnyValue $ Text $ pack $ decompile p

tomlValToP :: AnyValue -> Either TomlBiMapError Pattern
tomlValToP (AnyValue v)
  | Text t <- v = Right $ simplify $ compile $ unpack t
  | otherwise = Left $ WrongValue $ MatchError TText (AnyValue v)

_Pattern :: TomlBiMap Pattern AnyValue
_Pattern = invert $ prism pToTomlVal tomlValToP

instance HasItemCodec Pattern where
  hasItemCodec = Left _Pattern

templateConfigCodec :: TomlCodec TemplateConfig
templateConfigCodec = genericCodec

data Option
  = BoolOpt
      { name :: Text,
        desc :: Maybe Text,
        defB :: Maybe Bool
      }
  | TextOpt
      { name :: Text,
        desc :: Maybe Text,
        defT :: Maybe Text
      }
  deriving (Show, Eq, Generic)

pair' :: TomlCodec a -> TomlCodec b -> TomlCodec c -> TomlCodec (a, b, c)
pair' a b c =
  (,,)
    <$> a .= (^. _1)
      <*> b .= (^. _2)
      <*> c .= (^. _3)

matchB :: Option -> Maybe (Text, Maybe Text, Bool)
matchB BoolOpt {..} = fmap (name,desc,) defB
matchB _ = Nothing

matchB' :: Option -> Maybe (Text, Maybe Text)
matchB' BoolOpt {..} = Just (name, desc)
matchB' _ = Nothing

matchT :: Option -> Maybe (Text, Maybe Text, Text)
matchT TextOpt {..} = fmap (name,desc,) defT
matchT _ = Nothing

matchT' :: Option -> Maybe (Text, Maybe Text)
matchT' TextOpt {..} = Just (name, desc)
matchT' _ = Nothing

descCodec :: TomlCodec (Maybe Text)
descCodec = dioptional (text "desc")

tyCodec :: Text -> TomlCodec Text
tyCodec ty = match _Ty "type"
  where
    _Ty = invert $ prism (AnyValue . T.Text) tyFromToml
    tyFromToml (AnyValue v)
      | T.Text t <- v =
        if t == ty
          then Right t
          else Left $ ArbitraryError [i|Expecting type #{ty}, got type #{t}|]
      | otherwise =
        Left $ WrongValue $ MatchError TText (AnyValue v)

optCodec :: TomlCodec Option
optCodec =
  dimatch matchB (\(a, b, c) -> BoolOpt a b (Just c)) bOptCodec
    <|> dimatch matchB' (\(a, b) -> BoolOpt a b Nothing) bOptCodec'
    <|> dimatch matchT (\(a, b, c) -> TextOpt a b (Just c)) tOptCodec
    <|> dimatch matchT' (\(a, b) -> BoolOpt a b Nothing) tOptCodec'
  where
    bOptCodec :: TomlCodec (Text, Maybe Text, Bool)
    bOptCodec = pair' (text "name") descCodec (bool "default")
    bOptCodec' :: TomlCodec (Text, Maybe Text)
    bOptCodec' =
      pair (text "name") descCodec
        <* tyCodec "bool" .= const "bool"
    tOptCodec :: TomlCodec (Text, Maybe Text, Text)
    tOptCodec =
      pair' (text "name") descCodec (text "default")
    tOptCodec' :: TomlCodec (Text, Maybe Text)
    tOptCodec' =
      pair (text "name") descCodec
        <* tyCodec "text" .= const "text"

instance HasItemCodec Option where
  hasItemCodec = Right optCodec

data OptionalIgnores = Optional
  { when :: Text,
    ignores :: [Pattern]
  }
  deriving (Show, Eq, Generic)

instance HasItemCodec OptionalIgnores where
  hasItemCodec = Right genericCodec