{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE TypeFamilies #-} module Buildable where import Data.String import qualified Data.DList as D import Data.Char ( intToDigit ) import Data.Text.Lazy ( Text ) import qualified Data.Text.Lazy.Builder as T import qualified Data.Text.Lazy.Builder.Int as T import Data.Semigroup ( Semigroup(..) ) import qualified Data.Text.Lazy as L import qualified Data.Text as S newtype Sized a = Sized { unSized :: (a, Int) } deriving (Show, Ord, Eq) type SizedStr = Sized (D.DList Char) type SizedBuilder = Sized T.Builder instance IsString a => IsString (Sized a) where fromString s = Sized (fromString s, length s) instance Semigroup a => Semigroup (Sized a) where Sized (a, b) <> Sized (c, d) = Sized (a <> c, b + d) {-# INLINE (<>) #-} instance MONOID_HEAD => Monoid (Sized a) where mempty = Sized (mempty, 0) mappend = (<>) {-# INLINE mappend #-} class MONOID_HEAD => Buildable a where type Output a :: * str :: String -> a sText :: S.Text -> a sText = str . S.unpack lText :: L.Text -> a lText = str . L.unpack singleton :: Char -> a digit :: Int -> a digit = singleton . intToDigit {-# INLINE digit #-} cons :: Char -> a -> a cons c s = singleton c <> s {-# INLINE cons #-} repeatN :: Int -> Char -> a repeatN n = str . replicate n size :: a -> Int finalize :: a -> Output a instance Buildable SizedStr where type Output SizedStr = String str a = Sized (D.fromList a, length a) singleton c = Sized (D.singleton c, 1) finalize = D.toList . fst . unSized cons c (Sized (r, m)) = Sized (D.cons c r, m + 1) repeatN n c = Sized (D.replicate n c, n) size = snd . unSized instance Buildable SizedBuilder where type Output SizedBuilder = Text str a = Sized (fromString a, length a) sText a = Sized (T.fromText a, S.length a) lText a = Sized (T.fromLazyText a, fromIntegral (L.length a)) singleton c = Sized (T.singleton c, 1) digit c = Sized (T.hexadecimal c, 1) finalize = T.toLazyText . fst . unSized size = snd . unSized