{-# LANGUAGE CPP #-} -- | -- Module : Streamly.FileSystem.Path -- Copyright : (c) 2023 Composewell Technologies -- License : BSD3 -- Maintainer : streamly@composewell.com -- Portability : GHC -- -- File system paths that are extensible, high-performance and preserve the OS -- and filesystem encoding. -- -- The 'Path' type is built on top of Streamly's 'Array' type, leveraging all -- its operations — including support for both pinned and unpinned -- representations. The API integrates with streams, prioritizes safety, -- flexibility, and performance. It supports configurable equality for -- cross-platform compatibility and user-defined path matching. It is designed -- for extensibility and fine-grained type safety as well. For type-safe -- adaptations, see the "Streamly.Internal.FileSystem.Path.*" modules. -- -- 'Path' is interconvertible with the 'OsPath' type from the @filepath@ -- package at zero runtime cost. While the API is mostly compatible with that -- of the @filepath@ package, some differences exist due to a slightly -- different design philosophy focused on better safety. -- -- = Rooted vs Unrooted Paths -- -- To ensure the safety of the path append operation, we distinguish between -- rooted paths and free path segments or unrooted paths. A path that starts -- from an explicit or implicit file system root is called a rooted path or an -- anchored path. For example, @\/usr\/bin@ is a rooted path with @/@ as an -- explicit root directory. Similarly, @.\/bin@ is a rooted path with the -- current directoy \".\" as an implicit root. A path that is not rooted is -- called an unrooted path or unanchored path; for example, @local\/bin@ is an -- unrooted path. -- -- This distinction ensures the safety of the path append operation. You can -- append only an unrooted path to another path, it does not make sense to -- append a rooted path to another path. The default append operation in the -- Path module checks for this and fails if the operation is invalid. -- -- Rooted vs unrooted distinction is a stricter form of relative vs absolute -- path distinction. In this model, for better safety, paths relative to the -- current directory are also treated in the same way as absolute paths, from -- the perspective of a path append operation. This is because the meaning of -- current directory is context dependent and dynamic, therefore, appending it -- to another path is not allowed. Only unrooted path segments (e.g. -- @local/bin@) can be appended to any other path using safe operations. -- -- = File vs. Directory Paths -- -- By default, a path with a trailing separator (e.g. @local/@) is implicitly -- considered a directory path. However, the absence of a trailing separator -- does not indicate whether the path is a file or a directory — it could be -- either. Therefore, when using the @Path@ type, the append operation allows -- appending to paths even if they lack a trailing separator. -- -- = Compatibility with the filepath package -- -- Any path type can be converted to the 'FilePath' type from the @filepath@ -- package by using the 'toString' operation. Operations to convert to and from -- the 'OsPath' type at zero cost are provided in the @streamly-filepath@ -- package. Zero-cost interconversion is possible because the 'Path' type uses -- an underlying representation which is compatible with the 'OsPath' type. -- -- = Path Creation Quasiquoter -- -- The 'path' quasiquoter is useful in creating valid paths that are checked -- during the compile time. module Streamly.FileSystem.Path ( -- * Setup -- | To execute the code examples provided in this module in ghci, please -- run the following commands first. -- -- $setup -- * Type Path , OsWord -- * Construction , validatePath , fromArray , fromString , fromString_ -- * Statically Verified String Literals -- | Quasiquoters. , path -- * Statically Verified Strings -- | Template Haskell expression splices. , pathE -- * Elimination , toArray -- , toChars -- need fromChars as well , toString -- , asOsCString -- * Path Info , isRooted , isUnrooted -- * Joining , unsafeJoin , join , joinStr -- * Splitting root , splitRoot -- * Splitting path components , splitPath -- , splitPath_ -- * Splitting file extension , splitExtension , takeExtension , dropExtension -- , addExtension -- , replaceExtension -- * Splitting file and dir , splitFile , takeFileName , takeDirectory , takeFileBase -- * Equality , EqCfg , ignoreCase , ignoreTrailingSeparators , allowRelativeEquality , eqPath ) where {- Documentation on typed paths. We can add this back into the module level documentation when we introduce the typed paths. -- = Rooted Paths vs Branches -- -- /Flexible typing/: you can choose the level of type safety you want. 'Path' -- is the basic path type which can represent a file, directory, absolute or -- relative path with no restrictions. Depending on how much type safety you -- want, you can choose appropriate type wrappers or a combination of those to -- wrap the 'Path' type in stricter types. -- The "Streamly.FileSystem.Path.Seg" module provides explicit types for path -- segments, distinguishing rooted paths from branches. Rooted paths use the -- @Rooted Path@ type, and branches use the @Branch Path@ type. If you use the -- generic 'Path' type, append may fail at run time if you attempt to append -- a rooted path to another rooted path. In contrast, using the @Rooted Path@ -- and @Branch Path@ types guarantees compile-time safety, preventing such errors. -- = File vs. Directory Paths -- -- Independent of the rooted or branch distinction, you can also make a -- type-level distinction between file and directory nodes using the -- "Streamly.FileSystem.Path.Node" module. The type @File Path@ represents a -- file, whereas @Dir Path@ represents a directory. This distinction provides -- safety against appending to file type paths — append operations are not -- allowed on paths of type 'File'. -- = Flexible Typing -- -- You can use the 'Rooted', 'Branch', 'Dir', and 'File' types independently by -- importing only the required modules. If you want both types of distinctions, -- you can use them together via the "Streamly.FileSystem.Path.SegNode" module. -- For example, @Rooted (Dir Path)@ represents a rooted path that is a -- directory. You can append other paths only to paths that have a 'Dir' type, -- and only a path of type 'Branch' can be appended. -- -- You may choose to use the basic 'Path' type or any combination of the safer -- types. You can upgrade or downgrade the safety level by converting between -- types using the @adapt@ operation. When converting from a less restrictive -- type to a more restrictive one, run-time checks are performed, and the -- conversion may fail. However, converting from a more restrictive type to a -- less restrictive one is always allowed. -- -- = Extensibility -- -- You can define your own newtype wrappers similar to 'File' or 'Dir' to -- provide custom restrictions if you want. -- -} import Streamly.Internal.FileSystem.Path #include "DocTestFileSystemPath.hs"