Skip to content

Commit

Permalink
Merge pull request #4 from Syuparn/add-test
Browse files Browse the repository at this point in the history
add test
  • Loading branch information
Syuparn authored Feb 19, 2021
2 parents cdf7723 + 756c147 commit 380ad23
Show file tree
Hide file tree
Showing 5 changed files with 241 additions and 60 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: GoTest

on: [push]

jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:

- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: ^1.15
id: go

- name: Check out code into the Go module directory
uses: actions/checkout@v2

- name: Build
run: go build

- name: GoTest
run: go test -v -race -coverprofile=coverage.txt -covermode=atomic ./...
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# tmplscript
![](https://github.com/Syuparn/tmplscript/workflows/GoTest/badge.svg?branch=master)

executable go-template command (like awk and jq!)

# usage
Expand Down
78 changes: 78 additions & 0 deletions funcmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package main

import (
"fmt"
"reflect"
"sort"
"strings"
"text/template"

"github.com/Masterminds/sprig"
)

func funcMap() template.FuncMap {
// NOTE: FuncMap is for html/template, TxtFuncMap is for text/template
funcMap := sprig.TxtFuncMap()

// add (meta-)functions to describe functions
funcMap["searchFunc"] = searchFunc(funcMap)
funcMap["docFunc"] = docFunc(funcMap)
return funcMap
}

func searchFunc(funcMap template.FuncMap) func(string) []string {
return func(prefix string) []string {
keys := []string{}
for k := range funcMap {
if strings.HasPrefix(k, prefix) {
keys = append(keys, k)
}
}

// sort alphabetically
sort.Strings(keys)

return keys
}
}

func docFunc(funcMap template.FuncMap) func(string) string {
return func(name string) string {
f, ok := funcMap[name]
if !ok {
return fmt.Sprintf("function %s is not defined (or embedded)", name)
}

rt := reflect.TypeOf(f)
if rt.Kind() != reflect.Func {
return fmt.Sprintf("%s is not a function", name)
}

paramTypes := []string{}
for i := 0; i < rt.NumIn(); i++ {
paramTypes = append(paramTypes, rt.In(i).String())
}

if rt.IsVariadic() {
paramTypes[len(paramTypes)-1] = toVariadic(paramTypes[len(paramTypes)-1])
}

returnTypes := []string{}
for i := 0; i < rt.NumOut(); i++ {
returnTypes = append(returnTypes, rt.Out(i).String())
}

paramList := strings.Join(paramTypes, " ")
returnList := strings.Join(returnTypes, " ")

if paramList == "" {
return fmt.Sprintf("%s -> (%s)", name, returnList)
}
return fmt.Sprintf("%s %s -> (%s)", name, paramList, returnList)
}
}

func toVariadic(typeStr string) string {
// replace prefix `[]` to `...` in variadic parameter
return "..." + strings.TrimPrefix(typeStr, "[]")
}
137 changes: 137 additions & 0 deletions funcmap_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package main

import (
"fmt"
"testing"
"text/template"
)

func TestSearchFunc(t *testing.T) {
tests := []struct {
title string
funcMap template.FuncMap
key string
expected []string
}{
{
"not found",
map[string]interface{}{},
"",
[]string{},
},
{
"empty key gets all functions",
map[string]interface{}{
"a": func() {},
"b": func() {},
},
"",
[]string{"a", "b"},
},
{
"key gets all functions whose key starts with it",
map[string]interface{}{
"fail": func() {},
"find": func() {},
"finish": func() {},
"get": func() {},
},
"fi",
[]string{"find", "finish"},
},
}

for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf(tt.title), func(t *testing.T) {
actual := searchFunc(tt.funcMap)(tt.key)

if len(actual) != len(tt.expected) {
t.Fatalf("wrong length. got %d, expected %d", len(actual), len(tt.expected))
}

for i, elem := range actual {
if elem != tt.expected[i] {
t.Errorf("actual[%d] is wrong. got %s, expected %s",
i, elem, tt.expected[i])
}
}
})
}
}

func TestDocFunc(t *testing.T) {
tests := []struct {
title string
funcMap template.FuncMap
key string
expected string
}{
{
"arity 0, return 0",
map[string]interface{}{
"fail": func() {},
},
"fail",
"fail -> ()",
},
{
"arity 1, return 1",
map[string]interface{}{
"itoa": func(i int) string { return fmt.Sprint(i) },
},
"itoa",
"itoa int -> (string)",
},
{
"arity 3, return 1",
map[string]interface{}{
"myTernary": func(i int, s string, f float64) bool { return false },
},
"myTernary",
"myTernary int string float64 -> (bool)",
},
{
"arity 1, return 3",
map[string]interface{}{
"myFunc": func(i int) (string, bool, error) { return "", true, nil },
},
"myFunc",
"myFunc int -> (string bool error)",
},
{
"variadic",
map[string]interface{}{
"myFunc": func(i int, strs ...string) {},
},
"myFunc",
"myFunc int ...string -> ()",
},
{
"not found",
map[string]interface{}{},
"myFunc",
"function myFunc is not defined (or embedded)",
},
{
"not function",
map[string]interface{}{
"val": 1,
},
"val",
"val is not a function",
},
}

for _, tt := range tests {
tt := tt
t.Run(fmt.Sprintf(tt.title), func(t *testing.T) {
actual := docFunc(tt.funcMap)(tt.key)

if actual != tt.expected {
t.Errorf("wrong output. expected `%s`, got `%s`",
tt.expected, actual)
}
})
}
}
60 changes: 0 additions & 60 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,7 @@ import (
"fmt"
"io/ioutil"
"os"
"reflect"
"strings"
"text/template"

"github.com/Masterminds/sprig"
)

var (
Expand Down Expand Up @@ -117,59 +113,3 @@ func runREPLMode() {
func diffStr(newStr, previousStr string) string {
return newStr[len(previousStr):]
}

func funcMap() template.FuncMap {
// NOTE: FuncMap is for html/template, TxtFuncMap is for text/template
funcMap := sprig.TxtFuncMap()

// add (meta-)functions to describe functions
funcMap["searchFunc"] = searchFunc(funcMap)
funcMap["docFunc"] = docFunc(funcMap)
return funcMap
}

func searchFunc(funcMap template.FuncMap) func(string) []string {
return func(prefix string) []string {
keys := []string{}
for k := range funcMap {
if strings.HasPrefix(k, prefix) {
keys = append(keys, k)
}
}

return keys
}
}

func docFunc(funcMap template.FuncMap) func(string) string {
return func(name string) string {
f, ok := funcMap[name]
if !ok {
return fmt.Sprintf("function %s is not defined (or embedded)", name)
}

rt := reflect.TypeOf(f)
if rt.Kind() != reflect.Func {
return fmt.Sprintf("%s is not a function", name)
}

paramTypes := []string{}
for i := 0; i < rt.NumIn(); i++ {
paramTypes = append(paramTypes, rt.In(i).String())
}

// add `...` to variadic parameter
if rt.IsVariadic() {
paramTypes[len(paramTypes)-1] = "..." + paramTypes[len(paramTypes)-1]
}

returnTypes := []string{}
for i := 0; i < rt.NumOut(); i++ {
returnTypes = append(returnTypes, rt.Out(i).String())
}

paramList := strings.Join(paramTypes, " ")
returnList := strings.Join(returnTypes, " ")
return fmt.Sprintf("%s %s -> (%s)", name, paramList, returnList)
}
}

0 comments on commit 380ad23

Please sign in to comment.