Skip to content
This repository was archived by the owner on Mar 22, 2021. It is now read-only.

Commit 844eb00

Browse files
committed
Add function with_env
1 parent 3687681 commit 844eb00

4 files changed

Lines changed: 87 additions & 7 deletions

File tree

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,31 @@ Returns:
189189

190190
- The current directory for temporary files (string).
191191

192+
### with\_env
193+
194+
`with_env (environment, callback)`
195+
196+
Run an action within a custom environment. Only the environment
197+
variables given by `environment` will be set, when `callback` is
198+
called. The original environment is restored after this function
199+
finishes, even if an error occurs while running the callback
200+
action.
201+
202+
Parameters:
203+
204+
`environment`:
205+
: Environment variables and their values to be set before
206+
running `callback`. (table with string keys and string
207+
values)
208+
209+
`callback`:
210+
: Action to execute in the custom environment (function)
211+
212+
Returns:
213+
214+
- The result(s) of the call to `callback`
215+
216+
192217
### with\_tmpdir {#system-with_tmpdir}
193218

194219
`with_tmpdir ([parent_dir,] templ, callback)`

hslua-module-system.cabal

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ source-repository head
2727

2828
library
2929
build-depends: base >= 4.9 && < 5
30-
, directory >= 1.3 && < 1.4
30+
, containers >= 0.5 && < 0.7
31+
, directory >= 1.3 && < 1.4
32+
, exceptions >= 0.8 && < 0.11
3133
, hslua >= 1.0 && < 1.2
3234
, temporary >= 1.2 && < 1.4
3335
default-extensions: LambdaCase

src/Foreign/Lua/Module/System.hs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,14 @@ where
1818

1919
import Control.Applicative ((<$>))
2020
import Control.Exception (IOException, try)
21+
import Control.Monad (forM_)
22+
import Control.Monad.Catch (bracket)
2123
import Data.Maybe (fromMaybe)
24+
import Data.Version (versionBranch)
2225
import Foreign.Lua (Lua, NumResults(..), Optional (..), Peekable, Pushable,
2326
StackIndex, ToHaskellFunction)
2427

25-
import qualified Data.Version
28+
import qualified Data.Map as Map
2629
import qualified Foreign.Lua as Lua
2730
import qualified System.Directory as Directory
2831
import qualified System.Environment as Env
@@ -35,7 +38,7 @@ pushModule = do
3538
Lua.newtable
3639
addField "arch" Info.arch
3740
addField "compiler_name" Info.compilerName
38-
addField "compiler_version" (Data.Version.versionBranch Info.compilerVersion)
41+
addField "compiler_version" (versionBranch Info.compilerVersion)
3942
addField "os" Info.os
4043
addFunction "chdir" chdir
4144
addFunction "currentdir" currentdir
@@ -46,6 +49,7 @@ pushModule = do
4649
addFunction "rmdir" rmdir
4750
addFunction "setenv" setenv
4851
addFunction "tmpdirname" tmpdirname
52+
addFunction "with_env" with_env
4953
addFunction "with_tmpdir" with_tmpdir
5054
return 1
5155

@@ -100,6 +104,20 @@ instance Peekable AnyValue where
100104
instance Pushable AnyValue where
101105
push (AnyValue idx) = Lua.pushvalue idx
102106

107+
-- | Run an action, then restore the old environment variable values.
108+
with_env :: Map.Map String String -> Callback -> Lua NumResults
109+
with_env environment callback =
110+
bracket (Lua.liftIO Env.getEnvironment)
111+
setEnvironment
112+
(\_ -> setEnvironment (Map.toList environment) >> invoke callback)
113+
where
114+
setEnvironment newEnv = Lua.liftIO $ do
115+
-- Crude, but fast enough: delete all entries in new environment,
116+
-- then restore old environment one-by-one.
117+
curEnv <- Env.getEnvironment
118+
forM_ curEnv (Env.unsetEnv . fst)
119+
forM_ newEnv (uncurry Env.setEnv)
120+
103121
with_tmpdir :: String -- ^ parent dir or template
104122
-> AnyValue -- ^ template or callback
105123
-> Optional Callback -- ^ callback or nil
@@ -112,15 +130,24 @@ with_tmpdir parentDir tmpl callback =
112130
-- temporary directory.
113131
let tmpl' = parentDir
114132
callback' <- Lua.peek (fromAnyValue tmpl)
115-
Temp.withSystemTempDirectory tmpl' (callWithFilename callback')
133+
Temp.withSystemTempDirectory tmpl' (invokeWithFilePath callback')
116134
Just callback' -> do
117135
-- all args given. Second value must be converted to a string.
118136
tmpl' <- Lua.peek (fromAnyValue tmpl)
119-
Temp.withTempDirectory parentDir tmpl' (callWithFilename callback')
137+
Temp.withTempDirectory parentDir tmpl' (invokeWithFilePath callback')
138+
139+
invoke :: Callback -> Lua NumResults
140+
invoke callback = do
141+
oldTop <- Lua.gettop
142+
Lua.push callback
143+
Lua.call 0 Lua.multret
144+
newTop <- Lua.gettop
145+
return . NumResults . fromIntegral . Lua.fromStackIndex $
146+
newTop - oldTop
120147

121148
-- | Call Lua callback function with the given filename as its argument.
122-
callWithFilename :: Callback -> FilePath -> Lua NumResults
123-
callWithFilename callback filename = do
149+
invokeWithFilePath :: Callback -> FilePath -> Lua NumResults
150+
invokeWithFilePath callback filename = do
124151
oldTop <- Lua.gettop
125152
Lua.push callback
126153
Lua.push filename

test/system-module-tests.lua

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,32 @@ in_tmpdir(test_mkdir_rmdir)
5959
-- tmpdirname
6060
assert(type(system.tmpdirname()) == 'string', "tmpdirname should return a string")
6161

62+
-- with_env
63+
local outer_value = 'outer test value'
64+
local inner_value = 'inner test value'
65+
local inner_only = 'test #2'
66+
67+
function check_env ()
68+
assert(os.getenv 'SYSTEM_TEST' == inner_value, "env has test value")
69+
assert(os.getenv 'SYSTEM_TEST_INNER_ONLY' == inner_only,
70+
"inner only exists")
71+
assert(os.getenv 'SYSTEM_TEST_OUTER_ONLY' == nil,
72+
"outer only variable should be unset")
73+
end
74+
75+
local test_env = {
76+
SYSTEM_TEST = inner_value,
77+
SYSTEM_TEST_INNER_ONLY = inner_only
78+
}
79+
system.setenv('SYSTEM_TEST_OUTER_ONLY', outer_value)
80+
system.setenv('SYSTEM_TEST', outer_value)
81+
system.with_env(test_env, check_env)
82+
83+
assert(system.getenv 'SYSTEM_TEST' == outer_value, "value was restored")
84+
assert(system.getenv 'SYSTEM_TEST_INNER_ONLY' == nil, "value was restored")
85+
assert(system.getenv 'SYSTEM_TEST_OUTER_ONLY' == outer_value,
86+
"value was restored")
87+
6288
-- with_tmpdir
6389
local token = 'Banana'
6490
function write_read_token (tmpdir)

0 commit comments

Comments
 (0)