module Language.Fluent.Function where

import Data.Either.Extra (maybeToEither)
import Data.HashMap.Strict (HashMap)
import Data.HashMap.Strict qualified as HashMap
import Data.List qualified as List
import Data.List.NonEmpty (NonEmpty (..))
import Data.Monoid (Endo (..))
import Data.Text (Text)
import Data.Text qualified as Text
import Language.Fluent.AST (Identifier (..))
import Language.Fluent.Number (NumberOptions (..), Style (..))
import Language.Fluent.Pattern (Pattern (..), PatternElement (..))
import Language.Fluent.Pattern qualified as Pattern
import Language.Fluent.Time (Digits, TimeOptions (..))
import Language.Fluent.Value (Value, asText)
import Language.Fluent.Value qualified as Value
import Language.Fluent.Width (Width)
import Text.Read (readMaybe)
import Prelude

builtins
    :: HashMap
        Identifier
        ( [Pattern]
          -> HashMap Identifier Value
          -> Either String Pattern
        )
builtins :: HashMap
  Identifier
  ([Pattern] -> HashMap Identifier Value -> Either String Pattern)
builtins = [(Identifier,
  [Pattern] -> HashMap Identifier Value -> Either String Pattern)]
-> HashMap
     Identifier
     ([Pattern] -> HashMap Identifier Value -> Either String Pattern)
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HashMap.fromList [(Text -> Identifier
Identifier Text
"NUMBER", [Pattern] -> HashMap Identifier Value -> Either String Pattern
number), (Text -> Identifier
Identifier Text
"DATETIME", [Pattern] -> HashMap Identifier Value -> Either String Pattern
datetime)]

-- | @NUMBER($ratio, minimumFractionDigits: 2)@
number :: [Pattern] -> HashMap Identifier Value -> Either String Pattern
number :: [Pattern] -> HashMap Identifier Value -> Either String Pattern
number [Pattern]
positional HashMap Identifier Value
args = do
    Value
given <- String -> Maybe Value -> Either String Value
forall a b. a -> Maybe b -> Either a b
maybeToEither String
"NUMBER: expected one positional argument" (Maybe Value -> Either String Value)
-> Maybe Value -> Either String Value
forall a b. (a -> b) -> a -> b
$ [Pattern] -> Maybe Value
lone [Pattern]
positional
    case Value
given of
        Value.Number Scientific
value NumberOptions
opts -> do
            Endo NumberOptions
change <- (Identifier -> Value -> Either String (Endo NumberOptions))
-> HashMap Identifier Value -> Either String (Endo NumberOptions)
forall options.
(Identifier -> Value -> Either String (Endo options))
-> HashMap Identifier Value -> Either String (Endo options)
changes Identifier -> Value -> Either String (Endo NumberOptions)
numberOption HashMap Identifier Value
args
            Style
style <- Style -> Either String Style
numberStyle NumberOptions
opts.style
            Pattern -> Either String Pattern
forall a. a -> Either String a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Pattern -> Either String Pattern)
-> (NumberOptions -> Pattern)
-> NumberOptions
-> Either String Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Pattern
forall v. ToValue v => v -> Pattern
Pattern.fromValue (Value -> Pattern)
-> (NumberOptions -> Value) -> NumberOptions -> Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> NumberOptions -> Value
Value.Number Scientific
value (NumberOptions -> Either String Pattern)
-> NumberOptions -> Either String Pattern
forall a b. (a -> b) -> a -> b
$ (Endo NumberOptions -> NumberOptions -> NumberOptions
forall a. Endo a -> a -> a
appEndo Endo NumberOptions
change NumberOptions
opts){style}
        Value.Invalid String
reason -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
reason
        Value
_ -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
"NUMBER: the positional argument is not a number"
  where
    option :: Text -> Maybe Text
option Text
name = Value -> Text
asText (Value -> Text) -> Maybe Value -> Maybe Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Identifier -> HashMap Identifier Value -> Maybe Value
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HashMap.lookup (Text -> Identifier
Identifier Text
name) HashMap Identifier Value
args

    numberStyle :: Style -> Either String Style
    numberStyle :: Style -> Either String Style
numberStyle Style
current = case (Text -> Maybe Text
option Text
"style", Text -> Maybe Text
option Text
"currency", Text -> Maybe Text
option Text
"unit") of
        (Just Text
"decimal", Maybe Text
_, Maybe Text
_) -> Style -> Either String Style
forall a b. b -> Either a b
Right Style
Decimal
        (Just Text
"percent", Maybe Text
_, Maybe Text
_) -> Style -> Either String Style
forall a b. b -> Either a b
Right Style
Percent
        (Just Text
"currency", Maybe Text
currency, Maybe Text
_) ->
            Either String Style
-> (Text -> Either String Style)
-> Maybe Text
-> Either String Style
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Either String Style
forall a b. a -> Either a b
Left String
"NUMBER: the currency style needs a currency") (Style -> Either String Style
forall a b. b -> Either a b
Right (Style -> Either String Style)
-> (Text -> Style) -> Text -> Either String Style
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Style
Currency) Maybe Text
currency
        (Just Text
"unit", Maybe Text
_, Maybe Text
unit) ->
            Either String Style
-> (Text -> Either String Style)
-> Maybe Text
-> Either String Style
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Either String Style
forall a b. a -> Either a b
Left String
"NUMBER: the unit style needs a unit") (Style -> Either String Style
forall a b. b -> Either a b
Right (Style -> Either String Style)
-> (Text -> Style) -> Text -> Either String Style
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Style
Unit) Maybe Text
unit
        (Just Text
it, Maybe Text
_, Maybe Text
_) -> String -> Either String Style
forall a b. a -> Either a b
Left (String -> Either String Style) -> String -> Either String Style
forall a b. (a -> b) -> a -> b
$ String
"NUMBER: no such style: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
it
        (Maybe Text
Nothing, Just Text
currency, Maybe Text
_) -> Style -> Either String Style
forall a b. b -> Either a b
Right (Style -> Either String Style) -> Style -> Either String Style
forall a b. (a -> b) -> a -> b
$ Text -> Style
Currency Text
currency
        (Maybe Text
Nothing, Maybe Text
Nothing, Just Text
unit) -> Style -> Either String Style
forall a b. b -> Either a b
Right (Style -> Either String Style) -> Style -> Either String Style
forall a b. (a -> b) -> a -> b
$ Text -> Style
Unit Text
unit
        (Maybe Text
Nothing, Maybe Text
Nothing, Maybe Text
Nothing) -> Style -> Either String Style
forall a b. b -> Either a b
Right Style
current

    numberOption :: Identifier -> Value -> Either String (Endo NumberOptions)
    numberOption :: Identifier -> Value -> Either String (Endo NumberOptions)
numberOption (Identifier Text
name) (Value -> Text
asText -> Text
text) = case Text
name of
        Text
"style" -> Endo NumberOptions -> Either String (Endo NumberOptions)
forall a b. b -> Either a b
Right Endo NumberOptions
forall a. Monoid a => a
mempty
        Text
"currency" -> Endo NumberOptions -> Either String (Endo NumberOptions)
forall a b. b -> Either a b
Right Endo NumberOptions
forall a. Monoid a => a
mempty
        Text
"unit" -> Endo NumberOptions -> Either String (Endo NumberOptions)
forall a b. b -> Either a b
Right Endo NumberOptions
forall a. Monoid a => a
mempty
        Text
"type" -> (Form -> NumberOptions -> NumberOptions)
-> Either String Form -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Form
it NumberOptions
options -> NumberOptions
options{form = it}) Either String Form
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"currencyDisplay" -> (CurrencyDisplay -> NumberOptions -> NumberOptions)
-> Either String CurrencyDisplay
-> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\CurrencyDisplay
it NumberOptions
options -> NumberOptions
options{currencyDisplay = it}) Either String CurrencyDisplay
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"unitDisplay" -> (Width -> NumberOptions -> NumberOptions)
-> Either String Width -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Width
it NumberOptions
options -> NumberOptions
options{unitDisplay = it}) Either String Width
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"useGrouping" -> (Bool -> NumberOptions -> NumberOptions)
-> Either String Bool -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Bool
it NumberOptions
options -> NumberOptions
options{useGrouping = it}) (Either String Bool -> Either String (Endo NumberOptions))
-> Either String Bool -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Bool
flag Text
text
        Text
"minimumIntegerDigits" ->
            (Int -> NumberOptions -> NumberOptions)
-> Either String Int -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Int
it NumberOptions
options -> NumberOptions
options{minimumIntegerDigits = it}) (Either String Int -> Either String (Endo NumberOptions))
-> Either String Int -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Int
count Text
text
        Text
"minimumFractionDigits" ->
            (Int -> NumberOptions -> NumberOptions)
-> Either String Int -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Int
it NumberOptions
options -> NumberOptions
options{minimumFractionDigits = it}) (Either String Int -> Either String (Endo NumberOptions))
-> Either String Int -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Int
count Text
text
        Text
"maximumFractionDigits" ->
            (Int -> NumberOptions -> NumberOptions)
-> Either String Int -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Int
it NumberOptions
options -> NumberOptions
options{maximumFractionDigits = Just it}) (Either String Int -> Either String (Endo NumberOptions))
-> Either String Int -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Int
count Text
text
        Text
"minimumSignificantDigits" ->
            (Int -> NumberOptions -> NumberOptions)
-> Either String Int -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Int
it NumberOptions
options -> NumberOptions
options{minimumSignificantDigits = Just it}) (Either String Int -> Either String (Endo NumberOptions))
-> Either String Int -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Int
count Text
text
        Text
"maximumSignificantDigits" ->
            (Int -> NumberOptions -> NumberOptions)
-> Either String Int -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Int
it NumberOptions
options -> NumberOptions
options{maximumSignificantDigits = Just it}) (Either String Int -> Either String (Endo NumberOptions))
-> Either String Int -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Int
count Text
text
        Text
it -> String -> Either String (Endo NumberOptions)
forall a b. a -> Either a b
Left (String -> Either String (Endo NumberOptions))
-> String -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ String
"NUMBER: no such option: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
it
      where
        parsed :: (Bounded a, Enum a, Read a, Show a) => Either String a
        parsed :: forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed = String -> Text -> Text -> Either String a
forall a.
(Bounded a, Enum a, Read a, Show a) =>
String -> Text -> Text -> Either String a
named String
"NUMBER" Text
name Text
text

    count :: Text -> Either String Int
    count :: Text -> Either String Int
count (Text -> String
Text.unpack -> String
it) = Either String Int
-> (Int -> Either String Int) -> Maybe Int -> Either String Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Either String Int
forall a b. a -> Either a b
Left (String -> Either String Int) -> String -> Either String Int
forall a b. (a -> b) -> a -> b
$ String
"not a count: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
it) Int -> Either String Int
forall a b. b -> Either a b
Right (Maybe Int -> Either String Int)
-> (String -> Maybe Int) -> String -> Either String Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe (String -> Either String Int) -> String -> Either String Int
forall a b. (a -> b) -> a -> b
$ String
it

-- | @DATETIME($date, month: "long")@
datetime :: [Pattern] -> HashMap Identifier Value -> Either String Pattern
datetime :: [Pattern] -> HashMap Identifier Value -> Either String Pattern
datetime [Pattern]
positional HashMap Identifier Value
args = do
    Value
given <- String -> Maybe Value -> Either String Value
forall a b. a -> Maybe b -> Either a b
maybeToEither String
"DATETIME: expected one positional argument" (Maybe Value -> Either String Value)
-> Maybe Value -> Either String Value
forall a b. (a -> b) -> a -> b
$ [Pattern] -> Maybe Value
lone [Pattern]
positional
    case Value
given of
        Value.Time UTCTime
value TimeOptions
opts -> do
            Endo TimeOptions
change <- (Identifier -> Value -> Either String (Endo TimeOptions))
-> HashMap Identifier Value -> Either String (Endo TimeOptions)
forall options.
(Identifier -> Value -> Either String (Endo options))
-> HashMap Identifier Value -> Either String (Endo options)
changes Identifier -> Value -> Either String (Endo TimeOptions)
timeOption HashMap Identifier Value
args
            Pattern -> Either String Pattern
forall a. a -> Either String a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Pattern -> Either String Pattern)
-> (TimeOptions -> Pattern) -> TimeOptions -> Either String Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> Pattern
forall v. ToValue v => v -> Pattern
Pattern.fromValue (Value -> Pattern)
-> (TimeOptions -> Value) -> TimeOptions -> Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> TimeOptions -> Value
Value.Time UTCTime
value (TimeOptions -> Either String Pattern)
-> TimeOptions -> Either String Pattern
forall a b. (a -> b) -> a -> b
$ Endo TimeOptions -> TimeOptions -> TimeOptions
forall a. Endo a -> a -> a
appEndo Endo TimeOptions
change TimeOptions
opts
        Value.Invalid String
reason -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
reason
        Value
_ -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
"DATETIME: the positional argument is not a time"
  where
    timeOption :: Identifier -> Value -> Either String (Endo TimeOptions)
    timeOption :: Identifier -> Value -> Either String (Endo TimeOptions)
timeOption (Identifier Text
name) (Value -> Text
asText -> Text
text) = case Text
name of
        Text
"timeZone" -> (Text -> TimeOptions -> TimeOptions)
-> Either String Text -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Text
it TimeOptions
options -> TimeOptions
options{timeZone = it}) (Either String Text -> Either String (Endo TimeOptions))
-> Either String Text -> Either String (Endo TimeOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Text
zone Text
text
        Text
"hour12" -> (Bool -> TimeOptions -> TimeOptions)
-> Either String Bool -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Bool
it TimeOptions
options -> TimeOptions
options{hour12 = Just it}) (Either String Bool -> Either String (Endo TimeOptions))
-> Either String Bool -> Either String (Endo TimeOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Bool
flag Text
text
        Text
"weekday" -> (Width -> TimeOptions -> TimeOptions)
-> Either String Width -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Width
it TimeOptions
options -> TimeOptions
options{weekday = Just it}) Either String Width
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"era" -> (Width -> TimeOptions -> TimeOptions)
-> Either String Width -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Width
it TimeOptions
options -> TimeOptions
options{era = Just it}) Either String Width
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"year" -> (Digits -> TimeOptions -> TimeOptions)
-> Either String Digits -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Digits
it TimeOptions
options -> TimeOptions
options{year = Just it}) Either String Digits
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"month" -> (Either Digits Width -> TimeOptions -> TimeOptions)
-> Either String (Either Digits Width)
-> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Either Digits Width
it TimeOptions
options -> TimeOptions
options{month = Just it}) Either String (Either Digits Width)
monthField
        Text
"day" -> (Digits -> TimeOptions -> TimeOptions)
-> Either String Digits -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Digits
it TimeOptions
options -> TimeOptions
options{day = Just it}) Either String Digits
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"hour" -> (Digits -> TimeOptions -> TimeOptions)
-> Either String Digits -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Digits
it TimeOptions
options -> TimeOptions
options{hour = Just it}) Either String Digits
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"minute" -> (Digits -> TimeOptions -> TimeOptions)
-> Either String Digits -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Digits
it TimeOptions
options -> TimeOptions
options{minute = Just it}) Either String Digits
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"second" -> (Digits -> TimeOptions -> TimeOptions)
-> Either String Digits -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Digits
it TimeOptions
options -> TimeOptions
options{second = Just it}) Either String Digits
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
"timeZoneName" -> (Width -> TimeOptions -> TimeOptions)
-> Either String Width -> Either String (Endo TimeOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Width
it TimeOptions
options -> TimeOptions
options{timeZoneName = Just it}) Either String Width
forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed
        Text
it -> String -> Either String (Endo TimeOptions)
forall a b. a -> Either a b
Left (String -> Either String (Endo TimeOptions))
-> String -> Either String (Endo TimeOptions)
forall a b. (a -> b) -> a -> b
$ String
"DATETIME: no such option: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
it
      where
        parsed :: (Bounded a, Enum a, Read a, Show a) => Either String a
        parsed :: forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed = String -> Text -> Text -> Either String a
forall a.
(Bounded a, Enum a, Read a, Show a) =>
String -> Text -> Text -> Either String a
named String
"DATETIME" Text
name Text
text
        monthField :: Either String (Either Digits Width)
monthField
            | Right Digits
digits <- forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed @Digits = Either Digits Width -> Either String (Either Digits Width)
forall a b. b -> Either a b
Right (Either Digits Width -> Either String (Either Digits Width))
-> Either Digits Width -> Either String (Either Digits Width)
forall a b. (a -> b) -> a -> b
$ Digits -> Either Digits Width
forall a b. a -> Either a b
Left Digits
digits
            | Right Width
width <- forall a. (Bounded a, Enum a, Read a, Show a) => Either String a
parsed @Width = Either Digits Width -> Either String (Either Digits Width)
forall a b. b -> Either a b
Right (Either Digits Width -> Either String (Either Digits Width))
-> Either Digits Width -> Either String (Either Digits Width)
forall a b. (a -> b) -> a -> b
$ Width -> Either Digits Width
forall a b. b -> Either a b
Right Width
width
            | Bool
otherwise = String -> Either String (Either Digits Width)
forall a b. a -> Either a b
Left (String -> Either String (Either Digits Width))
-> String -> Either String (Either Digits Width)
forall a b. (a -> b) -> a -> b
$ String
"DATETIME: no such month: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
text

    zone :: Text -> Either String Text
    zone :: Text -> Either String Text
zone Text
"" = String -> Either String Text
forall a b. a -> Either a b
Left String
"DATETIME: the timeZone names no zone"
    zone Text
it = Text -> Either String Text
forall a b. b -> Either a b
Right Text
it

changes
    :: (Identifier -> Value -> Either String (Endo options))
    -> HashMap Identifier Value
    -> Either String (Endo options)
changes :: forall options.
(Identifier -> Value -> Either String (Endo options))
-> HashMap Identifier Value -> Either String (Endo options)
changes Identifier -> Value -> Either String (Endo options)
option = ([Endo options] -> Endo options)
-> Either String [Endo options] -> Either String (Endo options)
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Endo options] -> Endo options
forall a. Monoid a => [a] -> a
mconcat (Either String [Endo options] -> Either String (Endo options))
-> (HashMap Identifier Value -> Either String [Endo options])
-> HashMap Identifier Value
-> Either String (Endo options)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Identifier, Value) -> Either String (Endo options))
-> [(Identifier, Value)] -> Either String [Endo options]
forall (t :: * -> *) (f :: * -> *) a b.
(Traversable t, Applicative f) =>
(a -> f b) -> t a -> f (t b)
forall (f :: * -> *) a b.
Applicative f =>
(a -> f b) -> [a] -> f [b]
traverse ((Identifier -> Value -> Either String (Endo options))
-> (Identifier, Value) -> Either String (Endo options)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Identifier -> Value -> Either String (Endo options)
option) ([(Identifier, Value)] -> Either String [Endo options])
-> (HashMap Identifier Value -> [(Identifier, Value)])
-> HashMap Identifier Value
-> Either String [Endo options]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Identifier Value -> [(Identifier, Value)]
forall k v. HashMap k v -> [(k, v)]
HashMap.toList

named :: forall a. (Bounded a, Enum a, Read a, Show a) => String -> Text -> Text -> Either String a
named :: forall a.
(Bounded a, Enum a, Read a, Show a) =>
String -> Text -> Text -> Either String a
named String
fn (Text -> String
Text.unpack -> String
what) (Text -> String
Text.unpack -> String
text) = String -> Maybe a -> Either String a
forall a b. a -> Maybe b -> Either a b
maybeToEither String
err (Maybe a -> Either String a) -> Maybe a -> Either String a
forall a b. (a -> b) -> a -> b
$ String -> Maybe a
forall a. Read a => String -> Maybe a
readMaybe String
text
  where
    err :: String
err =
        [String] -> String
forall a. Monoid a => [a] -> a
mconcat
            [ String
fn
            , String
": expected one of "
            , String -> [String] -> String
forall a. [a] -> [[a]] -> [a]
List.intercalate String
", " (a -> String
forall a. Show a => a -> String
show (a -> String) -> [a] -> [String]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [forall a. Bounded a => a
minBound @a .. a
forall a. Bounded a => a
maxBound])
            , String
" for "
            , String
what
            , String
", but got "
            , String
text
            ]

set :: (a -> options -> options) -> Either String a -> Either String (Endo options)
set :: forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set a -> options -> options
change = (a -> Endo options)
-> Either String a -> Either String (Endo options)
forall a b. (a -> b) -> Either String a -> Either String b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((a -> Endo options)
 -> Either String a -> Either String (Endo options))
-> (a -> Endo options)
-> Either String a
-> Either String (Endo options)
forall a b. (a -> b) -> a -> b
$ (options -> options) -> Endo options
forall a. (a -> a) -> Endo a
Endo ((options -> options) -> Endo options)
-> (a -> options -> options) -> a -> Endo options
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> options -> options
change

lone :: [Pattern] -> Maybe Value
lone :: [Pattern] -> Maybe Value
lone [Pattern (Value Value
it :| [])] = Value -> Maybe Value
forall a. a -> Maybe a
Just Value
it
lone [Pattern]
_ = Maybe Value
forall a. Maybe a
Nothing

flag :: Text -> Either String Bool
flag :: Text -> Either String Bool
flag (Text -> String
Text.unpack -> String
it)
    | String
it String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String
"true", String
"1"] = Bool -> Either String Bool
forall a b. b -> Either a b
Right Bool
True
    | String
it String -> [String] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [String
"false", String
"0"] = Bool -> Either String Bool
forall a b. b -> Either a b
Right Bool
False
    | Bool
otherwise = String -> Either String Bool
forall a b. a -> Either a b
Left (String -> Either String Bool) -> String -> Either String Bool
forall a b. (a -> b) -> a -> b
$ String
"not a flag: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
it