-- | Folds for text streams

module Control.Foldl.Text (
    -- * Folding
      fold
    , foldM

    -- * Folds
    , head
    , last
    , null
    , length
    , any
    , all
    , maximum
    , minimum
    , elem
    , notElem
    , find
    , index
    , elemIndex
    , findIndex
    , count
    , lazy

    -- * Re-exports
    -- $reexports
    , module Control.Foldl
    , module Data.Text
    ) where

import Control.Foldl (Fold, FoldM)
import Control.Foldl.Internal (Maybe'(..), strict, Either'(..), hush)
import Data.Text (Text)
import Prelude hiding (
    head, last, null, length, any, all, maximum, minimum, elem, notElem )

import qualified Control.Foldl
import qualified Control.Foldl.Internal
import qualified Data.Text
import qualified Data.Text.Lazy

-- | Apply a strict left 'Fold' to lazy text
fold :: Fold Text a -> Data.Text.Lazy.Text -> a
fold :: forall a. Fold Text a -> Text -> a
fold (Control.Foldl.Fold x -> Text -> x
step x
begin x -> a
done) Text
as =
    x -> a
done (forall a. (a -> Text -> a) -> a -> Text -> a
Data.Text.Lazy.foldlChunks x -> Text -> x
step x
begin Text
as)
{-# INLINABLE fold #-}

-- | Apply a strict monadic left 'FoldM' to lazy text
foldM :: Monad m => FoldM m Text a -> Data.Text.Lazy.Text -> m a
foldM :: forall (m :: * -> *) a. Monad m => FoldM m Text a -> Text -> m a
foldM (Control.Foldl.FoldM x -> Text -> m x
step m x
begin x -> m a
done) Text
as = do
    x
x <- forall a. (a -> Text -> a) -> a -> Text -> a
Data.Text.Lazy.foldlChunks m x -> Text -> m x
step' m x
begin Text
as
    x -> m a
done x
x
  where
    step' :: m x -> Text -> m x
step' m x
mx Text
bs = do
      x
x <- m x
mx
      x
x seq :: forall a b. a -> b -> b
`seq` x -> Text -> m x
step x
x Text
bs
{-# INLINABLE foldM #-}

{-| Get the first character of a text stream or return 'Nothing' if the stream
    is empty
-}
head :: Fold Text (Maybe Char)
head :: Fold Text (Maybe Char)
head = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Maybe' Char -> Text -> Maybe' Char
step forall a. Maybe' a
Nothing' forall a. Maybe' a -> Maybe a
Control.Foldl.Internal.lazy
  where
    step :: Maybe' Char -> Text -> Maybe' Char
step Maybe' Char
mc Text
txt =
        if Text -> Bool
Data.Text.null Text
txt
        then Maybe' Char
mc
        else case Maybe' Char
mc of
            Just' Char
_  -> Maybe' Char
mc
            Maybe' Char
Nothing' -> forall a. a -> Maybe' a
Just' (Text -> Char
Data.Text.head Text
txt)
{-# INLINABLE head #-}

{-| Get the last character of a text stream or return 'Nothing' if the text
    stream is empty
-}
last :: Fold Text (Maybe Char)
last :: Fold Text (Maybe Char)
last = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Maybe' Char -> Text -> Maybe' Char
step forall a. Maybe' a
Nothing' forall a. Maybe' a -> Maybe a
Control.Foldl.Internal.lazy
  where
    step :: Maybe' Char -> Text -> Maybe' Char
step Maybe' Char
mc Text
txt =
        if Text -> Bool
Data.Text.null Text
txt
        then Maybe' Char
mc
        else forall a. a -> Maybe' a
Just' (Text -> Char
Data.Text.last Text
txt)
        -- TODO: Use `unsafeLast` when Debian Stable Haskell Platform has it
{-# INLINABLE last #-}

-- | Returns 'True' if the text stream is empty, 'False' otherwise
null :: Fold Text Bool
null :: Fold Text Bool
null = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Bool -> Text -> Bool
step Bool
True forall a. a -> a
id
  where
    step :: Bool -> Text -> Bool
step Bool
isNull Text
txt = Bool
isNull Bool -> Bool -> Bool
&& Text -> Bool
Data.Text.null Text
txt 
{-# INLINABLE null #-}

-- | Return the length of the text stream in characters
length :: Num n => Fold Text n
length :: forall n. Num n => Fold Text n
length =
    forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold (\n
n Text
txt -> n
n forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int
Data.Text.length Text
txt)) n
0 forall a. a -> a
id
{-# INLINABLE length #-}

{-| @(all predicate)@ returns 'True' if all characters satisfy the predicate,
    'False' otherwise
-}
all :: (Char -> Bool) -> Fold Text Bool
all :: (Char -> Bool) -> Fold Text Bool
all Char -> Bool
predicate =
    forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold (\Bool
b Text
txt -> Bool
b Bool -> Bool -> Bool
&& (Char -> Bool) -> Text -> Bool
Data.Text.all Char -> Bool
predicate Text
txt) Bool
True forall a. a -> a
id
{-# INLINABLE all #-}

{-| @(any predicate)@ returns 'True' if any character satisfies the predicate,
    'False' otherwise
-}
any :: (Char -> Bool) -> Fold Text Bool
any :: (Char -> Bool) -> Fold Text Bool
any Char -> Bool
predicate =
    forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold (\Bool
b Text
txt -> Bool
b Bool -> Bool -> Bool
|| (Char -> Bool) -> Text -> Bool
Data.Text.any Char -> Bool
predicate Text
txt) Bool
False forall a. a -> a
id
{-# INLINABLE any #-}

-- | Computes the maximum character
maximum :: Fold Text (Maybe Char)
maximum :: Fold Text (Maybe Char)
maximum = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Maybe' Char -> Text -> Maybe' Char
step forall a. Maybe' a
Nothing' forall a. Maybe' a -> Maybe a
Control.Foldl.Internal.lazy
  where
    step :: Maybe' Char -> Text -> Maybe' Char
step Maybe' Char
mc Text
txt =
        if Text -> Bool
Data.Text.null Text
txt
        then Maybe' Char
mc
        else forall a. a -> Maybe' a
Just' (case Maybe' Char
mc of
            Maybe' Char
Nothing' -> Text -> Char
Data.Text.maximum Text
txt
            Just' Char
c -> forall a. Ord a => a -> a -> a
max Char
c (Text -> Char
Data.Text.maximum Text
txt) )
{-# INLINABLE maximum #-}

-- | Computes the minimum character
minimum :: Fold Text (Maybe Char)
minimum :: Fold Text (Maybe Char)
minimum = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Maybe' Char -> Text -> Maybe' Char
step forall a. Maybe' a
Nothing' forall a. Maybe' a -> Maybe a
Control.Foldl.Internal.lazy
  where
    step :: Maybe' Char -> Text -> Maybe' Char
step Maybe' Char
mc Text
txt =
        if Text -> Bool
Data.Text.null Text
txt
        then Maybe' Char
mc
        else forall a. a -> Maybe' a
Just' (case Maybe' Char
mc of
            Maybe' Char
Nothing' -> Text -> Char
Data.Text.minimum Text
txt
            Just' Char
c -> forall a. Ord a => a -> a -> a
min Char
c (Text -> Char
Data.Text.minimum Text
txt) )
{-# INLINABLE minimum #-}

{-| @(elem c)@ returns 'True' if the text stream has a character equal to @c@,
    'False' otherwise
-}
elem :: Char -> Fold Text Bool
elem :: Char -> Fold Text Bool
elem Char
c = (Char -> Bool) -> Fold Text Bool
any (Char
c forall a. Eq a => a -> a -> Bool
==)
{-# INLINABLE elem #-}

{-| @(notElem c)@ returns 'False' if the text stream has a character equal to
    @c@, 'True' otherwise
-}
notElem :: Char -> Fold Text Bool
notElem :: Char -> Fold Text Bool
notElem Char
c = (Char -> Bool) -> Fold Text Bool
all (Char
c forall a. Eq a => a -> a -> Bool
/=)
{-# INLINABLE notElem #-}

{-| @(find predicate)@ returns the first character that satisfies the predicate
    or 'Nothing' if no character satisfies the predicate
-}
find :: (Char -> Bool) -> Fold Text (Maybe Char)
find :: (Char -> Bool) -> Fold Text (Maybe Char)
find Char -> Bool
predicate = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Maybe' Char -> Text -> Maybe' Char
step forall a. Maybe' a
Nothing' forall a. Maybe' a -> Maybe a
Control.Foldl.Internal.lazy
  where
    step :: Maybe' Char -> Text -> Maybe' Char
step Maybe' Char
mc Text
txt = case Maybe' Char
mc of
        Maybe' Char
Nothing' -> forall a. Maybe a -> Maybe' a
strict ((Char -> Bool) -> Text -> Maybe Char
Data.Text.find Char -> Bool
predicate Text
txt)
        Just' Char
_  -> Maybe' Char
mc
{-# INLINABLE find #-}

{-| @(index n)@ returns the @n@th character of the text stream, or 'Nothing' if
    the stream has an insufficient number of characters
-}
index :: Integral n => n -> Fold Text (Maybe Char)
index :: forall n. Integral n => n -> Fold Text (Maybe Char)
index n
i = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold Either' Int Char -> Text -> Either' Int Char
step (forall a b. a -> Either' a b
Left' (forall a b. (Integral a, Num b) => a -> b
fromIntegral n
i)) forall a b. Either' a b -> Maybe b
hush
  where
    step :: Either' Int Char -> Text -> Either' Int Char
step Either' Int Char
x Text
txt = case Either' Int Char
x of
        Left' Int
remainder ->
            let len :: Int
len = Text -> Int
Data.Text.length Text
txt
            in  if Int
remainder forall a. Ord a => a -> a -> Bool
< Int
len
                then forall a b. b -> Either' a b
Right' (Text -> Int -> Char
Data.Text.index Text
txt Int
remainder)
                else forall a b. a -> Either' a b
Left'  (Int
remainder forall a. Num a => a -> a -> a
- Int
len)
        Either' Int Char
_               -> Either' Int Char
x
{-# INLINABLE index #-}

{-| @(elemIndex c)@ returns the index of the first character that equals @c@,
    or 'Nothing' if no character matches
-}
elemIndex :: Num n => Char -> Fold Text (Maybe n)
elemIndex :: forall n. Num n => Char -> Fold Text (Maybe n)
elemIndex Char
c = forall n. Num n => (Char -> Bool) -> Fold Text (Maybe n)
findIndex (Char
c forall a. Eq a => a -> a -> Bool
==)
{-# INLINABLE elemIndex #-}

{-| @(findIndex predicate)@ returns the index of the first character that
    satisfies the predicate, or 'Nothing' if no character satisfies the
    predicate
-}
findIndex :: Num n => (Char -> Bool) -> Fold Text (Maybe n)
findIndex :: forall n. Num n => (Char -> Bool) -> Fold Text (Maybe n)
findIndex Char -> Bool
predicate = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold forall {b}. Num b => Either' b b -> Text -> Either' b b
step (forall a b. a -> Either' a b
Left' n
0) forall a b. Either' a b -> Maybe b
hush
  where
    step :: Either' b b -> Text -> Either' b b
step Either' b b
x Text
txt = case Either' b b
x of
        Left' b
m -> case (Char -> Bool) -> Text -> Maybe Int
Data.Text.findIndex Char -> Bool
predicate Text
txt of
            Maybe Int
Nothing -> forall a b. a -> Either' a b
Left'  (b
m forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Int
Data.Text.length Text
txt))
            Just Int
n  -> forall a b. b -> Either' a b
Right' (b
m forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
n)
        Either' b b
_       -> Either' b b
x
{-# INLINABLE findIndex #-}

-- | @(count c)@ returns the number of times @c@ appears
count :: Num n => Char -> Fold Text n
count :: forall n. Num n => Char -> Fold Text n
count Char
c = forall a b x. (x -> a -> x) -> x -> (x -> b) -> Fold a b
Control.Foldl.Fold forall {a}. Num a => a -> Text -> a
step n
0 forall a. a -> a
id
  where
    step :: a -> Text -> a
step a
n Text
txt = a
n forall a. Num a => a -> a -> a
+ forall a b. (Integral a, Num b) => a -> b
fromIntegral (Text -> Text -> Int
Data.Text.count (Char -> Text
Data.Text.singleton Char
c) Text
txt)
{-# INLINABLE count #-}

-- | Combine all the strict `Text` chunks to build a lazy `Text`
lazy :: Fold Text Data.Text.Lazy.Text
lazy :: Fold Text Text
lazy = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [Text] -> Text
Data.Text.Lazy.fromChunks forall a. Fold a [a]
Control.Foldl.list
{-# INLINABLE lazy #-}

{- $reexports
    "Control.Foldl" re-exports the 'Fold' type

    @Data.Text@ re-exports the 'Text' type
-}