<commit_before>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is an environment var containing the GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is an environment var containing the GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise users.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is an environment var to handle debug mode
	EnvDebug = "GHR_DEBUG"
)

// Exit codes are set to a value that represent an exit code for a particular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeReleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	defaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is set
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintRedf prints red error message to console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the main command line object
type CLI struct {
	// outStream and errStream correspond to stdout and stderr, respectively,
	// to take messages from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		name       string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&name, "name", "", "")
	flags.StringVar(&name, "n", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", defaultParallel, "")
	flags.IntVar(&parallel, "p", defaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flags
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner username.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		origin, err := gitconfig.OriginURL()
		if err == nil {
			owner = retrieveOwnerName(origin)
		}
		if len(owner) == 0 {
			owner, err = gitconfig.GithubUser()
			if err != nil {
				owner, err = gitconfig.Username()
			}

			if err != nil {
				PrintRedf(cli.errStream,
					"Failed to set up ghr: repository owner name not found\n")
				fmt.Fprintf(cli.errStream,
					"Please set it via `-u` option.\n\n"+
						"You can set default owner name in `github.username` or `user.name`\n"+
						"in `~/.gitconfig` file\n")
				return ExitCodeOwnerNotFound
			}
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeRepoNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub API token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API URL. Base URL can also be provided via env var for use with GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	Debugf("Name: %s", name)

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(name),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()
	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	if !draft {
		_, err := ghr.GitHub.EditRelease(ctx, *release.ID, &github.RepositoryRelease{
			Draft: github.Bool(false),
		})
		if err != nil {
			PrintRedf(cli.errStream, "Failed to publish release: %s\n", err)
			return ExitCodeError
		}
	}

	return ExitCodeOK
}

var ownerNameReg = regexp.MustCompile(`([-a-zA-Z0-9]+)/[^/]+$`)

func retrieveOwnerName(repoURL string) string {
	matched := ownerNameReg.FindStringSubmatch(repoURL)
	if len(matched) < 2 {
		return ""
	}
	return matched[1]
}

// maskString is used to mask a string which should not be displayed
// directly, like the auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository owner name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -name, -n          GitHub release title. By default the tag is used.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

`
<commit_msg>Add --soft option to cli<commit_after>// Command ghr is a tool to create a Github Release and upload your
// artifacts in parallel.
package main

import (
	"context"
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/google/go-github/github"
	"github.com/mitchellh/colorstring"
	"github.com/tcnksm/go-gitconfig"
)

const (
	// EnvGitHubToken is an environment var containing the GitHub API token
	EnvGitHubToken = "GITHUB_TOKEN"

	// EnvGitHubAPI is an environment var containing the GitHub API base endpoint.
	// This is used mainly by GitHub Enterprise users.
	EnvGitHubAPI = "GITHUB_API"

	// EnvDebug is an environment var to handle debug mode
	EnvDebug = "GHR_DEBUG"
)

// Exit codes are set to a value that represent an exit code for a particular error.
const (
	ExitCodeOK int = 0

	// Errors start at 10
	ExitCodeError = 10 + iota
	ExitCodeParseFlagsError
	ExitCodeBadArgs
	ExitCodeInvalidURL
	ExitCodeTokenNotFound
	ExitCodeOwnerNotFound
	ExitCodeRepoNotFound
	ExitCodeReleaseError
)

const (
	defaultCheckTimeout = 2 * time.Second
	defaultBaseURL      = "https://api.github.com/"
	defaultParallel     = -1
)

// Debugf prints debug output when EnvDebug is set
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}

// PrintRedf prints red error message to console.
func PrintRedf(w io.Writer, format string, args ...interface{}) {
	format = fmt.Sprintf("[red]%s[reset]", format)
	fmt.Fprint(w,
		colorstring.Color(fmt.Sprintf(format, args...)))
}

// CLI is the main command line object
type CLI struct {
	// outStream and errStream correspond to stdout and stderr, respectively,
	// to take messages from the CLI.
	outStream, errStream io.Writer
}

// Run invokes the CLI with the given arguments.
func (cli *CLI) Run(args []string) int {

	var (
		owner string
		repo  string
		token string

		commitish  string
		name       string
		body       string
		draft      bool
		prerelease bool

		parallel int

		recreate bool
		replace  bool
		soft     bool

		stat    bool
		version bool
		debug   bool
	)

	flags := flag.NewFlagSet(Name, flag.ContinueOnError)
	flags.SetOutput(cli.errStream)
	flags.Usage = func() {
		fmt.Fprint(cli.errStream, helpText)
	}

	flags.StringVar(&owner, "username", "", "")
	flags.StringVar(&owner, "owner", "", "")
	flags.StringVar(&owner, "u", "", "")

	flags.StringVar(&repo, "repository", "", "")
	flags.StringVar(&repo, "r", "", "")

	flags.StringVar(&token, "token", os.Getenv(EnvGitHubToken), "")
	flags.StringVar(&token, "t", os.Getenv(EnvGitHubToken), "")

	flags.StringVar(&commitish, "commitish", "", "")
	flags.StringVar(&commitish, "c", "", "")

	flags.StringVar(&name, "name", "", "")
	flags.StringVar(&name, "n", "", "")

	flags.StringVar(&body, "body", "", "")
	flags.StringVar(&body, "b", "", "")

	flags.BoolVar(&draft, "draft", false, "")
	flags.BoolVar(&prerelease, "prerelease", false, "")

	flags.IntVar(&parallel, "parallel", defaultParallel, "")
	flags.IntVar(&parallel, "p", defaultParallel, "")

	flags.BoolVar(&recreate, "delete", false, "")
	flags.BoolVar(&recreate, "recreate", false, "")

	flags.BoolVar(&replace, "replace", false, "")

	flags.BoolVar(&soft, "soft", false, "")

	flags.BoolVar(&version, "version", false, "")
	flags.BoolVar(&version, "v", false, "")

	flags.BoolVar(&debug, "debug", false, "")

	// Deprecated
	flags.BoolVar(&stat, "stat", false, "")

	// Parse flags
	if err := flags.Parse(args[1:]); err != nil {
		return ExitCodeParseFlagsError
	}

	if debug {
		os.Setenv(EnvDebug, "1")
		Debugf("Run as DEBUG mode")
	}

	// Show version and check latest version release
	if version {
		fmt.Fprintf(cli.outStream, OutputVersion())
		return ExitCodeOK
	}

	parsedArgs := flags.Args()
	if len(parsedArgs) != 2 {
		PrintRedf(cli.errStream,
			"Invalid argument: you must set TAG and PATH name.\n")
		return ExitCodeBadArgs
	}
	tag, path := parsedArgs[0], parsedArgs[1]

	// Extract github repository owner username.
	// If it's not provided via command line flag, read it from .gitconfig
	// (github user or git user).
	if len(owner) == 0 {
		origin, err := gitconfig.OriginURL()
		if err == nil {
			owner = retrieveOwnerName(origin)
		}
		if len(owner) == 0 {
			owner, err = gitconfig.GithubUser()
			if err != nil {
				owner, err = gitconfig.Username()
			}

			if err != nil {
				PrintRedf(cli.errStream,
					"Failed to set up ghr: repository owner name not found\n")
				fmt.Fprintf(cli.errStream,
					"Please set it via `-u` option.\n\n"+
						"You can set default owner name in `github.username` or `user.name`\n"+
						"in `~/.gitconfig` file\n")
				return ExitCodeOwnerNotFound
			}
		}
	}
	Debugf("Owner: %s", owner)

	// Extract repository name from files.
	// If not provided, read it from .git/config file.
	if len(repo) == 0 {
		var err error
		repo, err = gitconfig.Repository()
		if err != nil {
			PrintRedf(cli.errStream,
				"Failed to set up ghr: repository name not found\n")
			fmt.Fprintf(cli.errStream,
				"ghr reads it from `.git/config` file. Change directory to \n"+
					"repository root directory or setup git repository.\n"+
					"Or set it via `-r` option.\n")
			return ExitCodeRepoNotFound
		}
	}
	Debugf("Repository: %s", repo)

	// If GitHub API token is not provided via command line flag
	// or env var then read it from .gitconfig file.
	if len(token) == 0 {
		var err error
		token, err = gitconfig.GithubToken()
		if err != nil {
			PrintRedf(cli.errStream, "Failed to set up ghr: token not found\n")
			fmt.Fprintf(cli.errStream,
				"To use ghr, you need a GitHub API token.\n"+
					"Please set it via `%s` env var or `-t` option.\n\n"+
					"If you don't have one, visit official doc (goo.gl/jSnoI)\n"+
					"and get it first.\n",
				EnvGitHubToken)
			return ExitCodeTokenNotFound
		}
	}
	Debugf("Github API Token: %s", maskString(token))

	// Set Base GitHub API URL. Base URL can also be provided via env var for use with GHE.
	baseURLStr := defaultBaseURL
	if urlStr := os.Getenv(EnvGitHubAPI); len(urlStr) != 0 {
		baseURLStr = urlStr
	}
	Debugf("Base GitHub API URL: %s", baseURLStr)

	if parallel <= 0 {
		parallel = runtime.NumCPU()
	}
	Debugf("Parallel factor: %d", parallel)

	localAssets, err := LocalAssets(path)
	if err != nil {
		PrintRedf(cli.errStream,
			"Failed to find assets from %s: %s\n", path, err)
		return ExitCodeError
	}
	Debugf("Number of file to upload: %d", len(localAssets))

	// Create a GitHub client
	gitHubClient, err := NewGitHubClient(owner, repo, token, baseURLStr)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to construct GitHub client: %s\n", err)
		return ExitCodeError
	}

	ghr := GHR{
		GitHub:    gitHubClient,
		outStream: cli.outStream,
	}

	Debugf("Name: %s", name)

	// Prepare create release request
	req := &github.RepositoryRelease{
		Name:            github.String(name),
		TagName:         github.String(tag),
		Prerelease:      github.Bool(prerelease),
		Draft:           github.Bool(draft),
		TargetCommitish: github.String(commitish),
		Body:            github.String(body),
	}

	ctx := context.TODO()

	if soft {
		_, err := ghr.GitHub.GetRelease(ctx, *req.TagName)

		if err == nil {
			fmt.Fprintf(cli.outStream, "ghr aboted since tag `%s` already exists\n", *req.TagName)
			return ExitCodeOK
		}

		if err != nil && err != ErrReleaseNotFound {
			PrintRedf(cli.errStream, "Failed to get GitHub release: %s\n", err)
			return ExitCodeError
		}
	}

	release, err := ghr.CreateRelease(ctx, req, recreate)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to create GitHub release page: %s\n", err)
		return ExitCodeError
	}

	if replace {
		err := ghr.DeleteAssets(ctx, *release.ID, localAssets, parallel)
		if err != nil {
			PrintRedf(cli.errStream, "Failed to delete existing assets: %s\n", err)
			return ExitCodeError
		}
	}

	// FIXME(tcnksm): More ideal way to change this
	// This is for Github enterprise
	if err := ghr.GitHub.SetUploadURL(*release.UploadURL); err != nil {
		fmt.Fprintf(cli.errStream, "Failed to set upload URL %s: %s\n", *release.UploadURL, err)
		return ExitCodeError
	}

	err = ghr.UploadAssets(ctx, *release.ID, localAssets, parallel)
	if err != nil {
		PrintRedf(cli.errStream, "Failed to upload one of assets: %s\n", err)
		return ExitCodeError
	}

	if !draft {
		_, err := ghr.GitHub.EditRelease(ctx, *release.ID, &github.RepositoryRelease{
			Draft: github.Bool(false),
		})
		if err != nil {
			PrintRedf(cli.errStream, "Failed to publish release: %s\n", err)
			return ExitCodeError
		}
	}

	return ExitCodeOK
}

var ownerNameReg = regexp.MustCompile(`([-a-zA-Z0-9]+)/[^/]+$`)

func retrieveOwnerName(repoURL string) string {
	matched := ownerNameReg.FindStringSubmatch(repoURL)
	if len(matched) < 2 {
		return ""
	}
	return matched[1]
}

// maskString is used to mask a string which should not be displayed
// directly, like the auth token
func maskString(s string) string {
	if len(s) < 5 {
		return "**** (masked)"
	}

	return s[:5] + "**** (masked)"
}

var helpText = `Usage: ghr [options...] TAG PATH

ghr is a tool to create Release on Github and upload your
artifacts to it. ghr parallelizes upload of multiple artifacts.

You must specify tag (e.g., v1.0.0) and PATH to local artifacts.
If PATH is directory, ghr globs all files in the directory and
upload it. If PATH is a file then, upload only it.

And you also must provide GitHub API token which has enough permission
(For a private repository you need the 'repo' scope and for a public
repository need 'public_repo' scope). You can get token from GitHub's
account setting page.

You can use ghr on GitHub Enterprise. Set base URL via GITHUB_API
environment variable.

Options:

  -username, -u      Github repository owner name. By default, ghr
                     extracts it from global gitconfig value.

  -repository, -r    GitHub repository name. By default, ghr extracts
                     repository name from current directory's .git/config.

  -name, -n          GitHub release title. By default the tag is used.

  -token, -t         GitHub API Token. By default, ghr reads it from
                     'GITHUB_TOKEN' env var.

  -parallel=-1       Parallelization factor. This option limits amount
                     of parallelism of uploading. By default, ghr uses
                     number of logic CPU.

  -recreate          Recreate release if it already exists. If want to
                     upload to same release and replace use '-replace'.

  -replace           Replace artifacts if it is already uploaded. ghr
                     thinks it's same when local artifact base name
                     and uploaded file name are same.

  -soft              Stop uploading if the repository already has release
                     with the specified tag.
`
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"regexp"
)

type parseStage int

const (
	psNumArgs parseStage = iota
	psCommand
	psWatchTarget
	psInvertMatch
)

type parseError struct {
	Stage   parseStage
	Message string
}

func (e parseError) Error() string {
	return fmt.Sprintf("parse: %s: %s", parseStageStr(e.Stage), e.Message)
}

func parseStageStr(stage parseStage) string {
	switch stage {
	case psNumArgs:
		return "arg count"
	case psCommand:
		return "COMMAND"
	case psWatchTarget:
		return "DIR_TO_WATCH"
	case psInvertMatch:
		return "FILE_IGNORE_PATTERN"
	}
	panic(fmt.Sprintf("unexpected parseStage found, '%d'", int(stage)))
}

func expectedNonZero(stage parseStage) parseError {
	return parseError{
		Stage:   stage,
		Message: fmt.Sprintf("expected non-zero %s as argument", parseStageStr(stage)),
	}
}

func parseCli() (runDirective, error) {
	args := os.Args[1:]
	if len(args) < 1 {
		return runDirective{}, &parseError{
			Stage:   psNumArgs,
			Message: "at least COMMAND argument needed",
		}
	}

	buildCmd := args[0]
	if len(buildCmd) < 1 {
		return runDirective{}, expectedNonZero(psCommand)
	}

	directive := runDirective{BuildCmd: buildCmd}
	if len(args) > 1 {
		watchTargetPath := args[1]
		if len(watchTargetPath) < 1 {
			return runDirective{}, expectedNonZero(psWatchTarget)
		}
		watchTarget, e := os.Stat(watchTargetPath)
		if e != nil {
			return runDirective{}, parseError{Stage: psWatchTarget, Message: e.Error()}
		}
		if !watchTarget.IsDir() {
			return runDirective{}, parseError{
				Stage:   psWatchTarget,
				Message: fmt.Sprintf("%s must be a directory", parseStageStr(psWatchTarget)),
			}
		}
		directive.WatchTarget = watchTarget

		if len(args) > 2 {
			invertMatch, e := regexp.Compile(args[1])
			if e != nil {
				return runDirective{}, &parseError{
					Stage: psInvertMatch,
					Message: fmt.Sprintf(
						"reading %s pattern: %s", parseStageStr(psInvertMatch), e),
				}
			}
			directive.InvertMatch = invertMatch
		}
	}

	return directive, nil
}
<commit_msg>bug: parsing wrong commandling argument<commit_after>package main

import (
	"fmt"
	"os"
	"regexp"
)

type parseStage int

const (
	psNumArgs parseStage = iota
	psCommand
	psWatchTarget
	psInvertMatch
)

type parseError struct {
	Stage   parseStage
	Message string
}

func (e parseError) Error() string {
	return fmt.Sprintf("parse: %s: %s", parseStageStr(e.Stage), e.Message)
}

func parseStageStr(stage parseStage) string {
	switch stage {
	case psNumArgs:
		return "arg count"
	case psCommand:
		return "COMMAND"
	case psWatchTarget:
		return "DIR_TO_WATCH"
	case psInvertMatch:
		return "FILE_IGNORE_PATTERN"
	}
	panic(fmt.Sprintf("unexpected parseStage found, '%d'", int(stage)))
}

func expectedNonZero(stage parseStage) parseError {
	return parseError{
		Stage:   stage,
		Message: fmt.Sprintf("expected non-zero %s as argument", parseStageStr(stage)),
	}
}

func parseCli() (runDirective, error) {
	args := os.Args[1:]
	if len(args) < 1 {
		return runDirective{}, &parseError{
			Stage:   psNumArgs,
			Message: "at least COMMAND argument needed",
		}
	}

	buildCmd := args[0]
	if len(buildCmd) < 1 {
		return runDirective{}, expectedNonZero(psCommand)
	}

	directive := runDirective{BuildCmd: buildCmd}
	if len(args) > 1 {
		watchTargetPath := args[1]
		if len(watchTargetPath) < 1 {
			return runDirective{}, expectedNonZero(psWatchTarget)
		}
		watchTarget, e := os.Stat(watchTargetPath)
		if e != nil {
			return runDirective{}, parseError{Stage: psWatchTarget, Message: e.Error()}
		}
		if !watchTarget.IsDir() {
			return runDirective{}, parseError{
				Stage:   psWatchTarget,
				Message: fmt.Sprintf("%s must be a directory", parseStageStr(psWatchTarget)),
			}
		}
		directive.WatchTarget = watchTarget

		if len(args) > 2 {
			invertMatch, e := regexp.Compile(args[2])
			if e != nil {
				return runDirective{}, &parseError{
					Stage: psInvertMatch,
					Message: fmt.Sprintf(
						"reading %s pattern: %s", parseStageStr(psInvertMatch), e),
				}
			}
			directive.InvertMatch = invertMatch
		}
	}

	return directive, nil
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xlangx

import (
	"bytes"
	"reflect"

	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/runtime/graphx/schema"
	"github.com/apache/beam/sdks/go/pkg/beam/internal/errors"
	"github.com/apache/beam/sdks/go/pkg/beam/model/pipeline_v1"
	"google.golang.org/protobuf/proto"
)

// EncodeStructPayload takes a native Go struct and returns a marshaled
// ExternalConfigurationPayload proto, containing a Schema representation of
// the original type and the original value encoded as a Row. This is intended
// to be used as the expansion payload for an External transform.
func EncodeStructPayload(pl interface{}) ([]byte, error) {
	rt := reflect.TypeOf(pl)

	// Encode payload value as a Row.
	enc, err := coder.RowEncoderForStruct(rt)
	if err != nil {
		err = errors.WithContext(err, "creating Row encoder for payload")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}
	var buf bytes.Buffer
	if err := enc(pl, &buf); err != nil {
		err = errors.WithContext(err, "encoding payload as Row")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	// Convert payload type into Schema representation.
	scm, err := schema.FromType(rt)
	if err != nil {
		err = errors.WithContext(err, "creating schema for payload")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	// Put schema and row into payload proto, and marshal it.
	ecp := &pipeline_v1.ExternalConfigurationPayload{
		Schema:  scm,
		Payload: buf.Bytes(),
	}
	plBytes, err := proto.Marshal(ecp)
	if err != nil {
		err = errors.Wrapf(err, "failed to marshal payload as proto")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	return plBytes, nil
}
<commit_msg>Minor: name pipeline_v1 import to match rest of SDK. (#14775)<commit_after>// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.  See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.  You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package xlangx

import (
	"bytes"
	"reflect"

	pipepb "github.com/apache/beam/sdks/go/pkg/beam/model/pipeline_v1"
	"github.com/apache/beam/sdks/go/pkg/beam/core/graph/coder"
	"github.com/apache/beam/sdks/go/pkg/beam/core/runtime/graphx/schema"
	"github.com/apache/beam/sdks/go/pkg/beam/internal/errors"
	"google.golang.org/protobuf/proto"
)

// EncodeStructPayload takes a native Go struct and returns a marshaled
// ExternalConfigurationPayload proto, containing a Schema representation of
// the original type and the original value encoded as a Row. This is intended
// to be used as the expansion payload for an External transform.
func EncodeStructPayload(pl interface{}) ([]byte, error) {
	rt := reflect.TypeOf(pl)

	// Encode payload value as a Row.
	enc, err := coder.RowEncoderForStruct(rt)
	if err != nil {
		err = errors.WithContext(err, "creating Row encoder for payload")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}
	var buf bytes.Buffer
	if err := enc(pl, &buf); err != nil {
		err = errors.WithContext(err, "encoding payload as Row")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	// Convert payload type into Schema representation.
	scm, err := schema.FromType(rt)
	if err != nil {
		err = errors.WithContext(err, "creating schema for payload")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	// Put schema and row into payload proto, and marshal it.
	ecp := &pipepb.ExternalConfigurationPayload{
		Schema:  scm,
		Payload: buf.Bytes(),
	}
	plBytes, err := proto.Marshal(ecp)
	if err != nil {
		err = errors.Wrapf(err, "failed to marshal payload as proto")
		return []byte{}, errors.WithContextf(err, "encoding external payload %v", pl)
	}

	return plBytes, nil
}
<|endoftext|>


<commit_before>package cloudflare

import (
	"encoding/json"
	"net/url"

	"github.com/pkg/errors"
)

/*
Create a DNS record and return the response (created ID & metadata).

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
  POST /zones/:zone_identifier/dns_records
*/
func (api *API) CreateDNSRecord(zoneID string, rr DNSRecord) (*DNSRecordResponse, error) {
	uri := "/zones/" + zoneID + "/dns_records"
	res, err := api.makeRequest("POST", uri, rr)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}

	var recordResp *DNSRecordResponse
	err = json.Unmarshal(res, recordResp)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return recordResp, nil
}

/*
Fetches DNS records for a zone.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
  GET /zones/:zone_identifier/dns_records
*/
func (api *API) DNSRecords(zoneID string, rr DNSRecord) ([]DNSRecord, error) {
	// Construct a query string
	v := url.Values{}
	if rr.Name != "" {
		v.Set("name", rr.Name)
	}
	if rr.Type != "" {
		v.Set("type", rr.Type)
	}
	if rr.Content != "" {
		v.Set("content", rr.Content)
	}
	var query string
	if len(v) > 0 {
		query = "?" + v.Encode()
	}
	uri := "/zones/" + zoneID + "/dns_records" + query
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return []DNSRecord{}, errors.Wrap(err, errMakeRequestError)
	}
	var r DNSListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []DNSRecord{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

/*
Fetches a single DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
  GET /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DNSRecord(zoneID, recordID string) (DNSRecord, error) {
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return DNSRecord{}, errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return DNSRecord{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

/*
Change a DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
  PUT /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) UpdateDNSRecord(zoneID, recordID string, rr DNSRecord) error {
	rec, err := api.DNSRecord(zoneID, recordID)
	if err != nil {
		return err
	}
	rr.Name = rec.Name
	rr.Type = rec.Type
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("PUT", uri, rr)
	if err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}

/*
Delete a DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record
  DELETE /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DeleteDNSRecord(zoneID, recordID string) error {
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("DELETE", uri, nil)
	if err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}
<commit_msg>[bugfix] CreateDNSRecord wasn't being passed a pointer. (#53)<commit_after>package cloudflare

import (
	"encoding/json"
	"net/url"

	"github.com/pkg/errors"
)

/*
Create a DNS record and return the response (created ID & metadata).

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-create-dns-record
  POST /zones/:zone_identifier/dns_records
*/
func (api *API) CreateDNSRecord(zoneID string, rr DNSRecord) (*DNSRecordResponse, error) {
	uri := "/zones/" + zoneID + "/dns_records"
	res, err := api.makeRequest("POST", uri, rr)
	if err != nil {
		return nil, errors.Wrap(err, errMakeRequestError)
	}

	var recordResp *DNSRecordResponse
	err = json.Unmarshal(res, &recordResp)
	if err != nil {
		return nil, errors.Wrap(err, errUnmarshalError)
	}

	return recordResp, nil
}

/*
Fetches DNS records for a zone.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-list-dns-records
  GET /zones/:zone_identifier/dns_records
*/
func (api *API) DNSRecords(zoneID string, rr DNSRecord) ([]DNSRecord, error) {
	// Construct a query string
	v := url.Values{}
	if rr.Name != "" {
		v.Set("name", rr.Name)
	}
	if rr.Type != "" {
		v.Set("type", rr.Type)
	}
	if rr.Content != "" {
		v.Set("content", rr.Content)
	}
	var query string
	if len(v) > 0 {
		query = "?" + v.Encode()
	}
	uri := "/zones/" + zoneID + "/dns_records" + query
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return []DNSRecord{}, errors.Wrap(err, errMakeRequestError)
	}
	var r DNSListResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return []DNSRecord{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

/*
Fetches a single DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-dns-record-details
  GET /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DNSRecord(zoneID, recordID string) (DNSRecord, error) {
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("GET", uri, nil)
	if err != nil {
		return DNSRecord{}, errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return DNSRecord{}, errors.Wrap(err, errUnmarshalError)
	}
	return r.Result, nil
}

/*
Change a DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-update-dns-record
  PUT /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) UpdateDNSRecord(zoneID, recordID string, rr DNSRecord) error {
	rec, err := api.DNSRecord(zoneID, recordID)
	if err != nil {
		return err
	}
	rr.Name = rec.Name
	rr.Type = rec.Type
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("PUT", uri, rr)
	if err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}

/*
Delete a DNS record.

API reference:
  https://api.cloudflare.com/#dns-records-for-a-zone-delete-dns-record
  DELETE /zones/:zone_identifier/dns_records/:identifier
*/
func (api *API) DeleteDNSRecord(zoneID, recordID string) error {
	uri := "/zones/" + zoneID + "/dns_records/" + recordID
	res, err := api.makeRequest("DELETE", uri, nil)
	if err != nil {
		return errors.Wrap(err, errMakeRequestError)
	}
	var r DNSRecordResponse
	err = json.Unmarshal(res, &r)
	if err != nil {
		return errors.Wrap(err, errUnmarshalError)
	}
	return nil
}
<|endoftext|>


<commit_before>/*
Package telnet provides TELNET and TELNETS client and server implementations
in a style similar to the "net/http" library that is part of the Go standard library,
including support for "middleware"; TELNETS is secure TELNET, with the TELNET protocol
over a secured TLS (or SSL) connection.

Example TELNET Server

ListenAndServe starts a TELNET server with a given address and handler.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServe(":23", handler)
	if nil != err {
		panic(err)
	}

Example TELNETS Server

ListenAndServeTLS starts a (secure) TELNETS server with a given address and handler,
using the specified "cert.pem" and "key.pem" files.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServeTLS(":992", "cert.pem", "key.pem", handler)
	if nil != err {
		panic(err)
	}

TELNET vs TELNETS

If you are communicating over the open Internet, you should be using (the secure) TELNETS and ListenAndServeTLS.

If you are communicating just on localhost, then using just (the un-secure) TELNET and telnet.ListenAndServe may be OK.

If you are not sure which to use, use TELNETS and ListenAndServeTLS.

Example TELNET Shell Server

The previous 2 exaple servers were very very simple. Specifically, they just echoed back whatever
you submitted to it.

If you typed:

	Apple Banana Cherry\r\n

... it would send back:

	Apple Banana Cherry\r\n

(Exactly the same data you sent it.)

A more useful TELNET server can be made using the "github.com/reiver/go-telnet/telsh" sub-package.

The `telsh` sub-package provides "middleware" that enables you to create a "shell" interface (also
called a "command line interface" or "CLI") which most people would expect when using TELNET OR TELNETS.

For example:

	package main

	import (
		"github.com/reiver/go-oi"
		"github.com/reiver/go-telnet"
		"github.com/reiver/go-telnet/telsh"

		"time"
	)

	func main() {

		shellHandler := telsh.NewShellHandler()

		commandName := "date"
		shellHandler.Register(commandName, danceProducer)

		commandName = "animate"
		shellHandler.Register(commandName, animateProducer)

		addr := ":23"
		if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
			panic(err)
		}
	}

Note that in the example, so far, we have registered 2 commands: `date` and `animate`.

For this to actually work, we need to have code for the `date` and `animate` commands.

The actual implemenation for the `date` command could be done like the following:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := time.Now().Format(layout)

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

	func dateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(dateHandler)
	}

	var dateProducer = ProducerFunc(dateProducerFunc)

Note that your "real" work is in the `dateHandlerFunc` func.

And the actual implementation for the `animate` command could as done as follows:

	func animateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {

		for i:=0; i<20; i++ {
			oi.LongWriteString(stdout, "\r⠋")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠙")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠹")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠸")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠼")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠴")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠦")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠧")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠇")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠏")
			time.Sleep(50*time.Millisecond)
		}
		oi.LongWriteString(stdout, "\r \r\n")

		return nil
	}

	func animateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(animateHandler)
	}

	var animateProducer = ProducerFunc(animateProducerFunc)

Again, note that your "real" work is in the `animateHandlerFunc` func.

Generating PEM Files

If you are using the telnet.ListenAndServeTLS func or the telnet.Server.ListenAndServeTLS method, you will need to
supply "cert.pem" and "key.pem" files.

If you do not already have these files, the Go soure code contains a tool for generating these files for you.

It can be found at:

	$GOROOT/src/crypto/tls/generate_cert.go

So, for example, if your `$GOROOT` is the "/usr/local/go" directory, then it would be at:

	/usr/local/go/src/crypto/tls/generate_cert.go

If you run the command:

	go run $GOROOT/src/crypto/tls/generate_cert.go --help

... then you get the help information for "generate_cert.go".

Of course, you would replace or set `$GOROOT` with whatever your path actualy is. Again, for example,
if your `$GOROOT` is the "/usr/local/go" directory, then it would be:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --help

To demonstrate the usage of "generate_cert.go", you might run the following to generate certificates
that were bound to the hosts `127.0.0.1` and `localhost`:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --ca --host='127.0.0.1,localhost'

If you are not sure where "generate_cert.go" is on your computer, on Linux and Unix based systems, you might
be able to find the file with the command:

	locate /src/crypto/tls/generate_cert.go

(If it finds it, it should output the full path to this file.)

*/
package telnet
<commit_msg>updated docs<commit_after>/*
Package telnet provides TELNET and TELNETS client and server implementations
in a style similar to the "net/http" library that is part of the Go standard library,
including support for "middleware"; TELNETS is secure TELNET, with the TELNET protocol
over a secured TLS (or SSL) connection.

Example TELNET Server

ListenAndServe starts a TELNET server with a given address and handler.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServe(":23", handler)
	if nil != err {
		panic(err)
	}

Example TELNETS Server

ListenAndServeTLS starts a (secure) TELNETS server with a given address and handler,
using the specified "cert.pem" and "key.pem" files.

	handler := telnet.EchoHandler

	err := telnet.ListenAndServeTLS(":992", "cert.pem", "key.pem", handler)
	if nil != err {
		panic(err)
	}

TELNET vs TELNETS

If you are communicating over the open Internet, you should be using (the secure) TELNETS and ListenAndServeTLS.

If you are communicating just on localhost, then using just (the un-secure) TELNET and telnet.ListenAndServe may be OK.

If you are not sure which to use, use TELNETS and ListenAndServeTLS.

Example TELNET Shell Server

The previous 2 exaple servers were very very simple. Specifically, they just echoed back whatever
you submitted to it.

If you typed:

	Apple Banana Cherry\r\n

... it would send back:

	Apple Banana Cherry\r\n

(Exactly the same data you sent it.)

A more useful TELNET server can be made using the "github.com/reiver/go-telnet/telsh" sub-package.

The `telsh` sub-package provides "middleware" that enables you to create a "shell" interface (also
called a "command line interface" or "CLI") which most people would expect when using TELNET OR TELNETS.

For example:

	package main

	import (
		"github.com/reiver/go-oi"
		"github.com/reiver/go-telnet"
		"github.com/reiver/go-telnet/telsh"

		"time"
	)

	func main() {

		shellHandler := telsh.NewShellHandler()

		commandName := "date"
		shellHandler.Register(commandName, danceProducer)

		commandName = "animate"
		shellHandler.Register(commandName, animateProducer)

		addr := ":23"
		if err := telnet.ListenAndServe(addr, shellHandler); nil != err {
			panic(err)
		}
	}

Note that in the example, so far, we have registered 2 commands: `date` and `animate`.

For this to actually work, we need to have code for the `date` and `animate` commands.

The actual implemenation for the `date` command could be done like the following:

	func dateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {
		const layout = "Mon Jan 2 15:04:05 -0700 MST 2006"
		s := time.Now().Format(layout)

		if _, err := oi.LongWriteString(stdout, s); nil != err {
			return err
		}

		return nil
	}

	func dateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(dateHandler)
	}

	var dateProducer = ProducerFunc(dateProducerFunc)

Note that your "real" work is in the `dateHandlerFunc` func.

And the actual implementation for the `animate` command could as done as follows:

	func animateHandlerFunc(stdin io.ReadCloser, stdout io.WriteCloser, stderr io.WriteCloser) error {

		for i:=0; i<20; i++ {
			oi.LongWriteString(stdout, "\r⠋")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠙")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠹")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠸")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠼")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠴")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠦")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠧")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠇")
			time.Sleep(50*time.Millisecond)

			oi.LongWriteString(stdout, "\r⠏")
			time.Sleep(50*time.Millisecond)
		}
		oi.LongWriteString(stdout, "\r \r\n")

		return nil
	}

	func animateProducerFunc(ctx telsh.Context, name string, args ...string) telsh.Handler{
		return telsh.PromoteHandlerFunc(animateHandler)
	}

	var animateProducer = ProducerFunc(animateProducerFunc)

Again, note that your "real" work is in the `animateHandlerFunc` func.

Generating PEM Files

If you are using the telnet.ListenAndServeTLS func or the telnet.Server.ListenAndServeTLS method, you will need to
supply "cert.pem" and "key.pem" files.

If you do not already have these files, the Go soure code contains a tool for generating these files for you.

It can be found at:

	$GOROOT/src/crypto/tls/generate_cert.go

So, for example, if your `$GOROOT` is the "/usr/local/go" directory, then it would be at:

	/usr/local/go/src/crypto/tls/generate_cert.go

If you run the command:

	go run $GOROOT/src/crypto/tls/generate_cert.go --help

... then you get the help information for "generate_cert.go".

Of course, you would replace or set `$GOROOT` with whatever your path actualy is. Again, for example,
if your `$GOROOT` is the "/usr/local/go" directory, then it would be:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --help

To demonstrate the usage of "generate_cert.go", you might run the following to generate certificates
that were bound to the hosts `127.0.0.1` and `localhost`:

	go run /usr/local/go/src/crypto/tls/generate_cert.go --ca --host='127.0.0.1,localhost'

If you are not sure where "generate_cert.go" is on your computer, on Linux and Unix based systems, you might
be able to find the file with the command:

	locate /src/crypto/tls/generate_cert.go

(If it finds it, it should output the full path to this file.)

Terminal Codes

Terminal codes (also sometimes called 'terminal control codes') are used to issue various kinds of commands
to the terminal.

(Note that 'terminal control codes' are a completely separate concept for 'TELNET commands',
and the two should NOT be conflated or confused.)

The most common types of 'terminal codes' are the 'ANSI escape codes'. (Although there are other types too.)

ANSI Escape Codes

ANSI escape codes (also sometimes called 'ANSI escape sequences') are a common type of 'terminal code' used
to do things such as:

• moving the cursor,

• erasing the display,

• erasing the line,

• setting the graphics mode,

• setting the foregroup color,

• setting the background color,

• setting the screen resolution, and

• setting keyboard strings.

Setting The Foreground Color With ANSI Escape Codes

One of the abilities of ANSI escape codes is to set the foreground color.

Here is a table showing codes for this:

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[30m"` | `[]byte{27, '[', '3','0', 'm'}` |
	| Red          | `"\x1b[31m"` | `[]byte{27, '[', '3','1', 'm'}` |
	| Green        | `"\x1b[32m"` | `[]byte{27, '[', '3','2', 'm'}` |
	| Brown/Yellow | `"\x1b[33m"` | `[]byte{27, '[', '3','3', 'm'}` |
	| Blue         | `"\x1b[34m"` | `[]byte{27, '[', '3','4', 'm'}` |
	| Magenta      | `"\x1b[35m"` | `[]byte{27, '[', '3','5', 'm'}` |
	| Cyan         | `"\x1b[36m"` | `[]byte{27, '[', '3','6', 'm'}` |
	| Gray/White   | `"\x1b[37m"` | `[]byte{27, '[', '3','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'3'` and whatever.)

Setting The Background Color With ANSI Escape Codes

Another of the abilities of ANSI escape codes is to set the background color.

	| ANSI Color   | Go string    | Go []byte                       |
	| ------------ | ------------ | ------------------------------- |
	| Black        | `"\x1b[40m"` | `[]byte{27, '[', '4','0', 'm'}` |
	| Red          | `"\x1b[41m"` | `[]byte{27, '[', '4','1', 'm'}` |
	| Green        | `"\x1b[42m"` | `[]byte{27, '[', '4','2', 'm'}` |
	| Brown/Yellow | `"\x1b[43m"` | `[]byte{27, '[', '4','3', 'm'}` |
	| Blue         | `"\x1b[44m"` | `[]byte{27, '[', '4','4', 'm'}` |
	| Magenta      | `"\x1b[45m"` | `[]byte{27, '[', '4','5', 'm'}` |
	| Cyan         | `"\x1b[46m"` | `[]byte{27, '[', '4','6', 'm'}` |
	| Gray/White   | `"\x1b[47m"` | `[]byte{27, '[', '4','7', 'm'}` |

(Note that in the `[]byte` that the first `byte` is the number `27` (which
is the "escape" character) where the third and fouth characters are the
**not** number literals, but instead character literals `'3'` and whatever.)

*/
package telnet
<|endoftext|>


<commit_before>// docgo is a [Go](http://golang.org) implementation of [Jeremy Ashkenas]
// (http://github.com/jashkenas)'s [docco] (http://jashkenas.github.com/docco/),
// a literate-programming-style documentation generator.  Running docgo on your
// Go source files produces HTML with comments and code side-by-side.
//
// Comments are processed by [Markdown]
// (http://daringfireball.net/projects/markdown) using a [fork]
// (http://github.com/dhconnelly/blackfriday) of [Russ Ross]
// (http://github.com/russross)'s [BlackFriday]
// (http://github.com/russross/blackfriday) library, and code is
// syntax-highlighted using [litebrite](http://dhconnelly.github.com/litebrite),
// a Go syntax highlighting library.
//
// The source is available on [GitHub](http://github.com/dhconnelly/docgo).
//
// With a recent Go weekly build (I'm using `weekly.2012-2-07`), you can get,
// install, and run docgo by doing the following at a command line:
//
// `go get github.com/dhconnelly/docgo`

// `go install github.com/dhconnelly/docgo`

// `docgo file.go`
//
// This will create `file.html` in the current directory.
//
// There are two command-line flags:
//
// - `resdir`: a path to the directory containing the CSS styles and HTML
//   templates (this is usually the docgo source directory)
// - `outdir`: the directory to which the generated HTML should be writen

// docgo is copyright 2012 Daniel Connelly.  All rights reserved.  Use of
// this source code is governed by a BSD-style license that can be found in
// the `LICENSE` file.

// ## Imports and globals

package main

import (
	"bytes"
	"flag"
	"github.com/dhconnelly/blackfriday"
	"github.com/dhconnelly/litebrite"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
)

var (
	templ  *template.Template                     // html template for generated docs
	style  string                                 // css styles to inline
	match  = regexp.MustCompile(`^\s*//\s?`) // pattern for extracted comments
	sep    = "/*[docgoseparator]*/"               // replacement for comment groups
	unsep  = regexp.MustCompile(`<div class="comment">/\*\[docgoseparator\]\*/</div>`)
	outdir = flag.String("outdir", ".", "output directory for docs")
	dftres = os.Getenv("GOPATH") + "/src/github.com/dhconnelly/docgo"
	resdir = flag.String("resdir", dftres, "directory containing CSS and templates")
)

// ## Generating documentation

type docs struct {
	Filename string
	Sections []*section
	Style    string
}

type section struct {
	Doc  string
	Code string
}

// Extract comments from source code, pass them through markdown, highlight the
// code, and render to a string.
func GenerateDocs(title, src string) string {
	sections := extractSections(src)
	highlightCode(sections)
	markdownComments(sections)

	var b bytes.Buffer
	err := templ.Execute(&b, docs{title, sections, style})
	if err != nil {
		panic(err.Error())
	}
	return b.String()
}

// ## Processing sections

// Split the source into sections, where each section contains a comment group
// (consecutive leading-line // comments) and the code that follows that group.
func extractSections(source string) []*section {
	sections := make([]*section, 0)
	current := new(section)

	for _, line := range strings.Split(source, "\n") {
		// When a candidate comment line is found, add it to the current
		// comment group (or create a new section if code has already been
		// added to the current section).
		if match.FindString(line) != "" {
			if current.Code != "" {
				sections = append(sections, current)
				current = new(section)
			}
			// Strip out the comment delimiters
			current.Doc += match.ReplaceAllString(line, "") + "\n"
		} else {
			current.Code += line + "\n"
		}
	}

	return append(sections, current)
}

// Apply markdown to each section's documentation.
func markdownComments(sections []*section) {
	for _, section := range sections {
		// IMHO BlackFriday should use a string interface, since it
		// operates on text (not arbitrary binary) data...
		section.Doc = string(blackfriday.MarkdownBasic([]byte(section.Doc)))
	}
}

// Apply syntax highlighting to each section's code.
func highlightCode(sections []*section) {
	// Rejoin the source code fragments, using sep as delimiter
	segments := make([]string, 0)
	for _, section := range sections {
		segments = append(segments, section.Code)
	}
	code := strings.Join(segments, sep)

	// Highlight the joined source
	h := litebrite.Highlighter{"operator", "ident", "literal", "keyword", "comment"}
	hlcode := h.Highlight(code)

	// Collect the code between subsequent `unsep`s
	matches := append(unsep.FindAllStringIndex(hlcode, -1), []int{len(hlcode), 0})
	lastend := 0
	for i, match := range matches {
		sections[i].Code = hlcode[lastend:match[0]]
		lastend = match[1]
	}
}

// ## Setup and running

// Load the HTML template and CSS styles for output.
func loadResources(path string) {
	data, err := ioutil.ReadFile(path + "/doc.css")
	if err != nil {
		panic(err.Error())
	}
	style = string(data)
	templ = template.Must(template.ParseFiles(path + "/doc.templ"))
}

// Generate documentation for a source file.
func processFile(filename string) {
	src, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err.Error())
	}
	name := filename[strings.LastIndex(filename, "/")+1:]
	outname := *outdir + "/" + name[:len(name)-2] + "html"
	docs := GenerateDocs(name, string(src))
	err2 := ioutil.WriteFile(outname, []byte(docs), 0666)
	if err2 != nil {
		panic(err2.Error())
	}
}

func main() {
	flag.Parse()
	loadResources(*resdir)
	for _, filename := range flag.Args() {
		processFile(filename)
	}
}
<commit_msg>blackfriday was updated to support Go 1--pointing back to russross's repo<commit_after>// docgo is a [Go](http://golang.org) implementation of [Jeremy Ashkenas]
// (http://github.com/jashkenas)'s [docco] (http://jashkenas.github.com/docco/),
// a literate-programming-style documentation generator.  Running docgo on your
// Go source files produces HTML with comments and code side-by-side.
//
// Comments are processed by [Markdown]
// (http://daringfireball.net/projects/markdown) using [Russ Ross]
// (http://github.com/russross)'s [BlackFriday]
// (http://github.com/russross/blackfriday) library, and code is
// syntax-highlighted using [litebrite](http://dhconnelly.github.com/litebrite),
// a Go syntax highlighting library.
//
// The source is available on [GitHub](http://github.com/dhconnelly/docgo).
//
// With a recent Go weekly build (I'm using `weekly.2012-2-07`), you can get,
// install, and run docgo by doing the following at a command line:
//
// `go get github.com/dhconnelly/docgo`

// `go install github.com/dhconnelly/docgo`

// `docgo file.go`
//
// This will create `file.html` in the current directory.
//
// There are two command-line flags:
//
// - `resdir`: a path to the directory containing the CSS styles and HTML
//   templates (this is usually the docgo source directory)
// - `outdir`: the directory to which the generated HTML should be writen

// docgo is copyright 2012 Daniel Connelly.  All rights reserved.  Use of
// this source code is governed by a BSD-style license that can be found in
// the `LICENSE` file.

// ## Imports and globals

package main

import (
	"bytes"
	"flag"
	"github.com/russross/blackfriday"
	"github.com/dhconnelly/litebrite"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
	"text/template"
)

var (
	templ  *template.Template                     // html template for generated docs
	style  string                                 // css styles to inline
	match  = regexp.MustCompile(`^\s*//\s?`) // pattern for extracted comments
	sep    = "/*[docgoseparator]*/"               // replacement for comment groups
	unsep  = regexp.MustCompile(`<div class="comment">/\*\[docgoseparator\]\*/</div>`)
	outdir = flag.String("outdir", ".", "output directory for docs")
	dftres = os.Getenv("GOPATH") + "/src/github.com/dhconnelly/docgo"
	resdir = flag.String("resdir", dftres, "directory containing CSS and templates")
)

// ## Generating documentation

type docs struct {
	Filename string
	Sections []*section
	Style    string
}

type section struct {
	Doc  string
	Code string
}

// Extract comments from source code, pass them through markdown, highlight the
// code, and render to a string.
func GenerateDocs(title, src string) string {
	sections := extractSections(src)
	highlightCode(sections)
	markdownComments(sections)

	var b bytes.Buffer
	err := templ.Execute(&b, docs{title, sections, style})
	if err != nil {
		panic(err.Error())
	}
	return b.String()
}

// ## Processing sections

// Split the source into sections, where each section contains a comment group
// (consecutive leading-line // comments) and the code that follows that group.
func extractSections(source string) []*section {
	sections := make([]*section, 0)
	current := new(section)

	for _, line := range strings.Split(source, "\n") {
		// When a candidate comment line is found, add it to the current
		// comment group (or create a new section if code has already been
		// added to the current section).
		if match.FindString(line) != "" {
			if current.Code != "" {
				sections = append(sections, current)
				current = new(section)
			}
			// Strip out the comment delimiters
			current.Doc += match.ReplaceAllString(line, "") + "\n"
		} else {
			current.Code += line + "\n"
		}
	}

	return append(sections, current)
}

// Apply markdown to each section's documentation.
func markdownComments(sections []*section) {
	for _, section := range sections {
		// IMHO BlackFriday should use a string interface, since it
		// operates on text (not arbitrary binary) data...
		section.Doc = string(blackfriday.MarkdownBasic([]byte(section.Doc)))
	}
}

// Apply syntax highlighting to each section's code.
func highlightCode(sections []*section) {
	// Rejoin the source code fragments, using sep as delimiter
	segments := make([]string, 0)
	for _, section := range sections {
		segments = append(segments, section.Code)
	}
	code := strings.Join(segments, sep)

	// Highlight the joined source
	h := litebrite.Highlighter{"operator", "ident", "literal", "keyword", "comment"}
	hlcode := h.Highlight(code)

	// Collect the code between subsequent `unsep`s
	matches := append(unsep.FindAllStringIndex(hlcode, -1), []int{len(hlcode), 0})
	lastend := 0
	for i, match := range matches {
		sections[i].Code = hlcode[lastend:match[0]]
		lastend = match[1]
	}
}

// ## Setup and running

// Load the HTML template and CSS styles for output.
func loadResources(path string) {
	data, err := ioutil.ReadFile(path + "/doc.css")
	if err != nil {
		panic(err.Error())
	}
	style = string(data)
	templ = template.Must(template.ParseFiles(path + "/doc.templ"))
}

// Generate documentation for a source file.
func processFile(filename string) {
	src, err := ioutil.ReadFile(filename)
	if err != nil {
		panic(err.Error())
	}
	name := filename[strings.LastIndex(filename, "/")+1:]
	outname := *outdir + "/" + name[:len(name)-2] + "html"
	docs := GenerateDocs(name, string(src))
	err2 := ioutil.WriteFile(outname, []byte(docs), 0666)
	if err2 != nil {
		panic(err2.Error())
	}
}

func main() {
	flag.Parse()
	loadResources(*resdir)
	for _, filename := range flag.Args() {
		processFile(filename)
	}
}
<|endoftext|>


<commit_before>//Package cairo wraps libcairo, a 2D graphics library with support for multiple
//output devices.
//Libcairo is is designed to produce consistent output on all output media
//while taking advantage of display hardware acceleration when available.
//
//The cairo API provides operations similar to the drawing operators
//of PostScript and PDF.
//Operations in cairo including stroking and filling cubic Bézier splines,
//transforming and compositing translucent images, and antialiased text
//rendering.
//All drawing operations can be transformed by any affine transformation
//(scale, rotation, shear, etc.)
//
//Naming Conventions
//
//Cairo refers to this package and its related packages.
//Libcairo refers to the C library that this package is a binding to.
//
//Libcairo version
//
//This package requires libcairo version 1.12 or greater.
//Libcairo must be compiled with:
//	CAIRO_HAS_PNG_FUNCTIONS
//	CAIRO_HAS_IMAGE_SURFACE
//
//Related packages, such as cairo/ps, may require further options compiled
//in to libcairo, but they will be documented.
//
//Libcairo can be found at http://cairographics.org .
//
//Xtensions
//
//Many types, functions, and methods are prefixed by Xtension.
//You may ignore these unless you are writing an extension.
//An extension is a package that integrates another portion of libcairo or
//binds with a library that supports libcairo integration.
//
package cairo
<commit_msg>noted unimplementeds<commit_after>//Package cairo wraps libcairo, a 2D graphics library with support for multiple
//output devices.
//Libcairo is is designed to produce consistent output on all output media
//while taking advantage of display hardware acceleration when available.
//
//The cairo API provides operations similar to the drawing operators
//of PostScript and PDF.
//Operations in cairo including stroking and filling cubic Bézier splines,
//transforming and compositing translucent images, and antialiased text
//rendering.
//All drawing operations can be transformed by any affine transformation
//(scale, rotation, shear, etc.)
//
//Naming Conventions
//
//Cairo refers to this package and its related packages.
//Libcairo refers to the C library that this package is a binding to.
//
//Libcairo version
//
//This package requires libcairo version 1.12 or greater.
//Libcairo must be compiled with:
//	CAIRO_HAS_PNG_FUNCTIONS
//	CAIRO_HAS_IMAGE_SURFACE
//
//Related packages, such as cairo/ps, may require further options compiled
//in to libcairo, but they will be documented.
//
//Libcairo can be found at http://cairographics.org .
//
//Xtensions
//
//Many types, functions, and methods are prefixed by Xtension.
//You may ignore these unless you are writing an extension.
//An extension is a package that integrates another portion of libcairo or
//binds with a library that supports libcairo integration.
//
//Not implemented
//	User patterns
//	User fonts
package cairo
<|endoftext|>


<commit_before>// Package cdl provides a configuration definition language for Go
//
// There are several ways to import configuration files into Go, from
// unmarshalling your own JSON using `encoding/json` to projects such as
// Viper (http://spf13.com/project/viper) which will read the configuration
// in JSON, YAML, TOML or from etcd, the command line, or possibly other
// sources. All of these have in common that in the end they produce
// something that looks in Go like
//
//     map[string]interface{}
//
// However, these have the issue that they don't validate the configuration.
// If the supplied configuration omits mandatory keys, or puts in extra
// ones, or does both by misspelling one key, or puts the right bit of
// configuration at the wrong level, or any other error is made that
// doesn't actually prevent the JSON, YAML etc. parsing, then it's left
// for you to detect that manually in your program.
//
// cdl makes this all much easier. Simply supply a cdl template, compile
// it (once) using
//     ct := cdl.Compile(...)
// then validate using
//     err := ct.Validate(object)
//
// If the validation fails, you will get an `error` return with a context
// that will allow a user to discover the error in his file.
//
// Templates
//
// cdl templates are themselves a
//     map[string]interface{}
//
// but are flat (i.e. only one level deep). The key represents a point in
// in the hierarchy to parse, and the value specifies what may appear at
// that point. The value is normally a `string`, but may be a pointer
// to a validation function you supply.
//
// For example:
//     template := cdl.Template{
// 		"/":     "{}apple peach? lemon",
// 		"apple": "float64",
// 		"peach": isOneOrTwo,
// 	}
//
// Here:
//  * The root level is specified to be a map ('`{}`'), which may consist of the elements
//    `apple`, `peach` and `lemon`.
//
//  * There must be an `apple` element and `lemon` element, but the `peach` element is optional
//
//  * The `apple` element must be a `float64`
//
//  * In order to validate the `peach` element, your own validator function (`isOneOrTwo`)
//     is called. If this returns a `cdl.CdlError`, that error will be passed to the user (as
//     an `error`). If it returns `nil`, then validation will continue.
//
//  * There is no validation at all on `peach`
//
// Let's take a more complicated example:
// 	template := cdl.Template{
// 		"/":          "{}apple peach? pear* plum+ raspberry{1,3} strawberry! kiwi{1,4}? guava!{1,2} orange?{2,31}",
// 		"apple":      "float64",
// 		"peach":      isOneOrTwo,
// 		"strawberry": "[nectarine]{1,3}",
// 		"nectarine":  "string",
// 		"raspberry":  "string"
// 	}
//
// Here we have allowed in the root level:
//  * `strawberry`: The `!` indicates it is mandatory; this is the default, so the `!` is unnecessary.
//     Each `strawberry` must be an array of `nectarine` with between 1 and 3 components, and each
//     `nectarine` must be a `string`.
//
//  * `rasbperry`: This is a shorthand for writing the same thing as above, i.e. an array of between
//     1 and 3 `raspberry`, each of which must be a string.
//
//  * `pear`: An array of zero or more items. Note the empty array must be there (if the array itself is
//     optional, write `pear?*`).
//
//  * `plum`: An array of one or more entries.
//
//  * `kiwi`: An optionally present array of between 1 and 4 entries
//
//  * `guava`: A mandatory array of between 1 and 2 entries
//
// Template syntax in detail
//
// 1. Each key must either be `/` (for the root key) or consist of word characters (i.e. matching `\w+` in regexp terms)
//
// 2. Each key must have a value, which may be either a validator function, or
// a validation instruction in the form of a `string`
//
// 3. A validator function is a function with the signature
//    func(obj interface{}) (err *CdlError)`
//
// 4. Each validation instruction may be either
//   * The Go name of a type (not a slice), e.g. `bool`, `string` etc. (in quotes as it's a `string`)
//   * An array specifier, having a form beginning `[]`
//   * A map specifier, having a form beginning `{}`
//
// 5. An array specifier has the form `[]key` optionally followed by a range specifier
//   * The key (`key` above) consists of word characters.
//   * The key need not be specified within the template (if it isn't, no validation will be done on it).
//
// 6. A range specifier takes the form
//   * `{n,m}` (meaning between `n` and `m`) or
//   * `{n,}` (meaning at least `n`).
//
// 7. A map specifier has the form `{}` followed by zero or more space-separated map elements
//
// 8. A map element consists of a key (`key`) followed by zero or more modifiers
//   * The key consists of word characters.
//   * The key need not be specified within the template (if it isn't, no validation will be done on it).
//
// 9. Permitted modifiers are:
//   * `?` means the key is optional
//   * `!` means the key is mandatory (the default)
//   * `*` means the key is an array of 0 or more elements
//   * `+` means the key is an array of 1 or more elements
//   * A *range specifier* (see above), i.e.
//     * `{n,m}` (meaning between `n` and `m`) or
//     * `{n,}` (meaning at least `n`)
//
// Validator Functions
//
// Where the validator is passed, it is a function with signature:
//     func (o interface{}) *cdl.CdlError
//
// Here's an example showing how it can return an error and send supplementary data back to the user.
// Note that cdl itself will add the appropriate context.
//
//     func isOneOrTwo(o interface{}) *cdl.CdlError {
//     	if v, ok := o.(float64); !ok {
//     		return cdl.NewError(cdl.ErrBadValue).SetSupplementary("is not a float64")
//     	} else {
//     		if v != 1 && v != 2 {
//     			return cdl.NewError(cdl.ErrBadValue).SetSupplementary("is not 1 or 2")
//     		}
//     	}
//     	return nil
//     }
//
package cdl
<commit_msg>Improve documentation formatting<commit_after>// Package cdl provides a configuration definition language for Go
//
// There are several ways to import configuration files into Go, from
// unmarshalling your own JSON using `encoding/json` to projects such as
// Viper (http://spf13.com/project/viper) which will read the configuration
// in JSON, YAML, TOML or from etcd, the command line, or possibly other
// sources. All of these have in common that in the end they produce
// something that looks in Go like
//
//     map[string]interface{}
//
// However, these have the issue that they don't validate the configuration.
// If the supplied configuration omits mandatory keys, or puts in extra
// ones, or does both by misspelling one key, or puts the right bit of
// configuration at the wrong level, or any other error is made that
// doesn't actually prevent the JSON, YAML etc. parsing, then it's left
// for you to detect that manually in your program.
//
// cdl makes this all much easier. Simply supply a cdl template, compile
// it (once) using
//     ct := cdl.Compile(...)
// then validate using
//     err := ct.Validate(object)
//
// If the validation fails, you will get an `error` return with a context
// that will allow a user to discover the error in his file.
//
// Templates
//
// cdl templates are themselves a
//     map[string]interface{}
//
// but are flat (i.e. only one level deep). The key represents a point in
// in the hierarchy to parse, and the value specifies what may appear at
// that point. The value is normally a `string`, but may be a pointer
// to a validation function you supply.
//
// For example:
//     template := cdl.Template{
// 		"/":     "{}apple peach? lemon",
// 		"apple": "float64",
// 		"peach": isOneOrTwo,
// 	}
//
// Here:
//  * The root level is specified to be a map ('`{}`'), which may consist of the elements
//    `apple`, `peach` and `lemon`.
//
//  * There must be an `apple` element and `lemon` element, but the `peach` element
//    is optional.
//
//  * The `apple` element must be a `float64`
//
//  * In order to validate the `peach` element, your own validator function (`isOneOrTwo`)
//     is called. If this returns a `cdl.CdlError`, that error will be passed to the user
//     (as an `error`). If it returns `nil`, then validation will continue.
//
//  * There is no validation at all on `peach`
//
// Let's take a more complicated example:
// 	template := cdl.Template{
// 		"/":          "{}apple peach? pear* plum+ raspberry{1,3} strawberry! kiwi{1,4}? guava!{1,2} orange?{2,31}",
// 		"apple":      "float64",
// 		"peach":      isOneOrTwo,
// 		"strawberry": "[nectarine]{1,3}",
// 		"nectarine":  "string",
// 		"raspberry":  "string"
// 	}
//
// Here we have allowed in the root level:
//  * `strawberry`: The `!` indicates it is mandatory; this is the default, so the `!`
//     is unnecessary. Each `strawberry` must be an array of `nectarine` with between
//      1 and 3 components, and each `nectarine` must be a `string`.
//
//  * `rasbperry`: This is a shorthand for writing the same thing as above, i.e. an
//     array of between 1 and 3 `raspberry`, each of which must be a string.
//
//  * `pear`: An array of zero or more items. Note the empty array must be there (if
//     the array itself is optional, write `pear?*`).
//
//  * `plum`: An array of one or more entries.
//
//  * `kiwi`: An optionally present array of between 1 and 4 entries
//
//  * `guava`: A mandatory array of between 1 and 2 entries
//
// Template syntax in detail
//
// 1. Each key must either be `/` (for the root key) or consist of word characters
// (i.e. matching `\w+` in regexp terms)
//
// 2. Each key must have a value, which may be either a validator function, or
// a validation instruction in the form of a `string`
//
// 3. A validator function is a function with the signature
//    func(obj interface{}) (err *CdlError)`
//
// 4. Each validation instruction may be either
//   * The Go name of a type (not a slice), e.g. `bool`, `string` etc. (in quotes as
//     it's a `string`)
//   * An array specifier, having a form beginning `[]`
//   * A map specifier, having a form beginning `{}`
//
// 5. An array specifier has the form `[]key` optionally followed by a range specifier
//   * The key (`key` above) consists of word characters.
//   * The key need not be specified within the template (if it isn't, no validation
//     will be done on it).
//
// 6. A range specifier takes the form
//   * `{n,m}` (meaning between `n` and `m`) or
//   * `{n,}` (meaning at least `n`).
//
// 7. A map specifier has the form `{}` followed by zero or more space-separated
//    map elements
//
// 8. A map element consists of a key (`key`) followed by zero or more modifiers
//   * The key consists of word characters.
//   * The key need not be specified within the template (if it isn't, no validation
//     will be done on it).
//
// 9. Permitted modifiers are:
//   * `?` means the key is optional
//   * `!` means the key is mandatory (the default)
//   * `*` means the key is an array of 0 or more elements
//   * `+` means the key is an array of 1 or more elements
//   * A range specifier (see above), i.e.
//     * `{n,m}` (meaning between `n` and `m`) or
//     * `{n,}` (meaning at least `n`)
//
// Validator Functions
//
// Where the validator is passed, it is a function with signature:
//     func (o interface{}) *cdl.CdlError
//
// Here's an example showing how it can return an error and send supplementary data back to the user.
// Note that cdl itself will add the appropriate context.
//
//     func isOneOrTwo(o interface{}) *cdl.CdlError {
//     	if v, ok := o.(float64); !ok {
//     		return cdl.NewError(cdl.ErrBadValue).SetSupplementary("is not a float64")
//     	} else {
//     		if v != 1 && v != 2 {
//     			return cdl.NewError(cdl.ErrBadValue).SetSupplementary("is not 1 or 2")
//     		}
//     	}
//     	return nil
//     }
//
package cdl
<|endoftext|>


<commit_before>/*
Event Sourcing Data Access Library

Package incata is a source eventing data access library. The name combines incremental (inc) and data (ata).
Details about event sourcing can be read on Martin Fowlers site(http://martinfowler.com/eaaDev/EventSourcing.html).

Currently we support two relational DB's, MS Sql Server and Postgresql.

The stored Event has the following structure:

    type Event struct {
      Id        int64
      SourceID  uuid.UUID
      Created   time.Time
      Payload   interface{}
      EventType string
      Version   int
    }

The payload is the actual data that we like to store in our DB.
Since the serializer can be anything the data type is set to interface{}.
This means that our db table column for the Payload have to match the serializer's result data type.

In order to use the appender or retriever we have to provide the following:

1. a serializer or deserializer which implements the Serializer or Deserializer interface or the .  A JSONMarshaller is provided.

2. a writer or reader which implements the Writer or Reader interface. A SQLWriter and SQLReader is provided.

3. a appender and retriever which implement the Appender and Retriever interface. Appender and Retriever are provided.

The supported relational DB's are MS Sql Server and PostgreSQL.

Check out the examples in the examples folder for setting up the default marshaller and reader/writers.

MS SQL Server Setup

SQL Server Driver used:

    "github.com/denisenkom/go-mssqldb"

DB Table setup (Provide a table name)

        CREATE TABLE {TableName} (
          Id BIGINT IDENTITY
          ,SourceId UNIQUEIDENTIFIER NOT NULL
          ,Created DATETIME2 NOT NULL
          ,EventType NVARCHAR(250) NOT NULL
          ,Version INT NOT NULL
          ,Payload NVARCHAR(MAX) NOT NULL
          ,CONSTRAINT PK_Event PRIMARY KEY CLUSTERED (Id)
        ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

         GO

         CREATE INDEX IX_Event_SourceId
         ON {TableName} (SourceId)
         ON [PRIMARY]
         GO

PostgreSQL Setup

PostgreSQL Driver used:

    "github.com/lib/pq"

DB Table setup (Provide a table name)

      CREATE TABLE {TableName}
      (
       "Id" serial NOT NULL,
       "SourceId" uuid,
       "Created" timestamp without time zone,
       "EventType" character varying(250),
       "Version" integer,
       "Payload" text,
       CONSTRAINT "PK_Event" PRIMARY KEY ("Id")
      )
      WITH (
       OIDS=FALSE
      );

      CREATE INDEX "event_idx_sourceId"
       ON {TableName}
       USING btree
       ("SourceId");

For a full guide visit https://github.com/mantzas/incata
*/
package incata
<commit_msg>golint fix<commit_after>/*
Package incata is a source eventing data access library. The name combines incremental (inc) and data (ata).
Details about event sourcing can be read on Martin Fowlers site(http://martinfowler.com/eaaDev/EventSourcing.html).

Currently we support two relational DB's, MS Sql Server and Postgresql.

The stored Event has the following structure:

    type Event struct {
      Id        int64
      SourceID  uuid.UUID
      Created   time.Time
      Payload   interface{}
      EventType string
      Version   int
    }

The payload is the actual data that we like to store in our DB.
Since the serializer can be anything the data type is set to interface{}.
This means that our db table column for the Payload have to match the serializer's result data type.

In order to use the appender or retriever we have to provide the following:

1. a serializer or deserializer which implements the Serializer or Deserializer interface or the .  A JSONMarshaller is provided.

2. a writer or reader which implements the Writer or Reader interface. A SQLWriter and SQLReader is provided.

3. a appender and retriever which implement the Appender and Retriever interface. Appender and Retriever are provided.

The supported relational DB's are MS Sql Server and PostgreSQL.

Check out the examples in the examples folder for setting up the default marshaller and reader/writers.

MS SQL Server Setup

SQL Server Driver used:

    "github.com/denisenkom/go-mssqldb"

DB Table setup (Provide a table name)

        CREATE TABLE {TableName} (
          Id BIGINT IDENTITY
          ,SourceId UNIQUEIDENTIFIER NOT NULL
          ,Created DATETIME2 NOT NULL
          ,EventType NVARCHAR(250) NOT NULL
          ,Version INT NOT NULL
          ,Payload NVARCHAR(MAX) NOT NULL
          ,CONSTRAINT PK_Event PRIMARY KEY CLUSTERED (Id)
        ) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]

         GO

         CREATE INDEX IX_Event_SourceId
         ON {TableName} (SourceId)
         ON [PRIMARY]
         GO

PostgreSQL Setup

PostgreSQL Driver used:

    "github.com/lib/pq"

DB Table setup (Provide a table name)

      CREATE TABLE {TableName}
      (
       "Id" serial NOT NULL,
       "SourceId" uuid,
       "Created" timestamp without time zone,
       "EventType" character varying(250),
       "Version" integer,
       "Payload" text,
       CONSTRAINT "PK_Event" PRIMARY KEY ("Id")
      )
      WITH (
       OIDS=FALSE
      );

      CREATE INDEX "event_idx_sourceId"
       ON {TableName}
       USING btree
       ("SourceId");

For a full guide visit https://github.com/mantzas/incata
*/
package incata
<|endoftext|>


<commit_before>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID

			lo.G.Debugf("User[%s] not found in: %v", userID, roleUsers)
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
					userList = uaaUsers.GetByName(userID)
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				}
			}
		} else {
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			}
		}
	}
	return ldapUsers, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<commit_msg>update to remove incorrect log statements<commit_after>package user

import (
	"fmt"
	"strings"

	"github.com/pivotalservices/cf-mgmt/ldap"
	"github.com/pivotalservices/cf-mgmt/uaa"
	"github.com/pkg/errors"
	"github.com/xchapter7x/lo"
)

func (m *DefaultManager) SyncLdapUsers(roleUsers *RoleUsers, uaaUsers *uaa.Users, usersInput UsersInput) error {
	origin := m.LdapConfig.Origin
	if m.LdapConfig.Enabled {
		ldapUsers, err := m.GetLDAPUsers(uaaUsers, usersInput)
		if err != nil {
			return err
		}
		lo.G.Debugf("LdapUsers: %+v", ldapUsers)
		for _, inputUser := range ldapUsers {
			userToUse := m.UpdateUserInfo(inputUser)
			userID := userToUse.UserID
			userList := uaaUsers.GetByName(userID)
			if len(userList) == 0 {
				lo.G.Debug("User", userID, "doesn't exist in cloud foundry, so creating user")
				if userGUID, err := m.UAAMgr.CreateExternalUser(userID, userToUse.Email, userToUse.UserDN, m.LdapConfig.Origin); err != nil {
					lo.G.Errorf("Unable to create user %s with error %s", userID, err.Error())
					continue
				} else {
					uaaUsers.Add(uaa.User{
						Username:   userID,
						ExternalID: userToUse.UserDN,
						Origin:     m.LdapConfig.Origin,
						Email:      userToUse.Email,
						GUID:       userGUID,
					})
				}
			}
			if !roleUsers.HasUserForOrigin(userID, origin) {
				user := uaaUsers.GetByNameAndOrigin(userID, origin)
				if user == nil {
					return fmt.Errorf("Unabled to find user %s for origin %s", userID, origin)
				}
				if err := usersInput.AddUser(usersInput, user.Username, user.GUID); err != nil {
					return errors.Wrap(err, fmt.Sprintf("User %s with origin %s", user.Username, user.Origin))
				}
			} else {
				lo.G.Debugf("User[%s] found in role", userID)
				roleUsers.RemoveUserForOrigin(userID, origin)
			}
		}
	} else {
		lo.G.Debug("Skipping LDAP sync as LDAP is disabled (enable by updating config/ldap.yml)")
	}
	return nil
}

func (m *DefaultManager) GetLDAPUsers(uaaUsers *uaa.Users, usersInput UsersInput) ([]ldap.User, error) {
	var ldapUsers []ldap.User
	for _, groupName := range usersInput.UniqueLdapGroupNames() {
		userDNList, err := m.LdapMgr.GetUserDNs(groupName)
		if err != nil {
			return nil, err
		}
		for _, userDN := range userDNList {
			lo.G.Debugf("Checking for userDN %s", userDN)
			uaaUser := uaaUsers.GetByExternalID(userDN)
			if uaaUser != nil {
				lo.G.Debugf("UserDN [%s] found in UAA as [%s], skipping ldap lookup", userDN, uaaUser.Username)
				ldapUsers = append(ldapUsers, ldap.User{
					UserID: uaaUser.Username,
					UserDN: userDN,
					Email:  uaaUser.Email,
				})
			} else {
				user, err := m.LdapMgr.GetUserByDN(userDN)
				if err != nil {
					return nil, err
				}
				if user != nil {
					ldapUsers = append(ldapUsers, *user)
				}
			}
		}
	}
	for _, userID := range usersInput.LdapUsers {
		userList := uaaUsers.GetByName(userID)
		if len(userList) > 0 {
			lo.G.Debugf("UserID [%s] found in UAA, skipping ldap lookup", userID)
			for _, uaaUser := range userList {
				if strings.EqualFold(uaaUser.Origin, m.LdapConfig.Origin) {
					ldapUsers = append(ldapUsers, ldap.User{
						UserID: userID,
						UserDN: uaaUser.ExternalID,
						Email:  uaaUser.Email,
					})
				}
			}
		} else {
			user, err := m.LdapMgr.GetUserByID(userID)
			if err != nil {
				return nil, err
			}
			if user != nil {
				ldapUsers = append(ldapUsers, *user)
			}
		}
	}
	return ldapUsers, nil
}

func (m *DefaultManager) UpdateUserInfo(user ldap.User) ldap.User {
	userID := strings.ToLower(user.UserID)
	externalID := user.UserDN
	email := user.Email
	if email == "" {
		email = fmt.Sprintf("%s@user.from.ldap.cf", userID)
	}

	return ldap.User{
		UserID: userID,
		UserDN: externalID,
		Email:  email,
	}
}
<|endoftext|>