owoify-hs-1.0.0.0: src/Data/Owoify/Internal/Entity/Word.hs
{-# LANGUAGE NamedFieldPuns #-}
module Data.Owoify.Internal.Entity.Word
( InnerWord(..)
, innerReplace
, innerReplaceWithFuncSingle
, innerReplaceWithFuncMultiple
, toText
)
where
import Prelude
import Data.Maybe (listToMaybe)
import Data.Text.Lazy (strip, Text)
import qualified Data.Text.Lazy (replace)
import Text.RE.PCRE.Text.Lazy ((*=~), anyMatches, matches, RE)
import Text.RE.Replace (replaceAll)
import Data.List (nub)
-- | Basic type for manipulating strings.
data InnerWord = InnerWord
{ innerWord :: Text
, innerReplacedWords :: [Text]
} deriving (Eq, Show)
toText :: InnerWord -> Text
toText InnerWord{ innerWord } = innerWord
testAndGetReplacingWord :: RE -> Text -> Text -> Text
testAndGetReplacingWord searchValue replaceValue str =
let matchedItems = str *=~ searchValue in
if anyMatches matchedItems then
case listToMaybe $ matches matchedItems of
Nothing -> str
Just hd -> Data.Text.Lazy.replace hd replaceValue str
else
str
containsReplacedWords :: InnerWord -> RE -> Text -> Bool
containsReplacedWords InnerWord { innerReplacedWords } searchValue replaceValue =
any (\s -> let matchedItems = s *=~ searchValue in
anyMatches matchedItems && (
let replacedWord = Data.Text.Lazy.replace (head $ matches matchedItems) replaceValue s in
replacedWord == s)) innerReplacedWords
buildCollection :: RE -> Text -> [Text]
buildCollection searchValue str = matches $ str *=~ searchValue
buildReplacedWords :: Functor f => Text -> f Text -> f Text
buildReplacedWords replaceValue texts = (\s -> Data.Text.Lazy.replace s replaceValue s) <$> texts
-- | Match the `word` against `searchValue` and replace matched strings with `replaceValue`.
innerReplace :: InnerWord -> RE -> Text -> Bool -> InnerWord
innerReplace word@InnerWord { innerWord, innerReplacedWords } searchValue replaceValue replaceReplacedWords
| not replaceReplacedWords && containsReplacedWords word searchValue replaceValue = word
| otherwise = if replacingWord == innerWord then word else InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }
where
matchedItems = innerWord *=~ searchValue
collection = matches matchedItems
replacingWord = case listToMaybe collection of
Nothing -> innerWord
Just _ -> strip $ replaceAll replaceValue matchedItems
replacedWords = buildReplacedWords replaceValue collection
-- | Match the `word` against `searchValue` and replace matched strings with the string resulting from invoking `f`.
innerReplaceWithFuncSingle :: InnerWord -> RE -> (() -> Text) -> Bool -> InnerWord
innerReplaceWithFuncSingle word@InnerWord { innerWord, innerReplacedWords } searchValue f replaceReplacedWords
| not replaceReplacedWords && containsReplacedWords word searchValue replaceValue = word
| replacingWord == innerWord = word
| otherwise = InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }
where
replaceValue = f ()
replacingWord
= strip
$ testAndGetReplacingWord searchValue replaceValue innerWord
collection = buildCollection searchValue replaceValue
replacedWords = buildReplacedWords replaceValue collection
-- | Match the `word` against `searchValue` and replace matched strings with the string resulting from invoking `f`.
--
-- The difference between this and `replaceWithFuncSingle` is that the `f` here takes two `String` arguments.
innerReplaceWithFuncMultiple :: InnerWord -> RE -> (Text -> Text -> Text) -> Bool -> InnerWord
innerReplaceWithFuncMultiple word@InnerWord { innerWord, innerReplacedWords } searchValue f replaceReplacedWords
| not $ anyMatches matchedItems = word
| otherwise =
if (not replaceReplacedWords && containsReplacedWords word searchValue replaceValue) || (replacingWord == innerWord) then word
else InnerWord { innerWord = replacingWord, innerReplacedWords = nub $ innerReplacedWords <> replacedWords }
where
matchedItems = innerWord *=~ searchValue
collection = matches matchedItems
(s1 : s2 : s3 : _) = if length collection == 3 then collection else [innerWord, innerWord, innerWord]
replaceValue = f s2 s3
replacingWord = strip $ Data.Text.Lazy.replace s1 replaceValue innerWord
replacedWords = buildReplacedWords replaceValue collection