nmdc-webfrontend/vendor/github.com/googollee/go-socket.io/attachment.go

169 lines
3.7 KiB
Go

package socketio
import (
"bytes"
"encoding/json"
"fmt"
"io"
"reflect"
)
// Attachment is an attachment handler used in emit args. All attachments will be sent as binary data in the transport layer. When using an attachment, make sure it is a pointer.
//
// For example:
//
// type Arg struct {
// Title string `json:"title"`
// File *Attachment `json:"file"`
// }
//
// f, _ := os.Open("./some_file")
// arg := Arg{
// Title: "some_file",
// File: &Attachment{
// Data: f,
// }
// }
//
// socket.Emit("send file", arg)
// socket.On("get file", func(so Socket, arg Arg) {
// b, _ := ioutil.ReadAll(arg.File.Data)
// })
type Attachment struct {
Data io.ReadWriter
num int
}
func encodeAttachments(v interface{}) []io.Reader {
index := 0
return encodeAttachmentValue(reflect.ValueOf(v), &index)
}
func encodeAttachmentValue(v reflect.Value, index *int) []io.Reader {
v = reflect.Indirect(v)
ret := []io.Reader{}
if !v.IsValid() {
return ret
}
switch v.Kind() {
case reflect.Struct:
if v.Type().Name() == "Attachment" {
a, ok := v.Addr().Interface().(*Attachment)
if !ok {
panic("can't convert")
}
a.num = *index
ret = append(ret, a.Data)
(*index)++
return ret
}
for i, n := 0, v.NumField(); i < n; i++ {
var r []io.Reader
r = encodeAttachmentValue(v.Field(i), index)
ret = append(ret, r...)
}
case reflect.Map:
if v.IsNil() {
return ret
}
for _, key := range v.MapKeys() {
var r []io.Reader
r = encodeAttachmentValue(v.MapIndex(key), index)
ret = append(ret, r...)
}
case reflect.Slice:
if v.IsNil() {
return ret
}
fallthrough
case reflect.Array:
for i, n := 0, v.Len(); i < n; i++ {
var r []io.Reader
r = encodeAttachmentValue(v.Index(i), index)
ret = append(ret, r...)
}
case reflect.Interface:
ret = encodeAttachmentValue(reflect.ValueOf(v.Interface()), index)
}
return ret
}
func decodeAttachments(v interface{}, binary [][]byte) error {
return decodeAttachmentValue(reflect.ValueOf(v), binary)
}
func decodeAttachmentValue(v reflect.Value, binary [][]byte) error {
v = reflect.Indirect(v)
if !v.IsValid() {
return fmt.Errorf("invalid value")
}
switch v.Kind() {
case reflect.Struct:
if v.Type().Name() == "Attachment" {
a, ok := v.Addr().Interface().(*Attachment)
if !ok {
panic("can't convert")
}
if a.num >= len(binary) || a.num < 0 {
return fmt.Errorf("out of range")
}
if a.Data == nil {
a.Data = bytes.NewBuffer(nil)
}
for b := binary[a.num]; len(b) > 0; {
n, err := a.Data.Write(b)
if err != nil {
return err
}
b = b[n:]
}
return nil
}
for i, n := 0, v.NumField(); i < n; i++ {
if err := decodeAttachmentValue(v.Field(i), binary); err != nil {
return err
}
}
case reflect.Map:
if v.IsNil() {
return nil
}
for _, key := range v.MapKeys() {
if err := decodeAttachmentValue(v.MapIndex(key), binary); err != nil {
return err
}
}
case reflect.Slice:
if v.IsNil() {
return nil
}
fallthrough
case reflect.Array:
for i, n := 0, v.Len(); i < n; i++ {
if err := decodeAttachmentValue(v.Index(i), binary); err != nil {
return err
}
}
case reflect.Interface:
if err := decodeAttachmentValue(reflect.ValueOf(v.Interface()), binary); err != nil {
return err
}
}
return nil
}
func (a Attachment) MarshalJSON() ([]byte, error) {
return []byte(fmt.Sprintf("{\"_placeholder\":true,\"num\":%d}", a.num)), nil
}
func (a *Attachment) UnmarshalJSON(b []byte) error {
var v struct {
Num int `json:"num"`
}
if err := json.Unmarshal(b, &v); err != nil {
return err
}
a.num = v.Num
return nil
}