├── .gitignore ├── wombat.png ├── .codecov.yml ├── fp ├── testobj │ └── model.go ├── fp.go ├── compare │ ├── by_itself_test.go │ ├── ptr.go │ ├── simple_value.go │ ├── simple_value_test.go │ ├── by_field_test.go │ ├── by_itself.go │ ├── ptr_test.go │ └── by_field.go └── max │ ├── by_field_test.go │ ├── plz_test.go │ ├── by_itself_test.go │ ├── by_field.go │ └── by_itself.go ├── container ├── container.go └── pair │ ├── pair_test.go │ └── pair.go ├── disable.travis.yml ├── cp ├── tests │ ├── two_levels_test.go │ ├── model │ │ └── test_object.go │ ├── helper │ │ └── helper.go │ ├── fuzz_template_test.go │ ├── new_value_test.go │ ├── one_level_test.go │ ├── nil_test.go │ ├── simple_value_test.go │ ├── existing_value_test.go │ └── empty_interface_test.go ├── simple_value.go ├── from_interface.go ├── from_ptr.go ├── from_ptr_ptr.go ├── into_interface.go ├── slice_to_array.go ├── array_to_array.go ├── slice_to_slice.go ├── into_ptr.go ├── map_to_struct.go ├── map_to_map.go ├── struct_to_struct.go ├── struct_to_map.go └── anything.go ├── example ├── run_test.go ├── model │ └── model.go ├── pair.go ├── fp.go ├── cp.go └── generated.go ├── test.sh ├── generic ├── func_expand_test.go ├── declare_during_normal.go ├── declare_during_codegen.go ├── types.go ├── struct_define.go ├── gen.go ├── func_signature_test.go ├── symbol_name.go ├── struct_expand.go ├── generate_code.go ├── func_signature.go ├── func_expand.go └── func_define.go ├── cmd └── wombat-codegen │ └── main.go ├── cpJson ├── tests │ ├── model │ │ └── test_object.go │ ├── one_level_test.go │ ├── discard_test.go │ ├── invalid_test.go │ ├── simple_value_test.go │ ├── fuzz_template_test.go │ ├── existing_value_test.go │ ├── two_levels_test.go │ └── nil_test.go ├── ptr_to_json.go ├── simple_value_2_json.go ├── json_2_simple_value.go ├── slice_2_json.go ├── array_2_json.go ├── map_2_json.go ├── json_2_map.go ├── json_2_array.go ├── json_to_ptr.go ├── json_2_slice.go ├── init.go ├── struct_2_json.go └── json_2_struct.go ├── compiler ├── compiler_test.go └── compiler.go ├── README.md ├── wombat.go └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /coverage.txt 2 | -------------------------------------------------------------------------------- /wombat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v2pro/plz.codegen/HEAD/wombat.png -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "cp/tests/.*" 3 | - "cpJson/tests/.*" 4 | -------------------------------------------------------------------------------- /fp/testobj/model.go: -------------------------------------------------------------------------------- 1 | package testobj 2 | 3 | type TestObject struct { 4 | Field int 5 | } -------------------------------------------------------------------------------- /container/container.go: -------------------------------------------------------------------------------- 1 | package container 2 | 3 | import "github.com/v2pro/wombat/container/pair" 4 | 5 | var Pair = pair.Pair -------------------------------------------------------------------------------- /fp/fp.go: -------------------------------------------------------------------------------- 1 | package fp 2 | 3 | import ( 4 | // register functions to plz 5 | _ "github.com/v2pro/wombat/fp/compare" 6 | _ "github.com/v2pro/wombat/fp/max" 7 | ) 8 | 9 | 10 | -------------------------------------------------------------------------------- /disable.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - master 5 | 6 | before_install: 7 | - go get -t -v ./... 8 | 9 | script: 10 | - ./test.sh 11 | 12 | after_success: 13 | - bash <(curl -s https://codecov.io/bash) 14 | -------------------------------------------------------------------------------- /cp/tests/two_levels_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | ) 7 | 8 | func Test_slice_slice_int_2_slice_slice_int(t *testing.T) { 9 | runFuzzTest(t, reflect.TypeOf([][]int{}), reflect.TypeOf([][]int{})) 10 | } -------------------------------------------------------------------------------- /example/run_test.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import "testing" 4 | 5 | func Test_copy(t *testing.T) { 6 | Demo_copy(t) 7 | } 8 | 9 | func Test_max_min(t *testing.T) { 10 | Demo_max_min(t) 11 | } 12 | 13 | func Test_pair(t *testing.T) { 14 | Demo_pair(t) 15 | } 16 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | echo "" > coverage.txt 5 | 6 | for d in $(go list ./... | grep -v vendor); do 7 | go test -coverprofile=profile.out $d 8 | if [ -f profile.out ]; then 9 | cat profile.out >> coverage.txt 10 | rm profile.out 11 | fi 12 | done 13 | -------------------------------------------------------------------------------- /generic/func_expand_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | ) 7 | 8 | func Test_expand_symbol_name(t *testing.T) { 9 | should := require.New(t) 10 | expanded := expandSymbolName("cmp", ArgMap{"T": Int}) 11 | should.Equal("cmp_T_int", expanded) 12 | } 13 | -------------------------------------------------------------------------------- /generic/declare_during_normal.go: -------------------------------------------------------------------------------- 1 | //+build !codegen 2 | 3 | package generic 4 | 5 | var inDeclaringByExample = false 6 | 7 | type funcDeclaration struct { 8 | funcTemplate *FuncTemplate 9 | templateArgs []interface{} 10 | } 11 | 12 | var funcDeclarations = []funcDeclaration{} 13 | 14 | func Declare(example func()) { 15 | } 16 | -------------------------------------------------------------------------------- /cmd/wombat-codegen/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | "flag" 6 | "github.com/v2pro/wombat" 7 | ) 8 | 9 | func main() { 10 | pkgPath := flag.String("pkg", "", "the package to generate generic code for") 11 | flag.Parse() 12 | if *pkgPath == "" { 13 | flag.Usage() 14 | os.Exit(1) 15 | } 16 | wombat.Codegen(*pkgPath) 17 | } 18 | -------------------------------------------------------------------------------- /cp/tests/model/test_object.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type TypeA struct { 4 | Int int 5 | String string 6 | } 7 | 8 | type TypeB struct { 9 | Int int 10 | String string 11 | } 12 | 13 | type TypeC struct { 14 | Field1 int 15 | Field2 int 16 | } 17 | 18 | type TypeD struct { 19 | Field int 20 | } 21 | 22 | type TypeE struct { 23 | Field *int 24 | } -------------------------------------------------------------------------------- /fp/compare/by_itself_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/wombat/generic" 7 | ) 8 | 9 | func Test_compare(t *testing.T) { 10 | should := require.New(t) 11 | f := generic.Expand(ByItself, "T", generic.Int). 12 | (func(int, int) int) 13 | should.Equal(-1, f(3, 4)) 14 | should.Equal(0, f(3, 3)) 15 | should.Equal(1, f(4, 3)) 16 | } -------------------------------------------------------------------------------- /cp/simple_value.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | Anything.ImportFunc(copySimpleValue) 7 | } 8 | 9 | var copySimpleValue = generic.DefineFunc("CopySimpleValue(err *error, dst DT, src ST)"). 10 | Param("DT", "the dst type to copy into"). 11 | Param("ST", "the src type to copy from"). 12 | Source(` 13 | if dst != nil { 14 | *dst = ({{.DT|elem|name}})(src) 15 | } 16 | `) -------------------------------------------------------------------------------- /cpJson/tests/model/test_object.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type TypeA struct { 4 | Int int 5 | String string 6 | } 7 | 8 | type TypeB struct { 9 | Field *int 10 | } 11 | 12 | type TypeC struct { 13 | Slice []int 14 | Array [3]int 15 | Map map[string]int 16 | Struct TypeD 17 | } 18 | 19 | type TypeD struct { 20 | Int int 21 | String string 22 | } 23 | 24 | type TypeE struct { 25 | Field1 string 26 | Field3 string 27 | } -------------------------------------------------------------------------------- /fp/compare/ptr.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | ByItself.ImportFunc(comparePtr) 7 | } 8 | 9 | var comparePtr = generic.DefineFunc("ComparePtr(val1 T, val2 T) int"). 10 | Param("T", "the type of value to compare"). 11 | ImportFunc(ByItself). 12 | Source(` 13 | {{ $compare := expand "CompareByItself" "T" (.T|elem) }} 14 | return {{$compare}}(*val1, *val2)`) 15 | -------------------------------------------------------------------------------- /cp/tests/helper/helper.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import "encoding/json" 4 | 5 | func ToJson(obj interface{}) string { 6 | output, err := json.MarshalIndent(obj, "", " ") 7 | if err != nil { 8 | panic(err.Error()) 9 | } 10 | return string(output) 11 | } 12 | 13 | func FromJson(obj interface{}, encodedAsJson string) { 14 | err := json.Unmarshal([]byte(encodedAsJson), obj) 15 | if err != nil { 16 | panic(err.Error()) 17 | } 18 | } -------------------------------------------------------------------------------- /fp/compare/simple_value.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | ByItself.ImportFunc(compareSimpleValue) 7 | } 8 | 9 | var compareSimpleValue = generic.DefineFunc("CompareSimpleValue(val1 T, val2 T) int"). 10 | Param("T", "the type of value to compare"). 11 | Source(` 12 | if val1 < val2 { 13 | return -1 14 | } else if val1 == val2 { 15 | return 0 16 | } else { 17 | return 1 18 | }`) 19 | -------------------------------------------------------------------------------- /generic/declare_during_codegen.go: -------------------------------------------------------------------------------- 1 | //+build codegen 2 | 3 | package generic 4 | 5 | var inDeclaringByExample = false 6 | 7 | type funcDeclaration struct { 8 | funcTemplate *FuncTemplate 9 | templateArgs []interface{} 10 | } 11 | 12 | var funcDeclarations = []funcDeclaration{} 13 | 14 | func Declare(example func()) { 15 | defer func() { 16 | inDeclaringByExample = false 17 | }() 18 | inDeclaringByExample = true 19 | example() 20 | } 21 | -------------------------------------------------------------------------------- /compiler/compiler_test.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | ) 7 | 8 | func Test_dynamic_compile(t *testing.T) { 9 | should := require.New(t) 10 | plugin, err := DynamicCompile(` 11 | package main 12 | func Hello() string { 13 | return "Hello" 14 | } 15 | `) 16 | should.Nil(err) 17 | symbol, err := plugin.Lookup("Hello") 18 | should.Nil(err) 19 | f := symbol.(func() string) 20 | should.Equal("Hello", f()) 21 | } -------------------------------------------------------------------------------- /cp/from_interface.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | Anything.ImportFunc(copyFromInterface) 7 | } 8 | 9 | var copyFromInterface = generic.DefineFunc("CopyFromInterface(err *error, dst DT, src ST)"). 10 | Param("DT", "the dst type to copy into"). 11 | Param("ST", "the src type to copy from"). 12 | ImportFunc(Anything). 13 | Source(` 14 | newErr := copyDynamically(dst, src) 15 | if newErr != nil && *err == nil { 16 | *err = newErr 17 | }`) 18 | -------------------------------------------------------------------------------- /cp/from_ptr.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | Anything.ImportFunc(copyFromPtr) 7 | } 8 | 9 | var copyFromPtr = generic.DefineFunc("CopyFromPtr(err *error, dst DT, src ST)"). 10 | Param("DT", "the dst type to copy into"). 11 | Param("ST", "the src type to copy from"). 12 | ImportFunc(Anything). 13 | Source(` 14 | {{ $cp := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 15 | if src == nil { 16 | return 17 | } 18 | {{$cp}}(err, dst, *src)`) 19 | -------------------------------------------------------------------------------- /fp/compare/simple_value_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func init() { 10 | generic.DynamicCompilationEnabled = true 11 | } 12 | 13 | func Test_compare_int(t *testing.T) { 14 | should := require.New(t) 15 | f := generic.Expand(compareSimpleValue, "T", generic.Int). 16 | (func(int, int) int) 17 | should.Equal(-1, f(3, 4)) 18 | should.Equal(0, f(3, 3)) 19 | should.Equal(1, f(4, 3)) 20 | } 21 | -------------------------------------------------------------------------------- /cp/from_ptr_ptr.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | Anything.ImportFunc(copyFromPtrPtr) 7 | } 8 | 9 | var copyFromPtrPtr = generic.DefineFunc("CopyFromPtrPtr(err *error, dst DT, src ST)"). 10 | Param("DT", "the dst type to copy into"). 11 | Param("ST", "the src type to copy from"). 12 | ImportFunc(Anything). 13 | Source(` 14 | {{ $cp := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 15 | if src == nil { 16 | {{$cp}}(err, dst, nil) 17 | return 18 | } 19 | {{$cp}}(err, dst, *src)`) 20 | -------------------------------------------------------------------------------- /cpJson/tests/one_level_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | "github.com/v2pro/wombat/cpJson/tests/model" 7 | ) 8 | 9 | func Test_array_int(t *testing.T) { 10 | runFuzzTest(t, reflect.TypeOf([3]int{})) 11 | } 12 | 13 | func Test_slice_int(t *testing.T) { 14 | runFuzzTest(t, reflect.TypeOf([]int{})) 15 | } 16 | 17 | func Test_map_string_int(t *testing.T) { 18 | runFuzzTest(t, reflect.TypeOf(map[string]int{})) 19 | } 20 | 21 | func Test_struct(t *testing.T) { 22 | runFuzzTest(t, reflect.TypeOf(model.TypeA{})) 23 | } -------------------------------------------------------------------------------- /fp/compare/by_field_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/wombat/generic" 7 | "reflect" 8 | "github.com/v2pro/wombat/fp/testobj" 9 | ) 10 | 11 | func Test_compare_by_field(t *testing.T) { 12 | should := require.New(t) 13 | testObjectType := reflect.TypeOf(testobj.TestObject{}) 14 | f := generic.Expand(ByField, "T", testObjectType, "F", "Field"). 15 | (func(testobj.TestObject, testobj.TestObject) int) 16 | should.Equal(-1, f(testobj.TestObject{2}, testobj.TestObject{3})) 17 | } -------------------------------------------------------------------------------- /example/model/model.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type IntStringPair interface { 4 | First() int 5 | SetFirst(val int) 6 | Second() string 7 | SetSecond(val string) 8 | } 9 | 10 | type User struct { 11 | Score int 12 | } 13 | 14 | type UserProperties struct { 15 | City string 16 | Age int 17 | } 18 | type User2 struct { 19 | FirstName string 20 | LastName string 21 | Tags []int 22 | Properties *UserProperties 23 | } 24 | type UserInfo struct { 25 | FirstName *string 26 | LastName *string 27 | Tags []int 28 | Properties map[string]interface{} 29 | } -------------------------------------------------------------------------------- /generic/types.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "reflect" 4 | 5 | var Int = reflect.TypeOf(int(0)) 6 | var Int8 = reflect.TypeOf(int8(0)) 7 | var Int16 = reflect.TypeOf(int16(0)) 8 | var Int32 = reflect.TypeOf(int32(0)) 9 | var Int64 = reflect.TypeOf(int64(0)) 10 | var Uint = reflect.TypeOf(uint(0)) 11 | var Uint8 = reflect.TypeOf(uint8(0)) 12 | var Uint16 = reflect.TypeOf(uint16(0)) 13 | var Uint32 = reflect.TypeOf(uint32(0)) 14 | var Uint64 = reflect.TypeOf(uint64(0)) 15 | var Float32 = reflect.TypeOf(float32(0)) 16 | var Float64 = reflect.TypeOf(float64(0)) 17 | var String = reflect.TypeOf("") 18 | var Bool = reflect.TypeOf(true) -------------------------------------------------------------------------------- /fp/max/by_field_test.go: -------------------------------------------------------------------------------- 1 | package max 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "reflect" 7 | "github.com/v2pro/wombat/generic" 8 | "github.com/v2pro/wombat/fp/testobj" 9 | ) 10 | 11 | func init() { 12 | generic.DynamicCompilationEnabled = true 13 | } 14 | 15 | func Test_max_by_field(t *testing.T) { 16 | should := require.New(t) 17 | f := generic.Expand(ByField, "T", reflect.TypeOf([]testobj.TestObject{}), "F", "Field"). 18 | (func([]testobj.TestObject) testobj.TestObject) 19 | maxE := f([]testobj.TestObject{ 20 | {1}, {3}, {2}, 21 | }) 22 | should.Equal(testobj.TestObject{3}, maxE) 23 | } -------------------------------------------------------------------------------- /cpJson/ptr_to_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyPtrToJson) 11 | toJsonMap[reflect.Ptr] = "CopyPtrToJson" 12 | } 13 | 14 | var copyPtrToJson = generic.DefineFunc("CopyPtrToJson(err *error, dst DT, src ST)"). 15 | Param("DT", "the dst type to copy into"). 16 | Param("ST", "the src type to copy from"). 17 | ImportFunc(cp.Anything). 18 | Source(` 19 | {{ $cp := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 20 | if src == nil { 21 | dst.WriteNil() 22 | return 23 | } 24 | {{$cp}}(err, dst, *src) 25 | `) 26 | -------------------------------------------------------------------------------- /cpJson/tests/discard_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/wombat/cpJson/tests/model" 7 | ) 8 | 9 | func Test_discard_extra_struct_field(t *testing.T) { 10 | should := require.New(t) 11 | 12 | dst := model.TypeE{} 13 | src := `{"Field1":"1","Field2":"2","Field3":"3"}` 14 | should.Nil(jsonCopy(&dst, src)) 15 | should.Equal("1", dst.Field1) 16 | should.Equal("3", dst.Field3) 17 | } 18 | 19 | func Test_discard_extra_array_element(t *testing.T) { 20 | should := require.New(t) 21 | dst := [2]int{} 22 | src := `[1,2,3]` 23 | should.Nil(jsonCopy(&dst, src)) 24 | should.Equal([2]int{1, 2}, dst) 25 | } -------------------------------------------------------------------------------- /container/pair/pair_test.go: -------------------------------------------------------------------------------- 1 | package pair 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | "github.com/stretchr/testify/require" 7 | "reflect" 8 | ) 9 | 10 | func init() { 11 | generic.DynamicCompilationEnabled = true 12 | } 13 | 14 | func Test_pair(t *testing.T) { 15 | type IntStringPair interface { 16 | First() int 17 | SetFirst(val int) 18 | Second() string 19 | SetSecond(val string) 20 | } 21 | should := require.New(t) 22 | intStringPairType := reflect.TypeOf(new(IntStringPair)).Elem() 23 | pair := generic.New(Pair, intStringPairType).(IntStringPair) 24 | should.Equal(0, pair.First()) 25 | pair.SetFirst(1) 26 | should.Equal(1, pair.First()) 27 | } -------------------------------------------------------------------------------- /cp/into_interface.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | func init() { 6 | Anything.ImportFunc(copyIntoInterface) 7 | } 8 | 9 | var copyIntoInterface = generic.DefineFunc("CopyIntoInterface(err *error, dst DT, src ST)"). 10 | Param("DT", "the dst type to copy into"). 11 | Param("ST", "the src type to copy from"). 12 | ImportFunc(Anything). 13 | Source(` 14 | if *dst == nil { 15 | newDst := new({{.ST|name}}) 16 | newErr := copyDynamically(newDst, src) 17 | if newErr != nil && *err == nil { 18 | *err = newErr 19 | } 20 | *dst = *newDst 21 | } else { 22 | newErr := copyDynamically(*dst, src) 23 | if newErr != nil && *err == nil { 24 | *err = newErr 25 | } 26 | }`) 27 | -------------------------------------------------------------------------------- /container/pair/pair.go: -------------------------------------------------------------------------------- 1 | package pair 2 | 3 | import "github.com/v2pro/wombat/generic" 4 | 5 | var Pair = generic.DefineStruct("Pair"). 6 | Source(` 7 | {{ $T1 := .I | method "First" | returnType }} 8 | {{ $T2 := .I | method "Second" | returnType }} 9 | 10 | type {{.structName}} struct { 11 | first {{$T1|name}} 12 | second {{$T2|name}} 13 | } 14 | 15 | func (pair *{{.structName}}) SetFirst(val {{$T1|name}}) { 16 | pair.first = val 17 | } 18 | 19 | func (pair *{{.structName}}) First() {{$T1|name}} { 20 | return pair.first 21 | } 22 | 23 | func (pair *{{.structName}}) SetSecond(val {{$T2|name}}) { 24 | pair.second = val 25 | } 26 | 27 | func (pair *{{.structName}}) Second() {{$T2|name}} { 28 | return pair.second 29 | }`) 30 | -------------------------------------------------------------------------------- /cpJson/simple_value_2_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copySimpleValueToJson) 11 | for kind := range simpleValueMap { 12 | toJsonMap[kind] = "CopySimpleValueToJson" 13 | } 14 | } 15 | 16 | var copySimpleValueToJson = generic.DefineFunc( 17 | "CopySimpleValueToJson(err *error, dst DT, src ST)"). 18 | Param("DT", "the dst type to copy into"). 19 | Param("ST", "the src type to copy from"). 20 | ImportFunc(cp.Anything). 21 | Generators( 22 | "opFuncName", func(typ reflect.Type) string { 23 | return simpleValueMap[typ.Kind()] 24 | }). 25 | Source(` 26 | dst.Write{{.ST|opFuncName}}(src) 27 | `) -------------------------------------------------------------------------------- /cpJson/json_2_simple_value.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyJsonToSimpleValue) 11 | for kind := range simpleValueMap { 12 | fromJsonMap[kind] = "CopyJsonToSimpleValue" 13 | } 14 | } 15 | 16 | var copyJsonToSimpleValue = generic.DefineFunc( 17 | "CopyJsonToSimpleValue(err *error, dst DT, src ST)"). 18 | Param("DT", "the dst type to copy into"). 19 | Param("ST", "the src type to copy from"). 20 | ImportFunc(cp.Anything). 21 | Generators( 22 | "opFuncName", func(typ reflect.Type) string { 23 | return simpleValueMap[typ.Kind()] 24 | }). 25 | Source(` 26 | *dst = src.Read{{.DT|elem|opFuncName}}() 27 | `) 28 | -------------------------------------------------------------------------------- /fp/max/plz_test.go: -------------------------------------------------------------------------------- 1 | package max 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | "github.com/v2pro/wombat/fp/testobj" 8 | "github.com/v2pro/wombat/generic" 9 | ) 10 | 11 | func init() { 12 | generic.DynamicCompilationEnabled = true 13 | } 14 | 15 | func Test_max_of_nothing(t *testing.T) { 16 | should := require.New(t) 17 | should.Nil(plz.Max()) 18 | } 19 | 20 | func Test_max_of_simple_value(t *testing.T) { 21 | should := require.New(t) 22 | should.Equal(3, plz.Max(1, 3, 2)) 23 | } 24 | 25 | func Test_max_of_struct_by_field(t *testing.T) { 26 | should := require.New(t) 27 | should.Equal(testobj.TestObject{2}, plz.Max( 28 | testobj.TestObject{1}, testobj.TestObject{2}, 29 | "Field")) 30 | } 31 | 32 | -------------------------------------------------------------------------------- /example/pair.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "reflect" 7 | "github.com/v2pro/wombat/generic" 8 | "github.com/v2pro/wombat/example/model" 9 | "github.com/v2pro/wombat/container" 10 | "github.com/v2pro/wombat/container/pair" 11 | ) 12 | 13 | func init() { 14 | generic.Declare(func() { 15 | generic.New(pair.Pair, reflect.TypeOf(new(model.IntStringPair)).Elem()) 16 | }) 17 | } 18 | 19 | func Demo_pair(t *testing.T) { 20 | should := require.New(t) 21 | intStringPairType := reflect.TypeOf(new(model.IntStringPair)).Elem() 22 | pair := generic.New(container.Pair, intStringPairType).(model.IntStringPair) 23 | should.Equal(0, pair.First()) 24 | pair.SetFirst(1) 25 | should.Equal(1, pair.First()) 26 | } 27 | -------------------------------------------------------------------------------- /generic/struct_define.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | type StructTemplateBuilder struct { 4 | structTemplate *StructTemplate 5 | } 6 | 7 | func DefineStruct(structName string) *StructTemplateBuilder { 8 | return &StructTemplateBuilder{ 9 | structTemplate: &StructTemplate{ 10 | structName: structName, 11 | generators: map[string]interface{}{ 12 | "name": genName, 13 | "method": genMethod, 14 | "returnType": genReturnType, 15 | }, 16 | }, 17 | } 18 | } 19 | 20 | func (builder *StructTemplateBuilder) Source(source string) *StructTemplate { 21 | builder.structTemplate.templateSource = source 22 | return builder.structTemplate 23 | } 24 | 25 | type StructTemplate struct { 26 | structName string 27 | templateSource string 28 | generators map[string]interface{} 29 | } -------------------------------------------------------------------------------- /cp/slice_to_array.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copySliceToArray) 10 | } 11 | 12 | var copySliceToArray = generic.DefineFunc("CopySliceToArray(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "ptrArrayElem", func(dstType reflect.Type) reflect.Type { 18 | return reflect.PtrTo(dstType.Elem().Elem()) 19 | }). 20 | Source(` 21 | {{ $cp := expand "CopyAnything" "DT" (.DT|ptrArrayElem) "ST" (.ST|elem) }} 22 | dstLen := len(*dst) 23 | if len(src) < dstLen { 24 | dstLen = len(src) 25 | } 26 | for i := 0; i < dstLen; i++ { 27 | {{$cp}}(err, &dst[i], src[i]) 28 | }`) 29 | -------------------------------------------------------------------------------- /fp/compare/by_itself.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | var ByItself = generic.DefineFunc("CompareByItself(val1 T, val2 T) int"). 9 | Param("T", "the type of value to compare"). 10 | Generators("dispatch", dispatch). 11 | Source(` 12 | {{ $compare := expand (.T|dispatch) "T" .T }} 13 | return {{$compare}}(val1, val2)`) 14 | 15 | func dispatch(typ reflect.Type) string { 16 | switch typ.Kind() { 17 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 18 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 19 | reflect.Float32, reflect.Float64: 20 | return "CompareSimpleValue" 21 | case reflect.Ptr: 22 | return "ComparePtr" 23 | } 24 | panic("unsupported type: " + typ.String()) 25 | } 26 | -------------------------------------------------------------------------------- /cpJson/slice_2_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copySliceToJson) 11 | toJsonMap[reflect.Slice] = "CopySliceToJson" 12 | } 13 | 14 | var copySliceToJson = generic.DefineFunc( 15 | "CopySliceToJson(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Source(` 20 | {{ $cpElem := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 21 | if src == nil { 22 | dst.WriteNil() 23 | return 24 | } 25 | dst.WriteArrayStart() 26 | for i, elem := range src { 27 | if i != 0 { 28 | dst.WriteMore() 29 | } 30 | {{$cpElem}}(err, dst, elem) 31 | } 32 | dst.WriteArrayEnd() 33 | `) 34 | -------------------------------------------------------------------------------- /example/fp.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "github.com/v2pro/plz" 6 | _ "github.com/v2pro/wombat/fp" 7 | "testing" 8 | "github.com/v2pro/wombat/example/model" 9 | "github.com/v2pro/wombat/generic" 10 | ) 11 | 12 | //go:generate go install github.com/v2pro/wombat/cmd/wombat-codegen 13 | //go:generate $GOPATH/bin/wombat-codegen -pkg github.com/v2pro/wombat/example 14 | func init() { 15 | generic.Declare(func() { 16 | plz.Max(int(0)) 17 | plz.Max(float64(0)) 18 | plz.Max(model.User{}, "Score") 19 | }) 20 | } 21 | 22 | func Demo_max_min(t *testing.T) { 23 | should := require.New(t) 24 | should.Equal(3, plz.Max(1, 3, 2)) 25 | should.Equal(float64(3), plz.Max(1.0, 3.0, 2.0)) 26 | should.Equal(model.User{3}, plz.Max( 27 | model.User{1}, model.User{3}, model.User{2}, 28 | "Score")) 29 | } 30 | -------------------------------------------------------------------------------- /fp/compare/ptr_test.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/wombat/generic" 7 | "reflect" 8 | ) 9 | 10 | func Test_compare_ptr_int(t *testing.T) { 11 | should := require.New(t) 12 | f := generic.Expand(comparePtr, "T", reflect.PtrTo(generic.Int)). 13 | (func(*int, *int) int) 14 | should.Equal(-1, f(ptrOf(3), ptrOf(4))) 15 | should.Equal(0, f(ptrOf(3), ptrOf(3))) 16 | should.Equal(1, f(ptrOf(4), ptrOf(3))) 17 | } 18 | 19 | func Test_compare_ptr_ptr_int(t *testing.T) { 20 | should := require.New(t) 21 | f := generic.Expand(comparePtr, "T", reflect.PtrTo(reflect.PtrTo(generic.Int))). 22 | (func(**int, **int) int) 23 | should.Equal(-1, f(ptrPtrOf(3), ptrPtrOf(4))) 24 | } 25 | 26 | func ptrOf(obj int) *int { 27 | return &obj 28 | } 29 | 30 | func ptrPtrOf(obj int) **int { 31 | ptr := &obj 32 | return &ptr 33 | } -------------------------------------------------------------------------------- /cpJson/array_2_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyArrayToJson) 11 | toJsonMap[reflect.Array] = "CopyArrayToJson" 12 | } 13 | 14 | var copyArrayToJson = generic.DefineFunc( 15 | "CopyArrayToJson(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Generators( 20 | "elems", func(typ reflect.Type) []bool { 21 | return make([]bool, typ.Len()) 22 | }). 23 | Source(` 24 | {{ $cpElem := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 25 | dst.WriteArrayStart() 26 | {{ range $index, $_ := .ST|elems }} 27 | {{ if ne $index 0 }} 28 | dst.WriteMore() 29 | {{ end }} 30 | {{$cpElem}}(err, dst, src[{{$index}}]) 31 | {{ end }} 32 | dst.WriteArrayEnd() 33 | `) 34 | -------------------------------------------------------------------------------- /cpJson/map_2_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyMapToJson) 11 | toJsonMap[reflect.Map] = "CopyMapToJson" 12 | } 13 | 14 | var copyMapToJson = generic.DefineFunc( 15 | "CopyMapToJson(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Source(` 20 | {{ $cpElem := expand "CopyAnything" "DT" .DT "ST" (.ST|elem) }} 21 | if src == nil { 22 | dst.WriteNil() 23 | return 24 | } 25 | dst.WriteObjectStart() 26 | isFirst := true 27 | for k, v := range src { 28 | if isFirst { 29 | isFirst = false 30 | } else { 31 | dst.WriteMore() 32 | } 33 | dst.WriteString(k) 34 | dst.WriteRaw(":") 35 | {{$cpElem}}(err, dst, v) 36 | } 37 | dst.WriteObjectEnd() 38 | `) 39 | -------------------------------------------------------------------------------- /cp/array_to_array.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyArrayToArray) 10 | } 11 | 12 | var copyArrayToArray = generic.DefineFunc("CopyArrayToArray(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "minLength", func(dstType, srcType reflect.Type) int { 18 | if dstType.Elem().Len() < srcType.Len() { 19 | return dstType.Elem().Len() 20 | } else { 21 | return srcType.Len() 22 | } 23 | }, 24 | "ptrArrayElem", func(dstType reflect.Type) reflect.Type { 25 | return reflect.PtrTo(dstType.Elem().Elem()) 26 | }). 27 | Source(` 28 | {{ $cp := expand "CopyAnything" "DT" (.DT|ptrArrayElem) "ST" (.ST|elem) }} 29 | for i := 0; i < {{minLength .DT .ST}}; i++ { 30 | {{$cp}}(err, &dst[i], src[i]) 31 | } 32 | `) 33 | -------------------------------------------------------------------------------- /fp/compare/by_field.go: -------------------------------------------------------------------------------- 1 | package compare 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | "github.com/v2pro/plz" 7 | ) 8 | 9 | var logger = plz.LoggerOf("package", "compare") 10 | 11 | var ByField = generic.DefineFunc("CompareByField(val1 T, val2 T) int"). 12 | Param("T", "the type of value to compare"). 13 | Param("F", "the field name"). 14 | ImportFunc(ByItself). 15 | Generators("fieldOf", genFieldOf). 16 | Source(` 17 | {{ $field := fieldOf .T .F }} 18 | {{ $compare := expand "CompareByItself" "T" $field.Type }} 19 | return {{$compare}}(val1.{{.F}}, val2.{{.F}})`) 20 | 21 | func genFieldOf(typ reflect.Type, fieldName string) reflect.StructField { 22 | field, found := typ.FieldByName(fieldName) 23 | if !found { 24 | msg := "field " + fieldName + " not found in " + typ.String() 25 | logger.Error(nil, msg, "fieldName", fieldName, "type", typ.String()) 26 | panic(msg) 27 | } 28 | return field 29 | } 30 | -------------------------------------------------------------------------------- /cpJson/json_2_map.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyJsonToMap) 11 | // dispatch handled in init.go directly 12 | } 13 | 14 | var copyJsonToMap = generic.DefineFunc( 15 | "CopyJsonToMap(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Generators( 20 | "ptrMapElem", func(typ reflect.Type) reflect.Type { 21 | return reflect.PtrTo(typ.Elem()) 22 | }). 23 | Source(` 24 | {{ $cpElem := expand "CopyAnything" "DT" (.DT|ptrMapElem) "ST" .ST }} 25 | src.ReadMapCB(func(iter *jsoniter.Iterator, key string) bool { 26 | elem, found := dst[key] 27 | if found { 28 | {{$cpElem}}(err, &elem, iter) 29 | dst[key] = elem 30 | } else { 31 | newElem := new({{.DT|elem|name}}) 32 | {{$cpElem}}(err, newElem, iter) 33 | dst[key] = *newElem 34 | } 35 | return true 36 | })`) 37 | -------------------------------------------------------------------------------- /example/cp.go: -------------------------------------------------------------------------------- 1 | package example 2 | 3 | import ( 4 | "github.com/stretchr/testify/require" 5 | "github.com/v2pro/plz" 6 | "testing" 7 | _ "github.com/v2pro/wombat/cp" 8 | "github.com/v2pro/wombat/example/model" 9 | "github.com/v2pro/wombat/generic" 10 | ) 11 | 12 | func init() { 13 | generic.Declare(func() { 14 | plz.Copy(&model.UserInfo{}, model.User2{}) 15 | strVal := "" 16 | plz.Copy(&strVal, strVal) 17 | intVal := int(0) 18 | plz.Copy(&intVal, intVal) 19 | }) 20 | } 21 | 22 | func Demo_copy(t *testing.T) { 23 | should := require.New(t) 24 | 25 | userInfo := model.UserInfo{} 26 | plz.Copy(&userInfo, model.User2{ 27 | FirstName: "A", 28 | LastName: "B", 29 | Tags: []int{1, 2, 3}, 30 | Properties: &model.UserProperties{ 31 | "C", 32 | 30, 33 | }, 34 | }) 35 | should.Equal("A", *userInfo.FirstName) 36 | should.Equal("B", *userInfo.LastName) 37 | should.Equal([]int{1, 2, 3}, userInfo.Tags) 38 | should.Equal("C", userInfo.Properties["City"]) 39 | should.Equal(30, userInfo.Properties["Age"]) 40 | } 41 | -------------------------------------------------------------------------------- /cpJson/json_2_array.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyJsonToArray) 11 | fromJsonMap[reflect.Array] = "CopyJsonToArray" 12 | } 13 | 14 | var copyJsonToArray = generic.DefineFunc( 15 | "CopyJsonToArray(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Generators( 20 | "ptrArrayElem", func(typ reflect.Type) reflect.Type { 21 | return reflect.PtrTo(typ.Elem().Elem()) 22 | }, 23 | "arrayLen", func(typ reflect.Type) int { 24 | return typ.Elem().Len() 25 | }). 26 | Source(` 27 | {{ $cpElem := expand "CopyAnything" "DT" (.DT|ptrArrayElem) "ST" .ST }} 28 | index := 0 29 | src.ReadArrayCB(func(iter *jsoniter.Iterator) bool { 30 | if index < {{.DT|arrayLen}} { 31 | {{$cpElem}}(err, &dst[index], iter) 32 | } else { 33 | iter.Skip() 34 | } 35 | index++ 36 | return true 37 | }) 38 | `) 39 | -------------------------------------------------------------------------------- /cpJson/tests/invalid_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | "github.com/json-iterator/go" 8 | "errors" 9 | ) 10 | 11 | func Test_invalid_int(t *testing.T) { 12 | should := require.New(t) 13 | dst := int(0) 14 | src := `"100"` 15 | should.NotNil(jsonCopy(&dst, src)) 16 | } 17 | 18 | func Test_indirect_invalid_int(t *testing.T) { 19 | should := require.New(t) 20 | var dst int 21 | var pDst interface{} = &dst 22 | src := `"100"` 23 | should.NotNil(jsonCopy(&pDst, src)) 24 | } 25 | 26 | func Test_stream_err(t *testing.T) { 27 | should := require.New(t) 28 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, &faultyWriter{}, 512) 29 | srcBytes := []byte{} 30 | for i := 0; i < 2048; i++ { 31 | srcBytes = append(srcBytes, 'A') 32 | } 33 | src := string(srcBytes) 34 | should.NotNil(plz.Copy(stream, src)) 35 | } 36 | 37 | type faultyWriter struct { 38 | } 39 | 40 | func (writer *faultyWriter) Write([]byte) (int, error) { 41 | return 0, errors.New("faulty") 42 | } -------------------------------------------------------------------------------- /cp/slice_to_slice.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copySliceToSlice) 10 | } 11 | 12 | var copySliceToSlice = generic.DefineFunc("CopySliceToSlice(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "ptrSliceElem", func(dstType reflect.Type) reflect.Type { 18 | return reflect.PtrTo(dstType.Elem().Elem()) 19 | }). 20 | Source(` 21 | {{ $cp := expand "CopyAnything" "DT" (.DT|ptrSliceElem) "ST" (.ST|elem) }} 22 | if src == nil { 23 | *dst = nil 24 | return 25 | } 26 | dstLen := len(*dst) 27 | if len(src) < dstLen { 28 | dstLen = len(src) 29 | } 30 | for i := 0; i < dstLen; i++ { 31 | {{$cp}}(err, &(*dst)[i], src[i]) 32 | } 33 | defDst := *dst 34 | for i := dstLen; i < len(src); i++ { 35 | newElem := new({{ .DT|elem|elem|name }}) 36 | {{$cp}}(err, newElem, src[i]) 37 | defDst = append(defDst, *newElem) 38 | } 39 | *dst = defDst 40 | `) 41 | -------------------------------------------------------------------------------- /cpJson/json_to_ptr.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyJsonToPtr) 11 | fromJsonMap[reflect.Map] = "CopyJsonToPtr" 12 | fromJsonMap[reflect.Ptr] = "CopyJsonToPtr" 13 | } 14 | 15 | var copyJsonToPtr = generic.DefineFunc( 16 | "CopyJsonToPtr(err *error, dst DT, src ST)"). 17 | Param("DT", "the dst type to copy into"). 18 | Param("ST", "the src type to copy from"). 19 | ImportFunc(cp.Anything). 20 | Generators( 21 | "isMap", func(typ reflect.Type) bool { 22 | return typ.Kind() == reflect.Map 23 | }). 24 | Source(` 25 | {{ $cp := expand "CopyAnything" "DT" (.DT|elem) "ST" .ST }} 26 | if dst == nil { 27 | src.Skip() 28 | return 29 | } 30 | if src.ReadNil() { 31 | *dst = nil 32 | return 33 | } 34 | defDst := *dst 35 | if defDst == nil { 36 | {{ if .DT|elem|isMap }} 37 | defDst = {{.DT|elem|name}}{} 38 | {{ else }} 39 | defDst = new({{.DT|elem|elem|name}}) 40 | {{ end }} 41 | {{$cp}}(err, defDst, src) 42 | *dst = defDst 43 | return 44 | } 45 | {{ $cp }}(err, *dst, src) 46 | `) 47 | -------------------------------------------------------------------------------- /cpJson/json_2_slice.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | ) 8 | 9 | func init() { 10 | cp.Anything.ImportFunc(copyJsonToSlice) 11 | fromJsonMap[reflect.Slice] = "CopyJsonToSlice" 12 | } 13 | 14 | var copyJsonToSlice = generic.DefineFunc( 15 | "CopyJsonToSlice(err *error, dst DT, src ST)"). 16 | Param("DT", "the dst type to copy into"). 17 | Param("ST", "the src type to copy from"). 18 | ImportFunc(cp.Anything). 19 | Generators( 20 | "ptrSliceElem", func(typ reflect.Type) reflect.Type { 21 | return reflect.PtrTo(typ.Elem().Elem()) 22 | }). 23 | Source(` 24 | {{ $cpElem := expand "CopyAnything" "DT" (.DT|ptrSliceElem) "ST" .ST }} 25 | if src.ReadNil() { 26 | *dst = nil 27 | return 28 | } 29 | index := 0 30 | originalLen := len(*dst) 31 | src.ReadArrayCB(func(iter *jsoniter.Iterator) bool { 32 | if index < originalLen { 33 | elem := &(*dst)[index] 34 | {{$cpElem}}(err, elem, iter) 35 | } else { 36 | elem := new({{.DT|elem|elem|name}}) 37 | {{$cpElem}}(err, elem, iter) 38 | *dst = append(*dst, *elem) 39 | } 40 | index++ 41 | return true 42 | })`) 43 | -------------------------------------------------------------------------------- /cp/into_ptr.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyIntoPtr) 10 | } 11 | 12 | var copyIntoPtr = generic.DefineFunc("CopyIntoPtr(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "isMap", func(typ reflect.Type) bool { 18 | return typ.Kind() == reflect.Map 19 | }, 20 | "isNullable", func(typ reflect.Type) bool { 21 | switch typ.Kind() { 22 | case reflect.Ptr, reflect.Map, reflect.Interface, reflect.Slice: 23 | return true 24 | } 25 | return false 26 | }). 27 | Source(` 28 | {{ $cp := expand "CopyAnything" "DT" (.DT|elem) "ST" .ST }} 29 | {{ if .ST|isNullable }} 30 | if src == nil { 31 | *dst = nil 32 | return 33 | } 34 | {{ end }} 35 | defDst := *dst 36 | if defDst == nil { 37 | {{ if .DT|elem|isMap }} 38 | defDst = {{ .DT|elem|name }}{} 39 | {{ else }} 40 | defDst = new({{ .DT|elem|elem|name }}) 41 | {{ end }} 42 | {{$cp}}(err, defDst, src) 43 | *dst = defDst 44 | return 45 | } 46 | {{$cp}}(err, *dst, src) 47 | `) 48 | -------------------------------------------------------------------------------- /fp/max/by_itself_test.go: -------------------------------------------------------------------------------- 1 | package max 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | "reflect" 7 | "github.com/stretchr/testify/require" 8 | "github.com/google/gofuzz" 9 | ) 10 | 11 | func init() { 12 | generic.DynamicCompilationEnabled = true 13 | } 14 | 15 | func Test_slice_int(t *testing.T) { 16 | should := require.New(t) 17 | f := generic.Expand(ByItself, "T", reflect.TypeOf([]int{})). 18 | (func([]int) int) 19 | should.Equal(3, f([]int{1, 3, 2})) 20 | } 21 | 22 | func Benchmark_slice_int(b *testing.B) { 23 | fuzzer := fuzz.New() 24 | datasets := make([][]int, 32) 25 | typedDatasets := make([][]int, 32) 26 | for i := 0; i < len(datasets); i++ { 27 | dataset := make([]int, 100) 28 | typedDataset := make([]int, 100) 29 | for j := 0; j < len(dataset); j++ { 30 | val := int(0) 31 | fuzzer.Fuzz(&val) 32 | dataset[j] = val 33 | typedDataset[j] = val 34 | } 35 | datasets[i] = dataset 36 | typedDatasets[i] = typedDataset 37 | } 38 | f := generic.Expand(ByItself, "T", reflect.TypeOf([]int{})). 39 | (func([]int) int) 40 | b.Run("plz", func(b *testing.B) { 41 | b.ReportAllocs() 42 | for i := 0; i < b.N; i++ { 43 | f(datasets[i%32]) 44 | } 45 | }) 46 | } -------------------------------------------------------------------------------- /cp/tests/fuzz_template_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | "testing" 7 | _ "github.com/v2pro/wombat/cp" 8 | ) 9 | 10 | func init() { 11 | generic.DynamicCompilationEnabled = true 12 | } 13 | 14 | var fuzzTestFunc = generic.DefineFunc("RandomTest(t *testing.T)"). 15 | Param("DT", "destination type"). 16 | Param("ST", "source type"). 17 | ImportPackage("testing"). 18 | ImportPackage("github.com/stretchr/testify/require"). 19 | ImportPackage("github.com/google/gofuzz"). 20 | ImportPackage("github.com/v2pro/wombat/cp/tests/helper"). 21 | ImportPackage("github.com/v2pro/plz"). 22 | Source(` 23 | should := require.New(t) 24 | fz := fuzz.New().MaxDepth(10).NilChance(0.3) 25 | for i := 0; i < 100; i++ { 26 | var src {{.ST|name}} 27 | fz.Fuzz(&src) 28 | srcJson := helper.ToJson(src) 29 | var dst1 {{.DT|name}} 30 | should.Nil(plz.Copy(&dst1, src)) 31 | var dst2 {{.DT|name}} 32 | helper.FromJson(&dst2, srcJson) 33 | dst1Json := helper.ToJson(dst1) 34 | dst2Json := helper.ToJson(dst2) 35 | should.Equal(dst2Json, dst1Json) 36 | }`) 37 | 38 | func runFuzzTest(t *testing.T, dstType reflect.Type, srcType reflect.Type) { 39 | f := generic.Expand(fuzzTestFunc, "DT", dstType, "ST", srcType). 40 | (func(*testing.T)) 41 | f(t) 42 | } 43 | -------------------------------------------------------------------------------- /cpJson/tests/simple_value_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | ) 7 | 8 | func Test_int(t *testing.T) { 9 | runFuzzTest(t, generic.Int) 10 | } 11 | 12 | func Test_int8(t *testing.T) { 13 | runFuzzTest(t, generic.Int8) 14 | } 15 | 16 | func Test_int16(t *testing.T) { 17 | runFuzzTest(t, generic.Int16) 18 | } 19 | 20 | func Test_int32(t *testing.T) { 21 | runFuzzTest(t, generic.Int32) 22 | } 23 | 24 | func Test_int64(t *testing.T) { 25 | runFuzzTest(t, generic.Int64) 26 | } 27 | 28 | func Test_uint(t *testing.T) { 29 | runFuzzTest(t, generic.Uint) 30 | } 31 | 32 | func Test_uint8(t *testing.T) { 33 | runFuzzTest(t, generic.Uint8) 34 | } 35 | 36 | func Test_uint16(t *testing.T) { 37 | runFuzzTest(t, generic.Uint16) 38 | } 39 | 40 | func Test_uint32(t *testing.T) { 41 | runFuzzTest(t, generic.Uint32) 42 | } 43 | 44 | func Test_uint64(t *testing.T) { 45 | runFuzzTest(t, generic.Uint64) 46 | } 47 | 48 | func Test_float32(t *testing.T) { 49 | runFuzzTest(t, generic.Float32) 50 | } 51 | 52 | func Test_float64(t *testing.T) { 53 | runFuzzTest(t, generic.Float64) 54 | } 55 | 56 | func Test_string(t *testing.T) { 57 | runFuzzTest(t, generic.String) 58 | } 59 | 60 | func Test_bool(t *testing.T) { 61 | runFuzzTest(t, generic.Bool) 62 | } 63 | 64 | -------------------------------------------------------------------------------- /cp/tests/new_value_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | "github.com/v2pro/wombat/cp/tests/model" 8 | ) 9 | 10 | func Test_new_int_2_ptr_int(t *testing.T) { 11 | should := require.New(t) 12 | var dst *int 13 | src := int(1) 14 | should.Nil(plz.Copy(&dst, src)) 15 | should.Equal(1, *dst) 16 | } 17 | 18 | func Test_new_array_int_2_ptr_array_int(t *testing.T) { 19 | should := require.New(t) 20 | var dst *[3]int 21 | src := [3]int{1, 2, 3} 22 | should.Nil(plz.Copy(&dst, src)) 23 | should.Equal([3]int{1, 2, 3}, *dst) 24 | } 25 | 26 | func Test_new_struct_2_ptr_struct(t *testing.T) { 27 | should := require.New(t) 28 | var dst *model.TypeC 29 | src := model.TypeC{Field1:1} 30 | should.Nil(plz.Copy(&dst, src)) 31 | should.Equal(1, dst.Field1) 32 | } 33 | 34 | func Test_new_slice_int_2_slice_int(t *testing.T) { 35 | should := require.New(t) 36 | var dst []int 37 | src := []int{1, 2, 3} 38 | should.Nil(plz.Copy(&dst, src)) 39 | should.Equal([]int{1, 2, 3}, dst) 40 | } 41 | 42 | func Test_new_map_string_int_2_map_string_int(t *testing.T) { 43 | should := require.New(t) 44 | var dst map[string]int 45 | src := map[string]int{"Field": 1} 46 | should.Nil(plz.Copy(&dst, src)) 47 | should.Equal(map[string]int{"Field": 1}, dst) 48 | } 49 | -------------------------------------------------------------------------------- /generic/gen.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "reflect" 5 | "fmt" 6 | "crypto/sha1" 7 | "encoding/base32" 8 | "runtime" 9 | ) 10 | 11 | func genName(typ reflect.Type) string { 12 | if typ.PkgPath() != "" { 13 | if typ.PkgPath() == state.pkgPath { 14 | return typ.Name() 15 | } 16 | state.importPackages[typ.PkgPath()] = true 17 | } 18 | switch typ.Kind() { 19 | case reflect.Ptr: 20 | genName(typ.Elem()) 21 | case reflect.Slice: 22 | genName(typ.Elem()) 23 | case reflect.Array: 24 | genName(typ.Elem()) 25 | } 26 | return typ.String() 27 | } 28 | 29 | func genKey(typ reflect.Type) reflect.Type { 30 | return typ.Key() 31 | } 32 | 33 | func genElem(typ reflect.Type) reflect.Type { 34 | return typ.Elem() 35 | } 36 | 37 | func genMethod(methodName string, typ reflect.Type) (*reflect.Method, error) { 38 | method, found := typ.MethodByName(methodName) 39 | if found { 40 | return &method, nil 41 | } else { 42 | return nil, fmt.Errorf("method %s not found in %s", methodName, typ.String()) 43 | } 44 | } 45 | 46 | func genReturnType(method *reflect.Method) reflect.Type { 47 | return method.Type.Out(0) 48 | } 49 | 50 | func hash(source string) string { 51 | h := sha1.New() 52 | h.Write([]byte(source)) 53 | h.Write([]byte(runtime.Version())) 54 | return "g" + base32.StdEncoding.EncodeToString(h.Sum(nil)) 55 | } 56 | -------------------------------------------------------------------------------- /generic/func_signature_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | ) 7 | 8 | func Test_parse_signature(t *testing.T) { 9 | tests := []struct { 10 | input string 11 | expected funcSignature 12 | }{ 13 | {" compare (a T, b T) int", funcSignature{ 14 | funcName: "compare", 15 | funcParams: []funcParam{ 16 | {"a", "T"}, 17 | {"b", "T"}, 18 | }, 19 | funcReturns: []funcReturn{ 20 | {"", "int"}, 21 | }, 22 | }}, 23 | {" compare (a T, b T)", funcSignature{ 24 | funcName: "compare", 25 | funcParams: []funcParam{ 26 | {"a", "T"}, 27 | {"b", "T"}, 28 | }, 29 | funcReturns: []funcReturn{ 30 | }, 31 | }}, 32 | {" compare ()", funcSignature{ 33 | funcName: "compare", 34 | funcParams: []funcParam{ 35 | }, 36 | funcReturns: []funcReturn{ 37 | }, 38 | }}, 39 | {" compare () (a int, b err)", funcSignature{ 40 | funcName: "compare", 41 | funcParams: []funcParam{ 42 | }, 43 | funcReturns: []funcReturn{ 44 | {"a", "int"}, 45 | {"b", "err"}, 46 | }, 47 | }}, 48 | } 49 | for _, test := range tests { 50 | t.Run(test.input, func(t *testing.T) { 51 | should := require.New(t) 52 | signature, err := parseSignature(test.input) 53 | should.Nil(err) 54 | should.Equal(test.expected, *signature) 55 | }) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /cpJson/tests/fuzz_template_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "testing" 6 | "reflect" 7 | _ "github.com/v2pro/wombat/cpJson" 8 | ) 9 | 10 | func init() { 11 | generic.DynamicCompilationEnabled = true 12 | } 13 | 14 | var fuzzTestFunc = generic.DefineFunc("RandomTest(t *testing.T)"). 15 | Param("T", "type for test"). 16 | ImportPackage("testing"). 17 | ImportPackage("fmt"). 18 | ImportPackage("reflect"). 19 | ImportPackage("github.com/stretchr/testify/require"). 20 | ImportPackage("github.com/google/gofuzz"). 21 | ImportPackage("github.com/v2pro/plz"). 22 | ImportPackage("github.com/json-iterator/go"). 23 | Source(` 24 | should := require.New(t) 25 | fz := fuzz.New().MaxDepth(10).NilChance(0.3) 26 | for i := 0; i < 100; i++ { 27 | var src {{.T|name}} 28 | fz.Fuzz(&src) 29 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 512) 30 | should.NilValue(plz.Copy(stream, src)) 31 | var dst {{.T|name}} 32 | iterator := jsoniter.ParseBytes(jsoniter.ConfigDefault, stream.Buffer()) 33 | should.NilValue(plz.Copy(&dst, iterator)) 34 | if !reflect.DeepEqual(src, dst) { 35 | fmt.Println(src) 36 | fmt.Println(string(stream.Buffer())) 37 | fmt.Println(dst) 38 | t.FailNow() 39 | } 40 | }`) 41 | 42 | func runFuzzTest(t *testing.T, typ reflect.Type) { 43 | f := generic.Expand(fuzzTestFunc, "T", typ). 44 | (func(*testing.T)) 45 | f(t) 46 | } 47 | -------------------------------------------------------------------------------- /cpJson/init.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "reflect" 5 | "github.com/json-iterator/go" 6 | "github.com/v2pro/wombat/cp" 7 | ) 8 | 9 | var iteratorType = reflect.TypeOf(new(jsoniter.Iterator)) 10 | var streamType = reflect.TypeOf(new(jsoniter.Stream)) 11 | var toJsonMap = map[reflect.Kind]string{} 12 | var fromJsonMap = map[reflect.Kind]string{} 13 | var simpleValueMap = map[reflect.Kind]string{ 14 | reflect.Int: "Int", 15 | reflect.Int8: "Int8", 16 | reflect.Int16: "Int16", 17 | reflect.Int32: "Int32", 18 | reflect.Int64: "Int64", 19 | reflect.Uint: "Uint", 20 | reflect.Uint8: "Uint8", 21 | reflect.Uint16: "Uint16", 22 | reflect.Uint32: "Uint32", 23 | reflect.Uint64: "Uint64", 24 | reflect.Float32: "Float32", 25 | reflect.Float64: "Float64", 26 | reflect.String: "StringValue", 27 | reflect.Bool: "BoolValue", 28 | } 29 | 30 | func init() { 31 | cp.Dispatchers = append(cp.Dispatchers, func(dstType, srcType reflect.Type) string { 32 | if srcType != iteratorType { 33 | return "" 34 | } 35 | if dstType.Kind() == reflect.Map { 36 | return "CopyJsonToMap" 37 | } 38 | if dstType.Kind() != reflect.Ptr { 39 | return "" 40 | } 41 | return fromJsonMap[dstType.Elem().Kind()] 42 | }) 43 | cp.Dispatchers = append(cp.Dispatchers, func(dstType, srcType reflect.Type) string { 44 | if dstType != streamType { 45 | return "" 46 | } 47 | return toJsonMap[srcType.Kind()] 48 | }) 49 | } 50 | -------------------------------------------------------------------------------- /cpJson/tests/existing_value_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | "github.com/json-iterator/go" 8 | "github.com/v2pro/wombat/cpJson/tests/model" 9 | ) 10 | 11 | func Test_existing_array_ptr_int(t *testing.T) { 12 | should := require.New(t) 13 | existing := int(0) 14 | dst := [3]*int{&existing} 15 | src := `[1,2,3]` 16 | should.Nil(jsonCopy(&dst, src)) 17 | should.Equal(1, existing) 18 | } 19 | 20 | func Test_existing_slice_ptr_int(t *testing.T) { 21 | should := require.New(t) 22 | existing := int(0) 23 | dst := []*int{&existing} 24 | src := `[1,2,3]` 25 | should.Nil(jsonCopy(&dst, src)) 26 | should.Equal(1, existing) 27 | } 28 | 29 | func Test_existing_map_string_ptr_int(t *testing.T) { 30 | should := require.New(t) 31 | existing := int(0) 32 | dst := map[string]*int{"Field": &existing} 33 | src := `{"Field":1}` 34 | should.Nil(jsonCopy(&dst, src)) 35 | should.Equal(1, existing) 36 | } 37 | 38 | func Test_existing_struct(t *testing.T) { 39 | should := require.New(t) 40 | 41 | existing := int(0) 42 | dst := model.TypeB{Field:&existing} 43 | src := `{"Field":1}` 44 | should.Nil(jsonCopy(&dst, src)) 45 | should.Equal(1, existing) 46 | } 47 | 48 | func jsonCopy(dst interface{}, srcJson string) error { 49 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, srcJson) 50 | return plz.Copy(dst, iter) 51 | } 52 | -------------------------------------------------------------------------------- /cp/map_to_struct.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyMapToStruct) 10 | } 11 | 12 | var copyMapToStruct = generic.DefineFunc("CopyMapToStruct(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "calcBindings", func(dstType, srcType reflect.Type) interface{} { 18 | bindings := []interface{}{} 19 | for i := 0; i < dstType.NumField(); i++ { 20 | dstField := dstType.Field(i) 21 | bindings = append(bindings, map[string]interface{}{ 22 | "srcFieldName": dstField.Name, 23 | "srcFieldType": srcType.Elem(), 24 | "dstFieldName": dstField.Name, 25 | "dstFieldType": reflect.PtrTo(dstField.Type), 26 | }) 27 | } 28 | return bindings 29 | }, 30 | "assignCp", func(binding map[string]interface{}, cpFuncName string) string { 31 | binding["cp"] = cpFuncName 32 | return "" 33 | }). 34 | Source(` 35 | {{ $bindings := calcBindings (.DT|elem) .ST }} 36 | {{ range $_, $binding := $bindings}} 37 | {{ $cp := expand "CopyAnything" "DT" $binding.dstFieldType "ST" $binding.srcFieldType }} 38 | {{ assignCp $binding $cp }} 39 | {{ end }} 40 | for key, elem := range src { 41 | switch key { 42 | {{ range $_, $binding := $bindings }} 43 | case "{{$binding.srcFieldName}}": 44 | {{$binding.cp}}(err, &dst.{{$binding.dstFieldName}}, elem) 45 | {{ end }} 46 | } 47 | }`) 48 | -------------------------------------------------------------------------------- /fp/max/by_field.go: -------------------------------------------------------------------------------- 1 | package max 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/fp/compare" 6 | "reflect" 7 | "github.com/v2pro/plz/util" 8 | ) 9 | 10 | func init() { 11 | util.GenMaxByField = func(typ reflect.Type, fieldName string) func(collection []interface{}) interface{} { 12 | return generic.Expand(ByFieldForPlz, "T", typ, "F", fieldName). 13 | (func(collection []interface{}) interface{}) 14 | } 15 | } 16 | 17 | var ByField = generic.DefineFunc("MaxByField(vals T) E"). 18 | Param("T", "array type"). 19 | Param("E", "array element type", func(argMap generic.ArgMap) interface{} { 20 | return argMap["T"].(reflect.Type).Elem() 21 | }). 22 | Param("F", "the field to compare"). 23 | ImportFunc(compare.ByField). 24 | Source(` 25 | {{ $compare := expand "CompareByField" "T" .E "F" .F }} 26 | currentMax := vals[0] 27 | for i := 1; i < len(vals); i++ { 28 | if {{$compare}}(vals[i], currentMax) > 0 { 29 | currentMax = vals[i] 30 | } 31 | } 32 | return currentMax`) 33 | 34 | var ByFieldForPlz = generic.DefineFunc("MaxByFieldForPlz(vals []interface{}) interface{}"). 35 | Param("T", "array element type"). 36 | Param("F", "the field to compare"). 37 | ImportFunc(compare.ByField). 38 | Source(` 39 | {{ $compare := expand "CompareByField" "T" .T "F" .F }} 40 | currentMax := vals[0].({{.T|name}}) 41 | for i := 1; i < len(vals); i++ { 42 | typedVal := vals[i].({{.T|name}}) 43 | if {{$compare}}(typedVal, currentMax) > 0 { 44 | currentMax = typedVal 45 | } 46 | } 47 | return currentMax`) 48 | -------------------------------------------------------------------------------- /cp/map_to_map.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyMapToMap) 10 | } 11 | 12 | var copyMapToMap = generic.DefineFunc("CopyMapToMap(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "ptrMapElem", func(typ reflect.Type) reflect.Type { 18 | if typ.Kind() != reflect.Map { 19 | panic("expects Map, but found " + typ.String()) 20 | } 21 | return reflect.PtrTo(typ.Elem()) 22 | }, 23 | "ptrMapKey", func(typ reflect.Type) reflect.Type { 24 | if typ.Kind() != reflect.Map { 25 | panic("expects Map, but found " + typ.String()) 26 | } 27 | return reflect.PtrTo(typ.Key()) 28 | }, 29 | "mapKey", func(typ reflect.Type) reflect.Type { 30 | if typ.Kind() != reflect.Map { 31 | panic("expects Map, but found " + typ.String()) 32 | } 33 | return typ.Key() 34 | }). 35 | Source(` 36 | {{ $cpElem := expand "CopyAnything" "DT" (.DT|ptrMapElem) "ST" (.ST|elem) }} 37 | {{ $cpKey := expand "CopyAnything" "DT" (.DT|ptrMapKey) "ST" (.ST|mapKey) }} 38 | for key, elem := range src { 39 | existingElem, found := dst[key] 40 | if found { 41 | {{$cpElem}}(err, &existingElem, elem) 42 | dst[key] = existingElem 43 | } else { 44 | newKey := new({{.DT|mapKey|name}}) 45 | {{$cpKey}}(err, newKey, key) 46 | newElem := new({{.DT|elem|name}}) 47 | {{$cpElem}}(err, newElem, elem) 48 | dst[*newKey] = *newElem 49 | } 50 | }`) 51 | -------------------------------------------------------------------------------- /cp/tests/one_level_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | "reflect" 7 | "github.com/v2pro/wombat/cp/tests/model" 8 | ) 9 | 10 | func Test_ptr_int_2_int(t *testing.T) { 11 | runFuzzTest(t, generic.Int, reflect.PtrTo(generic.Int)) 12 | } 13 | 14 | func Test_int_2_ptr_int(t *testing.T) { 15 | runFuzzTest(t, reflect.PtrTo(generic.Int), generic.Int) 16 | } 17 | 18 | func Test_array_int_2_array_int(t *testing.T) { 19 | runFuzzTest(t, reflect.TypeOf([3]int{}), reflect.TypeOf([3]int{})) 20 | } 21 | 22 | func Test_array_int_2_slice_int(t *testing.T) { 23 | runFuzzTest(t, reflect.TypeOf([]int{}), reflect.TypeOf([3]int{})) 24 | } 25 | 26 | func Test_slice_int_2_array_int(t *testing.T) { 27 | runFuzzTest(t, reflect.TypeOf([3]int{}), reflect.TypeOf([]int{})) 28 | } 29 | 30 | func Test_slice_int_2_slice_int(t *testing.T) { 31 | runFuzzTest(t, reflect.TypeOf([]int{}), reflect.TypeOf([]int{})) 32 | } 33 | 34 | func Test_map_string_int_2_map_string_int(t *testing.T) { 35 | runFuzzTest(t, reflect.TypeOf(map[string]int{}), reflect.TypeOf(map[string]int{})) 36 | } 37 | 38 | func Test_struct_2_struct(t *testing.T) { 39 | runFuzzTest(t, reflect.TypeOf(model.TypeA{}), reflect.TypeOf(model.TypeB{})) 40 | } 41 | 42 | func Test_struct_2_map_string_int(t *testing.T) { 43 | runFuzzTest(t, reflect.TypeOf(map[string]int{}), reflect.TypeOf(model.TypeC{})) 44 | } 45 | 46 | func Test_map_string_int_2_struct(t *testing.T) { 47 | runFuzzTest(t, reflect.TypeOf(model.TypeC{}), reflect.TypeOf(map[string]int{})) 48 | } -------------------------------------------------------------------------------- /cp/struct_to_struct.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyStructToStruct) 10 | } 11 | 12 | var copyStructToStruct = generic.DefineFunc("CopyStructToStruct(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "calcBindings", func(dstType, srcType reflect.Type) interface{} { 18 | bindings := []interface{}{} 19 | for i := 0; i < dstType.NumField(); i++ { 20 | dstField := dstType.Field(i) 21 | srcField, srcFieldFound := srcType.FieldByName(dstField.Name) 22 | if !srcFieldFound { 23 | continue 24 | } 25 | bindings = append(bindings, map[string]interface{}{ 26 | "srcFieldName": srcField.Name, 27 | "srcFieldType": srcField.Type, 28 | "dstFieldName": dstField.Name, 29 | "dstFieldType": reflect.PtrTo(dstField.Type), 30 | }) 31 | } 32 | return bindings 33 | }, 34 | "assignCp", func(binding map[string]interface{}, cpFuncName string) string { 35 | binding["cp"] = cpFuncName 36 | return "" 37 | }). 38 | Source(` 39 | {{ $bindings := calcBindings (.DT|elem) .ST }} 40 | {{ range $_, $binding := $bindings}} 41 | {{ $cp := expand "CopyAnything" "DT" $binding.dstFieldType "ST" $binding.srcFieldType }} 42 | {{ assignCp $binding $cp }} 43 | {{ end }} 44 | {{ range $_, $binding := $bindings }} 45 | {{$binding.cp}}(err, &dst.{{$binding.dstFieldName}}, src.{{$binding.srcFieldName}}) 46 | {{ end }}`) 47 | -------------------------------------------------------------------------------- /cp/tests/nil_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | ) 8 | 9 | func Test_nil_int_2_int(t *testing.T) { 10 | should := require.New(t) 11 | src := int(1) 12 | should.Nil(plz.Copy((*int)(nil), src)) 13 | } 14 | 15 | func Test_nil_ptr_int_2_int(t *testing.T) { 16 | should := require.New(t) 17 | dst := int(0) 18 | should.Nil(plz.Copy(&dst, (*int)(nil))) 19 | } 20 | 21 | func Test_nil_ptr_ptr_int_2_int(t *testing.T) { 22 | should := require.New(t) 23 | dst := int(0) 24 | should.Nil(plz.Copy(&dst, (**int)(nil))) 25 | var src *int 26 | should.Nil(plz.Copy(&dst, &src)) 27 | } 28 | 29 | func Test_nil_ptr_int_2_ptr_int(t *testing.T) { 30 | should := require.New(t) 31 | dst := int(0) 32 | pDst := &dst 33 | should.Nil(plz.Copy(&pDst, (*int)(nil))) 34 | should.Nil(pDst) 35 | } 36 | 37 | func Test_nil_ptr_ptr_int_2_ptr_int(t *testing.T) { 38 | should := require.New(t) 39 | dst := int(0) 40 | pDst := &dst 41 | should.Nil(plz.Copy(&pDst, (**int)(nil))) 42 | should.Nil(pDst) 43 | pDst = &dst 44 | var src *int 45 | should.Nil(plz.Copy(&pDst, &src)) 46 | should.Nil(pDst) 47 | } 48 | 49 | func Test_nil_slice_int_2_slice_int(t *testing.T) { 50 | should := require.New(t) 51 | dst := []int{1, 2, 3} 52 | var src []int 53 | should.Nil(plz.Copy(&dst, src)) 54 | should.Nil(dst) 55 | } 56 | 57 | func Test_nil_map_string_int_2_map_string_int(t *testing.T) { 58 | should := require.New(t) 59 | dst := map[string]int{"Field": 1} 60 | var src map[string]int 61 | should.Nil(plz.Copy(&dst, src)) 62 | should.Nil(dst) 63 | } 64 | -------------------------------------------------------------------------------- /cp/tests/simple_value_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/v2pro/wombat/generic" 6 | ) 7 | 8 | func Test_int_2_int(t *testing.T) { 9 | runFuzzTest(t, generic.Int, generic.Int) 10 | } 11 | 12 | func Test_int8_2_int8(t *testing.T) { 13 | runFuzzTest(t, generic.Int8, generic.Int8) 14 | } 15 | 16 | func Test_int16_2_int16(t *testing.T) { 17 | runFuzzTest(t, generic.Int16, generic.Int16) 18 | } 19 | 20 | func Test_int32_2_int32(t *testing.T) { 21 | runFuzzTest(t, generic.Int32, generic.Int32) 22 | } 23 | 24 | func Test_int64_2_int64(t *testing.T) { 25 | runFuzzTest(t, generic.Int64, generic.Int64) 26 | } 27 | 28 | func Test_uint_2_uint(t *testing.T) { 29 | runFuzzTest(t, generic.Uint, generic.Uint) 30 | } 31 | 32 | func Test_uint8_2_uint8(t *testing.T) { 33 | runFuzzTest(t, generic.Uint8, generic.Uint8) 34 | } 35 | 36 | func Test_uint16_2_uint16(t *testing.T) { 37 | runFuzzTest(t, generic.Uint16, generic.Uint16) 38 | } 39 | 40 | func Test_uint32_2_uint32(t *testing.T) { 41 | runFuzzTest(t, generic.Uint32, generic.Uint32) 42 | } 43 | 44 | func Test_uint64_2_uint64(t *testing.T) { 45 | runFuzzTest(t, generic.Uint64, generic.Uint64) 46 | } 47 | 48 | func Test_bool_2_bool(t *testing.T) { 49 | runFuzzTest(t, generic.Bool, generic.Bool) 50 | } 51 | 52 | func Test_string_2_string(t *testing.T) { 53 | runFuzzTest(t, generic.String, generic.String) 54 | } 55 | 56 | func Test_float32_2_float32(t *testing.T) { 57 | runFuzzTest(t, generic.Float32, generic.Float32) 58 | } 59 | 60 | func Test_float64_2_float64(t *testing.T) { 61 | runFuzzTest(t, generic.Float64, generic.Float64) 62 | } 63 | -------------------------------------------------------------------------------- /cpJson/tests/two_levels_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "reflect" 6 | "github.com/v2pro/wombat/cpJson/tests/model" 7 | ) 8 | 9 | func Test_array_array_int(t *testing.T) { 10 | runFuzzTest(t, reflect.TypeOf([3][3]int{})) 11 | } 12 | 13 | func Test_array_map_string_int(t *testing.T) { 14 | runFuzzTest(t, reflect.TypeOf([3]map[string]int{})) 15 | } 16 | 17 | func Test_array_slice_int(t *testing.T) { 18 | runFuzzTest(t, reflect.TypeOf([3][]int{})) 19 | } 20 | 21 | func Test_array_struct(t *testing.T) { 22 | runFuzzTest(t, reflect.TypeOf([3]model.TypeA{})) 23 | } 24 | 25 | func Test_slice_array_int(t *testing.T) { 26 | runFuzzTest(t, reflect.TypeOf([][3]int{})) 27 | } 28 | 29 | func Test_slice_map_string_int(t *testing.T) { 30 | runFuzzTest(t, reflect.TypeOf([]map[string]int{})) 31 | } 32 | 33 | func Test_slice_slice_int(t *testing.T) { 34 | runFuzzTest(t, reflect.TypeOf([][]int{})) 35 | } 36 | 37 | func Test_slice_struct(t *testing.T) { 38 | runFuzzTest(t, reflect.TypeOf([]model.TypeA{})) 39 | } 40 | 41 | func Test_struct_and_other(t *testing.T) { 42 | runFuzzTest(t, reflect.TypeOf(model.TypeC{})) 43 | } 44 | 45 | func Test_map_string_array_int(t *testing.T) { 46 | runFuzzTest(t, reflect.TypeOf(map[string][3]int{})) 47 | } 48 | 49 | func Test_map_string_slice_int(t *testing.T) { 50 | runFuzzTest(t, reflect.TypeOf(map[string][]int{})) 51 | } 52 | 53 | func Test_map_string_struct(t *testing.T) { 54 | runFuzzTest(t, reflect.TypeOf(map[string]model.TypeA{})) 55 | } 56 | 57 | func Test_map_string_map_string_int(t *testing.T) { 58 | runFuzzTest(t, reflect.TypeOf(map[string]map[string]int{})) 59 | } -------------------------------------------------------------------------------- /fp/max/by_itself.go: -------------------------------------------------------------------------------- 1 | package max 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/fp/compare" 6 | "reflect" 7 | "github.com/v2pro/plz/util" 8 | ) 9 | 10 | func init() { 11 | util.GenMaxByItself = func(typ reflect.Type) func(collection []interface{}) interface{} { 12 | switch typ.Kind() { 13 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 14 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 15 | reflect.Float32, reflect.Float64: 16 | return generic.Expand(ByItselfForPlz, "T", typ). 17 | (func(collection []interface{}) interface{}) 18 | } 19 | return nil 20 | } 21 | } 22 | 23 | var ByItself = generic.DefineFunc("MaxByItself(vals T) E"). 24 | Param("T", "array type"). 25 | Param("E", "array element type", func(argMap generic.ArgMap) interface{} { 26 | return argMap["T"].(reflect.Type).Elem() 27 | }). 28 | ImportFunc(compare.ByItself). 29 | Source(` 30 | {{ $compare := expand "CompareByItself" "T" .E }} 31 | currentMax := vals[0] 32 | for i := 1; i < len(vals); i++ { 33 | if {{$compare}}(vals[i], currentMax) > 0 { 34 | currentMax = vals[i] 35 | } 36 | } 37 | return currentMax`) 38 | 39 | var ByItselfForPlz = generic.DefineFunc("MaxByItselfForPlz(vals []interface{}) interface{}"). 40 | Param("T", "array element type"). 41 | ImportFunc(compare.ByItself). 42 | Source(` 43 | {{ $compare := expand "CompareByItself" "T" .T }} 44 | currentMax := vals[0].({{.T|name}}) 45 | for i := 1; i < len(vals); i++ { 46 | typedVal := vals[i].({{.T|name}}) 47 | if {{$compare}}(typedVal, currentMax) > 0 { 48 | currentMax = typedVal 49 | } 50 | } 51 | return currentMax`) 52 | -------------------------------------------------------------------------------- /cp/struct_to_map.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | func init() { 9 | Anything.ImportFunc(copyStructToMap) 10 | } 11 | 12 | var copyStructToMap = generic.DefineFunc("CopyStructToMap(err *error, dst DT, src ST)"). 13 | Param("DT", "the dst type to copy into"). 14 | Param("ST", "the src type to copy from"). 15 | ImportFunc(Anything). 16 | Generators( 17 | "calcBindings", func(dstType, srcType reflect.Type) interface{} { 18 | bindings := []interface{}{} 19 | for i := 0; i < srcType.NumField(); i++ { 20 | srcField := srcType.Field(i) 21 | bindings = append(bindings, map[string]interface{}{ 22 | "srcFieldName": srcField.Name, 23 | "srcFieldType": srcField.Type, 24 | "dstFieldName": srcField.Name, 25 | "dstFieldType": reflect.PtrTo(dstType.Elem()), 26 | }) 27 | } 28 | return bindings 29 | }, 30 | "assignCp", func(binding map[string]interface{}, cpFuncName string) string { 31 | binding["cp"] = cpFuncName 32 | return "" 33 | }). 34 | Source(` 35 | {{ $bindings := calcBindings .DT .ST }} 36 | {{ range $_, $binding := $bindings}} 37 | {{ $cp := expand "CopyAnything" "DT" $binding.dstFieldType "ST" $binding.srcFieldType }} 38 | {{ assignCp $binding $cp }} 39 | {{ end }} 40 | var existingElem {{.DT|elem|name}} 41 | var found bool 42 | {{ range $_, $binding := $bindings }} 43 | existingElem, found = dst["{{$binding.dstFieldName}}"] 44 | if found { 45 | {{$binding.cp}}(err, &existingElem, src.{{$binding.srcFieldName}}) 46 | dst["{{$binding.dstFieldName}}"] = existingElem 47 | } else { 48 | newElem := new({{$binding.dstFieldType|elem|name}}) 49 | {{$binding.cp}}(err, newElem, src.{{$binding.srcFieldName}}) 50 | dst["{{ $binding.dstFieldName }}"] = *newElem 51 | } 52 | {{ end }}`) 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wombat 2 | 3 | [![Sourcegraph](https://sourcegraph.com/github.com/v2pro/wombat/-/badge.svg)](https://sourcegraph.com/github.com/v2pro/wombat?badge) 4 | [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/v2pro/wombat) 5 | [![Build Status](https://travis-ci.org/v2pro/wombat.svg?branch=master)](https://travis-ci.org/v2pro/wombat) 6 | [![codecov](https://codecov.io/gh/v2pro/wombat/branch/master/graph/badge.svg)](https://codecov.io/gh/v2pro/wombat) 7 | [![rcard](https://goreportcard.com/badge/github.com/v2pro/wombat)](https://goreportcard.com/report/github.com/v2pro/wombat) 8 | [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://raw.githubusercontent.com/v2pro/wombat/master/LICENSE) 9 | [![Gitter chat](https://badges.gitter.im/gitterHQ/gitter.png)](https://gitter.im/v2pro/Lobby) 10 | 11 | 12 | 13 | binding & validation & functional goodies. 14 | 15 | implements api defined in `v2pro/plz/util` package 16 | 17 | # Example 18 | 19 | ```golang 20 | //go:generate go install github.com/v2pro/wombat/cmd/wombat-codegen 21 | //go:generate $GOPATH/bin/wombat-codegen -pkg github.com/v2pro/wombat/example 22 | func init() { 23 | generic.Declare(func() { 24 | plz.Max(int(0)) 25 | plz.Max(float64(0)) 26 | plz.Max(model.User{}, "Score") 27 | }) 28 | } 29 | 30 | func Demo_max_min(t *testing.T) { 31 | should := require.New(t) 32 | should.Equal(3, plz.Max(1, 3, 2)) 33 | should.Equal(float64(3), plz.Max(1.0, 3.0, 2.0)) 34 | should.Equal(model.User{3}, plz.Max( 35 | model.User{1}, model.User{3}, model.User{2}, 36 | "Score")) 37 | } 38 | ``` 39 | 40 | replace `github.com/v2pro/wombat/example` with your package name. 41 | 42 | you need call `go generate` before compile -------------------------------------------------------------------------------- /cpJson/struct_2_json.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | "github.com/v2pro/plz/lang/tagging" 8 | ) 9 | 10 | func init() { 11 | cp.Anything.ImportFunc(copyStructToJson) 12 | toJsonMap[reflect.Struct] = "CopyStructToJson" 13 | } 14 | 15 | var copyStructToJson = generic.DefineFunc( 16 | "CopyStructToJson(err *error, dst DT, src ST)"). 17 | Param("DT", "the dst type to copy into"). 18 | Param("ST", "the src type to copy from"). 19 | ImportFunc(cp.Anything). 20 | Generators( 21 | "calcBindings", func(dstType, srcType reflect.Type) interface{} { 22 | bindings := []interface{}{} 23 | tags := tagging.Get(srcType) 24 | for i := 0; i < srcType.NumField(); i++ { 25 | srcField := srcType.Field(i) 26 | jsonTag := tags.Fields[srcField.Name]["json"].Text() 27 | dstFieldName := srcField.Name 28 | if jsonTag != "" { 29 | dstFieldName = jsonTag 30 | } 31 | bindings = append(bindings, map[string]interface{}{ 32 | "srcFieldName": srcField.Name, 33 | "srcFieldType": srcField.Type, 34 | "dstFieldName": dstFieldName, 35 | "dstFieldType": dstType, 36 | }) 37 | } 38 | return bindings 39 | }, 40 | "assignCp", func(binding map[string]interface{}, cpFuncName string) string { 41 | binding["cp"] = cpFuncName 42 | return "" 43 | }). 44 | Source(` 45 | {{ $bindings := calcBindings .DT .ST }} 46 | {{ range $_, $binding := $bindings}} 47 | {{ $cp := expand "CopyAnything" "DT" $binding.dstFieldType "ST" $binding.srcFieldType }} 48 | {{ assignCp $binding $cp }} 49 | {{ end }} 50 | dst.WriteObjectStart() 51 | {{ range $i, $binding := $bindings }} 52 | {{ if ne $i 0 }} 53 | dst.WriteMore() 54 | {{ end }} 55 | dst.WriteObjectField("{{$binding.dstFieldName}}") 56 | {{$binding.cp}}(err, dst, src.{{$binding.srcFieldName}}) 57 | {{ end }} 58 | dst.WriteObjectEnd()`) 59 | -------------------------------------------------------------------------------- /cpJson/json_2_struct.go: -------------------------------------------------------------------------------- 1 | package cpJson 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "github.com/v2pro/wombat/cp" 6 | "reflect" 7 | "github.com/v2pro/plz/lang/tagging" 8 | ) 9 | 10 | func init() { 11 | cp.Anything.ImportFunc(copyJsonToStruct) 12 | fromJsonMap[reflect.Struct] = "CopyJsonToStruct" 13 | } 14 | 15 | var copyJsonToStruct = generic.DefineFunc( 16 | "CopyJsonToStruct(err *error, dst DT, src ST)"). 17 | Param("DT", "the dst type to copy into"). 18 | Param("ST", "the src type to copy from"). 19 | ImportFunc(cp.Anything). 20 | Generators( 21 | "calcBindings", func(dstType, srcType reflect.Type) interface{} { 22 | bindings := []interface{}{} 23 | tags := tagging.Get(dstType) 24 | for i := 0; i < dstType.NumField(); i++ { 25 | dstField := dstType.Field(i) 26 | srcFieldName := dstField.Name 27 | jsonTag := tags.Fields[dstField.Name]["json"].Text() 28 | if jsonTag != "" { 29 | srcFieldName = jsonTag 30 | } 31 | bindings = append(bindings, map[string]interface{}{ 32 | "srcFieldName": srcFieldName, 33 | "srcFieldType": srcType, 34 | "dstFieldName": dstField.Name, 35 | "dstFieldType": reflect.PtrTo(dstField.Type), 36 | }) 37 | } 38 | return bindings 39 | }, 40 | "assignCp", func(binding map[string]interface{}, cpFuncName string) string { 41 | binding["cp"] = cpFuncName 42 | return "" 43 | }). 44 | Source(` 45 | {{ $bindings := calcBindings (.DT|elem) .ST }} 46 | {{ range $_, $binding := $bindings}} 47 | {{ $cp := expand "CopyAnything" "DT" $binding.dstFieldType "ST" $binding.srcFieldType }} 48 | {{ assignCp $binding $cp }} 49 | {{ end }} 50 | src.ReadObjectCB(func(iter *jsoniter.Iterator, field string) bool { 51 | switch field { 52 | {{ range $_, $binding := $bindings }} 53 | case "{{ $binding.srcFieldName }}": 54 | {{$binding.cp}}(err, &dst.{{$binding.dstFieldName}}, iter) 55 | {{ end }} 56 | default: 57 | iter.Skip() 58 | } 59 | return true 60 | })`) 61 | -------------------------------------------------------------------------------- /generic/symbol_name.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "reflect" 5 | "fmt" 6 | "strings" 7 | "sort" 8 | "strconv" 9 | ) 10 | 11 | type MangledNameProvider interface { 12 | MangledName() string 13 | } 14 | 15 | func expandSymbolName(plainName string, argMap map[string]interface{}) string { 16 | expanded := []byte(plainName) 17 | keys := []string{} 18 | for key := range argMap { 19 | keys = append(keys, key) 20 | } 21 | sort.Strings(keys) 22 | for _, key := range keys { 23 | value := argMap[key] 24 | if value == nil { 25 | panic("template arg " + key + " is nil") 26 | } 27 | expanded = append(expanded, '_') 28 | expanded = append(expanded, key...) 29 | switch typedArg := value.(type) { 30 | case string: 31 | expanded = append(expanded, '_') 32 | expanded = append(expanded, typedArg...) 33 | case bool: 34 | expanded = append(expanded, '_') 35 | expanded = strconv.AppendBool(expanded, typedArg) 36 | case reflect.Type: 37 | expanded = append(expanded, '_') 38 | expanded = append(expanded, typeToSymbol(typedArg)...) 39 | case MangledNameProvider: 40 | expanded = append(expanded, '_') 41 | expanded = append(expanded, typedArg.MangledName()...) 42 | default: 43 | panic(fmt.Sprintf("unsupported template arg %v of type %s", value, reflect.TypeOf(value).String())) 44 | } 45 | } 46 | return string(expanded) 47 | } 48 | 49 | func typeToSymbol(typ reflect.Type) string { 50 | switch typ.Kind() { 51 | case reflect.Map: 52 | return "map_" + typeToSymbol(typ.Key()) + "_to_" + typeToSymbol(typ.Elem()) 53 | case reflect.Slice: 54 | return "slice_" + typeToSymbol(typ.Elem()) 55 | case reflect.Array: 56 | return fmt.Sprintf("array_%d_%s", typ.Len(), typeToSymbol(typ.Elem())) 57 | case reflect.Ptr: 58 | return "ptr_" + typeToSymbol(typ.Elem()) 59 | default: 60 | typeName := typ.String() 61 | typeName = strings.Replace(typeName, ".", "__", -1) 62 | if strings.Contains(typeName, "{") { 63 | typeName = hash(typeName) 64 | } 65 | return typeName 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cp/tests/existing_value_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | "github.com/v2pro/wombat/cp/tests/model" 8 | ) 9 | 10 | func Test_existing_int_2_ptr_int(t *testing.T) { 11 | should := require.New(t) 12 | dst := int(1) 13 | pDst := &dst 14 | src := int(2) 15 | should.Nil(plz.Copy(&pDst, src)) 16 | should.Equal(2, dst) 17 | } 18 | 19 | func Test_existing_slice_int_2_slice_ptr_int(t *testing.T) { 20 | should := require.New(t) 21 | existing := int(0) 22 | dst := []*int{&existing} 23 | src := []int{1, 2, 3} 24 | should.Nil(plz.Copy(&dst, src)) 25 | should.Equal(1, existing) 26 | should.Equal(2, *dst[1]) 27 | should.Equal(3, *dst[2]) 28 | } 29 | 30 | func Test_existing_array_int_2_array_ptr_int(t *testing.T) { 31 | should := require.New(t) 32 | existing := int(0) 33 | dst := [3]*int{&existing} 34 | src := [3]int{1, 2, 3} 35 | should.Nil(plz.Copy(&dst, src)) 36 | should.Equal(1, existing) 37 | should.Equal(2, *dst[1]) 38 | should.Equal(3, *dst[2]) 39 | } 40 | 41 | func Test_existing_struct_int_2_struct_ptr_int(t *testing.T) { 42 | should := require.New(t) 43 | existing := int(0) 44 | dst := model.TypeE{Field: &existing} 45 | src := model.TypeD{Field: 1} 46 | should.Nil(plz.Copy(&dst, src)) 47 | should.Equal(1, existing) 48 | } 49 | 50 | func Test_existing_map_string_int_2_map_string_ptr_int(t *testing.T) { 51 | should := require.New(t) 52 | existing := int(0) 53 | dst := map[string]*int{"Field": &existing} 54 | src := map[string]int{"Field": 1} 55 | should.Nil(plz.Copy(&dst, src)) 56 | should.Equal(1, existing) 57 | } 58 | 59 | func Test_existing_int_2_ptr_ei(t *testing.T) { 60 | should := require.New(t) 61 | existing := int(0) 62 | var dst interface{} = &existing 63 | src := int(1) 64 | should.Nil(plz.Copy(&dst, src)) 65 | should.Equal(1, existing) 66 | } 67 | 68 | func Test_existing_slice_int_2_ptr_ei(t *testing.T) { 69 | should := require.New(t) 70 | existing := int(0) 71 | var dst interface{} = &[]*int{&existing} 72 | src := []int{1, 2, 3} 73 | should.Nil(plz.Copy(&dst, src)) 74 | should.Equal(1, existing) 75 | } -------------------------------------------------------------------------------- /wombat.go: -------------------------------------------------------------------------------- 1 | package wombat 2 | 3 | import ( 4 | "reflect" 5 | "os/exec" 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | ) 10 | 11 | var Int = reflect.TypeOf(int(0)) 12 | var Int8 = reflect.TypeOf(int8(0)) 13 | var Int16 = reflect.TypeOf(int16(0)) 14 | var Int32 = reflect.TypeOf(int32(0)) 15 | var Int64 = reflect.TypeOf(int64(0)) 16 | var Uint = reflect.TypeOf(uint(0)) 17 | var Uint8 = reflect.TypeOf(uint8(0)) 18 | var Uint16 = reflect.TypeOf(uint16(0)) 19 | var Uint32 = reflect.TypeOf(uint32(0)) 20 | var Uint64 = reflect.TypeOf(uint64(0)) 21 | var Float32 = reflect.TypeOf(float32(0)) 22 | var Float64 = reflect.TypeOf(float64(0)) 23 | var String = reflect.TypeOf("") 24 | var Bool = reflect.TypeOf(true) 25 | 26 | func Codegen(pkgPath string) { 27 | gopath := os.Getenv("GOPATH") 28 | if gopath == "" { 29 | reportError("GOPATH env not found") 30 | } 31 | writeCodeGeneratorMain(gopath, pkgPath) 32 | executeTmpCodegen(gopath + "/bin/tmp-codegen") 33 | } 34 | 35 | func writeCodeGeneratorMain(gopath string, pkgPath string) { 36 | dir := gopath+"/src/tmp-codegen" 37 | if _, err := os.Stat(dir); err != nil { 38 | err := os.Mkdir(dir, 0777) 39 | if err != nil { 40 | reportError(err.Error()) 41 | } 42 | } 43 | ioutil.WriteFile(dir+"/main.go", []byte(fmt.Sprintf(` 44 | package main 45 | import _ "%s" 46 | import "github.com/v2pro/wombat/generic" 47 | func main() { 48 | generic.GenerateCode("%s", "%s") 49 | } 50 | `, pkgPath, gopath, pkgPath)), 0666) 51 | goInstallTmpCodegen() 52 | } 53 | 54 | func goInstallTmpCodegen() { 55 | cmd := exec.Command("go", "install", "-tags", "codegen", "tmp-codegen") 56 | var errBuf bytes.Buffer 57 | cmd.Stderr = &errBuf 58 | var outBuf bytes.Buffer 59 | cmd.Stdout = &outBuf 60 | err := cmd.Run() 61 | if err != nil { 62 | fmt.Println(errBuf.String()) 63 | fmt.Println(outBuf.String()) 64 | reportError(err.Error()) 65 | } 66 | } 67 | 68 | func executeTmpCodegen(file string) { 69 | cmd := exec.Command(file) 70 | var errBuf bytes.Buffer 71 | cmd.Stderr = &errBuf 72 | var outBuf bytes.Buffer 73 | cmd.Stdout = &outBuf 74 | err := cmd.Run() 75 | if err != nil { 76 | fmt.Println(errBuf.String()) 77 | fmt.Println(outBuf.String()) 78 | reportError(err.Error()) 79 | } 80 | fmt.Println(errBuf.String()) 81 | fmt.Println(outBuf.String()) 82 | } 83 | 84 | func reportError(msg string) { 85 | fmt.Println(msg) 86 | os.Exit(1) 87 | } 88 | 89 | -------------------------------------------------------------------------------- /generic/struct_expand.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "text/template" 5 | "reflect" 6 | "fmt" 7 | ) 8 | 9 | type structCacheKey struct { 10 | structName string 11 | interfaceType reflect.Type 12 | } 13 | 14 | var structCtors = map[structCacheKey]func() interface{}{ 15 | } 16 | 17 | func New(structTemplate *StructTemplate, interfaceType reflect.Type) interface{} { 18 | cacheKey := structCacheKey{ 19 | structName: structTemplate.structName, 20 | interfaceType: interfaceType, 21 | } 22 | ctor := structCtors[cacheKey] 23 | if ctor == nil { 24 | ctor = structTemplate.expandCtor(interfaceType) 25 | structCtors[cacheKey] = ctor 26 | } 27 | return ctor() 28 | } 29 | 30 | func (structTemplate *StructTemplate) expandCtor(interfaceType reflect.Type) func() interface{} { 31 | ctorTemplate := structTemplate.ctorTemplate() 32 | ctor := Expand(ctorTemplate, "I", interfaceType).(func() interface{}) 33 | return ctor 34 | } 35 | 36 | func (structTemplate *StructTemplate) ctorTemplate() *FuncTemplate { 37 | return DefineFunc("New_" + structTemplate.structName + "()interface{}"). 38 | Param("I", "interface of the expanded struct"). 39 | ImportStruct(structTemplate). 40 | Source(fmt.Sprintf(` 41 | {{ $struct := expand "%s" "I" .I }} 42 | return &{{$struct}}{}`, structTemplate.structName)) 43 | } 44 | 45 | func (structTemplate *StructTemplate) expand(templateArgs []interface{}) (string, error) { 46 | argMap := map[string]interface{}{} 47 | for i := 0; i < len(templateArgs); i += 2 { 48 | argName := templateArgs[i].(string) 49 | argVal := templateArgs[i+1] 50 | argMap[argName] = argVal 51 | } 52 | expandedStructName := expandSymbolName(structTemplate.structName, argMap) 53 | argMap["structName"] = expandedStructName 54 | parsedTemplate, err := structTemplate.parse() 55 | if err != nil { 56 | return "", err 57 | } 58 | err = parsedTemplate.Execute(state.out, argMap) 59 | if err != nil { 60 | return "", err 61 | } 62 | return expandedStructName, nil 63 | } 64 | 65 | func (structTemplate *StructTemplate) parse() (*template.Template, error) { 66 | parsedTemplate := templates[structTemplate.structName] 67 | if parsedTemplate == nil { 68 | var err error 69 | parsedTemplate, err = template.New(structTemplate.structName). 70 | Funcs(structTemplate.generators). 71 | Parse(structTemplate.templateSource) 72 | if err != nil { 73 | return nil, err 74 | } 75 | templates[structTemplate.structName] = parsedTemplate 76 | } 77 | return parsedTemplate, nil 78 | } 79 | -------------------------------------------------------------------------------- /cp/tests/empty_interface_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/v2pro/plz" 7 | ) 8 | 9 | func Test_ei_ptr_ei_2_int(t *testing.T) { 10 | should := require.New(t) 11 | dst := int(0) 12 | var src interface{} = int(1) 13 | should.Nil(plz.Copy(&dst, &src)) 14 | should.Equal(1, dst) 15 | } 16 | 17 | func Test_ei_ptr_ei_2_slice_int(t *testing.T) { 18 | should := require.New(t) 19 | dst := []int{} 20 | var src interface{} = []int{1, 2, 3} 21 | should.Nil(plz.Copy(&dst, &src)) 22 | should.Equal([]int{1, 2, 3}, dst) 23 | } 24 | 25 | func Test_ei_int_2_ptr_ei(t *testing.T) { 26 | should := require.New(t) 27 | var dst interface{} 28 | src := int(1) 29 | should.Nil(plz.Copy(&dst, src)) 30 | should.Equal(1, dst) 31 | } 32 | 33 | func Test_ei_slice_int_2_ptr_ei(t *testing.T) { 34 | should := require.New(t) 35 | var dst interface{} 36 | src := []int{1, 2, 3} 37 | should.Nil(plz.Copy(&dst, src)) 38 | should.Equal([]int{1, 2, 3}, dst) 39 | src[0] = 2 40 | should.Equal([]int{1, 2, 3}, dst) 41 | } 42 | 43 | func Test_ei_slice_ei_2_slice_int(t *testing.T) { 44 | should := require.New(t) 45 | src := []interface{}{1, 2, 3} 46 | dst := []int{} 47 | should.Nil(plz.Copy(&dst, src)) 48 | should.Equal([]int{1, 2, 3}, dst) 49 | } 50 | 51 | func Test_ei_slice_int_2_slice_ei(t *testing.T) { 52 | should := require.New(t) 53 | src := []int{1, 2, 3} 54 | dst := []interface{}{} 55 | should.Nil(plz.Copy(&dst, src)) 56 | should.Equal([]interface{}{1, 2, 3}, dst) 57 | } 58 | 59 | func Test_ei_slice_ei_2_slice_ei(t *testing.T) { 60 | should := require.New(t) 61 | src := []interface{}{1, "2", 3} 62 | dst := []interface{}{} 63 | should.Nil(plz.Copy(&dst, src)) 64 | should.Equal([]interface{}{1, "2", 3}, dst) 65 | } 66 | 67 | func Test_ei_map_string_ei_2_map_string_int(t *testing.T) { 68 | should := require.New(t) 69 | src := map[string]interface{}{"Field": 1} 70 | dst := map[string]int{} 71 | should.Nil(plz.Copy(dst, src)) 72 | should.Equal(map[string]int{"Field": 1}, dst) 73 | } 74 | 75 | func Test_ei_map_string_int_2_map_string_ei(t *testing.T) { 76 | should := require.New(t) 77 | src := map[string]int{"Field": 1} 78 | dst := map[string]interface{}{} 79 | should.Nil(plz.Copy(dst, src)) 80 | should.Equal(map[string]interface{}{"Field": 1}, dst) 81 | } 82 | 83 | func Test_ei_map_string_ei_2_map_string_ei(t *testing.T) { 84 | should := require.New(t) 85 | src := map[string]interface{}{"Field1": 1, "Field2": "2"} 86 | dst := map[string]interface{}{} 87 | should.Nil(plz.Copy(dst, src)) 88 | should.Equal(map[string]interface{}{"Field1": 1, "Field2": "2"}, dst) 89 | } 90 | -------------------------------------------------------------------------------- /generic/generate_code.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "fmt" 7 | "strings" 8 | ) 9 | 10 | var expandedFuncs = map[string]interface{}{} 11 | 12 | func GenerateCode(gopath string, pkgPath string) { 13 | state.out = bytes.NewBuffer(nil) 14 | state.importPackages = map[string]bool{ 15 | "github.com/v2pro/wombat/generic": true, 16 | } 17 | state.declarations = map[string]bool{} 18 | state.expandedFuncNames = map[string]bool{} 19 | state.pkgPath = pkgPath 20 | pkgName := findPackageName(gopath, pkgPath) 21 | prelog := []byte(` 22 | package `) 23 | prelog = append(prelog, pkgName...) 24 | for _, funcDeclaration := range funcDeclarations { 25 | funcDeclaration.funcTemplate.expand(funcDeclaration.templateArgs) 26 | } 27 | for importPackage := range state.importPackages { 28 | prelog = append(prelog, '\n') 29 | prelog = append(prelog, `import "`...) 30 | prelog = append(prelog, importPackage...) 31 | prelog = append(prelog, '"') 32 | } 33 | prelog = append(prelog, "\nfunc init() {"...) 34 | for _, funcDeclaration := range funcDeclarations { 35 | expandedFuncName, err := funcDeclaration.funcTemplate.expand(funcDeclaration.templateArgs) 36 | if err != nil { 37 | panic(err.Error()) 38 | } 39 | for declaration := range funcDeclaration.funcTemplate.declarations { 40 | state.declarations[declaration] = true 41 | } 42 | prelog = append(prelog, '\n') 43 | prelog = append(prelog, `generic.RegisterExpandedFunc("`...) 44 | prelog = append(prelog, expandedFuncName...) 45 | prelog = append(prelog, `",`...) 46 | prelog = append(prelog, expandedFuncName...) 47 | prelog = append(prelog, ')') 48 | } 49 | prelog = append(prelog, '}') 50 | for declaration := range state.declarations { 51 | prelog = append(prelog, '\n') 52 | prelog = append(prelog, declaration...) 53 | } 54 | source := append([]byte(prelog), state.out.Bytes()...) 55 | err := ioutil.WriteFile(gopath+"/src/"+pkgPath+"/generated.go", source, 0666) 56 | if err != nil { 57 | panic(err.Error()) 58 | } 59 | } 60 | 61 | func findPackageName(gopath string, pkgPath string) string { 62 | dir := gopath + "/src/" + pkgPath 63 | files, err := ioutil.ReadDir(dir) 64 | if err != nil { 65 | panic(err.Error()) 66 | } 67 | for _, file := range files { 68 | content, err := ioutil.ReadFile(dir + "/" + file.Name()) 69 | if err != nil { 70 | fmt.Println(err.Error()) 71 | continue 72 | } 73 | lines := strings.Split(string(content), "\n") 74 | for _, line := range lines { 75 | if strings.Index(line, "package ") == 0 { 76 | return strings.TrimSpace(line[8:]) 77 | } 78 | } 79 | } 80 | panic("can not tell package from .go files in " + dir) 81 | } 82 | 83 | func RegisterExpandedFunc(expandedFuncName string, expandedFunc interface{}) { 84 | expandedFuncs[expandedFuncName] = expandedFunc 85 | } 86 | -------------------------------------------------------------------------------- /cpJson/tests/nil_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "testing" 5 | "github.com/stretchr/testify/require" 6 | "github.com/json-iterator/go" 7 | "github.com/v2pro/plz" 8 | "github.com/v2pro/wombat/cpJson/tests/model" 9 | ) 10 | 11 | func Test_nil_ptr_int(t *testing.T) { 12 | should := require.New(t) 13 | var src *int 14 | should.Equal("null", copyToJson(src)) 15 | one := int(0) 16 | dst := &one 17 | should.Nil(copyFromJson(&dst, `null`)) 18 | should.Nil(dst) 19 | } 20 | 21 | func Test_nil_ptr_ptr_int(t *testing.T) { 22 | should := require.New(t) 23 | var src *int 24 | pSrc := &src 25 | should.Equal(`null`, copyToJson(pSrc)) 26 | pSrc = nil 27 | should.Equal(`null`, copyToJson(pSrc)) 28 | var dst *int 29 | pDst := &dst 30 | should.Nil(copyFromJson(&pDst, `null`)) 31 | should.Nil(pDst) 32 | } 33 | 34 | func Test_nil_slice_ptr_int(t *testing.T) { 35 | should := require.New(t) 36 | src := []*int{nil} 37 | should.Equal(`[null]`, copyToJson(src)) 38 | one := int(1) 39 | dst := []*int{&one} 40 | should.Nil(copyFromJson(&dst, `[null]`)) 41 | should.Nil(dst[0]) 42 | } 43 | 44 | func Test_nil_slice_int(t *testing.T) { 45 | should := require.New(t) 46 | var src []int 47 | should.Equal(`null`, copyToJson(src)) 48 | dst := []int{1} 49 | should.Nil(copyFromJson(&dst, `null`)) 50 | should.Nil(dst) 51 | } 52 | 53 | func Test_nil_array_ptr_int(t *testing.T) { 54 | should := require.New(t) 55 | src := [3]*int{} 56 | should.Equal(`[null,null,null]`, copyToJson(src)) 57 | one := int(1) 58 | dst := [3]*int{&one} 59 | should.Nil(copyFromJson(&dst, `[null]`)) 60 | should.Nil(dst[0]) 61 | } 62 | 63 | func Test_nil_map_string_ptr_int(t *testing.T) { 64 | should := require.New(t) 65 | src := map[string]*int{"Field": nil} 66 | should.Equal(`{"Field":null}`, copyToJson(src)) 67 | one := int(1) 68 | dst := map[string]*int{"Field": &one} 69 | should.Nil(copyFromJson(&dst, `{"Field":null}`)) 70 | should.Nil(dst["Field"]) 71 | } 72 | 73 | func Test_nil_map_string_int(t *testing.T) { 74 | should := require.New(t) 75 | var src map[string]int 76 | should.Equal(`null`, copyToJson(src)) 77 | dst := map[string]int{"Field": 1} 78 | should.Nil(copyFromJson(&dst, `null`)) 79 | should.Nil(dst) 80 | } 81 | 82 | func Test_nil_struct(t *testing.T) { 83 | should := require.New(t) 84 | 85 | src := model.TypeB{} 86 | should.Equal(`{"Field":null}`, copyToJson(src)) 87 | one := int(1) 88 | dst := model.TypeB{Field: &one} 89 | should.Nil(copyFromJson(&dst, `{"Field":null}`)) 90 | should.Nil(dst.Field) 91 | } 92 | 93 | func copyToJson(src interface{}) string { 94 | stream := jsoniter.NewStream(jsoniter.ConfigDefault, nil, 512) 95 | err := plz.Copy(stream, src) 96 | if err != nil { 97 | panic(err.Error()) 98 | } 99 | return string(stream.Buffer()) 100 | } 101 | 102 | func copyFromJson(dst interface{}, srcJson string) error { 103 | iter := jsoniter.ParseString(jsoniter.ConfigDefault, srcJson) 104 | return plz.Copy(dst, iter) 105 | } 106 | -------------------------------------------------------------------------------- /generic/func_signature.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "strings" 5 | "fmt" 6 | "bytes" 7 | "reflect" 8 | ) 9 | 10 | func parseSignature(input string) (*funcSignature, error) { 11 | leftBrace := strings.IndexByte(input, '(') 12 | if leftBrace == -1 { 13 | return nil, fmt.Errorf("( not found in " + input) 14 | } 15 | funcName := strings.TrimSpace(input[:leftBrace]) 16 | rightBrace := strings.IndexByte(input, ')') 17 | if rightBrace == -1 { 18 | return nil, fmt.Errorf(") not found in " + input) 19 | } 20 | signature := &funcSignature{ 21 | funcName: funcName, 22 | } 23 | params := strings.TrimSpace(input[leftBrace+1:rightBrace]) 24 | if len(params) > 0 { 25 | for _, param := range strings.Split(params, ",") { 26 | nameAndType := strings.SplitN(strings.TrimSpace(param), " ", 2) 27 | signature.funcParams = append(signature.funcParams, funcParam{ 28 | paramName: strings.TrimSpace(nameAndType[0]), 29 | paramType: strings.TrimSpace(nameAndType[1]), 30 | }) 31 | } 32 | } else { 33 | signature.funcParams = []funcParam{} 34 | } 35 | returns := strings.TrimFunc(input[rightBrace:], func(r rune) bool { 36 | switch r { 37 | case ' ', '\t', '\r', '\n', '(', ')': 38 | return true 39 | } 40 | return false 41 | }) 42 | if len(returns) > 0 { 43 | for _, ret := range strings.Split(returns, ",") { 44 | nameAndType := strings.SplitN(strings.TrimSpace(ret), " ", 2) 45 | if len(nameAndType) == 1 { 46 | nameAndType = []string{"", nameAndType[0]} 47 | } 48 | signature.funcReturns = append(signature.funcReturns, funcReturn{ 49 | returnName: strings.TrimSpace(nameAndType[0]), 50 | returnType: strings.TrimSpace(nameAndType[1]), 51 | }) 52 | } 53 | } else { 54 | signature.funcReturns = []funcReturn{} 55 | } 56 | return signature, nil 57 | } 58 | 59 | type funcSignature struct { 60 | funcName string 61 | funcParams []funcParam 62 | funcReturns []funcReturn 63 | } 64 | 65 | type funcParam struct { 66 | paramName string 67 | paramType string 68 | } 69 | 70 | type funcReturn struct { 71 | returnName string 72 | returnType string 73 | } 74 | 75 | func (signature *funcSignature) expand(out *bytes.Buffer, 76 | expandedFuncName string, argMap map[string]interface{}) { 77 | out.WriteString("\nfunc ") 78 | out.WriteString(expandedFuncName) 79 | out.WriteByte('(') 80 | for i, param := range signature.funcParams { 81 | if i != 0 { 82 | out.WriteByte(',') 83 | } 84 | out.WriteString(param.paramName) 85 | out.WriteByte(' ') 86 | typ, isType := argMap[param.paramType].(reflect.Type) 87 | if isType { 88 | out.WriteString(genName(typ)) 89 | } else { 90 | out.WriteString(param.paramType) 91 | } 92 | } 93 | out.WriteByte(')') 94 | if len(signature.funcReturns) > 0 { 95 | out.WriteByte('(') 96 | for i, ret := range signature.funcReturns { 97 | if i != 0 { 98 | out.WriteByte(',') 99 | } 100 | out.WriteString(ret.returnName) 101 | out.WriteByte(' ') 102 | typ, isType := argMap[ret.returnType].(reflect.Type) 103 | if isType { 104 | out.WriteString(genName(typ)) 105 | } else { 106 | out.WriteString(ret.returnType) 107 | } 108 | } 109 | out.WriteByte(')') 110 | } 111 | out.WriteByte('{') 112 | } 113 | -------------------------------------------------------------------------------- /cp/anything.go: -------------------------------------------------------------------------------- 1 | package cp 2 | 3 | import ( 4 | "github.com/v2pro/wombat/generic" 5 | "reflect" 6 | ) 7 | 8 | var Dispatchers = []func(dstType, srcType reflect.Type) string{} 9 | 10 | func init() { 11 | util.GenCopy = func(dstType reflect.Type, srcType reflect.Type) func(interface{}, interface{}) error { 12 | var ct func(interface{}, interface{}) error 13 | funcObj := generic.Expand(AnythingForPlz, 14 | "DT", dstType, 15 | "ST", srcType, 16 | "CT", reflect.TypeOf(ct)) 17 | f := funcObj.(func(func(interface{}, interface{}) error, interface{}, interface{}) error) 18 | return func(dst, src interface{}) error { 19 | return f(util.Copy, dst, src) 20 | } 21 | } 22 | } 23 | 24 | var Anything = generic.DefineFunc("CopyAnything(err *error, dst DT, src ST)"). 25 | Param("DT", "the dst type to copy into"). 26 | Param("ST", "the src type to copy from"). 27 | Generators( 28 | "dispatch", dispatch, 29 | "hasErrorField", func(typ reflect.Type) bool { 30 | if typ.Kind() != reflect.Ptr { 31 | return false 32 | } 33 | typ = typ.Elem() 34 | if typ.Kind() != reflect.Struct { 35 | return false 36 | } 37 | _, found := typ.FieldByName("Error") 38 | return found 39 | }). 40 | ImportPackage("io"). 41 | Declare("var ioEOF = io.EOF"). 42 | Source(` 43 | {{ $tmpl := dispatch .DT .ST }} 44 | {{ $cp := expand $tmpl "DT" .DT "ST" .ST }} 45 | {{$cp}}(err, dst, src) 46 | {{ if hasErrorField .ST }} 47 | if src.Error != nil && src.Error != ioEOF { 48 | *err = src.Error 49 | } 50 | {{ end }} 51 | {{ if hasErrorField .DT }} 52 | if dst.Error != nil { 53 | *err = dst.Error 54 | } 55 | {{ end }} 56 | `) 57 | 58 | 59 | func dispatch(dstType reflect.Type, srcType reflect.Type) string { 60 | for _, dispatcher := range Dispatchers { 61 | tmpl := dispatcher(dstType, srcType) 62 | if tmpl != "" { 63 | return tmpl 64 | } 65 | } 66 | if srcType.Kind() == reflect.Ptr { 67 | switch srcType.Elem().Kind() { 68 | case reflect.Ptr, reflect.Map, reflect.Slice: 69 | return "CopyFromPtrPtr" 70 | } 71 | } 72 | if srcType.Kind() == reflect.Interface && srcType.NumMethod() == 0 { 73 | return "CopyFromInterface" 74 | } 75 | if dstType.Kind() == reflect.Map && 76 | srcType.Kind() == reflect.Map { 77 | return "CopyMapToMap" 78 | } 79 | if dstType.Kind() == reflect.Map && 80 | srcType.Kind() == reflect.Struct { 81 | return "CopyStructToMap" 82 | } 83 | if dstType.Kind() == reflect.Ptr { 84 | if dstType.Elem().Kind() == reflect.Ptr || dstType.Elem().Kind() == reflect.Map { 85 | return "CopyIntoPtr" 86 | } 87 | if dstType.Elem().Kind() == reflect.Interface && dstType.Elem().NumMethod() == 0 { 88 | return "CopyIntoInterface" 89 | } 90 | if srcType.Kind() == reflect.Slice && dstType.Elem().Kind() == reflect.Array { 91 | return "CopySliceToArray" 92 | } 93 | if srcType.Kind() == reflect.Array && dstType.Elem().Kind() == reflect.Slice { 94 | return "CopySliceToSlice" 95 | } 96 | if srcType.Kind() == reflect.Map && dstType.Elem().Kind() == reflect.Struct { 97 | return "CopyMapToStruct" 98 | } 99 | if dstType.Elem().Kind() == srcType.Kind() { 100 | switch dstType.Elem().Kind() { 101 | case reflect.Array: 102 | return "CopyArrayToArray" 103 | case reflect.Slice: 104 | return "CopySliceToSlice" 105 | case reflect.Struct: 106 | return "CopyStructToStruct" 107 | case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, 108 | reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, 109 | reflect.Bool, reflect.String, reflect.Float32, reflect.Float64: 110 | return "CopySimpleValue" 111 | } 112 | } 113 | } 114 | if srcType.Kind() == reflect.Ptr { 115 | return "CopyFromPtr" 116 | } 117 | panic("do not know how to copy " + srcType.String() + " to " + dstType.String()) 118 | } 119 | 120 | var AnythingForPlz = generic.DefineFunc("CopyAnythingForPlz(theCopyDynamically CT, dst interface{}, src interface{}) error"). 121 | Param("DT", "the dst type to copy into"). 122 | Param("ST", "the src type to copy from"). 123 | Param("CT", "type of copy dynamically function"). 124 | ImportFunc(Anything). 125 | Declare("var copyDynamically func(interface{}, interface{}) error"). 126 | Source(` 127 | {{ $cp := expand "CopyAnything" "DT" .DT "ST" .ST }} 128 | copyDynamically = theCopyDynamically 129 | var err error 130 | {{$cp}}(&err, dst.({{.DT|name}}), src.({{.ST|name}})) 131 | return err`) 132 | -------------------------------------------------------------------------------- /compiler/compiler.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "os" 5 | "io/ioutil" 6 | "os/exec" 7 | "plugin" 8 | "crypto/sha1" 9 | "encoding/base32" 10 | "runtime" 11 | "bytes" 12 | "strings" 13 | "strconv" 14 | "fmt" 15 | "github.com/v2pro/plz/countlog" 16 | ) 17 | 18 | type API interface { 19 | DynamicCompile(source string) (*plugin.Plugin, error) 20 | } 21 | 22 | var ConfigDefault = Config{ 23 | PluginCacheDir: os.Getenv("HOME") + "/.docstore_handler", 24 | SourceTempDir: os.Getenv("HOME") + "/.docstore_handler", 25 | }.Froze() 26 | 27 | func DynamicCompile(source string) (*plugin.Plugin, error) { 28 | if os.Getenv("DOCSTORE_DEBUG") == "true" { 29 | fmt.Println(annotateLines(source)) 30 | } 31 | return ConfigDefault.DynamicCompile(source) 32 | } 33 | 34 | type Config struct { 35 | PluginCacheDir string 36 | SourceTempDir string 37 | } 38 | 39 | func (config Config) Froze() API { 40 | return &frozenConfig{config: config} 41 | } 42 | 43 | type frozenConfig struct { 44 | config Config 45 | } 46 | 47 | func (frozen *frozenConfig) DynamicCompile(source string) (*plugin.Plugin, error) { 48 | cfg := frozen.config 49 | if _, err := os.Stat(cfg.PluginCacheDir); err != nil { 50 | err := os.Mkdir(cfg.PluginCacheDir, 0777) 51 | if err != nil { 52 | countlog.Error( 53 | "event!compiler.failed to create plugin cache dir", 54 | "err", err, 55 | "dir", cfg.PluginCacheDir) 56 | return nil, err 57 | } 58 | } 59 | sourceHash := hash(source) 60 | srcFileName := cfg.SourceTempDir + "/" + sourceHash + ".go" 61 | soFileName := cfg.PluginCacheDir + "/" + sourceHash + ".so" 62 | if _, err := os.Stat(soFileName); err == nil { 63 | thePlugin, err := plugin.Open(soFileName) 64 | if err != nil { 65 | countlog.Warn( 66 | "event!compiler.failed to load cached plugin", 67 | "soFileName", soFileName) 68 | } else { 69 | if verifySourceHash(thePlugin, sourceHash) { 70 | countlog.Debug("event!compiler.reuse plugin", "soFileName", soFileName) 71 | return thePlugin, nil 72 | } 73 | countlog.Info("event!compiler.cached date plugin is out of date", "soFileName", soFileName) 74 | } 75 | } 76 | err := ioutil.WriteFile(srcFileName, []byte(fmt.Sprintf(` 77 | %s 78 | var SOURCE__HASH = "%s" 79 | `, source, sourceHash)), 0666) 80 | if err != nil { 81 | countlog.Error("event!compiler.failed to write source temp file", 82 | "err", err, 83 | "srcFileName", srcFileName) 84 | return nil, err 85 | } 86 | countlog.Debug("event!compiler.build plugin", "soFileName", soFileName, "srcFileName", srcFileName) 87 | cmd := exec.Command("go", "build", "-gcflags", "-N", "-buildmode=plugin", "-o", soFileName, srcFileName) 88 | var errBuf bytes.Buffer 89 | cmd.Stderr = &errBuf 90 | var outBuf bytes.Buffer 91 | cmd.Stdout = &outBuf 92 | err = cmd.Run() 93 | if err != nil { 94 | countlog.Error( 95 | "event!compiler.failed to compile generated plugin", 96 | "err", err, 97 | "stdout", outBuf.String(), 98 | "stderr", errBuf.String(), 99 | "srcFileName", srcFileName, 100 | "source", annotateLines(source)) 101 | return nil, err 102 | } 103 | countlog.Debug("event!compiler.open plugin", "soFileName", soFileName) 104 | thePlugin, err := plugin.Open(soFileName) 105 | if err != nil { 106 | countlog.Error( 107 | "event!compiler.failed to load generated plugin", 108 | "err", err, 109 | "soFileName", soFileName) 110 | return nil, err 111 | } 112 | return thePlugin, nil 113 | } 114 | 115 | func hash(source string) string { 116 | h := sha1.New() 117 | h.Write([]byte(source)) 118 | h.Write([]byte(runtime.Version())) 119 | return "g" + base32.StdEncoding.EncodeToString(h.Sum(nil)) 120 | } 121 | 122 | func annotateLines(source string) string { 123 | var buf bytes.Buffer 124 | lines := strings.Split(source, "\n") 125 | for i, line := range lines { 126 | lineNo := strconv.FormatInt(int64(i+1), 10) 127 | buf.WriteString(lineNo) 128 | buf.WriteString(": ") 129 | buf.WriteString(line) 130 | buf.WriteString("\n") 131 | } 132 | return buf.String() 133 | } 134 | 135 | func verifySourceHash(thePlugin *plugin.Plugin, sourceHash string) bool { 136 | symbol, err := thePlugin.Lookup("SOURCE__HASH") 137 | if err != nil { 138 | countlog.Error("event!compiler.SOURCE__HASH missing from so", "err", err) 139 | return false 140 | } 141 | actualSourceHash, isStr := symbol.(*string) 142 | if !isStr { 143 | countlog.Error("event!compiler.SOURCE__HASH is not string") 144 | return false 145 | } 146 | if *actualSourceHash != sourceHash { 147 | countlog.Error("event!compiler.SOURCE__HASH mismatch", 148 | "expected", sourceHash, 149 | "actual", *actualSourceHash) 150 | return false 151 | } 152 | return true 153 | } 154 | -------------------------------------------------------------------------------- /generic/func_expand.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "text/template" 5 | "sync" 6 | "bytes" 7 | "errors" 8 | "github.com/v2pro/quokka/docstore/compiler" 9 | "github.com/v2pro/plz/countlog" 10 | ) 11 | 12 | var expandLock = &sync.Mutex{} 13 | var templates = map[string]*template.Template{} 14 | var state = struct { 15 | pkgPath string 16 | out *bytes.Buffer 17 | importPackages map[string]bool 18 | declarations map[string]bool 19 | expandedFuncNames map[string]bool 20 | }{} 21 | var DynamicCompilationEnabled = false 22 | 23 | func Expand(funcTemplate *FuncTemplate, templateArgs ...interface{}) interface{} { 24 | expandLock.Lock() 25 | defer expandLock.Unlock() 26 | state.out = bytes.NewBuffer(nil) 27 | state.importPackages = map[string]bool{} 28 | state.declarations = map[string]bool{} 29 | state.expandedFuncNames = map[string]bool{} 30 | expandedFuncName, err := funcTemplate.expand(templateArgs) 31 | if err != nil { 32 | countlog.Error("event!expand func template failed", 33 | "err", err, 34 | "funcTemplate", funcTemplate.funcName, 35 | "templateArgs", templateArgs) 36 | panic(err.Error()) 37 | } 38 | if inDeclaringByExample { 39 | funcDeclarations = append(funcDeclarations, funcDeclaration{ 40 | funcTemplate: funcTemplate, 41 | templateArgs: templateArgs, 42 | }) 43 | } else { 44 | expandedFunc := expandedFuncs[expandedFuncName] 45 | if expandedFunc != nil { 46 | return expandedFunc 47 | } 48 | if !DynamicCompilationEnabled { 49 | countlog.Error("event!dynamic compilation disabled. "+ 50 | "please add generic.DeclareFunc to init() and re-run codegen", 51 | "funcTemplate", funcTemplate.funcName, 52 | "templateArgs", templateArgs, 53 | "definedInFile", funcTemplate.definedInFile, 54 | "expandedFuncName", expandedFuncName) 55 | panic("dynamic compilation disabled") 56 | } 57 | } 58 | prelog := []byte("package main") 59 | for importPackage := range state.importPackages { 60 | prelog = append(prelog, "\nimport \""...) 61 | prelog = append(prelog, importPackage...) 62 | prelog = append(prelog, '"') 63 | } 64 | for declaration := range state.declarations { 65 | prelog = append(prelog, '\n') 66 | prelog = append(prelog, declaration...) 67 | } 68 | expandedSource := string(append(prelog, state.out.String()...)) 69 | plugin, err := compiler.DynamicCompile(expandedSource) 70 | if err != nil { 71 | panic(err.Error()) 72 | } 73 | symbol, err := plugin.Lookup(expandedFuncName) 74 | if err != nil { 75 | countlog.Error("event!lookup symbol failed", 76 | "err", err, 77 | "expandedFuncName", expandedFuncName, 78 | "expandedSource", expandedSource) 79 | panic(err.Error()) 80 | } 81 | return symbol 82 | } 83 | 84 | func (funcTemplate *FuncTemplate) expand(templateArgs []interface{}) (string, error) { 85 | for pkg := range funcTemplate.importedPackages { 86 | state.importPackages[pkg] = true 87 | } 88 | for declaration := range funcTemplate.declarations { 89 | state.declarations[declaration] = true 90 | } 91 | argMap, err := funcTemplate.toArgMap(templateArgs) 92 | if err != nil { 93 | return "", err 94 | } 95 | localOut := bytes.NewBuffer(nil) 96 | expandedFuncName := expandSymbolName(funcTemplate.funcName, argMap) 97 | if state.expandedFuncNames[expandedFuncName] { 98 | return expandedFuncName, nil 99 | } 100 | state.expandedFuncNames[expandedFuncName] = true 101 | parsedTemplate, err := funcTemplate.parse() 102 | if err != nil { 103 | return "", err 104 | } 105 | funcTemplate.funcSignature.expand(localOut, expandedFuncName, argMap) 106 | err = parsedTemplate.Execute(localOut, argMap) 107 | if err != nil { 108 | return "", err 109 | } 110 | localOut.WriteString("\n}") 111 | state.out.Write(localOut.Bytes()) 112 | return expandedFuncName, nil 113 | } 114 | 115 | func (funcTemplate *FuncTemplate) parse() (*template.Template, error) { 116 | parsedTemplate := templates[funcTemplate.funcName] 117 | if parsedTemplate == nil { 118 | var err error 119 | parsedTemplate, err = template.New(funcTemplate.funcName). 120 | Funcs(funcTemplate.generators). 121 | Parse(funcTemplate.templateSource) 122 | if err != nil { 123 | return nil, err 124 | } 125 | templates[funcTemplate.funcName] = parsedTemplate 126 | } 127 | return parsedTemplate, nil 128 | } 129 | 130 | type ArgMap map[string]interface{} 131 | 132 | func (funcTemplate *FuncTemplate) toArgMap(templateArgs []interface{}) (ArgMap, error) { 133 | argMap := ArgMap{} 134 | params := map[string]TemplateParam{} 135 | for k, v := range funcTemplate.templateParams { 136 | params[k] = v 137 | } 138 | for i := 0; i < len(templateArgs); i += 2 { 139 | argName := templateArgs[i].(string) 140 | _, found := funcTemplate.templateParams[argName] 141 | if found { 142 | delete(params, argName) 143 | } else { 144 | return nil, errors.New("argument " + argName + " not declared as param") 145 | } 146 | argVal := templateArgs[i+1] 147 | argMap[argName] = argVal 148 | } 149 | for _, param := range params { 150 | if param.DefaultValueProvider == nil { 151 | return nil, errors.New("missing param " + param.Name) 152 | } 153 | argMap[param.Name] = param.DefaultValueProvider(argMap) 154 | } 155 | return argMap, nil 156 | } 157 | -------------------------------------------------------------------------------- /generic/func_define.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "runtime" 7 | "github.com/v2pro/plz/countlog" 8 | ) 9 | 10 | type FuncTemplateBuilder struct { 11 | funcTemplate *FuncTemplate 12 | } 13 | 14 | func DefineFunc(signature string) *FuncTemplateBuilder { 15 | importedFuncTemplates := map[string]*FuncTemplate{} 16 | importedStructTemplates := map[string]*StructTemplate{} 17 | parsedSignature, err := parseSignature(signature) 18 | if err != nil { 19 | panic(err.Error()) 20 | } 21 | _, definedInFile, _, _ := runtime.Caller(1) 22 | return &FuncTemplateBuilder{funcTemplate: &FuncTemplate{ 23 | funcSignature: parsedSignature, 24 | definedInFile: definedInFile, 25 | templateParams: map[string]TemplateParam{}, 26 | importedFuncTemplates: importedFuncTemplates, 27 | importedStructTemplates: importedStructTemplates, 28 | importedPackages: map[string]bool{}, 29 | declarations: map[string]bool{}, 30 | generators: map[string]interface{}{ 31 | "name": genName, 32 | "key": genKey, 33 | "elem": genElem, 34 | "expand": func(depName string, templateArgs ...interface{}) (string, error) { 35 | depFunc := importedFuncTemplates[depName] 36 | if depFunc != nil { 37 | expandedFuncName, err := depFunc.expand(templateArgs) 38 | if err != nil { 39 | countlog.Error("event!expand func failed", 40 | "depName", depName, 41 | "err", err, 42 | "templateArgs", templateArgs) 43 | return "", err 44 | } 45 | return expandedFuncName, nil 46 | } 47 | depStruct := importedStructTemplates[depName] 48 | if depStruct != nil { 49 | expandedStructName, err := depStruct.expand(templateArgs) 50 | if err != nil { 51 | countlog.Error("event!expand struct failed", 52 | "depName", depName, 53 | "err", err, 54 | "templateArgs", templateArgs) 55 | return "", err 56 | } 57 | return expandedStructName, nil 58 | } 59 | countlog.Error("event!missing dependency", "depName", depName) 60 | return "", fmt.Errorf( 61 | "referenced generic function %s should be imported by ImportFunc", 62 | depName) 63 | }, 64 | }, 65 | }} 66 | } 67 | 68 | func (builder *FuncTemplateBuilder) Param(paramName string, paramDescription string, defaultValues ...interface{}) *FuncTemplateBuilder { 69 | param := TemplateParam{ 70 | Name: paramName, 71 | Description: paramDescription, 72 | } 73 | switch len(defaultValues) { 74 | case 1: 75 | defaultValueProvider, isProvider := defaultValues[0].(func(ArgMap) interface{}) 76 | if isProvider { 77 | param.DefaultValueProvider = defaultValueProvider 78 | } else { 79 | if reflect.TypeOf(defaultValues[0]).Kind() == reflect.Func { 80 | panic("default value provider should be func(map[string]interface{})interface{}") 81 | } 82 | param.DefaultValueProvider = func(argMap ArgMap) interface{} { 83 | return defaultValues[0] 84 | } 85 | } 86 | case 0: 87 | // ignore 88 | default: 89 | panic("only one default value should be provided") 90 | } 91 | builder.funcTemplate.templateParams[paramName] = param 92 | return builder 93 | } 94 | 95 | func (builder *FuncTemplateBuilder) Generators(kv ...interface{}) *FuncTemplateBuilder { 96 | for i := 0; i < len(kv); i += 2 { 97 | k := kv[i].(string) 98 | v := kv[i+1] 99 | builder.funcTemplate.generators[k] = v 100 | } 101 | return builder 102 | } 103 | 104 | func (builder *FuncTemplateBuilder) ImportFunc(funcTemplates ...*FuncTemplate) *FuncTemplateBuilder { 105 | for _, funcTemplate := range funcTemplates { 106 | builder.funcTemplate.importedFuncTemplates[funcTemplate.funcName] = funcTemplate 107 | } 108 | return builder 109 | } 110 | 111 | func (builder *FuncTemplateBuilder) ImportStruct(structTemplates ...*StructTemplate) *FuncTemplateBuilder { 112 | for _, structTemplate := range structTemplates { 113 | builder.funcTemplate.importedStructTemplates[structTemplate.structName] = structTemplate 114 | } 115 | return builder 116 | } 117 | 118 | func (builder *FuncTemplateBuilder) ImportPackage(pkg string) *FuncTemplateBuilder { 119 | builder.funcTemplate.importedPackages[pkg] = true 120 | return builder 121 | } 122 | 123 | func (builder *FuncTemplateBuilder) Declare(declaration string) *FuncTemplateBuilder { 124 | builder.funcTemplate.declarations[declaration] = true 125 | return builder 126 | } 127 | 128 | func (builder *FuncTemplateBuilder) Source(source string) *FuncTemplate { 129 | builder.funcTemplate.templateSource = source 130 | return builder.funcTemplate 131 | } 132 | 133 | type FuncTemplate struct { 134 | *funcSignature 135 | definedInFile string 136 | templateParams map[string]TemplateParam 137 | templateSource string 138 | generators map[string]interface{} 139 | importedFuncTemplates map[string]*FuncTemplate 140 | importedStructTemplates map[string]*StructTemplate 141 | importedPackages map[string]bool 142 | declarations map[string]bool 143 | } 144 | 145 | func (funcTemplate *FuncTemplate) ImportFunc(funcTemplates ...*FuncTemplate) { 146 | for _, dep := range funcTemplates { 147 | funcTemplate.importedFuncTemplates[dep.funcName] = dep 148 | } 149 | } 150 | 151 | type TemplateParam struct { 152 | Name string 153 | Description string 154 | DefaultValueProvider func(argMap ArgMap) interface{} 155 | } 156 | -------------------------------------------------------------------------------- /example/generated.go: -------------------------------------------------------------------------------- 1 | 2 | package example 3 | import "io" 4 | import "github.com/v2pro/wombat/example/model" 5 | import "github.com/v2pro/wombat/generic" 6 | func init() { 7 | generic.RegisterExpandedFunc("CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_model__UserInfo_ST_model__User2",CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_model__UserInfo_ST_model__User2) 8 | generic.RegisterExpandedFunc("CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_string_ST_string",CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_string_ST_string) 9 | generic.RegisterExpandedFunc("CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_int_ST_int",CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_int_ST_int) 10 | generic.RegisterExpandedFunc("MaxByItselfForPlz_T_int",MaxByItselfForPlz_T_int) 11 | generic.RegisterExpandedFunc("MaxByItselfForPlz_T_float64",MaxByItselfForPlz_T_float64) 12 | generic.RegisterExpandedFunc("MaxByFieldForPlz_F_Score_T_model__User",MaxByFieldForPlz_F_Score_T_model__User) 13 | generic.RegisterExpandedFunc("New_Pair_I_model__IntStringPair",New_Pair_I_model__IntStringPair)} 14 | var copyDynamically func(interface{}, interface{}) error 15 | var ioEOF = io.EOF 16 | func CopySimpleValue_DT_ptr_string_ST_string(err *error,dst *string,src string){ 17 | if dst != nil { 18 | *dst = (string)(src) 19 | } 20 | 21 | } 22 | func CopyAnything_DT_ptr_string_ST_string(err *error,dst *string,src string){ 23 | 24 | 25 | CopySimpleValue_DT_ptr_string_ST_string(err, dst, src) 26 | 27 | 28 | 29 | } 30 | func CopyIntoPtr_DT_ptr_ptr_string_ST_string(err *error,dst **string,src string){ 31 | 32 | 33 | defDst := *dst 34 | if defDst == nil { 35 | 36 | defDst = new(string) 37 | 38 | CopyAnything_DT_ptr_string_ST_string(err, defDst, src) 39 | *dst = defDst 40 | return 41 | } 42 | CopyAnything_DT_ptr_string_ST_string(err, *dst, src) 43 | 44 | } 45 | func CopyAnything_DT_ptr_ptr_string_ST_string(err *error,dst **string,src string){ 46 | 47 | 48 | CopyIntoPtr_DT_ptr_ptr_string_ST_string(err, dst, src) 49 | 50 | 51 | 52 | } 53 | func CopySimpleValue_DT_ptr_int_ST_int(err *error,dst *int,src int){ 54 | if dst != nil { 55 | *dst = (int)(src) 56 | } 57 | 58 | } 59 | func CopyAnything_DT_ptr_int_ST_int(err *error,dst *int,src int){ 60 | 61 | 62 | CopySimpleValue_DT_ptr_int_ST_int(err, dst, src) 63 | 64 | 65 | 66 | } 67 | func CopySliceToSlice_DT_ptr_slice_int_ST_slice_int(err *error,dst *[]int,src []int){ 68 | 69 | if src == nil { 70 | *dst = nil 71 | return 72 | } 73 | dstLen := len(*dst) 74 | if len(src) < dstLen { 75 | dstLen = len(src) 76 | } 77 | for i := 0; i < dstLen; i++ { 78 | CopyAnything_DT_ptr_int_ST_int(err, &(*dst)[i], src[i]) 79 | } 80 | defDst := *dst 81 | for i := dstLen; i < len(src); i++ { 82 | newElem := new(int) 83 | CopyAnything_DT_ptr_int_ST_int(err, newElem, src[i]) 84 | defDst = append(defDst, *newElem) 85 | } 86 | *dst = defDst 87 | 88 | } 89 | func CopyAnything_DT_ptr_slice_int_ST_slice_int(err *error,dst *[]int,src []int){ 90 | 91 | 92 | CopySliceToSlice_DT_ptr_slice_int_ST_slice_int(err, dst, src) 93 | 94 | 95 | 96 | } 97 | func CopyIntoInterface_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_string(err *error,dst *interface {},src string){ 98 | if *dst == nil { 99 | newDst := new(string) 100 | newErr := copyDynamically(newDst, src) 101 | if newErr != nil && *err == nil { 102 | *err = newErr 103 | } 104 | *dst = *newDst 105 | } else { 106 | newErr := copyDynamically(*dst, src) 107 | if newErr != nil && *err == nil { 108 | *err = newErr 109 | } 110 | } 111 | } 112 | func CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_string(err *error,dst *interface {},src string){ 113 | 114 | 115 | CopyIntoInterface_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_string(err, dst, src) 116 | 117 | 118 | 119 | } 120 | func CopyIntoInterface_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_int(err *error,dst *interface {},src int){ 121 | if *dst == nil { 122 | newDst := new(int) 123 | newErr := copyDynamically(newDst, src) 124 | if newErr != nil && *err == nil { 125 | *err = newErr 126 | } 127 | *dst = *newDst 128 | } else { 129 | newErr := copyDynamically(*dst, src) 130 | if newErr != nil && *err == nil { 131 | *err = newErr 132 | } 133 | } 134 | } 135 | func CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_int(err *error,dst *interface {},src int){ 136 | 137 | 138 | CopyIntoInterface_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_int(err, dst, src) 139 | 140 | 141 | 142 | } 143 | func CopyStructToMap_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_model__UserProperties(err *error,dst map[string]interface {},src model.UserProperties){ 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | var existingElem interface {} 153 | var found bool 154 | 155 | existingElem, found = dst["City"] 156 | if found { 157 | CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_string(err, &existingElem, src.City) 158 | dst["City"] = existingElem 159 | } else { 160 | newElem := new(interface {}) 161 | CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_string(err, newElem, src.City) 162 | dst["City"] = *newElem 163 | } 164 | 165 | existingElem, found = dst["Age"] 166 | if found { 167 | CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_int(err, &existingElem, src.Age) 168 | dst["Age"] = existingElem 169 | } else { 170 | newElem := new(interface {}) 171 | CopyAnything_DT_ptr_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_int(err, newElem, src.Age) 172 | dst["Age"] = *newElem 173 | } 174 | 175 | } 176 | func CopyAnything_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_model__UserProperties(err *error,dst map[string]interface {},src model.UserProperties){ 177 | 178 | 179 | CopyStructToMap_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_model__UserProperties(err, dst, src) 180 | 181 | 182 | 183 | } 184 | func CopyFromPtr_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err *error,dst map[string]interface {},src *model.UserProperties){ 185 | 186 | if src == nil { 187 | return 188 | } 189 | CopyAnything_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_model__UserProperties(err, dst, *src) 190 | } 191 | func CopyAnything_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err *error,dst map[string]interface {},src *model.UserProperties){ 192 | 193 | 194 | CopyFromPtr_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err, dst, src) 195 | 196 | 197 | 198 | } 199 | func CopyIntoPtr_DT_ptr_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err *error,dst *map[string]interface {},src *model.UserProperties){ 200 | 201 | 202 | if src == nil { 203 | *dst = nil 204 | return 205 | } 206 | 207 | defDst := *dst 208 | if defDst == nil { 209 | 210 | defDst = map[string]interface {}{} 211 | 212 | CopyAnything_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err, defDst, src) 213 | *dst = defDst 214 | return 215 | } 216 | CopyAnything_DT_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err, *dst, src) 217 | 218 | } 219 | func CopyAnything_DT_ptr_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err *error,dst *map[string]interface {},src *model.UserProperties){ 220 | 221 | 222 | CopyIntoPtr_DT_ptr_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err, dst, src) 223 | 224 | 225 | 226 | } 227 | func CopyStructToStruct_DT_ptr_model__UserInfo_ST_model__User2(err *error,dst *model.UserInfo,src model.User2){ 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | CopyAnything_DT_ptr_ptr_string_ST_string(err, &dst.FirstName, src.FirstName) 244 | 245 | CopyAnything_DT_ptr_ptr_string_ST_string(err, &dst.LastName, src.LastName) 246 | 247 | CopyAnything_DT_ptr_slice_int_ST_slice_int(err, &dst.Tags, src.Tags) 248 | 249 | CopyAnything_DT_ptr_map_string_to_gQPAJHSWGPSUIJTIJRIBX4GE44DBOXJ5R_ST_ptr_model__UserProperties(err, &dst.Properties, src.Properties) 250 | 251 | } 252 | func CopyAnything_DT_ptr_model__UserInfo_ST_model__User2(err *error,dst *model.UserInfo,src model.User2){ 253 | 254 | 255 | CopyStructToStruct_DT_ptr_model__UserInfo_ST_model__User2(err, dst, src) 256 | 257 | 258 | 259 | } 260 | func CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_model__UserInfo_ST_model__User2(theCopyDynamically func(interface {}, interface {}) error,dst interface{},src interface{})( error){ 261 | 262 | copyDynamically = theCopyDynamically 263 | var err error 264 | CopyAnything_DT_ptr_model__UserInfo_ST_model__User2(&err, dst.(*model.UserInfo), src.(model.User2)) 265 | return err 266 | } 267 | func CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_string_ST_string(theCopyDynamically func(interface {}, interface {}) error,dst interface{},src interface{})( error){ 268 | 269 | copyDynamically = theCopyDynamically 270 | var err error 271 | CopyAnything_DT_ptr_string_ST_string(&err, dst.(*string), src.(string)) 272 | return err 273 | } 274 | func CopyAnythingForPlz_CT_gXD56NMK6FVXA26GM25XVH4ZILKLIKTKL_DT_ptr_int_ST_int(theCopyDynamically func(interface {}, interface {}) error,dst interface{},src interface{})( error){ 275 | 276 | copyDynamically = theCopyDynamically 277 | var err error 278 | CopyAnything_DT_ptr_int_ST_int(&err, dst.(*int), src.(int)) 279 | return err 280 | } 281 | func CompareSimpleValue_T_int(val1 int,val2 int)( int){ 282 | if val1 < val2 { 283 | return -1 284 | } else if val1 == val2 { 285 | return 0 286 | } else { 287 | return 1 288 | } 289 | } 290 | func CompareByItself_T_int(val1 int,val2 int)( int){ 291 | 292 | return CompareSimpleValue_T_int(val1, val2) 293 | } 294 | func MaxByItselfForPlz_T_int(vals []interface{})( interface{}){ 295 | 296 | currentMax := vals[0].(int) 297 | for i := 1; i < len(vals); i++ { 298 | typedVal := vals[i].(int) 299 | if CompareByItself_T_int(typedVal, currentMax) > 0 { 300 | currentMax = typedVal 301 | } 302 | } 303 | return currentMax 304 | } 305 | func CompareSimpleValue_T_float64(val1 float64,val2 float64)( int){ 306 | if val1 < val2 { 307 | return -1 308 | } else if val1 == val2 { 309 | return 0 310 | } else { 311 | return 1 312 | } 313 | } 314 | func CompareByItself_T_float64(val1 float64,val2 float64)( int){ 315 | 316 | return CompareSimpleValue_T_float64(val1, val2) 317 | } 318 | func MaxByItselfForPlz_T_float64(vals []interface{})( interface{}){ 319 | 320 | currentMax := vals[0].(float64) 321 | for i := 1; i < len(vals); i++ { 322 | typedVal := vals[i].(float64) 323 | if CompareByItself_T_float64(typedVal, currentMax) > 0 { 324 | currentMax = typedVal 325 | } 326 | } 327 | return currentMax 328 | } 329 | func CompareByField_F_Score_T_model__User(val1 model.User,val2 model.User)( int){ 330 | 331 | 332 | return CompareByItself_T_int(val1.Score, val2.Score) 333 | } 334 | func MaxByFieldForPlz_F_Score_T_model__User(vals []interface{})( interface{}){ 335 | 336 | currentMax := vals[0].(model.User) 337 | for i := 1; i < len(vals); i++ { 338 | typedVal := vals[i].(model.User) 339 | if CompareByField_F_Score_T_model__User(typedVal, currentMax) > 0 { 340 | currentMax = typedVal 341 | } 342 | } 343 | return currentMax 344 | } 345 | 346 | 347 | 348 | type Pair_I_model__IntStringPair struct { 349 | first int 350 | second string 351 | } 352 | 353 | func (pair *Pair_I_model__IntStringPair) SetFirst(val int) { 354 | pair.first = val 355 | } 356 | 357 | func (pair *Pair_I_model__IntStringPair) First() int { 358 | return pair.first 359 | } 360 | 361 | func (pair *Pair_I_model__IntStringPair) SetSecond(val string) { 362 | pair.second = val 363 | } 364 | 365 | func (pair *Pair_I_model__IntStringPair) Second() string { 366 | return pair.second 367 | } 368 | func New_Pair_I_model__IntStringPair()( interface{}){ 369 | 370 | return &Pair_I_model__IntStringPair{} 371 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------