{-|
Module      : Headroom.Command.Readers
Description : Custom readers for /optparse-applicative/ library
Copyright   : (c) 2019-2020 Vaclav Svejcar
License     : BSD-3
Maintainer  : vaclav.svejcar@gmail.com
Stability   : experimental
Portability : POSIX

This module contains custom readers required by the /optparse-applicative/
library to parse data types such as 'LicenseType' or 'FileType'.
-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}
module Headroom.Command.Readers
  ( licenseReader
  , licenseTypeReader
  , parseLicenseAndFileType
  )
where

import           Data.Either.Combinators        ( maybeToRight )
import           Headroom.Types                 ( FileType
                                                , LicenseType
                                                )
import           Headroom.Types.EnumExtra       ( EnumExtra(..) )
import           Options.Applicative
import           RIO
import qualified RIO.Text                      as T
import qualified RIO.Text.Partial              as TP



-- | Reader for tuple of 'LicenseType' and 'FileType'.
licenseReader :: ReadM (LicenseType, FileType)
licenseReader :: ReadM (LicenseType, FileType)
licenseReader = (String -> Either String (LicenseType, FileType))
-> ReadM (LicenseType, FileType)
forall a. (String -> Either String a) -> ReadM a
eitherReader String -> Either String (LicenseType, FileType)
parseLicense
 where
  parseLicense :: String -> Either String (LicenseType, FileType)
parseLicense raw :: String
raw = String
-> Maybe (LicenseType, FileType)
-> Either String (LicenseType, FileType)
forall b a. b -> Maybe a -> Either b a
maybeToRight String
errMsg (Text -> Maybe (LicenseType, FileType)
parseLicenseAndFileType (Text -> Maybe (LicenseType, FileType))
-> Text -> Maybe (LicenseType, FileType)
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
raw)
  errMsg :: String
errMsg = Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
    [ "invalid license/file type, must be in format 'licenseType:fileType' "
    , "(e.g. bsd3:haskell)"
    , "\nAvailable license types: "
    , Text -> Text
T.toLower (EnumExtra LicenseType => Text
forall a. EnumExtra a => Text
allValuesToText @LicenseType)
    , "\nAvailable file types: "
    , Text -> Text
T.toLower (EnumExtra FileType => Text
forall a. EnumExtra a => Text
allValuesToText @FileType)
    ]


-- | Reader for 'LicenseType'.
licenseTypeReader :: ReadM LicenseType
licenseTypeReader :: ReadM LicenseType
licenseTypeReader = (String -> Either String LicenseType) -> ReadM LicenseType
forall a. (String -> Either String a) -> ReadM a
eitherReader String -> Either String LicenseType
forall a. EnumExtra a => String -> Either String a
parseLicenseType
 where
  parseLicenseType :: String -> Either String a
parseLicenseType raw :: String
raw = String -> Maybe a -> Either String a
forall b a. b -> Maybe a -> Either b a
maybeToRight String
errMsg (Text -> Maybe a
forall a. EnumExtra a => Text -> Maybe a
textToEnum (Text -> Maybe a) -> Text -> Maybe a
forall a b. (a -> b) -> a -> b
$ String -> Text
T.pack String
raw)
  errMsg :: String
errMsg = Text -> String
T.unpack (Text -> String) -> Text -> String
forall a b. (a -> b) -> a -> b
$ [Text] -> Text
forall a. Monoid a => [a] -> a
mconcat
    [ "invalid license type, available options: "
    , Text -> Text
T.toLower (EnumExtra LicenseType => Text
forall a. EnumExtra a => Text
allValuesToText @LicenseType)
    ]


-- | Parses 'LicenseType' and 'FileType' from the input string,
-- formatted as @licenseType:fileType@.
--
-- >>> parseLicenseAndFileType "bsd3:haskell"
-- Just (BSD3,Haskell)
parseLicenseAndFileType :: Text -> Maybe (LicenseType, FileType)
parseLicenseAndFileType :: Text -> Maybe (LicenseType, FileType)
parseLicenseAndFileType raw :: Text
raw
  | [rawLicenseType :: Text
rawLicenseType, rawFileType :: Text
rawFileType] <- Text -> Text -> [Text]
TP.splitOn ":" Text
raw = do
    LicenseType
licenseType <- Text -> Maybe LicenseType
forall a. EnumExtra a => Text -> Maybe a
textToEnum Text
rawLicenseType
    FileType
fileType    <- Text -> Maybe FileType
forall a. EnumExtra a => Text -> Maybe a
textToEnum Text
rawFileType
    (LicenseType, FileType) -> Maybe (LicenseType, FileType)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (LicenseType
licenseType, FileType
fileType)
  | Bool
otherwise = Maybe (LicenseType, FileType)
forall a. Maybe a
Nothing