Support loading config from different formats (#228)

This commit is contained in:
Monsoon 2021-02-12 22:12:58 +08:00 committed by GitHub
parent 96d7156eba
commit 1b87264c53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 112 additions and 30 deletions

View file

@ -22,9 +22,13 @@ type ConfigFormat struct {
// ConfigLoader is a utility to load Xray config from external source.
type ConfigLoader func(input interface{}) (*Config, error)
// ConfigBuilder is a builder to build core.Config from filenames and formats
type ConfigBuilder func(files []string, formats []string) (*Config, error)
var (
configLoaderByName = make(map[string]*ConfigFormat)
configLoaderByExt = make(map[string]*ConfigFormat)
configLoaderByName = make(map[string]*ConfigFormat)
configLoaderByExt = make(map[string]*ConfigFormat)
ConfigBuilderForFiles ConfigBuilder
)
// RegisterConfigLoader add a new ConfigLoader.
@ -46,6 +50,21 @@ func RegisterConfigLoader(format *ConfigFormat) error {
return nil
}
func GetFormatByExtension(ext string) string {
switch strings.ToLower(ext) {
case "pb", "protobuf":
return "protobuf"
case "yaml", "yml":
return "yaml"
case "toml":
return "toml"
case "json":
return "json"
default:
return ""
}
}
func getExtension(filename string) string {
idx := strings.LastIndexByte(filename, '.')
if idx == -1 {
@ -54,23 +73,48 @@ func getExtension(filename string) string {
return filename[idx+1:]
}
// LoadConfig loads config with given format from given source.
// input accepts 2 different types:
// * []string slice of multiple filename/url(s) to open to read
// * io.Reader that reads a config content (the original way)
func LoadConfig(formatName string, filename string, input interface{}) (*Config, error) {
ext := getExtension(filename)
if len(ext) > 0 {
if f, found := configLoaderByExt[ext]; found {
return f.Loader(input)
func getFormat(filename string) string {
return GetFormatByExtension(getExtension(filename))
}
func LoadConfig(formatName string, input interface{}) (*Config, error) {
switch v := input.(type) {
case cmdarg.Arg:
formats := make([]string, len(v))
hasProtobuf := false
for i, file := range v {
f := getFormat(file)
if f == "" {
f = formatName
}
if f == "protobuf" {
hasProtobuf = true
}
formats[i] = f
}
// only one protobuf config file is allowed
if hasProtobuf {
if len(v) == 1 {
return configLoaderByName["protobuf"].Loader(v)
} else {
return nil, newError("Only one protobuf config file is allowed").AtWarning()
}
}
// to avoid import cycle
return ConfigBuilderForFiles(v, formats)
case io.Reader:
if f, found := configLoaderByName[formatName]; found {
return f.Loader(v)
} else {
return nil, newError("Unable to load config in", formatName).AtWarning()
}
}
if f, found := configLoaderByName[formatName]; found {
return f.Loader(input)
}
return nil, newError("Unable to load config in ", formatName).AtWarning()
return nil, newError("Unable to load config").AtWarning()
}
func loadProtobufConfig(data []byte) (*Config, error) {