txt-0.0.2.0: Data/Text/Lazy.hs
module Data.Text.Lazy
(Text, fromChars, chars, cons, snoc, uncons, unsnoc, reverse, inits, tails,
break, span, breakEnd, spanEnd, breaks, splitAt, length, unfoldr,
findIndex, findIndexEnd, stripPrefix, stripSuffix, isPrefixOf, isSuffixOf,
fromStrict, toStrict, fromChunks, toChunks) where
import Prelude hiding (break, length, reverse, span, splitAt)
import Control.Applicative
import Control.Arrow
import Control.Monad
import Data.Bits
import qualified Data.ByteString.Lazy as B
import qualified Data.ByteString.Lazy.UTF8 as UTF
import Data.Foldable hiding (length)
import Data.Function (on)
import Data.Functor.Const (Const (..))
import Data.List.NonEmpty (NonEmpty (..))
import qualified Data.List.NonEmpty as NE
import Data.Monoid (Sum (..))
import Data.String
import Data.Tuple (swap)
import Data.Word
import Util
import Data.Text.Lazy.Private
import qualified Data.Text.Private as Strict
chars :: Applicative p => (Char -> p Char) -> Text -> p Text
chars f = uncons & \ case Just (x, xs) -> cons <$> f x <*> chars f xs
Nothing -> pure mempty
fromChars :: Foldable f => f Char -> Text
fromChars = Text . fromString . toList
cons :: Char -> Text -> Text
cons x xs = fromString [x] <> xs
snoc :: Text -> Char -> Text
snoc xs x = xs <> fromString [x]
uncons :: Text -> Maybe (Char, Text)
uncons = unText & UTF.uncons & (fmap . fmap) Text
unsnoc :: Text -> Maybe (Text, Char)
unsnoc (Text xs) = [(Text xs, x) | k <- B.findIndexEnd isStartByte xs
, (xs, ys) <- Just $ B.splitAt k xs
, (x, _) <- UTF.decode ys]
reverse :: Text -> Text
reverse = unfoldr (fmap swap . unsnoc)
unfoldr :: (a -> Maybe (Char, a)) -> a -> Text
unfoldr f = Text . B.unfoldr go . (,) mempty
where go (bs, a) | Just (b, bs) <- B.uncons bs = Just (b, (bs, a))
| otherwise = f a >>= (UTF.fromString . pure *** id >>> go)
splitAt :: Word -> Text -> (Text, Text)
splitAt n (Text bs) = join (***) Text . go 0 n $ bs
where go k 0 _ = B.splitAt k bs
go k n bs' = case UTF.decode bs' of Nothing -> (bs, mempty)
Just (_, δ) -> go (k+δ) (n-1) (B.drop δ bs')
break, span, breakEnd, spanEnd :: (Char -> Bool) -> Text -> (Text, Text)
break f = join (***) Text . UTF.break f . unText
span f = join (***) Text . UTF.span f . unText
breakEnd f = spanEnd (not . f)
spanEnd f xs | Just k <- findIndexEnd (not . f) xs = splitAt k xs
| otherwise = (mempty, xs)
findIndex, findIndexEnd :: Alternative f => (Char -> Bool) -> Text -> f Word
findIndex f = go 0
where go n = uncons & \ case Nothing -> empty
Just (x, xs) -> n <$ guard (f x) <|> go (n+1) xs
findIndexEnd f = flip go <*> \ xs -> length xs - 1
where go n = unsnoc & \ case Nothing -> empty
Just (xs, x) -> n <$ guard (f x) <|> go (n-1) xs
breaks :: Text -> NonEmpty (Text, Text)
breaks = liftA2 NE.zip inits tails
inits, tails :: Text -> NonEmpty Text
inits = NE.unfoldr $ id &&& unsnoc & fmap fst
tails = NE.unfoldr $ id &&& uncons & fmap snd
length :: Text -> Word
length = getSum . getConst . chars (pure . Const $ Sum 1)
stripPrefix, stripSuffix :: Text -> Text -> Maybe Text
stripPrefix = fmap Text ∘∘ B.stripPrefix `on` unText
stripSuffix = fmap Text ∘∘ B.stripSuffix `on` unText
isPrefixOf, isSuffixOf :: Text -> Text -> Bool
isPrefixOf = B.isPrefixOf `on` unText
isSuffixOf = B.isSuffixOf `on` unText
isStartByte, isContByte :: Word8 -> Bool
isStartByte = not . isContByte
isContByte b = 0x80 == b .&. 0xC0
fromStrict :: Strict.Text -> Text
fromStrict = Text . B.fromStrict . Strict.unText
toStrict :: Text -> Strict.Text
toStrict = Strict.Text . B.toStrict . unText
fromChunks :: Foldable f => f Strict.Text -> Text
fromChunks = Text . B.fromChunks . fmap Strict.unText . toList
toChunks :: Text -> [Strict.Text]
toChunks = fmap Strict.Text . B.toChunks . unText