{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
module EasyLogger.LogStr
    ( LogStr (..)
    , ToLogStr (..)
    , logStrLen
    , fromLogStr
    , mkMinLogStrLen
    ) where

import qualified Data.ByteString         as BS
import           Data.ByteString.Builder (Builder)
import qualified Data.ByteString.Builder as B
import qualified Data.ByteString.Char8   as S8
import qualified Data.ByteString.Lazy    as BL
import           Data.String             (IsString (..))
import qualified Data.Text               as T
import qualified Data.Text.Encoding      as T
import qualified Data.Text.Lazy          as TL
import qualified Data.Text.Lazy.Encoding as TL

import           Data.Int
import           Data.Word

toBuilder :: BS.ByteString -> Builder
toBuilder :: ByteString -> Builder
toBuilder = ByteString -> Builder
B.byteString

fromBuilder :: Builder -> BS.ByteString
fromBuilder :: Builder -> ByteString
fromBuilder = ByteString -> ByteString
BL.toStrict (ByteString -> ByteString)
-> (Builder -> ByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> ByteString
B.toLazyByteString


-- | Log message builder. Use ('<>') to append two LogStr in O(1).
data LogStr = LogStr !Int Builder

instance Semigroup LogStr where
    {-# INLINE (<>) #-}
    LogStr Int
s1 Builder
b1 <> :: LogStr -> LogStr -> LogStr
<> LogStr Int
s2 Builder
b2 = Int -> Builder -> LogStr
LogStr (Int
s1 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
s2) (Builder
b1 Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
b2)

instance Monoid LogStr where
    mempty :: LogStr
mempty = Int -> Builder -> LogStr
LogStr Int
0 (ByteString -> Builder
toBuilder ByteString
BS.empty)

instance IsString LogStr where
    {-# INLINE fromString #-}
    fromString :: String -> LogStr
fromString = Text -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Text -> LogStr) -> (String -> Text) -> String -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TL.pack

-- | Converting 'LogStr' to 'ByteString'.
fromLogStr :: LogStr -> BS.ByteString
fromLogStr :: LogStr -> ByteString
fromLogStr (LogStr Int
_ Builder
builder) = Builder -> ByteString
fromBuilder Builder
builder

logStrLen :: LogStr -> Int
logStrLen :: LogStr -> Int
logStrLen (LogStr Int
l Builder
_) = Int
l

mkMinLogStrLen :: Int -> LogStr -> LogStr
mkMinLogStrLen :: Int -> LogStr -> LogStr
mkMinLogStrLen Int
minLen logStr :: LogStr
logStr@(LogStr Int
l Builder
_)
  | Int
len Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 = LogStr
logStr LogStr -> LogStr -> LogStr
forall a. Semigroup a => a -> a -> a
<> Int -> Builder -> LogStr
LogStr Int
len (ByteString -> Builder
toBuilder (ByteString -> Builder) -> ByteString -> Builder
forall a b. (a -> b) -> a -> b
$ Int -> Word8 -> ByteString
BS.replicate (Int
minLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
l) Word8
space)
  | Bool
otherwise = LogStr
logStr
  where
    len :: Int
len = Int
minLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
l
    space :: Word8
space = Word8
32


-- | Types that can be converted to a 'LogStr'. Instances for
-- types from the @text@ library use a UTF-8 encoding. Instances
-- for numerical types use a decimal encoding.
class ToLogStr msg where
    toLogStr :: msg -> LogStr

instance ToLogStr LogStr where
    {-# INLINE toLogStr #-}
    toLogStr :: LogStr -> LogStr
toLogStr = LogStr -> LogStr
forall a. a -> a
id
instance ToLogStr S8.ByteString where
    {-# INLINE toLogStr #-}
    toLogStr :: ByteString -> LogStr
toLogStr ByteString
bs = Int -> Builder -> LogStr
LogStr (ByteString -> Int
BS.length ByteString
bs) (ByteString -> Builder
toBuilder ByteString
bs)
instance ToLogStr BL.ByteString where
    {-# INLINE toLogStr #-}
    toLogStr :: ByteString -> LogStr
toLogStr ByteString
b = Int -> Builder -> LogStr
LogStr (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int64
BL.length ByteString
b)) (ByteString -> Builder
B.lazyByteString ByteString
b)
instance ToLogStr Builder where
    {-# INLINE toLogStr #-}
    toLogStr :: Builder -> LogStr
toLogStr Builder
x = let b :: ByteString
b = Builder -> ByteString
B.toLazyByteString Builder
x in Int -> Builder -> LogStr
LogStr (Int64 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ByteString -> Int64
BL.length ByteString
b)) (ByteString -> Builder
B.lazyByteString ByteString
b)
instance ToLogStr String where
    {-# INLINE toLogStr #-}
    toLogStr :: String -> LogStr
toLogStr = Text -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Text -> LogStr) -> (String -> Text) -> String -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TL.pack
instance ToLogStr T.Text where
    {-# INLINE toLogStr #-}
    toLogStr :: Text -> LogStr
toLogStr = ByteString -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (ByteString -> LogStr) -> (Text -> ByteString) -> Text -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
T.encodeUtf8
instance ToLogStr TL.Text where
    {-# INLINE toLogStr #-}
    toLogStr :: Text -> LogStr
toLogStr = ByteString -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (ByteString -> LogStr) -> (Text -> ByteString) -> Text -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
TL.encodeUtf8

-- | @since 2.4.14
instance ToLogStr Int where
    {-# INLINE toLogStr #-}
    toLogStr :: Int -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Int -> Builder) -> Int -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Builder
B.intDec
-- | @since 2.4.14
instance ToLogStr Int8 where
    {-# INLINE toLogStr #-}
    toLogStr :: Int8 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Int8 -> Builder) -> Int8 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int8 -> Builder
B.int8Dec
-- | @since 2.4.14
instance ToLogStr Int16 where
    {-# INLINE toLogStr #-}
    toLogStr :: Int16 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Int16 -> Builder) -> Int16 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int16 -> Builder
B.int16Dec
-- | @since 2.4.14
instance ToLogStr Int32 where
    {-# INLINE toLogStr #-}
    toLogStr :: Int32 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Int32 -> Builder) -> Int32 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int32 -> Builder
B.int32Dec
-- | @since 2.4.14
instance ToLogStr Int64 where
    {-# INLINE toLogStr #-}
    toLogStr :: Int64 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Int64 -> Builder) -> Int64 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int64 -> Builder
B.int64Dec

-- | @since 2.4.14
instance ToLogStr Word where
    {-# INLINE toLogStr #-}
    toLogStr :: Word -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Word -> Builder) -> Word -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word -> Builder
B.wordDec
-- | @since 2.4.14
instance ToLogStr Word8 where
    {-# INLINE toLogStr #-}
    toLogStr :: Word8 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Word8 -> Builder) -> Word8 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Builder
B.word8Dec
-- | @since 2.4.14
instance ToLogStr Word16 where
    {-# INLINE toLogStr #-}
    toLogStr :: Word16 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Word16 -> Builder) -> Word16 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word16 -> Builder
B.word16Dec
-- | @since 2.4.14
instance ToLogStr Word32 where
    {-# INLINE toLogStr #-}
    toLogStr :: Word32 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Word32 -> Builder) -> Word32 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word32 -> Builder
B.word32Dec
-- | @since 2.4.14
instance ToLogStr Word64 where
    {-# INLINE toLogStr #-}
    toLogStr :: Word64 -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Word64 -> Builder) -> Word64 -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> Builder
B.word64Dec

-- | @since 2.4.14
instance ToLogStr Integer where
    {-# INLINE toLogStr #-}
    toLogStr :: Integer -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Integer -> Builder) -> Integer -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Builder
B.integerDec
-- | @since 2.4.14
instance ToLogStr Float where
    {-# INLINE toLogStr #-}
    toLogStr :: Float -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Float -> Builder) -> Float -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Float -> Builder
B.floatDec
-- | @since 2.4.14
instance ToLogStr Double where
    {-# INLINE toLogStr #-}
    toLogStr :: Double -> LogStr
toLogStr = Builder -> LogStr
forall msg. ToLogStr msg => msg -> LogStr
toLogStr (Builder -> LogStr) -> (Double -> Builder) -> Double -> LogStr
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Double -> Builder
B.doubleDec

instance Show LogStr where
  show :: LogStr -> String
show = Text -> String
forall a. Show a => a -> String
show (Text -> String) -> (LogStr -> Text) -> LogStr -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Text
T.decodeUtf8 (ByteString -> Text) -> (LogStr -> ByteString) -> LogStr -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LogStr -> ByteString
fromLogStr

instance Eq LogStr where
  LogStr
a == :: LogStr -> LogStr -> Bool
== LogStr
b = LogStr -> ByteString
fromLogStr LogStr
a ByteString -> ByteString -> Bool
forall a. Eq a => a -> a -> Bool
== LogStr -> ByteString
fromLogStr LogStr
b