streamly-core-0.3.0: src/Streamly/FileSystem/Path.hs
{-# 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"