55 "runtime"
66
77 "github.com/AdRoll/baker"
8+
89 lua "github.com/yuin/gopher-lua"
910)
1011
@@ -16,11 +17,13 @@ var LUADesc = baker.FilterDesc{
1617 Help : `Run a baker filter defined in a lua script` ,
1718}
1819
20+ // LUAConfig holds the configuration for the LUA filter.
1921type LUAConfig struct {
2022 Script string `help:"Path to the lua script where the baker filter is defined" required:"true"`
2123 FilterName string `help:"Name of the lua function to run as baker filter" required:"true"`
2224}
2325
26+ // LUA allows to run a baker filter from an external script written in lua.
2427type LUA struct {
2528 l * lua.LState // lua state used during all the baker filter lifetime
2629 ud * lua.LUserData // pre-allocated (reused) userdata for the processed record
@@ -29,6 +32,7 @@ type LUA struct {
2932 next func (baker.Record )
3033}
3134
35+ // NewLUA returns a new LUA filter.
3236func NewLUA (cfg baker.FilterParams ) (baker.Filter , error ) {
3337 dcfg := cfg .DecodedConfig .(* LUAConfig )
3438
@@ -69,9 +73,20 @@ func NewLUA(cfg baker.FilterParams) (baker.Filter, error) {
6973 return f , nil
7074}
7175
76+ // registerLUATypes registers, in the given lua state, some lua types
77+ // and utility functions useful to run a baker filter:
78+ // - the record type
79+ // - createRecord function (creates and returns a new record)
80+ // - validateRecord function (takes a record and returns a boolean and a
81+ // number), see baker.ComponentParams.ValidateRecord
82+ // - fieldByName function (returns a field index given its name, or nil
83+ // if the given field name doesn't exist)
84+ // - fieldNames, an lua table where field names are indexed by their field
85+ // field indexes
7286func registerLUATypes (l * lua.LState , comp baker.ComponentParams ) {
7387 registerLUARecordType (l )
7488
89+ // Registers the 'createRecord' lua function.
7590 l .SetGlobal ("createRecord" , l .NewFunction (func (L * lua.LState ) int {
7691 rec := comp .CreateRecord ()
7792 ud := recordToLua (l , rec )
@@ -106,8 +121,14 @@ func registerLUATypes(l *lua.LState, comp baker.ComponentParams) {
106121 l .SetGlobal ("fieldNames" , fields )
107122}
108123
124+ // TODO: at the moment LUA filter doesn't publish stats.
125+ // There are multiple ways to do it, either require the filter to update
126+ // the numbers of processed and filtered records, or deduce them automatically
127+ // by hooking into next and Process functions (if that proves too costly
128+ // this could be disabled in configuration.
109129func (t * LUA ) Stats () baker.FilterStats { return baker.FilterStats {} }
110130
131+ // Process forwards records to the lua-written filter.
111132func (t * LUA ) Process (rec baker.Record , next func (baker.Record )) {
112133 // Modify the record inside the pre-allocated user value
113134 t .ud .Value = & luaRecord {r : rec }
@@ -122,6 +143,8 @@ func (t *LUA) Process(rec baker.Record, next func(baker.Record)) {
122143 Protect : true ,
123144 }, t .ud , t .luaNext )
124145
146+ // TODO: should not panic here and instead increment a filter-specific
147+ // metric that tracks the number of lua runtime errors.
125148 if err != nil {
126149 panic (err )
127150 }
@@ -131,19 +154,25 @@ func (t *LUA) Process(rec baker.Record, next func(baker.Record)) {
131154
132155const luaRecordTypeName = "record"
133156
157+ // registers the 'record' type into the given lua state.
134158func registerLUARecordType (L * lua.LState ) {
135159 mt := L .NewTypeMetatable (luaRecordTypeName )
136160 L .SetGlobal (luaRecordTypeName , mt )
137161 L .SetField (mt , "__index" , L .SetFuncs (L .NewTable (), luaRecordMethods ))
138162}
139163
164+ // converts a baker.Record to lua user data, suitable to be pushed onto
165+ // an lua stack.
140166func recordToLua (L * lua.LState , r baker.Record ) * lua.LUserData {
141167 ud := L .NewUserData ()
142168 ud .Value = & luaRecord {r : r }
143169 L .SetMetatable (ud , L .GetTypeMetatable (luaRecordTypeName ))
144170 return ud
145171}
146172
173+ // checks that the element at current stack index n is an lua
174+ // user data, holding an luaRecord, and returns it. Raises an lua
175+ // runtime error if the element is not a record.
147176func checkLuaRecord (L * lua.LState , n int ) * luaRecord {
148177 ud := L .CheckUserData (n )
149178 if v , ok := ud .Value .(* luaRecord ); ok {
@@ -153,17 +182,21 @@ func checkLuaRecord(L *lua.LState, n int) *luaRecord {
153182 return nil
154183}
155184
185+ // faster version of checkLuaRecord that panics if the stack element
186+ // is not a record, rather than raising an lua runtime error.
156187func fastcheckLuaRecord (L * lua.LState , n int ) * luaRecord {
157188 return L .Get (n ).(* lua.LUserData ).Value .(* luaRecord )
158189}
159190
191+ // holds the lua-bound methods of baker.Record.
160192var luaRecordMethods = map [string ]lua.LGFunction {
161193 "get" : luaRecordGet ,
162194 "set" : luaRecordSet ,
163195 "copy" : luaRecordCopy ,
164196 "clear" : luaRecordClear ,
165197}
166198
199+ // lua wrapper over a baker Record.
167200type luaRecord struct {
168201 r baker.Record
169202}
0 commit comments