Skip to content

Commit 9019eb6

Browse files
Rename some Path APIs and fix docs, doctests of some
1 parent 1be8b88 commit 9019eb6

13 files changed

Lines changed: 501 additions & 242 deletions

File tree

bench-test-lib/src/BenchTestLib/DirIO.hs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ import qualified Streamly.Internal.Data.Unfold as Unfold
6161
import qualified Streamly.Internal.FileSystem.DirIO as Dir
6262
import qualified Streamly.FileSystem.Path as Path
6363
#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
64-
import qualified Streamly.Internal.FileSystem.Path as Path (toChunk)
6564
import qualified Streamly.Internal.FileSystem.Posix.ReadDir as Dir
6665
#endif
6766

@@ -165,7 +164,7 @@ listDirChunkedWith
165164
-> [Char] -> Stream IO Word8
166165
listDirChunkedWith act inp = do
167166
Stream.unfoldEachEndBy 10 Array.reader
168-
$ fmap (Array.asBytes . Path.toChunk)
167+
$ fmap (Array.asBytes . Path.toArray)
169168
$ Stream.unfoldEach Unfold.fromList
170169
$ fmap (either id id)
171170
$ act
@@ -177,7 +176,7 @@ listDirWith
177176
-> [Char] -> Stream IO Word8
178177
listDirWith act inp = do
179178
Stream.unfoldEachEndBy 10 Array.reader
180-
$ fmap (Array.asBytes . Path.toChunk . either id id)
179+
$ fmap (Array.asBytes . Path.toArray . either id id)
181180
$ act
182181
$ Stream.fromPure (Left (fromJust $ Path.fromString inp))
183182

core/src/DocTestFileSystemPath.hs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{- $setup
2+
>>> :m
3+
>>> :set -XQuasiQuotes
4+
>>> import Control.Exception (SomeException, evaluate, try)
5+
>>> import Data.Either (Either, isLeft)
6+
>>> import Data.Maybe (fromJust, isJust, isNothing)
7+
>>> import Streamly.FileSystem.Path (Path, path)
8+
>>> import qualified Streamly.Data.Array as Array
9+
>>> import qualified Streamly.Data.Stream as Stream
10+
>>> import qualified Streamly.FileSystem.Path as Path
11+
>>> import qualified Streamly.Unicode.Stream as Unicode
12+
13+
For APIs that have not been released yet.
14+
15+
>>> import qualified Streamly.Internal.FileSystem.Path as Path
16+
17+
Utilities:
18+
19+
>>> fails x = isLeft <$> (try (evaluate x) :: IO (Either SomeException String))
20+
-}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{- $setup
2+
>>> :m
3+
>>> :set -XQuasiQuotes
4+
>>> import Control.Exception (SomeException, evaluate, try)
5+
>>> import Data.Either (Either, isLeft)
6+
>>> import Data.Maybe (isNothing, isJust)
7+
>>> import qualified Streamly.Data.Array as Array
8+
>>> import qualified Streamly.Data.Stream as Stream
9+
>>> import qualified Streamly.Unicode.Stream as Unicode
10+
11+
For APIs that have not been released yet.
12+
13+
>>> import Streamly.Internal.FileSystem.PosixPath (PosixPath, path)
14+
>>> import qualified Streamly.Internal.FileSystem.PosixPath as Path
15+
16+
Utilities:
17+
18+
>>> fails x = isLeft <$> (try (evaluate x) :: IO (Either SomeException String))
19+
-}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{- $setup
2+
>>> :m
3+
>>> :set -XQuasiQuotes
4+
>>> import Control.Exception (SomeException, evaluate, try)
5+
>>> import Data.Either (Either, isLeft)
6+
>>> import Data.Maybe (fromJust, isNothing, isJust)
7+
>>> import Data.Word (Word16)
8+
>>> import Streamly.Data.Array (Array)
9+
>>> import qualified Streamly.Data.Array as Array
10+
>>> import qualified Streamly.Data.Stream as Stream
11+
>>> import qualified Streamly.Unicode.Stream as Unicode
12+
13+
For APIs that have not been released yet.
14+
15+
>>> import Streamly.Internal.FileSystem.WindowsPath (WindowsPath, path)
16+
>>> import qualified Streamly.Internal.FileSystem.WindowsPath as Path
17+
18+
Utilities:
19+
20+
>>> fails x = isLeft <$> (try (evaluate x) :: IO (Either SomeException String))
21+
-}

core/src/Streamly/FileSystem/Path.hs

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE CPP #-}
12
-- |
23
-- Module : Streamly.FileSystem.Path
34
-- Copyright : (c) 2023 Composewell Technologies
@@ -68,11 +69,20 @@
6869

6970
module Streamly.FileSystem.Path
7071
(
72+
-- * Setup
73+
-- | To execute the code examples provided in this module in ghci, please
74+
-- run the following commands first.
75+
--
76+
-- $setup
77+
7178
-- * Type
7279
Path
80+
, OsWord
7381

7482
-- * Construction
75-
, fromChunk
83+
, isValidPath
84+
, validatePath
85+
, fromArray
7686
, fromString
7787

7888
-- * Statically Verified String Literals
@@ -84,19 +94,20 @@ module Streamly.FileSystem.Path
8494
, pathE
8595

8696
-- * Elimination
87-
, toChunk
97+
, toArray
8898
, toChars
8999
, toString
90-
, asOsCString
100+
-- , asOsCString
91101

92102
-- * Path Info
93103
, isRooted
94104
, isBranch
95105

96-
-- * Separators
97-
, dropTrailingSeparators
98-
, hasTrailingSeparator
99-
, addTrailingSeparator
106+
-- These are unstable APIs, see comments in the internal module.
107+
-- -- * Separators
108+
-- , dropTrailingSeparators
109+
-- , hasTrailingSeparator
110+
-- , addTrailingSeparator
100111

101112
-- * Joining
102113
, unsafeExtend
@@ -123,14 +134,11 @@ module Streamly.FileSystem.Path
123134

124135
-- * Equality
125136
, EqCfg
126-
, eqPath
127-
128-
#ifndef IS_WINDOWS
129-
-- ** Config options (Posix)
130137
, ignoreTrailingSeparators
131138
, ignoreCase
132139
, allowRelativeEquality
133-
#endif
140+
141+
, eqPath
134142
)
135143
where
136144

@@ -186,3 +194,5 @@ where
186194
-}
187195

188196
import Streamly.Internal.FileSystem.Path
197+
198+
#include "DocTestFileSystemPath.hs"

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
{-# LANGUAGE CPP #-}
12
-- |
23
-- Module : Streamly.Internal.FileSystem.Path
34
-- Copyright : (c) 2023 Composewell Technologies

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

Lines changed: 59 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ module Streamly.Internal.FileSystem.Path.Common
1919
, validateFile
2020

2121
-- * Construction
22-
, fromChunk
23-
, unsafeFromChunk
22+
, fromArray
23+
, unsafeFromArray
2424
, fromChars
2525
, unsafeFromChars
2626

@@ -34,7 +34,9 @@ module Streamly.Internal.FileSystem.Path.Common
3434
-- * Separators
3535
, primarySeparator
3636
, isSeparator
37+
, isSeparatorWord
3738
, dropTrailingSeparators
39+
, dropTrailingBy
3840
, hasTrailingSeparator
3941
, hasLeadingSeparator
4042

@@ -85,7 +87,7 @@ module Streamly.Internal.FileSystem.Path.Common
8587
, normalizeSeparators
8688
-- , normalize -- separators and /./ components (split/combine)
8789
, eqPathBytes
88-
, EqCfg
90+
, EqCfg(..)
8991
, ignoreTrailingSeparators
9092
, ignoreCase
9193
, allowRelativeEquality
@@ -165,6 +167,10 @@ data OS = Windows | Posix deriving Eq
165167
-- XXX We can use Enum type class to include the Char type as well so that the
166168
-- functions can work on Array Word8/Word16/Char but that may be slow.
167169

170+
-- XXX Windows is supported only on little endian machines so generally we do
171+
-- not need covnersion from LE to BE format unless we want to manipulate
172+
-- windows paths on big-endian machines.
173+
168174
-- | Unsafe, may tructate to shorter word types, can only be used safely for
169175
-- characters that fit in the given word size.
170176
charToWord :: Integral a => Char -> a
@@ -235,6 +241,9 @@ isSeparatorWord os = isSeparator os . wordToChar
235241
-- @a@. On Windows "c:" and "c:/" are different paths, therefore, we do not
236242
-- drop the trailing separator from "c:/" or for that matter a separator
237243
-- preceded by a ':'.
244+
--
245+
-- Can't use any arbitrary predicate "p", the logic in this depends on assuming
246+
-- that it is a path separator.
238247
{-# INLINE dropTrailingBy #-}
239248
dropTrailingBy :: (Unbox a, Integral a) =>
240249
OS -> (a -> Bool) -> Array a -> Array a
@@ -245,13 +254,23 @@ dropTrailingBy os p arr =
245254
in if n == 0
246255
then arr
247256
else if n == len -- "////"
248-
then fst $ Array.unsafeBreakAt 1 arr
249-
-- "c:////"
257+
then
258+
-- Even though "//" is not allowed as a valid path.
259+
-- We still handle that case in this low level function.
260+
if os == Windows
261+
&& n >= 2
262+
&& Array.unsafeGetIndex 0 arr == Array.unsafeGetIndex 1 arr
263+
then fst $ Array.unsafeBreakAt 2 arr -- make it "//" share name
264+
else fst $ Array.unsafeBreakAt 1 arr
265+
-- "c:////" - keep one "/" after colon in ".*:///" otherwise it will
266+
-- change the meaning. "c:/" may also appear, in the middle e.g.
267+
-- in UNC paths.
250268
else if (os == Windows)
251269
&& (Array.unsafeGetIndex (len - n - 1) arr == charToWord ':')
252270
then fst $ Array.unsafeBreakAt (len - n + 1) arr
253271
else arr1
254272

273+
-- XXX we cannot compact "//" to "/" on windows
255274
{-# INLINE compactTrailingBy #-}
256275
compactTrailingBy :: Unbox a => (a -> Bool) -> Array a -> Array a
257276
compactTrailingBy p arr =
@@ -1158,8 +1177,9 @@ validatePathWith allowRoot Windows path
11581177
validatePath :: (MonadThrow m, Integral a, Unbox a) => OS -> Array a -> m ()
11591178
validatePath = validatePathWith True
11601179

1161-
-- | Like validatePath but on Windows only full paths are allowed, path roots
1162-
-- only are not allowed. Thus "//x/" is not valid.
1180+
-- | Like validatePath but on Windows the path must refer to a file system
1181+
-- object, share roots or prefixes not referring to a specific path are not
1182+
-- allowed. Thus "//x/" is not a valid path.
11631183
{-# INLINE validatePath' #-}
11641184
validatePath' :: (MonadThrow m, Integral a, Unbox a) => OS -> Array a -> m ()
11651185
validatePath' = validatePathWith False
@@ -1187,18 +1207,18 @@ isValidPath' os path =
11871207
Nothing -> False
11881208
Just _ -> True
11891209

1190-
{-# INLINE unsafeFromChunk #-}
1191-
unsafeFromChunk :: Array a -> Array a
1192-
unsafeFromChunk = id
1210+
{-# INLINE unsafeFromArray #-}
1211+
unsafeFromArray :: Array a -> Array a
1212+
unsafeFromArray = id
11931213

1194-
{-# INLINE fromChunk #-}
1195-
fromChunk :: forall m a. (MonadThrow m, Unbox a, Integral a) =>
1214+
{-# INLINE fromArray #-}
1215+
fromArray :: forall m a. (MonadThrow m, Unbox a, Integral a) =>
11961216
OS -> Array a -> m (Array a)
1197-
fromChunk os arr = validatePath os arr >> pure arr
1217+
fromArray os arr = validatePath os arr >> pure arr
11981218
{-
11991219
let arr1 = Array.unsafeCast arr :: Array a
12001220
in validatePath os arr1 >> pure arr1
1201-
fromChunk Windows arr =
1221+
fromArray Windows arr =
12021222
case Array.cast arr of
12031223
Nothing ->
12041224
throwM
@@ -1228,7 +1248,7 @@ fromChars :: (MonadThrow m, Unbox a, Integral a) =>
12281248
-> m (Array a)
12291249
fromChars os encode s =
12301250
let arr = unsafeFromChars encode s
1231-
in fromChunk os (Array.unsafeCast arr)
1251+
in fromArray os (Array.unsafeCast arr)
12321252

12331253
{-# INLINE toChars #-}
12341254
toChars :: (Monad m, Unbox a) =>
@@ -1392,6 +1412,7 @@ eqPathBytes = Array.byteEq
13921412
-- control the strictness.
13931413
--
13941414
-- The default configuration is as follows:
1415+
--
13951416
-- >>> :{
13961417
-- defaultMod = ignoreTrailingSeparators False
13971418
-- . ignoreCase False
@@ -1411,33 +1432,36 @@ data EqCfg =
14111432
-- , noIgnoreRedundantDot -- "x\/.\/" \/= "x"
14121433
}
14131434

1414-
-- | Default equality check configuration.
1415-
--
1416-
-- > :{
1417-
-- > eqCfg = EqCfg
1418-
-- > { ignoreTrailingSeparators = False
1419-
-- > , ignoreCase = False
1420-
-- > , allowRelativeEquality = False
1421-
-- > }
1422-
-- > :}
1423-
--
1424-
eqCfg :: EqCfg
1425-
eqCfg = EqCfg
1426-
{ _ignoreTrailingSeparators = False
1427-
, _ignoreCase = False
1428-
, _allowRelativeEquality = False
1429-
}
1430-
1435+
-- | When set to 'False', a path with a trailing slash and a path without are
1436+
-- treated as unequal e.g. "x" is not the same as "x\/". The latter is a
1437+
-- directory.
1438+
--
1439+
-- /Default/: False
14311440
ignoreTrailingSeparators :: Bool -> EqCfg -> EqCfg
14321441
ignoreTrailingSeparators val conf = conf { _ignoreTrailingSeparators = val }
14331442

1443+
-- | When set to 'False', comparison is case sensitive.
1444+
--
1445+
-- /Posix Default/: False
1446+
--
1447+
-- /Windows Default/: True
14341448
ignoreCase :: Bool -> EqCfg -> EqCfg
14351449
ignoreCase val conf = conf { _ignoreCase = val }
14361450

1451+
-- | When set to 'False':
1452+
--
1453+
-- * paths with a leading "." and without a leading "." e.g. ".\/x\/y" and
1454+
-- "x\/y" are treated as unequal. The first one is a dynamically rooted path
1455+
-- and the second one is a branch or free path segment.
1456+
--
1457+
-- * Two paths starting with a leading "." may not actually be equal even if
1458+
-- they are literally equal, depending on the meaning of ".". We return unequal
1459+
-- even though they may be equal sometimes.
1460+
--
1461+
-- /Default/: False
14371462
allowRelativeEquality :: Bool -> EqCfg -> EqCfg
14381463
allowRelativeEquality val conf = conf { _allowRelativeEquality = val }
14391464

1440-
14411465
data PosixRoot = PosixRootAbs | PosixRootRel deriving Eq
14421466

14431467
data WindowsRoot =
@@ -1547,8 +1571,8 @@ eqComponentsWith ignCase decoder os a b =
15471571
{-# INLINE eqPath #-}
15481572
eqPath :: (Unbox a, Integral a) =>
15491573
(Stream Identity a -> Stream Identity Char)
1550-
-> OS -> (EqCfg -> EqCfg) -> Array a -> Array a -> Bool
1551-
eqPath decoder os configMod a b =
1574+
-> OS -> EqCfg -> Array a -> Array a -> Bool
1575+
eqPath decoder os EqCfg{..} a b =
15521576
let (rootA, stemA) = splitRoot os a
15531577
(rootB, stemB) = splitRoot os b
15541578

@@ -1570,5 +1594,3 @@ eqPath decoder os configMod a b =
15701594
eqRelative
15711595
&& eqTrailingSep
15721596
&& eqComponentsWith _ignoreCase decoder os stemA stemB
1573-
where
1574-
EqCfg {..} = configMod eqCfg

0 commit comments

Comments
 (0)