{-# LANGUAGE OverloadedStrings #-}
module HsLua.Module.Path (
documentedModule
, separator
, search_path_separator
, add_extension
, combine
, directory
, filename
, is_absolute
, is_relative
, join
, make_relative
, normalize
, split
, split_extension
, split_search_path
, treat_strings_as_paths
)
where
import Data.Text (Text)
import Data.Version (Version, makeVersion)
import HsLua.Core
( LuaError, getglobal, getmetatable, nth, pop, rawset, remove, top )
import HsLua.Marshalling
( Peeker, peekList, peekString, pushList, pushName, pushString )
import HsLua.Packaging
import qualified Data.Text as T
import qualified System.FilePath as Path
documentedModule :: LuaError e => Module e
documentedModule :: forall e. LuaError e => Module e
documentedModule = Module
{ moduleName :: Name
moduleName = Name
"path"
, moduleDescription :: Text
moduleDescription = Text
"Module for file path manipulations."
, moduleFields :: [Field e]
moduleFields = forall e. [Field e]
fields
, moduleFunctions :: [DocumentedFunction e]
moduleFunctions = forall e. LuaError e => [DocumentedFunction e]
functions
, moduleOperations :: [(Operation, DocumentedFunction e)]
moduleOperations = []
, moduleTypeInitializers :: [LuaE e Name]
moduleTypeInitializers = []
}
fields :: [Field e]
fields :: forall e. [Field e]
fields =
[ forall e. Field e
separator
, forall e. Field e
search_path_separator
]
separator :: Field e
separator :: forall e. Field e
separator = Field
{ fieldName :: Text
fieldName = Text
"separator"
, fieldType :: TypeSpec
fieldType = TypeSpec
"string"
, fieldDescription :: Text
fieldDescription = Text
"The character that separates directories."
, fieldPushValue :: LuaE e ()
fieldPushValue = forall e. FilePath -> LuaE e ()
pushString [Char
Path.pathSeparator]
}
search_path_separator :: Field e
search_path_separator :: forall e. Field e
search_path_separator = Field
{ fieldName :: Text
fieldName = Text
"search_path_separator"
, fieldType :: TypeSpec
fieldType = TypeSpec
"string"
, fieldDescription :: Text
fieldDescription = Text
"The character that is used to separate the entries in "
forall a. Semigroup a => a -> a -> a
<> Text
"the `PATH` environment variable."
, fieldPushValue :: LuaE e ()
fieldPushValue = forall e. FilePath -> LuaE e ()
pushString [Char
Path.searchPathSeparator]
}
functions :: LuaError e => [DocumentedFunction e]
functions :: forall e. LuaError e => [DocumentedFunction e]
functions =
[ forall e. DocumentedFunction e
directory
, forall e. DocumentedFunction e
filename
, forall e. DocumentedFunction e
is_absolute
, forall e. DocumentedFunction e
is_relative
, forall e. LuaError e => DocumentedFunction e
join
, forall e. DocumentedFunction e
make_relative
, forall e. DocumentedFunction e
normalize
, forall e. LuaError e => DocumentedFunction e
split
, forall e. DocumentedFunction e
split_extension
, forall e. LuaError e => DocumentedFunction e
split_search_path
, forall e. LuaError e => DocumentedFunction e
treat_strings_as_paths
]
directory :: DocumentedFunction e
directory :: forall e. DocumentedFunction e
directory = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"directory"
### liftPure Path.takeDirectory
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"The filepath up to the last directory separator."
#? ("Gets the directory name, i.e., removes the last directory " <>
"separator and everything after from the given path.")
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
filename :: DocumentedFunction e
filename :: forall e. DocumentedFunction e
filename = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"filename"
### liftPure Path.takeFileName
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"File name part of the input path."
#? "Get the file name."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
is_absolute :: DocumentedFunction e
is_absolute :: forall e. DocumentedFunction e
is_absolute = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"is_absolute"
### liftPure Path.isAbsolute
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e Bool
boolResult (Text
"`true` iff `filepath` is an absolute path, " forall a. Semigroup a => a -> a -> a
<>
Text
"`false` otherwise.")
#? "Checks whether a path is absolute, i.e. not fixed to a root."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
is_relative :: DocumentedFunction e
is_relative :: forall e. DocumentedFunction e
is_relative = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"is_relative"
### liftPure Path.isRelative
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e Bool
boolResult (Text
"`true` iff `filepath` is a relative path, " forall a. Semigroup a => a -> a -> a
<>
Text
"`false` otherwise.")
#? "Checks whether a path is relative or fixed to a root."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
join :: LuaError e => DocumentedFunction e
join :: forall e. LuaError e => DocumentedFunction e
join = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"join"
### liftPure Path.joinPath
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
parameter (forall a e. LuaError e => Peeker e a -> Peeker e [a]
peekList forall e. Peeker e FilePath
peekFilePath) TypeSpec
"{string,...}"
Text
"filepaths" Text
"path components"
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"The joined path."
#? "Join path elements back together by the directory separator."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
make_relative :: DocumentedFunction e
make_relative :: forall e. DocumentedFunction e
make_relative = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"make_relative"
### liftPure3 makeRelative
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
parameter
forall e. Peeker e FilePath
peekFilePath
TypeSpec
"string"
Text
"path"
Text
"path to be made relative"
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
parameter
forall e. Peeker e FilePath
peekFilePath
TypeSpec
"string"
Text
"root"
Text
"root path"
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e a. Parameter e a -> Parameter e (Maybe a)
opt (forall e. Text -> Text -> Parameter e Bool
boolParam Text
"unsafe" Text
"whether to allow `..` in the result.")
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"contracted filename"
#? mconcat
[ "Contract a filename, based on a relative path. Note that the "
, "resulting path will never introduce `..` paths, as the "
, "presence of symlinks means `../b` may not reach `a/b` if it "
, "starts from `a/c`. For a worked example see "
, "[this blog post](http://neilmitchell.blogspot.co.uk"
, "/2015/10/filepaths-are-subtle-symlinks-are-hard.html)."
]
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
normalize :: DocumentedFunction e
normalize :: forall e. DocumentedFunction e
normalize = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"normalize"
### liftPure Path.normalise
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"The normalized path."
#? T.unlines
[ "Normalizes a path."
, ""
, " - `//` makes sense only as part of a (Windows) network drive;"
, " elsewhere, multiple slashes are reduced to a single"
, " `path.separator` (platform dependent)."
, " - `/` becomes `path.separator` (platform dependent)."
, " - `./` is removed."
, " - an empty path becomes `.`"
]
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
split :: LuaError e => DocumentedFunction e
split :: forall e. LuaError e => DocumentedFunction e
split = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"split"
### liftPure Path.splitDirectories
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. LuaError e => Text -> FunctionResults e [FilePath]
filepathListResult Text
"List of all path components."
#? "Splits a path by the directory separator."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
split_extension :: DocumentedFunction e
split_extension :: forall e. DocumentedFunction e
split_extension = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"split_extension"
### liftPure Path.splitExtension
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> (forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
functionResult (forall e. FilePath -> LuaE e ()
pushString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> a
fst) TypeSpec
"string" Text
"filepath without extension"
forall a. [a] -> [a] -> [a]
++
forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
functionResult (forall e. FilePath -> LuaE e ()
pushString forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. (a, b) -> b
snd) TypeSpec
"string" Text
"extension or empty string")
#? ("Splits the last extension from a file path and returns the parts. "
<> "The extension, if present, includes the leading separator; "
<> "if the path has no extension, then the empty string is returned "
<> "as the extension.")
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
split_search_path :: LuaError e => DocumentedFunction e
split_search_path :: forall e. LuaError e => DocumentedFunction e
split_search_path = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"split_search_path"
### liftPure Path.splitSearchPath
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> Parameter
{ parameterPeeker :: Peeker e FilePath
parameterPeeker = forall e. Peeker e FilePath
peekString
, parameterDoc :: ParameterDoc
parameterDoc = ParameterDoc
{ parameterName :: Text
parameterName = Text
"search_path"
, parameterType :: TypeSpec
parameterType = TypeSpec
"string"
, parameterDescription :: Text
parameterDescription = Text
"platform-specific search path"
, parameterIsOptional :: Bool
parameterIsOptional = Bool
False
}
}
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. LuaError e => Text -> FunctionResults e [FilePath]
filepathListResult Text
"list of directories in search path"
#? ("Takes a string and splits it on the `search_path_separator` "
<> "character. Blank items are ignored on Windows, "
<> "and converted to `.` on Posix. "
<> "On Windows path elements are stripped of quotes.")
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
combine :: DocumentedFunction e
combine :: forall e. DocumentedFunction e
combine = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"combine"
### liftPure2 Path.combine
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"combined paths"
#? "Combine two paths with a path separator."
add_extension :: DocumentedFunction e
add_extension :: forall e. DocumentedFunction e
add_extension = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"add_extension"
### liftPure2 Path.addExtension
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> forall e. Parameter e FilePath
filepathParam
forall e a b.
HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
<#> Parameter
{ parameterPeeker :: Peeker e FilePath
parameterPeeker = forall e. Peeker e FilePath
peekString
, parameterDoc :: ParameterDoc
parameterDoc = ParameterDoc
{ parameterName :: Text
parameterName = Text
"extension"
, parameterType :: TypeSpec
parameterType = TypeSpec
"string"
, parameterDescription :: Text
parameterDescription = Text
"an extension, with or without separator dot"
, parameterIsOptional :: Bool
parameterIsOptional = Bool
False
}
}
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> forall e. Text -> FunctionResults e FilePath
filepathResult Text
"filepath with extension"
#? "Adds an extension, even if there is already one."
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
stringAugmentationFunctions :: LuaError e => [DocumentedFunction e]
stringAugmentationFunctions :: forall e. LuaError e => [DocumentedFunction e]
stringAugmentationFunctions =
[ forall e. DocumentedFunction e
directory
, forall e. DocumentedFunction e
filename
, forall e. DocumentedFunction e
is_absolute
, forall e. DocumentedFunction e
is_relative
, forall e. DocumentedFunction e
normalize
, forall e. LuaError e => DocumentedFunction e
split
, forall e. DocumentedFunction e
split_extension
, forall e. LuaError e => DocumentedFunction e
split_search_path
]
treat_strings_as_paths :: LuaError e => DocumentedFunction e
treat_strings_as_paths :: forall e. LuaError e => DocumentedFunction e
treat_strings_as_paths = forall a e. Name -> a -> HsFnPrecursor e a
defun Name
"treat_strings_as_paths"
### do let addFunction fn = do
pushName (functionName fn)
pushDocumentedFunction fn
rawset (nth 3)
pushString "" *> getmetatable top *> remove (nth 2)
mapM_ addFunction
[setName "__add" add_extension, setName "__div" combine]
pop 1
_ <- getglobal "string"
mapM_ addFunction stringAugmentationFunctions
pop 1
forall e a.
HsFnPrecursor e (LuaE e a)
-> FunctionResults e a -> DocumentedFunction e
=#> []
#? ("Augment the string module such that strings can be used as "
<> "path objects.")
forall e. DocumentedFunction e -> Version -> DocumentedFunction e
`since` Version
initialVersion
peekFilePath :: Peeker e FilePath
peekFilePath :: forall e. Peeker e FilePath
peekFilePath = forall e. Peeker e FilePath
peekString
filepathParam :: Parameter e FilePath
filepathParam :: forall e. Parameter e FilePath
filepathParam = forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
parameter forall e. Peeker e FilePath
peekFilePath TypeSpec
"string" Text
"filepath" Text
"path"
filepathResult :: Text
-> FunctionResults e FilePath
filepathResult :: forall e. Text -> FunctionResults e FilePath
filepathResult = forall e. Text -> FunctionResults e FilePath
stringResult
filepathListResult :: LuaError e
=> Text
-> FunctionResults e [FilePath]
filepathListResult :: forall e. LuaError e => Text -> FunctionResults e [FilePath]
filepathListResult = forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
functionResult (forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
pushList forall e. FilePath -> LuaE e ()
pushString) TypeSpec
"{string,...}"
makeRelative :: FilePath
-> FilePath
-> Maybe Bool
-> FilePath
makeRelative :: FilePath -> FilePath -> Maybe Bool -> FilePath
makeRelative FilePath
path FilePath
root (Just Bool
True)
| FilePath -> FilePath -> Bool
Path.equalFilePath FilePath
root FilePath
path = FilePath
"."
| FilePath -> FilePath
Path.takeDrive FilePath
root forall a. Eq a => a -> a -> Bool
/= FilePath -> FilePath
Path.takeDrive FilePath
path = FilePath
path
| Bool
otherwise =
let toParts :: FilePath -> [FilePath]
toParts = FilePath -> [FilePath]
Path.splitDirectories forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> FilePath
Path.normalise
go :: [FilePath] -> [FilePath] -> FilePath
go (FilePath
pp:[FilePath]
pps) (FilePath
rp:[FilePath]
rps)
| FilePath
pp forall a. Eq a => a -> a -> Bool
== FilePath
rp = [FilePath] -> [FilePath] -> FilePath
go [FilePath]
pps [FilePath]
rps
go [FilePath]
pps [FilePath]
rps
= [FilePath] -> FilePath
Path.joinPath forall a b. (a -> b) -> a -> b
$ forall a. Int -> a -> [a]
replicate (forall (t :: * -> *) a. Foldable t => t a -> Int
length [FilePath]
rps) FilePath
".." forall a. [a] -> [a] -> [a]
++ [FilePath]
pps
in [FilePath] -> [FilePath] -> FilePath
go (FilePath -> [FilePath]
toParts FilePath
path) (FilePath -> [FilePath]
toParts FilePath
root)
makeRelative FilePath
path FilePath
root Maybe Bool
_unsafe = FilePath -> FilePath -> FilePath
Path.makeRelative FilePath
root FilePath
path
initialVersion :: Version
initialVersion :: Version
initialVersion = [Int] -> Version
makeVersion [Int
0,Int
1,Int
0]