hformat 0.3.3.0 → 0.3.3.1
raw patch · 3 files changed
+345/−342 lines, 3 filesdep +semigroupsdep ~basePVP ok
version bump matches the API change (PVP)
Dependencies added: semigroups
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- hformat.cabal +48/−45
- src/Text/Format.hs +246/−246
- tests/Test.hs +51/−51
hformat.cabal view
@@ -1,45 +1,48 @@-name: hformat -version: 0.3.3.0 -synopsis: Simple Haskell formatting -description: String formatting -homepage: http://github.com/mvoidex/hformat -license: BSD3 -license-file: LICENSE -author: Alexandr Ruchkin -maintainer: voidex@live.com -category: Text -build-type: Simple -cabal-version: >=1.10 - -source-repository head - type: git - location: git://github.com/mvoidex/hformat.git - -library - hs-source-dirs: src - default-language: Haskell2010 - ghc-options: -Wall -fno-warn-tabs - default-extensions: UnicodeSyntax - 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 - type: exitcode-stdio-1.0 - main-is: Test.hs - hs-source-dirs: tests - ghc-options: -threaded -Wall -fno-warn-tabs - default-language: Haskell2010 - default-extensions: UnicodeSyntax - build-depends: - base >= 4.8 && < 5, - base-unicode-symbols >= 0.2, - text >= 1.2.1, - hspec, - hformat +name: hformat+version: 0.3.3.1+synopsis: Simple Haskell formatting+description: String formatting+homepage: http://github.com/mvoidex/hformat+license: BSD3+license-file: LICENSE+author: Alexandr Ruchkin+maintainer: voidex@live.com+category: Text+build-type: Simple+cabal-version: >=1.10++source-repository head+ type: git+ location: git://github.com/mvoidex/hformat.git++library+ hs-source-dirs: src+ default-language: Haskell2010+ ghc-options: -Wall -fno-warn-tabs+ default-extensions: UnicodeSyntax+ exposed-modules:+ Text.Format+ Text.Format.Flags+ Text.Format.Colored+ if !impl(ghc >= 8.0)+ build-depends:+ semigroups >= 0.18.2 && < 0.19+ build-depends:+ base >= 4.8 && < 5,+ base-unicode-symbols >= 0.2,+ ansi-terminal >= 0.6,+ text >= 1.2.1++test-suite test+ type: exitcode-stdio-1.0+ main-is: Test.hs+ hs-source-dirs: tests+ ghc-options: -threaded -Wall -fno-warn-tabs+ default-language: Haskell2010+ default-extensions: UnicodeSyntax+ build-depends:+ base >= 4.8 && < 5,+ base-unicode-symbols >= 0.2,+ text >= 1.2.1,+ hspec,+ hformat
src/Text/Format.hs view
@@ -1,246 +1,246 @@-{-# LANGUAGE FlexibleInstances, UndecidableInstances, DefaultSignatures #-} - --- | Format string with named args --- --- >-- Named args --- >"My name is {name}, I am {age} years old" ~~ ("name" ~% "Joe") ~~ ("age" ~% 24) ≡ "My name is Joe, I am 24 years old" --- >-- Arg can have default value --- >"{var:x} = {val:10}" ~~ ("var" ~% y) ≡ "y = 10" --- >-- Numeric position can be used --- >"{0} {1} {0}" ~~ "foo" ~~ "bar" ≡ "foo bar foo" --- >-- Positions can be omitted --- >"{} {}" ~~ "foo" ~~ 10 ≡ "foo 10" --- >-- Double braces to escape them --- >"{} and {{}}" ~~ 10 ≡ "10 and {}" -module Text.Format ( - FormattedPart(..), Formatted(..), withFlags, - FormatArg(..), Format(..), Formatter(..), - prebuild, build, getNamedArguments, - Formattable(..), Hole(..), fmt, FormatResult(..), - format, formats, (~~), (~%), - - module Text.Format.Flags - ) where - -import Prelude.Unicode - -import Control.Applicative -import Data.Char (intToDigit) -import Data.List (find, intercalate, nub) -import Data.Maybe (fromMaybe, listToMaybe) -import Data.Semigroup (Semigroup(..)) -import qualified Data.Text as T -import Data.Text.Lazy (Text, unpack) -import Data.String -import Numeric -import Text.Read (readMaybe) -import Text.ParserCombinators.ReadP - -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 Semigroup Formatted where - Formatted l <> Formatted r = Formatted $ l ++ r - -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 = mconcat ∘ map formattedValue ∘ formattedParts ∘ prebuild - -instance IsString Format where - fromString str = Format str [] - -data Formatter = Formatter { - formatter ∷ Either String Int, - formatterDefault ∷ Maybe String, - formatterFlags ∷ [String] } - -instance Show Formatter where - 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 ← munch (∉ "=:}") - v ← option Nothing $ do - _ ← char '=' - v' ← munch1 (∉ ":}") - return $ Just v' - cs ← option [] $ do - _ ← char ':' - flip sepBy (char ',') (munch1 (∉ ",}")) - return $ Formatter (maybe (Left n) Right $ readMaybe n) v cs - -prebuild ∷ Format → Formatted -prebuild = buildFormat True - -build ∷ Format → Formatted -build = buildFormat False - -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 `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] → 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 fmtCfgs - byName (FormatNamed n _) = n ≡ name - byName _ = False - 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 fmtCfgs - isPos (FormatPos _) = True - isPos _ = False - -getNamedArguments ∷ Format → [String] -getNamedArguments = nub ∘ getNamedArguments' - where - getNamedArguments' ∷ Format → [String] - getNamedArguments' (Format "" _) = mempty - getNamedArguments' (Format ('{':'{':fstr') args) = getNamedArguments' (Format fstr' args) - getNamedArguments' (Format ('}':'}':fstr') args) = getNamedArguments' (Format fstr' args) - getNamedArguments' (Format ('{':'}':fstr') args) = getNamedArguments' (Format fstr' args) - getNamedArguments' (Format fstr@('{':fstr') args) = case reads fstr of - [] → error $ "Can't parse formatter at " ++ fstr' - (f, fstr''):_ → getNamedArgument f `mappend` getNamedArguments' (Format fstr'' args) - where - getNamedArguments' (Format fstr' args) = getNamedArguments' (Format fstr'' args) where - (_, fstr'') = break (∈ "{}") fstr' - getNamedArgument ∷ Formatter → [String] - getNamedArgument (Formatter (Left name) _ _) = [name] - getNamedArgument _ = mempty - --- | 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 - -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 Formattable String where - formattable s _ = fromString s - -instance Formattable Char where - formattable ch _ = fromString [ch] - -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 Formattable Text where - formattable s _ = fromString ∘ unpack $ s - -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 Formatted where - hole v = [FormatPos $ const v] - -instance {-# OVERLAPPING #-} Hole FormatArg where - hole = return - -instance {-# OVERLAPPING #-} Hole [FormatArg] where - hole = id - -instance {-# OVERLAPPING #-} Hole [[FormatArg]] where - hole = concat - -instance {-# OVERLAPPABLE #-} Formattable a ⇒ Hole a where - hole v = [FormatPos $ formatted v] - -fmt ∷ Formattable a ⇒ a → FormatArg -fmt v = FormatPos $ formatted v - -class FormatResult r where - formatResult ∷ Format → r - -instance FormatResult Format where - formatResult = id - -instance {-# OVERLAPPING #-} FormatResult String where - formatResult = mconcat ∘ map formattedValue ∘ formattedParts ∘ build - -instance {-# OVERLAPPABLE #-} IsString s ⇒ FormatResult s where - formatResult = fromString ∘ formatResult - -instance {-# OVERLAPPABLE #-} FormatResult Formatted where - formatResult = build - -format ∷ FormatResult r ⇒ String → r -format = formatResult ∘ fromString - -formats ∷ FormatResult r ⇒ String → [FormatArg] → r -formats f = formatResult ∘ Format f - -infixl 7 ~~ - -(~~) ∷ (Hole a, FormatResult r) ⇒ Format → a → r -fstr ~~ arg = formatResult $ fstr { formatArgs = formatArgs fstr ++ hole arg } - -infixr 8 ~% - -(~%) ∷ 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 "" +{-# LANGUAGE FlexibleInstances, UndecidableInstances, DefaultSignatures #-}++-- | Format string with named args+--+-- >-- Named args+-- >"My name is {name}, I am {age} years old" ~~ ("name" ~% "Joe") ~~ ("age" ~% 24) ≡ "My name is Joe, I am 24 years old"+-- >-- Arg can have default value+-- >"{var:x} = {val:10}" ~~ ("var" ~% y) ≡ "y = 10"+-- >-- Numeric position can be used+-- >"{0} {1} {0}" ~~ "foo" ~~ "bar" ≡ "foo bar foo"+-- >-- Positions can be omitted+-- >"{} {}" ~~ "foo" ~~ 10 ≡ "foo 10"+-- >-- Double braces to escape them+-- >"{} and {{}}" ~~ 10 ≡ "10 and {}"+module Text.Format (+ FormattedPart(..), Formatted(..), withFlags,+ FormatArg(..), Format(..), Formatter(..),+ prebuild, build, getNamedArguments,+ Formattable(..), Hole(..), fmt, FormatResult(..),+ format, formats, (~~), (~%),++ module Text.Format.Flags+ ) where++import Prelude.Unicode++import Control.Applicative+import Data.Char (intToDigit)+import Data.List (find, intercalate, nub)+import Data.Maybe (fromMaybe, listToMaybe)+import Data.Semigroup (Semigroup(..))+import qualified Data.Text as T+import Data.Text.Lazy (Text, unpack)+import Data.String+import Numeric+import Text.Read (readMaybe)+import Text.ParserCombinators.ReadP++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 Semigroup Formatted where+ Formatted l <> Formatted r = Formatted $ l ++ r++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 = mconcat ∘ map formattedValue ∘ formattedParts ∘ prebuild++instance IsString Format where+ fromString str = Format str []++data Formatter = Formatter {+ formatter ∷ Either String Int,+ formatterDefault ∷ Maybe String,+ formatterFlags ∷ [String] }++instance Show Formatter where+ 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 ← munch (∉ "=:}")+ v ← option Nothing $ do+ _ ← char '='+ v' ← munch1 (∉ ":}")+ return $ Just v'+ cs ← option [] $ do+ _ ← char ':'+ flip sepBy (char ',') (munch1 (∉ ",}"))+ return $ Formatter (maybe (Left n) Right $ readMaybe n) v cs++prebuild ∷ Format → Formatted+prebuild = buildFormat True++build ∷ Format → Formatted+build = buildFormat False++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 `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] → 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 fmtCfgs+ byName (FormatNamed n _) = n ≡ name+ byName _ = False+ 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 fmtCfgs+ isPos (FormatPos _) = True+ isPos _ = False++getNamedArguments ∷ Format → [String]+getNamedArguments = nub ∘ getNamedArguments'+ where+ getNamedArguments' ∷ Format → [String]+ getNamedArguments' (Format "" _) = mempty+ getNamedArguments' (Format ('{':'{':fstr') args) = getNamedArguments' (Format fstr' args)+ getNamedArguments' (Format ('}':'}':fstr') args) = getNamedArguments' (Format fstr' args)+ getNamedArguments' (Format ('{':'}':fstr') args) = getNamedArguments' (Format fstr' args)+ getNamedArguments' (Format fstr@('{':fstr') args) = case reads fstr of+ [] → error $ "Can't parse formatter at " ++ fstr'+ (f, fstr''):_ → getNamedArgument f `mappend` getNamedArguments' (Format fstr'' args)+ where+ getNamedArguments' (Format fstr' args) = getNamedArguments' (Format fstr'' args) where+ (_, fstr'') = break (∈ "{}") fstr'+ getNamedArgument ∷ Formatter → [String]+ getNamedArgument (Formatter (Left name) _ _) = [name]+ getNamedArgument _ = mempty++-- | 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++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 Formattable String where+ formattable s _ = fromString s++instance Formattable Char where+ formattable ch _ = fromString [ch]++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 Formattable Text where+ formattable s _ = fromString ∘ unpack $ s++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 Formatted where+ hole v = [FormatPos $ const v]++instance {-# OVERLAPPING #-} Hole FormatArg where+ hole = return++instance {-# OVERLAPPING #-} Hole [FormatArg] where+ hole = id++instance {-# OVERLAPPING #-} Hole [[FormatArg]] where+ hole = concat++instance {-# OVERLAPPABLE #-} Formattable a ⇒ Hole a where+ hole v = [FormatPos $ formatted v]++fmt ∷ Formattable a ⇒ a → FormatArg+fmt v = FormatPos $ formatted v++class FormatResult r where+ formatResult ∷ Format → r++instance FormatResult Format where+ formatResult = id++instance {-# OVERLAPPING #-} FormatResult String where+ formatResult = mconcat ∘ map formattedValue ∘ formattedParts ∘ build++instance {-# OVERLAPPABLE #-} IsString s ⇒ FormatResult s where+ formatResult = fromString ∘ formatResult++instance {-# OVERLAPPABLE #-} FormatResult Formatted where+ formatResult = build++format ∷ FormatResult r ⇒ String → r+format = formatResult ∘ fromString++formats ∷ FormatResult r ⇒ String → [FormatArg] → r+formats f = formatResult ∘ Format f++infixl 7 ~~++(~~) ∷ (Hole a, FormatResult r) ⇒ Format → a → r+fstr ~~ arg = formatResult $ fstr { formatArgs = formatArgs fstr ++ hole arg }++infixr 8 ~%++(~%) ∷ 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 ""
tests/Test.hs view
@@ -1,51 +1,51 @@-{-# LANGUAGE OverloadedStrings #-} - -module Main ( - main - ) where - -import Prelude.Unicode - -import Text.Format - -import Test.Hspec - -main ∷ IO () -main = hspec $ do - describe "positional arguments" $ do - it "should format unnamed arguments" - (format "{} + {} = {}" ~~ (10 ∷ Int) ~~ (12 ∷ Int) ~~ (22 ∷ Int) ≡ str "10 + 12 = 22") - it "should format positional arguments" - (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) ≡ str "1 + 2 = 3") - describe "default values" $ do - it "should accept default values for positional arguments" - (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) ≡ 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)] ≡ str "3 + 5 = 8") - describe "escape" $ - it "should escape curly braces" - (format "{} is not {{}}" ~~ str "{}" ≡ str "{} is not {}") - describe "mix" $ - it "should process mixed arguments" - (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}" ~~ str "foo" ∷ Format) ≡ str "foo ≡ {1}" - describe "list of named arguments" $ - it "should list the named arguments" $ - getNamedArguments "Positional arguments are ignored: {0} {} {2}. I want just the following, without repetition: {foo}, {bar} and {foo}." ≡ ["foo", "bar"] - -str ∷ String → String -str = id +{-# LANGUAGE OverloadedStrings #-}++module Main (+ main+ ) where++import Prelude.Unicode++import Text.Format++import Test.Hspec++main ∷ IO ()+main = hspec $ do+ describe "positional arguments" $ do+ it "should format unnamed arguments"+ (format "{} + {} = {}" ~~ (10 ∷ Int) ~~ (12 ∷ Int) ~~ (22 ∷ Int) ≡ str "10 + 12 = 22")+ it "should format positional arguments"+ (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) ≡ str "1 + 2 = 3")+ describe "default values" $ do+ it "should accept default values for positional arguments"+ (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) ≡ 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)] ≡ str "3 + 5 = 8")+ describe "escape" $+ it "should escape curly braces"+ (format "{} is not {{}}" ~~ str "{}" ≡ str "{} is not {}")+ describe "mix" $+ it "should process mixed arguments"+ (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}" ~~ str "foo" ∷ Format) ≡ str "foo ≡ {1}"+ describe "list of named arguments" $+ it "should list the named arguments" $+ getNamedArguments "Positional arguments are ignored: {0} {} {2}. I want just the following, without repetition: {foo}, {bar} and {foo}." ≡ ["foo", "bar"]++str ∷ String → String+str = id