<commit_before>/*
 *        Copyright (C) 2014 Stefan 'glaxx' Luecke
 *
 *        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 <http://www.gnu.org/licenses/>.
 *
 *        Authors:	Stefan Luecke <glaxx@glaxx.net>
 */

 /*
 * TODO List:
 * - Test leap year behavior
 * - Test
 */

package cron

import (
//	"fmt"
	"time"
//	"sort"
	"log"
	"container/list"
)

type crontime struct {
	second []int
	minute []int
	hour   []int
	dow    []int //Day of Week
	dom    []int //Day of Month
	month  []int
	calculatedTime time.Time
	calculationInProgress bool
	eventList list.List
}

func (c *crontime) DurationUntilNextEvent() time.Duration {
	return c.nextEvent().Sub(time.Now())
}

func (c *crontime) GetNextEvent() time.Time {
	return c.eventList.Front().Value.(time.Time)
}

func (c *crontime) nextEvent() time.Time {
	if !c.calculationInProgress && c.eventList.Len() == 0{
		r := c.calculateEvent(time.Now())
		go c.fillList(r)
		return r
	} else if c.calculationInProgress && c.eventList.Len() == 0{
		// shit just got real aka TODO
		panic("Shit")

	} else if c.eventList.Len() > 0 {
		e := c.eventList.Front()
		r := e.Value.(time.Time)
		c.eventList.Remove(e)
		go c.fillList(c.eventList.Back().Value.(time.Time))
		return r
	}
	panic("shit 2")
}

func (c *crontime) fillList(baseTime time.Time) {
	if c.eventList.Len() == 0 {
		c.eventList.PushBack(c.calculateEvent(baseTime))
	}
	for ; c.eventList.Len() < 5; {
		c.eventList.PushBack(c.calculateEvent(c.eventList.Back().Value.(time.Time)))
	}
}

func (c *crontime) setCalculationInProgress(set bool) {
	c.calculationInProgress = set
}

// This functions calculates the next event
func (c *crontime) calculateEvent(baseTime time.Time) time.Time{
	c.calculationInProgress = true
	defer c.setCalculationInProgress(false)
	baseTime = setNanoecond(baseTime, 10000)
	c.calculatedTime = baseTime // Ignore all Events in the Past & initial 'result'
	//c.calculatedTime = setNanoecond(c.calculatedTime, 10000)
	c.nextValidMonth(baseTime)
	c.nextValidDay(baseTime)
	c.nextValidHour(baseTime)
	c.nextValidMinute(baseTime)
	c.nextValidSecond(baseTime)
	log.Println("Cronbee has found a time stamp: ", c.calculatedTime)
	return c.calculatedTime
}

// Calculates the next valid Month based upon the previous results.
func (c *crontime) nextValidMonth(baseTime time.Time) {
	for _, mon := range c.month {
		if mon >= int(c.calculatedTime.Month()) {
			c.calculatedTime = setMonth(c.calculatedTime, mon)
			return
		}
	}
	// If no result was found try it again in the following year
	c.calculatedTime = c.calculatedTime.AddDate(1, 0, 0)
	c.calculatedTime = setMonth(c.calculatedTime, c.month[0])
	log.Println(c.calculatedTime)
	c.nextValidMonth(baseTime)
}

// Calculates the next valid Day based upon the previous results.
func (c *crontime) nextValidDay(baseTime time.Time) {
	for _, dom := range c.dom {
		if c.calculatedTime.Month() == baseTime.Month() {
			if !hasPassed(dom, c.calculatedTime.Day()) {
				for _, dow := range c.dow {
					if monthHasDow(dow, dom, int(c.calculatedTime.Month()), c.calculatedTime.Year()){
						c.calculatedTime = setDay(c.calculatedTime, dom)
						return
					}
				}
			}
		} else {
			for _, dow := range c.dow {
				if monthHasDow(dow, dom, int(c.calculatedTime.Month()), c.calculatedTime.Year()){
					c.calculatedTime = setDay(c.calculatedTime, dom)
					return
				}
			}
		}
	}
	// If no result was found try it again in the following month.
	c.calculatedTime = c.calculatedTime.AddDate(0, 1, 0)
	c.nextValidMonth(baseTime)
	c.nextValidDay(baseTime)
}

// Calculates the next valid Hour based upon the previous results.
func (c *crontime) nextValidHour(baseTime time.Time) {
	for _, hour := range c.hour {
		if c.calculatedTime.Day() == baseTime.Day() {
			if !hasPassed(hour, c.calculatedTime.Hour()) {
				c.calculatedTime = setHour(c.calculatedTime, hour)
				return
			}
		} else {
			c.calculatedTime = setHour(c.calculatedTime, hour)
			return
		}
	}
	// If no result was found try it again in the following day.
	c.calculatedTime = c.calculatedTime.AddDate(0, 0, 1)
	c.nextValidDay(baseTime)
	c.nextValidHour(baseTime)
}

// Calculates the next valid Minute based upon the previous results.
func (c *crontime) nextValidMinute(baseTime time.Time) {
	for _, min := range c.minute {
		if c.calculatedTime.Hour() == baseTime.Hour() {
			if !hasPassed(min, c.calculatedTime.Minute()) {
				c.calculatedTime = setMinute(c.calculatedTime, min)
				return
			}
		} else {
			c.calculatedTime = setMinute(c.calculatedTime, min)
			return
		}
	}
	c.calculatedTime = c.calculatedTime.Add(1 * time.Hour)
	c.nextValidHour(baseTime)
	c.nextValidMinute(baseTime)
}

// Calculates the next valid Second based upon the previous results.
func (c *crontime) nextValidSecond(baseTime time.Time) {
	for _, sec := range c.second {
		if !c.minuteHasPassed(baseTime) {
			// check if sec is in the past. <= prevents triggering the same event twice
			if sec > c.calculatedTime.Second() {
				c.calculatedTime = setSecond(c.calculatedTime, sec)
				return
			}
		} else {
			c.calculatedTime = setSecond(c.calculatedTime, sec)
			return
		}
	}
	c.calculatedTime = c.calculatedTime.Add(1 * time.Minute)
	c.calculatedTime = setSecond(c.calculatedTime, 0)
	c.nextValidMinute(baseTime)
	c.nextValidSecond(baseTime)
}

func (c *crontime) minuteHasPassed(baseTime time.Time) bool {
	if c.calculatedTime.Year() > baseTime.Year() {
		return true
	} else if c.calculatedTime.Month() > baseTime.Month() {
		return true
	} else if c.calculatedTime.Day() > baseTime.Day() {
		return true
	} else if c.calculatedTime.Hour() > baseTime.Hour() {
		return true
	} else if c.calculatedTime.Minute() > baseTime.Minute() {
		return true
	} else {
		return false
	}
}

func hasPassed(value, tstamp int) bool{
	return value < tstamp
}

func monthHasDom(dom, month, year int) bool {
	switch month{
		case 1:	return dom <= 31
		case 2:
			if isLeapYear(year) {
				return dom <= 29
			} else {
				return dom <= 28
			}
		case 3: return dom <= 31
		case 4: return dom <= 30
		case 5: return dom <= 31
		case 6: return dom <= 30
		case 7: return dom <= 31
		case 8: return dom <= 31
		case 9: return dom <= 30
		case 10: return dom <= 31
		case 11: return dom <= 30
		case 12: return dom <= 31
		default: panic("strange thingys are happening!")
	}
}

// Check if the combination of day(of month), month and year is the weekday dow.
func monthHasDow(dow, dom, month, year int) bool{
	if !monthHasDom(dom, month, year) {return false}
	Nday := dom % 7
	var Nmonth int
	switch month{
		case 1: Nmonth = 0
		case 2: Nmonth = 3
		case 3: Nmonth = 3
		case 4: Nmonth = 6
		case 5: Nmonth = 1
		case 6: Nmonth = 4
		case 7: Nmonth = 6
		case 8: Nmonth = 2
		case 9: Nmonth = 5
		case 10: Nmonth = 0
		case 11: Nmonth = 3
		case 12: Nmonth = 5
	}
	var Nyear int
	temp := year % 100
	if temp != 0{
		Nyear = (temp + (temp / 4)) % 7
	} else {
		Nyear = 0
	}
	Ncent := (3 - ((year / 100) %4)) * 2
	var Nsj int
	if isLeapYear(year) {
		Nsj = -1
	} else {
		Nsj = 0
	}
	W := (Nday + Nmonth + Nyear + Ncent + Nsj) % 7
	return dow == W
}

func isLeapYear(year int) bool{
	return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
//
func setMonth(tstamp time.Time, month int) time.Time {
	if month > 12 || month < 1 { panic("ERROR Month") }
	return tstamp.AddDate(0, -absolute(int(tstamp.Month()), month), 0)
}

func setDay(tstamp time.Time, day int) time.Time {
	if day > 31 || day < 1{ panic("ERROR Day") }
	return tstamp.AddDate(0, 0, -absolute(tstamp.Day(), day))
}

func setHour(tstamp time.Time, hour int) time.Time {
	if hour >= 24 || hour < 0 { panic("ERROR Hour") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Hour(), hour)) * time.Hour)
}

func setMinute(tstamp time.Time, minute int) time.Time {
	if minute >= 60 || minute < 0{ panic("ERROR Minute") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Minute(), minute)) * time.Minute)
}

func setSecond(tstamp time.Time, second int) time.Time {
	if second >= 60 || second < 0 { panic("ERROR Second") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Second(), second)) * time.Second)
}

func setNanoecond(tstamp time.Time, nanosecond int) time.Time {
	return tstamp.Add(time.Duration(-absolute(tstamp.Nanosecond(), nanosecond)) * time.Nanosecond)
}

func absolute(a, b int) int {
	return a - b
}<commit_msg>Some bugfixes regarding the timestamp calculation<commit_after>/*
 *        Copyright (C) 2014 Stefan 'glaxx' Luecke
 *
 *        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 <http://www.gnu.org/licenses/>.
 *
 *        Authors:	Stefan Luecke <glaxx@glaxx.net>
 */

 /*
 * TODO List:
 * - Test leap year behavior
 * - Test
 */

package cron

import (
//	"fmt"
	"time"
//	"sort"
	"log"
	"container/list"
)

type crontime struct {
	second []int
	minute []int
	hour   []int
	dow    []int //Day of Week
	dom    []int //Day of Month
	month  []int
	calculatedTime time.Time
	calculationInProgress bool
	eventList list.List
}

func (c *crontime) DurationUntilNextEvent() time.Duration {
	return c.nextEvent().Sub(time.Now())
}

func (c *crontime) GetNextEvent() time.Time {
	return c.eventList.Front().Value.(time.Time)
}

func (c *crontime) nextEvent() time.Time {
	if !c.calculationInProgress && c.eventList.Len() == 0{
		r := c.calculateEvent(time.Now())
		go c.fillList(r)
		return r
	} else if c.calculationInProgress && c.eventList.Len() == 0{
		// shit just got real aka TODO
		panic("Shit")

	} else if c.eventList.Len() > 0 {
		e := c.eventList.Front()
		r := e.Value.(time.Time)
		c.eventList.Remove(e)
		go c.fillList(c.eventList.Back().Value.(time.Time))
		return r
	}
	panic("shit 2")
}

func (c *crontime) fillList(baseTime time.Time) {
	if c.eventList.Len() == 0 {
		c.eventList.PushBack(c.calculateEvent(baseTime))
	}
	for ; c.eventList.Len() < 5; {
		c.eventList.PushBack(c.calculateEvent(c.eventList.Back().Value.(time.Time)))
	}
}

func (c *crontime) setCalculationInProgress(set bool) {
	c.calculationInProgress = set
}

// This functions calculates the next event
func (c *crontime) calculateEvent(baseTime time.Time) time.Time{
	c.calculationInProgress = true
	defer c.setCalculationInProgress(false)
	baseTime = setNanoecond(baseTime, 10000)
	c.calculatedTime = baseTime // Ignore all Events in the Past & initial 'result'
	//c.calculatedTime = setNanoecond(c.calculatedTime, 10000)
	c.nextValidMonth(baseTime)
	c.nextValidDay(baseTime)
	c.nextValidHour(baseTime)
	c.nextValidMinute(baseTime)
	c.nextValidSecond(baseTime)
	log.Println("Cronbee has found a time stamp: ", c.calculatedTime)
	return c.calculatedTime
}

// Calculates the next valid Month based upon the previous results.
func (c *crontime) nextValidMonth(baseTime time.Time) {
	for _, mon := range c.month {
		if mon >= int(c.calculatedTime.Month()) {
			//log.Print("Inside Month", mon, c.calculatedTime)
			c.calculatedTime = setMonth(c.calculatedTime, mon)
			//log.Println(" :: and out", c.calculatedTime)
			return
		}
	}
	// If no result was found try it again in the following year
	c.calculatedTime = c.calculatedTime.AddDate(1, 0, 0)
	c.calculatedTime = setMonth(c.calculatedTime, c.month[0])
	//log.Println("Cronbee: Month", c.calculatedTime, baseTime, c.month)
	c.nextValidMonth(baseTime)
}

// Calculates the next valid Day based upon the previous results.
func (c *crontime) nextValidDay(baseTime time.Time) {
	for _, dom := range c.dom {
		if dom >= c.calculatedTime.Day() {
			for _, dow := range c.dow {
				if monthHasDow(dow, dom, int(c.calculatedTime.Month()), c.calculatedTime.Year()){
					c.calculatedTime = setDay(c.calculatedTime, dom)
					//log.Println("Cronbee: Day-INS-1:", c.calculatedTime)
					return
				}
			}
		}
	}/* else {
		for _, dow := range c.dow {
			if monthHasDow(dow, dom, int(c.calculatedTime.Month()), c.calculatedTime.Year()){
				c.calculatedTime = setDay(c.calculatedTime, dom)
				log.Println("Cronbee: Day-INS-2:", c.calculatedTime)
				return
			}
		}
	}*/
	// If no result was found try it again in the following month.
	c.calculatedTime = c.calculatedTime.AddDate(0, 1, 0)
	c.calculatedTime = setDay(c.calculatedTime, c.dom[0])
	//log.Println("Cronbee: Day", c.calculatedTime, baseTime)
	c.nextValidMonth(baseTime)
	c.nextValidDay(baseTime)
}

// Calculates the next valid Hour based upon the previous results.
func (c *crontime) nextValidHour(baseTime time.Time) {
	for _, hour := range c.hour {
		if c.calculatedTime.Day() == baseTime.Day() {
			if !hasPassed(hour, c.calculatedTime.Hour()) {
				c.calculatedTime = setHour(c.calculatedTime, hour)
				return
			}
		} else {
			c.calculatedTime = setHour(c.calculatedTime, hour)
			return
		}
	}
	// If no result was found try it again in the following day.
	c.calculatedTime = c.calculatedTime.AddDate(0, 0, 1)     // <-|
	c.calculatedTime = setHour(c.calculatedTime, c.hour[0])  //   |
	//log.Println("Cronbee: Hour", c.calculatedTime, baseTime) // |
	c.nextValidMonth(baseTime) // May trigger a new month --------|
	c.nextValidDay(baseTime)
	c.nextValidHour(baseTime)
}

// Calculates the next valid Minute based upon the previous results.
func (c *crontime) nextValidMinute(baseTime time.Time) {
	for _, min := range c.minute {
		if c.calculatedTime.Hour() == baseTime.Hour() {
			if !hasPassed(min, c.calculatedTime.Minute()) {
				c.calculatedTime = setMinute(c.calculatedTime, min)
				return
			}
		} else {
			c.calculatedTime = setMinute(c.calculatedTime, min)
			return
		}
	}
	c.calculatedTime = c.calculatedTime.Add(1 * time.Hour)
	c.calculatedTime = setMinute(c.calculatedTime, c.minute[0])
	//log.Println("Cronbee: Minute", c.calculatedTime, baseTime)
	c.nextValidHour(baseTime)
	c.nextValidMinute(baseTime)
}

// Calculates the next valid Second based upon the previous results.
func (c *crontime) nextValidSecond(baseTime time.Time) {
	for _, sec := range c.second {
		if !c.minuteHasPassed(baseTime) {
			// check if sec is in the past. <= prevents triggering the same event twice
			if sec > c.calculatedTime.Second() {
				c.calculatedTime = setSecond(c.calculatedTime, sec)
				return
			}
		} else {
			c.calculatedTime = setSecond(c.calculatedTime, sec)
			return
		}
	}
	c.calculatedTime = c.calculatedTime.Add(1 * time.Minute)
	c.calculatedTime = setSecond(c.calculatedTime, 0)
	//log.Println("Cronbee: Second", c.calculatedTime, baseTime)
	c.nextValidMinute(baseTime)
	c.nextValidSecond(baseTime)
}

func (c *crontime) minuteHasPassed(baseTime time.Time) bool {
	if c.calculatedTime.Year() > baseTime.Year() {
		return true
	} else if c.calculatedTime.Month() > baseTime.Month() {
		return true
	} else if c.calculatedTime.Day() > baseTime.Day() {
		return true
	} else if c.calculatedTime.Hour() > baseTime.Hour() {
		return true
	} else if c.calculatedTime.Minute() > baseTime.Minute() {
		return true
	} else {
		return false
	}
}

func hasPassed(value, tstamp int) bool{
	return value < tstamp
}

func monthHasDom(dom, month, year int) bool {
	switch month{
		case 1:	return dom <= 31
		case 2:
			if isLeapYear(year) {
				return dom <= 29
			} else {
				return dom <= 28
			}
		case 3: return dom <= 31
		case 4: return dom <= 30
		case 5: return dom <= 31
		case 6: return dom <= 30
		case 7: return dom <= 31
		case 8: return dom <= 31
		case 9: return dom <= 30
		case 10: return dom <= 31
		case 11: return dom <= 30
		case 12: return dom <= 31
		default: panic("strange thingys are happening!")
	}
}

// Check if the combination of day(of month), month and year is the weekday dow.
func monthHasDow(dow, dom, month, year int) bool{
	if !monthHasDom(dom, month, year) {return false}
	Nday := dom % 7
	var Nmonth int
	switch month{
		case 1: Nmonth = 0
		case 2: Nmonth = 3
		case 3: Nmonth = 3
		case 4: Nmonth = 6
		case 5: Nmonth = 1
		case 6: Nmonth = 4
		case 7: Nmonth = 6
		case 8: Nmonth = 2
		case 9: Nmonth = 5
		case 10: Nmonth = 0
		case 11: Nmonth = 3
		case 12: Nmonth = 5
	}
	var Nyear int
	temp := year % 100
	if temp != 0{
		Nyear = (temp + (temp / 4)) % 7
	} else {
		Nyear = 0
	}
	Ncent := (3 - ((year / 100) %4)) * 2
	var Nsj int
	if isLeapYear(year) {
		Nsj = -1
	} else {
		Nsj = 0
	}
	W := (Nday + Nmonth + Nyear + Ncent + Nsj) % 7
	return dow == W
}

func isLeapYear(year int) bool{
	return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
//
func setMonth(tstamp time.Time, month int) time.Time {
	if month > 12 || month < 1 { panic("ERROR Month") }
	return tstamp.AddDate(0, -absolute(int(tstamp.Month()), month), 0)
}

func setDay(tstamp time.Time, day int) time.Time {
	if day > 31 || day < 1{ panic("ERROR Day") }
	return tstamp.AddDate(0, 0, -absolute(tstamp.Day(), day))
}

func setHour(tstamp time.Time, hour int) time.Time {
	if hour >= 24 || hour < 0 { panic("ERROR Hour") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Hour(), hour)) * time.Hour)
}

func setMinute(tstamp time.Time, minute int) time.Time {
	if minute >= 60 || minute < 0{ panic("ERROR Minute") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Minute(), minute)) * time.Minute)
}

func setSecond(tstamp time.Time, second int) time.Time {
	if second >= 60 || second < 0 { panic("ERROR Second") }
	return tstamp.Add(time.Duration(-absolute(tstamp.Second(), second)) * time.Second)
}

func setNanoecond(tstamp time.Time, nanosecond int) time.Time {
	return tstamp.Add(time.Duration(-absolute(tstamp.Nanosecond(), nanosecond)) * time.Nanosecond)
}

func absolute(a, b int) int {
	return a - b
}<|endoftext|>


<commit_before>package store

import (
	"errors"
	"sync/atomic"
)

type (
	Getter interface {
		Get() (string, error)
	}

	GetFunc func() (string, error)

	Setter interface {
		Set(string) error
	}

	SetFunc func(string) error

	// Singleton is a thread-safe abstraction to load and store a string
	Singleton interface {
		Getter
		Setter
	}

	SingletonAdapter struct {
		GetFunc
		SetFunc
	}

	SingletonDecorator interface {
		Decorate(Singleton) Singleton
	}
)

func (f GetFunc) Get() (string, error) { return f() }
func (f SetFunc) Set(s string) error   { return f(s) }

var ErrNotFound = errors.New("value not found in store")

func NewInMemorySingleton() Singleton {
	var value atomic.Value
	return &SingletonAdapter{
		func() (string, error) {
			x := value.Load()
			if x == nil {
				return "", ErrNotFound
			}
			return x.(string), nil
		},
		func(s string) error {
			value.Store(s)
			return nil
		},
	}
}

type (
	GetFuncDecorator func(Getter, string, error) (string, error)
	SetFuncDecorator func(Setter, string, error) error
)

func DoSet() SetFuncDecorator {
	return func(s Setter, v string, _ error) error {
		return s.Set(v)
	}
}

func (f SetFuncDecorator) AndThen(f2 SetFuncDecorator) SetFuncDecorator {
	return func(s Setter, v string, err error) error {
		err = f(s, v, err)
		if err != nil {
			return err
		}
		return f2(s, v, nil)
	}
}

func (f SetFuncDecorator) Decorate(s Singleton) Singleton {
	if f == nil {
		return s
	}
	return &SingletonAdapter{
		s.Get,
		SetFunc(func(v string) error {
			return f(s, v, nil)
		}),
	}
}

func DoGet() GetFuncDecorator {
	return func(s Getter, _ string, _ error) (string, error) {
		return s.Get()
	}
}

func (f GetFuncDecorator) AndThen(f2 GetFuncDecorator) GetFuncDecorator {
	return func(s Getter, v string, err error) (string, error) {
		v, err = f(s, v, err)
		if err != nil {
			return v, err
		}
		return f2(s, v, nil)
	}
}

func (f GetFuncDecorator) Decorate(s Singleton) Singleton {
	if f == nil {
		return s
	}
	return &SingletonAdapter{
		GetFunc(func() (string, error) {
			return f(s, "", nil)
		}),
		s.Set,
	}
}

func DecorateSingleton(s Singleton, ds ...SingletonDecorator) Singleton {
	for _, d := range ds {
		if d != nil {
			s = d.Decorate(s)
		}
	}
	return s
}

// GetOrPanic curries the result of a Getter invocation: the returned func only ever returns the string component when
// the error component of the underlying Get() call is nil. If Get() generates an error then the curried func panics.
func GetOrPanic(g Getter) func() string {
	return func() string {
		v, err := g.Get()
		if err != nil {
			panic(err)
		}
		return v
	}
}

func GetIgnoreErrors(g Getter) func() string {
	return func() string {
		v, _ := g.Get()
		return v
	}
}

// SetOrPanic curries the result of a Setter invocation: the returned func only ever returns normally when the error
// component of the underlying Set() call is nil. If Set() generates an error then the curried func panics.
func SetOrPanic(s Setter) func(v string) {
	return func(v string) {
		err := s.Set(v)
		if err != nil {
			panic(err)
		}
	}
}
<commit_msg>singleton: ErrNotFound should be a const<commit_after>package store

import (
	"sync/atomic"
)

type (
	Getter interface {
		Get() (string, error)
	}

	GetFunc func() (string, error)

	Setter interface {
		Set(string) error
	}

	SetFunc func(string) error

	// Singleton is a thread-safe abstraction to load and store a string
	Singleton interface {
		Getter
		Setter
	}

	SingletonAdapter struct {
		GetFunc
		SetFunc
	}

	SingletonDecorator interface {
		Decorate(Singleton) Singleton
	}

	Error string
)

func (err Error) Error() string { return string(err) }

const ErrNotFound = Error("value not found in store")

func (f GetFunc) Get() (string, error) { return f() }
func (f SetFunc) Set(s string) error   { return f(s) }

func NewInMemorySingleton() Singleton {
	var value atomic.Value
	return &SingletonAdapter{
		func() (string, error) {
			x := value.Load()
			if x == nil {
				return "", ErrNotFound
			}
			return x.(string), nil
		},
		func(s string) error {
			value.Store(s)
			return nil
		},
	}
}

type (
	GetFuncDecorator func(Getter, string, error) (string, error)
	SetFuncDecorator func(Setter, string, error) error
)

func DoSet() SetFuncDecorator {
	return func(s Setter, v string, _ error) error {
		return s.Set(v)
	}
}

func (f SetFuncDecorator) AndThen(f2 SetFuncDecorator) SetFuncDecorator {
	return func(s Setter, v string, err error) error {
		err = f(s, v, err)
		if err != nil {
			return err
		}
		return f2(s, v, nil)
	}
}

func (f SetFuncDecorator) Decorate(s Singleton) Singleton {
	if f == nil {
		return s
	}
	return &SingletonAdapter{
		s.Get,
		SetFunc(func(v string) error {
			return f(s, v, nil)
		}),
	}
}

func DoGet() GetFuncDecorator {
	return func(s Getter, _ string, _ error) (string, error) {
		return s.Get()
	}
}

func (f GetFuncDecorator) AndThen(f2 GetFuncDecorator) GetFuncDecorator {
	return func(s Getter, v string, err error) (string, error) {
		v, err = f(s, v, err)
		if err != nil {
			return v, err
		}
		return f2(s, v, nil)
	}
}

func (f GetFuncDecorator) Decorate(s Singleton) Singleton {
	if f == nil {
		return s
	}
	return &SingletonAdapter{
		GetFunc(func() (string, error) {
			return f(s, "", nil)
		}),
		s.Set,
	}
}

func DecorateSingleton(s Singleton, ds ...SingletonDecorator) Singleton {
	for _, d := range ds {
		if d != nil {
			s = d.Decorate(s)
		}
	}
	return s
}

// GetOrPanic curries the result of a Getter invocation: the returned func only ever returns the string component when
// the error component of the underlying Get() call is nil. If Get() generates an error then the curried func panics.
func GetOrPanic(g Getter) func() string {
	return func() string {
		v, err := g.Get()
		if err != nil {
			panic(err)
		}
		return v
	}
}

func GetIgnoreErrors(g Getter) func() string {
	return func() string {
		v, _ := g.Get()
		return v
	}
}

// SetOrPanic curries the result of a Setter invocation: the returned func only ever returns normally when the error
// component of the underlying Set() call is nil. If Set() generates an error then the curried func panics.
func SetOrPanic(s Setter) func(v string) {
	return func(v string) {
		err := s.Set(v)
		if err != nil {
			panic(err)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Go 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 analysisflags defines helpers for processing flags of
// analysis driver tools.
package analysisflags

import (
	"crypto/sha256"
	"encoding/json"
	"flag"
	"fmt"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"

	"golang.org/x/tools/go/analysis"
)

// flags common to all {single,multi,unit}checkers.
var (
	JSON    = false // -json
	Context = -1    // -c=N: if N>0, display offending line plus N lines of context
)

// Parse creates a flag for each of the analyzer's flags,
// including (in multi mode) a flag named after the analyzer,
// parses the flags, then filters and returns the list of
// analyzers enabled by flags.
func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
	// Connect each analysis flag to the command line as -analysis.flag.
	enabled := make(map[*analysis.Analyzer]*triState)
	for _, a := range analyzers {
		var prefix string

		// Add -NAME flag to enable it.
		if multi {
			prefix = a.Name + "."

			enable := new(triState)
			enableUsage := "enable " + a.Name + " analysis"
			flag.Var(enable, a.Name, enableUsage)
			enabled[a] = enable
		}

		a.Flags.VisitAll(func(f *flag.Flag) {
			if !multi && flag.Lookup(f.Name) != nil {
				log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
				return
			}

			name := prefix + f.Name
			flag.Var(f.Value, name, f.Usage)
		})
	}

	// standard flags: -flags, -V.
	printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
	addVersionFlag()

	// flags common to all checkers
	flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
	flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)

	// Add shims for legacy vet flags to enable existing
	// scripts that run vet to continue to work.
	_ = flag.Bool("source", false, "no effect (deprecated)")
	_ = flag.Bool("v", false, "no effect (deprecated)")
	_ = flag.Bool("all", false, "no effect (deprecated)")
	_ = flag.String("tags", "", "no effect (deprecated)")
	for old, new := range vetLegacyFlags {
		newFlag := flag.Lookup(new)
		if newFlag != nil && flag.Lookup(old) == nil {
			flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
		}
	}

	flag.Parse() // (ExitOnError)

	// -flags: print flags so that go vet knows which ones are legitimate.
	if *printflags {
		printFlags()
		os.Exit(0)
	}

	// If any -NAME flag is true,  run only those analyzers. Otherwise,
	// if any -NAME flag is false, run all but those analyzers.
	if multi {
		var hasTrue, hasFalse bool
		for _, ts := range enabled {
			switch *ts {
			case setTrue:
				hasTrue = true
			case setFalse:
				hasFalse = true
			}
		}

		var keep []*analysis.Analyzer
		if hasTrue {
			for _, a := range analyzers {
				if *enabled[a] == setTrue {
					keep = append(keep, a)
				}
			}
			analyzers = keep
		} else if hasFalse {
			for _, a := range analyzers {
				if *enabled[a] != setFalse {
					keep = append(keep, a)
				}
			}
			analyzers = keep
		}
	}

	return analyzers
}

func printFlags() {
	type jsonFlag struct {
		Name  string
		Bool  bool
		Usage string
	}
	var flags []jsonFlag = nil
	flag.VisitAll(func(f *flag.Flag) {
		// Don't report {single,multi}checker debugging
		// flags as these have no effect on unitchecker
		// (as invoked by 'go vet').
		switch f.Name {
		case "debug", "cpuprofile", "memprofile", "trace":
			return
		}

		b, ok := f.Value.(interface{ IsBoolFlag() bool })
		isBool := ok && b.IsBoolFlag()
		flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
	})
	data, err := json.MarshalIndent(flags, "", "\t")
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(data)
}

// addVersionFlag registers a -V flag that, if set,
// prints the executable version and exits 0.
//
// It is a variable not a function to permit easy
// overriding in the copy vendored in $GOROOT/src/cmd/vet:
//
// func init() { addVersionFlag = objabi.AddVersionFlag }
var addVersionFlag = func() {
	flag.Var(versionFlag{}, "V", "print version and exit")
}

// versionFlag minimally complies with the -V protocol required by "go vet".
type versionFlag struct{}

func (versionFlag) IsBoolFlag() bool { return true }
func (versionFlag) Get() interface{} { return nil }
func (versionFlag) String() string   { return "" }
func (versionFlag) Set(s string) error {
	if s != "full" {
		log.Fatalf("unsupported flag value: -V=%s", s)
	}

	// This replicates the miminal subset of
	// cmd/internal/objabi.AddVersionFlag, which is private to the
	// go tool yet forms part of our command-line interface.
	// TODO(adonovan): clarify the contract.

	// Print the tool version so the build system can track changes.
	// Formats:
	//   $progname version devel ... buildID=...
	//   $progname version go1.9.1
	progname := os.Args[0]
	f, err := os.Open(progname)
	if err != nil {
		log.Fatal(err)
	}
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		log.Fatal(err)
	}
	f.Close()
	fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
		progname, string(h.Sum(nil)))
	os.Exit(0)
	return nil
}

// A triState is a boolean that knows whether
// it has been set to either true or false.
// It is used to identify whether a flag appears;
// the standard boolean flag cannot
// distinguish missing from unset.
// It also satisfies flag.Value.
type triState int

const (
	unset triState = iota
	setTrue
	setFalse
)

func triStateFlag(name string, value triState, usage string) *triState {
	flag.Var(&value, name, usage)
	return &value
}

// triState implements flag.Value, flag.Getter, and flag.boolFlag.
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
func (ts *triState) Get() interface{} {
	return *ts == setTrue
}

func (ts triState) isTrue() bool {
	return ts == setTrue
}

func (ts *triState) Set(value string) error {
	b, err := strconv.ParseBool(value)
	if err != nil {
		// This error message looks poor but package "flag" adds
		// "invalid boolean value %q for -NAME: %s"
		return fmt.Errorf("want true or false")
	}
	if b {
		*ts = setTrue
	} else {
		*ts = setFalse
	}
	return nil
}

func (ts *triState) String() string {
	switch *ts {
	case unset:
		return "true"
	case setTrue:
		return "true"
	case setFalse:
		return "false"
	}
	panic("not reached")
}

func (ts triState) IsBoolFlag() bool {
	return true
}

// Legacy flag support

// vetLegacyFlags maps flags used by legacy vet to their corresponding
// new names. The old names will continue to work.
var vetLegacyFlags = map[string]string{
	// Analyzer name changes
	"bool":       "bools",
	"buildtags":  "buildtag",
	"methods":    "stdmethods",
	"rangeloops": "loopclosure",

	// Analyzer flags
	"compositewhitelist":  "composites.whitelist",
	"printfuncs":          "printf.funcs",
	"shadowstrict":        "shadow.strict",
	"unusedfuncs":         "unusedresult.funcs",
	"unusedstringmethods": "unusedresult.stringmethods",
}

// ---- output helpers common to all drivers ----

// PrintPlain prints a diagnostic in plain text form,
// with context specified by the -c flag.
func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
	posn := fset.Position(diag.Pos)
	fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)

	// -c=N: show offending line plus N lines of context.
	if Context >= 0 {
		data, _ := ioutil.ReadFile(posn.Filename)
		lines := strings.Split(string(data), "\n")
		for i := posn.Line - Context; i <= posn.Line+Context; i++ {
			if 1 <= i && i <= len(lines) {
				fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
			}
		}
	}
}

// A JSONTree is a mapping from package ID to analysis name to result.
// Each result is either a jsonError or a list of jsonDiagnostic.
type JSONTree map[string]map[string]interface{}

// Add adds the result of analysis 'name' on package 'id'.
// The result is either a list of diagnostics or an error.
func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
	var v interface{}
	if err != nil {
		type jsonError struct {
			Err string `json:"error"`
		}
		v = jsonError{err.Error()}
	} else if len(diags) > 0 {
		type jsonDiagnostic struct {
			Category string `json:"category,omitempty"`
			Posn     string `json:"posn"`
			Message  string `json:"message"`
		}
		var diagnostics []jsonDiagnostic
		for _, f := range diags {
			diagnostics = append(diagnostics, jsonDiagnostic{
				Category: f.Category,
				Posn:     fset.Position(f.Pos).String(),
				Message:  f.Message,
			})
		}
		v = diagnostics
	}
	if v != nil {
		m, ok := tree[id]
		if !ok {
			m = make(map[string]interface{})
			tree[id] = m
		}
		m[name] = v
	}
}

func (tree JSONTree) Print() {
	data, err := json.MarshalIndent(tree, "", "\t")
	if err != nil {
		log.Panicf("internal error: JSON marshalling failed: %v", err)
	}
	fmt.Printf("%s\n", data)
}
<commit_msg>go/analysis: allow overriding V flag without code patches<commit_after>// Copyright 2018 The Go 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 analysisflags defines helpers for processing flags of
// analysis driver tools.
package analysisflags

import (
	"crypto/sha256"
	"encoding/json"
	"flag"
	"fmt"
	"go/token"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"strings"

	"golang.org/x/tools/go/analysis"
)

// flags common to all {single,multi,unit}checkers.
var (
	JSON    = false // -json
	Context = -1    // -c=N: if N>0, display offending line plus N lines of context
)

// Parse creates a flag for each of the analyzer's flags,
// including (in multi mode) a flag named after the analyzer,
// parses the flags, then filters and returns the list of
// analyzers enabled by flags.
func Parse(analyzers []*analysis.Analyzer, multi bool) []*analysis.Analyzer {
	// Connect each analysis flag to the command line as -analysis.flag.
	enabled := make(map[*analysis.Analyzer]*triState)
	for _, a := range analyzers {
		var prefix string

		// Add -NAME flag to enable it.
		if multi {
			prefix = a.Name + "."

			enable := new(triState)
			enableUsage := "enable " + a.Name + " analysis"
			flag.Var(enable, a.Name, enableUsage)
			enabled[a] = enable
		}

		a.Flags.VisitAll(func(f *flag.Flag) {
			if !multi && flag.Lookup(f.Name) != nil {
				log.Printf("%s flag -%s would conflict with driver; skipping", a.Name, f.Name)
				return
			}

			name := prefix + f.Name
			flag.Var(f.Value, name, f.Usage)
		})
	}

	// standard flags: -flags, -V.
	printflags := flag.Bool("flags", false, "print analyzer flags in JSON")
	addVersionFlag()

	// flags common to all checkers
	flag.BoolVar(&JSON, "json", JSON, "emit JSON output")
	flag.IntVar(&Context, "c", Context, `display offending line with this many lines of context`)

	// Add shims for legacy vet flags to enable existing
	// scripts that run vet to continue to work.
	_ = flag.Bool("source", false, "no effect (deprecated)")
	_ = flag.Bool("v", false, "no effect (deprecated)")
	_ = flag.Bool("all", false, "no effect (deprecated)")
	_ = flag.String("tags", "", "no effect (deprecated)")
	for old, new := range vetLegacyFlags {
		newFlag := flag.Lookup(new)
		if newFlag != nil && flag.Lookup(old) == nil {
			flag.Var(newFlag.Value, old, "deprecated alias for -"+new)
		}
	}

	flag.Parse() // (ExitOnError)

	// -flags: print flags so that go vet knows which ones are legitimate.
	if *printflags {
		printFlags()
		os.Exit(0)
	}

	// If any -NAME flag is true,  run only those analyzers. Otherwise,
	// if any -NAME flag is false, run all but those analyzers.
	if multi {
		var hasTrue, hasFalse bool
		for _, ts := range enabled {
			switch *ts {
			case setTrue:
				hasTrue = true
			case setFalse:
				hasFalse = true
			}
		}

		var keep []*analysis.Analyzer
		if hasTrue {
			for _, a := range analyzers {
				if *enabled[a] == setTrue {
					keep = append(keep, a)
				}
			}
			analyzers = keep
		} else if hasFalse {
			for _, a := range analyzers {
				if *enabled[a] != setFalse {
					keep = append(keep, a)
				}
			}
			analyzers = keep
		}
	}

	return analyzers
}

func printFlags() {
	type jsonFlag struct {
		Name  string
		Bool  bool
		Usage string
	}
	var flags []jsonFlag = nil
	flag.VisitAll(func(f *flag.Flag) {
		// Don't report {single,multi}checker debugging
		// flags as these have no effect on unitchecker
		// (as invoked by 'go vet').
		switch f.Name {
		case "debug", "cpuprofile", "memprofile", "trace":
			return
		}

		b, ok := f.Value.(interface{ IsBoolFlag() bool })
		isBool := ok && b.IsBoolFlag()
		flags = append(flags, jsonFlag{f.Name, isBool, f.Usage})
	})
	data, err := json.MarshalIndent(flags, "", "\t")
	if err != nil {
		log.Fatal(err)
	}
	os.Stdout.Write(data)
}

// addVersionFlag registers a -V flag that, if set,
// prints the executable version and exits 0.
//
// If the -V flag already exists — for example, because it was already
// registered by a call to cmd/internal/objabi.AddVersionFlag — then
// addVersionFlag does nothing.
func addVersionFlag() {
	if flag.Lookup("V") == nil {
		flag.Var(versionFlag{}, "V", "print version and exit")
	}
}

// versionFlag minimally complies with the -V protocol required by "go vet".
type versionFlag struct{}

func (versionFlag) IsBoolFlag() bool { return true }
func (versionFlag) Get() interface{} { return nil }
func (versionFlag) String() string   { return "" }
func (versionFlag) Set(s string) error {
	if s != "full" {
		log.Fatalf("unsupported flag value: -V=%s", s)
	}

	// This replicates the miminal subset of
	// cmd/internal/objabi.AddVersionFlag, which is private to the
	// go tool yet forms part of our command-line interface.
	// TODO(adonovan): clarify the contract.

	// Print the tool version so the build system can track changes.
	// Formats:
	//   $progname version devel ... buildID=...
	//   $progname version go1.9.1
	progname := os.Args[0]
	f, err := os.Open(progname)
	if err != nil {
		log.Fatal(err)
	}
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		log.Fatal(err)
	}
	f.Close()
	fmt.Printf("%s version devel comments-go-here buildID=%02x\n",
		progname, string(h.Sum(nil)))
	os.Exit(0)
	return nil
}

// A triState is a boolean that knows whether
// it has been set to either true or false.
// It is used to identify whether a flag appears;
// the standard boolean flag cannot
// distinguish missing from unset.
// It also satisfies flag.Value.
type triState int

const (
	unset triState = iota
	setTrue
	setFalse
)

func triStateFlag(name string, value triState, usage string) *triState {
	flag.Var(&value, name, usage)
	return &value
}

// triState implements flag.Value, flag.Getter, and flag.boolFlag.
// They work like boolean flags: we can say vet -printf as well as vet -printf=true
func (ts *triState) Get() interface{} {
	return *ts == setTrue
}

func (ts triState) isTrue() bool {
	return ts == setTrue
}

func (ts *triState) Set(value string) error {
	b, err := strconv.ParseBool(value)
	if err != nil {
		// This error message looks poor but package "flag" adds
		// "invalid boolean value %q for -NAME: %s"
		return fmt.Errorf("want true or false")
	}
	if b {
		*ts = setTrue
	} else {
		*ts = setFalse
	}
	return nil
}

func (ts *triState) String() string {
	switch *ts {
	case unset:
		return "true"
	case setTrue:
		return "true"
	case setFalse:
		return "false"
	}
	panic("not reached")
}

func (ts triState) IsBoolFlag() bool {
	return true
}

// Legacy flag support

// vetLegacyFlags maps flags used by legacy vet to their corresponding
// new names. The old names will continue to work.
var vetLegacyFlags = map[string]string{
	// Analyzer name changes
	"bool":       "bools",
	"buildtags":  "buildtag",
	"methods":    "stdmethods",
	"rangeloops": "loopclosure",

	// Analyzer flags
	"compositewhitelist":  "composites.whitelist",
	"printfuncs":          "printf.funcs",
	"shadowstrict":        "shadow.strict",
	"unusedfuncs":         "unusedresult.funcs",
	"unusedstringmethods": "unusedresult.stringmethods",
}

// ---- output helpers common to all drivers ----

// PrintPlain prints a diagnostic in plain text form,
// with context specified by the -c flag.
func PrintPlain(fset *token.FileSet, diag analysis.Diagnostic) {
	posn := fset.Position(diag.Pos)
	fmt.Fprintf(os.Stderr, "%s: %s\n", posn, diag.Message)

	// -c=N: show offending line plus N lines of context.
	if Context >= 0 {
		data, _ := ioutil.ReadFile(posn.Filename)
		lines := strings.Split(string(data), "\n")
		for i := posn.Line - Context; i <= posn.Line+Context; i++ {
			if 1 <= i && i <= len(lines) {
				fmt.Fprintf(os.Stderr, "%d\t%s\n", i, lines[i-1])
			}
		}
	}
}

// A JSONTree is a mapping from package ID to analysis name to result.
// Each result is either a jsonError or a list of jsonDiagnostic.
type JSONTree map[string]map[string]interface{}

// Add adds the result of analysis 'name' on package 'id'.
// The result is either a list of diagnostics or an error.
func (tree JSONTree) Add(fset *token.FileSet, id, name string, diags []analysis.Diagnostic, err error) {
	var v interface{}
	if err != nil {
		type jsonError struct {
			Err string `json:"error"`
		}
		v = jsonError{err.Error()}
	} else if len(diags) > 0 {
		type jsonDiagnostic struct {
			Category string `json:"category,omitempty"`
			Posn     string `json:"posn"`
			Message  string `json:"message"`
		}
		var diagnostics []jsonDiagnostic
		for _, f := range diags {
			diagnostics = append(diagnostics, jsonDiagnostic{
				Category: f.Category,
				Posn:     fset.Position(f.Pos).String(),
				Message:  f.Message,
			})
		}
		v = diagnostics
	}
	if v != nil {
		m, ok := tree[id]
		if !ok {
			m = make(map[string]interface{})
			tree[id] = m
		}
		m[name] = v
	}
}

func (tree JSONTree) Print() {
	data, err := json.MarshalIndent(tree, "", "\t")
	if err != nil {
		log.Panicf("internal error: JSON marshalling failed: %v", err)
	}
	fmt.Printf("%s\n", data)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"github.com/arbovm/levenshtein"
	"os"
	"strings"
	"sync"
)

//GLOBALS
var findings_matches []string
var findings_leven []int
var wg sync.WaitGroup

func abs(x int) int {
	if x < 0 {
		return -x
	} else if x == 0 {
		return 0 // return correctly abs(-0)
	}
	return x
}

func generateHash(path string) {
	inFile, _ := os.Open(path)
	defer inFile.Close()
	scanner := bufio.NewScanner(inFile)
	scanner.Split(bufio.ScanLines)

	for scanner.Scan() {
		s := strings.Replace(scanner.Text(), "/", "", -1)
		partials, num := getPartials(s)
		for i := 0; i < num; i++ {
			addToCache(partials[i], s)
		}
	}
}

func addToCache(spartial string, s string) {
	f, err := os.OpenFile("cache/"+spartial, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	if _, err = f.WriteString(s + "\n"); err != nil {
		panic(err)
	}
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
			break
		}
	}
	return false
}

func getPartials(s string) ([]string, int) {
	partials := make([]string, 1000)
	num := 0
	s = strings.Replace(s, "/", "", -1)
	slen := len(s)
	if slen <= 4 {
		partials[num] = "asdf"
		num = num + 1
	} else {
		for i := 0; i <= slen-4; i++ {
			partials[num] = strings.ToLower(s[i : i+4])
			num = num + 1
		}
	}
	return partials, num
}

func getMatch(s string, path string) (string, int) {
	partials, num := getPartials(s)
	matches := make([]string, 10000)
	numm := 0

	N := 8
	for i := 0; i < num; i++ {

		inFile, _ := os.Open(path + partials[i])
		defer inFile.Close()
		scanner := bufio.NewScanner(inFile)
		scanner.Split(bufio.ScanLines)

		for scanner.Scan() {
			//if stringInSlice(scanner.Text(),matches) == false { ITS NOT WORTH LOOKING THROUGH DUPLICATES
			matches[numm] = scanner.Text()
			numm = numm + 1
			// }
		}

	}

	matches2 := make([]string, numm)
	matches2 = matches[0:numm]
	findings_leven = make([]int, N)
	findings_matches = make([]string, N)

	wg.Add(N)
	for i := 0; i < N; i++ {
		go search(matches2[i*len(matches2)/N:(i+1)*len(matches2)/N], s, i)
	}
	wg.Wait()

	lowest := 100
	best_index := 0
	for i := 0; i < len(findings_leven); i++ {
		if findings_leven[i] < lowest {
			lowest = findings_leven[i]
			best_index = i
		}
	}

	return findings_matches[best_index],lowest
}

func search(matches []string, target string, process int) {
	defer wg.Done()
	match := "No match"
	target = strings.ToLower(target)
	bestLevenshtein := 1000
	for i := 0; i < len(matches); i++ {
		d := levenshtein.Distance(target, strings.ToLower(matches[i]))
		if d < bestLevenshtein {
			bestLevenshtein = d
			match = matches[i]
		}
	}
	findings_matches[process] = match
	findings_leven[process] = bestLevenshtein
}

func main() {
	if strings.EqualFold(os.Args[1], "help") {
		fmt.Println("Version 1.0 - 4-mer tuples\n")
		fmt.Println("./match-concurrent build <NAME OF WORDLIST> - builds cache/ folder in current directory\n")
		fmt.Println("./match-concurrent 'word or words to match' /directions/to/cache/\n")
	} else if strings.EqualFold(os.Args[1], "build") {
		os.Mkdir("cache", 0775)
		generateHash(os.Args[2])
	} else {
		match,lowest := getMatch(os.Args[1],os.Args[2])
		fmt.Printf("%v|||%v\n", match,lowest)
	}
}
<commit_msg>Switched to 5-mers, for a little more speed<commit_after>package main

import (
	"bufio"
	"fmt"
	"github.com/arbovm/levenshtein"
	"os"
	"strings"
	"sync"
)

//GLOBALS
var findings_matches []string
var findings_leven []int
var wg sync.WaitGroup

func abs(x int) int {
	if x < 0 {
		return -x
	} else if x == 0 {
		return 0 // return correctly abs(-0)
	}
	return x
}

func generateHash(path string) {
	inFile, _ := os.Open(path)
	defer inFile.Close()
	scanner := bufio.NewScanner(inFile)
	scanner.Split(bufio.ScanLines)

	for scanner.Scan() {
		s := strings.Replace(scanner.Text(), "/", "", -1)
		partials, num := getPartials(s)
		for i := 0; i < num; i++ {
			addToCache(partials[i], s)
		}
	}
}

func addToCache(spartial string, s string) {
	f, err := os.OpenFile("cache/"+spartial, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
	if err != nil {
		panic(err)
	}

	defer f.Close()

	if _, err = f.WriteString(s + "\n"); err != nil {
		panic(err)
	}
}

func stringInSlice(a string, list []string) bool {
	for _, b := range list {
		if b == a {
			return true
			break
		}
	}
	return false
}

func getPartials(s string) ([]string, int) {
	partials := make([]string, 100000)
	num := 0
	s = strings.Replace(s, "/", "", -1)
	slen := len(s)
	if slen <= 5 {
		partials[num] = "asdf"
		num = num + 1
	} else {
		for i := 0; i <= slen-5; i++ {
			partials[num] = strings.ToLower(s[i : i+5])
			num = num + 1
		}
	}
	return partials, num
}

func getMatch(s string, path string) (string, int) {
	partials, num := getPartials(s)
	matches := make([]string, 1000000)
	numm := 0

	N := 8
	for i := 0; i < num; i++ {

		inFile, _ := os.Open(path + partials[i])
		defer inFile.Close()
		scanner := bufio.NewScanner(inFile)
		scanner.Split(bufio.ScanLines)

		for scanner.Scan() {
			//if stringInSlice(scanner.Text(),matches) == false { ITS NOT WORTH LOOKING THROUGH DUPLICATES
			matches[numm] = scanner.Text()
			numm = numm + 1
			// }
		}

	}

	matches2 := make([]string, numm)
	matches2 = matches[0:numm]
	findings_leven = make([]int, N)
	findings_matches = make([]string, N)

	wg.Add(N)
	for i := 0; i < N; i++ {
		go search(matches2[i*len(matches2)/N:(i+1)*len(matches2)/N], s, i)
	}
	wg.Wait()

	lowest := 100
	best_index := 0
	for i := 0; i < len(findings_leven); i++ {
		if findings_leven[i] < lowest {
			lowest = findings_leven[i]
			best_index = i
		}
	}

	return findings_matches[best_index],lowest
}

func search(matches []string, target string, process int) {
	defer wg.Done()
	match := "No match"
	target = strings.ToLower(target)
	bestLevenshtein := 1000
	for i := 0; i < len(matches); i++ {
		d := levenshtein.Distance(target, strings.ToLower(matches[i]))
		if d < bestLevenshtein {
			bestLevenshtein = d
			match = matches[i]
		}
	}
	findings_matches[process] = match
	findings_leven[process] = bestLevenshtein
}

func main() {
	if strings.EqualFold(os.Args[1], "help") {
		fmt.Println("Version 1.0 - 4-mer tuples\n")
		fmt.Println("./match-concurrent build <NAME OF WORDLIST> - builds cache/ folder in current directory\n")
		fmt.Println("./match-concurrent 'word or words to match' /directions/to/cache/\n")
	} else if strings.EqualFold(os.Args[1], "build") {
		os.Mkdir("cache", 0775)
		generateHash(os.Args[2])
	} else {
		match,lowest := getMatch(os.Args[1],os.Args[2])
		fmt.Printf("%v|||%v\n", match,lowest)
	}
}
<|endoftext|>


<commit_before>package htmltest

import (
	"crypto/x509"
	"fmt"
	"github.com/wjdp/htmltest/htmldoc"
	"github.com/wjdp/htmltest/issues"
	"github.com/wjdp/htmltest/output"
	"golang.org/x/net/html"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

func (hT *HTMLTest) checkLink(document *htmldoc.Document, node *html.Node) {
	attrs := htmldoc.ExtractAttrs(node.Attr,
		[]string{"href", "rel"})

	// Check if favicon
	if htmldoc.AttrPresent(node.Attr, "rel") &&
		(attrs["rel"] == "icon" || attrs["rel"] == "shortcut icon") &&
		node.Parent.Data == "head" {
		document.State.FaviconPresent = true
	}

	// Ignore if rel=dns-prefetch, see #40. If we have more cases like this a hashable type should be created and
	// checked against.
	if attrs["rel"] == "dns-prefetch" {
		return
	}

	// Create reference
	ref, err := htmldoc.NewReference(document, node, attrs["href"])
	if err != nil {
		hT.issueStore.AddIssue(issues.Issue{
			Level:    issues.LevelError,
			Document: document,
			Message:  fmt.Sprintf("bad reference: %q", err),
		})
		return
	}

	// Check for missing href, fail for link nodes
	if !htmldoc.AttrPresent(node.Attr, "href") {
		switch node.Data {
		case "a":
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelDebug,
				Message:   "anchor without href",
				Reference: ref,
			})
			return
		case "link":
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "link tag missing href",
				Reference: ref,
			})
			return
		}
	}

	// Blank href
	if attrs["href"] == "" {
		if !hT.opts.IgnoreEmptyHref {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "href blank",
				Reference: ref,
			})
		}
		return
	}

	// href="#"
	if attrs["href"] == "#" {
		if hT.opts.CheckInternalHash && !hT.opts.IgnoreInternalEmptyHash {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "empty hash",
				Reference: ref,
			})
		}
		return
	}

	// Route reference check
	switch ref.Scheme() {
	case "http":
		hT.enforceHTTPS(ref)
		hT.checkExternal(ref)
	case "https":
		hT.checkExternal(ref)
	case "file":
		hT.checkInternal(ref)
	case "self":
		hT.checkInternalHash(ref)
	case "mailto":
		hT.checkMailto(ref)
	case "tel":
		hT.checkTel(ref)
	}

	// TODO: Other schemes
	// What to do about unknown schemes, could be perfectly valid or a typo.
	// Perhaps show a warning, which can be suppressed per-scheme in options.
	// Preload with a couple of common ones, ftp &c.

}

func (hT *HTMLTest) checkExternal(ref *htmldoc.Reference) {
	issueLevel := issues.LevelError
	if hT.opts.IgnoreExternalBrokenLinks {
		issueLevel = issues.LevelWarning
	}
	if !hT.opts.CheckExternal {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping external check",
			Reference: ref,
		})
		return
	}

	urlStr := ref.URLString()

	// Does this url match an url ignore rule?
	if hT.opts.isURLIgnored(urlStr) {
		return
	}

	if hT.opts.StripQueryString && !InList(hT.opts.StripQueryExcludes, urlStr) {
		urlStr = htmldoc.URLStripQueryString(urlStr)
	}
	var statusCode int

	cR, isCached := hT.refCache.Get(urlStr)

	if isCached && statusCodeValid(cR.StatusCode) {
		// If we have a valid result in cache, use that
		statusCode = cR.StatusCode
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "from cache",
			Reference: ref,
		})
	} else {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "fresh",
			Reference: ref,
		})

		// Build the request
		req, err := http.NewRequest("GET", urlStr, nil)
		// Only error NewRequest raises is if the url isn't valid, we have already checked it by this point so OK just
		// to panic if err != nil.
		output.CheckErrorPanic(err)

		// Set UA header
		req.Header.Set("User-Agent", "htmltest/"+hT.opts.Version)

		// Set headers from HTTPHeaders option
		for key, value := range hT.opts.HTTPHeaders {
			// Due to the way we're loading in config these keys and values are interface{}. In normal cases they are
			// strings, but could very easily be ints (side note: this isn't great, we'll fix this later, #73)
			req.Header.Set(fmt.Sprintf("%v", key), fmt.Sprintf("%v", value))
		}

		hT.httpChannel <- true // Add to http concurrency limiter

		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelInfo,
			Message:   "hitting",
			Reference: ref,
		})

		resp, err := hT.httpClient.Do(req)

		<-hT.httpChannel // Bump off http concurrency limiter

		if err != nil {
			if strings.Contains(err.Error(), "dial tcp") {
				// Remove long prefix
				prefix := "Get " + urlStr + ": dial tcp: lookup "
				cleanedMessage := strings.TrimPrefix(err.Error(), prefix)
				// Add error
				hT.issueStore.AddIssue(issues.Issue{
					Level:     issueLevel,
					Message:   cleanedMessage,
					Reference: ref,
				})
				return
			}
			if strings.Contains(err.Error(), "Client.Timeout") {
				hT.issueStore.AddIssue(issues.Issue{
					Level:     issueLevel,
					Message:   "request exceeded our ExternalTimeout",
					Reference: ref,
				})
				return
			}

			if certErr, ok := err.(*url.Error).Err.(x509.UnknownAuthorityError); ok {
				err = validateCertChain(certErr.Cert)
				if err == nil {
					hT.issueStore.AddIssue(issues.Issue{
						Level:     issues.LevelWarning,
						Reference: ref,
						Message:   "incomplete certificate chain",
					})
					return
				}
			}

			// Unhandled client error, return generic error
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issueLevel,
				Message:   err.Error(),
				Reference: ref,
			})

			return
		}
		// Save cached result
		hT.refCache.Save(urlStr, resp.StatusCode)
		statusCode = resp.StatusCode
	}

	switch statusCode {
	case http.StatusOK:
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   http.StatusText(statusCode),
			Reference: ref,
		})
	case http.StatusPartialContent:
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   http.StatusText(statusCode),
			Reference: ref,
		})
	default:
		attrs := htmldoc.ExtractAttrs(ref.Node.Attr, []string{"rel"})
		if attrs["rel"] == "canonical" && hT.opts.IgnoreCanonicalBrokenLinks {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelWarning,
				Message:   http.StatusText(statusCode) + " [rel=\"canonical\"]",
				Reference: ref,
			})
		} else {
			// Failed VCRed requests end up here with a status code of zero
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issueLevel,
				Message:   fmt.Sprintf("%s %d", "Non-OK status:", statusCode),
				Reference: ref,
			})
		}
	}

	// TODO check a hash id exists in external page if present in reference (URL.Fragment)
}

func (hT *HTMLTest) checkInternal(ref *htmldoc.Reference) {
	if !hT.opts.CheckInternal {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping internal check",
			Reference: ref,
		})
		return
	}

	// First lookup in document store,
	refDoc, refExists := hT.documentStore.ResolveRef(ref)

	if refExists {
		// If the resolved ref is an index.html and the path doesn't end in a
		// trailing slash (and isn't linking directly to the index), complain.
		if !hT.opts.IgnoreDirectoryMissingTrailingSlash && path.Base(refDoc.SitePath) == hT.opts.DirectoryIndex &&
			!strings.HasSuffix(ref.URL.Path, hT.opts.DirectoryIndex) && !strings.HasSuffix(ref.URL.Path, "/") {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "target is a directory, href lacks trailing slash",
				Reference: ref,
			})
			refExists = false
		}
	} else {
		// If that fails attempt to lookup with filesystem, resolve a path and check
		refOsPath := path.Join(hT.opts.DirectoryPath, ref.RefSitePath())
		refExists = hT.checkFile(ref, refOsPath)
	}

	if refExists && len(ref.URL.Fragment) > 0 {
		// Is also a hash link
		hT.checkInternalHash(ref)
	}
}

func (hT *HTMLTest) checkInternalHash(ref *htmldoc.Reference) {
	if !hT.opts.CheckInternalHash {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping hash check",
			Reference: ref,
		})
		return
	}

	// var refDoc *htmldoc.Document
	if len(ref.URL.Fragment) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "missing hash",
			Reference: ref,
		})
	}

	if len(ref.URL.Path) > 0 {
		// internal
		refDoc, _ := hT.documentStore.ResolveRef(ref)
		if !refDoc.IsHashValid(ref.URL.Fragment) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "hash does not exist",
				Reference: ref,
			})
		}
	} else {
		// self
		if !ref.Document.IsHashValid(ref.URL.Fragment) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "hash does not exist",
				Reference: ref,
			})
		}
	}
}

func (hT *HTMLTest) checkFile(ref *htmldoc.Reference, absPath string) bool {
	f, err := os.Stat(absPath)
	if os.IsNotExist(err) {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "target does not exist",
			Reference: ref,
		})
		return false
	}
	output.CheckErrorPanic(err)

	if f.IsDir() {
		f, err = os.Stat(path.Join(absPath, hT.opts.DirectoryIndex))
		if os.IsNotExist(err) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "target is a directory, no index",
				Reference: ref,
			})
			return false
		}
	}
	return true
}

func (hT *HTMLTest) checkMailto(ref *htmldoc.Reference) {
	if !hT.opts.CheckMailto {
		return
	}
	if len(ref.URL.Opaque) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "mailto is empty",
			Reference: ref,
		})
		return
	}
	if !strings.Contains(ref.URL.Opaque, "@") {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "contains an invalid email address",
			Reference: ref,
		})
		return
	}
}

func (hT *HTMLTest) checkTel(ref *htmldoc.Reference) {
	if !hT.opts.CheckTel {
		return
	}
	if len(ref.URL.Opaque) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "tel is empty",
			Reference: ref,
		})
		return
	}
}
<commit_msg>Fix bad error handling in checkExternal<commit_after>package htmltest

import (
	"crypto/x509"
	"fmt"
	"github.com/wjdp/htmltest/htmldoc"
	"github.com/wjdp/htmltest/issues"
	"github.com/wjdp/htmltest/output"
	"golang.org/x/net/html"
	"net/http"
	"net/url"
	"os"
	"path"
	"strings"
)

func (hT *HTMLTest) checkLink(document *htmldoc.Document, node *html.Node) {
	attrs := htmldoc.ExtractAttrs(node.Attr,
		[]string{"href", "rel"})

	// Check if favicon
	if htmldoc.AttrPresent(node.Attr, "rel") &&
		(attrs["rel"] == "icon" || attrs["rel"] == "shortcut icon") &&
		node.Parent.Data == "head" {
		document.State.FaviconPresent = true
	}

	// Ignore if rel=dns-prefetch, see #40. If we have more cases like this a hashable type should be created and
	// checked against.
	if attrs["rel"] == "dns-prefetch" {
		return
	}

	// Create reference
	ref, err := htmldoc.NewReference(document, node, attrs["href"])
	if err != nil {
		hT.issueStore.AddIssue(issues.Issue{
			Level:    issues.LevelError,
			Document: document,
			Message:  fmt.Sprintf("bad reference: %q", err),
		})
		return
	}

	// Check for missing href, fail for link nodes
	if !htmldoc.AttrPresent(node.Attr, "href") {
		switch node.Data {
		case "a":
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelDebug,
				Message:   "anchor without href",
				Reference: ref,
			})
			return
		case "link":
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "link tag missing href",
				Reference: ref,
			})
			return
		}
	}

	// Blank href
	if attrs["href"] == "" {
		if !hT.opts.IgnoreEmptyHref {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "href blank",
				Reference: ref,
			})
		}
		return
	}

	// href="#"
	if attrs["href"] == "#" {
		if hT.opts.CheckInternalHash && !hT.opts.IgnoreInternalEmptyHash {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "empty hash",
				Reference: ref,
			})
		}
		return
	}

	// Route reference check
	switch ref.Scheme() {
	case "http":
		hT.enforceHTTPS(ref)
		hT.checkExternal(ref)
	case "https":
		hT.checkExternal(ref)
	case "file":
		hT.checkInternal(ref)
	case "self":
		hT.checkInternalHash(ref)
	case "mailto":
		hT.checkMailto(ref)
	case "tel":
		hT.checkTel(ref)
	}

	// TODO: Other schemes
	// What to do about unknown schemes, could be perfectly valid or a typo.
	// Perhaps show a warning, which can be suppressed per-scheme in options.
	// Preload with a couple of common ones, ftp &c.

}

func (hT *HTMLTest) checkExternal(ref *htmldoc.Reference) {
	issueLevel := issues.LevelError
	if hT.opts.IgnoreExternalBrokenLinks {
		issueLevel = issues.LevelWarning
	}
	if !hT.opts.CheckExternal {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping external check",
			Reference: ref,
		})
		return
	}

	urlStr := ref.URLString()

	// Does this url match an url ignore rule?
	if hT.opts.isURLIgnored(urlStr) {
		return
	}

	if hT.opts.StripQueryString && !InList(hT.opts.StripQueryExcludes, urlStr) {
		urlStr = htmldoc.URLStripQueryString(urlStr)
	}
	var statusCode int

	cR, isCached := hT.refCache.Get(urlStr)

	if isCached && statusCodeValid(cR.StatusCode) {
		// If we have a valid result in cache, use that
		statusCode = cR.StatusCode
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "from cache",
			Reference: ref,
		})
	} else {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "fresh",
			Reference: ref,
		})

		// Build the request
		req, err := http.NewRequest("GET", urlStr, nil)
		// Only error NewRequest raises is if the url isn't valid, we have already checked it by this point so OK just
		// to panic if err != nil.
		output.CheckErrorPanic(err)

		// Set UA header
		req.Header.Set("User-Agent", "htmltest/"+hT.opts.Version)

		// Set headers from HTTPHeaders option
		for key, value := range hT.opts.HTTPHeaders {
			// Due to the way we're loading in config these keys and values are interface{}. In normal cases they are
			// strings, but could very easily be ints (side note: this isn't great, we'll fix this later, #73)
			req.Header.Set(fmt.Sprintf("%v", key), fmt.Sprintf("%v", value))
		}

		hT.httpChannel <- true // Add to http concurrency limiter

		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelInfo,
			Message:   "hitting",
			Reference: ref,
		})

		resp, err := hT.httpClient.Do(req)

		<-hT.httpChannel // Bump off http concurrency limiter

		if err != nil {
			if strings.Contains(err.Error(), "Client.Timeout") {
				hT.issueStore.AddIssue(issues.Issue{
					Level:     issueLevel,
					Message:   "request exceeded our ExternalTimeout",
					Reference: ref,
				})
				return
			}

			if certErr, ok := err.(*url.Error).Err.(x509.UnknownAuthorityError); ok {
				err = validateCertChain(certErr.Cert)
				if err == nil {
					hT.issueStore.AddIssue(issues.Issue{
						Level:     issues.LevelWarning,
						Reference: ref,
						Message:   "incomplete certificate chain",
					})
					return
				}
			}

			// More generic, should be kept below more specific cases
			if strings.Contains(err.Error(), "dial tcp") {
				// Remove long prefix
				prefix := "Get " + urlStr + ": dial tcp: lookup "
				cleanedMessage := strings.TrimPrefix(err.Error(), prefix)
				// Add error
				hT.issueStore.AddIssue(issues.Issue{
					Level:     issueLevel,
					Message:   cleanedMessage,
					Reference: ref,
				})
				return
			}

			// Unhandled client error, return generic error
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issueLevel,
				Message:   err.Error(),
				Reference: ref,
			})

			return
		}
		// Save cached result
		hT.refCache.Save(urlStr, resp.StatusCode)
		statusCode = resp.StatusCode
	}

	switch statusCode {
	case http.StatusOK:
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   http.StatusText(statusCode),
			Reference: ref,
		})
	case http.StatusPartialContent:
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   http.StatusText(statusCode),
			Reference: ref,
		})
	default:
		attrs := htmldoc.ExtractAttrs(ref.Node.Attr, []string{"rel"})
		if attrs["rel"] == "canonical" && hT.opts.IgnoreCanonicalBrokenLinks {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelWarning,
				Message:   http.StatusText(statusCode) + " [rel=\"canonical\"]",
				Reference: ref,
			})
		} else {
			// Failed VCRed requests end up here with a status code of zero
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issueLevel,
				Message:   fmt.Sprintf("%s %d", "Non-OK status:", statusCode),
				Reference: ref,
			})
		}
	}

	// TODO check a hash id exists in external page if present in reference (URL.Fragment)
}

func (hT *HTMLTest) checkInternal(ref *htmldoc.Reference) {
	if !hT.opts.CheckInternal {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping internal check",
			Reference: ref,
		})
		return
	}

	// First lookup in document store,
	refDoc, refExists := hT.documentStore.ResolveRef(ref)

	if refExists {
		// If the resolved ref is an index.html and the path doesn't end in a
		// trailing slash (and isn't linking directly to the index), complain.
		if !hT.opts.IgnoreDirectoryMissingTrailingSlash && path.Base(refDoc.SitePath) == hT.opts.DirectoryIndex &&
			!strings.HasSuffix(ref.URL.Path, hT.opts.DirectoryIndex) && !strings.HasSuffix(ref.URL.Path, "/") {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "target is a directory, href lacks trailing slash",
				Reference: ref,
			})
			refExists = false
		}
	} else {
		// If that fails attempt to lookup with filesystem, resolve a path and check
		refOsPath := path.Join(hT.opts.DirectoryPath, ref.RefSitePath())
		refExists = hT.checkFile(ref, refOsPath)
	}

	if refExists && len(ref.URL.Fragment) > 0 {
		// Is also a hash link
		hT.checkInternalHash(ref)
	}
}

func (hT *HTMLTest) checkInternalHash(ref *htmldoc.Reference) {
	if !hT.opts.CheckInternalHash {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelDebug,
			Message:   "skipping hash check",
			Reference: ref,
		})
		return
	}

	// var refDoc *htmldoc.Document
	if len(ref.URL.Fragment) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "missing hash",
			Reference: ref,
		})
	}

	if len(ref.URL.Path) > 0 {
		// internal
		refDoc, _ := hT.documentStore.ResolveRef(ref)
		if !refDoc.IsHashValid(ref.URL.Fragment) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "hash does not exist",
				Reference: ref,
			})
		}
	} else {
		// self
		if !ref.Document.IsHashValid(ref.URL.Fragment) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "hash does not exist",
				Reference: ref,
			})
		}
	}
}

func (hT *HTMLTest) checkFile(ref *htmldoc.Reference, absPath string) bool {
	f, err := os.Stat(absPath)
	if os.IsNotExist(err) {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "target does not exist",
			Reference: ref,
		})
		return false
	}
	output.CheckErrorPanic(err)

	if f.IsDir() {
		f, err = os.Stat(path.Join(absPath, hT.opts.DirectoryIndex))
		if os.IsNotExist(err) {
			hT.issueStore.AddIssue(issues.Issue{
				Level:     issues.LevelError,
				Message:   "target is a directory, no index",
				Reference: ref,
			})
			return false
		}
	}
	return true
}

func (hT *HTMLTest) checkMailto(ref *htmldoc.Reference) {
	if !hT.opts.CheckMailto {
		return
	}
	if len(ref.URL.Opaque) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "mailto is empty",
			Reference: ref,
		})
		return
	}
	if !strings.Contains(ref.URL.Opaque, "@") {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "contains an invalid email address",
			Reference: ref,
		})
		return
	}
}

func (hT *HTMLTest) checkTel(ref *htmldoc.Reference) {
	if !hT.opts.CheckTel {
		return
	}
	if len(ref.URL.Opaque) == 0 {
		hT.issueStore.AddIssue(issues.Issue{
			Level:     issues.LevelError,
			Message:   "tel is empty",
			Reference: ref,
		})
		return
	}
}
<|endoftext|>


<commit_before>package Image

import (
	"GT/Graphics/Opengl"
	"fmt"
	"image"

	_ "image/jpeg"
	_ "image/png"
)

type GameImage interface {
	image.Image
	VertexData() *Opengl.OpenGLVertexInfo
}

type Image struct {
	// data          *image.Image
	*aggregateImageSection

	// uvs for image
	uvs []float32

	isFont bool
}

type FontImage struct {
	Image
	r rune
}

func (this *FontImage) VertexData() *Opengl.OpenGLVertexInfo {
	v := Opengl.OpenGLVertexInfo{}

	w := float32(this.Bounds().Dx())
	h := float32(this.Bounds().Dy())

	// first tri
	idx := v.NewVertex(0*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[0], this.uvs[1])

	idx = v.NewVertex(1*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(0*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	// second tri
	idx = v.NewVertex(0*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	idx = v.NewVertex(1*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(1*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[4], this.uvs[5])

	return &v
}

// SpriteSheetImage TODO: may be useful for sprite grouping in aggregate, SpriteRenderer will handle animations though
type SpriteSheetImage struct {
	Image
	section image.Rectangle
}

// Bounds will "override" the normal image bounds
func (this Image) Bounds() image.Rectangle {
	return this.section
}

func getUVFromPosition(point image.Point) (u, v float32) {

	u = float32(point.X) / float32(AggrImg.aggregateImage.Bounds().Dx())
	v = float32(point.Y) / float32(AggrImg.aggregateImage.Bounds().Dy())

	return
}

func getUVs(bounds image.Rectangle) []float32 {
	var uvs []float32

	topLeft := image.Point{bounds.Min.X, bounds.Min.Y}
	u, v := getUVFromPosition(topLeft)
	uvs = append(uvs, u, v)

	topRight := image.Point{bounds.Max.X, bounds.Min.Y}
	u, v = getUVFromPosition(topRight)
	uvs = append(uvs, u, v)

	bottomRight := image.Point{bounds.Max.X, bounds.Max.Y}
	u, v = getUVFromPosition(bottomRight)
	uvs = append(uvs, u, v)

	bottomLeft := image.Point{bounds.Min.X, bounds.Max.Y}
	u, v = getUVFromPosition(bottomLeft)
	uvs = append(uvs, u, v)

	return uvs
}

func (this *Image) VertexData() *Opengl.OpenGLVertexInfo {
	v := Opengl.OpenGLVertexInfo{}

	w := float32(this.Bounds().Dx())
	h := float32(this.Bounds().Dy())

	// first tri
	idx := v.NewVertex(-0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[0], this.uvs[1])

	idx = v.NewVertex(0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(-0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	// second tri
	idx = v.NewVertex(-0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	idx = v.NewVertex(0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[4], this.uvs[5])

	return &v
}

// returns a new image that is just a sub image based on given bounds
func (this *Image) SubImage(bounds image.Rectangle) (Image, error) {

	// create totally new image with copy of section
	aggrImgCopy := *this.aggregateImageSection
	img := Image{aggregateImageSection: &aggrImgCopy}

	origB := img.Bounds()

	if origB.Bounds().Dy() < bounds.Dy() {
		return img, fmt.Errorf("Bounds of sub image exceeds image size, sub %d vs original %d", bounds.Dy(), origB.Dy())
	}

	// this is actually a sub image within a subimage

	img.section = image.Rectangle{origB.Min.Add(bounds.Min), origB.Min.Add(bounds.Max)}
	img.uvs = getUVs(img.section)
	img.isFont = this.isFont

	return img, nil
}

// creates a new image
func NewImage(path string) (retImg Image, err error) {

	if newImg := AggrImg.GetImageSection(path); newImg != nil {

		imgRet := Image{newImg, getUVs(newImg.section), false}

		return imgRet, nil
	}

	return Image{}, fmt.Errorf("Cannot get valid image section for path: %s", path)

}

// creates a new font rune image
func NewFontImage(font string, r rune) (retImg FontImage, err error) {

	if fontSec := AggrImg.GetFontImageSection(font); fontSec != nil {

		fontImg := Image{fontSec.aggregateImageSection, getUVs(fontSec.section), true}

		runeImg, _ := fontImg.SubImage(fontSec.FontSections[r])

		return FontImage{runeImg, r}, nil
	}

	return FontImage{}, fmt.Errorf("Cannot get valid font section for font: %s", font)

}
<commit_msg>removed isFont boolean from Image now that we have GameImage interface<commit_after>package Image

import (
	"GT/Graphics/Opengl"
	"fmt"
	"image"

	_ "image/jpeg"
	_ "image/png"
)

type GameImage interface {
	image.Image
	VertexData() *Opengl.OpenGLVertexInfo
}

type Image struct {
	// data          *image.Image
	*aggregateImageSection

	// uvs for image
	uvs []float32
}

type FontImage struct {
	Image
	r rune
}

func (this *FontImage) VertexData() *Opengl.OpenGLVertexInfo {
	v := Opengl.OpenGLVertexInfo{}

	w := float32(this.Bounds().Dx())
	h := float32(this.Bounds().Dy())

	// first tri
	idx := v.NewVertex(0*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[0], this.uvs[1])

	idx = v.NewVertex(1*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(0*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	// second tri
	idx = v.NewVertex(0*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	idx = v.NewVertex(1*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(1*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[4], this.uvs[5])

	return &v
}

// SpriteSheetImage TODO: may be useful for sprite grouping in aggregate, SpriteRenderer will handle animations though
type SpriteSheetImage struct {
	Image
	section image.Rectangle
}

// Bounds will "override" the normal image bounds
func (this Image) Bounds() image.Rectangle {
	return this.section
}

func getUVFromPosition(point image.Point) (u, v float32) {

	u = float32(point.X) / float32(AggrImg.aggregateImage.Bounds().Dx())
	v = float32(point.Y) / float32(AggrImg.aggregateImage.Bounds().Dy())

	return
}

func getUVs(bounds image.Rectangle) []float32 {
	var uvs []float32

	topLeft := image.Point{bounds.Min.X, bounds.Min.Y}
	u, v := getUVFromPosition(topLeft)
	uvs = append(uvs, u, v)

	topRight := image.Point{bounds.Max.X, bounds.Min.Y}
	u, v = getUVFromPosition(topRight)
	uvs = append(uvs, u, v)

	bottomRight := image.Point{bounds.Max.X, bounds.Max.Y}
	u, v = getUVFromPosition(bottomRight)
	uvs = append(uvs, u, v)

	bottomLeft := image.Point{bounds.Min.X, bounds.Max.Y}
	u, v = getUVFromPosition(bottomLeft)
	uvs = append(uvs, u, v)

	return uvs
}

func (this *Image) VertexData() *Opengl.OpenGLVertexInfo {
	v := Opengl.OpenGLVertexInfo{}

	w := float32(this.Bounds().Dx())
	h := float32(this.Bounds().Dy())

	// first tri
	idx := v.NewVertex(-0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[0], this.uvs[1])

	idx = v.NewVertex(0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(-0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	// second tri
	idx = v.NewVertex(-0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[6], this.uvs[7])

	idx = v.NewVertex(0.5*w, -0.5*h, 1.0)
	v.SetUV(idx, this.uvs[2], this.uvs[3])

	idx = v.NewVertex(0.5*w, 0.5*h, 1.0)
	v.SetUV(idx, this.uvs[4], this.uvs[5])

	return &v
}

// returns a new image that is just a sub image based on given bounds
func (this *Image) SubImage(bounds image.Rectangle) (Image, error) {

	// create totally new image with copy of section
	aggrImgCopy := *this.aggregateImageSection
	img := Image{aggregateImageSection: &aggrImgCopy}

	origB := img.Bounds()

	if origB.Bounds().Dy() < bounds.Dy() {
		return img, fmt.Errorf("Bounds of sub image exceeds image size, sub %d vs original %d", bounds.Dy(), origB.Dy())
	}

	// this is actually a sub image within a subimage

	img.section = image.Rectangle{origB.Min.Add(bounds.Min), origB.Min.Add(bounds.Max)}
	img.uvs = getUVs(img.section)

	return img, nil
}

// creates a new image
func NewImage(path string) (retImg Image, err error) {

	if newImg := AggrImg.GetImageSection(path); newImg != nil {

		imgRet := Image{newImg, getUVs(newImg.section)}

		return imgRet, nil
	}

	return Image{}, fmt.Errorf("Cannot get valid image section for path: %s", path)

}

// creates a new font rune image
func NewFontImage(font string, r rune) (retImg FontImage, err error) {

	if fontSec := AggrImg.GetFontImageSection(font); fontSec != nil {

		fontImg := Image{fontSec.aggregateImageSection, getUVs(fontSec.section)}

		runeImg, _ := fontImg.SubImage(fontSec.FontSections[r])

		return FontImage{runeImg, r}, nil
	}

	return FontImage{}, fmt.Errorf("Cannot get valid font section for font: %s", font)

}
<|endoftext|>


<commit_before>package render

import (
	"strings"

	"github.com/weaveworks/common/mtime"
	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/probe/kubernetes"
	"github.com/weaveworks/scope/report"
)

const (
	k8sNamespaceLabel   = "io.kubernetes.pod.namespace"
	swarmNamespaceLabel = "com.docker.stack.namespace"
)

// CustomRenderer allow for mapping functions that received the entire topology
// in one call - useful for functions that need to consider the entire graph.
// We should minimise the use of this renderer type, as it is very inflexible.
type CustomRenderer struct {
	RenderFunc func(Nodes) Nodes
	Renderer
}

// Render implements Renderer
func (c CustomRenderer) Render(rpt report.Report) Nodes {
	return c.RenderFunc(c.Renderer.Render(rpt))
}

// FilterFunc is the function type used by Filters
type FilterFunc func(report.Node) bool

// AnyFilterFunc checks if any of the filterfuncs matches.
func AnyFilterFunc(fs ...FilterFunc) FilterFunc {
	return func(n report.Node) bool {
		for _, f := range fs {
			if f(n) {
				return true
			}
		}
		return false
	}
}

// ComposeFilterFuncs composes filterfuncs into a single FilterFunc checking all.
func ComposeFilterFuncs(fs ...FilterFunc) FilterFunc {
	return func(n report.Node) bool {
		for _, f := range fs {
			if !f(n) {
				return false
			}
		}
		return true
	}
}

// Complement takes a FilterFunc f and returns a FilterFunc that has the same
// effects, if any, and returns the opposite truth value.
func Complement(f FilterFunc) FilterFunc {
	return func(node report.Node) bool { return !f(node) }
}

// Apply applies the filter to all nodes
func (f FilterFunc) Apply(nodes Nodes) Nodes {
	output := report.Nodes{}
	inDegrees := map[string]int{}
	filtered := nodes.Filtered
	for id, node := range nodes.Nodes {
		if f(node) {
			output[id] = node
			inDegrees[id] = 0
		} else {
			filtered++
		}
	}

	// Deleted nodes also need to be cut as destinations in adjacency lists.
	for id, node := range output {
		newAdjacency := report.MakeIDList()
		for _, dstID := range node.Adjacency {
			if _, ok := output[dstID]; ok {
				newAdjacency = newAdjacency.Add(dstID)
				inDegrees[dstID]++
			}
		}
		node.Adjacency = newAdjacency
		output[id] = node
	}

	// Remove unconnected pseudo nodes, see #483.
	for id, inDegree := range inDegrees {
		if inDegree > 0 {
			continue
		}
		node := output[id]
		if node.Topology != Pseudo || len(node.Adjacency) > 0 {
			continue
		}
		delete(output, id)
		filtered++
	}
	return Nodes{Nodes: output, Filtered: filtered}
}

// Filter removes nodes from a view based on a predicate.
type Filter struct {
	Renderer
	FilterFunc FilterFunc
}

// MakeFilter makes a new Filter (that ignores pseudo nodes).
func MakeFilter(f FilterFunc, r Renderer) Renderer {
	return Filter{
		Renderer: r,
		FilterFunc: func(n report.Node) bool {
			return n.Topology == Pseudo || f(n)
		},
	}
}

// MakeFilterPseudo makes a new Filter that will not ignore pseudo nodes.
func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer {
	return Filter{
		Renderer:   r,
		FilterFunc: f,
	}
}

// Render implements Renderer
func (f Filter) Render(rpt report.Report) Nodes {
	return f.FilterFunc.Apply(f.Renderer.Render(rpt))
}

// IsConnectedMark is the key added to Node.Metadata by
// ColorConnected to indicate a node has an edge pointing to it or
// from it
const IsConnectedMark = "is_connected"

// ColorConnected colors nodes with the IsConnectedMark key if they
// have edges to or from them.  Edges to/from yourself are not counted
// here (see #656).
func ColorConnected(r Renderer) Renderer {
	return CustomRenderer{
		Renderer: r,
		RenderFunc: func(input Nodes) Nodes {
			connected := map[string]struct{}{}
			void := struct{}{}

			for id, node := range input.Nodes {
				for _, adj := range node.Adjacency {
					if adj != id {
						connected[id] = void
						connected[adj] = void
					}
				}
			}

			output := input.Copy()
			for id := range connected {
				output[id] = output[id].WithLatest(IsConnectedMark, mtime.Now(), "true")
			}
			return Nodes{Nodes: output, Filtered: input.Filtered}
		},
	}
}

// IsConnected checks whether the node has been marked with the
// IsConnectedMark.
func IsConnected(node report.Node) bool {
	_, ok := node.Latest.Lookup(IsConnectedMark)
	return ok
}

// FilterUnconnected produces a renderer that filters unconnected nodes
// from the given renderer
func FilterUnconnected(r Renderer) Renderer {
	return MakeFilterPseudo(IsConnected, ColorConnected(r))
}

// FilterUnconnectedPseudo produces a renderer that filters
// unconnected pseudo nodes from the given renderer
func FilterUnconnectedPseudo(r Renderer) Renderer {
	return MakeFilterPseudo(
		func(node report.Node) bool {
			if !IsPseudoTopology(node) {
				return true
			}
			return IsConnected(node)
		},
		ColorConnected(r),
	)
}

// Noop allows all nodes through
func Noop(_ report.Node) bool { return true }

// IsRunning checks if the node is a running docker container
func IsRunning(n report.Node) bool {
	state, ok := n.Latest.Lookup(docker.ContainerState)
	return !ok || (state == docker.StateRunning || state == docker.StateRestarting || state == docker.StatePaused)
}

// IsStopped checks if the node is *not* a running docker container
var IsStopped = Complement(IsRunning)

// IsApplication checks if the node is an "application" node
func IsApplication(n report.Node) bool {
	containerName, _ := n.Latest.Lookup(docker.ContainerName)
	if _, ok := systemContainerNames[containerName]; ok {
		return false
	}
	imageName, _ := n.Latest.Lookup(docker.ImageName)
	imagePrefix := strings.SplitN(imageName, ":", 2)[0] // :(
	if _, ok := systemImagePrefixes[imagePrefix]; ok || kubernetes.IsPauseImageName(imagePrefix) {
		return false
	}
	roleLabel, _ := n.Latest.Lookup(docker.LabelPrefix + "works.weave.role")
	if roleLabel == "system" {
		return false
	}
	roleLabel, _ = n.Latest.Lookup(docker.ImageLabelPrefix + "works.weave.role")
	if roleLabel == "system" {
		return false
	}
	namespace, _ := n.Latest.Lookup(docker.LabelPrefix + k8sNamespaceLabel)
	if namespace == "kube-system" {
		return false
	}
	podName, _ := n.Latest.Lookup(docker.LabelPrefix + "io.kubernetes.pod.name")
	if strings.HasPrefix(podName, "kube-system/") {
		return false
	}
	return true
}

// IsSystem checks if the node is a "system" node
var IsSystem = Complement(IsApplication)

// HasLabel checks if the node has the desired docker label
func HasLabel(labelKey string, labelValue string) FilterFunc {
	return func(n report.Node) bool {
		value, _ := n.Latest.Lookup(docker.LabelPrefix + labelKey)
		if value == labelValue {
			return true
		}
		return false
	}
}

// DoesNotHaveLabel checks if the node does NOT have the specified docker label
func DoesNotHaveLabel(labelKey string, labelValue string) FilterFunc {
	return Complement(HasLabel(labelKey, labelValue))
}

// IsNotPseudo returns true if the node is not a pseudo node
// or internet/service nodes.
func IsNotPseudo(n report.Node) bool {
	return n.Topology != Pseudo || strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix)
}

// IsNamespace checks if the node is a pod/service in the specified namespace
func IsNamespace(namespace string) FilterFunc {
	return func(n report.Node) bool {
		tryKeys := []string{kubernetes.Namespace, docker.LabelPrefix + k8sNamespaceLabel, docker.StackNamespace, docker.LabelPrefix + swarmNamespaceLabel}
		gotNamespace := ""
		for _, key := range tryKeys {
			if value, ok := n.Latest.Lookup(key); ok {
				gotNamespace = value
				break
			}
		}
		// Special case for docker
		if namespace == docker.DefaultNamespace && gotNamespace == "" {
			return true
		}
		return namespace == gotNamespace
	}
}

// IsTopology checks if the node is from a particular report topology
func IsTopology(topology string) FilterFunc {
	return func(n report.Node) bool {
		return n.Topology == topology
	}
}

// IsPseudoTopology returns true if the node is in a pseudo topology,
// mimicing the check performed by MakeFilter() instead of the more complex check in IsNotPseudo()
var IsPseudoTopology = IsTopology(Pseudo)

var systemContainerNames = map[string]struct{}{
	"weavescope": {},
	"weavedns":   {},
	"weave":      {},
	"weaveproxy": {},
	"weaveexec":  {},
	"ecs-agent":  {},
}

var systemImagePrefixes = map[string]struct{}{
	"swarm":                          {},
	"weaveworks/scope":               {},
	"weaveworks/weavedns":            {},
	"weaveworks/weave":               {},
	"weaveworks/weaveproxy":          {},
	"weaveworks/weaveexec":           {},
	"amazon/amazon-ecs-agent":        {},
	"openshift/origin-pod":           {},
	"docker.io/openshift/origin-pod": {},
}
<commit_msg>cosmetic: move a function<commit_after>package render

import (
	"strings"

	"github.com/weaveworks/common/mtime"
	"github.com/weaveworks/scope/probe/docker"
	"github.com/weaveworks/scope/probe/kubernetes"
	"github.com/weaveworks/scope/report"
)

const (
	k8sNamespaceLabel   = "io.kubernetes.pod.namespace"
	swarmNamespaceLabel = "com.docker.stack.namespace"
)

// CustomRenderer allow for mapping functions that received the entire topology
// in one call - useful for functions that need to consider the entire graph.
// We should minimise the use of this renderer type, as it is very inflexible.
type CustomRenderer struct {
	RenderFunc func(Nodes) Nodes
	Renderer
}

// Render implements Renderer
func (c CustomRenderer) Render(rpt report.Report) Nodes {
	return c.RenderFunc(c.Renderer.Render(rpt))
}

// FilterFunc is the function type used by Filters
type FilterFunc func(report.Node) bool

// AnyFilterFunc checks if any of the filterfuncs matches.
func AnyFilterFunc(fs ...FilterFunc) FilterFunc {
	return func(n report.Node) bool {
		for _, f := range fs {
			if f(n) {
				return true
			}
		}
		return false
	}
}

// ComposeFilterFuncs composes filterfuncs into a single FilterFunc checking all.
func ComposeFilterFuncs(fs ...FilterFunc) FilterFunc {
	return func(n report.Node) bool {
		for _, f := range fs {
			if !f(n) {
				return false
			}
		}
		return true
	}
}

// Complement takes a FilterFunc f and returns a FilterFunc that has the same
// effects, if any, and returns the opposite truth value.
func Complement(f FilterFunc) FilterFunc {
	return func(node report.Node) bool { return !f(node) }
}

// Apply applies the filter to all nodes
func (f FilterFunc) Apply(nodes Nodes) Nodes {
	output := report.Nodes{}
	inDegrees := map[string]int{}
	filtered := nodes.Filtered
	for id, node := range nodes.Nodes {
		if f(node) {
			output[id] = node
			inDegrees[id] = 0
		} else {
			filtered++
		}
	}

	// Deleted nodes also need to be cut as destinations in adjacency lists.
	for id, node := range output {
		newAdjacency := report.MakeIDList()
		for _, dstID := range node.Adjacency {
			if _, ok := output[dstID]; ok {
				newAdjacency = newAdjacency.Add(dstID)
				inDegrees[dstID]++
			}
		}
		node.Adjacency = newAdjacency
		output[id] = node
	}

	// Remove unconnected pseudo nodes, see #483.
	for id, inDegree := range inDegrees {
		if inDegree > 0 {
			continue
		}
		node := output[id]
		if node.Topology != Pseudo || len(node.Adjacency) > 0 {
			continue
		}
		delete(output, id)
		filtered++
	}
	return Nodes{Nodes: output, Filtered: filtered}
}

// Filter removes nodes from a view based on a predicate.
type Filter struct {
	Renderer
	FilterFunc FilterFunc
}

// MakeFilter makes a new Filter (that ignores pseudo nodes).
func MakeFilter(f FilterFunc, r Renderer) Renderer {
	return Filter{
		Renderer: r,
		FilterFunc: func(n report.Node) bool {
			return n.Topology == Pseudo || f(n)
		},
	}
}

// MakeFilterPseudo makes a new Filter that will not ignore pseudo nodes.
func MakeFilterPseudo(f FilterFunc, r Renderer) Renderer {
	return Filter{
		Renderer:   r,
		FilterFunc: f,
	}
}

// Render implements Renderer
func (f Filter) Render(rpt report.Report) Nodes {
	return f.FilterFunc.Apply(f.Renderer.Render(rpt))
}

// IsConnectedMark is the key added to Node.Metadata by
// ColorConnected to indicate a node has an edge pointing to it or
// from it
const IsConnectedMark = "is_connected"

// IsConnected checks whether the node has been marked with the
// IsConnectedMark.
func IsConnected(node report.Node) bool {
	_, ok := node.Latest.Lookup(IsConnectedMark)
	return ok
}

// ColorConnected colors nodes with the IsConnectedMark key if they
// have edges to or from them.  Edges to/from yourself are not counted
// here (see #656).
func ColorConnected(r Renderer) Renderer {
	return CustomRenderer{
		Renderer: r,
		RenderFunc: func(input Nodes) Nodes {
			connected := map[string]struct{}{}
			void := struct{}{}

			for id, node := range input.Nodes {
				for _, adj := range node.Adjacency {
					if adj != id {
						connected[id] = void
						connected[adj] = void
					}
				}
			}

			output := input.Copy()
			for id := range connected {
				output[id] = output[id].WithLatest(IsConnectedMark, mtime.Now(), "true")
			}
			return Nodes{Nodes: output, Filtered: input.Filtered}
		},
	}
}

// FilterUnconnected produces a renderer that filters unconnected nodes
// from the given renderer
func FilterUnconnected(r Renderer) Renderer {
	return MakeFilterPseudo(IsConnected, ColorConnected(r))
}

// FilterUnconnectedPseudo produces a renderer that filters
// unconnected pseudo nodes from the given renderer
func FilterUnconnectedPseudo(r Renderer) Renderer {
	return MakeFilterPseudo(
		func(node report.Node) bool {
			if !IsPseudoTopology(node) {
				return true
			}
			return IsConnected(node)
		},
		ColorConnected(r),
	)
}

// Noop allows all nodes through
func Noop(_ report.Node) bool { return true }

// IsRunning checks if the node is a running docker container
func IsRunning(n report.Node) bool {
	state, ok := n.Latest.Lookup(docker.ContainerState)
	return !ok || (state == docker.StateRunning || state == docker.StateRestarting || state == docker.StatePaused)
}

// IsStopped checks if the node is *not* a running docker container
var IsStopped = Complement(IsRunning)

// IsApplication checks if the node is an "application" node
func IsApplication(n report.Node) bool {
	containerName, _ := n.Latest.Lookup(docker.ContainerName)
	if _, ok := systemContainerNames[containerName]; ok {
		return false
	}
	imageName, _ := n.Latest.Lookup(docker.ImageName)
	imagePrefix := strings.SplitN(imageName, ":", 2)[0] // :(
	if _, ok := systemImagePrefixes[imagePrefix]; ok || kubernetes.IsPauseImageName(imagePrefix) {
		return false
	}
	roleLabel, _ := n.Latest.Lookup(docker.LabelPrefix + "works.weave.role")
	if roleLabel == "system" {
		return false
	}
	roleLabel, _ = n.Latest.Lookup(docker.ImageLabelPrefix + "works.weave.role")
	if roleLabel == "system" {
		return false
	}
	namespace, _ := n.Latest.Lookup(docker.LabelPrefix + k8sNamespaceLabel)
	if namespace == "kube-system" {
		return false
	}
	podName, _ := n.Latest.Lookup(docker.LabelPrefix + "io.kubernetes.pod.name")
	if strings.HasPrefix(podName, "kube-system/") {
		return false
	}
	return true
}

// IsSystem checks if the node is a "system" node
var IsSystem = Complement(IsApplication)

// HasLabel checks if the node has the desired docker label
func HasLabel(labelKey string, labelValue string) FilterFunc {
	return func(n report.Node) bool {
		value, _ := n.Latest.Lookup(docker.LabelPrefix + labelKey)
		if value == labelValue {
			return true
		}
		return false
	}
}

// DoesNotHaveLabel checks if the node does NOT have the specified docker label
func DoesNotHaveLabel(labelKey string, labelValue string) FilterFunc {
	return Complement(HasLabel(labelKey, labelValue))
}

// IsNotPseudo returns true if the node is not a pseudo node
// or internet/service nodes.
func IsNotPseudo(n report.Node) bool {
	return n.Topology != Pseudo || strings.HasSuffix(n.ID, TheInternetID) || strings.HasPrefix(n.ID, ServiceNodeIDPrefix)
}

// IsNamespace checks if the node is a pod/service in the specified namespace
func IsNamespace(namespace string) FilterFunc {
	return func(n report.Node) bool {
		tryKeys := []string{kubernetes.Namespace, docker.LabelPrefix + k8sNamespaceLabel, docker.StackNamespace, docker.LabelPrefix + swarmNamespaceLabel}
		gotNamespace := ""
		for _, key := range tryKeys {
			if value, ok := n.Latest.Lookup(key); ok {
				gotNamespace = value
				break
			}
		}
		// Special case for docker
		if namespace == docker.DefaultNamespace && gotNamespace == "" {
			return true
		}
		return namespace == gotNamespace
	}
}

// IsTopology checks if the node is from a particular report topology
func IsTopology(topology string) FilterFunc {
	return func(n report.Node) bool {
		return n.Topology == topology
	}
}

// IsPseudoTopology returns true if the node is in a pseudo topology,
// mimicing the check performed by MakeFilter() instead of the more complex check in IsNotPseudo()
var IsPseudoTopology = IsTopology(Pseudo)

var systemContainerNames = map[string]struct{}{
	"weavescope": {},
	"weavedns":   {},
	"weave":      {},
	"weaveproxy": {},
	"weaveexec":  {},
	"ecs-agent":  {},
}

var systemImagePrefixes = map[string]struct{}{
	"swarm":                          {},
	"weaveworks/scope":               {},
	"weaveworks/weavedns":            {},
	"weaveworks/weave":               {},
	"weaveworks/weaveproxy":          {},
	"weaveworks/weaveexec":           {},
	"amazon/amazon-ecs-agent":        {},
	"openshift/origin-pod":           {},
	"docker.io/openshift/origin-pod": {},
}
<|endoftext|>