diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e4910b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,93 @@ +# Created by .ignore support plugin (hsz.mobi) +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +vendor/ + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/ + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +*.log +*.bak +#swagger.json +.cache/ +coverage.* +data/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..e7c71ce --- /dev/null +++ b/.travis.yml @@ -0,0 +1,38 @@ +language: go +cache: + directories: + - "$GOPATH/pkg/mod" +install: true +addons: + artifacts: + debug: true +go: +- 1.13.x +env: + global: + - GOPROXY=https://proxy.golang.org + - GO111MODULE=on + - GOARCH=amd64 + - GO_FOR_RELEASE=1.13 +jobs: + allow_failures: + - go: master + include: + - go: master + env: JOB=test + script: + - go test -race -coverprofile=coverage.txt -covermode=atomic ./... + after_success: + - bash <(curl -s https://codecov.io/bash) + deploy: + provider: releases + api_key: + secure: OU8Dv4Z7XKnXWcwVRSCiMfhgfLHXxUOqk0KF0qEKqxb61HFTFKHWci9bW47n4vYF3/2YI23d8UcFQJcTLMYEe8PdMkNa+yDu1A0mWIAcuVHQ/bKJNEhFklnfac9kzf8mDkpjMmY2jvHXeFolLBfoznluCV2A7YnbfBK/zJ0FG8OpaTZyziJV+61PqwDVou3Zk9jDUSF6Okzjcxsn8NqpultK9uQ46+/CVfoh6aQMWTLU58wuFk2VT46/BBfchyHZl2C+QX0kBrp2jlYNXrdVZXGQVJ2AK2Jzk5l9FO0g2LZT0Tg6CatiTvNUewdlnYrEldkRvWU96y8TcaokNnTVLoZSKxztPX3E1c0a8sFHFvam64IQytRg+Bb3g2yJv4G7H0iC0gJEOJIQRukKbqY+yKzmQWhUyLXNyAMkaD4EZsWI5ec9wvzaTXAviK8pAYsiCRO2h3X6SdiFyGxMUDRZUIq48776gOcXK0TsdTBLqYEHXc5BKIPyDfpo3AN+2wdRxeyWnKAXxJVHSKurYSnJjk/0HTcZlkKeEvZgG2K1o2/MksuUovF71aTGStcb4/ma9bO5O38ZAoBOivp1UvIa0kKu5N9x+OdOxXdQBBnIZo5S8/YRjyejL4p6dNRHGq4wNWfrw3lbZU4KNqmWzLtj1qzjcnBjiy02BOpS6dpxrm4= + skip_cleanup: true + on: + repo: go-convert/fields + tags: true +notifications: + email: + on_success: change + on_failure: always diff --git a/LICENSE b/LICENSE index 261eeb9..0311b1a 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright (c) 2020 Alex aka mailoman Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 68b5054..78d172b 100644 --- a/README.md +++ b/README.md @@ -1 +1,175 @@ -# fields \ No newline at end of file +[![Go Report Card](https://goreportcard.com/badge/github.com/go-convert/fields)](https://goreportcard.com/report/github.com/go-convert/fields) +[![Actions Status](https://github.com/go-convert/fields/workflows/Go/badge.svg)](https://github.com/go-convert/fields/actions) +[![Build Status](https://travis-ci.org/go-convert/fields.svg?branch=master)](https://travis-ci.org/go-convert/fields) +[![codecov](https://codecov.io/gh/go-convert/fields/branch/master/graph/badge.svg)](https://codecov.io/gh/go-convert/fields) + +# golang convert fields lib + +## Idea +Main idea was to build a lib/helper to simplify copying same-name fields from one struct object to another one. + +## Examples +### Example 1: most common usage, with sub-cases +```go +import ( + "log" + "encoding/json" + + "github.com/go-convert/fields" +) + +type FromStruct struct { + ID string `conv:"new,update"` + Field1 string `conv:"new,update"` + Field2 string `conv:"update"` +} + +type ToStruct struct { + ID string + Field1 string + Field2 string + Field3 string +} + +func main() { + fromDefault := FromStruct{ + "1", "field 1 value", "field 2 value", + } + toDefault := ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", + } + + log.Print("example 1.1 - new") + from := fromDefault + to := toDefault + + fields.Сonvert(from, "new", &to) + fromB, _ := json.Marshal(from) + toB, _ := json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 1.2 - update") + from = fromDefault + to = toDefault + + fields.Сonvert(from, "update", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) +} +``` +After running `go run examples/example-1/main.go` + +Result will be something like below: +```bash +2020/01/14 21:46:32 example 1.1 - new +2020/01/14 21:46:32 from {"ID":"1","Field1":"field 1 value","Field2":"field 2 value"} => to {"ID":"1","Field1":"field 1 value","Field2":"old field 2 value","Field3":"old field 3 value"} +2020/01/14 21:46:32 example 1.2 - update +2020/01/14 21:46:32 from {"ID":"1","Field1":"field 1 value","Field2":"field 2 value"} => to {"ID":"1","Field1":"field 1 value","Field2":"field 2 value","Field3":"old field 3 value"} +``` + +### Example 2: custom config setup usage, with sub-cases +* New name of tag: `copy` +* New tag value for omitting: `ignore` +* New tag value for omitting empty field: `noempty` + +```go +import ( + "log" + "encoding/json" + + "github.com/go-convert/fields" +) + +type FromStructCustom struct { + ID string `copy:"new,update"` + Field1 string `copy:"new,update"` + Field2 string `copy:"update"` + Field3 string `copy:"category-1,ignore"` + Field4 string `copy:"category-1,noempty"` +} + +type ToStruct struct { + ID string + Field1 string + Field2 string + Field3 string + Field4 string +} + +func main() { + fields.SetTagName("copy") + fields.SetOmitTagName("ignore") + fields.SetOmitEmptyTagName("noempty") + + log.Print("example 2.1 - new") + from := FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + } + to := ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "new", &to) + fromB, _ := json.Marshal(from) + toB, _ := json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 2.2.1 - category-1") + from = FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + } + to = ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "category-1", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 2.2.2 - category-1") + from = FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "", + } + to = ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "category-1", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) +} +``` +After running `go run examples/example-1/main.go` + +Result will be something like below: +```bash +2020/01/14 23:13:26 example 2.1 - new +2020/01/14 23:13:26 from {"ID":"1","Field1":"field 1 value","Field2":"field 2 value","Field3":"omitting","Field4":"not empty"} => to {"ID":"1","Field1":"field 1 value","Field2":"old field 2 value","Field3":"old field 3 value","Field4":"old field 4 value"} +2020/01/14 23:13:26 example 2.2.1 - category-1 +2020/01/14 23:13:26 from {"ID":"1","Field1":"field 1 value","Field2":"field 2 value","Field3":"omitting","Field4":"not empty"} => to {"ID":"old val 1","Field1":"old field 1 value","Field2":"old field 2 value","Field3":"old field 3 value","Field4":"not empty"} +2020/01/14 23:13:26 example 2.2.2 - category-1 +2020/01/14 23:13:26 from {"ID":"1","Field1":"field 1 value","Field2":"field 2 value","Field3":"omitting","Field4":""} => to {"ID":"old val 1","Field1":"old field 1 value","Field2":"old field 2 value","Field3":"old field 3 value","Field4":"old field 4 value"} +``` + +## License +Copyright (c) 2020 Alex aka mailoman + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/common.go b/common.go new file mode 100644 index 0000000..836a9dd --- /dev/null +++ b/common.go @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 14.01.2020 + * + */ + +package fields + +const ( + defaultTagName = "conv" + defaultOmitTagName = "omit" + defaultOmitEmptyTagName = "omitempty" +) + +var ( + tagName = defaultTagName + omitTagName = defaultOmitTagName + omitEmptyTagName = defaultOmitEmptyTagName +) + +// ConvertTagName returns special tag name +func ConvertTagName() string { + return tagName +} + +// OmitTagName returns omitting tag name +func OmitTagName() string { + return omitTagName +} + +// OmitEmptyTagName returns omitting empty field tag name +func OmitEmptyTagName() string { + return omitEmptyTagName +} + +// SetTagName sets special tag name +func SetTagName(value string) { + tagName = value +} + +// SetOmitTagName sets omitting tag name +func SetOmitTagName(value string) { + omitTagName = value +} + +// SetOmitEmptyTagName sets omitting empty field tag name +func SetOmitEmptyTagName(value string) { + omitEmptyTagName = value +} + +// SetDefaults resets all configured tag names to defaults +func SetDefaults() { + SetTagName(defaultTagName) + SetOmitTagName(defaultOmitTagName) + SetOmitEmptyTagName(defaultOmitEmptyTagName) +} diff --git a/examples/example-1/main.go b/examples/example-1/main.go new file mode 100644 index 0000000..3fe3dd2 --- /dev/null +++ b/examples/example-1/main.go @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 14.01.2020 + * + */ + +package main + +import ( + "encoding/json" + "log" + + "github.com/go-convert/fields" +) + +type fromStruct struct { + ID string `conv:"new,update"` + Field1 string `conv:"new,update"` + Field2 string `conv:"update"` +} + +type toStruct struct { + ID string + Field1 string + Field2 string + Field3 string +} + +func main() { + fromDefault := fromStruct{ + "1", "field 1 value", "field 2 value", + } + toDefault := toStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", + } + + log.Print("example 1.1 - new") + from := fromDefault + to := toDefault + + fields.Сonvert(from, "new", &to) + fromB, _ := json.Marshal(from) + toB, _ := json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 1.2 - update") + from = fromDefault + to = toDefault + + fields.Сonvert(from, "update", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) +} diff --git a/examples/example-2/main.go b/examples/example-2/main.go new file mode 100644 index 0000000..7b6f3a4 --- /dev/null +++ b/examples/example-2/main.go @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 14.01.2020 + * + */ + +package main + +import ( + "encoding/json" + "log" + + "github.com/go-convert/fields" +) + +type fromStructCustom struct { + ID string `copy:"new,update"` + Field1 string `copy:"new,update"` + Field2 string `copy:"update"` + Field3 string `copy:"category-1,ignore"` + Field4 string `copy:"category-1,noempty"` +} + +type toStruct struct { + ID string + Field1 string + Field2 string + Field3 string + Field4 string +} + +func main() { + fields.SetTagName("copy") + fields.SetOmitTagName("ignore") + fields.SetOmitEmptyTagName("noempty") + + log.Print("example 2.1 - new") + from := fromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + } + to := toStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "new", &to) + fromB, _ := json.Marshal(from) + toB, _ := json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 2.2.1 - category-1") + from = fromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + } + to = toStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "category-1", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) + + log.Print("example 2.2.2 - category-1") + from = fromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "", + } + to = toStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + } + + fields.Сonvert(from, "category-1", &to) + fromB, _ = json.Marshal(from) + toB, _ = json.Marshal(to) + + log.Printf("from %s => to %s", string(fromB), string(toB)) +} diff --git a/fields.go b/fields.go new file mode 100644 index 0000000..959ccce --- /dev/null +++ b/fields.go @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 14.01.2020 + * + */ + +package fields + +import ( + "log" + "reflect" + "strings" +) + +//Convert copies field values from one struct to another based on tags setup +func Сonvert(from interface{}, kind string, to interface{}) { + if nil == from { + log.Print("nil value is not supported") + + return + } + + v := reflect.ValueOf(from) + vt := reflect.ValueOf(to) + t := v.Type() + + if t.Kind() != reflect.Struct { + log.Printf("type %s is not supported", t.Kind()) + + return + } + + if vt.Kind() == reflect.Ptr { + vt = reflect.ValueOf(to).Elem() + } + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + // skip unexported fields. from godoc: + // PkgPath is the package path that qualifies a lower case (unexported) + // field name. It is empty for upper case (exported) field names. + if f.PkgPath != "" { + continue + } + fv := v.Field(i) + found, omit, omitempty := readTag(f, kind) + // skip if tag "omit" set. + if omit { + continue + } + // skip empty values when "omitempty" set. + if omitempty && fv.String() == "" { + continue + } + + if found { + ft := vt.FieldByName(f.Name) + ft.Set(fv) + } + } +} + +func readTag(f reflect.StructField, kind string) (bool, bool, bool) { + val, ok := f.Tag.Lookup(ConvertTagName()) + if !ok { + return false, false, false + } + opts := strings.Split(val, ",") + + return indexOf(opts, kind) > -1, indexOf(opts, OmitTagName()) > -1, indexOf(opts, OmitEmptyTagName()) > -1 +} + +func indexOf(a []string, x string) int { + for i, n := range a { + if x == n { + return i + } + } + + return -1 +} diff --git a/fields_test.go b/fields_test.go new file mode 100644 index 0000000..2c41405 --- /dev/null +++ b/fields_test.go @@ -0,0 +1,359 @@ +/* + * Copyright (c) 2020 Alex aka mailoman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * @author Alex aka mailoman + * @copyright Copyright (c) 2020 Alex aka mailoman + * @since 14.01.2020 + * + */ + +package fields_test + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/go-convert/fields" +) + +type FromStruct struct { + ID string `conv:"new,update"` + Field1 string `conv:"new,update"` + Field2 string `conv:"update"` + Field3 string `conv:"update,omit"` + Field4 string `conv:"category-1,omitempty"` +} + +type FromStructNone struct { + ID string `conv1:"new,update"` + Field1 string `conv2:"new,update"` + Field2 string `conv3:"update"` +} + +type ToStruct struct { + ID string + Field1 string + Field2 string + Field3 string + Field4 string +} + +type FromStructCustom struct { + ID string `copy:"new,update"` + Field1 string `copy:"new,update"` + Field2 string `copy:"update"` + Field3 string `copy:"update,ignore"` + Field4 string `copy:"category-1,noempty"` +} + +var ( + // testsNormal provides normal test cases + testsNormal = []struct { + from FromStruct + kind string + to ToStruct + expectedTo ToStruct + }{ + // case 1.1 + { + FromStruct{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "new", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 1.2 + { + FromStruct{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "field 2 value", "old field 3 value", "", + }, + }, + // case 1.3.1 + { + FromStruct{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + }, + // case 1.3.2 + { + FromStruct{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "not empty", + }, + }, + } + + // testsNone provides not applicable test cases + testsNone = []struct { + from FromStructNone + kind string + to ToStruct + expectedTo ToStruct + }{ + // case 1.1 + { + FromStructNone{ + "1", "field 1 value", "field 2 value", + }, + "new", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 1.2 + { + FromStructNone{ + "1", "field 1 value", "field 2 value", + }, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + } + + // testsNegative provides failing test cases + testsNegative = []struct { + from interface{} + kind string + to ToStruct + expectedTo ToStruct + }{ + // case 1 + { + "string value", + "new", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 2 + { + 1, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 3 + { + nil, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + } + + // testsCustomNormal provides normal test cases with custom init config + testsCustomNormal = []struct { + from FromStructCustom + kind string + to ToStruct + expectedTo ToStruct + }{ + // case 1.1 + { + FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "new", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "old field 2 value", "old field 3 value", "", + }, + }, + // case 1.2 + { + FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "update", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "", + }, + ToStruct{ + "1", "field 1 value", "field 2 value", "old field 3 value", "", + }, + }, + // case 1.3.1 + { + FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + }, + // case 1.3.2 + { + FromStructCustom{ + "1", "field 1 value", "field 2 value", "omitting", "not empty", + }, + "category-1", + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "old field 4 value", + }, + ToStruct{ + "old val 1", "old field 1 value", "old field 2 value", "old field 3 value", "not empty", + }, + }, + } +) + +func TestConvert_Normal(t *testing.T) { + for _, test := range testsNormal { + + to := &ToStruct{} + *to = test.to + + fields.Сonvert(test.from, test.kind, to) + + assert.NotEmpty(t, *to) + assert.Equal(t, test.expectedTo, *to) + } +} + +func TestConvert_None(t *testing.T) { + for _, test := range testsNone { + + to := &ToStruct{} + *to = test.to + + fields.Сonvert(test.from, test.kind, to) + + assert.NotEmpty(t, *to) + assert.Equal(t, test.expectedTo, *to) + } +} + +func TestConvert_Negative(t *testing.T) { + for _, test := range testsNegative { + + to := &ToStruct{} + *to = test.to + + fields.Сonvert(test.from, test.kind, to) + + assert.NotEmpty(t, *to) + assert.Equal(t, test.expectedTo, *to) + } +} + +func TestConvert_Custom(t *testing.T) { + fields.SetTagName("copy") + fields.SetOmitTagName("ignore") + fields.SetOmitEmptyTagName("noempty") + for _, test := range testsCustomNormal { + + to := &ToStruct{} + *to = test.to + + fields.Сonvert(test.from, test.kind, to) + + assert.NotEmpty(t, *to) + assert.Equal(t, test.expectedTo, *to) + } +} + +func TestSetDefaults(t *testing.T) { + // not default values after previous tests + assert.NotEqual(t, "conv", fields.ConvertTagName()) + assert.NotEqual(t, "omit", fields.OmitTagName()) + assert.NotEqual(t, "omitempty", fields.OmitEmptyTagName()) + + fields.SetDefaults() + + assert.Equal(t, "conv", fields.ConvertTagName()) + assert.Equal(t, "omit", fields.OmitTagName()) + assert.Equal(t, "omitempty", fields.OmitEmptyTagName()) +} + +func TestConvertTagName(t *testing.T) { + defaultValue := fields.ConvertTagName() + assert.Equal(t, "conv", defaultValue) + + newValue := "newTagName" + fields.SetTagName(newValue) + + assert.Equal(t, newValue, fields.ConvertTagName()) +} + +func TestOmitTagName(t *testing.T) { + defaultValue := fields.OmitTagName() + assert.Equal(t, defaultValue, "omit") + + newValue := "newOmitTagName" + fields.SetOmitTagName(newValue) + + assert.Equal(t, newValue, fields.OmitTagName()) +} + +func TestOmitEmptyTagName(t *testing.T) { + defaultValue := fields.OmitEmptyTagName() + assert.Equal(t, defaultValue, "omitempty") + + newValue := "newOmitEmptyTagName" + fields.SetOmitEmptyTagName(newValue) + + assert.Equal(t, newValue, fields.OmitEmptyTagName()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..fa25bb0 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/go-convert/fields + +go 1.13 + +require github.com/stretchr/testify v1.4.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..8fdee58 --- /dev/null +++ b/go.sum @@ -0,0 +1,11 @@ +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=