Skip to content

Commit 1be8b88

Browse files
Use Path type in portable module instead of PosixPath type
1 parent 1d55cbd commit 1be8b88

File tree

4 files changed

+81
-117
lines changed

4 files changed

+81
-117
lines changed

core/src/Streamly/FileSystem/Path.hs

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,56 @@
99
-- and filesystem encoding.
1010
--
1111
-- The 'Path' type is built on top of Streamly's 'Array' type, leveraging all
12-
-- its operations and advantages—including support for both pinned and unpinned
12+
-- its operations including support for both pinned and unpinned
1313
-- representations. It is designed for extensibility and fine-grained type
14-
-- safety. For type-safe adaptations, refer to the
15-
-- "Streamly.Internal.FileSystem.Path.*" modules. The type also offers a
16-
-- powerful and flexible path comparison mechanism.
14+
-- safety. For type-safe adaptations, see the
15+
-- "Streamly.Internal.FileSystem.Path.*" modules.
1716
--
18-
-- 'Path' is interconvertible with the 'OsPath' type from the filepath package
19-
-- at zero runtime cost. While the API is mostly compatible with that of the
20-
-- filepath package, some differences exist due to a slightly different design
21-
-- philosophy focused on enhanced safety.
17+
-- 'Path' is interconvertible with the 'OsPath' type from the @filepath@
18+
-- package at zero runtime cost. While the API is mostly compatible with that
19+
-- of the @filepath@ package, some differences exist due to a slightly
20+
-- different design philosophy focused on better safety.
2221
--
2322
-- = Rooted Paths vs Branches
2423
--
25-
-- To ensure the safety of the append operation, we distinguish between rooted
26-
-- paths and branch-type paths. A path that starts from an explicit or implicit
27-
-- root in the file system is called a rooted path or an anchored path. For
28-
-- example, @\/usr\/bin@ is a rooted path starting from the explicit root
29-
-- directory @/@. Similarly, @.\/bin@ is rooted implicitly, anchored at the
30-
-- current directory. A path that is not rooted is called a branch or
24+
-- To ensure the safety of the path append operation, we distinguish between
25+
-- rooted paths and branch-type paths. A path that starts from an explicit or
26+
-- implicit file system root is called a rooted path or an anchored path. For
27+
-- example, @\/usr\/bin@ is a rooted path with an explicit root directory @/@.
28+
-- Similarly, @.\/bin@ is a rooted path with an implicit root, anchored at the
29+
-- current directory. A path that is not rooted is called a branch type path or
3130
-- unanchored path; for example, @local\/bin@ is a branch.
3231
--
3332
-- This distinction ensures the safety of the path append operation. You can
34-
-- always append a branch to a rooted path or to another branch. However, it
35-
-- does not make sense to append one rooted path to another. The default append
36-
-- operation in the Path module checks for this and fails if the operation is
37-
-- invalid. However, the programmer can force the unsafe behavior by using the
38-
-- unsafe append operation. Alternatively, you can drop the root explicitly and
39-
-- use the safe append.
33+
-- append only a branch type path to another path, it does not make sense to
34+
-- append a rooted path to another path. The default append operation in the
35+
-- Path module checks for this and fails if the operation is invalid. However,
36+
-- the programmer can force the unsafe behavior by using the unsafe append
37+
-- operation. Alternatively, you can drop the root explicitly and use the safe
38+
-- append.
4039
--
4140
-- Rooted vs branch distinction is a stricter form of relative vs absolute path
4241
-- distinction. Essentially, paths relative to the current directory are also
4342
-- treated in the same way as absolute paths, from the perspective of an append
44-
-- operation. Only branch-type paths can be appended to any other path using
43+
-- operation. The meaning of current directory is context dependent and
44+
-- dynamic, therefore, appending it to another path is not allowed. Only pure
45+
-- branch-type paths (e.g. @local/bin@) can be appended to any other path using
4546
-- safe operations.
4647
--
4748
-- = File vs. Directory Paths
4849
--
49-
-- By default, a path with a trailing separator is implicitly considered a
50-
-- directory path. However, the absence of a trailing separator does not
51-
-- indicate whether the path is a file or a directory — it could be either.
52-
-- Therefore, when using the @Path@ type, the append operation allows appending
53-
-- to paths even if they lack a trailing separator.
50+
-- By default, a path with a trailing separator (e.g. @local/@) is implicitly
51+
-- considered a directory path. However, the absence of a trailing separator
52+
-- does not indicate whether the path is a file or a directory — it could be
53+
-- either. Therefore, when using the @Path@ type, the append operation allows
54+
-- appending to paths even if they lack a trailing separator.
5455
--
5556
-- = Compatibility with the filepath package
5657
--
57-
-- Any path type can be converted to the 'FilePath' type from the filepath
58+
-- Any path type can be converted to the 'FilePath' type from the @filepath@
5859
-- package by using the 'toString' operation. Operations to convert to and from
5960
-- the 'OsPath' type at zero cost are provided in the @streamly-filepath@
60-
-- package. Zero-cost interconversion is possible because the 'Path' type use
61+
-- package. Zero-cost interconversion is possible because the 'Path' type uses
6162
-- an underlying representation which is compatible with the 'OsPath' type.
6263
--
6364
-- = Path Creation Quasiquoter
@@ -86,11 +87,7 @@ module Streamly.FileSystem.Path
8687
, toChunk
8788
, toChars
8889
, toString
89-
#if !defined(mingw32_HOST_OS) && !defined(__MINGW32__)
90-
, asCString
91-
#else
92-
, asCWString
93-
#endif
90+
, asOsCString
9491

9592
-- * Path Info
9693
, isRooted

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

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -118,42 +118,11 @@
118118
-- always create partial wrappers from these if that is convenient to use.
119119
--
120120

121-
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
122-
-- #define IS_WINDOWS
123-
#define OS_PATH WindowsPath
124-
#define FS_WORD Word16
125-
#define FS_CSTRING CWString
126-
#else
127-
#define OS_PATH PosixPath
128-
#define FS_WORD Word8
129-
#define FS_CSTRING CString
130-
#endif
131-
132-
-- #define IS_PORTABLE
133-
-- #include "Streamly/Internal/FileSystem/PosixPath.hs"
134-
135-
module Streamly.Internal.FileSystem.Path
136-
(
137-
Path
138-
, FsWord
139-
, FsCString
140-
, asFsCString
141-
, module Streamly.Internal.FileSystem.OS_PATH
142-
)
143-
where
144-
145-
import Data.Word (FS_WORD)
146-
import Foreign.C.String (FS_CSTRING)
147-
import Streamly.Internal.FileSystem.OS_PATH
148-
149-
type Path = OS_PATH
150-
type FsWord = FS_WORD
151-
type FsCString = FS_CSTRING
121+
#define IS_PORTABLE
152122

153-
{-# INLINE asFsCString #-}
154-
asFsCString :: OS_PATH -> (FsCString -> IO a) -> IO a
155123
#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
156-
asFsCString = asCWString
124+
#define IS_WINDOWS
125+
#include "Streamly/Internal/FileSystem/WindowsPath.hs"
157126
#else
158-
asFsCString = asCString
127+
#include "Streamly/Internal/FileSystem/PosixPath.hs"
159128
#endif

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

Lines changed: 43 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
{-# LANGUAGE TemplateHaskell #-}
22

3+
#if defined(IS_PORTABLE)
4+
#define OS_PATH_TYPE Path
5+
#define OS_WORD_TYPE OsWord
6+
#define OS_CSTRING_TYPE OsCString
7+
#define AS_OS_CSTRING asOsCString
8+
#elif defined(IS_WINDOWS)
9+
#define OS_PATH_TYPE WindowsPath
10+
#define OS_WORD_TYPE Word16
11+
#define OS_CSTRING_TYPE CWString
12+
#define AS_OS_CSTRING asCWString
13+
#else
14+
#define OS_PATH_TYPE PosixPath
15+
#define OS_WORD_TYPE Word8
16+
#define OS_CSTRING_TYPE CString
17+
#define AS_OS_CSTRING asCString
18+
#endif
19+
320
-- Anything other than windows (Linux/macOS/FreeBSD) is Posix
421
#if defined(IS_WINDOWS)
522
#define OS_NAME Windows
623
#define OS_PATH WindowsPath
7-
#define OS_PATH_TYPE WindowsPath
8-
#define FS_WORD Word16
9-
#define REAL_FS_WORD Word16
10-
#define FS_CSTRING CWString
11-
#define REAL_FS_CSTRING CWString
12-
#define AS_FS_CSTRING asCWString
24+
#define OS_WORD Word16
25+
#define OS_CSTRING CWString
1326
#define UNICODE_ENCODER encodeUtf16le'
1427
#define UNICODE_DECODER decodeUtf16le'
1528
#define UNICODE_DECODER_LAX decodeUtf16le
@@ -18,30 +31,15 @@
1831
#else
1932
#define OS_NAME Posix
2033
#define OS_PATH PosixPath
21-
#define OS_PATH_TYPE PosixPath
22-
#define FS_WORD Word8
23-
#define REAL_FS_WORD Word8
24-
#define FS_CSTRING CString
25-
#define REAL_FS_CSTRING CString
26-
#define AS_FS_CSTRING asCString
34+
#define OS_WORD Word8
35+
#define OS_CSTRING CString
2736
#define UNICODE_ENCODER encodeUtf8'
2837
#define UNICODE_DECODER decodeUtf8'
2938
#define UNICODE_DECODER_LAX decodeUtf8
3039
#define CODEC_NAME UTF-8
3140
#define SEPARATORS @/@
3241
#endif
3342

34-
#if defined(IS_PORTABLE)
35-
#undef OS_PATH_TYPE
36-
#define OS_PATH_TYPE Path
37-
#undef FS_WORD
38-
#define FS_WORD FsWord
39-
#undef FS_CSTRING
40-
#define FS_CSTRING FsCString
41-
#undef AS_FS_CSTRING
42-
#define AS_FS_CSTRING asFsCString
43-
#endif
44-
4543
-- |
4644
-- Module : Streamly.Internal.FileSystem.OS_PATH_TYPE
4745
-- Copyright : (c) 2023 Composewell Technologies
@@ -51,7 +49,7 @@
5149
--
5250
-- This module implements a OS_PATH_TYPE type representing a file system path for
5351
-- OS_NAME operating systems. The only assumption about the encoding of the
54-
-- path is that it maps the characters SEPARATORS and @.@ to FS_WORD
52+
-- path is that it maps the characters SEPARATORS and @.@ to OS_WORD_TYPE
5553
-- representing their ASCII values. Operations are provided to encode and
5654
-- decode using CODEC_NAME encoding.
5755
--
@@ -76,8 +74,8 @@ module Streamly.Internal.FileSystem.OS_PATH_TYPE
7674
(
7775
-- * Type
7876
#if defined(IS_PORTABLE)
79-
FS_WORD
80-
, FS_CSTRING
77+
OS_WORD_TYPE
78+
, OS_CSTRING_TYPE
8179
, OS_PATH_TYPE
8280
#else
8381
OS_PATH_TYPE (..)
@@ -123,7 +121,7 @@ module Streamly.Internal.FileSystem.OS_PATH_TYPE
123121
, toChars
124122
, toChars_
125123
, toString
126-
, AS_FS_CSTRING
124+
, AS_OS_CSTRING
127125
, toString_
128126
, showRaw
129127

@@ -260,7 +258,11 @@ For APIs that have not been released yet.
260258
-- Path components may have limits.
261259
-- Total path length may have a limit.
262260

263-
#if !defined(IS_PORTABLE)
261+
#if defined(IS_PORTABLE)
262+
type OS_PATH_TYPE = OS_PATH
263+
type OS_WORD_TYPE = OS_WORD
264+
type OS_CSTRING_TYPE = OS_CSTRING
265+
#else
264266
-- | A type representing file system paths on OS_NAME.
265267
--
266268
-- A OS_PATH_TYPE is validated before construction unless unsafe constructors are
@@ -270,7 +272,7 @@ For APIs that have not been released yet.
270272
-- Note that in some cases the file system may perform unicode normalization on
271273
-- paths (e.g. Apple HFS), it may cause surprising results as the path used by
272274
-- the user may not have the same bytes as later returned by the file system.
273-
newtype OS_PATH = OS_PATH (Array FS_WORD)
275+
newtype OS_PATH = OS_PATH (Array OS_WORD_TYPE)
274276

275277
-- XXX The Eq instance may be provided but it will require some sensible
276278
-- defaults for comparison. For example, should we use case sensitive or
@@ -281,10 +283,6 @@ instance IsPath OS_PATH OS_PATH where
281283
unsafeFromPath = id
282284
fromPath = pure
283285
toPath = id
284-
#else
285-
type OS_PATH_TYPE = OS_PATH
286-
type FS_WORD = REAL_FS_WORD
287-
type FS_CSTRING = REAL_FS_CSTRING
288286
#endif
289287

290288
-- XXX Use rewrite rules to eliminate intermediate conversions for better
@@ -348,7 +346,7 @@ addTrailingSeparator p = unsafeExtend p sep
348346

349347
-- | Throws an exception if the path is not valid. See 'isValidPath' for the
350348
-- list of validations.
351-
validatePath :: MonadThrow m => Array FS_WORD -> m ()
349+
validatePath :: MonadThrow m => Array OS_WORD_TYPE -> m ()
352350
validatePath = Common.validatePath Common.OS_NAME
353351

354352
#ifndef IS_WINDOWS
@@ -362,7 +360,7 @@ validatePath = Common.validatePath Common.OS_NAME
362360
-- >>> isValid "\0"
363361
-- False
364362
--
365-
isValidPath :: Array FS_WORD -> Bool
363+
isValidPath :: Array OS_WORD_TYPE -> Bool
366364
isValidPath = Common.isValidPath Common.OS_NAME
367365
#endif
368366

@@ -382,7 +380,7 @@ isValidPath = Common.isValidPath Common.OS_NAME
382380
-- per 'isValidPath'.
383381
--
384382
{-# INLINE unsafeFromChunk #-}
385-
unsafeFromChunk :: IsPath OS_PATH_TYPE a => Array FS_WORD -> a
383+
unsafeFromChunk :: IsPath OS_PATH_TYPE a => Array OS_WORD_TYPE -> a
386384
unsafeFromChunk =
387385
#ifndef DEBUG
388386
unsafeFromPath . OS_PATH . Common.unsafeFromChunk
@@ -395,7 +393,7 @@ unsafeFromChunk =
395393
-- | Convert a byte array into a Path.
396394
-- Throws 'InvalidPath' if 'isValidPath' fails on the path.
397395
--
398-
fromChunk :: (MonadThrow m, IsPath OS_PATH_TYPE a) => Array FS_WORD -> m a
396+
fromChunk :: (MonadThrow m, IsPath OS_PATH_TYPE a) => Array OS_WORD_TYPE -> m a
399397
fromChunk arr = Common.fromChunk Common.OS_NAME arr >>= fromPath . OS_PATH
400398

401399
-- XXX Should be a Fold instead?
@@ -423,7 +421,7 @@ fromChars s =
423421

424422
-- | Create a raw path i.e. an array representing the path. Note that the path
425423
-- is not validated, therefore, it may not be valid according to 'isValidPath'.
426-
rawFromString :: [Char] -> Array FS_WORD
424+
rawFromString :: [Char] -> Array OS_WORD_TYPE
427425
rawFromString =
428426
Common.unsafeFromChars Unicode.UNICODE_ENCODER
429427
. Stream.fromList
@@ -504,7 +502,7 @@ path = mkQ pathE
504502
-- XXX unPath?
505503

506504
-- | Convert the path to an array.
507-
toChunk :: IsPath OS_PATH_TYPE a => a -> Array FS_WORD
505+
toChunk :: IsPath OS_PATH_TYPE a => a -> Array OS_WORD_TYPE
508506
toChunk p = let OS_PATH arr = toPath p in arr
509507

510508
-- | Decode the path to a stream of Unicode chars using strict CODEC_NAME decoding.
@@ -566,15 +564,15 @@ instance Show OS_PATH where
566564
#ifndef IS_WINDOWS
567565
-- | Use the path as a pinned CString. Useful for using a PosixPath in
568566
-- system calls on Posix.
569-
{-# INLINE AS_FS_CSTRING #-}
570-
AS_FS_CSTRING :: OS_PATH_TYPE -> (FS_CSTRING -> IO a) -> IO a
571-
AS_FS_CSTRING p = Array.asCStringUnsafe (toChunk p)
567+
{-# INLINE AS_OS_CSTRING #-}
568+
AS_OS_CSTRING :: OS_PATH_TYPE -> (OS_CSTRING_TYPE -> IO a) -> IO a
569+
AS_OS_CSTRING p = Array.asCStringUnsafe (toChunk p)
572570
#else
573571
-- | Use the path as a pinned CWString. Useful for using a WindowsPath in
574572
-- system calls on Windows.
575-
{-# INLINE AS_FS_CSTRING #-}
576-
AS_FS_CSTRING :: OS_PATH_TYPE -> (FS_CSTRING -> IO a) -> IO a
577-
AS_FS_CSTRING p = Array.asCWString (toChunk p)
573+
{-# INLINE AS_OS_CSTRING #-}
574+
AS_OS_CSTRING :: OS_PATH_TYPE -> (OS_CSTRING_TYPE -> IO a) -> IO a
575+
AS_OS_CSTRING p = Array.asCWString (toChunk p)
578576
#endif
579577

580578
------------------------------------------------------------------------------

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,21 +146,21 @@ For APIs that have not been released yet.
146146
-- True
147147
-- >>> isValid "\\\\??\\x"
148148
-- True
149-
isValidPath :: Array FS_WORD -> Bool
149+
isValidPath :: Array OS_WORD_TYPE -> Bool
150150
isValidPath = Common.isValidPath Common.OS_NAME
151151

152152
-- | Like 'validatePath' on but more strict. A share root must be followed by a
153153
-- non-empty path. Thus "\/\/x\/" is not considered a valid path.
154154
validatePath' ::
155-
MonadThrow m => Array FS_WORD -> m ()
155+
MonadThrow m => Array OS_WORD_TYPE -> m ()
156156
validatePath' = Common.validatePath' Common.Windows
157157

158158
-- | Like 'isValidPath' but more strict, see validatePath' for differences.
159159
isValidPath' ::
160-
Array FS_WORD -> Bool
160+
Array OS_WORD_TYPE -> Bool
161161
isValidPath' = Common.isValidPath' Common.Windows
162162

163-
-- | Read a raw array of FS_WORD as a path type.
163+
-- | Read a raw array of OS_WORD_TYPE as a path type.
164164
--
165165
-- >>> readRaw = fromJust . Path.fromChunk . read
166166
--

0 commit comments

Comments
 (0)