Commits

Bryan O'Sullivan committed f6b7f54

Introduce a Format type to make format strings a little safer.

Comments (0)

Files changed (6)

Data/Text/Format.hs

 module Data.Text.Format
     (
     -- * Types
-      Only(..)
+      Format
+    , Only(..)
     -- ** Types for format control
     , Fast(..)
     , Shown(..)
 import qualified Data.Text.Buildable as B
 import Data.Text.Format.Params (Params(..))
 import Data.Text.Format.Functions ((<>))
-import Data.Text.Format.Types (Fast(..), Only(..), Shown(..))
+import Data.Text.Format.Types.Internal (Fast(..), Format(..), Only(..), Shown(..))
 import Data.Text.Lazy.Builder
 import Prelude hiding (print)
 import System.IO (Handle)
 import qualified Data.Text.Lazy.IO as LT
 
 -- | Render a format string and arguments to a 'Builder'.
-build :: Params ps => ST.Text -> ps -> Builder
-build fmt ps = zipParams (map fromText . ST.splitOn "{}" $ fmt) xs
+build :: Params ps => Format -> ps -> Builder
+build (Format fmt) ps = zipParams (map fromText . ST.splitOn "{}" $ fmt) xs
   where zipParams (f:fs) (y:ys) = f <> y <> zipParams fs ys
         zipParams [f] []        = f
         zipParams _ _ = error . LT.unpack $ format
         xs = buildParams ps
 
 -- | Render a format string and arguments to a 'LT.Text'.
-format :: Params ps => ST.Text -> ps -> LT.Text
+format :: Params ps => Format -> ps -> LT.Text
 format fmt ps = toLazyText $ build fmt ps
 
 -- | Render a format string and arguments, then print the result.
-print :: Params ps => ST.Text -> ps -> IO ()
+print :: Params ps => Format -> ps -> IO ()
 print fmt ps = LT.putStr . toLazyText $ build fmt ps
 
 -- | Render a format string and arguments, then print the result to
 -- the given file handle.
-hprint :: Params ps => Handle -> ST.Text -> ps -> IO ()
+hprint :: Params ps => Handle -> Format -> ps -> IO ()
 hprint h fmt ps = LT.hPutStr h . toLazyText $ build fmt ps
 
 -- | Pad the left hand side of a string until it reaches @k@

Data/Text/Format/RealFloat.hs

 import Data.Text.Format.Functions ((<>), i2d)
 import Data.Text.Format.RealFloat.Functions (roundTo)
 import Data.Text.Format.Int (integral)
-import Data.Text.Format.Types (FPFormat(..))
+import Data.Text.Format.Types.Internal (FPFormat(..))
 import qualified Data.Text as T
 import Data.Array.Base (unsafeAt)
 import Data.Text.Lazy.Builder

Data/Text/Format/RealFloat/Fast.hs

 import Data.Text.Format.Int (integral)
 import Data.Text.Format.RealFloat.Fast.Internal (posToDigits)
 import Data.Text.Format.RealFloat.Functions (roundTo)
-import Data.Text.Format.Types (FPFormat(..))
+import Data.Text.Format.Types.Internal (FPFormat(..))
 import Data.Text.Lazy.Builder
 import qualified Data.Text as T
 

Data/Text/Format/Types.hs

-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
 
 -- |
 -- Module      : Data.Text.Format.Types
 
 module Data.Text.Format.Types
     (
-      FPFormat(..)
+      Format
     , Fast(..)
     , Only(..)
     , Shown(..)
     ) where
 
--- | Control the rendering of floating point numbers.
-data FPFormat = Exponent
-              -- ^ Scientific notation (e.g. @2.3e123@).
-              | Fixed
-              -- ^ Standard decimal notation.
-              | Generic
-              -- ^ Use decimal notation for values between @0.1@ and
-              -- @9,999,999@, and scientific notation otherwise.
-                deriving (Enum, Read, Show)
-
--- | Render a floating point number using a much faster algorithm than
--- the default (up to 10x faster). This performance comes with a
--- potential cost in readability, as the faster algorithm can produce
--- strings that are longer than the default algorithm
--- (e.g. \"@1.3300000000000001@\" instead of \"@1.33@\").
-newtype Fast a = Fast {
-      fromFast :: a
-    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
-                Floating, RealFloat)
-
--- | Use this @newtype@ wrapper for your single parameter if you are
--- formatting a string containing exactly one substitution site.
-newtype Only a = Only {
-      fromOnly :: a
-    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
-                Floating, RealFloat, Enum, Integral, Bounded)
-
--- | Render a value using its 'Show' instance.
-newtype Shown a = Shown {
-      shown :: a
-    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
-                Floating, RealFloat, Enum, Integral, Bounded)
+import Data.Text.Format.Types.Internal

Data/Text/Format/Types/Internal.hs

+{-# LANGUAGE DeriveDataTypeable, GeneralizedNewtypeDeriving #-}
+
+-- |
+-- Module      : Data.Text.Format.Types.Internal
+-- Copyright   : (c) 2011 MailRank, Inc.
+--
+-- License     : BSD-style
+-- Maintainer  : bos@mailrank.com
+-- Stability   : experimental
+-- Portability : GHC
+--
+-- Types for text mangling.
+
+module Data.Text.Format.Types.Internal
+    (
+      Format(..)
+    , FPFormat(..)
+    , Fast(..)
+    , Only(..)
+    , Shown(..)
+    ) where
+
+import Data.Monoid (Monoid(..))
+import Data.String (IsString(..))
+import Data.Text (Text)
+import Data.Typeable (Typeable)
+
+-- | A format string. This is intentionally incompatible with other
+-- string types, to make it difficult to construct a format string by
+-- concatenating string fragments (a very common way to accidentally
+-- make code vulnerable to malicious data).
+--
+-- This type is an instance of 'IsString', so the easiest way to
+-- construct a query is to enable the @OverloadedStrings@ language
+-- extension and then simply write the query in double quotes.
+--
+-- > {-# LANGUAGE OverloadedStrings #-}
+-- >
+-- > import Data.Text.Format
+-- >
+-- > f :: Format
+-- > f = "hello {}"
+--
+-- The underlying type is 'Text', so literal Haskell strings that
+-- contain Unicode characters will be correctly handled.
+newtype Format = Format Text
+    deriving (Eq, Ord, Typeable)
+
+instance Monoid Format where
+    Format a `mappend` Format b = Format (a `mappend` b)
+    mempty = Format mempty
+
+instance IsString Format where
+    fromString = Format . fromString
+
+-- | Control the rendering of floating point numbers.
+data FPFormat = Exponent
+              -- ^ Scientific notation (e.g. @2.3e123@).
+              | Fixed
+              -- ^ Standard decimal notation.
+              | Generic
+              -- ^ Use decimal notation for values between @0.1@ and
+              -- @9,999,999@, and scientific notation otherwise.
+                deriving (Enum, Read, Show)
+
+-- | Render a floating point number using a much faster algorithm than
+-- the default (up to 10x faster). This performance comes with a
+-- potential cost in readability, as the faster algorithm can produce
+-- strings that are longer than the default algorithm
+-- (e.g. \"@1.3300000000000001@\" instead of \"@1.33@\").
+newtype Fast a = Fast {
+      fromFast :: a
+    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
+                Floating, RealFloat)
+
+-- | Use this @newtype@ wrapper for your single parameter if you are
+-- formatting a string containing exactly one substitution site.
+newtype Only a = Only {
+      fromOnly :: a
+    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
+                Floating, RealFloat, Enum, Integral, Bounded)
+
+-- | Render a value using its 'Show' instance.
+newtype Shown a = Shown {
+      shown :: a
+    } deriving (Eq, Show, Read, Ord, Num, Fractional, Real, RealFrac,
+                Floating, RealFloat, Enum, Integral, Bounded)

text-format.cabal

     Data.Text.Format.RealFloat.Fast
     Data.Text.Format.RealFloat.Fast.Internal
     Data.Text.Format.RealFloat.Functions
+    Data.Text.Format.Types.Internal
 
   build-depends:
     array,
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.