hformat 0.1.0.1 → 0.2.0.0
raw patch · 3 files changed
+63/−36 lines, 3 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Text.Format: (%=) :: FormatBuild a ⇒ String -> a -> [FormatArg]
- Text.Format: instance Data.String.IsString s ⇒ Text.Format.FormatResult s
- Text.Format: instance Text.Format.FormatBuild a ⇒ Text.Format.Hole a
+ Text.Format: (~%) :: FormatBuild a => String -> a -> FormatArg
+ Text.Format: formats :: FormatResult r => String -> [FormatArg] -> r
+ Text.Format: infixl 7 ~~
+ Text.Format: infixr 8 ~%
+ Text.Format: instance Data.String.IsString s => Text.Format.FormatResult s
+ Text.Format: instance GHC.Show.Show Text.Format.Format
+ 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.Hole Text.Format.FormatArg
+ Text.Format: prebuild :: Format -> Text
- Text.Format: (~~) :: (Hole a, FormatResult r) ⇒ Format -> a -> r
+ Text.Format: (~~) :: (Hole a, FormatResult r) => Format -> a -> r
- Text.Format: fmt :: Hole a ⇒ a -> [FormatArg]
+ Text.Format: fmt :: FormatBuild a => a -> FormatArg
- Text.Format: format :: FormatResult r ⇒ String -> r
+ Text.Format: format :: FormatResult r => String -> r
- Text.Format: formatBuild :: FormatBuild a ⇒ a -> Builder
+ Text.Format: formatBuild :: (FormatBuild a, Show a) => a -> Builder
- Text.Format: formatResult :: FormatResult r ⇒ Format -> r
+ Text.Format: formatResult :: FormatResult r => Format -> r
- Text.Format: hole :: Hole a ⇒ a -> [FormatArg]
+ Text.Format: hole :: Hole a => a -> [FormatArg]
Files
- hformat.cabal +1/−1
- src/Text/Format.hs +46/−22
- tests/Test.hs +16/−13
hformat.cabal view
@@ -1,5 +1,5 @@ name: hformat -version: 0.1.0.1 +version: 0.2.0.0 synopsis: Simple Haskell formatting description: String formatting homepage: http://github.com/mvoidex/hformat
src/Text/Format.hs view
@@ -3,9 +3,9 @@ -- | 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" +-- >"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" +-- >"{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 @@ -14,9 +14,9 @@ -- >"{} and {{}}" ~~ 10 ≡ "10 and {}" module Text.Format ( FormatArg(..), Format(..), Formatter(..), - build, + prebuild, build, FormatBuild(..), Hole(..), fmt, FormatResult(..), - format, (~~), (%=) + format, formats, (~~), (~%) ) where import Prelude.Unicode @@ -38,6 +38,9 @@ formatString ∷ String, formatArgs ∷ [FormatArg] } +instance Show Format where + show = unpack ∘ prebuild + instance IsString Format where fromString str = Format str [] @@ -57,8 +60,14 @@ return $ Just v' return $ Formatter (maybe (Left n) Right $ readMaybe n) v +prebuild ∷ Format → Text +prebuild = buildFormat True + build ∷ Format → Text -build fstr = B.toLazyText $ mconcat $ build' 0 fstr where +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) @@ -70,18 +79,24 @@ build' i (Format fstr' args) = fromString s : build' i (Format fstr'' args) where (s, fstr'') = break (∈ "{}") fstr' formatArg' ∷ Formatter → [FormatArg] → Builder - formatArg' (Formatter (Left name) defVal) args = fromMaybe (error $ "Argument " ++ name ++ " not set") (lookArg <|> fmap B.fromString defVal) where - lookArg = do - FormatNamed _ fval ← find byName args - return fval - byName (FormatNamed n _) = n ≡ name - byName _ = False - formatArg' (Formatter (Right i) defVal) args = fromMaybe (error $ "Argument at index " ++ show i ++ " not set") (lookIdx <|> fmap B.fromString defVal) where - lookIdx = do - FormatPos fval ← listToMaybe $ drop i $ filter isPos args - return fval - isPos (FormatPos _) = True - isPos _ = False + formatArg' f@(Formatter (Left name) defVal) args + | pre = fromMaybe (formatBuild f) lookArg + | otherwise = fromMaybe (error $ "Argument " ++ name ++ " not set") (lookArg <|> fmap formatBuild defVal) + where + lookArg = do + FormatNamed _ fval ← find byName args + return fval + 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) + where + lookIdx = do + FormatPos fval ← listToMaybe $ drop i $ filter isPos args + return fval + isPos (FormatPos _) = True + isPos _ = False -- | FormatBuild class, by default using @show@ class FormatBuild a where @@ -107,12 +122,18 @@ instance FormatBuild T.Text where formatBuild = B.fromText +instance FormatBuild Formatter where + formatBuild = formatBuild ∘ show + class Hole a where hole ∷ a → [FormatArg] instance Hole Builder where hole = return ∘ FormatPos +instance {-# OVERLAPPING #-} Hole FormatArg where + hole = return + instance {-# OVERLAPPING #-} Hole [FormatArg] where hole = id @@ -122,8 +143,8 @@ instance {-# OVERLAPPABLE #-} FormatBuild a ⇒ Hole a where hole = return ∘ FormatPos ∘ formatBuild -fmt ∷ Hole a ⇒ a → [FormatArg] -fmt = hole +fmt ∷ FormatBuild a ⇒ a → FormatArg +fmt = FormatPos ∘ formatBuild class FormatResult r where formatResult ∷ Format → r @@ -140,12 +161,15 @@ 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 %= +infixr 8 ~% -(%=) ∷ FormatBuild a ⇒ String → a → [FormatArg] -name %= value = [FormatNamed name (formatBuild value)] +(~%) ∷ FormatBuild a ⇒ String → a → FormatArg +name ~% value = FormatNamed name (formatBuild value)
tests/Test.hs view
@@ -11,24 +11,27 @@ main ∷ IO () main = hspec $ do describe "positional arguments" $ do - it "should format unnamed arguments" $ + it "should format unnamed arguments" (format "{} + {} = {}" ~~ (10 ∷ Int) ~~ (12 ∷ Int) ~~ (22 ∷ Int) ≡ "10 + 12 = 22") - it "should format positional arguments" $ + it "should format positional arguments" (format "{0} + {0} = {1}" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ "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") + it "should format named arguments" + (format "{x} + {y} = {z}" ~~ "x" ~% (1 ∷ Int) ~~ "y" ~% (2 ∷ Int) ~~ "z" ~% (3 ∷ Int) ≡ "1 + 2 = 3") describe "default values" $ do - it "should accept default values for positional arguments" $ + it "should accept default values for positional arguments" (format "{0:foo} is {1:bar}" ~~ "blah" ≡ "blah is bar") - it "should accept default values for named arguments" $ - (format "{x:12} + {y:13}" ~~ "y" %= (10 ∷ Int) ≡ "12 + 10") - describe "lists" $ do - it "should accept list of values" $ - (format "{0} + {x:10} = {1}" ~~ [fmt (3 ∷ Int), "x" %= (5 ∷ Int), fmt (8 ∷ Int)] ≡ "3 + 5 = 8") + it "should accept default values for named arguments" + (format "{x:12} + {y:13}" ~~ "y" ~% (10 ∷ Int) ≡ "12 + 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") describe "escape" $ - it "should escape curly braces" $ + it "should escape curly braces" (format "{} is not {{}}" ~~ "{}" ≡ "{} 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") + it "should process mixed arguments" + (format "{1:foo} and {} are {what:args}" ~~ "what" ~% "quux" ~~ (10 ∷ Int) ~~ (20 ∷ Int) ≡ "20 and 10 are quux") + describe "prebuild" $ + it "should show partially formatted" $ + show (format "{0} ≡ {1}" ~~ "foo" ∷ Format) ≡ "foo ≡ {1}"