    {-# LANGUAGE FlexibleContexts #-}
    --------------------------------------------------------------------------------
    -- | Miscellaneous string manipulation functions.
    module Hakyll.Core.Util.String
        ( trim
        , replaceAll
        , splitAll
        , needlePrefix
        , removeWinPathSeparator
        ) where

    --------------------------------------------------------------------------------
    import Data.Char (isSpace)
    import Data.List (isPrefixOf)
    import Data.Maybe (listToMaybe)
    import Text.Regex.TDFA ((=~~))

    --------------------------------------------------------------------------------
    -- | Trim a string (drop spaces, tabs and newlines at both sides).
    trim :: String -> String
    trim :: [Char] -> [Char]
    trim = [Char] -> [Char]
    forall a. [a] -> [a]
    reverse ([Char] -> [Char]) -> ([Char] -> [Char]) -> [Char] -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [Char]
    trim' ([Char] -> [Char]) -> ([Char] -> [Char]) -> [Char] -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [Char]
    forall a. [a] -> [a]
    reverse ([Char] -> [Char]) -> ([Char] -> [Char]) -> [Char] -> [Char]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [Char]
    trim'
      where
        trim' :: [Char] -> [Char]
    trim' = (Char -> Bool) -> [Char] -> [Char]
    forall a. (a -> Bool) -> [a] -> [a]
    dropWhile Char -> Bool
    isSpace

    --------------------------------------------------------------------------------
    -- | A simple (but inefficient) regex replace funcion
    replaceAll :: String              -- ^ Pattern
               -> (String -> String)  -- ^ Replacement (called on match)
               -> String              -- ^ Source string
               -> String              -- ^ Result
    replaceAll :: [Char] -> ([Char] -> [Char]) -> [Char] -> [Char]
    replaceAll [Char]
    pattern [Char] -> [Char]
    f [Char]
    source = [Char] -> [Char]
    replaceAll' [Char]
    source
      where
        replaceAll' :: [Char] -> [Char]
    replaceAll' [Char]
    src = case [(Int, Int)] -> Maybe (Int, Int)
    forall a. [a] -> Maybe a
    listToMaybe ([Char]
    src [Char] -> [Char] -> [(Int, Int)]
    forall source source1 target (m :: * -> *).
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target, MonadFail m) =>
    source1 -> source -> m target
    =~~ [Char]
    pattern) of
            Maybe (Int, Int)
    Nothing     -> [Char]
    src
            Just (Int
    o, Int
    l) ->
                let ([Char]
    before, [Char]
    tmp) = Int -> [Char] -> ([Char], [Char])
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    o [Char]
    src
                    ([Char]
    capture, [Char]
    after) = Int -> [Char] -> ([Char], [Char])
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    l [Char]
    tmp
                in [Char]
    before [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char] -> [Char]
    f [Char]
    capture [Char] -> [Char] -> [Char]
    forall a. [a] -> [a] -> [a]
    ++ [Char] -> [Char]
    replaceAll' [Char]
    after

    --------------------------------------------------------------------------------
    -- | A simple regex split function. The resulting list will contain no empty
    -- strings.
    splitAll :: String    -- ^ Pattern
             -> String    -- ^ String to split
             -> [String]  -- ^ Result
    splitAll :: [Char] -> [Char] -> [[Char]]
    splitAll [Char]
    pattern = ([Char] -> Bool) -> [[Char]] -> [[Char]]
    forall a. (a -> Bool) -> [a] -> [a]
    filter (Bool -> Bool
    not (Bool -> Bool) -> ([Char] -> Bool) -> [Char] -> Bool
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> Bool
    forall a. [a] -> Bool
    forall (t :: * -> *) a. Foldable t => t a -> Bool
    null) ([[Char]] -> [[Char]])
    -> ([Char] -> [[Char]]) -> [Char] -> [[Char]]
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . [Char] -> [[Char]]
    forall {a}. RegexLike Regex [a] => [a] -> [[a]]
    splitAll'
      where
        splitAll' :: [a] -> [[a]]
    splitAll' [a]
    src = case [(Int, Int)] -> Maybe (Int, Int)
    forall a. [a] -> Maybe a
    listToMaybe ([a]
    src [a] -> [Char] -> [(Int, Int)]
    forall source source1 target (m :: * -> *).
    (RegexMaker Regex CompOption ExecOption source,
     RegexContext Regex source1 target, MonadFail m) =>
    source1 -> source -> m target
    =~~ [Char]
    pattern) of
            Maybe (Int, Int)
    Nothing     -> [[a]
    src]
            Just (Int
    o, Int
    l) ->
                let ([a]
    before, [a]
    tmp) = Int -> [a] -> ([a], [a])
    forall a. Int -> [a] -> ([a], [a])
    splitAt Int
    o [a]
    src
                in [a]
    before [a] -> [[a]] -> [[a]]
    forall a. a -> [a] -> [a]
    : [a] -> [[a]]
    splitAll' (Int -> [a] -> [a]
    forall a. Int -> [a] -> [a]
    drop Int
    l [a]
    tmp)

    --------------------------------------------------------------------------------
    -- | Find the first instance of needle (must be non-empty) in haystack. We
    -- return the prefix of haystack before needle is matched.
    --
    -- Examples:
    --
    -- > needlePrefix "cd" "abcde" = "ab"
    --
    -- > needlePrefix "ab" "abc" = ""
    --
    -- > needlePrefix "ab" "xxab" = "xx"
    --
    -- > needlePrefix "a" "xx" = "xx"
    needlePrefix :: String -> String -> Maybe String
    needlePrefix :: [Char] -> [Char] -> Maybe [Char]
    needlePrefix [Char]
    needle [Char]
    haystack = [Char] -> [Char] -> Maybe [Char]
    go [] [Char]
    haystack
      where
        go :: [Char] -> [Char] -> Maybe [Char]
    go [Char]
    _   []                     = Maybe [Char]
    forall a. Maybe a
    Nothing
        go [Char]
    acc xss :: [Char]
    xss@(Char
    x:[Char]
    xs)
            | [Char]
    needle [Char] -> [Char] -> Bool
    forall a. Eq a => [a] -> [a] -> Bool
    `isPrefixOf` [Char]
    xss = [Char] -> Maybe [Char]
    forall a. a -> Maybe a
    Just ([Char] -> Maybe [Char]) -> [Char] -> Maybe [Char]
    forall a b. (a -> b) -> a -> b
    $ [Char] -> [Char]
    forall a. [a] -> [a]
    reverse [Char]
    acc
            | Bool
    otherwise               = [Char] -> [Char] -> Maybe [Char]
    go (Char
    x Char -> [Char] -> [Char]
    forall a. a -> [a] -> [a]
    : [Char]
    acc) [Char]
    xs

    --------------------------------------------------------------------------------
    -- | Translate native Windows path separators '\\' to '/' if present.
    removeWinPathSeparator :: String -> String
    removeWinPathSeparator :: [Char] -> [Char]
    removeWinPathSeparator = (Char -> [Char]) -> [Char] -> [Char]
    forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
    concatMap (\Char
    c -> if Char
    c Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    == Char
    '\\' then [Char
    '/'] else [Char
    c])