hformat 0.2.0.0 → 0.3.0.0
raw patch · 5 files changed
+237/−75 lines, 5 filesdep +ansi-terminaldep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: ansi-terminal
Dependency ranges changed: base
API changes (from Hackage documentation)
- Text.Format: class FormatBuild a where formatBuild = fromString ∘ show
- Text.Format: formatBuild :: (FormatBuild a, Show a) => a -> Builder
- Text.Format: instance Text.Format.FormatBuild Data.Text.Internal.Lazy.Text
- Text.Format: instance Text.Format.FormatBuild Data.Text.Internal.Text
- Text.Format: instance Text.Format.FormatBuild GHC.Base.String
- Text.Format: instance Text.Format.FormatBuild GHC.Integer.Type.Integer
- Text.Format: instance Text.Format.FormatBuild GHC.Types.Bool
- Text.Format: instance Text.Format.FormatBuild GHC.Types.Char
- Text.Format: instance Text.Format.FormatBuild GHC.Types.Double
- Text.Format: instance Text.Format.FormatBuild GHC.Types.Float
- Text.Format: instance Text.Format.FormatBuild GHC.Types.Int
- Text.Format: instance Text.Format.FormatBuild Text.Format.Formatter
- Text.Format: instance Text.Format.FormatBuild a => Text.Format.Hole a
- Text.Format: instance Text.Format.FormatResult Data.Text.Internal.Lazy.Text
- Text.Format: instance Text.Format.Hole Data.Text.Internal.Builder.Builder
+ Text.Format: Formatted :: [FormattedPart] -> Formatted
+ Text.Format: FormattedPart :: FormatFlags -> String -> FormattedPart
+ Text.Format: [formattedFlags] :: FormattedPart -> FormatFlags
+ Text.Format: [formattedParts] :: Formatted -> [FormattedPart]
+ Text.Format: [formattedValue] :: FormattedPart -> String
+ Text.Format: [formatterFlags] :: Formatter -> [String]
+ Text.Format: class Formattable a where formattable x _ = fromString ∘ show $ x
+ Text.Format: data FormattedPart
+ Text.Format: formattable :: (Formattable a, Show a) => a -> FormatFlags -> Formatted
+ Text.Format: instance Data.String.IsString Text.Format.Formatted
+ Text.Format: instance Data.String.IsString Text.Format.FormattedPart
+ Text.Format: instance GHC.Base.Monoid Text.Format.Formatted
+ Text.Format: instance GHC.Classes.Eq Text.Format.Formatted
+ Text.Format: instance GHC.Classes.Eq Text.Format.FormattedPart
+ Text.Format: instance GHC.Classes.Ord Text.Format.Formatted
+ Text.Format: instance GHC.Classes.Ord Text.Format.FormattedPart
+ Text.Format: instance GHC.Show.Show Text.Format.Formatted
+ Text.Format: instance GHC.Show.Show Text.Format.FormattedPart
+ Text.Format: instance Text.Format.FormatResult GHC.Base.String
+ Text.Format: instance Text.Format.FormatResult Text.Format.Formatted
+ Text.Format: instance Text.Format.Formattable Data.Text.Internal.Lazy.Text
+ Text.Format: instance Text.Format.Formattable Data.Text.Internal.Text
+ Text.Format: instance Text.Format.Formattable GHC.Base.String
+ Text.Format: instance Text.Format.Formattable GHC.Integer.Type.Integer
+ Text.Format: instance Text.Format.Formattable GHC.Types.Bool
+ Text.Format: instance Text.Format.Formattable GHC.Types.Char
+ Text.Format: instance Text.Format.Formattable GHC.Types.Double
+ Text.Format: instance Text.Format.Formattable GHC.Types.Float
+ Text.Format: instance Text.Format.Formattable GHC.Types.Int
+ Text.Format: instance Text.Format.Formattable Text.Format.Formatter
+ Text.Format: instance Text.Format.Formattable a => Text.Format.Hole a
+ Text.Format: instance Text.Format.Hole Text.Format.Formatted
+ Text.Format: newtype Formatted
+ Text.Format: withFlags :: String -> [String] -> Formatted
+ Text.Format.Colored: colored :: Formatted -> IO ()
+ Text.Format.Colored: coloredLine :: Formatted -> IO ()
+ Text.Format.Colored: hColored :: Handle -> Formatted -> IO ()
+ Text.Format.Colored: hColoredLine :: Handle -> Formatted -> IO ()
+ Text.Format.Flags: baseFlag :: (Read a, Integral a) => [String] -> a
+ Text.Format.Flags: getFlag :: (String -> Maybe a) -> [String] -> Maybe a
+ Text.Format.Flags: hasFlag :: String -> [String] -> Bool
+ Text.Format.Flags: preciseFlag :: [String] -> Maybe Int
+ Text.Format.Flags: type FormatFlags = [String]
- Text.Format: (~%) :: FormatBuild a => String -> a -> FormatArg
+ Text.Format: (~%) :: Formattable a => String -> a -> FormatArg
- Text.Format: FormatNamed :: String -> Builder -> FormatArg
+ Text.Format: FormatNamed :: String -> ([String] -> Formatted) -> FormatArg
- Text.Format: FormatPos :: Builder -> FormatArg
+ Text.Format: FormatPos :: ([String] -> Formatted) -> FormatArg
- Text.Format: Formatter :: Either String Int -> Maybe String -> Formatter
+ Text.Format: Formatter :: Either String Int -> Maybe String -> [String] -> Formatter
- Text.Format: build :: Format -> Text
+ Text.Format: build :: Format -> Formatted
- Text.Format: fmt :: FormatBuild a => a -> FormatArg
+ Text.Format: fmt :: Formattable a => a -> FormatArg
- Text.Format: prebuild :: Format -> Text
+ Text.Format: prebuild :: Format -> Formatted
Files
- hformat.cabal +5/−2
- src/Text/Format.hs +113/−64
- src/Text/Format/Colored.hs +62/−0
- src/Text/Format/Flags.hs +37/−0
- tests/Test.hs +20/−9
hformat.cabal view
@@ -1,5 +1,5 @@ name: hformat -version: 0.2.0.0 +version: 0.3.0.0 synopsis: Simple Haskell formatting description: String formatting homepage: http://github.com/mvoidex/hformat @@ -20,11 +20,14 @@ default-language: Haskell2010 ghc-options: -Wall -fno-warn-tabs default-extensions: UnicodeSyntax - exposed-modules: + exposed-modules: Text.Format + Text.Format.Flags + Text.Format.Colored build-depends: base >= 4.8 && < 5, base-unicode-symbols >= 0.2, + ansi-terminal >= 0.6, text >= 1.2.1 test-suite test
src/Text/Format.hs view
@@ -13,123 +13,161 @@ -- >-- Double braces to escape them -- >"{} and {{}}" ~~ 10 ≡ "10 and {}" module Text.Format ( + FormattedPart(..), Formatted(..), withFlags, FormatArg(..), Format(..), Formatter(..), prebuild, build, - FormatBuild(..), Hole(..), fmt, FormatResult(..), - format, formats, (~~), (~%) + Formattable(..), Hole(..), fmt, FormatResult(..), + format, formats, (~~), (~%), + + module Text.Format.Flags ) where import Prelude.Unicode import Control.Applicative -import Data.List (find) +import Data.Char (intToDigit) +import Data.List (find, intercalate, nub) import Data.Maybe (fromMaybe, listToMaybe) import qualified Data.Text as T import Data.Text.Lazy (Text, unpack) -import Data.Text.Lazy.Builder (Builder) -import qualified Data.Text.Lazy.Builder as B import Data.String +import Numeric import Text.Read (readMaybe) import Text.ParserCombinators.ReadP -data FormatArg = FormatNamed String Builder | FormatPos Builder +import Text.Format.Flags +data FormattedPart = FormattedPart { + formattedFlags ∷ FormatFlags, + formattedValue ∷ String } + deriving (Eq, Ord, Show) + +instance IsString FormattedPart where + fromString = FormattedPart [] ∘ fromString + +newtype Formatted = Formatted { formattedParts ∷ [FormattedPart] } deriving (Eq, Ord, Show) + +instance IsString Formatted where + fromString = Formatted ∘ return ∘ fromString + +instance Monoid Formatted where + mempty = Formatted [] + Formatted l `mappend` Formatted r = Formatted $ l ++ r + +withFlags ∷ String → [String] → Formatted +withFlags v fs = Formatted [FormattedPart fs v] + +data FormatArg = FormatNamed String ([String] → Formatted) | FormatPos ([String] → Formatted) + data Format = Format { formatString ∷ String, formatArgs ∷ [FormatArg] } instance Show Format where - show = unpack ∘ prebuild + show = mconcat ∘ map formattedValue ∘ formattedParts ∘ prebuild instance IsString Format where fromString str = Format str [] data Formatter = Formatter { formatter ∷ Either String Int, - formatterDefault ∷ Maybe String } + formatterDefault ∷ Maybe String, + formatterFlags ∷ [String] } instance Show Formatter where - show (Formatter f def) = "{" ++ either id show f ++ maybe "" (':':) def ++ "}" + show (Formatter f def cfgs) = "{" ++ concat parts ++ "}" where + parts = [either id show f, fromMaybe "" (fmap ('=':) def), if null cfgs then "" else (':' : intercalate "," cfgs)] instance Read Formatter where readsPrec _ = readP_to_S $ between (char '{') (char '}') $ do - n ← munch1 (∉ ":}") + n ← munch (∉ "=:}") v ← option Nothing $ do - _ ← char ':' - v' ← munch1 (≢ '}') + _ ← char '=' + v' ← munch1 (∉ ":}") return $ Just v' - return $ Formatter (maybe (Left n) Right $ readMaybe n) v + cs ← option [] $ do + _ ← char ':' + flip sepBy (char ',') (munch1 (∉ ",}")) + return $ Formatter (maybe (Left n) Right $ readMaybe n) v cs -prebuild ∷ Format → Text +prebuild ∷ Format → Formatted prebuild = buildFormat True -build ∷ Format → Text +build ∷ Format → Formatted build = buildFormat False -buildFormat ∷ Bool → Format → Text -buildFormat pre fstr = B.toLazyText $ mconcat $ build' 0 fstr where - build' ∷ Int → Format → [Builder] - build' _ (Format "" _) = [] - build' i (Format ('{':'{':fstr') args) = B.singleton '{' : build' i (Format fstr' args) - build' i (Format ('}':'}':fstr') args) = B.singleton '}' : build' i (Format fstr' args) - build' i (Format ('{':'}':fstr') args) = formatArg' (Formatter (Right i) Nothing) args : build' (succ i) (Format fstr' args) +buildFormat ∷ Bool → Format → Formatted +buildFormat pre fstr = build' 0 fstr where + build' ∷ Int → Format → Formatted + build' _ (Format "" _) = mempty + build' i (Format ('{':'{':fstr') args) = fromString "{" `mappend` build' i (Format fstr' args) + build' i (Format ('}':'}':fstr') args) = fromString "}" `mappend` build' i (Format fstr' args) + build' i (Format ('{':'}':fstr') args) = formatArg' (Formatter (Right i) Nothing []) args `mappend` build' (succ i) (Format fstr' args) build' i (Format ('{':fstr') args) = case reads ('{':fstr') of [] → error $ "Can't parse formatter at " ++ fstr' - (f, fstr''):_ → formatArg' f args : build' i (Format fstr'' args) - build' i (Format fstr' args) = fromString s : build' i (Format fstr'' args) where + (f, fstr''):_ → formatArg' f args `mappend` build' i (Format fstr'' args) + build' i (Format fstr' args) = fromString s `mappend` build' i (Format fstr'' args) where (s, fstr'') = break (∈ "{}") fstr' - formatArg' ∷ Formatter → [FormatArg] → Builder - formatArg' f@(Formatter (Left name) defVal) args - | pre = fromMaybe (formatBuild f) lookArg - | otherwise = fromMaybe (error $ "Argument " ++ name ++ " not set") (lookArg <|> fmap formatBuild defVal) + formatArg' ∷ Formatter → [FormatArg] → Formatted + formatArg' f@(Formatter (Left name) defVal fmtCfgs) args + | pre = fromMaybe (formatted f fmtCfgs) lookArg + | otherwise = fromMaybe (error $ "Argument " ++ name ++ " not set") (lookArg <|> fmap (flip formatted fmtCfgs) defVal) where lookArg = do FormatNamed _ fval ← find byName args - return fval + return $ fval fmtCfgs byName (FormatNamed n _) = n ≡ name byName _ = False - formatArg' f@(Formatter (Right i) defVal) args - | pre = fromMaybe (formatBuild f) lookIdx - | otherwise = fromMaybe (error $ "Argument at index " ++ show i ++ " not set") (lookIdx <|> fmap formatBuild defVal) + formatArg' f@(Formatter (Right i) defVal fmtCfgs) args + | pre = fromMaybe (formatted f fmtCfgs) lookIdx + | otherwise = fromMaybe (error $ "Argument at index " ++ show i ++ " not set") (lookIdx <|> fmap (flip formatted fmtCfgs) defVal) where lookIdx = do FormatPos fval ← listToMaybe $ drop i $ filter isPos args - return fval + return $ fval fmtCfgs isPos (FormatPos _) = True isPos _ = False --- | FormatBuild class, by default using @show@ -class FormatBuild a where - formatBuild ∷ a → Builder - default formatBuild ∷ Show a ⇒ a → Builder - formatBuild = B.fromString ∘ show +-- | Formattable class, by default using @show@ +class Formattable a where + formattable ∷ a → FormatFlags → Formatted + default formattable ∷ Show a ⇒ a → FormatFlags → Formatted + formattable x _ = fromString ∘ show $ x -instance FormatBuild String where - formatBuild = B.fromString +formatted ∷ Formattable a ⇒ a → FormatFlags → Formatted +formatted v fmts = Formatted ∘ map addFmts ∘ formattedParts ∘ formattable v $ fmts where + addFmts (FormattedPart flags' v') = FormattedPart (nub $ fmts ++ flags') v' -instance FormatBuild Char where - formatBuild = B.singleton +instance Formattable String where + formattable s _ = fromString s -instance FormatBuild Int -instance FormatBuild Integer -instance FormatBuild Double -instance FormatBuild Float -instance FormatBuild Bool +instance Formattable Char where + formattable ch _ = fromString [ch] -instance FormatBuild Text where - formatBuild = B.fromLazyText +instance Formattable Int where + formattable i fmts = fromString ∘ formatInt (baseFlag fmts) $ i +instance Formattable Integer where + formattable i fmts = fromString ∘ formatInt (baseFlag fmts) $ i +instance Formattable Double where + formattable d fmts = fromString ∘ formatDouble (preciseFlag fmts) $ d +instance Formattable Float where + formattable f fmts = fromString ∘ formatDouble (preciseFlag fmts) $ f +instance Formattable Bool -instance FormatBuild T.Text where - formatBuild = B.fromText +instance Formattable Text where + formattable s _ = fromString ∘ unpack $ s -instance FormatBuild Formatter where - formatBuild = formatBuild ∘ show +instance Formattable T.Text where + formattable s _ = fromString ∘ T.unpack $ s +instance Formattable Formatter where + formattable s _ = fromString ∘ show $ s + class Hole a where hole ∷ a → [FormatArg] -instance Hole Builder where - hole = return ∘ FormatPos +instance Hole Formatted where + hole v = [FormatPos $ const v] instance {-# OVERLAPPING #-} Hole FormatArg where hole = return @@ -140,11 +178,11 @@ instance {-# OVERLAPPING #-} Hole [[FormatArg]] where hole = concat -instance {-# OVERLAPPABLE #-} FormatBuild a ⇒ Hole a where - hole = return ∘ FormatPos ∘ formatBuild +instance {-# OVERLAPPABLE #-} Formattable a ⇒ Hole a where + hole v = [FormatPos $ formatted v] -fmt ∷ FormatBuild a ⇒ a → FormatArg -fmt = FormatPos ∘ formatBuild +fmt ∷ Formattable a ⇒ a → FormatArg +fmt v = FormatPos $ formatted v class FormatResult r where formatResult ∷ Format → r @@ -152,12 +190,15 @@ instance FormatResult Format where formatResult = id -instance {-# OVERLAPPING #-} FormatResult Text where - formatResult = build +instance {-# OVERLAPPING #-} FormatResult String where + formatResult = mconcat ∘ map formattedValue ∘ formattedParts ∘ build instance {-# OVERLAPPABLE #-} IsString s ⇒ FormatResult s where - formatResult = fromString ∘ unpack ∘ formatResult + formatResult = fromString ∘ formatResult +instance {-# OVERLAPPABLE #-} FormatResult Formatted where + formatResult = build + format ∷ FormatResult r ⇒ String → r format = formatResult ∘ fromString @@ -171,5 +212,13 @@ infixr 8 ~% -(~%) ∷ FormatBuild a ⇒ String → a → FormatArg -name ~% value = FormatNamed name (formatBuild value) +(~%) ∷ Formattable a ⇒ String → a → FormatArg +name ~% value = FormatNamed name (formatted value) + +-- * Util + +formatInt ∷ (Show a, Integral a) ⇒ a → a → String +formatInt base v = showIntAtBase base intToDigit v "" + +formatDouble ∷ RealFloat a ⇒ Maybe Int → a → String +formatDouble p v = showGFloat p v ""
+ src/Text/Format/Colored.hs view
@@ -0,0 +1,62 @@+module Text.Format.Colored ( + colored, coloredLine, + hColored, hColoredLine + ) where + +import Prelude.Unicode + +import Data.Maybe (mapMaybe) +import System.Console.ANSI +import System.IO + +import Text.Format + +colored ∷ Formatted → IO () +colored = hColored stdout + +coloredLine ∷ Formatted → IO () +coloredLine = hColoredLine stdout + +hColored ∷ Handle → Formatted → IO () +hColored h (Formatted fs) = mapM_ go fs >> setSGR [] where + go (FormattedPart flags v) = setFlags flags >> hPutStr h v >> setSGR [] + setFlags = setSGR ∘ mapMaybe toSGR + toSGR "bold" = Just $ SetConsoleIntensity BoldIntensity + toSGR "italic" = Just $ SetItalicized True + toSGR "undelined" = Just $ SetUnderlining SingleUnderline + toSGR "black" = Just $ SetColor Foreground Vivid Black + toSGR "red" = Just $ SetColor Foreground Vivid Red + toSGR "green" = Just $ SetColor Foreground Vivid Green + toSGR "yellow" = Just $ SetColor Foreground Vivid Yellow + toSGR "blue" = Just $ SetColor Foreground Vivid Blue + toSGR "magenta" = Just $ SetColor Foreground Vivid Magenta + toSGR "cyan" = Just $ SetColor Foreground Vivid Cyan + toSGR "white" = Just $ SetColor Foreground Vivid White + toSGR "darkgray" = Just $ SetColor Foreground Dull Black + toSGR "darkred" = Just $ SetColor Foreground Dull Red + toSGR "darkgreen" = Just $ SetColor Foreground Dull Green + toSGR "darkyellow" = Just $ SetColor Foreground Dull Yellow + toSGR "darkblue" = Just $ SetColor Foreground Dull Blue + toSGR "darkmagenta" = Just $ SetColor Foreground Dull Magenta + toSGR "darkcyan" = Just $ SetColor Foreground Dull Cyan + toSGR "gray" = Just $ SetColor Foreground Dull White + toSGR "bg/black" = Just $ SetColor Background Vivid Black + toSGR "bg/red" = Just $ SetColor Background Vivid Red + toSGR "bg/green" = Just $ SetColor Background Vivid Green + toSGR "bg/yellow" = Just $ SetColor Background Vivid Yellow + toSGR "bg/blue" = Just $ SetColor Background Vivid Blue + toSGR "bg/magenta" = Just $ SetColor Background Vivid Magenta + toSGR "bg/cyan" = Just $ SetColor Background Vivid Cyan + toSGR "bg/white" = Just $ SetColor Background Vivid White + toSGR "bg/darkgray" = Just $ SetColor Background Dull Black + toSGR "bg/darkred" = Just $ SetColor Background Dull Red + toSGR "bg/darkgreen" = Just $ SetColor Background Dull Green + toSGR "bg/darkyellow" = Just $ SetColor Background Dull Yellow + toSGR "bg/darkblue" = Just $ SetColor Background Dull Blue + toSGR "bg/darkmagenta" = Just $ SetColor Background Dull Magenta + toSGR "bg/darkcyan" = Just $ SetColor Background Dull Cyan + toSGR "bg/gray" = Just $ SetColor Background Dull White + toSGR _ = Nothing + +hColoredLine ∷ Handle → Formatted → IO () +hColoredLine h f = hColored h f >> hPutStrLn h ""
+ src/Text/Format/Flags.hs view
@@ -0,0 +1,37 @@+module Text.Format.Flags ( + FormatFlags, + getFlag, hasFlag, + baseFlag, preciseFlag + ) where + +import Prelude.Unicode + +import Control.Applicative +import Data.List (stripPrefix) +import Data.Maybe (listToMaybe, mapMaybe, fromMaybe) +import Text.Read + +type FormatFlags = [String] + +getFlag ∷ (String → Maybe a) → [String] → Maybe a +getFlag fn = listToMaybe ∘ mapMaybe fn + +hasFlag ∷ String → [String] → Bool +hasFlag = (∈) + +getFlagValue ∷ Read a ⇒ String → [String] → Maybe a +getFlagValue nm fmts = do + f ← getFlag (stripPrefix (nm ++ "=")) fmts + readMaybe f + +baseFlag ∷ (Read a, Integral a) ⇒ [String] → a +baseFlag fmts + | hasFlag "bin" fmts ∨ hasFlag "b" fmts = 2 + | hasFlag "octal" fmts ∨ hasFlag "o" fmts = 8 + | hasFlag "hex" fmts ∨ hasFlag "h" fmts = 16 + | otherwise = fromMaybe 10 (getFlagValue "base" fmts <|> getFlagValue "b" fmts) + +preciseFlag ∷ [String] → Maybe Int +preciseFlag fmts = read <$> listToMaybe (mapMaybe preciseFlag' fmts) where + preciseFlag' ('d':d) = Just d + preciseFlag' _ = Nothing
tests/Test.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE OverloadedStrings #-} + module Main ( main ) where @@ -12,26 +14,35 @@ main = hspec $ do describe "positional arguments" $ do it "should format unnamed arguments" - (format "{} + {} = {}" ~~ (10 ∷ Int) ~~ (12 ∷ Int) ~~ (22 ∷ Int) ≡ "10 + 12 = 22") + (format "{} + {} = {}" ~~ (10 ∷ Int) ~~ (12 ∷ Int) ~~ (22 ∷ Int) ≡ str "10 + 12 = 22") it "should format positional arguments" - (format "{0} + {0} = {1}" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ "10 + 10 = 20") + (format "{0} + {0} = {1}" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ str "10 + 10 = 20") describe "named arguments" $ it "should format named arguments" - (format "{x} + {y} = {z}" ~~ "x" ~% (1 ∷ Int) ~~ "y" ~% (2 ∷ Int) ~~ "z" ~% (3 ∷ Int) ≡ "1 + 2 = 3") + (format "{x} + {y} = {z}" ~~ "x" ~% (1 ∷ Int) ~~ "y" ~% (2 ∷ Int) ~~ "z" ~% (3 ∷ Int) ≡ str "1 + 2 = 3") describe "default values" $ do it "should accept default values for positional arguments" - (format "{0:foo} is {1:bar}" ~~ "blah" ≡ "blah is bar") + (format "{0=foo} is {1=bar}" ~~ str "blah" ≡ str "blah is bar") it "should accept default values for named arguments" - (format "{x:12} + {y:13}" ~~ "y" ~% (10 ∷ Int) ≡ "12 + 10") + (format "{x=12} + {y=13}" ~~ "y" ~% (10 ∷ Int) ≡ str "12 + 10") + describe "format options" $ do + it "should accept format options" + (format "x is {0=foo:octal}" ~~ (10 ∷ Int) ≡ str "x is 12") + describe "colorized output" $ do + it "should accept colors" + (format "x is {0:red}" ~~ (10 ∷ Int) ≡ Formatted [FormattedPart [] "x is ", FormattedPart ["red"] "10"]) describe "lists" $ it "should accept list of values" - (format "{0} + {x:10} = {1}" ~~ [fmt (3 ∷ Int), "x" ~% (5 ∷ Int), fmt (8 ∷ Int)] ≡ "3 + 5 = 8") + (format "{0} + {x:10} = {1}" ~~ [fmt (3 ∷ Int), "x" ~% (5 ∷ Int), fmt (8 ∷ Int)] ≡ str "3 + 5 = 8") describe "escape" $ it "should escape curly braces" - (format "{} is not {{}}" ~~ "{}" ≡ "{} is not {}") + (format "{} is not {{}}" ~~ str "{}" ≡ str "{} is not {}") describe "mix" $ it "should process mixed arguments" - (format "{1:foo} and {} are {what:args}" ~~ "what" ~% "quux" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ "20 and 10 are quux") + (format "{1=foo} and {} are {what=args}" ~~ str "what" ~% str "quux" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ str "20 and 10 are quux") describe "prebuild" $ it "should show partially formatted" $ - show (format "{0} ≡ {1}" ~~ "foo" ∷ Format) ≡ "foo ≡ {1}" + show (format "{0} ≡ {1}" ~~ str "foo" ∷ Format) ≡ str "foo ≡ {1}" + +str ∷ String → String +str = id