initial import
This commit is contained in:
40
core/conf/config.go
Normal file
40
core/conf/config.go
Normal file
@@ -0,0 +1,40 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path"
|
||||
|
||||
"zero/core/mapping"
|
||||
)
|
||||
|
||||
var loaders = map[string]func([]byte, interface{}) error{
|
||||
".json": LoadConfigFromJsonBytes,
|
||||
".yaml": LoadConfigFromYamlBytes,
|
||||
".yml": LoadConfigFromYamlBytes,
|
||||
}
|
||||
|
||||
func LoadConfig(file string, v interface{}) error {
|
||||
if content, err := ioutil.ReadFile(file); err != nil {
|
||||
return err
|
||||
} else if loader, ok := loaders[path.Ext(file)]; ok {
|
||||
return loader(content, v)
|
||||
} else {
|
||||
return fmt.Errorf("unrecoginized file type: %s", file)
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfigFromJsonBytes(content []byte, v interface{}) error {
|
||||
return mapping.UnmarshalJsonBytes(content, v)
|
||||
}
|
||||
|
||||
func LoadConfigFromYamlBytes(content []byte, v interface{}) error {
|
||||
return mapping.UnmarshalYamlBytes(content, v)
|
||||
}
|
||||
|
||||
func MustLoad(path string, v interface{}) {
|
||||
if err := LoadConfig(path, v); err != nil {
|
||||
log.Fatalf("error: config file %s, %s", path, err.Error())
|
||||
}
|
||||
}
|
||||
109
core/conf/properties.go
Normal file
109
core/conf/properties.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"zero/core/iox"
|
||||
)
|
||||
|
||||
// PropertyError represents a configuration error message.
|
||||
type PropertyError struct {
|
||||
error
|
||||
message string
|
||||
}
|
||||
|
||||
// Properties interface provides the means to access configuration.
|
||||
type Properties interface {
|
||||
GetString(key string) string
|
||||
SetString(key, value string)
|
||||
GetInt(key string) int
|
||||
SetInt(key string, value int)
|
||||
ToString() string
|
||||
}
|
||||
|
||||
// Properties config is a key/value pair based configuration structure.
|
||||
type mapBasedProperties struct {
|
||||
properties map[string]string
|
||||
lock sync.RWMutex
|
||||
}
|
||||
|
||||
// Loads the properties into a properties configuration instance. May return the
|
||||
// configuration itself along with an error that indicates if there was a problem loading the configuration.
|
||||
func LoadProperties(filename string) (Properties, error) {
|
||||
lines, err := iox.ReadTextLines(filename, iox.WithoutBlank(), iox.OmitWithPrefix("#"))
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
raw := make(map[string]string)
|
||||
for i := range lines {
|
||||
pair := strings.Split(lines[i], "=")
|
||||
if len(pair) != 2 {
|
||||
// invalid property format
|
||||
return nil, &PropertyError{
|
||||
message: fmt.Sprintf("invalid property format: %s", pair),
|
||||
}
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(pair[0])
|
||||
value := strings.TrimSpace(pair[1])
|
||||
raw[key] = value
|
||||
}
|
||||
|
||||
return &mapBasedProperties{
|
||||
properties: raw,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (config *mapBasedProperties) GetString(key string) string {
|
||||
config.lock.RLock()
|
||||
ret := config.properties[key]
|
||||
config.lock.RUnlock()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (config *mapBasedProperties) SetString(key, value string) {
|
||||
config.lock.Lock()
|
||||
config.properties[key] = value
|
||||
config.lock.Unlock()
|
||||
}
|
||||
|
||||
func (config *mapBasedProperties) GetInt(key string) int {
|
||||
config.lock.RLock()
|
||||
// default 0
|
||||
value, _ := strconv.Atoi(config.properties[key])
|
||||
config.lock.RUnlock()
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
func (config *mapBasedProperties) SetInt(key string, value int) {
|
||||
config.lock.Lock()
|
||||
config.properties[key] = strconv.Itoa(value)
|
||||
config.lock.Unlock()
|
||||
}
|
||||
|
||||
// Dumps the configuration internal map into a string.
|
||||
func (config *mapBasedProperties) ToString() string {
|
||||
config.lock.RLock()
|
||||
ret := fmt.Sprintf("%s", config.properties)
|
||||
config.lock.RUnlock()
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// Returns the error message.
|
||||
func (configError *PropertyError) Error() string {
|
||||
return configError.message
|
||||
}
|
||||
|
||||
// Builds a new properties configuration structure
|
||||
func NewProperties() Properties {
|
||||
return &mapBasedProperties{
|
||||
properties: make(map[string]string),
|
||||
}
|
||||
}
|
||||
44
core/conf/properties_test.go
Normal file
44
core/conf/properties_test.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"zero/core/fs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestProperties(t *testing.T) {
|
||||
text := `app.name = test
|
||||
|
||||
app.program=app
|
||||
|
||||
# this is comment
|
||||
app.threads = 5`
|
||||
tmpfile, err := fs.TempFilenameWithText(text)
|
||||
assert.Nil(t, err)
|
||||
defer os.Remove(tmpfile)
|
||||
|
||||
props, err := LoadProperties(tmpfile)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "test", props.GetString("app.name"))
|
||||
assert.Equal(t, "app", props.GetString("app.program"))
|
||||
assert.Equal(t, 5, props.GetInt("app.threads"))
|
||||
}
|
||||
|
||||
func TestSetString(t *testing.T) {
|
||||
key := "a"
|
||||
value := "the value of a"
|
||||
props := NewProperties()
|
||||
props.SetString(key, value)
|
||||
assert.Equal(t, value, props.GetString(key))
|
||||
}
|
||||
|
||||
func TestSetInt(t *testing.T) {
|
||||
key := "a"
|
||||
value := 101
|
||||
props := NewProperties()
|
||||
props.SetInt(key, value)
|
||||
assert.Equal(t, value, props.GetInt(key))
|
||||
}
|
||||
Reference in New Issue
Block a user