{-# LANGUAGE OverloadedStrings #-} -- | Various parsing and printing utilities module Language.Bitcoin.Utils ( parens, brackets, application, hex, comma, argList, alphanum, spacePadded, showText, applicationText, requiredContextValue, maybeFail, ) where import Control.Applicative ((<|>)) import Control.Monad (void) import Control.Monad.Trans.Class (lift) import Control.Monad.Trans.Except (Except, throwE) import Control.Monad.Trans.Reader (ReaderT, asks) import Data.Attoparsec.Text (Parser) import qualified Data.Attoparsec.Text as A import Data.ByteString (ByteString) import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Data.Text (Text, pack) import Haskoin.Util (decodeHex) parens :: Parser a -> Parser a parens p = A.char '(' >> p <* A.char ')' brackets :: Parser a -> Parser a brackets p = A.char '[' >> p <* A.char ']' application :: Text -> Parser a -> Parser a application fname p = A.string fname >> parens (spacePadded p) hex :: Parser ByteString hex = A.many1' hexChar >>= maybeFail "Invalid hex" id . decodeHex . pack where hexChar = A.satisfy $ A.inClass chars chars = ['0' .. '9'] <> ['a' .. 'f'] <> ['A' .. 'F'] -- | Allow for a leading comma comma :: Parser a -> Parser a comma p = spacePadded (A.char ',') >> p argList :: Parser a -> Parser [a] argList p = spacePadded p `A.sepBy` A.char ',' alphanum :: Parser Char alphanum = A.digit <|> A.letter spacePadded :: Parser a -> Parser a spacePadded p = spaces >> p <* spaces spaces :: Parser () spaces = void $ A.many' A.space showText :: Show a => a -> Text showText = pack . show applicationText :: Text -> Text -> Text applicationText f x = f <> "(" <> x <> ")" maybeFail :: String -> (a -> b) -> Maybe a -> Parser b maybeFail msg f = maybe (fail msg) (return . f) requiredContextValue :: (r -> Map Text c) -> e -> Text -> ReaderT r (Except e) c requiredContextValue f e name = asks (Map.lookup name . f) >>= maybe (lift $ throwE e) return