<commit_before>package collectors

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/metadata"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_windows_processes})
}

func WatchProcesses(procs []*WatchedProc) error {
	return fmt.Errorf("process watching not implemented on Darwin")
}

// These are silly processes but exist on my machine, will need to update KMB
var processInclusions = regexp.MustCompile("chrome|powershell|scollector|SocketServer")
var serviceInclusions = regexp.MustCompile("WinRM")

func c_windows_processes() (opentsdb.MultiDataPoint, error) {
	var dst []Win32_PerfRawData_PerfProc_Process
	var q = wmi.CreateQuery(&dst, `WHERE Name <> '_Total'`)
	err := queryWmi(q, &dst)
	if err != nil {
		return nil, err
	}

	var svc_dst []Win32_Service
	var svc_q = wmi.CreateQuery(&svc_dst, `WHERE Name <> '_Total'`)
	err = queryWmi(svc_q, &svc_dst)
	if err != nil {
		return nil, err
	}

	var iis_dst []WorkerProcess
	iis_q := wmi.CreateQuery(&iis_dst, "")
	err = queryWmiNamespace(iis_q, &iis_dst, "root\\WebAdministration")
	if err != nil {
		// Don't return from this error since the name space might exist.
		iis_dst = nil
	}

	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		var name string
		service_match := false
		iis_match := false
		process_match := processInclusions.MatchString(v.Name)

		id := "0"

		if process_match {
			raw_name := strings.Split(v.Name, "#")
			name = raw_name[0]
			if len(raw_name) == 2 {
				id = raw_name[1]
			}
			// If you have a hash sign in your process name you don't deserve monitoring ;-)
			if len(raw_name) > 2 {
				continue
			}
		}

		// A Service match could "overwrite" a process match, but that is probably what we would want
		for _, svc := range svc_dst {
			if serviceInclusions.MatchString(svc.Name) {
				// It is possible the pid has gone and been reused, but I think this unlikely
				// And I'm not aware of an atomic join we could do anyways
				if svc.ProcessId == v.IDProcess {
					id = "0"
					service_match = true
					name = svc.Name
					break
				}
			}
		}

		for _, a_pool := range iis_dst {
			if a_pool.ProcessId == v.IDProcess {
				id = "0"
				iis_match = true
				name = strings.Join([]string{"iis", a_pool.AppPoolName}, "_")
				break
			}
		}

		if !(service_match || process_match || iis_match) {
			continue
		}

		//Use timestamp from WMI to fix issues with CPU metrics
		ts := TSys100NStoEpoch(v.Timestamp_Sys100NS)
		AddTS(&md, "win.proc.cpu", ts, v.PercentPrivilegedTime/NS100_Seconds, opentsdb.TagSet{"name": name, "id": id, "type": "privileged"}, metadata.Counter, metadata.Pct, descWinProcCpu_priv)
		AddTS(&md, "win.proc.cpu", ts, v.PercentUserTime/NS100_Seconds, opentsdb.TagSet{"name": name, "id": id, "type": "user"}, metadata.Counter, metadata.Pct, descWinProcCpu_user)
		AddTS(&md, "win.proc.cpu_total", ts, v.PercentProcessorTime/NS100_Seconds, opentsdb.TagSet{"name": name, "id": id}, metadata.Counter, metadata.Pct, descWinProcCpu_total)
		Add(&md, "win.proc.elapsed_time", (v.Timestamp_Object-v.ElapsedTime)/v.Frequency_Object, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Second, descWinProcElapsed_time)
		Add(&md, "win.proc.handle_count", v.HandleCount, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Count, descWinProcHandle_count)
		Add(&md, "win.proc.io_bytes", v.IOOtherBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_other)
		Add(&md, "win.proc.io_bytes", v.IOReadBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_read)
		Add(&md, "win.proc.io_bytes", v.IOWriteBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_write)
		Add(&md, "win.proc.io_operations", v.IOOtherOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.io_operations", v.IOReadOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.io_operations", v.IOWriteOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.mem.page_faults", v.PageFaultsPersec, opentsdb.TagSet{"name": name, "id": id}, metadata.Counter, metadata.PerSecond, descWinProcMemPage_faults)
		Add(&md, "win.proc.mem.pagefile_bytes", v.PageFileBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPagefile_bytes)
		Add(&md, "win.proc.mem.pagefile_bytes_peak", v.PageFileBytesPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPagefile_bytes_peak)
		Add(&md, "win.proc.mem.pool_nonpaged_bytes", v.PoolNonpagedBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPool_nonpaged_bytes)
		Add(&md, "win.proc.mem.pool_paged_bytes", v.PoolPagedBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPool_paged_bytes)
		Add(&md, "win.proc.mem.vm.bytes", v.VirtualBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemVmBytes)
		Add(&md, "win.proc.mem.vm.bytes_peak", v.VirtualBytesPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemVmBytes_peak)
		Add(&md, "win.proc.mem.working_set", v.WorkingSet, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set)
		Add(&md, "win.proc.mem.working_set_peak", v.WorkingSetPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set_peak)
		Add(&md, "win.proc.mem.working_set_private", v.WorkingSetPrivate, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set_private)
		Add(&md, "win.proc.priority_base", v.PriorityBase, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.None, descWinProcPriority_base)
		Add(&md, "win.proc.private_bytes", v.PrivateBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcPrivate_bytes)
		Add(&md, "win.proc.thread_count", v.ThreadCount, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Count, descWinProcthread_count)
	}
	return md, nil
}

// Divide CPU by 1e5 because: 1 seconds / 100 Nanoseconds = 1e7. This is the
// percent time as a decimal, so divide by two less zeros to make it the same as
// the result * 100.
const NS100_Seconds = 1e5

const (
	descWinProcCpu_priv               = "Percentage of elapsed time that this thread has spent executing code in privileged mode."
	descWinProcCpu_total              = "Percentage of elapsed time that this process's threads have spent executing code in user or privileged mode."
	descWinProcCpu_user               = "Percentage of elapsed time that this process's threads have spent executing code in user mode."
	descWinProcElapsed_time           = "Elapsed time in seconds this process has been running."
	descWinProcHandle_count           = "Total number of handles the process has open across all threads."
	descWinProcIo_bytes_other         = "Rate at which the process is issuing bytes to I/O operations that do not involve data such as control operations."
	descWinProcIo_bytes_read          = "Rate at which the process is reading bytes from I/O operations."
	descWinProcIo_bytes_write         = "Rate at which the process is writing bytes to I/O operations."
	descWinProcIo_operations          = "Rate at which the process is issuing I/O operations that are neither a read or a write request."
	descWinProcIo_operations_read     = "Rate at which the process is issuing read I/O operations."
	descWinProcIo_operations_write    = "Rate at which the process is issuing write I/O operations."
	descWinProcMemPage_faults         = "Rate of page faults by the threads executing in this process."
	descWinProcMemPagefile_bytes      = "Current number of bytes this process has used in the paging file(s)."
	descWinProcMemPagefile_bytes_peak = "Maximum number of bytes this process has used in the paging file(s)."
	descWinProcMemPool_nonpaged_bytes = "Total number of bytes for objects that cannot be written to disk when they are not being used."
	descWinProcMemPool_paged_bytes    = "Total number of bytes for objects that can be written to disk when they are not being used."
	descWinProcMemVmBytes             = "Current size, in bytes, of the virtual address space that the process is using."
	descWinProcMemVmBytes_peak        = "Maximum number of bytes of virtual address space that the process has used at any one time."
	descWinProcMemWorking_set         = "Current number of bytes in the working set of this process at any point in time."
	descWinProcMemWorking_set_peak    = "Maximum number of bytes in the working set of this process at any point in time."
	descWinProcMemWorking_set_private = "Current number of bytes in the working set that are not shared with other processes."
	descWinProcPriority_base          = "Current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process base priority of the process."
	descWinProcPrivate_bytes          = "Current number of bytes this process has allocated that cannot be shared with other processes."
	descWinProcthread_count           = "Number of threads currently active in this process."
)

// Actually a CIM_StatisticalInformation.
type Win32_PerfRawData_PerfProc_Process struct {
	ElapsedTime             uint64
	Frequency_Object        uint64
	HandleCount             uint32
	IDProcess               uint32
	IOOtherBytesPersec      uint64
	IOOtherOperationsPersec uint64
	IOReadBytesPersec       uint64
	IOReadOperationsPersec  uint64
	IOWriteBytesPersec      uint64
	IOWriteOperationsPersec uint64
	Name                    string
	PageFaultsPersec        uint32
	PageFileBytes           uint64
	PageFileBytesPeak       uint64
	PercentPrivilegedTime   uint64
	PercentProcessorTime    uint64
	PercentUserTime         uint64
	PoolNonpagedBytes       uint32
	PoolPagedBytes          uint32
	PriorityBase            uint32
	PrivateBytes            uint64
	ThreadCount             uint32
	Timestamp_Object        uint64
	Timestamp_Sys100NS      uint64
	VirtualBytes            uint64
	VirtualBytesPeak        uint64
	WorkingSet              uint64
	WorkingSetPeak          uint64
	WorkingSetPrivate       uint64
}

// Actually a Win32_BaseServce.
type Win32_Service struct {
	Name      string
	ProcessId uint32
}

type WorkerProcess struct {
	AppPoolName string
	ProcessId   uint32
}
<commit_msg>cmd/scollector: Convert win.proc.cpu* to 0-100% range instead of being per core.<commit_after>package collectors

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/StackExchange/wmi"
	"github.com/bosun-monitor/scollector/metadata"
	"github.com/bosun-monitor/scollector/opentsdb"
)

func init() {
	collectors = append(collectors, &IntervalCollector{F: c_windows_processes})
}

func WatchProcesses(procs []*WatchedProc) error {
	return fmt.Errorf("process watching not implemented on Darwin")
}

// These are silly processes but exist on my machine, will need to update KMB
var processInclusions = regexp.MustCompile("chrome|powershell|scollector|SocketServer")
var serviceInclusions = regexp.MustCompile("WinRM")

func c_windows_processes() (opentsdb.MultiDataPoint, error) {
	var dst []Win32_PerfRawData_PerfProc_Process
	var q = wmi.CreateQuery(&dst, `WHERE Name <> '_Total'`)
	err := queryWmi(q, &dst)
	if err != nil {
		return nil, err
	}

	var svc_dst []Win32_Service
	var svc_q = wmi.CreateQuery(&svc_dst, `WHERE Name <> '_Total'`)
	err = queryWmi(svc_q, &svc_dst)
	if err != nil {
		return nil, err
	}

	var iis_dst []WorkerProcess
	iis_q := wmi.CreateQuery(&iis_dst, "")
	err = queryWmiNamespace(iis_q, &iis_dst, "root\\WebAdministration")
	if err != nil {
		// Don't return from this error since the name space might exist.
		iis_dst = nil
	}

	var numberOfLogicalProcessors uint64
	var core_dst []Win32_Processor
	var core_q = wmi.CreateQuery(&core_dst, `WHERE Name <> '_Total'`)
	err = queryWmi(core_q, &core_dst)
	if err != nil {
		return nil, err
	}
	for _, y := range core_dst {
		numberOfLogicalProcessors = uint64(y.NumberOfLogicalProcessors)
	}

	var md opentsdb.MultiDataPoint
	for _, v := range dst {
		var name string
		service_match := false
		iis_match := false
		process_match := processInclusions.MatchString(v.Name)

		id := "0"

		if process_match {
			raw_name := strings.Split(v.Name, "#")
			name = raw_name[0]
			if len(raw_name) == 2 {
				id = raw_name[1]
			}
			// If you have a hash sign in your process name you don't deserve monitoring ;-)
			if len(raw_name) > 2 {
				continue
			}
		}

		// A Service match could "overwrite" a process match, but that is probably what we would want
		for _, svc := range svc_dst {
			if serviceInclusions.MatchString(svc.Name) {
				// It is possible the pid has gone and been reused, but I think this unlikely
				// And I'm not aware of an atomic join we could do anyways
				if svc.ProcessId == v.IDProcess {
					id = "0"
					service_match = true
					name = svc.Name
					break
				}
			}
		}

		for _, a_pool := range iis_dst {
			if a_pool.ProcessId == v.IDProcess {
				id = "0"
				iis_match = true
				name = strings.Join([]string{"iis", a_pool.AppPoolName}, "_")
				break
			}
		}

		if !(service_match || process_match || iis_match) {
			continue
		}

		//Use timestamp from WMI to fix issues with CPU metrics
		ts := TSys100NStoEpoch(v.Timestamp_Sys100NS)
		AddTS(&md, "win.proc.cpu", ts, v.PercentPrivilegedTime/NS100_Seconds/numberOfLogicalProcessors, opentsdb.TagSet{"name": name, "id": id, "type": "privileged"}, metadata.Counter, metadata.Pct, descWinProcCpu_priv)
		AddTS(&md, "win.proc.cpu", ts, v.PercentUserTime/NS100_Seconds/numberOfLogicalProcessors, opentsdb.TagSet{"name": name, "id": id, "type": "user"}, metadata.Counter, metadata.Pct, descWinProcCpu_user)
		AddTS(&md, "win.proc.cpu_total", ts, v.PercentProcessorTime/NS100_Seconds/numberOfLogicalProcessors, opentsdb.TagSet{"name": name, "id": id}, metadata.Counter, metadata.Pct, descWinProcCpu_total)
		Add(&md, "win.proc.elapsed_time", (v.Timestamp_Object-v.ElapsedTime)/v.Frequency_Object, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Second, descWinProcElapsed_time)
		Add(&md, "win.proc.handle_count", v.HandleCount, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Count, descWinProcHandle_count)
		Add(&md, "win.proc.io_bytes", v.IOOtherBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_other)
		Add(&md, "win.proc.io_bytes", v.IOReadBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_read)
		Add(&md, "win.proc.io_bytes", v.IOWriteBytesPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"}, metadata.Counter, metadata.BytesPerSecond, descWinProcIo_bytes_write)
		Add(&md, "win.proc.io_operations", v.IOOtherOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "other"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.io_operations", v.IOReadOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "read"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.io_operations", v.IOWriteOperationsPersec, opentsdb.TagSet{"name": name, "id": id, "type": "write"}, metadata.Counter, metadata.Operation, descWinProcIo_operations)
		Add(&md, "win.proc.mem.page_faults", v.PageFaultsPersec, opentsdb.TagSet{"name": name, "id": id}, metadata.Counter, metadata.PerSecond, descWinProcMemPage_faults)
		Add(&md, "win.proc.mem.pagefile_bytes", v.PageFileBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPagefile_bytes)
		Add(&md, "win.proc.mem.pagefile_bytes_peak", v.PageFileBytesPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPagefile_bytes_peak)
		Add(&md, "win.proc.mem.pool_nonpaged_bytes", v.PoolNonpagedBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPool_nonpaged_bytes)
		Add(&md, "win.proc.mem.pool_paged_bytes", v.PoolPagedBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemPool_paged_bytes)
		Add(&md, "win.proc.mem.vm.bytes", v.VirtualBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemVmBytes)
		Add(&md, "win.proc.mem.vm.bytes_peak", v.VirtualBytesPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemVmBytes_peak)
		Add(&md, "win.proc.mem.working_set", v.WorkingSet, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set)
		Add(&md, "win.proc.mem.working_set_peak", v.WorkingSetPeak, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set_peak)
		Add(&md, "win.proc.mem.working_set_private", v.WorkingSetPrivate, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcMemWorking_set_private)
		Add(&md, "win.proc.priority_base", v.PriorityBase, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.None, descWinProcPriority_base)
		Add(&md, "win.proc.private_bytes", v.PrivateBytes, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Bytes, descWinProcPrivate_bytes)
		Add(&md, "win.proc.thread_count", v.ThreadCount, opentsdb.TagSet{"name": name, "id": id}, metadata.Gauge, metadata.Count, descWinProcthread_count)
	}
	return md, nil
}

// Divide CPU by 1e5 because: 1 seconds / 100 Nanoseconds = 1e7. This is the
// percent time as a decimal, so divide by two less zeros to make it the same as
// the result * 100.
const NS100_Seconds = 1e5

const (
	descWinProcCpu_priv               = "Percentage of elapsed time that this thread has spent executing code in privileged mode."
	descWinProcCpu_total              = "Percentage of elapsed time that this process's threads have spent executing code in user or privileged mode."
	descWinProcCpu_user               = "Percentage of elapsed time that this process's threads have spent executing code in user mode."
	descWinProcElapsed_time           = "Elapsed time in seconds this process has been running."
	descWinProcHandle_count           = "Total number of handles the process has open across all threads."
	descWinProcIo_bytes_other         = "Rate at which the process is issuing bytes to I/O operations that do not involve data such as control operations."
	descWinProcIo_bytes_read          = "Rate at which the process is reading bytes from I/O operations."
	descWinProcIo_bytes_write         = "Rate at which the process is writing bytes to I/O operations."
	descWinProcIo_operations          = "Rate at which the process is issuing I/O operations that are neither a read or a write request."
	descWinProcIo_operations_read     = "Rate at which the process is issuing read I/O operations."
	descWinProcIo_operations_write    = "Rate at which the process is issuing write I/O operations."
	descWinProcMemPage_faults         = "Rate of page faults by the threads executing in this process."
	descWinProcMemPagefile_bytes      = "Current number of bytes this process has used in the paging file(s)."
	descWinProcMemPagefile_bytes_peak = "Maximum number of bytes this process has used in the paging file(s)."
	descWinProcMemPool_nonpaged_bytes = "Total number of bytes for objects that cannot be written to disk when they are not being used."
	descWinProcMemPool_paged_bytes    = "Total number of bytes for objects that can be written to disk when they are not being used."
	descWinProcMemVmBytes             = "Current size, in bytes, of the virtual address space that the process is using."
	descWinProcMemVmBytes_peak        = "Maximum number of bytes of virtual address space that the process has used at any one time."
	descWinProcMemWorking_set         = "Current number of bytes in the working set of this process at any point in time."
	descWinProcMemWorking_set_peak    = "Maximum number of bytes in the working set of this process at any point in time."
	descWinProcMemWorking_set_private = "Current number of bytes in the working set that are not shared with other processes."
	descWinProcPriority_base          = "Current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process base priority of the process."
	descWinProcPrivate_bytes          = "Current number of bytes this process has allocated that cannot be shared with other processes."
	descWinProcthread_count           = "Number of threads currently active in this process."
)

// Actually a CIM_StatisticalInformation.
type Win32_PerfRawData_PerfProc_Process struct {
	ElapsedTime             uint64
	Frequency_Object        uint64
	HandleCount             uint32
	IDProcess               uint32
	IOOtherBytesPersec      uint64
	IOOtherOperationsPersec uint64
	IOReadBytesPersec       uint64
	IOReadOperationsPersec  uint64
	IOWriteBytesPersec      uint64
	IOWriteOperationsPersec uint64
	Name                    string
	PageFaultsPersec        uint32
	PageFileBytes           uint64
	PageFileBytesPeak       uint64
	PercentPrivilegedTime   uint64
	PercentProcessorTime    uint64
	PercentUserTime         uint64
	PoolNonpagedBytes       uint32
	PoolPagedBytes          uint32
	PriorityBase            uint32
	PrivateBytes            uint64
	ThreadCount             uint32
	Timestamp_Object        uint64
	Timestamp_Sys100NS      uint64
	VirtualBytes            uint64
	VirtualBytesPeak        uint64
	WorkingSet              uint64
	WorkingSetPeak          uint64
	WorkingSetPrivate       uint64
}

// Actually a Win32_BaseServce.
type Win32_Service struct {
	Name      string
	ProcessId uint32
}

type WorkerProcess struct {
	AppPoolName string
	ProcessId   uint32
}
<|endoftext|>


<commit_before>package http

//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg http -path Transport,Internet,Headers,HTTP

import (
	"bytes"
	"context"
	"io"
	"net"
	"net/http"
	"strings"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/serial"
)

const (
	CRLF   = "\r\n"
	ENDING = CRLF + CRLF

	// max length of HTTP header. Safety precaution for DDoS attack.
	maxHeaderLength = 8192
)

var (
	ErrHeaderToLong = newError("Header too long.")
	writeCRLF       = serial.WriteString(CRLF)
)

type Reader interface {
	Read(io.Reader) (*buf.Buffer, error)
}

type Writer interface {
	Write(io.Writer) error
}

type NoOpReader struct{}

func (v *NoOpReader) Read(io.Reader) (*buf.Buffer, error) {
	return nil, nil
}

type NoOpWriter struct{}

func (v *NoOpWriter) Write(io.Writer) error {
	return nil
}

type HeaderReader struct {
}

func (*HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
	buffer := buf.New()
	totalBytes := 0
	endingDetected := false
	for totalBytes < maxHeaderLength {
		err := buffer.AppendSupplier(buf.ReadFrom(reader))
		if err != nil {
			buffer.Release()
			return nil, err
		}
		if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 {
			buffer.SliceFrom(n + len(ENDING))
			endingDetected = true
			break
		}
		if buffer.Len() >= len(ENDING) {
			totalBytes += buffer.Len() - len(ENDING)
			leftover := buffer.BytesFrom(-len(ENDING))
			buffer.Reset(func(b []byte) (int, error) {
				return copy(b, leftover), nil
			})
		}
	}
	if buffer.IsEmpty() {
		buffer.Release()
		return nil, nil
	}
	if !endingDetected {
		buffer.Release()
		return nil, ErrHeaderToLong
	}
	return buffer, nil
}

type HeaderWriter struct {
	header *buf.Buffer
}

func NewHeaderWriter(header *buf.Buffer) *HeaderWriter {
	return &HeaderWriter{
		header: header,
	}
}

func (w *HeaderWriter) Write(writer io.Writer) error {
	if w.header == nil {
		return nil
	}
	_, err := writer.Write(w.header.Bytes())
	w.header.Release()
	w.header = nil
	return err
}

type HttpConn struct {
	net.Conn

	readBuffer    *buf.Buffer
	oneTimeReader Reader
	oneTimeWriter Writer
	errorWriter   Writer
}

func NewHttpConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer) *HttpConn {
	return &HttpConn{
		Conn:          conn,
		oneTimeReader: reader,
		oneTimeWriter: writer,
		errorWriter:   errorWriter,
	}
}

func (c *HttpConn) Read(b []byte) (int, error) {
	if c.oneTimeReader != nil {
		buffer, err := c.oneTimeReader.Read(c.Conn)
		if err != nil {
			return 0, err
		}
		c.readBuffer = buffer
		c.oneTimeReader = nil
	}

	if !c.readBuffer.IsEmpty() {
		nBytes, _ := c.readBuffer.Read(b)
		if c.readBuffer.IsEmpty() {
			c.readBuffer.Release()
			c.readBuffer = nil
		}
		return nBytes, nil
	}

	return c.Conn.Read(b)
}

// Write implements io.Writer.
func (c *HttpConn) Write(b []byte) (int, error) {
	if c.oneTimeWriter != nil {
		err := c.oneTimeWriter.Write(c.Conn)
		c.oneTimeWriter = nil
		if err != nil {
			return 0, err
		}
	}

	return c.Conn.Write(b)
}

// Close implements net.Conn.Close().
func (c *HttpConn) Close() error {
	if c.oneTimeWriter != nil && c.errorWriter != nil {
		// Connection is being closed but header wasn't sent. This means the client request
		// is probably not valid. Sending back a server error header in this case.
		c.errorWriter.Write(c.Conn)
	}

	return c.Conn.Close()
}

func formResponseHeader(config *ResponseConfig) *HeaderWriter {
	header := buf.New()
	header.AppendSupplier(serial.WriteString(strings.Join([]string{config.GetFullVersion(), config.GetStatusValue().Code, config.GetStatusValue().Reason}, " ")))
	header.AppendSupplier(writeCRLF)

	headers := config.PickHeaders()
	for _, h := range headers {
		header.AppendSupplier(serial.WriteString(h))
		header.AppendSupplier(writeCRLF)
	}
	if !config.HasHeader("Date") {
		header.AppendSupplier(serial.WriteString("Date: "))
		header.AppendSupplier(serial.WriteString(time.Now().Format(http.TimeFormat)))
		header.AppendSupplier(writeCRLF)
	}
	header.AppendSupplier(writeCRLF)
	return &HeaderWriter{
		header: header,
	}
}

type HttpAuthenticator struct {
	config *Config
}

func (a HttpAuthenticator) GetClientWriter() *HeaderWriter {
	header := buf.New()
	config := a.config.Request
	header.AppendSupplier(serial.WriteString(strings.Join([]string{config.GetMethodValue(), config.PickUri(), config.GetFullVersion()}, " ")))
	header.AppendSupplier(writeCRLF)

	headers := config.PickHeaders()
	for _, h := range headers {
		header.AppendSupplier(serial.WriteString(h))
		header.AppendSupplier(writeCRLF)
	}
	header.AppendSupplier(writeCRLF)
	return &HeaderWriter{
		header: header,
	}
}

func (a HttpAuthenticator) GetServerWriter() *HeaderWriter {
	return formResponseHeader(a.config.Response)
}

func (a HttpAuthenticator) Client(conn net.Conn) net.Conn {
	if a.config.Request == nil && a.config.Response == nil {
		return conn
	}
	var reader Reader = new(NoOpReader)
	if a.config.Request != nil {
		reader = new(HeaderReader)
	}

	var writer Writer = new(NoOpWriter)
	if a.config.Response != nil {
		writer = a.GetClientWriter()
	}
	return NewHttpConn(conn, reader, writer, new(NoOpWriter))
}

func (a HttpAuthenticator) Server(conn net.Conn) net.Conn {
	if a.config.Request == nil && a.config.Response == nil {
		return conn
	}
	return NewHttpConn(conn, new(HeaderReader), a.GetServerWriter(), formResponseHeader(&ResponseConfig{
		Version: &Version{
			Value: "1.1",
		},
		Status: &Status{
			Code:   "500",
			Reason: "Internal Server Error",
		},
		Header: []*Header{
			{
				Name:  "Connection",
				Value: []string{"close"},
			},
			{
				Name:  "Cache-Control",
				Value: []string{"private"},
			},
			{
				Name:  "Content-Length",
				Value: []string{"0"},
			},
		},
	}))
}

func NewHttpAuthenticator(ctx context.Context, config *Config) (HttpAuthenticator, error) {
	return HttpAuthenticator{
		config: config,
	}, nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return NewHttpAuthenticator(ctx, config.(*Config))
	}))
}
<commit_msg>refactor<commit_after>package http

//go:generate go run $GOPATH/src/v2ray.com/core/tools/generrorgen/main.go -pkg http -path Transport,Internet,Headers,HTTP

import (
	"bytes"
	"context"
	"io"
	"net"
	"net/http"
	"strings"
	"time"

	"v2ray.com/core/common"
	"v2ray.com/core/common/buf"
	"v2ray.com/core/common/serial"
)

const (
	// CRLF is the line ending in HTTP header
	CRLF = "\r\n"

	// ENDING is the double line ending between HTTP header and body.
	ENDING = CRLF + CRLF

	// max length of HTTP header. Safety precaution for DDoS attack.
	maxHeaderLength = 8192
)

var (
	ErrHeaderToLong = newError("Header too long.")
	writeCRLF       = serial.WriteString(CRLF)
)

type Reader interface {
	Read(io.Reader) (*buf.Buffer, error)
}

type Writer interface {
	Write(io.Writer) error
}

type NoOpReader struct{}

func (NoOpReader) Read(io.Reader) (*buf.Buffer, error) {
	return nil, nil
}

type NoOpWriter struct{}

func (NoOpWriter) Write(io.Writer) error {
	return nil
}

type HeaderReader struct {
}

func (*HeaderReader) Read(reader io.Reader) (*buf.Buffer, error) {
	buffer := buf.New()
	totalBytes := 0
	endingDetected := false
	for totalBytes < maxHeaderLength {
		err := buffer.AppendSupplier(buf.ReadFrom(reader))
		if err != nil {
			buffer.Release()
			return nil, err
		}
		if n := bytes.Index(buffer.Bytes(), []byte(ENDING)); n != -1 {
			buffer.SliceFrom(n + len(ENDING))
			endingDetected = true
			break
		}
		if buffer.Len() >= len(ENDING) {
			totalBytes += buffer.Len() - len(ENDING)
			leftover := buffer.BytesFrom(-len(ENDING))
			buffer.Reset(func(b []byte) (int, error) {
				return copy(b, leftover), nil
			})
		}
	}
	if buffer.IsEmpty() {
		buffer.Release()
		return nil, nil
	}
	if !endingDetected {
		buffer.Release()
		return nil, ErrHeaderToLong
	}
	return buffer, nil
}

type HeaderWriter struct {
	header *buf.Buffer
}

func NewHeaderWriter(header *buf.Buffer) *HeaderWriter {
	return &HeaderWriter{
		header: header,
	}
}

func (w *HeaderWriter) Write(writer io.Writer) error {
	if w.header == nil {
		return nil
	}
	_, err := writer.Write(w.header.Bytes())
	w.header.Release()
	w.header = nil
	return err
}

type HttpConn struct {
	net.Conn

	readBuffer    *buf.Buffer
	oneTimeReader Reader
	oneTimeWriter Writer
	errorWriter   Writer
}

func NewHttpConn(conn net.Conn, reader Reader, writer Writer, errorWriter Writer) *HttpConn {
	return &HttpConn{
		Conn:          conn,
		oneTimeReader: reader,
		oneTimeWriter: writer,
		errorWriter:   errorWriter,
	}
}

func (c *HttpConn) Read(b []byte) (int, error) {
	if c.oneTimeReader != nil {
		buffer, err := c.oneTimeReader.Read(c.Conn)
		if err != nil {
			return 0, err
		}
		c.readBuffer = buffer
		c.oneTimeReader = nil
	}

	if !c.readBuffer.IsEmpty() {
		nBytes, _ := c.readBuffer.Read(b)
		if c.readBuffer.IsEmpty() {
			c.readBuffer.Release()
			c.readBuffer = nil
		}
		return nBytes, nil
	}

	return c.Conn.Read(b)
}

// Write implements io.Writer.
func (c *HttpConn) Write(b []byte) (int, error) {
	if c.oneTimeWriter != nil {
		err := c.oneTimeWriter.Write(c.Conn)
		c.oneTimeWriter = nil
		if err != nil {
			return 0, err
		}
	}

	return c.Conn.Write(b)
}

// Close implements net.Conn.Close().
func (c *HttpConn) Close() error {
	if c.oneTimeWriter != nil && c.errorWriter != nil {
		// Connection is being closed but header wasn't sent. This means the client request
		// is probably not valid. Sending back a server error header in this case.
		c.errorWriter.Write(c.Conn)
	}

	return c.Conn.Close()
}

func formResponseHeader(config *ResponseConfig) *HeaderWriter {
	header := buf.New()
	header.AppendSupplier(serial.WriteString(strings.Join([]string{config.GetFullVersion(), config.GetStatusValue().Code, config.GetStatusValue().Reason}, " ")))
	header.AppendSupplier(writeCRLF)

	headers := config.PickHeaders()
	for _, h := range headers {
		header.AppendSupplier(serial.WriteString(h))
		header.AppendSupplier(writeCRLF)
	}
	if !config.HasHeader("Date") {
		header.AppendSupplier(serial.WriteString("Date: "))
		header.AppendSupplier(serial.WriteString(time.Now().Format(http.TimeFormat)))
		header.AppendSupplier(writeCRLF)
	}
	header.AppendSupplier(writeCRLF)
	return &HeaderWriter{
		header: header,
	}
}

type HttpAuthenticator struct {
	config *Config
}

func (a HttpAuthenticator) GetClientWriter() *HeaderWriter {
	header := buf.New()
	config := a.config.Request
	header.AppendSupplier(serial.WriteString(strings.Join([]string{config.GetMethodValue(), config.PickUri(), config.GetFullVersion()}, " ")))
	header.AppendSupplier(writeCRLF)

	headers := config.PickHeaders()
	for _, h := range headers {
		header.AppendSupplier(serial.WriteString(h))
		header.AppendSupplier(writeCRLF)
	}
	header.AppendSupplier(writeCRLF)
	return &HeaderWriter{
		header: header,
	}
}

func (a HttpAuthenticator) GetServerWriter() *HeaderWriter {
	return formResponseHeader(a.config.Response)
}

func (a HttpAuthenticator) Client(conn net.Conn) net.Conn {
	if a.config.Request == nil && a.config.Response == nil {
		return conn
	}
	var reader Reader = NoOpReader{}
	if a.config.Request != nil {
		reader = new(HeaderReader)
	}

	var writer Writer = NoOpWriter{}
	if a.config.Response != nil {
		writer = a.GetClientWriter()
	}
	return NewHttpConn(conn, reader, writer, NoOpWriter{})
}

func (a HttpAuthenticator) Server(conn net.Conn) net.Conn {
	if a.config.Request == nil && a.config.Response == nil {
		return conn
	}
	return NewHttpConn(conn, new(HeaderReader), a.GetServerWriter(), formResponseHeader(&ResponseConfig{
		Version: &Version{
			Value: "1.1",
		},
		Status: &Status{
			Code:   "500",
			Reason: "Internal Server Error",
		},
		Header: []*Header{
			{
				Name:  "Connection",
				Value: []string{"close"},
			},
			{
				Name:  "Cache-Control",
				Value: []string{"private"},
			},
			{
				Name:  "Content-Length",
				Value: []string{"0"},
			},
		},
	}))
}

func NewHttpAuthenticator(ctx context.Context, config *Config) (HttpAuthenticator, error) {
	return HttpAuthenticator{
		config: config,
	}, nil
}

func init() {
	common.Must(common.RegisterConfig((*Config)(nil), func(ctx context.Context, config interface{}) (interface{}, error) {
		return NewHttpAuthenticator(ctx, config.(*Config))
	}))
}
<|endoftext|>


<commit_before>// Package handlers contains all api handlers.
package handlers

import (
	"fmt"
	"math/rand"
	"net/http"
)

// Api holds all routes.
type Api struct {
	mux *http.ServeMux
}

//
func (a *Api) New() http.Handler {
	m := http.NewServeMux()
	m.HandleFunc("/a", aecho)
	m.HandleFunc("/b", becho)
	a.mux = m
	return a
}

func (a *Api) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.mux.ServeHTTP(w, r)
}

func aecho(w http.ResponseWriter, r *http.Request) {
	n := rand.Intn(1000)
	fmt.Fprintf(w, "a) You asked to %s %s result: %d\n", r.Method, r.URL.Path, n)
}

func becho(w http.ResponseWriter, r *http.Request) {
	n := rand.Intn(1000)
	fmt.Fprintf(w, "b) You asked to %s %s result: %d\n", r.Method, r.URL.Path, n)
}
<commit_msg>rest/internal/handlers: clean api<commit_after>// Package handlers contains all api handlers.
package handlers

import (
	"fmt"
	"math/rand"
	"net/http"
)

// API holds all routes.
type API struct {
	mux *http.ServeMux
}

// Routes inits all api routes.
func (a *API) Routes() http.Handler {
	m := http.NewServeMux()
	m.HandleFunc("/a", aecho)
	m.HandleFunc("/b", becho)
	a.mux = m

	return a
}

// ServeHTTP implements http.Handler.
func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	a.mux.ServeHTTP(w, r)
}

func aecho(w http.ResponseWriter, r *http.Request) {
	n := rand.Intn(1000)
	fmt.Fprintf(w, "a) You asked to %s %s result: %d\n", r.Method, r.URL.Path, n)
}

func becho(w http.ResponseWriter, r *http.Request) {
	n := rand.Intn(1000)
	fmt.Fprintf(w, "b) You asked to %s %s result: %d\n", r.Method, r.URL.Path, n)
}
<|endoftext|>


<commit_before>// Copyright 2017 Xiaomi, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugins

import (
	"bytes"
	"encoding/json"
	"github.com/open-falcon/falcon-plus/common/model"
	"github.com/open-falcon/falcon-plus/modules/agent/g"
	"github.com/toolkits/file"
	"github.com/toolkits/sys"
	"log"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"
)

type PluginScheduler struct {
	Ticker *time.Ticker
	Plugin *Plugin
	Quit   chan struct{}
}

func NewPluginScheduler(p *Plugin) *PluginScheduler {
	scheduler := PluginScheduler{Plugin: p}
	scheduler.Ticker = time.NewTicker(time.Duration(p.Cycle) * time.Second)
	scheduler.Quit = make(chan struct{})
	return &scheduler
}

func (this *PluginScheduler) Schedule() {
	go func() {
		for {
			select {
			case <-this.Ticker.C:
				PluginRun(this.Plugin)
			case <-this.Quit:
				this.Ticker.Stop()
				return
			}
		}
	}()
}

func (this *PluginScheduler) Stop() {
	close(this.Quit)
}

func PluginRun(plugin *Plugin) {

	timeout := plugin.Cycle*1000 - 500
	fpath := filepath.Join(g.Config().Plugin.Dir, plugin.FilePath)

	if !file.IsExist(fpath) {
		log.Println("no such plugin:", fpath)
		return
	}

	debug := g.Config().Debug
	if debug {
		log.Println(fpath, "running...")
	}

	cmd := exec.Command(fpath)
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	cmd.Start()
	if debug {
		log.Println("plugin started:", fpath)
	}

	err, isTimeout := sys.CmdRunWithTimeout(cmd, time.Duration(timeout)*time.Millisecond)

	errStr := stderr.String()
	if errStr != "" {
		logFile := filepath.Join(g.Config().Plugin.LogDir, plugin.FilePath+".stderr.log")
		if _, err = file.WriteString(logFile, errStr); err != nil {
			log.Printf("[ERROR] write log to %s fail, error: %s\n", logFile, err)
		}
	}

	if isTimeout {
		// has be killed
		if err == nil && debug {
			log.Println("[INFO] timeout and kill process", fpath, "successfully")
		}

		if err != nil {
			log.Println("[ERROR] kill process", fpath, "occur error:", err)
		}

		return
	}

	if err != nil {
		log.Println("[ERROR] exec plugin", fpath, "fail. error:", err)
		return
	}

	// exec successfully
	data := stdout.Bytes()
	if len(data) == 0 {
		if debug {
			log.Println("[DEBUG] stdout of", fpath, "is blank")
		}
		return
	}

	var metrics []*model.MetricValue
	err = json.Unmarshal(data, &metrics)
	if err != nil {
		log.Printf("[ERROR] json.Unmarshal stdout of %s fail. error:%s stdout: \n%s\n", fpath, err, stdout.String())
		return
	}

	g.SendToTransfer(metrics)
}
<commit_msg>修复插件执行失败(无执行权限等)导致agent进程退出bug<commit_after>// Copyright 2017 Xiaomi, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package plugins

import (
	"bytes"
	"encoding/json"
	"log"
	"os/exec"
	"path/filepath"
	"syscall"
	"time"

	"github.com/open-falcon/falcon-plus/common/model"
	"github.com/open-falcon/falcon-plus/modules/agent/g"
	"github.com/toolkits/file"
	"github.com/toolkits/sys"
)

type PluginScheduler struct {
	Ticker *time.Ticker
	Plugin *Plugin
	Quit   chan struct{}
}

func NewPluginScheduler(p *Plugin) *PluginScheduler {
	scheduler := PluginScheduler{Plugin: p}
	scheduler.Ticker = time.NewTicker(time.Duration(p.Cycle) * time.Second)
	scheduler.Quit = make(chan struct{})
	return &scheduler
}

func (this *PluginScheduler) Schedule() {
	go func() {
		for {
			select {
			case <-this.Ticker.C:
				PluginRun(this.Plugin)
			case <-this.Quit:
				this.Ticker.Stop()
				return
			}
		}
	}()
}

func (this *PluginScheduler) Stop() {
	close(this.Quit)
}

func PluginRun(plugin *Plugin) {

	timeout := plugin.Cycle*1000 - 500
	fpath := filepath.Join(g.Config().Plugin.Dir, plugin.FilePath)

	if !file.IsExist(fpath) {
		log.Println("no such plugin:", fpath)
		return
	}

	debug := g.Config().Debug
	if debug {
		log.Println(fpath, "running...")
	}

	cmd := exec.Command(fpath)
	var stdout bytes.Buffer
	cmd.Stdout = &stdout
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	err := cmd.Start()
	if err != nil {
		log.Printf("[ERROR] plugin start fail, error: %s\n", err)
		return
	}
	if debug {
		log.Println("plugin started:", fpath)
	}

	err, isTimeout := sys.CmdRunWithTimeout(cmd, time.Duration(timeout)*time.Millisecond)

	errStr := stderr.String()
	if errStr != "" {
		logFile := filepath.Join(g.Config().Plugin.LogDir, plugin.FilePath+".stderr.log")
		if _, err = file.WriteString(logFile, errStr); err != nil {
			log.Printf("[ERROR] write log to %s fail, error: %s\n", logFile, err)
		}
	}

	if isTimeout {
		// has be killed
		if err == nil && debug {
			log.Println("[INFO] timeout and kill process", fpath, "successfully")
		}

		if err != nil {
			log.Println("[ERROR] kill process", fpath, "occur error:", err)
		}

		return
	}

	if err != nil {
		log.Println("[ERROR] exec plugin", fpath, "fail. error:", err)
		return
	}

	// exec successfully
	data := stdout.Bytes()
	if len(data) == 0 {
		if debug {
			log.Println("[DEBUG] stdout of", fpath, "is blank")
		}
		return
	}

	var metrics []*model.MetricValue
	err = json.Unmarshal(data, &metrics)
	if err != nil {
		log.Printf("[ERROR] json.Unmarshal stdout of %s fail. error:%s stdout: \n%s\n", fpath, err, stdout.String())
		return
	}

	g.SendToTransfer(metrics)
}
<|endoftext|>


<commit_before>package vault

import (
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
)

const (
	DenyCapability   = "deny"
	CreateCapability = "create"
	ReadCapability   = "read"
	UpdateCapability = "update"
	DeleteCapability = "delete"
	ListCapability   = "list"
	SudoCapability   = "sudo"
	RootCapability   = "root"

	// Backwards compatibility
	OldDenyPathPolicy  = "deny"
	OldReadPathPolicy  = "read"
	OldWritePathPolicy = "write"
	OldSudoPathPolicy  = "sudo"
)

const (
	DenyCapabilityInt uint32 = 1 << iota
	CreateCapabilityInt
	ReadCapabilityInt
	UpdateCapabilityInt
	DeleteCapabilityInt
	ListCapabilityInt
	SudoCapabilityInt
)

var (
	cap2Int = map[string]uint32{
		DenyCapability:   DenyCapabilityInt,
		CreateCapability: CreateCapabilityInt,
		ReadCapability:   ReadCapabilityInt,
		UpdateCapability: UpdateCapabilityInt,
		DeleteCapability: DeleteCapabilityInt,
		ListCapability:   ListCapabilityInt,
		SudoCapability:   SudoCapabilityInt,
	}
)

// Policy is used to represent the policy specified by
// an ACL configuration.
type Policy struct {
	Name  string              `hcl:"name"`
	Paths []*PathCapabilities `hcl:"-"`
	Raw   string
}

// PathCapabilities represents a policy for a path in the namespace.
type PathCapabilities struct {
	Prefix             string
	Policy             string
	Capabilities       []string
	CapabilitiesBitmap uint32 `hcl:"-"`
	Glob               bool
}

// Parse is used to parse the specified ACL rules into an
// intermediary set of policies, before being compiled into
// the ACL
func Parse(rules string) (*Policy, error) {
	// Parse the rules
	root, err := hcl.Parse(rules)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	// Top-level item should be the object list
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("Failed to parse policy: does not contain a root object")
	}

	// Check for invalid top-level keys
	valid := []string{
		"name",
		"path",
	}
	if err := checkHCLKeys(list, valid); err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	// Create the initial policy and store the raw text of the rules
	p := &Policy{Raw: rules}
	if err := hcl.DecodeObject(&p, list); err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	if o := list.Filter("path"); len(o.Items) > 0 {
		if err := parsePaths(p, o); err != nil {
			return nil, fmt.Errorf("Failed to parse policy: %s", err)
		}
	}

	return p, nil
}

func parsePaths(result *Policy, list *ast.ObjectList) error {
	paths := make([]*PathCapabilities, 0, len(list.Items))
	for _, item := range list.Items {
		key := "path"
		if len(item.Keys) > 0 {
			key = item.Keys[0].Token.Value().(string)
		}

		valid := []string{
			"policy",
			"capabilities",
		}
		if err := checkHCLKeys(item.Val, valid); err != nil {
			return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
		}

		var pc PathCapabilities
		pc.Prefix = key
		if err := hcl.DecodeObject(&pc, item.Val); err != nil {
			return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
		}

		// Strip a leading '/' as paths in Vault start after the / in the API path
		if len(pc.Prefix) > 0 && pc.Prefix[0] == '/' {
			pc.Prefix = pc.Prefix[1:]
		}

		// Strip the glob character if found
		if strings.HasSuffix(pc.Prefix, "*") {
			pc.Prefix = strings.TrimSuffix(pc.Prefix, "*")
			pc.Glob = true
		}

		// Map old-style policies into capabilities
		if len(pc.Policy) > 0 {
			switch pc.Policy {
			case OldDenyPathPolicy:
				pc.Capabilities = []string{DenyCapability}
			case OldReadPathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{ReadCapability, ListCapability}...)
			case OldWritePathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability}...)
			case OldSudoPathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability}...)
			default:
				return fmt.Errorf("path %q: invalid policy '%s'", key, pc.Policy)
			}
		}

		// Initialize the map
		pc.CapabilitiesBitmap = 0
		for _, cap := range pc.Capabilities {
			switch cap {
			// If it's deny, don't include any other capability
			case DenyCapability:
				pc.Capabilities = []string{DenyCapability}
				pc.CapabilitiesBitmap = DenyCapabilityInt
				goto PathFinished
			case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability:
				pc.CapabilitiesBitmap |= cap2Int[cap]
			default:
				return fmt.Errorf("path %q: invalid capability '%s'", key, cap)
			}
		}

	PathFinished:

		paths = append(paths, &pc)
	}

	result.Paths = paths
	return nil
}

func checkHCLKeys(node ast.Node, valid []string) error {
	var list *ast.ObjectList
	switch n := node.(type) {
	case *ast.ObjectList:
		list = n
	case *ast.ObjectType:
		list = n.List
	default:
		return fmt.Errorf("cannot check HCL keys of type %T", n)
	}

	validMap := make(map[string]struct{}, len(valid))
	for _, v := range valid {
		validMap[v] = struct{}{}
	}

	var result error
	for _, item := range list.Items {
		key := item.Keys[0].Token.Value().(string)
		if _, ok := validMap[key]; !ok {
			result = multierror.Append(result, fmt.Errorf(
				"invalid key '%s' on line %d", key, item.Assign.Line))
		}
	}

	return result
}
<commit_msg>Preserve pointer<commit_after>package vault

import (
	"fmt"
	"strings"

	"github.com/hashicorp/go-multierror"
	"github.com/hashicorp/hcl"
	"github.com/hashicorp/hcl/hcl/ast"
)

const (
	DenyCapability   = "deny"
	CreateCapability = "create"
	ReadCapability   = "read"
	UpdateCapability = "update"
	DeleteCapability = "delete"
	ListCapability   = "list"
	SudoCapability   = "sudo"
	RootCapability   = "root"

	// Backwards compatibility
	OldDenyPathPolicy  = "deny"
	OldReadPathPolicy  = "read"
	OldWritePathPolicy = "write"
	OldSudoPathPolicy  = "sudo"
)

const (
	DenyCapabilityInt uint32 = 1 << iota
	CreateCapabilityInt
	ReadCapabilityInt
	UpdateCapabilityInt
	DeleteCapabilityInt
	ListCapabilityInt
	SudoCapabilityInt
)

var (
	cap2Int = map[string]uint32{
		DenyCapability:   DenyCapabilityInt,
		CreateCapability: CreateCapabilityInt,
		ReadCapability:   ReadCapabilityInt,
		UpdateCapability: UpdateCapabilityInt,
		DeleteCapability: DeleteCapabilityInt,
		ListCapability:   ListCapabilityInt,
		SudoCapability:   SudoCapabilityInt,
	}
)

// Policy is used to represent the policy specified by
// an ACL configuration.
type Policy struct {
	Name  string              `hcl:"name"`
	Paths []*PathCapabilities `hcl:"-"`
	Raw   string
}

// PathCapabilities represents a policy for a path in the namespace.
type PathCapabilities struct {
	Prefix             string
	Policy             string
	Capabilities       []string
	CapabilitiesBitmap uint32 `hcl:"-"`
	Glob               bool
}

// Parse is used to parse the specified ACL rules into an
// intermediary set of policies, before being compiled into
// the ACL
func Parse(rules string) (*Policy, error) {
	// Parse the rules
	root, err := hcl.Parse(rules)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	// Top-level item should be the object list
	list, ok := root.Node.(*ast.ObjectList)
	if !ok {
		return nil, fmt.Errorf("Failed to parse policy: does not contain a root object")
	}

	// Check for invalid top-level keys
	valid := []string{
		"name",
		"path",
	}
	if err := checkHCLKeys(list, valid); err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	// Create the initial policy and store the raw text of the rules
	var p Policy
	p.Raw = rules
	if err := hcl.DecodeObject(&p, list); err != nil {
		return nil, fmt.Errorf("Failed to parse policy: %s", err)
	}

	if o := list.Filter("path"); len(o.Items) > 0 {
		if err := parsePaths(&p, o); err != nil {
			return nil, fmt.Errorf("Failed to parse policy: %s", err)
		}
	}

	return &p, nil
}

func parsePaths(result *Policy, list *ast.ObjectList) error {
	paths := make([]*PathCapabilities, 0, len(list.Items))
	for _, item := range list.Items {
		key := "path"
		if len(item.Keys) > 0 {
			key = item.Keys[0].Token.Value().(string)
		}

		valid := []string{
			"policy",
			"capabilities",
		}
		if err := checkHCLKeys(item.Val, valid); err != nil {
			return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
		}

		var pc PathCapabilities
		pc.Prefix = key
		if err := hcl.DecodeObject(&pc, item.Val); err != nil {
			return multierror.Prefix(err, fmt.Sprintf("path %q:", key))
		}

		// Strip a leading '/' as paths in Vault start after the / in the API path
		if len(pc.Prefix) > 0 && pc.Prefix[0] == '/' {
			pc.Prefix = pc.Prefix[1:]
		}

		// Strip the glob character if found
		if strings.HasSuffix(pc.Prefix, "*") {
			pc.Prefix = strings.TrimSuffix(pc.Prefix, "*")
			pc.Glob = true
		}

		// Map old-style policies into capabilities
		if len(pc.Policy) > 0 {
			switch pc.Policy {
			case OldDenyPathPolicy:
				pc.Capabilities = []string{DenyCapability}
			case OldReadPathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{ReadCapability, ListCapability}...)
			case OldWritePathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability}...)
			case OldSudoPathPolicy:
				pc.Capabilities = append(pc.Capabilities, []string{CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability}...)
			default:
				return fmt.Errorf("path %q: invalid policy '%s'", key, pc.Policy)
			}
		}

		// Initialize the map
		pc.CapabilitiesBitmap = 0
		for _, cap := range pc.Capabilities {
			switch cap {
			// If it's deny, don't include any other capability
			case DenyCapability:
				pc.Capabilities = []string{DenyCapability}
				pc.CapabilitiesBitmap = DenyCapabilityInt
				goto PathFinished
			case CreateCapability, ReadCapability, UpdateCapability, DeleteCapability, ListCapability, SudoCapability:
				pc.CapabilitiesBitmap |= cap2Int[cap]
			default:
				return fmt.Errorf("path %q: invalid capability '%s'", key, cap)
			}
		}

	PathFinished:

		paths = append(paths, &pc)
	}

	result.Paths = paths
	return nil
}

func checkHCLKeys(node ast.Node, valid []string) error {
	var list *ast.ObjectList
	switch n := node.(type) {
	case *ast.ObjectList:
		list = n
	case *ast.ObjectType:
		list = n.List
	default:
		return fmt.Errorf("cannot check HCL keys of type %T", n)
	}

	validMap := make(map[string]struct{}, len(valid))
	for _, v := range valid {
		validMap[v] = struct{}{}
	}

	var result error
	for _, item := range list.Items {
		key := item.Keys[0].Token.Value().(string)
		if _, ok := validMap[key]; !ok {
			result = multierror.Append(result, fmt.Errorf(
				"invalid key '%s' on line %d", key, item.Assign.Line))
		}
	}

	return result
}
<|endoftext|>


<commit_before>// Package particle provides options for generating renderable
// particle sources.
package particle

import (
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/event"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/render"
	"image"
	"image/color"
	"image/draw"
	"math"
	"time"
)

var (
	Square = func(x, y, size int) bool {
		return true
	}
	Diamond = func(x, y, size int) bool {
		radius := size / 2
		return math.Abs(float64(x-radius))+math.Abs(float64(y-radius)) < float64(radius)
	}
)

// ParticleGenerator represents the various options
// one needs to or may provide in order to generate a
// ParticleSource.
// Modeled after Parcycle
type ParticleGenerator struct {
	// This float is currently forced to an integer
	// at new particle rotation. This should be changed
	// to something along the lines of 'new per 30 frames',
	// or allow low fractional values to be meaningful,
	// so that more fine-tuned particle generation speeds are possible.
	NewPerFrame, NewPerFrameRand float64
	X, Y                         float64
	// The number of frames each particle should persist
	// before being removed.
	LifeSpan, LifeSpanRand float64
	// 0 - between quadrant 1 and 4
	// 90 - between quadrant 2 and 1
	Angle, AngleRand float64
	Speed, SpeedRand float64
	SpreadX, SpreadY float64
	// Duration in milliseconds for the particle source.
	// After this many milliseconds have passed, it will
	// stop sending out new particles. Old particles will
	// not be removed until their individual lifespans run
	// out.
	// A duration of -1 represents never stopping.
	Duration int
	// Rotational acceleration, to change angle over time
	Rotation, RotationRand float64
	// Gravity X and Gravity Y represent particle acceleration per frame.
	GravityX, GravityY         float64
	StartColor, StartColorRand color.Color
	EndColor, EndColorRand     color.Color
	// The size, in pixel radius, of spawned particles
	Size, SizeRand int
	//
	// Some sort of particle type, for rendering triangles or squares or circles...
	Shape ShapeFunction
}

// A ParticleSource is used to store and control a set of particles.
type ParticleSource struct {
	render.Layered
	Generator     ParticleGenerator
	particles     []Particle
	rotateBinding event.Binding
	cID           event.CID
}

type ShapeFunction func(x, y, size int) bool

// A particle is a colored pixel at a given position, moving in a certain direction.
// After a while, it will dissipate.
type Particle struct {
	x, y       float64
	velX, velY float64
	startColor color.Color
	endColor   color.Color
	life       float64
	totalLife  float64
	size       int
}

func (ps *ParticleSource) Init() event.CID {
	return plastic.NextID(ps)
}

// Generate takes a generator and converts it into a source,
// drawing particles and binding functions for particle generation
// and rotation.
func (pg *ParticleGenerator) Generate(layer int) *ParticleSource {

	// Convert rotation from degrees to radians
	pg.Rotation = pg.Rotation / 180 * math.Pi
	pg.RotationRand = pg.Rotation / 180 * math.Pi

	// Make a source
	ps := ParticleSource{
		Generator: *pg,
		particles: make([]Particle, 0),
	}

	// Bind things to that source:
	cID := ps.Init()
	binding, _ := cID.Bind(rotateParticles, "EnterFrame")
	ps.rotateBinding = binding
	ps.cID = cID
	render.Draw(&ps, layer)
	if pg.Duration != -1 {
		go func(ps_p *ParticleSource, duration int) {
			select {
			case <-time.After(time.Duration(duration) * time.Millisecond):
				ps_p.Stop()
			}
		}(&ps, pg.Duration)
	}
	return &ps
}

func (ps *ParticleSource) Draw(buff draw.Image) {
	for _, p := range ps.particles {

		r, g, b, a := p.startColor.RGBA()
		r2, g2, b2, a2 := p.endColor.RGBA()
		progress := p.life / p.totalLife
		c := color.RGBA64{
			uint16OnScale(r, r2, progress),
			uint16OnScale(g, g2, progress),
			uint16OnScale(b, b2, progress),
			uint16OnScale(a, a2, progress),
		}

		img := image.NewRGBA64(image.Rect(0, 0, p.size, p.size))

		for i := 0; i < p.size; i++ {
			for j := 0; j < p.size; j++ {
				if ps.Generator.Shape(i, j, p.size) {
					img.SetRGBA64(i, j, c)
				}
			}
		}

		halfSize := float64(p.size / 2)

		render.ShinyDraw(buff, img, int(p.x-halfSize), int(p.y-halfSize))
	}
}

// rotateParticles updates particles over time as long
// as a ParticleSource is active.
func rotateParticles(id int, nothing interface{}) error {

	ps := plastic.GetEntity(id).(*ParticleSource)
	pg := ps.Generator

	newParticles := make([]Particle, 0)

	for _, p := range ps.particles {

		// Ignore dead particles
		if p.life > 0 {

			// Move towards doom
			p.life--

			// Be dragged down by the weight of the soul
			p.velX += pg.GravityX
			p.velY += pg.GravityY

			// Apply rotational acceleration
			if pg.Rotation != 0 && pg.RotationRand != 0 {
				magnitude := math.Abs(p.velX) + math.Abs(p.velY)
				angle := math.Atan2(p.velX, p.velY)
				angle += pg.Rotation + floatFromSpread(pg.RotationRand)
				p.velX = math.Sin(angle)
				p.velY = math.Cos(angle)
				magnitude = magnitude / (math.Abs(p.velX) + math.Abs(p.velY))
				p.velX = p.velX * magnitude
				p.velY = p.velY * magnitude
			}

			p.x += p.velX
			p.y += p.velY

			newParticles = append(newParticles, p)
		}
	}

	// Regularly create particles (up until max particles)
	newParticleRand := roundFloat(floatFromSpread(pg.NewPerFrameRand))
	newParticleCount := int(pg.NewPerFrame) + newParticleRand
	for i := 0; i < newParticleCount; i++ {

		angle := (pg.Angle + floatFromSpread(pg.AngleRand)) * math.Pi / 180.0
		speed := pg.Speed + floatFromSpread(pg.SpeedRand)
		startLife := pg.LifeSpan + floatFromSpread(pg.LifeSpanRand)

		newParticles = append(newParticles, Particle{
			x:          pg.X + floatFromSpread(pg.SpreadX),
			y:          pg.Y + floatFromSpread(pg.SpreadY),
			velX:       speed * math.Cos(angle) * -1,
			velY:       speed * math.Sin(angle) * -1,
			startColor: randColor(pg.StartColor, pg.StartColorRand),
			endColor:   randColor(pg.EndColor, pg.EndColorRand),
			life:       startLife,
			totalLife:  startLife,
			size:       pg.Size + intFromSpread(pg.SizeRand),
		})
	}

	ps.particles = newParticles

	return nil
}

// clearParticles is used after a ParticleSource has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id int, nothing interface{}) error {

	ps := plastic.GetEntity(id).(*ParticleSource)
	pg := ps.Generator

	if len(ps.particles) > 0 {
		newParticles := make([]Particle, 0)
		for _, p := range ps.particles {

			// Ignore dead particles
			if p.life > 0 {

				p.life--

				p.velX += pg.GravityX
				p.velY += pg.GravityY

				// Apply rotational acceleration
				if pg.Rotation != 0 && pg.RotationRand != 0 {
					magnitude := math.Abs(p.velX) + math.Abs(p.velY)
					angle := math.Atan2(p.velX, p.velY)
					angle += pg.Rotation + floatFromSpread(pg.RotationRand)
					p.velX = math.Sin(angle)
					p.velY = math.Cos(angle)
					magnitude = magnitude / (math.Abs(p.velX) + math.Abs(p.velY))
					p.velX = p.velX * magnitude
					p.velY = p.velY * magnitude
				}

				p.x += p.velX
				p.y += p.velY

				newParticles = append(newParticles, p)
			}
		}
		ps.particles = newParticles
	} else {
		ps.UnDraw()
		ps.rotateBinding.Unbind()
	}
	return nil
}

// Stop manually stops a ParticleSource, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *ParticleSource) Stop() {
	ps.rotateBinding.Unbind()
	ps.rotateBinding, _ = ps.cID.Bind(clearParticles, "EnterFrame")
}

// A particle source has no concept of an individual
// rgba buffer, and so it returns nothing when its
// rgba buffer is queried. This may change.
func (ps *ParticleSource) GetRGBA() *image.RGBA {
	return nil
}

func (ps *ParticleSource) ShiftX(x float64) {
	ps.Generator.X += x
}

func (ps *ParticleSource) ShiftY(y float64) {
	ps.Generator.Y += y
}

func (ps *ParticleSource) GetX() float64 {
	return ps.Generator.X
}

func (ps *ParticleSource) GetY() float64 {
	return ps.Generator.Y
}
func (ps *ParticleSource) SetPos(x, y float64) {
	ps.Generator.X = x
	ps.Generator.Y = y
}

// Pausing a ParticleSource just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *ParticleSource) Pause() {
	ps.rotateBinding.Unbind()
}

// Unpausing a ParticleSource rebinds it's rotate function.
func (ps *ParticleSource) UnPause() {
	binding, _ := ps.cID.Bind(rotateParticles, "EnterFrame")
	ps.rotateBinding = binding
}
<commit_msg>Finished basic AI and added the fake death pill<commit_after>// Package particle provides options for generating renderable
// particle sources.
package particle

import (
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/event"
	"bitbucket.org/oakmoundstudio/plasticpiston/plastic/render"
	"image"
	"image/color"
	"image/draw"
	"math"
	"time"
)

var (
	Square = func(x, y, size int) bool {
		return true
	}
	Diamond = func(x, y, size int) bool {
		radius := size / 2
		return math.Abs(float64(x-radius))+math.Abs(float64(y-radius)) < float64(radius)
	}
)

// ParticleGenerator represents the various options
// one needs to or may provide in order to generate a
// ParticleSource.
// Modeled after Parcycle
type ParticleGenerator struct {
	// This float is currently forced to an integer
	// at new particle rotation. This should be changed
	// to something along the lines of 'new per 30 frames',
	// or allow low fractional values to be meaningful,
	// so that more fine-tuned particle generation speeds are possible.
	NewPerFrame, NewPerFrameRand float64
	X, Y                         float64
	// The number of frames each particle should persist
	// before being removed.
	LifeSpan, LifeSpanRand float64
	// 0 - between quadrant 1 and 4
	// 90 - between quadrant 2 and 1
	Angle, AngleRand float64
	Speed, SpeedRand float64
	SpreadX, SpreadY float64
	// Duration in milliseconds for the particle source.
	// After this many milliseconds have passed, it will
	// stop sending out new particles. Old particles will
	// not be removed until their individual lifespans run
	// out.
	// A duration of -1 represents never stopping.
	Duration int
	// Rotational acceleration, to change angle over time
	Rotation, RotationRand float64
	// Gravity X and Gravity Y represent particle acceleration per frame.
	GravityX, GravityY         float64
	StartColor, StartColorRand color.Color
	EndColor, EndColorRand     color.Color
	// The size, in pixel radius, of spawned particles
	Size, SizeRand int
	//
	// Some sort of particle type, for rendering triangles or squares or circles...
	Shape ShapeFunction
}

// A ParticleSource is used to store and control a set of particles.
type ParticleSource struct {
	render.Layered
	Generator     ParticleGenerator
	particles     []Particle
	rotateBinding event.Binding
	clearBinding  event.Binding
	cID           event.CID
}

type ShapeFunction func(x, y, size int) bool

// A particle is a colored pixel at a given position, moving in a certain direction.
// After a while, it will dissipate.
type Particle struct {
	x, y       float64
	velX, velY float64
	startColor color.Color
	endColor   color.Color
	life       float64
	totalLife  float64
	size       int
}

func (ps *ParticleSource) Init() event.CID {
	return plastic.NextID(ps)
}

// Generate takes a generator and converts it into a source,
// drawing particles and binding functions for particle generation
// and rotation.
func (pg *ParticleGenerator) Generate(layer int) *ParticleSource {

	// Convert rotation from degrees to radians
	pg.Rotation = pg.Rotation / 180 * math.Pi
	pg.RotationRand = pg.Rotation / 180 * math.Pi

	// Make a source
	ps := ParticleSource{
		Generator: *pg,
		particles: make([]Particle, 0),
	}

	// Bind things to that source:
	cID := ps.Init()
	binding, _ := cID.Bind(rotateParticles, "EnterFrame")
	ps.rotateBinding = binding
	ps.cID = cID
	render.Draw(&ps, layer)
	if pg.Duration != -1 {
		go func(ps_p *ParticleSource, duration int) {
			select {
			case <-time.After(time.Duration(duration) * time.Millisecond):
				ps_p.Stop()
			}
		}(&ps, pg.Duration)
	}
	return &ps
}

func (ps *ParticleSource) Draw(buff draw.Image) {
	for _, p := range ps.particles {

		r, g, b, a := p.startColor.RGBA()
		r2, g2, b2, a2 := p.endColor.RGBA()
		progress := p.life / p.totalLife
		c := color.RGBA64{
			uint16OnScale(r, r2, progress),
			uint16OnScale(g, g2, progress),
			uint16OnScale(b, b2, progress),
			uint16OnScale(a, a2, progress),
		}

		img := image.NewRGBA64(image.Rect(0, 0, p.size, p.size))

		for i := 0; i < p.size; i++ {
			for j := 0; j < p.size; j++ {
				if ps.Generator.Shape(i, j, p.size) {
					img.SetRGBA64(i, j, c)
				}
			}
		}

		halfSize := float64(p.size / 2)

		render.ShinyDraw(buff, img, int(p.x-halfSize), int(p.y-halfSize))
	}
}

// rotateParticles updates particles over time as long
// as a ParticleSource is active.
func rotateParticles(id int, nothing interface{}) error {

	ps := plastic.GetEntity(id).(*ParticleSource)
	pg := ps.Generator

	newParticles := make([]Particle, 0)

	for _, p := range ps.particles {

		// Ignore dead particles
		if p.life > 0 {

			// Move towards doom
			p.life--

			// Be dragged down by the weight of the soul
			p.velX += pg.GravityX
			p.velY += pg.GravityY

			// Apply rotational acceleration
			if pg.Rotation != 0 && pg.RotationRand != 0 {
				magnitude := math.Abs(p.velX) + math.Abs(p.velY)
				angle := math.Atan2(p.velX, p.velY)
				angle += pg.Rotation + floatFromSpread(pg.RotationRand)
				p.velX = math.Sin(angle)
				p.velY = math.Cos(angle)
				magnitude = magnitude / (math.Abs(p.velX) + math.Abs(p.velY))
				p.velX = p.velX * magnitude
				p.velY = p.velY * magnitude
			}

			p.x += p.velX
			p.y += p.velY

			newParticles = append(newParticles, p)
		}
	}

	// Regularly create particles (up until max particles)
	newParticleRand := roundFloat(floatFromSpread(pg.NewPerFrameRand))
	newParticleCount := int(pg.NewPerFrame) + newParticleRand
	for i := 0; i < newParticleCount; i++ {

		angle := (pg.Angle + floatFromSpread(pg.AngleRand)) * math.Pi / 180.0
		speed := pg.Speed + floatFromSpread(pg.SpeedRand)
		startLife := pg.LifeSpan + floatFromSpread(pg.LifeSpanRand)

		newParticles = append(newParticles, Particle{
			x:          pg.X + floatFromSpread(pg.SpreadX),
			y:          pg.Y + floatFromSpread(pg.SpreadY),
			velX:       speed * math.Cos(angle) * -1,
			velY:       speed * math.Sin(angle) * -1,
			startColor: randColor(pg.StartColor, pg.StartColorRand),
			endColor:   randColor(pg.EndColor, pg.EndColorRand),
			life:       startLife,
			totalLife:  startLife,
			size:       pg.Size + intFromSpread(pg.SizeRand),
		})
	}

	ps.particles = newParticles

	return nil
}

// clearParticles is used after a ParticleSource has been stopped
// to continue moving old particles for as long as they exist.
func clearParticles(id int, nothing interface{}) error {

	ps := plastic.GetEntity(id).(*ParticleSource)
	pg := ps.Generator

	if len(ps.particles) > 0 {
		newParticles := make([]Particle, 0)
		for _, p := range ps.particles {

			// Ignore dead particles
			if p.life > 0 {

				p.life--

				p.velX += pg.GravityX
				p.velY += pg.GravityY

				// Apply rotational acceleration
				if pg.Rotation != 0 && pg.RotationRand != 0 {
					magnitude := math.Abs(p.velX) + math.Abs(p.velY)
					angle := math.Atan2(p.velX, p.velY)
					angle += pg.Rotation + floatFromSpread(pg.RotationRand)
					p.velX = math.Sin(angle)
					p.velY = math.Cos(angle)
					magnitude = magnitude / (math.Abs(p.velX) + math.Abs(p.velY))
					p.velX = p.velX * magnitude
					p.velY = p.velY * magnitude
				}

				p.x += p.velX
				p.y += p.velY

				newParticles = append(newParticles, p)
			}
		}
		ps.particles = newParticles
	} else {
		ps.UnDraw()
		ps.rotateBinding.Unbind()
	}
	return nil
}

func clearAtExit(id int, nothing interface{}) error {
	ps := plastic.GetEntity(id).(*ParticleSource)
	ps.clearBinding.Unbind()
	ps.rotateBinding.Unbind()
	ps.rotateBinding, _ = ps.cID.Bind(clearParticles, "EnterFrame")
	return nil
}

// Stop manually stops a ParticleSource, if its duration is infinite
// or if it should be stopped before expriring naturally.
func (ps *ParticleSource) Stop() {
	ps.clearBinding, _ = ps.cID.Bind(clearAtExit, "ExitFrame")
}

// A particle source has no concept of an individual
// rgba buffer, and so it returns nothing when its
// rgba buffer is queried. This may change.
func (ps *ParticleSource) GetRGBA() *image.RGBA {
	return nil
}

func (ps *ParticleSource) ShiftX(x float64) {
	ps.Generator.X += x
}

func (ps *ParticleSource) ShiftY(y float64) {
	ps.Generator.Y += y
}

func (ps *ParticleSource) GetX() float64 {
	return ps.Generator.X
}

func (ps *ParticleSource) GetY() float64 {
	return ps.Generator.Y
}
func (ps *ParticleSource) SetPos(x, y float64) {
	ps.Generator.X = x
	ps.Generator.Y = y
}

// Pausing a ParticleSource just stops the repetition
// of its rotation function, which moves, destroys,
// ages and generates particles. Existing particles will
// stay in place.
func (ps *ParticleSource) Pause() {
	ps.rotateBinding.Unbind()
}

// Unpausing a ParticleSource rebinds it's rotate function.
func (ps *ParticleSource) UnPause() {
	binding, _ := ps.cID.Bind(rotateParticles, "EnterFrame")
	ps.rotateBinding = binding
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	log "github.com/Sirupsen/logrus"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	relRoot    = flag.String("path", ".", "Document root")
	listenPort = flag.Int("port", 8080, "Port")
	listenHost = flag.String("host", "0.0.0.0", "Host")
	logFormat  = flag.String("log-format", "text", "Log format: text, json")
	quiet      = flag.Bool("quiet", false, "No banner on startup")
)

const Version = 2

func withLogging(l *log.Logger, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		startTime := time.Now()
		h.ServeHTTP(w, r)
		l.WithFields(log.Fields{
			"method":   r.Method,
			"path":     r.URL.Path,
			"duration": time.Since(startTime),
		}).Info()
	}
}

func main() {
	flag.Parse()

	logger := &log.Logger{
		Out:   os.Stderr,
		Level: log.InfoLevel,
	}
	switch *logFormat {
	case "json":
		logger.Formatter = new(log.JSONFormatter)
	default:
		logger.Formatter = new(log.TextFormatter)
	}

	absRoot, err := filepath.Abs(*relRoot)
	if err != nil {
		logger.Fatalf("%v", err)
	}

	if _, err = os.Stat(absRoot); err != nil {
		logger.Fatalf("%v", err)
	}

	if !*quiet {
		logger.WithField("type", "banner").Infof("Safron version %d", Version)
		logger.WithField("type", "banner").Infof("Listening to http://%s:%d", *listenHost, *listenPort)
		logger.WithField("type", "banner").Infof("Serving %s", absRoot)
	}

	listen := *listenHost + ":" + strconv.Itoa(*listenPort)
	handler := withLogging(logger, http.FileServer(http.Dir(absRoot)))

	if err = http.ListenAndServe(listen, handler); err != nil {
		logger.Fatalf("%v", err)
	}
}
<commit_msg>safron: fix canonical import path for logrus<commit_after>package main

import (
	"flag"
	log "github.com/sirupsen/logrus"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"time"
)

var (
	relRoot    = flag.String("path", ".", "Document root")
	listenPort = flag.Int("port", 8080, "Port")
	listenHost = flag.String("host", "0.0.0.0", "Host")
	logFormat  = flag.String("log-format", "text", "Log format: text, json")
	quiet      = flag.Bool("quiet", false, "No banner on startup")
)

const Version = 2

func withLogging(l *log.Logger, h http.Handler) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		startTime := time.Now()
		h.ServeHTTP(w, r)
		l.WithFields(log.Fields{
			"method":   r.Method,
			"path":     r.URL.Path,
			"duration": time.Since(startTime),
		}).Info()
	}
}

func main() {
	flag.Parse()

	logger := &log.Logger{
		Out:   os.Stderr,
		Level: log.InfoLevel,
	}
	switch *logFormat {
	case "json":
		logger.Formatter = new(log.JSONFormatter)
	default:
		logger.Formatter = new(log.TextFormatter)
	}

	absRoot, err := filepath.Abs(*relRoot)
	if err != nil {
		logger.Fatalf("%v", err)
	}

	if _, err = os.Stat(absRoot); err != nil {
		logger.Fatalf("%v", err)
	}

	if !*quiet {
		logger.WithField("type", "banner").Infof("Safron version %d", Version)
		logger.WithField("type", "banner").Infof("Listening to http://%s:%d", *listenHost, *listenPort)
		logger.WithField("type", "banner").Infof("Serving %s", absRoot)
	}

	listen := *listenHost + ":" + strconv.Itoa(*listenPort)
	handler := withLogging(logger, http.FileServer(http.Dir(absRoot)))

	if err = http.ListenAndServe(listen, handler); err != nil {
		logger.Fatalf("%v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/nsf/termbox-go"
	"log"
	"math/rand"
	"time"
)

const (
	ColorWall  = termbox.ColorRed
	ColorEmpty = termbox.ColorBlack
	ColorSnake = termbox.ColorGreen
	ColorFood  = termbox.ColorYellow
)

type Cell struct {
	X     int
	Y     int
	Clear bool
	Color termbox.Attribute
}

type Segment struct {
	X   int
	Y   int
	Dir int
}

func gameOver(snake []Segment, board [][]Cell) {
	for i := range snake {
		termbox.SetCell(snake[i].X, snake[i].Y, 0x0000, termbox.ColorBlack, termbox.ColorRed)
	}
	termbox.Flush()
	return
}

func moveSnake(snake []Segment, board [][]Cell, lastDir *int) {
	food := false
	for {
		if !food {
			randX := rand.Intn(len(board[0]))
			randY := rand.Intn(len(board))
			for !board[randY][randX].Clear {
				randX = rand.Intn(len(board[0]))
				randY = rand.Intn(len(board))
			}
			board[randY][randX].Clear = false
			board[randY][randX].Color = ColorFood
			termbox.SetCell(randX, randY, 0x0000, termbox.ColorBlack, ColorFood)
			food = true
		}
		for i := 0; i < len(snake); i++ {
			if i == len(snake)-1 {
				board[snake[i].Y][snake[i].X].Color = ColorEmpty
				board[snake[i].Y][snake[i].X].Clear = true
				termbox.SetCell(snake[i].X, snake[i].Y, 0x0000, termbox.ColorBlack, ColorEmpty)
			}
			switch snake[i].Dir {
			case 0:
				snake[i].Y = snake[i].Y - 1
				break
			case 1:
				snake[i].X = snake[i].X + 1
				break
			case 2:
				snake[i].Y = snake[i].Y + 1
				break
			case 3:
				snake[i].X = snake[i].X - 1
				break
			}
			if i == 0 {
				if !board[snake[0].Y][snake[0].X].Clear { //collision
					if board[snake[0].Y][snake[0].X].Color == ColorFood { //with food
						food = false
						newX := snake[len(snake)-1].X
						newY := snake[len(snake)-1].Y
						switch snake[len(snake)-1].Dir {
						case 0:
							newY = newY + 1
							break
						case 1:
							newX = newX - 1
							break
						case 2:
							newY = newY - 1
							break
						case 3:
							newX = newX + 1
							break
						}
						snake = append(snake, Segment{newX, newY, snake[len(snake)-1].Dir})
						board[newY][newX].Color = ColorSnake
						board[newY][newX].Clear = false
					} else { //with wall
						switch snake[0].Dir { //undo move of head segment
						case 0:
							snake[0].Y = snake[0].Y + 1
							break
						case 1:
							snake[0].X = snake[0].X - 1
							break
						case 2:
							snake[0].Y = snake[0].Y - 1
							break
						case 3:
							snake[0].X = snake[0].X + 1
							break
						}
						gameOver(snake, board)
						return
					}
				}
				board[snake[0].Y][snake[0].X].Color = ColorSnake
				board[snake[0].Y][snake[0].X].Clear = false
				termbox.SetCell(snake[0].X, snake[0].Y, 0x0000, termbox.ColorBlack, ColorSnake)
			}
		}
		for j := len(snake) - 1; j > 0; j-- {
			snake[j].Dir = snake[j-1].Dir
		}
		*lastDir = snake[0].Dir
		termbox.Flush()
		time.Sleep(100 * time.Millisecond)
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	err := termbox.Init()
	if err != nil {
		log.Panicln(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	const boardSize = 40
	board := make([][]Cell, boardSize)
	for y := range board {
		board[y] = make([]Cell, boardSize*2)
		for x := range board[y] {
			if x == 0 || y == 0 || x == (boardSize*2)-1 || y == boardSize-1 {
				board[y][x] = Cell{x, y, false, ColorWall}
			} else {
				board[y][x] = Cell{x, y, true, ColorEmpty}
			}
		}
	}
	snake := make([]Segment, 7, 16)
	for i := range snake {
		snake[i] = Segment{40 - i, 20, 1}
		board[20][40-i].Color = ColorSnake
		board[20][40-i].Clear = false
	}
	for _, row := range board {
		for _, cell := range row {
			termbox.SetCell(cell.X, cell.Y, 0x0000, termbox.ColorBlack, cell.Color)
		}
	}
	termbox.Flush()
	lastDir := snake[0].Dir
	go moveSnake(snake, board, &lastDir)
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp:
				if lastDir != 2 {
					snake[0].Dir = 0
				}
				break
			case termbox.KeyArrowRight:
				if lastDir != 3 {
					snake[0].Dir = 1
				}
				break
			case termbox.KeyArrowDown:
				if lastDir != 0 {
					snake[0].Dir = 2
				}
				break
			case termbox.KeyArrowLeft:
				if lastDir != 1 {
					snake[0].Dir = 3
				}
				break
			case termbox.KeyCtrlC:
				return
			}
		case termbox.EventError:
			log.Panic(ev.Err)
			break
		}
	}
}
<commit_msg>change moveSnake to user time.Ticker instead of time.Sleep<commit_after>package main

import (
	"github.com/nsf/termbox-go"
	"log"
	"math/rand"
	"time"
)

const (
	ColorWall  = termbox.ColorRed
	ColorEmpty = termbox.ColorBlack
	ColorSnake = termbox.ColorGreen
	ColorFood  = termbox.ColorYellow
)

type Cell struct {
	X     int
	Y     int
	Clear bool
	Color termbox.Attribute
}

type Segment struct {
	X   int
	Y   int
	Dir int
}

func gameOver(snake []Segment, board [][]Cell) {
	for i := range snake {
		termbox.SetCell(snake[i].X, snake[i].Y, 0x0000, termbox.ColorBlack, termbox.ColorRed)
	}
	termbox.Flush()
	return
}

func moveSnake(snake []Segment, board [][]Cell, lastDir *int) {
	ticker := time.NewTicker(100 * time.Millisecond)
	food := false
	for {
		if !food {
			randX := rand.Intn(len(board[0]))
			randY := rand.Intn(len(board))
			for !board[randY][randX].Clear {
				randX = rand.Intn(len(board[0]))
				randY = rand.Intn(len(board))
			}
			board[randY][randX].Clear = false
			board[randY][randX].Color = ColorFood
			termbox.SetCell(randX, randY, 0x0000, termbox.ColorBlack, ColorFood)
			food = true
		}
		for i := 0; i < len(snake); i++ {
			if i == len(snake)-1 {
				board[snake[i].Y][snake[i].X].Color = ColorEmpty
				board[snake[i].Y][snake[i].X].Clear = true
				termbox.SetCell(snake[i].X, snake[i].Y, 0x0000, termbox.ColorBlack, ColorEmpty)
			}
			switch snake[i].Dir {
			case 0:
				snake[i].Y = snake[i].Y - 1
				break
			case 1:
				snake[i].X = snake[i].X + 1
				break
			case 2:
				snake[i].Y = snake[i].Y + 1
				break
			case 3:
				snake[i].X = snake[i].X - 1
				break
			}
			if i == 0 {
				if !board[snake[0].Y][snake[0].X].Clear { //collision
					if board[snake[0].Y][snake[0].X].Color == ColorFood { //with food
						food = false
						newX := snake[len(snake)-1].X
						newY := snake[len(snake)-1].Y
						switch snake[len(snake)-1].Dir {
						case 0:
							newY = newY + 1
							break
						case 1:
							newX = newX - 1
							break
						case 2:
							newY = newY - 1
							break
						case 3:
							newX = newX + 1
							break
						}
						snake = append(snake, Segment{newX, newY, snake[len(snake)-1].Dir})
						board[newY][newX].Color = ColorSnake
						board[newY][newX].Clear = false
					} else { //with wall
						switch snake[0].Dir { //undo move of head segment
						case 0:
							snake[0].Y = snake[0].Y + 1
							break
						case 1:
							snake[0].X = snake[0].X - 1
							break
						case 2:
							snake[0].Y = snake[0].Y - 1
							break
						case 3:
							snake[0].X = snake[0].X + 1
							break
						}
						gameOver(snake, board)
						return
					}
				}
				board[snake[0].Y][snake[0].X].Color = ColorSnake
				board[snake[0].Y][snake[0].X].Clear = false
				termbox.SetCell(snake[0].X, snake[0].Y, 0x0000, termbox.ColorBlack, ColorSnake)
			}
		}
		for j := len(snake) - 1; j > 0; j-- {
			snake[j].Dir = snake[j-1].Dir
		}
		*lastDir = snake[0].Dir
		termbox.Flush()
		<-ticker.C
	}
}

func main() {
	rand.Seed(time.Now().UnixNano())
	err := termbox.Init()
	if err != nil {
		log.Panicln(err)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	const boardSize = 40
	board := make([][]Cell, boardSize)
	for y := range board {
		board[y] = make([]Cell, boardSize*2)
		for x := range board[y] {
			if x == 0 || y == 0 || x == (boardSize*2)-1 || y == boardSize-1 {
				board[y][x] = Cell{x, y, false, ColorWall}
			} else {
				board[y][x] = Cell{x, y, true, ColorEmpty}
			}
		}
	}
	snake := make([]Segment, 7, 16)
	for i := range snake {
		snake[i] = Segment{40 - i, 20, 1}
		board[20][40-i].Color = ColorSnake
		board[20][40-i].Clear = false
	}
	for _, row := range board {
		for _, cell := range row {
			termbox.SetCell(cell.X, cell.Y, 0x0000, termbox.ColorBlack, cell.Color)
		}
	}
	termbox.Flush()
	lastDir := snake[0].Dir
	go moveSnake(snake, board, &lastDir)
	for {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyArrowUp:
				if lastDir != 2 {
					snake[0].Dir = 0
				}
				break
			case termbox.KeyArrowRight:
				if lastDir != 3 {
					snake[0].Dir = 1
				}
				break
			case termbox.KeyArrowDown:
				if lastDir != 0 {
					snake[0].Dir = 2
				}
				break
			case termbox.KeyArrowLeft:
				if lastDir != 1 {
					snake[0].Dir = 3
				}
				break
			case termbox.KeyCtrlC:
				return
			}
		case termbox.EventError:
			log.Panic(ev.Err)
			break
		}
	}
}
<|endoftext|>


<commit_before>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = expr.ErrorResultIdentifier

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = expr.ErrorResult
)

// Type defines a user type. A user type has a unique name and may be an alias
// to an existing type or may describe a completely new type using a list of
// attributes (object fields). Attribute types may themselves be user type.
// When a user type is defined as an alias to another type it may define
// additional validations - for example it a user type which is an alias of
// String may define a validation pattern that all instances of the type
// must match.
//
// Type is a top level definition.
//
// Type takes two or three arguments: the first argument is the name of the type.
// The name must be unique. The second argument is either another type or a
// function. If the second argument is a type then there may be a function passed
// as third argument.
//
// Example:
//
//     // simple alias
//     var MyString = Type("MyString", String)
//
//     // alias with description and additional validation
//     var Hostname = Type("Hostname", String, func() {
//         Description("A host name")
//         Format(FormatHostname)
//     })
//
//     // new type
//     var SumPayload = Type("SumPayload", func() {
//         Description("Type sent to add method")
//
//         Attribute("a", String)                 // string attribute "a"
//         Attribute("b", Int32, "operand")       // attribute with description
//         Attribute("operands", ArrayOf(Int32))  // array attribute
//         Attribute("ops", MapOf(String, Int32)) // map attribute
//         Attribute("c", SumMod)                 // attribute using user type
//         Attribute("len", Int64, func() {       // attribute with validation
//             Minimum(1)
//         })
//
//         Required("a")                          // Required attributes
//         Required("b", "c")
//     })
//
func Type(name string, args ...interface{}) expr.UserType {
	if len(args) > 2 {
		eval.ReportError("too many arguments")
		return nil
	}
	if t := expr.Root.UserType(name); t != nil {
		eval.ReportError("type %#v defined twice", name)
		return nil
	}

	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}

	var (
		base expr.DataType
		fn   func()
	)
	if len(args) == 0 {
		// Make Type behave like Attribute
		args = []interface{}{expr.String}
	}
	switch a := args[0].(type) {
	case expr.DataType:
		base = a
		if len(args) == 2 {
			d, ok := args[1].(func())
			if !ok {
				eval.ReportError("third argument must be a function")
				return nil
			}
			fn = d
		}
	case func():
		base = &expr.Object{}
		fn = a
		if len(args) == 2 {
			eval.ReportError("only one argument allowed when it is a function")
			return nil
		}
	default:
		eval.InvalidArgError("type or function", args[0])
		return nil
	}

	t := &expr.UserTypeExpr{
		TypeName:      name,
		AttributeExpr: &expr.AttributeExpr{Type: base, DSLFunc: fn},
	}
	expr.Root.Types = append(expr.Root.Types, t)
	return t
}

// ArrayOf creates an array type from its element type.
//
// ArrayOf may be used wherever types can.
// The first argument of ArrayOf is the type of the array elements specified by
// name or by reference.
// The second argument of ArrayOf is an optional function that defines
// validations for the array elements.
//
// Examples:
//
//    var Names = ArrayOf(String, func() {
//        Pattern("[a-zA-Z]+") // Validates elements of the array
//    })
//
//    var Account = Type("Account", func() {
//        Attribute("bottles", ArrayOf(Bottle), "Account bottles", func() {
//            MinLength(1) // Validates array as a whole
//        })
//    })
//
// Note: CollectionOf and ArrayOf both return array types. CollectionOf returns
// a result type where ArrayOf returns a user type. In general you want to use
// CollectionOf if the argument is a result type and ArrayOf if it is a user
// type.
func ArrayOf(v interface{}, fn ...func()) *expr.Array {
	var t expr.DataType
	var ok bool
	t, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			t = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if t == nil {
		eval.ReportError("invalid ArrayOf argument: not a type and not a known user type name")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("ArrayOf: too many arguments")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	at := expr.AttributeExpr{Type: t}
	if len(fn) == 1 {
		eval.Execute(fn[0], &at)
	}
	return &expr.Array{ElemType: &at}
}

// MapOf creates a map from its key and element types.
//
// MapOf may be used wherever types can.
// MapOf takes two arguments: the key and value types either by name of by reference.
//
// Example:
//
//    var ReviewByID = MapOf(Int64, String, func() {
//        Key(func() {
//            Minimum(1)           // Validates keys of the map
//        })
//        Elem(func() {
//            Pattern("[a-zA-Z]+") // Validates values of the map
//        })
//    })
//
//    var Review = Type("Review", func() {
//        Attribute("ratings", MapOf(Bottle, Int32), "Bottle ratings")
//    })
//
func MapOf(k, v interface{}, fn ...func()) *expr.Map {
	var tk, tv expr.DataType
	var ok bool
	tk, ok = k.(expr.DataType)
	if !ok {
		if name, ok := k.(string); ok {
			tk = expr.Root.UserType(name)
		}
	}
	tv, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			tv = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if tk == nil {
		eval.ReportError("invalid MapOf key argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if expr.IsMap(tk) {
		eval.ReportError("invalid MapOf key type: key type must be a primitive, array, or user type")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if tv == nil {
		eval.ReportError("invalid MapOf value argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("MapOf: too many arguments")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	kat := expr.AttributeExpr{Type: tk}
	vat := expr.AttributeExpr{Type: tv}
	m := &expr.Map{KeyType: &kat, ElemType: &vat}
	if len(fn) == 1 {
		mat := expr.AttributeExpr{Type: m}
		eval.Execute(fn[0], &mat)
	}
	return m
}

// Key makes it possible to specify validations for map keys.
//
// Example:
//
//    Attribute("map", MapOf(String, Int), func() {
//        Key(func() {
//            Format(FormatDateTime) // map keys are timestamps
//        })
//    })
//
func Key(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	if m, ok := at.Type.(*expr.Map); ok {
		eval.Execute(fn, m.KeyType)
		return
	}
	eval.IncompatibleDSL()
}

// Elem makes it possible to specify validations for array and map values.
//
// Example:
//
//    Attribute("array", ArrayOf(Int), func() {
//        Elem(func() {
//            Enum(1, 2, 3, 4, 5) // list possible values for array elements
//        })
//    })
//
//    Attribute("map", MapOf(String, Int), func() {
//        Elem(func() {
//            Minimum(1)
//            Maximum(100)
//        })
//    })
//
func Elem(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	switch e := at.Type.(type) {
	case *expr.Array:
		eval.Execute(fn, e.ElemType)
	case *expr.Map:
		eval.Execute(fn, e.ElemType)
	default:
		eval.IncompatibleDSL()
	}
}
<commit_msg>Correct typo in the help text (#2800)<commit_after>package dsl

import (
	"goa.design/goa/v3/eval"
	"goa.design/goa/v3/expr"
)

var (
	// ErrorResultIdentifier is the result type identifier used for error
	// responses.
	ErrorResultIdentifier = expr.ErrorResultIdentifier

	// ErrorResult is the built-in result type for error responses.
	ErrorResult = expr.ErrorResult
)

// Type defines a user type. A user type has a unique name and may be an alias
// to an existing type or may describe a completely new type using a list of
// attributes (object fields). Attribute types may themselves be user type.
// When a user type is defined as an alias to another type it may define
// additional validations - for example if a user type which is an alias of
// String may define a validation pattern that all instances of the type
// must match.
//
// Type is a top level definition.
//
// Type takes two or three arguments: the first argument is the name of the type.
// The name must be unique. The second argument is either another type or a
// function. If the second argument is a type then there may be a function passed
// as third argument.
//
// Example:
//
//     // simple alias
//     var MyString = Type("MyString", String)
//
//     // alias with description and additional validation
//     var Hostname = Type("Hostname", String, func() {
//         Description("A host name")
//         Format(FormatHostname)
//     })
//
//     // new type
//     var SumPayload = Type("SumPayload", func() {
//         Description("Type sent to add method")
//
//         Attribute("a", String)                 // string attribute "a"
//         Attribute("b", Int32, "operand")       // attribute with description
//         Attribute("operands", ArrayOf(Int32))  // array attribute
//         Attribute("ops", MapOf(String, Int32)) // map attribute
//         Attribute("c", SumMod)                 // attribute using user type
//         Attribute("len", Int64, func() {       // attribute with validation
//             Minimum(1)
//         })
//
//         Required("a")                          // Required attributes
//         Required("b", "c")
//     })
//
func Type(name string, args ...interface{}) expr.UserType {
	if len(args) > 2 {
		eval.ReportError("too many arguments")
		return nil
	}
	if t := expr.Root.UserType(name); t != nil {
		eval.ReportError("type %#v defined twice", name)
		return nil
	}

	if _, ok := eval.Current().(eval.TopExpr); !ok {
		eval.IncompatibleDSL()
		return nil
	}

	var (
		base expr.DataType
		fn   func()
	)
	if len(args) == 0 {
		// Make Type behave like Attribute
		args = []interface{}{expr.String}
	}
	switch a := args[0].(type) {
	case expr.DataType:
		base = a
		if len(args) == 2 {
			d, ok := args[1].(func())
			if !ok {
				eval.ReportError("third argument must be a function")
				return nil
			}
			fn = d
		}
	case func():
		base = &expr.Object{}
		fn = a
		if len(args) == 2 {
			eval.ReportError("only one argument allowed when it is a function")
			return nil
		}
	default:
		eval.InvalidArgError("type or function", args[0])
		return nil
	}

	t := &expr.UserTypeExpr{
		TypeName:      name,
		AttributeExpr: &expr.AttributeExpr{Type: base, DSLFunc: fn},
	}
	expr.Root.Types = append(expr.Root.Types, t)
	return t
}

// ArrayOf creates an array type from its element type.
//
// ArrayOf may be used wherever types can.
// The first argument of ArrayOf is the type of the array elements specified by
// name or by reference.
// The second argument of ArrayOf is an optional function that defines
// validations for the array elements.
//
// Examples:
//
//    var Names = ArrayOf(String, func() {
//        Pattern("[a-zA-Z]+") // Validates elements of the array
//    })
//
//    var Account = Type("Account", func() {
//        Attribute("bottles", ArrayOf(Bottle), "Account bottles", func() {
//            MinLength(1) // Validates array as a whole
//        })
//    })
//
// Note: CollectionOf and ArrayOf both return array types. CollectionOf returns
// a result type where ArrayOf returns a user type. In general you want to use
// CollectionOf if the argument is a result type and ArrayOf if it is a user
// type.
func ArrayOf(v interface{}, fn ...func()) *expr.Array {
	var t expr.DataType
	var ok bool
	t, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			t = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if t == nil {
		eval.ReportError("invalid ArrayOf argument: not a type and not a known user type name")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("ArrayOf: too many arguments")
		return &expr.Array{ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	at := expr.AttributeExpr{Type: t}
	if len(fn) == 1 {
		eval.Execute(fn[0], &at)
	}
	return &expr.Array{ElemType: &at}
}

// MapOf creates a map from its key and element types.
//
// MapOf may be used wherever types can.
// MapOf takes two arguments: the key and value types either by name of by reference.
//
// Example:
//
//    var ReviewByID = MapOf(Int64, String, func() {
//        Key(func() {
//            Minimum(1)           // Validates keys of the map
//        })
//        Elem(func() {
//            Pattern("[a-zA-Z]+") // Validates values of the map
//        })
//    })
//
//    var Review = Type("Review", func() {
//        Attribute("ratings", MapOf(Bottle, Int32), "Bottle ratings")
//    })
//
func MapOf(k, v interface{}, fn ...func()) *expr.Map {
	var tk, tv expr.DataType
	var ok bool
	tk, ok = k.(expr.DataType)
	if !ok {
		if name, ok := k.(string); ok {
			tk = expr.Root.UserType(name)
		}
	}
	tv, ok = v.(expr.DataType)
	if !ok {
		if name, ok := v.(string); ok {
			tv = expr.Root.UserType(name)
		}
	}
	// never return nil to avoid panics, errors are reported after DSL execution
	if tk == nil {
		eval.ReportError("invalid MapOf key argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if expr.IsMap(tk) {
		eval.ReportError("invalid MapOf key type: key type must be a primitive, array, or user type")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if tv == nil {
		eval.ReportError("invalid MapOf value argument: not a type and not a known user type name")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	if len(fn) > 1 {
		eval.ReportError("MapOf: too many arguments")
		return &expr.Map{KeyType: &expr.AttributeExpr{Type: expr.String}, ElemType: &expr.AttributeExpr{Type: expr.String}}
	}
	kat := expr.AttributeExpr{Type: tk}
	vat := expr.AttributeExpr{Type: tv}
	m := &expr.Map{KeyType: &kat, ElemType: &vat}
	if len(fn) == 1 {
		mat := expr.AttributeExpr{Type: m}
		eval.Execute(fn[0], &mat)
	}
	return m
}

// Key makes it possible to specify validations for map keys.
//
// Example:
//
//    Attribute("map", MapOf(String, Int), func() {
//        Key(func() {
//            Format(FormatDateTime) // map keys are timestamps
//        })
//    })
//
func Key(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	if m, ok := at.Type.(*expr.Map); ok {
		eval.Execute(fn, m.KeyType)
		return
	}
	eval.IncompatibleDSL()
}

// Elem makes it possible to specify validations for array and map values.
//
// Example:
//
//    Attribute("array", ArrayOf(Int), func() {
//        Elem(func() {
//            Enum(1, 2, 3, 4, 5) // list possible values for array elements
//        })
//    })
//
//    Attribute("map", MapOf(String, Int), func() {
//        Elem(func() {
//            Minimum(1)
//            Maximum(100)
//        })
//    })
//
func Elem(fn func()) {
	at, ok := eval.Current().(*expr.AttributeExpr)
	if !ok {
		eval.IncompatibleDSL()
		return
	}
	switch e := at.Type.(type) {
	case *expr.Array:
		eval.Execute(fn, e.ElemType)
	case *expr.Map:
		eval.Execute(fn, e.ElemType)
	default:
		eval.IncompatibleDSL()
	}
}
<|endoftext|>