hslua-module-system (empty) → 0.1.0
raw patch · 6 files changed
+291/−0 lines, 6 filesdep +basedep +directorydep +hsluasetup-changed
Dependencies added: base, directory, hslua, hslua-module-system, tasty, tasty-hunit, temporary, text
Files
- CHANGELOG.md +5/−0
- LICENSE +20/−0
- Setup.hs +2/−0
- hslua-module-system.cabal +49/−0
- src/Foreign/Lua/Module/System.hs +161/−0
- test/test-hslua-module-system.hs +54/−0
+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for hslua-module-system++## 0.1.0 -- 2019-04-26++* First version. Released on an unsuspecting world.
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2019 Albert Krewinkel++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ hslua-module-system.cabal view
@@ -0,0 +1,49 @@+name: hslua-module-system+version: 0.1.0+synopsis: Lua module wrapper around Haskell's System module.++description: Provides access to system information and functionality+ to Lua scripts via Haskell's `System` module.+ .+ Intended usage for this package is to preload it by adding+ the loader function to `package.preload`. Note that the+ Lua `package` library must have already been loaded before+ the loader can be added.+homepage: https://github.com/hslua/hslua-module-system+license: MIT+license-file: LICENSE+author: Albert Krewinkel+maintainer: albert+hslua@zeitkraut.de+copyright: Albert Krewinkel <albert+hslua@zeitkraut.de>+category: Foreign+build-type: Simple+extra-source-files: CHANGELOG.md+cabal-version: >=1.10++source-repository head+ type: git+ location: https://github.com/hslua/hslua-module-system.git++library+ build-depends: base >= 4.9 && < 5+ , directory >= 1.3 && < 1.4+ , hslua >= 1.0 && < 1.2+ , temporary >= 1.2 && < 1.4+ default-extensions: LambdaCase+ default-language: Haskell2010+ exposed-modules: Foreign.Lua.Module.System+ hs-source-dirs: src+ other-extensions: OverloadedStrings++test-suite test-hslua-module-system+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: test-hslua-module-system.hs+ hs-source-dirs: test+ ghc-options: -Wall -threaded+ build-depends: base+ , hslua+ , hslua-module-system+ , tasty+ , tasty-hunit+ , text
+ src/Foreign/Lua/Module/System.hs view
@@ -0,0 +1,161 @@+{-# LANGUAGE ScopedTypeVariables #-}+{-|+Module : Foreign.Lua.Module.System+Copyright : © 2019 Albert Krewinkel+License : MIT+Maintainer : Albert Krewinkel <albert+hslua@zeitkraut.de>+Stability : alpha+Portability : Requires language extensions ForeignFunctionInterface,+ OverloadedStrings.++Provide a Lua module containing a selection of @'System'@ functions.+-}+module Foreign.Lua.Module.System+ ( pushModule+ , preloadModule+ )+where++import Control.Applicative ((<$>))+import Control.Exception (IOException, catch, evaluate, try)+import Data.Maybe (fromMaybe)+import Foreign.Lua (Lua, NumResults(..), Optional (..), Peekable, Pushable,+ StackIndex, ToHaskellFunction)+import System.IO.Error (IOError, isDoesNotExistError)++import qualified Foreign.Lua as Lua+import qualified System.Directory as Directory+import qualified System.Environment as Env+import qualified System.IO.Temp as Temp++-- | Pushes the @text@ module to the lua stack.+pushModule :: Lua NumResults+pushModule = do+ Lua.newtable+ addFunction "chdir" chdir+ addFunction "currentdir" currentdir+ addFunction "env" env+ addFunction "getenv" getenv+ addFunction "ls" ls+ addFunction "pwd" currentdir+ addFunction "setenv" setenv+ addFunction "tmpdirname" tmpdirname+ addFunction "with_tmpdir" with_tmpdir+ return 1++-- | Add the text module under the given name to the table of preloaded+-- packages.+preloadModule :: String -> Lua ()+preloadModule = flip addPackagePreloader pushModule++-- | Registers a preloading function. Takes an module name and the Lua+-- operation which produces the package.+addPackagePreloader :: String -> Lua NumResults -> Lua ()+addPackagePreloader name modulePusher = do+ Lua.getfield Lua.registryindex Lua.preloadTableRegistryField+ Lua.pushHaskellFunction modulePusher+ Lua.setfield (-2) name+ Lua.pop 1++-- | Attach a function to the table at the top of the stack, using the+-- given name.+addFunction :: ToHaskellFunction a => String -> a -> Lua ()+addFunction name fn = do+ Lua.push name+ Lua.pushHaskellFunction fn+ Lua.rawset (-3)++-- | Lua callback function+newtype Callback = Callback { callbackStackIndex :: StackIndex }++instance Peekable Callback where+ peek idx = do+ isFn <- Lua.isfunction idx+ if isFn+ then return (Callback idx)+ else Lua.throwException "Function expected"++instance Pushable Callback where+ push (Callback idx) = Lua.pushvalue idx+++-- | Any value of unknown type+newtype AnyValue = AnyValue { fromAnyValue :: StackIndex }++instance Peekable AnyValue where+ peek = return . AnyValue++instance Pushable AnyValue where+ push (AnyValue idx) = Lua.pushvalue idx++with_tmpdir :: String -- ^ parent dir or template+ -> AnyValue -- ^ template or callback+ -> Optional Callback -- ^ callback or nil+ -> Lua NumResults+with_tmpdir parentDir tmpl callback = do+ case fromOptional callback of+ Nothing -> do+ -- At most two args. The first arg (parent dir) has probably been+ -- omitted, so we shift arguments and use the system's canonical+ -- temporary directory.+ let tmpl' = parentDir+ callback' <- Lua.peek (fromAnyValue tmpl)+ Temp.withSystemTempDirectory tmpl' (callWithFilename callback')+ Just callback' -> do+ -- all args given. Second value must be converted to a string.+ tmpl' <- Lua.peek (fromAnyValue tmpl)+ Temp.withTempDirectory parentDir tmpl' (callWithFilename callback')++-- | Call Lua callback function with the given filename as its argument.+callWithFilename :: Callback -> FilePath -> Lua NumResults+callWithFilename callback filename = do+ oldTop <- Lua.gettop+ Lua.push callback+ Lua.push filename+ Lua.call (Lua.NumArgs 1) Lua.multret+ newTop <- Lua.gettop+ return . NumResults . fromIntegral . Lua.fromStackIndex $+ newTop - oldTop++-- | List the contents of a directory.+ls :: Optional FilePath -> Lua [FilePath]+ls fp = do+ let fp' = fromMaybe "." (fromOptional fp)+ ioToLua (Directory.listDirectory fp')++-- | Change current working directory.+chdir :: FilePath -> Lua ()+chdir fp = ioToLua $ Directory.setCurrentDirectory fp++-- | Return the current working directory.+currentdir :: Lua FilePath+currentdir = ioToLua Directory.getCurrentDirectory++-- | Retrieve the entire environment+env :: Lua NumResults+env = do+ kvs <- ioToLua Env.getEnvironment+ let addValue (k, v) = Lua.push k *> Lua.push v *> Lua.rawset (-3)+ Lua.newtable+ mapM_ addValue kvs+ return (NumResults 1)++-- | Returns the value of an environment variable+getenv :: String -> Lua (Optional String)+getenv name = ioToLua (Optional <$> Env.lookupEnv name)++-- | Set the specified environment variable to a new value.+setenv :: String -> String -> Lua ()+setenv name value = ioToLua (Env.setEnv name value)++-- | Get the current directory for temporary files.+tmpdirname :: Lua FilePath+tmpdirname = ioToLua Directory.getTemporaryDirectory++-- | Convert a System IO operation to a Lua operation.+ioToLua :: IO a -> Lua a+ioToLua action = do+ result <- Lua.liftIO (try action)+ case result of+ Right result' -> return result'+ Left err -> Lua.throwException (show (err :: IOException))
+ test/test-hslua-module-system.hs view
@@ -0,0 +1,54 @@+{-# LANGUAGE OverloadedStrings #-}+{-|+Module : Main+Copyright : © 2019 Albert Krewinkel+License : MIT+Maintainer : Albert Krewinkel <albert+hslua@zeitkraut.de>+Stability : alpha+Portability : Requires language extensions ForeignFunctionInterface,+ OverloadedStrings.++Tests for the `system` Lua module.+-}++import Control.Monad (void, when)+import Foreign.Lua (Lua)+import Foreign.Lua.Module.System (preloadModule, pushModule)+import Test.Tasty (TestTree, defaultMain, testGroup)+import Test.Tasty.HUnit (assertEqual, testCase)++import qualified Foreign.Lua as Lua++main :: IO ()+main = defaultMain $ testGroup "hslua-module-system" [tests]++-- | HSpec tests for the Lua 'system' module+tests :: TestTree+tests = testGroup "HsLua System module"+ [ testCase "system module can be pushed to the stack" $+ Lua.run (void pushModule)++ , testCase "text module can be added to the preloader" . Lua.run $ do+ Lua.openlibs+ preloadModule "system"+ assertEqual' "function not added to preloader" Lua.TypeFunction =<< do+ Lua.getglobal' "package.preload.system"+ Lua.ltype (-1)++ , testCase "text module can be loaded as hstext" . Lua.run $ do+ Lua.openlibs+ preloadModule "system"+ assertEqual' "loading the module fails " Lua.OK =<<+ Lua.dostring "require 'system'"++ , testCase "Lua tests pass" . Lua.run $ do+ Lua.openlibs+ preloadModule "system"+ assertEqual' "error while running lua tests" Lua.OK =<< do+ st <- Lua.loadfile "test/system-module-tests.lua"+ when (st == Lua.OK) $ Lua.call 0 0+ return st+ ]++assertEqual' :: (Show a, Eq a) => String -> a -> a -> Lua ()+assertEqual' msg expected = Lua.liftIO . assertEqual msg expected