|
@@ -22,9 +22,13 @@ type ConfigFormat struct {
|
|
|
// ConfigLoader is a utility to load Xray config from external source.
|
|
// ConfigLoader is a utility to load Xray config from external source.
|
|
|
type ConfigLoader func(input interface{}) (*Config, error)
|
|
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 (
|
|
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.
|
|
// RegisterConfigLoader add a new ConfigLoader.
|
|
@@ -46,6 +50,21 @@ func RegisterConfigLoader(format *ConfigFormat) error {
|
|
|
return nil
|
|
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 {
|
|
func getExtension(filename string) string {
|
|
|
idx := strings.LastIndexByte(filename, '.')
|
|
idx := strings.LastIndexByte(filename, '.')
|
|
|
if idx == -1 {
|
|
if idx == -1 {
|
|
@@ -54,23 +73,48 @@ func getExtension(filename string) string {
|
|
|
return filename[idx+1:]
|
|
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()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
|
|
|
|
|
- if f, found := configLoaderByName[formatName]; found {
|
|
|
|
|
- return f.Loader(input)
|
|
|
|
|
|
|
+ // 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()
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- return nil, newError("Unable to load config in ", formatName).AtWarning()
|
|
|
|
|
|
|
+ return nil, newError("Unable to load config").AtWarning()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func loadProtobufConfig(data []byte) (*Config, error) {
|
|
func loadProtobufConfig(data []byte) (*Config, error) {
|