{-# OPTIONS -fno-warn-missing-methods #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE NoRebindableSyntax #-}
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE StandaloneDeriving #-}

-- | Compatible API with the `text' package.

module Data.Text
  ( Text
  -- * Creation and elimination
  , pack
  , unpack
  , fromString
  , empty
  -- * Conversions
  , showInt
  , toShortest
  -- * I/O
  , putStrLn
  -- * Breaking into many substrings
  , splitOn
  , stripSuffix
  -- * Basic interface
  , cons
  , snoc
  , append
  , (<>)
  , uncons
  , head
  , init
  , last
  , tail
  , null
  , length
  -- * Special folds
  , maximum
  , all
  , any
  , concatMap
  , concat
  , minimum
  -- * Case conversion
  , toLower
  , toUpper
  -- * Transformations
  , map
  , intercalate
  , intersperse
  , reverse
  -- * Predicates
  , isPrefixOf
  -- * Substrings
  , drop
  , take
  -- * Breaking into lines and words
  , unlines
  , lines
  ) where

import Data.Data
import FFI
import Data.Nullable (fromNullable)
import Prelude (Eq,String,Int,Bool,Char,Maybe,Double,Ord,Show,error)
import qualified "base" Data.String as B (IsString (..))

-- | A space efficient, packed, unboxed Unicode text type.
data Text
deriving instance Eq Text
deriving instance Data Text
deriving instance Typeable Text
deriving instance Show Text
instance Ord Text
instance B.IsString Text where fromString :: String -> Text
fromString = String -> String -> Text
forall a. String -> a
error String
"the method fromString can never be called"

-- | O(n) The intercalate function takes a Text and a list of Texts and
-- concatenates the list after interspersing the first argument
-- between each element of the list.
intercalate :: Text -> [Text] -> Text
intercalate :: Text -> [Text] -> Text
intercalate = String -> Text -> [Text] -> Text
forall s a. IsString s => s -> a
ffi String
"%2.join(%1)"

-- | Convert from a string to text.
fromString :: String -> Text
fromString :: String -> Text
fromString = String -> String -> Text
forall s a. IsString s => s -> a
ffi String
"%1"

-- | O(n) Adds a character to the end of a Text. This copies the
-- entire array in the process, unless fused. Subject to
-- fusion. Performs replacement on invalid scalar values.
snoc :: Text -> Char -> Text
snoc :: Text -> Char -> Text
snoc = String -> Text -> Char -> Text
forall s a. IsString s => s -> a
ffi String
"%1 + %2"

-- | O(n) Adds a character to the front of a Text. This function is
-- more costly than its List counterpart because it requires copying a
-- new array. Subject to fusion. Performs replacement on invalid
-- scalar values.
cons :: Char -> Text -> Text
cons :: Char -> Text -> Text
cons = String -> Char -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1 + %2"

-- | O(n) Convert a String into a Text. Subject to fusion. Performs
-- replacement on invalid scalar values.
pack :: String -> Text
pack :: String -> Text
pack = String -> String -> Text
forall s a. IsString s => s -> a
ffi String
"%1"

-- | O(n) Convert a Text into a String. Subject to fusion.
unpack :: Text -> String
unpack :: Text -> String
unpack = String -> Text -> String
forall s a. IsString s => s -> a
ffi String
"%1"

-- | O(n) Appends one Text to the other by copying both of them into a
-- new Text. Subject to fusion.
append :: Text -> Text -> Text
append :: Text -> Text -> Text
append = String -> Text -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1 + %2"

-- | Append two texts.
(<>) :: Text -> Text -> Text
<> :: Text -> Text -> Text
(<>) = String -> Text -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1 + %2"

-- | O(n) Returns the number of characters in a Text. Subject to
-- fusion.
length :: Text -> Int
length :: Text -> Int
length = String -> Text -> Int
forall s a. IsString s => s -> a
ffi String
"%1.length"

-- | O(1) Tests whether a Text is empty or not. Subject to fusion.
null :: Text -> Bool
null :: Text -> Bool
null = String -> Text -> Bool
forall s a. IsString s => s -> a
ffi String
"%1.length == 0"

-- | O(n) take n, applied to a Text, returns the prefix of the Text of
-- length n, or the Text itself if n is greater than the length of the
-- Text. Subject to fusion.
take :: Int -> Text -> Text
take :: Int -> Text -> Text
take = String -> Int -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%2.substring(0,%1)"

-- | O(n) drop n, applied to a Text, returns the suffix of the Text
-- after the first n characters, or the empty Text if n is greater
-- than the length of the Text. Subject to fusion.
drop :: Int -> Text -> Text
drop :: Int -> Text -> Text
drop = String -> Int -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%2.substring(%1)"

-- | O(1) The empty Text.

-- Basic interface
empty :: Text
empty :: Text
empty = String -> Text
forall s a. IsString s => s -> a
ffi String
"\"\""

-- | O(n) Breaks a Text up into a list of Texts at newline Chars. The
-- resulting strings do not contain newlines.
lines :: Text -> [Text]
lines :: Text -> [Text]
lines = String -> Text -> [Text]
forall s a. IsString s => s -> a
ffi String
"%1.split('\\n')"

-- | O(n) Joins lines, after appending a terminating newline to each.
unlines :: [Text] -> Text
unlines :: [Text] -> Text
unlines = String -> [Text] -> Text
forall s a. IsString s => s -> a
ffi String
"%1.join('\\n')"

-- | O(n) The isPrefixOf function takes two Texts and returns True iff
-- the first is a prefix of the second. Subject to fusion.
-- http://docs.closure-library.googlecode.com/git/closure_goog_string_string.js.source.html
isPrefixOf :: Text -> Text -> Bool
isPrefixOf :: Text -> Text -> Bool
isPrefixOf = String -> Text -> Text -> Bool
forall s a. IsString s => s -> a
ffi String
"%2.lastIndexOf(%1, 0) == 0"

-- | O(n) The intersperse function takes a character and places it
-- between the characters of a Text.  Subject to fusion. Performs
-- replacement on invalid scalar values.
intersperse :: Char -> Text -> Text
intersperse :: Char -> Text -> Text
intersperse = String -> Char -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%2.split('').join(%1)"

-- | O(n) Reverse the characters of a string. Subject to fusion.
reverse :: Text -> Text
reverse :: Text -> Text
reverse = String -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1.split('').reverse().join('')"

-- | O(n) Return the prefix of the second string if its suffix matches
-- the entire first string.
stripSuffix :: Text -- ^ Suffix.
                -> Text -- ^ Text.
                -> Maybe Text
stripSuffix :: Text -> Text -> Maybe Text
stripSuffix Text
prefix Text
text =
  Nullable Text -> Maybe Text
forall a. Nullable a -> Maybe a
fromNullable (Text -> Text -> Nullable Text
extract Text
prefix Text
text)
  where extract :: Text -> Text -> Nullable Text
        extract :: Text -> Text -> Nullable Text
extract =
          String -> Text -> Text -> Nullable Text
forall s a. IsString s => s -> a
ffi String
"(function(suffix,text){ return text.substring(text.length - suffix.length) == suffix? text.substring(0,text.length - suffix.length) : null; })(%1,%2)"

-- | O(m+n) Break a Text into pieces separated by the first Text
-- argument, consuming the delimiter. An empty delimiter is
-- invalid, and will cause an error to be raised.
splitOn :: Text -> Text -> [Text]
splitOn :: Text -> Text -> [Text]
splitOn = String -> Text -> Text -> [Text]
forall s a. IsString s => s -> a
ffi String
"%2.split(%1)"

-- |
putStrLn :: Text -> Fay ()
putStrLn :: Text -> Fay ()
putStrLn = String -> Text -> Fay ()
forall s a. IsString s => s -> a
ffi String
"console.log('%%s',%1)"

-- |
toShortest :: Double -> Text
toShortest :: Double -> Text
toShortest = String -> Double -> Text
forall s a. IsString s => s -> a
ffi String
"%1.toString()"

-- |
showInt :: Int -> Text
showInt :: Int -> Text
showInt = String -> Int -> Text
forall s a. IsString s => s -> a
ffi String
"%1.toString()"

-- | O(1) Returns the first character and rest of a Text, or Nothing
-- if empty. Subject to fusion.
uncons :: Text -> Maybe (Char, Text)
uncons :: Text -> Maybe (Char, Text)
uncons = String -> Text -> Maybe (Char, Text)
forall s a. IsString s => s -> a
ffi String
"%1[0] ? { instance: 'Just', slot1 : [%1[0],%1.slice(1)] } : { instance : 'Nothing' }"

-- | O(1) Returns the first character of a Text, which must be
-- non-empty. Subject to fusion.
head :: Text -> Char
head :: Text -> Char
head = String -> Text -> Char
forall s a. IsString s => s -> a
ffi String
"%1[0] || (function () {throw new Error('Data.Text.head: empty Text'); }())"

-- | O(1) Returns the last character of a Text, which must be
-- non-empty. Subject to fusion.
last :: Text -> Char
last :: Text -> Char
last = String -> Text -> Char
forall s a. IsString s => s -> a
ffi String
"%1.length ? %1[%1.length-1] : (function() { throw new Error('Data.Text.last: empty Text') })()"

-- | O(1) Returns all characters after the head of a Text, which must
-- be non-empty. Subject to fusion.
tail :: Text -> Text
tail :: Text -> Text
tail = String -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1.length ? %1.slice(1) : (function () { throw new Error('Data.Text.tail: empty Text') })()"

-- | O(1) Returns all but the last character of a Text, which must be
-- non-empty. Subject to fusion.
init :: Text -> Text
init :: Text -> Text
init = String -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1.length ? %1.slice(0,-1) : (function () { throw new Error('Data.Text.init: empty Text') })()"

-- | O(n) map f t is the Text obtained by applying f to each element
-- of t. Subject to fusion. Performs replacement on invalid scalar
-- values.
map :: (Char -> Char) -> Text -> Text
map :: (Char -> Char) -> Text -> Text
map = String -> (Char -> Char) -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"[].map.call(%2, %1).join('')"

-- | O(n) Convert a string to lower case, using simple case
-- conversion. The result string may be longer than the input
-- string. For instance, "İ" (Latin capital letter I with dot above,
-- U+0130) maps to the sequence "i" (Latin small letter i, U+0069)
-- followed by " ̇" (combining dot above, U+0307).
toLower :: Text -> Text
toLower :: Text -> Text
toLower = String -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1.toLowerCase()"

-- | O(n) Convert a string to upper case, using simple case
-- conversion. The result string may be longer than the input
-- string. For instance, the German "ß" (eszett, U+00DF) maps to the
-- two-letter sequence "SS".
toUpper :: Text -> Text
toUpper :: Text -> Text
toUpper = String -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"%1.toUpperCase()"

-- | O(n) Concatenate a list of Texts.
concat :: [Text] -> Text
concat :: [Text] -> Text
concat = String -> [Text] -> Text
forall s a. IsString s => s -> a
ffi String
"%1.join('')"

-- | O(n) Map a function over a Text that results in a Text, and
-- concatenate the results.
concatMap :: (Char -> Text) -> Text -> Text
concatMap :: (Char -> Text) -> Text -> Text
concatMap = String -> (Char -> Text) -> Text -> Text
forall s a. IsString s => s -> a
ffi String
"[].map.call(%2, %1).join('')"

-- | O(n) any p t determines whether any character in the Text t
-- satisifes the predicate p. Subject to fusion.
any :: (Char -> Bool) -> Text -> Bool
any :: (Char -> Bool) -> Text -> Bool
any = String -> (Char -> Bool) -> Text -> Bool
forall s a. IsString s => s -> a
ffi String
"[].filter.call(%2, %1).length > 0"

-- | O(n) all p t determines whether all characters in the Text t
-- satisify the predicate p. Subject to fusion.
all :: (Char -> Bool) -> Text -> Bool
all :: (Char -> Bool) -> Text -> Bool
all = String -> (Char -> Bool) -> Text -> Bool
forall s a. IsString s => s -> a
ffi String
"[].filter.call(%2, %1).length == %1.length"

-- | O(n) maximum returns the maximum value from a Text, which must be
-- non-empty. Subject to fusion.
maximum :: Text -> Char
maximum :: Text -> Char
maximum = String -> Text -> Char
forall s a. IsString s => s -> a
ffi String
"(function (s) { \
  \   if (s === '') { throw new Error('Data.Text.maximum: empty string'); } \
  \   var max = s[0]; \
  \   for (var i = 1; i < s.length; s++) { \
  \     if (s[i] > max) { max = s[i]; } \
  \   } \
  \   return max; \
  \ })(%1)"

-- | O(n) minimum returns the minimum value from a Text, which must be
-- non-empty. Subject to fusion.
minimum :: Text -> Char
minimum :: Text -> Char
minimum = String -> Text -> Char
forall s a. IsString s => s -> a
ffi String
"(function (s) { \
  \   if (s === '') { throw new Error('Data.Text.maximum: empty string'); } \
  \   var min = s[0]; \
  \   for (var i = 1; i < s.length; s++) { \
  \     if (s[i] < min) { min = s[i]; } \
  \   } \
  \   return min; \
  \ })(%1)"