package jsonv4 import ( "bytes" "encoding/json" "github.com/pelletier/go-toml" "os" "strings" "google.golang.org/protobuf/proto" core "github.com/v2fly/v2ray-core/v4" "github.com/v2fly/v2ray-core/v4/infra/conf/merge" "github.com/v2fly/v2ray-core/v4/infra/conf/serial" "github.com/v2fly/v2ray-core/v4/main/commands/base" "github.com/v2fly/v2ray-core/v4/main/commands/helpers" "gopkg.in/yaml.v2" ) var cmdConvert = &base.Command{ CustomFlags: true, UsageLine: "{{.Exec}} convert [c1.json] [.json] [dir1] ...", Short: "convert config files", Long: ` Convert config files between different formats. Files are merged before convert. Arguments: -i, -input The input format. Available values: "auto", "json", "toml", "yaml" Default: "auto" -o, -output The output format Available values: "json", "toml", "yaml", "protobuf" / "pb" Default: "json" -r Load folders recursively. Examples: {{.Exec}} {{.LongName}} -output=protobuf "path/to/dir" (1) {{.Exec}} {{.LongName}} -o=yaml config.toml (2) {{.Exec}} {{.LongName}} c1.json c2.json (3) {{.Exec}} {{.LongName}} -output=yaml c1.yaml .yaml (4) (1) Merge all supported files in dir and convert to protobuf (2) Convert toml to yaml (3) Merge json files (4) Merge yaml files Use "{{.Exec}} help config-merge" for more information about merge. `, } func init() { cmdConvert.Run = executeConvert // break init loop } var ( inputFormat string outputFormat string confDirRecursively bool ) func setConfArgs(cmd *base.Command) { cmd.Flag.StringVar(&inputFormat, "input", core.FormatAuto, "") cmd.Flag.StringVar(&inputFormat, "i", core.FormatAuto, "") cmd.Flag.StringVar(&outputFormat, "output", "json", "") cmd.Flag.StringVar(&outputFormat, "o", "json", "") cmd.Flag.BoolVar(&confDirRecursively, "r", false, "") } func executeConvert(cmd *base.Command, args []string) { setConfArgs(cmd) cmd.Flag.Parse(args) inputFormat = strings.ToLower(inputFormat) outputFormat = strings.ToLower(outputFormat) m, err := helpers.LoadConfigToMap(cmd.Flag.Args(), inputFormat, confDirRecursively) if err != nil { base.Fatalf("failed to merge: %s", err) } err = merge.ApplyRules(m) if err != nil { base.Fatalf("failed to apply merge rules: %s", err) } var out []byte switch outputFormat { case core.FormatJSON: out, err = json.Marshal(m) if err != nil { base.Fatalf("failed to convert to json: %s", err) } case core.FormatTOML: out, err = toml.Marshal(m) if err != nil { base.Fatalf("failed to convert to toml: %s", err) } case core.FormatYAML: out, err = yaml.Marshal(m) if err != nil { base.Fatalf("failed to convert to yaml: %s", err) } case core.FormatProtobuf, core.FormatProtobufShort: data, err := json.Marshal(m) if err != nil { base.Fatalf("failed to marshal json: %s", err) } r := bytes.NewReader(data) cf, err := serial.DecodeJSONConfig(r) if err != nil { base.Fatalf("failed to decode json: %s", err) } pbConfig, err := cf.Build() if err != nil { base.Fatalf(err.Error()) } out, err = proto.Marshal(pbConfig) if err != nil { base.Fatalf("failed to convert to protobuf: %s", err) } default: base.Errorf("invalid output format: %s", outputFormat) base.Errorf("Run '%s help %s' for details.", base.CommandEnv.Exec, cmd.LongName()) base.Exit() } if _, err := os.Stdout.Write(out); err != nil { base.Fatalf("failed to write stdout: %s", err) } }