email-header-0.2.0: src/Network/Email/Header/Layout.hs
-- | A layout that keeps track of line positions.
module Network.Email.Header.Layout
( Layout
, layout
, span
, break
, position
, nicest
) where
import Prelude hiding (span, break)
import Data.Monoid
type LayoutStep a = Int -> (Int -> Bool, a)
-- | An abstract type representing a lazy layout.
newtype Layout a = Layout { runLayout :: LayoutStep a -> LayoutStep a }
instance Monoid (Layout a) where
mempty = Layout id
mappend a b = Layout $ runLayout a . runLayout b
-- | Run a layout with an initial position.
layout :: Monoid a => Int -> Layout a -> a
layout p l = snd $ runLayout l (const (const True, mempty)) p
-- | Layout an element of a given length.
span :: Monoid a => Int -> a -> Layout a
span k s = Layout $ \c p ->
let ~(fits, b) = c (p + k)
in (\w -> p <= w && fits w, s <> b)
-- | Layout a new line and set the initial position.
break :: Int -> Layout a
break i = Layout $ \c p ->
let ~(_, b) = c i
in (\w -> p <= w, b)
-- | Use the current line position to produce a layout.
position :: (Int -> Layout a) -> Layout a
position f = Layout $ \c p -> runLayout (f p) c p
-- | Choose the first layout if the first line fits within the given length,
-- and the second otherwise.
nicest :: Int -> Layout a -> Layout a -> Layout a
nicest w x y = Layout $ \c p ->
let a@(fits, _) = runLayout x c p
b = runLayout y c p
in if fits w then a else b