diff --git a/.github/actions/setup-go/action.yml b/.github/actions/setup-go/action.yml index e01ef74..406a99e 100644 --- a/.github/actions/setup-go/action.yml +++ b/.github/actions/setup-go/action.yml @@ -2,11 +2,6 @@ name: Setup Go description: | Setup Go -inputs: - go-version: - description: Used Go version - default: '1.19' - runs: using: "composite" steps: @@ -18,7 +13,4 @@ runs: - id: go-setup uses: actions/setup-go@v3 with: - go-version: ${{ env.GO_VERSION }} - - run: | - go mod download - shell: bash + go-version-file: 'go.mod' diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml index 815fd76..ac5752e 100644 --- a/.github/workflows/lint.yaml +++ b/.github/workflows/lint.yaml @@ -23,4 +23,4 @@ jobs: - name: Run golangci-lint uses: golangci/golangci-lint-action@v3 with: - version: v1.50.1 + version: v1.52.2 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0808482..95a8e22 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ How to release a new version: ## [Unreleased] +## [0.4.0] - 2023-03-27 +### Added +- Support for string IDs. + +### Removed +- Useless marshalling/unmarshalling of custom IDs. + +### Changed +- Updated Go version to 1.20. + ## [0.3.1] - 2023-02-07 ### Added - Repo init command. @@ -36,7 +46,8 @@ How to release a new version: ### Added - Added Changelog. -[Unreleased]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.3.1...HEAD +[Unreleased]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.4.0...HEAD +[0.4.0]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.3.1...v0.4.0 [0.3.1]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.2.2...v0.3.0 [0.2.2]: https://github.com/strvcom/strv-backend-go-tea/compare/v0.2.1...v0.2.2 diff --git a/README.md b/README.md index 505ea8e..26dee72 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,12 @@ import ( //go:generate tea gen id -i ./id.go -o ./id_gen.go type ( - User uint64 - RefreshToken uuid.UUID + User uint64 + RefreshToken uuid.UUID + DeviceIdentifier string ) ``` -After triggering `go generate ./...` within an app, methods `MarshalText`, `MarshalJSON`, `UnmarshalText` and `UnmarshalJSON` are generated. +After triggering `go generate ./...` within an app, methods `MarshalText` and `UnmarshalText` along with other useful functions are generated. ### openapi This command provides a set of tools to manage OpenAPI specifications. diff --git a/cmd/tea/gen_id.go b/cmd/tea/gen_id.go index 26b2a86..99e3d81 100644 --- a/cmd/tea/gen_id.go +++ b/cmd/tea/gen_id.go @@ -8,6 +8,7 @@ import ( "go/parser" "go/token" "os" + "sort" "strings" "text/template" @@ -62,12 +63,19 @@ type GenIDOptions struct { OutputFilePath string } +const stringTemplate = `{{ range .ids }} +func (i *{{ . }}) UnmarshalText(data []byte) error { + *i = {{ . }}(data) + return nil +} + +func (i {{ . }}) MarshalText() ([]byte, error) { + return []byte(i), nil +} +{{ end }}` + const uint64Template = ` func unmarshalUint64(i *uint64, idTypeName string, data []byte) error { - l := len(data) - if l > 2 && data[0] == '"' && data[l-1] == '"' { - data = data[1 : l-1] - } uintNum, err := strconv.ParseUint(string(data), 10, 64) if err != nil { return fmt.Errorf("parsing %q id value: %w", idTypeName, err) @@ -80,17 +88,9 @@ func (i {{ . }}) MarshalText() ([]byte, error) { return []byte(fmt.Sprintf("%d", i)), nil } -func (i {{ . }}) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("\"%d\"", i)), nil -} - func (i *{{ . }}) UnmarshalText(data []byte) error { return unmarshalUint64((*uint64)(i), "{{ . }}", data) } - -func (i *{{ . }}) UnmarshalJSON(data []byte) error { - return unmarshalUint64((*uint64)(i), "{{ . }}", data) -} {{ end }}` const uuidTemplate = ` @@ -124,18 +124,10 @@ func (i {{ . }}) MarshalText() ([]byte, error) { return []byte(uuid.UUID(i).String()), nil } -func (i {{ . }}) MarshalJSON() ([]byte, error) { - return []byte(fmt.Sprintf("\"%s\"", uuid.UUID(i).String())), nil -} - func (i *{{ . }}) UnmarshalText(data []byte) error { return unmarshalUUID((*uuid.UUID)(i), "{{ . }}", data) } -func (i *{{ . }}) UnmarshalJSON(data []byte) error { - return unmarshalUUID((*uuid.UUID)(i), "{{ . }}", data) -} - func (i *{{ . }}) Scan(data any) error { return scanUUID((*uuid.UUID)(i), "{{ . }}", data) } @@ -160,6 +152,10 @@ func (i IDs) generate() ([]byte, error) { if genData, err = i.generateUUID(); err != nil { return nil, fmt.Errorf("generating uuid.UUID ids: %w", err) } + case "string": + if genData, err = i.generateStringID(); err != nil { + return nil, fmt.Errorf("generating string ids: %w", err) + } } if _, err = output.Write(genData); err != nil { @@ -214,28 +210,84 @@ func (i IDs) generateUUID() ([]byte, error) { return generatedOutput.Bytes(), nil } -func (i IDs) generateHeader() []byte { - var d []byte - d = append(d, "package id\n\n"...) - d = append(d, "import (\n"...) - d = append(d, "\t\"fmt\"\n"...) +func (i IDs) generateStringID() ([]byte, error) { + ids, ok := i["string"] + if !ok { + return nil, nil + } + generatedOutput := &bytes.Buffer{} + data := map[string][]string{ + "ids": ids, + } + + t, err := template.New("string").Parse(stringTemplate) + if err != nil { + return nil, err + } + if err = t.Execute(generatedOutput, data); err != nil { + return nil, err + } + + return generatedOutput.Bytes(), nil +} + +func (i IDs) generateHeader() []byte { + // In case of a new type, add import dependencies to standardImports and externalImports. + standardImports := make(map[string]struct{}) + externalImports := make(map[string]struct{}) if _, ok := i["uint64"]; ok { - d = append(d, "\t\"strconv\"\n"...) + standardImports["fmt"] = struct{}{} + standardImports["strconv"] = struct{}{} } if _, ok := i["uuid.UUID"]; ok { - d = append(d, "\n"...) - d = append(d, "\t\"github.com/google/uuid\"\n"...) + standardImports["fmt"] = struct{}{} + externalImports["github.com/google/uuid"] = struct{}{} + } + + var ( + d []byte + standardImportsLen = len(standardImports) + externalImportsLen = len(externalImports) + ) + + d = append(d, "package id\n"...) + if standardImportsLen == 0 && externalImportsLen == 0 { + return d + } + + d = append(d, "\nimport (\n"...) + for _, v := range sortedMapKeys(standardImports) { + d = append(d, fmt.Sprintf("\t\"%s\"\n", v)...) + } + + if externalImportsLen == 0 { + return append(d, ")\n"...) + } + + d = append(d, "\n"...) + for _, v := range sortedMapKeys(externalImports) { + d = append(d, fmt.Sprintf("\t\"%s\"\n", v)...) } return append(d, ")\n"...) } +func sortedMapKeys[V any](m map[string]V) []string { + keys := make([]string, 0, len(m)) + for k := range m { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + func supportedType(typ string) bool { - if typ != "uint64" && typ != "uuid.UUID" { - return false + switch typ { + case "uint64", "uuid.UUID", "string": + return true } - return true + return false } func extractIDs(filename string) (IDs, error) { diff --git a/go.mod b/go.mod index 8834b2f..9fd0e4a 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module go.strv.io/tea -go 1.19 +go 1.20 require ( github.com/Masterminds/sprig/v3 v3.2.2