packages feed

headroom-0.1.0.0: src/Headroom/Types/Utils.hs

{-|
Module      : Headroom.Types.Utils
Description : tilities related to data types
Copyright   : (c) 2019-2020 Vaclav Svejcar
License     : BSD-3
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

Utilities related to data types.
-}
{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Headroom.Types.Utils
  ( allValues
  , customOptions
  , dropFieldPrefix
  , readEnumCI
  , symbolCase
  )
where

import           Data.Aeson                     ( Options
                                                , defaultOptions
                                                , fieldLabelModifier
                                                )
import           RIO
import qualified RIO.Char                      as C
import qualified RIO.List                      as L
import           Text.Read                      ( ReadS )


-- | Returns all values of enum.
allValues :: (Bounded a, Enum a) => [a]
allValues = [minBound ..]

-- | Custom /Aeson/ options.
customOptions :: Options
customOptions =
  defaultOptions { fieldLabelModifier = symbolCase '-' . dropFieldPrefix }

-- | Drops prefix from camel-case text.
--
-- >>> dropFieldPrefix "xxHelloWorld"
-- "helloWorld"
dropFieldPrefix :: String -> String
dropFieldPrefix = \case
  (x : n : xs) | C.isUpper x && C.isUpper n -> x : n : xs
  (x : n : xs) | C.isUpper x -> C.toLower x : n : xs
  (_ : xs)                   -> dropFieldPrefix xs
  []                         -> []

-- | Parses enum value from its string representation.
readEnumCI :: (Bounded a, Enum a, Show a) => ReadS a
readEnumCI str =
  let textRepr = fmap C.toLower . show
      result   = L.find (\item -> textRepr item == fmap C.toLower str) allValues
  in  maybe [] (\item -> [(item, "")]) result

-- | Transforms camel-case text into text cased with given symbol.
--
-- >>> symbolCase '-' "fooBar"
-- "foo-bar"
symbolCase :: Char   -- ^ word separator symbol
           -> String -- ^ input text
           -> String -- ^ processed text
symbolCase sym = \case
  [] -> []
  (x : xs) | C.isUpper x -> sym : C.toLower x : symbolCase sym xs
           | otherwise   -> x : symbolCase sym xs