The linter checks that the Structures are created by the Factory, and not directly.
The checking helps to provide invariants without exclusion and helps avoid creating an invalid object.
go install github.com/maranqz/gofactory/cmd/gofactory@latest
--packageGlobs
– list of glob packages, which can create structures without factories inside the glob package. By default, all structures from another package should be created by factories, tests.--packageGlobsOnly
– use a factory to initiate a structure for glob packages only, tests. Doesn't make sense without--packageGlobs
.
Bad | Good |
---|---|
package main
import (
"fmt"
"bad"
)
func main() {
// Use factory for bad.User
u := &bad.User{
ID: -1,
}
fmt.Println(u.ID) // -1
fmt.Println(u.CreatedAt) // time.Time{}
} package bad
import "time"
type User struct {
ID int64
CreatedAt time.Time
}
var sequenceID = int64(0)
func NextID() int64 {
sequenceID++
return sequenceID
}
|
package main
import (
"fmt"
"good"
)
func main() {
u := good.NewUser()
fmt.Println(u.ID) // auto increment
fmt.Println(u.CreatedAt) // time.Now()
} package user
import "time"
type User struct {
ID int64
CreatedAt time.Time
}
func NewUser() *User {
return &User{
ID: nextID(),
CreatedAt: time.Now(),
}
}
var sequenceID = int64(0)
func nextID() int64 {
sequenceID++
return sequenceID
} |
Linter doesn't catch some cases.
- Buffered channel. You can initialize struct in line
v, ok := <-bufCh
example. - Local initialization, example.
- Named return. If you want to block that case, you can use nonamedreturns linter, example.
- var declaration,
var initilized nested.Struct
gives structure without factory, example. To block that case, you can use gopublicfield to prevent fill of structure fields.
- Catch nested struct in the same package, example.
return Struct{ Other: OtherStruct{}, // want `Use factory for nested.Struct` }
- Resolve false negative issue with
var declaration
.
- Type assertion, type declaration and type underlying, tests.