// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // text-util.go package utils import ( "bytes" "errors" "fmt" "io" "io/ioutil" "os" "os/user" "sort" "strings" "time" ) type TextTemplate struct { varMap map[string]string UserName string Uid string ProgramDir string HomeDir string env map[string]string } func NewTextTemplate(args ...string) (instance *TextTemplate) { instance = &TextTemplate{} user, err := user.Current() if err == nil { instance.UserName = user.Username instance.HomeDir = user.HomeDir instance.Uid = user.Uid } instance.ProgramDir, _ = GetProgramDirPath() instance.varMap = make(map[string]string, 5+len(args)) instance.initVarMap() for _, arg := range args { if len(arg) == 0 { break } parts := strings.SplitN(arg, ":", 2) if len(parts) == 2 { instance.varMap[parts[0]] = parts[1] } else { instance.varMap[parts[0]] = "" } } instance.env = make(map[string]string) for _, v := range os.Environ() { parts := strings.SplitN(v, "=", 2) instance.env[parts[0]] = parts[1] } return } func (self *TextTemplate) initVarMap() { self.varMap["dt"] = "Current timestamp as YYYY-MM-DD_HH-MM-SS" self.varMap["ymd"] = "Current date as YYYY-MM-DD" self.varMap["hm"] = "Current time as HH-MM" self.varMap["h"] = "Current time as HH" self.varMap["hms"] = "Current time as HH-MM-SS" self.varMap["month"] = "Month name" self.varMap["month-num"] = "Month as number (mm)" self.varMap["week-day"] = "Week day name" self.varMap["week-day-num"] = "Week day as number" self.varMap["yesterday"] = "Yesterday date" self.varMap["uid"] = self.Uid self.varMap["username"] = self.UserName self.varMap["home"] = self.HomeDir self.varMap["progdir"] = self.ProgramDir self.varMap["hostname"], _ = os.Hostname() } func (self *TextTemplate) AddVar(name, value string) { self.varMap[name] = value } // func MakeVarMap() map[string]string { // return map[string]string{ // "ymd": "Current date as YYYY-MM-DD", // "hms": "Current time as HH-MM-SS", // // "host": "Hostname", // "uid": "UID", // "username": "Username", // "home": "Home directory", // } // } func (self *TextTemplate) updateVarMap() { var now = time.Now() self.varMap["ymd"] = fmt.Sprintf("%04d-%02d-%02d", now.Year(), now.Month(), now.Day()) self.varMap["hms"] = fmt.Sprintf("%02d-%02d-%02d", now.Hour(), now.Minute(), now.Second()) self.varMap["hm"] = fmt.Sprintf("%02d-%02d", now.Hour(), now.Minute()) self.varMap["h"] = fmt.Sprintf("%02d", now.Hour()) self.varMap["month"] = now.Format("January") self.varMap["month-num"] = fmt.Sprintf("%02d", now.Month()) self.varMap["week-day-num"] = fmt.Sprintf("%d", 1+now.Weekday()) self.varMap["week-day"] = now.Format("Monday") self.varMap["dt"] = self.varMap["ymd"] + "_" + self.varMap["hms"] yday := now.AddDate(0, 0, -1) self.varMap["yesterday"] = fmt.Sprintf("%04d-%02d-%02d", yday.Year(), yday.Month(), yday.Day()) // self.varMap["uid"] = strconv.Itoa(int(self.Uid)) // self.varMap["uid"] = self.Uid // self.varMap["username"] = self.UserName // // self.varMap["host"] = self.Hostname // self.varMap["home"] = self.HomeDir } func (self *TextTemplate) Expand(template string) string { self.updateVarMap() result := os.Expand(template, func(name string) string { value, ok := self.varMap[name] if ok { return value } else { return "" } }) return result } func (self *TextTemplate) ExpandEnv(template string) (result string) { if len(template) > 0 { self.updateVarMap() result = os.Expand(template, func(name string) string { value, ok := self.varMap[name] if ok { return value } else { return "${" + name + "}" } }) // for k, v := range self.varMap { // self.env[k] = v // } result = os.ExpandEnv(result) // result = os.Expand(result, func(name string) string { // value, ok := self.env[name] // if ok { // return value // } else { // return "" // } // }) } return result } func (self *TextTemplate) Value(key string) (value string, ok bool) { value, ok = self.varMap[key] return } func (self *TextTemplate) String() string { var sb strings.Builder keys := make([]string, 0, len(self.varMap)) for k := range self.varMap { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { sb.WriteString(fmt.Sprintf("%s=%#v\n", k, self.varMap[k])) } // for k, v := range self.varMap { // sb.WriteString(fmt.Sprintf("%s=%#v\n", k, v)) // } return sb.String() } func ParseParamPairsFile(sourceFile string, separator string, hook func(line int, name, value string) (err error)) (err error) { var source io.Reader if source, err = os.Open(sourceFile); err == nil { err = ParseParamPairs(source, separator, hook) } return } func ParseParamPairs(source io.Reader, separator string, hook func(line int, name, value string) (err error)) (err error) { var content, sep, name, value []byte var sb strings.Builder if content, err = ioutil.ReadAll(source); err != nil { return } if len(separator) == 0 { sep = []byte{' '} } else { sep = []byte(separator) } for lineCount, rawLine := range bytes.Split(content, []byte{'\n'}) { rawLine = bytes.TrimSpace(rawLine) if len(rawLine) == 0 || rawLine[0] == '#' { continue } parts := bytes.SplitN(rawLine, sep, 2) name = bytes.TrimSpace(parts[0]) if len(parts) > 1 { value = bytes.TrimSpace(parts[1]) } else { value = []byte{} } quoteOn := false escMode := false sb.Reset() for _, c := range value { switch c { case '"': if escMode { sb.WriteByte(c) escMode = false } else { quoteOn = !quoteOn } case '\\': if escMode { sb.WriteByte(c) escMode = false } else { escMode = true } case 'n': if escMode { sb.WriteByte('\n') escMode = false } else { sb.WriteByte(c) } case 'r': if escMode { sb.WriteByte('\r') escMode = false } else { sb.WriteByte(c) } case 't': if escMode { sb.WriteByte('\t') escMode = false } else { sb.WriteByte(c) } case '#': if quoteOn { sb.WriteByte(c) } else if escMode { sb.WriteByte(c) escMode = false } else { goto endloop } default: if escMode { escMode = false } sb.WriteByte(c) } } endloop: if err == nil && quoteOn { err = errors.New("unbalanced quotes") break } if err == nil { if err = hook(lineCount, string(name), sb.String()); err != nil { break } } } return }