--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+var (
+ Reset string = "\033[0m"
+ Red = "\033[31m"
+ Green = "\033[32m"
+ Yellow = "\033[33m"
+ Blue = "\033[34m"
+ Purple = "\033[35m"
+ Cyan = "\033[36m"
+ Gray = "\033[37m"
+ White = "\033[97m"
+)
+
+func NotInteractive() {
+ Reset = ""
+ Red = ""
+ Green = ""
+ Yellow = ""
+ Blue = ""
+ Purple = ""
+ Cyan = ""
+ Gray = ""
+ White = ""
+}
+
+func Interactive() {
+ Reset = "\033[0m"
+ Red = "\033[31m"
+ Green = "\033[32m"
+ Yellow = "\033[33m"
+ Blue = "\033[34m"
+ Purple = "\033[35m"
+ Cyan = "\033[36m"
+ Gray = "\033[37m"
+ White = "\033[97m"
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import (
+ "fmt"
+ "runtime"
+)
+
+func Mypanic(err error) {
+ if err == nil {
+ return
+ }
+
+ Logf(FATAL, "%v", err)
+}
+
+func Deferrable(f func() error, pre func(), post func()) {
+ if pre != nil {
+ pre()
+ }
+ Mypanic(f())
+ if post != nil {
+ post()
+ }
+}
+
+func AppendError(err error) error {
+ if err == nil {
+ return nil
+ }
+ var fileName = trace()[1]
+ return fmt.Errorf("%s: %v", fileName, err)
+}
+
+func trace() (receipts []string) {
+ var p = make([]uintptr, 10)
+ runtime.Callers(2, p)
+
+ for _, pcx := range p {
+ f := runtime.FuncForPC(pcx)
+ receipts = append(receipts, f.Name())
+ }
+
+ return
+}
--- /dev/null
+module utility
+
+go 1.18
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+)
+
+func Lessf(format string, args ...interface{}) error {
+ str := fmt.Sprintf(format, args...)
+ return less(str)
+}
+
+func less(str string) (err error) {
+ echo := exec.Command("echo", str)
+ echo.Stdin = os.Stdin
+ echo.Stderr = os.Stderr
+
+ lessCmd := exec.Command("less")
+ lessCmd.Stdout = os.Stdout
+
+ if lessCmd.Stdin, err = echo.StdoutPipe(); err != nil {
+ return
+ }
+
+ if err = lessCmd.Start(); err != nil {
+ return
+ }
+ if err = echo.Run(); err != nil {
+ return
+ }
+ if err = lessCmd.Wait(); err != nil {
+ return
+ }
+
+ return
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import (
+ "fmt"
+ "os"
+ "sync"
+ "testing"
+)
+
+type LogLevel int
+
+const (
+ FATAL LogLevel = (1 << iota) >> 1
+ ERROR
+ WARNING
+ INFO
+ VERBOSE
+)
+
+var levelToString = map[LogLevel]string{
+ FATAL: " FATAL ",
+ ERROR: " ERROR ",
+ WARNING: "WARNING",
+ INFO: " INFO ",
+ VERBOSE: "VERBOSE",
+}
+
+func getLevelColor(level LogLevel) string {
+ switch level {
+ case FATAL:
+ return Red
+ case ERROR:
+ return Red
+ case WARNING:
+ return Blue
+ case INFO:
+ return Green
+ }
+
+ return Gray
+}
+
+var logGuard *sync.Mutex = &sync.Mutex{}
+var MaximumLevel LogLevel = WARNING
+
+func Logf(level LogLevel, format string, args ...interface{}) {
+ logGuard.Lock()
+ defer logGuard.Unlock()
+
+ if level > MaximumLevel {
+ return
+ }
+
+ format = logfCompose(level, format)
+
+ if level == FATAL {
+ _, _ = fmt.Fprintf(os.Stderr, format, args...)
+ os.Exit(1)
+ } else {
+ _, _ = fmt.Fprintf(os.Stderr, format, args...)
+ }
+}
+
+func logfCompose(level LogLevel, format string) string {
+ format = "[" +
+ getLevelColor(level) + levelToString[level] + Reset +
+ "] " + format
+
+ if format[len(format)-1] != '\n' {
+ format = format + "\n"
+ }
+ return format
+}
+
+func Tlogf(t *testing.T, level LogLevel, format string, args ...interface{}) {
+ logGuard.Lock()
+ defer logGuard.Unlock()
+
+ format = logfCompose(level, format)
+
+ if level == FATAL || level == ERROR {
+ t.Errorf(format, args...)
+ } else {
+ t.Logf(format, args...)
+ }
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import "testing"
+
+type testType struct {
+ A int
+ B int
+}
+
+func (t *testType) GetAB(a, b int) (int, int) {
+ return a + t.A, b + t.B
+}
+
+func (t *testType) GetABinterface(a int, b interface{}) (int, int) {
+ return a + t.A, 0
+}
+
+func (t *testType) GetABstruct(a int, b testType) (int, int) {
+ return a + t.A, t.B + b.B
+}
+
+func TestMethodOk0(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "GetAB", "")
+
+ if f == nil {
+ t.Fail()
+ return
+ }
+
+ results, err := f(0, 1)
+
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if results[0].(int) != 0 {
+ t.Error(0)
+ }
+ if results[1].(int) != 2 {
+ t.Error(1)
+ }
+}
+
+func TestMethodOk1(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "GetAB", "interface")
+
+ if f == nil {
+ t.Fail()
+ return
+ }
+
+ results, err := f(0, 1)
+
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if results[0].(int) != 0 {
+ t.Error(0)
+ }
+ if results[1].(int) != 0 {
+ t.Error(1)
+ }
+}
+
+func TestMethodOk2(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "GetAB", "struct")
+
+ if f == nil {
+ t.Fail()
+ return
+ }
+
+ results, err := f(0, obj)
+
+ if err != nil {
+ t.Error(err)
+ return
+ }
+
+ if results[0].(int) != 0 {
+ t.Error(0)
+ }
+ if results[1].(int) != 2 {
+ t.Error(1)
+ }
+}
+
+func TestMethodArgCountKo(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "Get", "AB")
+
+ if f == nil {
+ t.Fail()
+ return
+ }
+
+ _, err := f(0)
+
+ if err == nil {
+ t.Fail()
+ }
+}
+
+func TestMethodArgTypeKo(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "Get", "AB")
+
+ if f == nil {
+ t.Fail()
+ return
+ }
+
+ _, err := f(0, "1")
+
+ if err == nil {
+ t.Fail()
+ }
+}
+
+func TestMethodNameKo(t *testing.T) {
+ obj := testType{0, 1}
+
+ f := GetMethod(&obj, "Get2", "AB")
+
+ if f != nil {
+ t.Fail()
+ }
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package net
+
+import (
+ "fmt"
+ "io"
+ "strconv"
+)
+
+type Command struct {
+ Params map[string]string
+ Data []byte
+}
+
+func (c *Command) readData(r io.Reader) (err error) {
+ var nTot, n int
+ c.Data = make([]byte, c.GetParamInt("Content-Length"))
+
+ if cap(c.Data) == 0 {
+ return
+ }
+
+ for nTot < cap(c.Data) && err == nil {
+ n, err = r.Read(c.Data[nTot:])
+ nTot += n
+ }
+
+ return
+}
+
+func (c *Command) GetParamInt(param string) (n int) {
+ n, _ = strconv.Atoi(c.GetParam(param))
+ return
+}
+
+func (c *Command) GetParam(param string) (str string) {
+ str, b := c.Params[param]
+
+ if !b {
+ str = ""
+ }
+
+ return
+}
+
+func (c *Command) Print(w io.Writer) (err error) {
+ for k, v := range c.Params {
+ str := fmt.Sprintf("%s: %s\r\n", k, v)
+
+ _, err = w.Write([]byte(str))
+ if err != nil {
+ break
+ }
+ }
+
+ _, err = w.Write([]byte("\r\n"))
+ if err == nil {
+ _, err = w.Write(c.Data)
+ }
+
+ return
+}
+
+func splitCommandValue(str string) (comm string, value string) {
+ var i int
+
+ for _, commx := range str {
+ if commx == ':' {
+ break
+ }
+
+ comm += string(commx)
+ i++
+ }
+
+ if len(str) >= i+2 {
+ value = str[i+2:]
+ }
+
+ return
+}
+
+func (c *Command) InheritParameters(oth *Command, paramNames ...string) {
+ for _, px := range paramNames {
+ c.Params[px] = oth.Params[px]
+ }
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package net
+
+import (
+ "io"
+)
+
+type Scanner struct {
+ r io.Reader
+
+ buffer []byte
+ pos int
+ len int
+ command *Command
+}
+
+func NewScanner(r io.Reader, netBufSize int) *Scanner {
+ return &Scanner{
+ r: r,
+ pos: 0,
+ len: 0,
+ buffer: make([]byte, netBufSize),
+ }
+}
+
+func (s *Scanner) Scan() (err error) {
+ var line string
+ var lineComplete bool
+
+ s.command = &Command{
+ Params: make(map[string]string),
+ }
+
+ for err == nil && line != "\r\n" {
+ if err = s.read(); err != nil {
+ break
+ }
+
+ line, lineComplete = s.lineFromBuf(s.buffer, line)
+
+ for lineComplete {
+ if line == "\r\n" {
+ break
+ }
+
+ line = line[:len(line)-2] // removing trailing \r\n
+
+ command, value := splitCommandValue(line)
+ s.command.Params[command] = value
+ line = ""
+
+ line, lineComplete = s.lineFromBuf(s.buffer, line)
+ }
+ }
+
+ if err == nil {
+ err = s.command.readData(s.r)
+ s.pos = s.len
+ }
+
+ return
+}
+
+func (s *Scanner) lineFromBuf(buf []byte, part string) (line string, complete bool) {
+ var readCR bool
+ var rx byte
+ var i int
+
+ line = part
+
+ if s.pos == s.len {
+ return
+ }
+
+BUFER_LOOP:
+ for i, rx = range s.buffer[s.pos:s.len] {
+ line += string(rx)
+
+ switch rx {
+ case '\r':
+ readCR = true
+ case '\n':
+ if readCR {
+ complete = true
+ break BUFER_LOOP
+ }
+ default:
+ readCR = false
+ }
+ }
+
+ s.pos += i + 1
+
+ return
+}
+
+func (s *Scanner) read() (err error) {
+ if s.pos == s.len {
+ s.len, err = s.r.Read(s.buffer)
+ s.pos = 0
+ }
+
+ return
+}
+
+func (s *Scanner) Command() *Command {
+ return s.command
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import (
+ "fmt"
+ "reflect"
+)
+
+type genericFunc func(...interface{}) ([]interface{}, error)
+
+func checkMethodArgs(methodVO reflect.Method, args []reflect.Value) error {
+ if m, a := methodVO.Type.NumIn(), len(args); m != a+1 {
+ return fmt.Errorf("expected %d arguments, got %d", m, a)
+ }
+
+ for i, arg := range args {
+ argTypeExpected := methodVO.Type.In(i + 1)
+
+ if i == 0 {
+ continue // skip the object itself
+ }
+
+ if !arg.Type().AssignableTo(argTypeExpected) {
+ return fmt.Errorf("expected %v argument, got %v (parameter #%d)", argTypeExpected.Kind(), arg.Kind(), i)
+ }
+ }
+
+ return nil
+}
+
+func newGenericFunc(obj interface{}, methodTO reflect.Method, methodVO reflect.Value) genericFunc {
+ return func(argsI ...interface{}) (returnValues []interface{}, argError error) {
+ var args = make([]reflect.Value, len(argsI))
+ for i, arg := range argsI {
+ args[i] = reflect.ValueOf(arg)
+ }
+
+ if argError = checkMethodArgs(methodTO, args); argError != nil {
+ return
+ }
+
+ returnValuesVO := methodVO.Call(args)
+
+ returnValues = make([]interface{}, len(returnValuesVO))
+ for i, returnValueVO := range returnValuesVO {
+ returnValues[i] = returnValueVO.Interface()
+ }
+
+ return
+ }
+}
+
+func GetMethod(obj interface{}, name string, suffix string) genericFunc {
+ to := reflect.TypeOf(obj)
+ vo := reflect.ValueOf(obj)
+
+ methodTO, b := to.MethodByName(name + suffix)
+
+ if b {
+ methodVO := vo.MethodByName(name + suffix)
+
+ return newGenericFunc(obj, methodTO, methodVO)
+ }
+
+ return nil
+}
+
+var typeDefault = map[reflect.Kind]interface{}{
+ reflect.String: "",
+ reflect.Int64: int64(0),
+ reflect.Int32: int32(0),
+ reflect.Int16: int16(0),
+ reflect.Int: int(0),
+ reflect.Float64: float64(0),
+ reflect.Float32: float32(0),
+}
+
+func TypeDefault(k reflect.Kind) (v interface{}, b bool) {
+ v, b = typeDefault[k]
+ return
+}
+
+func IPtrToI(fv reflect.Value, i interface{}) (a interface{}) {
+ var diPtr = i.(*interface{})
+ var di = *diPtr
+
+ switch fv.Kind() {
+ case reflect.String:
+ a = di.(string)
+ case reflect.Int64:
+ a = di.(*int64)
+ case reflect.Int16:
+ a = di.(*int16)
+ case reflect.Int32:
+ a = di.(*int32)
+ case reflect.Int:
+ a = di.(*int)
+ case reflect.Float32:
+ a = di.(*float32)
+ case reflect.Float64:
+ a = di.(*float64)
+ }
+
+ return
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+type lockable interface {
+ Lock()
+ Unlock()
+}
+
+type rlockable interface {
+ RLock()
+ RUnlock()
+}
+
+func Monitor(mu lockable) func() {
+ mu.Lock()
+ return func() {
+ mu.Unlock()
+ }
+}
+
+func RMonitor(mu rlockable) func() {
+ mu.RLock()
+ return func() {
+ mu.RUnlock()
+ }
+}
--- /dev/null
+/*
+MIT License
+
+Copyright (c) 2023 Mattia Cabrini
+
+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.
+*/
+
+package utility
+
+import (
+ "fmt"
+ "io"
+ "os"
+ "sync"
+)
+
+var syncIoGuard = &sync.Mutex{}
+
+func Fprintf(fp io.Writer, format string, args ...any) {
+ defer Monitor(syncIoGuard)()
+
+ fmt.Fprint(fp, format, args)
+}
+
+func Printf(format string, args ...any) {
+ defer Monitor(syncIoGuard)()
+
+ fmt.Fprint(os.Stdout, format, args)
+}