penny-lib-0.2.0.0: Penny/Copper/Payees.hs
-- | Payee parsers. There are two types of payee parsers:
--
-- Quoted payees. These allow the most latitude in the characters
-- allowed. They are surrounded by @<@ and @>@.
--
-- Unquoted payees. These are not surrounded by @<@ and @>@. Their
-- first character must be a letter or number.
module Penny.Copper.Payees (
-- * Parse any payee
payee
-- * Quoted payees
, quotedChar
, quotedPayee
-- * Unquoted payees
, unquotedFirstChar
, unquotedRestChars
, unquotedPayee
-- * Rendering
, smartRender
, quoteRender
) where
import Control.Applicative ((<$>), (<*>), (<|>))
import Data.Text (pack, Text, snoc, cons)
import qualified Data.Text as X
import Text.Parsec (char, satisfy, many, between, (<?>))
import Text.Parsec.Text ( Parser )
import Penny.Copper.Util (rangeLettersToSymbols, rangeLetters)
import qualified Penny.Lincoln.Bits as B
import Penny.Lincoln.TextNonEmpty as TNE
quotedChar :: Char -> Bool
quotedChar c = allowed && not banned where
allowed = rangeLettersToSymbols c || c == ' '
banned = c == '>'
payee :: Parser B.Payee
payee = quotedPayee <|> unquotedPayee
quotedPayee :: Parser B.Payee
quotedPayee = between (char '<') (char '>') p <?> "quoted payee" where
p = (\c cs -> B.Payee (TextNonEmpty c (pack cs)))
<$> satisfy quotedChar
<*> many (satisfy quotedChar)
unquotedFirstChar :: Char -> Bool
unquotedFirstChar = rangeLetters
unquotedRestChars :: Char -> Bool
unquotedRestChars = quotedChar
unquotedPayee :: Parser B.Payee
unquotedPayee = let
p c cs = B.Payee (TextNonEmpty c (pack cs))
in p
<$> satisfy unquotedFirstChar
<*> many (satisfy unquotedRestChars)
<?> "unquoted payee"
-- | Render a payee with a minimum of quoting. Fails if cannot be
-- rendered at all.
smartRender :: B.Payee -> Maybe Text
smartRender (B.Payee p) = let
TextNonEmpty f r = p
noQuoteNeeded = unquotedFirstChar f
&& X.all unquotedRestChars r
renderable = TNE.all quotedChar p
quoted = '<' `cons` TNE.toText p `snoc` '>'
makeText
| noQuoteNeeded = Just $ TNE.toText p
| renderable = Just quoted
| otherwise = Nothing
in makeText
-- | Renders with quotes, whether the payee needs it or not.
quoteRender :: B.Payee -> Maybe Text
quoteRender (B.Payee p) = let
renderable = TNE.all quotedChar p
quoted = '<' `cons` TNE.toText p `snoc` '>'
in if renderable
then Just quoted
else Nothing