<commit_before>package tmi

import (
	"bytes"
	"sort"
	"strconv"
	"strings"
)

const (
	actionPrefix    = "\x01ACTION "     // Indicates the start of an ACTION(/me) message
	actionPrefixLen = len(actionPrefix) // Length of the action start, never changes so we save a const
	actionSuffix    = '\x01'            // Indicates the end of the same ^

	prefix     byte = 0x3A // ":" Prefix/trailing/emoticon-separator
	prefixUser byte = 0x21 // "!" Username
	prefixTags byte = 0x40 // "@" Tags (and hostname, but we don't care about hostnames in tmi)
	space      byte = 0x20 // " " Separator

	tagSep byte = ';' // Tags separator
	tagAss byte = '=' // Tags assignator
	emSep  byte = '-' // Emote separator

	maxLength = 510 // Maximum length is 512 - 2 for the line endings.
)

// Emote struct for storing one emote, with a single from/to position.
// Storing each emote occurance in one object allows us to properly sort the emotes
// to ease the
type Emote struct {
	ID     string `json:"id"`
	From   int    `json:"from"`
	To     int    `json:"to"`
	Source string `json:"source"` // Source is used to allow parsing and inserting more emotes, ex. from BTTV
}

//ByPos is a sorting interface for Emotes
type ByPos []*Emote

func (a ByPos) Len() int           { return len(a) }
func (a ByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPos) Less(i, j int) bool { return a[i].From < a[j].From }

// Message struct contains all the relevant data for a message
type Message struct {
	From     string            `json:"from"`
	Command  string            `json:"command"`
	Params   []string          `json:"params"`
	Trailing string            `json:"trailing"`
	Tags     map[string]string `json:"tags"`
	Emotes   []*Emote          `json:"emotes"`
	Action   bool              `json:"action, omitempty"`
}

// ParseEmotes is a short way to automatically parse and save the message's emotes, using tmi.ParseEmotes
func (m *Message) ParseEmotes() {
	if m == nil {
		return
	}
	if m.Tags != nil {
		s, ok := m.Tags["emotes"]
		if !ok {
			return
		}
		m.Emotes = ParseEmotes(s)
		sort.Sort(ByPos(m.Emotes))
	}
}

// Bytes is used to return a Message to a []byte, in case we want to send a *Message to the server
// This does not return a parsed Message to its original form, but rather a message
// in the basic form that the server expects
func (m *Message) Bytes() []byte {
	var buf bytes.Buffer

	buf.WriteString(m.Command)

	if len(m.Params) > 0 {
		buf.WriteByte(space)
		buf.WriteString(strings.Join(m.Params, string(space)))
		buf.WriteString(m.Trailing)
	}
	if buf.Len() > (maxLength) {
		buf.Truncate(maxLength)
	}
	return buf.Bytes()
}

// String returns a stringified version of the message, see Message.Bytes
func (m *Message) String() string {
	return string(m.Bytes())
}

// Channel is a simple method to get the channel, aka the first param
func (m *Message) Channel() string {
	if len(m.Params) > 0 {
		if m.Params[0][0] == '#' {
			return m.Params[0]
		}
	}
	return ""
}

// ParseMessage parses a message from a raw string into a *Message
func ParseMessage(raw string) *Message {
	raw = strings.TrimSpace(raw)
	if len(raw) < 1 {
		return nil
	}
	m := new(Message)

	// Next delimiter, before the next part we want to parse (i)
	// nextDelimiter is always relative to the current position, so actual index
	// is position + nextDelimiter
	nextDelimiter := 0
	// working position/cursor (c)
	position := 0

	//Extract tags
	if raw[0] == prefixTags {
		// If tags are indicated, but no space after (no command), return nil
		if nextDelimiter = strings.IndexByte(raw, space); nextDelimiter < 0 {
			return nil
		}
		m.Tags = ParseTags(raw[1:nextDelimiter])
		position += nextDelimiter + 1
	}

	// Extract and sipmlify prefix as "From";
	// since all twitch users have generic host/nick (user!user@user.tmi.twitch.tv)
	// We only really need a single "from", instead of breaking it up to save arbitrary data
	// Only possible use case would be if we explicitly need to know if a message
	// was sent from a user or if it was sent from server
	if raw[position] == prefix {
		// If prefix is indicated but no space after (command is missing), return nil
		if nextDelimiter = strings.IndexByte(raw[position:], space); position+nextDelimiter < 0 {
			return nil
		}

		m.From = raw[position+1 : position+nextDelimiter]

		if a := strings.IndexByte(m.From, prefixUser); a != -1 {
			m.From = m.From[0:a]
		}
		// Move position forward
		position += nextDelimiter + 1
	}

	// Find end of command
	nextDelimiter = strings.IndexByte(raw[position:], space)
	if nextDelimiter < 0 {
		// Nothing after command, return
		m.Command = raw[position:]
		return m
	}
	m.Command = raw[position : position+nextDelimiter]
	position += nextDelimiter + 1

	// Find prefix for trailing
	nextDelimiter = strings.IndexByte(raw[position:], prefix)

	var params []string
	if nextDelimiter < 0 {
		//no trailing
		params = strings.Split(raw[position:], string(space))
	} else {
		// Has trailing
		if nextDelimiter > 0 {
			// Has params
			params = strings.Split(raw[position:position+nextDelimiter-1], string(space))
		}
		m.Trailing = raw[position+nextDelimiter+1:]
	}
	if len(params) > 0 {
		m.Params = params
	}
	return cleanMessage(m)
}

// Do some default normalising and cleaning
func cleanMessage(m *Message) *Message {
	if m.Command == "PRIVMSG" {
		//Normalise ACTION(/me) on PRIVMSGs
		tLen := len(m.Trailing)
		if tLen > actionPrefixLen {
			if (m.Trailing[0:actionPrefixLen] == actionPrefix) && (m.Trailing[tLen-1] == actionSuffix) {
				m.Trailing = m.Trailing[actionPrefixLen : tLen-1]
				m.Action = true
			}
		}
	}
	return m
}

// ParseEmotes transform the emotes string from the tag and returns a slice
// containing individual *Emote instances for each emote occurance.
func ParseEmotes(emoteString string) []*Emote {
	emotes := []*Emote{}
	if emoteString == "" {
		return nil
	}
	emoteSplit := strings.Split(emoteString, "/")
	var occuranceSplit []string
	var split []string
	var id string
	for _, e := range emoteSplit {
		split = strings.Split(e, ":")
		id = split[0]
		occuranceSplit = strings.Split(split[1], ",")

		for _, o := range occuranceSplit {
			i := strings.IndexByte(o, emSep)
			from, _ := strconv.Atoi(o[:i])
			to, _ := strconv.Atoi(o[i+1:])
			emotes = append(emotes, &Emote{ID: id, From: from, To: to, Source: "twitch"})
		}
	}
	return emotes
}

// ParseTags turns the tag prefix string into a proper map[string]string
func ParseTags(s string) map[string]string {
	result := make(map[string]string)

	c, i, i2 := 0, 0, 0 // text cursor and indexes
	for {
		i = strings.IndexByte(s[c:], tagSep) // i = end of command, i2 = equal-sign
		if i > 0 {
			i2 = strings.IndexByte(s[c:c+i], tagAss)
			result[s[c:c+i2]] = s[c+i2+1 : c+i]
			c += i + 1
		} else {
			i2 = strings.IndexByte(s[c:], tagAss)
			result[s[c:c+i2]] = s[c+i2+1:]
			break
		}
	}
	return result
}
<commit_msg>Properly add trailing with Bytes/Stringify<commit_after>package tmi

import (
	"bytes"
	"sort"
	"strconv"
	"strings"
)

const (
	actionPrefix    = "\x01ACTION "     // Indicates the start of an ACTION(/me) message
	actionPrefixLen = len(actionPrefix) // Length of the action start, never changes so we save a const
	actionSuffix    = '\x01'            // Indicates the end of the same ^

	prefix     byte = 0x3A // ":" Prefix/trailing/emoticon-separator
	prefixUser byte = 0x21 // "!" Username
	prefixTags byte = 0x40 // "@" Tags (and hostname, but we don't care about hostnames in tmi)
	space      byte = 0x20 // " " Separator

	tagSep byte = ';' // Tags separator
	tagAss byte = '=' // Tags assignator
	emSep  byte = '-' // Emote separator

	maxLength = 510 // Maximum length is 512 - 2 for the line endings.
)

// Emote struct for storing one emote, with a single from/to position.
// Storing each emote occurance in one object allows us to properly sort the emotes
// to ease the
type Emote struct {
	ID     string `json:"id"`
	From   int    `json:"from"`
	To     int    `json:"to"`
	Source string `json:"source"` // Source is used to allow parsing and inserting more emotes, ex. from BTTV
}

//ByPos is a sorting interface for Emotes
type ByPos []*Emote

func (a ByPos) Len() int           { return len(a) }
func (a ByPos) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByPos) Less(i, j int) bool { return a[i].From < a[j].From }

// Message struct contains all the relevant data for a message
type Message struct {
	From     string            `json:"from"`
	Command  string            `json:"command"`
	Params   []string          `json:"params"`
	Trailing string            `json:"trailing"`
	Tags     map[string]string `json:"tags"`
	Emotes   []*Emote          `json:"emotes"`
	Action   bool              `json:"action, omitempty"`
}

// ParseEmotes is a short way to automatically parse and save the message's emotes, using tmi.ParseEmotes
func (m *Message) ParseEmotes() {
	if m == nil {
		return
	}
	if m.Tags != nil {
		s, ok := m.Tags["emotes"]
		if !ok {
			return
		}
		m.Emotes = ParseEmotes(s)
		sort.Sort(ByPos(m.Emotes))
	}
}

// Bytes is used to return a Message to a []byte, in case we want to send a *Message to the server
// This does not return a parsed Message to its original form, but rather a message
// in the basic form that the server expects
func (m *Message) Bytes() []byte {
	var buf bytes.Buffer

	buf.WriteString(m.Command)

	if len(m.Params) > 0 {
		buf.WriteByte(space)
		buf.WriteString(strings.Join(m.Params, string(space)))
    if len(m.Trailing) > 0 {
      buf.WriteByte(space)
      buf.WriteByte(prefix)
      buf.WriteString(m.Trailing)
    }
	}
	if buf.Len() > (maxLength) {
		buf.Truncate(maxLength)
	}
	return buf.Bytes()
}

// String returns a stringified version of the message, see Message.Bytes
func (m *Message) String() string {
	return string(m.Bytes())
}

// Channel is a simple method to get the channel, aka the first param
func (m *Message) Channel() string {
	if len(m.Params) > 0 {
		if m.Params[0][0] == '#' {
			return m.Params[0]
		}
	}
	return ""
}

// ParseMessage parses a message from a raw string into a *Message
func ParseMessage(raw string) *Message {
	raw = strings.TrimSpace(raw)
	if len(raw) < 1 {
		return nil
	}
	m := new(Message)

	// Next delimiter, before the next part we want to parse (i)
	// nextDelimiter is always relative to the current position, so actual index
	// is position + nextDelimiter
	nextDelimiter := 0
	// working position/cursor (c)
	position := 0

	//Extract tags
	if raw[0] == prefixTags {
		// If tags are indicated, but no space after (no command), return nil
		if nextDelimiter = strings.IndexByte(raw, space); nextDelimiter < 0 {
			return nil
		}
		m.Tags = ParseTags(raw[1:nextDelimiter])
		position += nextDelimiter + 1
	}

	// Extract and sipmlify prefix as "From";
	// since all twitch users have generic host/nick (user!user@user.tmi.twitch.tv)
	// We only really need a single "from", instead of breaking it up to save arbitrary data
	// Only possible use case would be if we explicitly need to know if a message
	// was sent from a user or if it was sent from server
	if raw[position] == prefix {
		// If prefix is indicated but no space after (command is missing), return nil
		if nextDelimiter = strings.IndexByte(raw[position:], space); position+nextDelimiter < 0 {
			return nil
		}

		m.From = raw[position+1 : position+nextDelimiter]

		if a := strings.IndexByte(m.From, prefixUser); a != -1 {
			m.From = m.From[0:a]
		}
		// Move position forward
		position += nextDelimiter + 1
	}

	// Find end of command
	nextDelimiter = strings.IndexByte(raw[position:], space)
	if nextDelimiter < 0 {
		// Nothing after command, return
		m.Command = raw[position:]
		return m
	}
	m.Command = raw[position : position+nextDelimiter]
	position += nextDelimiter + 1

	// Find prefix for trailing
	nextDelimiter = strings.IndexByte(raw[position:], prefix)

	var params []string
	if nextDelimiter < 0 {
		//no trailing
		params = strings.Split(raw[position:], string(space))
	} else {
		// Has trailing
		if nextDelimiter > 0 {
			// Has params
			params = strings.Split(raw[position:position+nextDelimiter-1], string(space))
		}
		m.Trailing = raw[position+nextDelimiter+1:]
	}
	if len(params) > 0 {
		m.Params = params
	}
	return cleanMessage(m)
}

// Do some default normalising and cleaning
func cleanMessage(m *Message) *Message {
	if m.Command == "PRIVMSG" {
		//Normalise ACTION(/me) on PRIVMSGs
		tLen := len(m.Trailing)
		if tLen > actionPrefixLen {
			if (m.Trailing[0:actionPrefixLen] == actionPrefix) && (m.Trailing[tLen-1] == actionSuffix) {
				m.Trailing = m.Trailing[actionPrefixLen : tLen-1]
				m.Action = true
			}
		}
	}
	return m
}

// ParseEmotes transform the emotes string from the tag and returns a slice
// containing individual *Emote instances for each emote occurance.
func ParseEmotes(emoteString string) []*Emote {
	emotes := []*Emote{}
	if emoteString == "" {
		return nil
	}
	emoteSplit := strings.Split(emoteString, "/")
	var occuranceSplit []string
	var split []string
	var id string
	for _, e := range emoteSplit {
		split = strings.Split(e, ":")
		id = split[0]
		occuranceSplit = strings.Split(split[1], ",")

		for _, o := range occuranceSplit {
			i := strings.IndexByte(o, emSep)
			from, _ := strconv.Atoi(o[:i])
			to, _ := strconv.Atoi(o[i+1:])
			emotes = append(emotes, &Emote{ID: id, From: from, To: to, Source: "twitch"})
		}
	}
	return emotes
}

// ParseTags turns the tag prefix string into a proper map[string]string
func ParseTags(s string) map[string]string {
	result := make(map[string]string)

	c, i, i2 := 0, 0, 0 // text cursor and indexes
	for {
		i = strings.IndexByte(s[c:], tagSep) // i = end of command, i2 = equal-sign
		if i > 0 {
			i2 = strings.IndexByte(s[c:c+i], tagAss)
			result[s[c:c+i2]] = s[c+i2+1 : c+i]
			c += i + 1
		} else {
			i2 = strings.IndexByte(s[c:], tagAss)
			result[s[c:c+i2]] = s[c+i2+1:]
			break
		}
	}
	return result
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 The Syncthing Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

//go:generate -command genxdr go run ../../Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go
//go:generate genxdr -o message_xdr.go message.go

package protocol

import "fmt"

type IndexMessage struct {
	Folder string // max:64
	Files  []FileInfo
}

type FileInfo struct {
	Name         string // max:8192
	Flags        uint32
	Modified     int64
	Version      uint64
	LocalVersion uint64
	Blocks       []BlockInfo
}

func (f FileInfo) String() string {
	return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%d, Size:%d, Blocks:%v}",
		f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.Blocks)
}

func (f FileInfo) Size() (bytes int64) {
	if f.IsDeleted() || f.IsDirectory() {
		return 128
	}
	for _, b := range f.Blocks {
		bytes += int64(b.Size)
	}
	return
}

func (f FileInfo) IsDeleted() bool {
	return f.Flags&FlagDeleted != 0
}

func (f FileInfo) IsInvalid() bool {
	return f.Flags&FlagInvalid != 0
}

func (f FileInfo) IsDirectory() bool {
	return f.Flags&FlagDirectory != 0
}

func (f FileInfo) IsSymlink() bool {
	return f.Flags&FlagSymlink != 0
}

func (f FileInfo) HasPermissionBits() bool {
	return f.Flags&FlagNoPermBits == 0
}

// Used for unmarshalling a FileInfo structure but skipping the actual block list
type FileInfoTruncated struct {
	Name         string // max:8192
	Flags        uint32
	Modified     int64
	Version      uint64
	LocalVersion uint64
	NumBlocks    uint32
}

func (f FileInfoTruncated) String() string {
	return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%d, Size:%d, NumBlocks:%d}",
		f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.NumBlocks)
}

func BlocksToSize(num uint32) int64 {
	if num < 2 {
		return BlockSize / 2
	}
	return int64(num-1)*BlockSize + BlockSize/2
}

// Returns a statistical guess on the size, not the exact figure
func (f FileInfoTruncated) Size() int64 {
	if f.IsDeleted() || f.IsDirectory() {
		return 128
	}
	return BlocksToSize(f.NumBlocks)
}

func (f FileInfoTruncated) IsDeleted() bool {
	return f.Flags&FlagDeleted != 0
}

func (f FileInfoTruncated) IsInvalid() bool {
	return f.Flags&FlagInvalid != 0
}

func (f FileInfoTruncated) IsDirectory() bool {
	return f.Flags&FlagDirectory != 0
}

func (f FileInfoTruncated) IsSymlink() bool {
	return f.Flags&FlagSymlink != 0
}

func (f FileInfoTruncated) HasPermissionBits() bool {
	return f.Flags&FlagNoPermBits == 0
}

type FileIntf interface {
	Size() int64
	IsDeleted() bool
	IsInvalid() bool
	IsDirectory() bool
	IsSymlink() bool
	HasPermissionBits() bool
}

type BlockInfo struct {
	Offset int64 // noencode (cache only)
	Size   uint32
	Hash   []byte // max:64
}

func (b BlockInfo) String() string {
	return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash)
}

type RequestMessage struct {
	Folder string // max:64
	Name   string // max:8192
	Offset uint64
	Size   uint32
}

type ResponseMessage struct {
	Data []byte
}

type ClusterConfigMessage struct {
	ClientName    string   // max:64
	ClientVersion string   // max:64
	Folders       []Folder // max:64
	Options       []Option // max:64
}

func (o *ClusterConfigMessage) GetOption(key string) string {
	for _, option := range o.Options {
		if option.Key == key {
			return option.Value
		}
	}
	return ""
}

type Folder struct {
	ID      string // max:64
	Devices []Device
}

type Device struct {
	ID              []byte // max:32
	Flags           uint32
	MaxLocalVersion uint64
}

type Option struct {
	Key   string // max:64
	Value string // max:1024
}

type CloseMessage struct {
	Reason string // max:1024
}

type EmptyMessage struct{}
<commit_msg>Add job queue (fixes #629)<commit_after>// Copyright (C) 2014 The Syncthing Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.

//go:generate -command genxdr go run ../../Godeps/_workspace/src/github.com/calmh/xdr/cmd/genxdr/main.go
//go:generate genxdr -o message_xdr.go message.go

package protocol

import "fmt"

type IndexMessage struct {
	Folder string // max:64
	Files  []FileInfo
}

type FileInfo struct {
	Name         string // max:8192
	Flags        uint32
	Modified     int64
	Version      uint64
	LocalVersion uint64
	Blocks       []BlockInfo
}

func (f FileInfo) String() string {
	return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%d, Size:%d, Blocks:%v}",
		f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.Blocks)
}

func (f FileInfo) Size() (bytes int64) {
	if f.IsDeleted() || f.IsDirectory() {
		return 128
	}
	for _, b := range f.Blocks {
		bytes += int64(b.Size)
	}
	return
}

func (f FileInfo) IsDeleted() bool {
	return f.Flags&FlagDeleted != 0
}

func (f FileInfo) IsInvalid() bool {
	return f.Flags&FlagInvalid != 0
}

func (f FileInfo) IsDirectory() bool {
	return f.Flags&FlagDirectory != 0
}

func (f FileInfo) IsSymlink() bool {
	return f.Flags&FlagSymlink != 0
}

func (f FileInfo) HasPermissionBits() bool {
	return f.Flags&FlagNoPermBits == 0
}

func (f FileInfo) ToTruncated() FileInfoTruncated {
	return FileInfoTruncated{
		Name:         f.Name,
		Flags:        f.Flags,
		Modified:     f.Modified,
		Version:      f.Version,
		LocalVersion: f.LocalVersion,
		NumBlocks:    uint32(len(f.Blocks)),
	}
}

// Used for unmarshalling a FileInfo structure but skipping the actual block list
type FileInfoTruncated struct {
	Name         string // max:8192
	Flags        uint32
	Modified     int64
	Version      uint64
	LocalVersion uint64
	NumBlocks    uint32
}

func (f FileInfoTruncated) String() string {
	return fmt.Sprintf("File{Name:%q, Flags:0%o, Modified:%d, Version:%d, Size:%d, NumBlocks:%d}",
		f.Name, f.Flags, f.Modified, f.Version, f.Size(), f.NumBlocks)
}

func BlocksToSize(num uint32) int64 {
	if num < 2 {
		return BlockSize / 2
	}
	return int64(num-1)*BlockSize + BlockSize/2
}

// Returns a statistical guess on the size, not the exact figure
func (f FileInfoTruncated) Size() int64 {
	if f.IsDeleted() || f.IsDirectory() {
		return 128
	}
	return BlocksToSize(f.NumBlocks)
}

func (f FileInfoTruncated) IsDeleted() bool {
	return f.Flags&FlagDeleted != 0
}

func (f FileInfoTruncated) IsInvalid() bool {
	return f.Flags&FlagInvalid != 0
}

func (f FileInfoTruncated) IsDirectory() bool {
	return f.Flags&FlagDirectory != 0
}

func (f FileInfoTruncated) IsSymlink() bool {
	return f.Flags&FlagSymlink != 0
}

func (f FileInfoTruncated) HasPermissionBits() bool {
	return f.Flags&FlagNoPermBits == 0
}

type FileIntf interface {
	Size() int64
	IsDeleted() bool
	IsInvalid() bool
	IsDirectory() bool
	IsSymlink() bool
	HasPermissionBits() bool
}

type BlockInfo struct {
	Offset int64 // noencode (cache only)
	Size   uint32
	Hash   []byte // max:64
}

func (b BlockInfo) String() string {
	return fmt.Sprintf("Block{%d/%d/%x}", b.Offset, b.Size, b.Hash)
}

type RequestMessage struct {
	Folder string // max:64
	Name   string // max:8192
	Offset uint64
	Size   uint32
}

type ResponseMessage struct {
	Data []byte
}

type ClusterConfigMessage struct {
	ClientName    string   // max:64
	ClientVersion string   // max:64
	Folders       []Folder // max:64
	Options       []Option // max:64
}

func (o *ClusterConfigMessage) GetOption(key string) string {
	for _, option := range o.Options {
		if option.Key == key {
			return option.Value
		}
	}
	return ""
}

type Folder struct {
	ID      string // max:64
	Devices []Device
}

type Device struct {
	ID              []byte // max:32
	Flags           uint32
	MaxLocalVersion uint64
}

type Option struct {
	Key   string // max:64
	Value string // max:1024
}

type CloseMessage struct {
	Reason string // max:1024
}

type EmptyMessage struct{}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to the Message struct

package discordgo

import (
	"io"
	"regexp"
	"strings"
)

// MessageType is the type of Message
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault MessageType = iota
	MessageTypeRecipientAdd
	MessageTypeRecipientRemove
	MessageTypeCall
	MessageTypeChannelNameChange
	MessageTypeChannelIconChange
	MessageTypeChannelPinnedMessage
	MessageTypeGuildMemberJoin
)

// A Message stores all data related to a specific Discord message.
type Message struct {
	// The ID of the message.
	ID string `json:"id"`

	// The ID of the channel in which the message was sent.
	ChannelID string `json:"channel_id"`

	// The ID of the guild in which the message was sent.
	GuildID string `json:"guild_id,omitempty"`

	// The content of the message.
	Content string `json:"content"`

	// The time at which the messsage was sent.
	// CAUTION: this field may be removed in a
	// future API version; it is safer to calculate
	// the creation time via the ID.
	Timestamp Timestamp `json:"timestamp"`

	// The time at which the last edit of the message
	// occurred, if it has been edited.
	EditedTimestamp Timestamp `json:"edited_timestamp"`

	// The roles mentioned in the message.
	MentionRoles []string `json:"mention_roles"`

	// Whether the message is text-to-speech.
	Tts bool `json:"tts"`

	// Whether the message mentions everyone.
	MentionEveryone bool `json:"mention_everyone"`

	// The author of the message. This is not guaranteed to be a
	// valid user (webhook-sent messages do not possess a full author).
	Author *User `json:"author"`

	// A list of attachments present in the message.
	Attachments []*MessageAttachment `json:"attachments"`

	// A list of embeds present in the message. Multiple
	// embeds can currently only be sent by webhooks.
	Embeds []*MessageEmbed `json:"embeds"`

	// A list of users mentioned in the message.
	Mentions []*User `json:"mentions"`

	// A list of reactions to the message.
	Reactions []*MessageReactions `json:"reactions"`

	// The type of the message.
	Type MessageType `json:"type"`
}

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content string        `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`
	Tts     bool          `json:"tts"`
	Files   []*File       `json:"-"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content *string       `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`

	ID      string
	Channel string
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID string, messageID string) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embed = embed
	return m
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	ProxyURL string `json:"proxy_url"`
	Filename string `json:"filename"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	Size     int    `json:"size"`
}

// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        string                 `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+user.Username,
		).Replace(content)
	}
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1)
	}

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		channel, err := s.State.Channel(mention[2 : len(mention)-1])
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}
<commit_msg>Add WebhookID member to message property (#607)<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to the Message struct

package discordgo

import (
	"io"
	"regexp"
	"strings"
)

// MessageType is the type of Message
type MessageType int

// Block contains the valid known MessageType values
const (
	MessageTypeDefault MessageType = iota
	MessageTypeRecipientAdd
	MessageTypeRecipientRemove
	MessageTypeCall
	MessageTypeChannelNameChange
	MessageTypeChannelIconChange
	MessageTypeChannelPinnedMessage
	MessageTypeGuildMemberJoin
)

// A Message stores all data related to a specific Discord message.
type Message struct {
	// The ID of the message.
	ID string `json:"id"`

	// The ID of the channel in which the message was sent.
	ChannelID string `json:"channel_id"`

	// The ID of the guild in which the message was sent.
	GuildID string `json:"guild_id,omitempty"`

	// The content of the message.
	Content string `json:"content"`

	// The time at which the messsage was sent.
	// CAUTION: this field may be removed in a
	// future API version; it is safer to calculate
	// the creation time via the ID.
	Timestamp Timestamp `json:"timestamp"`

	// The time at which the last edit of the message
	// occurred, if it has been edited.
	EditedTimestamp Timestamp `json:"edited_timestamp"`

	// The roles mentioned in the message.
	MentionRoles []string `json:"mention_roles"`

	// Whether the message is text-to-speech.
	Tts bool `json:"tts"`

	// Whether the message mentions everyone.
	MentionEveryone bool `json:"mention_everyone"`

	// The author of the message. This is not guaranteed to be a
	// valid user (webhook-sent messages do not possess a full author).
	Author *User `json:"author"`

	// A list of attachments present in the message.
	Attachments []*MessageAttachment `json:"attachments"`

	// A list of embeds present in the message. Multiple
	// embeds can currently only be sent by webhooks.
	Embeds []*MessageEmbed `json:"embeds"`

	// A list of users mentioned in the message.
	Mentions []*User `json:"mentions"`

	// A list of reactions to the message.
	Reactions []*MessageReactions `json:"reactions"`

	// The type of the message.
	Type MessageType `json:"type"`

	// The webhook ID of the message, if it was generated by a webhook
	WebhookID string `json:"webhook_id"`
}

// File stores info about files you e.g. send in messages.
type File struct {
	Name        string
	ContentType string
	Reader      io.Reader
}

// MessageSend stores all parameters you can send with ChannelMessageSendComplex.
type MessageSend struct {
	Content string        `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`
	Tts     bool          `json:"tts"`
	Files   []*File       `json:"-"`

	// TODO: Remove this when compatibility is not required.
	File *File `json:"-"`
}

// MessageEdit is used to chain parameters via ChannelMessageEditComplex, which
// is also where you should get the instance from.
type MessageEdit struct {
	Content *string       `json:"content,omitempty"`
	Embed   *MessageEmbed `json:"embed,omitempty"`

	ID      string
	Channel string
}

// NewMessageEdit returns a MessageEdit struct, initialized
// with the Channel and ID.
func NewMessageEdit(channelID string, messageID string) *MessageEdit {
	return &MessageEdit{
		Channel: channelID,
		ID:      messageID,
	}
}

// SetContent is the same as setting the variable Content,
// except it doesn't take a pointer.
func (m *MessageEdit) SetContent(str string) *MessageEdit {
	m.Content = &str
	return m
}

// SetEmbed is a convenience function for setting the embed,
// so you can chain commands.
func (m *MessageEdit) SetEmbed(embed *MessageEmbed) *MessageEdit {
	m.Embed = embed
	return m
}

// A MessageAttachment stores data for message attachments.
type MessageAttachment struct {
	ID       string `json:"id"`
	URL      string `json:"url"`
	ProxyURL string `json:"proxy_url"`
	Filename string `json:"filename"`
	Width    int    `json:"width"`
	Height   int    `json:"height"`
	Size     int    `json:"size"`
}

// MessageEmbedFooter is a part of a MessageEmbed struct.
type MessageEmbedFooter struct {
	Text         string `json:"text,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedImage is a part of a MessageEmbed struct.
type MessageEmbedImage struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedThumbnail is a part of a MessageEmbed struct.
type MessageEmbedThumbnail struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedVideo is a part of a MessageEmbed struct.
type MessageEmbedVideo struct {
	URL      string `json:"url,omitempty"`
	ProxyURL string `json:"proxy_url,omitempty"`
	Width    int    `json:"width,omitempty"`
	Height   int    `json:"height,omitempty"`
}

// MessageEmbedProvider is a part of a MessageEmbed struct.
type MessageEmbedProvider struct {
	URL  string `json:"url,omitempty"`
	Name string `json:"name,omitempty"`
}

// MessageEmbedAuthor is a part of a MessageEmbed struct.
type MessageEmbedAuthor struct {
	URL          string `json:"url,omitempty"`
	Name         string `json:"name,omitempty"`
	IconURL      string `json:"icon_url,omitempty"`
	ProxyIconURL string `json:"proxy_icon_url,omitempty"`
}

// MessageEmbedField is a part of a MessageEmbed struct.
type MessageEmbedField struct {
	Name   string `json:"name,omitempty"`
	Value  string `json:"value,omitempty"`
	Inline bool   `json:"inline,omitempty"`
}

// An MessageEmbed stores data for message embeds.
type MessageEmbed struct {
	URL         string                 `json:"url,omitempty"`
	Type        string                 `json:"type,omitempty"`
	Title       string                 `json:"title,omitempty"`
	Description string                 `json:"description,omitempty"`
	Timestamp   string                 `json:"timestamp,omitempty"`
	Color       int                    `json:"color,omitempty"`
	Footer      *MessageEmbedFooter    `json:"footer,omitempty"`
	Image       *MessageEmbedImage     `json:"image,omitempty"`
	Thumbnail   *MessageEmbedThumbnail `json:"thumbnail,omitempty"`
	Video       *MessageEmbedVideo     `json:"video,omitempty"`
	Provider    *MessageEmbedProvider  `json:"provider,omitempty"`
	Author      *MessageEmbedAuthor    `json:"author,omitempty"`
	Fields      []*MessageEmbedField   `json:"fields,omitempty"`
}

// MessageReactions holds a reactions object for a message.
type MessageReactions struct {
	Count int    `json:"count"`
	Me    bool   `json:"me"`
	Emoji *Emoji `json:"emoji"`
}

// ContentWithMentionsReplaced will replace all @<id> mentions with the
// username of the mention.
func (m *Message) ContentWithMentionsReplaced() (content string) {
	content = m.Content

	for _, user := range m.Mentions {
		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+user.Username,
		).Replace(content)
	}
	return
}

var patternChannels = regexp.MustCompile("<#[^>]*>")

// ContentWithMoreMentionsReplaced will replace all @<id> mentions with the
// username of the mention, but also role IDs and more.
func (m *Message) ContentWithMoreMentionsReplaced(s *Session) (content string, err error) {
	content = m.Content

	if !s.StateEnabled {
		content = m.ContentWithMentionsReplaced()
		return
	}

	channel, err := s.State.Channel(m.ChannelID)
	if err != nil {
		content = m.ContentWithMentionsReplaced()
		return
	}

	for _, user := range m.Mentions {
		nick := user.Username

		member, err := s.State.Member(channel.GuildID, user.ID)
		if err == nil && member.Nick != "" {
			nick = member.Nick
		}

		content = strings.NewReplacer(
			"<@"+user.ID+">", "@"+user.Username,
			"<@!"+user.ID+">", "@"+nick,
		).Replace(content)
	}
	for _, roleID := range m.MentionRoles {
		role, err := s.State.Role(channel.GuildID, roleID)
		if err != nil || !role.Mentionable {
			continue
		}

		content = strings.Replace(content, "<@&"+role.ID+">", "@"+role.Name, -1)
	}

	content = patternChannels.ReplaceAllStringFunc(content, func(mention string) string {
		channel, err := s.State.Channel(mention[2 : len(mention)-1])
		if err != nil || channel.Type == ChannelTypeGuildVoice {
			return mention
		}

		return "#" + channel.Name
	})
	return
}
<|endoftext|>


<commit_before>package tusd

import (
	"sync/atomic"
)

// Metrics provides numbers about the usage of the tusd handler. Since these may
// be accessed from multiple goroutines, it is necessary to read and modify them
// atomically using the functions exposed in the sync/atomic package, such as
// atomic.LoadUint64. In addition the maps must not be modified to prevent data
// races.
type Metrics struct {
	// RequestTotal counts the number of incoming requests per method
	RequestsTotal map[string]*uint64
	// ErrorsTotal counts the number of returned errors by their message
	ErrorsTotal       map[string]*uint64
	BytesReceived     *uint64
	UploadsFinished   *uint64
	UploadsCreated    *uint64
	UploadsTerminated *uint64
}

// incRequestsTotal increases the counter for this request method atomically by
// one. The method must be one of GET, HEAD, POST, PATCH, DELETE.
func (m Metrics) incRequestsTotal(method string) {
	atomic.AddUint64(m.RequestsTotal[method], 1)
}

// incErrorsTotal increases the counter for this error atomically by one.
func (m Metrics) incErrorsTotal(err error) {
	msg := err.Error()
	if _, ok := ErrStatusCodes[err]; !ok {
		msg = "system error"
	}

	atomic.AddUint64(m.ErrorsTotal[msg], 1)
}

// incBytesReceived increases the number of received bytes atomically be the
// specified number.
func (m Metrics) incBytesReceived(delta uint64) {
	atomic.AddUint64(m.BytesReceived, delta)
}

// incUploadsFinished increases the counter for finished uploads atomically by one.
func (m Metrics) incUploadsFinished() {
	atomic.AddUint64(m.UploadsFinished, 1)
}

// incUploadsCreated increases the counter for completed uploads atomically by one.
func (m Metrics) incUploadsCreated() {
	atomic.AddUint64(m.UploadsCreated, 1)
}

// incUploadsTerminated increases the counter for completed uploads atomically by one.
func (m Metrics) incUploadsTerminated() {
	atomic.AddUint64(m.UploadsTerminated, 1)
}

func newMetrics() Metrics {
	return Metrics{
		RequestsTotal: map[string]*uint64{
			"GET":    new(uint64),
			"HEAD":   new(uint64),
			"POST":   new(uint64),
			"PATCH":  new(uint64),
			"DELETE": new(uint64),
		},
		ErrorsTotal:       newErrorsTotalMap(),
		BytesReceived:     new(uint64),
		UploadsFinished:   new(uint64),
		UploadsCreated:    new(uint64),
		UploadsTerminated: new(uint64),
	}
}

func newErrorsTotalMap() map[string]*uint64 {
	m := make(map[string]*uint64, len(ErrStatusCodes)+1)

	for err, _ := range ErrStatusCodes {
		m[err.Error()] = new(uint64)
	}

	m["system error"] = new(uint64)

	return m
}
<commit_msg>Allow OPTIONS and other methods in metrics<commit_after>package tusd

import (
	"sync/atomic"
)

// Metrics provides numbers about the usage of the tusd handler. Since these may
// be accessed from multiple goroutines, it is necessary to read and modify them
// atomically using the functions exposed in the sync/atomic package, such as
// atomic.LoadUint64. In addition the maps must not be modified to prevent data
// races.
type Metrics struct {
	// RequestTotal counts the number of incoming requests per method
	RequestsTotal map[string]*uint64
	// ErrorsTotal counts the number of returned errors by their message
	ErrorsTotal       map[string]*uint64
	BytesReceived     *uint64
	UploadsFinished   *uint64
	UploadsCreated    *uint64
	UploadsTerminated *uint64
}

// incRequestsTotal increases the counter for this request method atomically by
// one. The method must be one of GET, HEAD, POST, PATCH, DELETE.
func (m Metrics) incRequestsTotal(method string) {
	if ptr, ok := m.RequestsTotal[method]; ok {
		atomic.AddUint64(ptr, 1)
	}
}

// incErrorsTotal increases the counter for this error atomically by one.
func (m Metrics) incErrorsTotal(err error) {
	msg := err.Error()
	if _, ok := ErrStatusCodes[err]; !ok {
		msg = "system error"
	}

	atomic.AddUint64(m.ErrorsTotal[msg], 1)
}

// incBytesReceived increases the number of received bytes atomically be the
// specified number.
func (m Metrics) incBytesReceived(delta uint64) {
	atomic.AddUint64(m.BytesReceived, delta)
}

// incUploadsFinished increases the counter for finished uploads atomically by one.
func (m Metrics) incUploadsFinished() {
	atomic.AddUint64(m.UploadsFinished, 1)
}

// incUploadsCreated increases the counter for completed uploads atomically by one.
func (m Metrics) incUploadsCreated() {
	atomic.AddUint64(m.UploadsCreated, 1)
}

// incUploadsTerminated increases the counter for completed uploads atomically by one.
func (m Metrics) incUploadsTerminated() {
	atomic.AddUint64(m.UploadsTerminated, 1)
}

func newMetrics() Metrics {
	return Metrics{
		RequestsTotal: map[string]*uint64{
			"GET":     new(uint64),
			"HEAD":    new(uint64),
			"POST":    new(uint64),
			"PATCH":   new(uint64),
			"DELETE":  new(uint64),
			"OPTIONS": new(uint64),
		},
		ErrorsTotal:       newErrorsTotalMap(),
		BytesReceived:     new(uint64),
		UploadsFinished:   new(uint64),
		UploadsCreated:    new(uint64),
		UploadsTerminated: new(uint64),
	}
}

func newErrorsTotalMap() map[string]*uint64 {
	m := make(map[string]*uint64, len(ErrStatusCodes)+1)

	for err, _ := range ErrStatusCodes {
		m[err.Error()] = new(uint64)
	}

	m["system error"] = new(uint64)

	return m
}
<|endoftext|>


<commit_before>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016-2018
	All Rights Reserved
	Documentation http://djthorpe.github.io/gopi/
	For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"time"
)

/////////////////////////////////////////////////////////////////////
// TYPES

type Metric struct {
	Rate  MetricRate
	Type  MetricType
	Name  string
	Mean  float64 // Mean value per hour (or whatever rate)
	Total uint    // Total over the past hour (or whatever rate)
}

type (
	MetricRate uint
	MetricType uint
)

/////////////////////////////////////////////////////////////////////
// INTERFACE

// Metrics returns various metrics for host and
// custom metrics
type Metrics interface {
	Driver

	// Uptimes for host and for application
	UptimeHost() time.Duration
	UptimeApp() time.Duration

	// Load Average (1, 5 and 15 minutes)
	LoadAverage() (float64, float64, float64)

	// Return metric channel, which when you send a value on
	// it will store the metric
	NewMetric(MetricType, MetricRate, string) (chan<- uint, error)

	// Return Metric for channel
	Metric(chan<- uint) *Metric

	// Return all metrics of a particular type, or METRIC_TYPE_NONE
	// for all metrics
	Metrics(MetricType) []*Metric
}

/////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	METRIC_RATE_NONE MetricRate = iota
	METRIC_RATE_SECOND
	METRIC_RATE_MINUTE
	METRIC_RATE_HOUR
)

const (
	METRIC_TYPE_NONE    MetricType = iota
	METRIC_TYPE_PURE               // Pure number
	METRIC_TYPE_CELCIUS            // Temperature
)

/////////////////////////////////////////////////////////////////////
// STRINGIFY

func (v MetricRate) String() string {
	switch v {
	case METRIC_RATE_SECOND:
		return "METRIC_RATE_SECOND"
	case METRIC_RATE_MINUTE:
		return "METRIC_RATE_MINUTE"
	case METRIC_RATE_HOUR:
		return "METRIC_RATE_HOUR"
	default:
		return "[?? Invalid MetricRate value]"
	}
}
<commit_msg>Updated metrics<commit_after>/*
	Go Language Raspberry Pi Interface
	(c) Copyright David Thorpe 2016-2018
	All Rights Reserved
	Documentation http://djthorpe.github.io/gopi/
	For Licensing and Usage information, please see LICENSE.md
*/

package gopi

import (
	"time"
)

/////////////////////////////////////////////////////////////////////
// TYPES

type Metric struct {
	Rate  MetricRate
	Type  MetricType
	Name  string
	Value uint    // Last value
	Mean  float64 // Mean value per hour (or whatever rate)
	Total uint    // Total over the past hour (or whatever rate)
}

type (
	MetricRate uint
	MetricType uint
)

/////////////////////////////////////////////////////////////////////
// INTERFACE

// Metrics returns various metrics for host and
// custom metrics
type Metrics interface {
	Driver

	// Uptimes for host and for application
	UptimeHost() time.Duration
	UptimeApp() time.Duration

	// Load Average (1, 5 and 15 minutes)
	LoadAverage() (float64, float64, float64)

	// Return metric channel, which when you send a value on
	// it will store the metric
	NewMetricUint(MetricType, MetricRate, string) (chan<- uint, error)

	// Return all metrics of a particular type, or METRIC_TYPE_NONE
	// for all metrics
	Metrics(MetricType) []*Metric
}

/////////////////////////////////////////////////////////////////////
// CONSTANTS

const (
	METRIC_RATE_NONE MetricRate = iota
	METRIC_RATE_SECOND
	METRIC_RATE_MINUTE
	METRIC_RATE_HOUR
)

const (
	METRIC_TYPE_NONE    MetricType = iota
	METRIC_TYPE_PURE               // Pure number
	METRIC_TYPE_CELCIUS            // Temperature
)

/////////////////////////////////////////////////////////////////////
// STRINGIFY

func (v MetricRate) String() string {
	switch v {
	case METRIC_RATE_SECOND:
		return "METRIC_RATE_SECOND"
	case METRIC_RATE_MINUTE:
		return "METRIC_RATE_MINUTE"
	case METRIC_RATE_HOUR:
		return "METRIC_RATE_HOUR"
	default:
		return "[?? Invalid MetricRate value]"
	}
}
<|endoftext|>


<commit_before>package hrd

import (
	"fmt"
	"time"

	"appengine/datastore"
)

// Key represents the datastore key for an entity.
// It also contains meta data about said entity.
type Key struct {
	*datastore.Key

	// source describes where the entity was read from.
	source string

	// version is the entity's version.
	version int64

	// synced is the last time the entity was read/written.
	synced time.Time

	// opts are the options to use for reading/writing the entity.
	opts *operationOpts

	// err contains an error if the entity could not be loaded/saved.
	err *error
}

type keyBatch struct {
	keys   []*Key
	lo, hi int
}

// newKey creates a Key from a datastore.Key.
func newKey(k *datastore.Key) *Key {
	return &Key{Key: k}
}

// newKeys creates a sequence of Key from a sequence of datastore.Key.
func newKeys(keys []*datastore.Key) []*Key {
	ret := make([]*Key, len(keys))
	for i, k := range keys {
		ret[i] = newKey(k)
	}
	return ret
}

// Exists is whether the entity with this key exists in the datastore.
func (key *Key) Exists() bool {
	return !key.synced.IsZero()
}

// IDString returns the ID of this Key as a string.
func (key *Key) IDString() (id string) {
	id = key.StringID()
	if id == "" && key.IntID() > 0 {
		id = fmt.Sprintf("%v", key.IntID())
	}
	return
}

func setKey(src interface{}, key *Key) {
	var parentKey = key.Parent()
	if parentKey != nil {
		id := parentKey.IntID()
		if parent, ok := src.(numParent); id != 0 && ok {
			parent.SetParent(id)
		}
		sid := parentKey.StringID()
		if parent, ok := src.(textParent); sid != "" && ok {
			parent.SetParent(sid)
		}
	}

	id := key.IntID()
	if ident, ok := src.(numIdentifier); id != 0 && ok {
		ident.SetID(id)
	}
	sid := key.StringID()
	if ident, ok := src.(textIdentifier); sid != "" && ok {
		ident.SetID(sid)
	}

	if v, ok := src.(versioned); ok {
		key.version = v.Version()
	}
}

// toMemKey converts a Key to a string. It includes the entity's version
// to prevent reading old versions of an entity from memcache.
func toMemKey(k *Key) string {
	return fmt.Sprintf("%v-%v", k.Encode(), k.version)
}

// toMemKeys converts a sequence of Key to a sequence of string.
func toMemKeys(keys []*Key) []string {
	ret := make([]string, len(keys))
	for i, k := range keys {
		if !k.Incomplete() {
			ret[i] = toMemKey(k)
		}
	}
	return ret
}

// toDSKeys converts a sequence of Key to a sequence of datastore.Key.
func toDSKeys(keys []*Key) []*datastore.Key {
	ret := make([]*datastore.Key, len(keys))
	for i, k := range keys {
		ret[i] = k.Key
	}
	return ret
}

func toKeyBatches(keys []*Key, batchSize int) []*keyBatch {
	batchCount := (len(keys) / batchSize) + 1
	batches := make([]*keyBatch, batchCount)
	for i := 0; i < batchCount; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(keys) {
			hi = len(keys)
		}
		batches[i] = &keyBatch{
			keys: keys[lo:hi],
			lo:   lo,
			hi:   hi,
		}
	}
	return batches
}
<commit_msg>prefix memcache keys with 'hrd'<commit_after>package hrd

import (
	"fmt"
	"time"

	"appengine/datastore"
)

// Key represents the datastore key for an entity.
// It also contains meta data about said entity.
type Key struct {
	*datastore.Key

	// source describes where the entity was read from.
	source string

	// version is the entity's version.
	version int64

	// synced is the last time the entity was read/written.
	synced time.Time

	// opts are the options to use for reading/writing the entity.
	opts *operationOpts

	// err contains an error if the entity could not be loaded/saved.
	err *error
}

type keyBatch struct {
	keys   []*Key
	lo, hi int
}

// newKey creates a Key from a datastore.Key.
func newKey(k *datastore.Key) *Key {
	return &Key{Key: k}
}

// newKeys creates a sequence of Key from a sequence of datastore.Key.
func newKeys(keys []*datastore.Key) []*Key {
	ret := make([]*Key, len(keys))
	for i, k := range keys {
		ret[i] = newKey(k)
	}
	return ret
}

// Exists is whether the entity with this key exists in the datastore.
func (key *Key) Exists() bool {
	return !key.synced.IsZero()
}

// IDString returns the ID of this Key as a string.
func (key *Key) IDString() (id string) {
	id = key.StringID()
	if id == "" && key.IntID() > 0 {
		id = fmt.Sprintf("%v", key.IntID())
	}
	return
}

func setKey(src interface{}, key *Key) {
	var parentKey = key.Parent()
	if parentKey != nil {
		id := parentKey.IntID()
		if parent, ok := src.(numParent); id != 0 && ok {
			parent.SetParent(id)
		}
		sid := parentKey.StringID()
		if parent, ok := src.(textParent); sid != "" && ok {
			parent.SetParent(sid)
		}
	}

	id := key.IntID()
	if ident, ok := src.(numIdentifier); id != 0 && ok {
		ident.SetID(id)
	}
	sid := key.StringID()
	if ident, ok := src.(textIdentifier); sid != "" && ok {
		ident.SetID(sid)
	}

	if v, ok := src.(versioned); ok {
		key.version = v.Version()
	}
}

// toMemKey converts a Key to a string. It includes the entity's version
// to prevent reading old versions of an entity from memcache.
func toMemKey(k *Key) string {
	return fmt.Sprintf("hrd:%v:%v", k.Encode(), k.version)
}

// toMemKeys converts a sequence of Key to a sequence of string.
func toMemKeys(keys []*Key) []string {
	ret := make([]string, len(keys))
	for i, k := range keys {
		if !k.Incomplete() {
			ret[i] = toMemKey(k)
		}
	}
	return ret
}

// toDSKeys converts a sequence of Key to a sequence of datastore.Key.
func toDSKeys(keys []*Key) []*datastore.Key {
	ret := make([]*datastore.Key, len(keys))
	for i, k := range keys {
		ret[i] = k.Key
	}
	return ret
}

func toKeyBatches(keys []*Key, batchSize int) []*keyBatch {
	batchCount := (len(keys) / batchSize) + 1
	batches := make([]*keyBatch, batchCount)
	for i := 0; i < batchCount; i++ {
		lo := i * putMultiLimit
		hi := (i + 1) * putMultiLimit
		if hi > len(keys) {
			hi = len(keys)
		}
		batches[i] = &keyBatch{
			keys: keys[lo:hi],
			lo:   lo,
			hi:   hi,
		}
	}
	return batches
}
<|endoftext|>


<commit_before>// Package kml provides convenince methods for creating and writing KML documents.
package kml

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"image/color"
	"io"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	NS    = "http://www.opengis.net/kml/2.2"
	NS_GX = "http://www.google.com/kml/ext/2.2"
)

var (
	lastId      int
	lastIdMutex sync.Mutex
)

func GetId() string {
	lastIdMutex.Lock()
	lastId++
	id := lastId
	lastIdMutex.Unlock()
	return strconv.Itoa(id)
}

// A Coordinate represents a single geographical coordinate.
// Lon and Lat are in degrees, Alt is in meters.
type Coordinate struct {
	Lon, Lat, Alt float64
}

// A Vec2 represents a screen position.
type Vec2 struct {
	X, Y           float64
	XUnits, YUnits string
}

// An Element represents an abstract KML element.
type Element interface {
	xml.Marshaler
	StringXML() (string, error)
	Write(io.Writer) error
}

// A SimpleElement is an Element with a single value.
type SimpleElement struct {
	xml.StartElement
	value string
}

// A CompoundElement is an Element with children.
type CompoundElement struct {
	xml.StartElement
	id       int
	children []Element
}

// A SharedElement is an element with an id.
type SharedElement struct {
	CompoundElement
	id string
}

// MarshalXML marshals se to e. start is ignored.
func (se *SimpleElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(se.StartElement); err != nil {
		return err
	}
	if err := e.EncodeToken(xml.CharData(se.value)); err != nil {
		return err
	}
	endElement := xml.EndElement{Name: se.Name}
	if err := e.EncodeToken(endElement); err != nil {
		return err
	}
	return nil
}

// StringXML returns se encoded in XML.
func (se *SimpleElement) StringXML() (string, error) {
	return stringXML(se)
}

// Write writes an XML header and se to w.
func (se *SimpleElement) Write(w io.Writer) error {
	return write(w, se)
}

// Add adds children to ce.
func (ce *CompoundElement) Add(children ...Element) *CompoundElement {
	ce.children = append(ce.children, children...)
	return ce
}

// MarshalXML marshals ce to e. start is ignored.
func (ce *CompoundElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(ce.StartElement); err != nil {
		return err
	}
	for _, c := range ce.children {
		if err := e.EncodeElement(c, ce.StartElement); err != nil {
			return err
		}
	}
	endElement := xml.EndElement{Name: ce.Name}
	if err := e.EncodeToken(endElement); err != nil {
		return err
	}
	return nil
}

// StringXML returns ce encoded in XML.
func (ce *CompoundElement) StringXML() (string, error) {
	return stringXML(ce)
}

// Write writes an XML header and ce to w.
func (ce *CompoundElement) Write(w io.Writer) error {
	return write(w, ce)
}

// Id returns se's id.
func (se *SharedElement) Id() string {
	return se.id
}

func Altitude(value int) *SimpleElement                  { return newSEInt("altitude", value) }
func AltitudeMode(value string) *SimpleElement           { return newSEString("altitudeMode", value) }
func BalloonStyle(children ...Element) *CompoundElement  { return newCE("BalloonStyle", children) }
func Begin(value time.Time) *SimpleElement               { return newSETime("begin", value) }
func BgColor(value color.Color) *SimpleElement           { return newSEColor("bgColor", value) }
func Camera(children ...Element) *CompoundElement        { return newCE("Camera", children) }
func Color(value color.Color) *SimpleElement             { return newSEColor("color", value) }
func Data(children ...Element) *CompoundElement          { return newCE("Data", children) }
func Description(value string) *SimpleElement            { return newSEString("description", value) }
func Document(children ...Element) *CompoundElement      { return newCE("Document", children) }
func East(value float64) *SimpleElement                  { return newSEFloat("east", value) }
func End(value time.Time) *SimpleElement                 { return newSETime("end", value) }
func Extrude(value bool) *SimpleElement                  { return newSEBool("extrude", value) }
func Folder(children ...Element) *CompoundElement        { return newCE("Folder", children) }
func GroundOverlay(children ...Element) *CompoundElement { return newCE("GroundOverlay", children) }
func GxAltitudeMode(value string) *SimpleElement         { return newSEString("gx:altitudeMode", value) }
func GxTrack(children ...Element) *CompoundElement       { return newCE("gx:Track", children) }
func Heading(value float64) *SimpleElement               { return newSEFloat("heading", value) }
func HotSpot(value Vec2) *SimpleElement                  { return newSEVec2("hotSpot", value) }
func Href(value *url.URL) *SimpleElement                 { return newSEString("href", value.String()) }
func Icon(children ...Element) *CompoundElement          { return newCE("Icon", children) }
func IconStyle(children ...Element) *CompoundElement     { return newCE("IconStyle", children) }
func InnerBoundaryIs(value Element) *CompoundElement     { return newCEElement("innerBoundaryIs", value) }
func LabelStyle(children ...Element) *CompoundElement    { return newCE("LabelStyle", children) }
func LatLonBox(children ...Element) *CompoundElement     { return newCE("LatLonBox", children) }
func Latitude(value float64) *SimpleElement              { return newSEFloat("latitude", value) }
func LineString(children ...Element) *CompoundElement    { return newCE("LineString", children) }
func LineStyle(children ...Element) *CompoundElement     { return newCE("LineStyle", children) }
func LinearRing(children ...Element) *CompoundElement    { return newCE("LinearRing", children) }
func ListItemType(value string) *SimpleElement           { return newSEString("listItemType", value) }
func ListStyle(children ...Element) *CompoundElement     { return newCE("ListStyle", children) }
func Longitude(value float64) *SimpleElement             { return newSEFloat("longitude", value) }
func MultiGeometry(children ...Element) *CompoundElement { return newCE("MultiGeometry", children) }
func Name(value string) *SimpleElement                   { return newSEString("name", value) }
func North(value float64) *SimpleElement                 { return newSEFloat("north", value) }
func Open(value bool) *SimpleElement                     { return newSEBool("open", value) }
func OuterBoundaryIs(value Element) *CompoundElement     { return newCEElement("outerBoundaryIs", value) }
func OverlayXY(value Vec2) *SimpleElement                { return newSEVec2("overlayXY", value) }
func Placemark(children ...Element) *CompoundElement     { return newCE("Placemark", children) }
func Point(children ...Element) *CompoundElement         { return newCE("Point", children) }
func PolyStyle(children ...Element) *CompoundElement     { return newCE("PolyStyle", children) }
func Polygon(children ...Element) *CompoundElement       { return newCE("Polygon", children) }
func Roll(value float64) *SimpleElement                  { return newSEFloat("roll", value) }
func Rotation(value float64) *SimpleElement              { return newSEFloat("rotation", value) }
func Scale(value float64) *SimpleElement                 { return newSEFloat("scale", value) }
func ScreenOverlay(children ...Element) *CompoundElement { return newCE("ScreenOverlay", children) }
func ScreenXY(value Vec2) *SimpleElement                 { return newSEVec2("screenXY", value) }
func Snippet(value string) *SimpleElement                { return newSEString("snippet", value) }
func South(value float64) *SimpleElement                 { return newSEFloat("south", value) }
func Style(children ...Element) *CompoundElement         { return newCE("Style", children) }
func Tesselate(value bool) *SimpleElement                { return newSEBool("tesselate", value) }
func Text(value string) *SimpleElement                   { return newSEString("text", value) }
func Tilt(value float64) *SimpleElement                  { return newSEFloat("tilt", value) }
func TimeSpan(children ...Element) *CompoundElement      { return newCE("TimeSpan", children) }
func Value(value string) *SimpleElement                  { return newSEString("value", value) }
func Visibility(value bool) *SimpleElement               { return newSEBool("visibility", value) }
func West(value float64) *SimpleElement                  { return newSEFloat("west", value) }
func When(value time.Time) *SimpleElement                { return newSETime("when", value) }
func Width(value float64) *SimpleElement                 { return newSEFloat("width", value) }

func Coordinates(value ...Coordinate) *SimpleElement {
	cs := make([]string, len(value))
	for i, c := range value {
		cs[i] = strconv.FormatFloat(c.Lon, 'f', -1, 64) + "," +
			strconv.FormatFloat(c.Lat, 'f', -1, 64) + "," +
			strconv.FormatFloat(c.Alt, 'f', -1, 64)
	}
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: "coordinates"},
		},
		value: strings.Join(cs, " "),
	}
}

func GxCoord(value Coordinate) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: "gx:coord"},
		},
		value: strconv.FormatFloat(value.Lon, 'f', -1, 64) + " " +
			strconv.FormatFloat(value.Lat, 'f', -1, 64) + " " +
			strconv.FormatFloat(value.Alt, 'f', -1, 64),
	}
}

func HrefMustParse(value string) *SimpleElement {
	url, err := url.Parse(value)
	if err != nil {
		panic(err)
	}
	return Href(url)
}

func SharedStyle(id string, children ...Element) *SharedElement {
	return newSharedE("Style", id, children)
}

func StyleURL(style *SharedElement) *SimpleElement {
	return newSEString("styleUrl", "#"+style.Id())
}

func KML(children ...Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Space: NS, Local: "kml"},
		},
		children: children,
	}
}

func GxKML(children ...Element) *CompoundElement {
	kml := KML(children...)
	// FIXME find a more correct way to do this
	kml.Attr = append(kml.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:gx"}, Value: NS_GX})
	return kml
}

func stringXML(m xml.Marshaler) (string, error) {
	b := &bytes.Buffer{}
	e := xml.NewEncoder(b)
	if err := e.Encode(m); err != nil {
		return "", err
	}
	return b.String(), nil
}

func write(w io.Writer, m xml.Marshaler) error {
	if _, err := w.Write([]byte(xml.Header)); err != nil {
		return err
	}
	e := xml.NewEncoder(w)
	if err := e.Encode(m); err != nil {
		return err
	}
	return nil
}

func newSEBool(name string, value bool) *SimpleElement {
	var v string
	if value {
		v = "1"
	} else {
		v = "0"
	}
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        v,
	}
}

func newSEColor(name string, value color.Color) *SimpleElement {
	r, g, b, a := value.RGBA()
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        fmt.Sprintf("%02x%02x%02x%02x", a/256, b/256, g/256, r/256),
	}
}

func newSEFloat(name string, value float64) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        strconv.FormatFloat(value, 'f', -1, 64),
	}
}

func newSEInt(name string, value int) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        strconv.Itoa(value),
	}
}

func newSEVec2(name string, value Vec2) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
			Attr: []xml.Attr{
				{Name: xml.Name{Local: "x"}, Value: strconv.FormatFloat(value.X, 'f', -1, 64)},
				{Name: xml.Name{Local: "y"}, Value: strconv.FormatFloat(value.Y, 'f', -1, 64)},
				{Name: xml.Name{Local: "xunits"}, Value: value.XUnits},
				{Name: xml.Name{Local: "yunits"}, Value: value.YUnits},
			},
		},
	}
}

func newSEString(name string, value string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        value,
	}
}

func newSETime(name string, value time.Time) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        value.Format(time.RFC3339),
	}
}

func newSEVoid(name string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
	}
}

func newCE(name string, children []Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
		},
		children: children,
	}
}

func newCEElement(name string, child Element) *CompoundElement {
	return newCE(name, []Element{child})
}

func newSharedE(name, id string, children []Element) *SharedElement {
	return &SharedElement{
		CompoundElement: CompoundElement{
			StartElement: xml.StartElement{
				Name: xml.Name{Local: name},
				Attr: []xml.Attr{
					{Name: xml.Name{Local: "id"}, Value: id},
				},
			},
			children: children,
		},
		id: id,
	}
}
<commit_msg>Move StyeURL into list<commit_after>// Package kml provides convenince methods for creating and writing KML documents.
package kml

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"image/color"
	"io"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"
)

const (
	NS    = "http://www.opengis.net/kml/2.2"
	NS_GX = "http://www.google.com/kml/ext/2.2"
)

var (
	lastId      int
	lastIdMutex sync.Mutex
)

func GetId() string {
	lastIdMutex.Lock()
	lastId++
	id := lastId
	lastIdMutex.Unlock()
	return strconv.Itoa(id)
}

// A Coordinate represents a single geographical coordinate.
// Lon and Lat are in degrees, Alt is in meters.
type Coordinate struct {
	Lon, Lat, Alt float64
}

// A Vec2 represents a screen position.
type Vec2 struct {
	X, Y           float64
	XUnits, YUnits string
}

// An Element represents an abstract KML element.
type Element interface {
	xml.Marshaler
	StringXML() (string, error)
	Write(io.Writer) error
}

// A SimpleElement is an Element with a single value.
type SimpleElement struct {
	xml.StartElement
	value string
}

// A CompoundElement is an Element with children.
type CompoundElement struct {
	xml.StartElement
	id       int
	children []Element
}

// A SharedElement is an element with an id.
type SharedElement struct {
	CompoundElement
	id string
}

// MarshalXML marshals se to e. start is ignored.
func (se *SimpleElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(se.StartElement); err != nil {
		return err
	}
	if err := e.EncodeToken(xml.CharData(se.value)); err != nil {
		return err
	}
	endElement := xml.EndElement{Name: se.Name}
	if err := e.EncodeToken(endElement); err != nil {
		return err
	}
	return nil
}

// StringXML returns se encoded in XML.
func (se *SimpleElement) StringXML() (string, error) {
	return stringXML(se)
}

// Write writes an XML header and se to w.
func (se *SimpleElement) Write(w io.Writer) error {
	return write(w, se)
}

// Add adds children to ce.
func (ce *CompoundElement) Add(children ...Element) *CompoundElement {
	ce.children = append(ce.children, children...)
	return ce
}

// MarshalXML marshals ce to e. start is ignored.
func (ce *CompoundElement) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
	if err := e.EncodeToken(ce.StartElement); err != nil {
		return err
	}
	for _, c := range ce.children {
		if err := e.EncodeElement(c, ce.StartElement); err != nil {
			return err
		}
	}
	endElement := xml.EndElement{Name: ce.Name}
	if err := e.EncodeToken(endElement); err != nil {
		return err
	}
	return nil
}

// StringXML returns ce encoded in XML.
func (ce *CompoundElement) StringXML() (string, error) {
	return stringXML(ce)
}

// Write writes an XML header and ce to w.
func (ce *CompoundElement) Write(w io.Writer) error {
	return write(w, ce)
}

// Id returns se's id.
func (se *SharedElement) Id() string {
	return se.id
}

func Altitude(value int) *SimpleElement                  { return newSEInt("altitude", value) }
func AltitudeMode(value string) *SimpleElement           { return newSEString("altitudeMode", value) }
func BalloonStyle(children ...Element) *CompoundElement  { return newCE("BalloonStyle", children) }
func Begin(value time.Time) *SimpleElement               { return newSETime("begin", value) }
func BgColor(value color.Color) *SimpleElement           { return newSEColor("bgColor", value) }
func Camera(children ...Element) *CompoundElement        { return newCE("Camera", children) }
func Color(value color.Color) *SimpleElement             { return newSEColor("color", value) }
func Data(children ...Element) *CompoundElement          { return newCE("Data", children) }
func Description(value string) *SimpleElement            { return newSEString("description", value) }
func Document(children ...Element) *CompoundElement      { return newCE("Document", children) }
func East(value float64) *SimpleElement                  { return newSEFloat("east", value) }
func End(value time.Time) *SimpleElement                 { return newSETime("end", value) }
func Extrude(value bool) *SimpleElement                  { return newSEBool("extrude", value) }
func Folder(children ...Element) *CompoundElement        { return newCE("Folder", children) }
func GroundOverlay(children ...Element) *CompoundElement { return newCE("GroundOverlay", children) }
func GxAltitudeMode(value string) *SimpleElement         { return newSEString("gx:altitudeMode", value) }
func GxTrack(children ...Element) *CompoundElement       { return newCE("gx:Track", children) }
func Heading(value float64) *SimpleElement               { return newSEFloat("heading", value) }
func HotSpot(value Vec2) *SimpleElement                  { return newSEVec2("hotSpot", value) }
func Href(value *url.URL) *SimpleElement                 { return newSEString("href", value.String()) }
func Icon(children ...Element) *CompoundElement          { return newCE("Icon", children) }
func IconStyle(children ...Element) *CompoundElement     { return newCE("IconStyle", children) }
func InnerBoundaryIs(value Element) *CompoundElement     { return newCEElement("innerBoundaryIs", value) }
func LabelStyle(children ...Element) *CompoundElement    { return newCE("LabelStyle", children) }
func LatLonBox(children ...Element) *CompoundElement     { return newCE("LatLonBox", children) }
func Latitude(value float64) *SimpleElement              { return newSEFloat("latitude", value) }
func LineString(children ...Element) *CompoundElement    { return newCE("LineString", children) }
func LineStyle(children ...Element) *CompoundElement     { return newCE("LineStyle", children) }
func LinearRing(children ...Element) *CompoundElement    { return newCE("LinearRing", children) }
func ListItemType(value string) *SimpleElement           { return newSEString("listItemType", value) }
func ListStyle(children ...Element) *CompoundElement     { return newCE("ListStyle", children) }
func Longitude(value float64) *SimpleElement             { return newSEFloat("longitude", value) }
func MultiGeometry(children ...Element) *CompoundElement { return newCE("MultiGeometry", children) }
func Name(value string) *SimpleElement                   { return newSEString("name", value) }
func North(value float64) *SimpleElement                 { return newSEFloat("north", value) }
func Open(value bool) *SimpleElement                     { return newSEBool("open", value) }
func OuterBoundaryIs(value Element) *CompoundElement     { return newCEElement("outerBoundaryIs", value) }
func OverlayXY(value Vec2) *SimpleElement                { return newSEVec2("overlayXY", value) }
func Placemark(children ...Element) *CompoundElement     { return newCE("Placemark", children) }
func Point(children ...Element) *CompoundElement         { return newCE("Point", children) }
func PolyStyle(children ...Element) *CompoundElement     { return newCE("PolyStyle", children) }
func Polygon(children ...Element) *CompoundElement       { return newCE("Polygon", children) }
func Roll(value float64) *SimpleElement                  { return newSEFloat("roll", value) }
func Rotation(value float64) *SimpleElement              { return newSEFloat("rotation", value) }
func Scale(value float64) *SimpleElement                 { return newSEFloat("scale", value) }
func ScreenOverlay(children ...Element) *CompoundElement { return newCE("ScreenOverlay", children) }
func ScreenXY(value Vec2) *SimpleElement                 { return newSEVec2("screenXY", value) }
func Snippet(value string) *SimpleElement                { return newSEString("snippet", value) }
func South(value float64) *SimpleElement                 { return newSEFloat("south", value) }
func Style(children ...Element) *CompoundElement         { return newCE("Style", children) }
func StyleURL(style *SharedElement) *SimpleElement       { return newSEString("styleUrl", "#"+style.Id()) }
func Tesselate(value bool) *SimpleElement                { return newSEBool("tesselate", value) }
func Text(value string) *SimpleElement                   { return newSEString("text", value) }
func Tilt(value float64) *SimpleElement                  { return newSEFloat("tilt", value) }
func TimeSpan(children ...Element) *CompoundElement      { return newCE("TimeSpan", children) }
func Value(value string) *SimpleElement                  { return newSEString("value", value) }
func Visibility(value bool) *SimpleElement               { return newSEBool("visibility", value) }
func West(value float64) *SimpleElement                  { return newSEFloat("west", value) }
func When(value time.Time) *SimpleElement                { return newSETime("when", value) }
func Width(value float64) *SimpleElement                 { return newSEFloat("width", value) }

func Coordinates(value ...Coordinate) *SimpleElement {
	cs := make([]string, len(value))
	for i, c := range value {
		cs[i] = strconv.FormatFloat(c.Lon, 'f', -1, 64) + "," +
			strconv.FormatFloat(c.Lat, 'f', -1, 64) + "," +
			strconv.FormatFloat(c.Alt, 'f', -1, 64)
	}
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: "coordinates"},
		},
		value: strings.Join(cs, " "),
	}
}

func GxCoord(value Coordinate) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: "gx:coord"},
		},
		value: strconv.FormatFloat(value.Lon, 'f', -1, 64) + " " +
			strconv.FormatFloat(value.Lat, 'f', -1, 64) + " " +
			strconv.FormatFloat(value.Alt, 'f', -1, 64),
	}
}

func HrefMustParse(value string) *SimpleElement {
	url, err := url.Parse(value)
	if err != nil {
		panic(err)
	}
	return Href(url)
}

func SharedStyle(id string, children ...Element) *SharedElement {
	return newSharedE("Style", id, children)
}

func KML(children ...Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Space: NS, Local: "kml"},
		},
		children: children,
	}
}

func GxKML(children ...Element) *CompoundElement {
	kml := KML(children...)
	// FIXME find a more correct way to do this
	kml.Attr = append(kml.Attr, xml.Attr{Name: xml.Name{Local: "xmlns:gx"}, Value: NS_GX})
	return kml
}

func stringXML(m xml.Marshaler) (string, error) {
	b := &bytes.Buffer{}
	e := xml.NewEncoder(b)
	if err := e.Encode(m); err != nil {
		return "", err
	}
	return b.String(), nil
}

func write(w io.Writer, m xml.Marshaler) error {
	if _, err := w.Write([]byte(xml.Header)); err != nil {
		return err
	}
	e := xml.NewEncoder(w)
	if err := e.Encode(m); err != nil {
		return err
	}
	return nil
}

func newSEBool(name string, value bool) *SimpleElement {
	var v string
	if value {
		v = "1"
	} else {
		v = "0"
	}
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        v,
	}
}

func newSEColor(name string, value color.Color) *SimpleElement {
	r, g, b, a := value.RGBA()
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        fmt.Sprintf("%02x%02x%02x%02x", a/256, b/256, g/256, r/256),
	}
}

func newSEFloat(name string, value float64) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        strconv.FormatFloat(value, 'f', -1, 64),
	}
}

func newSEInt(name string, value int) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        strconv.Itoa(value),
	}
}

func newSEVec2(name string, value Vec2) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
			Attr: []xml.Attr{
				{Name: xml.Name{Local: "x"}, Value: strconv.FormatFloat(value.X, 'f', -1, 64)},
				{Name: xml.Name{Local: "y"}, Value: strconv.FormatFloat(value.Y, 'f', -1, 64)},
				{Name: xml.Name{Local: "xunits"}, Value: value.XUnits},
				{Name: xml.Name{Local: "yunits"}, Value: value.YUnits},
			},
		},
	}
}

func newSEString(name string, value string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        value,
	}
}

func newSETime(name string, value time.Time) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
		value:        value.Format(time.RFC3339),
	}
}

func newSEVoid(name string) *SimpleElement {
	return &SimpleElement{
		StartElement: xml.StartElement{Name: xml.Name{Local: name}},
	}
}

func newCE(name string, children []Element) *CompoundElement {
	return &CompoundElement{
		StartElement: xml.StartElement{
			Name: xml.Name{Local: name},
		},
		children: children,
	}
}

func newCEElement(name string, child Element) *CompoundElement {
	return newCE(name, []Element{child})
}

func newSharedE(name, id string, children []Element) *SharedElement {
	return &SharedElement{
		CompoundElement: CompoundElement{
			StartElement: xml.StartElement{
				Name: xml.Name{Local: name},
				Attr: []xml.Attr{
					{Name: xml.Name{Local: "id"}, Value: id},
				},
			},
			children: children,
		},
		id: id,
	}
}
<|endoftext|>


<commit_before>/*
SPDX-License-Identifier: MIT

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// lhc is a checker to find code files missing license headers.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/zxiiro/license-header-checker/license"
)

var VERSION = "0.1.0"

type License struct {
	Name string
	Text string
}

// Compare a license header with an approved list of license headers.
// Returns the name of the license that was approved. Else "".
func accepted_license(check string, approved []License) string {
	for _, i := range approved {
		if check == i.Text {
			return i.Name
		}
	}

	return ""
}

// check and exit if error.
func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

func findFiles(directory string, patterns []string) []string {
	var files []string
	filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			for _, p := range patterns {
				f, _ := filepath.Glob(filepath.Join(path, p))
				files = append(files, f...)
			}
		}
		return nil
	})
	return files
}

// fetchLicense from file and return license text.
func fetchLicense(filename string) string {
	comment, multilineComment := false, false
	licenseText := ""

	var scanner *bufio.Scanner
	if filename == "MIT" {
		scanner = bufio.NewScanner(strings.NewReader(license.MIT_LICENSE))
	} else if filename == "EPL-1.0" {
		scanner = bufio.NewScanner(strings.NewReader(license.EPL_10_LICENSE))
	} else {
		file, err := os.Open(filename)
		check(err)
		defer file.Close()

		// Read the first 2 bytes to decide if it is a comment string
		b := make([]byte, 2)
		_, err = file.Read(b)
		check(err)
		if isComment(string(b)) {
			comment = true
		}
		file.Seek(0, 0) // Reset so we can read the full file next

		scanner = bufio.NewScanner(file)
	}

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > 50 {
			break
		}

		s := scanner.Text()

		if ignoreComment(s) {
			continue
		}

		if comment == true {
			if strings.HasPrefix(s, "/*") {
				multilineComment = true
			} else if strings.Contains(s, "*/") {
				multilineComment = false
			}

			if !multilineComment && !isComment(s) ||
				// EPL headers can contain contributors list.
				strings.Contains(strings.ToUpper(s), " * CONTRIBUTORS:") {
				break
			}

			s = trimComment(s)
		}

		licenseText += s
	}

	return stripSpaces(licenseText)
}

// Check if a string is a comment line.
func isComment(str string) bool {
	if !strings.HasPrefix(str, "#") &&
		!strings.HasPrefix(str, "//") &&
		!strings.HasPrefix(str, "/*") {
		return false
	}

	return true
}

// Ignore certain lines containing key strings
func ignoreComment(str string) bool {
	s := strings.ToUpper(str)
	if strings.HasPrefix(s, "#!") ||
		strings.Contains(s, "COPYRIGHT") ||
		strings.Contains(s, "SPDX-LICENSE-IDENTIFIER") ||
		// License name in LICENSE file but not header
		strings.Contains(s, "MIT LICENSE") {
		return true
	}

	return false
}

// Strip whitespace from string.
func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

// Trim the comment prefix from string.
func trimComment(str string) string {
	str = strings.TrimLeft(str, "#")
	str = strings.TrimLeft(str, "//")
	str = strings.TrimLeft(str, "/*")
	str = strings.TrimLeft(str, " *")
	str = strings.Split(str, "*/")[0]
	str = strings.TrimLeft(str, "*")
	return str
}

// Usage prints a statement to explain how to use this command.
func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [FILE]...\n", os.Args[0])
	fmt.Printf("Compare FILE with an expected license header.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	directoryPtr := flag.String("directory", ".", "Directory to search for files.")
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	var accepted_licenses []License
	for _, l := range strings.Split(*licensePtr, ",") {
		license := License{l, fetchLicense(l)}
		accepted_licenses = append(accepted_licenses, license)
	}
	checkFiles := findFiles(*directoryPtr, flag.Args())

	for _, f := range checkFiles {
		headerText := fetchLicense(f)
		if accepted_license(headerText, accepted_licenses) != "" {
			fmt.Println("✔", f)
		} else {
			fmt.Println("✘", f)
		}
	}
}
<commit_msg>Add verifier to confirm SPDX and License matches<commit_after>/*
SPDX-License-Identifier: MIT

Copyright (c) 2017 Thanh Ha

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/

// lhc is a checker to find code files missing license headers.
package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"unicode"

	"github.com/zxiiro/license-header-checker/license"
)

var LICENSE_HEADER_LINES = 50
var VERSION = "0.1.0"

type License struct {
	Name string
	Text string
}

// Compare a license header with an approved list of license headers.
// Returns the name of the license that was approved. Else "".
func accepted_license(check string, approved []License) string {
	for _, i := range approved {
		if check == i.Text {
			return i.Name
		}
	}

	return ""
}

// check and exit if error.
func check(e error) {
	if e != nil {
		fmt.Println(e)
		os.Exit(1)
	}
}

func checkSPDX(license string, filename string) bool {
	file, err := os.Open(filename)
	check(err)
	defer file.Close()
	scanner := bufio.NewScanner(file)

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > LICENSE_HEADER_LINES {
			break
		}

		s := strings.ToUpper(scanner.Text())
		if strings.Contains(s, "SPDX-LICENSE-IDENTIFIER:") {
			spdx := stripSpaces(strings.SplitN(s, ":", 2)[1])
			if spdx == license {
				return true
			} else {
				return false
			}
		}
	}

	return false
}

func findFiles(directory string, patterns []string) []string {
	var files []string
	filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
		if info.IsDir() {
			for _, p := range patterns {
				f, _ := filepath.Glob(filepath.Join(path, p))
				files = append(files, f...)
			}
		}
		return nil
	})
	return files
}

// fetchLicense from file and return license text.
func fetchLicense(filename string) string {
	comment, multilineComment := false, false
	licenseText := ""

	var scanner *bufio.Scanner
	if filename == "MIT" {
		scanner = bufio.NewScanner(strings.NewReader(license.MIT_LICENSE))
	} else if filename == "EPL-1.0" {
		scanner = bufio.NewScanner(strings.NewReader(license.EPL_10_LICENSE))
	} else {
		file, err := os.Open(filename)
		check(err)
		defer file.Close()

		// Read the first 2 bytes to decide if it is a comment string
		b := make([]byte, 2)
		_, err = file.Read(b)
		check(err)
		if isComment(string(b)) {
			comment = true
		}
		file.Seek(0, 0) // Reset so we can read the full file next

		scanner = bufio.NewScanner(file)
	}

	i := 0
	for scanner.Scan() {
		// Read only the first few lines to not read entire code file
		i++
		if i > LICENSE_HEADER_LINES {
			break
		}

		s := scanner.Text()

		if ignoreComment(s) {
			continue
		}

		if comment == true {
			if strings.HasPrefix(s, "/*") {
				multilineComment = true
			} else if strings.Contains(s, "*/") {
				multilineComment = false
			}

			if !multilineComment && !isComment(s) ||
				// EPL headers can contain contributors list.
				strings.Contains(strings.ToUpper(s), " * CONTRIBUTORS:") {
				break
			}

			s = trimComment(s)
		}

		licenseText += s
	}

	return stripSpaces(licenseText)
}

// Check if a string is a comment line.
func isComment(str string) bool {
	if !strings.HasPrefix(str, "#") &&
		!strings.HasPrefix(str, "//") &&
		!strings.HasPrefix(str, "/*") {
		return false
	}

	return true
}

// Ignore certain lines containing key strings
func ignoreComment(str string) bool {
	s := strings.ToUpper(str)
	if strings.HasPrefix(s, "#!") ||
		strings.Contains(s, "COPYRIGHT") ||
		strings.Contains(s, "SPDX-LICENSE-IDENTIFIER") ||
		// License name in LICENSE file but not header
		strings.Contains(s, "MIT LICENSE") {
		return true
	}

	return false
}

// Strip whitespace from string.
func stripSpaces(str string) string {
	return strings.Map(func(r rune) rune {
		if unicode.IsSpace(r) {
			return -1
		}
		return r
	}, str)
}

// Trim the comment prefix from string.
func trimComment(str string) string {
	str = strings.TrimLeft(str, "#")
	str = strings.TrimLeft(str, "//")
	str = strings.TrimLeft(str, "/*")
	str = strings.TrimLeft(str, " *")
	str = strings.Split(str, "*/")[0]
	str = strings.TrimLeft(str, "*")
	return str
}

// Usage prints a statement to explain how to use this command.
func usage() {
	fmt.Printf("Usage: %s [OPTIONS] [FILE]...\n", os.Args[0])
	fmt.Printf("Compare FILE with an expected license header.\n")
	fmt.Printf("\nOptions:\n")
	flag.PrintDefaults()
}

func main() {
	directoryPtr := flag.String("directory", ".", "Directory to search for files.")
	SPDXPtr := flag.Bool("spdx", false, "Verify SDPX identifier matches license.")
	licensePtr := flag.String("license", "license.txt", "Comma-separated list of license files to compare against.")
	versionPtr := flag.Bool("version", false, "Print version")

	flag.Usage = usage
	flag.Parse()

	if *versionPtr {
		fmt.Println("License Checker version", VERSION)
		os.Exit(0)
	}

	fmt.Println("Search Patterns:", flag.Args())

	var accepted_licenses []License
	for _, l := range strings.Split(*licensePtr, ",") {
		license := License{l, fetchLicense(l)}
		accepted_licenses = append(accepted_licenses, license)
	}
	checkFiles := findFiles(*directoryPtr, flag.Args())

	for _, file := range checkFiles {
		headerText := fetchLicense(file)
		license := accepted_license(headerText, accepted_licenses)
		result := ""
		if license != "" {
			result = result + "✔"
		} else {
			result = result + "✘"
		}
		if *SPDXPtr {
			if checkSPDX(license, file) {
				result = result + "✔"
			} else {
				result = result + "✘"
			}
		}
		fmt.Println(result, file)
	}
}
<|endoftext|>