-- |
-- Module         : Data.ByteString.Search.Substitution
-- Copyright      : Daniel Fischer
-- Licence        : BSD3
-- Maintainer     : Daniel Fischer <daniel.is.fischer@googlemail.com>
-- Stability      : Provisional
-- Portability    : portable
--
-- Class for values to be substituted into strict and lazy 'S.ByteString's
-- by the @replace@ functions defined in this package.
--
module Data.ByteString.Search.Substitution ( Substitution(..)) where

import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Lazy.Internal as LI

-- | Type class of meaningful substitutions for replace functions
--   on ByteStrings. Instances for strict and lazy ByteStrings are
--   provided here.
class Substitution a where
    -- | @'substitution'@ transforms a value to a substitution function.
    substitution :: a -> ([S.ByteString] -> [S.ByteString])
    -- | @'prependCycle' sub lazyBS@ shall prepend infinitely many copies
    --   of @sub@ to @lazyBS@ without entering an infinite loop in case
    --   of an empty @sub@, so e.g.
    --
    -- @
    --   'prependCycle' \"\" \"ab\" == \"ab\"
    -- @
    --
    -- shall (quickly) evaluate to 'True'.
    -- For non-empty @sub@, the cycle shall be constructed efficiently.
    prependCycle :: a -> (L.ByteString -> L.ByteString)

instance Substitution S.ByteString where
    {-# INLINE substitution #-}
    substitution :: ByteString -> [ByteString] -> [ByteString]
substitution ByteString
sub = if ByteString -> Bool
S.null ByteString
sub then [ByteString] -> [ByteString]
forall a. a -> a
id else (ByteString
sub ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:)
    {-# INLINE prependCycle #-}
    prependCycle :: ByteString -> ByteString -> ByteString
prependCycle ByteString
sub
        | ByteString -> Bool
S.null ByteString
sub    = ByteString -> ByteString
forall a. a -> a
id
        | Bool
otherwise     = let c :: ByteString
c = ByteString -> ByteString -> ByteString
LI.Chunk ByteString
sub ByteString
c in ByteString -> ByteString -> ByteString
forall a b. a -> b -> a
const ByteString
c

instance Substitution L.ByteString where
    {-# INLINE substitution #-}
    substitution :: ByteString -> [ByteString] -> [ByteString]
substitution ByteString
LI.Empty = [ByteString] -> [ByteString]
forall a. a -> a
id
    substitution (LI.Chunk ByteString
c ByteString
t) = (ByteString
c ByteString -> [ByteString] -> [ByteString]
forall a. a -> [a] -> [a]
:) ([ByteString] -> [ByteString])
-> ([ByteString] -> [ByteString]) -> [ByteString] -> [ByteString]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([ByteString] -> ByteString -> [ByteString])
-> ByteString -> [ByteString] -> [ByteString]
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((ByteString -> [ByteString] -> [ByteString])
-> [ByteString] -> ByteString -> [ByteString]
forall a. (ByteString -> a -> a) -> a -> ByteString -> a
LI.foldrChunks (:)) ByteString
t
    {-# INLINE prependCycle #-}
    prependCycle :: ByteString -> ByteString -> ByteString
prependCycle ByteString
sub
        | ByteString -> Bool
L.null ByteString
sub    = ByteString -> ByteString
forall a. a -> a
id
    prependCycle ByteString
sub = let cyc :: ByteString
cyc = (ByteString -> ByteString -> ByteString)
-> ByteString -> ByteString -> ByteString
forall a. (ByteString -> a -> a) -> a -> ByteString -> a
LI.foldrChunks ByteString -> ByteString -> ByteString
LI.Chunk ByteString
cyc ByteString
sub in ByteString -> ByteString -> ByteString
forall a b. a -> b -> a
const ByteString
cyc