From: Mattia Cabrini Date: Thu, 6 Jul 2023 12:36:25 +0000 (+0200) Subject: Initial Commit X-Git-Tag: v0.0.0~1 X-Git-Url: https://git.theboydaily.dev/mattia?a=commitdiff_plain;h=227c57adcf8d52c826820acbcae2b7b8df20be89;p=go-utility.git Initial Commit --- diff --git a/colour.go b/colour.go new file mode 100644 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 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 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 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 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 index 0000000..1b340b2 --- /dev/null +++ b/methods_test.go @@ -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 index 0000000..8058116 --- /dev/null +++ b/net/command.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 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 index 0000000..f423006 --- /dev/null +++ b/net/scanner.go @@ -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 index 0000000..bb7f2f5 --- /dev/null +++ b/reflect.go @@ -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 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 index 0000000..edb4c1b --- /dev/null +++ b/sync_fmt.go @@ -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) +}