Initial commit with main funcs and tests
This commit is contained in:
parent
d58b07d9d3
commit
8e0406167b
93
.gitignore
vendored
Normal file
93
.gitignore
vendored
Normal file
@ -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/
|
38
.travis.yml
Normal file
38
.travis.yml
Normal file
@ -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
|
2
LICENSE
2
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 <alex@webz.asia>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
176
README.md
176
README.md
@ -1 +1,175 @@
|
||||
# fields
|
||||
[![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 <alex@webz.asia>
|
||||
|
||||
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.
|
||||
|
71
common.go
Normal file
71
common.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
*
|
||||
* 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 <alex@webz.asia>
|
||||
* @copyright Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
* @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)
|
||||
}
|
71
examples/example-1/main.go
Normal file
71
examples/example-1/main.go
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
*
|
||||
* 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 <alex@webz.asia>
|
||||
* @copyright Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
* @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))
|
||||
}
|
93
examples/example-2/main.go
Normal file
93
examples/example-2/main.go
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
*
|
||||
* 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 <alex@webz.asia>
|
||||
* @copyright Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
* @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))
|
||||
}
|
96
fields.go
Normal file
96
fields.go
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
*
|
||||
* 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 <alex@webz.asia>
|
||||
* @copyright Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
* @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
|
||||
}
|
359
fields_test.go
Normal file
359
fields_test.go
Normal file
@ -0,0 +1,359 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
*
|
||||
* 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 <alex@webz.asia>
|
||||
* @copyright Copyright (c) 2020 Alex aka mailoman <alex@webz.asia>
|
||||
* @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())
|
||||
}
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/go-convert/fields
|
||||
|
||||
go 1.13
|
||||
|
||||
require github.com/stretchr/testify v1.4.0
|
11
go.sum
Normal file
11
go.sum
Normal file
@ -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=
|
Loading…
Reference in New Issue
Block a user