<commit_before>package resources

import (
	"sort"
	"strings"

	"github.com/lxc/lxd/shared/api"
)

func vpdKnownKey(name string) bool {
	// Sanity check.
	if name == "" {
		return false
	}

	// Prefixes and fields we care about (sorted).
	prefixes := []int{'V', 'Y', 'Z'}
	fields := []string{"CC", "EC", "FC", "FN", "MN", "NA", "PN", "RM", "SN"}

	// Extract the prefix.
	prefix := int(name[0])

	// Check if starting by a valid prefix.
	if pos := sort.SearchInts(prefixes, prefix); pos < len(prefixes) && prefixes[pos] == prefix {
		return true
	}

	// Check if a key we're interested in.
	if pos := sort.SearchStrings(fields, name); pos < len(fields) && fields[pos] == name {
		return true
	}

	return false
}

func vpdReadInt(buf []byte, length int) ([]byte, int) {
	value := 0
	for i, n := range buf[:length] {
		value += int(n) << (i * 8)
	}

	return buf[length:], value
}

func vpdReadString(buf []byte, length int) ([]byte, string) {
	if length > len(buf) {
		length = len(buf)
	}

	return buf[length:], strings.Trim(string(buf[:length]), "\x00 ")
}

func vpdReadEntries(buf []byte, length int) ([]byte, map[string]string) {
	entries := map[string]string{}
	vpdBuf := buf[:length]

	for len(vpdBuf) > 0 {
		var key string
		var entryLen int
		var value string

		// Read 2-char key.
		vpdBuf, key = vpdReadString(vpdBuf, 2)

		// Read 1 byte for the entry length.
		vpdBuf, entryLen = vpdReadInt(vpdBuf, 1)
		if entryLen == 0 {
			continue
		}

		// Read the entry value.
		vpdBuf, value = vpdReadString(vpdBuf, entryLen)
		if vpdKnownKey(key) {
			entries[key] = value
		}
	}

	return buf[length:], entries
}

func parsePCIVPD(buf []byte) api.ResourcesPCIVPD {
	vpd := api.ResourcesPCIVPD{Entries: map[string]string{}}

	for len(buf) > 0 {
		var tag int
		var length int

		// Read the 1-byte entry type.
		buf, tag = vpdReadInt(buf, 1)
		if (tag & 0x80) == 0x80 {
			// Large resource data, Read the 2-bytes entry length.
			buf, length = vpdReadInt(buf, 2)
		} else {
			// Small resource data, size is in the tag itself.
			length = tag & 0x07
		}

		switch tag {
		case 0x82:
			// Product name.
			buf, vpd.ProductName = vpdReadString(buf, length)
		case 0x90:
			// Read/only VPD entries.
			fallthrough
		case 0x91:
			// Read/write VPD entries.
			var entries map[string]string
			buf, entries = vpdReadEntries(buf, length)

			// Append values since there might be multiple sections.
			for key, value := range entries {
				vpd.Entries[key] = value
			}

		default:
			// For other tags, just skip the value.
			buf = buf[length:]
		}
	}

	return vpd
}
<commit_msg>lxd/resources: Removes all one-line assign and check statements.<commit_after>package resources

import (
	"sort"
	"strings"

	"github.com/lxc/lxd/shared/api"
)

func vpdKnownKey(name string) bool {
	// Sanity check.
	if name == "" {
		return false
	}

	// Prefixes and fields we care about (sorted).
	prefixes := []int{'V', 'Y', 'Z'}
	fields := []string{"CC", "EC", "FC", "FN", "MN", "NA", "PN", "RM", "SN"}

	// Extract the prefix.
	prefix := int(name[0])

	// Check if starting by a valid prefix.
	pos := sort.SearchInts(prefixes, prefix)
	if pos < len(prefixes) && prefixes[pos] == prefix {
		return true
	}

	// Check if a key we're interested in.
	pos = sort.SearchStrings(fields, name)
	if pos < len(fields) && fields[pos] == name {
		return true
	}

	return false
}

func vpdReadInt(buf []byte, length int) ([]byte, int) {
	value := 0
	for i, n := range buf[:length] {
		value += int(n) << (i * 8)
	}

	return buf[length:], value
}

func vpdReadString(buf []byte, length int) ([]byte, string) {
	if length > len(buf) {
		length = len(buf)
	}

	return buf[length:], strings.Trim(string(buf[:length]), "\x00 ")
}

func vpdReadEntries(buf []byte, length int) ([]byte, map[string]string) {
	entries := map[string]string{}
	vpdBuf := buf[:length]

	for len(vpdBuf) > 0 {
		var key string
		var entryLen int
		var value string

		// Read 2-char key.
		vpdBuf, key = vpdReadString(vpdBuf, 2)

		// Read 1 byte for the entry length.
		vpdBuf, entryLen = vpdReadInt(vpdBuf, 1)
		if entryLen == 0 {
			continue
		}

		// Read the entry value.
		vpdBuf, value = vpdReadString(vpdBuf, entryLen)
		if vpdKnownKey(key) {
			entries[key] = value
		}
	}

	return buf[length:], entries
}

func parsePCIVPD(buf []byte) api.ResourcesPCIVPD {
	vpd := api.ResourcesPCIVPD{Entries: map[string]string{}}

	for len(buf) > 0 {
		var tag int
		var length int

		// Read the 1-byte entry type.
		buf, tag = vpdReadInt(buf, 1)
		if (tag & 0x80) == 0x80 {
			// Large resource data, Read the 2-bytes entry length.
			buf, length = vpdReadInt(buf, 2)
		} else {
			// Small resource data, size is in the tag itself.
			length = tag & 0x07
		}

		switch tag {
		case 0x82:
			// Product name.
			buf, vpd.ProductName = vpdReadString(buf, length)
		case 0x90:
			// Read/only VPD entries.
			fallthrough
		case 0x91:
			// Read/write VPD entries.
			var entries map[string]string
			buf, entries = vpdReadEntries(buf, length)

			// Append values since there might be multiple sections.
			for key, value := range entries {
				vpd.Entries[key] = value
			}

		default:
			// For other tags, just skip the value.
			buf = buf[length:]
		}
	}

	return vpd
}
<|endoftext|>


<commit_before>package rpc

import (
	"errors"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
	"ember/measure"
)

var ErrUnknown = errors.New("unknown error type on call api")

func (p *Server) List() (apis map[string][]string, err error) {
	apis = p.trait
	return
}

func (p *Server) Run(port int) error {
	mux := http.NewServeMux()
	mux.HandleFunc("/", p.Serve)
	return http.ListenAndServe(":" + strconv.Itoa(port), mux)
}

func (p *Server) Reg(api ApiTrait) (err error) {
	return p.reg("", api, api.Trait())
}

func (p *Server) reg(prefix string, api interface{}, trait map[string][]string) (err error) {
	typ := reflect.TypeOf(api)
	for i := 0; i < typ.NumMethod(); i++ {
		method := typ.Method(i)
		name := prefix + method.Name
		if _, ok := trait[method.Name]; !ok {
			continue
		}
		fn := method.Func.Interface()
		err = p.create(name, api, fn)
		if err != nil {
			return
		}
		p.fns[name] = reflect.ValueOf(fn)
		p.objs[name] = api
		p.trait[name] = trait[method.Name]
	}
	return
}

func (p *Server) create(name string, api interface{}, fn interface{}) (err error) {
	fv := reflect.ValueOf(fn)

	err = callable(fv)
	if err != nil {
		return
	}

	nOut := fv.Type().NumOut()
	if nOut == 0 || fv.Type().Out(nOut - 1).Kind() != reflect.Interface {
		err = NewErrRpcServer(fmt.Errorf("%s return final output param must be error interface", name))
		return
	}

	_, b := fv.Type().Out(nOut - 1).MethodByName("Error")
	if !b {
		err = NewErrRpcServer(fmt.Errorf("%s return final output param must be error interface", name))
		return
	}

	if _, ok := p.fns[name]; ok {
		err = NewErrRpcServer(fmt.Errorf("%s has registered", name))
		return
	}
	return
}

func (p *Server) Serve(w http.ResponseWriter, r *http.Request) {
	begin := time.Now().UnixNano()
	cost := begin
	cb := 0

	url := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
	name := url[len(url) - 1]

	defer func() {
		end := time.Now().UnixNano()
		p.measure.Record("api.cost.handle." + name, cost - begin)
		p.measure.Record("api.cost.all." + name, end - begin)
		p.measure.Record("api.response." + name, int64(cb))
	}()

	var status string
	var detail string
	result, err := p.handle(name, w, r)
	cost = time.Now().UnixNano()
	if err == nil {
		status = StatusOK
	} else {
		status = StatusErr
		result = nil
		detail = err.Error()
	}

	resp := NewResponse(status, detail, result)
	ret, err := json.Marshal(resp)
	if err != nil {
		if detail != "" {
			panic("error marshal: must OK")
		}
		resp = NewResponse(StatusErr, NewErrRpcServer(err).Error(), nil)
		ret, err = json.Marshal(resp)
		if err != nil {
			panic("error marshal: must OK, too")
		}
	}

	cb = len(ret)

	h := w.Header()
	h.Set("Content-Type", "text/json")

	if resp.Status == StatusOK {
		w.WriteHeader(HttpCodeOK)
	} else {
		w.WriteHeader(HttpCodeErr)
	}

	_, err = w.Write(ret)
	// TODO: log here
}

func (p *Server) handle(name string, w http.ResponseWriter, r *http.Request) (result []interface{}, err error) {
	defer r.Body.Close()
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}

	if len(data) == 0 {
		return p.invoke(name, nil)
	}
	var in map[string]json.RawMessage
	err = json.Unmarshal(data, &in)
	if err != nil {
		return
	}

	return p.invoke(name, in)
}

func (p *Server) invoke(name string, args map[string]json.RawMessage) (ret []interface{}, err error) {
	fn, ok := p.fns[name]

	if !ok {
		err = NewErrRpcServer(fmt.Errorf("api %s not registered", name))
		return
	}
	if len(args) + 1 != fn.Type().NumIn() {
		err = NewErrRpcServer(fmt.Errorf("api %s params count unmatched", name))
		return
	}

	in := make([]reflect.Value, fn.Type().NumIn())
	in[0] = reflect.ValueOf(p.objs[name])

	for i, argName := range p.trait[name] {
		if _, ok := args[argName]; !ok {
			return nil, NewErrRpcServer(fmt.Errorf("api %s arg %s missing", argName))
		} else {
			typ := fn.Type().In(i + 1)
			val := reflect.New(typ)
			err = json.Unmarshal(args[argName], val.Interface())
			if err != nil {
				return nil, NewErrRpcServer(err)
			}
			in[i + 1] = val.Elem()
		}
	}

	out, err := call(fn, in)
	if err != nil {
		return nil, err
	}

	ret = make([]interface{}, len(out))
	for i := 0; i < len(ret); i++ {
		ret[i] = out[i].Interface()
	}

	pv := out[len(out) - 1].Interface()
	if pv != nil {
		if e, ok := pv.(error); ok {
			err = NewErrRpcServer(e)
		} else if e, ok := pv.(string); ok {
			err = NewErrRpcServer(fmt.Errorf(e))
		}
		return nil, err
	}

	return ret, nil
}

func NewServer() (p *Server) {
	p = &Server {
		make(map[string]reflect.Value),
		make(map[string]interface{}),
		make(map[string][]string),
		measure.NewMeasure(time.Second * 60, time.Second * 60 * 60 * 24),
	}

	err := p.reg("Measure.", p.measure, MeasureTrait)
	if err != nil {
		panic(err)
	}

	err = p.reg("Api.", p, BuiltinTrait)
	if err != nil {
		panic(err)
	}
	return
}

type Server struct {
	fns map[string]reflect.Value
	objs map[string]interface{}
	trait map[string][]string
	measure *measure.Measure
}

var MeasureTrait = map[string][]string {
	"Sync": {"time"},
}
var BuiltinTrait = map[string][]string {
	"List": {},
}

const (
	StatusOK = "OK"
	StatusErr = "error"
	HttpCodeOK = http.StatusOK
	HttpCodeErr = 599
)

func NewResponse(status, detail string, result []interface{}) *Response {
	return &Response {
		status,
		detail,
		result,
	}
}

type Response struct {
	Status string `json:"status"`
	Detail string `json:"detail"`
	Result []interface{} `json:"result"`
}

func callable(fn reflect.Value) (err error) {
	defer func() {
		if e := recover(); e != nil {
			if r, ok := e.(error); ok {
				err = r
			} else if s, ok := e.(string); ok {
				err = NewErrRpcServer(errors.New(s))
			} else {
				err = NewErrRpcServer(ErrUnknown)
			}
		}
	}()
	fn.Type().NumIn()
	return
}

func call(fn reflect.Value, in []reflect.Value) (out []reflect.Value, err error) {
	defer func() {
		e := recover()
		if e != nil {
			if r, ok := e.(error); ok {
				err = r
			} else if s, ok := e.(string); ok {
				err = errors.New(s)
			} else {
				err = errors.New("unknown error type on call api")
			}
		}
	}()
	out = fn.Call(in)
	return
}

type ErrRpcServer struct {
	err error
}

func NewErrRpcServer(e error) *ErrRpcServer {
	return &ErrRpcServer{e}
}

func (p *ErrRpcServer) Error() string {
	return "rpc server error: " + p.err.Error()
}

type ApiTrait interface {
	Trait() map[string][]string
}
<commit_msg>Fixing recover/panic<commit_after>package rpc

import (
	"errors"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"
	"ember/measure"
)

var ErrUnknown = errors.New("unknown error type on call api")

func (p *Server) List() (apis map[string][]string, err error) {
	apis = p.trait
	return
}

func (p *Server) Run(port int) error {
	mux := http.NewServeMux()
	mux.HandleFunc("/", p.Serve)
	return http.ListenAndServe(":" + strconv.Itoa(port), mux)
}

func (p *Server) Reg(api ApiTrait) (err error) {
	return p.reg("", api, api.Trait())
}

func (p *Server) reg(prefix string, api interface{}, trait map[string][]string) (err error) {
	typ := reflect.TypeOf(api)
	for i := 0; i < typ.NumMethod(); i++ {
		method := typ.Method(i)
		name := prefix + method.Name
		if _, ok := trait[method.Name]; !ok {
			continue
		}
		fn := method.Func.Interface()
		err = p.create(name, api, fn)
		if err != nil {
			return
		}
		p.fns[name] = reflect.ValueOf(fn)
		p.objs[name] = api
		p.trait[name] = trait[method.Name]
	}
	return
}

func (p *Server) create(name string, api interface{}, fn interface{}) (err error) {
	fv := reflect.ValueOf(fn)

	err = callable(fv)
	if err != nil {
		return
	}

	nOut := fv.Type().NumOut()
	if nOut == 0 || fv.Type().Out(nOut - 1).Kind() != reflect.Interface {
		err = NewErrRpcServer(fmt.Errorf("%s return final output param must be error interface", name))
		return
	}

	_, b := fv.Type().Out(nOut - 1).MethodByName("Error")
	if !b {
		err = NewErrRpcServer(fmt.Errorf("%s return final output param must be error interface", name))
		return
	}

	if _, ok := p.fns[name]; ok {
		err = NewErrRpcServer(fmt.Errorf("%s has registered", name))
		return
	}
	return
}

func (p *Server) Serve(w http.ResponseWriter, r *http.Request) {
	begin := time.Now().UnixNano()
	cost := begin
	cb := 0

	url := strings.Split(strings.Trim(r.URL.Path, "/"), "/")
	name := url[len(url) - 1]

	defer func() {
		end := time.Now().UnixNano()
		p.measure.Record("api.cost.handle." + name, cost - begin)
		p.measure.Record("api.cost.all." + name, end - begin)
		p.measure.Record("api.response." + name, int64(cb))
	}()

	var status string
	var detail string
	result, err := p.handle(name, w, r)
	cost = time.Now().UnixNano()
	if err == nil {
		status = StatusOK
	} else {
		status = StatusErr
		result = nil
		detail = err.Error()
	}

	resp := NewResponse(status, detail, result)
	ret, err := json.Marshal(resp)
	if err != nil {
		if detail != "" {
			panic("error marshal: must OK")
		}
		resp = NewResponse(StatusErr, NewErrRpcServer(err).Error(), nil)
		ret, err = json.Marshal(resp)
		if err != nil {
			panic("error marshal: must OK, too")
		}
	}

	cb = len(ret)

	h := w.Header()
	h.Set("Content-Type", "text/json")

	if resp.Status == StatusOK {
		w.WriteHeader(HttpCodeOK)
	} else {
		w.WriteHeader(HttpCodeErr)
	}

	_, err = w.Write(ret)
	// TODO: log here
}

func (p *Server) handle(name string, w http.ResponseWriter, r *http.Request) (result []interface{}, err error) {
	defer r.Body.Close()
	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return
	}

	if len(data) == 0 {
		return p.invoke(name, nil)
	}
	var in map[string]json.RawMessage
	err = json.Unmarshal(data, &in)
	if err != nil {
		return
	}

	return p.invoke(name, in)
}

func (p *Server) invoke(name string, args map[string]json.RawMessage) (ret []interface{}, err error) {
	fn, ok := p.fns[name]

	if !ok {
		err = NewErrRpcServer(fmt.Errorf("api %s not registered", name))
		return
	}
	if len(args) + 1 != fn.Type().NumIn() {
		err = NewErrRpcServer(fmt.Errorf("api %s params count unmatched", name))
		return
	}

	in := make([]reflect.Value, fn.Type().NumIn())
	in[0] = reflect.ValueOf(p.objs[name])

	for i, argName := range p.trait[name] {
		if _, ok := args[argName]; !ok {
			return nil, NewErrRpcServer(fmt.Errorf("api %s arg %s missing", argName))
		} else {
			typ := fn.Type().In(i + 1)
			val := reflect.New(typ)
			err = json.Unmarshal(args[argName], val.Interface())
			if err != nil {
				return nil, NewErrRpcServer(err)
			}
			in[i + 1] = val.Elem()
		}
	}

	out, err := call(fn, in)
	if err != nil {
		return nil, err
	}

	ret = make([]interface{}, len(out))
	for i := 0; i < len(ret); i++ {
		ret[i] = out[i].Interface()
	}

	pv := out[len(out) - 1].Interface()
	if pv != nil {
		if e, ok := pv.(error); ok {
			err = NewErrRpcServer(e)
		} else if e, ok := pv.(string); ok {
			err = NewErrRpcServer(fmt.Errorf(e))
		}
		return nil, err
	}

	return ret, nil
}

func NewServer() (p *Server) {
	p = &Server {
		make(map[string]reflect.Value),
		make(map[string]interface{}),
		make(map[string][]string),
		measure.NewMeasure(time.Second * 60, time.Second * 60 * 60 * 24),
	}

	err := p.reg("Measure.", p.measure, MeasureTrait)
	if err != nil {
		panic(err)
	}

	err = p.reg("Api.", p, BuiltinTrait)
	if err != nil {
		panic(err)
	}
	return
}

type Server struct {
	fns map[string]reflect.Value
	objs map[string]interface{}
	trait map[string][]string
	measure *measure.Measure
}

var MeasureTrait = map[string][]string {
	"Sync": {"time"},
}
var BuiltinTrait = map[string][]string {
	"List": {},
}

const (
	StatusOK = "OK"
	StatusErr = "error"
	HttpCodeOK = http.StatusOK
	HttpCodeErr = 599
)

func NewResponse(status, detail string, result []interface{}) *Response {
	return &Response {
		status,
		detail,
		result,
	}
}

type Response struct {
	Status string `json:"status"`
	Detail string `json:"detail"`
	Result []interface{} `json:"result"`
}

func callable(fn reflect.Value) (err error) {
	defer func() {
		if e := recover(); e != nil {
			if r, ok := e.(error); ok {
				err = NewErrRpcServer(r)
			} else if s, ok := e.(string); ok {
				err = NewErrRpcServer(errors.New(s))
			} else {
				err = NewErrRpcServer(ErrUnknown)
			}
		}
	}()
	fn.Type().NumIn()
	return
}

func call(fn reflect.Value, in []reflect.Value) (out []reflect.Value, err error) {
	defer func() {
		e := recover()
		if e != nil {
			if r, ok := e.(error); ok {
				err = NewErrRpcServer(r)
			} else if s, ok := e.(string); ok {
				err = errors.New(s)
			} else {
				err = errors.New("unknown error type on call api")
			}
		}
	}()
	out = fn.Call(in)
	return
}

type ErrRpcServer struct {
	err error
}

func NewErrRpcServer(e error) *ErrRpcServer {
	return &ErrRpcServer{e}
}

func (p *ErrRpcServer) Error() string {
	return "rpc server error: " + p.err.Error()
}

type ApiTrait interface {
	Trait() map[string][]string
}
<|endoftext|>


<commit_before>//go:build !bindata
// +build !bindata

/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package main

import (
	"github.com/admpub/nging/v4/application/library/bindata"
)

func init() {
	bindata.PathAliases.Add(`caddy`, `../../nging-plugins/caddymanager/template/backend`)
	bindata.PathAliases.Add(`collector`, `../../nging-plugins/collector/template/backend`)
	bindata.PathAliases.Add(`db`, `../../nging-plugins/dbmanager/template/backend`)
	bindata.PathAliases.Add(`ddns`, `../../nging-plugins/ddnsmanager/template/backend`)
	bindata.PathAliases.Add(`download`, `../../nging-plugins/dlmanager/template/backend`)
	bindata.PathAliases.Add(`frp`, `../../nging-plugins/frpmanager/template/backend`)
	bindata.PathAliases.Add(`ftp`, `../../nging-plugins/ftpmanager/template/backend`)
	bindata.PathAliases.Add(`server`, `../../nging-plugins/servermanager/template/backend`)
	bindata.PathAliases.Add(`term`, `../../nging-plugins/sshmanager/template/backend`)

	bindata.Initialize()
}
<commit_msg>update<commit_after>//go:build !bindata
// +build !bindata

/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package main

import (
	"github.com/admpub/nging/v4/application/library/bindata"
)

const NgingPluginDir = `../../nging-plugins`

func init() {
	bindata.PathAliases.Add(`caddy`, NgingPluginDir+`/caddymanager/template/backend`)
	bindata.PathAliases.Add(`collector`, NgingPluginDir+`/collector/template/backend`)
	bindata.PathAliases.Add(`db`, NgingPluginDir+`/dbmanager/template/backend`)
	bindata.PathAliases.Add(`ddns`, NgingPluginDir+`/ddnsmanager/template/backend`)
	bindata.PathAliases.Add(`download`, NgingPluginDir+`/dlmanager/template/backend`)
	bindata.PathAliases.Add(`frp`, NgingPluginDir+`/frpmanager/template/backend`)
	bindata.PathAliases.Add(`ftp`, NgingPluginDir+`/ftpmanager/template/backend`)
	bindata.PathAliases.Add(`server`, NgingPluginDir+`/servermanager/template/backend`)
	bindata.PathAliases.Add(`term`, NgingPluginDir+`/sshmanager/template/backend`)

	bindata.Initialize()
}
<|endoftext|>


<commit_before>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package testing provide test helpers for various actions.
package testing

import (
	"io/ioutil"
	"net/http"
	"strconv"

	"github.com/tsuru/config"
	"launchpad.net/gocheck"
)

type T struct {
	Admin        user
	AdminTeam    team
	GitAPIServer string
	GitRWHost    string
	GitROHost    string
}

type user struct {
	Email    string
	Password string
}

type team struct {
	Name  string `bson:"_id"`
	Users []string
}

func (t *T) SetGitConfs(c *gocheck.C) {
	t.GitAPIServer, _ = config.GetString("git:api-server")
	t.GitROHost, _ = config.GetString("git:ro-host")
	t.GitRWHost, _ = config.GetString("git:rw-host")
}

func (t *T) RollbackGitConfs(c *gocheck.C) {
	config.Set("git:api-server", t.GitAPIServer)
	config.Set("git:ro-host", t.GitROHost)
	config.Set("git:rw-host", t.GitRWHost)
}

type TestHandler struct {
	Body    []byte
	Method  string
	Url     string
	Content string
	Header  http.Header
}

func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Method = r.Method
	h.Url = r.URL.String()
	b, _ := ioutil.ReadAll(r.Body)
	h.Body = b
	h.Header = r.Header
	w.Write([]byte(h.Content))
}

type MultiTestHandler struct {
	Body               [][]byte
	Method             []string
	Url                []string
	Content            string
	ConditionalContent map[string]interface{}
	Header             []http.Header
	RspCode            int
}

func (h *MultiTestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Method = append(h.Method, r.Method)
	h.Url = append(h.Url, r.URL.String())
	b, _ := ioutil.ReadAll(r.Body)
	h.Body = append(h.Body, b)
	h.Header = append(h.Header, r.Header)
	if h.RspCode == 0 {
		h.RspCode = http.StatusOK
	}
	condContent := h.ConditionalContent[r.URL.String()]
	if content, ok := condContent.(string); ok {
		w.WriteHeader(h.RspCode)
		w.Write([]byte(content))
	} else if content, ok := condContent.([]string); ok {
		code, _ := strconv.Atoi(content[0])
		w.WriteHeader(code)
		w.Write([]byte(content[1]))
	} else {
		w.WriteHeader(h.RspCode)
		w.Write([]byte(h.Content))
	}
}
<commit_msg>testing: remove the type T<commit_after>// Copyright 2015 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package testing provide test helpers for various actions.
package testing

import (
	"io/ioutil"
	"net/http"
	"strconv"
)

type TestHandler struct {
	Body    []byte
	Method  string
	Url     string
	Content string
	Header  http.Header
}

func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Method = r.Method
	h.Url = r.URL.String()
	b, _ := ioutil.ReadAll(r.Body)
	h.Body = b
	h.Header = r.Header
	w.Write([]byte(h.Content))
}

type MultiTestHandler struct {
	Body               [][]byte
	Method             []string
	Url                []string
	Content            string
	ConditionalContent map[string]interface{}
	Header             []http.Header
	RspCode            int
}

func (h *MultiTestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.Method = append(h.Method, r.Method)
	h.Url = append(h.Url, r.URL.String())
	b, _ := ioutil.ReadAll(r.Body)
	h.Body = append(h.Body, b)
	h.Header = append(h.Header, r.Header)
	if h.RspCode == 0 {
		h.RspCode = http.StatusOK
	}
	condContent := h.ConditionalContent[r.URL.String()]
	if content, ok := condContent.(string); ok {
		w.WriteHeader(h.RspCode)
		w.Write([]byte(content))
	} else if content, ok := condContent.([]string); ok {
		code, _ := strconv.Atoi(content[0])
		w.WriteHeader(code)
		w.Write([]byte(content[1]))
	} else {
		w.WriteHeader(h.RspCode)
		w.Write([]byte(h.Content))
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package textile

import (
	"context"
	"encoding/json"
	"errors"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/google/note-maps/notes"
	"github.com/google/note-maps/notes/change"
	"github.com/textileio/go-threads/common"
	"github.com/textileio/go-threads/core/app"
	core "github.com/textileio/go-threads/core/db"
	"github.com/textileio/go-threads/core/thread"
	"github.com/textileio/go-threads/db"
	"github.com/textileio/go-threads/util"
)

type textileError struct {
	m string
	w error
}

func (e textileError) Error() string    { return "notes/textile: " + e.m + ": " + e.w.Error() }
func (e textileError) Unwrap() error    { return e.w }
func wrapError(m string, w error) error { return textileError{m, w} }

type GetSecret func(key string) (secret []byte, err error)
type SetSecret func(key string, secret []byte) error

type Options struct {
	// BaseDirectory is a directory within which many databases might stored.
	//
	// It might typically be something like "$XDG_DATA_HOME/$argv[0]"
	BaseDirectory string

	// Thread is a printable multibase representation of the thread identifier.
	Thread string

	// Key is a printable representation of the thread encryption keys for
	// service/replication and, optionally, for reading.
	Key string

	// GetSecret will be used to load the thread encryption keys if none are
	// provided in the Key field.
	GetSecret

	// SetSecret will be used to store the thread encyrption keys.
	SetSecret
}

func (o *Options) expand() error {
	// Resolve the thread id:
	if o.Thread == "" {
		o.Thread = thread.NewIDV1(thread.Raw, 32).String()
	}
	// Resolve the thread encryption keys:
	if o.Key == "" {
		if o.GetSecret != nil {
			secret, err := o.GetSecret(o.Thread)
			if err != nil {
				return wrapError("loading key for "+o.Thread, err)
			}
			key, err := thread.KeyFromBytes(secret)
			if err != nil {
				return wrapError("decoding loaded key for "+o.Thread, err)
			}
			o.Key = key.String()
		}
		if o.Key == "" {
			o.Key = thread.NewRandomKey().String()
		}
	}
	return nil
}

type Option func(*Options)

func WithBaseDirectory(d string) Option {
	return func(o *Options) { o.BaseDirectory = d }
}

func WithPath(p string) Option {
	return func(o *Options) {
		o.BaseDirectory, o.Thread = filepath.Split(p)
	}
}

func WithThread(t string) Option { return func(o *Options) { o.Thread = t } }

// DefaultNetwork is a convenience function to build a
// github.com/textileio/core/app.Net for use with Open().
func DefaultNetwork(dir string) (app.Net, error) {
	return common.DefaultNetwork(
		dir,
		common.WithNetDebug(true),
		common.WithNetHostAddr(util.FreeLocalAddr()))
}

type Database struct {
	t        *db.DB
	initOnce sync.Once
	note     *db.Collection
	broke    error
}

// Open attempts to open a thread with local storage
func Open(ctx context.Context, n app.Net, opts ...Option) (*Database, error) {
	var o Options
	for _, opt := range opts {
		opt(&o)
	}
	if err := o.expand(); err != nil {
		return nil, err
	}
	tid, err := thread.Decode(o.Thread)
	if err != nil {
		return nil, wrapError("decoding thread "+o.Thread, err)
	}
	key, err := thread.KeyFromString(o.Key)
	if err != nil {
		return nil, wrapError("parsing thread key", err)
	}
	path := filepath.Join(o.BaseDirectory, o.Thread)
	d, err := db.NewDB(context.Background(), n, tid,
		db.WithNewThreadKey(key), db.WithNewRepoPath(path))
	if err != nil {
		return nil, wrapError("connecting to database", err)
	}
	if o.SetSecret != nil {
		err := o.SetSecret(o.Thread, key.Bytes())
		if err != nil {
			d.Close()
			return nil, wrapError("storing thread key", err)
		}
	}
	return &Database{t: d}, nil
}

func (d *Database) init() error {
	d.initOnce.Do(func() {
		cs := d.t.ListCollections()
		for _, c := range cs {
			if c.GetName() == "Notes" {
				d.note = c
			}
		}
		if d.note != nil {
			return
		}
		var err error
		d.note, err = d.t.NewCollection(db.CollectionConfig{
			Name:   "Note",
			Schema: util.SchemaFromInstance(&record{}, false),
		})
		if err != nil {
			d.broke = wrapError("creating note schema in database", err)
		}
	})
	return d.broke
}

func (d *Database) find(q *db.Query) ([]notes.Note, error) {
	bss, err := d.note.Find(q)
	if err != nil {
		return nil, err
	}
	notes := make([]notes.Note, len(bss))
	for i, bs := range bss {
		var r record
		if err = json.Unmarshal(bs, &r); err != nil {
			return nil, err
		}
		notes[i] = &note{record: r, loaded: true}
	}
	return notes, nil
}

func (d *Database) Find(q *notes.Query) ([]notes.Note, error) {
	if err := d.init(); err != nil {
		return nil, err
	}
	return d.find(&db.Query{})
}

func (d *Database) Load(ids []uint64) ([]notes.Note, error) {
	if err := d.init(); err != nil {
		return nil, err
	}
	var q *db.Query
	for _, id := range ids {
		or := db.Where("Id").Eq(id)
		if q == nil {
			q = or
		} else {
			q = q.Or(or)
		}
	}
	found, err := d.find(&db.Query{})
	if err != nil {
		return nil, err
	}
	id2found := make(map[uint64]int)
	for i, n := range found {
		id2found[n.GetId()] = i
	}
	ns := make([]notes.Note, len(ids))
	for i, id := range ids {
		if f, ok := id2found[id]; !ok {
			ns[i] = notes.EmptyNote(id)
		} else {
			ns[i] = found[f]
		}
	}
	return ns, nil
}

func (d *Database) Patch(ops []change.Operation) error {
	if err := d.init(); err != nil {
		return err
	}
	return d.note.WriteTxn(func(txn *db.Txn) error {
		cache := make(map[uint64]*note)
		created := make(map[uint64]bool)
		get := func(id uint64) (*note, error) {
			if _, ok := cache[id]; !ok {
				cid := core.InstanceID(strconv.FormatUint(id, 10))
				bs, err := txn.FindByID(cid)
				if err != nil {
					if err == db.ErrInstanceNotFound {
						var n note
						n.record.ID = cid
						n.loaded = true
						cache[id] = &n
						created[id] = true
					} else {
						return nil, wrapError("loading note "+string(cid), err)
					}
				} else {
					var n note
					if err := json.Unmarshal(bs, &n.record); err != nil {
						return nil, wrapError("parsing note "+string(cid)+" from storage", err)
					}
					n.loaded = true
					cache[id] = &n
				}
			}
			return cache[id], nil
		}
		for _, op := range ops {
			switch o := op.(type) {
			case change.AddContent:
				n, err := get(o.Id)
				if err != nil {
					return err
				}
				n.record.Contents = append(
					n.record.Contents,
					core.InstanceID(strconv.FormatUint(o.Add, 10)))
			case change.SetValue:
				n, err := get(o.Id)
				if err != nil {
					return err
				}
				n.record.ValueString = o.Lexical
				n.record.ValueType = core.InstanceID(strconv.FormatUint(o.Datatype, 10))
			default:
				panic("unrecognized op type")
			}
		}
		for _, n := range cache {
			bs, err := json.Marshal(n.record)
			if err != nil {
				return wrapError("marshalling updated note for storage", err)
			}
			if created[n.GetId()] {
				actual, err := txn.Create(bs)
				if err != nil {
					return wrapError("while saving note", err)
				}
				intended := strconv.FormatUint(n.GetId(), 10)
				if string(actual[0]) != intended {
					return errors.New("created note " + string(actual[0]) + ", intended " + intended)
				}
			} else {
				if err := txn.Save(bs); err != nil {
					return wrapError("while saving note", err)
				}
			}
		}
		return nil
	})
}

func (d *Database) Close() error { return d.t.Close() }

type record struct {
	ID          core.InstanceID   `json:"_id"`
	Types       []core.InstanceID `json:"types,omitempty"`
	Supertypes  []core.InstanceID `json:"super_types,omitempty"`
	ValueString string            `json:"value_string,omitempty"`
	ValueType   core.InstanceID   `json:"value_type,omitempty"`
	Contents    []core.InstanceID `json:"contents,omitempty"`
}

type note struct {
	record
	loaded bool
}

func (n *note) load() error {
	panic("not implemented")
}
func (n *note) GetId() uint64 {
	u, _ := strconv.ParseUint(string(n.ID), 10, 64)
	return u
}
func (n *note) GetTypes() ([]notes.Note, error) {
	if err := n.load(); err != nil {
		return nil, err
	}
	ns := make([]notes.Note, len(n.Types))
	for i, id := range n.Types {
		ns[i] = &note{record: record{ID: id}}
	}
	return ns, nil
}
func (n *note) GetSupertypes() ([]notes.Note, error) {
	return notes.EmptyNote(0).GetSupertypes()
}
func (n *note) GetValue() (string, notes.Note, error) {
	return notes.EmptyNote(0).GetValue()
}
func (n *note) GetContents() ([]notes.Note, error) {
	return notes.EmptyNote(0).GetContents()
}
<commit_msg>notes/textile: add some docs and fill some gaps<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package textile uses Textileio ThreadsDB to implement the
// github.com/google/note-maps/notes interfaces.
package textile

import (
	"context"
	"encoding/json"
	"errors"
	"path/filepath"
	"strconv"
	"sync"

	"github.com/google/note-maps/notes"
	"github.com/google/note-maps/notes/change"
	"github.com/textileio/go-threads/common"
	"github.com/textileio/go-threads/core/app"
	core "github.com/textileio/go-threads/core/db"
	"github.com/textileio/go-threads/core/thread"
	"github.com/textileio/go-threads/db"
	"github.com/textileio/go-threads/util"
)

type textileError struct {
	m string
	w error
}

func (e textileError) Error() string    { return "notes/textile: " + e.m + ": " + e.w.Error() }
func (e textileError) Unwrap() error    { return e.w }
func wrapError(m string, w error) error { return textileError{m, w} }

type GetSecret func(key string) (secret []byte, err error)
type SetSecret func(key string, secret []byte) error

type Options struct {
	// BaseDirectory is a directory within which many databases might stored.
	//
	// It might typically be something like "$XDG_DATA_HOME/$argv[0]"
	BaseDirectory string

	// Thread is a printable multibase representation of the thread identifier.
	Thread string

	// Key is a printable representation of the thread encryption keys for
	// service/replication and, optionally, for reading.
	Key string

	// GetSecret will be used to load the thread encryption keys if none are
	// provided in the Key field.
	GetSecret

	// SetSecret will be used to store the thread encyrption keys.
	SetSecret
}

func (o *Options) expand() error {
	// Resolve the thread id:
	randomNewThread := o.Thread == ""
	if randomNewThread {
		o.Thread = thread.NewIDV1(thread.Raw, 32).String()
	}
	// Resolve the thread encryption keys:
	if o.Key == "" {
		if !randomNewThread && o.GetSecret != nil {
			secret, err := o.GetSecret(o.Thread)
			if err != nil {
				return wrapError("loading key for "+o.Thread, err)
			}
			key, err := thread.KeyFromBytes(secret)
			if err != nil {
				return wrapError("decoding loaded key for "+o.Thread, err)
			}
			o.Key = key.String()
		}
		if o.Key == "" {
			o.Key = thread.NewRandomKey().String()
		}
	}
	return nil
}

type Option func(*Options)

func WithBaseDirectory(d string) Option {
	return func(o *Options) { o.BaseDirectory = d }
}
func WithPath(p string) Option {
	return func(o *Options) {
		o.BaseDirectory, o.Thread = filepath.Split(p)
	}
}

func WithThread(t string) Option { return func(o *Options) { o.Thread = t } }
func WithGetSecret(f GetSecret) Option {
	return func(o *Options) { o.GetSecret = f }
}
func WithSetSecret(f SetSecret) Option {
	return func(o *Options) { o.SetSecret = f }
}

// DefaultNetwork is a convenience function to build a
// github.com/textileio/core/app.Net for use with Open().
func DefaultNetwork(dir string) (app.Net, error) {
	return common.DefaultNetwork(
		dir,
		common.WithNetDebug(true),
		common.WithNetHostAddr(util.FreeLocalAddr()))
}

// NoteMap is meant to implement the notes.NoteMap interface.
type NoteMap struct {
	t        *db.DB
	initOnce sync.Once
	note     *db.Collection
	broke    error
}

// Open creates a NoteMap that replicates through net n.
//
// All options are optional, but if non are provided the database may not be
// reusable.
func Open(ctx context.Context, n app.Net, opts ...Option) (*NoteMap, error) {
	var o Options
	for _, opt := range opts {
		opt(&o)
	}
	if err := o.expand(); err != nil {
		return nil, err
	}
	tid, err := thread.Decode(o.Thread)
	if err != nil {
		return nil, wrapError("decoding thread "+o.Thread, err)
	}
	key, err := thread.KeyFromString(o.Key)
	if err != nil {
		return nil, wrapError("parsing thread key", err)
	}
	path := filepath.Join(o.BaseDirectory, o.Thread)
	d, err := db.NewDB(context.Background(), n, tid,
		db.WithNewThreadKey(key), db.WithNewRepoPath(path))
	if err != nil {
		return nil, wrapError("connecting to database", err)
	}
	if o.SetSecret != nil {
		err := o.SetSecret(o.Thread, key.Bytes())
		if err != nil {
			d.Close()
			return nil, wrapError("storing thread key", err)
		}
	}
	return &NoteMap{t: d}, nil
}

func (nm *NoteMap) init() error {
	nm.initOnce.Do(func() {
		cs := nm.t.ListCollections()
		for _, c := range cs {
			if c.GetName() == "Notes" {
				nm.note = c
			}
		}
		if nm.note != nil {
			return
		}
		var err error
		nm.note, err = nm.t.NewCollection(db.CollectionConfig{
			Name:   "Note",
			Schema: util.SchemaFromInstance(&record{}, false),
		})
		if err != nil {
			nm.broke = wrapError("creating note schema in database", err)
		}
	})
	return nm.broke
}

func (nm *NoteMap) find(q *db.Query) ([]notes.Note, error) {
	bss, err := nm.note.Find(q)
	if err != nil {
		return nil, err
	}
	notes := make([]notes.Note, len(bss))
	for i, bs := range bss {
		var r record
		if err = json.Unmarshal(bs, &r); err != nil {
			return nil, err
		}
		notes[i] = &note{record: r, loaded: true}
	}
	return notes, nil
}

func (nm *NoteMap) Find(q *notes.Query) ([]notes.Note, error) {
	if err := nm.init(); err != nil {
		return nil, err
	}
	return nm.find(&db.Query{})
}

func (nm *NoteMap) Load(ids []uint64) ([]notes.Note, error) {
	if err := nm.init(); err != nil {
		return nil, err
	}
	var q *db.Query
	for _, id := range ids {
		or := db.Where("Id").Eq(id)
		if q == nil {
			q = or
		} else {
			q = q.Or(or)
		}
	}
	found, err := nm.find(&db.Query{})
	if err != nil {
		return nil, err
	}
	id2found := make(map[uint64]int)
	for i, n := range found {
		id2found[n.GetId()] = i
	}
	ns := make([]notes.Note, len(ids))
	for i, id := range ids {
		if f, ok := id2found[id]; !ok {
			ns[i] = notes.EmptyNote(id)
		} else {
			ns[i] = found[f]
		}
	}
	return ns, nil
}

func (nm *NoteMap) Patch(ops []change.Operation) error {
	if err := nm.init(); err != nil {
		return err
	}
	return nm.note.WriteTxn(func(txn *db.Txn) error {
		cache := make(map[uint64]*note)
		created := make(map[uint64]bool)
		get := func(id uint64) (*note, error) {
			if _, ok := cache[id]; !ok {
				cid := core.InstanceID(strconv.FormatUint(id, 10))
				bs, err := txn.FindByID(cid)
				if err != nil {
					if err == db.ErrInstanceNotFound {
						var n note
						n.record.ID = cid
						n.loaded = true
						cache[id] = &n
						created[id] = true
					} else {
						return nil, wrapError("loading note "+string(cid), err)
					}
				} else {
					var n note
					if err := json.Unmarshal(bs, &n.record); err != nil {
						return nil, wrapError("parsing note "+string(cid)+" from storage", err)
					}
					n.loaded = true
					cache[id] = &n
				}
			}
			return cache[id], nil
		}
		for _, op := range ops {
			switch o := op.(type) {
			case change.AddContent:
				n, err := get(o.Id)
				if err != nil {
					return err
				}
				n.record.Contents = append(
					n.record.Contents,
					core.InstanceID(strconv.FormatUint(o.Add, 10)))
			case change.SetValue:
				n, err := get(o.Id)
				if err != nil {
					return err
				}
				n.record.ValueString = o.Lexical
				n.record.ValueType = core.InstanceID(strconv.FormatUint(o.Datatype, 10))
			default:
				panic("unrecognized op type")
			}
		}
		for _, n := range cache {
			bs, err := json.Marshal(n.record)
			if err != nil {
				return wrapError("marshalling updated note for storage", err)
			}
			if created[n.GetId()] {
				actual, err := txn.Create(bs)
				if err != nil {
					return wrapError("while saving note", err)
				}
				intended := strconv.FormatUint(n.GetId(), 10)
				if string(actual[0]) != intended {
					return errors.New("created note " + string(actual[0]) + ", intended " + intended)
				}
			} else {
				if err := txn.Save(bs); err != nil {
					return wrapError("while saving note", err)
				}
			}
		}
		return nil
	})
}

func (nm *NoteMap) Close() error { return nm.t.Close() }

type record struct {
	ID          core.InstanceID   `json:"_id"`
	Types       []core.InstanceID `json:"types,omitempty"`
	Supertypes  []core.InstanceID `json:"super_types,omitempty"`
	ValueString string            `json:"value_string,omitempty"`
	ValueType   core.InstanceID   `json:"value_type,omitempty"`
	Contents    []core.InstanceID `json:"contents,omitempty"`
}

type note struct {
	record
	loaded bool
}

func (n *note) load() error {
	panic("not implemented")
}
func (n *note) GetId() uint64 {
	u, _ := strconv.ParseUint(string(n.ID), 10, 64)
	return u
}
func (n *note) GetTypes() ([]notes.Note, error) {
	if err := n.load(); err != nil {
		return nil, err
	}
	ns := make([]notes.Note, len(n.Types))
	for i, id := range n.Types {
		ns[i] = &note{record: record{ID: id}}
	}
	return ns, nil
}
func (n *note) GetSupertypes() ([]notes.Note, error) {
	return notes.EmptyNote(0).GetSupertypes()
}
func (n *note) GetValue() (string, notes.Note, error) {
	return notes.EmptyNote(0).GetValue()
}
func (n *note) GetContents() ([]notes.Note, error) {
	return notes.EmptyNote(0).GetContents()
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package dynamic

import (
	"fmt"
	"io"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/rest"
)

type DynamicInterface interface {
	ClusterResource(resource schema.GroupVersionResource) DynamicResourceInterface
	NamespacedResource(resource schema.GroupVersionResource, namespace string) DynamicResourceInterface

	// Deprecated, this isn't how we want to do it
	ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface
	// Deprecated, this isn't how we want to do it
	NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface
}

type DynamicResourceInterface interface {
	Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	Delete(name string, options *metav1.DeleteOptions) error
	DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
	Get(name string, options metav1.GetOptions) (*unstructured.Unstructured, error)
	List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
	Watch(opts metav1.ListOptions) (watch.Interface, error)
	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error)
}

type dynamicClient struct {
	client *rest.RESTClient
}

var _ DynamicInterface = &dynamicClient{}

func NewForConfig(inConfig *rest.Config) (DynamicInterface, error) {
	config := rest.CopyConfig(inConfig)
	// for serializing the options
	config.GroupVersion = &schema.GroupVersion{}
	config.APIPath = "/if-you-see-this-search-for-the-break"
	config.AcceptContentTypes = "application/json"
	config.ContentType = "application/json"
	config.NegotiatedSerializer = basicNegotiatedSerializer{} // this gets used for discovery and error handling types
	if config.UserAgent == "" {
		config.UserAgent = rest.DefaultKubernetesUserAgent()
	}

	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}

	return &dynamicClient{client: restClient}, nil
}

type dynamicResourceClient struct {
	client      *dynamicClient
	namespace   string
	resource    schema.GroupVersionResource
	subresource string
}

func (c *dynamicClient) ClusterResource(resource schema.GroupVersionResource) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource}
}
func (c *dynamicClient) NamespacedResource(resource schema.GroupVersionResource, namespace string) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource, namespace: namespace}
}

func (c *dynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource, subresource: subresource}
}
func (c *dynamicClient) NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource, namespace: namespace, subresource: subresource}
}

func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("create not supported for subresources")
	}

	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Post().AbsPath(c.makeURLSegments("")...).Body(outBytes).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}

	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, err
	}
	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Put().AbsPath(c.makeURLSegments(accessor.GetName())...).Body(outBytes).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}

	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, err
	}

	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).Body(outBytes).Do()
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error {
	if opts == nil {
		opts = &metav1.DeleteOptions{}
	}
	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), opts)
	if err != nil {
		return err
	}

	result := c.client.client.Delete().AbsPath(c.makeURLSegments(name)...).Body(deleteOptionsByte).Do()
	return result.Error()
}

func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
	if len(c.subresource) > 0 {
		return fmt.Errorf("deletecollection not supported for subresources")
	}

	if opts == nil {
		opts = &metav1.DeleteOptions{}
	}
	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), opts)
	if err != nil {
		return err
	}

	result := c.client.client.Delete().AbsPath(c.makeURLSegments("")...).Body(deleteOptionsByte).SpecificallyVersionedParams(&listOptions, dynamicParameterCodec, versionV1).Do()
	return result.Error()
}

func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) {
	result := c.client.client.Get().AbsPath(c.makeURLSegments(name)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("list not supported for subresources")
	}

	result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	if list, ok := uncastObj.(*unstructured.UnstructuredList); ok {
		return list, nil
	}

	list, err := uncastObj.(*unstructured.Unstructured).ToList()
	if err != nil {
		return nil, err
	}
	return list, nil
}

func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("watch not supported for subresources")
	}

	internalGV := schema.GroupVersions{
		{Group: c.resource.Group, Version: runtime.APIVersionInternal},
		// always include the legacy group as a decoding target to handle non-error `Status` return types
		{Group: "", Version: runtime.APIVersionInternal},
	}
	s := &rest.Serializers{
		Encoder: watchNegotiatedSerializerInstance.EncoderForVersion(watchJsonSerializerInfo.Serializer, c.resource.GroupVersion()),
		Decoder: watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV),

		RenegotiatedDecoder: func(contentType string, params map[string]string) (runtime.Decoder, error) {
			return watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV), nil
		},
		StreamingSerializer: watchJsonSerializerInfo.StreamSerializer.Serializer,
		Framer:              watchJsonSerializerInfo.StreamSerializer.Framer,
	}

	wrappedDecoderFn := func(body io.ReadCloser) streaming.Decoder {
		framer := s.Framer.NewFrameReader(body)
		return streaming.NewDecoder(framer, s.StreamingSerializer)
	}

	opts.Watch = true
	return c.client.client.Get().AbsPath(c.makeURLSegments("")...).
		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
		WatchWithSpecificDecoders(wrappedDecoderFn, unstructured.UnstructuredJSONScheme)
}

func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) {
	result := c.client.client.Patch(pt).AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(data).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) makeURLSegments(name string) []string {
	url := []string{}
	if len(c.resource.Group) == 0 {
		url = append(url, "api")
	} else {
		url = append(url, "apis", c.resource.Group)
	}
	url = append(url, c.resource.Version)

	if len(c.namespace) > 0 {
		url = append(url, "namespaces", c.namespace)
	}
	url = append(url, c.resource.Resource)

	if len(name) > 0 {
		url = append(url, name)

		// subresources only work on things with names
		if len(c.subresource) > 0 {
			url = append(url, c.subresource)
		}
	} else {
		if len(c.subresource) > 0 {
			panic("somehow snuck a subresource and an empty name.  programmer error")
		}
	}

	return url
}
<commit_msg>make dynamic client slightly easier to use<commit_after>/*
Copyright 2018 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package dynamic

import (
	"fmt"
	"io"

	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/rest"
)

type DynamicInterface interface {
	Resource(resource schema.GroupVersionResource) NamespaceableDynamicResourceInterface

	// Deprecated, this isn't how we want to do it
	ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface
	// Deprecated, this isn't how we want to do it
	NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface
}

type DynamicResourceInterface interface {
	Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error)
	Delete(name string, options *metav1.DeleteOptions) error
	DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error
	Get(name string, options metav1.GetOptions) (*unstructured.Unstructured, error)
	List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error)
	Watch(opts metav1.ListOptions) (watch.Interface, error)
	Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error)
}

type NamespaceableDynamicResourceInterface interface {
	Namespace(string) DynamicResourceInterface
	DynamicResourceInterface
}

type dynamicClient struct {
	client *rest.RESTClient
}

var _ DynamicInterface = &dynamicClient{}

func NewForConfig(inConfig *rest.Config) (DynamicInterface, error) {
	config := rest.CopyConfig(inConfig)
	// for serializing the options
	config.GroupVersion = &schema.GroupVersion{}
	config.APIPath = "/if-you-see-this-search-for-the-break"
	config.AcceptContentTypes = "application/json"
	config.ContentType = "application/json"
	config.NegotiatedSerializer = basicNegotiatedSerializer{} // this gets used for discovery and error handling types
	if config.UserAgent == "" {
		config.UserAgent = rest.DefaultKubernetesUserAgent()
	}

	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}

	return &dynamicClient{client: restClient}, nil
}

type dynamicResourceClient struct {
	client      *dynamicClient
	namespace   string
	resource    schema.GroupVersionResource
	subresource string
}

func (c *dynamicClient) Resource(resource schema.GroupVersionResource) NamespaceableDynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource}
}

func (c *dynamicClient) ClusterSubresource(resource schema.GroupVersionResource, subresource string) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource, subresource: subresource}
}
func (c *dynamicClient) NamespacedSubresource(resource schema.GroupVersionResource, subresource, namespace string) DynamicResourceInterface {
	return &dynamicResourceClient{client: c, resource: resource, namespace: namespace, subresource: subresource}
}

func (c *dynamicResourceClient) Namespace(ns string) DynamicResourceInterface {
	ret := *c
	ret.namespace = ns
	return &ret
}

func (c *dynamicResourceClient) Create(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("create not supported for subresources")
	}

	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Post().AbsPath(c.makeURLSegments("")...).Body(outBytes).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}

	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) Update(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, err
	}
	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Put().AbsPath(c.makeURLSegments(accessor.GetName())...).Body(outBytes).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}

	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) UpdateStatus(obj *unstructured.Unstructured) (*unstructured.Unstructured, error) {
	accessor, err := meta.Accessor(obj)
	if err != nil {
		return nil, err
	}

	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
	if err != nil {
		return nil, err
	}

	result := c.client.client.Put().AbsPath(append(c.makeURLSegments(accessor.GetName()), "status")...).Body(outBytes).Do()
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) Delete(name string, opts *metav1.DeleteOptions) error {
	if opts == nil {
		opts = &metav1.DeleteOptions{}
	}
	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), opts)
	if err != nil {
		return err
	}

	result := c.client.client.Delete().AbsPath(c.makeURLSegments(name)...).Body(deleteOptionsByte).Do()
	return result.Error()
}

func (c *dynamicResourceClient) DeleteCollection(opts *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
	if len(c.subresource) > 0 {
		return fmt.Errorf("deletecollection not supported for subresources")
	}

	if opts == nil {
		opts = &metav1.DeleteOptions{}
	}
	deleteOptionsByte, err := runtime.Encode(deleteOptionsCodec.LegacyCodec(schema.GroupVersion{Version: "v1"}), opts)
	if err != nil {
		return err
	}

	result := c.client.client.Delete().AbsPath(c.makeURLSegments("")...).Body(deleteOptionsByte).SpecificallyVersionedParams(&listOptions, dynamicParameterCodec, versionV1).Do()
	return result.Error()
}

func (c *dynamicResourceClient) Get(name string, opts metav1.GetOptions) (*unstructured.Unstructured, error) {
	result := c.client.client.Get().AbsPath(c.makeURLSegments(name)...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) List(opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("list not supported for subresources")
	}

	result := c.client.client.Get().AbsPath(c.makeURLSegments("")...).SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	if list, ok := uncastObj.(*unstructured.UnstructuredList); ok {
		return list, nil
	}

	list, err := uncastObj.(*unstructured.Unstructured).ToList()
	if err != nil {
		return nil, err
	}
	return list, nil
}

func (c *dynamicResourceClient) Watch(opts metav1.ListOptions) (watch.Interface, error) {
	if len(c.subresource) > 0 {
		return nil, fmt.Errorf("watch not supported for subresources")
	}

	internalGV := schema.GroupVersions{
		{Group: c.resource.Group, Version: runtime.APIVersionInternal},
		// always include the legacy group as a decoding target to handle non-error `Status` return types
		{Group: "", Version: runtime.APIVersionInternal},
	}
	s := &rest.Serializers{
		Encoder: watchNegotiatedSerializerInstance.EncoderForVersion(watchJsonSerializerInfo.Serializer, c.resource.GroupVersion()),
		Decoder: watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV),

		RenegotiatedDecoder: func(contentType string, params map[string]string) (runtime.Decoder, error) {
			return watchNegotiatedSerializerInstance.DecoderToVersion(watchJsonSerializerInfo.Serializer, internalGV), nil
		},
		StreamingSerializer: watchJsonSerializerInfo.StreamSerializer.Serializer,
		Framer:              watchJsonSerializerInfo.StreamSerializer.Framer,
	}

	wrappedDecoderFn := func(body io.ReadCloser) streaming.Decoder {
		framer := s.Framer.NewFrameReader(body)
		return streaming.NewDecoder(framer, s.StreamingSerializer)
	}

	opts.Watch = true
	return c.client.client.Get().AbsPath(c.makeURLSegments("")...).
		SpecificallyVersionedParams(&opts, dynamicParameterCodec, versionV1).
		WatchWithSpecificDecoders(wrappedDecoderFn, unstructured.UnstructuredJSONScheme)
}

func (c *dynamicResourceClient) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (*unstructured.Unstructured, error) {
	result := c.client.client.Patch(pt).AbsPath(append(c.makeURLSegments(name), subresources...)...).Body(data).Do()
	if err := result.Error(); err != nil {
		return nil, err
	}
	retBytes, err := result.Raw()
	if err != nil {
		return nil, err
	}
	uncastObj, err := runtime.Decode(unstructured.UnstructuredJSONScheme, retBytes)
	if err != nil {
		return nil, err
	}
	return uncastObj.(*unstructured.Unstructured), nil
}

func (c *dynamicResourceClient) makeURLSegments(name string) []string {
	url := []string{}
	if len(c.resource.Group) == 0 {
		url = append(url, "api")
	} else {
		url = append(url, "apis", c.resource.Group)
	}
	url = append(url, c.resource.Version)

	if len(c.namespace) > 0 {
		url = append(url, "namespaces", c.namespace)
	}
	url = append(url, c.resource.Resource)

	if len(name) > 0 {
		url = append(url, name)

		// subresources only work on things with names
		if len(c.subresource) > 0 {
			url = append(url, c.subresource)
		}
	} else {
		if len(c.subresource) > 0 {
			panic("somehow snuck a subresource and an empty name.  programmer error")
		}
	}

	return url
}
<|endoftext|>


<commit_before>// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bdb

import (
	"io"
	"os"

	"github.com/btcsuite/btcwallet/walletdb"
	"github.com/coreos/bbolt"
)

// convertErr converts some bolt errors to the equivalent walletdb error.
func convertErr(err error) error {
	switch err {
	// Database open/create errors.
	case bbolt.ErrDatabaseNotOpen:
		return walletdb.ErrDbNotOpen
	case bbolt.ErrInvalid:
		return walletdb.ErrInvalid

	// Transaction errors.
	case bbolt.ErrTxNotWritable:
		return walletdb.ErrTxNotWritable
	case bbolt.ErrTxClosed:
		return walletdb.ErrTxClosed

	// Value/bucket errors.
	case bbolt.ErrBucketNotFound:
		return walletdb.ErrBucketNotFound
	case bbolt.ErrBucketExists:
		return walletdb.ErrBucketExists
	case bbolt.ErrBucketNameRequired:
		return walletdb.ErrBucketNameRequired
	case bbolt.ErrKeyRequired:
		return walletdb.ErrKeyRequired
	case bbolt.ErrKeyTooLarge:
		return walletdb.ErrKeyTooLarge
	case bbolt.ErrValueTooLarge:
		return walletdb.ErrValueTooLarge
	case bbolt.ErrIncompatibleValue:
		return walletdb.ErrIncompatibleValue
	}

	// Return the original error if none of the above applies.
	return err
}

// transaction represents a database transaction.  It can either by read-only or
// read-write and implements the walletdb Tx interfaces.  The transaction
// provides a root bucket against which all read and writes occur.
type transaction struct {
	boltTx *bbolt.Tx
}

func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket {
	return tx.ReadWriteBucket(key)
}

func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
	boltBucket := tx.boltTx.Bucket(key)
	if boltBucket == nil {
		return nil
	}
	return (*bucket)(boltBucket)
}

func (tx *transaction) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := tx.boltTx.CreateBucket(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

func (tx *transaction) DeleteTopLevelBucket(key []byte) error {
	err := tx.boltTx.DeleteBucket(key)
	if err != nil {
		return convertErr(err)
	}
	return nil
}

// Commit commits all changes that have been made through the root bucket and
// all of its sub-buckets to persistent storage.
//
// This function is part of the walletdb.Tx interface implementation.
func (tx *transaction) Commit() error {
	return convertErr(tx.boltTx.Commit())
}

// Rollback undoes all changes that have been made to the root bucket and all of
// its sub-buckets.
//
// This function is part of the walletdb.Tx interface implementation.
func (tx *transaction) Rollback() error {
	return convertErr(tx.boltTx.Rollback())
}

// bucket is an internal type used to represent a collection of key/value pairs
// and implements the walletdb Bucket interfaces.
type bucket bbolt.Bucket

// Enforce bucket implements the walletdb Bucket interfaces.
var _ walletdb.ReadWriteBucket = (*bucket)(nil)

// NestedReadWriteBucket retrieves a nested bucket with the given key.  Returns
// nil if the bucket does not exist.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
	boltBucket := (*bbolt.Bucket)(b).Bucket(key)
	// Don't return a non-nil interface to a nil pointer.
	if boltBucket == nil {
		return nil
	}
	return (*bucket)(boltBucket)
}

func (b *bucket) NestedReadBucket(key []byte) walletdb.ReadBucket {
	return b.NestedReadWriteBucket(key)
}

// CreateBucket creates and returns a new nested bucket with the given key.
// Returns ErrBucketExists if the bucket already exists, ErrBucketNameRequired
// if the key is empty, or ErrIncompatibleValue if the key value is otherwise
// invalid.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) CreateBucket(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := (*bbolt.Bucket)(b).CreateBucket(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

// CreateBucketIfNotExists creates and returns a new nested bucket with the
// given key if it does not already exist.  Returns ErrBucketNameRequired if the
// key is empty or ErrIncompatibleValue if the key value is otherwise invalid.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) CreateBucketIfNotExists(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := (*bbolt.Bucket)(b).CreateBucketIfNotExists(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

// DeleteNestedBucket removes a nested bucket with the given key.  Returns
// ErrTxNotWritable if attempted against a read-only transaction and
// ErrBucketNotFound if the specified bucket does not exist.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) DeleteNestedBucket(key []byte) error {
	return convertErr((*bbolt.Bucket)(b).DeleteBucket(key))
}

// ForEach invokes the passed function with every key/value pair in the bucket.
// This includes nested buckets, in which case the value is nil, but it does not
// include the key/value pairs within those nested buckets.
//
// NOTE: The values returned by this function are only valid during a
// transaction.  Attempting to access them after a transaction has ended will
// likely result in an access violation.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) ForEach(fn func(k, v []byte) error) error {
	return convertErr((*bbolt.Bucket)(b).ForEach(fn))
}

// Put saves the specified key/value pair to the bucket.  Keys that do not
// already exist are added and keys that already exist are overwritten.  Returns
// ErrTxNotWritable if attempted against a read-only transaction.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) Put(key, value []byte) error {
	return convertErr((*bbolt.Bucket)(b).Put(key, value))
}

// Get returns the value for the given key.  Returns nil if the key does
// not exist in this bucket (or nested buckets).
//
// NOTE: The value returned by this function is only valid during a
// transaction.  Attempting to access it after a transaction has ended
// will likely result in an access violation.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) Get(key []byte) []byte {
	return (*bbolt.Bucket)(b).Get(key)
}

// Delete removes the specified key from the bucket.  Deleting a key that does
// not exist does not return an error.  Returns ErrTxNotWritable if attempted
// against a read-only transaction.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) Delete(key []byte) error {
	return convertErr((*bbolt.Bucket)(b).Delete(key))
}

func (b *bucket) ReadCursor() walletdb.ReadCursor {
	return b.ReadWriteCursor()
}

// ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's
// key/value pairs and nested buckets in forward or backward order.
//
// This function is part of the walletdb.Bucket interface implementation.
func (b *bucket) ReadWriteCursor() walletdb.ReadWriteCursor {
	return (*cursor)((*bbolt.Bucket)(b).Cursor())
}

// cursor represents a cursor over key/value pairs and nested buckets of a
// bucket.
//
// Note that open cursors are not tracked on bucket changes and any
// modifications to the bucket, with the exception of cursor.Delete, invalidate
// the cursor. After invalidation, the cursor must be repositioned, or the keys
// and values returned may be unpredictable.
type cursor bbolt.Cursor

// Delete removes the current key/value pair the cursor is at without
// invalidating the cursor. Returns ErrTxNotWritable if attempted on a read-only
// transaction, or ErrIncompatibleValue if attempted when the cursor points to a
// nested bucket.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) Delete() error {
	return convertErr((*bbolt.Cursor)(c).Delete())
}

// First positions the cursor at the first key/value pair and returns the pair.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) First() (key, value []byte) {
	return (*bbolt.Cursor)(c).First()
}

// Last positions the cursor at the last key/value pair and returns the pair.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) Last() (key, value []byte) {
	return (*bbolt.Cursor)(c).Last()
}

// Next moves the cursor one key/value pair forward and returns the new pair.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) Next() (key, value []byte) {
	return (*bbolt.Cursor)(c).Next()
}

// Prev moves the cursor one key/value pair backward and returns the new pair.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) Prev() (key, value []byte) {
	return (*bbolt.Cursor)(c).Prev()
}

// Seek positions the cursor at the passed seek key. If the key does not exist,
// the cursor is moved to the next key after seek. Returns the new pair.
//
// This function is part of the walletdb.Cursor interface implementation.
func (c *cursor) Seek(seek []byte) (key, value []byte) {
	return (*bbolt.Cursor)(c).Seek(seek)
}

// db represents a collection of namespaces which are persisted and implements
// the walletdb.Db interface.  All database access is performed through
// transactions which are obtained through the specific Namespace.
type db bbolt.DB

// Enforce db implements the walletdb.Db interface.
var _ walletdb.DB = (*db)(nil)

func (db *db) beginTx(writable bool) (*transaction, error) {
	boltTx, err := (*bbolt.DB)(db).Begin(writable)
	if err != nil {
		return nil, convertErr(err)
	}
	return &transaction{boltTx: boltTx}, nil
}

func (db *db) BeginReadTx() (walletdb.ReadTx, error) {
	return db.beginTx(false)
}

func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) {
	return db.beginTx(true)
}

// Copy writes a copy of the database to the provided writer.  This call will
// start a read-only transaction to perform all operations.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Copy(w io.Writer) error {
	return convertErr((*bbolt.DB)(db).View(func(tx *bbolt.Tx) error {
		return tx.Copy(w)
	}))
}

// Close cleanly shuts down the database and syncs all data.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Close() error {
	return convertErr((*bbolt.DB)(db).Close())
}

// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// openDB opens the database at the provided path.  walletdb.ErrDbDoesNotExist
// is returned if the database doesn't exist and the create flag is not set.
func openDB(dbPath string, create bool) (walletdb.DB, error) {
	if !create && !fileExists(dbPath) {
		return nil, walletdb.ErrDbDoesNotExist
	}

	boltDB, err := bbolt.Open(dbPath, 0600, nil)
	return (*db)(boltDB), convertErr(err)
}
<commit_msg>walletdb/bdb: update interface implementation godoc<commit_after>// Copyright (c) 2014 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bdb

import (
	"io"
	"os"

	"github.com/btcsuite/btcwallet/walletdb"
	"github.com/coreos/bbolt"
)

// convertErr converts some bolt errors to the equivalent walletdb error.
func convertErr(err error) error {
	switch err {
	// Database open/create errors.
	case bbolt.ErrDatabaseNotOpen:
		return walletdb.ErrDbNotOpen
	case bbolt.ErrInvalid:
		return walletdb.ErrInvalid

	// Transaction errors.
	case bbolt.ErrTxNotWritable:
		return walletdb.ErrTxNotWritable
	case bbolt.ErrTxClosed:
		return walletdb.ErrTxClosed

	// Value/bucket errors.
	case bbolt.ErrBucketNotFound:
		return walletdb.ErrBucketNotFound
	case bbolt.ErrBucketExists:
		return walletdb.ErrBucketExists
	case bbolt.ErrBucketNameRequired:
		return walletdb.ErrBucketNameRequired
	case bbolt.ErrKeyRequired:
		return walletdb.ErrKeyRequired
	case bbolt.ErrKeyTooLarge:
		return walletdb.ErrKeyTooLarge
	case bbolt.ErrValueTooLarge:
		return walletdb.ErrValueTooLarge
	case bbolt.ErrIncompatibleValue:
		return walletdb.ErrIncompatibleValue
	}

	// Return the original error if none of the above applies.
	return err
}

// transaction represents a database transaction.  It can either by read-only or
// read-write and implements the walletdb Tx interfaces.  The transaction
// provides a root bucket against which all read and writes occur.
type transaction struct {
	boltTx *bbolt.Tx
}

func (tx *transaction) ReadBucket(key []byte) walletdb.ReadBucket {
	return tx.ReadWriteBucket(key)
}

func (tx *transaction) ReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
	boltBucket := tx.boltTx.Bucket(key)
	if boltBucket == nil {
		return nil
	}
	return (*bucket)(boltBucket)
}

func (tx *transaction) CreateTopLevelBucket(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := tx.boltTx.CreateBucket(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

func (tx *transaction) DeleteTopLevelBucket(key []byte) error {
	err := tx.boltTx.DeleteBucket(key)
	if err != nil {
		return convertErr(err)
	}
	return nil
}

// Commit commits all changes that have been made through the root bucket and
// all of its sub-buckets to persistent storage.
//
// This function is part of the walletdb.ReadWriteTx interface implementation.
func (tx *transaction) Commit() error {
	return convertErr(tx.boltTx.Commit())
}

// Rollback undoes all changes that have been made to the root bucket and all of
// its sub-buckets.
//
// This function is part of the walletdb.ReadTx interface implementation.
func (tx *transaction) Rollback() error {
	return convertErr(tx.boltTx.Rollback())
}

// bucket is an internal type used to represent a collection of key/value pairs
// and implements the walletdb Bucket interfaces.
type bucket bbolt.Bucket

// Enforce bucket implements the walletdb Bucket interfaces.
var _ walletdb.ReadWriteBucket = (*bucket)(nil)

// NestedReadWriteBucket retrieves a nested bucket with the given key.  Returns
// nil if the bucket does not exist.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) NestedReadWriteBucket(key []byte) walletdb.ReadWriteBucket {
	boltBucket := (*bbolt.Bucket)(b).Bucket(key)
	// Don't return a non-nil interface to a nil pointer.
	if boltBucket == nil {
		return nil
	}
	return (*bucket)(boltBucket)
}

func (b *bucket) NestedReadBucket(key []byte) walletdb.ReadBucket {
	return b.NestedReadWriteBucket(key)
}

// CreateBucket creates and returns a new nested bucket with the given key.
// Returns ErrBucketExists if the bucket already exists, ErrBucketNameRequired
// if the key is empty, or ErrIncompatibleValue if the key value is otherwise
// invalid.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) CreateBucket(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := (*bbolt.Bucket)(b).CreateBucket(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

// CreateBucketIfNotExists creates and returns a new nested bucket with the
// given key if it does not already exist.  Returns ErrBucketNameRequired if the
// key is empty or ErrIncompatibleValue if the key value is otherwise invalid.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) CreateBucketIfNotExists(key []byte) (walletdb.ReadWriteBucket, error) {
	boltBucket, err := (*bbolt.Bucket)(b).CreateBucketIfNotExists(key)
	if err != nil {
		return nil, convertErr(err)
	}
	return (*bucket)(boltBucket), nil
}

// DeleteNestedBucket removes a nested bucket with the given key.  Returns
// ErrTxNotWritable if attempted against a read-only transaction and
// ErrBucketNotFound if the specified bucket does not exist.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) DeleteNestedBucket(key []byte) error {
	return convertErr((*bbolt.Bucket)(b).DeleteBucket(key))
}

// ForEach invokes the passed function with every key/value pair in the bucket.
// This includes nested buckets, in which case the value is nil, but it does not
// include the key/value pairs within those nested buckets.
//
// NOTE: The values returned by this function are only valid during a
// transaction.  Attempting to access them after a transaction has ended will
// likely result in an access violation.
//
// This function is part of the walletdb.ReadBucket interface implementation.
func (b *bucket) ForEach(fn func(k, v []byte) error) error {
	return convertErr((*bbolt.Bucket)(b).ForEach(fn))
}

// Put saves the specified key/value pair to the bucket.  Keys that do not
// already exist are added and keys that already exist are overwritten.  Returns
// ErrTxNotWritable if attempted against a read-only transaction.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) Put(key, value []byte) error {
	return convertErr((*bbolt.Bucket)(b).Put(key, value))
}

// Get returns the value for the given key.  Returns nil if the key does
// not exist in this bucket (or nested buckets).
//
// NOTE: The value returned by this function is only valid during a
// transaction.  Attempting to access it after a transaction has ended
// will likely result in an access violation.
//
// This function is part of the walletdb.ReadBucket interface implementation.
func (b *bucket) Get(key []byte) []byte {
	return (*bbolt.Bucket)(b).Get(key)
}

// Delete removes the specified key from the bucket.  Deleting a key that does
// not exist does not return an error.  Returns ErrTxNotWritable if attempted
// against a read-only transaction.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) Delete(key []byte) error {
	return convertErr((*bbolt.Bucket)(b).Delete(key))
}

func (b *bucket) ReadCursor() walletdb.ReadCursor {
	return b.ReadWriteCursor()
}

// ReadWriteCursor returns a new cursor, allowing for iteration over the bucket's
// key/value pairs and nested buckets in forward or backward order.
//
// This function is part of the walletdb.ReadWriteBucket interface implementation.
func (b *bucket) ReadWriteCursor() walletdb.ReadWriteCursor {
	return (*cursor)((*bbolt.Bucket)(b).Cursor())
}

// cursor represents a cursor over key/value pairs and nested buckets of a
// bucket.
//
// Note that open cursors are not tracked on bucket changes and any
// modifications to the bucket, with the exception of cursor.Delete, invalidate
// the cursor. After invalidation, the cursor must be repositioned, or the keys
// and values returned may be unpredictable.
type cursor bbolt.Cursor

// Delete removes the current key/value pair the cursor is at without
// invalidating the cursor. Returns ErrTxNotWritable if attempted on a read-only
// transaction, or ErrIncompatibleValue if attempted when the cursor points to a
// nested bucket.
//
// This function is part of the walletdb.ReadWriteCursor interface implementation.
func (c *cursor) Delete() error {
	return convertErr((*bbolt.Cursor)(c).Delete())
}

// First positions the cursor at the first key/value pair and returns the pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) First() (key, value []byte) {
	return (*bbolt.Cursor)(c).First()
}

// Last positions the cursor at the last key/value pair and returns the pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Last() (key, value []byte) {
	return (*bbolt.Cursor)(c).Last()
}

// Next moves the cursor one key/value pair forward and returns the new pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Next() (key, value []byte) {
	return (*bbolt.Cursor)(c).Next()
}

// Prev moves the cursor one key/value pair backward and returns the new pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Prev() (key, value []byte) {
	return (*bbolt.Cursor)(c).Prev()
}

// Seek positions the cursor at the passed seek key. If the key does not exist,
// the cursor is moved to the next key after seek. Returns the new pair.
//
// This function is part of the walletdb.ReadCursor interface implementation.
func (c *cursor) Seek(seek []byte) (key, value []byte) {
	return (*bbolt.Cursor)(c).Seek(seek)
}

// db represents a collection of namespaces which are persisted and implements
// the walletdb.Db interface.  All database access is performed through
// transactions which are obtained through the specific Namespace.
type db bbolt.DB

// Enforce db implements the walletdb.Db interface.
var _ walletdb.DB = (*db)(nil)

func (db *db) beginTx(writable bool) (*transaction, error) {
	boltTx, err := (*bbolt.DB)(db).Begin(writable)
	if err != nil {
		return nil, convertErr(err)
	}
	return &transaction{boltTx: boltTx}, nil
}

func (db *db) BeginReadTx() (walletdb.ReadTx, error) {
	return db.beginTx(false)
}

func (db *db) BeginReadWriteTx() (walletdb.ReadWriteTx, error) {
	return db.beginTx(true)
}

// Copy writes a copy of the database to the provided writer.  This call will
// start a read-only transaction to perform all operations.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Copy(w io.Writer) error {
	return convertErr((*bbolt.DB)(db).View(func(tx *bbolt.Tx) error {
		return tx.Copy(w)
	}))
}

// Close cleanly shuts down the database and syncs all data.
//
// This function is part of the walletdb.Db interface implementation.
func (db *db) Close() error {
	return convertErr((*bbolt.DB)(db).Close())
}

// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
	if _, err := os.Stat(name); err != nil {
		if os.IsNotExist(err) {
			return false
		}
	}
	return true
}

// openDB opens the database at the provided path.  walletdb.ErrDbDoesNotExist
// is returned if the database doesn't exist and the create flag is not set.
func openDB(dbPath string, create bool) (walletdb.DB, error) {
	if !create && !fileExists(dbPath) {
		return nil, walletdb.ErrDbDoesNotExist
	}

	boltDB, err := bbolt.Open(dbPath, 0600, nil)
	return (*db)(boltDB), convertErr(err)
}
<|endoftext|>


<commit_before>package Marshal

import (
	. "ParquetType"
	. "SchemaHandler"
	"fmt"
	"testing"
)

type Student struct {
	Name    UTF8
	Age     INT32
	Weight  *INT32
	Classes *map[UTF8][]*Class
}

type Class struct {
	Name     UTF8
	ID       *INT64
	Required []UTF8
}

func (c Class) String() string {
	res := fmt.Sprintf("{Name:%s, ID:%v, Required:%s}", c.Name, c.ID, fmt.Sprint(c.Required))
	return res
}

func (s Student) String() string {
	cs := "{"
	for key, classes := range *s.Classes {
		s := string(key) + ":["
		for _, class := range classes {
			s += (*class).String() + ","
		}
		s += "]"
		cs += s
	}
	cs += "}"
	res := fmt.Sprintf("{Name:%s, Age:%d, Weight:%v, Classes:%s}", s.Name, s.Age, s.Weight, cs)
	return res
}

func TestMarshalUnmarshal(t *testing.T) {
	schemaHandler := NewSchemaHandlerFromStruct(new(Student))
	fmt.Println("SchemaHandler Finished")

	math01ID := INT64(1)
	math01 := Class{
		Name:     "Math1",
		ID:       &math01ID,
		Required: make([]UTF8, 0),
	}

	math02ID := INT64(2)
	math02 := Class{
		Name:     "Math2",
		ID:       &math02ID,
		Required: make([]UTF8, 0),
	}
	math02.Required = append(math02.Required, "Math01")

	physics := Class{
		Name:     "Physics",
		ID:       nil,
		Required: make([]UTF8, 0),
	}
	physics.Required = append(physics.Required, "Math01", "Math02")

	weight01 := INT32(60)
	stu01Class := make(map[UTF8][]*Class)
	stu01Class["Science"] = make([]*Class, 0)
	stu01Class["Science"] = append(stu01Class["Science"], &math01, &math02)
	stu01 := Student{
		Name:    "zxt",
		Age:     18,
		Weight:  &weight01,
		Classes: &stu01Class,
	}

	stu02Class := make(map[UTF8][]*Class)
	stu02Class["Science"] = make([]*Class, 0)
	stu02Class["Science"] = append(stu02Class["Science"], &physics)
	stu02 := Student{
		Name:    "tong",
		Age:     29,
		Weight:  nil,
		Classes: &stu02Class,
	}

	stus := make([]Student, 0)
	stus = append(stus, stu01, stu02)

	src := Marshal(stus, 0, len(stus), schemaHandler)
	fmt.Println("Marshal Finished")

	for name, table := range *src {
		fmt.Println(name)
		fmt.Println("Val: ", table.Values)
		fmt.Println("RL: ", table.RepetitionLevels)
		fmt.Println("DL: ", table.DefinitionLevels)
	}

	dst := make([]Student, 0)
	Unmarshal(src, &dst, schemaHandler)

	fmt.Println(dst)
	fmt.Println(stus)

}
<commit_msg>pass2<commit_after>package Marshal

import (
	. "ParquetType"
	. "SchemaHandler"
	"fmt"
	"testing"
)

type Student struct {
	Name    UTF8
	Age     INT32
	Weight  *INT32
	Classes *map[UTF8][]*Class
}

type Class struct {
	Name     UTF8
	ID       *INT64
	Required []UTF8
}

func (c Class) String() string {
	id := "nil"
	if c.ID != nil {
		id = fmt.Sprintf("%d", *c.ID)
	}
	res := fmt.Sprintf("{Name:%s, ID:%v, Required:%s}", c.Name, id, fmt.Sprint(c.Required))
	return res
}

func (s Student) String() string {
	weight := "nil"
	if s.Weight != nil {
		weight = fmt.Sprintf("%d", s.Weight)
	}

	cs := "{"
	for key, classes := range *s.Classes {
		s := string(key) + ":["
		for _, class := range classes {
			s += (*class).String() + ","
		}
		s += "]"
		cs += s
	}
	cs += "}"
	res := fmt.Sprintf("{Name:%s, Age:%d, Weight:%s, Classes:%s}", s.Name, s.Age, weight, cs)
	return res
}

func TestMarshalUnmarshal(t *testing.T) {
	schemaHandler := NewSchemaHandlerFromStruct(new(Student))
	fmt.Println("SchemaHandler Finished")

	math01ID := INT64(1)
	math01 := Class{
		Name:     "Math1",
		ID:       &math01ID,
		Required: make([]UTF8, 0),
	}

	math02ID := INT64(2)
	math02 := Class{
		Name:     "Math2",
		ID:       &math02ID,
		Required: make([]UTF8, 0),
	}
	math02.Required = append(math02.Required, "Math01")

	physics := Class{
		Name:     "Physics",
		ID:       nil,
		Required: make([]UTF8, 0),
	}
	physics.Required = append(physics.Required, "Math01", "Math02")

	weight01 := INT32(60)
	stu01Class := make(map[UTF8][]*Class)
	stu01Class["Science"] = make([]*Class, 0)
	stu01Class["Science"] = append(stu01Class["Science"], &math01, &math02)
	stu01 := Student{
		Name:    "zxt",
		Age:     18,
		Weight:  &weight01,
		Classes: &stu01Class,
	}

	stu02Class := make(map[UTF8][]*Class)
	stu02Class["Science"] = make([]*Class, 0)
	stu02Class["Science"] = append(stu02Class["Science"], &physics)
	stu02 := Student{
		Name:    "tong",
		Age:     29,
		Weight:  nil,
		Classes: &stu02Class,
	}

	stus := make([]Student, 0)
	stus = append(stus, stu01, stu02)

	src := Marshal(stus, 0, len(stus), schemaHandler)
	fmt.Println("Marshal Finished")

	for name, table := range *src {
		fmt.Println(name)
		fmt.Println("Val: ", table.Values)
		fmt.Println("RL: ", table.RepetitionLevels)
		fmt.Println("DL: ", table.DefinitionLevels)
	}

	dst := make([]Student, 0)
	Unmarshal(src, &dst, schemaHandler)

	fmt.Println(dst)
	fmt.Println(stus)

}
<|endoftext|>