func VarErrorf(status int, format string, values ...interface{}) VarError {
	return VarError{
		Status:  status,
		Message: fmt.Sprintf(format, values...),
	}
}
VarErrorf creates a new Error with a formatted message.
package snmp
import (
	"fmt"
	"io/ioutil"
	"log"
	"reflect"
	"sort"
	"github.com/PromonLogicalis/asn1"
)
type Getter func(oid asn1.Oid) (interface{}, error)
type Setter func(oid asn1.Oid, value interface{}) error
type Agent struct {
	log      *log.Logger
	ctx      *asn1.Context
	handlers []managedObject
	public   string
	private  string
}
func NewAgent() *Agent {
	a := &Agent{ctx: Asn1Context()}
	a.SetLogger(nil)
	a.SetCommunities("public", "private")
	return a
}
func (a *Agent) SetLogger(logger *log.Logger) {
	if logger == nil {
		logger = log.New(ioutil.Discard, "", 0)
	}
	a.log = logger
	a.ctx.SetLogger(logger)
}
func (a *Agent) SetCommunities(public, private string) {
	a.public, a.private = public, private
}
func (a *Agent) checkCommunity(community string) (rw bool, err error) {
	if community != a.public && community != a.private {
		err = fmt.Errorf("invalid community \"%s\"", community)
		return
	}
	if community == a.private {
		rw = true
	}
	return
}
func (a *Agent) AddRoManagedObject(oid asn1.Oid, getter Getter) error {
	return a.AddRwManagedObject(oid, getter, nil)
}
func (a *Agent) AddRwManagedObject(oid asn1.Oid, getter Getter,
	setter Setter) error {
	if getter == nil {
		return fmt.Errorf("a managed object should have at least a getter")
	}
	if setter == nil {
		setter = func(oid asn1.Oid, value interface{}) error {
			return VarErrorf(NotWritable, "OID %s is not writable", oid)
		}
	}
	if a.getManagedObject(oid, false) != nil {
		return fmt.Errorf("OID %d is already registered", oid)
	}
	h := managedObject{oid, nil, getter, setter}
	a.handlers = append(a.handlers, h)
	sort.Sort(sortableManagedObjects(a.handlers))
	return nil
}
type managedObject struct {
	oid asn1.Oid
	typ reflect.Type
	get Getter
	set Setter
}
type sortableManagedObjects []managedObject
func (h sortableManagedObjects) Len() int      { return len(h) }
func (h sortableManagedObjects) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h sortableManagedObjects) Less(i, j int) bool {
	return h[i].oid.Cmp(h[j].oid) < 0
}
func (a *Agent) getManagedObject(oid asn1.Oid, next bool) *managedObject {
	for _, h := range a.handlers {
		cmp := oid.Cmp(h.oid)
		if (!next && cmp == 0) || (next && cmp < 0) {
			return &h
		}
		if !next && cmp < 0 {
			break
		}
	}
	return nil
}
func (a *Agent) ProcessMessage(request *Message) (response *Message, err error) {
	if request.Version != 0 {
		err = fmt.Errorf("invalid SNMP version %d", request.Version)
		return
	}
	rw, err := a.checkCommunity(request.Community)
	if err != nil {
		return
	}
	a.log.Printf("request: %#v\n", request)
	var res GetResponsePdu
	switch pdu := request.Pdu.(type) {
	case GetRequestPdu:
		res = a.processPdu(Pdu(pdu), false, false)
	case GetNextRequestPdu:
		res = a.processPdu(Pdu(pdu), true, false)
	case SetRequestPdu:
		if rw {
			res = a.processPdu(Pdu(pdu), false, true)
		} else {
			res = GetResponsePdu(pdu)
			res.ErrorIndex = 1
			res.ErrorStatus = NoSuchName
		}
	default:
		err = fmt.Errorf("PDU not supported: %T", request.Pdu)
		return
	}
	copy := *request
	response = &copy
	response.Pdu = res
	a.log.Printf("response: %#v\n", response)
	return
}
func (a *Agent) ProcessDatagram(requestBytes []byte) (responseBytes []byte, err error) {
	request := Message{}
	ctx := Asn1Context()
	remaining, err := ctx.Decode(requestBytes, &request)
	if err != nil {
		return
	}
	if len(remaining) > 0 {
		err = fmt.Errorf("%d remaining bytes.\n", len(remaining))
		return
	}
	response, err := a.ProcessMessage(&request)
	if err != nil {
		return
	}
	responseBytes, err = ctx.Encode(*response)
	return
}
func (a *Agent) processPdu(pdu Pdu, next bool, set bool) GetResponsePdu {
	var variables []Variable
	var err error
	res := GetResponsePdu(pdu)
	for i, v := range pdu.Variables {
		a.log.Printf("oid: %s\n", v.Name)
		h := a.getManagedObject(v.Name, next)
		if h == nil {
			res.ErrorIndex = i + 1
			res.ErrorStatus = NoSuchName
			return res
		}
		var value interface{}
		if set {
			err = h.set(h.oid, v.Value)
		} else {
			value, err = h.get(h.oid)
		}
		if err != nil {
			res.ErrorIndex = i + 1
			if e, ok := err.(VarError); ok {
				res.ErrorStatus = e.Status
			} else {
				res.ErrorStatus = GenErr
			}
			return res
		}
		if !set {
			variables = append(variables, Variable{h.oid, value})
		}
	}
	if !set {
		res.Variables = variables
	}
	return res
}
type VarError struct {
	Status  int
	Message string
}
var _ error = VarError{}
func (e VarError) Error() string {
	return fmt.Sprintf("%s (status: %d)", e.Message, e.Status)
}