module Strelka.ParamsParsing.Value where import Strelka.Prelude hiding (maybe, list) import qualified Data.Attoparsec.Text as G import qualified Data.Text as E import qualified Attoparsec.Data as B {-| A parser of a parameter value. -} newtype Value a = Value (ReaderT [Text] (Except Text) a) deriving (Functor, Applicative, Alternative) {-| Lifts an Attoparsec parser into Value. -} {-# INLINE parser #-} parser :: G.Parser a -> Value a parser parser = matcher (first fromString . G.parseOnly finishedParser) where finishedParser = parser <* (G.endOfInput <|> fail "Didn't parse the whole data") {-| Lifts a text-matching function into Value. -} {-# INLINE matcher #-} matcher :: (Text -> Either Text a) -> Value a matcher matcher = Value (ReaderT (except . join . liftM matcher . head)) where head = \case x : _ -> Right x _ -> Left ("Not a single value is specified") {-# INLINE text #-} text :: Value Text text = matcher Right {-# INLINE string #-} string :: Value String string = matcher (Right . E.unpack) {-| Lifts a single value parser to the parser of a list of values. Useful for decoding lists of values of the same name. E.g., it'll work for the both following cases: > ?param[]=1¶m[]=2 > ?param=1¶m=2 In both cases the name of the parameter to look up will be \"param\". -} {-# INLINE list #-} list :: Value a -> Value [a] list (Value (ReaderT singleValueFn)) = Value (ReaderT (traverse (singleValueFn . pure))) {-| Lifts a single value parser to the parser of a possibly specified value. It's useful for decoding the difference between the following two cases: > ?param=value > ?param -} {-# INLINE maybe #-} maybe :: Value a -> Value (Maybe a) maybe (Value (ReaderT singleValueFn)) = Value (ReaderT (traverse (singleValueFn . pure) . listToMaybe)) {-# INLINE bool #-} bool :: Value Bool bool = fromMaybe False <$> maybe (parser B.lenientParser)