2
0

add comments

This commit is contained in:
eschao 2017-12-12 16:00:34 +08:00
parent e7703f94bf
commit 4bf6f65377
9 changed files with 203 additions and 4 deletions

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package cli
import (
@ -11,10 +26,13 @@ import (
"github.com/eschao/config/utils"
)
// anyValue wraps a reflect.Value object and implements flag.Value interface
// the reflect.Value could be Bool, String, Int, Uint and Float
type anyValue struct {
any reflect.Value
}
// newAnyValue creates an anyValue object
func newAnyValue(v reflect.Value) *anyValue {
return &anyValue{any: v}
}
@ -78,6 +96,8 @@ func (this *anyValue) Set(v string) error {
return nil
}
// sliceValue wraps a reflect.Value object and implements flag.Value interface
// the reflect.Value could only be a sliceable type
type sliceValue struct {
value reflect.Value
separator string
@ -99,19 +119,26 @@ func (this *sliceValue) Set(v string) error {
return utils.SetValueWithSlice(this.value, v, sp)
}
// errorHanling is a global flag.ErrorHandling
var errorHandling = flag.ExitOnError
// UsageFunc defines a callback function for printing command usage
type UsageFunc func(*Command) func()
// usageHandler is a global UsageFunc callback, default is nil which means it
// will use default flag.Usage function
var usageHandler UsageFunc = nil
// Command defines a command line structure
type Command struct {
Name string
FlagSet *flag.FlagSet
Usage string
SubCommands map[string]*Command
Name string // command name
FlagSet *flag.FlagSet // command arguments
Usage string // command usage description
SubCommands map[string]*Command // sub-commands
}
// New creates a command with given name, the command will use default
// ErrorHandling: flag.ExitOnError and default usage function: flag.Usage
func New(name string) *Command {
cmd := Command{
Name: name,
@ -122,6 +149,8 @@ func New(name string) *Command {
return &cmd
}
// NewWith creates a command with given name, error handling and customized
// usage function
func NewWith(name string, errHandling flag.ErrorHandling,
usageHandling UsageFunc) *Command {
errorHandling = errHandling
@ -139,6 +168,9 @@ func NewWith(name string, errHandling flag.ErrorHandling,
return &cmd
}
// Init analyzes the given structure interface, extracts cli definitions from
// its tag and installs command flagset by flag APIs. The interface must be a
// structure pointer, otherwise will return an error
func (this *Command) Init(i interface{}) error {
ptrRef := reflect.ValueOf(i)
@ -156,6 +188,7 @@ func (this *Command) Init(i interface{}) error {
return this.parseValue(valueOfStruct)
}
// parseValue parses a reflect.Value object and extracts cli definitions
func (this *Command) parseValue(v reflect.Value) error {
typeOfStruct := v.Type()
var err error
@ -181,6 +214,7 @@ func (this *Command) parseValue(v reflect.Value) error {
return err
}
// addFlag installs a command flag variable by flag API
func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
cmdTag, ok := f.Tag.Lookup("cli")
if !ok || cmdTag == "" {
@ -226,6 +260,7 @@ func (this *Command) addFlag(v reflect.Value, f reflect.StructField) error {
return nil
}
// createSubCommand creates sub-commands
func (this *Command) createSubCommand(tag reflect.StructTag) *Command {
cmdTag, ok := tag.Lookup("cli")
if !ok || cmdTag == "" {
@ -253,6 +288,8 @@ func (this *Command) createSubCommand(tag reflect.StructTag) *Command {
return &cmd
}
// Parse parses values from command line and save values into given structure.
// The Init(interface{}) function must be called before parsing
func (this *Command) Parse(args []string) error {
if err := this.FlagSet.Parse(args); err != nil {
return err

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package cli
import (

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package config
import (
@ -15,6 +30,7 @@ import (
"gopkg.in/yaml.v2"
)
// Default configuration file
const (
DefaultJSONConfig = "config.json"
DefaultYamlConfig = "config.yaml"
@ -27,6 +43,10 @@ const (
PropConfigType = "properties"
)
// ParseDefault parses the given structure, extract default value from its tag
// and set structure with these values.
// Normally, ParseDefault should be called before any other parsing functions
// to set default values for structure.
func ParseDefault(i interface{}) error {
ptrRef := reflect.ValueOf(i)
@ -108,10 +128,13 @@ func parseValue(v reflect.Value) error {
return err
}
// ParseEnv parses given structure interface and set it with corresponding
// environment values
func ParseEnv(i interface{}) error {
return env.ParseWith(i, "")
}
// ParseCli parses given structure interface and set it with command line input
func ParseCli(i interface{}) error {
cli := cli.New(os.Args[0])
if err := cli.Init(i); err != nil {
@ -123,11 +146,19 @@ func ParseCli(i interface{}) error {
return nil
}
// ParseConfig parses given structure interface and set it with default
// configuration file.
// configFlag is a command line flag to tell where to locate configure file.
// If the config file doesn't exist, the default config fill will be searched
// under the same folder with the fixed order: config.json, config.yaml and
// config.properties
func ParseConfig(i interface{}, configFlag string) error {
configFile := flag.String(configFlag, "", "Specifiy configuration file")
return ParseConfigFile(i, *configFile)
}
// ParseConfigFile parses given structure interface and set its value with
// the specified configuration file
func ParseConfigFile(i interface{}, configFile string) error {
var err error
if configFile == "" {
@ -156,6 +187,7 @@ func ParseConfigFile(i interface{}, configFile string) error {
return nil
}
// parseJSON parses JSON file and set structure with its value
func parseJSON(i interface{}, jsonFile string) error {
raw, err := ioutil.ReadFile(jsonFile)
if err != nil {
@ -165,6 +197,7 @@ func parseJSON(i interface{}, jsonFile string) error {
return json.Unmarshal(raw, i)
}
// parseYaml parses Yaml file and set structure with its value
func parseYaml(i interface{}, yamlFile string) error {
raw, err := ioutil.ReadFile(yamlFile)
if err != nil {
@ -174,10 +207,14 @@ func parseYaml(i interface{}, yamlFile string) error {
return yaml.Unmarshal(raw, i)
}
// parseProp parses Properties file and set structure with its value
func parseProp(i interface{}, propFile string) error {
return fmt.Errorf("Properties config has not implemented!")
}
// getDefaultConfigFile returns a existing default config file. The checking
// order is fixed with beginning from: config.json to config.yaml and
// config.properties
func getDefaultConfigFile() (string, error) {
exe, err := os.Executable()
if err != nil {
@ -207,6 +244,8 @@ func getDefaultConfigFile() (string, error) {
return "", fmt.Errorf("No default config file found in path: %s", path)
}
// getConfigFileType analyzes config file extension name and return
// corresponding type: json, yaml or properties
func getConfigFileType(configFile string) (string, error) {
ext := filepath.Ext(configFile)
if ext == ".json" {

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package config
import (

33
env/env.go vendored
View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package env
import (
@ -8,10 +23,25 @@ import (
"github.com/eschao/config/utils"
)
// Parse parses given structure interface, extracts environment definitions
// from its tag and sets structure with defined environement variables
func Parse(i interface{}) error {
return ParseWith(i, "")
}
// ParseWith parses with given structure interface and environment name prefix
// It is normally used in nested structure.
// For example: we have such structure
// type Database struct {
// Host string `env:"HOST"`
// }
// type Server struct {
// Server string `env:"SERVER"`
// DB Database `env:"DB_"`
// }
// The Server.DB.Host will be mapped to environment variable: DB_HOST which is
// concatenated from DB tag in Server struct and Host tag in Database struct
func ParseWith(i interface{}, prefix string) error {
ptrRef := reflect.ValueOf(i)
@ -29,6 +59,7 @@ func ParseWith(i interface{}, prefix string) error {
return parseValue(valueOfStruct, prefix)
}
// parseValue parses a reflect.Value object
func parseValue(v reflect.Value, prefix string) error {
typeOfStruct := v.Type()
var err error
@ -55,6 +86,7 @@ func parseValue(v reflect.Value, prefix string) error {
return err
}
// getEnvValue get environment value
func getEnvValue(envName string, f reflect.StructField) (string, bool) {
//fmt.Printf("Lookup ENV: %s\n", envName)
envValue, ok := os.LookupEnv(envName)
@ -65,6 +97,7 @@ func getEnvValue(envName string, f reflect.StructField) (string, bool) {
return envValue, ok
}
// setFieldValue sets a reflect.Value with environment value
func setFieldValue(v reflect.Value, f reflect.StructField, prefix string) error {
envName := f.Tag.Get("env")
if envName == "" {

15
env/env_test.go vendored
View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package env
import (

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package test
type DBConfig struct {

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package utils
import (

View File

@ -1,3 +1,18 @@
/*
* Copyright (C) 2017 eschao <esc.chao@gmail.com>
*
* 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.
*/
package utils
import (