    {-# LANGUAGE CPP #-}
    {-|
    Module      : HsLua.Module.System
    Copyright   : © 2019-2026 Albert Krewinkel
    License     : MIT
    Maintainer  : Albert Krewinkel <tarleb@hslua.org>
    Stability   : alpha
    Portability : Requires GHC 8 or later.

    Provide a Lua module containing a selection of @'System'@ functions.
    -}
    module HsLua.Module.System (

      -- * Module
        documentedModule

      -- ** Fields
      , arch
      , compiler_name
      , compiler_version
      , os

      -- ** Functions
      , cmd
      , cp
      , cputime
      , env
      , exists
      , getenv
      , getwd
      , ls
      , mkdir
      , read_file
      , rename
      , rm
      , rmdir
      , setenv
      , setwd
      , times
      , tmpdirname
      , with_env
      , with_tmpdir
      , with_wd
      , write_file
      , xdg
      )
    where

    import Control.Monad ((>=>), forM_)
    import Control.Monad.Catch (bracket)
    import Data.Maybe (fromMaybe)
    import Data.Text (Text)
    import Data.Version (versionBranch)
    import HsLua.Core
    import HsLua.List (newListMetatable)
    import HsLua.Marshalling
    import HsLua.Packaging
    import HsLua.Module.SystemUtils

    import qualified Data.ByteString as B
    import qualified Data.Text as T
    import qualified Data.Time as Time
    import qualified Data.Time.Format.ISO8601 as ISO8601
    import qualified HsLua.Core.Utf8 as Utf8
    import qualified System.CPUTime as CPUTime
    import qualified System.Directory as Directory
    import qualified System.Environment as Env
    import qualified System.Exit as Exit
    import qualified System.Info as Info
    import qualified System.IO.Temp as Temp
    import qualified System.Process as Process

    -- | The "system" module.
    documentedModule :: LuaError e => Module e
    documentedModule :: forall e. LuaError e => Module e
    documentedModule = Name -> Module e
    forall e. Name -> Module e
    defmodule Name
    "system"
      Module e -> [Field e] -> Module e
    forall e. Module e -> [Field e] -> Module e
    `withFields`
          [ Field e
    forall e. Field e
    arch
          , Field e
    forall e. Field e
    compiler_name
          , Field e
    forall e. LuaError e => Field e
    compiler_version
          , Field e
    forall e. Field e
    cputime_precision
          , Field e
    forall e. Field e
    os
          ]
      Module e -> [DocumentedFunction e] -> Module e
    forall e. Module e -> [DocumentedFunction e] -> Module e
    `withFunctions`
          [ DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    cmd
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    cp
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    cputime
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    env
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    exists
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    getenv
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    getwd
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    ls
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    mkdir
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    read_file
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    rename
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    rm
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    rmdir
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    setenv
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    setwd
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    times
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    tmpdirname
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    with_env
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    with_tmpdir
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    with_wd
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    write_file
          , DocumentedFunction e
    forall e. LuaError e => DocumentedFunction e
    xdg
          ]
      Module e -> Text -> Module e
    forall a. HasDescription a => a -> Text -> a
    `withDescription`
          Text
    "Access to the system's information and file functionality."

    --
    -- Fields
    --

    -- | Module field containing the machine architecture on which the
    -- program is running. Wraps @'Info.arch'@
    arch :: Field e
    arch :: forall e. Field e
    arch = Name -> Field e
    forall e. Name -> Field e
    deffield Name
    "arch"
      Field e -> TypeSpec -> Field e
    forall e. Field e -> TypeSpec -> Field e
    `withType`
        TypeSpec
    stringType
      Field e -> Text -> Field e
    forall a. HasDescription a => a -> Text -> a
    `withDescription`
        Text
    "The machine architecture on which the program is running."
      Field e -> LuaE e () -> Field e
    forall e. Field e -> LuaE e () -> Field e
    `withValue`
        String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString String
    Info.arch

    -- | Module field containing the Haskell implementation with which the
    -- host program was compiled. Wraps @'Info.compilerName'@.
    compiler_name :: Field e
    compiler_name :: forall e. Field e
    compiler_name = Name -> Field e
    forall e. Name -> Field e
    deffield Name
    "compiler_name"
      Field e -> TypeSpec -> Field e
    forall e. Field e -> TypeSpec -> Field e
    `withType`
        TypeSpec
    stringType
      Field e -> Text -> Field e
    forall a. HasDescription a => a -> Text -> a
    `withDescription`
        Text
    "The Haskell implementation with which the host program was compiled."
      Field e -> LuaE e () -> Field e
    forall e. Field e -> LuaE e () -> Field e
    `withValue`
        String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString String
    Info.compilerName

    -- | Module field containing the version of `compiler_name` with which
    -- the host program was compiled.
    compiler_version :: LuaError e => Field e
    compiler_version :: forall e. LuaError e => Field e
    compiler_version = Name -> Field e
    forall e. Name -> Field e
    deffield Name
    "compiler_version"
      Field e -> TypeSpec -> Field e
    forall e. Field e -> TypeSpec -> Field e
    `withType`
        TypeSpec
    stringType
      Field e -> Text -> Field e
    forall a. HasDescription a => a -> Text -> a
    `withDescription`
        Text
    "The Haskell implementation with which the host program was compiled."
      Field e -> LuaE e () -> Field e
    forall e. Field e -> LuaE e () -> Field e
    `withValue`
        Pusher e Int -> [Int] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e Int
    forall a e. (Integral a, Show a) => a -> LuaE e ()
    pushIntegral (Version -> [Int]
    versionBranch Version
    Info.compilerVersion)

    -- | Field containing the smallest measurable difference in CPU time.
    cputime_precision :: Field e
    cputime_precision :: forall e. Field e
    cputime_precision = Name -> Field e
    forall e. Name -> Field e
    deffield Name
    "cputime_precision"
      Field e -> TypeSpec -> Field e
    forall e. Field e -> TypeSpec -> Field e
    `withType` TypeSpec
    integerType
      Field e -> Text -> Field e
    forall a. HasDescription a => a -> Text -> a
    `withDescription` [Text] -> Text
    T.unlines
          [ Text
    "The smallest measurable difference in CPU time that the"
          , Text
    "implementation can record, and is given as an integral number of"
          , Text
    "picoseconds."
          ]
      Field e -> LuaE e () -> Field e
    forall e. Field e -> LuaE e () -> Field e
    `withValue` Integer -> LuaE e ()
    forall a e. (Integral a, Show a) => a -> LuaE e ()
    pushIntegral Integer
    CPUTime.cpuTimePrecision

    -- | Field containing the operating system on which the program is
    -- running.
    os :: Field e
    os :: forall e. Field e
    os = Name -> Field e
    forall e. Name -> Field e
    deffield Name
    "os"
      Field e -> TypeSpec -> Field e
    forall e. Field e -> TypeSpec -> Field e
    `withType` TypeSpec
    "string"
      Field e -> Text -> Field e
    forall a. HasDescription a => a -> Text -> a
    `withDescription` [Text] -> Text
    T.unlines
        [ Text
    "The operating system on which the program is running."
        , Text
    "The most common values are `darwin` (macOS), `freebsd`, `linux`,"
        , Text
    "`linux-android`, `mingw32` (Windows), `netbsd`, `openbsd`."
        ]
      Field e -> LuaE e () -> Field e
    forall e. Field e -> LuaE e () -> Field e
    `withValue` String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString String
    Info.os

    --
    -- Functions
    --

    -- | Run a system command
    cmd :: LuaError e => DocumentedFunction e
    cmd :: forall e. LuaError e => DocumentedFunction e
    cmd = Name
    -> (String
        -> [String]
        -> Maybe String
        -> Maybe ProcessOpts
        -> LuaE e (ExitCode, String, String))
    -> HsFnPrecursor
         e
         (String
          -> [String]
          -> Maybe String
          -> Maybe ProcessOpts
          -> LuaE e (ExitCode, String, String))
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "cmd"
      ### (\command args minput opts -> do
              let input = fromMaybe "" minput
              let cp_opts = (Process.proc command args)
                            { Process.env = processOptsEnv =<< opts
                            , Process.cwd = processOptsCwd =<< opts
                            }
              liftIO $ Process.readCreateProcessWithExitCode cp_opts input)
      HsFnPrecursor
      e
      (String
       -> [String]
       -> Maybe String
       -> Maybe ProcessOpts
       -> LuaE e (ExitCode, String, String))
    -> Parameter e String
    -> HsFnPrecursor
         e
         ([String]
          -> Maybe String
          -> Maybe ProcessOpts
          -> LuaE e (ExitCode, String, String))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "command" Text
    "command to execute"
      HsFnPrecursor
      e
      ([String]
       -> Maybe String
       -> Maybe ProcessOpts
       -> LuaE e (ExitCode, String, String))
    -> Parameter e [String]
    -> HsFnPrecursor
         e
         (Maybe String
          -> Maybe ProcessOpts -> LuaE e (ExitCode, String, String))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e [String]
    -> TypeSpec -> Text -> Text -> Parameter e [String]
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter (Peeker e String -> Peeker e [String]
    forall a e. LuaError e => Peeker e a -> Peeker e [a]
    peekList Peeker e String
    forall e. Peeker e String
    peekString) TypeSpec
    "{string,...}" Text
    "args" Text
    "command arguments"
      HsFnPrecursor
      e
      (Maybe String
       -> Maybe ProcessOpts -> LuaE e (ExitCode, String, String))
    -> Parameter e (Maybe String)
    -> HsFnPrecursor
         e (Maybe ProcessOpts -> LuaE e (ExitCode, String, String))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e String -> Parameter e (Maybe String)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Peeker e String -> TypeSpec -> Text -> Text -> Parameter e String
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e String
    forall e. Peeker e String
    peekString TypeSpec
    "string" Text
    "input" Text
    "input on stdin")
      HsFnPrecursor
      e (Maybe ProcessOpts -> LuaE e (ExitCode, String, String))
    -> Parameter e (Maybe ProcessOpts)
    -> HsFnPrecursor e (LuaE e (ExitCode, String, String))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e ProcessOpts -> Parameter e (Maybe ProcessOpts)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Peeker e ProcessOpts
    -> TypeSpec -> Text -> Text -> Parameter e ProcessOpts
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e ProcessOpts
    forall e. LuaError e => Peeker e ProcessOpts
    peekProcessOptions TypeSpec
    "table" Text
    "opts" Text
    "process options")
      HsFnPrecursor e (LuaE e (ExitCode, String, String))
    -> FunctionResults e (ExitCode, String, String)
    -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> (Pusher e (ExitCode, String, String)
    -> TypeSpec -> Text -> FunctionResults e (ExitCode, String, String)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (Pusher e ExitCode
    forall e. Pusher e ExitCode
    pushExitCode Pusher e ExitCode
    -> ((ExitCode, String, String) -> ExitCode)
    -> Pusher e (ExitCode, String, String)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (\(ExitCode
    a,String
    _,String
    _) -> ExitCode
    a)) TypeSpec
    "integer|boolean"
                          Text
    "exit code – `false` on success, an integer otherwise" FunctionResults e (ExitCode, String, String)
    -> FunctionResults e (ExitCode, String, String)
    -> FunctionResults e (ExitCode, String, String)
    forall a. Semigroup a => a -> a -> a
    <>
           Pusher e (ExitCode, String, String)
    -> TypeSpec -> Text -> FunctionResults e (ExitCode, String, String)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString (String -> LuaE e ())
    -> ((ExitCode, String, String) -> String)
    -> Pusher e (ExitCode, String, String)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . \(ExitCode
    _,String
    b,String
    _) -> String
    b) TypeSpec
    "string" Text
    "stdout" FunctionResults e (ExitCode, String, String)
    -> FunctionResults e (ExitCode, String, String)
    -> FunctionResults e (ExitCode, String, String)
    forall a. Semigroup a => a -> a -> a
    <>
           Pusher e (ExitCode, String, String)
    -> TypeSpec -> Text -> FunctionResults e (ExitCode, String, String)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString (String -> LuaE e ())
    -> ((ExitCode, String, String) -> String)
    -> Pusher e (ExitCode, String, String)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . \(ExitCode
    _,String
    _,String
    c) -> String
    c) TypeSpec
    "string" Text
    "stderr")
      #? T.unlines
         [ "Executes a system command with the given arguments and `input`"
         , "on *stdin*."
         ]

    -- | Copy a file
    cp :: LuaError e => DocumentedFunction e
    cp :: forall e. LuaError e => DocumentedFunction e
    cp = Name
    -> (String -> String -> LuaE e ())
    -> HsFnPrecursor e (String -> String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "cp"
      ### (\src tgt -> ioToLua $ Directory.copyFile src tgt)
      HsFnPrecursor e (String -> String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (String -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "source" Text
    "source file"
      HsFnPrecursor e (String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "target" Text
    "target destination"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? T.unlines
         [ "Copy a file with its permissions."
         , "If the destination file already exists, it is overwritten."
         ]

    -- | Access the CPU time, e.g. for benchmarking.
    cputime :: LuaError e => DocumentedFunction e
    cputime :: forall e. LuaError e => DocumentedFunction e
    cputime = Name -> LuaE e Integer -> HsFnPrecursor e (LuaE e Integer)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "cputime"
      ### ioToLua CPUTime.getCPUTime
      HsFnPrecursor e (LuaE e Integer)
    -> FunctionResults e Integer -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e Integer -> TypeSpec -> Text -> FunctionResults e Integer
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e Integer
    forall a e. (Integral a, Show a) => a -> LuaE e ()
    pushIntegral TypeSpec
    "integer" Text
    "CPU time in picoseconds"
      #? T.unlines
         [ "Returns the number of picoseconds CPU time used by the current"
         , "program. The precision of this result may vary in different"
         , "versions and on different platforms."
         ]

    -- | Retrieve the entire environment
    env :: LuaError e => DocumentedFunction e
    env :: forall e. LuaError e => DocumentedFunction e
    env = Name
    -> LuaE e [(String, String)]
    -> HsFnPrecursor e (LuaE e [(String, String)])
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "env"
      ### ioToLua Env.getEnvironment
      HsFnPrecursor e (LuaE e [(String, String)])
    -> FunctionResults e [(String, String)] -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e [(String, String)]
    -> TypeSpec -> Text -> FunctionResults e [(String, String)]
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (Pusher e String -> Pusher e String -> Pusher e [(String, String)]
    forall e a b.
    LuaError e =>
    Pusher e a -> Pusher e b -> Pusher e [(a, b)]
    pushKeyValuePairs Pusher e String
    forall e. String -> LuaE e ()
    pushString Pusher e String
    forall e. String -> LuaE e ()
    pushString) TypeSpec
    "table"
            Text
    "A table mapping environment variable names to their value."
      #? "Retrieves the entire environment as a string-indexed table."

    -- | Check the existence of a file path.
    exists :: LuaError e => DocumentedFunction e
    exists :: forall e. LuaError e => DocumentedFunction e
    exists = Name
    -> (String -> Maybe Text -> LuaE e Bool)
    -> HsFnPrecursor e (String -> Maybe Text -> LuaE e Bool)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "exists"
      ### (\fp mbType -> do
              case T.toLower <$> mbType of
                Nothing ->
                  -- any file type is fine
                  ioToLua $ Directory.doesPathExist fp
                Just "directory" ->
                  -- must be a directory or a symlink pointing to one
                  ioToLua $ Directory.doesDirectoryExist fp
                Just "file" ->
                  -- must be a file or a symlink pointing to one
                  ioToLua $ Directory.doesFileExist fp
                Just "symlink" ->
                  -- must exist and be a symlink
                  ioToLua $ (&&) <$> Directory.doesPathExist fp
                                 <*> Directory.pathIsSymbolicLink fp
                Just otherType ->
                  failLua $
                  "Unsupported filesystem object type: " <> T.unpack otherType)
      HsFnPrecursor e (String -> Maybe Text -> LuaE e Bool)
    -> Parameter e String
    -> HsFnPrecursor e (Maybe Text -> LuaE e Bool)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "path" Text
    "file path to check"
      HsFnPrecursor e (Maybe Text -> LuaE e Bool)
    -> Parameter e (Maybe Text) -> HsFnPrecursor e (LuaE e Bool)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e Text -> Parameter e (Maybe Text)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e Text
    forall e. Text -> Text -> Parameter e Text
    textParam Text
    "type" Text
    "the required type of the filesystem object")
      HsFnPrecursor e (LuaE e Bool)
    -> FunctionResults e Bool -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e Bool -> TypeSpec -> Text -> FunctionResults e Bool
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e Bool
    forall e. Pusher e Bool
    pushBool TypeSpec
    "boolean"
            Text
    "whether a filesystem object of type `type` exists at `path`."
      #? T.unlines
         [ "Check whether there exists a filesystem object at the given path."
         , "If `type` is given and either *directory* or *file*, then the"
         , "function returns `true` if and only if the file system object has"
         , "the given type, or if it's a symlink pointing to an object of that"
         , "type. Passing *symlink* as type requires the path itself to be a"
         , "symlink. Types other than those will cause an error."
         ]

    -- | Return the current working directory as an absolute path.
    getwd :: LuaError e => DocumentedFunction e
    getwd :: forall e. LuaError e => DocumentedFunction e
    getwd = Name -> LuaE e String -> HsFnPrecursor e (LuaE e String)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "getwd"
      ### ioToLua Directory.getCurrentDirectory
      HsFnPrecursor e (LuaE e String)
    -> FunctionResults e String -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Text -> FunctionResults e String
    forall e. Text -> [FunctionResult e String]
    filepathResult Text
    "The current working directory."
      #? "Obtain the current working directory as an absolute path."

    -- | Returns the value of an environment variable
    getenv :: LuaError e => DocumentedFunction e
    getenv :: forall e. LuaError e => DocumentedFunction e
    getenv = Name
    -> (String -> LuaE e (Maybe String))
    -> HsFnPrecursor e (String -> LuaE e (Maybe String))
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "getenv"
      ### ioToLua . Env.lookupEnv
      HsFnPrecursor e (String -> LuaE e (Maybe String))
    -> Parameter e String -> HsFnPrecursor e (LuaE e (Maybe String))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e String -> TypeSpec -> Text -> Text -> Parameter e String
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e String
    forall e. Peeker e String
    peekString TypeSpec
    "string" Text
    "var" Text
    "name of the environment"
      HsFnPrecursor e (LuaE e (Maybe String))
    -> FunctionResults e (Maybe String) -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e (Maybe String)
    -> TypeSpec -> Text -> FunctionResults e (Maybe String)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (LuaE e () -> (String -> LuaE e ()) -> Pusher e (Maybe String)
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe LuaE e ()
    forall e. LuaE e ()
    pushnil String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString) TypeSpec
    "string or nil"
            Text
    "value of the variable, or nil if the variable is not defined."
      #? T.unwords
        [ "Return the value of the environment variable `var`, or `nil` "
        , "if there is no such value." ]

    -- | List the contents of a directory.
    ls :: LuaError e => DocumentedFunction e
    ls :: forall e. LuaError e => DocumentedFunction e
    ls = Name
    -> (Maybe String -> LuaE e [String])
    -> HsFnPrecursor e (Maybe String -> LuaE e [String])
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "ls"
      ### ioToLua . Directory.listDirectory . fromMaybe "."
      HsFnPrecursor e (Maybe String -> LuaE e [String])
    -> Parameter e (Maybe String) -> HsFnPrecursor e (LuaE e [String])
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e String -> Parameter e (Maybe String)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    stringParam Text
    "directory"
               (Text
    "Path of the directory whose contents should be listed. "
                Text -> Text -> Text
    `T.append` Text
    "Defaults to `.`."))
      HsFnPrecursor e (LuaE e [String])
    -> FunctionResults e [String] -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e [String] -> TypeSpec -> Text -> FunctionResults e [String]
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e [String]
    forall e. LuaError e => Pusher e [String]
    pushFilePathList TypeSpec
    "table"
            (Text
    "A table of all entries in `directory`, except for the "
              Text -> Text -> Text
    `T.append` Text
    "special entries (`.`  and `..`).")
      #? "List the contents of a directory."

    -- | Create a new directory which is initially empty, or as near to
    -- empty as the operating system allows.
    --
    -- If the optional second parameter is `false`, then create the new
    -- directory only if it doesn't exist yet. If the parameter is `true`,
    -- then parent directories are created as necessary.
    mkdir :: LuaError e => DocumentedFunction e
    mkdir :: forall e. LuaError e => DocumentedFunction e
    mkdir = Name
    -> (String -> Maybe Bool -> LuaE e ())
    -> HsFnPrecursor e (String -> Maybe Bool -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "mkdir"
      ### (\fp createParent ->
             if createParent == Just True
             then ioToLua (Directory.createDirectoryIfMissing True fp)
             else ioToLua (Directory.createDirectory fp))
      HsFnPrecursor e (String -> Maybe Bool -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (Maybe Bool -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "dirname" Text
    "name of the new directory"
      HsFnPrecursor e (Maybe Bool -> LuaE e ())
    -> Parameter e (Maybe Bool) -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e Bool -> Parameter e (Maybe Bool)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e Bool
    forall e. Text -> Text -> Parameter e Bool
    boolParam Text
    "create_parent" Text
    "create parent directory if necessary")
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? T.concat
           [ "Create a new directory which is initially empty, or as near "
           , "to empty as the operating system allows. The function throws "
           , "an error if the directory cannot be created, e.g., if the "
           , "parent directory does not exist or if a directory of the "
           , "same name is already present.\n"
           , "\n"
           , "If the optional second parameter is provided and truthy, "
           , "then all directories, including parent directories, are "
           , "created as necessary.\n"
           ]

    -- | Returns the contents of a file.
    read_file :: LuaError e => DocumentedFunction e
    read_file :: forall e. LuaError e => DocumentedFunction e
    read_file = Name
    -> (String -> LuaE e ByteString)
    -> HsFnPrecursor e (String -> LuaE e ByteString)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "read_file"
      ### (ioToLua . B.readFile)
      HsFnPrecursor e (String -> LuaE e ByteString)
    -> Parameter e String -> HsFnPrecursor e (LuaE e ByteString)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "filepath" Text
    "File to read"
      HsFnPrecursor e (LuaE e ByteString)
    -> FunctionResults e ByteString -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e ByteString
    -> TypeSpec -> Text -> FunctionResults e ByteString
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e ByteString
    forall e. Pusher e ByteString
    pushByteString TypeSpec
    "string" Text
    "file contents"

    -- | Rename a file path.
    rename :: LuaError e => DocumentedFunction e
    rename :: forall e. LuaError e => DocumentedFunction e
    rename = Name
    -> (String -> String -> LuaE e ())
    -> HsFnPrecursor e (String -> String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "rename"
      ### (\old new -> ioToLua $ do
              isDir <- Directory.doesDirectoryExist old
              if isDir
                then Directory.renameDirectory old new
                else Directory.renameFile old new)
      HsFnPrecursor e (String -> String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (String -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "old" Text
    "original path"
      HsFnPrecursor e (String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "new" Text
    "new path"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? T.unlines
         [ "Change the name of an existing path from `old` to `new`."
         , ""
         , "If `old` is a directory and `new` is a directory that already"
         , "exists, then `new` is atomically replaced by the `old` directory."
         , "On Win32 platforms, this function fails if `new` is an existing"
         , "directory."
         , ""
         , "If `old` does not refer to a directory, then neither may `new`."
         , ""
         , "Renaming may not work across file system boundaries or due to"
         , "other system-specific reasons.  It's generally more robust to"
         , "copy the source path to its destination before deleting the"
         , "source."
         ]

    -- | Remove a file.
    rm :: LuaError e => DocumentedFunction e
    rm :: forall e. LuaError e => DocumentedFunction e
    rm = Name
    -> (String -> LuaE e ()) -> HsFnPrecursor e (String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "rm"
      ### ioToLua . Directory.removeFile
      HsFnPrecursor e (String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "filename" Text
    "file to remove"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? "Removes the directory entry for an existing file."

    -- | Remove an existing directory.
    rmdir :: LuaError e => DocumentedFunction e
    rmdir :: forall e. LuaError e => DocumentedFunction e
    rmdir = Name
    -> (String -> Maybe Bool -> LuaE e ())
    -> HsFnPrecursor e (String -> Maybe Bool -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "rmdir"
      ### (\fp recursive ->
             if recursive == Just True
             then ioToLua (Directory.removeDirectoryRecursive fp)
             else ioToLua (Directory.removeDirectory fp))
      HsFnPrecursor e (String -> Maybe Bool -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (Maybe Bool -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "dirname" Text
    "name of the directory to delete"
      HsFnPrecursor e (Maybe Bool -> LuaE e ())
    -> Parameter e (Maybe Bool) -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e Bool -> Parameter e (Maybe Bool)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e Bool
    forall e. Text -> Text -> Parameter e Bool
    boolParam Text
    "recursive" Text
    "delete content recursively")
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #?("Remove an existing, empty directory. If `recursive` is given, "
         `T.append` "then delete the directory and its contents recursively.")

    -- | Set the specified environment variable to a new value.
    setenv :: LuaError e => DocumentedFunction e
    setenv :: forall e. LuaError e => DocumentedFunction e
    setenv = Name
    -> (String -> String -> LuaE e ())
    -> HsFnPrecursor e (String -> String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "setenv"
      ### (\name value -> ioToLua (Env.setEnv name value))
      HsFnPrecursor e (String -> String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (String -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e String -> TypeSpec -> Text -> Text -> Parameter e String
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e String
    forall e. Peeker e String
    peekString TypeSpec
    "string" Text
    "name"
            Text
    "name of the environment variable"
      HsFnPrecursor e (String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e String -> TypeSpec -> Text -> Text -> Parameter e String
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e String
    forall e. Peeker e String
    peekString TypeSpec
    "string" Text
    "value" Text
    "new value"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? "Set the specified environment variable to a new value."

    -- | Change current working directory.
    setwd :: LuaError e => DocumentedFunction e
    setwd :: forall e. LuaError e => DocumentedFunction e
    setwd = Name
    -> (String -> LuaE e ()) -> HsFnPrecursor e (String -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "setwd"
      ### ioToLua . Directory.setCurrentDirectory
      HsFnPrecursor e (String -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "directory" Text
    "Path of the new working directory"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? "Change the working directory to the given path."

    -- | Get the modification time and access time of a file.
    times :: LuaError e => DocumentedFunction e
    times :: forall e. LuaError e => DocumentedFunction e
    times = Name
    -> (String -> LuaE e (UTCTime, UTCTime))
    -> HsFnPrecursor e (String -> LuaE e (UTCTime, UTCTime))
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "times"
      ### (\filepath -> ioToLua $
           (,) <$> Directory.getModificationTime filepath
               <*> Directory.getAccessTime filepath)
      HsFnPrecursor e (String -> LuaE e (UTCTime, UTCTime))
    -> Parameter e String
    -> HsFnPrecursor e (LuaE e (UTCTime, UTCTime))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "filepath" Text
    "file or directory path"
      HsFnPrecursor e (LuaE e (UTCTime, UTCTime))
    -> FunctionResults e (UTCTime, UTCTime) -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> (Pusher e (UTCTime, UTCTime)
    -> TypeSpec -> Text -> FunctionResults e (UTCTime, UTCTime)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (Pusher e UTCTime
    forall e. Pusher e UTCTime
    pushUTCTime Pusher e UTCTime
    -> ((UTCTime, UTCTime) -> UTCTime) -> Pusher e (UTCTime, UTCTime)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (UTCTime, UTCTime) -> UTCTime
    forall a b. (a, b) -> a
    fst) TypeSpec
    "table"
           Text
    "time at which the file or directory was last modified" FunctionResults e (UTCTime, UTCTime)
    -> FunctionResults e (UTCTime, UTCTime)
    -> FunctionResults e (UTCTime, UTCTime)
    forall a. Semigroup a => a -> a -> a
    <>
           Pusher e (UTCTime, UTCTime)
    -> TypeSpec -> Text -> FunctionResults e (UTCTime, UTCTime)
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult (Pusher e UTCTime
    forall e. Pusher e UTCTime
    pushUTCTime Pusher e UTCTime
    -> ((UTCTime, UTCTime) -> UTCTime) -> Pusher e (UTCTime, UTCTime)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (UTCTime, UTCTime) -> UTCTime
    forall a b. (a, b) -> b
    snd) TypeSpec
    "table"
           Text
    "time at which the file or directory was last accessed")
      #? T.unlines
         [ "Obtain the modification and access time of a file or directory."
         , "The times are returned as strings using the ISO 8601 format."
         ]

    -- | Get the current directory for temporary files.
    tmpdirname :: LuaError e => DocumentedFunction e
    tmpdirname :: forall e. LuaError e => DocumentedFunction e
    tmpdirname = Name -> LuaE e String -> HsFnPrecursor e (LuaE e String)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "tmpdirname"
      ### ioToLua Directory.getTemporaryDirectory
      HsFnPrecursor e (LuaE e String)
    -> FunctionResults e String -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e String -> TypeSpec -> Text -> FunctionResults e String
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e String
    forall e. String -> LuaE e ()
    pushString TypeSpec
    "string"
            Text
    "The current directory for temporary files."
      #? mconcat
         [ "Returns the current directory for temporary files.\n"
         , "\n"
         , "On Unix, `tmpdirname()` returns the value of the `TMPDIR` "
         , "environment variable or \"/tmp\" if the variable isn't defined. "
         , "On Windows, the function checks for the existence of environment "
         , "variables in the following order and uses the first path found:\n"
         , "\n"
         , "- TMP environment variable.\n"
         , "- TEMP environment variable.\n"
         , "- USERPROFILE environment variable.\n"
         , "- The Windows directory\n"
         , "\n"
         , "The operation may fail if the operating system has no notion of "
         , "temporary directory.\n"
         , "\n"
         , "The function doesn't verify whether the path exists.\n"
         ]

    -- | Run an action in a different directory, then restore the old
    -- working directory.
    with_wd :: LuaError e => DocumentedFunction e
    with_wd :: forall e. LuaError e => DocumentedFunction e
    with_wd = Name
    -> (String -> Callback -> LuaE e NumResults)
    -> HsFnPrecursor e (String -> Callback -> LuaE e NumResults)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "with_wd"
      ### (\fp callback ->
            bracket (ioToLua Directory.getCurrentDirectory)
                    (ioToLua . Directory.setCurrentDirectory)
               (\_ -> do
                  ioToLua (Directory.setCurrentDirectory fp)
                  callback `invokeWithFilePath` fp))
      HsFnPrecursor e (String -> Callback -> LuaE e NumResults)
    -> Parameter e String
    -> HsFnPrecursor e (Callback -> LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "directory"
            Text
    "Directory in which the given `callback` should be executed"
      HsFnPrecursor e (Callback -> LuaE e NumResults)
    -> Parameter e Callback -> HsFnPrecursor e (LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e Callback
    -> TypeSpec -> Text -> Text -> Parameter e Callback
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e Callback
    forall e. Peeker e Callback
    peekCallback TypeSpec
    "function" Text
    "callback"
            Text
    "Action to execute in the given directory"
      HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    forall e.
    HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    =?> Text
    "The results of the call to `callback`."
      #? T.unwords
         [ "Run an action within a different directory. This function will"
         , "change the working directory to `directory`, execute `callback`,"
         , "then switch back to the original working directory, even if an"
         , "error occurs while running the callback action."
         ]

    -- | Run an action, then restore the old environment variable values.
    with_env :: LuaError e => DocumentedFunction e
    with_env :: forall e. LuaError e => DocumentedFunction e
    with_env = Name
    -> ([(String, String)] -> Callback -> LuaE e NumResults)
    -> HsFnPrecursor
         e ([(String, String)] -> Callback -> LuaE e NumResults)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "with_env"
      ### (\environment callback ->
            bracket (ioToLua Env.getEnvironment)
                    setEnvironment
                    (\_ -> setEnvironment environment *> invoke callback))
      HsFnPrecursor
      e ([(String, String)] -> Callback -> LuaE e NumResults)
    -> Parameter e [(String, String)]
    -> HsFnPrecursor e (Callback -> LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e [(String, String)]
    -> TypeSpec -> Text -> Text -> Parameter e [(String, String)]
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter (Peeker e String -> Peeker e String -> Peeker e [(String, String)]
    forall e a b.
    LuaError e =>
    Peeker e a -> Peeker e b -> Peeker e [(a, b)]
    peekKeyValuePairs Peeker e String
    forall e. Peeker e String
    peekString Peeker e String
    forall e. Peeker e String
    peekString) TypeSpec
    "table"
            Text
    "environment"
            (Text
    "Environment variables and their values to be set before "
             Text -> Text -> Text
    `T.append` Text
    "running `callback`")
      HsFnPrecursor e (Callback -> LuaE e NumResults)
    -> Parameter e Callback -> HsFnPrecursor e (LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e Callback
    -> TypeSpec -> Text -> Text -> Parameter e Callback
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e Callback
    forall e. Peeker e Callback
    peekCallback TypeSpec
    "function" Text
    "callback"
            Text
    "Action to execute in the custom environment"
      HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    forall e.
    HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    =?> Text
    "The results of the call to `callback`."
      #? T.unwords
         [ "Run an action within a custom environment. Only the environment"
         , "variables given by `environment` will be set, when `callback` is"
         , "called. The original environment is restored after this function"
         , "finishes, even if an error occurs while running the callback"
         , "action."
         ]
     where
      setEnvironment :: t (String, String) -> LuaE e ()
    setEnvironment t (String, String)
    newEnv = IO () -> LuaE e ()
    forall e a. LuaError e => IO a -> LuaE e a
    ioToLua (IO () -> LuaE e ()) -> IO () -> LuaE e ()
    forall a b. (a -> b) -> a -> b
    $ do
        -- Crude, but fast enough: delete all entries in new environment,
        -- then restore old environment one-by-one.
        curEnv <- IO [(String, String)]
    Env.getEnvironment
        forM_ curEnv (Env.unsetEnv . fst)
        forM_ newEnv (uncurry Env.setEnv)

    -- | Provides a temporary directory for the given action.
    with_tmpdir :: LuaError e => DocumentedFunction e
    with_tmpdir :: forall e. LuaError e => DocumentedFunction e
    with_tmpdir = Name
    -> (Maybe String -> String -> Callback -> LuaE e NumResults)
    -> HsFnPrecursor
         e (Maybe String -> String -> Callback -> LuaE e NumResults)
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "with_tmpdir"
      ### (\mParentDir tmpl callback -> case mParentDir of
              Nothing -> do
                Temp.withSystemTempDirectory tmpl $
                  invokeWithFilePath callback
              Just parentDir -> do
                Temp.withTempDirectory parentDir tmpl $
                  invokeWithFilePath callback)
      HsFnPrecursor
      e (Maybe String -> String -> Callback -> LuaE e NumResults)
    -> Parameter e (Maybe String)
    -> HsFnPrecursor e (String -> Callback -> LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e (Maybe String)
    -> TypeSpec -> Text -> Text -> Parameter e (Maybe String)
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e (Maybe String)
    forall {e}. StackIndex -> Peek e (Maybe String)
    peekParentDir TypeSpec
    "string" Text
    "parent_dir"
            ([Text] -> Text
    forall a. Monoid a => [a] -> a
    mconcat
             [ Text
    "Parent directory to create the directory in. If this "
             , Text
    "parameter is omitted, the system's canonical temporary "
             , Text
    "directory is used."
             ])
      HsFnPrecursor e (String -> Callback -> LuaE e NumResults)
    -> Parameter e String
    -> HsFnPrecursor e (Callback -> LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    stringParam Text
    "templ" Text
    "Directory name template."
      HsFnPrecursor e (Callback -> LuaE e NumResults)
    -> Parameter e Callback -> HsFnPrecursor e (LuaE e NumResults)
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e Callback
    -> TypeSpec -> Text -> Text -> Parameter e Callback
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e Callback
    forall e. Peeker e Callback
    peekCallback TypeSpec
    "function" Text
    "callback"
            (Text
    "Function which takes the name of the temporary directory as "
             Text -> Text -> Text
    `T.append` Text
    "its first argument.")
      HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    forall e.
    HsFnPrecursor e (LuaE e NumResults) -> Text -> DocumentedFunction e
    =?> Text
    "The results of the call to `callback`."
      #? T.unlines
         [ "Create and use a temporary directory inside the given directory."
         , "The directory is deleted after the callback returns."
         ]
      where
        peekParentDir :: StackIndex -> Peek e (Maybe String)
    peekParentDir StackIndex
    idx = do
          args <- LuaE e StackIndex -> Peek e StackIndex
    forall e a. LuaE e a -> Peek e a
    liftLua LuaE e StackIndex
    forall e. LuaE e StackIndex
    gettop
          if args < 3
            then liftLua $ do
              pushnil
              insert idx
              return Nothing
            else Just <$> peekString idx

    -- | Write a string to a file.
    write_file :: LuaError e => DocumentedFunction e
    write_file :: forall e. LuaError e => DocumentedFunction e
    write_file = Name
    -> (String -> ByteString -> LuaE e ())
    -> HsFnPrecursor e (String -> ByteString -> LuaE e ())
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "write_file"
      ### (\filepath contents ->
             ioToLua $ B.writeFile filepath contents)
      HsFnPrecursor e (String -> ByteString -> LuaE e ())
    -> Parameter e String -> HsFnPrecursor e (ByteString -> LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "filepath" Text
    "path to target file"
      HsFnPrecursor e (ByteString -> LuaE e ())
    -> Parameter e ByteString -> HsFnPrecursor e (LuaE e ())
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e ByteString
    -> TypeSpec -> Text -> Text -> Parameter e ByteString
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e ByteString
    forall e. Peeker e ByteString
    peekByteString TypeSpec
    "string" Text
    "contents" Text
    "file contents"
      HsFnPrecursor e (LuaE e ())
    -> FunctionResults e () -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> []
      #? "Writes a string to a file."

    -- | Obtain the paths to special directories.
    xdg :: LuaError e => DocumentedFunction e
    xdg :: forall e. LuaError e => DocumentedFunction e
    xdg = Name
    -> (Either XdgDirectory XdgDirectoryList
        -> Maybe String -> LuaE e (Either String [String]))
    -> HsFnPrecursor
         e
         (Either XdgDirectory XdgDirectoryList
          -> Maybe String -> LuaE e (Either String [String]))
    forall a e. Name -> a -> HsFnPrecursor e a
    defun Name
    "xdg"
      ### (\xdgDirTypeOrList mfp->
             case xdgDirTypeOrList of
               Left xdgDirType -> Left <$>
                 let fp = fromMaybe "" mfp
                 in ioToLua $ Directory.getXdgDirectory xdgDirType fp
               Right xdgDirList ->
                 ioToLua $ Right <$> Directory.getXdgDirectoryList xdgDirList)

      HsFnPrecursor
      e
      (Either XdgDirectory XdgDirectoryList
       -> Maybe String -> LuaE e (Either String [String]))
    -> Parameter e (Either XdgDirectory XdgDirectoryList)
    -> HsFnPrecursor
         e (Maybe String -> LuaE e (Either String [String]))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Peeker e (Either XdgDirectory XdgDirectoryList)
    -> TypeSpec
    -> Text
    -> Text
    -> Parameter e (Either XdgDirectory XdgDirectoryList)
    forall e a. Peeker e a -> TypeSpec -> Text -> Text -> Parameter e a
    parameter Peeker e (Either XdgDirectory XdgDirectoryList)
    forall e. Peeker e (Either XdgDirectory XdgDirectoryList)
    peekXdgDirectory TypeSpec
    "string" Text
    "xdg_directory_type"
            ([Text] -> Text
    T.unlines
             [ Text
    "The type of the XDG directory or search path."
             , Text
    "Must be one of `config`, `data`, `cache`, `state`,"
             , Text
    "`datadirs`, or `configdirs`."
             , Text
    ""
             , Text
    "Matching is case-insensitive, and underscores and `XDG`"
             , Text
    "prefixes are ignored, so a value like"
             , Text
    "`XDG_DATA_DIRS` is also acceptable."
             , Text
    ""
             , Text
    "The `state` directory might not be available, depending"
             , Text
    "on the version of the underlying Haskell library."
             ])
      HsFnPrecursor e (Maybe String -> LuaE e (Either String [String]))
    -> Parameter e (Maybe String)
    -> HsFnPrecursor e (LuaE e (Either String [String]))
    forall e a b.
    HsFnPrecursor e (a -> b) -> Parameter e a -> HsFnPrecursor e b
    <#> Parameter e String -> Parameter e (Maybe String)
    forall e a. Parameter e a -> Parameter e (Maybe a)
    opt (Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    filepathParam Text
    "filepath"
               (Text
    "relative path that is appended to the path; ignored " Text -> Text -> Text
    forall a. Semigroup a => a -> a -> a
    <>
                Text
    "if the result is a list of search paths."))
      HsFnPrecursor e (LuaE e (Either String [String]))
    -> FunctionResults e (Either String [String])
    -> DocumentedFunction e
    forall e a.
    HsFnPrecursor e (LuaE e a)
    -> FunctionResults e a -> DocumentedFunction e
    =#> Pusher e (Either String [String])
    -> TypeSpec -> Text -> FunctionResults e (Either String [String])
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult ((String -> LuaE e ())
    -> ([String] -> LuaE e ()) -> Pusher e (Either String [String])
    forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
    either String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString [String] -> LuaE e ()
    forall e. LuaError e => Pusher e [String]
    pushFilePathList)
            TypeSpec
    "string|{string,...}"
            Text
    "Either a single file path, or a list of search paths."
      #? T.unlines
         [ "Access special directories and directory search paths."
         , ""
         , "Special directories for storing user-specific application"
         , "data, configuration, and cache files, as specified by the"
         , "[XDG Base Directory Specification](" <>
           "https://specifications.freedesktop.org/basedir-spec/latest/)."
         ]

    --
    -- Parameters
    --

    -- | Filepath function parameter.
    filepathParam :: Text  -- ^ name
                  -> Text  -- ^ description
                  -> Parameter e FilePath
    filepathParam :: forall e. Text -> Text -> Parameter e String
    filepathParam = Text -> Text -> Parameter e String
    forall e. Text -> Text -> Parameter e String
    stringParam

    -- | Result of a function returning a file path.
    filepathResult :: Text -- ^ Description
                   -> [FunctionResult e FilePath]
    filepathResult :: forall e. Text -> [FunctionResult e String]
    filepathResult = Pusher e String -> TypeSpec -> Text -> FunctionResults e String
    forall e a. Pusher e a -> TypeSpec -> Text -> FunctionResults e a
    functionResult Pusher e String
    forall e. String -> LuaE e ()
    pushString TypeSpec
    "string"

    --
    -- Process parameters
    --

    -- | Process options
    data ProcessOpts = ProcessOpts
      { ProcessOpts -> Maybe [(String, String)]
    processOptsEnv  :: Maybe [(String, String)]
      , ProcessOpts -> Maybe String
    processOptsCwd  :: Maybe FilePath
      }

    -- | Peek process creation options
    peekProcessOptions :: LuaError e => Peeker e ProcessOpts
    peekProcessOptions :: forall e. LuaError e => Peeker e ProcessOpts
    peekProcessOptions = Name
    -> (StackIndex -> LuaE e Bool)
    -> Peeker e ProcessOpts
    -> Peeker e ProcessOpts
    forall e a.
    Name -> (StackIndex -> LuaE e Bool) -> Peeker e a -> Peeker e a
    typeChecked Name
    "table" StackIndex -> LuaE e Bool
    forall e. StackIndex -> LuaE e Bool
    istable (Peeker e ProcessOpts -> Peeker e ProcessOpts)
    -> Peeker e ProcessOpts -> Peeker e ProcessOpts
    forall a b. (a -> b) -> a -> b
    $ \StackIndex
    idx -> do
      let peekEnv :: Peeker e [(String, String)]
    peekEnv = Peeker e String -> Peeker e String -> Peeker e [(String, String)]
    forall e a b.
    LuaError e =>
    Peeker e a -> Peeker e b -> Peeker e [(a, b)]
    peekKeyValuePairs Peeker e String
    forall e. Peeker e String
    peekString Peeker e String
    forall e. Peeker e String
    peekString
      env' <- Peeker e (Maybe [(String, String)])
    -> Name -> Peeker e (Maybe [(String, String)])
    forall e a. LuaError e => Peeker e a -> Name -> Peeker e a
    peekFieldRaw (Peeker e [(String, String)] -> Peeker e (Maybe [(String, String)])
    forall (m :: * -> *) e a.
    Alternative m =>
    Peeker e a -> Peeker e (m a)
    peekNilOr Peeker e [(String, String)]
    peekEnv)    Name
    "env" StackIndex
    idx
      cwd' <- peekFieldRaw (peekNilOr peekString) "cwd" idx
      return $ ProcessOpts
        { processOptsEnv = env'
        , processOptsCwd = cwd'
        }

    -- | Pushes an exit code; failure codes are pushed as integers, and
    -- success is pushed as `false`. This means that the value can be
    -- interpreted as a boolean `failed` value.
    pushExitCode :: Pusher e Exit.ExitCode
    pushExitCode :: forall e. Pusher e ExitCode
    pushExitCode = \case
      ExitCode
    Exit.ExitSuccess   -> Pusher e Bool
    forall e. Pusher e Bool
    pushBool Bool
    False
      Exit.ExitFailure Int
    n -> Int -> LuaE e ()
    forall a e. (Integral a, Show a) => a -> LuaE e ()
    pushIntegral Int
    n

    -- | Pushes a time as ISO 8601 string.
    pushUTCTime :: Pusher e Time.UTCTime
    pushUTCTime :: forall e. Pusher e UTCTime
    pushUTCTime = String -> LuaE e ()
    forall e. String -> LuaE e ()
    pushString (String -> LuaE e ())
    -> (UTCTime -> String) -> UTCTime -> LuaE e ()
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . UTCTime -> String
    forall t. ISO8601 t => t -> String
    ISO8601.iso8601Show

    -- | Get an XDG directory type identifier.
    peekXdgDirectory :: Peeker e
                        (Either Directory.XdgDirectory Directory.XdgDirectoryList)
    peekXdgDirectory :: forall e. Peeker e (Either XdgDirectory XdgDirectoryList)
    peekXdgDirectory =
      ((Text -> Text) -> Peek e Text -> Peek e Text
    forall a b. (a -> b) -> Peek e a -> Peek e b
    forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
    fmap Text -> Text
    cleanupXdgSpec (Peek e Text -> Peek e Text)
    -> (StackIndex -> Peek e Text) -> StackIndex -> Peek e Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . StackIndex -> Peek e Text
    forall e. Peeker e Text
    peekText) (StackIndex -> Peek e Text)
    -> (Text -> Peek e (Either XdgDirectory XdgDirectoryList))
    -> StackIndex
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall (m :: * -> *) a b c.
    Monad m =>
    (a -> m b) -> (b -> m c) -> a -> m c
    >=> \case
        Text
    "cache"       -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectory -> Either XdgDirectory XdgDirectoryList
    forall a b. a -> Either a b
    Left XdgDirectory
    Directory.XdgCache)
        Text
    "config"      -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectory -> Either XdgDirectory XdgDirectoryList
    forall a b. a -> Either a b
    Left XdgDirectory
    Directory.XdgConfig)
        Text
    "data"        -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectory -> Either XdgDirectory XdgDirectoryList
    forall a b. a -> Either a b
    Left XdgDirectory
    Directory.XdgData)
    #if MIN_VERSION_directory(1,3,7)
        Text
    "state"       -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectory -> Either XdgDirectory XdgDirectoryList
    forall a b. a -> Either a b
    Left XdgDirectory
    Directory.XdgState)
    #endif
        Text
    "datadirs"    -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectoryList -> Either XdgDirectory XdgDirectoryList
    forall a b. b -> Either a b
    Right XdgDirectoryList
    Directory.XdgDataDirs)
        Text
    "configdirs"  -> Either XdgDirectory XdgDirectoryList
    -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a. a -> Peek e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure (XdgDirectoryList -> Either XdgDirectory XdgDirectoryList
    forall a b. b -> Either a b
    Right XdgDirectoryList
    Directory.XdgConfigDirs)
        Text
    s             -> ByteString -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a e. ByteString -> Peek e a
    failPeek (ByteString -> Peek e (Either XdgDirectory XdgDirectoryList))
    -> ByteString -> Peek e (Either XdgDirectory XdgDirectoryList)
    forall a b. (a -> b) -> a -> b
    $
          ByteString
    "Expected 'cache', 'config', 'data', or 'state', got: " ByteString -> ByteString -> ByteString
    forall a. Semigroup a => a -> a -> a
    <>
          Text -> ByteString
    Utf8.fromText Text
    s
      where
        -- Cleanup the XDG directory specifier as to make matching easier
        -- while keeping things permissive.
        -- Remove underscores, any 'xdg' prefix, and
        -- make sure everything is lowercase
        cleanupXdgSpec :: Text -> Text
    cleanupXdgSpec =
          (\Text
    s -> Text -> Maybe Text -> Text
    forall a. a -> Maybe a -> a
    fromMaybe Text
    s (Maybe Text -> Text) -> Maybe Text -> Text
    forall a b. (a -> b) -> a -> b
    $ Text -> Text -> Maybe Text
    T.stripPrefix Text
    "xdg" Text
    s)
          (Text -> Text) -> (Text -> Text) -> Text -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . (Char -> Bool) -> Text -> Text
    T.filter (Char -> Char -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Char
    '_')
          (Text -> Text) -> (Text -> Text) -> Text -> Text
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Text -> Text
    T.toLower

    -- | Pushes a list of file paths.
    pushFilePathList :: LuaError e => Pusher e [FilePath]
    pushFilePathList :: forall e. LuaError e => Pusher e [String]
    pushFilePathList [String]
    fps = do
      Pusher e String -> [String] -> LuaE e ()
    forall e a. LuaError e => Pusher e a -> [a] -> LuaE e ()
    pushList Pusher e String
    forall e. String -> LuaE e ()
    pushString [String]
    fps
      Name -> LuaE e () -> LuaE e ()
    forall e. Name -> LuaE e () -> LuaE e ()
    newListMetatable Name
    "FilePath list" (() -> LuaE e ()
    forall a. a -> LuaE e a
    forall (f :: * -> *) a. Applicative f => a -> f a
    pure ())
      StackIndex -> LuaE e ()
    forall e. StackIndex -> LuaE e ()
    setmetatable (CInt -> StackIndex
    nth CInt
    2)