mirror of https://github.com/ethereum/go-ethereum
The code in config.go is unused. The main reason for removing it is to get rid github.com/rakyll/goini in Godeps (it has no license).pull/1515/head
parent
bfbcfbe4a9
commit
fd2356c620
@ -1,2 +0,0 @@ |
||||
language: go |
||||
go: 1.2 |
@ -1,144 +0,0 @@ |
||||
# globalconf |
||||
|
||||
[![Build Status](https://travis-ci.org/rakyll/globalconf.png?branch=master)](https://travis-ci.org/rakyll/globalconf) |
||||
|
||||
Effortlessly persist/retrieve flags of your Golang programs. If you need global configuration instead of requiring user always to set command line flags, you are looking at the right package. `globalconf` allows your users to not only provide flags, but config files and environment variables as well. |
||||
|
||||
## Usage |
||||
|
||||
~~~ go |
||||
import "github.com/rakyll/globalconf" |
||||
~~~ |
||||
|
||||
### Loading a config file |
||||
|
||||
By default, globalconf provides you a config file under `~/.config/<yourappname>/config.ini`. |
||||
|
||||
~~~ go |
||||
globalconf.New("appname") // loads from ~/.config/<appname>/config.ini |
||||
~~~ |
||||
|
||||
If you don't prefer the default location you can load from a specified path as well. |
||||
|
||||
~~~ go |
||||
globalconf.NewWithOptions(&globalconf.Options{ |
||||
Filename: "/path/to/config/file", |
||||
}) |
||||
~~~ |
||||
|
||||
You may like to override configuration with env variables. See "Environment variables" header to see how to it works. |
||||
|
||||
~~~ go |
||||
globalconf.NewWithOptions(&globalconf.Options{ |
||||
Filename: "/path/to/config/file", |
||||
EnvPrefix: "APPCONF_", |
||||
}) |
||||
~~~ |
||||
|
||||
### Parsing flag values |
||||
|
||||
`globalconf` populates flags with data in the config file if they are not already set. |
||||
|
||||
~~~ go |
||||
var ( |
||||
flagName = flag.String("name", "", "Name of the person.") |
||||
flagAddress = flag.String("addr", "", "Address of the person.") |
||||
) |
||||
~~~ |
||||
|
||||
Assume the configuration file to be loaded contains the following lines. |
||||
|
||||
name = Burcu |
||||
addr = Brandschenkestrasse 110, 8002 |
||||
|
||||
And your program is being started, `$ myapp -name=Jane` |
||||
~~~ go |
||||
conf, err := globalconf.New("myapp") |
||||
conf.ParseAll() |
||||
~~~ |
||||
|
||||
`*flagName` is going to be equal to `Jane`, whereas `*flagAddress` is `Brandschenkestrasse 110, 8002`, what is provided in the configuration file. |
||||
|
||||
### Custom flag sets |
||||
|
||||
Custom flagsets are supported, but required registration before parse is done. The default flagset `flag.CommandLine` is automatically registered. |
||||
|
||||
~~~ go |
||||
globalconf.Register("termopts", termOptsFlagSet) |
||||
conf.ParseAll() // parses command line and all registered flag sets |
||||
~~~ |
||||
|
||||
Custom flagset values should be provided in their own segment. Getting back to the sample ini config file, termopts values will have their own segment. |
||||
|
||||
name = Burcu |
||||
addr = Brandschenkestrasse 110, 8002 |
||||
|
||||
[termopts] |
||||
color = true |
||||
background = ff0000 |
||||
|
||||
### Environment variables |
||||
|
||||
If an EnvPrefix is provided, environment variables will take precedence over values in the configuration file. |
||||
Set the `EnvPrefix` option when calling `globalconf.NewWithOptions`. |
||||
An `EnvPrefix` will only be used if it is a non-empty string. |
||||
Command line flags will override the environment variables. |
||||
|
||||
~~~ go |
||||
opts := globalconf.Options{ |
||||
EnvPrefix: "MYAPP_", |
||||
Filename: "/path/to/config", |
||||
} |
||||
conf, err := globalconf.NewWithOptions(&opts) |
||||
conf.ParseAll() |
||||
~~~ |
||||
|
||||
With environment variables: |
||||
|
||||
APPCONF_NAME = Burcu |
||||
|
||||
and configuration: |
||||
|
||||
name = Jane |
||||
addr = Brandschenkestrasse 110, 8002 |
||||
|
||||
`name` will be set to "burcu" and `addr` will be set to "Brandschenkestrasse 110, 8002". |
||||
|
||||
### Modifying stored flags |
||||
|
||||
Modifications are persisted as long as you set a new flag and your GlobalConf |
||||
object was configured with a filename. |
||||
|
||||
~~~ go |
||||
f := &flag.Flag{Name: "name", Value: val} |
||||
conf.Set("", f) // if you are modifying a command line flag |
||||
|
||||
f := &flag.Flag{Name: "color", Value: val} |
||||
conf.Set("termopts", color) // if you are modifying a custom flag set flag |
||||
~~~ |
||||
|
||||
### Deleting stored flags |
||||
|
||||
Like Set, Deletions are persisted as long as you delete a flag's value and your |
||||
GlobalConf object was configured with a filename. |
||||
|
||||
~~~ go |
||||
conf.Delete("", "name") // removes command line flag "name"s value from config |
||||
conf.Delete("termopts", "color") // removes "color"s value from the custom flag set |
||||
~~~ |
||||
|
||||
## License |
||||
|
||||
Copyright 2014 Google Inc. All Rights Reserved. |
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
you may not use this file except in compliance with the License. |
||||
You may obtain a copy of the License at |
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
||||
Unless required by applicable law or agreed to in writing, software |
||||
distributed under the License is distributed on an "AS IS" BASIS, |
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
See the License for the specific language governing permissions and |
||||
limitations under the License. ![Analytics](https://ga-beacon.appspot.com/UA-46881978-1/globalconf?pixel) |
@ -1,179 +0,0 @@ |
||||
package globalconf |
||||
|
||||
import ( |
||||
"flag" |
||||
"io/ioutil" |
||||
"os" |
||||
"os/user" |
||||
"path" |
||||
"strings" |
||||
|
||||
ini "github.com/rakyll/goini" |
||||
) |
||||
|
||||
const ( |
||||
defaultConfigFileName = "config.ini" |
||||
) |
||||
|
||||
var flags map[string]*flag.FlagSet = make(map[string]*flag.FlagSet) |
||||
|
||||
// Represents a GlobalConf context.
|
||||
type GlobalConf struct { |
||||
Filename string |
||||
EnvPrefix string |
||||
dict *ini.Dict |
||||
} |
||||
|
||||
type Options struct { |
||||
Filename string |
||||
EnvPrefix string |
||||
} |
||||
|
||||
// NewWithOptions creates a GlobalConf from the provided
|
||||
// Options. The caller is responsible for creating any
|
||||
// referenced config files.
|
||||
func NewWithOptions(opts *Options) (g *GlobalConf, err error) { |
||||
Register("", flag.CommandLine) |
||||
|
||||
var dict ini.Dict |
||||
if opts.Filename != "" { |
||||
dict, err = ini.Load(opts.Filename) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
} else { |
||||
dict = make(ini.Dict, 0) |
||||
} |
||||
|
||||
return &GlobalConf{ |
||||
Filename: opts.Filename, |
||||
EnvPrefix: opts.EnvPrefix, |
||||
dict: &dict, |
||||
}, nil |
||||
} |
||||
|
||||
// Opens/creates a config file for the specified appName.
|
||||
// The path to config file is ~/.config/appName/config.ini.
|
||||
func New(appName string) (g *GlobalConf, err error) { |
||||
var u *user.User |
||||
if u, err = user.Current(); u == nil { |
||||
return |
||||
} |
||||
// Create config file's directory.
|
||||
dirPath := path.Join(u.HomeDir, ".config", appName) |
||||
if err = os.MkdirAll(dirPath, 0755); err != nil { |
||||
return |
||||
} |
||||
// Touch a config file if it doesn't exit.
|
||||
filePath := path.Join(dirPath, defaultConfigFileName) |
||||
if _, err = os.Stat(filePath); err != nil { |
||||
if !os.IsNotExist(err) { |
||||
return |
||||
} |
||||
// create file
|
||||
if err = ioutil.WriteFile(filePath, []byte{}, 0644); err != nil { |
||||
return |
||||
} |
||||
} |
||||
opts := Options{Filename: filePath} |
||||
return NewWithOptions(&opts) |
||||
} |
||||
|
||||
// Sets a flag's value and persists the changes to the disk.
|
||||
func (g *GlobalConf) Set(flagSetName string, f *flag.Flag) error { |
||||
g.dict.SetString(flagSetName, f.Name, f.Value.String()) |
||||
if g.Filename != "" { |
||||
return ini.Write(g.Filename, g.dict) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Deletes a flag from config file and persists the changes
|
||||
// to the disk.
|
||||
func (g *GlobalConf) Delete(flagSetName, flagName string) error { |
||||
g.dict.Delete(flagSetName, flagName) |
||||
if g.Filename != "" { |
||||
return ini.Write(g.Filename, g.dict) |
||||
} |
||||
return nil |
||||
} |
||||
|
||||
// Parses the config file for the provided flag set.
|
||||
// If the flags are already set, values are overwritten
|
||||
// by the values in the config file. Defaults are not set
|
||||
// if the flag is not in the file.
|
||||
func (g *GlobalConf) ParseSet(flagSetName string, set *flag.FlagSet) { |
||||
set.VisitAll(func(f *flag.Flag) { |
||||
val := getEnv(g.EnvPrefix, flagSetName, f.Name) |
||||
if val != "" { |
||||
set.Set(f.Name, val) |
||||
return |
||||
} |
||||
|
||||
val, found := g.dict.GetString(flagSetName, f.Name) |
||||
if found { |
||||
set.Set(f.Name, val) |
||||
} |
||||
}) |
||||
} |
||||
|
||||
// Parses all the registered flag sets, including the command
|
||||
// line set and sets values from the config file if they are
|
||||
// not already set.
|
||||
func (g *GlobalConf) Parse() { |
||||
for name, set := range flags { |
||||
alreadySet := make(map[string]bool) |
||||
set.Visit(func(f *flag.Flag) { |
||||
alreadySet[f.Name] = true |
||||
}) |
||||
set.VisitAll(func(f *flag.Flag) { |
||||
// if not already set, set it from dict if exists
|
||||
if alreadySet[f.Name] { |
||||
return |
||||
} |
||||
|
||||
val := getEnv(g.EnvPrefix, name, f.Name) |
||||
if val != "" { |
||||
set.Set(f.Name, val) |
||||
return |
||||
} |
||||
|
||||
val, found := g.dict.GetString(name, f.Name) |
||||
if found { |
||||
set.Set(f.Name, val) |
||||
} |
||||
}) |
||||
} |
||||
} |
||||
|
||||
// Parses command line flags and then, all of the registered
|
||||
// flag sets with the values provided in the config file.
|
||||
func (g *GlobalConf) ParseAll() { |
||||
if !flag.Parsed() { |
||||
flag.Parse() |
||||
} |
||||
g.Parse() |
||||
} |
||||
|
||||
// Looks up variable in environment
|
||||
func getEnv(envPrefix, flagSetName, flagName string) string { |
||||
// If we haven't set an EnvPrefix, don't lookup vals in the ENV
|
||||
if envPrefix == "" { |
||||
return "" |
||||
} |
||||
// Append a _ to flagSetName if it exists.
|
||||
if flagSetName != "" { |
||||
flagSetName += "_" |
||||
} |
||||
flagName = strings.Replace(flagName, ".", "_", -1) |
||||
flagName = strings.Replace(flagName, "-", "_", -1) |
||||
envKey := strings.ToUpper(envPrefix + flagSetName + flagName) |
||||
return os.Getenv(envKey) |
||||
} |
||||
|
||||
// Registers a flag set to be parsed. Register all flag sets
|
||||
// before calling this function. flag.CommandLine is automatically
|
||||
// registered.
|
||||
func Register(flagSetName string, set *flag.FlagSet) { |
||||
flags[flagSetName] = set |
||||
} |
@ -1,267 +0,0 @@ |
||||
package globalconf |
||||
|
||||
import ( |
||||
"flag" |
||||
"io/ioutil" |
||||
"os" |
||||
"testing" |
||||
) |
||||
|
||||
const envTestPrefix = "CONFTEST_" |
||||
|
||||
func TestNewWithOptionsNoFilename(t *testing.T) { |
||||
opts := Options{EnvPrefix: envTestPrefix} |
||||
|
||||
os.Setenv(envTestPrefix+"D", "EnvD") |
||||
|
||||
flagD := flag.String("d", "default", "") |
||||
flagE := flag.Bool("e", true, "") |
||||
|
||||
conf, err := NewWithOptions(&opts) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
conf.ParseAll() |
||||
|
||||
if *flagD != "EnvD" { |
||||
t.Errorf("flagD found %v, expected 'EnvD'", *flagD) |
||||
} |
||||
if !*flagE { |
||||
t.Errorf("flagE found %v, expected true", *flagE) |
||||
} |
||||
} |
||||
|
||||
func TestParse_Global(t *testing.T) { |
||||
resetForTesting("") |
||||
|
||||
os.Setenv(envTestPrefix+"D", "EnvD") |
||||
os.Setenv(envTestPrefix+"E", "true") |
||||
os.Setenv(envTestPrefix+"F", "5.5") |
||||
|
||||
flagA := flag.Bool("a", false, "") |
||||
flagB := flag.Float64("b", 0.0, "") |
||||
flagC := flag.String("c", "", "") |
||||
|
||||
flagD := flag.String("d", "", "") |
||||
flagE := flag.Bool("e", false, "") |
||||
flagF := flag.Float64("f", 0.0, "") |
||||
|
||||
parse(t, "./testdata/global.ini", envTestPrefix) |
||||
if !*flagA { |
||||
t.Errorf("flagA found %v, expected true", *flagA) |
||||
} |
||||
if *flagB != 5.6 { |
||||
t.Errorf("flagB found %v, expected 5.6", *flagB) |
||||
} |
||||
if *flagC != "Hello world" { |
||||
t.Errorf("flagC found %v, expected 'Hello world'", *flagC) |
||||
} |
||||
if *flagD != "EnvD" { |
||||
t.Errorf("flagD found %v, expected 'EnvD'", *flagD) |
||||
} |
||||
if !*flagE { |
||||
t.Errorf("flagE found %v, expected true", *flagE) |
||||
} |
||||
if *flagF != 5.5 { |
||||
t.Errorf("flagF found %v, expected 5.5", *flagF) |
||||
} |
||||
} |
||||
|
||||
func TestParse_DashConversion(t *testing.T) { |
||||
resetForTesting("") |
||||
|
||||
flagFooBar := flag.String("foo-bar", "", "") |
||||
os.Setenv("PREFIX_FOO_BAR", "baz") |
||||
|
||||
opts := Options{EnvPrefix: "PREFIX_"} |
||||
conf, err := NewWithOptions(&opts) |
||||
if err != nil { |
||||
t.Fatal(err) |
||||
} |
||||
conf.ParseAll() |
||||
|
||||
if *flagFooBar != "baz" { |
||||
t.Errorf("flagFooBar found %v, expected 5.5", *flagFooBar) |
||||
} |
||||
} |
||||
|
||||
func TestParse_GlobalWithDottedFlagname(t *testing.T) { |
||||
resetForTesting("") |
||||
os.Setenv(envTestPrefix+"SOME_VALUE", "some-value") |
||||
flagSomeValue := flag.String("some.value", "", "") |
||||
|
||||
parse(t, "./testdata/global.ini", envTestPrefix) |
||||
if *flagSomeValue != "some-value" { |
||||
t.Errorf("flagSomeValue found %v, some-value expected", *flagSomeValue) |
||||
} |
||||
} |
||||
|
||||
func TestParse_GlobalOverwrite(t *testing.T) { |
||||
resetForTesting("-b=7.6") |
||||
flagB := flag.Float64("b", 0.0, "") |
||||
|
||||
parse(t, "./testdata/global.ini", "") |
||||
if *flagB != 7.6 { |
||||
t.Errorf("flagB found %v, expected 7.6", *flagB) |
||||
} |
||||
} |
||||
|
||||
func TestParse_Custom(t *testing.T) { |
||||
resetForTesting("") |
||||
|
||||
os.Setenv(envTestPrefix+"CUSTOM_E", "Hello Env") |
||||
|
||||
flagB := flag.Float64("b", 5.0, "") |
||||
|
||||
name := "custom" |
||||
custom := flag.NewFlagSet(name, flag.ExitOnError) |
||||
flagD := custom.String("d", "dd", "") |
||||
flagE := custom.String("e", "ee", "") |
||||
|
||||
Register(name, custom) |
||||
parse(t, "./testdata/custom.ini", envTestPrefix) |
||||
if *flagB != 5.0 { |
||||
t.Errorf("flagB found %v, expected 5.0", *flagB) |
||||
} |
||||
if *flagD != "Hello d" { |
||||
t.Errorf("flagD found %v, expected 'Hello d'", *flagD) |
||||
} |
||||
if *flagE != "Hello Env" { |
||||
t.Errorf("flagE found %v, expected 'Hello Env'", *flagE) |
||||
} |
||||
} |
||||
|
||||
func TestParse_CustomOverwrite(t *testing.T) { |
||||
resetForTesting("-b=6") |
||||
flagB := flag.Float64("b", 5.0, "") |
||||
|
||||
name := "custom" |
||||
custom := flag.NewFlagSet(name, flag.ExitOnError) |
||||
flagD := custom.String("d", "dd", "") |
||||
|
||||
Register(name, custom) |
||||
parse(t, "./testdata/custom.ini", "") |
||||
if *flagB != 6.0 { |
||||
t.Errorf("flagB found %v, expected 6.0", *flagB) |
||||
} |
||||
if *flagD != "Hello d" { |
||||
t.Errorf("flagD found %v, expected 'Hello d'", *flagD) |
||||
} |
||||
} |
||||
|
||||
func TestParse_GlobalAndCustom(t *testing.T) { |
||||
resetForTesting("") |
||||
flagA := flag.Bool("a", false, "") |
||||
flagB := flag.Float64("b", 0.0, "") |
||||
flagC := flag.String("c", "", "") |
||||
|
||||
name := "custom" |
||||
custom := flag.NewFlagSet(name, flag.ExitOnError) |
||||
flagD := custom.String("d", "", "") |
||||
|
||||
Register(name, custom) |
||||
parse(t, "./testdata/globalandcustom.ini", "") |
||||
if !*flagA { |
||||
t.Errorf("flagA found %v, expected true", *flagA) |
||||
} |
||||
if *flagB != 5.6 { |
||||
t.Errorf("flagB found %v, expected 5.6", *flagB) |
||||
} |
||||
if *flagC != "Hello world" { |
||||
t.Errorf("flagC found %v, expected 'Hello world'", *flagC) |
||||
} |
||||
if *flagD != "Hello d" { |
||||
t.Errorf("flagD found %v, expected 'Hello d'", *flagD) |
||||
} |
||||
} |
||||
|
||||
func TestParse_GlobalAndCustomOverwrite(t *testing.T) { |
||||
resetForTesting("-a=true", "-b=5", "-c=Hello") |
||||
flagA := flag.Bool("a", false, "") |
||||
flagB := flag.Float64("b", 0.0, "") |
||||
flagC := flag.String("c", "", "") |
||||
|
||||
name := "custom" |
||||
custom := flag.NewFlagSet(name, flag.ExitOnError) |
||||
flagD := custom.String("d", "", "") |
||||
|
||||
Register(name, custom) |
||||
parse(t, "./testdata/globalandcustom.ini", "") |
||||
if !*flagA { |
||||
t.Errorf("flagA found %v, expected true", *flagA) |
||||
} |
||||
if *flagB != 5.0 { |
||||
t.Errorf("flagB found %v, expected 5.0", *flagB) |
||||
} |
||||
if *flagC != "Hello" { |
||||
t.Errorf("flagC found %v, expected 'Hello'", *flagC) |
||||
} |
||||
if *flagD != "Hello d" { |
||||
t.Errorf("flagD found %v, expected 'Hello d'", *flagD) |
||||
} |
||||
} |
||||
|
||||
func TestSet(t *testing.T) { |
||||
resetForTesting() |
||||
file, _ := ioutil.TempFile("", "") |
||||
conf := parse(t, file.Name(), "") |
||||
conf.Set("", &flag.Flag{Name: "a", Value: newFlagValue("test")}) |
||||
|
||||
flagA := flag.String("a", "", "") |
||||
parse(t, file.Name(), "") |
||||
if *flagA != "test" { |
||||
t.Errorf("flagA found %v, expected 'test'", *flagA) |
||||
} |
||||
} |
||||
|
||||
func TestDelete(t *testing.T) { |
||||
resetForTesting() |
||||
file, _ := ioutil.TempFile("", "") |
||||
conf := parse(t, file.Name(), "") |
||||
conf.Set("", &flag.Flag{Name: "a", Value: newFlagValue("test")}) |
||||
conf.Delete("", "a") |
||||
|
||||
flagA := flag.String("a", "", "") |
||||
parse(t, file.Name(), "") |
||||
if *flagA != "" { |
||||
t.Errorf("flagNewA found %v, expected ''", *flagA) |
||||
} |
||||
} |
||||
|
||||
func parse(t *testing.T, filename, envPrefix string) *GlobalConf { |
||||
opts := Options{ |
||||
Filename: filename, |
||||
EnvPrefix: envPrefix, |
||||
} |
||||
conf, err := NewWithOptions(&opts) |
||||
if err != nil { |
||||
t.Error(err) |
||||
} |
||||
conf.ParseAll() |
||||
return conf |
||||
} |
||||
|
||||
// Resets os.Args and the default flag set.
|
||||
func resetForTesting(args ...string) { |
||||
os.Clearenv() |
||||
|
||||
os.Args = append([]string{"cmd"}, args...) |
||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError) |
||||
} |
||||
|
||||
type flagValue struct { |
||||
str string |
||||
} |
||||
|
||||
func (f *flagValue) String() string { |
||||
return f.str |
||||
} |
||||
|
||||
func (f *flagValue) Set(value string) error { |
||||
f.str = value |
||||
return nil |
||||
} |
||||
|
||||
func newFlagValue(val string) *flagValue { |
||||
return &flagValue{str: val} |
||||
} |
@ -1,2 +0,0 @@ |
||||
[custom] |
||||
d = Hello d |
@ -1,3 +0,0 @@ |
||||
a = true |
||||
b = 5.6 |
||||
c = Hello world |
6
Godeps/_workspace/src/github.com/rakyll/globalconf/testdata/globalandcustom.ini
generated
vendored
6
Godeps/_workspace/src/github.com/rakyll/globalconf/testdata/globalandcustom.ini
generated
vendored
@ -1,6 +0,0 @@ |
||||
a = true |
||||
b = 5.6 |
||||
c = Hello world |
||||
|
||||
[custom] |
||||
d = Hello d |
@ -1,8 +0,0 @@ |
||||
.*.swp |
||||
|
||||
*.[689] |
||||
[689].out |
||||
|
||||
_obj |
||||
_test |
||||
_testmain.go |
@ -1,7 +0,0 @@ |
||||
test: |
||||
go test
|
||||
|
||||
format: |
||||
gofmt -w *.go
|
||||
|
||||
.PHONY: format test |
@ -1,18 +0,0 @@ |
||||
# |
||||
# This is an example of ini file |
||||
# |
||||
|
||||
[Pizza] |
||||
|
||||
Ham = yes; |
||||
Mushrooms = TRUE; |
||||
Capres = 0; |
||||
Cheese = Non; |
||||
|
||||
|
||||
[Wine] |
||||
|
||||
Grape = Cabernet Sauvignon; |
||||
Year = 1989; |
||||
Country = Spain; |
||||
Alcohol = 12.5; |
@ -1,241 +0,0 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"bufio" |
||||
"bytes" |
||||
"fmt" |
||||
"io/ioutil" |
||||
"os" |
||||
"regexp" |
||||
"strconv" |
||||
"strings" |
||||
"unicode" |
||||
) |
||||
|
||||
type Dict map[string]map[string]string |
||||
|
||||
type Error string |
||||
|
||||
var ( |
||||
regDoubleQuote = regexp.MustCompile("^([^= \t]+)[ \t]*=[ \t]*\"([^\"]*)\"$") |
||||
regSingleQuote = regexp.MustCompile("^([^= \t]+)[ \t]*=[ \t]*'([^']*)'$") |
||||
regNoQuote = regexp.MustCompile("^([^= \t]+)[ \t]*=[ \t]*([^#;]+)") |
||||
regNoValue = regexp.MustCompile("^([^= \t]+)[ \t]*=[ \t]*([#;].*)?") |
||||
) |
||||
|
||||
func Load(filename string) (dict Dict, err error) { |
||||
file, err := os.Open(filename) |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
defer file.Close() |
||||
|
||||
dict = make(map[string]map[string]string) |
||||
reader := bufio.NewReader(file) |
||||
lineno := 0 |
||||
section := "" |
||||
dict[section] = make(map[string]string) |
||||
|
||||
for err == nil { |
||||
l, _, err := reader.ReadLine() |
||||
if err != nil { |
||||
break |
||||
} |
||||
lineno++ |
||||
if len(l) == 0 { |
||||
continue |
||||
} |
||||
line := strings.TrimFunc(string(l), unicode.IsSpace) |
||||
|
||||
for line[len(line)-1] == '\\' { |
||||
line = line[:len(line)-1] |
||||
l, _, err := reader.ReadLine() |
||||
if err != nil { |
||||
return nil, err |
||||
} |
||||
line += strings.TrimFunc(string(l), unicode.IsSpace) |
||||
} |
||||
|
||||
section, err = dict.parseLine(section, line) |
||||
if err != nil { |
||||
return nil, newError( |
||||
err.Error() + fmt.Sprintf("'%s:%d'.", filename, lineno)) |
||||
} |
||||
} |
||||
|
||||
return |
||||
} |
||||
|
||||
func Write(filename string, dict *Dict) error { |
||||
buffer := dict.format() |
||||
return ioutil.WriteFile(filename, buffer.Bytes(), 0644) |
||||
} |
||||
|
||||
func (e Error) Error() string { |
||||
return string(e) |
||||
} |
||||
func (dict Dict) parseLine(section, line string) (string, error) { |
||||
// commets
|
||||
if line[0] == '#' || line[0] == ';' { |
||||
return section, nil |
||||
} |
||||
|
||||
// section name
|
||||
if line[0] == '[' && line[len(line)-1] == ']' { |
||||
section := strings.TrimFunc(line[1:len(line)-1], unicode.IsSpace) |
||||
section = strings.ToLower(section) |
||||
dict[section] = make(map[string]string) |
||||
return section, nil |
||||
} |
||||
|
||||
// key = value
|
||||
if m := regDoubleQuote.FindAllStringSubmatch(line, 1); m != nil { |
||||
dict.add(section, m[0][1], m[0][2]) |
||||
return section, nil |
||||
} else if m = regSingleQuote.FindAllStringSubmatch(line, 1); m != nil { |
||||
dict.add(section, m[0][1], m[0][2]) |
||||
return section, nil |
||||
} else if m = regNoQuote.FindAllStringSubmatch(line, 1); m != nil { |
||||
dict.add(section, m[0][1], strings.TrimFunc(m[0][2], unicode.IsSpace)) |
||||
return section, nil |
||||
} else if m = regNoValue.FindAllStringSubmatch(line, 1); m != nil { |
||||
dict.add(section, m[0][1], "") |
||||
return section, nil |
||||
} |
||||
|
||||
return section, newError("iniparser: syntax error at ") |
||||
} |
||||
|
||||
func (dict Dict) add(section, key, value string) { |
||||
key = strings.ToLower(key) |
||||
dict[section][key] = value |
||||
} |
||||
|
||||
func (dict Dict) GetBool(section, key string) (bool, bool) { |
||||
sec, ok := dict[section] |
||||
if !ok { |
||||
return false, false |
||||
} |
||||
value, ok := sec[key] |
||||
if !ok { |
||||
return false, false |
||||
} |
||||
v := value[0] |
||||
if v == 'y' || v == 'Y' || v == '1' || v == 't' || v == 'T' { |
||||
return true, true |
||||
} |
||||
if v == 'n' || v == 'N' || v == '0' || v == 'f' || v == 'F' { |
||||
return false, true |
||||
} |
||||
return false, false |
||||
} |
||||
|
||||
func (dict Dict) SetBool(section, key string, value bool) { |
||||
dict.SetString(section, key, strconv.FormatBool(value)) |
||||
} |
||||
|
||||
func (dict Dict) GetString(section, key string) (string, bool) { |
||||
sec, ok := dict[section] |
||||
if !ok { |
||||
return "", false |
||||
} |
||||
value, ok := sec[key] |
||||
if !ok { |
||||
return "", false |
||||
} |
||||
return value, true |
||||
} |
||||
|
||||
func (dict Dict) SetString(section, key, value string) { |
||||
_, ok := dict[section] |
||||
if !ok { |
||||
dict[section] = make(map[string]string) |
||||
} |
||||
dict[section][key] = value |
||||
} |
||||
|
||||
func (dict Dict) GetInt(section, key string) (int, bool) { |
||||
sec, ok := dict[section] |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
value, ok := sec[key] |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
i, err := strconv.Atoi(value) |
||||
if err != nil { |
||||
return 0, false |
||||
} |
||||
return i, true |
||||
} |
||||
|
||||
func (dict Dict) SetInt(section, key string, value int) { |
||||
dict.SetString(section, key, strconv.FormatInt(int64(value), 10)) |
||||
} |
||||
|
||||
func (dict Dict) GetDouble(section, key string) (float64, bool) { |
||||
sec, ok := dict[section] |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
value, ok := sec[key] |
||||
if !ok { |
||||
return 0, false |
||||
} |
||||
d, err := strconv.ParseFloat(value, 64) |
||||
if err != nil { |
||||
return 0, false |
||||
} |
||||
return d, true |
||||
} |
||||
|
||||
func (dict Dict) SetDouble(section, key string, value float64) { |
||||
dict.SetString(section, key, strconv.FormatFloat(value, 'f', -1, 64)) |
||||
} |
||||
|
||||
func (dict Dict) Delete(section, key string) { |
||||
_, ok := dict[section] |
||||
if !ok { |
||||
return |
||||
} |
||||
delete(dict[section], key) |
||||
// If there are no items left in the section,
|
||||
// delete the section.
|
||||
if len(dict[section]) == 0 { |
||||
delete(dict, section) |
||||
} |
||||
} |
||||
|
||||
func (dict Dict) GetSections() []string { |
||||
size := len(dict) |
||||
sections := make([]string, size) |
||||
i := 0 |
||||
for section, _ := range dict { |
||||
sections[i] = section |
||||
i++ |
||||
} |
||||
return sections |
||||
} |
||||
|
||||
func (dict Dict) String() string { |
||||
return (*dict.format()).String() |
||||
} |
||||
|
||||
func (dict Dict) format() *bytes.Buffer { |
||||
var buffer bytes.Buffer |
||||
for section, vals := range dict { |
||||
if section != "" { |
||||
buffer.WriteString(fmt.Sprintf("[%s]\n", section)) |
||||
} |
||||
for key, val := range vals { |
||||
buffer.WriteString(fmt.Sprintf("%s = %s\n", key, val)) |
||||
} |
||||
buffer.WriteString("\n") |
||||
} |
||||
return &buffer |
||||
} |
||||
|
||||
func newError(message string) (e error) { |
||||
return Error(message) |
||||
} |
@ -1,169 +0,0 @@ |
||||
package ini |
||||
|
||||
import ( |
||||
"io/ioutil" |
||||
"testing" |
||||
) |
||||
|
||||
const ( |
||||
exampleStr = `key1 = true |
||||
|
||||
[section1] |
||||
key1 = value2 |
||||
key2 = 5 |
||||
key3 = 1.3 |
||||
|
||||
[section2] |
||||
key1 = 5 |
||||
|
||||
` |
||||
) |
||||
|
||||
var ( |
||||
dict Dict |
||||
err error |
||||
) |
||||
|
||||
func init() { |
||||
dict, err = Load("example.ini") |
||||
} |
||||
|
||||
func TestLoad(t *testing.T) { |
||||
if err != nil { |
||||
t.Error("Example: load error:", err) |
||||
} |
||||
} |
||||
|
||||
func TestWrite(t *testing.T) { |
||||
d, err := Load("empty.ini") |
||||
if err != nil { |
||||
t.Error("Example: load error:", err) |
||||
} |
||||
d.SetString("", "key", "value") |
||||
tempFile, err := ioutil.TempFile("", "") |
||||
if err != nil { |
||||
t.Error("Write: Couldn't create temp file.", err) |
||||
} |
||||
err = Write(tempFile.Name(), &d) |
||||
if err != nil { |
||||
t.Error("Write: Couldn't write to temp config file.", err) |
||||
} |
||||
contents, err := ioutil.ReadFile(tempFile.Name()) |
||||
if err != nil { |
||||
t.Error("Write: Couldn't read from the temp config file.", err) |
||||
} |
||||
if string(contents) != "key = value\n\n" { |
||||
t.Error("Write: Contents of the config file doesn't match the expected.") |
||||
} |
||||
} |
||||
|
||||
func TestGetBool(t *testing.T) { |
||||
b, found := dict.GetBool("pizza", "ham") |
||||
if !found || !b { |
||||
t.Error("Example: parse error for key ham of section pizza.") |
||||
} |
||||
b, found = dict.GetBool("pizza", "mushrooms") |
||||
if !found || !b { |
||||
t.Error("Example: parse error for key mushrooms of section pizza.") |
||||
} |
||||
b, found = dict.GetBool("pizza", "capres") |
||||
if !found || b { |
||||
t.Error("Example: parse error for key capres of section pizza.") |
||||
} |
||||
b, found = dict.GetBool("pizza", "cheese") |
||||
if !found || b { |
||||
t.Error("Example: parse error for key cheese of section pizza.") |
||||
} |
||||
} |
||||
|
||||
func TestGetStringIntAndDouble(t *testing.T) { |
||||
str, found := dict.GetString("wine", "grape") |
||||
if !found || str != "Cabernet Sauvignon" { |
||||
t.Error("Example: parse error for key grape of section wine.") |
||||
} |
||||
i, found := dict.GetInt("wine", "year") |
||||
if !found || i != 1989 { |
||||
t.Error("Example: parse error for key year of section wine.") |
||||
} |
||||
str, found = dict.GetString("wine", "country") |
||||
if !found || str != "Spain" { |
||||
t.Error("Example: parse error for key grape of section wine.") |
||||
} |
||||
d, found := dict.GetDouble("wine", "alcohol") |
||||
if !found || d != 12.5 { |
||||
t.Error("Example: parse error for key grape of section wine.") |
||||
} |
||||
} |
||||
|
||||
func TestSetBoolAndStringAndIntAndDouble(t *testing.T) { |
||||
dict.SetBool("pizza", "ham", false) |
||||
b, found := dict.GetBool("pizza", "ham") |
||||
if !found || b { |
||||
t.Error("Example: bool set error for key ham of section pizza.") |
||||
} |
||||
dict.SetString("pizza", "ham", "no") |
||||
n, found := dict.GetString("pizza", "ham") |
||||
if !found || n != "no" { |
||||
t.Error("Example: string set error for key ham of section pizza.") |
||||
} |
||||
dict.SetInt("wine", "year", 1978) |
||||
i, found := dict.GetInt("wine", "year") |
||||
if !found || i != 1978 { |
||||
t.Error("Example: int set error for key year of section wine.") |
||||
} |
||||
dict.SetDouble("wine", "not-exists", 5.6) |
||||
d, found := dict.GetDouble("wine", "not-exists") |
||||
if !found || d != 5.6 { |
||||
t.Error("Example: float set error for not existing key for wine.") |
||||
} |
||||
} |
||||
|
||||
func TestDelete(t *testing.T) { |
||||
d, err := Load("empty.ini") |
||||
if err != nil { |
||||
t.Error("Example: load error:", err) |
||||
} |
||||
d.SetString("pizza", "ham", "yes") |
||||
d.Delete("pizza", "ham") |
||||
_, found := d.GetString("pizza", "ham") |
||||
if found { |
||||
t.Error("Example: delete error for key ham of section pizza.") |
||||
} |
||||
if len(d.GetSections()) > 1 { |
||||
t.Error("Only a single section should exist after deletion.") |
||||
} |
||||
} |
||||
|
||||
func TestGetNotExist(t *testing.T) { |
||||
_, found := dict.GetString("not", "exist") |
||||
if found { |
||||
t.Error("There is no key exist of section not.") |
||||
} |
||||
} |
||||
|
||||
func TestGetSections(t *testing.T) { |
||||
sections := dict.GetSections() |
||||
if len(sections) != 3 { |
||||
t.Error("The number of sections is wrong:", len(sections)) |
||||
} |
||||
for _, section := range sections { |
||||
if section != "" && section != "pizza" && section != "wine" { |
||||
t.Errorf("Section '%s' should not be exist.", section) |
||||
} |
||||
} |
||||
} |
||||
|
||||
func TestString(t *testing.T) { |
||||
d, err := Load("empty.ini") |
||||
if err != nil { |
||||
t.Error("Example: load error:", err) |
||||
} |
||||
d.SetBool("", "key1", true) |
||||
d.SetString("section1", "key1", "value2") |
||||
d.SetInt("section1", "key2", 5) |
||||
d.SetDouble("section1", "key3", 1.3) |
||||
d.SetDouble("section2", "key1", 5.0) |
||||
if d.String() != exampleStr { |
||||
t.Errorf("Dict cannot be stringified as expected.") |
||||
} |
||||
} |
@ -1,83 +0,0 @@ |
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package common |
||||
|
||||
import ( |
||||
"flag" |
||||
"fmt" |
||||
"os" |
||||
|
||||
"github.com/rakyll/globalconf" |
||||
) |
||||
|
||||
// Config struct
|
||||
type ConfigManager struct { |
||||
ExecPath string |
||||
Debug bool |
||||
Diff bool |
||||
DiffType string |
||||
Paranoia bool |
||||
VmType int |
||||
|
||||
conf *globalconf.GlobalConf |
||||
} |
||||
|
||||
// Read config
|
||||
//
|
||||
// Initialize Config from Config File
|
||||
func ReadConfig(ConfigFile string, Datadir string, EnvPrefix string) *ConfigManager { |
||||
if !FileExist(ConfigFile) { |
||||
// create ConfigFile if it does not exist, otherwise
|
||||
// globalconf will panic when trying to persist flags.
|
||||
fmt.Printf("config file '%s' doesn't exist, creating it\n", ConfigFile) |
||||
os.Create(ConfigFile) |
||||
} |
||||
g, err := globalconf.NewWithOptions(&globalconf.Options{ |
||||
Filename: ConfigFile, |
||||
EnvPrefix: EnvPrefix, |
||||
}) |
||||
if err != nil { |
||||
fmt.Println(err) |
||||
} else { |
||||
g.ParseAll() |
||||
} |
||||
cfg := &ConfigManager{ExecPath: Datadir, Debug: true, conf: g, Paranoia: true} |
||||
return cfg |
||||
} |
||||
|
||||
// provides persistence for flags
|
||||
func (c *ConfigManager) Save(key string, value interface{}) { |
||||
f := &flag.Flag{Name: key, Value: newConfValue(value)} |
||||
c.conf.Set("", f) |
||||
} |
||||
|
||||
func (c *ConfigManager) Delete(key string) { |
||||
c.conf.Delete("", key) |
||||
} |
||||
|
||||
// private type implementing flag.Value
|
||||
type confValue struct { |
||||
value string |
||||
} |
||||
|
||||
// generic constructor to allow persising non-string values directly
|
||||
func newConfValue(value interface{}) *confValue { |
||||
return &confValue{fmt.Sprintf("%v", value)} |
||||
} |
||||
|
||||
func (self confValue) String() string { return self.value } |
||||
func (self confValue) Set(s string) error { self.value = s; return nil } |
Loading…
Reference in new issue