diff --git a/Release-notes.md b/Release-notes.md index 210273c..0cd0a64 100644 --- a/Release-notes.md +++ b/Release-notes.md @@ -2,13 +2,20 @@ ## Latest changes +### New features + +- A new option **--version** has been added to every obitools command. + It will print the version of the command. +- In `obiscript` a `qualities` method has been added to retrieve or set the quality scores from a + BioSequence object. + ### Bug - In `obitag`, correct the wrong assignation of the **obitag_bestmatch** attribute. ## April 2nd, 2024. Release 4.2.0 -### New feature +### New features - A new OBITools named `obiscript` allows to process each sequence according to a Lua script. This is an experimental tool. The **--template** option allows for generating an example script on the `stdout`. diff --git a/pkg/obilua/lua_push_interface.go b/pkg/obilua/lua_push_interface.go index 4786191..a5092c6 100644 --- a/pkg/obilua/lua_push_interface.go +++ b/pkg/obilua/lua_push_interface.go @@ -37,15 +37,17 @@ func pushInterfaceToLua(L *lua.LState, val interface{}) { case []string: pushSliceStringToLua(L, v) case []int: - pushSliceIntToLua(L, v) + pushSliceNumericToLua(L, v) + case []byte: + pushSliceNumericToLua(L, v) case []float64: - pushSliceFloat64ToLua(L, v) + pushSliceNumericToLua(L, v) case []bool: pushSliceBoolToLua(L, v) case nil: L.Push(lua.LNil) default: - log.Fatalf("Cannot deal with value %v", val) + log.Fatalf("Cannot deal with value (%T) : %v", val, val) } } @@ -163,6 +165,21 @@ func pushSliceIntToLua(L *lua.LState, slice []int) { L.Push(luaTable) } +func pushSliceNumericToLua[T float64 | int | byte](L *lua.LState, slice []T) { + // Create a new Lua table + luaTable := L.NewTable() + + // Iterate over the Go slice and set the elements in the Lua table + for _, value := range slice { + // Append the value to the Lua table + // Lua is 1-indexed, so we use the length of the table + 1 as the next index + luaTable.Append(lua.LNumber(value)) + } + + // Push the Lua table onto the stack + L.Push(luaTable) +} + // pushSliceStringToLua creates a new Lua table and sets the elements in the table from the given Go slice. It then pushes the Lua table onto the stack. // // L *lua.LState - The Lua state diff --git a/pkg/obilua/lua_table.go b/pkg/obilua/lua_table.go index 1278377..61279c5 100644 --- a/pkg/obilua/lua_table.go +++ b/pkg/obilua/lua_table.go @@ -1,6 +1,10 @@ package obilua -import lua "github.com/yuin/gopher-lua" +import ( + "log" + + lua "github.com/yuin/gopher-lua" +) func Table2Interface(interpreter *lua.LState, table *lua.LTable) interface{} { // 07/03/2024: il y a sans doute plus efficace mais pour l'instant @@ -48,6 +52,39 @@ func Table2Interface(interpreter *lua.LState, table *lua.LTable) interface{} { } } +func Table2ByteSlice(interpreter *lua.LState, table *lua.LTable) []byte { + isArray := true + table.ForEach(func(key, value lua.LValue) { + if _, ok := key.(lua.LNumber); !ok { + isArray = false + } + }) + + if isArray { + val := make([]byte, table.Len()) + + for i := 1; i <= table.Len(); i++ { + v := table.RawGetInt(i) + switch v.Type() { + case lua.LTNumber: + if x:=float64(v.(lua.LNumber)); x <=255 { + val[i-1] = byte(x) + } else { + log.Fatalf("LUA: Value %f at index %d is to large to be converted to byte", x,i) + } + default: + log.Fatalf("LUA: Value %v at index %d cannot be converted to byte", v,i) + } + } + + return val + } + + log.Fatalf("LUA: Value %v cannot be converted to []byte", table) + + return nil +} + // } // return nil diff --git a/pkg/obilua/obiseq.go b/pkg/obilua/obiseq.go index c16ae09..e89ebf5 100644 --- a/pkg/obilua/obiseq.go +++ b/pkg/obilua/obiseq.go @@ -48,6 +48,7 @@ func newObiSeq(luaState *lua.LState) int { var bioSequenceMethods = map[string]lua.LGFunction{ "id": bioSequenceGetSetId, "sequence": bioSequenceGetSetSequence, + "qualities": bioSequenceGetSetQualities, "definition": bioSequenceGetSetDefinition, "count": bioSequenceGetSetCount, "taxid": bioSequenceGetSetTaxid, @@ -99,6 +100,29 @@ func bioSequenceGetSetSequence(luaState *lua.LState) int { return 1 } +func bioSequenceGetSetQualities(luaState *lua.LState) int { + s := checkBioSequence(luaState) + if luaState.GetTop() == 2 { + ud := luaState.CheckAny(2) + + // + // Perhaps the code needs some type checking on ud.Value + // It's a first test + // + + if v, ok := ud.(*lua.LTable); ok { + s.SetQualities(Table2ByteSlice(luaState, v)) + return 0 + } + + } + + value := []byte(s.Qualities()) + pushInterfaceToLua(luaState, value) + + return 1 +} + func bioSequenceGetSetDefinition(luaState *lua.LState) int { s := checkBioSequence(luaState) if luaState.GetTop() == 2 { diff --git a/pkg/obioptions/options.go b/pkg/obioptions/options.go index 9115628..24a4d60 100644 --- a/pkg/obioptions/options.go +++ b/pkg/obioptions/options.go @@ -21,6 +21,7 @@ var _BatchSize = 5000 var _Pprof = false var _Quality_Shift_Input = 33 var _Quality_Shift_Output = 33 +var _Version = "4.2.1" type ArgumentParser func([]string) (*getoptions.GetOpt, []string) @@ -30,6 +31,7 @@ func GenerateOptionParser(optionset ...func(*getoptions.GetOpt)) ArgumentParser options.SetMode(getoptions.Bundling) options.SetUnknownMode(getoptions.Fail) options.Bool("help", false, options.Alias("h", "?")) + options.Bool("version", false) options.BoolVar(&_Debug, "debug", false) options.BoolVar(&_Pprof, "pprof", false) @@ -62,6 +64,11 @@ func GenerateOptionParser(optionset ...func(*getoptions.GetOpt)) ArgumentParser os.Exit(1) } + if options.Called("version") { + fmt.Fprintf(os.Stderr, "obitools version %s\n", _Version) + os.Exit(0) + } + log.SetLevel(log.InfoLevel) if options.Called("debug") { log.SetLevel(log.DebugLevel)