123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186 |
- // Package codelib provides Lua functions for storing Lua functions in a database
- package codelib
- import (
- log "github.com/sirupsen/logrus"
- lua "github.com/xyproto/gopher-lua"
- "github.com/xyproto/pinterface"
- )
- // Library class is for storing and loading Lua source code to and from a data structure.
- const (
- defaultID = "__lua_code_library"
- // Class identifies the Library class in Lua
- Class = "CODELIB"
- )
- // Get the first argument, "self", and cast it from userdata to a library (which is really a key/value).
- func checkLibrary(L *lua.LState) pinterface.IKeyValue {
- ud := L.CheckUserData(1)
- if hash, ok := ud.Value.(pinterface.IKeyValue); ok {
- return hash
- }
- L.ArgError(1, "code library expected")
- return nil
- }
- // Given a namespace, register Lua code.
- // Takes two strings, returns true if successful.
- func libAdd(L *lua.LState) int {
- lualib := checkLibrary(L) // arg 1
- namespace := L.ToString(2)
- if namespace == "" {
- L.ArgError(2, "namespace expected")
- }
- code := L.ToString(3)
- if code == "" {
- log.Warn("Empty Lua code given to codelib:add")
- L.Push(lua.LBool(false))
- return 1
- }
- // Append the new code to the old code, if any
- oldcode, err := lualib.Get(namespace)
- if err != nil {
- oldcode = ""
- } else {
- oldcode += "\n"
- }
- L.Push(lua.LBool(nil == lualib.Set(namespace, oldcode+code)))
- return 1 // number of results
- }
- // Given a namespace, register Lua code as the only code.
- // Takes two strings, returns true if successful.
- func libSet(L *lua.LState) int {
- lualib := checkLibrary(L) // arg 1
- namespace := L.ToString(2)
- if namespace == "" {
- L.ArgError(2, "namespace expected")
- }
- code := L.ToString(3)
- // Empty string is fine, for clearing a key
- //if code == "" {
- // log.Warn("Empty Lua code given to codelib:set")
- // L.Push(lua.LBool(false))
- // return 1
- //}
- L.Push(lua.LBool(nil == lualib.Set(namespace, code)))
- return 1 // number of results
- }
- // Given a namespace, return Lua code, or an empty string.
- func libGet(L *lua.LState) int {
- lualib := checkLibrary(L) // arg 1
- namespace := L.ToString(2)
- if namespace == "" {
- L.ArgError(2, "namespace expected")
- }
- // Retrieve the Lua code from the HashMap
- code, err := lualib.Get(namespace)
- if err != nil {
- // Return an empty string if there was an error
- L.Push(lua.LString(""))
- }
- // Return the requested Lua code
- L.Push(lua.LString(code))
- return 1 // number of results
- }
- // Given a namespace, fetch all registered Lua code as a string.
- // Then run the Lua code for this LState.
- // Returns true of successful.
- func libImport(L *lua.LState) int {
- lualib := checkLibrary(L) // arg 1
- namespace := L.ToString(2)
- if namespace == "" {
- L.ArgError(2, "namespace expected")
- }
- code, err := lualib.Get(namespace)
- if err != nil {
- // Return false if there was an error
- L.Push(lua.LBool(false)) // error
- return 1
- }
- if err := L.DoString(code); err != nil {
- L.Close()
- log.Errorf("Error when importing Lua code:\n%s", err)
- L.Push(lua.LBool(false)) // error
- return 1 // number of results
- }
- L.Push(lua.LBool(true)) // ok
- return 1 // number of results
- }
- // String representation
- func libToString(L *lua.LState) int {
- L.Push(lua.LString("Code Library"))
- return 1 // number of results
- }
- // Clear the current code library
- func libClear(L *lua.LState) int {
- lualib := checkLibrary(L) // arg 1
- L.Push(lua.LBool(nil == lualib.Remove()))
- return 1 // number of results
- }
- // Create a new code library.
- // id is the name of the hash map.
- func newCodeLibrary(L *lua.LState, creator pinterface.ICreator, id string) (*lua.LUserData, error) {
- // Create a new Lua Library (hash map)
- lualib, err := creator.NewKeyValue(id)
- if err != nil {
- return nil, err
- }
- // Create a new userdata struct
- ud := L.NewUserData()
- ud.Value = lualib
- L.SetMetatable(ud, L.GetTypeMetatable(Class))
- return ud, nil
- }
- // The hash map methods that are to be registered
- var libMethods = map[string]lua.LGFunction{
- "__tostring": libToString,
- "add": libAdd,
- "set": libSet,
- "get": libGet,
- "import": libImport,
- "clear": libClear,
- }
- // Load makes functions related to building a library of Lua code available
- func Load(L *lua.LState, creator pinterface.ICreator) {
- // Register the Library class and the methods that belongs with it.
- mt := L.NewTypeMetatable(Class)
- mt.RawSetH(lua.LString("__index"), mt)
- L.SetFuncs(mt, libMethods)
- // The constructor for new Libraries takes only an optional id
- L.SetGlobal("CodeLib", L.NewFunction(func(L *lua.LState) int {
- // Check if the optional argument is given
- id := defaultID
- if L.GetTop() == 1 {
- id = L.ToString(1)
- }
- // Create a new Library in Lua
- userdata, err := newCodeLibrary(L, creator, id)
- if err != nil {
- L.Push(lua.LNil)
- L.Push(lua.LString(err.Error()))
- L.Push(lua.LNumber(1))
- return 3 // Number of returned values
- }
- // Return the hash map object
- L.Push(userdata)
- return 1 // number of results
- }))
- }
|