<commit_before>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
	"sync"
	"time"
)

// Zone represents a DNS zone. It's safe for concurrent use by
// multilpe goroutines.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
	mutex        *sync.RWMutex
	// timemodified?
	expired bool // Slave zone is expired
}

// SignatureConfig holds the parameters for zone (re)signing. This
// is copied from OpenDNSSEC. See:
// https://wiki.opendnssec.org/display/DOCS/kasp.xml
type SignatureConfig struct {
	// Validity period of the signatures, typically 2 to 4 weeks.
	Validity time.Duration
	// When the end of the validity approaches, how much time should remain
	// before we start to resign. Typical value is 3 days.
	Refresh time.Duration
	// Jitter is an amount of time added or subtracted from the
	// expiration time to ensure not all signatures expire a the same time.
	// Typical value is 12 hours.
	Jitter time.Duration
	// InceptionOffset is subtracted from the inception time to ensure badly
	// calibrated clocks on the internet can still validate a signature.
	// Typical value is 300 seconds.
	InceptionOffset time.Duration
}

func newSignatureConfig() *SignatureConfig {
	return &SignatureConfig{time.Duration(4*7*24) * time.Hour, time.Duration(3*24) * time.Hour, time.Duration(12) * time.Hour, time.Duration(300) * time.Second}
}

// DefaultSignaturePolicy has the following values. Validity is 4 weeks,
// Refresh is set to 3 days, Jitter to 12 hours and InceptionOffset to 300 seconds.
var DefaultSignatureConfig = newSignatureConfig()

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.mutex = new(sync.RWMutex)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// ZoneData holds all the RRs having their owner name equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
	mutex      *sync.RWMutex
}

// newZoneData creates a new zone data element
func newZoneData(s string) *ZoneData {
	zd := new(ZoneData)
	zd.Name = s
	zd.RR = make(map[uint16][]RR)
	zd.Signatures = make(map[uint16][]*RR_RRSIG)
	zd.mutex = new(sync.RWMutex)
	return zd
}

// toRadixName reverses a domain name so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		if s == "" {
			s = strings.ToLower(l) + s
			continue
		}
		s = strings.ToLower(l) + "." + s
	}
	return s
}

func (z *Zone) String() string {
	return z.Radix.String()
}

// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	// TODO(mg): quick check for doubles?
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd, exact := z.Radix.Find(key)
	if !exact {
		// Not an exact match, so insert new value
		defer z.mutex.Unlock()
		// Check if it's a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := newZoneData(r.Header().Name)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If the RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd, exact := z.Radix.Find(key)
	if !exact {
		defer z.mutex.Unlock()
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
// We can do better here, and include NXDOMAIN also. Much more efficient, only
// 1 tree walk.
func (z *Zone) Find(s string) *ZoneData {
	z.mutex.RLock()
	defer z.mutex.RUnlock()
	zd, e := z.Radix.Find(toRadixName(s))
	if !e {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Sign (re)signes the zone z. It adds keys to the zone (if not already there)
// and signs the keys with the KSKs and the rest of the zone with the ZSKs.
// NSEC is used for authenticated denial
// of existence. If config is nil DefaultSignatureConfig is used.
// TODO(mg): allow interaction with hsm
func (z *Zone) Sign(keys []*RR_DNSKEY, privkeys []PrivateKey, config *SignatureConfig) error {
	if config == nil {
		config = DefaultSignatureConfig
	}
	// TODO(mg): concurrently walk the zone and sign the rrsets
	// TODO(mg): nsec, or next pointer. Need to be a single tree-op

	return nil
}

// Sign each ZoneData in place.
// TODO(mg): assume not signed
func signZoneData(zd *ZoneData, privkeys []PrivateKey, signername string, config *SignatureConfig) {
	if zd.NonAuth == true {
		return
	}
	//s := new(RR_RRSIG)
	// signername
}
<commit_msg>add re-add predecessor<commit_after>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
	"sync"
	"time"
)

// Zone represents a DNS zone. It's safe for concurrent use by
// multilpe goroutines.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
	mutex        *sync.RWMutex
	// timemodified?
	expired bool // Slave zone is expired
}

// SignatureConfig holds the parameters for zone (re)signing. This
// is copied from OpenDNSSEC. See:
// https://wiki.opendnssec.org/display/DOCS/kasp.xml
type SignatureConfig struct {
	// Validity period of the signatures, typically 2 to 4 weeks.
	Validity time.Duration
	// When the end of the validity approaches, how much time should remain
	// before we start to resign. Typical value is 3 days.
	Refresh time.Duration
	// Jitter is an amount of time added or subtracted from the
	// expiration time to ensure not all signatures expire a the same time.
	// Typical value is 12 hours.
	Jitter time.Duration
	// InceptionOffset is subtracted from the inception time to ensure badly
	// calibrated clocks on the internet can still validate a signature.
	// Typical value is 300 seconds.
	InceptionOffset time.Duration
}

func newSignatureConfig() *SignatureConfig {
	return &SignatureConfig{time.Duration(4*7*24) * time.Hour, time.Duration(3*24) * time.Hour, time.Duration(12) * time.Hour, time.Duration(300) * time.Second}
}

// DefaultSignaturePolicy has the following values. Validity is 4 weeks,
// Refresh is set to 3 days, Jitter to 12 hours and InceptionOffset to 300 seconds.
var DefaultSignatureConfig = newSignatureConfig()

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.mutex = new(sync.RWMutex)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// ZoneData holds all the RRs having their owner name equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
	mutex      *sync.RWMutex
}

// newZoneData creates a new zone data element
func newZoneData(s string) *ZoneData {
	zd := new(ZoneData)
	zd.Name = s
	zd.RR = make(map[uint16][]RR)
	zd.Signatures = make(map[uint16][]*RR_RRSIG)
	zd.mutex = new(sync.RWMutex)
	return zd
}

// toRadixName reverses a domain name so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		if s == "" {
			s = strings.ToLower(l) + s
			continue
		}
		s = strings.ToLower(l) + "." + s
	}
	return s
}

func (z *Zone) String() string {
	return z.Radix.String()
}

// Insert inserts an RR into the zone. There is no check for duplicate data, although
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	// TODO(mg): quick check for doubles?
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd, exact := z.Radix.Find(key)
	if !exact {
		// Not an exact match, so insert new value
		defer z.mutex.Unlock()
		// Check if it's a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := newZoneData(r.Header().Name)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If the RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	z.mutex.Lock()
	zd, exact := z.Radix.Find(key)
	if !exact {
		defer z.mutex.Unlock()
		return nil
	}
	z.mutex.Unlock()
	zd.Value.(*ZoneData).mutex.Lock()
	defer zd.Value.(*ZoneData).mutex.Unlock()
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
// We can do better here, and include NXDOMAIN also. Much more efficient, only
// 1 tree walk.
func (z *Zone) Find(s string) *ZoneData {
	z.mutex.RLock()
	defer z.mutex.RUnlock()
	zd, e := z.Radix.Find(toRadixName(s))
	if !e {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
	z.mutex.RLock()
	defer z.mutex.RUnlock()
	zd := z.Radix.Predecessor(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Sign (re)signes the zone z. It adds keys to the zone (if not already there)
// and signs the keys with the KSKs and the rest of the zone with the ZSKs.
// NSEC is used for authenticated denial
// of existence. If config is nil DefaultSignatureConfig is used.
// TODO(mg): allow interaction with hsm
func (z *Zone) Sign(keys []*RR_DNSKEY, privkeys []PrivateKey, config *SignatureConfig) error {
	if config == nil {
		config = DefaultSignatureConfig
	}
	// TODO(mg): concurrently walk the zone and sign the rrsets
	// TODO(mg): nsec, or next pointer. Need to be a single tree-op

	return nil
}

// Sign each ZoneData in place.
// TODO(mg): assume not signed
func signZoneData(zd *ZoneData, privkeys []PrivateKey, signername string, config *SignatureConfig) {
	if zd.NonAuth == true {
		return
	}
	//s := new(RR_RRSIG)
	// signername
}
<|endoftext|>


<commit_before>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
)

// Zone represents a DNS zone. Currently there is no locking implemented.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
}

// ZoneData holds all the RRs having their ownername equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
}

// toRadixName reverses a domainname so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		s = strings.ToLower(l) + "." + s
	}
	return "." + s
}

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// Insert inserts an RR into the zone. There is no check for duplicate data, allthough
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	key := toRadixName(r.Header().Name)
	zd := z.Radix.Find(key)
	if zd == nil {
		// Check if its a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := new(ZoneData)
		zd.Name = r.Header().Name
		zd.RR = make(map[uint16][]RR)
		zd.Signatures = make(map[uint16][]*RR_RRSIG)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If there RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	zd := z.Radix.Find(key)
	if zd == nil {
		return nil
	}
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
func (z *Zone) Find(s string) *ZoneData {
	zd := z.Radix.Find(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
	zd := z.Radix.Predecessor(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}
<commit_msg>locking here...?<commit_after>package dns

// A structure for handling zone data

import (
	"github.com/miekg/radix"
	"strings"
)

// Zone represents a DNS zone. Currently there is no locking implemented.
type Zone struct {
	Origin       string // Origin of the zone
	Wildcard     int    // Whenever we see a wildcard name, this is incremented
	*radix.Radix        // Zone data
}

// ZoneData holds all the RRs having their ownername equal to Name.
type ZoneData struct {
	Name       string                 // Domain name for this node
	RR         map[uint16][]RR        // Map of the RR type to the RR
	Signatures map[uint16][]*RR_RRSIG // DNSSEC signatures for the RRs, stored under type covered
	NonAuth    bool                   // Always false, except for NSsets that differ from z.Origin
	mutex      *sync.RWMutex          // lock for reading/writing
}

// toRadixName reverses a domainname so that when we store it in the radix tree
// we preserve the nsec ordering of the zone (this idea was stolen from NSD).
// each label is also lowercased.
func toRadixName(d string) string {
	if d == "." {
		return "."
	}
	s := ""
	for _, l := range SplitLabels(d) {
		s = strings.ToLower(l) + "." + s
	}
	return "." + s
}

// NewZone creates an initialized zone with Origin set to origin.
func NewZone(origin string) *Zone {
	if origin == "" {
		origin = "."
	}
	if _, _, ok := IsDomainName(origin); !ok {
		return nil
	}
	z := new(Zone)
	z.Origin = Fqdn(origin)
	z.Radix = radix.New()
	return z
}

// Insert inserts an RR into the zone. There is no check for duplicate data, allthough
// Remove will remove all duplicates.
func (z *Zone) Insert(r RR) error {
	if !IsSubDomain(z.Origin, r.Header().Name) {
		return &Error{Err: "out of zone data", Name: r.Header().Name}
	}

	key := toRadixName(r.Header().Name)
	zd := z.Radix.Find(key)
	if zd == nil {
		// Check if its a wildcard name
		if len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
			z.Wildcard++
		}
		zd := new(ZoneData)
		zd.Name = r.Header().Name
		zd.RR = make(map[uint16][]RR)
		zd.Signatures = make(map[uint16][]*RR_RRSIG)
		switch t := r.Header().Rrtype; t {
		case TypeRRSIG:
			sigtype := r.(*RR_RRSIG).TypeCovered
			zd.Signatures[sigtype] = append(zd.Signatures[sigtype], r.(*RR_RRSIG))
		case TypeNS:
			// NS records with other names than z.Origin are non-auth
			if r.Header().Name != z.Origin {
				zd.NonAuth = true
			}
			fallthrough
		default:
			zd.RR[t] = append(zd.RR[t], r)
		}
		z.Radix.Insert(key, zd)
		return nil
	}
	// Name already there
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		zd.Value.(*ZoneData).Signatures[sigtype] = append(zd.Value.(*ZoneData).Signatures[sigtype], r.(*RR_RRSIG))
	case TypeNS:
		if r.Header().Name != z.Origin {
			zd.Value.(*ZoneData).NonAuth = true
		}
		fallthrough
	default:
		zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t], r)
	}
	return nil
}

// Remove removes the RR r from the zone. If there RR can not be found,
// this is a no-op.
func (z *Zone) Remove(r RR) error {
	key := toRadixName(r.Header().Name)
	zd := z.Radix.Find(key)
	if zd == nil {
		return nil
	}
	remove := false
	switch t := r.Header().Rrtype; t {
	case TypeRRSIG:
		sigtype := r.(*RR_RRSIG).TypeCovered
		for i, zr := range zd.Value.(*ZoneData).RR[sigtype] {
			if r == zr {
				zd.Value.(*ZoneData).RR[sigtype] = append(zd.Value.(*ZoneData).RR[sigtype][:i], zd.Value.(*ZoneData).RR[sigtype][i+1:]...)
				remove = true
			}
		}
	default:
		for i, zr := range zd.Value.(*ZoneData).RR[t] {
			if r == zr {
				zd.Value.(*ZoneData).RR[t] = append(zd.Value.(*ZoneData).RR[t][:i], zd.Value.(*ZoneData).RR[t][i+1:]...)
				remove = true
			}
		}
	}
	if remove && len(r.Header().Name) > 1 && r.Header().Name[0] == '*' && r.Header().Name[1] == '.' {
		z.Wildcard--
		if z.Wildcard < 0 {
			z.Wildcard = 0
		}
	}
	// TODO(mg): what to do if the whole structure is empty? Set it to nil?
	return nil
}

// Find looks up the ownername s in the zone and returns the
// data when found or nil when nothing is found.
func (z *Zone) Find(s string) *ZoneData {
	zd := z.Radix.Find(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}

// Predecessor searches the zone for a name shorter than s.
func (z *Zone) Predecessor(s string) *ZoneData {
	zd := z.Radix.Predecessor(toRadixName(s))
	if zd == nil {
		return nil
	}
	return zd.Value.(*ZoneData)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var KnownServiceNames = map[bluetooth.UUID]string{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
	bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
	bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
}

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind MetricKind
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return "Cycling Power Measure"
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
		return "Cycling Power Feature"
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return "Heart Rate"
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())

}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		src.ch.EnableNotifications(src.handleNotification)
	}
}

func (src *MetricSource) handleNotification(buf []byte) {
	fmt.Printf("%s: got %+v\n", src.Name(), buf)

	// TODO
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
	}
}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		cp := bluetooth.ConnectionParams{}
		for {
			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, cp)
			if err != nil {
				fmt.Printf("WARN: connect to <%s> failed: %+v\n", addr, err)
				continue
			}
			deviceChan <- device
			wg.Done()
			break
		}
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	for device := range deviceChan {
		println("discovering device services")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			println("found service", service.UUID().String())
			chars, err := service.DiscoverCharacteristics([]bluetooth.UUID{})
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				println("found characteristic", char.UUID().String())

				char.EnableNotifications(func(buf []byte) {
					println(char.UUID().String(), "data:", uint8(buf[0]))
				})
			}
		}
	}

	println("that's all!")
	select {}
}
<commit_msg>Begin parsing metrics<commit_after>package main

import (
	"flag"
	"fmt"
	"strings"
	"sync"

	"tinygo.org/x/bluetooth"
)

var KnownServiceUUIDs = []bluetooth.UUID{
	bluetooth.ServiceUUIDCyclingSpeedAndCadence,
	bluetooth.ServiceUUIDCyclingPower,
	bluetooth.ServiceUUIDHeartRate,

	// General controllable device, seems more involved.
	// bluetooth.ServiceUUIDFitnessMachine,
}

var KnownServiceCharacteristicUUIDs = map[bluetooth.UUID][]bluetooth.UUID{
	// https://www.bluetooth.com/specifications/specs/cycling-power-service-1-1/
	bluetooth.ServiceUUIDCyclingPower: {
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement,
		bluetooth.CharacteristicUUIDCyclingPowerFeature,
		// TODO:
		// Not a standardized characteristic, but this is offered by KICKR.
		// See GoldenCheetah source for some use examples:
		// https://github.com/GoldenCheetah/GoldenCheetah/blob/master/src/Train/BT40Device.cpp
		//
		// var WahooKickrControlCharacteristic = bluetooth.ParseUUID(
		// 	"a026e005-0a7d-4ab3-97fa-f1500f9feb8b"
		// )
		// TODO: Also, how does this one work?
		// bluetooth.CharacteristicUUIDCyclingPowerControlPoint,
	},
	bluetooth.ServiceUUIDHeartRate: {
		bluetooth.CharacteristicUUIDHeartRateMeasurement,
	},
}
var (
	KnownServiceNames = map[bluetooth.UUID]string{
		bluetooth.ServiceUUIDCyclingSpeedAndCadence: "Cycling Speed and Cadence",
		bluetooth.ServiceUUIDCyclingPower:           "Cycling Power",
		bluetooth.ServiceUUIDHeartRate:              "Heart Rate",
	}
	KnownCharacteristicNames = map[bluetooth.UUID]string{
		bluetooth.CharacteristicUUIDCyclingPowerMeasurement: "Cycling Power Measure",
		bluetooth.CharacteristicUUIDCyclingPowerFeature:     "Cycling Power Feature",
		bluetooth.CharacteristicUUIDHeartRateMeasurement:    "Heart Rate Measurement",
	}
)

type MetricKind int

const (
	MetricHeartRate = iota
	MetricCyclingPower
	MetricCyclingSpeed
	MetricCyclingCadence
)

type DeviceMetric struct {
	kind  MetricKind
	value int
}

type MetricSink struct {
}

type MetricSource struct {
	sinks []chan DeviceMetric

	svc *bluetooth.DeviceService
	ch  *bluetooth.DeviceCharacteristic
}

func NewMetricSource(
	svc *bluetooth.DeviceService,
	ch *bluetooth.DeviceCharacteristic,
) MetricSource {
	return MetricSource{
		sinks: []chan DeviceMetric{},
		svc:   svc,
		ch:    ch,
	}
}

func (src *MetricSource) Name() string {
	if name, ok := KnownCharacteristicNames[src.ch.UUID()]; ok {
		return name
	}
	return fmt.Sprintf("<unknown: %s>", src.ch.UUID().String())
}

func (src *MetricSource) AddSink(sink chan DeviceMetric) {
	src.sinks = append(src.sinks, sink)

	// Start listenening first time we add a sink
	if len(src.sinks) == 1 {
		handler := src.notificationHandler()
		src.ch.EnableNotifications(handler)
	}
}

func (src *MetricSource) notificationHandler() func([]byte) {
	switch src.ch.UUID() {
	case bluetooth.CharacteristicUUIDCyclingPowerMeasurement:
		return src.handleCyclingPowerMeasurement

	// TODO
	case bluetooth.CharacteristicUUIDCyclingPowerFeature:
	case bluetooth.CharacteristicUUIDHeartRateMeasurement:
		return src.handleHeartRateMeasurement
	}

	return nil
}

func (src *MetricSource) emit(m DeviceMetric) {
	for _, sink := range src.sinks {
		sink <- m
	}
}

const (
	// BPM size, 0 if u8, 1 if u16
	HeartRateFlagSize = 1 << 0

	// 00 unsupported
	// 01 unsupported
	// 10 supported, not detected
	// 11 supported, detected
	HeartRateFlagContactStatus = (1 << 1) | (1 << 2)

	HeartRateFlagHasEnergyExpended = 1 << 3
	HeartRateFlagHasRRInterval     = 1 << 4

	// bits 5-8 reserved
)

func (src *MetricSource) handleHeartRateMeasurement(buf []byte) {
	// malformed
	if len(buf) < 2 {
		return
	}

	flag := buf[0]

	is16Bit := (flag & HeartRateFlagSize) != 0
	contactStatus := (flag & HeartRateFlagContactStatus) >> 1

	contactSupported := contactStatus&(0b10) != 0
	contactFound := contactStatus&(0b01) != 0

	// No use sending this metric if the sensor isn't reading.
	if contactSupported && !contactFound {
		return
	}

	var hr int = int(buf[1])
	if is16Bit {
		hr = (hr << 8) | int(buf[2])
	}

	src.emit(DeviceMetric{
		kind:  MetricHeartRate,
		value: hr,
	})
}

const (
	CyclingPowerFlagHasPedalPowerBalance       = 1 << 0
	CyclingPowerFlagPedalPowerBalanceReference = 1 << 1
	CyclingPowerFlagHasAccumulatedTorque       = 1 << 2
	CyclingPowerFlagAccumulatedTorqueSource    = 1 << 3
	CyclingPowerFlagHasWheelRevolution         = 1 << 4
	CyclingPowerFlagHasCrankRevolution         = 1 << 5
	CyclingPowerFlagHasExtremeForceMagnitudes  = 1 << 6
	CyclingPowerFlagHasExtremeTorqueMagnitudes = 1 << 7
)

// Packet is [FLAG BYTE] [POWER WATTS]
func (src *MetricSource) handleCyclingPowerMeasurement(buf []byte) {
	// fmt.Printf("%s: got %+v\n", src.Name(), buf)
}

func scanDevices() {
	adapter := bluetooth.DefaultAdapter
	fmt.Println("Starting device scan...")

	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	// Keep track of addresses we've already looked ad
	addrsChecked := map[string]bool{}

	onScanResult := func(bt *bluetooth.Adapter, result bluetooth.ScanResult) {
		if _, seen := addrsChecked[result.Address.String()]; seen {
			return
		}
		addrsChecked[result.Address.String()] = true

		serviceNames := []string{}
		for _, s := range KnownServiceUUIDs {
			if !result.HasServiceUUID(s) {
				continue
			}

			serviceNames = append(serviceNames, KnownServiceNames[s])
		}

		// No matching services, skip this device.
		if len(serviceNames) == 0 {
			return
		}

		fmt.Printf("%s %-20s %-20s [RSSI:%d]\n",
			result.Address.String(),
			result.LocalName(),
			strings.Join(serviceNames, ","),
			result.RSSI,
		)
	}

	if err := adapter.Scan(onScanResult); err != nil {
		fmt.Println("FATAL: Failed to scan for devices")
		panic(err)
	}

	fmt.Println("Scan complete.")
}

var (
	flagScan                    = flag.Bool("scan", false, "scan for nearby devices")
	flagHeartRateAddr           = flag.String("hr", "", "address for heart rate device")
	flagCyclingPowerAddr        = flag.String("power", "", "address for cycling power device")
	flagCyclingSpeedCadenceAddr = flag.String("speed", "", "address for cycling speed/cadence device")
)

func init() {
	flag.Parse()
}

func main() {
	if *flagScan {
		scanDevices()
		return
	}

	adapter := bluetooth.DefaultAdapter
	if err := adapter.Enable(); err != nil {
		fmt.Println("FATAL: Failed to enable BLE")
		panic(err)
	}

	deviceChan := make(chan *bluetooth.Device)

	wg := sync.WaitGroup{}

	connectRetry := func(addr string) {
		uuid, err := bluetooth.ParseUUID(addr)
		if err != nil {
			fmt.Printf("FATAL: bad UUID given: <%s>\n", addr)
			panic(err)
		}

		cp := bluetooth.ConnectionParams{}
		for {
			// TODO: bluetooth.Address bit is not cross-platform.
			device, err := adapter.Connect(bluetooth.Address{uuid}, cp)
			if err != nil {
				fmt.Printf("WARN: connect to <%s> failed: %+v\n", addr, err)
				continue
			}

			deviceChan <- device
			break
		}

		wg.Done()
	}

	if *flagHeartRateAddr != "" {
		wg.Add(1)
		go connectRetry(*flagHeartRateAddr)
	}
	if *flagCyclingPowerAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingPowerAddr)
	}
	if *flagCyclingSpeedCadenceAddr != "" {
		wg.Add(1)
		go connectRetry(*flagCyclingSpeedCadenceAddr)
	}

	go func() {
		wg.Wait()
		close(deviceChan)
	}()

	metricsChan := make(chan DeviceMetric)
	go func() {
		for m := range metricsChan {
			fmt.Printf("Metric: %+v\n", m)
		}
	}()

	for device := range deviceChan {
		fmt.Println("Initializing device...")
		services, err := device.DiscoverServices(KnownServiceUUIDs)
		if err != nil {
			panic(err)
		}

		for _, service := range services {
			if name, ok := KnownServiceNames[service.UUID()]; ok {
				fmt.Printf("\tservice: %s\n", name)
			} else {
				fmt.Printf("\tservice: unknown <%+v>\n", service.UUID().String())
			}

			knownChars := KnownServiceCharacteristicUUIDs[service.UUID()]
			chars, err := service.DiscoverCharacteristics(knownChars)
			if err != nil {
				panic(err)
			}

			for _, char := range chars {
				name := KnownCharacteristicNames[char.UUID()]
				fmt.Printf("\t\tcharacteristic: %s\n", name)

				src := NewMetricSource(&service, &char)
				src.AddSink(metricsChan)
			}
		}
	}

	println("that's all!")
	select {}
}
<|endoftext|>


<commit_before>package a

import "errors"

var (
	ErrMethodNotSupport  = errors.New("METHOD NOT SUPPORT")
	ErrMethodNameNil     = errors.New("METHOD NAME NIL")
	ErrBizContentNameNil = errors.New("BIZ CONTENT NIL")
	ErrAppIdNil          = errors.New("APPID NIL")
	ErrSecretNil         = errors.New("SECRET NIL")
	ErrSign              = errors.New("SIGN ERROR")
	ErrVerifySign        = errors.New("VERIFY SIGN ERROR")
)

const (
	CAN_NOT_NIL  = "不能为空"
	FORAMT_ERROR = "格式错误"
)

type config struct {
	SandBoxEnable bool
}

var conf = newConfig()

func newConfig() *config {
	return &config{SandBoxEnable: false}
}

func EnableSandBox(enable bool) {
	conf.SandBoxEnable = enable
}

type Secret struct {
	AppId     string
	Pid       string
	AliPubRSA []byte
	PrivRSA   []byte
}

func (s *Secret) valid() error {
	if len(s.AppId) == 0 {
		return errors.New("appid 不能为空")
	}

	if len(s.Pid) == 0 {
		return errors.New("pid 不能为空")
	}

	if len(s.AliPubRSA) == 0 {
		return errors.New("支付宝公钥 不能为空")
	}

	if len(s.PrivRSA) == 0 {
		return errors.New("客户私钥 不能为空")
	}

	return nil
}

var secretLst map[string]Secret

func RegisterSecret(s ...Secret) error {
	if len(s) == 0 {
		return errors.New("配置参数不能为空!")
	}

	for _, v := range s {
		if err := v.valid(); err != nil {
			return err
		}
		secretLst[v.AppId] = v
	}

	return nil
}

func getSecret(appid string) Secret {
	return secretLst[appid]
}

func init() {
	secretLst = map[string]Secret{}
}
<commit_msg>对参数加同步锁<commit_after>package a

import "errors"
import "sync"

var (
	ErrMethodNotSupport  = errors.New("METHOD NOT SUPPORT")
	ErrMethodNameNil     = errors.New("METHOD NAME NIL")
	ErrBizContentNameNil = errors.New("BIZ CONTENT NIL")
	ErrAppIdNil          = errors.New("APPID NIL")
	ErrSecretNil         = errors.New("SECRET NIL")
	ErrSign              = errors.New("SIGN ERROR")
	ErrVerifySign        = errors.New("VERIFY SIGN ERROR")
)

const (
	CAN_NOT_NIL  = "不能为空"
	FORAMT_ERROR = "格式错误"
)

type config struct {
	SandBoxEnable bool
}

var conf = newConfig()

func newConfig() *config {
	return &config{SandBoxEnable: false}
}

func EnableSandBox(enable bool) {
	conf.SandBoxEnable = enable
}

type Secret struct {
	AppId     string
	Pid       string
	AliPubRSA []byte
	PrivRSA   []byte
}

func (s *Secret) valid() error {
	if len(s.AppId) == 0 {
		return errors.New("appid 不能为空")
	}

	if len(s.Pid) == 0 {
		return errors.New("pid 不能为空")
	}

	if len(s.AliPubRSA) == 0 {
		return errors.New("支付宝公钥 不能为空")
	}

	if len(s.PrivRSA) == 0 {
		return errors.New("客户私钥 不能为空")
	}

	return nil
}

var secretLst secretConfig

type secretConfig struct {
	Lst  map[string]Secret
	Lock sync.Mutex
}

func (s secretConfig) Get(k string) string {
	s.Lock.Lock()
	defer s.Lock.UnLock()
	return s.Lst[k]
}

func (s secretConfig) Set(k string, secret Secret) {
	s.Lock.Lock()
	defer d.Lock.UnLock()
	s.Lst[k] = v
}

func (s secretConfig) Del(k string) {
	s.Lock.Lock()
	defer d.Lock.UnLock()
	delete(s.Lst, k)
}

func RegisterSecret(s ...Secret) error {
	if len(s) == 0 {
		return errors.New("配置参数不能为空!")
	}

	for _, v := range s {
		if err := v.valid(); err != nil {
			return err
		}
		secretLst.Set(v.AppId, v)
	}

	return nil
}

func DeleteSecret(app_id string) {
	secretLst.Del(app_id)
}

func getSecret(appid string) Secret {
	return secretLst.Get(appid)
}

func init() {
	secretLst = secretConfig{}
}
<|endoftext|>


<commit_before>package hwaflib

func (ctx *Context) Version() string {
	version := "20131216"
	return version
}

func (ctx *Context) Revision() string {
	revision := "1b045f9"
	return revision
}

// EOF

<commit_msg>version: 20140115<commit_after>package hwaflib

func (ctx *Context) Version() string {
	version := "20140115"
	return version
}

func (ctx *Context) Revision() string {
	revision := "e22d10a"
	return revision
}

// EOF

<|endoftext|>


<commit_before>// Copyright 2015 realglobe, Inc.
//
// 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 idputil

import (
	"github.com/realglobe-Inc/edo-id-provider/database/session"
	"github.com/realglobe-Inc/edo-id-provider/scope"
	"github.com/realglobe-Inc/go-lib/erro"
)

// 許可情報。
type Consent interface {
	// 許可されているか。
	Allow(name string) bool
}

// 提供するスコープを返す。
// 必須スコープが許可されない場合のみ err が非 nil。
func ProvidedScopes(scopCons Consent, reqScops map[string]bool) (scops map[string]bool, err error) {
	scops = map[string]bool{}
	for scop := range reqScops {
		if scopCons.Allow(scop) {
			scops[scop] = true
			continue
		}

		// 許可されなかった。

		if scope.IsEssential(scop) {
			return nil, erro.New("essential scope " + scop + " is not allowed")
		}
	}
	return scops, nil
}

// 提供する属性を返す。
// scops: 提供するスコープ。
// 必須属性が許可されない場合のみ err が非 nil。
func ProvidedAttributes(scopCons, attrCons Consent, scops map[string]bool, reqClms session.Claims) (attrs map[string]bool, err error) {
	attrs = map[string]bool{}

	// スコープで許可された属性を加える。
	for scop := range scops {
		for attr := range scope.Attributes(scop) {
			attrs[attr] = true
		}
	}

	for attr, ent := range reqClms {
		// 許可スコープに含まれる属性も許す。
		if attrCons.Allow(attr) || scopCons.Allow(scope.FromAttribute(attr)) {
			attrs[attr] = true
			continue
		}

		// 許可されなかった。

		if ent.Essential() {
			return nil, erro.New("essential attribute " + attr + " is not allowed")
		}
	}

	return attrs, nil
}
<commit_msg>アカウント ID を常に返すように修正<commit_after>// Copyright 2015 realglobe, Inc.
//
// 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 idputil

import (
	"github.com/realglobe-Inc/edo-id-provider/database/session"
	"github.com/realglobe-Inc/edo-id-provider/scope"
	"github.com/realglobe-Inc/go-lib/erro"
)

// 許可情報。
type Consent interface {
	// 許可されているか。
	Allow(name string) bool
}

// 提供するスコープを返す。
// 必須スコープが許可されない場合のみ err が非 nil。
func ProvidedScopes(scopCons Consent, reqScops map[string]bool) (scops map[string]bool, err error) {
	scops = map[string]bool{}
	for scop := range reqScops {
		if scopCons.Allow(scop) {
			scops[scop] = true
			continue
		}

		// 許可されなかった。

		if scope.IsEssential(scop) {
			return nil, erro.New("essential scope " + scop + " is not allowed")
		}
	}
	return scops, nil
}

// 提供する属性を返す。
// scops: 提供するスコープ。
// 必須属性が許可されない場合のみ err が非 nil。
func ProvidedAttributes(scopCons, attrCons Consent, scops map[string]bool, reqClms session.Claims) (attrs map[string]bool, err error) {
	attrs = map[string]bool{}

	// アカウント ID は必須。
	attrs[tagSub] = true

	// スコープで許可された属性を加える。
	for scop := range scops {
		for attr := range scope.Attributes(scop) {
			attrs[attr] = true
		}
	}

	for attr, ent := range reqClms {
		// 許可スコープに含まれる属性も許す。
		if attrCons.Allow(attr) || scopCons.Allow(scope.FromAttribute(attr)) {
			attrs[attr] = true
			continue
		}

		// 許可されなかった。

		if ent.Essential() {
			return nil, erro.New("essential attribute " + attr + " is not allowed")
		}
	}

	return attrs, nil
}
<|endoftext|>


<commit_before>// +build OMIT

package main

import (
	"net/http"
)

func main() {
	// your http.Handle calls here
	http.ListenAndServe("localhost:4000", nil)
}
<commit_msg>go-tour: add log.Fatal to HTTP handlers exercise<commit_after>// +build OMIT

package main

import (
	"log"
	"net/http"
)

func main() {
	// your http.Handle calls here
	log.Fatal(http.ListenAndServe("localhost:4000", nil))
}
<|endoftext|>


<commit_before>package goble

import (
	"log"

	"github.com/dim13/goble/xpc"
)

const ALL = "__allEvents__"

// Event generated by blued, with associated data
type Event struct {
	Name               string
	State              string
	DeviceUUID         xpc.UUID
	ServiceUuid        string
	CharacteristicUuid string
	Peripheral         Peripheral
	Data               []byte
	Mtu                int
	IsNotification     bool
}

// The event handler function.
// Return true to terminate
type EventHandlerFunc func(Event) bool

// Emitter is an object to emit and handle Event(s)
type Emitter struct {
	handlers map[string]EventHandlerFunc
	event    chan Event
	verbose  bool
}

// Init initialize the emitter and start a goroutine to execute the event handlers
func (e *Emitter) Init() {
	e.handlers = make(map[string]EventHandlerFunc)
	e.event = make(chan Event)

	// event handler
	go func() {
		for {
			ev := <-e.event

			if fn, ok := e.handlers[ev.Name]; ok {
				if fn(ev) {
					break
				}
			} else if fn, ok := e.handlers[ALL]; ok {
				if fn(ev) {
					break
				}
			} else {
				if e.verbose {
					log.Println("unhandled Emit", ev)
				}
			}
		}

		close(e.event) // TOFIX: this causes new "emits" to panic.
	}()
}

func (e *Emitter) SetVerbose(v bool) {
	e.verbose = v
}

// Emit sends the event on the 'event' channel
func (e *Emitter) Emit(ev Event) {
	e.event <- ev
}

// On(event, cb) registers an handler for the specified event
func (e *Emitter) On(event string, fn EventHandlerFunc) {
	if fn == nil {
		delete(e.handlers, event)
	} else {
		e.handlers[event] = fn
	}
}
<commit_msg>Cleanup<commit_after>package goble

import (
	"log"

	"github.com/dim13/goble/xpc"
)

const ALL = "__allEvents__"

// Event generated by blued, with associated data
type Event struct {
	Name               string
	State              string
	DeviceUUID         xpc.UUID
	ServiceUuid        string
	CharacteristicUuid string
	Peripheral         Peripheral
	Data               []byte
	Mtu                int
	IsNotification     bool
}

// The event handler function.
// Return true to terminate
type EventHandlerFunc func(Event) bool

// Emitter is an object to emit and handle Event(s)
type Emitter struct {
	handlers map[string]EventHandlerFunc
	event    chan Event
	verbose  bool
}

// Init initialize the emitter and start a goroutine to execute the event handlers
func (e *Emitter) Init() {
	e.handlers = make(map[string]EventHandlerFunc)
	e.event = make(chan Event)

	// event handler
	go func() {
		defer close(e.event) // FIXME: this causes new "emits" to panic.
		for ev := range e.event {
			if fn, ok := e.handlers[ev.Name]; ok {
				if fn(ev) {
					return
				}
			} else if fn, ok := e.handlers[ALL]; ok {
				if fn(ev) {
					return
				}
			} else {
				if e.verbose {
					log.Println("unhandled Emit", ev)
				}
			}
		}
	}()
}

func (e *Emitter) SetVerbose(v bool) {
	e.verbose = v
}

// Emit sends the event on the 'event' channel
func (e *Emitter) Emit(ev Event) {
	e.event <- ev
}

// On(event, cb) registers an handler for the specified event
func (e *Emitter) On(event string, fn EventHandlerFunc) {
	if fn == nil {
		delete(e.handlers, event)
	} else {
		e.handlers[event] = fn
	}
}
<|endoftext|>


<commit_before>package goics

import (
	"io"
	"sort"
	"strings"
	"time"
)

// Line endings
const (
	CRLF   = "\r\n"
	CRLFSP = "\r\n "
)

// NewComponent returns a new Component and setups
// and setups Properties map for the component
// and also allows more Components inside it.
// VCALENDAR is a Component that has VEVENTS,
// VEVENTS can hold VALARMS
func NewComponent() *Component {
	return &Component{
		Elements:   make([]Componenter, 0),
		Properties: make(map[string]string),
	}
}

// Component is the base type for holding a
// ICal datatree before serilizing it
type Component struct {
	Tipo       string
	Elements   []Componenter
	Properties map[string]string
}

// Writes the component to the Writer
func (c *Component) Write(w *ICalEncode) {
	w.WriteLine("BEGIN:" + c.Tipo + CRLF)

	// Iterate over component properites
	var keys []string
	for k := range c.Properties {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, key := range keys {
		val := c.Properties[key]
		w.WriteLine(WriteStringField(key, val))
	}

	for _, xc := range c.Elements {
		xc.Write(w)
	}

	w.WriteLine("END:" + c.Tipo + CRLF)
}

// SetType of the component, as
// VCALENDAR VEVENT...
func (c *Component) SetType(t string) {
	c.Tipo = t
}

// AddComponent to the base component, just for building
// the component tree
func (c *Component) AddComponent(cc Componenter) {
	c.Elements = append(c.Elements, cc)
}

// AddProperty ads a property to the component
func (c *Component) AddProperty(key string, val string) {
	c.Properties[key] = val
}

// ICalEncode is the real writer, that wraps every line,
// in 75 chars length... Also gets the component from the emmiter
// and starts the iteration.
type ICalEncode struct {
	w io.Writer
}

// NewICalEncode generates a new encoder, and needs a writer
func NewICalEncode(w io.Writer) *ICalEncode {
	return &ICalEncode{
		w: w,
	}
}

// Encode the Component into the ical format
func (enc *ICalEncode) Encode(c ICalEmiter) {
	component := c.EmitICal()
	component.Write(enc)
}

// LineSize of the ics format
var LineSize = 75

// WriteLine in ics format max length = LineSize
// continuation lines start with a space.
func (enc *ICalEncode) WriteLine(s string) {
	if len(s) <= LineSize {
		io.WriteString(enc.w, s)
		return
	}
	length := len(s)
	current := 0
	// LineSize -2 is CRLF
	shortLine := LineSize - 2
	// First line write from 0 to totalline - 2 ( must include CRLFS)
	io.WriteString(enc.w, s[current:current+(shortLine)]+CRLFSP)
	current = shortLine
	// Rest of lines, we must include ^space at begining for marquing
	// continuation lines
	for (current + shortLine) <= length {
		io.WriteString(enc.w, s[current:current+(shortLine-1)]+CRLFSP)
		current += shortLine - 1
	}
	// Also we need to write the reminder
	io.WriteString(enc.w, s[current:length])
}

// FormatDateField returns a formated date: "DTEND;VALUE=DATE:20140406"
func FormatDateField(key string, val time.Time) (string, string) {
	return key + ";VALUE=DATE", val.Format("20060102")
}

// FormatDateTimeField in the form "X-MYDATETIME;VALUE=DATE-TIME:20120901T130000"
func FormatDateTimeField(key string, val time.Time) (string, string) {
	return key + ";VALUE=DATE-TIME", val.Format("20060102T150405")
}

// FormatDateTime as "DTSTART:19980119T070000Z"
func FormatDateTime(key string, val time.Time) (string, string) {
	return key, val.Format("20060102T150405Z")
}

// WriteStringField UID:asdfasdfаs@dfasdf.com
func WriteStringField(key string, val string) string {
	return strings.ToUpper(key) + ":" + quoteString(val) + CRLF
}

func quoteString(s string) string {
	s = strings.Replace(s, "\\", "\\\\", -1)
	s = strings.Replace(s, ";", "\\;", -1)
	s = strings.Replace(s, ",", "\\,", -1)
	s = strings.Replace(s, "\n", "\\n", -1)

	return s
}
<commit_msg>ensure UTC<commit_after>package goics

import (
	"io"
	"sort"
	"strings"
	"time"
)

// Line endings
const (
	CRLF   = "\r\n"
	CRLFSP = "\r\n "
)

// NewComponent returns a new Component and setups
// and setups Properties map for the component
// and also allows more Components inside it.
// VCALENDAR is a Component that has VEVENTS,
// VEVENTS can hold VALARMS
func NewComponent() *Component {
	return &Component{
		Elements:   make([]Componenter, 0),
		Properties: make(map[string]string),
	}
}

// Component is the base type for holding a
// ICal datatree before serilizing it
type Component struct {
	Tipo       string
	Elements   []Componenter
	Properties map[string]string
}

// Writes the component to the Writer
func (c *Component) Write(w *ICalEncode) {
	w.WriteLine("BEGIN:" + c.Tipo + CRLF)

	// Iterate over component properites
	var keys []string
	for k := range c.Properties {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	for _, key := range keys {
		val := c.Properties[key]
		w.WriteLine(WriteStringField(key, val))
	}

	for _, xc := range c.Elements {
		xc.Write(w)
	}

	w.WriteLine("END:" + c.Tipo + CRLF)
}

// SetType of the component, as
// VCALENDAR VEVENT...
func (c *Component) SetType(t string) {
	c.Tipo = t
}

// AddComponent to the base component, just for building
// the component tree
func (c *Component) AddComponent(cc Componenter) {
	c.Elements = append(c.Elements, cc)
}

// AddProperty ads a property to the component
func (c *Component) AddProperty(key string, val string) {
	c.Properties[key] = val
}

// ICalEncode is the real writer, that wraps every line,
// in 75 chars length... Also gets the component from the emmiter
// and starts the iteration.
type ICalEncode struct {
	w io.Writer
}

// NewICalEncode generates a new encoder, and needs a writer
func NewICalEncode(w io.Writer) *ICalEncode {
	return &ICalEncode{
		w: w,
	}
}

// Encode the Component into the ical format
func (enc *ICalEncode) Encode(c ICalEmiter) {
	component := c.EmitICal()
	component.Write(enc)
}

// LineSize of the ics format
var LineSize = 75

// WriteLine in ics format max length = LineSize
// continuation lines start with a space.
func (enc *ICalEncode) WriteLine(s string) {
	if len(s) <= LineSize {
		io.WriteString(enc.w, s)
		return
	}
	length := len(s)
	current := 0
	// LineSize -2 is CRLF
	shortLine := LineSize - 2
	// First line write from 0 to totalline - 2 ( must include CRLFS)
	io.WriteString(enc.w, s[current:current+(shortLine)]+CRLFSP)
	current = shortLine
	// Rest of lines, we must include ^space at begining for marquing
	// continuation lines
	for (current + shortLine) <= length {
		io.WriteString(enc.w, s[current:current+(shortLine-1)]+CRLFSP)
		current += shortLine - 1
	}
	// Also we need to write the reminder
	io.WriteString(enc.w, s[current:length])
}

// FormatDateField returns a formated date: "DTEND;VALUE=DATE:20140406"
func FormatDateField(key string, val time.Time) (string, string) {
	return key + ";VALUE=DATE", val.Format("20060102")
}

// FormatDateTimeField in the form "X-MYDATETIME;VALUE=DATE-TIME:20120901T130000"
func FormatDateTimeField(key string, val time.Time) (string, string) {
	return key + ";VALUE=DATE-TIME", val.Format("20060102T150405")
}

// FormatDateTime as "DTSTART:19980119T070000Z"
func FormatDateTime(key string, val time.Time) (string, string) {
	return key, val.UTC().Format("20060102T150405Z")
}

// WriteStringField UID:asdfasdfаs@dfasdf.com
func WriteStringField(key string, val string) string {
	return strings.ToUpper(key) + ":" + quoteString(val) + CRLF
}

func quoteString(s string) string {
	s = strings.Replace(s, "\\", "\\\\", -1)
	s = strings.Replace(s, ";", "\\;", -1)
	s = strings.Replace(s, ",", "\\,", -1)
	s = strings.Replace(s, "\n", "\\n", -1)

	return s
}
<|endoftext|>


<commit_before>package keys

import (
	"fmt"

	"github.com/ambientsound/pms/console"
	"github.com/ambientsound/pms/keysequence"
	"github.com/gdamore/tcell/v2"
)

// Binding holds a parsed, user provided key sequence.
type Binding struct {
	Command  string
	Sequence keysequence.KeySequence
}

// Sequencer holds all the keyboard bindings and their action mappings.
type Sequencer struct {
	binds []Binding
	event *tcell.EventKey
	input keysequence.KeySequence
}

// NewSequencer returns Sequencer.
func NewSequencer() *Sequencer {
	return &Sequencer{
		binds: make([]Binding, 0),
		input: make(keysequence.KeySequence, 0),
	}
}

// AddBind creates a new key mapping.
func (s *Sequencer) AddBind(seq keysequence.KeySequence, command string) error {
	if s.dupes(seq) {
		return fmt.Errorf("can't bind: conflicting with already bound key sequence")
	}
	s.binds = append(s.binds, Binding{Sequence: seq, Command: command})
	return nil
}

// RemoveBind removes a key mapping.
func (s *Sequencer) RemoveBind(seq keysequence.KeySequence) error {
	for i := range s.binds {
		if keysequence.Compare(s.binds[i].Sequence, seq) {
			// Overwrite this position with the last in the list
			s.binds[i] = s.binds[len(s.binds)-1]

			// Truncate to remove the (now duplicate) last entry
			s.binds = s.binds[:len(s.binds)-1]

			return nil
		}
	}

	return fmt.Errorf("can't unbind: sequence not bound")
}

// KeyInput feeds a keypress to the sequencer. Returns true if there is one match or more, or false if there is no match.
func (s *Sequencer) KeyInput(ev *tcell.EventKey) bool {
	console.Log("Key event: %s", keysequence.FormatKey(ev))
	s.input = append(s.input, ev)
	if len(s.find(s.input)) == 0 {
		s.input = make(keysequence.KeySequence, 0)
		return false
	}
	return true
}

// String returns the current input sequence as a string.
func (s *Sequencer) String() string {
	return keysequence.Format(s.input)
}

// dupes returns true if binding the given key event sequence will conflict with any other bound sequences.
func (s *Sequencer) dupes(seq keysequence.KeySequence) bool {
	matches := s.find(seq)
	return len(matches) > 0
}

// find returns a list of potential matches to key bindings.
func (s *Sequencer) find(seq keysequence.KeySequence) []Binding {
	binds := make([]Binding, 0)
	for i := range s.binds {
		if keysequence.StartsWith(s.binds[i].Sequence, seq) {
			binds = append(binds, s.binds[i])
		}
	}
	return binds
}

// Match returns a key binding if the current input sequence is found.
func (s *Sequencer) Match() *Binding {
	binds := s.find(s.input)
	if len(binds) != 1 {
		return nil
	}
	b := binds[0]
	//console.Log("Possible match found: %+v ||| %+v", b.Sequence, s.input)
	if !keysequence.Compare(b.Sequence, s.input) {
		return nil
	}
	//console.Log("Match found: %+v", b)
	s.input = make(keysequence.KeySequence, 0)
	return &b
}
<commit_msg>Pet the static checker: Remove unused struct member<commit_after>package keys

import (
	"fmt"

	"github.com/ambientsound/pms/console"
	"github.com/ambientsound/pms/keysequence"
	"github.com/gdamore/tcell/v2"
)

// Binding holds a parsed, user provided key sequence.
type Binding struct {
	Command  string
	Sequence keysequence.KeySequence
}

// Sequencer holds all the keyboard bindings and their action mappings.
type Sequencer struct {
	binds []Binding
	input keysequence.KeySequence
}

// NewSequencer returns Sequencer.
func NewSequencer() *Sequencer {
	return &Sequencer{
		binds: make([]Binding, 0),
		input: make(keysequence.KeySequence, 0),
	}
}

// AddBind creates a new key mapping.
func (s *Sequencer) AddBind(seq keysequence.KeySequence, command string) error {
	if s.dupes(seq) {
		return fmt.Errorf("can't bind: conflicting with already bound key sequence")
	}
	s.binds = append(s.binds, Binding{Sequence: seq, Command: command})
	return nil
}

// RemoveBind removes a key mapping.
func (s *Sequencer) RemoveBind(seq keysequence.KeySequence) error {
	for i := range s.binds {
		if keysequence.Compare(s.binds[i].Sequence, seq) {
			// Overwrite this position with the last in the list
			s.binds[i] = s.binds[len(s.binds)-1]

			// Truncate to remove the (now duplicate) last entry
			s.binds = s.binds[:len(s.binds)-1]

			return nil
		}
	}

	return fmt.Errorf("can't unbind: sequence not bound")
}

// KeyInput feeds a keypress to the sequencer. Returns true if there is one match or more, or false if there is no match.
func (s *Sequencer) KeyInput(ev *tcell.EventKey) bool {
	console.Log("Key event: %s", keysequence.FormatKey(ev))
	s.input = append(s.input, ev)
	if len(s.find(s.input)) == 0 {
		s.input = make(keysequence.KeySequence, 0)
		return false
	}
	return true
}

// String returns the current input sequence as a string.
func (s *Sequencer) String() string {
	return keysequence.Format(s.input)
}

// dupes returns true if binding the given key event sequence will conflict with any other bound sequences.
func (s *Sequencer) dupes(seq keysequence.KeySequence) bool {
	matches := s.find(seq)
	return len(matches) > 0
}

// find returns a list of potential matches to key bindings.
func (s *Sequencer) find(seq keysequence.KeySequence) []Binding {
	binds := make([]Binding, 0)
	for i := range s.binds {
		if keysequence.StartsWith(s.binds[i].Sequence, seq) {
			binds = append(binds, s.binds[i])
		}
	}
	return binds
}

// Match returns a key binding if the current input sequence is found.
func (s *Sequencer) Match() *Binding {
	binds := s.find(s.input)
	if len(binds) != 1 {
		return nil
	}
	b := binds[0]
	//console.Log("Possible match found: %+v ||| %+v", b.Sequence, s.input)
	if !keysequence.Compare(b.Sequence, s.input) {
		return nil
	}
	//console.Log("Match found: %+v", b)
	s.input = make(keysequence.KeySequence, 0)
	return &b
}
<|endoftext|>


<commit_before>package server

import (
	"crypto/md5"
	"fmt"
	"io"
	"net/http"

	"github.com/arxdsilva/Stsuru/web/persist"
	"github.com/asaskevich/govalidator"

	"github.com/alecthomas/template"
	"github.com/gorilla/mux"
)

// Server ...
type Server struct {
	Storage persist.Storage
	URL     string
}

// Listen Registers the routes used by Stsuru and redirects traffic
func (s *Server) Listen() {
	r := mux.NewRouter()
	r.HandleFunc("/", s.Home)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
	r.HandleFunc("/r/{id}", s.Redirect)
	r.HandleFunc("/link/add", s.AddLink)
	r.HandleFunc("/l/r/{id}", s.RemoveLink)
	http.Handle("/", r)
	fmt.Println("The server is now live @ localhost:8080")
	http.ListenAndServe(":8080", nil)
}

// AddLink validates the request's URL and asks Mongo to add It on list
func (s *Server) AddLink(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	link := r.Form["user_link"][0]
	v := validateURL(link)
	if !v {
		http.Redirect(w, r, "/", http.StatusNotModified)
		return
	}
	linkshort, dbHash := hash(link, s.URL)
	_, err := s.Storage.FindHash(dbHash)
	if err != nil {
		err = s.Storage.Save(link, linkshort, dbHash)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusNotModified)
}

// Home querys Storage for all It's elements and calls the specified HTML to load them into the page.
func (s *Server) Home(w http.ResponseWriter, r *http.Request) {
	path := "tmpl/index.html"
	d, err := s.Storage.List()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t, err := template.ParseFiles(path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = t.Execute(w, d)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// CSS loads style into the page
func CSS(w http.ResponseWriter, r *http.Request) {
	http.StripPrefix("/css/", http.FileServer(http.Dir("css/")))
}

// RemoveLink searches db for a certain link & removes It if It exists
func (s *Server) RemoveLink(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)
	idHash := id["id"]
	s.Storage.Remove(idHash)
	http.Redirect(w, r, "/", http.StatusFound)
}

// Redirect takes the hashed URL and checks Mongo If It exists;
func (s *Server) Redirect(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)
	idHash := id["id"]
	l, err := s.Storage.FindHash(idHash)
	if err != nil {
		http.Redirect(w, r, l, http.StatusNotFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)

}

func hash(link, path string) (string, string) {
	h := md5.New()
	io.WriteString(h, link)
	hash := string(h.Sum(nil))
	linkShort := fmt.Sprintf("%s%x", path, hash)
	dbHash := fmt.Sprintf("%x", hash)
	return linkShort, dbHash
}

func validateURL(l string) bool {
	isURL := govalidator.IsURL(l)
	validURL := govalidator.IsRequestURL(l)
	if !isURL || !validURL {
		return false
	}
	return true
}
<commit_msg>In #12 Implements Shorten() in server<commit_after>package server

import (
	"fmt"
	"net/http"
	"net/url"

	"github.com/arxdsilva/Stsuru/shortener"
	"github.com/arxdsilva/Stsuru/web/persist"

	"github.com/alecthomas/template"
	"github.com/gorilla/mux"
)

// Server ...
type Server struct {
	Storage persist.Storage
	URL     string
}

// Listen Registers the routes used by Stsuru and redirects traffic
func (s *Server) Listen() {
	r := mux.NewRouter()
	r.HandleFunc("/", s.Home)
	http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("css/"))))
	r.HandleFunc("/r/{id}", s.Redirect)
	r.HandleFunc("/link/add", s.AddLink)
	r.HandleFunc("/l/r/{id}", s.RemoveLink)
	http.Handle("/", r)
	fmt.Println("The server is now live @ localhost:8080")
	http.ListenAndServe(":8080", nil)
}

// AddLink validates the request's URL and asks Mongo to add It on list
func (s *Server) AddLink(w http.ResponseWriter, r *http.Request) {
	r.ParseForm()
	link := r.Form["user_link"][0]
	// Implementing Shorten
	u, err := url.Parse(link)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	newShort := shortener.NewShorten{
		U: u,
	}
	n, err := newShort.Shorten()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
	linkshort := n.String()
	dbHash := n.Path
	_, err = s.Storage.FindHash(dbHash)
	if err != nil {
		err = s.Storage.Save(link, linkshort, dbHash)
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusNotModified)
}

// Home querys Storage for all It's elements and calls the specified HTML to load them into the page.
func (s *Server) Home(w http.ResponseWriter, r *http.Request) {
	path := "tmpl/index.html"
	d, err := s.Storage.List()
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	t, err := template.ParseFiles(path)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	err = t.Execute(w, d)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

// CSS loads style into the page
func CSS(w http.ResponseWriter, r *http.Request) {
	http.StripPrefix("/css/", http.FileServer(http.Dir("css/")))
}

// RemoveLink searches db for a certain link & removes It if It exists
func (s *Server) RemoveLink(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)
	idHash := id["id"]
	s.Storage.Remove(idHash)
	http.Redirect(w, r, "/", http.StatusFound)
}

// Redirect takes the hashed URL and checks Mongo If It exists;
func (s *Server) Redirect(w http.ResponseWriter, r *http.Request) {
	id := mux.Vars(r)
	idHash := id["id"]
	l, err := s.Storage.FindHash(idHash)
	if err != nil {
		http.Redirect(w, r, l, http.StatusNotFound)
		return
	}
	http.Redirect(w, r, "/", http.StatusFound)

}
<|endoftext|>


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

package server

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/server/handler"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"github.com/gorilla/mux"
	"github.com/skratchdot/open-golang/open"
	"math"
	"net/http"
	"strings"
)

var (
	BasePath      = "/"
	TagPathPrefix = fmt.Sprintf("%stags.html#", BasePath)

	// Dynamic Routes
	PrintHandlerRoute  = `/{path:.+\.print$|print$}`
	JsonHandlerRoute   = `/{path:.+\.json$|json$}`
	LatestHandlerRoute = `/{path:.+\.latest$|latest$}`
	RtfHandlerRoute    = `/{path:.+\.rtf$|rtf$}`
	UpdateHandlerRoute = `/{path:.+\.ws$|ws$}`

	ItemHandlerRoute = "/{path:.*$}"

	TagmapHandlerRoute                = "/tags.html"
	SitemapHandlerRoute               = "/sitemap.html"
	XmlSitemapHandlerRoute            = "/sitemap.xml"
	RssHandlerRoute                   = "/feed.rss"
	RobotsTxtHandlerRoute             = "/robots.txt"
	SearchHandlerRoute                = "/search"
	OpenSearchDescriptionHandlerRoute = "/opensearch.xml"

	TypeAheadSearchHandlerRoute = "/search.json"
	TypeAheadTitlesHandlerRoute = "/titles.json"

	// Static Routes
	ThemeFolderRoute = "/theme"
)

func New(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter) (*Server, error) {

	// paths
	patherFactory := webpaths.NewFactory(logger, repository)
	itemPathProvider := patherFactory.Absolute(BasePath)
	tagPathProvider := patherFactory.Absolute(TagPathPrefix)
	webPathProvider := webpaths.NewWebPathProvider(patherFactory, itemPathProvider, tagPathProvider)

	// orchestrator
	orchestratorFactory := orchestrator.NewFactory(logger, config, repository, parser, converter, webPathProvider)

	// handlers
	handlerFactory := handler.NewFactory(logger, config, *orchestratorFactory)

	return &Server{
		logger: logger,
		config: config,

		handlerFactory: handlerFactory,
	}, nil

}

type Server struct {
	isRunning bool

	logger logger.Logger
	config config.Config

	handlerFactory *handler.Factory
}

func (server *Server) IsRunning() bool {
	return server.isRunning
}

func (server *Server) Start() chan error {
	result := make(chan error)

	go func() {
		server.isRunning = true

		// register requst routers
		requestRouter := mux.NewRouter()

		// websocket update handler
		// updateHub := update.NewHub(server.logger, server.updateHub)
		// go updateHub.Run()

		updateHandler := server.handlerFactory.NewUpdateHandler()
		requestRouter.Handle(UpdateHandlerRoute, websocket.Handler(updateHandler.Func()))

		// serve auxiliary dynamic files
		requestRouter.HandleFunc(RobotsTxtHandlerRoute, server.handlerFactory.NewRobotsTxtHandler().Func())
		requestRouter.HandleFunc(XmlSitemapHandlerRoute, server.handlerFactory.NewXmlSitemapHandler().Func())
		requestRouter.HandleFunc(TagmapHandlerRoute, server.handlerFactory.NewTagsHandler().Func())
		requestRouter.HandleFunc(SitemapHandlerRoute, server.handlerFactory.NewSitemapHandler().Func())
		requestRouter.HandleFunc(RssHandlerRoute, server.handlerFactory.NewRssHandler().Func())
		requestRouter.HandleFunc(PrintHandlerRoute, server.handlerFactory.NewPrintHandler().Func())
		requestRouter.HandleFunc(SearchHandlerRoute, server.handlerFactory.NewSearchHandler().Func())
		requestRouter.HandleFunc(OpenSearchDescriptionHandlerRoute, server.handlerFactory.NewOpenSearchDescriptionHandler().Func())
		requestRouter.HandleFunc(TypeAheadSearchHandlerRoute, server.handlerFactory.NewTypeAheadSearchHandler().Func())
		requestRouter.HandleFunc(TypeAheadTitlesHandlerRoute, server.handlerFactory.NewTypeAheadTitlesHandler().Func())

		// serve static files
		if themeFolder := server.config.ThemeFolder(); fsutil.DirectoryExists(themeFolder) {
			s := http.StripPrefix(ThemeFolderRoute, http.FileServer(http.Dir(themeFolder)))
			requestRouter.PathPrefix(ThemeFolderRoute).Handler(s)
		}

		// serve items
		requestRouter.HandleFunc(RtfHandlerRoute, server.handlerFactory.NewRtfHandler().Func())
		requestRouter.HandleFunc(JsonHandlerRoute, server.handlerFactory.NewJsonHandler().Func())
		requestRouter.HandleFunc(LatestHandlerRoute, server.handlerFactory.NewLatestHandler().Func())
		requestRouter.HandleFunc(ItemHandlerRoute, server.handlerFactory.NewItemHandler().Func())

		// start http server: http
		httpBinding := server.getHttpBinding()
		server.logger.Info("Starting http server %q\n", httpBinding)

		if err := http.ListenAndServe(httpBinding, requestRouter); err != nil {
			result <- fmt.Errorf("Server failed with error: %v", err)
		} else {
			result <- nil
		}

		server.isRunning = false
	}()

	// open the repository in the browser
	open.Run(server.getAddress())

	return result
}

func (server *Server) getHttpBinding() string {

	hostname := server.getHostname()
	port := server.getPort()

	if strings.TrimSpace(hostname) == "" {
		fmt.Sprintf(":%v", port)
	}

	return fmt.Sprintf("%s:%v", hostname, port)
}

func (server *Server) getAddress() string {
	hostname := server.getHostname()
	port := server.getPort()

	switch port {
	case 80:
		return fmt.Sprintf("http://%s", hostname)
	default:
		return fmt.Sprintf("http://%s:%v", hostname, port)
	}

	panic("Unreachable")
}

func (server *Server) getHostname() string {
	hostname := strings.ToLower(strings.TrimSpace(server.config.Server.Http.Hostname))
	if hostname == "" {
		return "localhost"
	}

	return hostname
}

func (server *Server) getPort() int {
	port := server.config.Server.Http.Port
	if port < 1 || port > math.MaxUint16 {
		panic(fmt.Sprintf("%q is an invalid value for a port. Ports can only be in the range of %v to %v,", port, 1, math.MaxUint16))
	}

	return port
}
<commit_msg>Introduced a max-age cache control header for static content<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package server

import (
	"code.google.com/p/go.net/websocket"
	"fmt"
	"github.com/andreaskoch/allmark2/common/config"
	"github.com/andreaskoch/allmark2/common/logger"
	"github.com/andreaskoch/allmark2/common/util/fsutil"
	"github.com/andreaskoch/allmark2/dataaccess"
	"github.com/andreaskoch/allmark2/services/converter"
	"github.com/andreaskoch/allmark2/services/parser"
	"github.com/andreaskoch/allmark2/web/orchestrator"
	"github.com/andreaskoch/allmark2/web/server/handler"
	"github.com/andreaskoch/allmark2/web/webpaths"
	"github.com/gorilla/mux"
	"github.com/skratchdot/open-golang/open"
	"math"
	"net/http"
	"strings"
)

var (
	BasePath      = "/"
	TagPathPrefix = fmt.Sprintf("%stags.html#", BasePath)

	// Dynamic Routes
	PrintHandlerRoute  = `/{path:.+\.print$|print$}`
	JsonHandlerRoute   = `/{path:.+\.json$|json$}`
	LatestHandlerRoute = `/{path:.+\.latest$|latest$}`
	RtfHandlerRoute    = `/{path:.+\.rtf$|rtf$}`
	UpdateHandlerRoute = `/{path:.+\.ws$|ws$}`

	ItemHandlerRoute = "/{path:.*$}"

	TagmapHandlerRoute                = "/tags.html"
	SitemapHandlerRoute               = "/sitemap.html"
	XmlSitemapHandlerRoute            = "/sitemap.xml"
	RssHandlerRoute                   = "/feed.rss"
	RobotsTxtHandlerRoute             = "/robots.txt"
	SearchHandlerRoute                = "/search"
	OpenSearchDescriptionHandlerRoute = "/opensearch.xml"

	TypeAheadSearchHandlerRoute = "/search.json"
	TypeAheadTitlesHandlerRoute = "/titles.json"

	// Static Routes
	ThemeFolderRoute = "/theme"
)

const (
	CACHE_MAXAGE_STATICCONTENT = 86400
)

func New(logger logger.Logger, config config.Config, repository dataaccess.Repository, parser parser.Parser, converter converter.Converter) (*Server, error) {

	// paths
	patherFactory := webpaths.NewFactory(logger, repository)
	itemPathProvider := patherFactory.Absolute(BasePath)
	tagPathProvider := patherFactory.Absolute(TagPathPrefix)
	webPathProvider := webpaths.NewWebPathProvider(patherFactory, itemPathProvider, tagPathProvider)

	// orchestrator
	orchestratorFactory := orchestrator.NewFactory(logger, config, repository, parser, converter, webPathProvider)

	// handlers
	handlerFactory := handler.NewFactory(logger, config, *orchestratorFactory)

	return &Server{
		logger: logger,
		config: config,

		handlerFactory: handlerFactory,
	}, nil

}

type Server struct {
	isRunning bool

	logger logger.Logger
	config config.Config

	handlerFactory *handler.Factory
}

func (server *Server) IsRunning() bool {
	return server.isRunning
}

func (server *Server) Start() chan error {
	result := make(chan error)

	go func() {
		server.isRunning = true

		// register requst routers
		requestRouter := mux.NewRouter()

		// websocket update handler
		// updateHub := update.NewHub(server.logger, server.updateHub)
		// go updateHub.Run()

		updateHandler := server.handlerFactory.NewUpdateHandler()
		requestRouter.Handle(UpdateHandlerRoute, websocket.Handler(updateHandler.Func()))

		// serve auxiliary dynamic files
		requestRouter.HandleFunc(RobotsTxtHandlerRoute, server.handlerFactory.NewRobotsTxtHandler().Func())
		requestRouter.HandleFunc(XmlSitemapHandlerRoute, server.handlerFactory.NewXmlSitemapHandler().Func())
		requestRouter.HandleFunc(TagmapHandlerRoute, server.handlerFactory.NewTagsHandler().Func())
		requestRouter.HandleFunc(SitemapHandlerRoute, server.handlerFactory.NewSitemapHandler().Func())
		requestRouter.HandleFunc(RssHandlerRoute, server.handlerFactory.NewRssHandler().Func())
		requestRouter.HandleFunc(PrintHandlerRoute, server.handlerFactory.NewPrintHandler().Func())
		requestRouter.HandleFunc(SearchHandlerRoute, server.handlerFactory.NewSearchHandler().Func())
		requestRouter.HandleFunc(OpenSearchDescriptionHandlerRoute, server.handlerFactory.NewOpenSearchDescriptionHandler().Func())
		requestRouter.HandleFunc(TypeAheadSearchHandlerRoute, server.handlerFactory.NewTypeAheadSearchHandler().Func())
		requestRouter.HandleFunc(TypeAheadTitlesHandlerRoute, server.handlerFactory.NewTypeAheadTitlesHandler().Func())

		// serve static files
		if themeFolder := server.config.ThemeFolder(); fsutil.DirectoryExists(themeFolder) {
			s := http.StripPrefix(ThemeFolderRoute, maxAgeHandler(CACHE_MAXAGE_STATICCONTENT, http.FileServer(http.Dir(themeFolder))))
			requestRouter.PathPrefix(ThemeFolderRoute).Handler(s)
		}

		// serve items
		requestRouter.HandleFunc(RtfHandlerRoute, server.handlerFactory.NewRtfHandler().Func())
		requestRouter.HandleFunc(JsonHandlerRoute, server.handlerFactory.NewJsonHandler().Func())
		requestRouter.HandleFunc(LatestHandlerRoute, server.handlerFactory.NewLatestHandler().Func())
		requestRouter.HandleFunc(ItemHandlerRoute, server.handlerFactory.NewItemHandler().Func())

		// start http server: http
		httpBinding := server.getHttpBinding()
		server.logger.Info("Starting http server %q\n", httpBinding)

		if err := http.ListenAndServe(httpBinding, requestRouter); err != nil {
			result <- fmt.Errorf("Server failed with error: %v", err)
		} else {
			result <- nil
		}

		server.isRunning = false
	}()

	// open the repository in the browser
	open.Run(server.getAddress())

	return result
}

func (server *Server) getHttpBinding() string {

	hostname := server.getHostname()
	port := server.getPort()

	if strings.TrimSpace(hostname) == "" {
		fmt.Sprintf(":%v", port)
	}

	return fmt.Sprintf("%s:%v", hostname, port)
}

func (server *Server) getAddress() string {
	hostname := server.getHostname()
	port := server.getPort()

	switch port {
	case 80:
		return fmt.Sprintf("http://%s", hostname)
	default:
		return fmt.Sprintf("http://%s:%v", hostname, port)
	}

	panic("Unreachable")
}

func (server *Server) getHostname() string {
	hostname := strings.ToLower(strings.TrimSpace(server.config.Server.Http.Hostname))
	if hostname == "" {
		return "localhost"
	}

	return hostname
}

func (server *Server) getPort() int {
	port := server.config.Server.Http.Port
	if port < 1 || port > math.MaxUint16 {
		panic(fmt.Sprintf("%q is an invalid value for a port. Ports can only be in the range of %v to %v,", port, 1, math.MaxUint16))
	}

	return port
}

func maxAgeHandler(seconds int, h http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Add("Cache-Control", fmt.Sprintf("max-age=%d, public, must-revalidate, proxy-revalidate", seconds))
		h.ServeHTTP(w, r)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net/http"
	"time"

	"github.com/bradfitz/go-smtpd/smtpd"
	"github.com/bradfitz/runsit/listen"
)

var (
	webAddr  = listen.NewFlag("web", ":8080", "Web port")
	smtpAddr = listen.NewFlag("smtp", ":2500", "SMTP port")
)

type server struct {
	httpServer http.Server
	smtpServer *smtpd.Server
}

func main() {
	flag.Parse()
	webln, err := webAddr.Listen()
	if err != nil {
		log.Fatalf("web listen: %v", err)
	}
	smtpln, err := smtpAddr.Listen()
	if err != nil {
		log.Fatalf("SMTP listen: %v", err)
	}

	srv := &server{
		smtpServer: &smtpd.Server{
			ReadTimeout:  5 * time.Minute,
			WriteTimeout: 5 * time.Minute,
		},
	}

	// TODO: Actually hook up the lookup
	// lookup := &lookupHandler {
	// 	lookup: NewLookup(&DummyStorage{}),
	// }
	// http.Handle("/.well-known/webfinger", lookup)

	log.Printf("Server up. web %s, smtp %s", webAddr, smtpAddr)
	go srv.runSMTP(smtpln)

	log.Fatal(srv.httpServer.Serve(webln))
}
<commit_msg>Fix merge conflict in webfistd.go<commit_after>package main

import (
	"flag"
	"log"
	"net/http"

	"github.com/bradfitz/go-smtpd/smtpd"
	"github.com/bradfitz/runsit/listen"
)

var (
	webAddr  = listen.NewFlag("web", ":8080", "Web port")
	smtpAddr = listen.NewFlag("smtp", ":2500", "SMTP port")
)

type server struct {
	httpServer http.Server
	smtpServer *smtpd.Server
}

func main() {
	flag.Parse()
	webln, err := webAddr.Listen()
	if err != nil {
		log.Fatalf("web listen: %v", err)
	}
	smtpln, err := smtpAddr.Listen()
	if err != nil {
		log.Fatalf("SMTP listen: %v", err)
	}

	var srv server
	srv.initSMTPServer()
	log.Printf("Server up. web %s, smtp %s", webAddr, smtpAddr)
	go srv.runSMTP(smtpln)

	// TODO: Actually hook up the lookup
	// lookup := &lookupHandler {
	// 	lookup: NewLookup(&DummyStorage{}),
	// }
	// http.Handle("/.well-known/webfinger", lookup)

	log.Fatal(srv.httpServer.Serve(webln))
}
<|endoftext|>


<commit_before>package internal

import (
	"fmt"
	"strings"

	"math"
	"regexp"
)

type CmdParams struct {
	Types         []FuncMeasurement
	Treshold      float64
	MinLines      int
	Top           int
	IncludeTests  bool
	IncludeVendor bool
	Ignore        *regexp.Regexp
}

type FunctionStats struct {
	Receiver, Name, Location string
	stats                    map[FuncMeasurement]float64
}

func newFunctionStats(name, location string) *FunctionStats {
	return &FunctionStats{
		Name:     name,
		Location: location,
		stats:    map[FuncMeasurement]float64{},
	}
}

func (fs FunctionStats) FuncWithRecv() string {
	if fs.Receiver == "" {
		return fs.Name
	}
	return fmt.Sprintf("%s.%s", fs.Receiver, fs.Name)
}

func (fs FunctionStats) Get(ty FuncMeasurement) (float64, error) {
	if strings.Index(string(ty), "/") > 0 {
		parts := strings.Split(string(ty), "/")
		if len(parts) != 2 {
			return 0, fmt.Errorf("Invalit type %s", ty)
		}
		a, b := FuncMeasurement(parts[0]), FuncMeasurement(parts[1])
		if !isValidBasicType(a) || !isValidBasicType(b) {
			return 0, fmt.Errorf("Invalit type %s", ty)
		}
		val1, val2 := fs.stats[a], fs.stats[b]

		if val2 == 0 {
			return math.NaN(), nil
		}
		return val1 / val2, nil
	} else if !isValidBasicType(ty) {
		return 0, fmt.Errorf("Invalit type %s", ty)
	}
	return fs.stats[ty], nil
}

func (fs FunctionStats) Set(ty FuncMeasurement, value float64) {
	fs.stats[ty] = value
}

func (fs FunctionStats) Incr(ty FuncMeasurement, value float64) {
	fs.stats[ty] += value
}

type FunctionStatsList struct {
	SortType FuncMeasurement
	Stats    []FunctionStats
}

func (s FunctionStatsList) Len() int      { return len(s.Stats) }
func (s FunctionStatsList) Swap(i, j int) { s.Stats[i], s.Stats[j] = s.Stats[j], s.Stats[i] }
func (s FunctionStatsList) Less(i, j int) bool {
	val1, _ := s.Stats[i].Get(s.SortType)
	val2, _ := s.Stats[j].Get(s.SortType)
	return val1 >= val2
}
<commit_msg>Pretty print receivers<commit_after>package internal

import (
	"fmt"
	"strings"

	"math"
	"regexp"
)

type CmdParams struct {
	Types         []FuncMeasurement
	Treshold      float64
	MinLines      int
	Top           int
	IncludeTests  bool
	IncludeVendor bool
	Ignore        *regexp.Regexp
}

type FunctionStats struct {
	Receiver, Name, Location string
	stats                    map[FuncMeasurement]float64
}

func newFunctionStats(name, location string) *FunctionStats {
	return &FunctionStats{
		Name:     name,
		Location: location,
		stats:    map[FuncMeasurement]float64{},
	}
}

func (fs FunctionStats) FuncWithRecv() string {
	if fs.Receiver == "" {
		return fs.Name + ""
	}
	return fmt.Sprintf("(%s) %s", fs.Receiver, fs.Name)
}

func (fs FunctionStats) Get(ty FuncMeasurement) (float64, error) {
	if strings.Index(string(ty), "/") > 0 {
		parts := strings.Split(string(ty), "/")
		if len(parts) != 2 {
			return 0, fmt.Errorf("Invalit type %s", ty)
		}
		a, b := FuncMeasurement(parts[0]), FuncMeasurement(parts[1])
		if !isValidBasicType(a) || !isValidBasicType(b) {
			return 0, fmt.Errorf("Invalit type %s", ty)
		}
		val1, val2 := fs.stats[a], fs.stats[b]

		if val2 == 0 {
			return math.NaN(), nil
		}
		return val1 / val2, nil
	} else if !isValidBasicType(ty) {
		return 0, fmt.Errorf("Invalit type %s", ty)
	}
	return fs.stats[ty], nil
}

func (fs FunctionStats) Set(ty FuncMeasurement, value float64) {
	fs.stats[ty] = value
}

func (fs FunctionStats) Incr(ty FuncMeasurement, value float64) {
	fs.stats[ty] += value
}

type FunctionStatsList struct {
	SortType FuncMeasurement
	Stats    []FunctionStats
}

func (s FunctionStatsList) Len() int      { return len(s.Stats) }
func (s FunctionStatsList) Swap(i, j int) { s.Stats[i], s.Stats[j] = s.Stats[j], s.Stats[i] }
func (s FunctionStatsList) Less(i, j int) bool {
	val1, _ := s.Stats[i].Get(s.SortType)
	val2, _ := s.Stats[j].Get(s.SortType)
	return val1 >= val2
}
<|endoftext|>