Compare commits

..

No commits in common. "d2975303094669ba4a00de2f3fbe098ae4b2eef6" and "db726467c6dd7d33ee1a5b424dd1d8d48784cbc9" have entirely different histories.

6 changed files with 0 additions and 706 deletions

View File

@ -1,137 +0,0 @@
// dict-set-context_test.go
package text
import (
"crypto/sha1"
"encoding/hex"
"errors"
"fmt"
"strings"
"testing"
"time"
)
func TestNewDictSetContext(t *testing.T) {
type inputType struct {
ctx ExpanderContext
source string
wantResult string
wantErr error
}
funcName := "scan"
flags := DictSetFlag(0)
vars := map[string]string{"one": "1", "two": "2", "three": "3"}
ctx1 := NewDictSetContext(flags, vars)
ctx2 := NewDictSetContext(KeepVar, vars)
AddTimeFuncs(ctx2)
/*
err = fmt.Errorf("unsupported '%%%c' time specifier at %d", ch, i)
*/
now := time.Now()
yday := now.Add(-24*time.Hour)
inputs := []inputType{
{ctx2, `form: ${|__time-fmt now ""}`, fmt.Sprintf("form: %d-%02d-%02d %02d:%02d:%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()), nil},
{ctx2, `form: ${|__time-fmt now "%-D"}`, fmt.Sprintf("form: %d-%d-%d", now.Year(), now.Month(), now.Day()), nil},
{ctx2, `form: ${|__time-fmt now "%:T"}`, fmt.Sprintf("form: %d:%d:%d", now.Hour(), now.Minute(), now.Second()), nil},
{ctx2, `form: ${|__time-fmt now "%02:T"}`,"", errors.New("invalid time specifier format at 3")},
{ctx2, `form: ${|__time-fmt ""}`,"", errors.New("time value is empty")},
{ctx2, `form: ${|__time-fmt yesterday "%-D"}`, fmt.Sprintf("form: %d-%d-%d", yday.Year(), yday.Month(), yday.Day()), nil},
{ctx1, "${one|pippo}", "1", nil},
{ctx1, "${one|__default pluto}", "1", nil},
{ctx1, "${six|__default pluto}", "pluto", nil},
{ctx1, "${six|__default - pluto paperino|__lower|__upper}", "PLUTO-PAPERINO", nil},
{ctx1, "${two|__join + 1 ${.} ${|__get three}}", "1+2+3", nil},
{ctx1, "${=one|__get}", "1", nil},
{ctx1, "${|__get unknown default}", "default", nil},
{ctx1, "${one|__set A ${.}}", "1", nil},
{ctx1, "${one|__set B }", "1", nil},
{ctx1, "${one|__set}", "1", nil},
{ctx1, "${=archive.tar.gz|__trim-suffix}", "archive.tar", nil},
{ctx1, "${=prog.exe|__trim-suffix .zip .png .exe .com}", "prog", nil},
{ctx1, "${=prog.exe|__trim-suffix .zip .png .jpeg .com}", "prog.exe", nil},
{ctx1, "${=ciccio}", "ciccio", nil},
{ctx1, "${=ciccio{}", "", errors.New("unbalanced open braces at offset 9")},
{ctx1, "${|__get two}", "2", nil},
{ctx1, "${one} + ${two} = ${three}", "1 + 2 = 3", nil},
{ctx1, "${one} + ${two} = ${four}", "1 + 2 = ", nil},
{ctx2, "${one} + ${two} = ${four}", "1 + 2 = ${four}", nil},
{ctx2, `now: ${|__time-fmt now "%:02T"}`, fmt.Sprintf("now: %02d:%02d:%02d", now.Hour(), now.Minute(), now.Second()), nil},
{ctx2, `hour: ${|__time-fmt now "%02H"}`, fmt.Sprintf("hour: %02d", now.Hour()), nil},
{ctx2, `minute: ${|__time-fmt now "%02M"}`, fmt.Sprintf("minute: %02d", now.Minute()), nil},
{ctx2, `second: ${|__time-fmt now "%02S" }`, fmt.Sprintf("second: %02d", now.Second()), nil},
{ctx2, `today: ${|__time-fmt now "%-02D"}`, fmt.Sprintf("today: %d-%02d-%02d", now.Year(), now.Month(), now.Day()), nil},
{ctx2, `year: ${|__time-fmt now "%Y"}`, fmt.Sprintf("year: %d", now.Year()), nil},
{ctx2, `month: ${|__time-fmt now "%m"}`, fmt.Sprintf("month: %02d", now.Month()), nil},
{ctx2, `weird: ${|__time-fmt now "%c"}`, "", errors.New(`unsupported 'c' time specifier at "%c"/2`)},
{ctx2, `perc: ${|__time-fmt now "%%"}`, "perc: %", nil},
{ctx1, `unknown var ${!unknown}`, ``, errors.New(`the variable specification "unknown" requires a value`)},
{ctx1, `${hello-word|}!`, `!`, nil},
{ctx1, `${hello-word|ciao}!`, `ciao!`, nil},
}
for i, input := range inputs {
gotResult, gotErr := ExpandStringTemplate(input.ctx, input.source)
if gotResult != input.wantResult {
t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult)
}
if gotErr == nil && input.wantErr == nil {
continue
}
if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %s(%q)/err = <%v>, want <%v>", i, funcName, input.source, gotErr, input.wantErr)
}
}
}
func TestCheckNumArgs(t *testing.T) {
type inputType struct {
info ExpanderFuncBlock
n int // number of args
want bool
}
funcName := "CheckNumArgs"
inputs := []inputType{
{ExpanderFuncBlock{"", nil, -1, -1, ""}, 0, true},
{ExpanderFuncBlock{"", nil, 0, -1, ""}, 10, true},
{ExpanderFuncBlock{"", nil, 2, 4, ""}, 1, false},
}
for i, input := range inputs {
got := input.info.CheckNumArgs(input.n)
if got != input.want {
t.Errorf("%d: %s(%d)/result = %v, want %v", i, funcName, input.n, got, input.want)
}
}
}
func TestIterateFunc(t *testing.T) {
var sb strings.Builder
funcName := "IterateFunc"
want := "f141c5fa802ed509c08ec057eea5ba0086a84b21"
vars := map[string]string{"one": "1", "two": "2", "three": "3"}
ctx := NewDictSetContext(KeepVar, vars)
ctx.AddFunc("dumm1", nil, 1, 2, "bla-bla")
ctx.AddFunc("dumm2", nil, 0, 5, strings.Repeat(".", 81))
ctx.AddFunc("dumm3", nil, -1, -1, "bla-bla")
ctx.AddFunc("dumm4", nil, 2, -1, "bla-bla")
ctx.IterateFunc(func(info *ExpanderFuncBlock) error {
sb.WriteString(info.String())
return nil
})
hasher := sha1.New()
hasher.Write([]byte(sb.String()))
got := hex.EncodeToString(hasher.Sum(nil))
//fmt.Println("sha:", got)
//fmt.Println(sb.String())
if got != want {
t.Errorf("%s()/result = %q, want %q", funcName, got, want)
}
}

View File

@ -1,2 +0,0 @@
// expander-context_test.go
package text

View File

@ -1,114 +0,0 @@
// scanner_test.go
package text
import (
"errors"
"fmt"
"strconv"
"testing"
)
type ctxExpType struct {
BaseExpander
funcs map[string]ExpanderFunc
}
func newCtxExp() *ctxExpType {
return &ctxExpType{funcs: make(map[string]ExpanderFunc)}
}
func (ctx *ctxExpType) Handle(spec string, flags ScannerFlag) (value string, err error) {
var f ExpanderFunc
//fmt.Println("Spec:", spec)
switch spec {
case "value":
value = "right-value"
case "|ciao":
value = "ciao"
case `|__sum(1,2,3)`:
if f, err = ctx.GetFunc("__sum"); err == nil {
value, err = f(ctx, "", []string{"1", "2", "3"})
}
case `|__cat(1,"2",3)`:
if f, err = ctx.GetFunc("__cat"); err == nil {
value, err = f(ctx, "", []string{"1", "2", "3"})
}
case `uno|due`:
value = "uno"
case "|__xyz()":
err = errors.New("unknown function __xyz")
default:
value = ""
}
return
}
func (ctx *ctxExpType) GetFunc(name string) (f ExpanderFunc, err error) {
var ok bool
if f, ok = ctx.funcs[name]; !ok {
err = fmt.Errorf("function %s not found", name)
}
return
}
func (ctx *ctxExpType) AddFunc(name string, f ExpanderFunc, minArgs, maxArgs int, description string) {
ctx.funcs[name] = f
}
func sum(ctx ExpanderContext, varValue string, args []string) (result string, err error) {
if len(varValue) > 0 {
result = varValue
} else {
var v int
s := 0
for _, arg := range args {
if v, err = strconv.Atoi(arg); err != nil {
return
} else {
s += v
}
}
result = strconv.Itoa(s)
}
return
}
func TestExpandStringTemplate(t *testing.T) {
type inputType struct {
ctx ExpanderContext
source string
wantResult string
wantErr error
}
funcName := "scan"
ctx := newCtxExp()
ctx.AddFunc("__sum", sum, 1, 1, "fake function")
inputs := []inputType{
{ctx, "this is the ${value}!", "this is the right-value!", nil},
{ctx, `pay \$31`, `pay $31`, nil},
{ctx, `\\regex\\`, `\regex\`, nil},
{ctx, `fake \x.`, `fake x.`, nil},
{ctx, ">${|ciao}<", ">ciao<", nil},
{ctx, `>${|__sum(1,2,3)}<`, ">6<", nil},
{ctx, `>${|__cat(1,"2",3)}<`, "", errors.New("function __cat not found")},
{ctx, `>${|__cat(1,"2",3)<`, "", errors.New("unbalanced open braces at offset 2")},
{ctx, `>${|__xyz()}<`, "", errors.New("unknown function __xyz")},
{ctx, `>${uno|due}<`, ">uno<", nil},
}
for i, input := range inputs {
gotResult, gotErr := ExpandStringTemplate(input.ctx, input.source)
if gotResult != input.wantResult {
t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult)
}
if gotErr == nil && input.wantErr == nil {
continue
}
if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %s(%q)/err = %v, want %v", i, funcName, input.source, gotErr, input.wantErr)
}
}
}

View File

@ -1,120 +0,0 @@
// scanner_test.go
package text
import (
"errors"
"testing"
)
func TestGetVarSpec(t *testing.T) {
type inputGetVarSpec struct {
source string
offset int
wantSpec string
wantFlags ScannerFlag
wantOffset int
wantErr error
}
funcName := "getVarSpec"
inputs := []inputGetVarSpec{
{"Hello ${name}.", 7, "name", ScannerFlag(0), 13, nil},
{"one ${!varspec}", 5, "varspec", ValueRequired, 15, nil},
{"one ${*varspec}", 5, "varspec", EncryptValue, 15, nil},
{"one ${*!varspec}", 5, "varspec", ValueRequired | EncryptValue, 16, nil},
{"one ${*!var{spec}", 5, "", ValueRequired | EncryptValue, 17, errors.New("unbalanced open braces at offset 11")},
{"one ${two}${three}", 11, "three", ScannerFlag(0), 18, nil},
{"one $2${three}", 5, "2", ScannerFlag(0), 6, nil},
{"one $!2${three}", 5, "", ScannerFlag(0), 5, nil},
{"one $2{3}${three}", 5, "2", ScannerFlag(0), 6, nil},
{"one ${{2}}{3}${three}", 5, "{2}", ScannerFlag(0), 10, nil},
{">${|ciao}<", 2, "|ciao", ScannerFlag(0), 9, nil},
{">${|{ciao}<", 2, "", ScannerFlag(0), 11, errors.New("unbalanced open braces at offset 4")},
}
for i, input := range inputs {
gotSpec, gotFlags, gotOffset, gotErr := getVarSpec(input.source, input.offset)
if gotSpec != input.wantSpec {
t.Errorf("%d: %s(%q, %d)/spec = %q, want %q", i, funcName, input.source, input.offset, gotSpec, input.wantSpec)
}
if gotFlags != input.wantFlags {
t.Errorf("%d: %s(%q, %d)/flags = %d, want %d", i, funcName, input.source, input.offset, gotFlags, input.wantFlags)
}
if gotOffset != input.wantOffset {
t.Errorf("%d: %s(%q, %d)/offset = %d, want %d", i, funcName, input.source, input.offset, gotOffset, input.wantOffset)
}
if gotErr == nil && input.wantErr == nil {
continue
}
if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %s(%q, %d)/err = %v, want %v", i, funcName, input.source, input.offset, gotErr, input.wantErr)
}
}
}
type ctxType struct{}
func (ctx *ctxType) Handle(spec string, flags ScannerFlag) (value string, err error) {
//fmt.Println("Spec:", spec)
switch spec {
case "value":
value = "right-value"
case "|ciao":
value = "ciao"
case `|__sum(1,2,3)`:
value = "6"
case `|__cat(1,"2",3)`:
value = "123"
case `uno|due`:
value = "uno"
case "|__xyz()":
err = errors.New("unknown function __xyz")
default:
value = ""
}
return
}
func TestScan(t *testing.T) {
type inputType struct {
ctx VariableHandler
source string
wantResult string
wantErr error
}
funcName := "scan"
inputs := []inputType{
{&ctxType{}, "this is the ${value}!", "this is the right-value!", nil},
{&ctxType{}, `pay \$31`, `pay $31`, nil},
{&ctxType{}, `\\regex\\`, `\regex\`, nil},
{&ctxType{}, `fake \x.`, `fake x.`, nil},
{&ctxType{}, ">${|ciao}<", ">ciao<", nil},
{&ctxType{}, `>${|__sum(1,2,3)}<`, ">6<", nil},
{&ctxType{}, `>${|__cat(1,"2",3)}<`, ">123<", nil},
{&ctxType{}, `>${|__cat(1,"2",3)<`, "", errors.New("unbalanced open braces at offset 2")},
{&ctxType{}, `>${|__xyz()}<`, "", errors.New("unknown function __xyz")},
{&ctxType{}, `>${uno|due}<`, ">uno<", nil},
}
for i, input := range inputs {
gotResult, gotErr := Scan(input.ctx, input.source)
if gotResult != input.wantResult {
t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult)
}
if gotErr == nil && input.wantErr == nil {
continue
}
if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) {
t.Errorf("%d: %s(%q)/err = %v, want %v", i, funcName, input.source, gotErr, input.wantErr)
}
}
}

306
tty.go
View File

@ -1,306 +0,0 @@
// tty.go
package text
import (
"fmt"
"io"
"os"
"strconv"
"strings"
)
type ctxFmt struct {
isTTY bool
ansiLen int
}
func translateColor(name string) (c int) {
//fmt.Println("TranslateColor:", name)
if len(name) == 0 {
return -1
}
norm := strings.ToLower(name)
switch {
case strings.HasPrefix("black", norm):
c = 30
case strings.HasPrefix("blue", norm):
c = 34
case strings.HasPrefix("green", norm):
c = 32
case strings.HasPrefix("red", norm):
c = 31
case strings.HasPrefix("white", norm):
c = 37
default:
c = -1
}
if c > 0 && name[0] >= 'A' && name[0] <= 'Z' {
c += 10
}
return
}
func extractArgs(spec string) (result string) {
if len(spec) < 2 || spec[0] != '(' || spec[len(spec)-1] != ')' {
return
}
result = spec[1 : len(spec)-1]
return
}
func parseColor(fg bool, spec string) (result string) {
var intro string
if spec = extractArgs(spec); len(spec) == 0 {
return
}
if spec[0] >= '0' && spec[0] <= '9' {
if fg {
intro = "38;"
} else {
intro = "48;"
}
parts := strings.SplitN(spec, ",", 3)
if len(parts) == 3 {
result = intro + "2;" + strings.Join(parts, ";")
} else if len(parts) == 1 {
result = intro + "5;" + parts[0]
}
return
}
if c := translateColor(spec); c >= 0 {
result = strconv.Itoa(c)
}
return
}
func advanceToTerms(s string, start int, terms string) (offset int) {
for offset = start; offset < len(s) && strings.IndexByte(terms, s[offset]) < 0; offset++ {
}
return
}
func (ctx *ctxFmt) putAnsiString(sb *strings.Builder, code string) {
if ctx.isTTY {
a, _ := sb.WriteString("\x1b[")
b, _ := sb.WriteString(code)
sb.WriteByte('m')
ctx.ansiLen += a + b + 1
}
}
func (ctx *ctxFmt) putAnsiCode(sb *strings.Builder, code int) {
if ctx.isTTY {
a, _ := sb.WriteString("\x1b[")
b, _ := sb.WriteString(strconv.Itoa(code))
sb.WriteByte('m')
ctx.ansiLen += a + b + 1
}
}
func (ctx *ctxFmt) putAnsiReset(sb *strings.Builder) {
if ctx.isTTY {
a, _ := sb.WriteString("\x1b[0m")
ctx.ansiLen += a
}
}
type caseMode uint8
const (
normal = caseMode(iota)
upper
lower
title
firstOnly
)
type alignMode uint8
const (
noAlign = alignMode(iota)
alignLeft
alignRight
)
func (ctx *ctxFmt) putText(sb *strings.Builder, text string, start int, mode caseMode, align alignMode, fieldSize int, filler byte) (offset int) {
if t := text[start+1:]; len(t) > 0 {
if align == alignRight {
for k := 0; k < fieldSize-len(t); k++ {
sb.WriteByte(filler)
}
}
if mode == upper {
sb.WriteString(strings.ToUpper(t))
} else if mode == lower {
sb.WriteString(strings.ToLower(t))
} else if mode == title {
sb.WriteString(strings.Title(t))
} else if mode == firstOnly {
if t[0] >= 'a' && t[0] <= 'z' {
sb.WriteByte(t[0] - 'a' + 'A')
} else {
sb.WriteByte(t[0])
}
sb.WriteString(strings.ToLower(t[1:]))
} else {
sb.WriteString(t)
}
if align == alignLeft {
for k := 0; k < fieldSize-len(t); k++ {
sb.WriteByte(filler)
}
}
offset = start + len(t)
}
return
}
func (ctx *ctxFmt) Handle(varSpec string, flags ScannerFlag) (value string, err error) {
var sb strings.Builder
var mode caseMode = normal
var align alignMode = noAlign
var fieldSize int
var filler byte = ' '
//fmt.Println("TTY:", varSpec)
if len(varSpec) == 0 {
ctx.putAnsiReset(&sb)
} else {
for i := 0; i < len(varSpec); i++ {
if j := advanceToTerms(varSpec, i, ";-"); j > i+1 {
if c := translateColor(varSpec[i:j]); c >= 0 {
ctx.putAnsiCode(&sb, c)
i = j
continue
}
}
ch := varSpec[i]
switch ch {
case '.':
ctx.putAnsiReset(&sb)
case 'b':
ctx.putAnsiString(&sb, "1")
case 'B':
ctx.putAnsiString(&sb, "22")
case 'i':
ctx.putAnsiString(&sb, "3")
case 'I':
ctx.putAnsiString(&sb, "23")
case 'u':
ctx.putAnsiString(&sb, "4")
case 'U':
ctx.putAnsiString(&sb, "24")
case 'c', 'C':
j := advanceToTerms(varSpec, i, ")")
if c := parseColor(ch == 'c', varSpec[i+1:j+1]); len(c) > 0 {
ctx.putAnsiString(&sb, c)
//sb.WriteString("\x1b[" + c + "m")
i = j
}
case '\'':
mode = upper
case ',':
mode = lower
case '"':
mode = title
case '^':
mode = firstOnly
case '<', '>':
j := advanceToTerms(varSpec, i, ")")
if arg := extractArgs(varSpec[i+1 : j+1]); len(arg) > 0 {
var err1 error
if arg[0] <= '0' || arg[0] > '9' {
filler = arg[0]
arg = arg[1:]
}
if fieldSize, err1 = strconv.Atoi(arg); err1 == nil {
if ch == '<' {
align = alignLeft
} else {
align = alignRight
}
}
}
i = j
case '-':
i = ctx.putText(&sb, varSpec, i, mode, align, fieldSize, filler)
ctx.putAnsiReset(&sb)
}
}
}
return sb.String(), nil
}
func IsTty(w io.Writer) (isTty bool) {
if fh, ok := w.(*os.File); ok {
if fi, err := fh.Stat(); err == nil {
isTty = (fi.Mode() & os.ModeCharDevice) != 0
}
}
return
}
func ToTty(source string) (result string, cleanLen int, err error) {
ctx := ctxFmt{true, 0}
if result, err = ScanExt('#', &ctx, source); err == nil {
cleanLen = len(result) - ctx.ansiLen
}
return
}
func ToTtyStream(w io.Writer, source string) (result string, cleanLen int, err error) {
if IsTty(w) {
return ToTty(source)
} else {
return source, len(source), nil
}
}
func Sprintf(templ string, args ...any) string {
templ, _, _ = ToTty(templ)
return fmt.Sprintf(templ, args...)
}
func Printf(templ string, args ...any) (int, error) {
return Fprintf(os.Stdout, templ, args...)
}
func Fprintf(w io.Writer, templ string, args ...any) (int, error) {
templ, _, _ = ToTtyStream(w, templ)
return fmt.Fprintf(w, templ, args...)
}
// func DebugSprintf() {
// list := []string{
// "#{<(.20)-Param}: #{>(06)-123}",
// "ciao #{c(r)i<(12)-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(r)i>(12)-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(red);i}Mario#. #{u;b}Come# #{GREEN;b}Stai#{.}?",
// "ciao #{c(red)i-Mario}. #{ub-Come} #{GREEN;b-Stai}?",
// "ciao #{c(r)i-Mario}. #{ub-Come} #{Gr;b-Stai}?",
// "ciao #{c(r)i-Mario}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(r)i'-gian carlo}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(r)i\"-gian carlo}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(r)i,-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
// "ciao #{c(r)i^-GIAN Carlo}. #{ub-Come} #{c(G)b-Stai}?",
// }
// fmt.Println("--- Sprintf() ---")
// for i, s := range list {
// x := Sprintf(s)
// fmt.Printf("--- Test nr %d: %q\n", i+1, s)
// fmt.Println(x)
// }
// fmt.Println("\n--- Sprintf() ---")
// for i, s := range list {
// fmt.Printf("--- Test nr %d: %q\n", i+1, s)
// Printf(s)
// fmt.Println()
// }
// }

View File

@ -1,27 +0,0 @@
// expander-context_test.go
package text
import (
"fmt"
"os"
"testing"
)
func TestSprintf(t *testing.T) {
list := []string{
"ciao #{c(red);i}Mario#. #{u;b}Come# #{GREEN;b}Stai#{.}?",
"ciao #{c(red)i-Mario}. #{ub-Come} #{GREEN;b-Stai#}?",
}
//s := Sprintf("ciao #{fg(139);i}Mario#. #{u;b}Come# #{GREEN;b}%s#{.}?", "Stai")
fmt.Println("--- Sprintf() ---")
for i, s := range list {
x := Sprintf(s)
fmt.Printf("--- Test nr %d: %q\n", i+1, s)
fmt.Println(x)
}
fmt.Println("\n--- Sprintf() ---")
for i, s := range list {
fmt.Printf("--- Test nr %d: %q\n", i+1, s)
Fprintf(os.Stdout, s)
}
}