<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
)

var bundleValidateFlags = []cli.Flag{
	cli.StringFlag{Name: "path", Usage: "path to a bundle"},
}

var (
	defaultRlimits = []string{
		"RLIMIT_CPU",
		"RLIMIT_FSIZE",
		"RLIMIT_DATA",
		"RLIMIT_STACK",
		"RLIMIT_CORE",
		"RLIMIT_RSS",
		"RLIMIT_NPROC",
		"RLIMIT_NOFILE",
		"RLIMIT_MEMLOCK",
		"RLIMIT_AS",
		"RLIMIT_LOCKS",
		"RLIMIT_SIGPENDING",
		"RLIMIT_MSGQUEUE",
		"RLIMIT_NICE",
		"RLIMIT_RTPRIO",
		"RLIMIT_RTTIME",
	}
)

var bundleValidateCommand = cli.Command{
	Name:  "validate",
	Usage: "validate a OCI bundle",
	Flags: bundleValidateFlags,
	Action: func(context *cli.Context) {
		inputPath := context.String("path")
		if inputPath == "" {
			logrus.Fatalf("Bundle path shouldn't be empty")
		}

		if _, err := os.Stat(inputPath); err != nil {
			logrus.Fatal(err)
		}

		configPath := path.Join(inputPath, "config.json")
		content, err := ioutil.ReadFile(configPath)
		if err != nil {
			logrus.Fatal(err)
		}
		if !utf8.Valid(content) {
			logrus.Fatalf("%q is not encoded in UTF-8", configPath)
		}
		var spec rspec.Spec
		if err = json.Unmarshal(content, &spec); err != nil {
			logrus.Fatal(err)
		}

		rootfsPath := path.Join(inputPath, spec.Root.Path)
		if fi, err := os.Stat(rootfsPath); err != nil {
			logrus.Fatalf("Cannot find the root path %q", rootfsPath)
		} else if !fi.IsDir() {
			logrus.Fatalf("root path %q is not a directory.", spec.Root.Path)
		}

		bundleValidate(spec, rootfsPath)
		logrus.Infof("Bundle validation succeeded.")
	},
}

func bundleValidate(spec rspec.Spec, rootfs string) {
	checkMandatoryField(spec)
	checkSemVer(spec.Version)
	checkPlatform(spec.Platform)
	checkProcess(spec.Process, rootfs)
	checkLinux(spec.Linux, rootfs)
}

func checkSemVer(version string) {
	re, _ := regexp.Compile("^(\\d+)?\\.(\\d+)?\\.(\\d+)?$")
	if ok := re.Match([]byte(version)); !ok {
		logrus.Fatalf("%q is not a valid version format, please read 'SemVer v2.0.0'", version)
	}
}

func checkPlatform(platform rspec.Platform) {
	validCombins := map[string][]string{
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == platform.OS {
			for _, arch := range archs {
				if arch == platform.Arch {
					return
				}
			}
			logrus.Fatalf("Combination of %q and %q is invalid.", platform.OS, platform.Arch)
		}
	}
	logrus.Fatalf("Operation system %q of the bundle is not supported yet.", platform.OS)
}

func checkProcess(process rspec.Process, rootfs string) {
	if !path.IsAbs(process.Cwd) {
		logrus.Fatalf("cwd %q is not an absolute path", process.Cwd)
	}

	for _, env := range process.Env {
		if !envValid(env) {
			logrus.Fatalf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'.", env)
		}
	}

	for index := 0; index < len(process.Capabilities); index++ {
		capability := process.Capabilities[index]
		if !capValid(capability) {
			logrus.Fatalf("capability %q is not valid, man capabilities(7)", process.Capabilities[index])
		}
	}

	for index := 0; index < len(process.Rlimits); index++ {
		if !rlimitValid(process.Rlimits[index].Type) {
			logrus.Fatalf("rlimit type %q is invalid.", process.Rlimits[index].Type)
		}
	}

	if len(process.ApparmorProfile) > 0 {
		profilePath := path.Join(rootfs, "/etc/apparmor.d", process.ApparmorProfile)
		_, err := os.Stat(profilePath)
		if err != nil {
			logrus.Fatal(err)
		}
	}
}

//Linux only
func checkLinux(spec rspec.Linux, rootfs string) {
	if len(spec.UIDMappings) > 5 {
		logrus.Fatalf("Only 5 UID mappings are allowed (linux kernel restriction).")
	}
	if len(spec.GIDMappings) > 5 {
		logrus.Fatalf("Only 5 GID mappings are allowed (linux kernel restriction).")
	}

	for index := 0; index < len(spec.Namespaces); index++ {
		if !namespaceValid(spec.Namespaces[index]) {
			logrus.Fatalf("namespace %v is invalid.", spec.Namespaces[index])
		}
	}

	for index := 0; index < len(spec.Devices); index++ {
		if !deviceValid(spec.Devices[index]) {
			logrus.Fatalf("device %v is invalid.", spec.Devices[index])
		}
	}

	if spec.Seccomp != nil {
		checkSeccomp(*spec.Seccomp)
	}

	switch spec.RootfsPropagation {
	case "":
	case "private":
	case "rprivate":
	case "slave":
	case "rslave":
	case "shared":
	case "rshared":
	default:
		logrus.Fatalf("rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared\"")
	}
}

func checkSeccomp(s rspec.Seccomp) {
	if !seccompActionValid(s.DefaultAction) {
		logrus.Fatalf("seccomp defaultAction %q is invalid.", s.DefaultAction)
	}
	for index := 0; index < len(s.Syscalls); index++ {
		if !syscallValid(s.Syscalls[index]) {
			logrus.Fatalf("syscall %v is invalid.", s.Syscalls[index])
		}
	}
	for index := 0; index < len(s.Architectures); index++ {
		switch s.Architectures[index] {
		case rspec.ArchX86:
		case rspec.ArchX86_64:
		case rspec.ArchX32:
		case rspec.ArchARM:
		case rspec.ArchAARCH64:
		case rspec.ArchMIPS:
		case rspec.ArchMIPS64:
		case rspec.ArchMIPS64N32:
		case rspec.ArchMIPSEL:
		case rspec.ArchMIPSEL64:
		case rspec.ArchMIPSEL64N32:
		default:
			logrus.Fatalf("seccomp architecture %q is invalid", s.Architectures[index])
		}
	}
}

func envValid(env string) bool {
	items := strings.Split(env, "=")
	if len(items) < 2 {
		return false
	}
	for _, ch := range strings.TrimSpace(items[0]) {
		if !unicode.IsDigit(ch) && !unicode.IsLetter(ch) && ch != '_' {
			return false
		}
	}
	return true
}

func capValid(capability string) bool {
	for _, val := range defaultCaps {
		if val == capability {
			return true
		}
	}
	return false
}

func rlimitValid(rlimit string) bool {
	for _, val := range defaultRlimits {
		if val == rlimit {
			return true
		}
	}
	return false
}

func namespaceValid(ns rspec.Namespace) bool {
	switch ns.Type {
	case rspec.PIDNamespace:
	case rspec.NetworkNamespace:
	case rspec.MountNamespace:
	case rspec.IPCNamespace:
	case rspec.UTSNamespace:
	case rspec.UserNamespace:
	default:
		return false
	}
	return true
}

func deviceValid(d rspec.Device) bool {
	switch d.Type {
	case "b":
	case "c":
	case "u":
		if d.Major <= 0 {
			return false
		}
		if d.Minor <= 0 {
			return false
		}
	case "p":
		if d.Major > 0 || d.Minor > 0 {
			return false
		}
	default:
		return false
	}
	return true
}

func seccompActionValid(secc rspec.Action) bool {
	switch secc {
	case "":
	case rspec.ActKill:
	case rspec.ActTrap:
	case rspec.ActErrno:
	case rspec.ActTrace:
	case rspec.ActAllow:
	default:
		return false
	}
	return true
}

func syscallValid(s rspec.Syscall) bool {
	if !seccompActionValid(s.Action) {
		return false
	}
	for index := 0; index < len(s.Args); index++ {
		arg := s.Args[index]
		switch arg.Op {
		case rspec.OpNotEqual:
		case rspec.OpLessEqual:
		case rspec.OpEqualTo:
		case rspec.OpGreaterEqual:
		case rspec.OpGreaterThan:
		case rspec.OpMaskedEqual:
		default:
			return false
		}
	}
	return true
}

func isStruct(t reflect.Type) bool {
	return t.Kind() == reflect.Struct
}

func isStructPtr(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) ([]string, bool) {
	var msgs []string
	mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty")
	switch field.Kind() {
	case reflect.Ptr:
		if mandatory && field.IsNil() == true {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
	case reflect.String:
		if mandatory && (field.Len() == 0) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
	case reflect.Slice:
		if mandatory && (field.Len() == 0) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
		valid := true
		for index := 0; index < field.Len(); index++ {
			mValue := field.Index(index)
			if mValue.CanInterface() {
				if ms, ok := checkMandatory(mValue.Interface()); !ok {
					msgs = append(msgs, ms...)
					valid = false
				}
			}
		}
		return msgs, valid
	case reflect.Map:
		if mandatory && ((field.IsNil() == true) || (field.Len() == 0)) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
		valid := true
		keys := field.MapKeys()
		for index := 0; index < len(keys); index++ {
			mValue := field.MapIndex(keys[index])
			if mValue.CanInterface() {
				if ms, ok := checkMandatory(mValue.Interface()); !ok {
					msgs = append(msgs, ms...)
					valid = false
				}
			}
		}
		return msgs, valid
	default:
	}

	return nil, true
}

func checkMandatory(obj interface{}) (msgs []string, valid bool) {
	objT := reflect.TypeOf(obj)
	objV := reflect.ValueOf(obj)
	if isStructPtr(objT) {
		objT = objT.Elem()
		objV = objV.Elem()
	} else if !isStruct(objT) {
		return nil, true
	}

	valid = true
	for i := 0; i < objT.NumField(); i++ {
		t := objT.Field(i).Type
		if isStructPtr(t) && objV.Field(i).IsNil() {
			if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") {
				msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name))
				valid = false
			}
		} else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() {
			if ms, ok := checkMandatory(objV.Field(i).Interface()); !ok {
				msgs = append(msgs, ms...)
				valid = false
			}
		} else {
			if ms, ok := checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name()); !ok {
				msgs = append(msgs, ms...)
				valid = false
			}
		}

	}
	return msgs, valid
}

func checkMandatoryField(obj interface{}) {
	if msgs, valid := checkMandatory(obj); !valid {
		logrus.Fatalf("Mandatory information missing: %s.", msgs)
	}
}
<commit_msg>validation: add hostname validation<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"reflect"
	"regexp"
	"strings"
	"unicode"
	"unicode/utf8"

	"github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	rspec "github.com/opencontainers/runtime-spec/specs-go"
)

var bundleValidateFlags = []cli.Flag{
	cli.StringFlag{Name: "path", Usage: "path to a bundle"},
}

var (
	defaultRlimits = []string{
		"RLIMIT_CPU",
		"RLIMIT_FSIZE",
		"RLIMIT_DATA",
		"RLIMIT_STACK",
		"RLIMIT_CORE",
		"RLIMIT_RSS",
		"RLIMIT_NPROC",
		"RLIMIT_NOFILE",
		"RLIMIT_MEMLOCK",
		"RLIMIT_AS",
		"RLIMIT_LOCKS",
		"RLIMIT_SIGPENDING",
		"RLIMIT_MSGQUEUE",
		"RLIMIT_NICE",
		"RLIMIT_RTPRIO",
		"RLIMIT_RTTIME",
	}
)

var bundleValidateCommand = cli.Command{
	Name:  "validate",
	Usage: "validate a OCI bundle",
	Flags: bundleValidateFlags,
	Action: func(context *cli.Context) {
		inputPath := context.String("path")
		if inputPath == "" {
			logrus.Fatalf("Bundle path shouldn't be empty")
		}

		if _, err := os.Stat(inputPath); err != nil {
			logrus.Fatal(err)
		}

		configPath := path.Join(inputPath, "config.json")
		content, err := ioutil.ReadFile(configPath)
		if err != nil {
			logrus.Fatal(err)
		}
		if !utf8.Valid(content) {
			logrus.Fatalf("%q is not encoded in UTF-8", configPath)
		}
		var spec rspec.Spec
		if err = json.Unmarshal(content, &spec); err != nil {
			logrus.Fatal(err)
		}

		rootfsPath := path.Join(inputPath, spec.Root.Path)
		if fi, err := os.Stat(rootfsPath); err != nil {
			logrus.Fatalf("Cannot find the root path %q", rootfsPath)
		} else if !fi.IsDir() {
			logrus.Fatalf("root path %q is not a directory.", spec.Root.Path)
		}

		bundleValidate(spec, rootfsPath)
		logrus.Infof("Bundle validation succeeded.")
	},
}

func bundleValidate(spec rspec.Spec, rootfs string) {
	checkMandatoryField(spec)
	checkSemVer(spec.Version)
	checkPlatform(spec.Platform)
	checkProcess(spec.Process, rootfs)
	checkLinux(spec.Linux, spec.Hostname, rootfs)
}

func checkSemVer(version string) {
	re, _ := regexp.Compile("^(\\d+)?\\.(\\d+)?\\.(\\d+)?$")
	if ok := re.Match([]byte(version)); !ok {
		logrus.Fatalf("%q is not a valid version format, please read 'SemVer v2.0.0'", version)
	}
}

func checkPlatform(platform rspec.Platform) {
	validCombins := map[string][]string{
		"darwin":    {"386", "amd64", "arm", "arm64"},
		"dragonfly": {"amd64"},
		"freebsd":   {"386", "amd64", "arm"},
		"linux":     {"386", "amd64", "arm", "arm64", "ppc64", "ppc64le", "mips64", "mips64le"},
		"netbsd":    {"386", "amd64", "arm"},
		"openbsd":   {"386", "amd64", "arm"},
		"plan9":     {"386", "amd64"},
		"solaris":   {"amd64"},
		"windows":   {"386", "amd64"}}
	for os, archs := range validCombins {
		if os == platform.OS {
			for _, arch := range archs {
				if arch == platform.Arch {
					return
				}
			}
			logrus.Fatalf("Combination of %q and %q is invalid.", platform.OS, platform.Arch)
		}
	}
	logrus.Fatalf("Operation system %q of the bundle is not supported yet.", platform.OS)
}

func checkProcess(process rspec.Process, rootfs string) {
	if !path.IsAbs(process.Cwd) {
		logrus.Fatalf("cwd %q is not an absolute path", process.Cwd)
	}

	for _, env := range process.Env {
		if !envValid(env) {
			logrus.Fatalf("env %q should be in the form of 'key=value'. The left hand side must consist solely of letters, digits, and underscores '_'.", env)
		}
	}

	for index := 0; index < len(process.Capabilities); index++ {
		capability := process.Capabilities[index]
		if !capValid(capability) {
			logrus.Fatalf("capability %q is not valid, man capabilities(7)", process.Capabilities[index])
		}
	}

	for index := 0; index < len(process.Rlimits); index++ {
		if !rlimitValid(process.Rlimits[index].Type) {
			logrus.Fatalf("rlimit type %q is invalid.", process.Rlimits[index].Type)
		}
	}

	if len(process.ApparmorProfile) > 0 {
		profilePath := path.Join(rootfs, "/etc/apparmor.d", process.ApparmorProfile)
		_, err := os.Stat(profilePath)
		if err != nil {
			logrus.Fatal(err)
		}
	}
}

//Linux only
func checkLinux(spec rspec.Linux, hostname string, rootfs string) {
	utsExists := false

	if len(spec.UIDMappings) > 5 {
		logrus.Fatalf("Only 5 UID mappings are allowed (linux kernel restriction).")
	}
	if len(spec.GIDMappings) > 5 {
		logrus.Fatalf("Only 5 GID mappings are allowed (linux kernel restriction).")
	}

	for index := 0; index < len(spec.Namespaces); index++ {
		if !namespaceValid(spec.Namespaces[index]) {
			logrus.Fatalf("namespace %v is invalid.", spec.Namespaces[index])
		} else if spec.Namespaces[index].Type == rspec.UTSNamespace {
			utsExists = true
		}
	}

	if !utsExists && hostname != "" {
		logrus.Fatalf("Hostname requires a new UTS namespace to be specified as well")
	}

	for index := 0; index < len(spec.Devices); index++ {
		if !deviceValid(spec.Devices[index]) {
			logrus.Fatalf("device %v is invalid.", spec.Devices[index])
		}
	}

	if spec.Seccomp != nil {
		checkSeccomp(*spec.Seccomp)
	}

	switch spec.RootfsPropagation {
	case "":
	case "private":
	case "rprivate":
	case "slave":
	case "rslave":
	case "shared":
	case "rshared":
	default:
		logrus.Fatalf("rootfsPropagation must be empty or one of \"private|rprivate|slave|rslave|shared|rshared\"")
	}
}

func checkSeccomp(s rspec.Seccomp) {
	if !seccompActionValid(s.DefaultAction) {
		logrus.Fatalf("seccomp defaultAction %q is invalid.", s.DefaultAction)
	}
	for index := 0; index < len(s.Syscalls); index++ {
		if !syscallValid(s.Syscalls[index]) {
			logrus.Fatalf("syscall %v is invalid.", s.Syscalls[index])
		}
	}
	for index := 0; index < len(s.Architectures); index++ {
		switch s.Architectures[index] {
		case rspec.ArchX86:
		case rspec.ArchX86_64:
		case rspec.ArchX32:
		case rspec.ArchARM:
		case rspec.ArchAARCH64:
		case rspec.ArchMIPS:
		case rspec.ArchMIPS64:
		case rspec.ArchMIPS64N32:
		case rspec.ArchMIPSEL:
		case rspec.ArchMIPSEL64:
		case rspec.ArchMIPSEL64N32:
		default:
			logrus.Fatalf("seccomp architecture %q is invalid", s.Architectures[index])
		}
	}
}

func envValid(env string) bool {
	items := strings.Split(env, "=")
	if len(items) < 2 {
		return false
	}
	for _, ch := range strings.TrimSpace(items[0]) {
		if !unicode.IsDigit(ch) && !unicode.IsLetter(ch) && ch != '_' {
			return false
		}
	}
	return true
}

func capValid(capability string) bool {
	for _, val := range defaultCaps {
		if val == capability {
			return true
		}
	}
	return false
}

func rlimitValid(rlimit string) bool {
	for _, val := range defaultRlimits {
		if val == rlimit {
			return true
		}
	}
	return false
}

func namespaceValid(ns rspec.Namespace) bool {
	switch ns.Type {
	case rspec.PIDNamespace:
	case rspec.NetworkNamespace:
	case rspec.MountNamespace:
	case rspec.IPCNamespace:
	case rspec.UTSNamespace:
	case rspec.UserNamespace:
	default:
		return false
	}
	return true
}

func deviceValid(d rspec.Device) bool {
	switch d.Type {
	case "b":
	case "c":
	case "u":
		if d.Major <= 0 {
			return false
		}
		if d.Minor <= 0 {
			return false
		}
	case "p":
		if d.Major > 0 || d.Minor > 0 {
			return false
		}
	default:
		return false
	}
	return true
}

func seccompActionValid(secc rspec.Action) bool {
	switch secc {
	case "":
	case rspec.ActKill:
	case rspec.ActTrap:
	case rspec.ActErrno:
	case rspec.ActTrace:
	case rspec.ActAllow:
	default:
		return false
	}
	return true
}

func syscallValid(s rspec.Syscall) bool {
	if !seccompActionValid(s.Action) {
		return false
	}
	for index := 0; index < len(s.Args); index++ {
		arg := s.Args[index]
		switch arg.Op {
		case rspec.OpNotEqual:
		case rspec.OpLessEqual:
		case rspec.OpEqualTo:
		case rspec.OpGreaterEqual:
		case rspec.OpGreaterThan:
		case rspec.OpMaskedEqual:
		default:
			return false
		}
	}
	return true
}

func isStruct(t reflect.Type) bool {
	return t.Kind() == reflect.Struct
}

func isStructPtr(t reflect.Type) bool {
	return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct
}

func checkMandatoryUnit(field reflect.Value, tagField reflect.StructField, parent string) ([]string, bool) {
	var msgs []string
	mandatory := !strings.Contains(tagField.Tag.Get("json"), "omitempty")
	switch field.Kind() {
	case reflect.Ptr:
		if mandatory && field.IsNil() == true {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
	case reflect.String:
		if mandatory && (field.Len() == 0) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
	case reflect.Slice:
		if mandatory && (field.Len() == 0) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
		valid := true
		for index := 0; index < field.Len(); index++ {
			mValue := field.Index(index)
			if mValue.CanInterface() {
				if ms, ok := checkMandatory(mValue.Interface()); !ok {
					msgs = append(msgs, ms...)
					valid = false
				}
			}
		}
		return msgs, valid
	case reflect.Map:
		if mandatory && ((field.IsNil() == true) || (field.Len() == 0)) {
			msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty.", parent, tagField.Name))
			return msgs, false
		}
		valid := true
		keys := field.MapKeys()
		for index := 0; index < len(keys); index++ {
			mValue := field.MapIndex(keys[index])
			if mValue.CanInterface() {
				if ms, ok := checkMandatory(mValue.Interface()); !ok {
					msgs = append(msgs, ms...)
					valid = false
				}
			}
		}
		return msgs, valid
	default:
	}

	return nil, true
}

func checkMandatory(obj interface{}) (msgs []string, valid bool) {
	objT := reflect.TypeOf(obj)
	objV := reflect.ValueOf(obj)
	if isStructPtr(objT) {
		objT = objT.Elem()
		objV = objV.Elem()
	} else if !isStruct(objT) {
		return nil, true
	}

	valid = true
	for i := 0; i < objT.NumField(); i++ {
		t := objT.Field(i).Type
		if isStructPtr(t) && objV.Field(i).IsNil() {
			if !strings.Contains(objT.Field(i).Tag.Get("json"), "omitempty") {
				msgs = append(msgs, fmt.Sprintf("'%s.%s' should not be empty", objT.Name(), objT.Field(i).Name))
				valid = false
			}
		} else if (isStruct(t) || isStructPtr(t)) && objV.Field(i).CanInterface() {
			if ms, ok := checkMandatory(objV.Field(i).Interface()); !ok {
				msgs = append(msgs, ms...)
				valid = false
			}
		} else {
			if ms, ok := checkMandatoryUnit(objV.Field(i), objT.Field(i), objT.Name()); !ok {
				msgs = append(msgs, ms...)
				valid = false
			}
		}

	}
	return msgs, valid
}

func checkMandatoryField(obj interface{}) {
	if msgs, valid := checkMandatory(obj); !valid {
		logrus.Fatalf("Mandatory information missing: %s.", msgs)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"archive/zip"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/itchio/butler/comm"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/pwr"
	"github.com/itchio/wharf/sync"
	"github.com/itchio/wharf/tlc"
	"github.com/itchio/wharf/wire"
)

func diff(target string, source string, patch string, compression pwr.CompressionSettings) {
	must(doDiff(target, source, patch, compression))
}

func doDiff(target string, source string, patch string, compression pwr.CompressionSettings) error {
	startTime := time.Now()

	var targetSignature []sync.BlockHash
	var targetContainer *tlc.Container

	if target == "/dev/null" {
		targetContainer = &tlc.Container{}
	} else {
		targetInfo, err := os.Lstat(target)
		if err != nil {
			return err
		}

		if targetInfo.IsDir() {
			comm.Opf("Hashing %s", target)
			targetContainer, err = tlc.Walk(target, filterPaths)
			if err != nil {
				return err
			}

			comm.StartProgress()
			targetSignature, err = pwr.ComputeSignature(targetContainer, targetContainer.NewFilePool(target), comm.NewStateConsumer())
			comm.EndProgress()
			if err != nil {
				return err
			}

			{
				prettySize := humanize.Bytes(uint64(targetContainer.Size))
				perSecond := humanize.Bytes(uint64(float64(targetContainer.Size) / time.Since(startTime).Seconds()))
				comm.Statf("%s (%s) @ %s/s\n", prettySize, targetContainer.Stats(), perSecond)
			}
		} else {
			signatureReader, err := os.Open(target)
			if err != nil {
				return err
			}

			targetContainer, targetSignature, err = pwr.ReadSignature(signatureReader)
			if err != nil {
				if err != wire.ErrFormat {
					return err
				}

				_, err = signatureReader.Seek(0, os.SEEK_SET)
				if err != nil {
					return err
				}

				stats, err := os.Lstat(target)
				if err != nil {
					return err
				}

				zr, err := zip.NewReader(signatureReader, stats.Size())
				if err != nil {
					return err
				}

				targetContainer, err = tlc.WalkZip(zr, filterPaths)
				if err != nil {
					return err
				}
				comm.Opf("Walking archive (%s)", targetContainer.Stats())

				// targetContainer.NewZipFilePool(zr)

				comm.StartProgress()
				targetSignature, err = pwr.ComputeSignature(targetContainer, targetContainer.NewFilePool(target), comm.NewStateConsumer())
				comm.EndProgress()
				if err != nil {
					return err
				}

				{
					prettySize := humanize.Bytes(uint64(targetContainer.Size))
					perSecond := humanize.Bytes(uint64(float64(targetContainer.Size) / time.Since(startTime).Seconds()))
					comm.Statf("%s (%s) @ %s/s\n", prettySize, targetContainer.Stats(), perSecond)
				}
				comm.Opf("Read signature from %s", target)
			} else {
				comm.Opf("Read signature from %s", target)
			}

			err = signatureReader.Close()
			if err != nil {
				return err
			}
		}

	}

	startTime = time.Now()

	var sourceContainer *tlc.Container
	if source == "/dev/null" {
		sourceContainer = &tlc.Container{}
	} else {
		var err error
		sourceContainer, err = tlc.Walk(source, filterPaths)
		if err != nil {
			return err
		}
	}

	patchWriter, err := os.Create(patch)
	if err != nil {
		return err
	}
	defer patchWriter.Close()

	signaturePath := patch + ".sig"
	signatureWriter, err := os.Create(signaturePath)
	if err != nil {
		return err
	}
	defer signatureWriter.Close()

	patchCounter := counter.NewWriter(patchWriter)
	signatureCounter := counter.NewWriter(signatureWriter)

	dctx := &pwr.DiffContext{
		SourceContainer: sourceContainer,
		FilePool:        sourceContainer.NewFilePool(source),

		TargetContainer: targetContainer,
		TargetSignature: targetSignature,

		Consumer:    comm.NewStateConsumer(),
		Compression: &compression,
	}

	comm.Opf("Diffing %s", source)
	comm.StartProgress()
	err = dctx.WritePatch(patchCounter, signatureCounter)
	if err != nil {
		return err
	}
	comm.EndProgress()

	{
		prettySize := humanize.Bytes(uint64(sourceContainer.Size))
		perSecond := humanize.Bytes(uint64(float64(sourceContainer.Size) / time.Since(startTime).Seconds()))
		comm.Statf("%s (%s) @ %s/s\n", prettySize, sourceContainer.Stats(), perSecond)
	}

	if *diffArgs.verify {
		tmpDir, err := ioutil.TempDir("", "pwr")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tmpDir)

		apply(patch, target, tmpDir, false, signaturePath)
	}

	{
		prettyPatchSize := humanize.Bytes(uint64(patchCounter.Count()))
		percReused := 100.0 * float64(dctx.ReusedBytes) / float64(dctx.FreshBytes+dctx.ReusedBytes)
		relToNew := 100.0 * float64(patchCounter.Count()) / float64(sourceContainer.Size)
		prettyFreshSize := humanize.Bytes(uint64(dctx.FreshBytes))

		comm.Statf("Re-used %.2f%% of old, added %s fresh data", percReused, prettyFreshSize)
		comm.Statf("%s patch (%.2f%% of the full size)", prettyPatchSize, relToNew)
	}

	return nil
}

func apply(patch string, target string, output string, inplace bool, sigpath string) {
	must(doApply(patch, target, output, inplace, sigpath))
}

func doApply(patch string, target string, output string, inplace bool, sigpath string) error {
	if output == "" {
		output = target
	}

	target = path.Clean(target)
	output = path.Clean(output)
	if output == target {
		if !inplace {
			comm.Dief("Refusing to destructively patch %s without --inplace", output)
		}
	}

	comm.Opf("Patching %s", output)
	startTime := time.Now()

	patchReader, err := os.Open(patch)
	if err != nil {
		return err
	}

	actx := &pwr.ApplyContext{
		TargetPath:        target,
		OutputPath:        output,
		InPlace:           inplace,
		SignatureFilePath: sigpath,

		Consumer: comm.NewStateConsumer(),
	}

	comm.StartProgress()
	err = actx.ApplyPatch(patchReader)
	if err != nil {
		return err
	}
	comm.EndProgress()

	container := actx.SourceContainer
	prettySize := humanize.Bytes(uint64(container.Size))
	perSecond := humanize.Bytes(uint64(float64(container.Size) / time.Since(startTime).Seconds()))

	if actx.InPlace {
		comm.Statf("patched %d, kept %d, deleted %d (%s stage)", actx.TouchedFiles, actx.NoopFiles, actx.DeletedFiles, humanize.Bytes(uint64(actx.StageSize)))
	}
	comm.Statf("%s (%s) @ %s/s\n", prettySize, container.Stats(), perSecond)

	return nil
}

func sign(output string, signature string, compression pwr.CompressionSettings) {
	must(doSign(output, signature, compression))
}

func doSign(output string, signature string, compression pwr.CompressionSettings) error {
	comm.Opf("Creating signature for %s", output)
	startTime := time.Now()

	container, err := tlc.Walk(output, filterPaths)
	if err != nil {
		return err
	}

	signatureWriter, err := os.Create(signature)
	if err != nil {
		return err
	}

	rawSigWire := wire.NewWriteContext(signatureWriter)
	rawSigWire.WriteMagic(pwr.SignatureMagic)

	rawSigWire.WriteMessage(&pwr.SignatureHeader{
		Compression: &compression,
	})

	sigWire, err := pwr.CompressWire(rawSigWire, &compression)
	if err != nil {
		return err
	}
	sigWire.WriteMessage(container)

	comm.StartProgress()
	err = pwr.ComputeSignatureToWriter(container, container.NewFilePool(output), comm.NewStateConsumer(), func(hash sync.BlockHash) error {
		return sigWire.WriteMessage(&pwr.BlockHash{
			WeakHash:   hash.WeakHash,
			StrongHash: hash.StrongHash,
		})
	})
	comm.EndProgress()
	if err != nil {
		return err
	}

	err = sigWire.Close()
	if err != nil {
		return err
	}

	prettySize := humanize.Bytes(uint64(container.Size))
	perSecond := humanize.Bytes(uint64(float64(container.Size) / time.Since(startTime).Seconds()))
	comm.Statf("%s (%s) @ %s/s\n", prettySize, container.Stats(), perSecond)

	return nil
}

func verify(signature string, output string) {
	must(doVerify(signature, output))
}

func doVerify(signature string, output string) error {
	comm.Opf("Verifying %s", output)
	startTime := time.Now()

	signatureReader, err := os.Open(signature)
	if err != nil {
		return err
	}
	defer signatureReader.Close()

	refContainer, refHashes, err := pwr.ReadSignature(signatureReader)
	if err != nil {
		return err
	}

	comm.StartProgress()
	hashes, err := pwr.ComputeSignature(refContainer, refContainer.NewFilePool(output), comm.NewStateConsumer())
	comm.EndProgress()
	if err != nil {
		return err
	}

	err = pwr.CompareHashes(refHashes, hashes, refContainer)
	if err != nil {
		comm.Logf(err.Error())
		comm.Dief("Some checks failed after checking %d block.", len(refHashes))
	}

	prettySize := humanize.Bytes(uint64(refContainer.Size))
	perSecond := humanize.Bytes(uint64(float64(refContainer.Size) / time.Since(startTime).Seconds()))
	comm.Statf("%s (%s) @ %s/s\n", prettySize, refContainer.Stats(), perSecond)

	return nil
}
<commit_msg>support diffing from zip<commit_after>package main

import (
	"archive/zip"
	"io/ioutil"
	"os"
	"path"
	"time"

	"github.com/dustin/go-humanize"
	"github.com/itchio/butler/comm"
	"github.com/itchio/wharf/counter"
	"github.com/itchio/wharf/pwr"
	"github.com/itchio/wharf/sync"
	"github.com/itchio/wharf/tlc"
	"github.com/itchio/wharf/wire"
)

func diff(target string, source string, patch string, compression pwr.CompressionSettings) {
	must(doDiff(target, source, patch, compression))
}

func doDiff(target string, source string, patch string, compression pwr.CompressionSettings) error {
	startTime := time.Now()

	var targetSignature []sync.BlockHash
	var targetContainer *tlc.Container

	if target == "/dev/null" {
		targetContainer = &tlc.Container{}
	} else {
		targetInfo, err := os.Lstat(target)
		if err != nil {
			return err
		}

		if targetInfo.IsDir() {
			comm.Opf("Hashing %s", target)
			targetContainer, err = tlc.Walk(target, filterPaths)
			if err != nil {
				return err
			}

			comm.StartProgress()
			targetSignature, err = pwr.ComputeSignature(targetContainer, targetContainer.NewFilePool(target), comm.NewStateConsumer())
			comm.EndProgress()
			if err != nil {
				return err
			}

			{
				prettySize := humanize.Bytes(uint64(targetContainer.Size))
				perSecond := humanize.Bytes(uint64(float64(targetContainer.Size) / time.Since(startTime).Seconds()))
				comm.Statf("%s (%s) @ %s/s\n", prettySize, targetContainer.Stats(), perSecond)
			}
		} else {
			signatureReader, err := os.Open(target)
			if err != nil {
				return err
			}

			targetContainer, targetSignature, err = pwr.ReadSignature(signatureReader)
			if err != nil {
				if err != wire.ErrFormat {
					return err
				}

				_, err = signatureReader.Seek(0, os.SEEK_SET)
				if err != nil {
					return err
				}

				stats, err := os.Lstat(target)
				if err != nil {
					return err
				}

				zr, err := zip.NewReader(signatureReader, stats.Size())
				if err != nil {
					return err
				}

				targetContainer, err = tlc.WalkZip(zr, filterPaths)
				if err != nil {
					return err
				}
				comm.Opf("Walking archive (%s)", targetContainer.Stats())

				comm.StartProgress()
				targetSignature, err = pwr.ComputeSignature(targetContainer, targetContainer.NewZipPool(zr), comm.NewStateConsumer())
				comm.EndProgress()
				if err != nil {
					return err
				}

				{
					prettySize := humanize.Bytes(uint64(targetContainer.Size))
					perSecond := humanize.Bytes(uint64(float64(targetContainer.Size) / time.Since(startTime).Seconds()))
					comm.Statf("%s (%s) @ %s/s\n", prettySize, targetContainer.Stats(), perSecond)
				}
				comm.Opf("Read signature from %s", target)
			} else {
				comm.Opf("Read signature from %s", target)
			}

			err = signatureReader.Close()
			if err != nil {
				return err
			}
		}

	}

	startTime = time.Now()

	var sourceContainer *tlc.Container
	if source == "/dev/null" {
		sourceContainer = &tlc.Container{}
	} else {
		var err error
		sourceContainer, err = tlc.Walk(source, filterPaths)
		if err != nil {
			return err
		}
	}

	patchWriter, err := os.Create(patch)
	if err != nil {
		return err
	}
	defer patchWriter.Close()

	signaturePath := patch + ".sig"
	signatureWriter, err := os.Create(signaturePath)
	if err != nil {
		return err
	}
	defer signatureWriter.Close()

	patchCounter := counter.NewWriter(patchWriter)
	signatureCounter := counter.NewWriter(signatureWriter)

	dctx := &pwr.DiffContext{
		SourceContainer: sourceContainer,
		FilePool:        sourceContainer.NewFilePool(source),

		TargetContainer: targetContainer,
		TargetSignature: targetSignature,

		Consumer:    comm.NewStateConsumer(),
		Compression: &compression,
	}

	comm.Opf("Diffing %s", source)
	comm.StartProgress()
	err = dctx.WritePatch(patchCounter, signatureCounter)
	if err != nil {
		return err
	}
	comm.EndProgress()

	{
		prettySize := humanize.Bytes(uint64(sourceContainer.Size))
		perSecond := humanize.Bytes(uint64(float64(sourceContainer.Size) / time.Since(startTime).Seconds()))
		comm.Statf("%s (%s) @ %s/s\n", prettySize, sourceContainer.Stats(), perSecond)
	}

	if *diffArgs.verify {
		tmpDir, err := ioutil.TempDir("", "pwr")
		if err != nil {
			return err
		}
		defer os.RemoveAll(tmpDir)

		apply(patch, target, tmpDir, false, signaturePath)
	}

	{
		prettyPatchSize := humanize.Bytes(uint64(patchCounter.Count()))
		percReused := 100.0 * float64(dctx.ReusedBytes) / float64(dctx.FreshBytes+dctx.ReusedBytes)
		relToNew := 100.0 * float64(patchCounter.Count()) / float64(sourceContainer.Size)
		prettyFreshSize := humanize.Bytes(uint64(dctx.FreshBytes))

		comm.Statf("Re-used %.2f%% of old, added %s fresh data", percReused, prettyFreshSize)
		comm.Statf("%s patch (%.2f%% of the full size)", prettyPatchSize, relToNew)
	}

	return nil
}

func apply(patch string, target string, output string, inplace bool, sigpath string) {
	must(doApply(patch, target, output, inplace, sigpath))
}

func doApply(patch string, target string, output string, inplace bool, sigpath string) error {
	if output == "" {
		output = target
	}

	target = path.Clean(target)
	output = path.Clean(output)
	if output == target {
		if !inplace {
			comm.Dief("Refusing to destructively patch %s without --inplace", output)
		}
	}

	comm.Opf("Patching %s", output)
	startTime := time.Now()

	patchReader, err := os.Open(patch)
	if err != nil {
		return err
	}

	actx := &pwr.ApplyContext{
		TargetPath:        target,
		OutputPath:        output,
		InPlace:           inplace,
		SignatureFilePath: sigpath,

		Consumer: comm.NewStateConsumer(),
	}

	comm.StartProgress()
	err = actx.ApplyPatch(patchReader)
	if err != nil {
		return err
	}
	comm.EndProgress()

	container := actx.SourceContainer
	prettySize := humanize.Bytes(uint64(container.Size))
	perSecond := humanize.Bytes(uint64(float64(container.Size) / time.Since(startTime).Seconds()))

	if actx.InPlace {
		comm.Statf("patched %d, kept %d, deleted %d (%s stage)", actx.TouchedFiles, actx.NoopFiles, actx.DeletedFiles, humanize.Bytes(uint64(actx.StageSize)))
	}
	comm.Statf("%s (%s) @ %s/s\n", prettySize, container.Stats(), perSecond)

	return nil
}

func sign(output string, signature string, compression pwr.CompressionSettings) {
	must(doSign(output, signature, compression))
}

func doSign(output string, signature string, compression pwr.CompressionSettings) error {
	comm.Opf("Creating signature for %s", output)
	startTime := time.Now()

	container, err := tlc.Walk(output, filterPaths)
	if err != nil {
		return err
	}

	signatureWriter, err := os.Create(signature)
	if err != nil {
		return err
	}

	rawSigWire := wire.NewWriteContext(signatureWriter)
	rawSigWire.WriteMagic(pwr.SignatureMagic)

	rawSigWire.WriteMessage(&pwr.SignatureHeader{
		Compression: &compression,
	})

	sigWire, err := pwr.CompressWire(rawSigWire, &compression)
	if err != nil {
		return err
	}
	sigWire.WriteMessage(container)

	comm.StartProgress()
	err = pwr.ComputeSignatureToWriter(container, container.NewFilePool(output), comm.NewStateConsumer(), func(hash sync.BlockHash) error {
		return sigWire.WriteMessage(&pwr.BlockHash{
			WeakHash:   hash.WeakHash,
			StrongHash: hash.StrongHash,
		})
	})
	comm.EndProgress()
	if err != nil {
		return err
	}

	err = sigWire.Close()
	if err != nil {
		return err
	}

	prettySize := humanize.Bytes(uint64(container.Size))
	perSecond := humanize.Bytes(uint64(float64(container.Size) / time.Since(startTime).Seconds()))
	comm.Statf("%s (%s) @ %s/s\n", prettySize, container.Stats(), perSecond)

	return nil
}

func verify(signature string, output string) {
	must(doVerify(signature, output))
}

func doVerify(signature string, output string) error {
	comm.Opf("Verifying %s", output)
	startTime := time.Now()

	signatureReader, err := os.Open(signature)
	if err != nil {
		return err
	}
	defer signatureReader.Close()

	refContainer, refHashes, err := pwr.ReadSignature(signatureReader)
	if err != nil {
		return err
	}

	comm.StartProgress()
	hashes, err := pwr.ComputeSignature(refContainer, refContainer.NewFilePool(output), comm.NewStateConsumer())
	comm.EndProgress()
	if err != nil {
		return err
	}

	err = pwr.CompareHashes(refHashes, hashes, refContainer)
	if err != nil {
		comm.Logf(err.Error())
		comm.Dief("Some checks failed after checking %d block.", len(refHashes))
	}

	prettySize := humanize.Bytes(uint64(refContainer.Size))
	perSecond := humanize.Bytes(uint64(float64(refContainer.Size) / time.Since(startTime).Seconds()))
	comm.Statf("%s (%s) @ %s/s\n", prettySize, refContainer.Stats(), perSecond)

	return nil
}
<|endoftext|>


<commit_before>package pbparser

import (
	"errors"
	"fmt"
	"strings"
)

func verify(pf *ProtoFile, p ImportModuleProvider) error {
	// validate syntax
	if err := validateSyntax(pf); err != nil {
		return err
	}

	if (len(pf.Dependencies) > 0 || len(pf.PublicDependencies) > 0) && p == nil {
		return errors.New("ImportModuleProvider is required to validate imports")
	}

	// make a map of dependency package to its parsed model...
	m := make(map[string]ProtoFile)

	// parse the dependencies...
	if err := parseDependencies(p, pf.Dependencies, m); err != nil {
		return err
	}

	// parse the public dependencies...
	if err := parseDependencies(p, pf.PublicDependencies, m); err != nil {
		return err
	}

	// collate the dependency package names...
	packageNames := getDependencyPackageNames(m)

	// validate if the NamedDataType fields of messages are all defined in the model;
	// either the main model or in dependencies
	fields := findFieldsToValidate(pf.Messages)
	for _, f := range fields {
		if err := validateFieldDataTypes(pf.PackageName, f, pf.Messages, pf.Enums, m, packageNames); err != nil {
			return err
		}
	}

	// validate if each rpc request/response type is defined in the model;
	// either the main model or in dependencies
	for _, s := range pf.Services {
		for _, rpc := range s.RPCs {
			if err := validateRPCDataType(pf.PackageName, s.Name, rpc.Name, rpc.RequestType, pf.Messages, m, packageNames); err != nil {
				return err
			}
			if err := validateRPCDataType(pf.PackageName, s.Name, rpc.Name, rpc.ResponseType, pf.Messages, m, packageNames); err != nil {
				return err
			}
		}
	}

	// validate if enum constants are uniqie across enums in the package
	if err := validateEnumConstants(pf); err != nil {
		return err
	}

	// TODO: add more checks here if needed

	return nil
}

func validateEnumConstants(pf *ProtoFile) error {
	m := make(map[string]bool)
	for _, en := range pf.Enums {
		for _, enc := range en.EnumConstants {
			if m[enc.Name] {
				return errors.New("Enum constant " + enc.Name + " is already defined in package " + pf.PackageName)
			}
			m[enc.Name] = true
		}
	}
	return nil
}

func validateSyntax(pf *ProtoFile) error {
	if pf.Syntax == "" {
		return errors.New("No syntax specified in the proto file")
	}
	return nil
}

func getDependencyPackageNames(m map[string]ProtoFile) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

type fd struct {
	name     string
	category string
}

func findFieldsToValidate(msgs []MessageElement) []fd {
	var fields []fd
	for _, msg := range msgs {
		for _, f := range msg.Fields {
			if f.Type.Category() == NamedDataTypeCategory {
				fields = append(fields, fd{name: f.Name, category: f.Type.Name()})
			}
		}
	}
	return fields
}

func validateFieldDataTypes(mainpkg string, f fd, msgs []MessageElement, enums []EnumElement, m map[string]ProtoFile, packageNames []string) error {
	found := false
	if strings.ContainsRune(f.category, '.') {
		inSamePkg, pkgName := isDatatypeInSamePackage(f.category, packageNames)
		if inSamePkg {
			// Check against normal and nested types & enums in same pacakge
			found = checkMsgOrEnumQualifiedName(mainpkg+"."+f.category, msgs, enums)
		} else {
			dpf, ok := m[pkgName]
			if !ok {
				msg := fmt.Sprintf("Package '%v' of Datatype: '%v' referenced in field: '%v' is not defined", pkgName, f.category, f.name)
				return errors.New(msg)
			}
			// Check against normal and nested types & enums in dependency pacakge
			found = checkMsgOrEnumQualifiedName(f.category, dpf.Messages, dpf.Enums)
		}
	} else {
		// Check both messages and enums
		found = checkMsgName(f.category, msgs)
		if !found {
			found = checkEnumName(f.category, enums)
		}
	}
	if !found {
		msg := fmt.Sprintf("Datatype: '%v' referenced in field: '%v' is not defined", f.category, f.name)
		return errors.New(msg)
	}
	return nil
}

func validateRPCDataType(mainpkg string, service string, rpc string, datatype NamedDataType,
	msgs []MessageElement, m map[string]ProtoFile, packageNames []string) error {
	found := false
	if strings.ContainsRune(datatype.Name(), '.') {
		inSamePkg, pkgName := isDatatypeInSamePackage(datatype.Name(), packageNames)
		if inSamePkg {
			// Check against normal as well as nested types in same pacakge
			found = checkMsgQualifiedName(mainpkg+"."+datatype.Name(), msgs)
		} else {
			dpf, ok := m[pkgName]
			if !ok {
				msg := fmt.Sprintf("Package '%v' of Datatype: '%v' referenced in RPC: '%v' of Service: '%v' is not defined OR is not a message type",
					pkgName, datatype.Name(), rpc, service)
				return errors.New(msg)
			}
			// Check against normal as well as nested fields in dependency pacakge
			found = checkMsgQualifiedName(datatype.Name(), dpf.Messages)
		}
	} else {
		found = checkMsgName(datatype.Name(), msgs)
	}
	if !found {
		msg := fmt.Sprintf("Datatype: '%v' referenced in RPC: '%v' of Service: '%v' is not defined OR is not a message type", datatype.Name(), rpc, service)
		return errors.New(msg)
	}
	return nil
}

func isDatatypeInSamePackage(datatypeName string, packageNames []string) (bool, string) {
	for _, pkg := range packageNames {
		if strings.HasPrefix(datatypeName, pkg+".") {
			return false, pkg
		}
	}
	return true, ""
}

func checkMsgName(m string, msgs []MessageElement) bool {
	for _, msg := range msgs {
		if msg.Name == m {
			return true
		}
	}
	return false
}

func checkEnumName(s string, enums []EnumElement) bool {
	for _, en := range enums {
		if en.Name == s {
			return true
		}
	}
	return false
}

func checkMsgOrEnumQualifiedName(s string, msgs []MessageElement, enums []EnumElement) bool {
	if checkMsgQualifiedName(s, msgs) {
		return true
	}
	return checkEnumQualifiedName(s, enums)
}

func checkMsgQualifiedName(s string, msgs []MessageElement) bool {
	for _, msg := range msgs {
		if msg.QualifiedName == s {
			return true
		}
	}
	return false
}

func checkEnumQualifiedName(s string, enums []EnumElement) bool {
	for _, en := range enums {
		if en.QualifiedName == s {
			return true
		}
	}
	return false
}

func parseDependencies(impr ImportModuleProvider, dependencies []string, m map[string]ProtoFile) error {
	for _, d := range dependencies {
		r, err := impr.Provide(d)
		if err != nil {
			msg := fmt.Sprintf("ImportModuleReader is unable to provide content of dependency module %v. Reason:: %v", d, err.Error())
			return errors.New(msg)
		}
		if r == nil {
			msg := fmt.Sprintf("ImportModuleReader is unable to provide reader for dependency module %v", d)
			return errors.New(msg)
		}

		dpf := ProtoFile{}
		if err := parse(r, &dpf); err != nil {
			msg := fmt.Sprintf("Unable to parse dependency %v. Reason:: %v", d, err.Error())
			return errors.New(msg)
		}

		if err := validateSyntax(&dpf); err != nil {
			return err
		}

		m[dpf.PackageName] = dpf
	}
	return nil
}
<commit_msg>Allow aliases in enums only if option allow_alias is specified<commit_after>package pbparser

import (
	"errors"
	"fmt"
	"strings"
)

func verify(pf *ProtoFile, p ImportModuleProvider) error {
	// validate syntax
	if err := validateSyntax(pf); err != nil {
		return err
	}

	if (len(pf.Dependencies) > 0 || len(pf.PublicDependencies) > 0) && p == nil {
		return errors.New("ImportModuleProvider is required to validate imports")
	}

	// make a map of dependency package to its parsed model...
	m := make(map[string]ProtoFile)

	// parse the dependencies...
	if err := parseDependencies(p, pf.Dependencies, m); err != nil {
		return err
	}

	// parse the public dependencies...
	if err := parseDependencies(p, pf.PublicDependencies, m); err != nil {
		return err
	}

	// collate the dependency package names...
	packageNames := getDependencyPackageNames(m)

	// validate if the NamedDataType fields of messages are all defined in the model;
	// either the main model or in dependencies
	fields := findFieldsToValidate(pf.Messages)
	for _, f := range fields {
		if err := validateFieldDataTypes(pf.PackageName, f, pf.Messages, pf.Enums, m, packageNames); err != nil {
			return err
		}
	}

	// validate if each rpc request/response type is defined in the model;
	// either the main model or in dependencies
	for _, s := range pf.Services {
		for _, rpc := range s.RPCs {
			if err := validateRPCDataType(pf.PackageName, s.Name, rpc.Name, rpc.RequestType, pf.Messages, m, packageNames); err != nil {
				return err
			}
			if err := validateRPCDataType(pf.PackageName, s.Name, rpc.Name, rpc.ResponseType, pf.Messages, m, packageNames); err != nil {
				return err
			}
		}
	}

	// validate if enum constants are uniqie across enums in the package
	if err := validateEnumConstants(pf); err != nil {
		return err
	}

	// allow aliases in enums only if option allow_alias is specified
	if err := validateEnumConstantTagAliases(pf); err != nil {
		return err
	}

	// TODO: add more checks here if needed

	return nil
}

func validateEnumConstantTagAliases(pf *ProtoFile) error {
	for _, en := range pf.Enums {
		m := make(map[int]bool)
		for _, enc := range en.EnumConstants {
			if m[enc.Tag] {
				if !isAllowAlias(&en) {
					return errors.New(enc.Name + " is reusing an enum value. If this is intended, set 'option allow_alias = true;' in the enum")
				}
			}
			m[enc.Tag] = true
		}
	}
	return nil
}

func isAllowAlias(en *EnumElement) bool {
	for _, op := range en.Options {
		if op.Name == "allow_alias" && op.Value == "true" {
			return true
		}
	}
	return false
}

func validateEnumConstants(pf *ProtoFile) error {
	m := make(map[string]bool)
	for _, en := range pf.Enums {
		for _, enc := range en.EnumConstants {
			if m[enc.Name] {
				return errors.New("Enum constant " + enc.Name + " is already defined in package " + pf.PackageName)
			}
			m[enc.Name] = true
		}
	}
	return nil
}

func validateSyntax(pf *ProtoFile) error {
	if pf.Syntax == "" {
		return errors.New("No syntax specified in the proto file")
	}
	return nil
}

func getDependencyPackageNames(m map[string]ProtoFile) []string {
	var keys []string
	for k := range m {
		keys = append(keys, k)
	}
	return keys
}

type fd struct {
	name     string
	category string
}

func findFieldsToValidate(msgs []MessageElement) []fd {
	var fields []fd
	for _, msg := range msgs {
		for _, f := range msg.Fields {
			if f.Type.Category() == NamedDataTypeCategory {
				fields = append(fields, fd{name: f.Name, category: f.Type.Name()})
			}
		}
	}
	return fields
}

func validateFieldDataTypes(mainpkg string, f fd, msgs []MessageElement, enums []EnumElement, m map[string]ProtoFile, packageNames []string) error {
	found := false
	if strings.ContainsRune(f.category, '.') {
		inSamePkg, pkgName := isDatatypeInSamePackage(f.category, packageNames)
		if inSamePkg {
			// Check against normal and nested types & enums in same pacakge
			found = checkMsgOrEnumQualifiedName(mainpkg+"."+f.category, msgs, enums)
		} else {
			dpf, ok := m[pkgName]
			if !ok {
				msg := fmt.Sprintf("Package '%v' of Datatype: '%v' referenced in field: '%v' is not defined", pkgName, f.category, f.name)
				return errors.New(msg)
			}
			// Check against normal and nested types & enums in dependency pacakge
			found = checkMsgOrEnumQualifiedName(f.category, dpf.Messages, dpf.Enums)
		}
	} else {
		// Check both messages and enums
		found = checkMsgName(f.category, msgs)
		if !found {
			found = checkEnumName(f.category, enums)
		}
	}
	if !found {
		msg := fmt.Sprintf("Datatype: '%v' referenced in field: '%v' is not defined", f.category, f.name)
		return errors.New(msg)
	}
	return nil
}

func validateRPCDataType(mainpkg string, service string, rpc string, datatype NamedDataType,
	msgs []MessageElement, m map[string]ProtoFile, packageNames []string) error {
	found := false
	if strings.ContainsRune(datatype.Name(), '.') {
		inSamePkg, pkgName := isDatatypeInSamePackage(datatype.Name(), packageNames)
		if inSamePkg {
			// Check against normal as well as nested types in same pacakge
			found = checkMsgQualifiedName(mainpkg+"."+datatype.Name(), msgs)
		} else {
			dpf, ok := m[pkgName]
			if !ok {
				msg := fmt.Sprintf("Package '%v' of Datatype: '%v' referenced in RPC: '%v' of Service: '%v' is not defined OR is not a message type",
					pkgName, datatype.Name(), rpc, service)
				return errors.New(msg)
			}
			// Check against normal as well as nested fields in dependency pacakge
			found = checkMsgQualifiedName(datatype.Name(), dpf.Messages)
		}
	} else {
		found = checkMsgName(datatype.Name(), msgs)
	}
	if !found {
		msg := fmt.Sprintf("Datatype: '%v' referenced in RPC: '%v' of Service: '%v' is not defined OR is not a message type", datatype.Name(), rpc, service)
		return errors.New(msg)
	}
	return nil
}

func isDatatypeInSamePackage(datatypeName string, packageNames []string) (bool, string) {
	for _, pkg := range packageNames {
		if strings.HasPrefix(datatypeName, pkg+".") {
			return false, pkg
		}
	}
	return true, ""
}

func checkMsgName(m string, msgs []MessageElement) bool {
	for _, msg := range msgs {
		if msg.Name == m {
			return true
		}
	}
	return false
}

func checkEnumName(s string, enums []EnumElement) bool {
	for _, en := range enums {
		if en.Name == s {
			return true
		}
	}
	return false
}

func checkMsgOrEnumQualifiedName(s string, msgs []MessageElement, enums []EnumElement) bool {
	if checkMsgQualifiedName(s, msgs) {
		return true
	}
	return checkEnumQualifiedName(s, enums)
}

func checkMsgQualifiedName(s string, msgs []MessageElement) bool {
	for _, msg := range msgs {
		if msg.QualifiedName == s {
			return true
		}
	}
	return false
}

func checkEnumQualifiedName(s string, enums []EnumElement) bool {
	for _, en := range enums {
		if en.QualifiedName == s {
			return true
		}
	}
	return false
}

func parseDependencies(impr ImportModuleProvider, dependencies []string, m map[string]ProtoFile) error {
	for _, d := range dependencies {
		r, err := impr.Provide(d)
		if err != nil {
			msg := fmt.Sprintf("ImportModuleReader is unable to provide content of dependency module %v. Reason:: %v", d, err.Error())
			return errors.New(msg)
		}
		if r == nil {
			msg := fmt.Sprintf("ImportModuleReader is unable to provide reader for dependency module %v", d)
			return errors.New(msg)
		}

		dpf := ProtoFile{}
		if err := parse(r, &dpf); err != nil {
			msg := fmt.Sprintf("Unable to parse dependency %v. Reason:: %v", d, err.Error())
			return errors.New(msg)
		}

		if err := validateSyntax(&dpf); err != nil {
			return err
		}

		m[dpf.PackageName] = dpf
	}
	return nil
}
<|endoftext|>


<commit_before>package phraseapp

func sendRequestPaginated(method, rawurl, ctype string, r io.Reader, status, page, perPage int) (io.ReadCloser, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}

	query := u.Query()
	query.Add("page", strconv.Itoa(page))
	query.Add("per_page", strconv.Itoa(perPage))

	u.RawQuery = query.Encode()

	req, err := http.NewRequest(method, u.String(), r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := send(req, status)
	if err != nil {
		return nil, err
	}

	return resp.Body, nil
}

func sendRequest(method, url, ctype string, r io.Reader, status int) (io.ReadCloser, error) {
	req, err := http.NewRequest(method, url, r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := send(req, status)
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func send(req *http.Request, status int) (*http.Response, error) {
	err := authenticate(req)
	if err != nil {
		return nil, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	err = handleResponseStatus(resp, status)
	if err != nil {
		resp.Body.Close()
	}
	return resp, err
}
<commit_msg>fixed imports<commit_after>package phraseapp

import (
	"io"
	"net/http"
	"net/url"
	"strconv"
)

func sendRequestPaginated(method, rawurl, ctype string, r io.Reader, status, page, perPage int) (io.ReadCloser, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}

	query := u.Query()
	query.Add("page", strconv.Itoa(page))
	query.Add("per_page", strconv.Itoa(perPage))

	u.RawQuery = query.Encode()

	req, err := http.NewRequest(method, u.String(), r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := send(req, status)
	if err != nil {
		return nil, err
	}

	return resp.Body, nil
}

func sendRequest(method, url, ctype string, r io.Reader, status int) (io.ReadCloser, error) {
	req, err := http.NewRequest(method, url, r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := send(req, status)
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func send(req *http.Request, status int) (*http.Response, error) {
	err := authenticate(req)
	if err != nil {
		return nil, err
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}

	err = handleResponseStatus(resp, status)
	if err != nil {
		resp.Body.Close()
	}
	return resp, err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

type endpointList []string

func (e *endpointList) Set(value string) error {
	// Accumulate endpoints when multiple options exists
	*e = append(*e, value)
	return nil
}
func (e *endpointList) String() string {
	return fmt.Sprint(*e)
}

// The set of accepted flags
var (
	srvEndpoints          endpointList
	srvPath               = flag.String("srv-path", "", "Service path (e.g github.com/foo/foo-srv)")
	srvBaseEndpoint       = flag.String("srv-base", "", "Service base endpoint. If omitted the service name will be used")
	srvDescr              = flag.String("srv-descr", "", "Service description")
	messageType           = flag.String("srv-message-type", "protobuf", "The message serialization to use. One of 'protobuf' or 'json'")
	initGitRepo           = flag.Bool("init-git-repo", true, "Initialize a git repo at the output folder")
	overwrite             = flag.Bool("overwrite-files", false, "Overwrite files in output folder if the folder already exists")
	useEtcd               = flag.Bool("etcd-enabled", true, "Use etcd for service discovery")
	useThrottle           = flag.Bool("throttle-enabled", false, "Use request throttle middleware")
	throttleMaxConcurrent = flag.Int("throttle-max-concurrent", 1000, "Max concurrent service requests")
	throttleMaxExecTime   = flag.Int("throttle-max-exec-time", 0, "Max execution time for a request in ms. No limit if set to 0")
	useTracer             = flag.Bool("tracer-enabled", true, "Use request tracing middleware")
	tracerQueueSize       = flag.Int("tracer-queue-size", 1000, "Max concurrent trace messages in queue")
	tracerTTL             = flag.Int("tracer-entry-ttl", 24*3600, "Trace entry TTL in seconds. TTL will be disabled if set to 0")

	pkgFolder = ""
	srvName   = ""
)

const (
	Protobuf = "protobuf"
	Json     = "json"
)

func init() {
	flag.Var(&srvEndpoints, "srv-endpoint", "An endpoint name (e.g AddUser), You may specify multiple endpoints by repeating the --srv-endpoint flag")
}

// Get the list of templates (*.tpl) under path. The method will scan the path recursively.
func getTemplates(path string) []string {
	list := make([]string, 0)
	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		// Recurse into dirs
		if info.IsDir() {
			return nil
		}

		if strings.HasSuffix(info.Name(), "_tpl") {
			list = append(list, path)
		}

		return nil
	})

	return list
}

func parseArgs() error {
	flag.Parse()

	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return fmt.Errorf("GOPATH env var not defined")
	}

	// Trim trailing slash if present
	*srvPath = strings.TrimRight(*srvPath, "/")

	if *srvPath == "" {
		return fmt.Errorf("Please specify the service path with the --srv-path option")
	}

	srvName = (*srvPath)[strings.LastIndex(*srvPath, "/")+1:]

	pkgFolder = fmt.Sprintf("%s/src/%s", gopath, *srvPath)
	info, err := os.Stat(pkgFolder)
	if err == nil {
		if !info.IsDir() {
			return fmt.Errorf("Specified package folder %s is actually a file", pkgFolder)
		}
		if !*overwrite {
			return fmt.Errorf("Specified package folder %s already exists. Use the --overwrite-files flag to proceed", pkgFolder)
		}
	}

	if *srvBaseEndpoint == "" {
		*srvBaseEndpoint = srvName
	}

	if len(srvEndpoints) == 0 {
		return fmt.Errorf("You need to specify at least one endpoint name using the --srv-endpoint flag")
	}

	if *messageType != Protobuf && *messageType != Json {
		return fmt.Errorf("Invalid service message type. Supported values are 'protobuf' and 'json'")
	}
	return nil
}

func initGit() error {
	fmt.Printf("\r\u274C  Init empty git repo")
	err := exec.Command("git", "init", pkgFolder).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Init empty git repo\n")
		return fmt.Errorf("Error initializing git repo: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Init empty git repo\n")

	return nil
}

func initBindings() error {
	fmt.Printf("\r\u274C  Creating initial protobuf bindings")
	err := exec.Command(
		"protoc",
		fmt.Sprintf("--%s=%s", "go_out", pkgFolder),
		fmt.Sprintf("--proto_path=%s", pkgFolder),
		fmt.Sprintf("%s/messages.proto", pkgFolder),
	).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Creating initial protobuf bindings\n")
		return fmt.Errorf("Error running protoc: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Creating initial protobuf bindings\n")

	return nil
}

func formatCode() error {
	fmt.Printf("\r\u274C  Running go fmt")
	err := exec.Command(
		"go",
		"fmt",
		fmt.Sprintf("%s/...", pkgFolder),
	).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Running go fmt\n")
		return fmt.Errorf("Error running go fmt: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Running go fmt\n")

	return nil
}

func genService() error {

	var err error

	fmt.Printf("Creating new usrv service at %s\n", pkgFolder)
	err = os.MkdirAll(pkgFolder, os.ModeDir|os.ModePerm)
	if err != nil {
		return fmt.Errorf("Error creating folder %s: %s", pkgFolder, err.Error())
	}

	// Build context
	context := map[string]interface{}{
		"PkgName":               "srv",
		"SrvPath":               *srvPath,
		"SrvName":               srvName,
		"SrvDescription":        *srvDescr,
		"SrvMessageType":        *messageType,
		"SrvBaseEndpoint":       *srvBaseEndpoint,
		"SrvEndpoints":          srvEndpoints,
		"UseEtcd":               *useEtcd,
		"UseThrottle":           *useThrottle,
		"ThrottleMaxConcurrent": *throttleMaxConcurrent,
		"ThrottleMaxExecTime":   *throttleMaxExecTime,
		"UseTracer":             *useTracer,
		"TracerQueueSize":       *tracerQueueSize,
		"TracerTTL":             *tracerTTL,
	}

	// Execute templates
	for _, tplFile := range getTemplates("templates") {
		// Depending on the selected message type exclude either protobuf template or json template
		if *messageType == Protobuf && strings.Contains(tplFile, "messages") {
			continue
		} else if *messageType == Json && strings.Contains(tplFile, ".proto") {
			continue
		}

		// Strip the _tpl extension and the templates/ prefix
		dstFilename := strings.Replace(
			strings.Replace(tplFile, "_tpl", "", 1),
			"templates/",
			"",
			1,
		)

		// Template contains a folder?
		if strings.Index(dstFilename, "/") != -1 {
			dstFolder := fmt.Sprintf(
				"%s/%s",
				pkgFolder,
				dstFilename[0:strings.LastIndex(dstFilename, "/")],
			)
			err = os.MkdirAll(dstFolder, os.ModeDir|os.ModePerm)
			if err != nil {
				return fmt.Errorf("Error creating folder %s: %s", dstFolder, err.Error())
			}
		}

		// Read template
		tplData, err := ioutil.ReadFile(tplFile)
		if err != nil {
			return fmt.Errorf("Error reading template %s: %s", tplFile, err.Error())
		}

		tpl, err := template.New(dstFilename).Parse(string(tplData))
		if err != nil {
			return fmt.Errorf("Error parsing template %s: %s", tplFile, err.Error())
		}

		dstPath := fmt.Sprintf("%s/%s", pkgFolder, dstFilename)
		outFile, err := os.Create(dstPath)
		if err != nil {
			return fmt.Errorf("Error opening %s for writing: %s", dstPath, err.Error())
		}
		fmt.Printf("\r\u231B  Processing: %s -> %s", tplFile, dstFilename)
		defer outFile.Close()

		err = tpl.Execute(outFile, context)
		if err != nil {
			fmt.Printf("\r\u274C  Processing: %s -> %s\n", tplFile, dstFilename)
			return fmt.Errorf("Error executing template %s: %s", tplFile, err.Error())
		}
		fmt.Printf("\r\u2713  Processing: %s -> %s\n", tplFile, dstFilename)
	}

	// Run go-fmt
	err = formatCode()
	if err != nil {
		return err
	}

	fmt.Printf("\u2713  Service created successfully")

	// Create initial bindings when using protobuf
	if *messageType == Protobuf {
		err = initBindings()
		if err != nil {
			return err
		}
	}

	// Init git repo
	if *initGitRepo {
		err = initGit()
		if err != nil {
			return err
		}
	}

	fmt.Println("\nNotes:")
	if *messageType == Protobuf {
		fmt.Printf("- The service protobuf messages are defined in %s/messages.proto.\n  After making any changes to the .proto file run 'go generate' to rebuild the go bindings.\n", pkgFolder)
	} else if *messageType == Json {
		fmt.Printf("- The service messages are defined in %s/messages.go.\n", pkgFolder)
	}
	fmt.Printf("- Add your service implementation inside %s/service.go.\n", pkgFolder)
	if *useEtcd {
		fmt.Printf("- The service is set up to use etcd for automatic configuration.\n  See %s/README.md for more details.\n", pkgFolder)
	}
	if *initGitRepo {
		fmt.Printf("- An empty git repo has been created for you.\n")
	}
	fmt.Printf("\n\n")
	return nil
}

func main() {
	// Parse args
	err := parseArgs()
	if err != nil {
		fmt.Fprintf(os.Stderr, "\u274C  %s\n\n", err.Error())
		flag.Usage()
		os.Exit(1)
	}

	// Preflight checks
	if *messageType == Protobuf {
		_, err = exec.LookPath("protoc-gen-go")
		if err != nil {
			fmt.Fprintf(os.Stderr, "\u274C  protoc-gen-go not be located in your current $PATH\n   Try running: go get -u github.com/golang/protobuf/{proto,protoc-gen-go}\n")
			os.Exit(1)
		}
	}

	// Create service
	err = genService()
	if err != nil {
		fmt.Printf("\u274C  %s\n", err.Error())
		os.Exit(1)
	}
}
<commit_msg>Fixed bug where .proto files where incorrectly excluded by the template filters when message type is set to protobuf<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"text/template"
)

type endpointList []string

func (e *endpointList) Set(value string) error {
	// Accumulate endpoints when multiple options exists
	*e = append(*e, value)
	return nil
}
func (e *endpointList) String() string {
	return fmt.Sprint(*e)
}

// The set of accepted flags
var (
	srvEndpoints          endpointList
	srvPath               = flag.String("srv-path", "", "Service path (e.g github.com/foo/foo-srv)")
	srvBaseEndpoint       = flag.String("srv-base", "", "Service base endpoint. If omitted the service name will be used")
	srvDescr              = flag.String("srv-descr", "", "Service description")
	messageType           = flag.String("srv-message-type", "protobuf", "The message serialization to use. One of 'protobuf' or 'json'")
	initGitRepo           = flag.Bool("init-git-repo", true, "Initialize a git repo at the output folder")
	overwrite             = flag.Bool("overwrite-files", false, "Overwrite files in output folder if the folder already exists")
	useEtcd               = flag.Bool("etcd-enabled", true, "Use etcd for service discovery")
	useThrottle           = flag.Bool("throttle-enabled", false, "Use request throttle middleware")
	throttleMaxConcurrent = flag.Int("throttle-max-concurrent", 1000, "Max concurrent service requests")
	throttleMaxExecTime   = flag.Int("throttle-max-exec-time", 0, "Max execution time for a request in ms. No limit if set to 0")
	useTracer             = flag.Bool("tracer-enabled", true, "Use request tracing middleware")
	tracerQueueSize       = flag.Int("tracer-queue-size", 1000, "Max concurrent trace messages in queue")
	tracerTTL             = flag.Int("tracer-entry-ttl", 24*3600, "Trace entry TTL in seconds. TTL will be disabled if set to 0")

	pkgFolder = ""
	srvName   = ""
)

const (
	Protobuf = "protobuf"
	Json     = "json"
)

func init() {
	flag.Var(&srvEndpoints, "srv-endpoint", "An endpoint name (e.g AddUser), You may specify multiple endpoints by repeating the --srv-endpoint flag")
}

// Get the list of templates (*.tpl) under path. The method will scan the path recursively.
func getTemplates(path string) []string {
	list := make([]string, 0)
	filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
		// Recurse into dirs
		if info.IsDir() {
			return nil
		}

		if strings.HasSuffix(info.Name(), "_tpl") {
			list = append(list, path)
		}

		return nil
	})

	return list
}

func parseArgs() error {
	flag.Parse()

	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		return fmt.Errorf("GOPATH env var not defined")
	}

	// Trim trailing slash if present
	*srvPath = strings.TrimRight(*srvPath, "/")

	if *srvPath == "" {
		return fmt.Errorf("Please specify the service path with the --srv-path option")
	}

	srvName = (*srvPath)[strings.LastIndex(*srvPath, "/")+1:]

	pkgFolder = fmt.Sprintf("%s/src/%s", gopath, *srvPath)
	info, err := os.Stat(pkgFolder)
	if err == nil {
		if !info.IsDir() {
			return fmt.Errorf("Specified package folder %s is actually a file", pkgFolder)
		}
		if !*overwrite {
			return fmt.Errorf("Specified package folder %s already exists. Use the --overwrite-files flag to proceed", pkgFolder)
		}
	}

	if *srvBaseEndpoint == "" {
		*srvBaseEndpoint = srvName
	}

	if len(srvEndpoints) == 0 {
		return fmt.Errorf("You need to specify at least one endpoint name using the --srv-endpoint flag")
	}

	if *messageType != Protobuf && *messageType != Json {
		return fmt.Errorf("Invalid service message type. Supported values are 'protobuf' and 'json'")
	}
	return nil
}

func initGit() error {
	fmt.Printf("\r\u274C  Init empty git repo")
	err := exec.Command("git", "init", pkgFolder).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Init empty git repo\n")
		return fmt.Errorf("Error initializing git repo: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Init empty git repo\n")

	return nil
}

func initBindings() error {
	fmt.Printf("\r\u274C  Creating initial protobuf bindings")
	err := exec.Command(
		"protoc",
		fmt.Sprintf("--%s=%s", "go_out", pkgFolder),
		fmt.Sprintf("--proto_path=%s", pkgFolder),
		fmt.Sprintf("%s/messages.proto", pkgFolder),
	).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Creating initial protobuf bindings\n")
		return fmt.Errorf("Error running protoc: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Creating initial protobuf bindings\n")

	return nil
}

func formatCode() error {
	fmt.Printf("\r\u274C  Running go fmt")
	err := exec.Command(
		"go",
		"fmt",
		fmt.Sprintf("%s/...", pkgFolder),
	).Run()
	if err != nil {
		fmt.Printf("\r\u274C  Running go fmt\n")
		return fmt.Errorf("Error running go fmt: %s", err.Error())
	}
	fmt.Printf("\r\u2713  Running go fmt\n")

	return nil
}

func genService() error {

	var err error

	fmt.Printf("Creating new usrv service at %s\n", pkgFolder)
	err = os.MkdirAll(pkgFolder, os.ModeDir|os.ModePerm)
	if err != nil {
		return fmt.Errorf("Error creating folder %s: %s", pkgFolder, err.Error())
	}

	// Build context
	context := map[string]interface{}{
		"PkgName":               "srv",
		"SrvPath":               *srvPath,
		"SrvName":               srvName,
		"SrvDescription":        *srvDescr,
		"SrvMessageType":        *messageType,
		"SrvBaseEndpoint":       *srvBaseEndpoint,
		"SrvEndpoints":          srvEndpoints,
		"UseEtcd":               *useEtcd,
		"UseThrottle":           *useThrottle,
		"ThrottleMaxConcurrent": *throttleMaxConcurrent,
		"ThrottleMaxExecTime":   *throttleMaxExecTime,
		"UseTracer":             *useTracer,
		"TracerQueueSize":       *tracerQueueSize,
		"TracerTTL":             *tracerTTL,
	}

	// Execute templates
	for _, tplFile := range getTemplates("templates") {
		// Depending on the selected message type exclude either protobuf template or json template
		if *messageType == Protobuf && strings.Contains(tplFile, "messages.go") {
			continue
		} else if *messageType == Json && strings.Contains(tplFile, ".proto") {
			continue
		}

		// Strip the _tpl extension and the templates/ prefix
		dstFilename := strings.Replace(
			strings.Replace(tplFile, "_tpl", "", 1),
			"templates/",
			"",
			1,
		)

		// Template contains a folder?
		if strings.Index(dstFilename, "/") != -1 {
			dstFolder := fmt.Sprintf(
				"%s/%s",
				pkgFolder,
				dstFilename[0:strings.LastIndex(dstFilename, "/")],
			)
			err = os.MkdirAll(dstFolder, os.ModeDir|os.ModePerm)
			if err != nil {
				return fmt.Errorf("Error creating folder %s: %s", dstFolder, err.Error())
			}
		}

		// Read template
		tplData, err := ioutil.ReadFile(tplFile)
		if err != nil {
			return fmt.Errorf("Error reading template %s: %s", tplFile, err.Error())
		}

		tpl, err := template.New(dstFilename).Parse(string(tplData))
		if err != nil {
			return fmt.Errorf("Error parsing template %s: %s", tplFile, err.Error())
		}

		dstPath := fmt.Sprintf("%s/%s", pkgFolder, dstFilename)
		outFile, err := os.Create(dstPath)
		if err != nil {
			return fmt.Errorf("Error opening %s for writing: %s", dstPath, err.Error())
		}
		fmt.Printf("\r\u231B  Processing: %s -> %s", tplFile, dstFilename)
		defer outFile.Close()

		err = tpl.Execute(outFile, context)
		if err != nil {
			fmt.Printf("\r\u274C  Processing: %s -> %s\n", tplFile, dstFilename)
			return fmt.Errorf("Error executing template %s: %s", tplFile, err.Error())
		}
		fmt.Printf("\r\u2713  Processing: %s -> %s\n", tplFile, dstFilename)
	}

	// Run go-fmt
	err = formatCode()
	if err != nil {
		return err
	}

	fmt.Printf("\u2713  Service created successfully")

	// Create initial bindings when using protobuf
	if *messageType == Protobuf {
		err = initBindings()
		if err != nil {
			return err
		}
	}

	// Init git repo
	if *initGitRepo {
		err = initGit()
		if err != nil {
			return err
		}
	}

	fmt.Println("\nNotes:")
	if *messageType == Protobuf {
		fmt.Printf("- The service protobuf messages are defined in %s/messages.proto.\n  After making any changes to the .proto file run 'go generate' to rebuild the go bindings.\n", pkgFolder)
	} else if *messageType == Json {
		fmt.Printf("- The service messages are defined in %s/messages.go.\n", pkgFolder)
	}
	fmt.Printf("- Add your service implementation inside %s/service.go.\n", pkgFolder)
	if *useEtcd {
		fmt.Printf("- The service is set up to use etcd for automatic configuration.\n  See %s/README.md for more details.\n", pkgFolder)
	}
	if *initGitRepo {
		fmt.Printf("- An empty git repo has been created for you.\n")
	}
	fmt.Printf("\n\n")
	return nil
}

func main() {
	// Parse args
	err := parseArgs()
	if err != nil {
		fmt.Fprintf(os.Stderr, "\u274C  %s\n\n", err.Error())
		flag.Usage()
		os.Exit(1)
	}

	// Preflight checks
	if *messageType == Protobuf {
		_, err = exec.LookPath("protoc-gen-go")
		if err != nil {
			fmt.Fprintf(os.Stderr, "\u274C  protoc-gen-go not be located in your current $PATH\n   Try running: go get -u github.com/golang/protobuf/{proto,protoc-gen-go}\n")
			os.Exit(1)
		}
	}

	// Create service
	err = genService()
	if err != nil {
		fmt.Printf("\u274C  %s\n", err.Error())
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package checkerlution

import (
	"github.com/couchbaselabs/logg"
	ng "github.com/tleyden/neurgo"
)

type Game struct {
	cortex               *ng.Cortex
	currentGameState     []float64
	currentPossibleMove  Move
	latestActuatorOutput []float64
}

func (game *Game) ChooseBestMove(gameState []float64, possibleMoves []Move) (bestMove Move) {

	game.currentGameState = gameState
	logg.LogTo("MAIN", "gameState: %v", gameState)

	var bestMoveRating []float64
	bestMoveRating = []float64{-1000000000}

	for _, move := range possibleMoves {

		logg.LogTo("MAIN", "possible move: %v", move)

		// present it to the neural net
		game.currentPossibleMove = move
		game.cortex.SyncSensors()
		game.cortex.SyncActuators()

		logg.LogTo("MAIN", "done sync'ing actuators")

		logg.LogTo("MAIN", "actuator output %v bestMoveRating: %v", game.latestActuatorOutput[0], bestMoveRating[0])
		if game.latestActuatorOutput[0] > bestMoveRating[0] {
			logg.LogTo("MAIN", "actuator output > bestMoveRating")
			bestMove = move
			bestMoveRating[0] = game.latestActuatorOutput[0]
		} else {
			logg.LogTo("MAIN", "actuator output < bestMoveRating, ignoring")
		}

	}
	return

}

func (game *Game) GameLoop() {

	// get a neurgo network
	game.CreateNeurgoCortex()
	cortex := game.cortex

	cortex.Run()

	for {

		// fetch game state and list of available moves from game server
		gameState, possibleMoves := game.FetchNewGameDocument()

		bestMove := game.ChooseBestMove(gameState, possibleMoves)

		game.PostChosenMove(bestMove)

		// when do we break out of the loop??

	}

	game.cortex.Shutdown()

}

func (game *Game) FetchNewGameDocument() (gameState []float64, possibleMoves []Move) {

	// TODO: this should be
	// - pulled from server
	// - parsed into json
	// - data structs should be extracted from json

	gameState = make([]float64, 32)

	possibleMove1 := Move{
		startLocation:   0,
		isCurrentlyKing: -1,
		endLocation:     1.0,
		willBecomeKing:  -0.5,
		captureValue:    1,
	}

	possibleMove2 := Move{
		startLocation:   1,
		isCurrentlyKing: -0.5,
		endLocation:     0.0,
		willBecomeKing:  0.5,
		captureValue:    0,
	}

	possibleMoves = []Move{possibleMove1, possibleMove2}
	return
}

func (game *Game) PostChosenMove(move Move) {
	logg.LogTo("MAIN", "chosen move: %v", move)
}

func (game *Game) CreateNeurgoCortex() {

	nodeId := ng.NewCortexId("cortex")
	game.cortex = &ng.Cortex{
		NodeId: nodeId,
	}
	game.CreateSensors()
	game.CreateActuator()
	game.CreateNeuron()
	game.ConnectNodes()
}

func (game *Game) ConnectNodes() {

	cortex := game.cortex

	cortex.Init()

	// connect sensors -> neuron(s)
	for _, sensor := range cortex.Sensors {
		for _, neuron := range cortex.Neurons {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}
	}

	// connect neuron to actuator
	for _, neuron := range cortex.Neurons {
		for _, actuator := range cortex.Actuators {
			neuron.ConnectOutbound(actuator)
			actuator.ConnectInbound(neuron)
		}
	}

}

func (game *Game) CreateNeuron() {
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableSigmoid(),
		NodeId:             ng.NewNeuronId("Neuron", 0.25),
		Bias:               ng.RandomBias(),
	}
	game.cortex.SetNeurons([]*ng.Neuron{neuron})
}

func (game *Game) CreateActuator() {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuatorFunc := func(outputs []float64) {
		logg.LogTo("MAIN", "actuator func called with: %v", outputs)
		game.latestActuatorOutput = outputs
		game.cortex.SyncChan <- actuatorNodeId // TODO: this should be in actuator itself, not in this function
	}
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: actuatorFunc,
	}
	game.cortex.SetActuators([]*ng.Actuator{actuator})

}

func (game *Game) CreateSensors() {

	sensorLayer := 0.0

	sensorFuncGameState := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func game state called")
		return game.currentGameState
	}
	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: sensorFuncGameState,
	}

	sensorFuncPossibleMove := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func possible move called")
		return game.currentPossibleMove.VectorRepresentation()
	}
	sensorPossibleMoveNodeId := ng.NewSensorId("SensorPossibleMove", sensorLayer)
	sensorPossibleMove := &ng.Sensor{
		NodeId:         sensorPossibleMoveNodeId,
		VectorLength:   5, // start_location, is_king, final_location, will_be_king, amt_would_capture
		SensorFunction: sensorFuncPossibleMove,
	}
	game.cortex.SetSensors([]*ng.Sensor{sensorGameState, sensorPossibleMove})

}
<commit_msg>refactor http get into its own method<commit_after>package checkerlution

import (
	"github.com/couchbaselabs/logg"
	ng "github.com/tleyden/neurgo"
)

type Game struct {
	cortex               *ng.Cortex
	currentGameState     []float64
	currentPossibleMove  Move
	latestActuatorOutput []float64
}

func (game *Game) GameLoop() {

	client := Client{}

	// get a neurgo network
	game.CreateNeurgoCortex()
	cortex := game.cortex

	cortex.Run()

	for {

		// fetch game state and list of available moves from game server
		gameState, possibleMoves := client.FetchNewGameDocument()

		bestMove := game.ChooseBestMove(gameState, possibleMoves)

		game.PostChosenMove(bestMove)

		// when do we break out of the loop??

	}

	game.cortex.Shutdown()

}

func (game *Game) ChooseBestMove(gameState []float64, possibleMoves []Move) (bestMove Move) {

	game.currentGameState = gameState
	logg.LogTo("MAIN", "gameState: %v", gameState)

	var bestMoveRating []float64
	bestMoveRating = []float64{-1000000000}

	for _, move := range possibleMoves {

		logg.LogTo("MAIN", "possible move: %v", move)

		// present it to the neural net
		game.currentPossibleMove = move
		game.cortex.SyncSensors()
		game.cortex.SyncActuators()

		logg.LogTo("MAIN", "done sync'ing actuators")

		logg.LogTo("MAIN", "actuator output %v bestMoveRating: %v", game.latestActuatorOutput[0], bestMoveRating[0])
		if game.latestActuatorOutput[0] > bestMoveRating[0] {
			logg.LogTo("MAIN", "actuator output > bestMoveRating")
			bestMove = move
			bestMoveRating[0] = game.latestActuatorOutput[0]
		} else {
			logg.LogTo("MAIN", "actuator output < bestMoveRating, ignoring")
		}

	}
	return

}

func (game *Game) PostChosenMove(move Move) {
	logg.LogTo("MAIN", "chosen move: %v", move)
}

func (game *Game) CreateNeurgoCortex() {

	nodeId := ng.NewCortexId("cortex")
	game.cortex = &ng.Cortex{
		NodeId: nodeId,
	}
	game.CreateSensors()
	game.CreateActuator()
	game.CreateNeuron()
	game.ConnectNodes()
}

func (game *Game) ConnectNodes() {

	cortex := game.cortex

	cortex.Init()

	// connect sensors -> neuron(s)
	for _, sensor := range cortex.Sensors {
		for _, neuron := range cortex.Neurons {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}
	}

	// connect neuron to actuator
	for _, neuron := range cortex.Neurons {
		for _, actuator := range cortex.Actuators {
			neuron.ConnectOutbound(actuator)
			actuator.ConnectInbound(neuron)
		}
	}

}

func (game *Game) CreateNeuron() {
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableSigmoid(),
		NodeId:             ng.NewNeuronId("Neuron", 0.25),
		Bias:               ng.RandomBias(),
	}
	game.cortex.SetNeurons([]*ng.Neuron{neuron})
}

func (game *Game) CreateActuator() {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuatorFunc := func(outputs []float64) {
		logg.LogTo("MAIN", "actuator func called with: %v", outputs)
		game.latestActuatorOutput = outputs
		game.cortex.SyncChan <- actuatorNodeId // TODO: this should be in actuator itself, not in this function
	}
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: actuatorFunc,
	}
	game.cortex.SetActuators([]*ng.Actuator{actuator})

}

func (game *Game) CreateSensors() {

	sensorLayer := 0.0

	sensorFuncGameState := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func game state called")
		return game.currentGameState
	}
	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: sensorFuncGameState,
	}

	sensorFuncPossibleMove := func(syncCounter int) []float64 {
		logg.LogTo("MAIN", "sensor func possible move called")
		return game.currentPossibleMove.VectorRepresentation()
	}
	sensorPossibleMoveNodeId := ng.NewSensorId("SensorPossibleMove", sensorLayer)
	sensorPossibleMove := &ng.Sensor{
		NodeId:         sensorPossibleMoveNodeId,
		VectorLength:   5, // start_location, is_king, final_location, will_be_king, amt_would_capture
		SensorFunction: sensorFuncPossibleMove,
	}
	game.cortex.SetSensors([]*ng.Sensor{sensorGameState, sensorPossibleMove})

}
<|endoftext|>


<commit_before>package util

func Map(x, inmin, inmax, outmin, outmax int64) int64 {
	return (x-inmin)*(outmax-outmin)/(inmax-inmin) + outmin
}
<commit_msg>add doc<commit_after>// Package util contains utility functions.
package util

// Map re-maps a number from one range to another.
//
// Example:
//
//	val := Map(angle, 0, 180, 1000, 2000)
//
func Map(x, inmin, inmax, outmin, outmax int64) int64 {
	return (x-inmin)*(outmax-outmin)/(inmax-inmin) + outmin
}
<|endoftext|>


<commit_before>package serial

/*

// MSDN article on Serial Communications:
// http://msdn.microsoft.com/en-us/library/ff802693.aspx

// Arduino Playground article on serial communication with Windows API:
// http://playground.arduino.cc/Interfacing/CPPWindows

#include <stdlib.h>
#include <windows.h>

*/
import "C"
import "syscall"

// OS dependent values

const devFolder = ""
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type windowsSerialPort struct {
	Handle int
}

func GetPortsList() ([]string, error) {
	return nil, nil
	/*
	   private static String[] getWindowsPortNames(Pattern pattern, Comparator<String> comparator) {
	       String[] portNames = serialInterface.getSerialPortNames();
	       if(portNames == null){
	           return new String[]{};
	       }
	       TreeSet<String> ports = new TreeSet<String>(comparator);
	       for(String portName : portNames){
	           if(pattern.matcher(portName).find()){
	               ports.add(portName);
	           }
	       }
	       return ports.toArray(new String[ports.size()]);
	   }
	*/
}

func (port *windowsSerialPort) Close() error {
	return nil
}

func (port *windowsSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.Handle, p)
}

func (port *windowsSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.Handle, p)
}

func OpenPort(portName string, useTIOCEXCL bool) (SerialPort, error) {
	portName = "\\\\.\\" + portName

	handle, err := syscall.CreateFile(portName, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED, 0)
	//handle := C.CreateFile(C.CString(portName), C.GENERIC_READ | C.GENERIC_WRITE, 0, 0, C.OPEN_EXISTING, C.FILE_FLAG_OVERLAPPED, 0)

	/*
	   JNIEXPORT jlong JNICALL Java_jssc_SerialNativeInterface_openPort(JNIEnv *env, jobject object, jstring portName, jboolean useTIOCEXCL){
	       char prefix[] = "\\\\.\\";
	       const char* port = env->GetStringUTFChars(portName, JNI_FALSE);

	       //since 2.1.0 -> string concat fix
	       char portFullName[strlen(prefix) + strlen(port) + 1];
	       strcpy(portFullName, prefix);
	       strcat(portFullName, port);
	       //<- since 2.1.0

	       HANDLE hComm = CreateFile(portFullName,
	                          	   	  GENERIC_READ | GENERIC_WRITE,
	                          	   	  0,
	                          	   	  0,
	                          	   	  OPEN_EXISTING,
	                          	   	  FILE_FLAG_OVERLAPPED,
	                          	   	  0);
	       env->ReleaseStringUTFChars(portName, port);
	*/
	/*
		if handle != syscall.INVALID_HANDLE_VALUE {
			var dcb C.DCB
			if C.GetCommState(handle, &dcb) != 0 {
				C.CloseHandle(handle)
				return nil,
			}
		}
	*/
	/*    //since 2.3.0 ->
	    if(hComm != INVALID_HANDLE_VALUE){
	    	DCB *dcb = new DCB();
	    	if(!GetCommState(hComm, dcb)){
	    		CloseHandle(hComm);//since 2.7.0
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_INCORRECT_SERIAL_PORT;//(-4)Incorrect serial port
	    	}
	    	delete dcb;
	    }
	    else {
	    	DWORD errorValue = GetLastError();
	    	if(errorValue == ERROR_ACCESS_DENIED){
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_BUSY;//(-1)Port busy
	    	}
	    	else if(errorValue == ERROR_FILE_NOT_FOUND){
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_NOT_FOUND;//(-2)Port not found
	    	}
	    }
	    //<- since 2.3.0
	    return (jlong)hComm;//since 2.4.0 changed to jlong
	};

	*/
}

// vi:ts=2
<commit_msg>Windows: added native getPortList<commit_after>package serial

/*

// MSDN article on Serial Communications:
// http://msdn.microsoft.com/en-us/library/ff802693.aspx

// Arduino Playground article on serial communication with Windows API:
// http://playground.arduino.cc/Interfacing/CPPWindows

#include <stdlib.h>
#include <windows.h>

//HANDLE invalid = INVALID_HANDLE_VALUE;

HKEY INVALID_PORT_LIST = 0;

HKEY openPortList() {
	HKEY handle;
	LPCSTR lpSubKey = "HARDWARE\\DEVICEMAP\\SERIALCOMM\\";
	DWORD res = RegOpenKeyExA(HKEY_LOCAL_MACHINE, lpSubKey, 0, KEY_READ, &handle);
	if (res != ERROR_SUCCESS)
		return INVALID_PORT_LIST;
	else
		return handle;
}

int countPortList(HKEY handle) {
	int count = 0;
	for (;;) {
		char name[256];
		DWORD nameSize = 256;
		DWORD res = RegEnumValueA(handle, count, name, &nameSize, NULL, NULL, NULL, NULL);
		if (res != ERROR_SUCCESS)
			return count;
		count++;
	}
}

char *getInPortList(HKEY handle, int i) {
	byte *data = (byte *) malloc(256);
	DWORD dataSize = 256;
	char name[256];
	DWORD nameSize = 256;
	DWORD res = RegEnumValueA(handle, i, name, &nameSize, NULL, NULL, data, &dataSize);
	if (res != ERROR_SUCCESS) {
		free(data);
		return NULL;
	}
	return data;
}

void closePortList(HKEY handle) {
	CloseHandle(handle);
}

*/
import "C"
import "syscall"
import "unsafe"

// OS dependent values

const devFolder = ""
const regexFilter = "(ttyS|ttyUSB|ttyACM|ttyAMA|rfcomm|ttyO)[0-9]{1,3}"

// opaque type that implements SerialPort interface for linux
type windowsSerialPort struct {
	Handle int
}

func GetPortsList() ([]string, error) {
	portList := C.openPortList()
	if portList == C.INVALID_PORT_LIST {
		return nil, &SerialPortError{code: ERROR_ENUMERATING_PORTS}
	}
	n := C.countPortList(portList)

	list := make([]string, n)
	for i := range list {
		portName := C.getInPortList(portList, C.int(i))
		list[i] = C.GoString(portName)
		C.free(unsafe.Pointer(portName))
	}

	C.closePortList(portList)
	return list, nil
}

func (port *windowsSerialPort) Close() error {
	return nil
}

func (port *windowsSerialPort) Read(p []byte) (n int, err error) {
	return syscall.Read(port.Handle, p)
}

func (port *windowsSerialPort) Write(p []byte) (n int, err error) {
	return syscall.Write(port.Handle, p)
}

func OpenPort(portName string, useTIOCEXCL bool) (SerialPort, error) {
	portName = "\\\\.\\" + portName

	handle, err := syscall.CreateFile(portName, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED, 0)
	//handle := C.CreateFile(C.CString(portName), C.GENERIC_READ | C.GENERIC_WRITE, 0, 0, C.OPEN_EXISTING, C.FILE_FLAG_OVERLAPPED, 0)

	/*
	   JNIEXPORT jlong JNICALL Java_jssc_SerialNativeInterface_openPort(JNIEnv *env, jobject object, jstring portName, jboolean useTIOCEXCL){
	       char prefix[] = "\\\\.\\";
	       const char* port = env->GetStringUTFChars(portName, JNI_FALSE);

	       //since 2.1.0 -> string concat fix
	       char portFullName[strlen(prefix) + strlen(port) + 1];
	       strcpy(portFullName, prefix);
	       strcat(portFullName, port);
	       //<- since 2.1.0

	       HANDLE hComm = CreateFile(portFullName,
	                          	   	  GENERIC_READ | GENERIC_WRITE,
	                          	   	  0,
	                          	   	  0,
	                          	   	  OPEN_EXISTING,
	                          	   	  FILE_FLAG_OVERLAPPED,
	                          	   	  0);
	       env->ReleaseStringUTFChars(portName, port);
	*/
	/*
		if handle != syscall.INVALID_HANDLE_VALUE {
			var dcb C.DCB
			if C.GetCommState(handle, &dcb) != 0 {
				C.CloseHandle(handle)
				return nil,
			}
		}
	*/
	/*    //since 2.3.0 ->
	    if(hComm != INVALID_HANDLE_VALUE){
	    	DCB *dcb = new DCB();
	    	if(!GetCommState(hComm, dcb)){
	    		CloseHandle(hComm);//since 2.7.0
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_INCORRECT_SERIAL_PORT;//(-4)Incorrect serial port
	    	}
	    	delete dcb;
	    }
	    else {
	    	DWORD errorValue = GetLastError();
	    	if(errorValue == ERROR_ACCESS_DENIED){
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_BUSY;//(-1)Port busy
	    	}
	    	else if(errorValue == ERROR_FILE_NOT_FOUND){
	    		hComm = (HANDLE)jssc_SerialNativeInterface_ERR_PORT_NOT_FOUND;//(-2)Port not found
	    	}
	    }
	    //<- since 2.3.0
	    return (jlong)hComm;//since 2.4.0 changed to jlong
	};

	*/
}

// vi:ts=2
<|endoftext|>