packages feed

symbolize-1.0.2.0: src/Symbolize/Textual.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE InstanceSigs #-}

-- NOTE: FlexibleInstances is needed to support `String` instance :-(

module Symbolize.Textual (Textual (..)) where

import Data.Array.Byte (ByteArray (ByteArray))
import Data.ByteString (ByteString)
import Data.ByteString.Short (ShortByteString)
import qualified Data.ByteString.Short as ShortByteString
import Data.Function ((&))
import Data.Text (Text)
import qualified Data.Text.Encoding as Text.Encoding
import qualified Data.Text.Encoding.Error as Text.Encoding.Error
import qualified Data.Text.Lazy as LText
import qualified Data.Text.Lazy.Builder as Text.Builder
import Data.Text.Short (ShortText)
import qualified Data.Text.Short as ShortText
import qualified Data.Text.Short.Unsafe as ShortText.Unsafe

-- | Implemented by any String-like types.
-- The symbol table uses `ShortText` for its internal storage, so any type which can be converted to it
-- can be turned to/from a `Symbolize.Symbol`.
--
-- Instance should handle potential invalid UTF-8 by using the Unicode replacement character,
-- c.f. `Data.Text.Encoding.Error.lenientDecode`.
class Textual a where
  -- | Turns this text value into a `ShortText`, performing UTF-8 checking if necessary
  toShortText :: a -> ShortText

  -- | Turns this text value into a `ShortText`, potentially skipping UTF-8 checks
  toShortTextUnsafe :: a -> ShortText
  toShortTextUnsafe = toShortText

  -- | Turns a `ShortText` value into a different textual value
  fromShortText :: ShortText -> a

-- |
-- - O(0) conversion (a no-op)
instance Textual ShortText where
  toShortText :: ShortText -> ShortText
  {-# INLINE toShortText #-}
  toShortText = id

  toShortTextUnsafe :: ShortText -> ShortText
  {-# INLINE toShortTextUnsafe #-}
  toShortTextUnsafe = id

  fromShortText :: ShortText -> ShortText
  {-# INLINE fromShortText #-}
  fromShortText = id

-- |
-- - O(1) conversion
instance Textual Text where
  toShortText :: Text -> ShortText
  {-# INLINE toShortText #-}
  toShortText = ShortText.fromText

  fromShortText :: ShortText -> Text
  {-# INLINE fromShortText #-}
  fromShortText = ShortText.toText

-- |
-- - O(n) conversion
instance Textual String where
  toShortText :: String -> ShortText
  {-# INLINE toShortText #-}
  toShortText = ShortText.fromString

  fromShortText :: ShortText -> String
  {-# INLINE fromShortText #-}
  fromShortText = ShortText.toString

-- |
-- - O(1) conversion
instance Textual LText.Text where
  toShortText :: LText.Text -> ShortText
  {-# INLINE toShortText #-}
  toShortText = ShortText.fromText . LText.toStrict

  fromShortText :: ShortText -> LText.Text
  {-# INLINE fromShortText #-}
  fromShortText = LText.fromStrict . ShortText.toText

-- |
-- - toShortText: O(n). Evaluates the entire builder.
-- - fromShortText: O(1)
instance Textual Text.Builder.Builder where
  toShortText :: Text.Builder.Builder -> ShortText
  {-# INLINE toShortText #-}
  toShortText = ShortText.fromText . LText.toStrict . Text.Builder.toLazyText

  fromShortText :: ShortText -> Text.Builder.Builder
  {-# INLINE fromShortText #-}
  fromShortText = Text.Builder.fromText . ShortText.toText

-- |
-- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character.
-- - fromShortText: O(0) no-op
instance Textual ShortByteString where
  toShortText :: ShortByteString -> ShortText
  {-# INLINE toShortText #-}
  toShortText byteString =
    byteString
      & ShortByteString.fromShort
      & Text.Encoding.decodeUtf8With Text.Encoding.Error.lenientDecode
      & ShortText.fromText

  toShortTextUnsafe :: ShortByteString -> ShortText
  {-# INLINE toShortTextUnsafe #-}
  toShortTextUnsafe = ShortText.Unsafe.fromShortByteStringUnsafe

  fromShortText :: ShortText -> ShortByteString
  {-# INLINE fromShortText #-}
  fromShortText = ShortText.toShortByteString

-- |
-- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character.
-- - fromShortText: O(n).
instance Textual ByteString where
  toShortText :: ByteString -> ShortText
  {-# INLINE toShortText #-}
  toShortText byteString =
    byteString
      & Text.Encoding.decodeUtf8With Text.Encoding.Error.lenientDecode
      & ShortText.fromText

  toShortTextUnsafe :: ByteString -> ShortText
  {-# INLINE toShortTextUnsafe #-}
  toShortTextUnsafe = toShortTextUnsafe . ShortByteString.toShort

  fromShortText :: ShortText -> ByteString
  {-# INLINE fromShortText #-}
  fromShortText = ShortText.toByteString

-- |
-- - toShortText: O(n). Turns invalid UTF-8 into the Unicode replacement character.
-- - fromShortText: O(0) no-op
instance Textual ByteArray where
  toShortText :: ByteArray -> ShortText
  {-# INLINE toShortText #-}
  toShortText (ByteArray ba) = toShortText (ShortByteString.SBS ba)

  toShortTextUnsafe :: ByteArray -> ShortText
  {-# INLINE toShortTextUnsafe #-}
  toShortTextUnsafe (ByteArray ba) = toShortTextUnsafe (ShortByteString.SBS ba)

  fromShortText :: ShortText -> ByteArray
  {-# INLINE fromShortText #-}
  fromShortText (fromShortText -> ShortByteString.SBS ba) = ByteArray ba