Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed to manage generated target code with []string instead of buffer. #2

Merged
merged 1 commit into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ assignees: ''

---

## gup version**
## vogen version**
v0.y.z

## Description (About the problem)
Expand Down
31 changes: 31 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
name: Coverage

on:
workflow_dispatch:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
unit_test:
name: Unit test (linux)

strategy:
matrix:
platform: [ubuntu-latest]

runs-on: ${{ matrix.platform }}

steps:
- uses: actions/checkout@v4

- uses: actions/setup-go@v5
with:
go-version: "1"
check-latest: true

- name: Run tests with coverage report output
run: go test -cover -coverpkg=./... -coverprofile=coverage.out ./...

- uses: k1LoW/octocov-action@v1
2 changes: 0 additions & 2 deletions .github/workflows/unit_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ jobs:
- "1"
- "1.23"
- "1.22"
- "1.21"
- "1.20"
fail-fast: false
runs-on: ${{ matrix.os }}

Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ go.work.sum

# Test file
testdata/example_output.go
coverage.*
/coverage.*
2 changes: 1 addition & 1 deletion .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
run:
go: "1.21"
go: "1.22"

issues:
exclude-use-default: false
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@
[![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->

The vogen is a tool to generate value objects in golang.
The vogen is a tool to generate value objects in golang. The vogen works both as a library and as a CLI. Both will automatically generate files with Value Objects defined.

In golang, the only way to implement Value Objects is to use structs. Implementing Getter and Equal() on a newly defined Value Object (structure) is a simple and tedious task. The vogen package simplifies that task.

### Supported OS and go version
## Supported OS and go version
- OS: Linux, macOS, Windows
- Go: 1.21 or later
- Go: 1.22 or later

### Example
#### Implement a value object metadata
## Example
### Implement a value object metadata

Firstly, write your value object metadata. Here is an example: gen/main.go

Expand Down Expand Up @@ -135,4 +135,4 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d

<!-- ALL-CONTRIBUTORS-LIST:END -->

This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module github.com/nao1215/vogen

go 1.21
go 1.22
168 changes: 45 additions & 123 deletions vogen.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package vogen

import (
"bytes"
"fmt"
"go/format"
"os"
Expand Down Expand Up @@ -52,15 +51,17 @@ type Vogen struct {
packageName string
// valueObjects is the list of valueObjects to be generated.
valueObjects []ValueObject
// buf is the buffer to write the generated code.
buf bytes.Buffer
// code is the list of code to be generated.
code []string
}

// New creates a new Vogen struct.
func New(opts ...Option) (*Vogen, error) {
vogen := &Vogen{
filePath: "value_object.go",
packageName: "vo",
filePath: "value_object.go",
packageName: "vo",
valueObjects: []ValueObject{},
code: []string{},
}
for _, opt := range opts {
if err := opt(vogen); err != nil {
Expand Down Expand Up @@ -103,164 +104,85 @@ func (vo *Vogen) validate(vos ...ValueObject) error {

// Generate generates ValueObject code and writes it to the specified file path.
func (vo *Vogen) Generate() error {
vo.buf.Reset()

if err := vo.writePackage(); err != nil {
return fmt.Errorf("failed to write package declaration: %w", err)
}

if err := vo.writeImports(); err != nil {
return fmt.Errorf("failed to write imports: %w", err)
}
vo.code = append(vo.code, "// Code generated by vogen. DO NOT EDIT.\n")
vo.code = append(vo.code, fmt.Sprintf("package %s\n\n", vo.packageName))
vo.writeImports()

for _, valueObject := range vo.valueObjects {
if err := vo.writeStruct(valueObject); err != nil {
return fmt.Errorf("failed to write struct for %s: %w", valueObject.StructName, err)
}

if err := vo.writeConstructor(valueObject); err != nil {
return fmt.Errorf("failed to write constructor for %s: %w", valueObject.StructName, err)
}

if err := vo.writeGetters(valueObject); err != nil {
return fmt.Errorf("failed to write getters for %s: %w", valueObject.StructName, err)
}

if err := vo.writeEqualMethod(valueObject); err != nil {
return fmt.Errorf("failed to write Equal method for %s: %w", valueObject.StructName, err)
}
}

if err := vo.generateFile(); err != nil {
return fmt.Errorf("failed to generate file: %w", err)
}

return nil
}

// writePackage writes the package declaration.
func (vo *Vogen) writePackage() error {
if _, err := vo.buf.WriteString("// Code generated by vogen. DO NOT EDIT.\n"); err != nil {
return err
vo.writeStruct(valueObject)
vo.writeConstructor(valueObject)
vo.writeGetters(valueObject)
vo.writeEqualMethod(valueObject)
}
if _, err := vo.buf.WriteString(fmt.Sprintf("package %s\n\n", vo.packageName)); err != nil {
return err
}
return nil
return vo.generateFile()
}

// writeImports writes the import statements, deduplicating imports.
func (vo *Vogen) writeImports() error {
// writeImports writes the import statements to the code.
func (vo *Vogen) writeImports() {
importSet := map[string]struct{}{}
for _, valueObject := range vo.valueObjects {
for _, imp := range valueObject.Imports {
importSet[imp] = struct{}{}
}
}

if len(importSet) > 0 {
if _, err := vo.buf.WriteString("import (\n"); err != nil {
return err
}
vo.code = append(vo.code, "import (\n")
for imp := range importSet {
if _, err := vo.buf.WriteString(fmt.Sprintf("\t\"%s\"\n", imp)); err != nil {
return err
}
}
if _, err := vo.buf.WriteString(")\n\n"); err != nil {
return err
vo.code = append(vo.code, fmt.Sprintf("\t\"%s\"\n", imp))
}
vo.code = append(vo.code, ")\n\n")
}
return nil
}

// writeStruct writes the struct definition for a ValueObject.
func (vo *Vogen) writeStruct(valueObject ValueObject) error {
if _, err := vo.buf.WriteString(fmt.Sprintf("// %s represents a value object.\n", valueObject.StructName)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("type %s struct {\n", valueObject.StructName)); err != nil {
return err
}
// writeStruct writes the struct to the code.
func (vo *Vogen) writeStruct(valueObject ValueObject) {
vo.code = append(vo.code, fmt.Sprintf("// %s represents a value object.\n", valueObject.StructName))
vo.code = append(vo.code, fmt.Sprintf("type %s struct {\n", valueObject.StructName))
for _, field := range valueObject.Fields {
if _, err := vo.buf.WriteString(fmt.Sprintf("\t%s %s\n", field.lowercaseName(), field.Type)); err != nil {
return err
}
}
if _, err := vo.buf.WriteString("}\n\n"); err != nil {
return err
vo.code = append(vo.code, fmt.Sprintf("\t%s %s\n", field.lowercaseName(), field.Type))
}
return nil
vo.code = append(vo.code, "}\n\n")
}

// writeConstructor writes the constructor function for a ValueObject.
func (vo *Vogen) writeConstructor(valueObject ValueObject) error {
// writeConstructor writes the constructor to the code.
func (vo *Vogen) writeConstructor(valueObject ValueObject) {
constructorArgs := []string{}
constructorInit := []string{}
for _, field := range valueObject.Fields {
constructorArgs = append(constructorArgs, fmt.Sprintf("%s %s", field.lowercaseName(), field.Type))
constructorInit = append(constructorInit, fmt.Sprintf("%s: %s", field.lowercaseName(), field.lowercaseName()))
}

if _, err := vo.buf.WriteString(fmt.Sprintf("// New%s creates a new instance of %s.\n", valueObject.StructName, valueObject.StructName)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("func New%s(%s) %s {\n", valueObject.StructName, strings.Join(constructorArgs, ", "), valueObject.StructName)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("\treturn %s{%s}\n", valueObject.StructName, strings.Join(constructorInit, ", "))); err != nil {
return err
}
if _, err := vo.buf.WriteString("}\n\n"); err != nil {
return err
}
return nil
vo.code = append(vo.code, fmt.Sprintf("// New%s creates a new instance of %s.\n", valueObject.StructName, valueObject.StructName))
vo.code = append(vo.code, fmt.Sprintf("func New%s(%s) %s {\n", valueObject.StructName, strings.Join(constructorArgs, ", "), valueObject.StructName))
vo.code = append(vo.code, fmt.Sprintf("\treturn %s{%s}\n", valueObject.StructName, strings.Join(constructorInit, ", ")))
vo.code = append(vo.code, "}\n\n")
}

// writeGetters writes getter methods for each field of a ValueObject.
func (vo *Vogen) writeGetters(valueObject ValueObject) error {
// writeGetters writes the getter methods to the code.
func (vo *Vogen) writeGetters(valueObject ValueObject) {
for _, field := range valueObject.Fields {
if _, err := vo.buf.WriteString(fmt.Sprintf("// %s returns the %s field.\n", field.Name, field.lowercaseName())); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("func (o %s) %s() %s {\n", valueObject.StructName, field.Name, field.Type)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("\treturn o.%s\n", field.lowercaseName())); err != nil {
return err
}
if _, err := vo.buf.WriteString("}\n\n"); err != nil {
return err
}
vo.code = append(vo.code, fmt.Sprintf("// %s returns the %s field.\n", field.Name, field.lowercaseName()))
vo.code = append(vo.code, fmt.Sprintf("func (o %s) %s() %s {\n", valueObject.StructName, field.Name, field.Type))
vo.code = append(vo.code, fmt.Sprintf("\treturn o.%s\n", field.lowercaseName()))
vo.code = append(vo.code, "}\n\n")
}
return nil
}

// writeEqualMethod writes the Equal method for a ValueObject.
func (vo *Vogen) writeEqualMethod(valueObject ValueObject) error {
// writeEqualMethod writes the Equal method to the code.
func (vo *Vogen) writeEqualMethod(valueObject ValueObject) {
equalChecks := []string{}
for _, field := range valueObject.Fields {
equalChecks = append(equalChecks, fmt.Sprintf("o.%s() == other.%s()", field.Name, field.Name))
}

if _, err := vo.buf.WriteString(fmt.Sprintf("// Equal checks if two %s objects are equal.\n", valueObject.StructName)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("func (o %s) Equal(other %s) bool {\n", valueObject.StructName, valueObject.StructName)); err != nil {
return err
}
if _, err := vo.buf.WriteString(fmt.Sprintf("\treturn %s\n", strings.Join(equalChecks, " && "))); err != nil {
return err
}
if _, err := vo.buf.WriteString("}\n\n"); err != nil {
return err
}
return nil
vo.code = append(vo.code, fmt.Sprintf("// Equal checks if two %s objects are equal.\n", valueObject.StructName))
vo.code = append(vo.code, fmt.Sprintf("func (o %s) Equal(other %s) bool {\n", valueObject.StructName, valueObject.StructName))
vo.code = append(vo.code, fmt.Sprintf("\treturn %s\n", strings.Join(equalChecks, " && ")))
vo.code = append(vo.code, "}\n\n")
}

// generateFile generates the ValueObject code and writes it to the specified file path.
// generateFile generates the file based on the code and writes it to the specified file path.
func (vo *Vogen) generateFile() error {
formattedCode, err := format.Source(vo.buf.Bytes())
formattedCode, err := format.Source([]byte(strings.Join(vo.code, "")))
if err != nil {
return fmt.Errorf("failed to format source code: %w", err)
}
Expand Down
Loading