mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-09-05 19:44:32 -04:00
* scalable commands column * new multi-json loader For both internal & external json loader This commit also: * applies -confdir to other formats, e.g. "yaml" in the future * multiple assign of -confdir is accepted * add flag to load confdir recursively * config loader can have alias name * json loader also accepts .jsonc * add merge command * add help topics for json merge, format loader * format loaders don't panic * apply lint style * add merge test * merge same tag in array, solve v2fly/discussion#97 * apply lint style * merge code optimize * fix merge cmdarg.Arg * update cmd description * improve merge logic * fix zero value overwrite * fix "null" lost after array merge * code optimize * fix merged slices not sorted * code optimize * add package doc * fix a typo
131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
// +build !confonly
|
|
|
|
package core
|
|
|
|
import (
|
|
"io"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"v2ray.com/core/common"
|
|
"v2ray.com/core/common/buf"
|
|
"v2ray.com/core/common/cmdarg"
|
|
"v2ray.com/core/main/confloader"
|
|
)
|
|
|
|
// ConfigFormat is a configurable format of V2Ray config file.
|
|
type ConfigFormat struct {
|
|
Name []string
|
|
Extension []string
|
|
Loader ConfigLoader
|
|
}
|
|
|
|
// ConfigLoader is a utility to load V2Ray config from external source.
|
|
type ConfigLoader func(input interface{}) (*Config, error)
|
|
|
|
var (
|
|
configLoaderByName = make(map[string]*ConfigFormat)
|
|
configLoaderByExt = make(map[string]*ConfigFormat)
|
|
)
|
|
|
|
// RegisterConfigLoader add a new ConfigLoader.
|
|
func RegisterConfigLoader(format *ConfigFormat) error {
|
|
for _, name := range format.Name {
|
|
lname := strings.ToLower(name)
|
|
if _, found := configLoaderByName[lname]; found {
|
|
return newError(name, " already registered.")
|
|
}
|
|
configLoaderByName[lname] = format
|
|
}
|
|
|
|
for _, ext := range format.Extension {
|
|
lext := strings.ToLower(ext)
|
|
if f, found := configLoaderByExt[lext]; found {
|
|
return newError(ext, " already registered to ", f.Name)
|
|
}
|
|
configLoaderByExt[lext] = format
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getExtension(filename string) string {
|
|
ext := filepath.Ext(filename)
|
|
return strings.ToLower(ext)
|
|
}
|
|
|
|
// GetConfigLoader get config loader by name and filename.
|
|
// Specify formatName to explicitly select a loader.
|
|
// Specify filename to choose loader by detect its extension.
|
|
// Leave formatName and filename blank for default loader
|
|
func GetConfigLoader(formatName string, filename string) (*ConfigFormat, error) {
|
|
if formatName != "" {
|
|
// if explicitly specified, we can safely assume that user knows what they are
|
|
if f, found := configLoaderByName[formatName]; found {
|
|
return f, nil
|
|
}
|
|
return nil, newError("Unable to load config in ", formatName).AtWarning()
|
|
}
|
|
// no explicitly specified loader, extenstion detect first
|
|
if ext := getExtension(filename); len(ext) > 0 {
|
|
if f, found := configLoaderByExt[ext]; found {
|
|
return f, nil
|
|
}
|
|
}
|
|
// default loader
|
|
if f, found := configLoaderByName["json"]; found {
|
|
return f, nil
|
|
}
|
|
panic("default loader not found")
|
|
}
|
|
|
|
// 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) {
|
|
f, err := GetConfigLoader(formatName, filename)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return f.Loader(input)
|
|
}
|
|
|
|
func loadProtobufConfig(data []byte) (*Config, error) {
|
|
config := new(Config)
|
|
if err := proto.Unmarshal(data, config); err != nil {
|
|
return nil, err
|
|
}
|
|
return config, nil
|
|
}
|
|
|
|
func init() {
|
|
common.Must(RegisterConfigLoader(&ConfigFormat{
|
|
Name: []string{"Protobuf", "pb"},
|
|
Extension: []string{".pb"},
|
|
Loader: func(input interface{}) (*Config, error) {
|
|
switch v := input.(type) {
|
|
case cmdarg.Arg:
|
|
r, err := confloader.LoadConfig(v[0])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
data, err := buf.ReadAllToBytes(r)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return loadProtobufConfig(data)
|
|
case io.Reader:
|
|
data, err := buf.ReadAllToBytes(v)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return loadProtobufConfig(data)
|
|
default:
|
|
return nil, newError("unknow type")
|
|
}
|
|
},
|
|
}))
|
|
}
|