V5: Add TOML Support (rebased from 36ba06837f)

This commit is contained in:
秋のかえで 2021-01-01 22:18:47 +08:00 committed by Shelikhoo
parent a95bb95267
commit dde9463275
No known key found for this signature in database
GPG Key ID: C4D5E79D22B25316
10 changed files with 252 additions and 13 deletions

1
go.mod
View File

@ -10,6 +10,7 @@ require (
github.com/jhump/protoreflect v1.8.2
github.com/lucas-clemente/quic-go v0.21.1
github.com/miekg/dns v1.1.42
github.com/pelletier/go-toml v1.8.1
github.com/pires/go-proxyproto v0.5.0
github.com/seiflotfy/cuckoofilter v0.0.0-20201222105146-bc6005554a0c
github.com/stretchr/testify v1.7.0

20
infra/conf/json/toml.go Normal file
View File

@ -0,0 +1,20 @@
package json
import (
"encoding/json"
"github.com/pelletier/go-toml"
)
// FromTOML convert toml to json
func FromTOML(v []byte) ([]byte, error) {
m := make(map[string]interface{})
if err := toml.Unmarshal(v, &m); err != nil {
return nil, err
}
j, err := json.Marshal(m)
if err != nil {
return nil, err
}
return j, nil
}

View File

@ -0,0 +1,113 @@
package json
import (
"encoding/json"
"testing"
)
func TestTOMLToJSON_V2Style(t *testing.T) {
input := `
[log]
loglevel = 'debug'
[[inbounds]]
port = 10800
listen = '127.0.0.1'
protocol = 'socks'
[inbounds.settings]
udp = true
[[outbounds]]
protocol = 'vmess'
[[outbounds.settings.vnext]]
port = 443
address = 'example.com'
[[outbounds.settings.vnext.users]]
id = '98a15fa6-2eb1-edd5-50ea-cfc428aaab78'
[outbounds.streamSettings]
network = 'tcp'
security = 'tls'
`
expected := `
{
"log": {
"loglevel": "debug"
},
"inbounds": [{
"port": 10800,
"listen": "127.0.0.1",
"protocol": "socks",
"settings": {
"udp": true
}
}],
"outbounds": [{
"protocol": "vmess",
"settings": {
"vnext": [{
"port": 443,
"address": "example.com",
"users": [{
"id": "98a15fa6-2eb1-edd5-50ea-cfc428aaab78"
}]
}]
},
"streamSettings": {
"network": "tcp",
"security": "tls"
}
}]
}
`
bs, err := FromTOML([]byte(input))
if err != nil {
t.Error(err)
}
m := make(map[string]interface{})
json.Unmarshal(bs, &m)
assertResult(t, m, expected)
}
func TestTOMLToJSON_ValueTypes(t *testing.T) {
input := `
boolean = [ true, false, true, false ]
float = [ 3.14, 685_230.15 ]
int = [ 123, 685_230 ]
string = [ "哈哈", "Hello world", "newline newline2" ]
date = [ "2018-02-17" ]
datetime = [ "2018-02-17T15:02:31+08:00" ]
mixed = [ true, false, 1, 0, "hello" ]
1 = 0
true = true
str = "hello"
[null]
nodeName = "node"
`
expected := `
{
"boolean": [true, false, true, false],
"float": [3.14, 685230.15],
"int": [123, 685230],
"null": {
"nodeName": "node"
},
"string": ["哈哈", "Hello world", "newline newline2"],
"date": ["2018-02-17"],
"datetime": ["2018-02-17T15:02:31+08:00"],
"mixed": [true,false,1,0,"hello"],
"1": 0,
"true": true,
"str": "hello"
}
`
bs, err := FromTOML([]byte(input))
if err != nil {
t.Error(err)
}
m := make(map[string]interface{})
json.Unmarshal(bs, &m)
assertResult(t, m, expected)
}

View File

@ -3,7 +3,7 @@ package api
import (
"testing"
statsService "v2ray.com/core/app/stats/command"
statsService "github.com/v2fly/v2ray-core/v4/app/stats/command"
)
func TestEmptyResponese_0(t *testing.T) {

View File

@ -3,6 +3,7 @@ package all
import (
"bytes"
"encoding/json"
"github.com/pelletier/go-toml"
"google.golang.org/protobuf/proto"
"os"
"strings"
@ -24,12 +25,12 @@ Arguments:
-i, -input
Specify the input format.
Available values: "json", "yaml"
Available values: "json", "toml", "yaml"
Default: "json"
-o, -output
Specify the output format
Available values: "json", "yaml", "protobuf" / "pb"
Available values: "json", "toml", "yaml", "protobuf" / "pb"
Default: "json"
-r
@ -38,16 +39,14 @@ Arguments:
Examples:
{{.Exec}} {{.LongName}} -output=protobuf config.json (1)
{{.Exec}} {{.LongName}} -output=yaml config.json (2)
{{.Exec}} {{.LongName}} -input=yaml config.yaml (3)
{{.Exec}} {{.LongName}} "path/to/dir" (4)
{{.Exec}} {{.LongName}} -i yaml -o protobuf c1.yaml <url>.yaml (5)
{{.Exec}} {{.LongName}} -input=toml config.toml (2)
{{.Exec}} {{.LongName}} "path/to/dir" (3)
{{.Exec}} {{.LongName}} -i yaml -o protobuf c1.yaml <url>.yaml (4)
(1) Convert json to protobuf
(2) Convert json to yaml
(3) Convert yaml to json
(4) Merge json files in dir
(5) Merge yaml files and convert to protobuf
(2) Convert toml to json
(3) Merge json files in dir
(4) Merge yaml files and convert to protobuf
Use "{{.Exec}} help config-merge" for more information about merge.
`,
@ -64,6 +63,7 @@ var (
)
var formatExtensions = map[string][]string{
"json": {".json", ".jsonc"},
"toml": {".toml"},
"yaml": {".yaml", ".yml"},
}
@ -97,6 +97,11 @@ func executeConvert(cmd *base.Command, args []string) {
if err != nil {
base.Fatalf("failed to marshal json: %s", err)
}
case "toml":
out, err = toml.Marshal(m)
if err != nil {
base.Fatalf("failed to marshal json: %s", err)
}
case "yaml":
out, err = yaml.Marshal(m)
if err != nil {

View File

@ -23,6 +23,15 @@ func mergeConvertToMap(files []string, format string) map[string]interface{} {
if err != nil {
base.Fatalf("failed to load json: %s", err)
}
case "toml":
bs, err := tomlsToJSONs(files)
if err != nil {
base.Fatalf("failed to convert toml to json: %s", err)
}
m, err = merge.BytesToMap(bs)
if err != nil {
base.Fatalf("failed to merge converted json: %s", err)
}
case "yaml":
bs, err := yamlsToJSONs(files)
if err != nil {
@ -111,3 +120,19 @@ func yamlsToJSONs(files []string) ([][]byte, error) {
}
return jsons, nil
}
func tomlsToJSONs(files []string) ([][]byte, error) {
jsons := make([][]byte, 0)
for _, file := range files {
bs, err := cmdarg.LoadArgToBytes(file)
if err != nil {
return nil, err
}
j, err := json.FromTOML(bs)
if err != nil {
return nil, err
}
jsons = append(jsons, j)
}
return jsons, nil
}

View File

@ -11,7 +11,10 @@ var docFormat = &base.Command{
{{.Exec}} supports different config formats:
* json (.json, .jsonc)
The default loader, multiple config files support.
The default loader, multiple config files support.
* toml (.toml)
The toml loader, multiple config files support.
* yaml (.yml)
The yaml loader, multiple config files support.

View File

@ -14,7 +14,7 @@ Merging of config files is applied in following commands:
{{.Exec}} test -c c1.yaml -c c2.yaml ...
{{.Exec}} convert c1.json dir1 ...
Support of yaml is implemented by converting yaml to json,
Support of toml and yaml is implemented by converting them to json,
both merge and load. So we take json as example here.
Suppose we have 2 JSON files,

View File

@ -76,6 +76,9 @@ import (
// The following line loads JSON internally
_ "github.com/v2fly/v2ray-core/v4/main/json"
// TOML config support.
_ "github.com/v2fly/v2ray-core/v4/main/toml"
// YAML config support.
_ "github.com/v2fly/v2ray-core/v4/main/yaml"

69
main/toml/toml.go Normal file
View File

@ -0,0 +1,69 @@
package toml
import (
"bytes"
"errors"
"io"
"io/ioutil"
"github.com/v2fly/v2ray-core/v4"
"github.com/v2fly/v2ray-core/v4/common"
"github.com/v2fly/v2ray-core/v4/common/cmdarg"
"github.com/v2fly/v2ray-core/v4/infra/conf/json"
"github.com/v2fly/v2ray-core/v4/infra/conf/merge"
"github.com/v2fly/v2ray-core/v4/infra/conf/serial"
)
func init() {
common.Must(core.RegisterConfigLoader(&core.ConfigFormat{
Name: []string{"TOML"},
Extension: []string{".toml"},
Loader: func(input interface{}) (*core.Config, error) {
switch v := input.(type) {
case cmdarg.Arg:
bs, err := tomlsToJSONs(v)
if err != nil {
return nil, err
}
data, err := merge.BytesToJSON(bs)
if err != nil {
return nil, err
}
r := bytes.NewReader(data)
cf, err := serial.DecodeJSONConfig(r)
if err != nil {
return nil, err
}
return cf.Build()
case io.Reader:
bs, err := ioutil.ReadAll(v)
if err != nil {
return nil, err
}
bs, err = json.FromTOML(bs)
if err != nil {
return nil, err
}
return serial.LoadJSONConfig(bytes.NewBuffer(bs))
default:
return nil, errors.New("unknow type")
}
},
}))
}
func tomlsToJSONs(files []string) ([][]byte, error) {
jsons := make([][]byte, 0)
for _, file := range files {
bs, err := cmdarg.LoadArgToBytes(file)
if err != nil {
return nil, err
}
j, err := json.FromTOML(bs)
if err != nil {
return nil, err
}
jsons = append(jsons, j)
}
return jsons, nil
}