<commit_before>package hl7

import (
	"errors"
)

// Data is an interface the represents a piece of data that
// is indexable.
type Data interface {
	Index(index int) (Data, bool)
}

// Composer is an interface that represents a piece of data that
// is indexable and composable.
type Composer interface {
	Data
	Append(values ...Data) error
}

// Field represents a slice of bytes. This is the most basic type in
// a HL7 message.
// e.g., in: MSH|^~\&|field|component1^component2
// "MSH", "^~\&", "field", "component1", "component2" are all Fields.
type Field []byte

// Index just returns itself at index 0.
func (f Field) Index(index int) (Data, bool) {
	if index != 0 {
		return nil, false
	}

	return f, true
}

func (f Field) String() string {
	return string(f)
}

// SubComponent is a type that contains Fields. Usually separated by '&'.
// e.g. subcomponent1&subcomponent2
type SubComponent []Field

// Index returns the Data at a certain index inside this SubComponent.
func (s SubComponent) Index(index int) (Data, bool) {
	if index < 0 || index >= len(s) {
		return nil, false
	}

	return s[index], true
}

// Append appends Data to this SubComponent.
func (s *SubComponent) Append(values ...Data) error {
	for _, v := range values {
		value, ok := v.(Field)

		if !ok {
			return errors.New(
				"subcomponents may only contain Fields",
			)
		}

		*s = append(*s, value)
	}

	return nil
}

// Component is a type that contains Fields and SubComponents. Usually separated by '^'.
// e.g. component1^component2
type Component []Data

// Index returns the Data at a certain index inside this Component.
func (c Component) Index(index int) (Data, bool) {
	if index < 0 || index >= len(c) {
		return nil, false
	}

	return c[index], true
}

// Append appends Data to this Component.
func (c *Component) Append(values ...Data) error {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent:
		default:
			return errors.New(
				"components may only contain Fields and SubComponents",
			)
		}

		*c = append(*c, v)
	}

	return nil
}

// Repeated is a type that contains Fields, SubComponents, and Components.
// Usually separated by '~'.
// e.g. component1a^component2a~component1b^component2b
type Repeated []Data

// Index returns the Data at a certain index inside this Repeated.
func (r Repeated) Index(index int) (Data, bool) {
	if index < 0 || index >= len(r) {
		return nil, false
	}

	return r[index], true
}

// Append appends Data to this Repeated.
func (r *Repeated) Append(values ...Data) error {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent, Component:
		default:
			return errors.New(
				"repeated fields may only contain Fields, SubComponents, and Components",
			)
		}

		*r = append(*r, v)
	}

	return nil
}

// Segment is a type that contains Fields, SubComponents, Components, and Repeated.
// Usually separated by '|'.
// e.g. MSH|field1
type Segment []Data

// Index returns the Data at a certain index inside this Segment.
func (s Segment) Index(index int) (Data, bool) {
	if index < 0 || index >= len(s) {
		return nil, false
	}

	return s[index], true
}

// Append appends Data to this Segment.
func (s *Segment) Append(values ...Data) (err error) {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent, Component, Repeated:
		default:
			return errors.New(
				"segments may only contain Fields, SubComponents, Components, and Repeated",
			)
		}

		*s = append(*s, v)
	}

	return nil
}
<commit_msg>added Len() method to the Data interface<commit_after>package hl7

import (
	"errors"
)

// Data is an interface the represents a piece of data that
// is indexable.
type Data interface {
	Index(index int) (Data, bool)
	Len() int
}

// Composer is an interface that represents a piece of data that
// is indexable and composable.
type Composer interface {
	Data
	Append(values ...Data) error
}

// Field represents a slice of bytes. This is the most basic type in
// a HL7 message.
// e.g., in: MSH|^~\&|field|component1^component2
// "MSH", "^~\&", "field", "component1", "component2" are all Fields.
type Field []byte

// Index just returns itself at index 0.
func (f Field) Index(index int) (Data, bool) {
	if index != 0 {
		return nil, false
	}

	return f, true
}

func (f Field) Len() int {
	return 1
}

func (f Field) String() string {
	return string(f)
}

// SubComponent is a type that contains Fields. Usually separated by '&'.
// e.g. subcomponent1&subcomponent2
type SubComponent []Field

// Index returns the Data at a certain index inside this SubComponent.
func (s SubComponent) Index(index int) (Data, bool) {
	if index < 0 || index >= len(s) {
		return nil, false
	}

	return s[index], true
}

func (s SubComponent) Len() int {
	return len(s)
}

// Append appends Data to this SubComponent.
func (s *SubComponent) Append(values ...Data) error {
	for _, v := range values {
		value, ok := v.(Field)

		if !ok {
			return errors.New(
				"subcomponents may only contain Fields",
			)
		}

		*s = append(*s, value)
	}

	return nil
}

// Component is a type that contains Fields and SubComponents. Usually separated by '^'.
// e.g. component1^component2
type Component []Data

// Index returns the Data at a certain index inside this Component.
func (c Component) Index(index int) (Data, bool) {
	if index < 0 || index >= len(c) {
		return nil, false
	}

	return c[index], true
}

func (c Component) Len() int {
	return len(c)
}

// Append appends Data to this Component.
func (c *Component) Append(values ...Data) error {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent:
		default:
			return errors.New(
				"components may only contain Fields and SubComponents",
			)
		}

		*c = append(*c, v)
	}

	return nil
}

// Repeated is a type that contains Fields, SubComponents, and Components.
// Usually separated by '~'.
// e.g. component1a^component2a~component1b^component2b
type Repeated []Data

// Index returns the Data at a certain index inside this Repeated.
func (r Repeated) Index(index int) (Data, bool) {
	if index < 0 || index >= len(r) {
		return nil, false
	}

	return r[index], true
}

func (r Repeated) Len() int {
	return len(r)
}

// Append appends Data to this Repeated.
func (r *Repeated) Append(values ...Data) error {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent, Component:
		default:
			return errors.New(
				"repeated fields may only contain Fields, SubComponents, and Components",
			)
		}

		*r = append(*r, v)
	}

	return nil
}

// Segment is a type that contains Fields, SubComponents, Components, and Repeated.
// Usually separated by '|'.
// e.g. MSH|field1
type Segment []Data

// Index returns the Data at a certain index inside this Segment.
func (s Segment) Index(index int) (Data, bool) {
	if index < 0 || index >= len(s) {
		return nil, false
	}

	return s[index], true
}

func (s Segment) Len() int {
	return len(s)
}

// Append appends Data to this Segment.
func (s *Segment) Append(values ...Data) (err error) {
	for _, v := range values {
		switch v.(type) {
		case Field, SubComponent, Component, Repeated:
		default:
			return errors.New(
				"segments may only contain Fields, SubComponents, Components, and Repeated",
			)
		}

		*s = append(*s, v)
	}

	return nil
}
<|endoftext|>


<commit_before>package hostess

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strings"
)

const default_osx = `
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost
`

const default_linux = `
127.0.0.1   localhost
127.0.1.1   HOSTNAME

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
`

type Hostname struct {
	Domain  string
	Ip      string
	Enabled bool
	// Ipv6    bool
}

func (h *Hostname) Format() string {
	r := fmt.Sprintf("%s %s", h.Ip, h.Domain)
	if !h.Enabled {
		r = "# " + r
	}
	return r
}

// Hostfile represents /etc/hosts (or a similar file, depending on OS), and
// includes a list of Hostnames. Hostfile includes
type Hostfile struct {
	Path  string
	Hosts map[string]*Hostname
	data  string
}

func TrimWS(s string) string {
	return strings.Trim(s, " \n\t")
}

// NewHostFile creates a new Hostfile object from the specified file.
func NewHostfile(path string) *Hostfile {
	return &Hostfile{path, make(map[string]*Hostname), ""}
}

func (h *Hostfile) ReadFile(path string) string {
	data, err := ioutil.ReadFile(h.Path)
	if err != nil {
		fmt.Println("Can't read ", h.Path)
		os.Exit(1)
	}
	h.data = string(data)
	return h.data
}

var line_parser = regexp.MustCompile(``)

func parseLine(line string) (Hostname, error) {
	// 1. Split on # to discard comments.
	// 2. Split on first space to find the IP
	// 3. Split remainder of line on whitespace to find
	//    domain names
	// 4. Validate the IP (maybe -- could be ipv4 or ipv6)
	hostname := Hostname{}
	if false {
		return hostname, errors.New("Can't parse hostname")
	}
	return hostname, nil
}

func (h *Hostfile) Read(hostfile string) []Hostname {
	var hosts = make([]Hostname, 0)
	return hosts
}

func getSortedMapKeys(m map[string][]string) []string {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i += 1
	}
	sort.Strings(keys)
	return keys
}

// MoveToFront looks for string in a slice of strings and if it finds it, moves
// it to the front of the slice.
// Note: this could probably be made faster using pointers to switch the values
// instead of copying a bunch of crap, but it works and speed is not a problem.
func MoveToFront(list []string, search string) []string {
	for k, v := range list {
		if v == search {
			list = append(list[:k], list[k+1:]...)
		}
	}
	return append([]string{search}, list...)
}

// ListDomainsByIp will look through Hostfile to find domains that match the
// specified Ip and return them in a sorted slice.
func (h *Hostfile) ListDomainsByIp(ip string) []string {
	names := make([]string, 0)
	for _, v := range h.Hosts {
		if v.Ip == ip {
			names = append(names, v.Domain)
		}
	}
	sort.Strings(names)

	// Magic for localhost only, to make sure it's the first domain on its line
	if ip == "127.0.0.1" {
		names = MoveToFront(names, "localhost")
	}

	return names
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
// 1. List is sorted by IP address
// 2. Commented items are left in place
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, localhost will always appear first in the domain list
func (h *Hostfile) Format() string {
	// localhost := "127.0.0.1 localhost"

	localhosts := make(map[string][]string)
	ips := make(map[string][]string)

	// Map domains and IPs into slices of domains keyd by IP
	// 127.0.0.1 = [localhost, blah, blah2]
	// 2.2.2.3 = [domain1, domain2]
	for _, hostname := range h.Hosts {
		if hostname.Ip[0:4] == "127." {
			localhosts[hostname.Ip] = append(localhosts[hostname.Ip], hostname.Domain)
		} else {
			ips[hostname.Ip] = append(ips[hostname.Ip], hostname.Domain)
		}
	}

	localhosts_keys := getSortedMapKeys(localhosts)
	ips_keys := getSortedMapKeys(ips)
	out := make([]string, 0)

	for _, ip := range localhosts_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, hostname := range h.Hosts {
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	for _, ip := range ips_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, hostname := range h.Hosts {
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	return strings.Join(out, "\n")
}

func (h *Hostfile) Save() error {
	// h.Format(h.Path)
	return nil
}

func (h *Hostfile) Add(host Hostname) error {
	host_f, found := h.Hosts[host.Domain]
	if found {
		if host_f.Ip == host.Ip {
			return errors.New(fmt.Sprintf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.Ip))
		} else {
			return errors.New(fmt.Sprintf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.Ip, host_f.Ip))
		}
	} else {
		h.Hosts[host.Domain] = &host
	}
	return nil
}

func (h *Hostfile) Delete(domain string) {
	delete(h.Hosts, domain)
}

func (h *Hostfile) Enable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = true
	}
}

func (h *Hostfile) Disable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = false
	}
}

func GetHostsPath() string {
	path := os.Getenv("HOSTESS_FILE")
	if path == "" {
		path = "/etc/hosts"
	}
	return path
}

func Hostess() {
	hostfile := NewHostfile(GetHostsPath())
	hostfile.ReadFile(hostfile.Path)
}
<commit_msg>TestFormatHostfile now passes consistently and is not subject to random map order bullshit<commit_after>package hostess

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"sort"
	"strings"
)

const default_osx = `
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##

127.0.0.1       localhost
255.255.255.255 broadcasthost
::1             localhost
fe80::1%lo0     localhost
`

const default_linux = `
127.0.0.1   localhost
127.0.1.1   HOSTNAME

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
ff02::3 ip6-allhosts
`

type Hostname struct {
	Domain  string
	Ip      string
	Enabled bool
	// Ipv6    bool
}

func (h *Hostname) Format() string {
	r := fmt.Sprintf("%s %s", h.Ip, h.Domain)
	if !h.Enabled {
		r = "# " + r
	}
	return r
}

// Hostfile represents /etc/hosts (or a similar file, depending on OS), and
// includes a list of Hostnames. Hostfile includes
type Hostfile struct {
	Path  string
	Hosts map[string]*Hostname
	data  string
}

func TrimWS(s string) string {
	return strings.Trim(s, " \n\t")
}

// NewHostFile creates a new Hostfile object from the specified file.
func NewHostfile(path string) *Hostfile {
	return &Hostfile{path, make(map[string]*Hostname), ""}
}

func (h *Hostfile) ReadFile(path string) string {
	data, err := ioutil.ReadFile(h.Path)
	if err != nil {
		fmt.Println("Can't read ", h.Path)
		os.Exit(1)
	}
	h.data = string(data)
	return h.data
}

var line_parser = regexp.MustCompile(``)

func parseLine(line string) (Hostname, error) {
	// 1. Split on # to discard comments.
	// 2. Split on first space to find the IP
	// 3. Split remainder of line on whitespace to find
	//    domain names
	// 4. Validate the IP (maybe -- could be ipv4 or ipv6)
	hostname := Hostname{}
	if false {
		return hostname, errors.New("Can't parse hostname")
	}
	return hostname, nil
}

func (h *Hostfile) Read(hostfile string) []Hostname {
	var hosts = make([]Hostname, 0)
	return hosts
}

func getSortedMapKeys(m map[string][]string) []string {
	keys := make([]string, len(m))
	i := 0
	for k := range m {
		keys[i] = k
		i += 1
	}
	sort.Strings(keys)
	return keys
}

// MoveToFront looks for string in a slice of strings and if it finds it, moves
// it to the front of the slice.
// Note: this could probably be made faster using pointers to switch the values
// instead of copying a bunch of crap, but it works and speed is not a problem.
func MoveToFront(list []string, search string) []string {
	for k, v := range list {
		if v == search {
			list = append(list[:k], list[k+1:]...)
		}
	}
	return append([]string{search}, list...)
}

// ListDomainsByIp will look through Hostfile to find domains that match the
// specified Ip and return them in a sorted slice.
func (h *Hostfile) ListDomainsByIp(ip string) []string {
	names := make([]string, 0)
	for _, v := range h.Hosts {
		if v.Ip == ip {
			names = append(names, v.Domain)
		}
	}
	sort.Strings(names)

	// Magic for localhost only, to make sure it's the first domain on its line
	if ip == "127.0.0.1" {
		names = MoveToFront(names, "localhost")
	}

	return names
}

// Format takes the current list of Hostnames in this Hostfile and turns it
// into a string suitable for use as an /etc/hosts file.
// Sorting uses the following logic:
// 1. List is sorted by IP address
// 2. Commented items are left in place
// 3. 127.* appears at the top of the list (so boot resolvers don't break)
// 4. When present, localhost will always appear first in the domain list
func (h *Hostfile) Format() string {
	// localhost := "127.0.0.1 localhost"

	localhosts := make(map[string][]string)
	ips := make(map[string][]string)

	// Map domains and IPs into slices of domains keyd by IP
	// 127.0.0.1 = [localhost, blah, blah2]
	// 2.2.2.3 = [domain1, domain2]
	for _, hostname := range h.Hosts {
		if hostname.Ip[0:4] == "127." {
			localhosts[hostname.Ip] = append(localhosts[hostname.Ip], hostname.Domain)
		} else {
			ips[hostname.Ip] = append(ips[hostname.Ip], hostname.Domain)
		}
	}

	localhosts_keys := getSortedMapKeys(localhosts)
	ips_keys := getSortedMapKeys(ips)
	out := make([]string, 0)

	for _, ip := range localhosts_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	for _, ip := range ips_keys {
		enabled := ip
		enabled_b := false
		disabled := "# " + ip
		disabled_b := false
		for _, domain := range h.ListDomainsByIp(ip) {
			hostname := *h.Hosts[domain]
			if hostname.Ip == ip {
				if hostname.Enabled {
					enabled += " " + hostname.Domain
					enabled_b = true
				} else {
					disabled += " " + hostname.Domain
					disabled_b = true
				}
			}
		}
		if enabled_b {
			out = append(out, enabled)
		}
		if disabled_b {
			out = append(out, disabled)
		}
	}

	return strings.Join(out, "\n")
}

func (h *Hostfile) Save() error {
	// h.Format(h.Path)
	return nil
}

func (h *Hostfile) Add(host Hostname) error {
	host_f, found := h.Hosts[host.Domain]
	if found {
		if host_f.Ip == host.Ip {
			return errors.New(fmt.Sprintf("Duplicate hostname entry for %s -> %s",
				host.Domain, host.Ip))
		} else {
			return errors.New(fmt.Sprintf("Conflicting hostname entries for %s -> %s and -> %s",
				host.Domain, host.Ip, host_f.Ip))
		}
	} else {
		h.Hosts[host.Domain] = &host
	}
	return nil
}

func (h *Hostfile) Delete(domain string) {
	delete(h.Hosts, domain)
}

func (h *Hostfile) Enable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = true
	}
}

func (h *Hostfile) Disable(domain string) {
	_, ok := h.Hosts[domain]
	if ok {
		h.Hosts[domain].Enabled = false
	}
}

func GetHostsPath() string {
	path := os.Getenv("HOSTESS_FILE")
	if path == "" {
		path = "/etc/hosts"
	}
	return path
}

func Hostess() {
	hostfile := NewHostfile(GetHostsPath())
	hostfile.ReadFile(hostfile.Path)
}
<|endoftext|>


<commit_before>package generator

import (
	"go/build"
	"os"
	"path/filepath"
	"sort"

	"goa.design/goa/codegen"
	"goa.design/goa/eval"
)

// Generate runs the code generation algorithms.
func Generate(dir, cmd string) ([]string, error) {
	// 1. Compute design roots.
	var roots []eval.Root
	{
		rs, err := eval.Context.Roots()
		if err != nil {
			return nil, err
		}
		roots = rs
	}

	// 2. Compute "gen" package import path.
	var genpkg string
	{
		base, err := filepath.Abs(dir)
		if err != nil {
			return nil, err
		}
		pkg, err := build.ImportDir(
			filepath.Join(base, codegen.Gendir), build.FindOnly)
		if err != nil {
			return nil, err
		}
		genpkg = pkg.ImportPath
	}

	// 3. Retrieve goa generators for give command.
	var genfuncs []Genfunc
	{
		gs, err := Generators(cmd)
		if err != nil {
			return nil, err
		}
		genfuncs = gs
	}

	// 4. Generate initial set of files produced by goa code generators.
	var genfiles []*codegen.File
	for _, gen := range genfuncs {
		fs, err := gen(genpkg, roots)
		if err != nil {
			return nil, err
		}
		genfiles = append(genfiles, fs...)
	}

	// 5. Run the code generation plugins.
	genfiles, err := codegen.RunPlugins(cmd, genpkg, roots, genfiles)
	if err != nil {
		return nil, err
	}

	// 6. Write the files.
	written := make(map[string]struct{})
	for _, f := range genfiles {
		filename, err := f.Render(dir)
		if err != nil {
			return nil, err
		}
		written[filename] = struct{}{}
	}

	// 7. Compute all output filenames.
	var outputs []string
	{
		outputs = make([]string, len(written))
		cwd, err := os.Getwd()
		if err != nil {
			cwd = "."
		}
		i := 0
		for o := range written {
			rel, err := filepath.Rel(cwd, o)
			if err != nil {
				rel = o
			}
			outputs[i] = rel
			i++
		}
	}
	sort.Strings(outputs)

	return outputs, nil
}
<commit_msg>Fix goa gen for Go 1.9<commit_after>package generator

import (
	"go/build"
	"os"
	"path/filepath"
	"sort"

	"goa.design/goa/codegen"
	"goa.design/goa/eval"
)

// Generate runs the code generation algorithms.
func Generate(dir, cmd string) ([]string, error) {
	// 1. Compute design roots.
	var roots []eval.Root
	{
		rs, err := eval.Context.Roots()
		if err != nil {
			return nil, err
		}
		roots = rs
	}

	// 2. Compute "gen" package import path.
	var genpkg string
	{
		base, err := filepath.Abs(dir)
		if err != nil {
			return nil, err
		}
		path := filepath.Join(base, codegen.Gendir)
		if err := os.MkdirAll(path, 0777); err != nil {
			return nil, err
		}
		pkg, err := build.ImportDir(path, build.FindOnly)
		if err != nil {
			return nil, err
		}
		genpkg = pkg.ImportPath
	}

	// 3. Retrieve goa generators for give command.
	var genfuncs []Genfunc
	{
		gs, err := Generators(cmd)
		if err != nil {
			return nil, err
		}
		genfuncs = gs
	}

	// 4. Generate initial set of files produced by goa code generators.
	var genfiles []*codegen.File
	for _, gen := range genfuncs {
		fs, err := gen(genpkg, roots)
		if err != nil {
			return nil, err
		}
		genfiles = append(genfiles, fs...)
	}

	// 5. Run the code generation plugins.
	genfiles, err := codegen.RunPlugins(cmd, genpkg, roots, genfiles)
	if err != nil {
		return nil, err
	}

	// 6. Write the files.
	written := make(map[string]struct{})
	for _, f := range genfiles {
		filename, err := f.Render(dir)
		if err != nil {
			return nil, err
		}
		written[filename] = struct{}{}
	}

	// 7. Compute all output filenames.
	var outputs []string
	{
		outputs = make([]string, len(written))
		cwd, err := os.Getwd()
		if err != nil {
			cwd = "."
		}
		i := 0
		for o := range written {
			rel, err := filepath.Rel(cwd, o)
			if err != nil {
				rel = o
			}
			outputs[i] = rel
			i++
		}
	}
	sort.Strings(outputs)

	return outputs, nil
}
<|endoftext|>


<commit_before>// +build !travis

package mercury

import (
	"flag"
	"testing"

	"github.com/jguyomard/slackbot-links/src/config"
)

var (
	testURL                   = "http://marcelpociot.com/blog/2016-10-19-write-your-own-slack-bot-using-laravel"
	testExpectedTitle         = "Write a Slack bot using Laravel and PHP"
	testExpectedDatePublished = "2016-10-19T00:00:00.000Z"
	testExpectedImageURL      = "http://marcelpociot.com/user/pages/blog/2016-10-19-write-your-own-slack-bot-using-laravel/Screen%20Shot%202016-10-19%20at%2010.36.15.png"
	testExpectedTotalPages    = 1
)

func init() {
	configFilePtr := flag.String("config-file", "/etc/slackbot-links/config.yaml", "conf file path")
	flag.Parse()
	config.SetFilePath(*configFilePtr)
}

func TestParse(t *testing.T) {
	infos, err := ParseURL(testURL)

	if err != nil {
		t.Fatal(err)
	}

	if infos.Title != testExpectedTitle {
		t.Fatal("mercury.Parse(): incorrect Title")
	}

	if infos.DatePublished != testExpectedDatePublished {
		t.Fatal("mercury.Parse(): incorrect DatePublished")
	}

	if infos.ImageURL != testExpectedImageURL {
		t.Fatal("mercury.Parse(): incorrect ImageURL")
	}

	if infos.TotalPages != testExpectedTotalPages {
		t.Fatal("mercury.Parse(): incorrect title")
	}
}
<commit_msg>Fix Mercury test<commit_after>// +build !travis

package mercury

import (
	"flag"
	"testing"

	"github.com/jguyomard/slackbot-links/src/config"
)

var (
	testURL                   = "https://ilonet.fr/welcome-to-hugo/"
	testExpectedTitle         = "Welcome to Hugo!"
	testExpectedDatePublished = "2016-06-11T00:00:00.000Z"
	testExpectedImageURL      = ""
	testExpectedTotalPages    = 1
	testExpectedWordCount     = 92
	testExpectedDirection     = "ltr"
)

func init() {
	configFilePtr := flag.String("config-file", "/etc/slackbot-links/config.yaml", "conf file path")
	flag.Parse()
	config.SetFilePath(*configFilePtr)
}

func TestParse(t *testing.T) {
	infos, err := ParseURL(testURL)

	if err != nil {
		t.Fatal(err)
	}

	if infos.Title != testExpectedTitle {
		t.Fatal("mercury.Parse(): incorrect Title")
	}

	if infos.DatePublished != testExpectedDatePublished {
		t.Fatal("mercury.Parse(): incorrect DatePublished")
	}

	if infos.ImageURL != testExpectedImageURL {
		t.Fatal("mercury.Parse(): incorrect ImageURL")
	}

	if infos.TotalPages != testExpectedTotalPages {
		t.Fatal("mercury.Parse(): incorrect title")
	}

	if infos.Direction != testExpectedDirection {
		t.Fatal("mercury.Parse(): incorrect title")
	}

	if infos.WordCount != testExpectedWordCount {
		t.Fatal("mercury.Parse(): incorrect title")
	}
}
<|endoftext|>


<commit_before>package common

import (
	"github.com/EngoEngine/ecs"
	"github.com/EngoEngine/engo"
	"github.com/EngoEngine/engo/math"
	"github.com/EngoEngine/gl"
)

type textShader struct {
	program *gl.Program

	indicesRectangles    []uint16
	indicesRectanglesVBO *gl.Buffer

	inPosition  int
	inTexCoords int
	inColor     int

	matrixProjection *gl.UniformLocation
	matrixView       *gl.UniformLocation
	matrixModel      *gl.UniformLocation

	projectionMatrix []float32
	viewMatrix       []float32
	modelMatrix      []float32

	camera        *CameraSystem
	cameraEnabled bool

	lastBuffer  *gl.Buffer
	lastTexture *gl.Texture
}

func (l *textShader) Setup(w *ecs.World) error {
	var err error
	l.program, err = LoadShader(`
attribute vec2 in_Position;
attribute vec2 in_TexCoords;
attribute vec4 in_Color;

uniform mat3 matrixProjection;
uniform mat3 matrixView;
uniform mat3 matrixModel;

varying vec4 var_Color;
varying vec2 var_TexCoords;

void main() {
  var_Color = in_Color;
  var_TexCoords = in_TexCoords;

  vec3 matr = matrixProjection * matrixView * matrixModel * vec3(in_Position, 1.0);
  gl_Position = vec4(matr.xy, 0, matr.z);
}
`, `
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif

varying vec4 var_Color;
varying vec2 var_TexCoords;

uniform sampler2D uf_Texture;

void main (void) {
  gl_FragColor = var_Color * texture2D(uf_Texture, var_TexCoords);
}`)

	if err != nil {
		return err
	}

	// Create and populate indices buffer
	l.indicesRectangles = make([]uint16, 6*bufferSize)
	for i, j := 0, 0; i < bufferSize*6; i, j = i+6, j+4 {
		l.indicesRectangles[i+0] = uint16(j + 0)
		l.indicesRectangles[i+1] = uint16(j + 1)
		l.indicesRectangles[i+2] = uint16(j + 2)
		l.indicesRectangles[i+3] = uint16(j + 0)
		l.indicesRectangles[i+4] = uint16(j + 2)
		l.indicesRectangles[i+5] = uint16(j + 3)
	}
	l.indicesRectanglesVBO = engo.Gl.CreateBuffer()
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectanglesVBO)
	engo.Gl.BufferData(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectangles, engo.Gl.STATIC_DRAW)

	// Define things that should be read from the texture buffer
	l.inPosition = engo.Gl.GetAttribLocation(l.program, "in_Position")
	l.inTexCoords = engo.Gl.GetAttribLocation(l.program, "in_TexCoords")
	l.inColor = engo.Gl.GetAttribLocation(l.program, "in_Color")

	// Define things that should be set per draw
	l.matrixProjection = engo.Gl.GetUniformLocation(l.program, "matrixProjection")
	l.matrixView = engo.Gl.GetUniformLocation(l.program, "matrixView")
	l.matrixModel = engo.Gl.GetUniformLocation(l.program, "matrixModel")

	l.projectionMatrix = make([]float32, 9)
	l.projectionMatrix[8] = 1

	l.viewMatrix = make([]float32, 9)
	l.viewMatrix[0] = 1
	l.viewMatrix[4] = 1
	l.viewMatrix[8] = 1

	l.modelMatrix = make([]float32, 9)
	l.modelMatrix[0] = 1
	l.modelMatrix[4] = 1
	l.modelMatrix[8] = 1

	return nil
}

func (l *textShader) Pre() {
	engo.Gl.Enable(engo.Gl.BLEND)
	engo.Gl.BlendFunc(engo.Gl.SRC_ALPHA, engo.Gl.ONE_MINUS_SRC_ALPHA)

	// Bind shader and buffer, enable attributes
	engo.Gl.UseProgram(l.program)
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectanglesVBO)
	engo.Gl.EnableVertexAttribArray(l.inPosition)
	engo.Gl.EnableVertexAttribArray(l.inTexCoords)
	engo.Gl.EnableVertexAttribArray(l.inColor)

	if engo.ScaleOnResize() {
		l.projectionMatrix[0] = 1 / (engo.GameWidth() / 2)
		l.projectionMatrix[4] = 1 / (-engo.GameHeight() / 2)
	} else {
		l.projectionMatrix[0] = 1 / (engo.CanvasWidth() / (2 * engo.CanvasScale()))
		l.projectionMatrix[4] = 1 / (-engo.CanvasHeight() / (2 * engo.CanvasScale()))
	}

	if l.cameraEnabled {
		l.viewMatrix[1], l.viewMatrix[0] = math.Sincos(l.camera.angle * math.Pi / 180)
		l.viewMatrix[3] = -l.viewMatrix[1]
		l.viewMatrix[4] = l.viewMatrix[0]
		l.viewMatrix[6] = -l.camera.x
		l.viewMatrix[7] = -l.camera.y
		l.viewMatrix[8] = l.camera.z
	} else {
		l.viewMatrix[6] = -1 / l.projectionMatrix[0]
		l.viewMatrix[7] = 1 / l.projectionMatrix[4]
	}

	engo.Gl.UniformMatrix3fv(l.matrixProjection, false, l.projectionMatrix)
	engo.Gl.UniformMatrix3fv(l.matrixView, false, l.viewMatrix)
}

func (l *textShader) updateBuffer(ren *RenderComponent, space *SpaceComponent) {
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
		return
	}

	if len(ren.BufferContent) < 20*len(txt.Text) {
		ren.BufferContent = make([]float32, 20*len(txt.Text)) // TODO: update this to actual value?
	}
	// Reset buffer so artifacts don't occur when txt.Text changes
	for i := 0; i < len(ren.BufferContent); i++ {
		ren.BufferContent[i] = 0
	}
	if changed := l.generateBufferContent(ren, space, ren.BufferContent); !changed {
		return
	}

	if ren.Buffer == nil {
		ren.Buffer = engo.Gl.CreateBuffer()
	}
	engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, ren.Buffer)
	engo.Gl.BufferData(engo.Gl.ARRAY_BUFFER, ren.BufferContent, engo.Gl.STATIC_DRAW)
}

func (l *textShader) generateBufferContent(ren *RenderComponent, space *SpaceComponent, buffer []float32) bool {
	var changed bool

	tint := colorToFloat32(ren.Color)
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
		return false
	}

	atlas, ok := atlasCache[*txt.Font]
	if !ok {
		// Generate texture first
		atlas = txt.Font.generateFontAtlas(UnicodeCap)
		atlasCache[*txt.Font] = atlas
	}

	var currentX float32
	var currentY float32

	var modifier float32 = 1
	if txt.RightToLeft {
		modifier = -1
	}

	letterSpace := float32(txt.Font.Size) * txt.LetterSpacing
	lineSpace := txt.LineSpacing * atlas.Height['X']

	for index, char := range txt.Text {
		// TODO: this might not work for all characters
		switch {
		case char == '\n':
			currentX = 0
			currentY += atlas.Height['X'] + lineSpace
			continue
		case char < 32: // all system stuff should be ignored
			continue
		}

		offset := 20 * index
		xoff := atlas.OffsetX[char]
		yoff := atlas.OffsetY[char]

		// These five are at 0, 0:
		setBufferValue(buffer, 0+offset, currentX+xoff, &changed)
		setBufferValue(buffer, 1+offset, currentY+yoff, &changed)
		setBufferValue(buffer, 2+offset, atlas.XLocation[char]/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 3+offset, atlas.YLocation[char]/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 4+offset, tint, &changed)

		// These five are at 1, 0:
		setBufferValue(buffer, 5+offset, currentX+xoff+atlas.Width[char]+letterSpace, &changed)
		setBufferValue(buffer, 6+offset, currentY+yoff, &changed)
		setBufferValue(buffer, 7+offset, (atlas.XLocation[char]+atlas.Width[char])/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 8+offset, atlas.YLocation[char]/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 9+offset, tint, &changed)

		// These five are at 1, 1:
		setBufferValue(buffer, 10+offset, currentX+xoff+atlas.Width[char]+letterSpace, &changed)
		setBufferValue(buffer, 11+offset, currentY+yoff+atlas.Height[char]+lineSpace, &changed)
		setBufferValue(buffer, 12+offset, (atlas.XLocation[char]+atlas.Width[char])/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 13+offset, (atlas.YLocation[char]+atlas.Height[char])/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 14+offset, tint, &changed)

		// These five are at 0, 1:
		setBufferValue(buffer, 15+offset, currentX+xoff, &changed)
		setBufferValue(buffer, 16+offset, currentY+yoff+atlas.Height[char]+lineSpace, &changed)
		setBufferValue(buffer, 17+offset, atlas.XLocation[char]/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 18+offset, (atlas.YLocation[char]+atlas.Height[char])/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 19+offset, tint, &changed)

		currentX += modifier * (atlas.Width[char] + letterSpace + xoff + atlas.RightSide[char])
	}

	return changed
}

func (l *textShader) Draw(ren *RenderComponent, space *SpaceComponent) {
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
	}

	if l.lastBuffer != ren.Buffer || ren.Buffer == nil {
		l.updateBuffer(ren, space)

		engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, ren.Buffer)
		engo.Gl.VertexAttribPointer(l.inPosition, 2, engo.Gl.FLOAT, false, 20, 0)
		engo.Gl.VertexAttribPointer(l.inTexCoords, 2, engo.Gl.FLOAT, false, 20, 8)
		engo.Gl.VertexAttribPointer(l.inColor, 4, engo.Gl.UNSIGNED_BYTE, true, 20, 16)

		l.lastBuffer = ren.Buffer
	}

	atlas, ok := atlasCache[*txt.Font]
	if !ok {
		// Generate texture first
		atlas = txt.Font.generateFontAtlas(UnicodeCap)
		atlasCache[*txt.Font] = atlas
	}

	if atlas.Texture != l.lastTexture {
		engo.Gl.BindTexture(engo.Gl.TEXTURE_2D, atlas.Texture)
		l.lastTexture = atlas.Texture
	}

	engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_WRAP_S, engo.Gl.CLAMP_TO_EDGE)
	engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_WRAP_T, engo.Gl.CLAMP_TO_EDGE)

	if space.Rotation != 0 {
		sin, cos := math.Sincos(space.Rotation * math.Pi / 180)

		l.modelMatrix[0] = ren.Scale.X * engo.GetGlobalScale().X * cos
		l.modelMatrix[1] = ren.Scale.X * engo.GetGlobalScale().X * sin
		l.modelMatrix[3] = ren.Scale.Y * engo.GetGlobalScale().Y * -sin
		l.modelMatrix[4] = ren.Scale.Y * engo.GetGlobalScale().Y * cos
	} else {
		l.modelMatrix[0] = ren.Scale.X * engo.GetGlobalScale().X
		l.modelMatrix[1] = 0
		l.modelMatrix[3] = 0
		l.modelMatrix[4] = ren.Scale.Y * engo.GetGlobalScale().Y
	}

	l.modelMatrix[6] = space.Position.X * engo.GetGlobalScale().X
	l.modelMatrix[7] = space.Position.Y * engo.GetGlobalScale().Y

	engo.Gl.UniformMatrix3fv(l.matrixModel, false, l.modelMatrix)

	engo.Gl.DrawElements(engo.Gl.TRIANGLES, 6*len(txt.Text), engo.Gl.UNSIGNED_SHORT, 0)
}

func (l *textShader) Post() {
	l.lastBuffer = nil
	l.lastTexture = nil

	// Cleanup
	engo.Gl.DisableVertexAttribArray(l.inPosition)
	engo.Gl.DisableVertexAttribArray(l.inTexCoords)
	engo.Gl.DisableVertexAttribArray(l.inColor)

	engo.Gl.BindTexture(engo.Gl.TEXTURE_2D, nil)
	engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, nil)
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, nil)

	engo.Gl.Disable(engo.Gl.BLEND)
}

func (l *textShader) SetCamera(c *CameraSystem) {
	if l.cameraEnabled {
		l.camera = c
	}
}
<commit_msg>fix linespace and letterspace in the font shader (#789)<commit_after>package common

import (
	"github.com/EngoEngine/ecs"
	"github.com/EngoEngine/engo"
	"github.com/EngoEngine/engo/math"
	"github.com/EngoEngine/gl"
)

type textShader struct {
	program *gl.Program

	indicesRectangles    []uint16
	indicesRectanglesVBO *gl.Buffer

	inPosition  int
	inTexCoords int
	inColor     int

	matrixProjection *gl.UniformLocation
	matrixView       *gl.UniformLocation
	matrixModel      *gl.UniformLocation

	projectionMatrix []float32
	viewMatrix       []float32
	modelMatrix      []float32

	camera        *CameraSystem
	cameraEnabled bool

	lastBuffer  *gl.Buffer
	lastTexture *gl.Texture
}

func (l *textShader) Setup(w *ecs.World) error {
	var err error
	l.program, err = LoadShader(`
attribute vec2 in_Position;
attribute vec2 in_TexCoords;
attribute vec4 in_Color;

uniform mat3 matrixProjection;
uniform mat3 matrixView;
uniform mat3 matrixModel;

varying vec4 var_Color;
varying vec2 var_TexCoords;

void main() {
  var_Color = in_Color;
  var_TexCoords = in_TexCoords;

  vec3 matr = matrixProjection * matrixView * matrixModel * vec3(in_Position, 1.0);
  gl_Position = vec4(matr.xy, 0, matr.z);
}
`, `
#ifdef GL_ES
#define LOWP lowp
precision mediump float;
#else
#define LOWP
#endif

varying vec4 var_Color;
varying vec2 var_TexCoords;

uniform sampler2D uf_Texture;

void main (void) {
  gl_FragColor = var_Color * texture2D(uf_Texture, var_TexCoords);
}`)

	if err != nil {
		return err
	}

	// Create and populate indices buffer
	l.indicesRectangles = make([]uint16, 6*bufferSize)
	for i, j := 0, 0; i < bufferSize*6; i, j = i+6, j+4 {
		l.indicesRectangles[i+0] = uint16(j + 0)
		l.indicesRectangles[i+1] = uint16(j + 1)
		l.indicesRectangles[i+2] = uint16(j + 2)
		l.indicesRectangles[i+3] = uint16(j + 0)
		l.indicesRectangles[i+4] = uint16(j + 2)
		l.indicesRectangles[i+5] = uint16(j + 3)
	}
	l.indicesRectanglesVBO = engo.Gl.CreateBuffer()
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectanglesVBO)
	engo.Gl.BufferData(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectangles, engo.Gl.STATIC_DRAW)

	// Define things that should be read from the texture buffer
	l.inPosition = engo.Gl.GetAttribLocation(l.program, "in_Position")
	l.inTexCoords = engo.Gl.GetAttribLocation(l.program, "in_TexCoords")
	l.inColor = engo.Gl.GetAttribLocation(l.program, "in_Color")

	// Define things that should be set per draw
	l.matrixProjection = engo.Gl.GetUniformLocation(l.program, "matrixProjection")
	l.matrixView = engo.Gl.GetUniformLocation(l.program, "matrixView")
	l.matrixModel = engo.Gl.GetUniformLocation(l.program, "matrixModel")

	l.projectionMatrix = make([]float32, 9)
	l.projectionMatrix[8] = 1

	l.viewMatrix = make([]float32, 9)
	l.viewMatrix[0] = 1
	l.viewMatrix[4] = 1
	l.viewMatrix[8] = 1

	l.modelMatrix = make([]float32, 9)
	l.modelMatrix[0] = 1
	l.modelMatrix[4] = 1
	l.modelMatrix[8] = 1

	return nil
}

func (l *textShader) Pre() {
	engo.Gl.Enable(engo.Gl.BLEND)
	engo.Gl.BlendFunc(engo.Gl.SRC_ALPHA, engo.Gl.ONE_MINUS_SRC_ALPHA)

	// Bind shader and buffer, enable attributes
	engo.Gl.UseProgram(l.program)
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, l.indicesRectanglesVBO)
	engo.Gl.EnableVertexAttribArray(l.inPosition)
	engo.Gl.EnableVertexAttribArray(l.inTexCoords)
	engo.Gl.EnableVertexAttribArray(l.inColor)

	if engo.ScaleOnResize() {
		l.projectionMatrix[0] = 1 / (engo.GameWidth() / 2)
		l.projectionMatrix[4] = 1 / (-engo.GameHeight() / 2)
	} else {
		l.projectionMatrix[0] = 1 / (engo.CanvasWidth() / (2 * engo.CanvasScale()))
		l.projectionMatrix[4] = 1 / (-engo.CanvasHeight() / (2 * engo.CanvasScale()))
	}

	if l.cameraEnabled {
		l.viewMatrix[1], l.viewMatrix[0] = math.Sincos(l.camera.angle * math.Pi / 180)
		l.viewMatrix[3] = -l.viewMatrix[1]
		l.viewMatrix[4] = l.viewMatrix[0]
		l.viewMatrix[6] = -l.camera.x
		l.viewMatrix[7] = -l.camera.y
		l.viewMatrix[8] = l.camera.z
	} else {
		l.viewMatrix[6] = -1 / l.projectionMatrix[0]
		l.viewMatrix[7] = 1 / l.projectionMatrix[4]
	}

	engo.Gl.UniformMatrix3fv(l.matrixProjection, false, l.projectionMatrix)
	engo.Gl.UniformMatrix3fv(l.matrixView, false, l.viewMatrix)
}

func (l *textShader) updateBuffer(ren *RenderComponent, space *SpaceComponent) {
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
		return
	}

	if len(ren.BufferContent) < 20*len(txt.Text) {
		ren.BufferContent = make([]float32, 20*len(txt.Text)) // TODO: update this to actual value?
	}
	// Reset buffer so artifacts don't occur when txt.Text changes
	for i := 0; i < len(ren.BufferContent); i++ {
		ren.BufferContent[i] = 0
	}
	if changed := l.generateBufferContent(ren, space, ren.BufferContent); !changed {
		return
	}

	if ren.Buffer == nil {
		ren.Buffer = engo.Gl.CreateBuffer()
	}
	engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, ren.Buffer)
	engo.Gl.BufferData(engo.Gl.ARRAY_BUFFER, ren.BufferContent, engo.Gl.STATIC_DRAW)
}

func (l *textShader) generateBufferContent(ren *RenderComponent, space *SpaceComponent, buffer []float32) bool {
	var changed bool

	tint := colorToFloat32(ren.Color)
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
		return false
	}

	atlas, ok := atlasCache[*txt.Font]
	if !ok {
		// Generate texture first
		atlas = txt.Font.generateFontAtlas(UnicodeCap)
		atlasCache[*txt.Font] = atlas
	}

	var currentX float32
	var currentY float32

	var modifier float32 = 1
	if txt.RightToLeft {
		modifier = -1
	}

	letterSpace := float32(txt.Font.Size) * txt.LetterSpacing
	lineSpace := txt.LineSpacing * atlas.Height['X']

	for index, char := range txt.Text {
		// TODO: this might not work for all characters
		switch {
		case char == '\n':
			currentX = 0
			currentY += atlas.Height['X'] + lineSpace
			continue
		case char < 32: // all system stuff should be ignored
			continue
		}

		offset := 20 * index
		xoff := atlas.OffsetX[char]
		yoff := atlas.OffsetY[char]

		// These five are at 0, 0:
		setBufferValue(buffer, 0+offset, currentX+xoff, &changed)
		setBufferValue(buffer, 1+offset, currentY+yoff, &changed)
		setBufferValue(buffer, 2+offset, atlas.XLocation[char]/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 3+offset, atlas.YLocation[char]/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 4+offset, tint, &changed)

		// These five are at 1, 0:
		setBufferValue(buffer, 5+offset, currentX+xoff+atlas.Width[char], &changed)
		setBufferValue(buffer, 6+offset, currentY+yoff, &changed)
		setBufferValue(buffer, 7+offset, (atlas.XLocation[char]+atlas.Width[char])/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 8+offset, atlas.YLocation[char]/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 9+offset, tint, &changed)

		// These five are at 1, 1:
		setBufferValue(buffer, 10+offset, currentX+xoff+atlas.Width[char], &changed)
		setBufferValue(buffer, 11+offset, currentY+yoff+atlas.Height[char], &changed)
		setBufferValue(buffer, 12+offset, (atlas.XLocation[char]+atlas.Width[char])/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 13+offset, (atlas.YLocation[char]+atlas.Height[char])/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 14+offset, tint, &changed)

		// These five are at 0, 1:
		setBufferValue(buffer, 15+offset, currentX+xoff, &changed)
		setBufferValue(buffer, 16+offset, currentY+yoff+atlas.Height[char], &changed)
		setBufferValue(buffer, 17+offset, atlas.XLocation[char]/atlas.TotalWidth, &changed)
		setBufferValue(buffer, 18+offset, (atlas.YLocation[char]+atlas.Height[char])/atlas.TotalHeight, &changed)
		setBufferValue(buffer, 19+offset, tint, &changed)

		currentX += modifier * (atlas.Width[char] + letterSpace + xoff + atlas.RightSide[char])
	}

	return changed
}

func (l *textShader) Draw(ren *RenderComponent, space *SpaceComponent) {
	txt, ok := ren.Drawable.(Text)
	if !ok {
		unsupportedType(ren.Drawable)
	}

	if l.lastBuffer != ren.Buffer || ren.Buffer == nil {
		l.updateBuffer(ren, space)

		engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, ren.Buffer)
		engo.Gl.VertexAttribPointer(l.inPosition, 2, engo.Gl.FLOAT, false, 20, 0)
		engo.Gl.VertexAttribPointer(l.inTexCoords, 2, engo.Gl.FLOAT, false, 20, 8)
		engo.Gl.VertexAttribPointer(l.inColor, 4, engo.Gl.UNSIGNED_BYTE, true, 20, 16)

		l.lastBuffer = ren.Buffer
	}

	atlas, ok := atlasCache[*txt.Font]
	if !ok {
		// Generate texture first
		atlas = txt.Font.generateFontAtlas(UnicodeCap)
		atlasCache[*txt.Font] = atlas
	}

	if atlas.Texture != l.lastTexture {
		engo.Gl.BindTexture(engo.Gl.TEXTURE_2D, atlas.Texture)
		l.lastTexture = atlas.Texture
	}

	engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_WRAP_S, engo.Gl.CLAMP_TO_EDGE)
	engo.Gl.TexParameteri(engo.Gl.TEXTURE_2D, engo.Gl.TEXTURE_WRAP_T, engo.Gl.CLAMP_TO_EDGE)

	if space.Rotation != 0 {
		sin, cos := math.Sincos(space.Rotation * math.Pi / 180)

		l.modelMatrix[0] = ren.Scale.X * engo.GetGlobalScale().X * cos
		l.modelMatrix[1] = ren.Scale.X * engo.GetGlobalScale().X * sin
		l.modelMatrix[3] = ren.Scale.Y * engo.GetGlobalScale().Y * -sin
		l.modelMatrix[4] = ren.Scale.Y * engo.GetGlobalScale().Y * cos
	} else {
		l.modelMatrix[0] = ren.Scale.X * engo.GetGlobalScale().X
		l.modelMatrix[1] = 0
		l.modelMatrix[3] = 0
		l.modelMatrix[4] = ren.Scale.Y * engo.GetGlobalScale().Y
	}

	l.modelMatrix[6] = space.Position.X * engo.GetGlobalScale().X
	l.modelMatrix[7] = space.Position.Y * engo.GetGlobalScale().Y

	engo.Gl.UniformMatrix3fv(l.matrixModel, false, l.modelMatrix)

	engo.Gl.DrawElements(engo.Gl.TRIANGLES, 6*len(txt.Text), engo.Gl.UNSIGNED_SHORT, 0)
}

func (l *textShader) Post() {
	l.lastBuffer = nil
	l.lastTexture = nil

	// Cleanup
	engo.Gl.DisableVertexAttribArray(l.inPosition)
	engo.Gl.DisableVertexAttribArray(l.inTexCoords)
	engo.Gl.DisableVertexAttribArray(l.inColor)

	engo.Gl.BindTexture(engo.Gl.TEXTURE_2D, nil)
	engo.Gl.BindBuffer(engo.Gl.ARRAY_BUFFER, nil)
	engo.Gl.BindBuffer(engo.Gl.ELEMENT_ARRAY_BUFFER, nil)

	engo.Gl.Disable(engo.Gl.BLEND)
}

func (l *textShader) SetCamera(c *CameraSystem) {
	if l.cameraEnabled {
		l.camera = c
	}
}
<|endoftext|>


<commit_before>package iplist

import (
	"encoding/binary"
	"io"
	"net"
	"os"

	"github.com/edsrzf/mmap-go"
)

// The packed format is an 8 byte integer of the number of ranges. Then 20
// bytes per range, consisting of 4 byte packed IP being the lower bound IP of
// the range, then 4 bytes of the upper, inclusive bound, 8 bytes for the
// offset of the description from the end of the packed ranges, and 4 bytes
// for the length of the description. After these packed ranges, are the
// concatenated descriptions.

const (
	packedRangesOffset = 8
	packedRangeLen     = 20
)

func (ipl *IPList) WritePacked(w io.Writer) (err error) {
	descOffsets := make(map[string]int64, len(ipl.ranges))
	descs := make([]string, 0, len(ipl.ranges))
	var nextOffset int64
	// This is a little monadic, no?
	write := func(b []byte, expectedLen int) {
		if err != nil {
			return
		}
		var n int
		n, err = w.Write(b)
		if err != nil {
			return
		}
		if n != expectedLen {
			panic(n)
		}
	}
	var b [8]byte
	binary.LittleEndian.PutUint64(b[:], uint64(len(ipl.ranges)))
	write(b[:], 8)
	for _, r := range ipl.ranges {
		write(r.First.To4(), 4)
		write(r.Last.To4(), 4)
		descOff, ok := descOffsets[r.Description]
		if !ok {
			descOff = nextOffset
			descOffsets[r.Description] = descOff
			descs = append(descs, r.Description)
			nextOffset += int64(len(r.Description))
		}
		binary.LittleEndian.PutUint64(b[:], uint64(descOff))
		write(b[:], 8)
		binary.LittleEndian.PutUint32(b[:], uint32(len(r.Description)))
		write(b[:4], 4)
	}
	for _, d := range descs {
		write([]byte(d), len(d))
	}
	return
}

func NewFromPacked(b []byte) PackedIPList {
	return PackedIPList(b)
}

type PackedIPList []byte

var _ Ranger = PackedIPList{}

func (pil PackedIPList) len() int {
	return int(binary.LittleEndian.Uint64(pil[:8]))
}

func (pil PackedIPList) NumRanges() int {
	return pil.len()
}

func (pil PackedIPList) getFirst(i int) net.IP {
	off := packedRangesOffset + packedRangeLen*i
	return net.IP(pil[off : off+4])
}

func (pil PackedIPList) getRange(i int) (ret Range) {
	rOff := packedRangesOffset + packedRangeLen*i
	last := pil[rOff+4 : rOff+8]
	descOff := int(binary.LittleEndian.Uint64(pil[rOff+8:]))
	descLen := int(binary.LittleEndian.Uint32(pil[rOff+16:]))
	descOff += packedRangesOffset + packedRangeLen*pil.len()
	ret = Range{
		pil.getFirst(i),
		net.IP(last),
		string(pil[descOff : descOff+descLen]),
	}
	return
}

func (pil PackedIPList) Lookup(ip net.IP) (r Range, ok bool) {
	ip4 := ip.To4()
	if ip4 == nil {
		// If the IP list was built successfully, then it only contained IPv4
		// ranges. Therefore no IPv6 ranges are blocked.
		if ip.To16() == nil {
			r = Range{
				Description: "bad IP",
			}
			ok = true
		}
		return
	}
	return lookup(pil.getFirst, pil.getRange, pil.len(), ip4)
}

func MMapPacked(filename string) (ret Ranger, err error) {
	f, err := os.Open(filename)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	if err != nil {
		return
	}
	defer f.Close()
	mm, err := mmap.Map(f, mmap.RDONLY, 0)
	if err != nil {
		return
	}
	ret = NewFromPacked(mm)
	return
}
<commit_msg>iplist: Add TODO to allow unmapping<commit_after>package iplist

import (
	"encoding/binary"
	"io"
	"net"
	"os"

	"github.com/edsrzf/mmap-go"
)

// The packed format is an 8 byte integer of the number of ranges. Then 20
// bytes per range, consisting of 4 byte packed IP being the lower bound IP of
// the range, then 4 bytes of the upper, inclusive bound, 8 bytes for the
// offset of the description from the end of the packed ranges, and 4 bytes
// for the length of the description. After these packed ranges, are the
// concatenated descriptions.

const (
	packedRangesOffset = 8
	packedRangeLen     = 20
)

func (ipl *IPList) WritePacked(w io.Writer) (err error) {
	descOffsets := make(map[string]int64, len(ipl.ranges))
	descs := make([]string, 0, len(ipl.ranges))
	var nextOffset int64
	// This is a little monadic, no?
	write := func(b []byte, expectedLen int) {
		if err != nil {
			return
		}
		var n int
		n, err = w.Write(b)
		if err != nil {
			return
		}
		if n != expectedLen {
			panic(n)
		}
	}
	var b [8]byte
	binary.LittleEndian.PutUint64(b[:], uint64(len(ipl.ranges)))
	write(b[:], 8)
	for _, r := range ipl.ranges {
		write(r.First.To4(), 4)
		write(r.Last.To4(), 4)
		descOff, ok := descOffsets[r.Description]
		if !ok {
			descOff = nextOffset
			descOffsets[r.Description] = descOff
			descs = append(descs, r.Description)
			nextOffset += int64(len(r.Description))
		}
		binary.LittleEndian.PutUint64(b[:], uint64(descOff))
		write(b[:], 8)
		binary.LittleEndian.PutUint32(b[:], uint32(len(r.Description)))
		write(b[:4], 4)
	}
	for _, d := range descs {
		write([]byte(d), len(d))
	}
	return
}

func NewFromPacked(b []byte) PackedIPList {
	return PackedIPList(b)
}

type PackedIPList []byte

var _ Ranger = PackedIPList{}

func (pil PackedIPList) len() int {
	return int(binary.LittleEndian.Uint64(pil[:8]))
}

func (pil PackedIPList) NumRanges() int {
	return pil.len()
}

func (pil PackedIPList) getFirst(i int) net.IP {
	off := packedRangesOffset + packedRangeLen*i
	return net.IP(pil[off : off+4])
}

func (pil PackedIPList) getRange(i int) (ret Range) {
	rOff := packedRangesOffset + packedRangeLen*i
	last := pil[rOff+4 : rOff+8]
	descOff := int(binary.LittleEndian.Uint64(pil[rOff+8:]))
	descLen := int(binary.LittleEndian.Uint32(pil[rOff+16:]))
	descOff += packedRangesOffset + packedRangeLen*pil.len()
	ret = Range{
		pil.getFirst(i),
		net.IP(last),
		string(pil[descOff : descOff+descLen]),
	}
	return
}

func (pil PackedIPList) Lookup(ip net.IP) (r Range, ok bool) {
	ip4 := ip.To4()
	if ip4 == nil {
		// If the IP list was built successfully, then it only contained IPv4
		// ranges. Therefore no IPv6 ranges are blocked.
		if ip.To16() == nil {
			r = Range{
				Description: "bad IP",
			}
			ok = true
		}
		return
	}
	return lookup(pil.getFirst, pil.getRange, pil.len(), ip4)
}

func MMapPacked(filename string) (ret Ranger, err error) {
	f, err := os.Open(filename)
	if os.IsNotExist(err) {
		err = nil
		return
	}
	if err != nil {
		return
	}
	defer f.Close()
	mm, err := mmap.Map(f, mmap.RDONLY, 0)
	if err != nil {
		return
	}
	// TODO: Need a destructor that unmaps this.
	ret = NewFromPacked(mm)
	return
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/permissions"
	"github.com/camd67/moebot/moebot_bot/util/db"
)

const (
	maxWrites     = 4               // Number of times to write out the time
	writeInterval = 5 * time.Second // Time between each write
)

type TimerCommand struct {
	chTimers syncChannelTimerMap
	Checker  permissions.PermissionChecker
}

func NewTimerCommand() *TimerCommand {
	tc := &TimerCommand{}
	tc.chTimers = syncChannelTimerMap{
		RWMutex: sync.RWMutex{},
		M:       make(map[string]*channelTimer),
	}
	return tc
}

func (tc *TimerCommand) Execute(pack *CommPackage) {
	channelID := pack.message.ChannelID
	if len(pack.params) > 0 && strings.EqualFold(pack.params[0], "start") {
		// Make sure the user has at least mod-level permissions before starting the timer
		if tc.Checker.HasPermission(pack.message.Author.ID, pack.member.Roles, pack.guild, db.PermMod) {
			tc.chTimers.Lock()

			// If this channel timer is currently writing out, tell it to stop
			if chTimer, ok := tc.chTimers.M[channelID]; ok {
				chTimer.Lock()
				if chTimer.isWriting {
					close(chTimer.requestCh)
				}
				chTimer.Unlock()
			}

			// Create a new timer
			tc.chTimers.M[channelID] = &channelTimer{
				time:      time.Now(),
				writes:    0,
				isWriting: false,
				requestCh: make(chan string, 10),
			}

			tc.chTimers.Unlock()
			pack.session.ChannelMessageSend(pack.message.ChannelID, "Timer started!")
		} else {
			pack.session.ChannelMessageSend(pack.message.ChannelID, pack.message.Author.Mention()+", you... you don't have permission to do that!")
		}
	} else {
		tc.chTimers.RLock()
		if chTimer, ok := tc.chTimers.M[channelID]; ok {
			chTimer.Lock()
			// Reset the number of writes
			chTimer.writes = 0

			// If the time is not writing, start it
			if !chTimer.isWriting {
				go chTimer.writeTimes(pack)
				chTimer.isWriting = true
			}
			chTimer.Unlock()
		} else {
			pack.session.ChannelMessageSend(pack.message.ChannelID, "No timer started for this channel...")
		}
		tc.chTimers.RUnlock()
	}
}

func (ct *channelTimer) writeTimes(pack *CommPackage) {
	duration := time.Since(ct.time)

	// Write the time once right away
	go func() {
		pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
		ct.Lock()
		ct.writes++
		ct.Unlock()
	}()

	// Synchronize the writes to be divisible by the interval (works well when interval is 5 so we get writes at times like 0:30, 0:35, 0:40, etc.)
	timeToSync := writeInterval - (duration % writeInterval)
	time.Sleep(timeToSync)
	duration += timeToSync

	// Write again if we spent a sufficient time syncing, otherwise just wait until the next write interval
	go func() {
		if timeToSync > time.Second {
			ct.Lock()
			pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
			ct.writes++
			ct.Unlock()
		}
	}()

	// Start writing until we reach the max number of writes or get a message to stop
	for {
		select {
		case msg, chOpen := <-ct.requestCh:
			// Break out of this loop if the channel was closed or a "stop" was issued
			if !chOpen || msg == "stop" {
				return
			}

		case <-time.After(writeInterval):
			// Increment the duration and write time to the channel
			duration += (writeInterval)
			go func() {
				pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
			}()

			// Exit once we've reached the max write count
			ct.Lock()
			ct.writes++
			if ct.writes >= maxWrites {
				ct.isWriting = false
				ct.Unlock()
				return
			}
			ct.Unlock()
		}
	}
}

// fmtDuration formats a duration into a hh:mm:ss format
func fmtDuration(dur time.Duration) string {
	remainingDur := dur.Round(time.Second)
	hours := remainingDur / time.Hour
	remainingDur -= hours * time.Hour
	minutes := remainingDur / time.Minute
	remainingDur -= minutes * time.Minute
	seconds := remainingDur / time.Second
	return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
}

func (tc *TimerCommand) GetPermLevel() db.Permission {
	return db.PermAll
}

func (tc *TimerCommand) GetCommandKeys() []string {
	return []string{"TIMER"}
}

func (tc *TimerCommand) GetCommandHelp(commPrefix string) string {
	return fmt.Sprintf("`%[1]s timer` - Checks the timer. Moderators may provide the `start` option to start (or restart) the timer.", commPrefix)
}

type syncChannelTimerMap struct {
	sync.RWMutex
	M map[string]*channelTimer
}

type channelTimer struct {
	sync.Mutex
	time      time.Time
	writes    int
	isWriting bool
	requestCh chan string
}
<commit_msg>Set isWriting to false when stopping the channelTimer to be consistent with the time.After block<commit_after>package commands

import (
	"fmt"
	"strings"
	"sync"
	"time"

	"github.com/camd67/moebot/moebot_bot/bot/permissions"
	"github.com/camd67/moebot/moebot_bot/util/db"
)

const (
	maxWrites     = 4               // Number of times to write out the time
	writeInterval = 5 * time.Second // Time between each write
)

type TimerCommand struct {
	chTimers syncChannelTimerMap
	Checker  permissions.PermissionChecker
}

func NewTimerCommand() *TimerCommand {
	tc := &TimerCommand{}
	tc.chTimers = syncChannelTimerMap{
		RWMutex: sync.RWMutex{},
		M:       make(map[string]*channelTimer),
	}
	return tc
}

func (tc *TimerCommand) Execute(pack *CommPackage) {
	channelID := pack.message.ChannelID
	if len(pack.params) > 0 && strings.EqualFold(pack.params[0], "start") {
		// Make sure the user has at least mod-level permissions before starting the timer
		if tc.Checker.HasPermission(pack.message.Author.ID, pack.member.Roles, pack.guild, db.PermMod) {
			tc.chTimers.Lock()

			// If this channel timer is currently writing out, tell it to stop
			if chTimer, ok := tc.chTimers.M[channelID]; ok {
				chTimer.Lock()
				if chTimer.isWriting {
					close(chTimer.requestCh)
				}
				chTimer.Unlock()
			}

			// Create a new timer
			tc.chTimers.M[channelID] = &channelTimer{
				time:      time.Now(),
				writes:    0,
				isWriting: false,
				requestCh: make(chan string, 10),
			}

			tc.chTimers.Unlock()
			pack.session.ChannelMessageSend(pack.message.ChannelID, "Timer started!")
		} else {
			pack.session.ChannelMessageSend(pack.message.ChannelID, pack.message.Author.Mention()+", you... you don't have permission to do that!")
		}
	} else {
		tc.chTimers.RLock()
		if chTimer, ok := tc.chTimers.M[channelID]; ok {
			chTimer.Lock()
			// Reset the number of writes
			chTimer.writes = 0

			// If the time is not writing, start it
			if !chTimer.isWriting {
				go chTimer.writeTimes(pack)
				chTimer.isWriting = true
			}
			chTimer.Unlock()
		} else {
			pack.session.ChannelMessageSend(pack.message.ChannelID, "No timer started for this channel...")
		}
		tc.chTimers.RUnlock()
	}
}

func (ct *channelTimer) writeTimes(pack *CommPackage) {
	duration := time.Since(ct.time)

	// Write the time once right away
	go func() {
		pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
		ct.Lock()
		ct.writes++
		ct.Unlock()
	}()

	// Synchronize the writes to be divisible by the interval (works well when interval is 5 so we get writes at times like 0:30, 0:35, 0:40, etc.)
	timeToSync := writeInterval - (duration % writeInterval)
	time.Sleep(timeToSync)
	duration += timeToSync

	// Write again if we spent a sufficient time syncing, otherwise just wait until the next write interval
	go func() {
		if timeToSync > time.Second {
			ct.Lock()
			pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
			ct.writes++
			ct.Unlock()
		}
	}()

	// Start writing until we reach the max number of writes or get a message to stop
	for {
		select {
		case msg, chOpen := <-ct.requestCh:
			// Break out of this loop if the channel was closed or a "stop" was issued
			if !chOpen || msg == "stop" {
				ct.Lock()
				ct.isWriting = false
				ct.Unlock()
				return
			}

		case <-time.After(writeInterval):
			// Increment the duration and write time to the channel
			duration += (writeInterval)
			go func() {
				pack.session.ChannelMessageSend(pack.message.ChannelID, fmtDuration(duration))
			}()

			// Exit once we've reached the max write count
			ct.Lock()
			ct.writes++
			if ct.writes >= maxWrites {
				ct.isWriting = false
				ct.Unlock()
				return
			}
			ct.Unlock()
		}
	}
}

// fmtDuration formats a duration into a hh:mm:ss format
func fmtDuration(dur time.Duration) string {
	remainingDur := dur.Round(time.Second)
	hours := remainingDur / time.Hour
	remainingDur -= hours * time.Hour
	minutes := remainingDur / time.Minute
	remainingDur -= minutes * time.Minute
	seconds := remainingDur / time.Second
	return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds)
}

func (tc *TimerCommand) GetPermLevel() db.Permission {
	return db.PermAll
}

func (tc *TimerCommand) GetCommandKeys() []string {
	return []string{"TIMER"}
}

func (tc *TimerCommand) GetCommandHelp(commPrefix string) string {
	return fmt.Sprintf("`%[1]s timer` - Checks the timer. Moderators may provide the `start` option to start (or restart) the timer.", commPrefix)
}

type syncChannelTimerMap struct {
	sync.RWMutex
	M map[string]*channelTimer
}

type channelTimer struct {
	sync.Mutex
	time      time.Time
	writes    int
	isWriting bool
	requestCh chan string
}
<|endoftext|>


<commit_before>package model

import (
	"database/sql"
	"reflect"
	"strings"
	"sync"
	"time"
)

// All important keys
const (
	OrderByPK               = "ngorm:order_by_primary_key"
	QueryDestination        = "ngorm:query_destination"
	QueryOption             = "ngorm:query_option"
	Query                   = "ngorm:query"
	HookAfterQuery          = "ngorm:query_after"
	HookAfterFindQuery      = "ngorm:query_after_find"
	HookBeforeCreate        = "ngorm:before_create_hook"
	HookBeforeSave          = "ngorm:before_save_hook"
	Create                  = "ngorm:create"
	HookCreateExec          = "ngorm:create_exec"
	BeforeCreate            = "ngorm:before_create"
	AfterCreate             = "ngorm:after_create"
	HookAfterCreate         = "ngorm:after_create"
	HookAfterSave           = "ngorm:after_save_hook"
	UpdateAttrs             = "ngorm:update_attrs"
	TableOptions            = "ngorm:table_options"
	HookSaveBeforeAss       = "ngorm:save_before_associations"
	HookUpdateTimestamp     = "ngorm:update_time_stamp"
	BlankColWithValue       = "ngorm:blank_columns_with_default_value"
	InsertOptions           = "ngorm:insert_option"
	UpdateColumn            = "ngorm:update_column"
	HookBeforeUpdate        = "ngorm:before_update_hook"
	HookAfterUpdate         = "ngorm:after_update_hook"
	UpdateInterface         = "ngorm:update_interface"
	BeforeUpdate            = "ngorm:before_update"
	AfterUpdate             = "ngorm:after_update"
	HookAssignUpdatingAttrs = "ngorm:assign_updating_attrs_hook"
	HookCreateSQL           = "ngorm:create_sql"
	UpdateOptions           = "ngorm:update_option"
	Update                  = "ngorm:update"
	HookUpdateSQL           = "ngorm:update_sql_hook"
	HookUpdateExec          = "ngorm:update_exec_hook"
	IgnoreProtectedAttrs    = "ngorm:ignore_protected_attrs"
)

//Model defines common fields that are used for defining SQL Tables. This is a
//helper that you can embed in your own struct definition.
//
// By embedding this, there is no need to define the supplied fields. For
// example.
//
//  type User struct {
//    Model
//    Name string
//  }
// Is the same as this
//  type User   struct {
//    ID        uint `gorm:"primary_key"`
//    CreatedAt time.Time
//    UpdatedAt time.Time
//    DeletedAt *time.Time `sql:"index"`
//    Name      string
//  }
type Model struct {
	ID        uint `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt *time.Time `sql:"index"`
}

//Struct model definition
type Struct struct {
	PrimaryFields    []*StructField
	StructFields     []*StructField
	ModelType        reflect.Type
	DefaultTableName string
}

// StructField model field's struct definition
type StructField struct {

	// DBName is the name of the field as it is seen in the database, for
	// instance a field ID can be represented in the database as id.
	DBName          string
	Name            string
	Names           []string
	IsPrimaryKey    bool
	IsNormal        bool
	IsIgnored       bool
	IsScanner       bool
	HasDefaultValue bool
	Tag             reflect.StructTag
	TagSettings     map[string]string
	Struct          reflect.StructField
	IsForeignKey    bool
	Relationship    *Relationship
}

//Clone retruns a deep copy of the StructField
func (s *StructField) Clone() *StructField {
	clone := &StructField{
		DBName:          s.DBName,
		Name:            s.Name,
		Names:           s.Names,
		IsPrimaryKey:    s.IsPrimaryKey,
		IsNormal:        s.IsNormal,
		IsIgnored:       s.IsIgnored,
		IsScanner:       s.IsScanner,
		HasDefaultValue: s.HasDefaultValue,
		Tag:             s.Tag,
		TagSettings:     map[string]string{},
		Struct:          s.Struct,
		IsForeignKey:    s.IsForeignKey,
		Relationship:    s.Relationship,
	}

	for key, value := range s.TagSettings {
		clone.TagSettings[key] = value
	}

	return clone
}

// Relationship described the relationship between models
type Relationship struct {
	Kind                         string
	PolymorphicType              string
	PolymorphicDBName            string
	PolymorphicValue             string
	ForeignFieldNames            []string
	ForeignDBNames               []string
	AssociationForeignFieldNames []string
	AssociationForeignDBNames    []string
	JoinTableHandler             *JoinTableHandler
}

//ParseTagSetting returns a map[string]string for the tags that are set.
func ParseTagSetting(tags reflect.StructTag) map[string]string {
	setting := map[string]string{}
	for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
		tags := strings.Split(str, ";")
		for _, value := range tags {
			v := strings.Split(value, ":")
			k := strings.TrimSpace(strings.ToUpper(v[0]))
			if len(v) >= 2 {
				setting[k] = strings.Join(v[1:], ":")
			} else {
				setting[k] = k
			}
		}
	}
	return setting
}

//SafeStructsMap provide safe storage and accessing of *Struct.
type SafeStructsMap struct {
	m map[reflect.Type]*Struct
	l *sync.RWMutex
}

//Set stores value with the given key.
func (s *SafeStructsMap) Set(key reflect.Type, value *Struct) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[key] = value
}

//Get retrieves the value stored with the gived key.
func (s *SafeStructsMap) Get(key reflect.Type) *Struct {
	s.l.RLock()
	defer s.l.RUnlock()
	return s.m[key]
}

//NewStructsMap returns a safe map for storing *Struct objects.
func NewStructsMap() *SafeStructsMap {
	return &SafeStructsMap{l: new(sync.RWMutex), m: make(map[reflect.Type]*Struct)}
}

//Scope is the scope level of SQL building.
type Scope struct {
	Value           interface{}
	SQL             string
	SQLVars         []interface{}
	InstanceID      string
	PrimaryKeyField *Field
	SkipLeft        bool
	SelectAttrs     *[]string
	MultiExpr       bool
	Exprs           []*Expr
	mu              sync.RWMutex
	data            map[string]interface{}
}

//NewScope return an empty scope. The scope is initialized to allow Set, and Get
//methods to work.
func NewScope() *Scope {
	return &Scope{
		data: make(map[string]interface{}),
	}
}

//Set sets a scope specific key value. This is only available in the scope.
func (s *Scope) Set(key string, value interface{}) {
	s.mu.Lock()
	s.data[key] = value
	s.mu.Unlock()
}

//Get retrieves the value with key from the scope.
func (s *Scope) Get(key string) (interface{}, bool) {
	s.mu.RLock()
	v, ok := s.data[key]
	s.mu.RUnlock()
	return v, ok
}

//Search is the search level of SQL building
type Search struct {
	WhereConditions  []map[string]interface{}
	OrConditions     []map[string]interface{}
	NotConditions    []map[string]interface{}
	HavingConditions []map[string]interface{}
	JoinConditions   []map[string]interface{}
	InitAttrs        []interface{}
	AssignAttrs      []interface{}
	Selects          map[string]interface{}
	Omits            []string
	Orders           []interface{}
	Preload          []SearchPreload
	Offset           interface{}
	Limit            interface{}
	Group            string
	TableName        string
	Raw              bool
	Unscoped         bool
	IgnoreOrderQuery bool
}

//SearchPreload is the preload search condition.
type SearchPreload struct {
	Schema     string
	Conditions []interface{}
}

//SQLCommon is the interface for SQL database interactions.
type SQLCommon interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	Begin() (*sql.Tx, error)
	Close() error
}

// Expr is SQL expression
type Expr struct {
	Q    string
	Args []interface{}
}

//JoinTableForeignKey info that point to a key to use in join table.
type JoinTableForeignKey struct {
	DBName            string
	AssociationDBName string
}

// JoinTableSource is a struct that contains model type and foreign keys
type JoinTableSource struct {
	ModelType   reflect.Type
	ForeignKeys []JoinTableForeignKey
}

// JoinTableHandler default join table handler
type JoinTableHandler struct {
	TableName   string          `sql:"-"`
	Source      JoinTableSource `sql:"-"`
	Destination JoinTableSource `sql:"-"`
}
<commit_msg>[model] Add godoc<commit_after>//Packcage model defines structures that are shared through out ngorm. They
//provide the base building blocks for ngorm abstractions.
package model

import (
	"database/sql"
	"reflect"
	"strings"
	"sync"
	"time"
)

// All important keys
const (
	OrderByPK               = "ngorm:order_by_primary_key"
	QueryDestination        = "ngorm:query_destination"
	QueryOption             = "ngorm:query_option"
	Query                   = "ngorm:query"
	HookAfterQuery          = "ngorm:query_after"
	HookAfterFindQuery      = "ngorm:query_after_find"
	HookBeforeCreate        = "ngorm:before_create_hook"
	HookBeforeSave          = "ngorm:before_save_hook"
	Create                  = "ngorm:create"
	HookCreateExec          = "ngorm:create_exec"
	BeforeCreate            = "ngorm:before_create"
	AfterCreate             = "ngorm:after_create"
	HookAfterCreate         = "ngorm:after_create"
	HookAfterSave           = "ngorm:after_save_hook"
	UpdateAttrs             = "ngorm:update_attrs"
	TableOptions            = "ngorm:table_options"
	HookSaveBeforeAss       = "ngorm:save_before_associations"
	HookUpdateTimestamp     = "ngorm:update_time_stamp"
	BlankColWithValue       = "ngorm:blank_columns_with_default_value"
	InsertOptions           = "ngorm:insert_option"
	UpdateColumn            = "ngorm:update_column"
	HookBeforeUpdate        = "ngorm:before_update_hook"
	HookAfterUpdate         = "ngorm:after_update_hook"
	UpdateInterface         = "ngorm:update_interface"
	BeforeUpdate            = "ngorm:before_update"
	AfterUpdate             = "ngorm:after_update"
	HookAssignUpdatingAttrs = "ngorm:assign_updating_attrs_hook"
	HookCreateSQL           = "ngorm:create_sql"
	UpdateOptions           = "ngorm:update_option"
	Update                  = "ngorm:update"
	HookUpdateSQL           = "ngorm:update_sql_hook"
	HookUpdateExec          = "ngorm:update_exec_hook"
	IgnoreProtectedAttrs    = "ngorm:ignore_protected_attrs"
)

//Model defines common fields that are used for defining SQL Tables. This is a
//helper that you can embed in your own struct definition.
//
// By embedding this, there is no need to define the supplied fields. For
// example.
//
//  type User struct {
//    Model
//    Name string
//  }
// Is the same as this
//  type User   struct {
//    ID        uint `gorm:"primary_key"`
//    CreatedAt time.Time
//    UpdatedAt time.Time
//    DeletedAt *time.Time `sql:"index"`
//    Name      string
//  }
type Model struct {
	ID        uint `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt *time.Time `sql:"index"`
}

//Struct model definition
type Struct struct {
	PrimaryFields    []*StructField
	StructFields     []*StructField
	ModelType        reflect.Type
	DefaultTableName string
}

// StructField model field's struct definition
type StructField struct {

	// DBName is the name of the field as it is seen in the database, for
	// instance a field ID can be represented in the database as id.
	DBName          string
	Name            string
	Names           []string
	IsPrimaryKey    bool
	IsNormal        bool
	IsIgnored       bool
	IsScanner       bool
	HasDefaultValue bool
	Tag             reflect.StructTag
	TagSettings     map[string]string
	Struct          reflect.StructField
	IsForeignKey    bool
	Relationship    *Relationship
}

//Clone retruns a deep copy of the StructField
func (s *StructField) Clone() *StructField {
	clone := &StructField{
		DBName:          s.DBName,
		Name:            s.Name,
		Names:           s.Names,
		IsPrimaryKey:    s.IsPrimaryKey,
		IsNormal:        s.IsNormal,
		IsIgnored:       s.IsIgnored,
		IsScanner:       s.IsScanner,
		HasDefaultValue: s.HasDefaultValue,
		Tag:             s.Tag,
		TagSettings:     map[string]string{},
		Struct:          s.Struct,
		IsForeignKey:    s.IsForeignKey,
		Relationship:    s.Relationship,
	}

	for key, value := range s.TagSettings {
		clone.TagSettings[key] = value
	}

	return clone
}

// Relationship described the relationship between models
type Relationship struct {
	Kind                         string
	PolymorphicType              string
	PolymorphicDBName            string
	PolymorphicValue             string
	ForeignFieldNames            []string
	ForeignDBNames               []string
	AssociationForeignFieldNames []string
	AssociationForeignDBNames    []string
	JoinTableHandler             *JoinTableHandler
}

//ParseTagSetting returns a map[string]string for the tags that are set.
func ParseTagSetting(tags reflect.StructTag) map[string]string {
	setting := map[string]string{}
	for _, str := range []string{tags.Get("sql"), tags.Get("gorm")} {
		tags := strings.Split(str, ";")
		for _, value := range tags {
			v := strings.Split(value, ":")
			k := strings.TrimSpace(strings.ToUpper(v[0]))
			if len(v) >= 2 {
				setting[k] = strings.Join(v[1:], ":")
			} else {
				setting[k] = k
			}
		}
	}
	return setting
}

//SafeStructsMap provide safe storage and accessing of *Struct.
type SafeStructsMap struct {
	m map[reflect.Type]*Struct
	l *sync.RWMutex
}

//Set stores value with the given key.
func (s *SafeStructsMap) Set(key reflect.Type, value *Struct) {
	s.l.Lock()
	defer s.l.Unlock()
	s.m[key] = value
}

//Get retrieves the value stored with the gived key.
func (s *SafeStructsMap) Get(key reflect.Type) *Struct {
	s.l.RLock()
	defer s.l.RUnlock()
	return s.m[key]
}

//NewStructsMap returns a safe map for storing *Struct objects.
func NewStructsMap() *SafeStructsMap {
	return &SafeStructsMap{l: new(sync.RWMutex), m: make(map[reflect.Type]*Struct)}
}

//Scope is the scope level of SQL building.
type Scope struct {
	Value           interface{}
	SQL             string
	SQLVars         []interface{}
	InstanceID      string
	PrimaryKeyField *Field
	SkipLeft        bool
	SelectAttrs     *[]string
	MultiExpr       bool
	Exprs           []*Expr
	mu              sync.RWMutex
	data            map[string]interface{}
}

//NewScope return an empty scope. The scope is initialized to allow Set, and Get
//methods to work.
func NewScope() *Scope {
	return &Scope{
		data: make(map[string]interface{}),
	}
}

//Set sets a scope specific key value. This is only available in the scope.
func (s *Scope) Set(key string, value interface{}) {
	s.mu.Lock()
	s.data[key] = value
	s.mu.Unlock()
}

//Get retrieves the value with key from the scope.
func (s *Scope) Get(key string) (interface{}, bool) {
	s.mu.RLock()
	v, ok := s.data[key]
	s.mu.RUnlock()
	return v, ok
}

//Search is the search level of SQL building
type Search struct {
	WhereConditions  []map[string]interface{}
	OrConditions     []map[string]interface{}
	NotConditions    []map[string]interface{}
	HavingConditions []map[string]interface{}
	JoinConditions   []map[string]interface{}
	InitAttrs        []interface{}
	AssignAttrs      []interface{}
	Selects          map[string]interface{}
	Omits            []string
	Orders           []interface{}
	Preload          []SearchPreload
	Offset           interface{}
	Limit            interface{}
	Group            string
	TableName        string
	Raw              bool
	Unscoped         bool
	IgnoreOrderQuery bool
}

//SearchPreload is the preload search condition.
type SearchPreload struct {
	Schema     string
	Conditions []interface{}
}

//SQLCommon is the interface for SQL database interactions.
type SQLCommon interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Prepare(query string) (*sql.Stmt, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	Begin() (*sql.Tx, error)
	Close() error
}

// Expr is SQL expression
type Expr struct {
	Q    string
	Args []interface{}
}

//JoinTableForeignKey info that point to a key to use in join table.
type JoinTableForeignKey struct {
	DBName            string
	AssociationDBName string
}

// JoinTableSource is a struct that contains model type and foreign keys
type JoinTableSource struct {
	ModelType   reflect.Type
	ForeignKeys []JoinTableForeignKey
}

// JoinTableHandler default join table handler
type JoinTableHandler struct {
	TableName   string          `sql:"-"`
	Source      JoinTableSource `sql:"-"`
	Destination JoinTableSource `sql:"-"`
}
<|endoftext|>


<commit_before>package main

import "github.com/dmulholland/args"

import (
    "fmt"
    "os"
    "path/filepath"
)

const version = "1.0.0-dev"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [COMMAND]

  Ironclad is a command line password manager.

Flags:
  -h, --help        Print the application's help text and exit.
  -v, --version     Print the application's version number and exit.

Commands:
  add               Add a new entry to a database.
  config            Set or print a configuration option.
  decrypt           Decrypt a file.
  delete            Delete entries from a database.
  dump              Dump a database's internal JSON data store.
  edit              Edit an existing database entry.
  encrypt           Encrypt a file.
  export            Export entries from a database.
  gen               Generate a random password.
  import            Import entries into a database.
  init              Initialize a new password database.
  list              List database entries.
  new               Add a new entry to a database.
  pass              Copy a password to the clipboard.
  purge             Purge deleted entries from a database.
  tags              List database tags.
  user              Copy a username to the clipboard.

Command Help:
  help <command>    Print the specified command's help text and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Instantiate an argument parser.
    parser := args.NewParser()
    parser.Helptext = helptext
    parser.Version = version

    // Register the 'add' command.
    addCmd := parser.NewCmd("add new", addHelp, addCallback)
    addCmd.NewString("file f")
    addCmd.NewFlag("no-editor")

    // Register the 'cache' command.
    parser.NewCmd("cache", cacheHelp, cacheCallback)

    // Register the 'config' command.
    parser.NewCmd("config", configHelp, configCallback)

    // Register the 'decrypt' command.
    decryptCmd := parser.NewCmd("decrypt", decryptHelp, decryptCallback)
    decryptCmd.NewString("out o")

    // Register the 'delete' command.
    deleteCmd := parser.NewCmd("delete", deleteHelp, deleteCallback)
    deleteCmd.NewString("file f")

    // Register the 'dump' command.
    dumpCmd := parser.NewCmd("dump", dumpHelp, dumpCallback)
    dumpCmd.NewString("file f")

    // Register the 'edit' command.
    editCmd := parser.NewCmd("edit", editHelp, editCallback)
    editCmd.NewString("file f")
    editCmd.NewFlag("title t")
    editCmd.NewFlag("url l")
    editCmd.NewFlag("username u")
    editCmd.NewFlag("password p")
    editCmd.NewFlag("notes n")
    editCmd.NewFlag("tags s")
    editCmd.NewFlag("email e")
    editCmd.NewFlag("no-editor")

    // Register the 'encrypt' command.
    encryptCmd := parser.NewCmd("encrypt", encryptHelp, encryptCallback)
    encryptCmd.NewString("out o")

    // Register the 'export' command.
    exportCmd := parser.NewCmd("export", exportHelp, exportCallback)
    exportCmd.NewString("file f")
    exportCmd.NewString("tag t")

    // Register the 'gen' command.
    genCmd := parser.NewCmd("gen", genHelp, genCallback)
    genCmd.NewString("file f")
    genCmd.NewFlag("digits d")
    genCmd.NewFlag("exclude-similar x")
    genCmd.NewFlag("lowercase l")
    genCmd.NewFlag("symbols s")
    genCmd.NewFlag("uppercase u")
    genCmd.NewFlag("readable r")
    genCmd.NewFlag("print p")

    // Register the 'import' command.
    importCmd := parser.NewCmd("import", importHelp, importCallback)
    importCmd.NewString("file f")

    // Register the 'init' command.
    parser.NewCmd("init", initHelp, initCallback)

    // Register the 'list' command.
    listCmd := parser.NewCmd("list show", listHelp, listCallback)
    listCmd.NewString("file f")
    listCmd.NewString("tag t")
    listCmd.NewFlag("verbose v")

    // Register the 'pass' command.
    passCmd := parser.NewCmd("pass", passHelp, passCallback)
    passCmd.NewString("file f")
    passCmd.NewFlag("readable r")
    passCmd.NewFlag("print p")

    // Register the 'purge' command.
    purgeCmd := parser.NewCmd("purge", purgeHelp, purgeCallback)
    purgeCmd.NewString("file f")

    // Register the 'tags' command.
    tagsCmd := parser.NewCmd("tags", tagsHelp, tagsCallback)
    tagsCmd.NewString("file f")

    // Register the 'user' command.
    userCmd := parser.NewCmd("user", userHelp, userCallback)
    userCmd.NewString("file f")
    userCmd.NewFlag("print p")

    // Parse the command line arguments. If no command is found, print the
    // help text and exit.
    parser.Parse()
    if !parser.HasCmd() {
        parser.ExitHelp()
    }
}
<commit_msg>Bump version to 1.0.0<commit_after>package main

import "github.com/dmulholland/args"

import (
    "fmt"
    "os"
    "path/filepath"
)

const version = "1.0.0"

var helptext = fmt.Sprintf(`
Usage: %s [FLAGS] [COMMAND]

  Ironclad is a command line password manager.

Flags:
  -h, --help        Print the application's help text and exit.
  -v, --version     Print the application's version number and exit.

Commands:
  add               Add a new entry to a database.
  config            Set or print a configuration option.
  decrypt           Decrypt a file.
  delete            Delete entries from a database.
  dump              Dump a database's internal JSON data store.
  edit              Edit an existing database entry.
  encrypt           Encrypt a file.
  export            Export entries from a database.
  gen               Generate a random password.
  import            Import entries into a database.
  init              Initialize a new password database.
  list              List database entries.
  new               Add a new entry to a database.
  pass              Copy a password to the clipboard.
  purge             Purge deleted entries from a database.
  tags              List database tags.
  user              Copy a username to the clipboard.

Command Help:
  help <command>    Print the specified command's help text and exit.
`, filepath.Base(os.Args[0]))

func main() {

    // Instantiate an argument parser.
    parser := args.NewParser()
    parser.Helptext = helptext
    parser.Version = version

    // Register the 'add' command.
    addCmd := parser.NewCmd("add new", addHelp, addCallback)
    addCmd.NewString("file f")
    addCmd.NewFlag("no-editor")

    // Register the 'cache' command.
    parser.NewCmd("cache", cacheHelp, cacheCallback)

    // Register the 'config' command.
    parser.NewCmd("config", configHelp, configCallback)

    // Register the 'decrypt' command.
    decryptCmd := parser.NewCmd("decrypt", decryptHelp, decryptCallback)
    decryptCmd.NewString("out o")

    // Register the 'delete' command.
    deleteCmd := parser.NewCmd("delete", deleteHelp, deleteCallback)
    deleteCmd.NewString("file f")

    // Register the 'dump' command.
    dumpCmd := parser.NewCmd("dump", dumpHelp, dumpCallback)
    dumpCmd.NewString("file f")

    // Register the 'edit' command.
    editCmd := parser.NewCmd("edit", editHelp, editCallback)
    editCmd.NewString("file f")
    editCmd.NewFlag("title t")
    editCmd.NewFlag("url l")
    editCmd.NewFlag("username u")
    editCmd.NewFlag("password p")
    editCmd.NewFlag("notes n")
    editCmd.NewFlag("tags s")
    editCmd.NewFlag("email e")
    editCmd.NewFlag("no-editor")

    // Register the 'encrypt' command.
    encryptCmd := parser.NewCmd("encrypt", encryptHelp, encryptCallback)
    encryptCmd.NewString("out o")

    // Register the 'export' command.
    exportCmd := parser.NewCmd("export", exportHelp, exportCallback)
    exportCmd.NewString("file f")
    exportCmd.NewString("tag t")

    // Register the 'gen' command.
    genCmd := parser.NewCmd("gen", genHelp, genCallback)
    genCmd.NewString("file f")
    genCmd.NewFlag("digits d")
    genCmd.NewFlag("exclude-similar x")
    genCmd.NewFlag("lowercase l")
    genCmd.NewFlag("symbols s")
    genCmd.NewFlag("uppercase u")
    genCmd.NewFlag("readable r")
    genCmd.NewFlag("print p")

    // Register the 'import' command.
    importCmd := parser.NewCmd("import", importHelp, importCallback)
    importCmd.NewString("file f")

    // Register the 'init' command.
    parser.NewCmd("init", initHelp, initCallback)

    // Register the 'list' command.
    listCmd := parser.NewCmd("list show", listHelp, listCallback)
    listCmd.NewString("file f")
    listCmd.NewString("tag t")
    listCmd.NewFlag("verbose v")

    // Register the 'pass' command.
    passCmd := parser.NewCmd("pass", passHelp, passCallback)
    passCmd.NewString("file f")
    passCmd.NewFlag("readable r")
    passCmd.NewFlag("print p")

    // Register the 'purge' command.
    purgeCmd := parser.NewCmd("purge", purgeHelp, purgeCallback)
    purgeCmd.NewString("file f")

    // Register the 'tags' command.
    tagsCmd := parser.NewCmd("tags", tagsHelp, tagsCallback)
    tagsCmd.NewString("file f")

    // Register the 'user' command.
    userCmd := parser.NewCmd("user", userHelp, userCallback)
    userCmd.NewString("file f")
    userCmd.NewFlag("print p")

    // Parse the command line arguments. If no command is found, print the
    // help text and exit.
    parser.Parse()
    if !parser.HasCmd() {
        parser.ExitHelp()
    }
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package lifecycle

import (
	"fmt"
	"io"
	"time"

	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch"
)

func init() {
	admission.RegisterPlugin("NamespaceLifecycle", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
		return NewLifecycle(client), nil
	})
}

// lifecycle is an implementation of admission.Interface.
// It enforces life-cycle constraints around a Namespace depending on its Phase
type lifecycle struct {
	*admission.Handler
	client             clientset.Interface
	store              cache.Store
	immortalNamespaces sets.String
}

func (l *lifecycle) Admit(a admission.Attributes) (err error) {
	// prevent deletion of immortal namespaces
	if a.GetOperation() == admission.Delete && a.GetKind() == api.Kind("Namespace") && l.immortalNamespaces.Has(a.GetName()) {
		return errors.NewForbidden(a.GetResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted"))
	}

	// if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do
	// if we're here, then the API server has found a route, which means that if we have a non-empty namespace
	// its a namespaced resource.
	if len(a.GetNamespace()) == 0 || a.GetKind() == api.Kind("Namespace") {
		return nil
	}

	namespaceObj, exists, err := l.store.Get(&api.Namespace{
		ObjectMeta: api.ObjectMeta{
			Name:      a.GetNamespace(),
			Namespace: "",
		},
	})
	if err != nil {
		return errors.NewInternalError(err)
	}

	// refuse to operate on non-existent namespaces
	if !exists {
		// in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
		namespaceObj, err = l.client.Core().Namespaces().Get(a.GetNamespace())
		if err != nil {
			if errors.IsNotFound(err) {
				return err
			}
			return errors.NewInternalError(err)
		}
	}

	// ensure that we're not trying to create objects in terminating namespaces
	if a.GetOperation() == admission.Create {
		namespace := namespaceObj.(*api.Namespace)
		if namespace.Status.Phase != api.NamespaceTerminating {
			return nil
		}

		// TODO: This should probably not be a 403
		return admission.NewForbidden(a, fmt.Errorf("Unable to create new content in namespace %s because it is being terminated.", a.GetNamespace()))
	}

	return nil
}

// NewLifecycle creates a new namespace lifecycle admission control handler
func NewLifecycle(c clientset.Interface) admission.Interface {
	store := cache.NewStore(cache.MetaNamespaceKeyFunc)
	reflector := cache.NewReflector(
		&cache.ListWatch{
			ListFunc: func(options api.ListOptions) (runtime.Object, error) {
				return c.Core().Namespaces().List(options)
			},
			WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
				return c.Core().Namespaces().Watch(options)
			},
		},
		&api.Namespace{},
		store,
		5*time.Minute,
	)
	reflector.Run()
	return &lifecycle{
		Handler:            admission.NewHandler(admission.Create, admission.Update, admission.Delete),
		client:             c,
		store:              store,
		immortalNamespaces: sets.NewString(api.NamespaceDefault),
	}
}
<commit_msg>Reduce namespace deletion test flakes by forcing a cache refresh when a namespace is initially deleted<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package lifecycle

import (
	"fmt"
	"io"
	"time"

	clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

	"k8s.io/kubernetes/pkg/admission"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/errors"
	"k8s.io/kubernetes/pkg/client/cache"
	"k8s.io/kubernetes/pkg/runtime"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch"
)

func init() {
	admission.RegisterPlugin("NamespaceLifecycle", func(client clientset.Interface, config io.Reader) (admission.Interface, error) {
		return NewLifecycle(client), nil
	})
}

// lifecycle is an implementation of admission.Interface.
// It enforces life-cycle constraints around a Namespace depending on its Phase
type lifecycle struct {
	*admission.Handler
	client             clientset.Interface
	store              cache.Store
	immortalNamespaces sets.String
}

func (l *lifecycle) Admit(a admission.Attributes) (err error) {
	// prevent deletion of immortal namespaces
	if a.GetOperation() == admission.Delete && a.GetKind() == api.Kind("Namespace") && l.immortalNamespaces.Has(a.GetName()) {
		return errors.NewForbidden(a.GetResource(), a.GetName(), fmt.Errorf("this namespace may not be deleted"))
	}

	// if we're here, then we've already passed authentication, so we're allowed to do what we're trying to do
	// if we're here, then the API server has found a route, which means that if we have a non-empty namespace
	// its a namespaced resource.
	if len(a.GetNamespace()) == 0 || a.GetKind() == api.Kind("Namespace") {
		// if a namespace is deleted, we want to prevent all further creates into it
		// while it is undergoing termination.  to reduce incidences where the cache
		// is slow to update, we forcefully remove the namespace from our local cache.
		// this will cause a live lookup of the namespace to get its latest state even
		// before the watch notification is received.
		if a.GetOperation() == admission.Delete {
			l.store.Delete(&api.Namespace{
				ObjectMeta: api.ObjectMeta{
					Name: a.GetName(),
				},
			})
		}
		return nil
	}

	namespaceObj, exists, err := l.store.Get(&api.Namespace{
		ObjectMeta: api.ObjectMeta{
			Name:      a.GetNamespace(),
			Namespace: "",
		},
	})
	if err != nil {
		return errors.NewInternalError(err)
	}

	// refuse to operate on non-existent namespaces
	if !exists {
		// in case of latency in our caches, make a call direct to storage to verify that it truly exists or not
		namespaceObj, err = l.client.Core().Namespaces().Get(a.GetNamespace())
		if err != nil {
			if errors.IsNotFound(err) {
				return err
			}
			return errors.NewInternalError(err)
		}
	}

	// ensure that we're not trying to create objects in terminating namespaces
	if a.GetOperation() == admission.Create {
		namespace := namespaceObj.(*api.Namespace)
		if namespace.Status.Phase != api.NamespaceTerminating {
			return nil
		}

		// TODO: This should probably not be a 403
		return admission.NewForbidden(a, fmt.Errorf("Unable to create new content in namespace %s because it is being terminated.", a.GetNamespace()))
	}

	return nil
}

// NewLifecycle creates a new namespace lifecycle admission control handler
func NewLifecycle(c clientset.Interface) admission.Interface {
	store := cache.NewStore(cache.MetaNamespaceKeyFunc)
	reflector := cache.NewReflector(
		&cache.ListWatch{
			ListFunc: func(options api.ListOptions) (runtime.Object, error) {
				return c.Core().Namespaces().List(options)
			},
			WatchFunc: func(options api.ListOptions) (watch.Interface, error) {
				return c.Core().Namespaces().Watch(options)
			},
		},
		&api.Namespace{},
		store,
		5*time.Minute,
	)
	reflector.Run()
	return &lifecycle{
		Handler:            admission.NewHandler(admission.Create, admission.Update, admission.Delete),
		client:             c,
		store:              store,
		immortalNamespaces: sets.NewString(api.NamespaceDefault),
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 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.

// +build nacl plan9 windows

package net

import "runtime"

// See main_unix_test.go for what these (don't) do.
func forceGoDNS() func() {
	switch runtime.GOOS {
	case "plan9", "windows":
		return func() {}
	default:
		return nil
	}
}

// See main_unix_test.go for what these (don't) do.
func forceCgoDNS() func() { return nil }
<commit_msg>net: fix typo<commit_after>// Copyright 2015 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.

// +build nacl plan9 windows

package net

import "runtime"

// See main_conf_test.go for what these (don't) do.
func forceGoDNS() func() {
	switch runtime.GOOS {
	case "plan9", "windows":
		return func() {}
	default:
		return nil
	}
}

// See main_conf_test.go for what these (don't) do.
func forceCgoDNS() func() { return nil }
<|endoftext|>