{-# 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])