<commit_before>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package util

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/x/lib/envvar"
)

const (
	// TODO(nlacasse): Rename to JIRI_PROFILE.
	jiriProfileEnv = "V23_PROFILE"
	javaEnv        = "JAVA_HOME"
)

// TODO(nlacasse): Rename this.
// VanadiumEnvironment returns the environment variables setting for the
// project. The util package captures the original state of the relevant
// environment variables when the tool is initialized and every invocation of
// this function updates this original state according to the jiri tool
// configuration.
//
// By default, the Go and VDL workspaces are added to the GOPATH and VDLPATH
// environment variables respectively. In addition, the V23_PROFILE environment
// variable can be used to activate an environment variable setting for various
// development profiles of the project (e.g. arm, android, java, or nacl).
// Unlike the default setting, the setting enabled by the V23_PROFILE
// environment variable can override existing environment.
func VanadiumEnvironment(ctx *tool.Context) (*envvar.Vars, error) {
	env := envvar.VarsFromOS()
	root, err := project.JiriRoot()
	if err != nil {
		return nil, err
	}
	config, err := LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	env.Set("CGO_ENABLED", "1")
	if err := setGoPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setVdlPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if profile := os.Getenv(jiriProfileEnv); profile != "" {
		fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q.
This can override values of existing environment variables.
`, profile)
		switch profile {
		case "android":
			// Cross-compilation for android on linux.
			if err := setAndroidEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "arm":
			// Cross-compilation for arm on linux.
			if err := setArmEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "java":
			// Building of a Go shared library for Java.
			if err := setJavaEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "nacl":
			// Cross-compilation for nacl.
			if err := setNaclEnv(ctx, env, root); err != nil {
				return nil, err
			}
		default:
			fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile)
		}
	}
	if err := setSyncbaseEnv(ctx, env, root); err != nil {
		return nil, err
	}
	return env, nil
}

// setJavaEnv sets the environment variables used for building a Go
// shared library that is invoked from Java code. If Java is not
// installed on the host, this function is a no-op.
func setJavaEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	jdkHome := os.Getenv(javaEnv)
	if jdkHome == "" {
		return nil
	}

	// Compile using Java Go 1.5 (installed via 'jiri profile install java').
	javaGoDir := filepath.Join(root, "third_party", "java", "go")

	// Set Go-specific environment variables.
	cflags := env.GetTokens("CGO_CFLAGS", " ")
	cflags = append(cflags, filepath.Join("-I"+jdkHome, "include"), filepath.Join("-I"+jdkHome, "include", runtime.GOOS))
	env.SetTokens("CGO_CFLAGS", cflags, " ")
	env.Set("GOROOT", javaGoDir)

	// Update PATH.
	if _, err := ctx.Run().Stat(javaGoDir); err != nil {
		return fmt.Errorf("Couldn't find java go installation directory %s: did you run \"jiri profile install java\"?", javaGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(javaGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setAndroidEnv sets the environment variables used for android
// cross-compilation.
func setAndroidEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	// Compile using Android Go 1.5 (installed via
	// 'jiri profile install android').
	androidGoDir := filepath.Join(root, "third_party", "android", "go")

	// Set Go-specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "7")
	env.Set("GOOS", "android")
	env.Set("GOROOT", androidGoDir)

	// Update PATH.
	if _, err := ctx.Run().Stat(androidGoDir); err != nil {
		return fmt.Errorf("Couldn't find android Go installation directory %s: did you run \"jiri profile install android\"?", androidGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(androidGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setArmEnv sets the environment variables used for arm cross-compilation.
func setArmEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "6")
	env.Set("GOOS", "linux")

	// Add the paths to arm cross-compilation tools to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "cout", "xgcc", "cross_arm"),
		filepath.Join(root, "third_party", "repos", "go_arm", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setGoPath adds the paths of Go workspaces to the GOPATH variable.
func setGoPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "GOPATH", root, config.GoWorkspaces(), "")
}

// setVdlPath adds the paths of VDL workspaces to the VDLPATH variable.
func setVdlPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "VDLPATH", root, config.VDLWorkspaces(), "src")
}

// setPathHelper is a utility function for setting path environment
// variables for different types of workspaces.
func setPathHelper(ctx *tool.Context, env *envvar.Vars, name, root string, workspaces []string, suffix string) error {
	path := env.GetTokens(name, ":")
	projects, _, err := project.ReadManifest(ctx)
	if err != nil {
		return err
	}
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a jiri project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single jiri project,
			// such as: $JIRI_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple jiri projects,
			// such as: $JIRI_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := ctx.Run().Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	env.SetTokens(name, path, ":")
	return nil
}

// TODO(nlacasse): Move setNaclEnv and setSyncbaseEnv out of jiri since they
// are not general-purpose.

// setNaclEnv sets the environment variables used for nacl
// cross-compilation.
func setNaclEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	env.Set("GOARCH", "amd64p32")
	env.Set("GOOS", "nacl")

	// Add the path to nacl cross-compiler to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "repos", "go_ppapi", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setSyncbaseEnv adds the LevelDB third-party C++ libraries Vanadium
// Go code depends on to the CGO_CFLAGS and CGO_LDFLAGS variables.
func setSyncbaseEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	libs := []string{
		"leveldb",
		"snappy",
	}
	// TODO(rogulenko): get these vars from a config file.
	goos, goarch := env.Get("GOOS"), env.Get("GOARCH")
	if goos == "" {
		goos = runtime.GOOS
	}
	if goarch == "" {
		goarch = runtime.GOARCH
	}
	for _, lib := range libs {
		cflags := env.GetTokens("CGO_CFLAGS", " ")
		cxxflags := env.GetTokens("CGO_CXXFLAGS", " ")
		ldflags := env.GetTokens("CGO_LDFLAGS", " ")
		dir, err := ThirdPartyCCodePath(goos, goarch)
		if err != nil {
			return err
		}
		dir = filepath.Join(dir, lib)
		if _, err := ctx.Run().Stat(dir); err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			continue
		}
		cflags = append(cflags, filepath.Join("-I"+dir, "include"))
		cxxflags = append(cxxflags, filepath.Join("-I"+dir, "include"))
		ldflags = append(ldflags, filepath.Join("-L"+dir, "lib"))
		if runtime.GOARCH == "linux" {
			ldflags = append(ldflags, "-Wl,-rpath", filepath.Join(dir, "lib"))
		}
		env.SetTokens("CGO_CFLAGS", cflags, " ")
		env.SetTokens("CGO_CXXFLAGS", cxxflags, " ")
		env.SetTokens("CGO_LDFLAGS", ldflags, " ")
	}
	return nil
}
<commit_msg>Rename V23_PROFILE variable to JIRI_PROFILE.<commit_after>// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package util

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"strings"

	"v.io/jiri/project"
	"v.io/jiri/tool"
	"v.io/x/lib/envvar"
)

const (
	jiriProfileEnv = "JIRI_PROFILE"
	javaEnv        = "JAVA_HOME"
)

// TODO(nlacasse): Rename this.
// VanadiumEnvironment returns the environment variables setting for the
// project. The util package captures the original state of the relevant
// environment variables when the tool is initialized and every invocation of
// this function updates this original state according to the jiri tool
// configuration.
//
// By default, the Go and VDL workspaces are added to the GOPATH and VDLPATH
// environment variables respectively. In addition, the JIRI_PROFILE
// environment variable can be used to activate an environment variable setting
// for various development profiles of the project (e.g. arm, android, java, or
// nacl).  Unlike the default setting, the setting enabled by the JIRI_PROFILE
// environment variable can override existing environment.
func VanadiumEnvironment(ctx *tool.Context) (*envvar.Vars, error) {
	env := envvar.VarsFromOS()
	root, err := project.JiriRoot()
	if err != nil {
		return nil, err
	}
	config, err := LoadConfig(ctx)
	if err != nil {
		return nil, err
	}
	env.Set("CGO_ENABLED", "1")
	if err := setGoPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if err := setVdlPath(ctx, env, root, config); err != nil {
		return nil, err
	}
	if profile := os.Getenv(jiriProfileEnv); profile != "" {
		fmt.Fprintf(ctx.Stdout(), `NOTE: Enabling environment variable setting for %q.
This can override values of existing environment variables.
`, profile)
		switch profile {
		case "android":
			// Cross-compilation for android on linux.
			if err := setAndroidEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "arm":
			// Cross-compilation for arm on linux.
			if err := setArmEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "java":
			// Building of a Go shared library for Java.
			if err := setJavaEnv(ctx, env, root); err != nil {
				return nil, err
			}
		case "nacl":
			// Cross-compilation for nacl.
			if err := setNaclEnv(ctx, env, root); err != nil {
				return nil, err
			}
		default:
			fmt.Fprintf(ctx.Stderr(), "Unknown environment profile %q", profile)
		}
	}
	if err := setSyncbaseEnv(ctx, env, root); err != nil {
		return nil, err
	}
	return env, nil
}

// setJavaEnv sets the environment variables used for building a Go
// shared library that is invoked from Java code. If Java is not
// installed on the host, this function is a no-op.
func setJavaEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	jdkHome := os.Getenv(javaEnv)
	if jdkHome == "" {
		return nil
	}

	// Compile using Java Go 1.5 (installed via 'jiri profile install java').
	javaGoDir := filepath.Join(root, "third_party", "java", "go")

	// Set Go-specific environment variables.
	cflags := env.GetTokens("CGO_CFLAGS", " ")
	cflags = append(cflags, filepath.Join("-I"+jdkHome, "include"), filepath.Join("-I"+jdkHome, "include", runtime.GOOS))
	env.SetTokens("CGO_CFLAGS", cflags, " ")
	env.Set("GOROOT", javaGoDir)

	// Update PATH.
	if _, err := ctx.Run().Stat(javaGoDir); err != nil {
		return fmt.Errorf("Couldn't find java go installation directory %s: did you run \"jiri profile install java\"?", javaGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(javaGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setAndroidEnv sets the environment variables used for android
// cross-compilation.
func setAndroidEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	// Compile using Android Go 1.5 (installed via
	// 'jiri profile install android').
	androidGoDir := filepath.Join(root, "third_party", "android", "go")

	// Set Go-specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "7")
	env.Set("GOOS", "android")
	env.Set("GOROOT", androidGoDir)

	// Update PATH.
	if _, err := ctx.Run().Stat(androidGoDir); err != nil {
		return fmt.Errorf("Couldn't find android Go installation directory %s: did you run \"jiri profile install android\"?", androidGoDir)
	}
	path := env.GetTokens("PATH", ":")
	path = append([]string{filepath.Join(androidGoDir, "bin")}, path...)
	env.SetTokens("PATH", path, ":")
	return nil
}

// setArmEnv sets the environment variables used for arm cross-compilation.
func setArmEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	// Set Go specific environment variables.
	env.Set("GOARCH", "arm")
	env.Set("GOARM", "6")
	env.Set("GOOS", "linux")

	// Add the paths to arm cross-compilation tools to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "cout", "xgcc", "cross_arm"),
		filepath.Join(root, "third_party", "repos", "go_arm", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setGoPath adds the paths of Go workspaces to the GOPATH variable.
func setGoPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "GOPATH", root, config.GoWorkspaces(), "")
}

// setVdlPath adds the paths of VDL workspaces to the VDLPATH variable.
func setVdlPath(ctx *tool.Context, env *envvar.Vars, root string, config *Config) error {
	return setPathHelper(ctx, env, "VDLPATH", root, config.VDLWorkspaces(), "src")
}

// setPathHelper is a utility function for setting path environment
// variables for different types of workspaces.
func setPathHelper(ctx *tool.Context, env *envvar.Vars, name, root string, workspaces []string, suffix string) error {
	path := env.GetTokens(name, ":")
	projects, _, err := project.ReadManifest(ctx)
	if err != nil {
		return err
	}
	for _, workspace := range workspaces {
		absWorkspace := filepath.Join(root, workspace, suffix)
		// Only append an entry to the path if the workspace is rooted
		// under a jiri project that exists locally or vice versa.
		for _, project := range projects {
			// We check if <project.Path> is a prefix of <absWorkspace> to
			// account for Go workspaces nested under a single jiri project,
			// such as: $JIRI_ROOT/release/projects/chat/go.
			//
			// We check if <absWorkspace> is a prefix of <project.Path> to
			// account for Go workspaces that span multiple jiri projects,
			// such as: $JIRI_ROOT/release/go.
			if strings.HasPrefix(absWorkspace, project.Path) || strings.HasPrefix(project.Path, absWorkspace) {
				if _, err := ctx.Run().Stat(filepath.Join(absWorkspace)); err == nil {
					path = append(path, absWorkspace)
					break
				}
			}
		}
	}
	env.SetTokens(name, path, ":")
	return nil
}

// TODO(nlacasse): Move setNaclEnv and setSyncbaseEnv out of jiri since they
// are not general-purpose.

// setNaclEnv sets the environment variables used for nacl
// cross-compilation.
func setNaclEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	env.Set("GOARCH", "amd64p32")
	env.Set("GOOS", "nacl")

	// Add the path to nacl cross-compiler to the PATH.
	path := env.GetTokens("PATH", ":")
	path = append([]string{
		filepath.Join(root, "third_party", "repos", "go_ppapi", "bin"),
	}, path...)
	env.SetTokens("PATH", path, ":")

	return nil
}

// setSyncbaseEnv adds the LevelDB third-party C++ libraries Vanadium
// Go code depends on to the CGO_CFLAGS and CGO_LDFLAGS variables.
func setSyncbaseEnv(ctx *tool.Context, env *envvar.Vars, root string) error {
	libs := []string{
		"leveldb",
		"snappy",
	}
	// TODO(rogulenko): get these vars from a config file.
	goos, goarch := env.Get("GOOS"), env.Get("GOARCH")
	if goos == "" {
		goos = runtime.GOOS
	}
	if goarch == "" {
		goarch = runtime.GOARCH
	}
	for _, lib := range libs {
		cflags := env.GetTokens("CGO_CFLAGS", " ")
		cxxflags := env.GetTokens("CGO_CXXFLAGS", " ")
		ldflags := env.GetTokens("CGO_LDFLAGS", " ")
		dir, err := ThirdPartyCCodePath(goos, goarch)
		if err != nil {
			return err
		}
		dir = filepath.Join(dir, lib)
		if _, err := ctx.Run().Stat(dir); err != nil {
			if !os.IsNotExist(err) {
				return err
			}
			continue
		}
		cflags = append(cflags, filepath.Join("-I"+dir, "include"))
		cxxflags = append(cxxflags, filepath.Join("-I"+dir, "include"))
		ldflags = append(ldflags, filepath.Join("-L"+dir, "lib"))
		if runtime.GOARCH == "linux" {
			ldflags = append(ldflags, "-Wl,-rpath", filepath.Join(dir, "lib"))
		}
		env.SetTokens("CGO_CFLAGS", cflags, " ")
		env.SetTokens("CGO_CXXFLAGS", cxxflags, " ")
		env.SetTokens("CGO_LDFLAGS", ldflags, " ")
	}
	return nil
}
<|endoftext|>


<commit_before>package gitlab

import "fmt"

// ValidateService handles communication with the validation related methods of
// the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
type ValidateService struct {
	client *Client
}

// LintResult represents the linting results.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
type LintResult struct {
	Status string   `json:"status"`
	Errors []string `json:"errors"`
}

// ProjectLintResult represents the linting results by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
type ProjectLintResult struct {
	Valid      bool     `json:"valid"`
	Errors     []string `json:"errors"`
	Warnings   []string `json:"warnings"`
	MergedYaml string   `json:"merged_yaml"`
}

// Lint validates .gitlab-ci.yml content.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
func (s *ValidateService) Lint(content string, options ...RequestOptionFunc) (*LintResult, *Response, error) {
	var opts struct {
		Content string `url:"content,omitempty" json:"content,omitempty"`
	}
	opts.Content = content

	req, err := s.client.NewRequest("POST", "ci/lint", &opts, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(LintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// ProjectLintOptions represents the available ProjectLint() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
type ProjectLintOptions struct {
	DryRun *bool `url:"dry_run,omitempty" json:"dry_run,omitempty"`
}

// ProjectLint validates .gitlab-ci.yml content by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
func (s *ValidateService) ProjectLint(pid interface{}, opt *ProjectLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/ci/lint", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, &opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(ProjectLintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// ProjectNamespaceLint represents the available ProjectNamespaceLint() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace
type ProjectNamespaceLintOptions struct {
	Content *string `url:"content,omitempty" json:"content,omitempty"`
	DryRun  *bool   `url:"dry_run,omitempty" json:"dry_run,omitempty"`
}

// ProjectNamespaceLint validates .gitlab-ci.yml content by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace
func (s *ValidateService) ProjectNamespaceLint(pid interface{}, opt *ProjectNamespaceLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/ci/lint", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, &opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(ProjectLintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}
<commit_msg>fix lint<commit_after>package gitlab

import "fmt"

// ValidateService handles communication with the validation related methods of
// the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
type ValidateService struct {
	client *Client
}

// LintResult represents the linting results.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
type LintResult struct {
	Status string   `json:"status"`
	Errors []string `json:"errors"`
}

// ProjectLintResult represents the linting results by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
type ProjectLintResult struct {
	Valid      bool     `json:"valid"`
	Errors     []string `json:"errors"`
	Warnings   []string `json:"warnings"`
	MergedYaml string   `json:"merged_yaml"`
}

// Lint validates .gitlab-ci.yml content.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/lint.html
func (s *ValidateService) Lint(content string, options ...RequestOptionFunc) (*LintResult, *Response, error) {
	var opts struct {
		Content string `url:"content,omitempty" json:"content,omitempty"`
	}
	opts.Content = content

	req, err := s.client.NewRequest("POST", "ci/lint", &opts, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(LintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// ProjectLintOptions represents the available ProjectLint() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
type ProjectLintOptions struct {
	DryRun *bool `url:"dry_run,omitempty" json:"dry_run,omitempty"`
}

// ProjectLint validates .gitlab-ci.yml content by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-projects-ci-configuration
func (s *ValidateService) ProjectLint(pid interface{}, opt *ProjectLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/ci/lint", pathEscape(project))

	req, err := s.client.NewRequest("GET", u, &opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(ProjectLintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}

// ProjectNamespaceLintOptions represents the available ProjectNamespaceLint() options.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace
type ProjectNamespaceLintOptions struct {
	Content *string `url:"content,omitempty" json:"content,omitempty"`
	DryRun  *bool   `url:"dry_run,omitempty" json:"dry_run,omitempty"`
}

// ProjectNamespaceLint validates .gitlab-ci.yml content by project.
//
// GitLab API docs: https://docs.gitlab.com/ee/api/lint.html#validate-a-ci-yaml-configuration-with-a-namespace
func (s *ValidateService) ProjectNamespaceLint(pid interface{}, opt *ProjectNamespaceLintOptions, options ...RequestOptionFunc) (*ProjectLintResult, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/ci/lint", pathEscape(project))

	req, err := s.client.NewRequest("POST", u, &opt, options)
	if err != nil {
		return nil, nil, err
	}

	l := new(ProjectLintResult)
	resp, err := s.client.Do(req, l)
	if err != nil {
		return nil, resp, err
	}

	return l, resp, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"time"
)

var (
	delay    = flag.Duration("d", 0, "delay between updates")
	duration = flag.Duration("D", 0, "duration to output continually")

	pid   = flag.Int("p", 0, "process to inspect")
	pname = flag.String("n", "", "name of process to inspec")

	socketregex = regexp.MustCompile(`\.gmx\.[0-9]+\.0`)
)

type conn struct {
	net.Conn
	*json.Decoder
	*json.Encoder
}

func dial(addr string) (*conn, error) {
	c, err := net.Dial("unix", addr)
	return &conn{
		c,
		json.NewDecoder(c),
		json.NewEncoder(c),
	}, err
}

func listGmxProcesses(f func(file string, args interface{})) {
	dir, err := os.Open(os.TempDir())
	if err != nil {
		log.Fatalf("unable to open %s: %v", os.TempDir(), err)
	}
	pids, err := dir.Readdirnames(0)
	if err != nil {
		log.Fatalf("unable to read pids: %v", err)
	}
	for _, pid := range pids {
		if socketregex.MatchString(pid) {
			c, err := dial(filepath.Join(os.TempDir(), pid))
			if err != nil {
				continue
			}
			defer c.Close()
			c.Encode([]string{"os.args"})
			var result = make(map[string]interface{})
			if err := c.Decode(&result); err != nil {
				log.Printf("unable to decode response from %s: %v", pid, err)
				continue
			}
			if args, ok := result["os.args"]; ok {
				f(pid, args)
			}
		}
	}
}

func findGmxProcess(pname string) int {
	var found int
	listGmxProcesses(func(file string, args interface{}) {
		if argslist, ok := args.([]interface{}); ok && len(argslist) >= 1 {
			name, ok := argslist[0].(string)
			if ok {
				if name == pname {
					str_pid := file[5 : len(file)-2] // ".gmx.####.0"
					numeric_pid, err := strconv.Atoi(str_pid)
					if err == nil {
						if found == 0 {
							fmt.Printf("Using %s\t%v\n", name, args)
							found = numeric_pid
						} else if found > 0 {
							fmt.Printf("Ambiguous situation. Both %d and %d could be %s. Use -p option\n", found, numeric_pid, pname)
							found = -1
						}
					}
				}
			}
		}
	})
	if found > 0 {
		return found
	}
	return 0
}

// fetchKeys returns all the registered keys from the process.
func fetchKeys(c *conn) []string {
	// retrieve list of registered keys
	if err := c.Encode([]string{"keys"}); err != nil {
		log.Fatalf("unable to send keys request to process: %v", err)
	}
	var result = make(map[string][]string)
	if err := c.Decode(&result); err != nil {
		log.Fatalf("unable to decode keys response: %v", err)
	}
	keys, ok := result["keys"]
	if !ok {
		log.Fatalf("gmx server did not return a keys list")
	}
	return keys
}

func main() {
	flag.Parse()
	if *pid == 0 && *pname != "" {
		*pid = findGmxProcess(*pname)
	}
	if *pid == 0 {
		listGmxProcesses(func(name string, args interface{}) { fmt.Printf("%s\t%v\n", name, args) })
		return
	}
	c, err := dial(filepath.Join(os.TempDir(), fmt.Sprintf(".gmx.%d.0", *pid)))
	if err != nil {
		log.Fatalf("unable to connect to process %d: %v", *pid, err)
	}
	defer c.Close()

	// match flag.Args() as regexps
	registeredKeys := fetchKeys(c)
	var keys []string
	for _, a := range flag.Args() {
		r, err := regexp.Compile(a)
		if err != nil {
			log.Fatal("unable to compile regex %v: %v", a, err)
		}
		for _, k := range registeredKeys {
			if r.MatchString(k) {
				keys = append(keys, k)
			}
		}
	}

	deadline := time.Now().Add(*duration)
	for {
		if err := c.Encode(keys); err != nil {
			log.Fatalf("unable to send request to process: %v", err)
		}
		var result = make(map[string]interface{})
		if err := c.Decode(&result); err != nil {
			log.Fatalf("unable to decode response: %v", err)
		}
		for _,k := range keys {
			if v, ok := result[k]; ok {
				fmt.Printf("%s: %v\n", k, v)
			}
		}
		if time.Now().After(deadline) {
			return
		}
		time.Sleep(*delay)
		fmt.Println("----") // separator line
	}
}
<commit_msg>keep the keys sorted. otherwise it is too jumpy from call to call when there are lots of keys displayed<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"os"
	"path/filepath"
	"regexp"
	"sort"
	"strconv"
	"time"
)

var (
	delay    = flag.Duration("d", 0, "delay between updates")
	duration = flag.Duration("D", 0, "duration to output continually")

	pid   = flag.Int("p", 0, "process to inspect")
	pname = flag.String("n", "", "name of process to inspec")

	socketregex = regexp.MustCompile(`\.gmx\.[0-9]+\.0`)
)

type conn struct {
	net.Conn
	*json.Decoder
	*json.Encoder
}

func dial(addr string) (*conn, error) {
	c, err := net.Dial("unix", addr)
	return &conn{
		c,
		json.NewDecoder(c),
		json.NewEncoder(c),
	}, err
}

func listGmxProcesses(f func(file string, args interface{})) {
	dir, err := os.Open(os.TempDir())
	if err != nil {
		log.Fatalf("unable to open %s: %v", os.TempDir(), err)
	}
	pids, err := dir.Readdirnames(0)
	if err != nil {
		log.Fatalf("unable to read pids: %v", err)
	}
	for _, pid := range pids {
		if socketregex.MatchString(pid) {
			c, err := dial(filepath.Join(os.TempDir(), pid))
			if err != nil {
				continue
			}
			defer c.Close()
			c.Encode([]string{"os.args"})
			var result = make(map[string]interface{})
			if err := c.Decode(&result); err != nil {
				log.Printf("unable to decode response from %s: %v", pid, err)
				continue
			}
			if args, ok := result["os.args"]; ok {
				f(pid, args)
			}
		}
	}
}

func findGmxProcess(pname string) int {
	var found int
	listGmxProcesses(func(file string, args interface{}) {
		if argslist, ok := args.([]interface{}); ok && len(argslist) >= 1 {
			name, ok := argslist[0].(string)
			if ok {
				if name == pname {
					str_pid := file[5 : len(file)-2] // ".gmx.####.0"
					numeric_pid, err := strconv.Atoi(str_pid)
					if err == nil {
						if found == 0 {
							fmt.Printf("Using %s\t%v\n", name, args)
							found = numeric_pid
						} else if found > 0 {
							fmt.Printf("Ambiguous situation. Both %d and %d could be %s. Use -p option\n", found, numeric_pid, pname)
							found = -1
						}
					}
				}
			}
		}
	})
	if found > 0 {
		return found
	}
	return 0
}

// fetchKeys returns all the registered keys from the process.
func fetchKeys(c *conn) []string {
	// retrieve list of registered keys
	if err := c.Encode([]string{"keys"}); err != nil {
		log.Fatalf("unable to send keys request to process: %v", err)
	}
	var result = make(map[string][]string)
	if err := c.Decode(&result); err != nil {
		log.Fatalf("unable to decode keys response: %v", err)
	}
	keys, ok := result["keys"]
	if !ok {
		log.Fatalf("gmx server did not return a keys list")
	}
	sort.Strings(keys)
	return keys
}

func main() {
	flag.Parse()
	if *pid == 0 && *pname != "" {
		*pid = findGmxProcess(*pname)
	}
	if *pid == 0 {
		listGmxProcesses(func(name string, args interface{}) { fmt.Printf("%s\t%v\n", name, args) })
		return
	}
	c, err := dial(filepath.Join(os.TempDir(), fmt.Sprintf(".gmx.%d.0", *pid)))
	if err != nil {
		log.Fatalf("unable to connect to process %d: %v", *pid, err)
	}
	defer c.Close()

	// match flag.Args() as regexps
	registeredKeys := fetchKeys(c)
	var keys []string
	for _, a := range flag.Args() {
		r, err := regexp.Compile(a)
		if err != nil {
			log.Fatal("unable to compile regex %v: %v", a, err)
		}
		for _, k := range registeredKeys {
			if r.MatchString(k) {
				keys = append(keys, k)
			}
		}
	}

	deadline := time.Now().Add(*duration)
	for {
		if err := c.Encode(keys); err != nil {
			log.Fatalf("unable to send request to process: %v", err)
		}
		var result = make(map[string]interface{})
		if err := c.Decode(&result); err != nil {
			log.Fatalf("unable to decode response: %v", err)
		}
		for _,k := range keys {
			if v, ok := result[k]; ok {
				fmt.Printf("%s: %v\n", k, v)
			}
		}
		if time.Now().After(deadline) {
			return
		}
		time.Sleep(*delay)
		fmt.Println("----") // separator line
	}
}
<|endoftext|>


<commit_before>package golangsdk

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool            `json:"available"`
	Addon                bool            `json:"addon"`
	Condition            string          `json:"condition"`
	ShippingOptions      ShippingOptions `json:"shipping_options"`
	HandlingDays         HandlingDays    `json:"handling_days"`
	PrimeOnly            bool            `json:"prime_only"`
	MarketplaceFulfilled bool            `json:"marketplace_fulfilled"`
	Currency             string          `json:"currency"`
	Seller               Seller          `json:"seller"`
	BuyBoxWinner         bool            `json:"buy_box_winner"`
	International        bool            `json:"international"`
	OfferId              string          `json:"offer_id"`
	Price                int             `json:"price"`
}

type ShippingOptions struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          string              `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Images             []string            `json:"images"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message string `json:"message"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	respBody, err := z.sendGetRequest(requestPath, options.Timeout)
	if err != nil {
		return nil, err
	}
	var resp ProductOffersResponse
	if err := json.Unmarshal(respBody, &resp); err != nil {
		return nil, err
	}
	if resp.Status == "failed" {
		return &resp, fmt.Errorf("Zinc API returned status 'failed' response=%v", resp)
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	respBody, err := z.sendGetRequest(requestPath, options.Timeout)
	if err != nil {
		return nil, err
	}
	var resp ProductDetailsResponse
	if err := json.Unmarshal(respBody, &resp); err != nil {
		return nil, err
	}
	if resp.Status == "failed" {
		return &resp, fmt.Errorf("Zinc API returned status 'failed' response=%v", resp)
	}
	return &resp, nil
}

func (z Zinc) sendGetRequest(requestPath string, timeout time.Duration) ([]byte, error) {
	httpReq, err := http.NewRequest("GET", requestPath, nil)
	if err != nil {
		return nil, err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	resp, err := client.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return respBody, nil
}
<commit_msg>Some crucial, small fixes. need /offers url and shipping options array<commit_after>package golangsdk

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"time"
)

const (
	zincBaseURL = "https://api.zinc.io/v1"
)

type Retailer string

const (
	Amazon     Retailer = "amazon"
	AmazonUK   Retailer = "amazon_uk"
	AmazonCA   Retailer = "amazon_ca"
	AmazonMX   Retailer = "amazon_mx"
	Walmart    Retailer = "walmart"
	Aliexpress Retailer = "aliexpress"
)

var DefaultProductOptions = ProductOptions{
	Timeout: time.Duration(time.Second * 90),
}

type Zinc struct {
	ClientToken string
	ZincBaseURL string
}

func NewZinc(clientToken string) (*Zinc, error) {
	z := Zinc{
		ClientToken: clientToken,
		ZincBaseURL: zincBaseURL,
	}
	return &z, nil
}

type ProductOffersResponse struct {
	Code     string            `json:"code"`
	Data     ErrorDataResponse `json:"data"`
	Status   string            `json:"status"`
	Retailer string            `json:"retailer"`
	Offers   []ProductOffer    `json:"offers"`
}

type ProductOffer struct {
	Available            bool             `json:"available"`
	Addon                bool             `json:"addon"`
	Condition            string           `json:"condition"`
	ShippingOptions      []ShippingOption `json:"shipping_options"`
	HandlingDays         HandlingDays     `json:"handling_days"`
	PrimeOnly            bool             `json:"prime_only"`
	MarketplaceFulfilled bool             `json:"marketplace_fulfilled"`
	Currency             string           `json:"currency"`
	Seller               Seller           `json:"seller"`
	BuyBoxWinner         bool             `json:"buy_box_winner"`
	International        bool             `json:"international"`
	OfferId              string           `json:"offer_id"`
	Price                int              `json:"price"`
}

type ShippingOption struct {
	Price int `json:"price"`
}

type HandlingDays struct {
	Max int `json:"max"`
	Min int `json:"min"`
}

type Seller struct {
	NumRatings      int    `json:"num_ratings"`
	PercentPositive int    `json:"percent_positive"`
	FirstParty      bool   `json:"first_party"`
	Name            string `json:"name"`
	Id              string `json:"id"`
}

type ProductDetailsResponse struct {
	Code               string              `json:"code"`
	Data               ErrorDataResponse   `json:"data"`
	Status             string              `json:"status"`
	ProductDescription string              `json:"product_description"`
	Retailer           string              `json:"retailer"`
	Epids              []ExternalProductId `json:"epids"`
	ProductDetails     []string            `json:"product_details"`
	Title              string              `json:"title"`
	VariantSpecifics   []VariantSpecific   `json:"variant_specifics"`
	ProductId          string              `json:"product_id"`
	MainImage          string              `json:"main_image"`
	Images             []string            `json:"images"`
}

type ExternalProductId struct {
	Type  string `json:"type"`
	Value string `json:"value"`
}

type VariantSpecific struct {
	Dimension string `json:"dimension"`
	Value     string `json:"value"`
}

type ErrorDataResponse struct {
	Message string `json:"message"`
}

type ProductOptions struct {
	MaxAge    int           `json:"max_age"`
	NewerThan time.Time     `json:"newer_than"`
	Timeout   time.Duration `json:"timeout"`
}

func (z Zinc) GetProductInfo(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, *ProductDetailsResponse, error) {
	offersChan := make(chan *ProductOffersResponse, 1)
	detailsChan := make(chan *ProductDetailsResponse, 1)
	errorsChan := make(chan error, 2)

	go func() {
		offers, err := z.GetProductOffers(productId, retailer, options)
		errorsChan <- err
		offersChan <- offers
	}()

	go func() {
		details, err := z.GetProductDetails(productId, retailer, options)
		errorsChan <- err
		detailsChan <- details
	}()

	offers := <-offersChan
	details := <-detailsChan
	for i := 0; i < 2; i++ {
		err := <-errorsChan
		if err != nil {
			return nil, nil, err
		}
	}
	return offers, details, nil
}

func (z Zinc) GetProductOffers(productId string, retailer Retailer, options ProductOptions) (*ProductOffersResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	values.Set("version", "2")
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v/offers?%v", z.ZincBaseURL, productId, values.Encode())

	respBody, err := z.sendGetRequest(requestPath, options.Timeout)
	if err != nil {
		return nil, err
	}
	var resp ProductOffersResponse
	if err := json.Unmarshal(respBody, &resp); err != nil {
		return nil, err
	}
	if resp.Status == "failed" {
		return &resp, fmt.Errorf("Zinc API returned status 'failed' response=%v", resp)
	}
	return &resp, nil
}

func (z Zinc) GetProductDetails(productId string, retailer Retailer, options ProductOptions) (*ProductDetailsResponse, error) {
	values := url.Values{}
	values.Set("retailer", string(retailer))
	if options.MaxAge != 0 {
		values.Set("max_age", strconv.Itoa(options.MaxAge))
	}
	if !options.NewerThan.IsZero() {
		values.Set("newer_than", strconv.FormatInt(options.NewerThan.Unix(), 10))
	}
	requestPath := fmt.Sprintf("%v/products/%v?%v", z.ZincBaseURL, productId, values.Encode())

	respBody, err := z.sendGetRequest(requestPath, options.Timeout)
	if err != nil {
		return nil, err
	}
	var resp ProductDetailsResponse
	if err := json.Unmarshal(respBody, &resp); err != nil {
		return nil, err
	}
	if resp.Status == "failed" {
		return &resp, fmt.Errorf("Zinc API returned status 'failed' response=%v", resp)
	}
	return &resp, nil
}

func (z Zinc) sendGetRequest(requestPath string, timeout time.Duration) ([]byte, error) {
	httpReq, err := http.NewRequest("GET", requestPath, nil)
	if err != nil {
		return nil, err
	}
	httpReq.SetBasicAuth(z.ClientToken, "")
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client := &http.Client{Transport: tr, Timeout: timeout}
	resp, err := client.Do(httpReq)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	return respBody, nil
}
<|endoftext|>


<commit_before>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"strings"
	"net"
	"time"
	"os"
)

var origins = map[string]string{} // map of originip/backend
var redisArg bool

func GetAndCheckServer() (server string) {
	limit := 0
	for limit < libgolb.NumberBack {
		server = libgolb.Conf.BackServers[libgolb.RoundRobin]
		connection, err := net.DialTimeout("tcp", server, time.Duration(libgolb.Conf.TimeOut) * time.Second)
		if err == nil {
			connection.Close()
			return
		}
		libgolb.RoundRobin++
		if libgolb.RoundRobin >= libgolb.NumberBack {
			libgolb.RoundRobin = 0
		}
	}
	return
}

func extractKey(RemoteAddr string) (key string){
	if libgolb.Conf.IpHashLevel != 5 {
		ip := strings.Split(RemoteAddr, ":")
		octets := strings.Split(ip[0], ".")
		key = strings.Join(octets[:libgolb.Conf.IpHashLevel], ".")
	} else {
		key = RemoteAddr
	}
	return

}

func GetAddress(origin string) (server string, result bool){
	var err error
	if redisArg == true {
		server, err = libgolb.RadixGetString(libgolb.LBClient, origin)
		if err != nil {
			result = false
		} else {
			result = true
		}
	} else {
		server, result = origins[origin]
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	origin := extractKey(req.RemoteAddr)
	libgolb.Log("misc", "Access From: "+ req.RemoteAddr + " Key: " +origin)
	server, errGS := GetAddress(origin)
	if errGS == false {
		server = GetAndCheckServer()
	}
	primaryServer := server
	limit := 0
	for limit < libgolb.NumberBack { // this for is used to check all servers and select the first one available
		resp, _ := http.NewRequest(req.Method, "http://"+server+"/", nil)
		resp.Header = req.Header
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = GetAndCheckServer()
			limit++
		} else {
			defer secondResp.Body.Close() // don't forget to close the Body !!!
			break
		}
	}
	if limit >= libgolb.NumberBack { // No Backend
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header { // Copy Header
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Add("Served-By", server)
	w.Header().Add("Server", libgolb.Conf.Name)
	io.Copy(w, secondResp.Body)
	if redisArg == false {
		if primaryServer != server {
			origins[origin] = server
		}
	} else {
		if primaryServer != server {
			_ = libgolb.RadixSet(libgolb.LBClient, origin, server)
		}
		_ = libgolb.RadixExpire(libgolb.LBClient, origin)
	}
	libgolb.Log("ok", "Answer From :"+server)
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {
	// Load configuration
	libgolb.ConfLoad(configuration)
	//Connect to Redis
	redis := libgolb.ConnectToRedis()
	if redis != nil {
		libgolb.Log("error", "Redis connection failed: Server = "+libgolb.Conf.RedisLB.Hostname+":"+libgolb.Conf.RedisLB.Port)
		os.Exit(1)
	}

	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb memory <configuration>
  golb redis <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	if arguments["redis"] == true {
		redisArg = true
	} else {
		redisArg = false
	}
	parseArgument(arguments["<configuration>"].(string))
}
<commit_msg>Fix bug with RedisConnect<commit_after>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"strings"
	"net"
	"time"
	"os"
)

var origins = map[string]string{} // map of originip/backend
var redisArg bool

func GetAndCheckServer() (server string) {
	limit := 0
	for limit < libgolb.NumberBack {
		server = libgolb.Conf.BackServers[libgolb.RoundRobin]
		connection, err := net.DialTimeout("tcp", server, time.Duration(libgolb.Conf.TimeOut) * time.Second)
		if err == nil {
			connection.Close()
			return
		}
		libgolb.RoundRobin++
		if libgolb.RoundRobin >= libgolb.NumberBack {
			libgolb.RoundRobin = 0
		}
	}
	return
}

func extractKey(RemoteAddr string) (key string){
	if libgolb.Conf.IpHashLevel != 5 {
		ip := strings.Split(RemoteAddr, ":")
		octets := strings.Split(ip[0], ".")
		key = strings.Join(octets[:libgolb.Conf.IpHashLevel], ".")
	} else {
		key = RemoteAddr
	}
	return

}

func GetAddress(origin string) (server string, result bool){
	var err error
	if redisArg == true {
		server, err = libgolb.RadixGetString(libgolb.LBClient, origin)
		if err != nil {
			result = false
		} else {
			result = true
		}
	} else {
		server, result = origins[origin]
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	origin := extractKey(req.RemoteAddr)
	libgolb.Log("misc", "Access From: "+ req.RemoteAddr + " Key: " +origin)
	server, errGS := GetAddress(origin)
	if errGS == false {
		server = GetAndCheckServer()
	}
	primaryServer := server
	limit := 0
	for limit < libgolb.NumberBack { // this for is used to check all servers and select the first one available
		resp, _ := http.NewRequest(req.Method, "http://"+server+"/", nil)
		resp.Header = req.Header
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = GetAndCheckServer()
			limit++
		} else {
			defer secondResp.Body.Close() // don't forget to close the Body !!!
			break
		}
	}
	if limit >= libgolb.NumberBack { // No Backend
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header { // Copy Header
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Add("Served-By", server)
	w.Header().Add("Server", libgolb.Conf.Name)
	io.Copy(w, secondResp.Body)
	if redisArg == false {
		if primaryServer != server {
			origins[origin] = server
		}
	} else {
		if primaryServer != server {
			_ = libgolb.RadixSet(libgolb.LBClient, origin, server)
		}
		_ = libgolb.RadixExpire(libgolb.LBClient, origin)
	}
	libgolb.Log("ok", "Answer From :"+server)
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {
	// Load configuration
	libgolb.ConfLoad(configuration)
	//Connect to Redis
	if redisArg  == true {
		redis := libgolb.ConnectToRedis()
		if redis != nil {
			libgolb.Log("error", "Redis connection failed: Server = "+libgolb.Conf.RedisLB.Hostname+":"+libgolb.Conf.RedisLB.Port)
			os.Exit(1)
		}
	}
	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb memory <configuration>
  golb redis <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	if arguments["redis"] == true {
		redisArg = true
	} else {
		redisArg = false
	}
	parseArgument(arguments["<configuration>"].(string))
}
<|endoftext|>


<commit_before>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func getServer() (server string) {
	server = libgolb.Conf.BackServers[libgolb.RoundRobin]
	libgolb.RoundRobin++
	if libgolb.RoundRobin >= libgolb.NumberBack {
		libgolb.RoundRobin = 0
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	serv := strings.Split(req.RemoteAddr, ":") // extract just IP without port
	libgolb.Log("misc", "Access From :"+serv[0])
	server, errGS := libgolb.RadixGetString(libgolb.LBClient, serv[0])
	if errGS != nil {
		server = getServer()
	}
	limit := 0
	for limit < libgolb.NumberBack {
		resp, _ := http.NewRequest(req.Method, "http://"+server+req.RequestURI, nil)
		for k, v := range req.Header {
			resp.Header[k] = v
		}
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = getServer()
			limit++
		} else {
			break
		}
	}
	if limit >= libgolb.NumberBack {
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header {
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Set("Status", "200")
	io.Copy(w, secondResp.Body)
	libgolb.RadixSet(libgolb.LBClient, serv[0], server)
	libgolb.Log("ok", "Answer From :"+serv[0])
	//TTL
	libgolb.RadixExpire(libgolb.LBClient, serv[0], strconv.Itoa(libgolb.Conf.TTL))
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {

	// Load configuration
	libgolb.ConfLoad(configuration)
	// Check Redis connection
	redis := libgolb.ConnectToRedis()
	if redis != nil {
		libgolb.Log("error", "Redis connection failed")
		os.Exit(1)
	}

	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc(`/{URI}`, golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	parseArgument(arguments["<configuration>"].(string))
}
<commit_msg>Fix Router<commit_after>package main

import (
	"../libgolb"
	"github.com/docopt/docopt-go"
	"github.com/gorilla/mux"
	"io"
	"net/http"
	"os"
	"strconv"
	"strings"
)

func getServer() (server string) {
	server = libgolb.Conf.BackServers[libgolb.RoundRobin]
	libgolb.RoundRobin++
	if libgolb.RoundRobin >= libgolb.NumberBack {
		libgolb.RoundRobin = 0
	}
	return
}

func golbGet(w http.ResponseWriter, req *http.Request) {
	var secondResp *http.Response
	var errsp error

	serv := strings.Split(req.RemoteAddr, ":") // extract just IP without port
	libgolb.Log("misc", "Access From :"+serv[0])
	server, errGS := libgolb.RadixGetString(libgolb.LBClient, serv[0])
	if errGS != nil {
		server = getServer()
	}
	limit := 0
	for limit < libgolb.NumberBack {
		resp, _ := http.NewRequest(req.Method, "http://"+server+req.RequestURI, nil)
		for k, v := range req.Header {
			resp.Header[k] = v
		}
		resp.Header.Set("X-Forwarded-For", req.RemoteAddr)
		secondResp, errsp = http.DefaultClient.Do(resp)
		if errsp != nil {
			libgolb.Log("error", "Connection with the HTTP file server failed: "+errsp.Error())
			server = getServer()
			limit++
		} else {
			break
		}
	}
	if limit >= libgolb.NumberBack {
		libgolb.HttpResponse(w, 500, "Internal server error\n")
		libgolb.Log("error", "No Backend Server avalaible")
		return
	}
	for k, v := range secondResp.Header {
		w.Header().Add(k, strings.Join(v, ""))
	}
	w.Header().Set("Status", "200")
	io.Copy(w, secondResp.Body)
	libgolb.RadixSet(libgolb.LBClient, serv[0], server)
	libgolb.Log("ok", "Answer From :"+serv[0])
	//TTL
	libgolb.RadixExpire(libgolb.LBClient, serv[0], strconv.Itoa(libgolb.Conf.TTL))
	libgolb.LogW3C(w, req, false)
}

func parseArgument(configuration string) {

	// Load configuration
	libgolb.ConfLoad(configuration)
	// Check Redis connection
	redis := libgolb.ConnectToRedis()
	if redis != nil {
		libgolb.Log("error", "Redis connection failed")
		os.Exit(1)
	}

	// Router
	rtr := mux.NewRouter()
	rtr.HandleFunc("/", golbGet).Methods("GET")
	http.Handle("/", rtr)

	// Listening
	libgolb.Log("ok", "Listening on "+libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port)
	err := http.ListenAndServe(libgolb.Conf.Server.Hostname+":"+libgolb.Conf.Server.Port, nil)
	libgolb.ErrCatcher("ListenAndServe: ", err)
}

func main() {
	usage := `Golb.

Usage:
  golb <configuration>
  golb -h | --help
  golb --version

Options:
  -h --help     Show this screen.
  --version     Show version.`

	arguments, _ := docopt.Parse(usage, nil, true, "GoLB 0.1", false)
	parseArgument(arguments["<configuration>"].(string))
}
<|endoftext|>


<commit_before>// THIS IS A WORK IN PROGRESS
// based on http://zguide.zeromq.org/php:chapter8#Detecting-Disappearances

// The Peer will listen on gomsg.Client and call on gomsg.Server

package grapevine

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/imdario/mergo"
	"github.com/quintans/gomsg"
	tk "github.com/quintans/toolkit"
	"github.com/quintans/toolkit/faults"
)

// defaults
const (
	UuidSize = 16
	PING     = "PING"
)

type node struct {
	uuid            string
	client          *gomsg.Client
	debouncer       *tk.Debouncer
	beaconLastTime  time.Time
	beaconCountdown int
}

type Config struct {
	Uuid                  []byte
	BeaconAddr            string
	BeaconName            string
	BeaconMaxDatagramSize int
	BeaconInterval        time.Duration
	BeaconMaxInterval     time.Duration
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	BeaconCountdown int
}

type Peer struct {
	sync.RWMutex
	*gomsg.Server

	cfg     Config
	tcpAddr string
	// peers ones will be used to listen
	peers map[string]*node
	// the server will be used to send
	udpConn      *net.UDPConn
	handlers     map[string][]interface{}
	beaconTicker *tk.Ticker
}

func NewPeer(cfg Config) *Peer {
	if cfg.Uuid == nil {
		cfg.Uuid = tk.NewUUID()
	}

	// apply defaults
	var defaultConfig = Config{
		BeaconMaxDatagramSize: 1024,
		BeaconAddr:            "224.0.0.1:9999",
		BeaconName:            "Beacon",
		BeaconInterval:        time.Second,
		BeaconMaxInterval:     time.Second * 2,
		BeaconCountdown:       3,
	}
	mergo.Merge(&cfg, defaultConfig)

	peer := &Peer{
		Server:   gomsg.NewServer(),
		cfg:      cfg,
		peers:    make(map[string]*node),
		handlers: make(map[string][]interface{}),
	}

	return peer
}

func (peer *Peer) Config() Config {
	return peer.cfg
}

func (peer *Peer) Bind(tcpAddr string) <-chan error {
	peer.tcpAddr = tcpAddr
	peer.Logger().Infof("Binding Cluster=%s, UUID=%X at %s", peer.cfg.BeaconName, peer.cfg.Uuid, tcpAddr)

	// special case where we receive a targeted request
	// when a peer tries to check if I exist
	// because it did not received the beacon in time
	peer.Server.Handle(PING, func() {})

	var err = peer.serveUDP(peer.beaconHandler)
	if err != nil {
		var cherr = make(chan error, 1)
		cherr <- err
		return cherr
	}
	peer.Server.AddBindListeners(func(l net.Listener) {
		peer.startBeacon()
	})

	return peer.Server.Listen(tcpAddr)
}

func (peer *Peer) checkPeer(uuid string, addr string) {
	peer.Lock()
	defer peer.Unlock()

	if n := peer.peers[uuid]; n != nil {
		if addr != n.client.Address() {
			peer.Logger().Infof("%s - OLD peer at %s", peer.tcpAddr, addr)
			// client reconnected with another address
			n.client.Destroy()
			n.debouncer.Kill()
			delete(peer.peers, uuid)
			peer.connectPeer(uuid, addr)
		} else {
			peer.checkBeacon(n)
		}
	} else {
		peer.Logger().Infof("%s - NEW peer at %s", peer.tcpAddr, addr)
		peer.connectPeer(uuid, addr)
	}
}

func (peer *Peer) checkBeacon(n *node) {
	if n.beaconCountdown == 0 {
		// this debouncer is only for UDP beacon when beaconCountdown == 0
		n.debouncer.Delay(nil)
	} else {
		var now = time.Now()
		if now.Sub(n.beaconLastTime) < peer.cfg.BeaconMaxInterval {
			n.beaconCountdown--
		} else {
			n.beaconCountdown = peer.cfg.BeaconCountdown
		}
		if n.beaconCountdown == 0 {
			// the client responded, switching to UDP
			peer.Logger().Infof("%s - Peer at %s responded. Switching to UDP listening", peer.tcpAddr, n.client.Address())
			// kill the TCP health check
			n.debouncer.Kill()
			peer.healthCheckByUDP(n)
		}
		n.beaconLastTime = now
	}
}

func (peer *Peer) connectPeer(uuid string, addr string) error {
	var cli = gomsg.NewClient()
	var e = <-cli.Connect(addr)
	if e != nil {
		peer.Logger().Errorf("%s - Unable to connect to %s", peer.tcpAddr, addr)
		return e
	}
	var n = &node{
		uuid:   uuid,
		client: cli,
	}
	peer.healthCheckByUDP(n)
	peer.peers[uuid] = n

	// apply all handlers
	for k, v := range peer.handlers {
		cli.Handle(k, v...)
	}
	return nil
}

func (peer *Peer) dropPeer(n *node) {
	peer.Lock()
	defer peer.Unlock()

	peer.Logger().Infof("%s - Droping unresponsive peer at %s", peer.tcpAddr, n.client.Address())
	n.client.Destroy()
	n.debouncer = nil
	delete(peer.peers, n.uuid)
}

// healthCheckByIP is the client that checks actively the remote peer
func (peer *Peer) healthCheckByTCP(n *node) {
	var ticker = tk.NewTicker(peer.cfg.BeaconInterval, func(t time.Time) {
		<-n.client.RequestTimeout(PING, nil, func() {
			n.debouncer.Delay(nil)
		}, peer.cfg.BeaconInterval)
	})
	n.debouncer = tk.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		peer.Logger().Debugf("%s - Failed to contact by TCP peer at %s.", peer.tcpAddr, n.client.Address())
		peer.dropPeer(n)
	})
	n.debouncer.OnExit = func() {
		ticker.Stop()
	}
}

func (peer *Peer) healthCheckByUDP(n *node) {
	n.debouncer = tk.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		// the client did not responded, switching to TCP
		peer.Logger().Debugf("%s - Silent peer at %s. Switching to TCP ping", peer.tcpAddr, n.client.Address())
		n.beaconCountdown = peer.cfg.BeaconCountdown
		peer.healthCheckByTCP(n)
	})
}

func (peer *Peer) beaconHandler(src *net.UDPAddr, n int, b []byte) {
	// starts with tag
	if bytes.HasPrefix(b, []byte(peer.cfg.BeaconName)) {
		var r = bytes.NewReader(b)
		r.Seek(int64(len(peer.cfg.BeaconName)), io.SeekStart)
		var uuid = make([]byte, UuidSize)
		r.Read(uuid)
		// ignore self
		if bytes.Compare(uuid, peer.cfg.Uuid) != 0 {
			var buf16 = make([]byte, 2)
			r.Read(buf16)
			var port = int(binary.LittleEndian.Uint16(buf16))
			peer.checkPeer(fmt.Sprintf("%X", uuid), src.IP.String()+":"+strconv.Itoa(port))
		}
	}
}

func (peer *Peer) startBeacon() error {
	addr, err := net.ResolveUDPAddr("udp", peer.cfg.BeaconAddr)
	if err != nil {
		return faults.Wrap(err)
	}
	c, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return faults.Wrap(err)
	}
	var buf16 = make([]byte, 2)
	var port = uint16(peer.Server.BindPort())
	binary.LittleEndian.PutUint16(buf16, port)

	var buf bytes.Buffer
	buf.WriteString(peer.cfg.BeaconName)
	buf.Write(peer.cfg.Uuid)
	buf.Write(buf16)

	var data = buf.Bytes()
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.Logger().Infof("Heartbeat on %s every %s",
		peer.cfg.BeaconAddr,
		peer.cfg.BeaconInterval)
	peer.beaconTicker = tk.NewDelayedTicker(0, peer.cfg.BeaconInterval, func(t time.Time) {
		var _, err = c.Write(data)
		if err != nil {
			peer.Logger().Errorf("Unable to write to UDP %s. Error: %+v", peer.cfg.BeaconAddr, faults.Wrap(err))
		}
	})

	return nil
}

func (peer *Peer) serveUDP(hnd func(*net.UDPAddr, int, []byte)) error {
	addr, err := net.ResolveUDPAddr("udp", peer.cfg.BeaconAddr)
	if err != nil {
		return faults.Wrap(err)
	}

	l, err := net.ListenUDP("udp", addr)
	if err != nil {
		return faults.Wrap(err)
	}
	l.SetReadBuffer(peer.cfg.BeaconMaxDatagramSize)
	peer.udpConn = l
	go func() {
		for {
			var payload = make([]byte, peer.cfg.BeaconMaxDatagramSize)
			n, src, err := l.ReadFromUDP(payload)
			if peer.udpConn == nil {
				return
			} else if err != nil {
				peer.Logger().Errorf("%s - ReadFromUDP failed: %+v", peer.tcpAddr, faults.Wrap(err))
				return
			}
			hnd(src, n, payload)
		}
	}()
	return nil
}

func (peer *Peer) Handle(name string, hnd ...interface{}) {
	peer.Logger().Infof("Registering handler for %s", name)
	peer.Lock()
	defer peer.Unlock()

	peer.handlers[name] = hnd
	for _, v := range peer.peers {
		v.client.Handle(name, hnd)
	}
}

func (peer *Peer) Cancel(name string) {
	peer.Logger().Infof("Canceling handler for %s", name)
	peer.Lock()
	defer peer.Unlock()

	for _, v := range peer.peers {
		v.client.Cancel(name)
	}
}

func (peer *Peer) Destroy() {
	peer.Server.Destroy()
	var conn = peer.udpConn
	peer.udpConn = nil
	if conn != nil {
		conn.Close()
	}
	peer.Lock()
	defer peer.Unlock()
	for _, v := range peer.peers {
		v.debouncer.Kill()
		v.client.Destroy()
	}
	peer.peers = make(map[string]*node)
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = nil
}
<commit_msg>hexadecimal conversion<commit_after>// THIS IS A WORK IN PROGRESS
// based on http://zguide.zeromq.org/php:chapter8#Detecting-Disappearances

// The Peer will listen on gomsg.Client and call on gomsg.Server

package grapevine

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"io"
	"net"
	"strconv"
	"sync"
	"time"

	"github.com/imdario/mergo"
	"github.com/quintans/gomsg"
	tk "github.com/quintans/toolkit"
	"github.com/quintans/toolkit/faults"
)

// defaults
const (
	UuidSize = 16
	PING     = "PING"
)

type node struct {
	uuid            string
	client          *gomsg.Client
	debouncer       *tk.Debouncer
	beaconLastTime  time.Time
	beaconCountdown int
}

type Config struct {
	Uuid                  []byte
	BeaconAddr            string
	BeaconName            string
	BeaconMaxDatagramSize int
	BeaconInterval        time.Duration
	BeaconMaxInterval     time.Duration
	// BeaconCountdown is the number of consecutive pings inside the ping window
	// to reactivate the UDP health check
	BeaconCountdown int
}

type Peer struct {
	sync.RWMutex
	*gomsg.Server

	cfg     Config
	tcpAddr string
	// peers ones will be used to listen
	peers map[string]*node
	// the server will be used to send
	udpConn      *net.UDPConn
	handlers     map[string][]interface{}
	beaconTicker *tk.Ticker
}

func NewPeer(cfg Config) *Peer {
	if cfg.Uuid == nil {
		cfg.Uuid = tk.NewUUID()
	}

	// apply defaults
	var defaultConfig = Config{
		BeaconMaxDatagramSize: 1024,
		BeaconAddr:            "224.0.0.1:9999",
		BeaconName:            "Beacon",
		BeaconInterval:        time.Second,
		BeaconMaxInterval:     time.Second * 2,
		BeaconCountdown:       3,
	}
	mergo.Merge(&cfg, defaultConfig)

	peer := &Peer{
		Server:   gomsg.NewServer(),
		cfg:      cfg,
		peers:    make(map[string]*node),
		handlers: make(map[string][]interface{}),
	}

	return peer
}

func (peer *Peer) Config() Config {
	return peer.cfg
}

func (peer *Peer) Bind(tcpAddr string) <-chan error {
	peer.tcpAddr = tcpAddr
	peer.Logger().Infof("Binding Cluster=%s, UUID=%X at %s", peer.cfg.BeaconName, peer.cfg.Uuid, tcpAddr)

	// special case where we receive a targeted request
	// when a peer tries to check if I exist
	// because it did not received the beacon in time
	peer.Server.Handle(PING, func() {})

	var err = peer.serveUDP(peer.beaconHandler)
	if err != nil {
		var cherr = make(chan error, 1)
		cherr <- err
		return cherr
	}
	peer.Server.AddBindListeners(func(l net.Listener) {
		peer.startBeacon()
	})

	return peer.Server.Listen(tcpAddr)
}

func (peer *Peer) checkPeer(uuid string, addr string) {
	peer.Lock()
	defer peer.Unlock()

	if n := peer.peers[uuid]; n != nil {
		if addr != n.client.Address() {
			peer.Logger().Infof("%s - OLD peer at %s", peer.tcpAddr, addr)
			// client reconnected with another address
			n.client.Destroy()
			n.debouncer.Kill()
			delete(peer.peers, uuid)
			peer.connectPeer(uuid, addr)
		} else {
			peer.checkBeacon(n)
		}
	} else {
		peer.Logger().Infof("%s - NEW peer at %s", peer.tcpAddr, addr)
		peer.connectPeer(uuid, addr)
	}
}

func (peer *Peer) checkBeacon(n *node) {
	if n.beaconCountdown == 0 {
		// this debouncer is only for UDP beacon when beaconCountdown == 0
		n.debouncer.Delay(nil)
	} else {
		var now = time.Now()
		if now.Sub(n.beaconLastTime) < peer.cfg.BeaconMaxInterval {
			n.beaconCountdown--
		} else {
			n.beaconCountdown = peer.cfg.BeaconCountdown
		}
		if n.beaconCountdown == 0 {
			// the client responded, switching to UDP
			peer.Logger().Infof("%s - Peer at %s responded. Switching to UDP listening", peer.tcpAddr, n.client.Address())
			// kill the TCP health check
			n.debouncer.Kill()
			peer.healthCheckByUDP(n)
		}
		n.beaconLastTime = now
	}
}

func (peer *Peer) connectPeer(uuid string, addr string) error {
	var cli = gomsg.NewClient()
	var e = <-cli.Connect(addr)
	if e != nil {
		peer.Logger().Errorf("%s - Unable to connect to %s", peer.tcpAddr, addr)
		return e
	}
	var n = &node{
		uuid:   uuid,
		client: cli,
	}
	peer.healthCheckByUDP(n)
	peer.peers[uuid] = n

	// apply all handlers
	for k, v := range peer.handlers {
		cli.Handle(k, v...)
	}
	return nil
}

func (peer *Peer) dropPeer(n *node) {
	peer.Lock()
	defer peer.Unlock()

	peer.Logger().Infof("%s - Droping unresponsive peer at %s", peer.tcpAddr, n.client.Address())
	n.client.Destroy()
	n.debouncer = nil
	delete(peer.peers, n.uuid)
}

// healthCheckByIP is the client that checks actively the remote peer
func (peer *Peer) healthCheckByTCP(n *node) {
	var ticker = tk.NewTicker(peer.cfg.BeaconInterval, func(t time.Time) {
		<-n.client.RequestTimeout(PING, nil, func() {
			n.debouncer.Delay(nil)
		}, peer.cfg.BeaconInterval)
	})
	n.debouncer = tk.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		peer.Logger().Debugf("%s - Failed to contact by TCP peer at %s.", peer.tcpAddr, n.client.Address())
		peer.dropPeer(n)
	})
	n.debouncer.OnExit = func() {
		ticker.Stop()
	}
}

func (peer *Peer) healthCheckByUDP(n *node) {
	n.debouncer = tk.NewDebounce(peer.cfg.BeaconMaxInterval, func(o interface{}) {
		// the client did not responded, switching to TCP
		peer.Logger().Debugf("%s - Silent peer at %s. Switching to TCP ping", peer.tcpAddr, n.client.Address())
		n.beaconCountdown = peer.cfg.BeaconCountdown
		peer.healthCheckByTCP(n)
	})
}

func (peer *Peer) beaconHandler(src *net.UDPAddr, n int, b []byte) {
	// starts with tag
	if bytes.HasPrefix(b, []byte(peer.cfg.BeaconName)) {
		var r = bytes.NewReader(b)
		r.Seek(int64(len(peer.cfg.BeaconName)), io.SeekStart)
		var uuid = make([]byte, UuidSize)
		r.Read(uuid)
		// ignore self
		if bytes.Compare(uuid, peer.cfg.Uuid) != 0 {
			var buf16 = make([]byte, 2)
			r.Read(buf16)
			var port = int(binary.LittleEndian.Uint16(buf16))
			peer.checkPeer(hex.EncodeToString(uuid), src.IP.String()+":"+strconv.Itoa(port))
		}
	}
}

func (peer *Peer) startBeacon() error {
	addr, err := net.ResolveUDPAddr("udp", peer.cfg.BeaconAddr)
	if err != nil {
		return faults.Wrap(err)
	}
	c, err := net.DialUDP("udp", nil, addr)
	if err != nil {
		return faults.Wrap(err)
	}
	var buf16 = make([]byte, 2)
	var port = uint16(peer.Server.BindPort())
	binary.LittleEndian.PutUint16(buf16, port)

	var buf bytes.Buffer
	buf.WriteString(peer.cfg.BeaconName)
	buf.Write(peer.cfg.Uuid)
	buf.Write(buf16)

	var data = buf.Bytes()
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.Logger().Infof("Heartbeat on %s every %s",
		peer.cfg.BeaconAddr,
		peer.cfg.BeaconInterval)
	peer.beaconTicker = tk.NewDelayedTicker(0, peer.cfg.BeaconInterval, func(t time.Time) {
		var _, err = c.Write(data)
		if err != nil {
			peer.Logger().Errorf("Unable to write to UDP %s. Error: %+v", peer.cfg.BeaconAddr, faults.Wrap(err))
		}
	})

	return nil
}

func (peer *Peer) serveUDP(hnd func(*net.UDPAddr, int, []byte)) error {
	addr, err := net.ResolveUDPAddr("udp", peer.cfg.BeaconAddr)
	if err != nil {
		return faults.Wrap(err)
	}

	l, err := net.ListenUDP("udp", addr)
	if err != nil {
		return faults.Wrap(err)
	}
	l.SetReadBuffer(peer.cfg.BeaconMaxDatagramSize)
	peer.udpConn = l
	go func() {
		for {
			var payload = make([]byte, peer.cfg.BeaconMaxDatagramSize)
			n, src, err := l.ReadFromUDP(payload)
			if peer.udpConn == nil {
				return
			} else if err != nil {
				peer.Logger().Errorf("%s - ReadFromUDP failed: %+v", peer.tcpAddr, faults.Wrap(err))
				return
			}
			hnd(src, n, payload)
		}
	}()
	return nil
}

func (peer *Peer) Handle(name string, hnd ...interface{}) {
	peer.Logger().Infof("Registering handler for %s", name)
	peer.Lock()
	defer peer.Unlock()

	peer.handlers[name] = hnd
	for _, v := range peer.peers {
		v.client.Handle(name, hnd)
	}
}

func (peer *Peer) Cancel(name string) {
	peer.Logger().Infof("Canceling handler for %s", name)
	peer.Lock()
	defer peer.Unlock()

	for _, v := range peer.peers {
		v.client.Cancel(name)
	}
}

func (peer *Peer) Destroy() {
	peer.Server.Destroy()
	var conn = peer.udpConn
	peer.udpConn = nil
	if conn != nil {
		conn.Close()
	}
	peer.Lock()
	defer peer.Unlock()
	for _, v := range peer.peers {
		v.debouncer.Kill()
		v.client.Destroy()
	}
	peer.peers = make(map[string]*node)
	if peer.beaconTicker != nil {
		peer.beaconTicker.Stop()
	}
	peer.beaconTicker = nil
}
<|endoftext|>


<commit_before>// Same copyright and license as the rest of the files in this project
// This file contains style related functions and structures

package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/pango"

	"github.com/gotk3/gotk3/glib"
)

/*
 * GtkLabel
 */

// Label is a representation of GTK's GtkLabel.
type Label struct {
	Widget
}

// native returns a pointer to the underlying GtkLabel.
func (v *Label) native() *C.GtkLabel {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkLabel(p)
}

func marshalLabel(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

func wrapLabel(obj *glib.Object) *Label {
	if obj == nil {
		return nil
	}

	return &Label{Widget{glib.InitiallyUnowned{obj}}}
}

// WidgetToLabel is a convience func that casts the given *Widget into a *Label.
func WidgetToLabel(widget *Widget) (*Label, error) {
	obj := glib.Take(unsafe.Pointer(widget.GObject))
	return wrapLabel(obj), nil
}

// LabelNew is a wrapper around gtk_label_new().
func LabelNew(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SetText is a wrapper around gtk_label_set_text().
func (v *Label) SetText(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_text(v.native(), (*C.gchar)(cstr))
}

// TODO:
// gtk_label_set_text_with_mnemonic().
// gtk_label_get_attributes().
func (v *Label) SetAttributes(attributes *pango.AttrList) {
	C.gtk_label_set_attributes(v.native(), (*C.PangoAttrList)(unsafe.Pointer(attributes.Native())))
}

// SetMarkup is a wrapper around gtk_label_set_markup().
func (v *Label) SetMarkup(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup(v.native(), (*C.gchar)(cstr))
}

// SetMarkupWithMnemonic is a wrapper around
// gtk_label_set_markup_with_mnemonic().
func (v *Label) SetMarkupWithMnemonic(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup_with_mnemonic(v.native(), (*C.gchar)(cstr))
}

// SetPattern is a wrapper around gtk_label_set_pattern().
func (v *Label) SetPattern(patern string) {
	cstr := C.CString(patern)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_pattern(v.native(), (*C.gchar)(cstr))
}

// SetJustify is a wrapper around gtk_label_set_justify().
func (v *Label) SetJustify(jtype Justification) {
	C.gtk_label_set_justify(v.native(), C.GtkJustification(jtype))
}

// SetEllipsize is a wrapper around gtk_label_set_ellipsize().
func (v *Label) SetEllipsize(mode pango.EllipsizeMode) {
	C.gtk_label_set_ellipsize(v.native(), C.PangoEllipsizeMode(mode))
}

// GetWidthChars is a wrapper around gtk_label_get_width_chars().
func (v *Label) GetWidthChars() int {
	c := C.gtk_label_get_width_chars(v.native())
	return int(c)
}

// SetWidthChars is a wrapper around gtk_label_set_width_chars().
func (v *Label) SetWidthChars(nChars int) {
	C.gtk_label_set_width_chars(v.native(), C.gint(nChars))
}

// GetMaxWidthChars is a wrapper around gtk_label_get_max_width_chars().
func (v *Label) GetMaxWidthChars() int {
	c := C.gtk_label_get_max_width_chars(v.native())
	return int(c)
}

// SetMaxWidthChars is a wrapper around gtk_label_set_max_width_chars().
func (v *Label) SetMaxWidthChars(nChars int) {
	C.gtk_label_set_max_width_chars(v.native(), C.gint(nChars))
}

// GetLineWrap is a wrapper around gtk_label_get_line_wrap().
func (v *Label) GetLineWrap() bool {
	c := C.gtk_label_get_line_wrap(v.native())
	return gobool(c)
}

// SetLineWrap is a wrapper around gtk_label_set_line_wrap().
func (v *Label) SetLineWrap(wrap bool) {
	C.gtk_label_set_line_wrap(v.native(), gbool(wrap))
}

// SetLineWrapMode is a wrapper around gtk_label_set_line_wrap_mode().
func (v *Label) SetLineWrapMode(wrapMode pango.WrapMode) {
	C.gtk_label_set_line_wrap_mode(v.native(), C.PangoWrapMode(wrapMode))
}

// TODO:
// gtk_label_get_line_wrap_mode().
// gtk_label_get_layout_offsets().
// gtk_label_get_layout().
// gtk_label_get_mnemonic_widget().

// GetSelectable is a wrapper around gtk_label_get_selectable().
func (v *Label) GetSelectable() bool {
	c := C.gtk_label_get_selectable(v.native())
	return gobool(c)
}

// GetText is a wrapper around gtk_label_get_text().
func (v *Label) GetText() (string, error) {
	c := C.gtk_label_get_text(v.native())
	if c == nil {
		return "", nilPtrErr
	}
	return C.GoString((*C.char)(c)), nil
}

// GetJustify is a wrapper around gtk_label_get_justify().
func (v *Label) GetJustify() Justification {
	c := C.gtk_label_get_justify(v.native())
	return Justification(c)
}

// GetEllipsize is a wrapper around gtk_label_get_ellipsize().
func (v *Label) GetEllipsize() pango.EllipsizeMode {
	c := C.gtk_label_get_ellipsize(v.native())
	return pango.EllipsizeMode(c)
}

// GetCurrentUri is a wrapper around gtk_label_get_current_uri().
func (v *Label) GetCurrentUri() string {
	c := C.gtk_label_get_current_uri(v.native())
	return C.GoString((*C.char)(c))
}

// GetTrackVisitedLinks is a wrapper around gtk_label_get_track_visited_links().
func (v *Label) GetTrackVisitedLinks() bool {
	c := C.gtk_label_get_track_visited_links(v.native())
	return gobool(c)
}

// SetTrackVisitedLinks is a wrapper around gtk_label_set_track_visited_links().
func (v *Label) SetTrackVisitedLinks(trackLinks bool) {
	C.gtk_label_set_track_visited_links(v.native(), gbool(trackLinks))
}

// GetAngle is a wrapper around gtk_label_get_angle().
func (v *Label) GetAngle() float64 {
	c := C.gtk_label_get_angle(v.native())
	return float64(c)
}

// SetAngle is a wrapper around gtk_label_set_angle().
func (v *Label) SetAngle(angle float64) {
	C.gtk_label_set_angle(v.native(), C.gdouble(angle))
}

// GetSelectionBounds is a wrapper around gtk_label_get_selection_bounds().
func (v *Label) GetSelectionBounds() (start, end int, nonEmpty bool) {
	var cstart, cend C.gint
	c := C.gtk_label_get_selection_bounds(v.native(), &cstart, &cend)
	return int(cstart), int(cend), gobool(c)
}

// GetSingleLineMode is a wrapper around gtk_label_get_single_line_mode().
func (v *Label) GetSingleLineMode() bool {
	c := C.gtk_label_get_single_line_mode(v.native())
	return gobool(c)
}

// SetSingleLineMode is a wrapper around gtk_label_set_single_line_mode().
func (v *Label) SetSingleLineMode(mode bool) {
	C.gtk_label_set_single_line_mode(v.native(), gbool(mode))
}

// GetUseMarkup is a wrapper around gtk_label_get_use_markup().
func (v *Label) GetUseMarkup() bool {
	c := C.gtk_label_get_use_markup(v.native())
	return gobool(c)
}

// SetUseMarkup is a wrapper around gtk_label_set_use_markup().
func (v *Label) SetUseMarkup(use bool) {
	C.gtk_label_set_use_markup(v.native(), gbool(use))
}

// GetUseUnderline is a wrapper around gtk_label_get_use_underline().
func (v *Label) GetUseUnderline() bool {
	c := C.gtk_label_get_use_underline(v.native())
	return gobool(c)
}

// SetUseUnderline is a wrapper around gtk_label_set_use_underline().
func (v *Label) SetUseUnderline(use bool) {
	C.gtk_label_set_use_underline(v.native(), gbool(use))
}

// LabelNewWithMnemonic is a wrapper around gtk_label_new_with_mnemonic().
func LabelNewWithMnemonic(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new_with_mnemonic((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SelectRegion is a wrapper around gtk_label_select_region().
func (v *Label) SelectRegion(startOffset, endOffset int) {
	C.gtk_label_select_region(v.native(), C.gint(startOffset),
		C.gint(endOffset))
}

// SetSelectable is a wrapper around gtk_label_set_selectable().
func (v *Label) SetSelectable(setting bool) {
	C.gtk_label_set_selectable(v.native(), gbool(setting))
}

// SetLabel is a wrapper around gtk_label_set_label().
func (v *Label) SetLabel(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_label(v.native(), (*C.gchar)(cstr))
}

// GetLabel is a wrapper around gtk_label_get_label().
func (v *Label) GetLabel() string {
	c := C.gtk_label_get_label(v.native())
	if c == nil {
		return ""
	}
	return C.GoString((*C.char)(c))
}

// GetMnemonicKeyval is a wrapper around gtk_label_get_mnemonic_keyval().
func (v *Label) GetMnemonicKeyval() uint {
	return uint(C.gtk_label_get_mnemonic_keyval(v.native()))
}

// SetMnemonicWidget is a wrapper around gtk_label_set_mnemonic_widget().
func (v *Label) SetMnemonicWidget(widget IWidget) {
	C.gtk_label_set_mnemonic_widget(v.native(), widget.toWidget())
}
<commit_msg>Add support for `gtk_label_get_attributes` and update `gtk_label_set_attributes` documentation<commit_after>// Same copyright and license as the rest of the files in this project
// This file contains style related functions and structures

package gtk

// #include <gtk/gtk.h>
// #include "gtk.go.h"
import "C"
import (
	"unsafe"

	"github.com/gotk3/gotk3/pango"

	"github.com/gotk3/gotk3/glib"
)

/*
 * GtkLabel
 */

// Label is a representation of GTK's GtkLabel.
type Label struct {
	Widget
}

// native returns a pointer to the underlying GtkLabel.
func (v *Label) native() *C.GtkLabel {
	if v == nil || v.GObject == nil {
		return nil
	}
	p := unsafe.Pointer(v.GObject)
	return C.toGtkLabel(p)
}

func marshalLabel(p uintptr) (interface{}, error) {
	c := C.g_value_get_object((*C.GValue)(unsafe.Pointer(p)))
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

func wrapLabel(obj *glib.Object) *Label {
	if obj == nil {
		return nil
	}

	return &Label{Widget{glib.InitiallyUnowned{obj}}}
}

// WidgetToLabel is a convience func that casts the given *Widget into a *Label.
func WidgetToLabel(widget *Widget) (*Label, error) {
	obj := glib.Take(unsafe.Pointer(widget.GObject))
	return wrapLabel(obj), nil
}

// LabelNew is a wrapper around gtk_label_new().
func LabelNew(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SetText is a wrapper around gtk_label_set_text().
func (v *Label) SetText(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_text(v.native(), (*C.gchar)(cstr))
}

// TODO:
// gtk_label_set_text_with_mnemonic().

// SetAttributes is a wrapper around "gtk_label_set_attributes".
func (v *Label) SetAttributes(attributes *pango.AttrList) {
	C.gtk_label_set_attributes(v.native(), (*C.PangoAttrList)(unsafe.Pointer(attributes.Native())))
}

// GetAttributes is a wrapper around "gtk_label_get_attributes".
func (v *Label) GetAttributes() (*pango.AttrList, error) {
	ptr := C.gtk_label_get_attributes(v.native())
	if ptr == nil {
		return nil, nilPtrErr
	}

	return pango.WrapAttrList(ptr), nil
}

// SetMarkup is a wrapper around gtk_label_set_markup().
func (v *Label) SetMarkup(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup(v.native(), (*C.gchar)(cstr))
}

// SetMarkupWithMnemonic is a wrapper around
// gtk_label_set_markup_with_mnemonic().
func (v *Label) SetMarkupWithMnemonic(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_markup_with_mnemonic(v.native(), (*C.gchar)(cstr))
}

// SetPattern is a wrapper around gtk_label_set_pattern().
func (v *Label) SetPattern(patern string) {
	cstr := C.CString(patern)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_pattern(v.native(), (*C.gchar)(cstr))
}

// SetJustify is a wrapper around gtk_label_set_justify().
func (v *Label) SetJustify(jtype Justification) {
	C.gtk_label_set_justify(v.native(), C.GtkJustification(jtype))
}

// SetEllipsize is a wrapper around gtk_label_set_ellipsize().
func (v *Label) SetEllipsize(mode pango.EllipsizeMode) {
	C.gtk_label_set_ellipsize(v.native(), C.PangoEllipsizeMode(mode))
}

// GetWidthChars is a wrapper around gtk_label_get_width_chars().
func (v *Label) GetWidthChars() int {
	c := C.gtk_label_get_width_chars(v.native())
	return int(c)
}

// SetWidthChars is a wrapper around gtk_label_set_width_chars().
func (v *Label) SetWidthChars(nChars int) {
	C.gtk_label_set_width_chars(v.native(), C.gint(nChars))
}

// GetMaxWidthChars is a wrapper around gtk_label_get_max_width_chars().
func (v *Label) GetMaxWidthChars() int {
	c := C.gtk_label_get_max_width_chars(v.native())
	return int(c)
}

// SetMaxWidthChars is a wrapper around gtk_label_set_max_width_chars().
func (v *Label) SetMaxWidthChars(nChars int) {
	C.gtk_label_set_max_width_chars(v.native(), C.gint(nChars))
}

// GetLineWrap is a wrapper around gtk_label_get_line_wrap().
func (v *Label) GetLineWrap() bool {
	c := C.gtk_label_get_line_wrap(v.native())
	return gobool(c)
}

// SetLineWrap is a wrapper around gtk_label_set_line_wrap().
func (v *Label) SetLineWrap(wrap bool) {
	C.gtk_label_set_line_wrap(v.native(), gbool(wrap))
}

// SetLineWrapMode is a wrapper around gtk_label_set_line_wrap_mode().
func (v *Label) SetLineWrapMode(wrapMode pango.WrapMode) {
	C.gtk_label_set_line_wrap_mode(v.native(), C.PangoWrapMode(wrapMode))
}

// TODO:
// gtk_label_get_line_wrap_mode().
// gtk_label_get_layout_offsets().
// gtk_label_get_layout().
// gtk_label_get_mnemonic_widget().

// GetSelectable is a wrapper around gtk_label_get_selectable().
func (v *Label) GetSelectable() bool {
	c := C.gtk_label_get_selectable(v.native())
	return gobool(c)
}

// GetText is a wrapper around gtk_label_get_text().
func (v *Label) GetText() (string, error) {
	c := C.gtk_label_get_text(v.native())
	if c == nil {
		return "", nilPtrErr
	}
	return C.GoString((*C.char)(c)), nil
}

// GetJustify is a wrapper around gtk_label_get_justify().
func (v *Label) GetJustify() Justification {
	c := C.gtk_label_get_justify(v.native())
	return Justification(c)
}

// GetEllipsize is a wrapper around gtk_label_get_ellipsize().
func (v *Label) GetEllipsize() pango.EllipsizeMode {
	c := C.gtk_label_get_ellipsize(v.native())
	return pango.EllipsizeMode(c)
}

// GetCurrentUri is a wrapper around gtk_label_get_current_uri().
func (v *Label) GetCurrentUri() string {
	c := C.gtk_label_get_current_uri(v.native())
	return C.GoString((*C.char)(c))
}

// GetTrackVisitedLinks is a wrapper around gtk_label_get_track_visited_links().
func (v *Label) GetTrackVisitedLinks() bool {
	c := C.gtk_label_get_track_visited_links(v.native())
	return gobool(c)
}

// SetTrackVisitedLinks is a wrapper around gtk_label_set_track_visited_links().
func (v *Label) SetTrackVisitedLinks(trackLinks bool) {
	C.gtk_label_set_track_visited_links(v.native(), gbool(trackLinks))
}

// GetAngle is a wrapper around gtk_label_get_angle().
func (v *Label) GetAngle() float64 {
	c := C.gtk_label_get_angle(v.native())
	return float64(c)
}

// SetAngle is a wrapper around gtk_label_set_angle().
func (v *Label) SetAngle(angle float64) {
	C.gtk_label_set_angle(v.native(), C.gdouble(angle))
}

// GetSelectionBounds is a wrapper around gtk_label_get_selection_bounds().
func (v *Label) GetSelectionBounds() (start, end int, nonEmpty bool) {
	var cstart, cend C.gint
	c := C.gtk_label_get_selection_bounds(v.native(), &cstart, &cend)
	return int(cstart), int(cend), gobool(c)
}

// GetSingleLineMode is a wrapper around gtk_label_get_single_line_mode().
func (v *Label) GetSingleLineMode() bool {
	c := C.gtk_label_get_single_line_mode(v.native())
	return gobool(c)
}

// SetSingleLineMode is a wrapper around gtk_label_set_single_line_mode().
func (v *Label) SetSingleLineMode(mode bool) {
	C.gtk_label_set_single_line_mode(v.native(), gbool(mode))
}

// GetUseMarkup is a wrapper around gtk_label_get_use_markup().
func (v *Label) GetUseMarkup() bool {
	c := C.gtk_label_get_use_markup(v.native())
	return gobool(c)
}

// SetUseMarkup is a wrapper around gtk_label_set_use_markup().
func (v *Label) SetUseMarkup(use bool) {
	C.gtk_label_set_use_markup(v.native(), gbool(use))
}

// GetUseUnderline is a wrapper around gtk_label_get_use_underline().
func (v *Label) GetUseUnderline() bool {
	c := C.gtk_label_get_use_underline(v.native())
	return gobool(c)
}

// SetUseUnderline is a wrapper around gtk_label_set_use_underline().
func (v *Label) SetUseUnderline(use bool) {
	C.gtk_label_set_use_underline(v.native(), gbool(use))
}

// LabelNewWithMnemonic is a wrapper around gtk_label_new_with_mnemonic().
func LabelNewWithMnemonic(str string) (*Label, error) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	c := C.gtk_label_new_with_mnemonic((*C.gchar)(cstr))
	if c == nil {
		return nil, nilPtrErr
	}
	obj := glib.Take(unsafe.Pointer(c))
	return wrapLabel(obj), nil
}

// SelectRegion is a wrapper around gtk_label_select_region().
func (v *Label) SelectRegion(startOffset, endOffset int) {
	C.gtk_label_select_region(v.native(), C.gint(startOffset),
		C.gint(endOffset))
}

// SetSelectable is a wrapper around gtk_label_set_selectable().
func (v *Label) SetSelectable(setting bool) {
	C.gtk_label_set_selectable(v.native(), gbool(setting))
}

// SetLabel is a wrapper around gtk_label_set_label().
func (v *Label) SetLabel(str string) {
	cstr := C.CString(str)
	defer C.free(unsafe.Pointer(cstr))
	C.gtk_label_set_label(v.native(), (*C.gchar)(cstr))
}

// GetLabel is a wrapper around gtk_label_get_label().
func (v *Label) GetLabel() string {
	c := C.gtk_label_get_label(v.native())
	if c == nil {
		return ""
	}
	return C.GoString((*C.char)(c))
}

// GetMnemonicKeyval is a wrapper around gtk_label_get_mnemonic_keyval().
func (v *Label) GetMnemonicKeyval() uint {
	return uint(C.gtk_label_get_mnemonic_keyval(v.native()))
}

// SetMnemonicWidget is a wrapper around gtk_label_set_mnemonic_widget().
func (v *Label) SetMnemonicWidget(widget IWidget) {
	C.gtk_label_set_mnemonic_widget(v.native(), widget.toWidget())
}
<|endoftext|>


<commit_before>package taskgraph.op

// For more information read: http://ewencp.org/blog/golang-iterators/
// We need an reasonably efficient way to enumerate all indexes.
//
// For example, we can do:
// sum := 0.0
// for it := param.GetIndexIterator(); it.Next(); {
//    sum += param.Get(it.Index());
//}
type IndexIterator interface {
	Index() uint64
	Next() bool
	Rewind()
}

// We need some interface to define function and how we optimize them.
type Parameter interface {
	Get(index uint64) float32
	Set(index uint64, value float32)
	Add(index uint64, value float32)

	// This allow us to generate a same wide parameter for manipulation.
	Clone() Parameter

	// This allow one to enumerate through all parameters
	IndexIterator() IndexIterator
}

// There are many different ways one can optimize a function, but the
// most effective ones need gradient.
// The interface is designed so that one can compose function easily.
// The semantics is simply add gradient from this function to corresponding
// dimensions on the output paramenter gradient.
type Function interface {
	Evaluate(param Parameter, gradient *Parameter) float32
}

// This defines an additive function. One can compose the function this way easily.
type SumFunction struct {
	func1,func2 *Function
}

func (rf *SumFunction) Evaluate(param Parameter, gradient *Parameter) float32 {
	sum := rf.func1.Evaluate(param, gradient)
	sum += rf.func2.Evaluate(param, gradient)
	return sum
}

// This implements l1 l2 regularization.
type Regularization struct {
	iter IndexIterator
	l1reg, l2reg float32
}

func (r *Regularization) Evaluate(param Parameter, gradient *Parameter) float32 {
	r.iter.Rewind()
	sum := float64(0)
	for r.iter.Next() {
		index := r.iter.Index()
		value := param.Get(index)
		sum += l1reg*value
		sum += 0.5*l2reg*value*value;
		gradient.Add(index, l1reg)
		gradient.Add(index, l2reg*value)
	}
	return float32(sum)
}

// High level interface for minimization. This assume that we start
// with one point in the parameter space, and end with an optimal
// point. Return true if we find optimal point.
type Minimizer interface {
	Minimize(func Function, param Parameter) bool
}
<commit_msg>make it compile<commit_after>package taskgraph_op

// For more information read: http://ewencp.org/blog/golang-iterators/
// We need an reasonably efficient way to enumerate all indexes.
//
// For example, we can do:
// sum := 0.0
// for it := param.GetIndexIterator(); it.Next(); {
//    sum += param.Get(it.Index());
//}
type IndexIterator interface {
	Index() uint64
	Next() bool
	Rewind()
}

// We need some interface to define function and how we optimize them.
type Parameter interface {
	Get(index uint64) float32
	Set(index uint64, value float32)
	Add(index uint64, value float32)

	// This allow us to generate a same wide parameter for manipulation.
	Clone() Parameter

	// This allow one to enumerate through all parameters
	IndexIterator() IndexIterator
}

// There are many different ways one can optimize a function, but the
// most effective ones need gradient.
// The interface is designed so that one can compose function easily.
// The semantics is simply add gradient from this function to corresponding
// dimensions on the output paramenter gradient.
type Function interface {
	Evaluate(param Parameter, gradient Parameter) float32
}

// This defines an additive function. One can compose the function this way easily.
type SumFunction struct {
	func1, func2 Function
}

func (rf *SumFunction) Evaluate(param Parameter, gradient Parameter) float32 {
	sum := rf.func1.Evaluate(param, gradient)
	sum += rf.func2.Evaluate(param, gradient)
	return sum
}

// This implements l1 l2 regularization.
type Regularization struct {
	iter         IndexIterator
	l1reg, l2reg float32
}

func (r *Regularization) Evaluate(param Parameter, gradient Parameter) float32 {
	r.iter.Rewind()
	sum := float64(0)
	for r.iter.Next() {
		index := r.iter.Index()
		value := param.Get(index)
		sum += float64(r.l1reg * value)
		sum += float64(0.5 * r.l2reg * value * value)
		gradient.Add(index, r.l1reg)
		gradient.Add(index, r.l2reg*value)
	}
	return float32(sum)
}

// High level interface for minimization. This assume that we start
// with one point in the parameter space, and end with an optimal
// point. Return true if we find optimal point.
type Minimizer interface {
	Minimize(function Function, param Parameter) bool
}
<|endoftext|>