]> Sergey Matveev's repositories - btrtrc.git/blobdiff - bencode/api.go
Drop support for go 1.20
[btrtrc.git] / bencode / api.go
index b4ddcec983dd87a83e38f26bc4c50c552972f40b..3c379abbf837355da04bf1cd9cd7522f0b110619 100644 (file)
@@ -1,11 +1,12 @@
 package bencode
 
 import (
-       "bufio"
        "bytes"
        "fmt"
        "io"
        "reflect"
+
+       "github.com/anacrolix/missinggo/expect"
 )
 
 //----------------------------------------------------------------------------
@@ -18,8 +19,8 @@ type MarshalTypeError struct {
        Type reflect.Type
 }
 
-func (this *MarshalTypeError) Error() string {
-       return "bencode: unsupported type: " + this.Type.String()
+func (e *MarshalTypeError) Error() string {
+       return "bencode: unsupported type: " + e.Type.String()
 }
 
 // Unmarshal argument must be a non-nil value of some pointer type.
@@ -40,13 +41,18 @@ func (e *UnmarshalInvalidArgError) Error() string {
 
 // Unmarshaler spotted a value that was not appropriate for a given Go value.
 type UnmarshalTypeError struct {
-       Value string
-       Type  reflect.Type
+       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 "bencode: value (" + e.Value + ") is not appropriate for type: " +
-               e.Type.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.
@@ -109,57 +115,50 @@ type Unmarshaler interface {
        UnmarshalBencode([]byte) error
 }
 
-//----------------------------------------------------------------------------
-// Stateless interface
-//----------------------------------------------------------------------------
-
-// Marshal the value 'v' to the bencode form, return the result as []byte and an
-// error if any.
+// 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{Writer: bufio.NewWriter(&buf)}
-       err := e.encode(v)
+       e := Encoder{w: &buf}
+       err := e.Encode(v)
        if err != nil {
                return nil, err
        }
        return buf.Bytes(), nil
 }
 
-// Unmarshal the bencode value in the 'data' to a value pointed by the 'v'
-// pointer, return a non-nil error if any.
-func Unmarshal(data []byte, v interface{}) error {
-       e := decoder{Reader: bufio.NewReader(bytes.NewBuffer(data))}
-       return e.decode(v)
+func MustMarshal(v interface{}) []byte {
+       b, err := Marshal(v)
+       expect.Nil(err)
+       return b
 }
 
-//----------------------------------------------------------------------------
-// Stateful interface
-//----------------------------------------------------------------------------
-
-type Decoder struct {
-       d decoder
+// 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
 }
 
-func NewDecoder(r io.Reader) *Decoder {
-       return &Decoder{decoder{Reader: bufio.NewReader(r)}}
+type ErrUnusedTrailingBytes struct {
+       NumUnusedBytes int
 }
 
-func (d *Decoder) Decode(v interface{}) error {
-       return d.d.decode(v)
+func (me ErrUnusedTrailingBytes) Error() string {
+       return fmt.Sprintf("%d unused trailing bytes", me.NumUnusedBytes)
 }
 
-type Encoder struct {
-       e encoder
+func NewDecoder(r io.Reader) *Decoder {
+       return &Decoder{r: &scanner{r: r}}
 }
 
 func NewEncoder(w io.Writer) *Encoder {
-       return &Encoder{encoder{Writer: bufio.NewWriter(w)}}
-}
-
-func (e *Encoder) Encode(v interface{}) error {
-       err := e.e.encode(v)
-       if err != nil {
-               return err
-       }
-       return nil
+       return &Encoder{w: w}
 }