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 (..), NumberLiteral (..))
import Language.Fluent.Number (NumberOptions (..), Style (..))
import Language.Fluent.Number qualified as Number
import Language.Fluent.Pattern (Pattern (..), PatternElement (..))
import Language.Fluent.Pattern qualified as Pattern
import Language.Fluent.Time (Digits, TimeOptions (..))
import Language.Fluent.Value (SomeValue (..))
import Language.Fluent.Width (Width)
import Text.Read (readMaybe)
import Prelude

builtins
    :: HashMap
        Identifier
        ( [Pattern]
          -> HashMap Identifier SomeValue
          -> Either String Pattern
        )
builtins :: HashMap
  Identifier
  ([Pattern]
   -> HashMap Identifier SomeValue -> Either String Pattern)
builtins = [(Identifier,
  [Pattern]
  -> HashMap Identifier SomeValue -> Either String Pattern)]
-> HashMap
     Identifier
     ([Pattern]
      -> HashMap Identifier SomeValue -> 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 SomeValue -> Either String Pattern
number), (Text -> Identifier
Identifier Text
"DATETIME", [Pattern] -> HashMap Identifier SomeValue -> Either String Pattern
datetime)]

-- | @NUMBER($ratio, minimumFractionDigits: 2)@
number :: [Pattern] -> HashMap Identifier SomeValue -> Either String Pattern
number :: [Pattern] -> HashMap Identifier SomeValue -> Either String Pattern
number [Pattern]
positional HashMap Identifier SomeValue
args = do
    SomeValue
given <- String -> Maybe SomeValue -> Either String SomeValue
forall a b. a -> Maybe b -> Either a b
maybeToEither String
"NUMBER: expected one positional argument" (Maybe SomeValue -> Either String SomeValue)
-> Maybe SomeValue -> Either String SomeValue
forall a b. (a -> b) -> a -> b
$ [Pattern] -> Maybe SomeValue
lone [Pattern]
positional
    case SomeValue
given of
        NumberValue Scientific
value NumberOptions
opts -> do
            Endo NumberOptions
change <- (Identifier -> SomeValue -> Either String (Endo NumberOptions))
-> HashMap Identifier SomeValue
-> Either String (Endo NumberOptions)
forall options.
(Identifier -> SomeValue -> Either String (Endo options))
-> HashMap Identifier SomeValue -> Either String (Endo options)
changes Identifier -> SomeValue -> Either String (Endo NumberOptions)
numberOption HashMap Identifier SomeValue
args
            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
. SomeValue -> Pattern
forall v. Value v => v -> Pattern
Pattern.fromValue (SomeValue -> Pattern)
-> (NumberOptions -> SomeValue) -> NumberOptions -> Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Scientific -> NumberOptions -> SomeValue
NumberValue 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
        SomeValue
_ -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
"NUMBER: the positional argument is not a number"
  where
    numberOption :: Identifier -> SomeValue -> Either String (Endo NumberOptions)
    numberOption :: Identifier -> SomeValue -> Either String (Endo NumberOptions)
numberOption (Identifier Text
name) (SomeValue -> Text
asText -> Text
text) = case Text
name of
        Text
"style" -> case Text
text of
            Text
"decimal" -> (Style -> NumberOptions -> NumberOptions)
-> Either String Style -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Style
it NumberOptions
options -> NumberOptions
options{style = it}) (Either String Style -> Either String (Endo NumberOptions))
-> Either String Style -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Style -> Either String Style
forall a b. b -> Either a b
Right Style
Decimal
            Text
"percent" -> (Style -> NumberOptions -> NumberOptions)
-> Either String Style -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Style
it NumberOptions
options -> NumberOptions
options{style = it}) (Either String Style -> Either String (Endo NumberOptions))
-> Either String Style -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Style -> Either String Style
forall a b. b -> Either a b
Right Style
Percent
            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
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 style: " String -> String -> String
forall a. Semigroup a => a -> a -> a
<> Text -> String
Text.unpack Text
it
        Text
"currency" -> (Text -> NumberOptions -> NumberOptions)
-> Either String Text -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Text
it NumberOptions
options -> NumberOptions
options{style = Currency it}) (Either String Text -> Either String (Endo NumberOptions))
-> Either String Text -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Text
forall a b. b -> Either a b
Right Text
text
        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
"unit" -> (Text -> NumberOptions -> NumberOptions)
-> Either String Text -> Either String (Endo NumberOptions)
forall a options.
(a -> options -> options)
-> Either String a -> Either String (Endo options)
set (\Text
it NumberOptions
options -> NumberOptions
options{style = Unit it}) (Either String Text -> Either String (Endo NumberOptions))
-> Either String Text -> Either String (Endo NumberOptions)
forall a b. (a -> b) -> a -> b
$ Text -> Either String Text
forall a b. b -> Either a b
Right Text
text
        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 SomeValue -> Either String Pattern
datetime :: [Pattern] -> HashMap Identifier SomeValue -> Either String Pattern
datetime [Pattern]
positional HashMap Identifier SomeValue
args = do
    SomeValue
given <- String -> Maybe SomeValue -> Either String SomeValue
forall a b. a -> Maybe b -> Either a b
maybeToEither String
"DATETIME: expected one positional argument" (Maybe SomeValue -> Either String SomeValue)
-> Maybe SomeValue -> Either String SomeValue
forall a b. (a -> b) -> a -> b
$ [Pattern] -> Maybe SomeValue
lone [Pattern]
positional
    case SomeValue
given of
        TimeValue UTCTime
value TimeOptions
opts -> do
            Endo TimeOptions
change <- (Identifier -> SomeValue -> Either String (Endo TimeOptions))
-> HashMap Identifier SomeValue -> Either String (Endo TimeOptions)
forall options.
(Identifier -> SomeValue -> Either String (Endo options))
-> HashMap Identifier SomeValue -> Either String (Endo options)
changes Identifier -> SomeValue -> Either String (Endo TimeOptions)
timeOption HashMap Identifier SomeValue
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
. SomeValue -> Pattern
forall v. Value v => v -> Pattern
Pattern.fromValue (SomeValue -> Pattern)
-> (TimeOptions -> SomeValue) -> TimeOptions -> Pattern
forall b c a. (b -> c) -> (a -> b) -> a -> c
. UTCTime -> TimeOptions -> SomeValue
TimeValue 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
        SomeValue
_ -> String -> Either String Pattern
forall a b. a -> Either a b
Left String
"DATETIME: the positional argument is not a time"
  where
    timeOption :: Identifier -> SomeValue -> Either String (Endo TimeOptions)
    timeOption :: Identifier -> SomeValue -> Either String (Endo TimeOptions)
timeOption (Identifier Text
name) (SomeValue -> 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 -> SomeValue -> Either String (Endo options))
    -> HashMap Identifier SomeValue
    -> Either String (Endo options)
changes :: forall options.
(Identifier -> SomeValue -> Either String (Endo options))
-> HashMap Identifier SomeValue -> Either String (Endo options)
changes Identifier -> SomeValue -> 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 SomeValue -> Either String [Endo options])
-> HashMap Identifier SomeValue
-> Either String (Endo options)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((Identifier, SomeValue) -> Either String (Endo options))
-> [(Identifier, SomeValue)] -> 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 -> SomeValue -> Either String (Endo options))
-> (Identifier, SomeValue) -> Either String (Endo options)
forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry Identifier -> SomeValue -> Either String (Endo options)
option) ([(Identifier, SomeValue)] -> Either String [Endo options])
-> (HashMap Identifier SomeValue -> [(Identifier, SomeValue)])
-> HashMap Identifier SomeValue
-> Either String [Endo options]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. HashMap Identifier SomeValue -> [(Identifier, SomeValue)]
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 SomeValue
lone :: [Pattern] -> Maybe SomeValue
lone [Pattern (Value SomeValue
it :| [])] = SomeValue -> Maybe SomeValue
forall a. a -> Maybe a
Just SomeValue
it
lone [Pattern]
_ = Maybe SomeValue
forall a. Maybe a
Nothing

asText :: SomeValue -> Text
asText :: SomeValue -> Text
asText (StringValue Text
it) = Text
it
asText (NumberValue Scientific
it NumberOptions
options) | NumberLiteral Text
text <- Scientific -> NumberOptions -> NumberLiteral
Number.toLiteral Scientific
it NumberOptions
options = Text
text
asText (TimeValue UTCTime
it TimeOptions
_) = UTCTime -> Text
forall a. Show a => a -> Text
Text.show UTCTime
it
asText SomeValue{} = Text
""

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