Skip to content

Commit

Permalink
🔨 Refactor interface and default implementation (#10)
Browse files Browse the repository at this point in the history
💄 Bump version and fix code example in README.md
  • Loading branch information
kitokirisaki authored May 2, 2023
1 parent d6adae8 commit 38aff30
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 278 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[![Go Version](https://img.shields.io/github/go-mod/go-version/core49/gonfig)](https://go.dev/doc/devel/release#go1.20)
[![Project status](https://img.shields.io/badge/version-0.2.0-green.svg)](https://github.com/core49/gonfig/releases)
[![Project status](https://img.shields.io/badge/version-0.3.0-green.svg)](https://github.com/core49/gonfig/releases)
[![Last commit](https://img.shields.io/github/last-commit/core49/gonfig/main)](https://github.com/core49/gonfig/commits/main)
[![Go Report Card](https://goreportcard.com/badge/github.com/core49/gonfig)](https://goreportcard.com/report/github.com/core49/gonfig)
[![codecov](https://img.shields.io/codecov/c/github/core49/gonfig?token=AO6U2S2I91)](https://codecov.io/gh/core49/gonfig)
Expand Down Expand Up @@ -48,12 +48,12 @@ func main() {
var config YourConfigStruct

// Check if the file exists or if the file is empty
if exists, err := conf.IsEmpty(config); err != nil || exists {
if exists, err := conf.IsEmpty(&config); err != nil || exists {
// handle error or what you want to do if the file exists
}

// Write an empty JSON skeleton of the struct/model to the file
if err := conf.WriteSkeleton(config); err != nil {
if err := conf.WriteSkeleton(&config); err != nil {
// Handle error
}

Expand Down
34 changes: 17 additions & 17 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
"time"
)

// New creates a new repository with the given options.
// It applies the flag configuration, generates a default file path if not disabled, and returns the repository
func New(options ...OptionFunc) (Repository, error) {
r := &repository{osArgs: os.Args, fileSystem: afero.NewOsFs()}
// New creates a new Repository with the given options.
// It applies the flag configuration, generates a default file path if not disabled, and returns the Repository
func New(options ...OptionFunc) (Gonfig, error) {
r := &Repository{osArgs: os.Args, fileSystem: afero.NewOsFs()}

for _, optionFunc := range options {
optionFunc(r)
Expand All @@ -34,49 +34,49 @@ func New(options ...OptionFunc) (Repository, error) {

// OptionSetOsArgs sets the os arguments.
func OptionSetOsArgs(osArgs []string) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.osArgs = osArgs
}
}

// OptionSetFileSystem sets the file system.
func OptionSetFileSystem(fs afero.Fs) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.fileSystem = fs
}
}

// OptionAppendFlagConfig appends the given `flagConfig` to the flag configuration.
func OptionAppendFlagConfig(fc flagConfig) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.flagConfiguration = append(r.flagConfiguration, fc)
}
}

// OptionSetFlagConfiguration sets the flag configuration to the given `flagConfiguration`.
func OptionSetFlagConfiguration(fc flagConfiguration) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.flagConfiguration = fc
}
}

// OptionDisableDefaultFlagConfiguration disables the default flag configuration.
func OptionDisableDefaultFlagConfiguration(v bool) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.disableDefaultFlagConfiguration = v
}
}

// OptionSetConfigFilePathVariable sets the file path and disables default path generation.
func OptionSetConfigFilePathVariable(path string) OptionFunc {
return func(r *repository) {
return func(r *Repository) {
r.filePath = path
r.disableDefaultFilePathGeneration = true
}
}

// applyFlagConfiguration applies flag configurations to command line arguments.
func (r *repository) applyFlagConfiguration() error {
func (r *Repository) applyFlagConfiguration() error {
fmt.Println(r.osArgs)
if len(r.osArgs) == 0 {
return errors.New("os args should not be empty")
Expand Down Expand Up @@ -105,16 +105,16 @@ func (r *repository) applyFlagConfiguration() error {
}

// checkModel verifies that the provided model is a non-nil pointer to a struct type.
func (r *repository) checkModel(model interface{}) error {
func (r *Repository) checkModel(model interface{}) error {
if model == nil || reflect.ValueOf(model).Kind() != reflect.Ptr || reflect.TypeOf(model).Elem().Kind() != reflect.Struct {
return ErrInvalidConfigModel
}

return nil
}

// openFile opens the repository's designated file using the file system.
func (r *repository) openFile() (afero.File, closeFileFunc, error) {
// openFile opens the Repository's designated file using the file system.
func (r *Repository) openFile() (afero.File, closeFileFunc, error) {
if len(r.filePath) == 0 {
return nil, nil, ErrEmptyConfigFilePath
}
Expand All @@ -128,7 +128,7 @@ func (r *repository) openFile() (afero.File, closeFileFunc, error) {
}

// Load loads configuration data from the file at the file path into the given model.
func (r *repository) Load(model interface{}) error {
func (r *Repository) Load(model interface{}) error {
if err := r.checkModel(model); err != nil {
return err
}
Expand All @@ -145,7 +145,7 @@ func (r *repository) Load(model interface{}) error {
}

// WriteSkeleton writes a JSON skeleton of the model to the file path.
func (r *repository) WriteSkeleton(model interface{}) error {
func (r *Repository) WriteSkeleton(model interface{}) error {
if err := r.checkModel(model); err != nil {
return err
}
Expand Down Expand Up @@ -179,7 +179,7 @@ func (r *repository) WriteSkeleton(model interface{}) error {
}

// IsEmpty checks if the config file is empty. Returns `true` if the file does not exist or is empty.
func (r *repository) IsEmpty(model interface{}) (bool, error) {
func (r *Repository) IsEmpty(model interface{}) (bool, error) {
if err := r.checkModel(model); err != nil {
return false, err
}
Expand Down
56 changes: 27 additions & 29 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import (
"time"
)

//go:generate mockgen --build_flags=--mod=mod -destination mock_afero_fs_test.go -package gonfig github.com/spf13/afero Fs

type configModel struct {
Name string `json:"name"`
Version int `json:"version"`
Expand Down Expand Up @@ -113,7 +111,7 @@ func TestNew(t *testing.T) {

func TestOptionSetFileSystem(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
r := &repository{}
r := &Repository{}
require.Empty(t, r.fileSystem)

afs := afero.NewMemMapFs()
Expand All @@ -127,7 +125,7 @@ func TestOptionSetFileSystem(t *testing.T) {

func TestOptionAppendFlagConfig(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
r := &repository{}
r := &Repository{}
fc := flagConfig{
Type: "string",
Name: "test",
Expand All @@ -145,7 +143,7 @@ func TestOptionAppendFlagConfig(t *testing.T) {

func TestOptionSetFlagConfiguration(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
r := &repository{}
r := &Repository{}

optionFunc := OptionSetFlagConfiguration(defaultFlagConfig)
optionFunc(r)
Expand All @@ -156,7 +154,7 @@ func TestOptionSetFlagConfiguration(t *testing.T) {

func TestOptionDisableDefaultFlagConfiguration(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
r := &repository{}
r := &Repository{}

optionFunc := OptionDisableDefaultFlagConfiguration(true)
optionFunc(r)
Expand All @@ -167,7 +165,7 @@ func TestOptionDisableDefaultFlagConfiguration(t *testing.T) {

func TestOptionSetConfigFilePathVariable(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
r := &repository{}
r := &Repository{}

optionFunc := OptionSetConfigFilePathVariable("/tmp/test.json")
optionFunc(r)
Expand All @@ -194,7 +192,7 @@ func TestRepository_Load(t *testing.T) {
_, err = file.Write(j)
require.NoError(t, err)

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -208,34 +206,34 @@ func TestRepository_Load(t *testing.T) {
t.Run("Fail", func(t *testing.T) {
t.Run("InvalidConfigModel", func(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.Load(nil)

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
t.Run("NoneStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.Load("string")

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
t.Run("NonePointerStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.Load(configModel{})

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
})
t.Run("EmptyFilePath", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.Load(&configModel{})

assert.EqualError(t, err, ErrEmptyConfigFilePath.Error())
})
t.Run("InvalidFilePath", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "not.existing",
fileSystem: afs,
}
Expand All @@ -250,7 +248,7 @@ func TestRepository_WriteSkeleton(t *testing.T) {
t.Run("Ok", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -276,7 +274,7 @@ func TestRepository_WriteSkeleton(t *testing.T) {
require.NoError(t, err)
require.NoError(t, file.Close())

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -286,26 +284,26 @@ func TestRepository_WriteSkeleton(t *testing.T) {
})
t.Run("InvalidConfigModel", func(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.WriteSkeleton(nil)

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
t.Run("NoneStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.WriteSkeleton("string")

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
t.Run("NonePointerStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.WriteSkeleton(configModel{})

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
})
})
t.Run("MarshalIndent", func(t *testing.T) {
r := &repository{}
r := &Repository{}

m := struct {
X chan int
Expand All @@ -315,15 +313,15 @@ func TestRepository_WriteSkeleton(t *testing.T) {
assert.Error(t, err)
})
t.Run("EmptyFilePath", func(t *testing.T) {
r := &repository{}
r := &Repository{}
err := r.WriteSkeleton(&configModel{})

assert.EqualError(t, err, ErrEmptyConfigFilePath.Error())
})
t.Run("InvalidFilePath", func(t *testing.T) {
afs := afero.NewReadOnlyFs(afero.NewMemMapFs())

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -340,7 +338,7 @@ func TestRepository_IsEmptyConfig(t *testing.T) {
t.Run("FileDoesNotExist", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -354,7 +352,7 @@ func TestRepository_IsEmptyConfig(t *testing.T) {
t.Run("Empty", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -374,7 +372,7 @@ func TestRepository_IsEmptyConfig(t *testing.T) {
t.Run("ContainsEmptyJSON", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand All @@ -397,7 +395,7 @@ func TestRepository_IsEmptyConfig(t *testing.T) {
t.Run("ContainsValidContent", func(t *testing.T) {
afs := afero.NewMemMapFs()

r := &repository{
r := &Repository{
filePath: "test.json",
fileSystem: afs,
}
Expand Down Expand Up @@ -425,29 +423,29 @@ func TestRepository_IsEmptyConfig(t *testing.T) {
t.Run("Fail", func(t *testing.T) {
t.Run("InvalidConfigModel", func(t *testing.T) {
t.Run("Nil", func(t *testing.T) {
r := &repository{}
r := &Repository{}
exists, err := r.IsEmpty(nil)

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
assert.False(t, exists)
})
t.Run("NoneStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
exists, err := r.IsEmpty("string")

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
assert.False(t, exists)
})
t.Run("NonePointerStruct", func(t *testing.T) {
r := &repository{}
r := &Repository{}
exists, err := r.IsEmpty(configModel{})

assert.EqualError(t, err, ErrInvalidConfigModel.Error())
assert.False(t, exists)
})
})
t.Run("EmptyFilePath", func(t *testing.T) {
r := &repository{}
r := &Repository{}
exists, err := r.IsEmpty(&configModel{})

assert.EqualError(t, err, ErrEmptyConfigFilePath.Error())
Expand Down
Loading

0 comments on commit 38aff30

Please sign in to comment.