2
0
mirror of synced 2025-02-23 22:28:11 +00:00
torrent/bencode/api.go
Matt Joiner 901a8b1b36 Don't panic on int parse failures
This means for UnmarshalTypeErrors we now include context. There are still some other error types remaining that are thrown up via panic.
2021-08-12 13:46:02 +10:00

165 lines
4.4 KiB
Go

package bencode
import (
"bytes"
"fmt"
"io"
"reflect"
"github.com/anacrolix/missinggo/expect"
)
//----------------------------------------------------------------------------
// Errors
//----------------------------------------------------------------------------
// In case if marshaler cannot encode a type, it will return this error. Typical
// example of such type is float32/float64 which has no bencode representation.
type MarshalTypeError struct {
Type reflect.Type
}
func (e *MarshalTypeError) Error() string {
return "bencode: unsupported type: " + e.Type.String()
}
// Unmarshal argument must be a non-nil value of some pointer type.
type UnmarshalInvalidArgError struct {
Type reflect.Type
}
func (e *UnmarshalInvalidArgError) Error() string {
if e.Type == nil {
return "bencode: Unmarshal(nil)"
}
if e.Type.Kind() != reflect.Ptr {
return "bencode: Unmarshal(non-pointer " + e.Type.String() + ")"
}
return "bencode: Unmarshal(nil " + e.Type.String() + ")"
}
// Unmarshaler spotted a value that was not appropriate for a given Go value.
type UnmarshalTypeError struct {
BencodeTypeName string
UnmarshalTargetType reflect.Type
}
// This could probably be a value type, but we may already have users assuming
// that it's passed by pointer.
func (e *UnmarshalTypeError) Error() string {
return fmt.Sprintf(
"can't unmarshal a bencode %v into a %v",
e.BencodeTypeName,
e.UnmarshalTargetType,
)
}
// Unmarshaler tried to write to an unexported (therefore unwritable) field.
type UnmarshalFieldError struct {
Key string
Type reflect.Type
Field reflect.StructField
}
func (e *UnmarshalFieldError) Error() string {
return "bencode: key \"" + e.Key + "\" led to an unexported field \"" +
e.Field.Name + "\" in type: " + e.Type.String()
}
// Malformed bencode input, unmarshaler failed to parse it.
type SyntaxError struct {
Offset int64 // location of the error
What error // error description
}
func (e *SyntaxError) Error() string {
return fmt.Sprintf("bencode: syntax error (offset: %d): %s", e.Offset, e.What)
}
// A non-nil error was returned after calling MarshalBencode on a type which
// implements the Marshaler interface.
type MarshalerError struct {
Type reflect.Type
Err error
}
func (e *MarshalerError) Error() string {
return "bencode: error calling MarshalBencode for type " + e.Type.String() + ": " + e.Err.Error()
}
// A non-nil error was returned after calling UnmarshalBencode on a type which
// implements the Unmarshaler interface.
type UnmarshalerError struct {
Type reflect.Type
Err error
}
func (e *UnmarshalerError) Error() string {
return "bencode: error calling UnmarshalBencode for type " + e.Type.String() + ": " + e.Err.Error()
}
//----------------------------------------------------------------------------
// Interfaces
//----------------------------------------------------------------------------
// Any type which implements this interface, will be marshaled using the
// specified method.
type Marshaler interface {
MarshalBencode() ([]byte, error)
}
// Any type which implements this interface, will be unmarshaled using the
// specified method.
type Unmarshaler interface {
UnmarshalBencode([]byte) error
}
// Marshal the value 'v' to the bencode form, return the result as []byte and
// an error if any.
func Marshal(v interface{}) ([]byte, error) {
var buf bytes.Buffer
e := Encoder{w: &buf}
err := e.Encode(v)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func MustMarshal(v interface{}) []byte {
b, err := Marshal(v)
expect.Nil(err)
return b
}
// Unmarshal the bencode value in the 'data' to a value pointed by the 'v' pointer, return a non-nil
// error if any. If there are trailing bytes, this results in ErrUnusedTrailingBytes, but the value
// will be valid. It's probably more consistent to use Decoder.Decode if you want to rely on this
// behaviour (inspired by Rust's serde here).
func Unmarshal(data []byte, v interface{}) (err error) {
buf := bytes.NewReader(data)
e := Decoder{r: buf}
err = e.Decode(v)
if err == nil && buf.Len() != 0 {
err = ErrUnusedTrailingBytes{buf.Len()}
}
return
}
type ErrUnusedTrailingBytes struct {
NumUnusedBytes int
}
func (me ErrUnusedTrailingBytes) Error() string {
return fmt.Sprintf("%d unused trailing bytes", me.NumUnusedBytes)
}
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: &scanner{r: r}}
}
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}