]> git repos ~mattia - go-utility.git/commitdiff
Initial Commit
authorMattia Cabrini <dev@matiacabrini.com>
Thu, 6 Jul 2023 12:36:25 +0000 (14:36 +0200)
committerMattia Cabrini <dev@matiacabrini.com>
Thu, 6 Jul 2023 12:36:25 +0000 (14:36 +0200)
colour.go [new file with mode: 0644]
error.go [new file with mode: 0644]
go.mod [new file with mode: 0644]
less.go [new file with mode: 0644]
log.go [new file with mode: 0644]
methods_test.go [new file with mode: 0644]
net/command.go [new file with mode: 0644]
net/scanner.go [new file with mode: 0644]
reflect.go [new file with mode: 0644]
sync.go [new file with mode: 0644]
sync_fmt.go [new file with mode: 0644]

diff --git a/colour.go b/colour.go
new file mode 100644 (file)
index 0000000..287ca11
--- /dev/null
+++ b/colour.go
@@ -0,0 +1,61 @@
+/*
+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"
+}
diff --git a/error.go b/error.go
new file mode 100644 (file)
index 0000000..18615de
--- /dev/null
+++ b/error.go
@@ -0,0 +1,68 @@
+/*
+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
+}
diff --git a/go.mod b/go.mod
new file mode 100644 (file)
index 0000000..d67608d
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module utility
+
+go 1.18
diff --git a/less.go b/less.go
new file mode 100644 (file)
index 0000000..c790a87
--- /dev/null
+++ b/less.go
@@ -0,0 +1,61 @@
+/*
+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
+}
diff --git a/log.go b/log.go
new file mode 100644 (file)
index 0000000..795266c
--- /dev/null
+++ b/log.go
@@ -0,0 +1,110 @@
+/*
+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...)
+       }
+}
diff --git a/methods_test.go b/methods_test.go
new file mode 100644 (file)
index 0000000..1b340b2
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+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()
+       }
+}
diff --git a/net/command.go b/net/command.go
new file mode 100644 (file)
index 0000000..8058116
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+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]
+       }
+}
diff --git a/net/scanner.go b/net/scanner.go
new file mode 100644 (file)
index 0000000..f423006
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+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
+}
diff --git a/reflect.go b/reflect.go
new file mode 100644 (file)
index 0000000..bb7f2f5
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+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
+}
diff --git a/sync.go b/sync.go
new file mode 100644 (file)
index 0000000..eeaf647
--- /dev/null
+++ b/sync.go
@@ -0,0 +1,49 @@
+/*
+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()
+       }
+}
diff --git a/sync_fmt.go b/sync_fmt.go
new file mode 100644 (file)
index 0000000..edb4c1b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+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)
+}