color support
This commit is contained in:
		
							
								
								
									
										36
									
								
								color.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								color.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,36 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import "github.com/fatih/color"
 | 
			
		||||
 | 
			
		||||
var colorCache = map[string]*color.Color{}
 | 
			
		||||
 | 
			
		||||
func makeColor(name string) (c *color.Color) {
 | 
			
		||||
	// caching
 | 
			
		||||
	if c, ok := colorCache[name]; ok {
 | 
			
		||||
		return c
 | 
			
		||||
	}
 | 
			
		||||
	defer func() { colorCache[name] = c }()
 | 
			
		||||
 | 
			
		||||
	switch name {
 | 
			
		||||
	case "black":
 | 
			
		||||
		return color.New(color.FgBlack)
 | 
			
		||||
	case "red":
 | 
			
		||||
		return color.New(color.FgRed)
 | 
			
		||||
	case "green":
 | 
			
		||||
		return color.New(color.FgGreen)
 | 
			
		||||
	case "yellow":
 | 
			
		||||
		return color.New(color.FgYellow)
 | 
			
		||||
	case "blue":
 | 
			
		||||
		return color.New(color.FgBlue)
 | 
			
		||||
	case "magenta":
 | 
			
		||||
		return color.New(color.FgMagenta)
 | 
			
		||||
	case "cyan":
 | 
			
		||||
		return color.New(color.FgCyan)
 | 
			
		||||
	case "white":
 | 
			
		||||
		return color.New(color.FgWhite)
 | 
			
		||||
	case "":
 | 
			
		||||
		return color.New()
 | 
			
		||||
	default:
 | 
			
		||||
		panic("unknown color name. valid color names: black, red, green, yellow, blue, magenta, cyan, white")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								go.mod
									
									
									
									
									
								
							@ -1,3 +1,10 @@
 | 
			
		||||
module git.tordarus.net/Tordarus/format
 | 
			
		||||
 | 
			
		||||
go 1.18
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/fatih/color v1.13.0 // indirect
 | 
			
		||||
	github.com/mattn/go-colorable v0.1.12 // indirect
 | 
			
		||||
	github.com/mattn/go-isatty v0.0.14 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e // indirect
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,14 @@
 | 
			
		||||
github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
 | 
			
		||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
 | 
			
		||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e h1:w36l2Uw3dRan1K3TyXriXvY+6T56GNmlKGcqiQUJDfM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20220517195934-5e4e11fc645e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 | 
			
		||||
							
								
								
									
										106
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								main.go
									
									
									
									
									
								
							@ -6,7 +6,6 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
@ -46,7 +45,7 @@ var (
 | 
			
		||||
	// it may be useful to have a boolean flag for this behavior
 | 
			
		||||
	lineParseAmount = flag.Int("n", 1, "amount of lines to feed into input pattern")
 | 
			
		||||
 | 
			
		||||
	replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?\}`)
 | 
			
		||||
	replacePattern = regexp.MustCompile(`\{(\d+)(?::(.*?))?(?::(.*?))?(?::(.*?))?\}`)
 | 
			
		||||
 | 
			
		||||
	numMutationPattern = regexp.MustCompile(`([+\-*/])(\d+|\((\d+)\))`)
 | 
			
		||||
)
 | 
			
		||||
@ -123,7 +122,8 @@ func replaceVars(format string, vars ...string) string {
 | 
			
		||||
	for _, replacement := range replacements {
 | 
			
		||||
		rplStr := replacement[0]
 | 
			
		||||
		varIndex, _ := strconv.Atoi(replacement[1])
 | 
			
		||||
		rplFmt := replacement[2]
 | 
			
		||||
		rplColor := makeColor(replacement[2])
 | 
			
		||||
		rplFmt := replacement[3]
 | 
			
		||||
 | 
			
		||||
		// default format if not specified by user
 | 
			
		||||
		if rplFmt == "" {
 | 
			
		||||
@ -132,106 +132,16 @@ func replaceVars(format string, vars ...string) string {
 | 
			
		||||
 | 
			
		||||
		if strings.HasSuffix(rplFmt, "d") { // replace integers
 | 
			
		||||
			value, _ := strconv.ParseInt(vars[varIndex], 10, 64)
 | 
			
		||||
			mutate := numMut2func[int64](replacement[3])
 | 
			
		||||
			format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, mutate(value, vars)), 1)
 | 
			
		||||
			mutate := numMut2func[int64](replacement[4])
 | 
			
		||||
			format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, mutate(value, vars)), 1)
 | 
			
		||||
		} else if strings.HasSuffix(rplFmt, "f") || strings.HasSuffix(rplFmt, "g") { // replace floats
 | 
			
		||||
			value, _ := strconv.ParseFloat(vars[varIndex], 64)
 | 
			
		||||
			mutate := numMut2func[float64](replacement[3])
 | 
			
		||||
			format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, mutate(value, vars)), 1)
 | 
			
		||||
			mutate := numMut2func[float64](replacement[4])
 | 
			
		||||
			format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, mutate(value, vars)), 1)
 | 
			
		||||
		} else { // replace strings
 | 
			
		||||
			format = strings.Replace(format, rplStr, fmt.Sprintf(rplFmt, vars[varIndex]), 1)
 | 
			
		||||
			format = strings.Replace(format, rplStr, rplColor.Sprintf(rplFmt, vars[varIndex]), 1)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return format
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var numMutatorCache = map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
func numMut2func[T int64 | float64](mutation string) (f func(value T, vars []string) T) {
 | 
			
		||||
	if mutation == "" {
 | 
			
		||||
		return func(value T, vars []string) T { return value }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// caching
 | 
			
		||||
	if v, ok := numMutatorCache[mutation]; ok {
 | 
			
		||||
		return v.(func(value T, vars []string) T)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() { numMutatorCache[mutation] = f }()
 | 
			
		||||
 | 
			
		||||
	matches := numMutationPattern.FindAllStringSubmatch(mutation, -1)
 | 
			
		||||
	mutators := make([]NumMutator, 0, len(matches))
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, match := range matches {
 | 
			
		||||
		mut := NumMutator{Op: NewNumOperatorFromString(match[1])}
 | 
			
		||||
		if match[3] == "" {
 | 
			
		||||
			mut.Value, err = strconv.Atoi(match[2])
 | 
			
		||||
			mut.Var = false
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("invalid number in number mutator: " + match[2])
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			mut.Var = true
 | 
			
		||||
			mut.Value, err = strconv.Atoi(match[3])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("invalid back reference group in number mutator: " + match[2])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		mutators = append(mutators, mut)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	numberParser := number_parser[T]()
 | 
			
		||||
 | 
			
		||||
	return func(value T, vars []string) T {
 | 
			
		||||
		for _, mutator := range mutators {
 | 
			
		||||
			var otherValue T
 | 
			
		||||
			if mutator.Var {
 | 
			
		||||
				other := numberParser(vars[mutator.Value])
 | 
			
		||||
				otherValue = T(other)
 | 
			
		||||
			} else {
 | 
			
		||||
				otherValue = T(mutator.Value)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch mutator.Op {
 | 
			
		||||
			case NumOperatorAdd:
 | 
			
		||||
				value += otherValue
 | 
			
		||||
			case NumOperatorSub:
 | 
			
		||||
				value -= otherValue
 | 
			
		||||
			case NumOperatorMul:
 | 
			
		||||
				value *= otherValue
 | 
			
		||||
			case NumOperatorDiv:
 | 
			
		||||
				value /= otherValue
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func number_parser[T int64 | float64]() func(str string) T {
 | 
			
		||||
	typeOfT := reflect.TypeOf(new(T)).Elem()
 | 
			
		||||
	typeOfInt64 := reflect.TypeOf(new(int64)).Elem()
 | 
			
		||||
	typeOfFloat64 := reflect.TypeOf(new(float64)).Elem()
 | 
			
		||||
 | 
			
		||||
	if typeOfT == typeOfInt64 {
 | 
			
		||||
		return func(str string) T {
 | 
			
		||||
			num, err := strconv.Atoi(str)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("expected integer but found " + str)
 | 
			
		||||
			}
 | 
			
		||||
			return T(num)
 | 
			
		||||
		}
 | 
			
		||||
	} else if typeOfT == typeOfFloat64 {
 | 
			
		||||
		return func(str string) T {
 | 
			
		||||
			num, err := strconv.ParseFloat(str, 64)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("expected float but found " + str)
 | 
			
		||||
			}
 | 
			
		||||
			return T(num)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	panic("invalid number type")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										94
									
								
								mutator.go
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								mutator.go
									
									
									
									
									
								
							@ -1,5 +1,10 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type NumMutator struct {
 | 
			
		||||
	Op    NumOperator
 | 
			
		||||
	Var   bool
 | 
			
		||||
@ -29,3 +34,92 @@ func NewNumOperatorFromString(str string) NumOperator {
 | 
			
		||||
		panic("invalid number operator: " + str)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var numMutatorCache = map[string]interface{}{}
 | 
			
		||||
 | 
			
		||||
func numMut2func[T int64 | float64](mutation string) (f func(value T, vars []string) T) {
 | 
			
		||||
	if mutation == "" {
 | 
			
		||||
		return func(value T, vars []string) T { return value }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// caching
 | 
			
		||||
	if v, ok := numMutatorCache[mutation]; ok {
 | 
			
		||||
		return v.(func(value T, vars []string) T)
 | 
			
		||||
	}
 | 
			
		||||
	defer func() { numMutatorCache[mutation] = f }()
 | 
			
		||||
 | 
			
		||||
	matches := numMutationPattern.FindAllStringSubmatch(mutation, -1)
 | 
			
		||||
	mutators := make([]NumMutator, 0, len(matches))
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, match := range matches {
 | 
			
		||||
		mut := NumMutator{Op: NewNumOperatorFromString(match[1])}
 | 
			
		||||
		if match[3] == "" {
 | 
			
		||||
			mut.Value, err = strconv.Atoi(match[2])
 | 
			
		||||
			mut.Var = false
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("invalid number in number mutator: " + match[2])
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			mut.Var = true
 | 
			
		||||
			mut.Value, err = strconv.Atoi(match[3])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("invalid back reference group in number mutator: " + match[2])
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		mutators = append(mutators, mut)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	numberParser := number_parser[T]()
 | 
			
		||||
 | 
			
		||||
	return func(value T, vars []string) T {
 | 
			
		||||
		for _, mutator := range mutators {
 | 
			
		||||
			var otherValue T
 | 
			
		||||
			if mutator.Var {
 | 
			
		||||
				other := numberParser(vars[mutator.Value])
 | 
			
		||||
				otherValue = T(other)
 | 
			
		||||
			} else {
 | 
			
		||||
				otherValue = T(mutator.Value)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			switch mutator.Op {
 | 
			
		||||
			case NumOperatorAdd:
 | 
			
		||||
				value += otherValue
 | 
			
		||||
			case NumOperatorSub:
 | 
			
		||||
				value -= otherValue
 | 
			
		||||
			case NumOperatorMul:
 | 
			
		||||
				value *= otherValue
 | 
			
		||||
			case NumOperatorDiv:
 | 
			
		||||
				value /= otherValue
 | 
			
		||||
			default:
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return value
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func number_parser[T int64 | float64]() func(str string) T {
 | 
			
		||||
	typeOfT := reflect.TypeOf(new(T)).Elem()
 | 
			
		||||
	typeOfInt64 := reflect.TypeOf(new(int64)).Elem()
 | 
			
		||||
	typeOfFloat64 := reflect.TypeOf(new(float64)).Elem()
 | 
			
		||||
 | 
			
		||||
	if typeOfT == typeOfInt64 {
 | 
			
		||||
		return func(str string) T {
 | 
			
		||||
			num, err := strconv.Atoi(str)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("expected integer but found " + str)
 | 
			
		||||
			}
 | 
			
		||||
			return T(num)
 | 
			
		||||
		}
 | 
			
		||||
	} else if typeOfT == typeOfFloat64 {
 | 
			
		||||
		return func(str string) T {
 | 
			
		||||
			num, err := strconv.ParseFloat(str, 64)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				panic("expected float but found " + str)
 | 
			
		||||
			}
 | 
			
		||||
			return T(num)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	panic("invalid number type")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user