{-# OPTIONS_GHC -fglasgow-exts #-}

-- AbsString.hs
-- An abstract string class, which makes it easier to switch string representations.

module AbsString where

import Prelude as P
import Data.ByteString.Base (c2w, w2c)
import Data.ByteString.Char8 as BSC
import Data.ByteString.Lazy.Char8 as BSLC
import Text.Regex as RE
import Test.HUnit

class (Eq s, Ord s) => AbsString s where
    s :: String -> s
    toString :: s -> String
    sLength :: s -> Int
    sAppend :: s -> s -> s
    sConcat :: [s] -> s
    putStr :: s -> IO ()
    putStrLn :: s -> IO ()
    readFile :: String -> IO s
    lines :: s -> [s]
    split :: Char -> s -> [s]

(+++) :: AbsString s => s -> s -> s
(+++) = sAppend

instance AbsString String where
    s = id
    toString = id
    sLength = P.length
    sAppend = (++)
    sConcat = P.concat
    putStr = P.putStr
    putStrLn = P.putStrLn
    readFile fn = P.readFile fn
    lines = P.lines
    split c = RE.splitRegex (mkRegex (escapeRegexChar c))

regexMetaChars = "\\|()[]^.*+?{}"

escapeRegexChar :: Char -> String
escapeRegexChar c = if c `P.elem` regexMetaChars then "\\"++[c] else [c]

instance AbsString BSC.ByteString where
    s = BSC.pack
    toString = BSC.unpack
    sLength = fromIntegral . BSC.length
    sAppend = BSC.append
    sConcat = BSC.concat
    putStr = BSC.putStr
    putStrLn = BSC.putStrLn
    readFile = BSC.readFile
    lines = BSC.lines
    split = BSC.split

instance AbsString BSLC.ByteString where
    s = BSLC.pack
    toString = BSLC.unpack
    sLength = fromIntegral . BSLC.length
    sAppend = BSLC.append
    sConcat = BSLC.concat
    putStr = BSLC.putStr
    putStrLn = BSLC.putStrLn
    readFile = BSLC.readFile
    lines = BSLC.lines
    split c s = BSLC.split c s

test_showString = TestCase $ do
  let aStr = s "Hello there" :: String
  assertEqual "" "Hello there" (toString aStr)

test_showByteString = TestCase $ do
  let aStr = s "Hello there" :: BSC.ByteString
  assertEqual "" "Hello there" (toString aStr)

test_showByteStringLazy = TestCase $ do
  let aStr = s "Hello there" :: BSLC.ByteString
  assertEqual "" "Hello there" (toString aStr)

runTests = runTestTT $
           TestList [TestLabel "test_showString" test_showString,
                     TestLabel "test_showByteString" test_showByteString,
                     TestLabel "test_showByteStringLazy" test_showByteStringLazy]
