-- | 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 = fmap (Encoding.decodeUtf8With Encoding.Error.lenientDecode) . ByteString.readFile -- | Drop until the element before the matching one. Return [] if the function -- never matches. dropBefore :: (a -> Bool) -> [a] -> [a] dropBefore f = go where go [] = [] go [_] = [] go xs@(_ : rest@(y:_)) | f y = xs | otherwise = go rest sortOn :: (Ord k) => (a -> k) -> [a] -> [a] sortOn key = List.sortBy (compare `Function.on` key) -- | Split list into chunks delimited by specified element. split :: (Eq a) => a -> [a] -> [[a]] split _ [] = [] split x xs = xs': split x (drop 1 xs'') where (xs', xs'') = break (==x) xs headt :: Text -> Maybe Char headt = fmap fst . Text.uncons mhead :: [a] -> Maybe a mhead [] = Nothing mhead (x:_) = Just x mlast :: [a] -> Maybe a mlast xs | null xs = Nothing | otherwise = Just (last xs) keyOn :: (a -> k) -> [a] -> [(k, a)] keyOn f xs = zip (map f xs) xs groupOn :: Eq k => (a -> k) -> [a] -> [[a]] groupOn key = List.groupBy (\a b -> key a == key 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 key = Map.toAscList . List.foldl' go Map.empty where go m x = Map.alter (Just . maybe [x] (x:)) (key x) m isSymbolCharacterCategory :: Char.GeneralCategory -> Bool isSymbolCharacterCategory cat = Set.member cat symbolCategories where symbolCategories :: Set.Set Char.GeneralCategory symbolCategories = Set.fromList [ Char.ConnectorPunctuation , Char.DashPunctuation , Char.OtherPunctuation , Char.MathSymbol , Char.CurrencySymbol , Char.ModifierSymbol , Char.OtherSymbol ] unique :: Ord a => [a] -> [a] unique = Set.toList . Set.fromList