From 3dd395b80eb049873e0a190f01bd474e90da6ce5 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Sat, 14 Oct 2023 18:57:19 +0800 Subject: [PATCH 1/5] Add more bytestring like functions --- System/OsString.hs | 146 +++ System/OsString/Common.hs | 873 ++++++++++++++++++ System/OsString/Internal.hs | 545 +++++++++++ changelog.md | 4 + filepath.cabal | 5 +- tests/abstract-filepath/OsPathSpec.hs | 4 +- tests/bytestring-tests/Main.hs | 5 +- tests/bytestring-tests/Properties/Common.hs | 256 ++++- tests/bytestring-tests/Properties/OsString.hs | 7 + .../Properties/PosixString.hs | 7 + .../Properties/ShortByteString.hs | 4 + .../Properties/ShortByteString/Word16.hs | 3 + .../Properties/WindowsString.hs | 7 + 13 files changed, 1834 insertions(+), 32 deletions(-) create mode 100644 tests/bytestring-tests/Properties/OsString.hs create mode 100644 tests/bytestring-tests/Properties/PosixString.hs create mode 100644 tests/bytestring-tests/Properties/WindowsString.hs diff --git a/System/OsString.hs b/System/OsString.hs index c11a4bdf..8de8e6fc 100644 --- a/System/OsString.hs +++ b/System/OsString.hs @@ -24,6 +24,8 @@ module System.OsString , encodeWith , encodeFS , osstr + , empty + , singleton , pack -- * OsString deconstruction @@ -40,6 +42,87 @@ module System.OsString -- * Word deconstruction , toChar + + -- * Basic interface + , snoc + , cons + , last + , tail + , uncons + , head + , init + , unsnoc + , null + , length + + -- * Transforming OsString + , map + , reverse + , intercalate + + -- * Reducing OsStrings (folds) + , foldl + , foldl' + , foldl1 + , foldl1' + , foldr + , foldr' + , foldr1 + , foldr1' + + -- * Special folds + , all + , any + , concat + + -- * Generating and unfolding OsStrings + , replicate + , unfoldr + , unfoldrN + + -- * Substrings + -- ** Breaking strings + , take + , takeEnd + , takeWhileEnd + , takeWhile + , drop + , dropEnd + , dropWhileEnd + , dropWhile + , break + , breakEnd + , span + , spanEnd + , splitAt + , split + , splitWith + , stripSuffix + , stripPrefix + + -- * Predicates + , isInfixOf + , isPrefixOf + , isSuffixOf + -- ** Search for arbitrary susbstrings + , breakSubstring + + -- * Searching OsStrings + -- ** Searching by equality + , elem + , find + , filter + , partition + + -- * Indexing OsStrings + , index + , indexMaybe + , (!?) + , elemIndex + , elemIndices + , count + , findIndex + , findIndices ) where @@ -51,10 +134,73 @@ import System.OsString.Internal , encodeFS , osstr , pack + , empty + , singleton , decodeUtf , decodeWith , decodeFS , unpack + , snoc + , cons + , last + , tail + , uncons + , head + , init + , unsnoc + , null + , length + , map + , reverse + , intercalate + , foldl + , foldl' + , foldl1 + , foldl1' + , foldr + , foldr' + , foldr1 + , foldr1' + , all + , any + , concat + , replicate + , unfoldr + , unfoldrN + , take + , takeEnd + , takeWhileEnd + , takeWhile + , drop + , dropEnd + , dropWhileEnd + , dropWhile + , break + , breakEnd + , span + , spanEnd + , splitAt + , split + , splitWith + , stripSuffix + , stripPrefix + , isInfixOf + , isPrefixOf + , isSuffixOf + , breakSubstring + , elem + , find + , filter + , partition + , index + , indexMaybe + , (!?) + , elemIndex + , elemIndices + , count + , findIndex + , findIndices ) import System.OsString.Internal.Types ( OsString, OsChar ) +import Prelude hiding (last, tail, head, init, null, length, map, reverse, foldl, foldr, foldl1, foldr1, all, any, concat, replicate, take, takeWhile, drop, dropWhile, break, span, splitAt, elem, filter) diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs index 80eb69b5..cd42b279 100644 --- a/System/OsString/Common.hs +++ b/System/OsString/Common.hs @@ -1,6 +1,7 @@ {- HLINT ignore "Unused LANGUAGE pragma" -} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE PatternSynonyms #-} +{-# LANGUAGE RankNTypes #-} -- This template expects CPP definitions for: -- MODULE_NAME = Posix | Windows -- IS_WINDOWS = False | True @@ -28,6 +29,8 @@ module System.OsString.MODULE_NAME , encodeFS , fromBytes , pstr + , singleton + , empty , pack -- * String deconstruction @@ -41,6 +44,87 @@ module System.OsString.MODULE_NAME -- * Word deconstruction , toChar + + -- * Basic interface + , snoc + , cons + , last + , tail + , uncons + , head + , init + , unsnoc + , null + , length + + -- * Transforming OsString + , map + , reverse + , intercalate + + -- * Reducing OsStrings (folds) + , foldl + , foldl' + , foldl1 + , foldl1' + , foldr + , foldr' + , foldr1 + , foldr1' + + -- ** Special folds + , all + , any + , concat + + -- ** Generating and unfolding OsStrings + , replicate + , unfoldr + , unfoldrN + + -- * Substrings + -- ** Breaking strings + , take + , takeEnd + , takeWhileEnd + , takeWhile + , drop + , dropEnd + , dropWhileEnd + , dropWhile + , break + , breakEnd + , span + , spanEnd + , splitAt + , split + , splitWith + , stripSuffix + , stripPrefix + + -- * Predicates + , isInfixOf + , isPrefixOf + , isSuffixOf + -- ** Search for arbitrary susbstrings + , breakSubstring + + -- * Searching OsStrings + -- ** Searching by equality + , elem + , find + , filter + , partition + + -- * Indexing OsStrings + , index + , indexMaybe + , (!?) + , elemIndex + , elemIndices + , count + , findIndex + , findIndices ) where @@ -87,6 +171,9 @@ import System.IO import GHC.IO.Encoding.UTF8 ( mkUTF8 ) import qualified System.OsPath.Data.ByteString.Short as BS #endif +import GHC.Stack (HasCallStack) +import Prelude hiding (last, tail, head, init, null, length, map, reverse, foldl, foldr, foldl1, foldr1, all, any, concat, replicate, take, takeWhile, drop, dropWhile, break, span, splitAt, elem, filter) +import Data.Bifunctor ( bimap ) @@ -295,6 +382,16 @@ pack = WindowsString . BS16.pack . fmap (\(WindowsChar w) -> w) pack = PosixString . BS.pack . fmap (\(PosixChar w) -> w) #endif +singleton :: PLATFORM_WORD -> PLATFORM_STRING +#ifdef WINDOWS +singleton = WindowsString . BS16.singleton . getWindowsChar +#else +singleton = PosixString . BS.singleton . getPosixChar +#endif + +empty :: PLATFORM_STRING +empty = mempty + #ifdef WINDOWS -- | Truncates to 2 octets. @@ -313,3 +410,779 @@ toChar (WindowsChar w) = chr $ fromIntegral w #else toChar (PosixChar w) = chr $ fromIntegral w #endif + +-- | /O(n)/ Append a byte to the end of a 'OsString' +-- +-- @since 1.4.200.0 +snoc :: PLATFORM_STRING -> PLATFORM_WORD -> PLATFORM_STRING +#ifdef WINDOWS +snoc (WindowsString s) (WindowsChar w) = WindowsString (BS16.snoc s w) +#else +snoc (PosixString s) (PosixChar w) = PosixString (BS.snoc s w) +#endif + +-- | /O(n)/ 'cons' is analogous to (:) for lists. +-- +-- @since 1.4.200.0 +cons :: PLATFORM_WORD -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +cons (WindowsChar w) (WindowsString s) = WindowsString (BS16.cons w s) +#else +cons (PosixChar w) (PosixString s) = PosixString (BS.cons w s) +#endif + + +-- | /O(1)/ Extract the last element of a OsString, which must be finite and non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'unsnoc' instead. +-- +-- @since 1.4.200.0 +last :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +last (WindowsString s) = WindowsChar (BS16.last s) +#else +last (PosixString s) = PosixChar (BS.last s) +#endif + +-- | /O(n)/ Extract the elements after the head of a OsString, which must be non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'uncons' instead. +-- +-- @since 1.4.200.0 +tail :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +tail (WindowsString s) = WindowsString (BS16.tail s) +#else +tail (PosixString s) = PosixString (BS.tail s) +#endif + +-- | /O(n)/ Extract the 'head' and 'tail' of a OsString, returning 'Nothing' +-- if it is empty. +-- +-- @since 1.4.200.0 +uncons :: PLATFORM_STRING -> Maybe (PLATFORM_WORD, PLATFORM_STRING) +#ifdef WINDOWS +uncons (WindowsString s) = (bimap WindowsChar WindowsString) <$> (BS16.uncons s) +#else +uncons (PosixString s) = (bimap PosixChar PosixString) <$> (BS.uncons s) +#endif + +-- | /O(1)/ Extract the first element of a OsString, which must be non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'uncons' instead. +-- +-- @since 1.4.200.0 +head :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +head (WindowsString s) = WindowsChar (BS16.head s) +#else +head (PosixString s) = PosixChar (BS.head s) +#endif + +-- | /O(n)/ Return all the elements of a 'OsString' except the last one. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'unsnoc' instead. +-- +-- @since 1.4.200.0 +init :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +init (WindowsString s) = WindowsString (BS16.init s) +#else +init (PosixString s) = PosixString (BS.init s) +#endif + +-- | /O(n)/ Extract the 'init' and 'last' of a OsString, returning 'Nothing' +-- if it is empty. +-- +-- @since 1.4.200.0 +unsnoc :: PLATFORM_STRING -> Maybe (PLATFORM_STRING, PLATFORM_WORD) +#ifdef WINDOWS +unsnoc (WindowsString s) = (bimap WindowsString WindowsChar) <$> (BS16.unsnoc s) +#else +unsnoc (PosixString s) = (bimap PosixString PosixChar) <$> (BS.unsnoc s) +#endif + +-- | /O(1)/. The empty 'OsString'. +-- +-- @since 1.4.200.0 +null :: PLATFORM_STRING -> Bool +#ifdef WINDOWS +null (WindowsString s) = BS16.null s +#else +null (PosixString s) = BS.null s +#endif + +-- | /O(1)/ The length of a 'OsString'. +-- +-- @since 1.4.200.0 +length :: PLATFORM_STRING -> Int +#ifdef WINDOWS +length (WindowsString s) = BS16.length s +#else +length (PosixString s) = BS.length s +#endif + +-- | /O(n)/ 'map' @f xs@ is the OsString obtained by applying @f@ to each +-- element of @xs@. +-- +-- @since 1.4.200.0 +map :: (PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +map f (WindowsString s) = WindowsString (BS16.map (getWindowsChar . f . WindowsChar) s) +#else +map f (PosixString s) = PosixString (BS.map (getPosixChar . f . PosixChar) s) +#endif + +-- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. +-- +-- @since 1.4.200.0 +reverse :: PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +reverse (WindowsString s) = WindowsString (BS16.reverse s) +#else +reverse (PosixString s) = PosixString (BS.reverse s) +#endif + +-- | /O(n)/ The 'intercalate' function takes a 'OsString' and a list of +-- 'OsString's and concatenates the list after interspersing the first +-- argument between each element of the list. +-- +-- @since 1.4.200.0 +intercalate :: PLATFORM_STRING -> [PLATFORM_STRING] -> PLATFORM_STRING +#ifdef WINDOWS +intercalate (WindowsString s) xs = WindowsString (BS16.intercalate s (fmap getWindowsString xs)) +#else +intercalate (PosixString s) xs = PosixString (BS.intercalate s (fmap getPosixString xs)) +#endif + +-- | 'foldl', applied to a binary operator, a starting value (typically +-- the left-identity of the operator), and a OsString, reduces the +-- OsString using the binary operator, from left to right. +-- +-- @since 1.4.200.0 +foldl :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a +#ifdef WINDOWS +foldl f a (WindowsString s) = BS16.foldl (\a' c -> f a' (WindowsChar c)) a s +#else +foldl f a (PosixString s) = BS.foldl (\a' c -> f a' (PosixChar c)) a s +#endif + +-- | 'foldl'' is like 'foldl', but strict in the accumulator. +-- +-- @since 1.4.200.0 +foldl' + :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a +#ifdef WINDOWS +foldl' f a (WindowsString s) = BS16.foldl' (\a' c -> f a' (WindowsChar c)) a s +#else +foldl' f a (PosixString s) = BS.foldl' (\a' c -> f a' (PosixChar c)) a s +#endif + +-- | 'foldl1' is a variant of 'foldl' that has no starting value +-- argument, and thus must be applied to non-empty 'OsString's. +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldl1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +foldl1 f (WindowsString s) = WindowsChar $ BS16.foldl1 (\a' c -> getWindowsChar $ f (WindowsChar a') (WindowsChar c)) s +#else +foldl1 f (PosixString s) = PosixChar $ BS.foldl1 (\a' c -> getPosixChar $ f (PosixChar a') (PosixChar c)) s +#endif + +-- | 'foldl1'' is like 'foldl1', but strict in the accumulator. +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldl1' + :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +foldl1' f (WindowsString s) = WindowsChar $ BS16.foldl1' (\a' c -> getWindowsChar $ f (WindowsChar a') (WindowsChar c)) s +#else +foldl1' f (PosixString s) = PosixChar $ BS.foldl1' (\a' c -> getPosixChar $ f (PosixChar a') (PosixChar c)) s +#endif + +-- | 'foldr', applied to a binary operator, a starting value +-- (typically the right-identity of the operator), and a OsString, +-- reduces the OsString using the binary operator, from right to left. +-- +-- @since 1.4.200.0 +foldr :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a +#ifdef WINDOWS +foldr f a (WindowsString s) = BS16.foldr (\c a' -> f (WindowsChar c) a') a s +#else +foldr f a (PosixString s) = BS.foldr (\c a' -> f (PosixChar c) a') a s +#endif + +-- | 'foldr'' is like 'foldr', but strict in the accumulator. +-- +-- @since 1.4.200.0 +foldr' + :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a +#ifdef WINDOWS +foldr' f a (WindowsString s) = BS16.foldr' (\c a' -> f (WindowsChar c) a') a s +#else +foldr' f a (PosixString s) = BS.foldr' (\c a' -> f (PosixChar c) a') a s +#endif + +-- | 'foldr1' is a variant of 'foldr' that has no starting value argument, +-- and thus must be applied to non-empty 'OsString's +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldr1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +foldr1 f (WindowsString s) = WindowsChar $ BS16.foldr1 (\c a' -> getWindowsChar $ f (WindowsChar c) (WindowsChar a')) s +#else +foldr1 f (PosixString s) = PosixChar $ BS.foldr1 (\c a' -> getPosixChar $ f (PosixChar c) (PosixChar a')) s +#endif + +-- | 'foldr1'' is a variant of 'foldr1', but is strict in the +-- accumulator. +-- +-- +-- @since 1.4.200.0 +-- @since 1.4.200.0 +foldr1' + :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD +#ifdef WINDOWS +foldr1' f (WindowsString s) = WindowsChar $ BS16.foldr1' (\c a' -> getWindowsChar $ f (WindowsChar c) (WindowsChar a')) s +#else +foldr1' f (PosixString s) = PosixChar $ BS.foldr1' (\c a' -> getPosixChar $ f (PosixChar c) (PosixChar a')) s +#endif + +-- | /O(n)/ Applied to a predicate and a 'OsString', 'all' determines +-- if all elements of the 'OsString' satisfy the predicate. +-- +-- @since 1.4.200.0 +all :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +all f (WindowsString s) = BS16.all (f . WindowsChar) s +#else +all f (PosixString s) = BS.all (f . PosixChar) s +#endif + +-- | /O(n)/ Applied to a predicate and a 'OsString', 'any' determines if +-- any element of the 'OsString' satisfies the predicate. +-- +-- @since 1.4.200.0 +any :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +any f (WindowsString s) = BS16.any (f . WindowsChar) s +#else +any f (PosixString s) = BS.any (f . PosixChar) s +#endif + +-- /O(n)/ Concatenate a list of OsStrings. +-- +-- @since 1.4.200.0 +concat :: [PLATFORM_STRING] -> PLATFORM_STRING +concat = mconcat + +-- | /O(n)/ 'replicate' @n x@ is a OsString of length @n@ with @x@ +-- the value of every element. The following holds: +-- +-- > replicate w c = unfoldr w (\u -> Just (u,u)) c +-- +-- @since 1.4.200.0 +replicate :: Int -> PLATFORM_WORD -> PLATFORM_STRING +#ifdef WINDOWS +replicate i (WindowsChar w) = WindowsString $ BS16.replicate i w +#else +replicate i (PosixChar w) = PosixString $ BS.replicate i w +#endif + +-- | /O(n)/, where /n/ is the length of the result. The 'unfoldr' +-- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a +-- OsString from a seed value. The function takes the element and +-- returns 'Nothing' if it is done producing the OsString or returns +-- 'Just' @(a,b)@, in which case, @a@ is the next byte in the string, +-- and @b@ is the seed value for further production. +-- +-- This function is not efficient/safe. It will build a list of @[Word8]@ +-- and run the generator until it returns `Nothing`, otherwise recurse infinitely, +-- then finally create a 'OsString'. +-- +-- If you know the maximum length, consider using 'unfoldrN'. +-- +-- Examples: +-- +-- > unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0 +-- > == pack [0, 1, 2, 3, 4, 5] +-- +-- @since 1.4.200.0 +unfoldr :: (a -> Maybe (PLATFORM_WORD, a)) -> a -> PLATFORM_STRING +#ifdef WINDOWS +unfoldr f a = WindowsString $ BS16.unfoldr (fmap (first getWindowsChar) . f) a +#else +unfoldr f a = PosixString $ BS.unfoldr (fmap (first getPosixChar) . f) a +#endif + +-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed +-- value. However, the length of the result is limited by the first +-- argument to 'unfoldrN'. This function is more efficient than 'unfoldr' +-- when the maximum length of the result is known. +-- +-- The following equation relates 'unfoldrN' and 'unfoldr': +-- +-- > fst (unfoldrN n f s) == take n (unfoldr f s) +-- +-- @since 1.4.200.0 +unfoldrN :: forall a. Int -> (a -> Maybe (PLATFORM_WORD, a)) -> a -> (PLATFORM_STRING, Maybe a) +#ifdef WINDOWS +unfoldrN n f a = first WindowsString $ BS16.unfoldrN n (fmap (first getWindowsChar) . f) a +#else +unfoldrN n f a = first PosixString $ BS.unfoldrN n (fmap (first getPosixChar) . f) a +#endif + +-- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix +-- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@. +-- +-- @since 1.4.200.0 +take :: Int -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +take n (WindowsString s) = WindowsString $ BS16.take n s +#else +take n (PosixString s) = PosixString $ BS.take n s +#endif + +-- | /O(n)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@. +-- Takes @n@ elements from end of bytestring. +-- +-- >>> takeEnd 3 "abcdefg" +-- "efg" +-- >>> takeEnd 0 "abcdefg" +-- "" +-- >>> takeEnd 4 "abc" +-- "abc" +-- +-- @since 1.4.200.0 +takeEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +takeEnd n (WindowsString s) = WindowsString $ BS16.takeEnd n s +#else +takeEnd n (PosixString s) = PosixString $ BS.takeEnd n s +#endif + +-- | Returns the longest (possibly empty) suffix of elements +-- satisfying the predicate. +-- +-- @'takeWhileEnd' p@ is equivalent to @'reverse' . 'takeWhile' p . 'reverse'@. +-- +-- @since 1.4.200.0 +takeWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +takeWhileEnd f (WindowsString s) = WindowsString $ BS16.takeWhileEnd (f . WindowsChar) s +#else +takeWhileEnd f (PosixString s) = PosixString $ BS.takeWhileEnd (f . PosixChar) s +#endif + +-- | Similar to 'Prelude.takeWhile', +-- returns the longest (possibly empty) prefix of elements +-- satisfying the predicate. +-- +-- @since 1.4.200.0 +takeWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +takeWhile f (WindowsString s) = WindowsString $ BS16.takeWhile (f . WindowsChar) s +#else +takeWhile f (PosixString s) = PosixString $ BS.takeWhile (f . PosixChar) s +#endif + +-- | /O(n)/ 'drop' @n@ @xs@ returns the suffix of @xs@ after the first n elements, or 'empty' if @n > 'length' xs@. +-- +-- @since 1.4.200.0 +drop :: Int -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +drop n (WindowsString s) = WindowsString $ BS16.drop n s +#else +drop n (PosixString s) = PosixString $ BS.drop n s +#endif + +-- | /O(n)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@. +-- Drops @n@ elements from end of bytestring. +-- +-- >>> dropEnd 3 "abcdefg" +-- "abcd" +-- >>> dropEnd 0 "abcdefg" +-- "abcdefg" +-- >>> dropEnd 4 "abc" +-- "" +-- +-- @since 1.4.200.0 +dropEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +dropEnd n (WindowsString s) = WindowsString $ BS16.dropEnd n s +#else +dropEnd n (PosixString s) = PosixString $ BS.dropEnd n s +#endif + +-- | Similar to 'Prelude.dropWhile', +-- drops the longest (possibly empty) prefix of elements +-- satisfying the predicate and returns the remainder. +-- +-- @since 1.4.200.0 +dropWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +dropWhile f (WindowsString s) = WindowsString $ BS16.dropWhile (f . WindowsChar) s +#else +dropWhile f (PosixString s) = PosixString $ BS.dropWhile (f . PosixChar) s +#endif + +-- | Similar to 'Prelude.dropWhileEnd', +-- drops the longest (possibly empty) suffix of elements +-- satisfying the predicate and returns the remainder. +-- +-- @'dropWhileEnd' p@ is equivalent to @'reverse' . 'dropWhile' p . 'reverse'@. +-- +-- @since 1.4.200.0 +dropWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +dropWhileEnd f (WindowsString s) = WindowsString $ BS16.dropWhileEnd (f . WindowsChar) s +#else +dropWhileEnd f (PosixString s) = PosixString $ BS.dropWhileEnd (f . PosixChar) s +#endif + +-- | Returns the longest (possibly empty) suffix of elements which __do not__ +-- satisfy the predicate and the remainder of the string. +-- +-- 'breakEnd' @p@ is equivalent to @'spanEnd' (not . p)@ and to @('takeWhileEnd' (not . p) &&& 'dropWhileEnd' (not . p))@. +-- +-- @since 1.4.200.0 +breakEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +breakEnd f (WindowsString s) = bimap WindowsString WindowsString $ BS16.breakEnd (f . WindowsChar) s +#else +breakEnd f (PosixString s) = bimap PosixString PosixString $ BS.breakEnd (f . PosixChar) s +#endif + +-- | Similar to 'Prelude.break', +-- returns the longest (possibly empty) prefix of elements which __do not__ +-- satisfy the predicate and the remainder of the string. +-- +-- 'break' @p@ is equivalent to @'span' (not . p)@ and to @('takeWhile' (not . p) &&& 'dropWhile' (not . p))@. +-- +-- @since 1.4.200.0 +break :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +break f (WindowsString s) = bimap WindowsString WindowsString $ BS16.break (f . WindowsChar) s +#else +break f (PosixString s) = bimap PosixString PosixString $ BS.break (f . PosixChar) s +#endif + +-- | Similar to 'Prelude.span', +-- returns the longest (possibly empty) prefix of elements +-- satisfying the predicate and the remainder of the string. +-- +-- 'span' @p@ is equivalent to @'break' (not . p)@ and to @('takeWhile' p &&& 'dropWhile' p)@. +-- +-- @since 1.4.200.0 +span :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +span f (WindowsString s) = bimap WindowsString WindowsString $ BS16.span (f . WindowsChar) s +#else +span f (PosixString s) = bimap PosixString PosixString $ BS.span (f . PosixChar) s +#endif + +-- | Returns the longest (possibly empty) suffix of elements +-- satisfying the predicate and the remainder of the string. +-- +-- 'spanEnd' @p@ is equivalent to @'breakEnd' (not . p)@ and to @('takeWhileEnd' p &&& 'dropWhileEnd' p)@. +-- +-- We have +-- +-- > spanEnd (not . isSpace) "x y z" == ("x y ", "z") +-- +-- and +-- +-- > spanEnd (not . isSpace) sbs +-- > == +-- > let (x, y) = span (not . isSpace) (reverse sbs) in (reverse y, reverse x) +-- +-- @since 1.4.200.0 +spanEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +spanEnd f (WindowsString s) = bimap WindowsString WindowsString $ BS16.spanEnd (f . WindowsChar) s +#else +spanEnd f (PosixString s) = bimap PosixString PosixString $ BS.spanEnd (f . PosixChar) s +#endif + +-- | /O(n)/ 'splitAt' @n sbs@ is equivalent to @('take' n sbs, 'drop' n sbs)@. +-- +-- @since 1.4.200.0 +splitAt :: Int -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +splitAt n (WindowsString s) = bimap WindowsString WindowsString $ BS16.splitAt n s +#else +splitAt n (PosixString s) = bimap PosixString PosixString $ BS.splitAt n s +#endif + +-- | /O(n)/ Break a 'OsString' into pieces separated by the byte +-- argument, consuming the delimiter. I.e. +-- +-- > split 10 "a\nb\nd\ne" == ["a","b","d","e"] -- fromEnum '\n' == 10 +-- > split 97 "aXaXaXa" == ["","X","X","X",""] -- fromEnum 'a' == 97 +-- > split 120 "x" == ["",""] -- fromEnum 'x' == 120 +-- > split undefined "" == [] -- and not [""] +-- +-- and +-- +-- > intercalate [c] . split c == id +-- > split == splitWith . (==) +-- +-- @since 1.4.200.0 +split :: PLATFORM_WORD -> PLATFORM_STRING -> [PLATFORM_STRING] +#ifdef WINDOWS +split (WindowsChar w) (WindowsString s) = WindowsString <$> BS16.split w s +#else +split (PosixChar w) (PosixString s) = PosixString <$> BS.split w s +#endif + +-- | /O(n)/ Splits a 'OsString' into components delimited by +-- separators, where the predicate returns True for a separator element. +-- The resulting components do not contain the separators. Two adjacent +-- separators result in an empty component in the output. eg. +-- +-- > splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97 +-- > splitWith undefined "" == [] -- and not [""] +-- +-- @since 1.4.200.0 +splitWith :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [PLATFORM_STRING] +#ifdef WINDOWS +splitWith f (WindowsString s) = WindowsString <$> BS16.splitWith (f . WindowsChar) s +#else +splitWith f (PosixString s) = PosixString <$> BS.splitWith (f . PosixChar) s +#endif + +-- | /O(n)/ The 'stripSuffix' function takes two OsStrings and returns 'Just' +-- the remainder of the second iff the first is its suffix, and otherwise +-- 'Nothing'. +-- +-- @since 1.4.200.0 +stripSuffix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING +#ifdef WINDOWS +stripSuffix (WindowsString a) (WindowsString b) = WindowsString <$> BS16.stripSuffix a b +#else +stripSuffix (PosixString a) (PosixString b) = PosixString <$> BS.stripSuffix a b +#endif + +-- | /O(n)/ The 'stripPrefix' function takes two OsStrings and returns 'Just' +-- the remainder of the second iff the first is its prefix, and otherwise +-- 'Nothing'. +-- +-- @since 1.4.200.0 +stripPrefix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING +#ifdef WINDOWS +stripPrefix (WindowsString a) (WindowsString b) = WindowsString <$> BS16.stripPrefix a b +#else +stripPrefix (PosixString a) (PosixString b) = PosixString <$> BS.stripPrefix a b +#endif + + +-- | Check whether one string is a substring of another. +-- +-- @since 1.4.200.0 +isInfixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +isInfixOf (WindowsString a) (WindowsString b) = BS16.isInfixOf a b +#else +isInfixOf (PosixString a) (PosixString b) = BS.isInfixOf a b +#endif + +-- |/O(n)/ The 'isPrefixOf' function takes two OsStrings and returns 'True' +-- +-- @since 1.4.200.0 +isPrefixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +isPrefixOf (WindowsString a) (WindowsString b) = BS16.isPrefixOf a b +#else +isPrefixOf (PosixString a) (PosixString b) = BS.isPrefixOf a b +#endif + +-- | /O(n)/ The 'isSuffixOf' function takes two OsStrings and returns 'True' +-- iff the first is a suffix of the second. +-- +-- The following holds: +-- +-- > isSuffixOf x y == reverse x `isPrefixOf` reverse y +-- +-- @since 1.4.200.0 +isSuffixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +isSuffixOf (WindowsString a) (WindowsString b) = BS16.isSuffixOf a b +#else +isSuffixOf (PosixString a) (PosixString b) = BS.isSuffixOf a b +#endif + + +-- | Break a string on a substring, returning a pair of the part of the +-- string prior to the match, and the rest of the string. +-- +-- The following relationships hold: +-- +-- > break (== c) l == breakSubstring (singleton c) l +-- +-- For example, to tokenise a string, dropping delimiters: +-- +-- > tokenise x y = h : if null t then [] else tokenise x (drop (length x) t) +-- > where (h,t) = breakSubstring x y +-- +-- To skip to the first occurrence of a string: +-- +-- > snd (breakSubstring x y) +-- +-- To take the parts of a string before a delimiter: +-- +-- > fst (breakSubstring x y) +-- +-- Note that calling `breakSubstring x` does some preprocessing work, so +-- you should avoid unnecessarily duplicating breakSubstring calls with the same +-- pattern. +-- +-- @since 1.4.200.0 +breakSubstring :: PLATFORM_STRING -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +breakSubstring (WindowsString a) (WindowsString b) = bimap WindowsString WindowsString $ BS16.breakSubstring a b +#else +breakSubstring (PosixString a) (PosixString b) = bimap PosixString PosixString $ BS.breakSubstring a b +#endif + +-- | /O(n)/ 'elem' is the 'OsString' membership predicate. +-- +-- @since 1.4.200.0 +elem :: PLATFORM_WORD -> PLATFORM_STRING -> Bool +#ifdef WINDOWS +elem (WindowsChar w) (WindowsString s) = BS16.elem w s +#else +elem (PosixChar w) (PosixString s) = BS.elem w s +#endif + +-- | /O(n)/ The 'find' function takes a predicate and a OsString, +-- and returns the first element in matching the predicate, or 'Nothing' +-- if there is no such element. +-- +-- > find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing +-- +-- @since 1.4.200.0 +find :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe PLATFORM_WORD +#ifdef WINDOWS +find f (WindowsString s) = WindowsChar <$> BS16.find (f . WindowsChar) s +#else +find f (PosixString s) = PosixChar <$> BS.find (f . PosixChar) s +#endif + +-- | /O(n)/ 'filter', applied to a predicate and a OsString, +-- returns a OsString containing those characters that satisfy the +-- predicate. +-- +-- @since 1.4.200.0 +filter :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING +#ifdef WINDOWS +filter f (WindowsString s) = WindowsString $ BS16.filter (f . WindowsChar) s +#else +filter f (PosixString s) = PosixString $ BS.filter (f . PosixChar) s +#endif + +-- | /O(n)/ The 'partition' function takes a predicate a OsString and returns +-- the pair of OsStrings with elements which do and do not satisfy the +-- predicate, respectively; i.e., +-- +-- > partition p bs == (filter p sbs, filter (not . p) sbs) +-- +-- @since 1.4.200.0 +partition :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) +#ifdef WINDOWS +partition f (WindowsString s) = bimap WindowsString WindowsString $ BS16.partition (f . WindowsChar) s +#else +partition f (PosixString s) = bimap PosixString PosixString $ BS.partition (f . PosixChar) s +#endif + +-- | /O(1)/ 'OsString' index (subscript) operator, starting from 0. +-- +-- @since 1.4.200.0 +index :: HasCallStack => PLATFORM_STRING -> Int -> PLATFORM_WORD +#ifdef WINDOWS +index (WindowsString s) n = WindowsChar $ BS16.index s n +#else +index (PosixString s) n = PosixChar $ BS.index s n +#endif + +-- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: +-- +-- > 0 <= n < length bs +-- +-- @since 1.4.200.0 +indexMaybe :: PLATFORM_STRING -> Int -> Maybe PLATFORM_WORD +#ifdef WINDOWS +indexMaybe (WindowsString s) n = WindowsChar <$> BS16.indexMaybe s n +#else +indexMaybe (PosixString s) n = PosixChar <$> BS.indexMaybe s n +#endif + +-- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: +-- +-- > 0 <= n < length bs +-- +-- @since 1.4.200.0 +(!?) :: PLATFORM_STRING -> Int -> Maybe PLATFORM_WORD +(!?) = indexMaybe + +-- | /O(n)/ The 'elemIndex' function returns the index of the first +-- element in the given 'OsString' which is equal to the query +-- element, or 'Nothing' if there is no such element. +-- +-- @since 1.4.200.0 +elemIndex :: PLATFORM_WORD -> PLATFORM_STRING -> Maybe Int +#ifdef WINDOWS +elemIndex (WindowsChar w) (WindowsString s) = BS16.elemIndex w s +#else +elemIndex (PosixChar w) (PosixString s) = BS.elemIndex w s +#endif + +-- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning +-- the indices of all elements equal to the query element, in ascending order. +-- +-- @since 1.4.200.0 +elemIndices :: PLATFORM_WORD -> PLATFORM_STRING -> [Int] +#ifdef WINDOWS +elemIndices (WindowsChar w) (WindowsString s) = BS16.elemIndices w s +#else +elemIndices (PosixChar w) (PosixString s) = BS.elemIndices w s +#endif + +-- | count returns the number of times its argument appears in the OsString +-- +-- @since 1.4.200.0 +count :: PLATFORM_WORD -> PLATFORM_STRING -> Int +#ifdef WINDOWS +count (WindowsChar w) (WindowsString s) = BS16.count w s +#else +count (PosixChar w) (PosixString s) = BS.count w s +#endif + +-- | /O(n)/ The 'findIndex' function takes a predicate and a 'OsString' and +-- returns the index of the first element in the OsString +-- satisfying the predicate. +-- +-- @since 1.4.200.0 +findIndex :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe Int +#ifdef WINDOWS +findIndex f (WindowsString s) = BS16.findIndex (f . WindowsChar) s +#else +findIndex f (PosixString s) = BS.findIndex (f . PosixChar) s +#endif + +-- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the +-- indices of all elements satisfying the predicate, in ascending order. +-- +-- @since 1.4.200.0 +findIndices :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [Int] +#ifdef WINDOWS +findIndices f (WindowsString s) = BS16.findIndices (f . WindowsChar) s +#else +findIndices f (PosixString s) = BS.findIndices (f . PosixChar) s +#endif diff --git a/System/OsString/Internal.hs b/System/OsString/Internal.hs index f72fdcb7..7023bd10 100644 --- a/System/OsString/Internal.hs +++ b/System/OsString/Internal.hs @@ -27,6 +27,8 @@ import qualified System.OsString.Windows as PF import GHC.IO.Encoding.UTF8 ( mkUTF8 ) import qualified System.OsString.Posix as PF #endif +import GHC.Stack (HasCallStack) +import Data.Bifunctor @@ -159,6 +161,12 @@ unpack (OsString x) = OsChar <$> PF.unpack x pack :: [OsChar] -> OsString pack = OsString . PF.pack . fmap (\(OsChar x) -> x) +empty :: OsString +empty = mempty + +singleton :: OsChar -> OsString +singleton = OsString . PF.singleton . getOsChar + -- | Truncates on unix to 1 and on Windows to 2 octets. unsafeFromChar :: Char -> OsChar @@ -172,3 +180,540 @@ toChar (OsChar (WindowsChar w)) = chr $ fromIntegral w toChar (OsChar (PosixChar w)) = chr $ fromIntegral w #endif +-- | /O(n)/ Append a byte to the end of a 'OsString' +-- +-- @since 1.4.200.0 +snoc :: OsString -> OsChar -> OsString +snoc (OsString s) (OsChar w) = OsString (PF.snoc s w) + +-- | /O(n)/ 'cons' is analogous to (:) for lists. +-- +-- @since 1.4.200.0 +cons :: OsChar -> OsString -> OsString +cons (OsChar w) (OsString s) = OsString (PF.cons w s) + +-- | /O(1)/ Extract the last element of a OsString, which must be finite and non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'unsnoc' instead. +-- +-- @since 1.4.200.0 +last :: HasCallStack => OsString -> OsChar +last (OsString s) = OsChar (PF.last s) + +-- | /O(n)/ Extract the elements after the head of a OsString, which must be non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'uncons' instead. +-- +-- @since 1.4.200.0 +tail :: HasCallStack => OsString -> OsString +tail (OsString s) = OsString (PF.tail s) + +-- | /O(n)/ Extract the 'head' and 'tail' of a OsString, returning 'Nothing' +-- if it is empty. +-- +-- @since 1.4.200.0 +uncons :: OsString -> Maybe (OsChar, OsString) +uncons (OsString s) = bimap OsChar OsString <$> PF.uncons s + +-- | /O(1)/ Extract the first element of a OsString, which must be non-empty. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'uncons' instead. +-- +-- @since 1.4.200.0 +head :: HasCallStack => OsString -> OsChar +head (OsString s) = OsChar (PF.head s) + +-- | /O(n)/ Return all the elements of a 'OsString' except the last one. +-- An exception will be thrown in the case of an empty OsString. +-- +-- This is a partial function, consider using 'unsnoc' instead. +-- +-- @since 1.4.200.0 +init :: HasCallStack => OsString -> OsString +init (OsString s) = OsString (PF.init s) + +-- | /O(n)/ Extract the 'init' and 'last' of a OsString, returning 'Nothing' +-- if it is empty. +-- +-- @since 1.4.200.0 +unsnoc :: OsString -> Maybe (OsString, OsChar) +unsnoc (OsString s) = bimap OsString OsChar <$> PF.unsnoc s + +-- | /O(1)/ Test whether a 'OsString' is empty. +-- +-- @since 1.4.200.0 +null :: OsString -> Bool +null (OsString s) = PF.null s + +-- | /O(1)/ The length of a 'OsString'. +-- +-- @since 1.4.200.0 +length :: OsString -> Int +length (OsString s) = PF.length s + +-- | /O(n)/ 'map' @f xs@ is the OsString obtained by applying @f@ to each +-- element of @xs@. +-- +-- @since 1.4.200.0 +map :: (OsChar -> OsChar) -> OsString -> OsString +map f (OsString s) = OsString (PF.map (getOsChar . f . OsChar) s) + +-- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. +-- +-- @since 1.4.200.0 +reverse :: OsString -> OsString +reverse (OsString s) = OsString (PF.reverse s) + +-- | /O(n)/ The 'intercalate' function takes a 'OsString' and a list of +-- 'OsString's and concatenates the list after interspersing the first +-- argument between each element of the list. +-- +-- @since 1.4.200.0 +intercalate :: OsString -> [OsString] -> OsString +intercalate (OsString s) xs = OsString (PF.intercalate s (fmap getOsString xs)) + +-- | 'foldl', applied to a binary operator, a starting value (typically +-- the left-identity of the operator), and a OsString, reduces the +-- OsString using the binary operator, from left to right. +-- +-- @since 1.4.200.0 +foldl :: (a -> OsChar -> a) -> a -> OsString -> a +foldl f a (OsString s) = PF.foldl (\a' c -> f a' (OsChar c)) a s + +-- | 'foldl'' is like 'foldl', but strict in the accumulator. +-- +-- @since 1.4.200.0 +foldl' :: (a -> OsChar -> a) -> a -> OsString -> a +foldl' f a (OsString s) = PF.foldl' (\a' c -> f a' (OsChar c)) a s + +-- | 'foldl1' is a variant of 'foldl' that has no starting value +-- argument, and thus must be applied to non-empty 'OsString's. +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldl1 :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar +foldl1 f (OsString s) = OsChar $ PF.foldl1 (\a' c -> getOsChar $ f (OsChar a') (OsChar c)) s + +-- | 'foldl1'' is like 'foldl1', but strict in the accumulator. +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldl1' :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar +foldl1' f (OsString s) = OsChar $ PF.foldl1' (\a' c -> getOsChar $ f (OsChar a') (OsChar c)) s + + +-- | 'foldr', applied to a binary operator, a starting value +-- (typically the right-identity of the operator), and a OsString, +-- reduces the OsString using the binary operator, from right to left. +-- +-- @since 1.4.200.0 +foldr :: (OsChar -> a -> a) -> a -> OsString -> a +foldr f a (OsString s) = PF.foldr (\c a' -> f (OsChar c) a') a s + +-- | 'foldr'' is like 'foldr', but strict in the accumulator. +-- +-- @since 1.4.200.0 +foldr' :: (OsChar -> a -> a) -> a -> OsString -> a +foldr' f a (OsString s) = PF.foldr' (\c a' -> f (OsChar c) a') a s + +-- | 'foldr1' is a variant of 'foldr' that has no starting value argument, +-- and thus must be applied to non-empty 'OsString's +-- An exception will be thrown in the case of an empty OsString. +-- +-- @since 1.4.200.0 +foldr1 :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar +foldr1 f (OsString s) = OsChar $ PF.foldr1 (\c a' -> getOsChar $ f (OsChar c) (OsChar a')) s + +-- | 'foldr1'' is a variant of 'foldr1', but is strict in the +-- accumulator. +-- +-- @since 1.4.200.0 +foldr1' :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar +foldr1' f (OsString s) = OsChar $ PF.foldr1' (\c a' -> getOsChar $ f (OsChar c) (OsChar a')) s + +-- | /O(n)/ Applied to a predicate and a 'OsString', 'all' determines +-- if all elements of the 'OsString' satisfy the predicate. +-- +-- @since 1.4.200.0 +all :: (OsChar -> Bool) -> OsString -> Bool +all f (OsString s) = PF.all (f . OsChar) s + +-- | /O(n)/ Applied to a predicate and a 'OsString', 'any' determines if +-- any element of the 'OsString' satisfies the predicate. +-- +-- @since 1.4.200.0 +any :: (OsChar -> Bool) -> OsString -> Bool +any f (OsString s) = PF.any (f . OsChar) s + +-- /O(n)/ Concatenate a list of OsStrings. +-- +-- @since 1.4.200.0 +concat :: [OsString] -> OsString +concat = mconcat + +-- | /O(n)/ 'replicate' @n x@ is a OsString of length @n@ with @x@ +-- the value of every element. The following holds: +-- +-- > replicate w c = unfoldr w (\u -> Just (u,u)) c +-- +-- @since 1.4.200.0 +replicate :: Int -> OsChar -> OsString +replicate i (OsChar w) = OsString $ PF.replicate i w + +-- | /O(n)/, where /n/ is the length of the result. The 'unfoldr' +-- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a +-- OsString from a seed value. The function takes the element and +-- returns 'Nothing' if it is done producing the OsString or returns +-- 'Just' @(a,b)@, in which case, @a@ is the next byte in the string, +-- and @b@ is the seed value for further production. +-- +-- This function is not efficient/safe. It will build a list of @[Word8]@ +-- and run the generator until it returns `Nothing`, otherwise recurse infinitely, +-- then finally create a 'OsString'. +-- +-- If you know the maximum length, consider using 'unfoldrN'. +-- +-- Examples: +-- +-- > unfoldr (\x -> if x <= 5 then Just (x, x + 1) else Nothing) 0 +-- > == pack [0, 1, 2, 3, 4, 5] +-- +-- @since 1.4.200.0 +unfoldr :: (a -> Maybe (OsChar, a)) -> a -> OsString +unfoldr f a = OsString $ PF.unfoldr (fmap (first getOsChar) . f) a + +-- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed +-- value. However, the length of the result is limited by the first +-- argument to 'unfoldrN'. This function is more efficient than 'unfoldr' +-- when the maximum length of the result is known. +-- +-- The following equation relates 'unfoldrN' and 'unfoldr': +-- +-- > fst (unfoldrN n f s) == take n (unfoldr f s) +-- +-- @since 1.4.200.0 +unfoldrN :: forall a. Int -> (a -> Maybe (OsChar, a)) -> a -> (OsString, Maybe a) +unfoldrN n f a = first OsString $ PF.unfoldrN n (fmap (first getOsChar) . f) a + +-- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix +-- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@. +-- +-- @since 1.4.200.0 +take :: Int -> OsString -> OsString +take n (OsString s) = OsString $ PF.take n s + +-- | /O(n)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@. +-- Takes @n@ elements from end of bytestring. +-- +-- >>> takeEnd 3 "abcdefg" +-- "efg" +-- >>> takeEnd 0 "abcdefg" +-- "" +-- >>> takeEnd 4 "abc" +-- "abc" +-- +-- @since 1.4.200.0 +takeEnd :: Int -> OsString -> OsString +takeEnd n (OsString s) = OsString $ PF.takeEnd n s + +-- | Returns the longest (possibly empty) suffix of elements +-- satisfying the predicate. +-- +-- @'takeWhileEnd' p@ is equivalent to @'reverse' . 'takeWhile' p . 'reverse'@. +-- +-- @since 1.4.200.0 +takeWhileEnd :: (OsChar -> Bool) -> OsString -> OsString +takeWhileEnd f (OsString s) = OsString $ PF.takeWhileEnd (f . OsChar) s + +-- | Similar to 'Prelude.takeWhile', +-- returns the longest (possibly empty) prefix of elements +-- satisfying the predicate. +-- +-- @since 1.4.200.0 +takeWhile :: (OsChar -> Bool) -> OsString -> OsString +takeWhile f (OsString s) = OsString $ PF.takeWhile (f . OsChar) s + +-- | /O(n)/ 'drop' @n@ @xs@ returns the suffix of @xs@ after the first n elements, or 'empty' if @n > 'length' xs@. +-- +-- @since 1.4.200.0 +drop :: Int -> OsString -> OsString +drop n (OsString s) = OsString $ PF.drop n s + +-- | /O(n)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@. +-- Drops @n@ elements from end of bytestring. +-- +-- >>> dropEnd 3 "abcdefg" +-- "abcd" +-- >>> dropEnd 0 "abcdefg" +-- "abcdefg" +-- >>> dropEnd 4 "abc" +-- "" +-- +-- @since 1.4.200.0 +dropEnd :: Int -> OsString -> OsString +dropEnd n (OsString s) = OsString $ PF.dropEnd n s + +-- | Similar to 'Prelude.dropWhile', +-- drops the longest (possibly empty) prefix of elements +-- satisfying the predicate and returns the remainder. +-- +-- @since 1.4.200.0 +dropWhile :: (OsChar -> Bool) -> OsString -> OsString +dropWhile f (OsString s) = OsString $ PF.dropWhile (f . OsChar) s + +-- | Similar to 'Prelude.dropWhileEnd', +-- drops the longest (possibly empty) suffix of elements +-- satisfying the predicate and returns the remainder. +-- +-- @'dropWhileEnd' p@ is equivalent to @'reverse' . 'dropWhile' p . 'reverse'@. +-- +-- @since 1.4.200.0 +dropWhileEnd :: (OsChar -> Bool) -> OsString -> OsString +dropWhileEnd f (OsString s) = OsString $ PF.dropWhileEnd (f . OsChar) s + +-- | Returns the longest (possibly empty) suffix of elements which __do not__ +-- satisfy the predicate and the remainder of the string. +-- +-- 'breakEnd' @p@ is equivalent to @'spanEnd' (not . p)@ and to @('takeWhileEnd' (not . p) &&& 'dropWhileEnd' (not . p))@. +-- +-- @since 1.4.200.0 +breakEnd :: (OsChar -> Bool) -> OsString -> (OsString, OsString) +breakEnd f (OsString s) = bimap OsString OsString $ PF.breakEnd (f . OsChar) s + +-- | Similar to 'Prelude.break', +-- returns the longest (possibly empty) prefix of elements which __do not__ +-- satisfy the predicate and the remainder of the string. +-- +-- 'break' @p@ is equivalent to @'span' (not . p)@ and to @('takeWhile' (not . p) &&& 'dropWhile' (not . p))@. +-- +-- @since 1.4.200.0 +break :: (OsChar -> Bool) -> OsString -> (OsString, OsString) +break f (OsString s) = bimap OsString OsString $ PF.break (f . OsChar) s + +-- | Similar to 'Prelude.span', +-- returns the longest (possibly empty) prefix of elements +-- satisfying the predicate and the remainder of the string. +-- +-- 'span' @p@ is equivalent to @'break' (not . p)@ and to @('takeWhile' p &&& 'dropWhile' p)@. +-- +-- @since 1.4.200.0 +span :: (OsChar -> Bool) -> OsString -> (OsString, OsString) +span f (OsString s) = bimap OsString OsString $ PF.span (f . OsChar) s + +-- | Returns the longest (possibly empty) suffix of elements +-- satisfying the predicate and the remainder of the string. +-- +-- 'spanEnd' @p@ is equivalent to @'breakEnd' (not . p)@ and to @('takeWhileEnd' p &&& 'dropWhileEnd' p)@. +-- +-- We have +-- +-- > spanEnd (not . isSpace) "x y z" == ("x y ", "z") +-- +-- and +-- +-- > spanEnd (not . isSpace) sbs +-- > == +-- > let (x, y) = span (not . isSpace) (reverse sbs) in (reverse y, reverse x) +-- +-- @since 1.4.200.0 +spanEnd :: (OsChar -> Bool) -> OsString -> (OsString, OsString) +spanEnd f (OsString s) = bimap OsString OsString $ PF.spanEnd (f . OsChar) s + +-- | /O(n)/ 'splitAt' @n sbs@ is equivalent to @('take' n sbs, 'drop' n sbs)@. +-- +-- @since 1.4.200.0 +splitAt :: Int -> OsString -> (OsString, OsString) +splitAt n (OsString s) = bimap OsString OsString $ PF.splitAt n s + +-- | /O(n)/ Break a 'OsString' into pieces separated by the byte +-- argument, consuming the delimiter. I.e. +-- +-- > split 10 "a\nb\nd\ne" == ["a","b","d","e"] -- fromEnum '\n' == 10 +-- > split 97 "aXaXaXa" == ["","X","X","X",""] -- fromEnum 'a' == 97 +-- > split 120 "x" == ["",""] -- fromEnum 'x' == 120 +-- > split undefined "" == [] -- and not [""] +-- +-- and +-- +-- > intercalate [c] . split c == id +-- > split == splitWith . (==) +-- +-- @since 1.4.200.0 +split :: OsChar -> OsString -> [OsString] +split (OsChar w) (OsString s) = OsString <$> PF.split w s + +-- | /O(n)/ Splits a 'OsString' into components delimited by +-- separators, where the predicate returns True for a separator element. +-- The resulting components do not contain the separators. Two adjacent +-- separators result in an empty component in the output. eg. +-- +-- > splitWith (==97) "aabbaca" == ["","","bb","c",""] -- fromEnum 'a' == 97 +-- > splitWith undefined "" == [] -- and not [""] +-- +-- @since 1.4.200.0 +splitWith :: (OsChar -> Bool) -> OsString -> [OsString] +splitWith f (OsString s) = OsString <$> PF.splitWith (f . OsChar) s + +-- | /O(n)/ The 'stripSuffix' function takes two OsStrings and returns 'Just' +-- the remainder of the second iff the first is its suffix, and otherwise +-- 'Nothing'. +-- +-- @since 1.4.200.0 +stripSuffix :: OsString -> OsString -> Maybe OsString +stripSuffix (OsString a) (OsString b) = OsString <$> PF.stripSuffix a b + +-- | /O(n)/ The 'stripPrefix' function takes two OsStrings and returns 'Just' +-- the remainder of the second iff the first is its prefix, and otherwise +-- 'Nothing'. +-- +-- @since 1.4.200.0 +stripPrefix :: OsString -> OsString -> Maybe OsString +stripPrefix (OsString a) (OsString b) = OsString <$> PF.stripPrefix a b + + +-- | Check whether one string is a substring of another. +-- +-- @since 1.4.200.0 +isInfixOf :: OsString -> OsString -> Bool +isInfixOf (OsString a) (OsString b) = PF.isInfixOf a b + +-- |/O(n)/ The 'isPrefixOf' function takes two OsStrings and returns 'True' +-- +-- @since 1.4.200.0 +isPrefixOf :: OsString -> OsString -> Bool +isPrefixOf (OsString a) (OsString b) = PF.isPrefixOf a b + +-- | /O(n)/ The 'isSuffixOf' function takes two OsStrings and returns 'True' +-- iff the first is a suffix of the second. +-- +-- The following holds: +-- +-- > isSuffixOf x y == reverse x `isPrefixOf` reverse y +-- +-- @since 1.4.200.0 +isSuffixOf :: OsString -> OsString -> Bool +isSuffixOf (OsString a) (OsString b) = PF.isSuffixOf a b + +-- | Break a string on a substring, returning a pair of the part of the +-- string prior to the match, and the rest of the string. +-- +-- The following relationships hold: +-- +-- > break (== c) l == breakSubstring (singleton c) l +-- +-- For example, to tokenise a string, dropping delimiters: +-- +-- > tokenise x y = h : if null t then [] else tokenise x (drop (length x) t) +-- > where (h,t) = breakSubstring x y +-- +-- To skip to the first occurrence of a string: +-- +-- > snd (breakSubstring x y) +-- +-- To take the parts of a string before a delimiter: +-- +-- > fst (breakSubstring x y) +-- +-- Note that calling `breakSubstring x` does some preprocessing work, so +-- you should avoid unnecessarily duplicating breakSubstring calls with the same +-- pattern. +-- +-- @since 1.4.200.0 +breakSubstring :: OsString -> OsString -> (OsString, OsString) +breakSubstring (OsString a) (OsString b) = bimap OsString OsString $ PF.breakSubstring a b + +-- | /O(n)/ 'elem' is the 'OsString' membership predicate. +-- +-- @since 1.4.200.0 +elem :: OsChar -> OsString -> Bool +elem (OsChar w) (OsString s) = PF.elem w s + +-- | /O(n)/ The 'find' function takes a predicate and a OsString, +-- and returns the first element in matching the predicate, or 'Nothing' +-- if there is no such element. +-- +-- > find f p = case findIndex f p of Just n -> Just (p ! n) ; _ -> Nothing +-- +-- @since 1.4.200.0 +find :: (OsChar -> Bool) -> OsString -> Maybe OsChar +find f (OsString s) = OsChar <$> PF.find (f . OsChar) s + +-- | /O(n)/ 'filter', applied to a predicate and a OsString, +-- returns a OsString containing those characters that satisfy the +-- predicate. +-- +-- @since 1.4.200.0 +filter :: (OsChar -> Bool) -> OsString -> OsString +filter f (OsString s) = OsString $ PF.filter (f . OsChar) s + +-- | /O(n)/ The 'partition' function takes a predicate a OsString and returns +-- the pair of OsStrings with elements which do and do not satisfy the +-- predicate, respectively; i.e., +-- +-- > partition p bs == (filter p sbs, filter (not . p) sbs) +-- +-- @since 1.4.200.0 +partition :: (OsChar -> Bool) -> OsString -> (OsString, OsString) +partition f (OsString s) = bimap OsString OsString $ PF.partition (f . OsChar) s + +-- | /O(1)/ 'OsString' index (subscript) operator, starting from 0. +-- +-- @since 1.4.200.0 +index :: HasCallStack => OsString -> Int -> OsChar +index (OsString s) n = OsChar $ PF.index s n + +-- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: +-- +-- > 0 <= n < length bs +-- +-- @since 1.4.200.0 +indexMaybe :: OsString -> Int -> Maybe OsChar +indexMaybe (OsString s) n = OsChar <$> PF.indexMaybe s n + +-- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: +-- +-- > 0 <= n < length bs +-- +-- @since 1.4.200.0 +(!?) :: OsString -> Int -> Maybe OsChar +(!?) = indexMaybe + +-- | /O(n)/ The 'elemIndex' function returns the index of the first +-- element in the given 'OsString' which is equal to the query +-- element, or 'Nothing' if there is no such element. +-- +-- @since 1.4.200.0 +elemIndex :: OsChar -> OsString -> Maybe Int +elemIndex (OsChar w) (OsString s) = PF.elemIndex w s + +-- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning +-- the indices of all elements equal to the query element, in ascending order. +-- +-- @since 1.4.200.0 +elemIndices :: OsChar -> OsString -> [Int] +elemIndices (OsChar w) (OsString s) = PF.elemIndices w s + +-- | count returns the number of times its argument appears in the OsString +-- +-- @since 1.4.200.0 +count :: OsChar -> OsString -> Int +count (OsChar w) (OsString s) = PF.count w s + +-- | /O(n)/ The 'findIndex' function takes a predicate and a 'OsString' and +-- returns the index of the first element in the OsString +-- satisfying the predicate. +-- +-- @since 1.4.200.0 +findIndex :: (OsChar -> Bool) -> OsString -> Maybe Int +findIndex f (OsString s) = PF.findIndex (f . OsChar) s + +-- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the +-- indices of all elements satisfying the predicate, in ascending order. +-- +-- @since 1.4.200.0 +findIndices :: (OsChar -> Bool) -> OsString -> [Int] +findIndices f (OsString s) = PF.findIndices (f . OsChar) s + diff --git a/changelog.md b/changelog.md index a5bca439..9d4bb126 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,10 @@ _Note: below all `FilePath` values are unquoted, so `\\` really means two backslashes._ +## 1.4.200.0 *??? 2023* + +* Introduce bytestring-like functions (substrings, predicates, searching, etc.) to `System.OsString`, `System.OsString.Windows` and `System.OsString.Posix` + ## 1.4.100.4 *Jul 2023* * Fix isInfixOf and breakSubString in Word16, wrt [#195](https://github.com/haskell/filepath/issues/195) diff --git a/filepath.cabal b/filepath.cabal index c3b76370..ae4eba4f 100644 --- a/filepath.cabal +++ b/filepath.cabal @@ -1,6 +1,6 @@ cabal-version: 2.2 name: filepath -version: 1.4.100.4 +version: 1.4.200.0 -- NOTE: Don't forget to update ./changelog.md license: BSD-3-Clause @@ -158,6 +158,9 @@ test-suite bytestring-tests hs-source-dirs: tests tests/bytestring-tests other-modules: Properties.ShortByteString + Properties.WindowsString + Properties.PosixString + Properties.OsString Properties.ShortByteString.Word16 TestUtil diff --git a/tests/abstract-filepath/OsPathSpec.hs b/tests/abstract-filepath/OsPathSpec.hs index bee6fb57..8f334516 100644 --- a/tests/abstract-filepath/OsPathSpec.hs +++ b/tests/abstract-filepath/OsPathSpec.hs @@ -14,8 +14,8 @@ import System.OsPath.Windows as Windows import System.OsPath.Encoding import qualified System.OsString.Internal.Types as OS import System.OsPath.Data.ByteString.Short ( toShort ) -import System.OsString.Posix as PosixS -import System.OsString.Windows as WindowsS +import System.OsString.Posix as PosixS hiding (map) +import System.OsString.Windows as WindowsS hiding (map) import Control.Exception import Data.ByteString ( ByteString ) diff --git a/tests/bytestring-tests/Main.hs b/tests/bytestring-tests/Main.hs index a37e79a9..ae8015a7 100644 --- a/tests/bytestring-tests/Main.hs +++ b/tests/bytestring-tests/Main.hs @@ -2,9 +2,12 @@ module Main (main) where +import qualified Properties.OsString as PropOs +import qualified Properties.PosixString as PropPos +import qualified Properties.WindowsString as PropWin import qualified Properties.ShortByteString as PropSBS import qualified Properties.ShortByteString.Word16 as PropSBSW16 import TestUtil main :: IO () -main = runTests (PropSBS.tests ++ PropSBSW16.tests) +main = runTests (PropSBS.tests ++ PropSBSW16.tests ++ PropWin.tests ++ PropPos.tests ++ PropOs.tests) diff --git a/tests/bytestring-tests/Properties/Common.hs b/tests/bytestring-tests/Properties/Common.hs index c5ef566a..77554be3 100644 --- a/tests/bytestring-tests/Properties/Common.hs +++ b/tests/bytestring-tests/Properties/Common.hs @@ -5,6 +5,10 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE ViewPatterns #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE DeriveGeneric #-} +{-# LANGUAGE StandaloneDeriving #-} +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# OPTIONS_GHC -Wno-orphans #-} -- We are happy to sacrifice optimizations in exchange for faster compilation, @@ -15,20 +19,46 @@ -fmax-simplifier-iterations=1 -fsimplifier-phases=0 -fno-call-arity -fno-case-merge -fno-cmm-elim-common-blocks -fno-cmm-sink -fno-cpr-anal -fno-cse -fno-do-eta-reduction -fno-float-in -fno-full-laziness - -fno-loopification -fno-specialise -fno-strictness #-} + -fno-loopification -fno-specialise -fno-strictness -Wno-unused-imports -Wno-unused-top-binds #-} + +#ifdef OSWORD +module Properties.OsString (tests) where +import System.OsString.Internal.Types (OsString(..), OsChar(..), getOsChar) +import qualified System.OsString as B +import qualified System.OsString as BS +import qualified System.OsPath.Data.ByteString.Short.Internal as BSI (_nul, isSpace) + +#else #ifdef WORD16 +#ifdef WIN +module Properties.WindowsString (tests) where +import qualified System.OsString.Windows as B +import qualified System.OsString.Windows as BS +#else module Properties.ShortByteString.Word16 (tests) where import System.OsPath.Data.ByteString.Short.Internal (_nul, isSpace) import qualified System.OsPath.Data.ByteString.Short.Word16 as B import qualified System.OsPath.Data.ByteString.Short as BS +#endif +#else +#ifdef POSIX +module Properties.PosixString (tests) where +import qualified System.OsString.Posix as B +import qualified System.OsString.Posix as BS #else module Properties.ShortByteString (tests) where import qualified System.OsPath.Data.ByteString.Short as B -import qualified Data.Char as C #endif +#endif +#endif + import Data.ByteString.Short (ShortByteString) +import qualified Data.Char as C +import qualified System.OsPath.Data.ByteString.Short.Word16 as B16 +import qualified System.OsPath.Data.ByteString.Short as B8 + import Data.Word import Control.Arrow @@ -40,7 +70,157 @@ import Test.QuickCheck import Test.QuickCheck.Monadic ( monadicIO, run ) import Text.Show.Functions () +import System.OsString.Internal.Types (WindowsString(..), WindowsChar(..), getWindowsChar, PosixChar(..), PosixString(..), getPosixChar, OsString(..), OsChar(..), getOsChar) +import qualified System.OsString.Posix as PBS +import qualified System.OsString.Windows as WBS +import qualified System.OsString as OBS +import qualified System.OsPath.Data.ByteString.Short.Internal as BSI (_nul, isSpace) + + +instance Arbitrary PosixString where + arbitrary = do + bs <- sized sizedByteString' + n <- choose (0, 2) + return (PBS.drop n bs) -- to give us some with non-0 offset + where + sizedByteString' :: Int -> Gen PosixString + sizedByteString' n = do m <- choose(0, n) + fmap (PosixString . B8.pack) $ vectorOf m arbitrary + +instance Arbitrary PosixChar where + arbitrary = fmap PosixChar (arbitrary @Word8) + +instance CoArbitrary PosixChar where + coarbitrary s = coarbitrary (PBS.toChar s) + +instance CoArbitrary PosixString where + coarbitrary s = coarbitrary (PBS.unpack s) + +deriving instance Num PosixChar + +deriving instance Bounded PosixChar + +instance Arbitrary WindowsString where + arbitrary = do + bs <- sized sizedByteString' + n <- choose (0, 2) + return (WBS.drop n bs) -- to give us some with non-0 offset + where + sizedByteString' :: Int -> Gen WindowsString + sizedByteString' n = do m <- choose(0, n) + fmap (WindowsString . B16.pack) $ vectorOf m arbitrary + +instance Arbitrary WindowsChar where + arbitrary = fmap WindowsChar (arbitrary @Word16) + +instance CoArbitrary WindowsChar where + coarbitrary s = coarbitrary (WBS.toChar s) + +instance CoArbitrary WindowsString where + coarbitrary s = coarbitrary (WBS.unpack s) + +deriving instance Num WindowsChar + +deriving instance Bounded WindowsChar + +isSpaceWin :: WindowsChar -> Bool +isSpaceWin = BSI.isSpace . getWindowsChar + +numWordWin :: WindowsString -> Int +numWordWin = B16.numWord16 . getWindowsString + + +swapWWin :: WindowsChar -> WindowsChar +swapWWin = WindowsChar . byteSwap16 . getWindowsChar + +isSpacePosix :: PosixChar -> Bool +isSpacePosix = C.isSpace . word8ToChar . getPosixChar + +numWordPosix :: PosixString -> Int +numWordPosix = B8.length . getPosixString + + +swapWPosix :: PosixChar -> PosixChar +swapWPosix = id + +#ifdef OSWORD +isSpace :: OsChar -> Bool +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) +isSpace = isSpaceWin . getOsChar +#else +isSpace = isSpacePosix . getOsChar +#endif + +numWord :: OsString -> Int +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) +numWord = numWordWin . getOsString +#else +numWord = numWordPosix . getOsString +#endif + +toElem :: OsChar -> OsChar +toElem = id + +swapW :: OsChar -> OsChar +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) +swapW = OsChar . swapWWin . getOsChar +#else +swapW = OsChar . swapWPosix . getOsChar +#endif + +instance Arbitrary OsString where + arbitrary = OsString <$> arbitrary + +instance Arbitrary OsChar where + arbitrary = OsChar <$> arbitrary + +instance CoArbitrary OsChar where + coarbitrary s = coarbitrary (OBS.toChar s) + +instance CoArbitrary OsString where + coarbitrary s = coarbitrary (OBS.unpack s) + +deriving instance Num OsChar +deriving instance Bounded OsChar + +instance Arbitrary ShortByteString where +#if defined(mingw32_HOST_OS) || defined(__MINGW32__) + arbitrary = getWindowsString <$> arbitrary +#else + arbitrary = getPosixString <$> arbitrary +#endif + +#else + #ifdef WORD16 + +instance Arbitrary ShortByteString where + arbitrary = do + bs <- sized sizedByteString + n <- choose (0, 2) + return (B16.drop n bs) -- to give us some with non-0 offset + where + sizedByteString :: Int -> Gen ShortByteString + sizedByteString n = do m <- choose(0, n) + fmap B16.pack $ vectorOf m arbitrary + +instance CoArbitrary ShortByteString where + coarbitrary s = coarbitrary (B16.unpack s) +#ifdef WIN + +isSpace :: WindowsChar -> Bool +isSpace = isSpaceWin + +numWord :: WindowsString -> Int +numWord = numWordWin + +toElem :: WindowsChar -> WindowsChar +toElem = id + +swapW :: WindowsChar -> WindowsChar +swapW = swapWWin + +#else numWord :: ShortByteString -> Int numWord = B.numWord16 @@ -50,18 +230,22 @@ toElem = id swapW :: Word16 -> Word16 swapW = byteSwap16 -sizedByteString :: Int -> Gen ShortByteString -sizedByteString n = do m <- choose(0, n) - fmap B.pack $ vectorOf m arbitrary -instance Arbitrary ShortByteString where - arbitrary = do - bs <- sized sizedByteString - n <- choose (0, 2) - return (B.drop n bs) -- to give us some with non-0 offset +#endif +#else +#ifdef POSIX -instance CoArbitrary ShortByteString where - coarbitrary s = coarbitrary (B.unpack s) +isSpace :: PosixChar -> Bool +isSpace = isSpacePosix + +numWord :: PosixString -> Int +numWord = numWordPosix + +toElem :: PosixChar -> PosixChar +toElem = id + +swapW :: PosixChar -> PosixChar +swapW = swapWPosix #else _nul :: Word8 @@ -70,12 +254,9 @@ _nul = 0x00 isSpace :: Word8 -> Bool isSpace = C.isSpace . word8ToChar --- | Total conversion to char. -word8ToChar :: Word8 -> Char -word8ToChar = C.chr . fromIntegral numWord :: ShortByteString -> Int -numWord = B.length +numWord = B8.length toElem :: Word8 -> Word8 toElem = id @@ -84,20 +265,23 @@ swapW :: Word8 -> Word8 swapW = id -sizedByteString :: Int -> Gen ShortByteString -sizedByteString n = do m <- choose(0, n) - fmap B.pack $ vectorOf m arbitrary + +#endif instance Arbitrary ShortByteString where arbitrary = do - bs <- sized sizedByteString + bs <- sized sizedByteString' n <- choose (0, 2) - return (B.drop n bs) -- to give us some with non-0 offset - shrink = map B.pack . shrink . B.unpack + return (B8.drop n bs) -- to give us some with non-0 offset + where + sizedByteString' :: Int -> Gen ShortByteString + sizedByteString' n = do m <- choose(0, n) + fmap B8.pack $ vectorOf m arbitrary + shrink = map B8.pack . shrink . B8.unpack instance CoArbitrary ShortByteString where - coarbitrary s = coarbitrary (B.unpack s) - + coarbitrary s = coarbitrary (B8.unpack s) +#endif #endif @@ -132,7 +316,7 @@ tests = , ("compare LT empty", property $ \x -> not (B.null x) ==> compare B.empty x == LT) , ("compare GT concat", - property $ \x y -> not (B.null y) ==> compare (x <> y) x == GT) + property $ \x y -> not (B.null y) ==> compare (x `mappend` y) x == GT) , ("compare char" , property $ \(toElem -> c) (toElem -> d) -> compare (swapW c) (swapW d) == compare (B.singleton c) (B.singleton d)) , ("compare unsigned", @@ -150,6 +334,16 @@ tests = once $ B.unpack mempty === []) #ifdef WORD16 +#ifdef WIN + , ("isInfixOf works correctly under UTF16", + once $ + let foo = WindowsString $ B8.pack [0xbb, 0x03] + foo' = WindowsString $ B8.pack [0xd2, 0xbb] + bar = WindowsString $ B8.pack [0xd2, 0xbb, 0x03, 0xad] + bar' = WindowsString $ B8.pack [0xd2, 0xbb, 0x03, 0xad, 0xd2, 0xbb, 0x03, 0xad, 0xbb, 0x03, 0x00, 0x00] + in [B.isInfixOf foo bar, B.isInfixOf foo' bar, B.isInfixOf foo bar'] === [False, True, True] + ) +#else , ("isInfixOf works correctly under UTF16", once $ let foo = BS.pack [0xbb, 0x03] @@ -158,6 +352,7 @@ tests = bar' = BS.pack [0xd2, 0xbb, 0x03, 0xad, 0xd2, 0xbb, 0x03, 0xad, 0xbb, 0x03, 0x00, 0x00] in [B.isInfixOf foo bar, B.isInfixOf foo' bar, B.isInfixOf foo bar'] === [False, True, True] ) +#endif #endif , ("break breakSubstring", property $ \(toElem -> c) x -> B.break (== c) x === B.breakSubstring (B.singleton c) x @@ -193,7 +388,7 @@ tests = , ("mappend" , property $ \x y -> B.unpack (mappend x y) === B.unpack x `mappend` B.unpack y) , ("<>" , - property $ \x y -> B.unpack (x <> y) === B.unpack x <> B.unpack y) + property $ \x y -> B.unpack (x `mappend` y) === B.unpack x `mappend` B.unpack y) , ("stimes" , property $ \(Positive n) x -> stimes (n :: Int) (x :: ShortByteString) === mtimesDefault n x) @@ -407,14 +602,15 @@ tests = -- property $ \n f (toElem -> a) -> B.unpack (B.take (fromIntegral n) (B.unfoldr (fmap (first toElem) . f) a)) === -- take n (unfoldr (fmap (first toElem) . f) a)) -- -#ifdef WORD16 +#if defined(WORD16) && !defined(WIN) && !defined(OSWORD) && !defined(POSIX) , ("useAsCWString str packCWString == str" , property $ \x -> not (B.any (== _nul) x) ==> monadicIO $ run (B.useAsCWString x B.packCWString >>= \x' -> pure (x == x'))) , ("useAsCWStringLen str packCWStringLen == str" , property $ \x -> not (B.any (== _nul) x) ==> monadicIO $ run (B.useAsCWStringLen x B.packCWStringLen >>= \x' -> pure (x == x'))) -#else +#endif +#if !defined(WORD16) && !defined(WIN) && !defined(OSWORD) && !defined(POSIX) , ("useAsCString str packCString == str" , property $ \x -> not (B.any (== _nul) x) ==> monadicIO $ run (B.useAsCString x B.packCString >>= \x' -> pure (x == x'))) @@ -439,3 +635,7 @@ splitWith f ys = go [] ys unsnoc :: [a] -> Maybe ([a], a) unsnoc [] = Nothing unsnoc xs = Just (init xs, last xs) + +-- | Total conversion to char. +word8ToChar :: Word8 -> Char +word8ToChar = C.chr . fromIntegral diff --git a/tests/bytestring-tests/Properties/OsString.hs b/tests/bytestring-tests/Properties/OsString.hs new file mode 100644 index 00000000..e81348b7 --- /dev/null +++ b/tests/bytestring-tests/Properties/OsString.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE CPP #-} +#undef WORD16 +#undef POSIX +#undef WIN +#define OSWORD +#include "Common.hs" + diff --git a/tests/bytestring-tests/Properties/PosixString.hs b/tests/bytestring-tests/Properties/PosixString.hs new file mode 100644 index 00000000..e0b9d981 --- /dev/null +++ b/tests/bytestring-tests/Properties/PosixString.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE CPP #-} +#undef WORD16 +#define POSIX +#undef WIN +#undef OSWORD +#include "Common.hs" + diff --git a/tests/bytestring-tests/Properties/ShortByteString.hs b/tests/bytestring-tests/Properties/ShortByteString.hs index 3040dfb8..97c91090 100644 --- a/tests/bytestring-tests/Properties/ShortByteString.hs +++ b/tests/bytestring-tests/Properties/ShortByteString.hs @@ -1,3 +1,7 @@ {-# LANGUAGE CPP #-} #undef WORD16 +#undef WIN +#undef POSIX +#undef OSWORD #include "Common.hs" + diff --git a/tests/bytestring-tests/Properties/ShortByteString/Word16.hs b/tests/bytestring-tests/Properties/ShortByteString/Word16.hs index aa426397..d604ef97 100644 --- a/tests/bytestring-tests/Properties/ShortByteString/Word16.hs +++ b/tests/bytestring-tests/Properties/ShortByteString/Word16.hs @@ -1,3 +1,6 @@ {-# LANGUAGE CPP #-} #define WORD16 +#undef WIN +#undef POSIX +#undef OSWORD #include "../Common.hs" diff --git a/tests/bytestring-tests/Properties/WindowsString.hs b/tests/bytestring-tests/Properties/WindowsString.hs new file mode 100644 index 00000000..1ce96b04 --- /dev/null +++ b/tests/bytestring-tests/Properties/WindowsString.hs @@ -0,0 +1,7 @@ +{-# LANGUAGE CPP #-} +#define WORD16 +#define WIN +#undef POSIX +#undef OSWORD +#include "Common.hs" + From 1145247d679144c8028b56dd125ca98d099b8470 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Sun, 15 Oct 2023 17:01:26 +0800 Subject: [PATCH 2/5] Coerce functions --- System/OsString/Common.hs | 340 ++++++++---------------------------- System/OsString/Internal.hs | 113 ++++++------ 2 files changed, 125 insertions(+), 328 deletions(-) diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs index cd42b279..46afc6a3 100644 --- a/System/OsString/Common.hs +++ b/System/OsString/Common.hs @@ -2,6 +2,8 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} +{-# OPTIONS_GHC -Wno-unused-imports #-} + -- This template expects CPP definitions for: -- MODULE_NAME = Posix | Windows -- IS_WINDOWS = False | True @@ -138,6 +140,7 @@ import System.OsString.Internal.Types ( #endif ) +import Data.Coerce import Data.Char import Control.Monad.Catch ( MonadThrow, throwM ) @@ -162,8 +165,7 @@ import System.OsPath.Encoding import System.IO ( TextEncoding, utf16le ) import GHC.IO.Encoding.UTF16 ( mkUTF16le ) -import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16 -import qualified System.OsPath.Data.ByteString.Short as BS8 +import qualified System.OsPath.Data.ByteString.Short.Word16 as BS #else import System.OsPath.Encoding import System.IO @@ -174,6 +176,8 @@ import qualified System.OsPath.Data.ByteString.Short as BS import GHC.Stack (HasCallStack) import Prelude hiding (last, tail, head, init, null, length, map, reverse, foldl, foldr, foldl1, foldr1, all, any, concat, replicate, take, takeWhile, drop, dropWhile, break, span, splitAt, elem, filter) import Data.Bifunctor ( bimap ) +import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16 +import qualified System.OsPath.Data.ByteString.Short as BS8 @@ -415,21 +419,13 @@ toChar (PosixChar w) = chr $ fromIntegral w -- -- @since 1.4.200.0 snoc :: PLATFORM_STRING -> PLATFORM_WORD -> PLATFORM_STRING -#ifdef WINDOWS -snoc (WindowsString s) (WindowsChar w) = WindowsString (BS16.snoc s w) -#else -snoc (PosixString s) (PosixChar w) = PosixString (BS.snoc s w) -#endif +snoc = coerce BS.snoc -- | /O(n)/ 'cons' is analogous to (:) for lists. -- -- @since 1.4.200.0 cons :: PLATFORM_WORD -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -cons (WindowsChar w) (WindowsString s) = WindowsString (BS16.cons w s) -#else -cons (PosixChar w) (PosixString s) = PosixString (BS.cons w s) -#endif +cons = coerce BS.cons -- | /O(1)/ Extract the last element of a OsString, which must be finite and non-empty. @@ -439,11 +435,7 @@ cons (PosixChar w) (PosixString s) = PosixString (BS.cons w s) -- -- @since 1.4.200.0 last :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -last (WindowsString s) = WindowsChar (BS16.last s) -#else -last (PosixString s) = PosixChar (BS.last s) -#endif +last = coerce BS.last -- | /O(n)/ Extract the elements after the head of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -452,22 +444,14 @@ last (PosixString s) = PosixChar (BS.last s) -- -- @since 1.4.200.0 tail :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -tail (WindowsString s) = WindowsString (BS16.tail s) -#else -tail (PosixString s) = PosixString (BS.tail s) -#endif +tail = coerce BS.tail -- | /O(n)/ Extract the 'head' and 'tail' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 uncons :: PLATFORM_STRING -> Maybe (PLATFORM_WORD, PLATFORM_STRING) -#ifdef WINDOWS -uncons (WindowsString s) = (bimap WindowsChar WindowsString) <$> (BS16.uncons s) -#else -uncons (PosixString s) = (bimap PosixChar PosixString) <$> (BS.uncons s) -#endif +uncons = coerce BS.uncons -- | /O(1)/ Extract the first element of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -476,11 +460,7 @@ uncons (PosixString s) = (bimap PosixChar PosixString) <$> (BS.uncons s) -- -- @since 1.4.200.0 head :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -head (WindowsString s) = WindowsChar (BS16.head s) -#else -head (PosixString s) = PosixChar (BS.head s) -#endif +head = coerce BS.head -- | /O(n)/ Return all the elements of a 'OsString' except the last one. -- An exception will be thrown in the case of an empty OsString. @@ -489,63 +469,39 @@ head (PosixString s) = PosixChar (BS.head s) -- -- @since 1.4.200.0 init :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -init (WindowsString s) = WindowsString (BS16.init s) -#else -init (PosixString s) = PosixString (BS.init s) -#endif +init = coerce BS.init -- | /O(n)/ Extract the 'init' and 'last' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 unsnoc :: PLATFORM_STRING -> Maybe (PLATFORM_STRING, PLATFORM_WORD) -#ifdef WINDOWS -unsnoc (WindowsString s) = (bimap WindowsString WindowsChar) <$> (BS16.unsnoc s) -#else -unsnoc (PosixString s) = (bimap PosixString PosixChar) <$> (BS.unsnoc s) -#endif +unsnoc = coerce BS.unsnoc -- | /O(1)/. The empty 'OsString'. -- -- @since 1.4.200.0 null :: PLATFORM_STRING -> Bool -#ifdef WINDOWS -null (WindowsString s) = BS16.null s -#else -null (PosixString s) = BS.null s -#endif +null = coerce BS.null -- | /O(1)/ The length of a 'OsString'. -- -- @since 1.4.200.0 length :: PLATFORM_STRING -> Int -#ifdef WINDOWS -length (WindowsString s) = BS16.length s -#else -length (PosixString s) = BS.length s -#endif +length = coerce BS.length -- | /O(n)/ 'map' @f xs@ is the OsString obtained by applying @f@ to each -- element of @xs@. -- -- @since 1.4.200.0 map :: (PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -map f (WindowsString s) = WindowsString (BS16.map (getWindowsChar . f . WindowsChar) s) -#else -map f (PosixString s) = PosixString (BS.map (getPosixChar . f . PosixChar) s) -#endif +map = coerce BS.map -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. -- -- @since 1.4.200.0 reverse :: PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -reverse (WindowsString s) = WindowsString (BS16.reverse s) -#else -reverse (PosixString s) = PosixString (BS.reverse s) -#endif +reverse = coerce BS.reverse -- | /O(n)/ The 'intercalate' function takes a 'OsString' and a list of -- 'OsString's and concatenates the list after interspersing the first @@ -553,11 +509,7 @@ reverse (PosixString s) = PosixString (BS.reverse s) -- -- @since 1.4.200.0 intercalate :: PLATFORM_STRING -> [PLATFORM_STRING] -> PLATFORM_STRING -#ifdef WINDOWS -intercalate (WindowsString s) xs = WindowsString (BS16.intercalate s (fmap getWindowsString xs)) -#else -intercalate (PosixString s) xs = PosixString (BS.intercalate s (fmap getPosixString xs)) -#endif +intercalate = coerce BS.intercalate -- | 'foldl', applied to a binary operator, a starting value (typically -- the left-identity of the operator), and a OsString, reduces the @@ -566,9 +518,9 @@ intercalate (PosixString s) xs = PosixString (BS.intercalate s (fmap getPosixStr -- @since 1.4.200.0 foldl :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a #ifdef WINDOWS -foldl f a (WindowsString s) = BS16.foldl (\a' c -> f a' (WindowsChar c)) a s +foldl f a (WindowsString s) = BS16.foldl (coerce f) a s #else -foldl f a (PosixString s) = BS.foldl (\a' c -> f a' (PosixChar c)) a s +foldl f a (PosixString s) = BS8.foldl (coerce f) a s #endif -- | 'foldl'' is like 'foldl', but strict in the accumulator. @@ -577,9 +529,9 @@ foldl f a (PosixString s) = BS.foldl (\a' c -> f a' (PosixChar c)) a s foldl' :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a #ifdef WINDOWS -foldl' f a (WindowsString s) = BS16.foldl' (\a' c -> f a' (WindowsChar c)) a s +foldl' f a (WindowsString s) = BS16.foldl' (coerce f) a s #else -foldl' f a (PosixString s) = BS.foldl' (\a' c -> f a' (PosixChar c)) a s +foldl' f a (PosixString s) = BS8.foldl' (coerce f) a s #endif -- | 'foldl1' is a variant of 'foldl' that has no starting value @@ -588,11 +540,7 @@ foldl' f a (PosixString s) = BS.foldl' (\a' c -> f a' (PosixChar c)) a s -- -- @since 1.4.200.0 foldl1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -foldl1 f (WindowsString s) = WindowsChar $ BS16.foldl1 (\a' c -> getWindowsChar $ f (WindowsChar a') (WindowsChar c)) s -#else -foldl1 f (PosixString s) = PosixChar $ BS.foldl1 (\a' c -> getPosixChar $ f (PosixChar a') (PosixChar c)) s -#endif +foldl1 = coerce BS.foldl1 -- | 'foldl1'' is like 'foldl1', but strict in the accumulator. -- An exception will be thrown in the case of an empty OsString. @@ -600,11 +548,7 @@ foldl1 f (PosixString s) = PosixChar $ BS.foldl1 (\a' c -> getPosixChar $ f (Pos -- @since 1.4.200.0 foldl1' :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -foldl1' f (WindowsString s) = WindowsChar $ BS16.foldl1' (\a' c -> getWindowsChar $ f (WindowsChar a') (WindowsChar c)) s -#else -foldl1' f (PosixString s) = PosixChar $ BS.foldl1' (\a' c -> getPosixChar $ f (PosixChar a') (PosixChar c)) s -#endif +foldl1' = coerce BS.foldl1' -- | 'foldr', applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a OsString, @@ -613,9 +557,9 @@ foldl1' f (PosixString s) = PosixChar $ BS.foldl1' (\a' c -> getPosixChar $ f (P -- @since 1.4.200.0 foldr :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a #ifdef WINDOWS -foldr f a (WindowsString s) = BS16.foldr (\c a' -> f (WindowsChar c) a') a s +foldr f a (WindowsString s) = BS16.foldr (coerce f) a s #else -foldr f a (PosixString s) = BS.foldr (\c a' -> f (PosixChar c) a') a s +foldr f a (PosixString s) = BS8.foldr (coerce f) a s #endif -- | 'foldr'' is like 'foldr', but strict in the accumulator. @@ -624,9 +568,9 @@ foldr f a (PosixString s) = BS.foldr (\c a' -> f (PosixChar c) a') a s foldr' :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a #ifdef WINDOWS -foldr' f a (WindowsString s) = BS16.foldr' (\c a' -> f (WindowsChar c) a') a s +foldr' f a (WindowsString s) = BS16.foldr' (coerce f) a s #else -foldr' f a (PosixString s) = BS.foldr' (\c a' -> f (PosixChar c) a') a s +foldr' f a (PosixString s) = BS8.foldr' (coerce f) a s #endif -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, @@ -635,11 +579,7 @@ foldr' f a (PosixString s) = BS.foldr' (\c a' -> f (PosixChar c) a') a s -- -- @since 1.4.200.0 foldr1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -foldr1 f (WindowsString s) = WindowsChar $ BS16.foldr1 (\c a' -> getWindowsChar $ f (WindowsChar c) (WindowsChar a')) s -#else -foldr1 f (PosixString s) = PosixChar $ BS.foldr1 (\c a' -> getPosixChar $ f (PosixChar c) (PosixChar a')) s -#endif +foldr1 = coerce BS.foldr1 -- | 'foldr1'' is a variant of 'foldr1', but is strict in the -- accumulator. @@ -649,33 +589,21 @@ foldr1 f (PosixString s) = PosixChar $ BS.foldr1 (\c a' -> getPosixChar $ f (Pos -- @since 1.4.200.0 foldr1' :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -#ifdef WINDOWS -foldr1' f (WindowsString s) = WindowsChar $ BS16.foldr1' (\c a' -> getWindowsChar $ f (WindowsChar c) (WindowsChar a')) s -#else -foldr1' f (PosixString s) = PosixChar $ BS.foldr1' (\c a' -> getPosixChar $ f (PosixChar c) (PosixChar a')) s -#endif +foldr1' = coerce BS.foldr1' -- | /O(n)/ Applied to a predicate and a 'OsString', 'all' determines -- if all elements of the 'OsString' satisfy the predicate. -- -- @since 1.4.200.0 all :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -all f (WindowsString s) = BS16.all (f . WindowsChar) s -#else -all f (PosixString s) = BS.all (f . PosixChar) s -#endif +all = coerce BS.all -- | /O(n)/ Applied to a predicate and a 'OsString', 'any' determines if -- any element of the 'OsString' satisfies the predicate. -- -- @since 1.4.200.0 any :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -any f (WindowsString s) = BS16.any (f . WindowsChar) s -#else -any f (PosixString s) = BS.any (f . PosixChar) s -#endif +any = coerce BS.any -- /O(n)/ Concatenate a list of OsStrings. -- @@ -690,11 +618,7 @@ concat = mconcat -- -- @since 1.4.200.0 replicate :: Int -> PLATFORM_WORD -> PLATFORM_STRING -#ifdef WINDOWS -replicate i (WindowsChar w) = WindowsString $ BS16.replicate i w -#else -replicate i (PosixChar w) = PosixString $ BS.replicate i w -#endif +replicate = coerce BS.replicate -- | /O(n)/, where /n/ is the length of the result. The 'unfoldr' -- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a @@ -719,7 +643,7 @@ unfoldr :: (a -> Maybe (PLATFORM_WORD, a)) -> a -> PLATFORM_STRING #ifdef WINDOWS unfoldr f a = WindowsString $ BS16.unfoldr (fmap (first getWindowsChar) . f) a #else -unfoldr f a = PosixString $ BS.unfoldr (fmap (first getPosixChar) . f) a +unfoldr f a = PosixString $ BS8.unfoldr (fmap (first getPosixChar) . f) a #endif -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed @@ -736,7 +660,7 @@ unfoldrN :: forall a. Int -> (a -> Maybe (PLATFORM_WORD, a)) -> a -> (PLATFORM_S #ifdef WINDOWS unfoldrN n f a = first WindowsString $ BS16.unfoldrN n (fmap (first getWindowsChar) . f) a #else -unfoldrN n f a = first PosixString $ BS.unfoldrN n (fmap (first getPosixChar) . f) a +unfoldrN n f a = first PosixString $ BS8.unfoldrN n (fmap (first getPosixChar) . f) a #endif -- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix @@ -744,11 +668,7 @@ unfoldrN n f a = first PosixString $ BS.unfoldrN n (fmap (first getPosixChar) . -- -- @since 1.4.200.0 take :: Int -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -take n (WindowsString s) = WindowsString $ BS16.take n s -#else -take n (PosixString s) = PosixString $ BS.take n s -#endif +take = coerce BS.take -- | /O(n)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@. -- Takes @n@ elements from end of bytestring. @@ -762,11 +682,7 @@ take n (PosixString s) = PosixString $ BS.take n s -- -- @since 1.4.200.0 takeEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -takeEnd n (WindowsString s) = WindowsString $ BS16.takeEnd n s -#else -takeEnd n (PosixString s) = PosixString $ BS.takeEnd n s -#endif +takeEnd = coerce BS.takeEnd -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate. @@ -775,11 +691,7 @@ takeEnd n (PosixString s) = PosixString $ BS.takeEnd n s -- -- @since 1.4.200.0 takeWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -takeWhileEnd f (WindowsString s) = WindowsString $ BS16.takeWhileEnd (f . WindowsChar) s -#else -takeWhileEnd f (PosixString s) = PosixString $ BS.takeWhileEnd (f . PosixChar) s -#endif +takeWhileEnd = coerce BS.takeWhileEnd -- | Similar to 'Prelude.takeWhile', -- returns the longest (possibly empty) prefix of elements @@ -787,21 +699,13 @@ takeWhileEnd f (PosixString s) = PosixString $ BS.takeWhileEnd (f . PosixChar) s -- -- @since 1.4.200.0 takeWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -takeWhile f (WindowsString s) = WindowsString $ BS16.takeWhile (f . WindowsChar) s -#else -takeWhile f (PosixString s) = PosixString $ BS.takeWhile (f . PosixChar) s -#endif +takeWhile = coerce BS.takeWhile -- | /O(n)/ 'drop' @n@ @xs@ returns the suffix of @xs@ after the first n elements, or 'empty' if @n > 'length' xs@. -- -- @since 1.4.200.0 drop :: Int -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -drop n (WindowsString s) = WindowsString $ BS16.drop n s -#else -drop n (PosixString s) = PosixString $ BS.drop n s -#endif +drop = coerce BS.drop -- | /O(n)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@. -- Drops @n@ elements from end of bytestring. @@ -815,11 +719,7 @@ drop n (PosixString s) = PosixString $ BS.drop n s -- -- @since 1.4.200.0 dropEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -dropEnd n (WindowsString s) = WindowsString $ BS16.dropEnd n s -#else -dropEnd n (PosixString s) = PosixString $ BS.dropEnd n s -#endif +dropEnd = coerce BS.dropEnd -- | Similar to 'Prelude.dropWhile', -- drops the longest (possibly empty) prefix of elements @@ -827,11 +727,7 @@ dropEnd n (PosixString s) = PosixString $ BS.dropEnd n s -- -- @since 1.4.200.0 dropWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -dropWhile f (WindowsString s) = WindowsString $ BS16.dropWhile (f . WindowsChar) s -#else -dropWhile f (PosixString s) = PosixString $ BS.dropWhile (f . PosixChar) s -#endif +dropWhile = coerce BS.dropWhile -- | Similar to 'Prelude.dropWhileEnd', -- drops the longest (possibly empty) suffix of elements @@ -841,11 +737,7 @@ dropWhile f (PosixString s) = PosixString $ BS.dropWhile (f . PosixChar) s -- -- @since 1.4.200.0 dropWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -dropWhileEnd f (WindowsString s) = WindowsString $ BS16.dropWhileEnd (f . WindowsChar) s -#else -dropWhileEnd f (PosixString s) = PosixString $ BS.dropWhileEnd (f . PosixChar) s -#endif +dropWhileEnd = coerce BS.dropWhileEnd -- | Returns the longest (possibly empty) suffix of elements which __do not__ -- satisfy the predicate and the remainder of the string. @@ -854,11 +746,7 @@ dropWhileEnd f (PosixString s) = PosixString $ BS.dropWhileEnd (f . PosixChar) s -- -- @since 1.4.200.0 breakEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -breakEnd f (WindowsString s) = bimap WindowsString WindowsString $ BS16.breakEnd (f . WindowsChar) s -#else -breakEnd f (PosixString s) = bimap PosixString PosixString $ BS.breakEnd (f . PosixChar) s -#endif +breakEnd = coerce BS.breakEnd -- | Similar to 'Prelude.break', -- returns the longest (possibly empty) prefix of elements which __do not__ @@ -868,11 +756,7 @@ breakEnd f (PosixString s) = bimap PosixString PosixString $ BS.breakEnd (f . Po -- -- @since 1.4.200.0 break :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -break f (WindowsString s) = bimap WindowsString WindowsString $ BS16.break (f . WindowsChar) s -#else -break f (PosixString s) = bimap PosixString PosixString $ BS.break (f . PosixChar) s -#endif +break = coerce BS.break -- | Similar to 'Prelude.span', -- returns the longest (possibly empty) prefix of elements @@ -882,11 +766,7 @@ break f (PosixString s) = bimap PosixString PosixString $ BS.break (f . PosixCha -- -- @since 1.4.200.0 span :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -span f (WindowsString s) = bimap WindowsString WindowsString $ BS16.span (f . WindowsChar) s -#else -span f (PosixString s) = bimap PosixString PosixString $ BS.span (f . PosixChar) s -#endif +span = coerce BS.span -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate and the remainder of the string. @@ -905,21 +785,13 @@ span f (PosixString s) = bimap PosixString PosixString $ BS.span (f . PosixChar) -- -- @since 1.4.200.0 spanEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -spanEnd f (WindowsString s) = bimap WindowsString WindowsString $ BS16.spanEnd (f . WindowsChar) s -#else -spanEnd f (PosixString s) = bimap PosixString PosixString $ BS.spanEnd (f . PosixChar) s -#endif +spanEnd = coerce BS.spanEnd -- | /O(n)/ 'splitAt' @n sbs@ is equivalent to @('take' n sbs, 'drop' n sbs)@. -- -- @since 1.4.200.0 splitAt :: Int -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -splitAt n (WindowsString s) = bimap WindowsString WindowsString $ BS16.splitAt n s -#else -splitAt n (PosixString s) = bimap PosixString PosixString $ BS.splitAt n s -#endif +splitAt = coerce BS.splitAt -- | /O(n)/ Break a 'OsString' into pieces separated by the byte -- argument, consuming the delimiter. I.e. @@ -936,11 +808,7 @@ splitAt n (PosixString s) = bimap PosixString PosixString $ BS.splitAt n s -- -- @since 1.4.200.0 split :: PLATFORM_WORD -> PLATFORM_STRING -> [PLATFORM_STRING] -#ifdef WINDOWS -split (WindowsChar w) (WindowsString s) = WindowsString <$> BS16.split w s -#else -split (PosixChar w) (PosixString s) = PosixString <$> BS.split w s -#endif +split = coerce BS.split -- | /O(n)/ Splits a 'OsString' into components delimited by -- separators, where the predicate returns True for a separator element. @@ -952,11 +820,7 @@ split (PosixChar w) (PosixString s) = PosixString <$> BS.split w s -- -- @since 1.4.200.0 splitWith :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [PLATFORM_STRING] -#ifdef WINDOWS -splitWith f (WindowsString s) = WindowsString <$> BS16.splitWith (f . WindowsChar) s -#else -splitWith f (PosixString s) = PosixString <$> BS.splitWith (f . PosixChar) s -#endif +splitWith = coerce BS.splitWith -- | /O(n)/ The 'stripSuffix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its suffix, and otherwise @@ -964,11 +828,7 @@ splitWith f (PosixString s) = PosixString <$> BS.splitWith (f . PosixChar) s -- -- @since 1.4.200.0 stripSuffix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING -#ifdef WINDOWS -stripSuffix (WindowsString a) (WindowsString b) = WindowsString <$> BS16.stripSuffix a b -#else -stripSuffix (PosixString a) (PosixString b) = PosixString <$> BS.stripSuffix a b -#endif +stripSuffix = coerce BS.stripSuffix -- | /O(n)/ The 'stripPrefix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its prefix, and otherwise @@ -976,32 +836,20 @@ stripSuffix (PosixString a) (PosixString b) = PosixString <$> BS.stripSuffix a b -- -- @since 1.4.200.0 stripPrefix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING -#ifdef WINDOWS -stripPrefix (WindowsString a) (WindowsString b) = WindowsString <$> BS16.stripPrefix a b -#else -stripPrefix (PosixString a) (PosixString b) = PosixString <$> BS.stripPrefix a b -#endif +stripPrefix = coerce BS.stripPrefix -- | Check whether one string is a substring of another. -- -- @since 1.4.200.0 isInfixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -isInfixOf (WindowsString a) (WindowsString b) = BS16.isInfixOf a b -#else -isInfixOf (PosixString a) (PosixString b) = BS.isInfixOf a b -#endif +isInfixOf = coerce BS.isInfixOf -- |/O(n)/ The 'isPrefixOf' function takes two OsStrings and returns 'True' -- -- @since 1.4.200.0 isPrefixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -isPrefixOf (WindowsString a) (WindowsString b) = BS16.isPrefixOf a b -#else -isPrefixOf (PosixString a) (PosixString b) = BS.isPrefixOf a b -#endif +isPrefixOf = coerce BS.isPrefixOf -- | /O(n)/ The 'isSuffixOf' function takes two OsStrings and returns 'True' -- iff the first is a suffix of the second. @@ -1012,11 +860,7 @@ isPrefixOf (PosixString a) (PosixString b) = BS.isPrefixOf a b -- -- @since 1.4.200.0 isSuffixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -isSuffixOf (WindowsString a) (WindowsString b) = BS16.isSuffixOf a b -#else -isSuffixOf (PosixString a) (PosixString b) = BS.isSuffixOf a b -#endif +isSuffixOf = coerce BS.isSuffixOf -- | Break a string on a substring, returning a pair of the part of the @@ -1045,21 +889,13 @@ isSuffixOf (PosixString a) (PosixString b) = BS.isSuffixOf a b -- -- @since 1.4.200.0 breakSubstring :: PLATFORM_STRING -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -breakSubstring (WindowsString a) (WindowsString b) = bimap WindowsString WindowsString $ BS16.breakSubstring a b -#else -breakSubstring (PosixString a) (PosixString b) = bimap PosixString PosixString $ BS.breakSubstring a b -#endif +breakSubstring = coerce BS.breakSubstring -- | /O(n)/ 'elem' is the 'OsString' membership predicate. -- -- @since 1.4.200.0 elem :: PLATFORM_WORD -> PLATFORM_STRING -> Bool -#ifdef WINDOWS -elem (WindowsChar w) (WindowsString s) = BS16.elem w s -#else -elem (PosixChar w) (PosixString s) = BS.elem w s -#endif +elem = coerce BS.elem -- | /O(n)/ The 'find' function takes a predicate and a OsString, -- and returns the first element in matching the predicate, or 'Nothing' @@ -1069,11 +905,7 @@ elem (PosixChar w) (PosixString s) = BS.elem w s -- -- @since 1.4.200.0 find :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe PLATFORM_WORD -#ifdef WINDOWS -find f (WindowsString s) = WindowsChar <$> BS16.find (f . WindowsChar) s -#else -find f (PosixString s) = PosixChar <$> BS.find (f . PosixChar) s -#endif +find = coerce BS.find -- | /O(n)/ 'filter', applied to a predicate and a OsString, -- returns a OsString containing those characters that satisfy the @@ -1081,11 +913,7 @@ find f (PosixString s) = PosixChar <$> BS.find (f . PosixChar) s -- -- @since 1.4.200.0 filter :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -#ifdef WINDOWS -filter f (WindowsString s) = WindowsString $ BS16.filter (f . WindowsChar) s -#else -filter f (PosixString s) = PosixString $ BS.filter (f . PosixChar) s -#endif +filter = coerce BS.filter -- | /O(n)/ The 'partition' function takes a predicate a OsString and returns -- the pair of OsStrings with elements which do and do not satisfy the @@ -1095,21 +923,13 @@ filter f (PosixString s) = PosixString $ BS.filter (f . PosixChar) s -- -- @since 1.4.200.0 partition :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -#ifdef WINDOWS -partition f (WindowsString s) = bimap WindowsString WindowsString $ BS16.partition (f . WindowsChar) s -#else -partition f (PosixString s) = bimap PosixString PosixString $ BS.partition (f . PosixChar) s -#endif +partition = coerce BS.partition -- | /O(1)/ 'OsString' index (subscript) operator, starting from 0. -- -- @since 1.4.200.0 index :: HasCallStack => PLATFORM_STRING -> Int -> PLATFORM_WORD -#ifdef WINDOWS -index (WindowsString s) n = WindowsChar $ BS16.index s n -#else -index (PosixString s) n = PosixChar $ BS.index s n -#endif +index = coerce BS.index -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -1117,11 +937,7 @@ index (PosixString s) n = PosixChar $ BS.index s n -- -- @since 1.4.200.0 indexMaybe :: PLATFORM_STRING -> Int -> Maybe PLATFORM_WORD -#ifdef WINDOWS -indexMaybe (WindowsString s) n = WindowsChar <$> BS16.indexMaybe s n -#else -indexMaybe (PosixString s) n = PosixChar <$> BS.indexMaybe s n -#endif +indexMaybe = coerce BS.indexMaybe -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -1137,32 +953,20 @@ indexMaybe (PosixString s) n = PosixChar <$> BS.indexMaybe s n -- -- @since 1.4.200.0 elemIndex :: PLATFORM_WORD -> PLATFORM_STRING -> Maybe Int -#ifdef WINDOWS -elemIndex (WindowsChar w) (WindowsString s) = BS16.elemIndex w s -#else -elemIndex (PosixChar w) (PosixString s) = BS.elemIndex w s -#endif +elemIndex = coerce BS.elemIndex -- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning -- the indices of all elements equal to the query element, in ascending order. -- -- @since 1.4.200.0 elemIndices :: PLATFORM_WORD -> PLATFORM_STRING -> [Int] -#ifdef WINDOWS -elemIndices (WindowsChar w) (WindowsString s) = BS16.elemIndices w s -#else -elemIndices (PosixChar w) (PosixString s) = BS.elemIndices w s -#endif +elemIndices = coerce BS.elemIndices -- | count returns the number of times its argument appears in the OsString -- -- @since 1.4.200.0 count :: PLATFORM_WORD -> PLATFORM_STRING -> Int -#ifdef WINDOWS -count (WindowsChar w) (WindowsString s) = BS16.count w s -#else -count (PosixChar w) (PosixString s) = BS.count w s -#endif +count = coerce BS.count -- | /O(n)/ The 'findIndex' function takes a predicate and a 'OsString' and -- returns the index of the first element in the OsString @@ -1170,19 +974,11 @@ count (PosixChar w) (PosixString s) = BS.count w s -- -- @since 1.4.200.0 findIndex :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe Int -#ifdef WINDOWS -findIndex f (WindowsString s) = BS16.findIndex (f . WindowsChar) s -#else -findIndex f (PosixString s) = BS.findIndex (f . PosixChar) s -#endif +findIndex = coerce BS.findIndex -- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the -- indices of all elements satisfying the predicate, in ascending order. -- -- @since 1.4.200.0 findIndices :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [Int] -#ifdef WINDOWS -findIndices f (WindowsString s) = BS16.findIndices (f . WindowsChar) s -#else -findIndices f (PosixString s) = BS.findIndices (f . PosixChar) s -#endif +findIndices = coerce BS.findIndices diff --git a/System/OsString/Internal.hs b/System/OsString/Internal.hs index 7023bd10..927c7966 100644 --- a/System/OsString/Internal.hs +++ b/System/OsString/Internal.hs @@ -29,6 +29,7 @@ import qualified System.OsString.Posix as PF #endif import GHC.Stack (HasCallStack) import Data.Bifunctor +import Data.Coerce (coerce) @@ -184,13 +185,13 @@ toChar (OsChar (PosixChar w)) = chr $ fromIntegral w -- -- @since 1.4.200.0 snoc :: OsString -> OsChar -> OsString -snoc (OsString s) (OsChar w) = OsString (PF.snoc s w) +snoc = coerce PF.snoc -- | /O(n)/ 'cons' is analogous to (:) for lists. -- -- @since 1.4.200.0 cons :: OsChar -> OsString -> OsString -cons (OsChar w) (OsString s) = OsString (PF.cons w s) +cons = coerce PF.cons -- | /O(1)/ Extract the last element of a OsString, which must be finite and non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -199,7 +200,7 @@ cons (OsChar w) (OsString s) = OsString (PF.cons w s) -- -- @since 1.4.200.0 last :: HasCallStack => OsString -> OsChar -last (OsString s) = OsChar (PF.last s) +last = coerce PF.last -- | /O(n)/ Extract the elements after the head of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -208,14 +209,14 @@ last (OsString s) = OsChar (PF.last s) -- -- @since 1.4.200.0 tail :: HasCallStack => OsString -> OsString -tail (OsString s) = OsString (PF.tail s) +tail = coerce PF.tail -- | /O(n)/ Extract the 'head' and 'tail' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 uncons :: OsString -> Maybe (OsChar, OsString) -uncons (OsString s) = bimap OsChar OsString <$> PF.uncons s +uncons = coerce PF.uncons -- | /O(1)/ Extract the first element of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -224,7 +225,7 @@ uncons (OsString s) = bimap OsChar OsString <$> PF.uncons s -- -- @since 1.4.200.0 head :: HasCallStack => OsString -> OsChar -head (OsString s) = OsChar (PF.head s) +head = coerce PF.head -- | /O(n)/ Return all the elements of a 'OsString' except the last one. -- An exception will be thrown in the case of an empty OsString. @@ -233,39 +234,39 @@ head (OsString s) = OsChar (PF.head s) -- -- @since 1.4.200.0 init :: HasCallStack => OsString -> OsString -init (OsString s) = OsString (PF.init s) +init = coerce PF.init -- | /O(n)/ Extract the 'init' and 'last' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 unsnoc :: OsString -> Maybe (OsString, OsChar) -unsnoc (OsString s) = bimap OsString OsChar <$> PF.unsnoc s +unsnoc = coerce PF.unsnoc -- | /O(1)/ Test whether a 'OsString' is empty. -- -- @since 1.4.200.0 null :: OsString -> Bool -null (OsString s) = PF.null s +null = coerce PF.null -- | /O(1)/ The length of a 'OsString'. -- -- @since 1.4.200.0 length :: OsString -> Int -length (OsString s) = PF.length s +length = coerce PF.length -- | /O(n)/ 'map' @f xs@ is the OsString obtained by applying @f@ to each -- element of @xs@. -- -- @since 1.4.200.0 map :: (OsChar -> OsChar) -> OsString -> OsString -map f (OsString s) = OsString (PF.map (getOsChar . f . OsChar) s) +map = coerce PF.map -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. -- -- @since 1.4.200.0 reverse :: OsString -> OsString -reverse (OsString s) = OsString (PF.reverse s) +reverse = coerce PF.reverse -- | /O(n)/ The 'intercalate' function takes a 'OsString' and a list of -- 'OsString's and concatenates the list after interspersing the first @@ -273,7 +274,7 @@ reverse (OsString s) = OsString (PF.reverse s) -- -- @since 1.4.200.0 intercalate :: OsString -> [OsString] -> OsString -intercalate (OsString s) xs = OsString (PF.intercalate s (fmap getOsString xs)) +intercalate = coerce PF.intercalate -- | 'foldl', applied to a binary operator, a starting value (typically -- the left-identity of the operator), and a OsString, reduces the @@ -281,13 +282,13 @@ intercalate (OsString s) xs = OsString (PF.intercalate s (fmap getOsString xs)) -- -- @since 1.4.200.0 foldl :: (a -> OsChar -> a) -> a -> OsString -> a -foldl f a (OsString s) = PF.foldl (\a' c -> f a' (OsChar c)) a s +foldl f a (OsString s) = PF.foldl (coerce f) a s -- | 'foldl'' is like 'foldl', but strict in the accumulator. -- -- @since 1.4.200.0 foldl' :: (a -> OsChar -> a) -> a -> OsString -> a -foldl' f a (OsString s) = PF.foldl' (\a' c -> f a' (OsChar c)) a s +foldl' f a (OsString s) = PF.foldl' (coerce f) a s -- | 'foldl1' is a variant of 'foldl' that has no starting value -- argument, and thus must be applied to non-empty 'OsString's. @@ -295,14 +296,14 @@ foldl' f a (OsString s) = PF.foldl' (\a' c -> f a' (OsChar c)) a s -- -- @since 1.4.200.0 foldl1 :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar -foldl1 f (OsString s) = OsChar $ PF.foldl1 (\a' c -> getOsChar $ f (OsChar a') (OsChar c)) s +foldl1 = coerce PF.foldl1 -- | 'foldl1'' is like 'foldl1', but strict in the accumulator. -- An exception will be thrown in the case of an empty OsString. -- -- @since 1.4.200.0 foldl1' :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar -foldl1' f (OsString s) = OsChar $ PF.foldl1' (\a' c -> getOsChar $ f (OsChar a') (OsChar c)) s +foldl1' = coerce PF.foldl1' -- | 'foldr', applied to a binary operator, a starting value @@ -311,13 +312,13 @@ foldl1' f (OsString s) = OsChar $ PF.foldl1' (\a' c -> getOsChar $ f (OsChar a') -- -- @since 1.4.200.0 foldr :: (OsChar -> a -> a) -> a -> OsString -> a -foldr f a (OsString s) = PF.foldr (\c a' -> f (OsChar c) a') a s +foldr f a (OsString s) = PF.foldr (coerce f) a s -- | 'foldr'' is like 'foldr', but strict in the accumulator. -- -- @since 1.4.200.0 foldr' :: (OsChar -> a -> a) -> a -> OsString -> a -foldr' f a (OsString s) = PF.foldr' (\c a' -> f (OsChar c) a') a s +foldr' f a (OsString s) = PF.foldr' (coerce f) a s -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, -- and thus must be applied to non-empty 'OsString's @@ -325,28 +326,28 @@ foldr' f a (OsString s) = PF.foldr' (\c a' -> f (OsChar c) a') a s -- -- @since 1.4.200.0 foldr1 :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar -foldr1 f (OsString s) = OsChar $ PF.foldr1 (\c a' -> getOsChar $ f (OsChar c) (OsChar a')) s +foldr1 = coerce PF.foldr1 -- | 'foldr1'' is a variant of 'foldr1', but is strict in the -- accumulator. -- -- @since 1.4.200.0 foldr1' :: (OsChar -> OsChar -> OsChar) -> OsString -> OsChar -foldr1' f (OsString s) = OsChar $ PF.foldr1' (\c a' -> getOsChar $ f (OsChar c) (OsChar a')) s +foldr1' = coerce PF.foldr1' -- | /O(n)/ Applied to a predicate and a 'OsString', 'all' determines -- if all elements of the 'OsString' satisfy the predicate. -- -- @since 1.4.200.0 all :: (OsChar -> Bool) -> OsString -> Bool -all f (OsString s) = PF.all (f . OsChar) s +all = coerce PF.all -- | /O(n)/ Applied to a predicate and a 'OsString', 'any' determines if -- any element of the 'OsString' satisfies the predicate. -- -- @since 1.4.200.0 any :: (OsChar -> Bool) -> OsString -> Bool -any f (OsString s) = PF.any (f . OsChar) s +any = coerce PF.any -- /O(n)/ Concatenate a list of OsStrings. -- @@ -361,7 +362,7 @@ concat = mconcat -- -- @since 1.4.200.0 replicate :: Int -> OsChar -> OsString -replicate i (OsChar w) = OsString $ PF.replicate i w +replicate = coerce PF.replicate -- | /O(n)/, where /n/ is the length of the result. The 'unfoldr' -- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a @@ -403,7 +404,7 @@ unfoldrN n f a = first OsString $ PF.unfoldrN n (fmap (first getOsChar) . f) a -- -- @since 1.4.200.0 take :: Int -> OsString -> OsString -take n (OsString s) = OsString $ PF.take n s +take = coerce PF.take -- | /O(n)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@. -- Takes @n@ elements from end of bytestring. @@ -417,7 +418,7 @@ take n (OsString s) = OsString $ PF.take n s -- -- @since 1.4.200.0 takeEnd :: Int -> OsString -> OsString -takeEnd n (OsString s) = OsString $ PF.takeEnd n s +takeEnd = coerce PF.takeEnd -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate. @@ -426,7 +427,7 @@ takeEnd n (OsString s) = OsString $ PF.takeEnd n s -- -- @since 1.4.200.0 takeWhileEnd :: (OsChar -> Bool) -> OsString -> OsString -takeWhileEnd f (OsString s) = OsString $ PF.takeWhileEnd (f . OsChar) s +takeWhileEnd = coerce PF.takeWhileEnd -- | Similar to 'Prelude.takeWhile', -- returns the longest (possibly empty) prefix of elements @@ -434,13 +435,13 @@ takeWhileEnd f (OsString s) = OsString $ PF.takeWhileEnd (f . OsChar) s -- -- @since 1.4.200.0 takeWhile :: (OsChar -> Bool) -> OsString -> OsString -takeWhile f (OsString s) = OsString $ PF.takeWhile (f . OsChar) s +takeWhile = coerce PF.takeWhile -- | /O(n)/ 'drop' @n@ @xs@ returns the suffix of @xs@ after the first n elements, or 'empty' if @n > 'length' xs@. -- -- @since 1.4.200.0 drop :: Int -> OsString -> OsString -drop n (OsString s) = OsString $ PF.drop n s +drop = coerce PF.drop -- | /O(n)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@. -- Drops @n@ elements from end of bytestring. @@ -454,7 +455,7 @@ drop n (OsString s) = OsString $ PF.drop n s -- -- @since 1.4.200.0 dropEnd :: Int -> OsString -> OsString -dropEnd n (OsString s) = OsString $ PF.dropEnd n s +dropEnd = coerce PF.dropEnd -- | Similar to 'Prelude.dropWhile', -- drops the longest (possibly empty) prefix of elements @@ -462,7 +463,7 @@ dropEnd n (OsString s) = OsString $ PF.dropEnd n s -- -- @since 1.4.200.0 dropWhile :: (OsChar -> Bool) -> OsString -> OsString -dropWhile f (OsString s) = OsString $ PF.dropWhile (f . OsChar) s +dropWhile = coerce PF.dropWhile -- | Similar to 'Prelude.dropWhileEnd', -- drops the longest (possibly empty) suffix of elements @@ -472,7 +473,7 @@ dropWhile f (OsString s) = OsString $ PF.dropWhile (f . OsChar) s -- -- @since 1.4.200.0 dropWhileEnd :: (OsChar -> Bool) -> OsString -> OsString -dropWhileEnd f (OsString s) = OsString $ PF.dropWhileEnd (f . OsChar) s +dropWhileEnd = coerce PF.dropWhileEnd -- | Returns the longest (possibly empty) suffix of elements which __do not__ -- satisfy the predicate and the remainder of the string. @@ -481,7 +482,7 @@ dropWhileEnd f (OsString s) = OsString $ PF.dropWhileEnd (f . OsChar) s -- -- @since 1.4.200.0 breakEnd :: (OsChar -> Bool) -> OsString -> (OsString, OsString) -breakEnd f (OsString s) = bimap OsString OsString $ PF.breakEnd (f . OsChar) s +breakEnd = coerce PF.breakEnd -- | Similar to 'Prelude.break', -- returns the longest (possibly empty) prefix of elements which __do not__ @@ -491,7 +492,7 @@ breakEnd f (OsString s) = bimap OsString OsString $ PF.breakEnd (f . OsChar) s -- -- @since 1.4.200.0 break :: (OsChar -> Bool) -> OsString -> (OsString, OsString) -break f (OsString s) = bimap OsString OsString $ PF.break (f . OsChar) s +break = coerce PF.break -- | Similar to 'Prelude.span', -- returns the longest (possibly empty) prefix of elements @@ -501,7 +502,7 @@ break f (OsString s) = bimap OsString OsString $ PF.break (f . OsChar) s -- -- @since 1.4.200.0 span :: (OsChar -> Bool) -> OsString -> (OsString, OsString) -span f (OsString s) = bimap OsString OsString $ PF.span (f . OsChar) s +span = coerce PF.span -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate and the remainder of the string. @@ -520,13 +521,13 @@ span f (OsString s) = bimap OsString OsString $ PF.span (f . OsChar) s -- -- @since 1.4.200.0 spanEnd :: (OsChar -> Bool) -> OsString -> (OsString, OsString) -spanEnd f (OsString s) = bimap OsString OsString $ PF.spanEnd (f . OsChar) s +spanEnd = coerce PF.spanEnd -- | /O(n)/ 'splitAt' @n sbs@ is equivalent to @('take' n sbs, 'drop' n sbs)@. -- -- @since 1.4.200.0 splitAt :: Int -> OsString -> (OsString, OsString) -splitAt n (OsString s) = bimap OsString OsString $ PF.splitAt n s +splitAt = coerce PF.splitAt -- | /O(n)/ Break a 'OsString' into pieces separated by the byte -- argument, consuming the delimiter. I.e. @@ -543,7 +544,7 @@ splitAt n (OsString s) = bimap OsString OsString $ PF.splitAt n s -- -- @since 1.4.200.0 split :: OsChar -> OsString -> [OsString] -split (OsChar w) (OsString s) = OsString <$> PF.split w s +split = coerce PF.split -- | /O(n)/ Splits a 'OsString' into components delimited by -- separators, where the predicate returns True for a separator element. @@ -555,7 +556,7 @@ split (OsChar w) (OsString s) = OsString <$> PF.split w s -- -- @since 1.4.200.0 splitWith :: (OsChar -> Bool) -> OsString -> [OsString] -splitWith f (OsString s) = OsString <$> PF.splitWith (f . OsChar) s +splitWith = coerce PF.splitWith -- | /O(n)/ The 'stripSuffix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its suffix, and otherwise @@ -563,7 +564,7 @@ splitWith f (OsString s) = OsString <$> PF.splitWith (f . OsChar) s -- -- @since 1.4.200.0 stripSuffix :: OsString -> OsString -> Maybe OsString -stripSuffix (OsString a) (OsString b) = OsString <$> PF.stripSuffix a b +stripSuffix = coerce PF.stripSuffix -- | /O(n)/ The 'stripPrefix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its prefix, and otherwise @@ -571,20 +572,20 @@ stripSuffix (OsString a) (OsString b) = OsString <$> PF.stripSuffix a b -- -- @since 1.4.200.0 stripPrefix :: OsString -> OsString -> Maybe OsString -stripPrefix (OsString a) (OsString b) = OsString <$> PF.stripPrefix a b +stripPrefix = coerce PF.stripPrefix -- | Check whether one string is a substring of another. -- -- @since 1.4.200.0 isInfixOf :: OsString -> OsString -> Bool -isInfixOf (OsString a) (OsString b) = PF.isInfixOf a b +isInfixOf = coerce PF.isInfixOf -- |/O(n)/ The 'isPrefixOf' function takes two OsStrings and returns 'True' -- -- @since 1.4.200.0 isPrefixOf :: OsString -> OsString -> Bool -isPrefixOf (OsString a) (OsString b) = PF.isPrefixOf a b +isPrefixOf = coerce PF.isPrefixOf -- | /O(n)/ The 'isSuffixOf' function takes two OsStrings and returns 'True' -- iff the first is a suffix of the second. @@ -595,7 +596,7 @@ isPrefixOf (OsString a) (OsString b) = PF.isPrefixOf a b -- -- @since 1.4.200.0 isSuffixOf :: OsString -> OsString -> Bool -isSuffixOf (OsString a) (OsString b) = PF.isSuffixOf a b +isSuffixOf = coerce PF.isSuffixOf -- | Break a string on a substring, returning a pair of the part of the -- string prior to the match, and the rest of the string. @@ -623,13 +624,13 @@ isSuffixOf (OsString a) (OsString b) = PF.isSuffixOf a b -- -- @since 1.4.200.0 breakSubstring :: OsString -> OsString -> (OsString, OsString) -breakSubstring (OsString a) (OsString b) = bimap OsString OsString $ PF.breakSubstring a b +breakSubstring = coerce PF.breakSubstring -- | /O(n)/ 'elem' is the 'OsString' membership predicate. -- -- @since 1.4.200.0 elem :: OsChar -> OsString -> Bool -elem (OsChar w) (OsString s) = PF.elem w s +elem = coerce PF.elem -- | /O(n)/ The 'find' function takes a predicate and a OsString, -- and returns the first element in matching the predicate, or 'Nothing' @@ -639,7 +640,7 @@ elem (OsChar w) (OsString s) = PF.elem w s -- -- @since 1.4.200.0 find :: (OsChar -> Bool) -> OsString -> Maybe OsChar -find f (OsString s) = OsChar <$> PF.find (f . OsChar) s +find = coerce PF.find -- | /O(n)/ 'filter', applied to a predicate and a OsString, -- returns a OsString containing those characters that satisfy the @@ -647,7 +648,7 @@ find f (OsString s) = OsChar <$> PF.find (f . OsChar) s -- -- @since 1.4.200.0 filter :: (OsChar -> Bool) -> OsString -> OsString -filter f (OsString s) = OsString $ PF.filter (f . OsChar) s +filter = coerce PF.filter -- | /O(n)/ The 'partition' function takes a predicate a OsString and returns -- the pair of OsStrings with elements which do and do not satisfy the @@ -657,13 +658,13 @@ filter f (OsString s) = OsString $ PF.filter (f . OsChar) s -- -- @since 1.4.200.0 partition :: (OsChar -> Bool) -> OsString -> (OsString, OsString) -partition f (OsString s) = bimap OsString OsString $ PF.partition (f . OsChar) s +partition = coerce PF.partition -- | /O(1)/ 'OsString' index (subscript) operator, starting from 0. -- -- @since 1.4.200.0 index :: HasCallStack => OsString -> Int -> OsChar -index (OsString s) n = OsChar $ PF.index s n +index = coerce PF.index -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -671,7 +672,7 @@ index (OsString s) n = OsChar $ PF.index s n -- -- @since 1.4.200.0 indexMaybe :: OsString -> Int -> Maybe OsChar -indexMaybe (OsString s) n = OsChar <$> PF.indexMaybe s n +indexMaybe = coerce PF.indexMaybe -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -687,20 +688,20 @@ indexMaybe (OsString s) n = OsChar <$> PF.indexMaybe s n -- -- @since 1.4.200.0 elemIndex :: OsChar -> OsString -> Maybe Int -elemIndex (OsChar w) (OsString s) = PF.elemIndex w s +elemIndex = coerce PF.elemIndex -- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning -- the indices of all elements equal to the query element, in ascending order. -- -- @since 1.4.200.0 elemIndices :: OsChar -> OsString -> [Int] -elemIndices (OsChar w) (OsString s) = PF.elemIndices w s +elemIndices = coerce PF.elemIndices -- | count returns the number of times its argument appears in the OsString -- -- @since 1.4.200.0 count :: OsChar -> OsString -> Int -count (OsChar w) (OsString s) = PF.count w s +count = coerce PF.count -- | /O(n)/ The 'findIndex' function takes a predicate and a 'OsString' and -- returns the index of the first element in the OsString @@ -708,12 +709,12 @@ count (OsChar w) (OsString s) = PF.count w s -- -- @since 1.4.200.0 findIndex :: (OsChar -> Bool) -> OsString -> Maybe Int -findIndex f (OsString s) = PF.findIndex (f . OsChar) s +findIndex = coerce PF.findIndex -- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the -- indices of all elements satisfying the predicate, in ascending order. -- -- @since 1.4.200.0 findIndices :: (OsChar -> Bool) -> OsString -> [Int] -findIndices f (OsString s) = PF.findIndices (f . OsChar) s +findIndices = coerce PF.findIndices From 80a64cbabc8e8938004f3cf7ea6c117242d53dcc Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Tue, 17 Oct 2023 00:34:17 +0800 Subject: [PATCH 3/5] Improve Prelude import --- System/OsString.hs | 2 +- System/OsString/Common.hs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/System/OsString.hs b/System/OsString.hs index 8de8e6fc..14b294c9 100644 --- a/System/OsString.hs +++ b/System/OsString.hs @@ -203,4 +203,4 @@ import System.OsString.Internal ) import System.OsString.Internal.Types ( OsString, OsChar ) -import Prelude hiding (last, tail, head, init, null, length, map, reverse, foldl, foldr, foldl1, foldr1, all, any, concat, replicate, take, takeWhile, drop, dropWhile, break, span, splitAt, elem, filter) +import Prelude () diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs index 46afc6a3..f4242f9d 100644 --- a/System/OsString/Common.hs +++ b/System/OsString/Common.hs @@ -174,7 +174,7 @@ import GHC.IO.Encoding.UTF8 ( mkUTF8 ) import qualified System.OsPath.Data.ByteString.Short as BS #endif import GHC.Stack (HasCallStack) -import Prelude hiding (last, tail, head, init, null, length, map, reverse, foldl, foldr, foldl1, foldr1, all, any, concat, replicate, take, takeWhile, drop, dropWhile, break, span, splitAt, elem, filter) +import Prelude (Bool, Int, Maybe(..), IO, String, Either(..), fmap, ($), (.), mconcat, fromEnum, fromInteger, mempty, fromIntegral, fail, (<$>), show, either, pure, const, flip) import Data.Bifunctor ( bimap ) import qualified System.OsPath.Data.ByteString.Short.Word16 as BS16 import qualified System.OsPath.Data.ByteString.Short as BS8 From 82a4812fcd1fc154641dc291703edfe9adf2f99c Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Tue, 17 Oct 2023 00:40:26 +0800 Subject: [PATCH 4/5] More coercing --- System/OsString/Common.hs | 65 +++++++++---------------------------- System/OsString/Internal.hs | 8 ++--- 2 files changed, 19 insertions(+), 54 deletions(-) diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs index f4242f9d..b979bf41 100644 --- a/System/OsString/Common.hs +++ b/System/OsString/Common.hs @@ -2,6 +2,7 @@ {-# LANGUAGE TypeApplications #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} {-# OPTIONS_GHC -Wno-unused-imports #-} -- This template expects CPP definitions for: @@ -367,11 +368,7 @@ pstr = -- | Unpack a platform string to a list of platform words. unpack :: PLATFORM_STRING -> [PLATFORM_WORD] -#ifdef WINDOWS -unpack (WindowsString ba) = WindowsChar <$> BS16.unpack ba -#else -unpack (PosixString ba) = PosixChar <$> BS.unpack ba -#endif +unpack = coerce BS.unpack -- | Pack a list of platform words to a platform string. @@ -380,18 +377,10 @@ unpack (PosixString ba) = PosixChar <$> BS.unpack ba -- convert from @[Char]@ to platform string is probably not what -- you want, because it will truncate unicode code points. pack :: [PLATFORM_WORD] -> PLATFORM_STRING -#ifdef WINDOWS -pack = WindowsString . BS16.pack . fmap (\(WindowsChar w) -> w) -#else -pack = PosixString . BS.pack . fmap (\(PosixChar w) -> w) -#endif +pack = coerce BS.pack singleton :: PLATFORM_WORD -> PLATFORM_STRING -#ifdef WINDOWS -singleton = WindowsString . BS16.singleton . getWindowsChar -#else -singleton = PosixString . BS.singleton . getPosixChar -#endif +singleton = coerce BS.singleton empty :: PLATFORM_STRING empty = mempty @@ -516,23 +505,15 @@ intercalate = coerce BS.intercalate -- OsString using the binary operator, from left to right. -- -- @since 1.4.200.0 -foldl :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a -#ifdef WINDOWS -foldl f a (WindowsString s) = BS16.foldl (coerce f) a s -#else -foldl f a (PosixString s) = BS8.foldl (coerce f) a s -#endif +foldl :: forall a. (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a +foldl = coerce (BS.foldl @a) -- | 'foldl'' is like 'foldl', but strict in the accumulator. -- -- @since 1.4.200.0 foldl' - :: (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a -#ifdef WINDOWS -foldl' f a (WindowsString s) = BS16.foldl' (coerce f) a s -#else -foldl' f a (PosixString s) = BS8.foldl' (coerce f) a s -#endif + :: forall a. (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a +foldl' = coerce (BS.foldl' @a) -- | 'foldl1' is a variant of 'foldl' that has no starting value -- argument, and thus must be applied to non-empty 'OsString's. @@ -555,23 +536,15 @@ foldl1' = coerce BS.foldl1' -- reduces the OsString using the binary operator, from right to left. -- -- @since 1.4.200.0 -foldr :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a -#ifdef WINDOWS -foldr f a (WindowsString s) = BS16.foldr (coerce f) a s -#else -foldr f a (PosixString s) = BS8.foldr (coerce f) a s -#endif +foldr :: forall a. (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a +foldr = coerce (BS.foldr @a) -- | 'foldr'' is like 'foldr', but strict in the accumulator. -- -- @since 1.4.200.0 foldr' - :: (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a -#ifdef WINDOWS -foldr' f a (WindowsString s) = BS16.foldr' (coerce f) a s -#else -foldr' f a (PosixString s) = BS8.foldr' (coerce f) a s -#endif + :: forall a. (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a +foldr' = coerce (BS.foldr' @a) -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, -- and thus must be applied to non-empty 'OsString's @@ -639,12 +612,8 @@ replicate = coerce BS.replicate -- > == pack [0, 1, 2, 3, 4, 5] -- -- @since 1.4.200.0 -unfoldr :: (a -> Maybe (PLATFORM_WORD, a)) -> a -> PLATFORM_STRING -#ifdef WINDOWS -unfoldr f a = WindowsString $ BS16.unfoldr (fmap (first getWindowsChar) . f) a -#else -unfoldr f a = PosixString $ BS8.unfoldr (fmap (first getPosixChar) . f) a -#endif +unfoldr :: forall a. (a -> Maybe (PLATFORM_WORD, a)) -> a -> PLATFORM_STRING +unfoldr = coerce (BS.unfoldr @a) -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed -- value. However, the length of the result is limited by the first @@ -657,11 +626,7 @@ unfoldr f a = PosixString $ BS8.unfoldr (fmap (first getPosixChar) . f) a -- -- @since 1.4.200.0 unfoldrN :: forall a. Int -> (a -> Maybe (PLATFORM_WORD, a)) -> a -> (PLATFORM_STRING, Maybe a) -#ifdef WINDOWS -unfoldrN n f a = first WindowsString $ BS16.unfoldrN n (fmap (first getWindowsChar) . f) a -#else -unfoldrN n f a = first PosixString $ BS8.unfoldrN n (fmap (first getPosixChar) . f) a -#endif +unfoldrN = coerce (BS.unfoldrN @a) -- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix -- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@. diff --git a/System/OsString/Internal.hs b/System/OsString/Internal.hs index 927c7966..67319b40 100644 --- a/System/OsString/Internal.hs +++ b/System/OsString/Internal.hs @@ -151,7 +151,7 @@ osstr = -- | Unpack an 'OsString' to a list of 'OsChar'. unpack :: OsString -> [OsChar] -unpack (OsString x) = OsChar <$> PF.unpack x +unpack = coerce PF.unpack -- | Pack a list of 'OsChar' to an 'OsString' @@ -160,18 +160,18 @@ unpack (OsString x) = OsChar <$> PF.unpack x -- convert from @[Char]@ to 'OsString' is probably not what -- you want, because it will truncate unicode code points. pack :: [OsChar] -> OsString -pack = OsString . PF.pack . fmap (\(OsChar x) -> x) +pack = coerce PF.pack empty :: OsString empty = mempty singleton :: OsChar -> OsString -singleton = OsString . PF.singleton . getOsChar +singleton = coerce PF.singleton -- | Truncates on unix to 1 and on Windows to 2 octets. unsafeFromChar :: Char -> OsChar -unsafeFromChar = OsChar . PF.unsafeFromChar +unsafeFromChar = coerce PF.unsafeFromChar -- | Converts back to a unicode codepoint (total). toChar :: OsChar -> Char From 97fc2e37f474d460f235190eb2921494da92ac31 Mon Sep 17 00:00:00 2001 From: Julian Ospald Date: Thu, 19 Oct 2023 17:04:37 +0800 Subject: [PATCH 5/5] Smaller improvements --- System/OsString/Common.hs | 134 ++++++++++++++++++------------------ System/OsString/Internal.hs | 25 +++---- 2 files changed, 79 insertions(+), 80 deletions(-) diff --git a/System/OsString/Common.hs b/System/OsString/Common.hs index b979bf41..ed9e45d5 100644 --- a/System/OsString/Common.hs +++ b/System/OsString/Common.hs @@ -166,13 +166,13 @@ import System.OsPath.Encoding import System.IO ( TextEncoding, utf16le ) import GHC.IO.Encoding.UTF16 ( mkUTF16le ) -import qualified System.OsPath.Data.ByteString.Short.Word16 as BS +import qualified System.OsPath.Data.ByteString.Short.Word16 as BSP #else import System.OsPath.Encoding import System.IO ( TextEncoding, utf8 ) import GHC.IO.Encoding.UTF8 ( mkUTF8 ) -import qualified System.OsPath.Data.ByteString.Short as BS +import qualified System.OsPath.Data.ByteString.Short as BSP #endif import GHC.Stack (HasCallStack) import Prelude (Bool, Int, Maybe(..), IO, String, Either(..), fmap, ($), (.), mconcat, fromEnum, fromInteger, mempty, fromIntegral, fail, (<$>), show, either, pure, const, flip) @@ -211,7 +211,7 @@ encodeWith enc str = unsafePerformIO $ do r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> WindowsString <$> BS8.packCStringLen cstr evaluate $ force $ first (flip EncodingError Nothing . displayException) r #else - r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> PosixString <$> BS.packCStringLen cstr + r <- try @SomeException $ GHC.withCStringLen enc str $ \cstr -> PosixString <$> BSP.packCStringLen cstr evaluate $ force $ first (flip EncodingError Nothing . displayException) r #endif @@ -278,7 +278,7 @@ decodeWith :: TextEncoding -> PLATFORM_STRING -> Either EncodingException String decodeWith unixEnc (PosixString ba) = unsafePerformIO $ do - r <- try @SomeException $ BS.useAsCStringLen ba $ \fp -> GHC.peekCStringLen unixEnc fp + r <- try @SomeException $ BSP.useAsCStringLen ba $ \fp -> GHC.peekCStringLen unixEnc fp evaluate $ force $ first (flip EncodingError Nothing . displayException) r #endif @@ -327,7 +327,7 @@ fromBytes bs = let ws = WindowsString . BS16.toShort $ bs in either throwM (const . pure $ ws) $ decodeWith ucs2le ws #else -fromBytes = pure . PosixString . BS.toShort +fromBytes = pure . PosixString . BSP.toShort #endif @@ -368,7 +368,7 @@ pstr = -- | Unpack a platform string to a list of platform words. unpack :: PLATFORM_STRING -> [PLATFORM_WORD] -unpack = coerce BS.unpack +unpack = coerce BSP.unpack -- | Pack a list of platform words to a platform string. @@ -377,10 +377,10 @@ unpack = coerce BS.unpack -- convert from @[Char]@ to platform string is probably not what -- you want, because it will truncate unicode code points. pack :: [PLATFORM_WORD] -> PLATFORM_STRING -pack = coerce BS.pack +pack = coerce BSP.pack singleton :: PLATFORM_WORD -> PLATFORM_STRING -singleton = coerce BS.singleton +singleton = coerce BSP.singleton empty :: PLATFORM_STRING empty = mempty @@ -408,13 +408,13 @@ toChar (PosixChar w) = chr $ fromIntegral w -- -- @since 1.4.200.0 snoc :: PLATFORM_STRING -> PLATFORM_WORD -> PLATFORM_STRING -snoc = coerce BS.snoc +snoc = coerce BSP.snoc -- | /O(n)/ 'cons' is analogous to (:) for lists. -- -- @since 1.4.200.0 cons :: PLATFORM_WORD -> PLATFORM_STRING -> PLATFORM_STRING -cons = coerce BS.cons +cons = coerce BSP.cons -- | /O(1)/ Extract the last element of a OsString, which must be finite and non-empty. @@ -424,7 +424,7 @@ cons = coerce BS.cons -- -- @since 1.4.200.0 last :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD -last = coerce BS.last +last = coerce BSP.last -- | /O(n)/ Extract the elements after the head of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -433,14 +433,14 @@ last = coerce BS.last -- -- @since 1.4.200.0 tail :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING -tail = coerce BS.tail +tail = coerce BSP.tail -- | /O(n)/ Extract the 'head' and 'tail' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 uncons :: PLATFORM_STRING -> Maybe (PLATFORM_WORD, PLATFORM_STRING) -uncons = coerce BS.uncons +uncons = coerce BSP.uncons -- | /O(1)/ Extract the first element of a OsString, which must be non-empty. -- An exception will be thrown in the case of an empty OsString. @@ -449,7 +449,7 @@ uncons = coerce BS.uncons -- -- @since 1.4.200.0 head :: HasCallStack => PLATFORM_STRING -> PLATFORM_WORD -head = coerce BS.head +head = coerce BSP.head -- | /O(n)/ Return all the elements of a 'OsString' except the last one. -- An exception will be thrown in the case of an empty OsString. @@ -458,39 +458,39 @@ head = coerce BS.head -- -- @since 1.4.200.0 init :: HasCallStack => PLATFORM_STRING -> PLATFORM_STRING -init = coerce BS.init +init = coerce BSP.init -- | /O(n)/ Extract the 'init' and 'last' of a OsString, returning 'Nothing' -- if it is empty. -- -- @since 1.4.200.0 unsnoc :: PLATFORM_STRING -> Maybe (PLATFORM_STRING, PLATFORM_WORD) -unsnoc = coerce BS.unsnoc +unsnoc = coerce BSP.unsnoc -- | /O(1)/. The empty 'OsString'. -- -- @since 1.4.200.0 null :: PLATFORM_STRING -> Bool -null = coerce BS.null +null = coerce BSP.null -- | /O(1)/ The length of a 'OsString'. -- -- @since 1.4.200.0 length :: PLATFORM_STRING -> Int -length = coerce BS.length +length = coerce BSP.length -- | /O(n)/ 'map' @f xs@ is the OsString obtained by applying @f@ to each -- element of @xs@. -- -- @since 1.4.200.0 map :: (PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_STRING -map = coerce BS.map +map = coerce BSP.map -- | /O(n)/ 'reverse' @xs@ efficiently returns the elements of @xs@ in reverse order. -- -- @since 1.4.200.0 reverse :: PLATFORM_STRING -> PLATFORM_STRING -reverse = coerce BS.reverse +reverse = coerce BSP.reverse -- | /O(n)/ The 'intercalate' function takes a 'OsString' and a list of -- 'OsString's and concatenates the list after interspersing the first @@ -498,7 +498,7 @@ reverse = coerce BS.reverse -- -- @since 1.4.200.0 intercalate :: PLATFORM_STRING -> [PLATFORM_STRING] -> PLATFORM_STRING -intercalate = coerce BS.intercalate +intercalate = coerce BSP.intercalate -- | 'foldl', applied to a binary operator, a starting value (typically -- the left-identity of the operator), and a OsString, reduces the @@ -506,14 +506,14 @@ intercalate = coerce BS.intercalate -- -- @since 1.4.200.0 foldl :: forall a. (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a -foldl = coerce (BS.foldl @a) +foldl = coerce (BSP.foldl @a) -- | 'foldl'' is like 'foldl', but strict in the accumulator. -- -- @since 1.4.200.0 foldl' :: forall a. (a -> PLATFORM_WORD -> a) -> a -> PLATFORM_STRING -> a -foldl' = coerce (BS.foldl' @a) +foldl' = coerce (BSP.foldl' @a) -- | 'foldl1' is a variant of 'foldl' that has no starting value -- argument, and thus must be applied to non-empty 'OsString's. @@ -521,7 +521,7 @@ foldl' = coerce (BS.foldl' @a) -- -- @since 1.4.200.0 foldl1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -foldl1 = coerce BS.foldl1 +foldl1 = coerce BSP.foldl1 -- | 'foldl1'' is like 'foldl1', but strict in the accumulator. -- An exception will be thrown in the case of an empty OsString. @@ -529,7 +529,7 @@ foldl1 = coerce BS.foldl1 -- @since 1.4.200.0 foldl1' :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -foldl1' = coerce BS.foldl1' +foldl1' = coerce BSP.foldl1' -- | 'foldr', applied to a binary operator, a starting value -- (typically the right-identity of the operator), and a OsString, @@ -537,14 +537,14 @@ foldl1' = coerce BS.foldl1' -- -- @since 1.4.200.0 foldr :: forall a. (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a -foldr = coerce (BS.foldr @a) +foldr = coerce (BSP.foldr @a) -- | 'foldr'' is like 'foldr', but strict in the accumulator. -- -- @since 1.4.200.0 foldr' :: forall a. (PLATFORM_WORD -> a -> a) -> a -> PLATFORM_STRING -> a -foldr' = coerce (BS.foldr' @a) +foldr' = coerce (BSP.foldr' @a) -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, -- and thus must be applied to non-empty 'OsString's @@ -552,31 +552,29 @@ foldr' = coerce (BS.foldr' @a) -- -- @since 1.4.200.0 foldr1 :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -foldr1 = coerce BS.foldr1 +foldr1 = coerce BSP.foldr1 -- | 'foldr1'' is a variant of 'foldr1', but is strict in the -- accumulator. -- --- --- @since 1.4.200.0 -- @since 1.4.200.0 foldr1' :: (PLATFORM_WORD -> PLATFORM_WORD -> PLATFORM_WORD) -> PLATFORM_STRING -> PLATFORM_WORD -foldr1' = coerce BS.foldr1' +foldr1' = coerce BSP.foldr1' -- | /O(n)/ Applied to a predicate and a 'OsString', 'all' determines -- if all elements of the 'OsString' satisfy the predicate. -- -- @since 1.4.200.0 all :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool -all = coerce BS.all +all = coerce BSP.all -- | /O(n)/ Applied to a predicate and a 'OsString', 'any' determines if -- any element of the 'OsString' satisfies the predicate. -- -- @since 1.4.200.0 any :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Bool -any = coerce BS.any +any = coerce BSP.any -- /O(n)/ Concatenate a list of OsStrings. -- @@ -591,7 +589,7 @@ concat = mconcat -- -- @since 1.4.200.0 replicate :: Int -> PLATFORM_WORD -> PLATFORM_STRING -replicate = coerce BS.replicate +replicate = coerce BSP.replicate -- | /O(n)/, where /n/ is the length of the result. The 'unfoldr' -- function is analogous to the List \'unfoldr\'. 'unfoldr' builds a @@ -613,7 +611,7 @@ replicate = coerce BS.replicate -- -- @since 1.4.200.0 unfoldr :: forall a. (a -> Maybe (PLATFORM_WORD, a)) -> a -> PLATFORM_STRING -unfoldr = coerce (BS.unfoldr @a) +unfoldr = coerce (BSP.unfoldr @a) -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed -- value. However, the length of the result is limited by the first @@ -626,14 +624,14 @@ unfoldr = coerce (BS.unfoldr @a) -- -- @since 1.4.200.0 unfoldrN :: forall a. Int -> (a -> Maybe (PLATFORM_WORD, a)) -> a -> (PLATFORM_STRING, Maybe a) -unfoldrN = coerce (BS.unfoldrN @a) +unfoldrN = coerce (BSP.unfoldrN @a) -- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix -- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@. -- -- @since 1.4.200.0 take :: Int -> PLATFORM_STRING -> PLATFORM_STRING -take = coerce BS.take +take = coerce BSP.take -- | /O(n)/ @'takeEnd' n xs@ is equivalent to @'drop' ('length' xs - n) xs@. -- Takes @n@ elements from end of bytestring. @@ -647,7 +645,7 @@ take = coerce BS.take -- -- @since 1.4.200.0 takeEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING -takeEnd = coerce BS.takeEnd +takeEnd = coerce BSP.takeEnd -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate. @@ -656,7 +654,7 @@ takeEnd = coerce BS.takeEnd -- -- @since 1.4.200.0 takeWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -takeWhileEnd = coerce BS.takeWhileEnd +takeWhileEnd = coerce BSP.takeWhileEnd -- | Similar to 'Prelude.takeWhile', -- returns the longest (possibly empty) prefix of elements @@ -664,13 +662,13 @@ takeWhileEnd = coerce BS.takeWhileEnd -- -- @since 1.4.200.0 takeWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -takeWhile = coerce BS.takeWhile +takeWhile = coerce BSP.takeWhile -- | /O(n)/ 'drop' @n@ @xs@ returns the suffix of @xs@ after the first n elements, or 'empty' if @n > 'length' xs@. -- -- @since 1.4.200.0 drop :: Int -> PLATFORM_STRING -> PLATFORM_STRING -drop = coerce BS.drop +drop = coerce BSP.drop -- | /O(n)/ @'dropEnd' n xs@ is equivalent to @'take' ('length' xs - n) xs@. -- Drops @n@ elements from end of bytestring. @@ -684,7 +682,7 @@ drop = coerce BS.drop -- -- @since 1.4.200.0 dropEnd :: Int -> PLATFORM_STRING -> PLATFORM_STRING -dropEnd = coerce BS.dropEnd +dropEnd = coerce BSP.dropEnd -- | Similar to 'Prelude.dropWhile', -- drops the longest (possibly empty) prefix of elements @@ -692,7 +690,7 @@ dropEnd = coerce BS.dropEnd -- -- @since 1.4.200.0 dropWhile :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -dropWhile = coerce BS.dropWhile +dropWhile = coerce BSP.dropWhile -- | Similar to 'Prelude.dropWhileEnd', -- drops the longest (possibly empty) suffix of elements @@ -702,7 +700,7 @@ dropWhile = coerce BS.dropWhile -- -- @since 1.4.200.0 dropWhileEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -dropWhileEnd = coerce BS.dropWhileEnd +dropWhileEnd = coerce BSP.dropWhileEnd -- | Returns the longest (possibly empty) suffix of elements which __do not__ -- satisfy the predicate and the remainder of the string. @@ -711,7 +709,7 @@ dropWhileEnd = coerce BS.dropWhileEnd -- -- @since 1.4.200.0 breakEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -breakEnd = coerce BS.breakEnd +breakEnd = coerce BSP.breakEnd -- | Similar to 'Prelude.break', -- returns the longest (possibly empty) prefix of elements which __do not__ @@ -721,7 +719,7 @@ breakEnd = coerce BS.breakEnd -- -- @since 1.4.200.0 break :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -break = coerce BS.break +break = coerce BSP.break -- | Similar to 'Prelude.span', -- returns the longest (possibly empty) prefix of elements @@ -731,7 +729,7 @@ break = coerce BS.break -- -- @since 1.4.200.0 span :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -span = coerce BS.span +span = coerce BSP.span -- | Returns the longest (possibly empty) suffix of elements -- satisfying the predicate and the remainder of the string. @@ -750,13 +748,13 @@ span = coerce BS.span -- -- @since 1.4.200.0 spanEnd :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -spanEnd = coerce BS.spanEnd +spanEnd = coerce BSP.spanEnd -- | /O(n)/ 'splitAt' @n sbs@ is equivalent to @('take' n sbs, 'drop' n sbs)@. -- -- @since 1.4.200.0 splitAt :: Int -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -splitAt = coerce BS.splitAt +splitAt = coerce BSP.splitAt -- | /O(n)/ Break a 'OsString' into pieces separated by the byte -- argument, consuming the delimiter. I.e. @@ -773,7 +771,7 @@ splitAt = coerce BS.splitAt -- -- @since 1.4.200.0 split :: PLATFORM_WORD -> PLATFORM_STRING -> [PLATFORM_STRING] -split = coerce BS.split +split = coerce BSP.split -- | /O(n)/ Splits a 'OsString' into components delimited by -- separators, where the predicate returns True for a separator element. @@ -785,7 +783,7 @@ split = coerce BS.split -- -- @since 1.4.200.0 splitWith :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [PLATFORM_STRING] -splitWith = coerce BS.splitWith +splitWith = coerce BSP.splitWith -- | /O(n)/ The 'stripSuffix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its suffix, and otherwise @@ -793,7 +791,7 @@ splitWith = coerce BS.splitWith -- -- @since 1.4.200.0 stripSuffix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING -stripSuffix = coerce BS.stripSuffix +stripSuffix = coerce BSP.stripSuffix -- | /O(n)/ The 'stripPrefix' function takes two OsStrings and returns 'Just' -- the remainder of the second iff the first is its prefix, and otherwise @@ -801,20 +799,20 @@ stripSuffix = coerce BS.stripSuffix -- -- @since 1.4.200.0 stripPrefix :: PLATFORM_STRING -> PLATFORM_STRING -> Maybe PLATFORM_STRING -stripPrefix = coerce BS.stripPrefix +stripPrefix = coerce BSP.stripPrefix -- | Check whether one string is a substring of another. -- -- @since 1.4.200.0 isInfixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -isInfixOf = coerce BS.isInfixOf +isInfixOf = coerce BSP.isInfixOf -- |/O(n)/ The 'isPrefixOf' function takes two OsStrings and returns 'True' -- -- @since 1.4.200.0 isPrefixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -isPrefixOf = coerce BS.isPrefixOf +isPrefixOf = coerce BSP.isPrefixOf -- | /O(n)/ The 'isSuffixOf' function takes two OsStrings and returns 'True' -- iff the first is a suffix of the second. @@ -825,7 +823,7 @@ isPrefixOf = coerce BS.isPrefixOf -- -- @since 1.4.200.0 isSuffixOf :: PLATFORM_STRING -> PLATFORM_STRING -> Bool -isSuffixOf = coerce BS.isSuffixOf +isSuffixOf = coerce BSP.isSuffixOf -- | Break a string on a substring, returning a pair of the part of the @@ -854,13 +852,13 @@ isSuffixOf = coerce BS.isSuffixOf -- -- @since 1.4.200.0 breakSubstring :: PLATFORM_STRING -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -breakSubstring = coerce BS.breakSubstring +breakSubstring = coerce BSP.breakSubstring -- | /O(n)/ 'elem' is the 'OsString' membership predicate. -- -- @since 1.4.200.0 elem :: PLATFORM_WORD -> PLATFORM_STRING -> Bool -elem = coerce BS.elem +elem = coerce BSP.elem -- | /O(n)/ The 'find' function takes a predicate and a OsString, -- and returns the first element in matching the predicate, or 'Nothing' @@ -870,7 +868,7 @@ elem = coerce BS.elem -- -- @since 1.4.200.0 find :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe PLATFORM_WORD -find = coerce BS.find +find = coerce BSP.find -- | /O(n)/ 'filter', applied to a predicate and a OsString, -- returns a OsString containing those characters that satisfy the @@ -878,7 +876,7 @@ find = coerce BS.find -- -- @since 1.4.200.0 filter :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> PLATFORM_STRING -filter = coerce BS.filter +filter = coerce BSP.filter -- | /O(n)/ The 'partition' function takes a predicate a OsString and returns -- the pair of OsStrings with elements which do and do not satisfy the @@ -888,13 +886,13 @@ filter = coerce BS.filter -- -- @since 1.4.200.0 partition :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> (PLATFORM_STRING, PLATFORM_STRING) -partition = coerce BS.partition +partition = coerce BSP.partition -- | /O(1)/ 'OsString' index (subscript) operator, starting from 0. -- -- @since 1.4.200.0 index :: HasCallStack => PLATFORM_STRING -> Int -> PLATFORM_WORD -index = coerce BS.index +index = coerce BSP.index -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -902,7 +900,7 @@ index = coerce BS.index -- -- @since 1.4.200.0 indexMaybe :: PLATFORM_STRING -> Int -> Maybe PLATFORM_WORD -indexMaybe = coerce BS.indexMaybe +indexMaybe = coerce BSP.indexMaybe -- | /O(1)/ 'OsString' index, starting from 0, that returns 'Just' if: -- @@ -918,20 +916,20 @@ indexMaybe = coerce BS.indexMaybe -- -- @since 1.4.200.0 elemIndex :: PLATFORM_WORD -> PLATFORM_STRING -> Maybe Int -elemIndex = coerce BS.elemIndex +elemIndex = coerce BSP.elemIndex -- | /O(n)/ The 'elemIndices' function extends 'elemIndex', by returning -- the indices of all elements equal to the query element, in ascending order. -- -- @since 1.4.200.0 elemIndices :: PLATFORM_WORD -> PLATFORM_STRING -> [Int] -elemIndices = coerce BS.elemIndices +elemIndices = coerce BSP.elemIndices -- | count returns the number of times its argument appears in the OsString -- -- @since 1.4.200.0 count :: PLATFORM_WORD -> PLATFORM_STRING -> Int -count = coerce BS.count +count = coerce BSP.count -- | /O(n)/ The 'findIndex' function takes a predicate and a 'OsString' and -- returns the index of the first element in the OsString @@ -939,11 +937,11 @@ count = coerce BS.count -- -- @since 1.4.200.0 findIndex :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> Maybe Int -findIndex = coerce BS.findIndex +findIndex = coerce BSP.findIndex -- | /O(n)/ The 'findIndices' function extends 'findIndex', by returning the -- indices of all elements satisfying the predicate, in ascending order. -- -- @since 1.4.200.0 findIndices :: (PLATFORM_WORD -> Bool) -> PLATFORM_STRING -> [Int] -findIndices = coerce BS.findIndices +findIndices = coerce BSP.findIndices diff --git a/System/OsString/Internal.hs b/System/OsString/Internal.hs index 67319b40..c0b90ec3 100644 --- a/System/OsString/Internal.hs +++ b/System/OsString/Internal.hs @@ -1,6 +1,8 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE UnliftedFFITypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module System.OsString.Internal where @@ -28,7 +30,6 @@ import GHC.IO.Encoding.UTF8 ( mkUTF8 ) import qualified System.OsString.Posix as PF #endif import GHC.Stack (HasCallStack) -import Data.Bifunctor import Data.Coerce (coerce) @@ -281,14 +282,14 @@ intercalate = coerce PF.intercalate -- OsString using the binary operator, from left to right. -- -- @since 1.4.200.0 -foldl :: (a -> OsChar -> a) -> a -> OsString -> a -foldl f a (OsString s) = PF.foldl (coerce f) a s +foldl :: forall a. (a -> OsChar -> a) -> a -> OsString -> a +foldl = coerce (PF.foldl @a) -- | 'foldl'' is like 'foldl', but strict in the accumulator. -- -- @since 1.4.200.0 -foldl' :: (a -> OsChar -> a) -> a -> OsString -> a -foldl' f a (OsString s) = PF.foldl' (coerce f) a s +foldl' :: forall a. (a -> OsChar -> a) -> a -> OsString -> a +foldl' = coerce (PF.foldl' @a) -- | 'foldl1' is a variant of 'foldl' that has no starting value -- argument, and thus must be applied to non-empty 'OsString's. @@ -311,14 +312,14 @@ foldl1' = coerce PF.foldl1' -- reduces the OsString using the binary operator, from right to left. -- -- @since 1.4.200.0 -foldr :: (OsChar -> a -> a) -> a -> OsString -> a -foldr f a (OsString s) = PF.foldr (coerce f) a s +foldr :: forall a. (OsChar -> a -> a) -> a -> OsString -> a +foldr = coerce (PF.foldr @a) -- | 'foldr'' is like 'foldr', but strict in the accumulator. -- -- @since 1.4.200.0 -foldr' :: (OsChar -> a -> a) -> a -> OsString -> a -foldr' f a (OsString s) = PF.foldr' (coerce f) a s +foldr' :: forall a. (OsChar -> a -> a) -> a -> OsString -> a +foldr' = coerce (PF.foldr' @a) -- | 'foldr1' is a variant of 'foldr' that has no starting value argument, -- and thus must be applied to non-empty 'OsString's @@ -383,8 +384,8 @@ replicate = coerce PF.replicate -- > == pack [0, 1, 2, 3, 4, 5] -- -- @since 1.4.200.0 -unfoldr :: (a -> Maybe (OsChar, a)) -> a -> OsString -unfoldr f a = OsString $ PF.unfoldr (fmap (first getOsChar) . f) a +unfoldr :: forall a. (a -> Maybe (OsChar, a)) -> a -> OsString +unfoldr = coerce (PF.unfoldr @a) -- | /O(n)/ Like 'unfoldr', 'unfoldrN' builds a OsString from a seed -- value. However, the length of the result is limited by the first @@ -397,7 +398,7 @@ unfoldr f a = OsString $ PF.unfoldr (fmap (first getOsChar) . f) a -- -- @since 1.4.200.0 unfoldrN :: forall a. Int -> (a -> Maybe (OsChar, a)) -> a -> (OsString, Maybe a) -unfoldrN n f a = first OsString $ PF.unfoldrN n (fmap (first getOsChar) . f) a +unfoldrN = coerce (PF.unfoldrN @a) -- | /O(n)/ 'take' @n@, applied to a OsString @xs@, returns the prefix -- of @xs@ of length @n@, or @xs@ itself if @n > 'length' xs@.