package serial import ( "bytes" "encoding/json" "io" core "github.com/v2fly/v2ray-core/v5" "github.com/v2fly/v2ray-core/v5/common/errors" json_reader "github.com/v2fly/v2ray-core/v5/infra/conf/json" v4 "github.com/v2fly/v2ray-core/v5/infra/conf/v4" ) type offset struct { line int char int } func findOffset(b []byte, o int) *offset { if o >= len(b) || o < 0 { return nil } line := 1 char := 0 for i, x := range b { if i == o { break } if x == '\n' { line++ char = 0 } else { char++ } } return &offset{line: line, char: char} } // DecodeJSONConfig reads from reader and decode the config into *conf.Config // syntax error could be detected. func DecodeJSONConfig(reader io.Reader) (*v4.Config, error) { jsonConfig := &v4.Config{} err := DecodeJSON(reader, jsonConfig) if err != nil { return nil, err } return jsonConfig, nil } // DecodeJSON reads from reader and decode into target // syntax error could be detected. func DecodeJSON(reader io.Reader, target interface{}) error { jsonContent := bytes.NewBuffer(make([]byte, 0, 10240)) jsonReader := io.TeeReader(&json_reader.Reader{ Reader: reader, }, jsonContent) decoder := json.NewDecoder(jsonReader) if err := decoder.Decode(target); err != nil { var pos *offset cause := errors.Cause(err) switch tErr := cause.(type) { case *json.SyntaxError: pos = findOffset(jsonContent.Bytes(), int(tErr.Offset)) case *json.UnmarshalTypeError: pos = findOffset(jsonContent.Bytes(), int(tErr.Offset)) } if pos != nil { return newError("failed to read config file at line ", pos.line, " char ", pos.char).Base(err) } return newError("failed to read config file").Base(err) } return nil } func LoadJSONConfig(reader io.Reader) (*core.Config, error) { jsonConfig, err := DecodeJSONConfig(reader) if err != nil { return nil, err } pbConfig, err := jsonConfig.Build() if err != nil { return nil, newError("failed to parse json config").Base(err) } return pbConfig, nil }