diff --git a/config/go.mod b/config/go.mod new file mode 100644 index 0000000..d08f1ae --- /dev/null +++ b/config/go.mod @@ -0,0 +1,5 @@ +module gitea.meta-tech.academy/go/core/config + +go 1.20 + +require gopkg.in/yaml.v3 v3.0.1 diff --git a/config/go.sum b/config/go.sum new file mode 100644 index 0000000..a62c313 --- /dev/null +++ b/config/go.sum @@ -0,0 +1,4 @@ +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/config/loader.go b/config/loader.go new file mode 100644 index 0000000..d8af616 --- /dev/null +++ b/config/loader.go @@ -0,0 +1,110 @@ +package config + +import ( + "os" + "reflect" + "regexp" + "strings" + + "gopkg.in/yaml.v3" +) + +type Cfg interface { + GetVar() []VarConfig +} + +type VarConfig struct { + Name string `yaml:"name"` + Value string `yaml:"value"` +} + +type ConfigLoader struct { + Var []VarConfig + Config interface{} + varDef *VarPattern + refstr []*reflect.Value +} + +func LoadConfig(path string, out Cfg, varPattern *VarPattern) { + cl := &ConfigLoader{varDef: varPattern, refstr: []*reflect.Value{}} + data, err := os.ReadFile(path) + if err != nil { + panic(err) + } + if err = yaml.Unmarshal(data, out); err != nil { + panic(err) + } + cl.Var = out.GetVar() + cl.Config = out + cl.parse() + cl.refstr = []*reflect.Value{} + // cl.setConfigPtr(outi) +} + +func (cl *ConfigLoader) findVarName(match string) string { + re := regexp.MustCompile(cl.varDef.getPatternCaptureName()) + parts := re.FindStringSubmatch(match) + if len(parts) > 0 { + match = parts[1] + } + return match +} + +func (cl *ConfigLoader) findVarValue(v string) string { + names := strings.SplitN(v, ".", 2) + if names[0] == "var" { + for _, varConfig := range cl.Var { + if varConfig.Name == names[1] { + v = varConfig.Value + } + } + } + return v +} + +func (cl *ConfigLoader) findStringRef(ref reflect.Value) { + switch ref.Kind() { + case reflect.Ptr, reflect.Interface: + cl.findStringRef(ref.Elem()) + case reflect.Struct: + for i := 0; i < ref.NumField(); i++ { + refc := ref.Field(i) + cl.findStringRef(refc) + } + case reflect.Slice: + if !ref.IsNil() { + for i := 0; i < ref.Len(); i++ { + refc := ref.Index(i) + cl.findStringRef(refc) + } + } + case reflect.String: + if ref.Len() > 0 { + cl.refstr = append(cl.refstr, &ref) + } + } +} + +func (cl *ConfigLoader) parse() { + var name string + var tmp string + var parts []string + var re2 *regexp.Regexp + + cl.findStringRef(reflect.ValueOf(&cl)) + + re := regexp.MustCompile(cl.varDef.getPatternVar()) + for _, ref := range cl.refstr { + tmp = ref.String() + parts = re.FindStringSubmatch(tmp) + if len(parts) > 0 { + s := strings.SplitAfter(parts[1], cl.varDef.Close) + for _, part := range s { + name = cl.findVarName(part) + re2 = regexp.MustCompile(cl.varDef.getPatternVarName(name)) + tmp = re2.ReplaceAllString(tmp, cl.findVarValue(name)) + } + ref.SetString(tmp) + } + } +} diff --git a/config/varpattern.go b/config/varpattern.go new file mode 100644 index 0000000..85e26d7 --- /dev/null +++ b/config/varpattern.go @@ -0,0 +1,31 @@ +package config + +import ( + "fmt" + "regexp" +) + +type VarPattern struct { + Open string + Close string +} + +func (vp *VarPattern) getPatternCaptureName() string { + return fmt.Sprintf("%s([^%s]+)%s", regexp.QuoteMeta(vp.Open), regexp.QuoteMeta(string(vp.Close[:1])), regexp.QuoteMeta((vp.Close))) +} + +func (vp *VarPattern) getPatternVar() string { + return fmt.Sprintf("(%s.+%s)", regexp.QuoteMeta(vp.Open), regexp.QuoteMeta(vp.Close)) +} + +func (vp *VarPattern) getPatternVarName(name string) string { + return fmt.Sprintf("%s%s%s", regexp.QuoteMeta(vp.Open), name, regexp.QuoteMeta(vp.Close)) +} + +func NewVarPattern(open string, close string) *VarPattern { + return &VarPattern{open, close} +} + +func DefaultVarPattern() *VarPattern { + return NewVarPattern("{%", "%}") +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..443c0a2 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module gitea.meta-tech.academy/go/core + +go 1.20 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e69de29 diff --git a/go.work b/go.work new file mode 100644 index 0000000..73678c0 --- /dev/null +++ b/go.work @@ -0,0 +1,8 @@ +go 1.20 + +use ( + ./config + ./style + ./util + ./sys +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 0000000..6252bc4 --- /dev/null +++ b/go.work.sum @@ -0,0 +1,2 @@ +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= diff --git a/style/go.mod b/style/go.mod new file mode 100644 index 0000000..aabb955 --- /dev/null +++ b/style/go.mod @@ -0,0 +1,10 @@ +module gitea.meta-tech.academy/go/core/style + +go 1.20 + +require github.com/gookit/color v1.5.4 + +require ( + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + golang.org/x/sys v0.10.0 // indirect +) diff --git a/style/go.sum b/style/go.sum new file mode 100644 index 0000000..ce86143 --- /dev/null +++ b/style/go.sum @@ -0,0 +1,10 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/style/style.go b/style/style.go new file mode 100644 index 0000000..7fce7cb --- /dev/null +++ b/style/style.go @@ -0,0 +1,114 @@ +package style + +import ( + "fmt" + "strings" + "gitea.meta-tech.academy/go/core/util" + "github.com/gookit/color" +) + +// Hello returns a greeting for the named person. +func Hello(name string) string { + // Return a greeting that embeds the name in a message. + message := fmt.Sprintf("Hi, %v. Welcome!", name) + return message +} + +const KEY_STYLE_NAME = 0 +const KEY_STYLE_COLOR = 1 +const KEY_STYLE_OPTION = 2 + +type Style struct { + color *color.RGBStyle + Name string +} + +type Styles struct { + List map[string]*Style + DefaultKeyStyle string + DefaultValStyle string + DefaultKeyPadding string + DefaultIndent string +} + +func (s *Style) Printf(format string, a ...any) { + s.color.Printf(format, a...) +} + +func (s *Style) Sprintf(format string, a ...any) string { + return s.color.Sprintf(format, a...) +} + +func (s *Style) Echo(data string, format ...string) { + fmt := "%s" + if len(format) > 1 { + fmt = format[0] + } + s.Printf(fmt, data) +} + +func (s *Style) Apply(data string, format ...string) string { + if len(format) > 1 { + return s.Sprintf(format[0], data) + } else { + return s.Sprintf(data) + } +} + +func NewStyleByDef(def string) *Style { + var o []string = []string{} + d := strings.Fields(def) + c := strings.Split(d[KEY_STYLE_COLOR], ",") + if len(d) > KEY_STYLE_OPTION { + o = strings.Split(d[KEY_STYLE_OPTION], ",") + } + util.prependToSliceStr(&c, "#") + var s *color.RGBStyle + switch len(c) { + case 1: + s = color.HEXStyle(c[0]) + case 2: + s = color.HEXStyle(c[0], c[1]) + } + for _, elm := range o { + s.AddOpts(color.Color(util.str2int(elm, 10, 0))) + } + // s.Printf(" %-20s\n", d[KEY_STYLE_NAME]) + return &Style{s, d[KEY_STYLE_NAME]} +} + +func NewStyles() *Styles { + l := &Styles{ + List: make(map[string]*Style), + DefaultKeyStyle: "key", + DefaultValStyle: "val", + DefaultKeyPadding: "18", + DefaultIndent: "4", + } + return l +} + +func (s *Styles) Apply(name string, data string) string { + return s.List[name].Apply(data) +} + +func (s *Styles) Echo(name string, data string) { + s.List[name].Echo(data) +} + +func (s *Styles) Keyval(key string, val string, names ...string) { + sk := s.List[s.DefaultKeyStyle] + sv := s.List[s.DefaultValStyle] + if len(names) > 1 { + sk = s.List[names[0]] + if len(names) > 2 { + sv = s.List[names[1]] + } + } + fmt.Printf( + "%-"+s.DefaultIndent+"s%s : %s\n", + " ", + sk.Sprintf("%-"+s.DefaultKeyPadding+"s", key), + sv.Apply(val), + ) +} diff --git a/sys/go.mod b/sys/go.mod new file mode 100644 index 0000000..68324f2 --- /dev/null +++ b/sys/go.mod @@ -0,0 +1,5 @@ +module gitea.meta-tech.academy/go/core/sys + +go 1.20 + +require golang.org/x/sys v0.13.0 diff --git a/sys/go.sum b/sys/go.sum new file mode 100644 index 0000000..d4673ec --- /dev/null +++ b/sys/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/sys/sys.go b/sys/sys.go new file mode 100644 index 0000000..666cca4 --- /dev/null +++ b/sys/sys.go @@ -0,0 +1,125 @@ +package sys + +import ( + "fmt" + "io" + "os" + "os/exec" + "os/signal" + "syscall" + + "golang.org/x/sys/unix" +) + +var TERM_WIDTH = 0 + +func RunCmd(cmd *exec.Cmd) int { + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return ManageStatusCmd(cmd, cmd.Run()) +} + +func RunShellCmd(call string) int { + cmd := exec.Command("sh", "-c", call) + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + return ManageStatusCmd(cmd, cmd.Run()) +} + +func ManageStatusCmd(cmd *exec.Cmd, err error) int { + if err == nil { + return 0 + } + // Figure out the exit code + if ws, ok := cmd.ProcessState.Sys().(syscall.WaitStatus); ok { + if ws.Exited() { + return ws.ExitStatus() + } + if ws.Signaled() { + return -int(ws.Signal()) + } + } + return -1 +} + +func RunInteractiveCmd(cmd string, withStderr bool) int { + fmt.Printf(" == go before command : %s\n", cmd) + icmd := exec.Command("bash", "-c", cmd) + icmd.Stdin = os.Stdin + icmd.Stdout = os.Stdout + if withStderr { + icmd.Stderr = os.Stderr + } + return ManageStatusCmd(icmd, icmd.Run()) +} + +func PipeCmd(cmd1 *exec.Cmd, cmd2 *exec.Cmd, buffers ...io.Writer) { + fmt.Println("> Pipe Commands") + if len(buffers) > 0 { + pr, pw := io.Pipe() + cmd1.Stdout = pw + cmd2.Stdin = pr + cmd2.Stdout = buffers[0] + if len(buffers) > 1 { + cmd2.Stderr = buffers[1] + } + + cmd1.Start() + cmd2.Start() + + go func() { + defer pw.Close() + err1 := cmd1.Wait() + if err1 != nil { + fmt.Printf("error1 : %v\n", err1) + } + }() + + err2 := cmd2.Wait() + if err2 != nil { + fmt.Printf("error2 : %v\n", err2) + } + fmt.Println("< Pipe Commands") + } +} + +func UpdateTermSize() error { + ws, err := unix.IoctlGetWinsize(syscall.Stdout, unix.TIOCGWINSZ) + if err != nil { + return err + } + TERM_WIDTH = int(ws.Col) + return nil +} + +func HandleTermChange() { + // set signal handler + sigwinch := make(chan os.Signal, 1) + defer close(sigwinch) + signal.Notify(sigwinch, syscall.SIGWINCH) + go func() { + for { + if _, ok := <-sigwinch; !ok { + return + } + _ = UpdateTermSize() + fmt.Printf("TERM WIDTH : %d\n", TERM_WIDTH) + } + }() +} + +func HandleSigKill() { + done := make(chan os.Signal, 1) + defer close(done) + signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) + go func() { + <-done // Will block here until user hits ctrl+c + cmd := exec.Command("tput", "cnorm") + RunCmd(cmd) + cmd = exec.Command("tput", "-x", "clear") + RunCmd(cmd) + os.Exit(0) + }() +} diff --git a/util/go.mod b/util/go.mod new file mode 100644 index 0000000..093c216 --- /dev/null +++ b/util/go.mod @@ -0,0 +1,3 @@ +module gitea.meta-tech.academy/go/core/util + +go 1.20 diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..bad6353 --- /dev/null +++ b/util/util.go @@ -0,0 +1,40 @@ +package util + +import ( + "strconv" + "strings" +) + +func prependToSliceStr(strs *[]string, prefix string) { + for i, elm := range *strs { + (*strs)[i] = prefix + elm + } +} + +func appendToSliceStr(strs *[]string, suffix string) { + for i, elm := range *strs { + (*strs)[i] = elm + suffix + } +} + +func str2int64(str string, base int, fallback int64) int64 { + str = strings.TrimSuffix(str, "\n") + num, err := strconv.ParseInt(str, base, 64) + if err != nil { + num = fallback + } + return num +} + +func str2int(str string, base int, fallback int) int { + return int(str2int64(str, base, int64(fallback))) +} + +func castStrings2ints(strs *[]string) []int { + a := make([]int, len(*strs)) + for i, elm := range *strs { + var f int = str2int(elm, 10, 0) + a[i] = f + } + return a +}