{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE DerivingStrategies, DeriveAnyClass #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeApplications #-}

module DSV.UTF8
  ( EncodeUtf8 (..)
  , DecodeUtf8 (..)
  , InvalidUtf8 (..)
  , utf8View, utf8TextView, encodeTextUtf8, utf8TextMaybe
  ) where

import DSV.ByteString
import DSV.IO
import DSV.Prelude
import DSV.Validation
import DSV.ViewType

-- text
import Data.Text (Text)
import qualified Data.Text
import qualified Data.Text.Encoding


--- Encode ---

class EncodeUtf8 a
  where
    encodeUtf8 :: a -> ByteString

instance EncodeUtf8 Text
  where
    encodeUtf8 =
        Data.Text.Encoding.encodeUtf8

instance EncodeUtf8 [Char]
  where
    encodeUtf8 =
        Data.Text.Encoding.encodeUtf8 .
        Data.Text.pack


--- Decode ---

class DecodeUtf8 a
  where
    decodeUtf8Maybe :: ByteString -> Maybe a

instance DecodeUtf8 Text
  where
    decodeUtf8Maybe =
        rightMaybe .
        Data.Text.Encoding.decodeUtf8'

instance DecodeUtf8 [Char]
  where
    decodeUtf8Maybe =
        fmap Data.Text.unpack .
        rightMaybe .
        Data.Text.Encoding.decodeUtf8'


--- Views ---

data InvalidUtf8 = InvalidUtf8
  deriving stock (Eq, Show)
  deriving anyclass Exception

utf8View :: DecodeUtf8 txt => View InvalidUtf8 ByteString txt
utf8View =
  View $ \x ->
    case decodeUtf8Maybe x of
        Nothing -> Failure InvalidUtf8
        Just y -> Success y

-- | Decode a byte string as UTF-8 text, failing with 'Nothing' if the decoding fails.
utf8TextMaybe :: ByteString -> Maybe Text
utf8TextMaybe = decodeUtf8Maybe @Text

utf8TextView :: View InvalidUtf8 ByteString Text
utf8TextView = utf8View @Text

encodeTextUtf8 :: Text -> ByteString
encodeTextUtf8 = encodeUtf8 @Text

rightMaybe :: Either a b -> Maybe b
rightMaybe = either (const Nothing) Just