codelib.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Package codelib provides Lua functions for storing Lua functions in a database
  2. package codelib
  3. import (
  4. log "github.com/sirupsen/logrus"
  5. lua "github.com/xyproto/gopher-lua"
  6. "github.com/xyproto/pinterface"
  7. )
  8. // Library class is for storing and loading Lua source code to and from a data structure.
  9. const (
  10. defaultID = "__lua_code_library"
  11. // Class identifies the Library class in Lua
  12. Class = "CODELIB"
  13. )
  14. // Get the first argument, "self", and cast it from userdata to a library (which is really a key/value).
  15. func checkLibrary(L *lua.LState) pinterface.IKeyValue {
  16. ud := L.CheckUserData(1)
  17. if hash, ok := ud.Value.(pinterface.IKeyValue); ok {
  18. return hash
  19. }
  20. L.ArgError(1, "code library expected")
  21. return nil
  22. }
  23. // Given a namespace, register Lua code.
  24. // Takes two strings, returns true if successful.
  25. func libAdd(L *lua.LState) int {
  26. lualib := checkLibrary(L) // arg 1
  27. namespace := L.ToString(2)
  28. if namespace == "" {
  29. L.ArgError(2, "namespace expected")
  30. }
  31. code := L.ToString(3)
  32. if code == "" {
  33. log.Warn("Empty Lua code given to codelib:add")
  34. L.Push(lua.LBool(false))
  35. return 1
  36. }
  37. // Append the new code to the old code, if any
  38. oldcode, err := lualib.Get(namespace)
  39. if err != nil {
  40. oldcode = ""
  41. } else {
  42. oldcode += "\n"
  43. }
  44. L.Push(lua.LBool(nil == lualib.Set(namespace, oldcode+code)))
  45. return 1 // number of results
  46. }
  47. // Given a namespace, register Lua code as the only code.
  48. // Takes two strings, returns true if successful.
  49. func libSet(L *lua.LState) int {
  50. lualib := checkLibrary(L) // arg 1
  51. namespace := L.ToString(2)
  52. if namespace == "" {
  53. L.ArgError(2, "namespace expected")
  54. }
  55. code := L.ToString(3)
  56. // Empty string is fine, for clearing a key
  57. //if code == "" {
  58. // log.Warn("Empty Lua code given to codelib:set")
  59. // L.Push(lua.LBool(false))
  60. // return 1
  61. //}
  62. L.Push(lua.LBool(nil == lualib.Set(namespace, code)))
  63. return 1 // number of results
  64. }
  65. // Given a namespace, return Lua code, or an empty string.
  66. func libGet(L *lua.LState) int {
  67. lualib := checkLibrary(L) // arg 1
  68. namespace := L.ToString(2)
  69. if namespace == "" {
  70. L.ArgError(2, "namespace expected")
  71. }
  72. // Retrieve the Lua code from the HashMap
  73. code, err := lualib.Get(namespace)
  74. if err != nil {
  75. // Return an empty string if there was an error
  76. L.Push(lua.LString(""))
  77. }
  78. // Return the requested Lua code
  79. L.Push(lua.LString(code))
  80. return 1 // number of results
  81. }
  82. // Given a namespace, fetch all registered Lua code as a string.
  83. // Then run the Lua code for this LState.
  84. // Returns true of successful.
  85. func libImport(L *lua.LState) int {
  86. lualib := checkLibrary(L) // arg 1
  87. namespace := L.ToString(2)
  88. if namespace == "" {
  89. L.ArgError(2, "namespace expected")
  90. }
  91. code, err := lualib.Get(namespace)
  92. if err != nil {
  93. // Return false if there was an error
  94. L.Push(lua.LBool(false)) // error
  95. return 1
  96. }
  97. if err := L.DoString(code); err != nil {
  98. L.Close()
  99. log.Errorf("Error when importing Lua code:\n%s", err)
  100. L.Push(lua.LBool(false)) // error
  101. return 1 // number of results
  102. }
  103. L.Push(lua.LBool(true)) // ok
  104. return 1 // number of results
  105. }
  106. // String representation
  107. func libToString(L *lua.LState) int {
  108. L.Push(lua.LString("Code Library"))
  109. return 1 // number of results
  110. }
  111. // Clear the current code library
  112. func libClear(L *lua.LState) int {
  113. lualib := checkLibrary(L) // arg 1
  114. L.Push(lua.LBool(nil == lualib.Remove()))
  115. return 1 // number of results
  116. }
  117. // Create a new code library.
  118. // id is the name of the hash map.
  119. func newCodeLibrary(L *lua.LState, creator pinterface.ICreator, id string) (*lua.LUserData, error) {
  120. // Create a new Lua Library (hash map)
  121. lualib, err := creator.NewKeyValue(id)
  122. if err != nil {
  123. return nil, err
  124. }
  125. // Create a new userdata struct
  126. ud := L.NewUserData()
  127. ud.Value = lualib
  128. L.SetMetatable(ud, L.GetTypeMetatable(Class))
  129. return ud, nil
  130. }
  131. // The hash map methods that are to be registered
  132. var libMethods = map[string]lua.LGFunction{
  133. "__tostring": libToString,
  134. "add": libAdd,
  135. "set": libSet,
  136. "get": libGet,
  137. "import": libImport,
  138. "clear": libClear,
  139. }
  140. // Load makes functions related to building a library of Lua code available
  141. func Load(L *lua.LState, creator pinterface.ICreator) {
  142. // Register the Library class and the methods that belongs with it.
  143. mt := L.NewTypeMetatable(Class)
  144. mt.RawSetH(lua.LString("__index"), mt)
  145. L.SetFuncs(mt, libMethods)
  146. // The constructor for new Libraries takes only an optional id
  147. L.SetGlobal("CodeLib", L.NewFunction(func(L *lua.LState) int {
  148. // Check if the optional argument is given
  149. id := defaultID
  150. if L.GetTop() == 1 {
  151. id = L.ToString(1)
  152. }
  153. // Create a new Library in Lua
  154. userdata, err := newCodeLibrary(L, creator, id)
  155. if err != nil {
  156. L.Push(lua.LNil)
  157. L.Push(lua.LString(err.Error()))
  158. L.Push(lua.LNumber(1))
  159. return 3 // Number of returned values
  160. }
  161. // Return the hash map object
  162. L.Push(userdata)
  163. return 1 // number of results
  164. }))
  165. }