package bencode import ( "bytes" "errors" "fmt" "io" "log" "reflect" "strings" ) // mapping / limitations // dict -> struct // list -> aray of same type type itemType int const ( itemError itemType = iota itemDict itemList itemNumber itemString ) var ErrValue = errors.New("invalid value") func Marshal(v interface{}) ([]byte, error) { var out bytes.Buffer val := reflect.ValueOf(v) err := marshalField(&out, val) return out.Bytes(), err } func marshalField(out io.Writer, v reflect.Value) error { if !v.IsValid() { return ErrValue } log.Println(v.Kind()) switch v.Kind() { case reflect.String: marshalString(out, v.String()) case reflect.Int: marshalInt(out, v.Int()) case reflect.Slice: marshalList(out, v) case reflect.Struct: marshalDict(out, v) } return nil } func marshalDict(out io.Writer, v reflect.Value) { t := v.Type() io.WriteString(out, "d") for i := 0; i < t.NumField(); i++ { f := t.Field(i) tag := f.Tag.Get("bencode") if tag == "-" { continue } name, _ := parseTag(tag) if name == "" { name = f.Name } if !isEmpty(v.Field(i)) { marshalString(out, name) marshalField(out, v.Field(i)) } } io.WriteString(out, "e") } func isEmpty(v reflect.Value) bool { switch v.Kind() { case reflect.Int: return v.Int() == 0 case reflect.String, reflect.Slice: return v.Len() == 0 case reflect.Interface, reflect.Ptr: return v.IsNil() } return false } func marshalList(out io.Writer, v reflect.Value) { switch v.Type().Elem().Kind() { case reflect.Uint8: marshalString(out, string(v.Bytes())) default: io.WriteString(out, "l") for i := 0; i < v.Len(); i++ { marshalField(out, v.Index(i)) } io.WriteString(out, "e") } } func marshalString(out io.Writer, s string) { fmt.Fprintf(out, "%d:%s", len(s), s) } func marshalInt(out io.Writer, i int64) { fmt.Fprintf(out, "i%de", i) } func Unmarshal(data []byte, v interface{}) error { return nil } func parseTag(tag string) (string, string) { if i := strings.Index(tag, ","); i != -1 { return tag[:i], tag[i+1:] } return tag, "" }