Skip to content

Commit 55203b1

Browse files
committed
Use (EqCfg -> EqCfg) config modifier instead of EqCfg
1 parent 5b9de04 commit 55203b1

3 files changed

Lines changed: 93 additions & 89 deletions

File tree

core/src/Streamly/Internal/FileSystem/Path/Common.hs

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,10 @@ module Streamly.Internal.FileSystem.Path.Common
8585
, normalizeSeparators
8686
-- , normalize -- separators and /./ components (split/combine)
8787
, eqPathBytes
88-
, EqCfg(..)
89-
, eqCfg
90-
, eqPathWith
88+
, EqCfg
89+
, ignoreTrailingSeparators
90+
, ignoreCase
91+
, allowRelativeEquality
9192
, eqPath
9293
-- , commonPrefix -- common prefix of two paths
9394
-- , eqPrefix -- common prefix is equal to first path
@@ -1388,11 +1389,19 @@ eqPathBytes = Array.byteEq
13881389
-- | Options for path comparison operation. By default path comparison uses a
13891390
-- strict criteria for equality. The following options are provided to
13901391
-- control the strictness.
1392+
--
1393+
-- The default configuration is as follows:
1394+
-- @
1395+
-- ignoreTrailingSeparators = False
1396+
-- ignoreCase = False
1397+
-- allowRelativeEquality = False
1398+
-- @
1399+
--
13911400
data EqCfg =
13921401
EqCfg
1393-
{ ignoreTrailingSeparators :: Bool -- ^ Allows "x\/" == "x"
1394-
, ignoreCase :: Bool -- ^ Allows "x" == \"X\"
1395-
, allowRelativeEquality :: Bool
1402+
{ _ignoreTrailingSeparators :: Bool -- ^ Allows "x\/" == "x"
1403+
, _ignoreCase :: Bool -- ^ Allows "x" == \"X\"
1404+
, _allowRelativeEquality :: Bool
13961405
-- ^ A leading dot is ignored, thus ".\/x" == ".\/x" and ".\/x" == "x".
13971406
-- On Windows allows "\/x" == \/x" and "C:x == C:x"
13981407

@@ -1413,11 +1422,21 @@ data EqCfg =
14131422
--
14141423
eqCfg :: EqCfg
14151424
eqCfg = EqCfg
1416-
{ ignoreTrailingSeparators = False
1417-
, ignoreCase = False
1418-
, allowRelativeEquality = False
1425+
{ _ignoreTrailingSeparators = False
1426+
, _ignoreCase = False
1427+
, _allowRelativeEquality = False
14191428
}
14201429

1430+
ignoreTrailingSeparators :: Bool -> EqCfg -> EqCfg
1431+
ignoreTrailingSeparators val conf = conf { _ignoreTrailingSeparators = val }
1432+
1433+
ignoreCase :: Bool -> EqCfg -> EqCfg
1434+
ignoreCase val conf = conf { _ignoreCase = val }
1435+
1436+
allowRelativeEquality :: Bool -> EqCfg -> EqCfg
1437+
allowRelativeEquality val conf = conf { _allowRelativeEquality = val }
1438+
1439+
14211440
data PosixRoot = PosixRootAbs | PosixRootRel deriving Eq
14221441

14231442
data WindowsRoot =
@@ -1521,38 +1540,34 @@ eqComponentsWith ignCase decoder os a b =
15211540
Array.byteEq (splitPath_ os a) (splitPath_ os b)
15221541

15231542
-- XXX can we do something like SpecConstr for such functions e.g. without
1524-
-- inlining the function we can use two copies one for allowRelativeEquality
1543+
-- inlining the function we can use two copies one for _allowRelativeEquality
15251544
-- True and other for False and so on for other values of PathEq.
15261545

1527-
{-# INLINE eqPathWith #-}
1528-
eqPathWith :: (Unbox a, Integral a) =>
1546+
{-# INLINE eqPath #-}
1547+
eqPath :: (Unbox a, Integral a) =>
15291548
(Stream Identity a -> Stream Identity Char)
1530-
-> OS -> EqCfg -> Array a -> Array a -> Bool
1531-
eqPathWith decoder os EqCfg{..} a b =
1549+
-> OS -> (EqCfg -> EqCfg) -> Array a -> Array a -> Bool
1550+
eqPath decoder os configMod a b =
15321551
let (rootA, stemA) = splitRoot os a
15331552
(rootB, stemB) = splitRoot os b
15341553

15351554
eqRelative =
1536-
if allowRelativeEquality
1537-
then eqRootLax ignoreCase os rootA rootB
1555+
if _allowRelativeEquality
1556+
then eqRootLax _ignoreCase os rootA rootB
15381557
else (not (isRootRelative os rootA)
15391558
&& not (isRootRelative os rootB))
1540-
&& eqRootStrict ignoreCase os rootA rootB
1559+
&& eqRootStrict _ignoreCase os rootA rootB
15411560

15421561
-- XXX If one ends in a "." and the other ends in ./ (and same for ".."
15431562
-- and "../") then they can be equal. We can append a slash in these two
15441563
-- cases before comparing.
15451564
eqTrailingSep =
1546-
ignoreTrailingSeparators
1565+
_ignoreTrailingSeparators
15471566
|| hasTrailingSeparator os a == hasTrailingSeparator os b
15481567

15491568
in
15501569
eqRelative
15511570
&& eqTrailingSep
1552-
&& eqComponentsWith ignoreCase decoder os stemA stemB
1553-
1554-
{-# INLINE eqPath #-}
1555-
eqPath :: (Unbox a, Integral a) =>
1556-
(Stream Identity a -> Stream Identity Char)
1557-
-> OS -> Array a -> Array a -> Bool
1558-
eqPath decoder os = eqPathWith decoder os eqCfg
1571+
&& eqComponentsWith _ignoreCase decoder os stemA stemB
1572+
where
1573+
EqCfg {..} = configMod eqCfg

core/src/Streamly/Internal/FileSystem/PosixPath.hs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,11 @@ module Streamly.Internal.FileSystem.OS_PATH
158158
, addExtension
159159

160160
-- * Equality
161+
, EqCfg
162+
, Common.ignoreTrailingSeparators
163+
, Common.ignoreCase
164+
, Common.allowRelativeEquality
161165
, eqPath
162-
, EqCfg(..)
163-
, eqCfg
164-
, eqPathWith
165166
, eqPathBytes
166167
, normalize
167168
)
@@ -181,7 +182,7 @@ import Foreign.C (CWString)
181182
import Language.Haskell.TH.Syntax (lift)
182183
import Streamly.Internal.Data.Array (Array(..))
183184
import Streamly.Internal.Data.Stream (Stream)
184-
import Streamly.Internal.FileSystem.Path.Common (mkQ, EqCfg(..), eqCfg)
185+
import Streamly.Internal.FileSystem.Path.Common (mkQ, EqCfg)
185186

186187
import qualified Streamly.Internal.Data.Array as Array
187188
import qualified Streamly.Internal.Data.Stream as Stream
@@ -1032,10 +1033,14 @@ addExtension (OS_PATH _a) = undefined
10321033
------------------------------------------------------------------------------
10331034

10341035
#ifndef IS_WINDOWS
1035-
-- | Checks two paths for logical equality. It performs some normalizations on
1036-
-- the paths before comparing them, specifically it drops redundant path
1037-
-- separators between path segments and redundant "\/.\/" components between
1038-
-- segments.
1036+
-- | Checks two paths for logical equality. This function can take a
1037+
-- configuration modifier to change the notion of equality. The default
1038+
-- configuration denoted by @id@ has behaviour defined below. The default
1039+
-- configuration is can be seen in the documentation of "EqCfg".
1040+
--
1041+
-- It performs some normalizations on the paths before comparing them,
1042+
-- specifically it drops redundant path separators between path segments and
1043+
-- redundant "\/.\/" components between segments.
10391044
--
10401045
-- Equality semantics followed by this routine are listed below. If it returns
10411046
-- equal then the paths are definitely equal, if it returns unequal then the
@@ -1064,7 +1069,7 @@ addExtension (OS_PATH _a) = undefined
10641069
-- must be files or both must be directories.
10651070
--
10661071
-- >>> :{
1067-
-- eq a b = Path.eqPath (pack a) (pack b)
1072+
-- eq a b = Path.eqPath id (pack a) (pack b)
10681073
-- :}
10691074
--
10701075
-- >>> eq "/x" "//x"
@@ -1097,19 +1102,12 @@ addExtension (OS_PATH _a) = undefined
10971102
-- >>> eq "./x" "./x"
10981103
-- False
10991104
--
1100-
eqPath :: OS_PATH -> OS_PATH -> Bool
1101-
eqPath (OS_PATH a) (OS_PATH b) =
1102-
Common.eqPath Unicode.UNICODE_DECODER
1103-
Common.OS_NAME a b
1104-
1105-
-- | Like 'eqPath' but we can control the equality options.
1105+
-- We can change the configuration using the available config modifiers.
11061106
--
11071107
-- >>> :{
1108-
-- cfg = Path.eqCfg
1109-
-- { Path.ignoreTrailingSeparators = True
1110-
-- , Path.allowRelativeEquality = True
1111-
-- }
1112-
-- eq a b = Path.eqPathWith cfg (pack a) (pack b)
1108+
-- cfg = Path.ignoreTrailingSeparators True
1109+
-- . Path.allowRelativeEquality True
1110+
-- eq a b = Path.eqPath cfg (pack a) (pack b)
11131111
-- :}
11141112
--
11151113
-- >>> eq "." "."
@@ -1139,9 +1137,9 @@ eqPath (OS_PATH a) (OS_PATH b) =
11391137
-- >>> eq "x" "x"
11401138
-- True
11411139
--
1142-
eqPathWith :: EqCfg -> OS_PATH -> OS_PATH -> Bool
1143-
eqPathWith cfg (OS_PATH a) (OS_PATH b) =
1144-
Common.eqPathWith Unicode.UNICODE_DECODER
1140+
eqPath :: (EqCfg -> EqCfg) -> OS_PATH -> OS_PATH -> Bool
1141+
eqPath cfg (OS_PATH a) (OS_PATH b) =
1142+
Common.eqPath Unicode.UNICODE_DECODER
11451143
Common.OS_NAME cfg a b
11461144
#endif
11471145

core/src/Streamly/Internal/FileSystem/WindowsPath.hs

Lines changed: 32 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -306,43 +306,6 @@ extendDir
306306
$ Common.append'
307307
Common.OS_NAME (Common.toString Unicode.UNICODE_DECODER) a b
308308

309-
-- | Like 'eqPath' but we can control the equality options.
310-
--
311-
-- >>> :{
312-
-- cfg = Path.eqCfg
313-
-- { Path.ignoreTrailingSeparators = True
314-
-- , Path.ignoreCase = True
315-
-- , Path.allowRelativeEquality = True
316-
-- }
317-
-- eq a b = Path.eqPathWith cfg (pack a) (pack b)
318-
-- :}
319-
--
320-
-- >>> eq "./x" "x"
321-
-- True
322-
--
323-
-- >>> eq "X/" "x"
324-
-- True
325-
--
326-
-- >>> eq "C:x" "c:X"
327-
-- True
328-
--
329-
-- >>> eq ".\\x" "./X"
330-
-- True
331-
--
332-
-- >>> eq "x//y" "x/y"
333-
-- True
334-
--
335-
-- >>> eq "x/./y" "x/y"
336-
-- True
337-
--
338-
-- >>> eq "x" "x"
339-
-- True
340-
--
341-
eqPathWith :: EqCfg -> OS_PATH -> OS_PATH -> Bool
342-
eqPathWith cfg (OS_PATH a) (OS_PATH b) =
343-
Common.eqPathWith Unicode.UNICODE_DECODER
344-
Common.OS_NAME cfg a b
345-
346309
-- | See the eqPath documentation in the
347310
-- "Streamly.Internal.FileSystem.PosixPath" module for details.
348311
--
@@ -353,7 +316,7 @@ eqPathWith cfg (OS_PATH a) (OS_PATH b) =
353316
-- * the comparison is case sensitive.
354317
--
355318
-- >>> :{
356-
-- eq a b = Path.eqPath (pack a) (pack b)
319+
-- eq a b = Path.eqPath id (pack a) (pack b)
357320
-- :}
358321
--
359322
-- The cases that are different from Posix:
@@ -373,10 +336,38 @@ eqPathWith cfg (OS_PATH a) (OS_PATH b) =
373336
-- >>> eq "c:x" "c:x"
374337
-- False
375338
--
376-
eqPath :: OS_PATH -> OS_PATH -> Bool
377-
eqPath (OS_PATH a) (OS_PATH b) =
339+
-- >>> :{
340+
-- cfg = Path.ignoreTrailingSeparators True
341+
-- . Path.ignoreCase True
342+
-- . Path.allowRelativeEquality True
343+
-- eq a b = Path.eqPath cfg (pack a) (pack b)
344+
-- :}
345+
--
346+
-- >>> eq "./x" "x"
347+
-- True
348+
--
349+
-- >>> eq "X/" "x"
350+
-- True
351+
--
352+
-- >>> eq "C:x" "c:X"
353+
-- True
354+
--
355+
-- >>> eq ".\\x" "./X"
356+
-- True
357+
--
358+
-- >>> eq "x//y" "x/y"
359+
-- True
360+
--
361+
-- >>> eq "x/./y" "x/y"
362+
-- True
363+
--
364+
-- >>> eq "x" "x"
365+
-- True
366+
--
367+
eqPath :: (EqCfg -> EqCfg) -> OS_PATH -> OS_PATH -> Bool
368+
eqPath cfg (OS_PATH a) (OS_PATH b) =
378369
Common.eqPath Unicode.UNICODE_DECODER
379-
Common.OS_NAME a b
370+
Common.OS_NAME cfg a b
380371

381372
-- | If a path is rooted then separate the root and the remaining path,
382373
-- otherwise root is returned as empty. If the path is rooted then the non-root

0 commit comments

Comments
 (0)