-- | Generic utilities.
module FastTags.Util where
import qualified Data.ByteString as ByteString
import qualified Data.Char as Char
import qualified Data.Function as Function
import qualified Data.List as List
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Text as Text
import Data.Text (Text)
import qualified Data.Text.Encoding as Encoding
import qualified Data.Text.Encoding.Error as Encoding.Error

-- | Read a UTF8 file, but don't crash on encoding errors.
readFileLenient :: FilePath -> IO Text
readFileLenient :: FilePath -> IO Text
readFileLenient = (ByteString -> Text) -> IO ByteString -> IO Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (OnDecodeError -> ByteString -> Text
Encoding.decodeUtf8With OnDecodeError
Encoding.Error.lenientDecode)
    (IO ByteString -> IO Text)
-> (FilePath -> IO ByteString) -> FilePath -> IO Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> IO ByteString
ByteString.readFile

-- | Drop until the element before the matching one.  Return [] if the function
-- never matches.
dropBefore :: (a -> Bool) -> [a] -> [a]
dropBefore :: (a -> Bool) -> [a] -> [a]
dropBefore a -> Bool
f = [a] -> [a]
go
    where
    go :: [a] -> [a]
go [] = []
    go [a
_] = []
    go xs :: [a]
xs@(a
_ : rest :: [a]
rest@(a
y:[a]
_))
        | a -> Bool
f a
y = [a]
xs
        | Bool
otherwise = [a] -> [a]
go [a]
rest

sortOn :: (Ord k) => (a -> k) -> [a] -> [a]
sortOn :: (a -> k) -> [a] -> [a]
sortOn a -> k
key = (a -> a -> Ordering) -> [a] -> [a]
forall a. (a -> a -> Ordering) -> [a] -> [a]
List.sortBy (k -> k -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (k -> k -> Ordering) -> (a -> k) -> a -> a -> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`Function.on` a -> k
key)

-- | Split list into chunks delimited by specified element.
split :: (Eq a) => a -> [a] -> [[a]]
split :: a -> [a] -> [[a]]
split a
_ [] = []
split a
x [a]
xs = [a]
xs'[a] -> [[a]] -> [[a]]
forall a. a -> [a] -> [a]
: a -> [a] -> [[a]]
forall a. Eq a => a -> [a] -> [[a]]
split a
x (Int -> [a] -> [a]
forall a. Int -> [a] -> [a]
drop Int
1 [a]
xs'')
    where ([a]
xs', [a]
xs'') = (a -> Bool) -> [a] -> ([a], [a])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break (a -> a -> Bool
forall a. Eq a => a -> a -> Bool
==a
x) [a]
xs

headt :: Text -> Maybe Char
headt :: Text -> Maybe Char
headt = ((Char, Text) -> Char) -> Maybe (Char, Text) -> Maybe Char
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Char, Text) -> Char
forall a b. (a, b) -> a
fst (Maybe (Char, Text) -> Maybe Char)
-> (Text -> Maybe (Char, Text)) -> Text -> Maybe Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Maybe (Char, Text)
Text.uncons

mhead :: [a] -> Maybe a
mhead :: [a] -> Maybe a
mhead [] = Maybe a
forall a. Maybe a
Nothing
mhead (a
x:[a]
_) = a -> Maybe a
forall a. a -> Maybe a
Just a
x

mlast :: [a] -> Maybe a
mlast :: [a] -> Maybe a
mlast [a]
xs
    | [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [a]
xs = Maybe a
forall a. Maybe a
Nothing
    | Bool
otherwise = a -> Maybe a
forall a. a -> Maybe a
Just ([a] -> a
forall a. [a] -> a
last [a]
xs)

keyOn :: (a -> k) -> [a] -> [(k, a)]
keyOn :: (a -> k) -> [a] -> [(k, a)]
keyOn a -> k
f [a]
xs = [k] -> [a] -> [(k, a)]
forall a b. [a] -> [b] -> [(a, b)]
zip ((a -> k) -> [a] -> [k]
forall a b. (a -> b) -> [a] -> [b]
map a -> k
f [a]
xs) [a]
xs

groupOn :: Eq k => (a -> k) -> [a] -> [[a]]
groupOn :: (a -> k) -> [a] -> [[a]]
groupOn a -> k
key = (a -> a -> Bool) -> [a] -> [[a]]
forall a. (a -> a -> Bool) -> [a] -> [[a]]
List.groupBy (\a
a a
b -> a -> k
key a
a k -> k -> Bool
forall a. Eq a => a -> a -> Bool
== a -> k
key a
b)

-- | Group the unsorted list into @(key x, xs)@ where all @xs@ compare equal
-- after @key@ is applied to them.  List is returned in sorted order.
groupOnKey :: Ord key => (a -> key) -> [a] -> [(key, [a])]
groupOnKey :: (a -> key) -> [a] -> [(key, [a])]
groupOnKey a -> key
key = Map key [a] -> [(key, [a])]
forall k a. Map k a -> [(k, a)]
Map.toAscList (Map key [a] -> [(key, [a])])
-> ([a] -> Map key [a]) -> [a] -> [(key, [a])]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Map key [a] -> a -> Map key [a])
-> Map key [a] -> [a] -> Map key [a]
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
List.foldl' Map key [a] -> a -> Map key [a]
go Map key [a]
forall k a. Map k a
Map.empty
    where go :: Map key [a] -> a -> Map key [a]
go Map key [a]
m a
x = (Maybe [a] -> Maybe [a]) -> key -> Map key [a] -> Map key [a]
forall k a.
Ord k =>
(Maybe a -> Maybe a) -> k -> Map k a -> Map k a
Map.alter ([a] -> Maybe [a]
forall a. a -> Maybe a
Just ([a] -> Maybe [a]) -> (Maybe [a] -> [a]) -> Maybe [a] -> Maybe [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> ([a] -> [a]) -> Maybe [a] -> [a]
forall b a. b -> (a -> b) -> Maybe a -> b
maybe [a
x] (a
xa -> [a] -> [a]
forall a. a -> [a] -> [a]
:)) (a -> key
key a
x) Map key [a]
m

isSymbolCharacterCategory :: Char.GeneralCategory -> Bool
isSymbolCharacterCategory :: GeneralCategory -> Bool
isSymbolCharacterCategory GeneralCategory
cat = case GeneralCategory
cat of
    GeneralCategory
Char.ConnectorPunctuation -> Bool
True
    GeneralCategory
Char.DashPunctuation      -> Bool
True
    GeneralCategory
Char.OtherPunctuation     -> Bool
True
    GeneralCategory
Char.MathSymbol           -> Bool
True
    GeneralCategory
Char.CurrencySymbol       -> Bool
True
    GeneralCategory
Char.ModifierSymbol       -> Bool
True
    GeneralCategory
Char.OtherSymbol          -> Bool
True
    GeneralCategory
_                         -> Bool
False

unique :: Ord a => [a] -> [a]
unique :: [a] -> [a]
unique = Set a -> [a]
forall a. Set a -> [a]
Set.toList (Set a -> [a]) -> ([a] -> Set a) -> [a] -> [a]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [a] -> Set a
forall a. Ord a => [a] -> Set a
Set.fromList