<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	ct "github.com/daviddengcn/go-colortext"
	"github.com/jpillora/backoff"
	"github.com/phrase/phraseapp-client/internal/paths"
	"github.com/phrase/phraseapp-client/internal/placeholders"
	"github.com/phrase/phraseapp-client/internal/print"
	"github.com/phrase/phraseapp-client/internal/spinner"
	"github.com/phrase/phraseapp-go/phraseapp"
)

type PushCommand struct {
	phraseapp.Config
	Wait               bool   `cli:"opt --wait desc='Wait for files to be processed'"`
	Branch             string `cli:"opt --branch"`
	UseLocalBranchName bool   `cli:"opt --use-local-branch-name desc='push from the branch with the name of your currently checked out branch (git or mercurial)'"`
}

func (cmd *PushCommand) Run() error {
	if cmd.Config.Debug {
		// suppresses content output
		cmd.Config.Debug = false
		Debug = true
	}

	client, err := newClient(cmd.Config.Credentials, cmd.Config.Debug)
	if err != nil {
		return err
	}

	sources, err := SourcesFromConfig(cmd.Config)
	if err != nil {
		return err
	}

	if err := sources.Validate(); err != nil {
		return err
	}

	formatMap, err := formatsByApiName(client)
	if err != nil {
		return fmt.Errorf("Error retrieving format list from PhraseApp: %s", err)
	}

	for _, source := range sources {
		formatName := source.GetFileFormat()
		if val, ok := formatMap[formatName]; ok {
			source.Format = val
		}

		if source.Format == nil {
			return fmt.Errorf("Format %q of source %q is not supported by PhraseApp!", formatName, source.File)
		}
	}

	projectsAffected := map[string]bool{}
	for _, source := range sources {
		projectsAffected[source.ProjectID] = true
	}

	branchName, err := usedBranchName(cmd.UseLocalBranchName, cmd.Branch)
	if err != nil {
		return err
	}
	cmd.Branch = branchName

	if cmd.Branch != "" {
		for projectID := range projectsAffected {
			_, err := client.BranchShow(projectID, cmd.Branch)
			if err != nil {
				if useLocalBranchName(cmd.UseLocalBranchName) {
					printCreateBranchQuestion(cmd.Branch)
					text, _ := bufio.NewReader(os.Stdin).ReadString('\n')

					if strings.Contains(text, "n") || strings.Contains(text, "N") {
						return nil
					}
				}

				branchParams := &phraseapp.BranchParams{Name: &cmd.Branch}
				branch, _ := client.BranchCreate(projectID, branchParams)

				fmt.Println()

				taskResult := make(chan string, 1)
				taskErr := make(chan error, 1)

				fmt.Printf("Waiting for branch %s is created!", branch.Name)
				spinner.While(func() {
					branchCreateResult, err := getBranchCreateResult(client, projectID, branch)
					taskResult <- branchCreateResult
					taskErr <- err
				})
				fmt.Println()

				if err := <-taskErr; err != nil {
					return err
				}

				switch <-taskResult {
				case "success":
					print.Success("Successfully created branch %s", branch.Name)
				case "error":
					print.Failure("There was an error creating branch %s.", branch.Name)
				}
			}
		}
	}

	projectIdToLocales, err := LocalesForProjects(client, sources, cmd.Branch)
	if err != nil {
		return err
	}
	for _, source := range sources {
		val, ok := projectIdToLocales[LocaleCacheKey{source.ProjectID, cmd.Branch}]
		if ok {
			source.RemoteLocales = val
		}
	}

	for _, source := range sources {
		err := source.Push(client, cmd.Wait, cmd.Branch)
		if err != nil {
			return err
		}
	}

	return nil
}

func (source *Source) Push(client *phraseapp.Client, waitForResults bool, branch string) error {
	localeFiles, err := source.LocaleFiles()
	if err != nil {
		return err
	}

	for _, localeFile := range localeFiles {
		fmt.Printf("Uploading %s... ", localeFile.RelPath())

		if localeFile.shouldCreateLocale(source, branch) {
			localeDetails, err := source.createLocale(client, localeFile, branch)
			if err == nil {
				localeFile.ID = localeDetails.ID
				localeFile.Code = localeDetails.Code
				localeFile.Name = localeDetails.Name
			} else {
				fmt.Printf("failed to create locale: %s\n", err)
				continue
			}
		}

		upload, err := source.uploadFile(client, localeFile, branch)
		if err != nil {
			return err
		}

		if waitForResults {
			fmt.Println()

			taskResult := make(chan string, 1)
			taskErr := make(chan error, 1)

			fmt.Printf("Upload ID: %s, filename: %s succeeded. Waiting for your file to be processed... ", upload.ID, upload.Filename)
			spinner.While(func() {
				result, err := getUploadResult(client, source.ProjectID, upload, branch)
				taskResult <- result
				taskErr <- err
			})
			fmt.Println()

			if err := <-taskErr; err != nil {
				return err
			}

			switch <-taskResult {
			case "success":
				print.Success("Successfully uploaded and processed %s.", localeFile.RelPath())
			case "error":
				print.Failure("There was an error processing %s. Your changes were not saved online.", localeFile.RelPath())
			}
		} else {
			fmt.Println("done!")
			fmt.Printf("Check upload ID: %s, filename: %s for information about processing results.\n", upload.ID, upload.Filename)
		}

		if Debug {
			fmt.Fprintln(os.Stderr, strings.Repeat("-", 10))
		}
	}

	return nil
}

func formatsByApiName(client *phraseapp.Client) (map[string]*phraseapp.Format, error) {
	formats, err := client.FormatsList(1, 25)
	if err != nil {
		return nil, err
	}
	formatMap := map[string]*phraseapp.Format{}
	for _, format := range formats {
		formatMap[format.ApiName] = format
	}
	return formatMap, nil
}

// Return all locale files from disk that match the source pattern.
func (source *Source) LocaleFiles() (LocaleFiles, error) {
	filePaths, err := paths.Glob(placeholders.ToGlobbingPattern(source.File))
	if err != nil {
		return nil, err
	}

	var localeFiles LocaleFiles
	for _, path := range filePaths {
		if paths.IsPhraseAppYmlConfig(path) {
			continue
		}

		localeFile := new(LocaleFile)
		localeFile.fillFromPath(path, source.File)

		localeFile.Path, err = filepath.Abs(path)
		if err != nil {
			return nil, err
		}

		locale := source.getRemoteLocaleForLocaleFile(localeFile)
		// TODO: sinnvoll?
		if locale != nil {
			localeFile.ExistsRemote = true
			localeFile.Code = locale.Code
			localeFile.Name = locale.Name
			localeFile.ID = locale.ID
		}

		if Debug {
			fmt.Printf(
				"Code:%q, Name:%q, ID:%q, Tag:%q\n",
				localeFile.Code, localeFile.Name, localeFile.ID, localeFile.Tag,
			)
		}

		localeFiles = append(localeFiles, localeFile)
	}

	if len(localeFiles) == 0 {
		abs, err := filepath.Abs(source.File)
		if err != nil {
			abs = source.File
		}
		return nil, fmt.Errorf("Could not find any files on your system that matches: '%s'", abs)
	}

	return localeFiles, nil
}

func (source *Source) getRemoteLocaleForLocaleFile(localeFile *LocaleFile) *phraseapp.Locale {
	candidates := source.RemoteLocales

	filterApplied := false

	filter := func(cands []*phraseapp.Locale, preCond string, pred func(cand *phraseapp.Locale) bool) []*phraseapp.Locale {
		if preCond == "" {
			return cands
		}
		filterApplied = true
		tmpCands := []*phraseapp.Locale{}
		for _, cand := range cands {
			if pred(cand) {
				tmpCands = append(tmpCands, cand)
			}
		}
		return tmpCands
	}

	localeName := source.replacePlaceholderInParams(localeFile)
	if localeName != "" {
		// This means the name can contain the value specified in LocaleID, with
		// `<locale_code>` being substituted by the value of the currently handled
		// localeFile (like push only locales with name `en-US`).
		candidates = filter(candidates, localeName, func(cand *phraseapp.Locale) bool {
			return strings.Contains(cand.Name, localeName)
		})
	} else {
		localeID := source.GetLocaleID()
		candidates = filter(candidates, localeID, func(cand *phraseapp.Locale) bool {
			return cand.Name == localeID || cand.ID == localeID
		})
	}

	candidates = filter(candidates, localeFile.Name, func(cand *phraseapp.Locale) bool {
		return cand.Name == localeFile.Name
	})

	candidates = filter(candidates, localeFile.Code, func(cand *phraseapp.Locale) bool {
		return cand.Code == localeFile.Code
	})

	// If no filter was applied the candidates list still contains all remote
	// locales, while actually nothing matches.
	if !filterApplied {
		return nil
	}

	switch len(candidates) {
	case 0:
		return nil
	case 1:
		return candidates[0]
	default:
		// TODO I guess this should return an error, as this is a problem.
		return candidates[0]
	}
}

func (localeFile *LocaleFile) fillFromPath(path, pattern string) {
	path = filepath.ToSlash(path)
	pathStart, patternStart, pathEnd, patternEnd, err := paths.SplitAtDirGlobOperator(path, pattern)
	if err != nil {
		print.Error(err)
		return
	}

	fillFrom := func(path, pattern string) {
		params, err := placeholders.Resolve(path, pattern)
		if err != nil {
			print.Error(err)
			return
		}

		for placeholder, value := range params {
			switch placeholder {
			case "locale_code":
				localeFile.Code = value
			case "locale_name":
				localeFile.Name = value
			case "tag":
				localeFile.Tag = value
			}
		}
	}

	fillFrom(pathStart, patternStart)
	fillFrom(pathEnd, patternEnd)
}

func (localeFile *LocaleFile) shouldCreateLocale(source *Source, branch string) bool {
	if localeFile.ExistsRemote {
		return false
	}

	if source.Format.IncludesLocaleInformation {
		return false
	}

	// we could not find an existing locale in PhraseApp
	// if a locale_name or locale_code was provided by the placeholder logic
	// we assume that it should be created
	// every other source should be uploaded and validated in uploads#create
	return (localeFile.Name != "" || localeFile.Code != "")
}

func getUploadResult(client *phraseapp.Client, projectID string, upload *phraseapp.Upload, branch string) (result string, err error) {
	b := &backoff.Backoff{
		Min:    500 * time.Millisecond,
		Max:    10 * time.Second,
		Factor: 2,
		Jitter: true,
	}

	for ; result != "success" && result != "error"; result = upload.State {
		time.Sleep(b.Duration())
		uploadShowParams := &phraseapp.UploadShowParams{Branch: &branch}
		upload, err = client.UploadShow(projectID, upload.ID, uploadShowParams)
		if err != nil {
			break
		}
	}

	return
}

func getBranchCreateResult(client *phraseapp.Client, projectID string, branch *phraseapp.Branch) (result string, err error) {
	b := &backoff.Backoff{
		Min:    500 * time.Millisecond,
		Max:    10 * time.Second,
		Factor: 2,
		Jitter: true,
	}

	for ; result != "success" && result != "error"; result = branch.State {
		time.Sleep(b.Duration())
		branch, err = client.BranchShow(projectID, branch.Name)
		if err != nil {
			break
		}
	}

	return
}

func printCreateBranchQuestion(branch string) {
	fmt.Printf("\nYou have currently checked out the branch '")
	ct.ChangeColor(ct.Green, false, ct.None, false)
	fmt.Printf("%s", branch)
	ct.ResetColor()
	fmt.Printf("'.\nThere currently is no branch in PhraseApp with this name.\n\n")
	fmt.Printf("Should we create a new branch in PhraseApp with the same name and push to it? [y/N]: ")
}
<commit_msg>do proper yes/no check<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	ct "github.com/daviddengcn/go-colortext"
	"github.com/jpillora/backoff"
	"github.com/phrase/phraseapp-client/internal/paths"
	"github.com/phrase/phraseapp-client/internal/placeholders"
	"github.com/phrase/phraseapp-client/internal/print"
	"github.com/phrase/phraseapp-client/internal/spinner"
	"github.com/phrase/phraseapp-go/phraseapp"
)

type PushCommand struct {
	phraseapp.Config
	Wait               bool   `cli:"opt --wait desc='Wait for files to be processed'"`
	Branch             string `cli:"opt --branch"`
	UseLocalBranchName bool   `cli:"opt --use-local-branch-name desc='push from the branch with the name of your currently checked out branch (git or mercurial)'"`
}

func (cmd *PushCommand) Run() error {
	if cmd.Config.Debug {
		// suppresses content output
		cmd.Config.Debug = false
		Debug = true
	}

	client, err := newClient(cmd.Config.Credentials, cmd.Config.Debug)
	if err != nil {
		return err
	}

	sources, err := SourcesFromConfig(cmd.Config)
	if err != nil {
		return err
	}

	if err := sources.Validate(); err != nil {
		return err
	}

	formatMap, err := formatsByApiName(client)
	if err != nil {
		return fmt.Errorf("Error retrieving format list from PhraseApp: %s", err)
	}

	for _, source := range sources {
		formatName := source.GetFileFormat()
		if val, ok := formatMap[formatName]; ok {
			source.Format = val
		}

		if source.Format == nil {
			return fmt.Errorf("Format %q of source %q is not supported by PhraseApp!", formatName, source.File)
		}
	}

	projectsAffected := map[string]bool{}
	for _, source := range sources {
		projectsAffected[source.ProjectID] = true
	}

	branchName, err := usedBranchName(cmd.UseLocalBranchName, cmd.Branch)
	if err != nil {
		return err
	}
	cmd.Branch = branchName

	if cmd.Branch != "" {
		for projectID := range projectsAffected {
			_, err := client.BranchShow(projectID, cmd.Branch)
			if err != nil {
				if useLocalBranchName(cmd.UseLocalBranchName) {
					printCreateBranchQuestion(cmd.Branch)
					text, _ := bufio.NewReader(os.Stdin).ReadString('\n')

					if !isYes(strings.TrimSpace(text)) {
						return nil
					}
				}

				branchParams := &phraseapp.BranchParams{Name: &cmd.Branch}
				branch, _ := client.BranchCreate(projectID, branchParams)

				fmt.Println()

				taskResult := make(chan string, 1)
				taskErr := make(chan error, 1)

				fmt.Printf("Waiting for branch %s is created!", branch.Name)
				spinner.While(func() {
					branchCreateResult, err := getBranchCreateResult(client, projectID, branch)
					taskResult <- branchCreateResult
					taskErr <- err
				})
				fmt.Println()

				if err := <-taskErr; err != nil {
					return err
				}

				switch <-taskResult {
				case "success":
					print.Success("Successfully created branch %s", branch.Name)
				case "error":
					print.Failure("There was an error creating branch %s.", branch.Name)
				}
			}
		}
	}

	projectIdToLocales, err := LocalesForProjects(client, sources, cmd.Branch)
	if err != nil {
		return err
	}
	for _, source := range sources {
		val, ok := projectIdToLocales[LocaleCacheKey{source.ProjectID, cmd.Branch}]
		if ok {
			source.RemoteLocales = val
		}
	}

	for _, source := range sources {
		err := source.Push(client, cmd.Wait, cmd.Branch)
		if err != nil {
			return err
		}
	}

	return nil
}

func (source *Source) Push(client *phraseapp.Client, waitForResults bool, branch string) error {
	localeFiles, err := source.LocaleFiles()
	if err != nil {
		return err
	}

	for _, localeFile := range localeFiles {
		fmt.Printf("Uploading %s... ", localeFile.RelPath())

		if localeFile.shouldCreateLocale(source, branch) {
			localeDetails, err := source.createLocale(client, localeFile, branch)
			if err == nil {
				localeFile.ID = localeDetails.ID
				localeFile.Code = localeDetails.Code
				localeFile.Name = localeDetails.Name
			} else {
				fmt.Printf("failed to create locale: %s\n", err)
				continue
			}
		}

		upload, err := source.uploadFile(client, localeFile, branch)
		if err != nil {
			return err
		}

		if waitForResults {
			fmt.Println()

			taskResult := make(chan string, 1)
			taskErr := make(chan error, 1)

			fmt.Printf("Upload ID: %s, filename: %s succeeded. Waiting for your file to be processed... ", upload.ID, upload.Filename)
			spinner.While(func() {
				result, err := getUploadResult(client, source.ProjectID, upload, branch)
				taskResult <- result
				taskErr <- err
			})
			fmt.Println()

			if err := <-taskErr; err != nil {
				return err
			}

			switch <-taskResult {
			case "success":
				print.Success("Successfully uploaded and processed %s.", localeFile.RelPath())
			case "error":
				print.Failure("There was an error processing %s. Your changes were not saved online.", localeFile.RelPath())
			}
		} else {
			fmt.Println("done!")
			fmt.Printf("Check upload ID: %s, filename: %s for information about processing results.\n", upload.ID, upload.Filename)
		}

		if Debug {
			fmt.Fprintln(os.Stderr, strings.Repeat("-", 10))
		}
	}

	return nil
}

func formatsByApiName(client *phraseapp.Client) (map[string]*phraseapp.Format, error) {
	formats, err := client.FormatsList(1, 25)
	if err != nil {
		return nil, err
	}
	formatMap := map[string]*phraseapp.Format{}
	for _, format := range formats {
		formatMap[format.ApiName] = format
	}
	return formatMap, nil
}

// Return all locale files from disk that match the source pattern.
func (source *Source) LocaleFiles() (LocaleFiles, error) {
	filePaths, err := paths.Glob(placeholders.ToGlobbingPattern(source.File))
	if err != nil {
		return nil, err
	}

	var localeFiles LocaleFiles
	for _, path := range filePaths {
		if paths.IsPhraseAppYmlConfig(path) {
			continue
		}

		localeFile := new(LocaleFile)
		localeFile.fillFromPath(path, source.File)

		localeFile.Path, err = filepath.Abs(path)
		if err != nil {
			return nil, err
		}

		locale := source.getRemoteLocaleForLocaleFile(localeFile)
		// TODO: sinnvoll?
		if locale != nil {
			localeFile.ExistsRemote = true
			localeFile.Code = locale.Code
			localeFile.Name = locale.Name
			localeFile.ID = locale.ID
		}

		if Debug {
			fmt.Printf(
				"Code:%q, Name:%q, ID:%q, Tag:%q\n",
				localeFile.Code, localeFile.Name, localeFile.ID, localeFile.Tag,
			)
		}

		localeFiles = append(localeFiles, localeFile)
	}

	if len(localeFiles) == 0 {
		abs, err := filepath.Abs(source.File)
		if err != nil {
			abs = source.File
		}
		return nil, fmt.Errorf("Could not find any files on your system that matches: '%s'", abs)
	}

	return localeFiles, nil
}

func (source *Source) getRemoteLocaleForLocaleFile(localeFile *LocaleFile) *phraseapp.Locale {
	candidates := source.RemoteLocales

	filterApplied := false

	filter := func(cands []*phraseapp.Locale, preCond string, pred func(cand *phraseapp.Locale) bool) []*phraseapp.Locale {
		if preCond == "" {
			return cands
		}
		filterApplied = true
		tmpCands := []*phraseapp.Locale{}
		for _, cand := range cands {
			if pred(cand) {
				tmpCands = append(tmpCands, cand)
			}
		}
		return tmpCands
	}

	localeName := source.replacePlaceholderInParams(localeFile)
	if localeName != "" {
		// This means the name can contain the value specified in LocaleID, with
		// `<locale_code>` being substituted by the value of the currently handled
		// localeFile (like push only locales with name `en-US`).
		candidates = filter(candidates, localeName, func(cand *phraseapp.Locale) bool {
			return strings.Contains(cand.Name, localeName)
		})
	} else {
		localeID := source.GetLocaleID()
		candidates = filter(candidates, localeID, func(cand *phraseapp.Locale) bool {
			return cand.Name == localeID || cand.ID == localeID
		})
	}

	candidates = filter(candidates, localeFile.Name, func(cand *phraseapp.Locale) bool {
		return cand.Name == localeFile.Name
	})

	candidates = filter(candidates, localeFile.Code, func(cand *phraseapp.Locale) bool {
		return cand.Code == localeFile.Code
	})

	// If no filter was applied the candidates list still contains all remote
	// locales, while actually nothing matches.
	if !filterApplied {
		return nil
	}

	switch len(candidates) {
	case 0:
		return nil
	case 1:
		return candidates[0]
	default:
		// TODO I guess this should return an error, as this is a problem.
		return candidates[0]
	}
}

func (localeFile *LocaleFile) fillFromPath(path, pattern string) {
	path = filepath.ToSlash(path)
	pathStart, patternStart, pathEnd, patternEnd, err := paths.SplitAtDirGlobOperator(path, pattern)
	if err != nil {
		print.Error(err)
		return
	}

	fillFrom := func(path, pattern string) {
		params, err := placeholders.Resolve(path, pattern)
		if err != nil {
			print.Error(err)
			return
		}

		for placeholder, value := range params {
			switch placeholder {
			case "locale_code":
				localeFile.Code = value
			case "locale_name":
				localeFile.Name = value
			case "tag":
				localeFile.Tag = value
			}
		}
	}

	fillFrom(pathStart, patternStart)
	fillFrom(pathEnd, patternEnd)
}

func (localeFile *LocaleFile) shouldCreateLocale(source *Source, branch string) bool {
	if localeFile.ExistsRemote {
		return false
	}

	if source.Format.IncludesLocaleInformation {
		return false
	}

	// we could not find an existing locale in PhraseApp
	// if a locale_name or locale_code was provided by the placeholder logic
	// we assume that it should be created
	// every other source should be uploaded and validated in uploads#create
	return (localeFile.Name != "" || localeFile.Code != "")
}

func getUploadResult(client *phraseapp.Client, projectID string, upload *phraseapp.Upload, branch string) (result string, err error) {
	b := &backoff.Backoff{
		Min:    500 * time.Millisecond,
		Max:    10 * time.Second,
		Factor: 2,
		Jitter: true,
	}

	for ; result != "success" && result != "error"; result = upload.State {
		time.Sleep(b.Duration())
		uploadShowParams := &phraseapp.UploadShowParams{Branch: &branch}
		upload, err = client.UploadShow(projectID, upload.ID, uploadShowParams)
		if err != nil {
			break
		}
	}

	return
}

func getBranchCreateResult(client *phraseapp.Client, projectID string, branch *phraseapp.Branch) (result string, err error) {
	b := &backoff.Backoff{
		Min:    500 * time.Millisecond,
		Max:    10 * time.Second,
		Factor: 2,
		Jitter: true,
	}

	for ; result != "success" && result != "error"; result = branch.State {
		time.Sleep(b.Duration())
		branch, err = client.BranchShow(projectID, branch.Name)
		if err != nil {
			break
		}
	}

	return
}

func printCreateBranchQuestion(branch string) {
	fmt.Printf("\nYou have currently checked out the branch '")
	ct.ChangeColor(ct.Green, false, ct.None, false)
	fmt.Printf("%s", branch)
	ct.ResetColor()
	fmt.Printf("'.\nThere currently is no branch in PhraseApp with this name.\n\n")
	fmt.Printf("Should we create a new branch in PhraseApp with the same name and push to it? [y/N]: ")
}

func isYes(text string) bool {
	return text == "y" ||
		text == "Y" ||
		text == "yes" ||
		text == "Yes" ||
		text == "YES"
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
#include <git2/errors.h>

int _go_git_push_status_foreach(git_push *push, void *data);
int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data);

*/
import "C"
import (
	"runtime"
	"unsafe"
)

type Push struct {
	ptr *C.git_push

	packbuilderProgress *PackbuilderProgressCallback
	transferProgress    *PushTransferProgressCallback
}

func newPushFromC(cpush *C.git_push) *Push {
	p := &Push{ptr: cpush}
	runtime.SetFinalizer(p, (*Push).Free)
	return p
}

func (p *Push) Free() {
	runtime.SetFinalizer(p, nil)
	C.git_push_free(p.ptr)
}

func (remote *Remote) NewPush() (*Push, error) {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cpush *C.git_push
	ret := C.git_push_new(&cpush, remote.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return newPushFromC(cpush), nil
}

func (p *Push) Finish() error {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_finish(p.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (p *Push) UnpackOk() bool {

	ret := C.git_push_unpack_ok(p.ptr)
	if ret == 0 {
		return false
	} else {
		return true
	}

}

func (p *Push) UpdateTips(sig *Signature, msg string) error {

	var csig *C.git_signature = nil
	if sig != nil {
		csig = sig.toC()
		defer C.free(unsafe.Pointer(csig))
	}

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_update_tips(p.ptr, csig, cmsg)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (p *Push) AddRefspec(refspec string) error {

	crefspec := C.CString(refspec)
	defer C.free(unsafe.Pointer(crefspec))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_add_refspec(p.ptr, crefspec)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

type PushOptions struct {
	Version       uint
	PbParallelism uint
}

func (p *Push) SetOptions(opts PushOptions) error {
	copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_set_options(p.ptr, &copts)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

type StatusForeachFunc func(ref string, msg string) int

//export statusForeach
func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int {
	ref := C.GoString(_ref)
	msg := C.GoString(_msg)

	cb := (*StatusForeachFunc)(_data)

	return C.int((*cb)(ref, msg))
}

func (p *Push) StatusForeach(callback StatusForeachFunc) error {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil

}

type PushCallbacks struct {
	PackbuilderProgress *PackbuilderProgressCallback
	TransferProgress    *PushTransferProgressCallback
}

type PackbuilderProgressCallback func(stage int, current uint, total uint) int
type PushTransferProgressCallback func(current uint, total uint, bytes uint) int

//export packbuilderProgress
func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int {
	return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total)))
}

//export pushTransferProgress
func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int {
	return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes)))
}

func (p *Push) SetCallbacks(callbacks PushCallbacks) {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// save callbacks so they don't get GC'd
	p.packbuilderProgress = callbacks.PackbuilderProgress
	p.transferProgress = callbacks.TransferProgress

	C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress))
}
<commit_msg>fix return for old go versions / travis<commit_after>package git

/*
#include <git2.h>
#include <git2/errors.h>

int _go_git_push_status_foreach(git_push *push, void *data);
int _go_git_push_set_callbacks(git_push *push, void *packbuilder_progress_data, void *transfer_progress_data);

*/
import "C"
import (
	"runtime"
	"unsafe"
)

type Push struct {
	ptr *C.git_push

	packbuilderProgress *PackbuilderProgressCallback
	transferProgress    *PushTransferProgressCallback
}

func newPushFromC(cpush *C.git_push) *Push {
	p := &Push{ptr: cpush}
	runtime.SetFinalizer(p, (*Push).Free)
	return p
}

func (p *Push) Free() {
	runtime.SetFinalizer(p, nil)
	C.git_push_free(p.ptr)
}

func (remote *Remote) NewPush() (*Push, error) {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	var cpush *C.git_push
	ret := C.git_push_new(&cpush, remote.ptr)
	if ret < 0 {
		return nil, MakeGitError(ret)
	}
	return newPushFromC(cpush), nil
}

func (p *Push) Finish() error {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_finish(p.ptr)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (p *Push) UnpackOk() bool {

	ret := C.git_push_unpack_ok(p.ptr)
	if ret == 0 {
		return false
	}
	return true

}

func (p *Push) UpdateTips(sig *Signature, msg string) error {

	var csig *C.git_signature = nil
	if sig != nil {
		csig = sig.toC()
		defer C.free(unsafe.Pointer(csig))
	}

	var cmsg *C.char
	if msg == "" {
		cmsg = nil
	} else {
		cmsg = C.CString(msg)
		defer C.free(unsafe.Pointer(cmsg))
	}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_update_tips(p.ptr, csig, cmsg)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

func (p *Push) AddRefspec(refspec string) error {

	crefspec := C.CString(refspec)
	defer C.free(unsafe.Pointer(crefspec))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_add_refspec(p.ptr, crefspec)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

type PushOptions struct {
	Version       uint
	PbParallelism uint
}

func (p *Push) SetOptions(opts PushOptions) error {
	copts := C.git_push_options{version: C.uint(opts.Version), pb_parallelism: C.uint(opts.PbParallelism)}

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C.git_push_set_options(p.ptr, &copts)
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil
}

type StatusForeachFunc func(ref string, msg string) int

//export statusForeach
func statusForeach(_ref *C.char, _msg *C.char, _data unsafe.Pointer) C.int {
	ref := C.GoString(_ref)
	msg := C.GoString(_msg)

	cb := (*StatusForeachFunc)(_data)

	return C.int((*cb)(ref, msg))
}

func (p *Push) StatusForeach(callback StatusForeachFunc) error {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ret := C._go_git_push_status_foreach(p.ptr, unsafe.Pointer(&callback))
	if ret < 0 {
		return MakeGitError(ret)
	}
	return nil

}

type PushCallbacks struct {
	PackbuilderProgress *PackbuilderProgressCallback
	TransferProgress    *PushTransferProgressCallback
}

type PackbuilderProgressCallback func(stage int, current uint, total uint) int
type PushTransferProgressCallback func(current uint, total uint, bytes uint) int

//export packbuilderProgress
func packbuilderProgress(stage C.int, current C.uint, total C.uint, data unsafe.Pointer) C.int {
	return C.int((*(*PackbuilderProgressCallback)(data))(int(stage), uint(current), uint(total)))
}

//export pushTransferProgress
func pushTransferProgress(current C.uint, total C.uint, bytes C.size_t, data unsafe.Pointer) C.int {
	return C.int((*(*PushTransferProgressCallback)(data))(uint(current), uint(total), uint(bytes)))
}

func (p *Push) SetCallbacks(callbacks PushCallbacks) {

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	// save callbacks so they don't get GC'd
	p.packbuilderProgress = callbacks.PackbuilderProgress
	p.transferProgress = callbacks.TransferProgress

	C._go_git_push_set_callbacks(p.ptr, unsafe.Pointer(p.packbuilderProgress), unsafe.Pointer(p.transferProgress))
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
)

var (
	stringClass *RString
)

// RString is the built in string class
type RString struct {
	*BaseClass
}

// StringObject represents string instances
type StringObject struct {
	Class *RString
	Value string
}

func (s *StringObject) Inspect() string {
	return s.Value
}

func (s *StringObject) returnClass() Class {
	if s.Class == nil {
		panic(fmt.Sprintf("String %s doesn't have class.", s.Inspect()))
	}

	return s.Class
}

func (s *StringObject) equal(e *StringObject) bool {
	return s.Value == e.Value
}

var (
	stringTable = make(map[string]*StringObject)
)

func initializeString(value string) *StringObject {
	addr, ok := stringTable[value]

	if !ok {
		s := &StringObject{Value: value, Class: stringClass}
		stringTable[value] = s
		return s
	}

	return addr
}

var builtinStringInstanceMethods = []*BuiltInMethodObject{
	{
		// Returns the concatineted of self and another String
		//
		// ```Ruby
		// "first" + "-second" # => "first-second"
		// ```
		// @return [String]
		Name: "+",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value
				return &StringObject{Value: leftValue + rightValue, Class: stringClass}
			}
		},
	},
	{
		// Returns self multiplying another Integer
		//
		// ```Ruby
		// "string " * 2 # => "string string string "
		// ```
		// @return [String]
		Name: "*",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*IntegerObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				if right.Value < 0 {
					return newError("Second argument must be greater than or equal to 0 String#*")
				}

				var result string

				for i := 0; i < right.Value; i++ {
					result += leftValue
				}

				return &StringObject{Value: result, Class: stringClass}
			}
		},
	},
	{
		// Returns a Boolean if first string greater than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		// @return [Boolean]
		Name: ">",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue > rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean if first string less than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		// @return [Boolean]
		Name: "<",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" == "second" # => false
		// "two" == "two" # => true
		// ```
		// @return [Boolean]
		Name: "==",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue == rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Integer. If first string is less than second string returns -1, if equal to returns 0, if greater returns 1
		//
		//
		// ```Ruby
		// "abc" <=> "abcd" # => -1
		// "abc" <=> "abc" # => 0
		// "abcd" <=> "abc" # => 1
		// ```
		// @return [Integer]
		Name: "<=>",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return initilaizeInteger(-1)
				}
				if leftValue > rightValue {
					return initilaizeInteger(1)
				}

				return initilaizeInteger(0)
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" != "second" # => true
		// "two" != "two" # => false
		// ```
		// @return [Boolean]
		Name: "!=",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue != rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Return a new String with the first character converted to uppercase but the rest of string converted to lowercase.
		//
		// ```Ruby
		// "test".capitalize # => "Test"
		// "tEST".capitalize # => "Test"
		// ```
		// @return [String]
		Name: "capitalize",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := []byte(receiver.(*StringObject).Value)
				start := string(str[0])
				rest := string(str[1:])
				result := strings.ToUpper(start) + strings.ToLower(rest)

				return initializeString(result)
			}
		},
	},
	{
		// Returns a new String with all characters is upcase
		//
		// ```Ruby
		// "very big".upcase # => "VERY BIG"
		// ```
		// @return [String]
		Name: "upcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(strings.ToUpper(str))
			}
		},
	},
	{
		// Returns a new String with all characters is lowercase
		//
		// ```Ruby
		// "erROR".downcase # => "error"
		// ```
		// @return [String]
		Name: "downcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(strings.ToLower(str))
			}
		},
	},
	{
		// Returns the character length of self
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		// @return [Integer]
		Name: "size",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initilaizeInteger(len(str))
			}
		},
	},
	{
		// Returns the character length of self
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		// @return [Integer]
		Name: "length",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initilaizeInteger(len(str))
			}
		},
	},
	{
		// Returns a new String with reverse order of self
		//
		// ```Ruby
		// "reverse".reverse # => "esrever"
		// ```
		// @return [String]
		Name: "reverse",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				var revert string
				for i := len(str) - 1; i >= 0; i-- {
					revert += string(str[i])
				}

				return initializeString(revert)
			}
		},
	},
	{
		// Returns a new String with self value
		//
		// ```Ruby
		// "string".to_s # => "string"
		// ```
		// @return [String]
		Name: "to_s",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(str)
			}
		},
	},
	{
		// Returns the result of converting self to Integer
		//
		// ```Ruby
		// "123".to_i # => 123
		// "3d print".to_i # => 3
		// "some text".to_i # => 0
		// ```
		// @return [Integer]
		Name: "to_i",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				parsedStr, err := strconv.ParseInt(str, 10, 0)

				if err == nil {
					return initilaizeInteger(int(parsedStr))
				}

				var digits string
				for _, char := range str {
					if unicode.IsDigit(char) {
						digits += string(char)
					} else {
						break
					}
				}

				if len(digits) > 0 {
					parsedStr, _ = strconv.ParseInt(digits, 10, 0)
					return initilaizeInteger(int(parsedStr))
				}

				return initilaizeInteger(0)
			}
		},
	},
}

func initString() {
	bc := &BaseClass{Name: "String", Methods: newEnvironment(), ClassMethods: newEnvironment(), Class: classClass, pseudoSuperClass: objectClass, superClass: objectClass}
	sc := &RString{BaseClass: bc}
	sc.setBuiltInMethods(builtinStringInstanceMethods, false)
	stringClass = sc
}
<commit_msg>Try to fix concurrent map write issue.<commit_after>package vm

import (
	"fmt"
	"strconv"
	"strings"
	"unicode"
	"sync"
)

var (
	stringClass *RString
)

// RString is the built in string class
type RString struct {
	*BaseClass
}

// StringObject represents string instances
type StringObject struct {
	Class *RString
	Value string
}

func (s *StringObject) Inspect() string {
	return s.Value
}

func (s *StringObject) returnClass() Class {
	if s.Class == nil {
		panic(fmt.Sprintf("String %s doesn't have class.", s.Inspect()))
	}

	return s.Class
}

func (s *StringObject) equal(e *StringObject) bool {
	return s.Value == e.Value
}

var (
	stringTable = make(map[string]*StringObject)
	mutex = &sync.Mutex{}
)

func initializeString(value string) *StringObject {
	addr, ok := stringTable[value]

	if !ok {
		s := &StringObject{Value: value, Class: stringClass}

		mutex.Lock()
		stringTable[value] = s
		mutex.Unlock()

		return s
	}

	return addr
}

var builtinStringInstanceMethods = []*BuiltInMethodObject{
	{
		// Returns the concatineted of self and another String
		//
		// ```Ruby
		// "first" + "-second" # => "first-second"
		// ```
		// @return [String]
		Name: "+",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value
				return &StringObject{Value: leftValue + rightValue, Class: stringClass}
			}
		},
	},
	{
		// Returns self multiplying another Integer
		//
		// ```Ruby
		// "string " * 2 # => "string string string "
		// ```
		// @return [String]
		Name: "*",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*IntegerObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				if right.Value < 0 {
					return newError("Second argument must be greater than or equal to 0 String#*")
				}

				var result string

				for i := 0; i < right.Value; i++ {
					result += leftValue
				}

				return &StringObject{Value: result, Class: stringClass}
			}
		},
	},
	{
		// Returns a Boolean if first string greater than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		// @return [Boolean]
		Name: ">",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue > rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean if first string less than second string
		//
		// ```Ruby
		// "a" < "b" # => true
		// ```
		// @return [Boolean]
		Name: "<",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" == "second" # => false
		// "two" == "two" # => true
		// ```
		// @return [Boolean]
		Name: "==",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue == rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Returns a Integer. If first string is less than second string returns -1, if equal to returns 0, if greater returns 1
		//
		//
		// ```Ruby
		// "abc" <=> "abcd" # => -1
		// "abc" <=> "abc" # => 0
		// "abcd" <=> "abc" # => 1
		// ```
		// @return [Integer]
		Name: "<=>",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue < rightValue {
					return initilaizeInteger(-1)
				}
				if leftValue > rightValue {
					return initilaizeInteger(1)
				}

				return initilaizeInteger(0)
			}
		},
	},
	{
		// Returns a Boolean of compared two strings
		//
		// ```Ruby
		// "first" != "second" # => true
		// "two" != "two" # => false
		// ```
		// @return [Boolean]
		Name: "!=",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				leftValue := receiver.(*StringObject).Value
				right, ok := args[0].(*StringObject)

				if !ok {
					return wrongTypeError(stringClass)
				}

				rightValue := right.Value

				if leftValue != rightValue {
					return TRUE
				}

				return FALSE
			}
		},
	},
	{
		// Return a new String with the first character converted to uppercase but the rest of string converted to lowercase.
		//
		// ```Ruby
		// "test".capitalize # => "Test"
		// "tEST".capitalize # => "Test"
		// ```
		// @return [String]
		Name: "capitalize",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := []byte(receiver.(*StringObject).Value)
				start := string(str[0])
				rest := string(str[1:])
				result := strings.ToUpper(start) + strings.ToLower(rest)

				return initializeString(result)
			}
		},
	},
	{
		// Returns a new String with all characters is upcase
		//
		// ```Ruby
		// "very big".upcase # => "VERY BIG"
		// ```
		// @return [String]
		Name: "upcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(strings.ToUpper(str))
			}
		},
	},
	{
		// Returns a new String with all characters is lowercase
		//
		// ```Ruby
		// "erROR".downcase # => "error"
		// ```
		// @return [String]
		Name: "downcase",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(strings.ToLower(str))
			}
		},
	},
	{
		// Returns the character length of self
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		// @return [Integer]
		Name: "size",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initilaizeInteger(len(str))
			}
		},
	},
	{
		// Returns the character length of self
		//
		// ```Ruby
		// "zero".size # => 4
		// "".size # => 0
		// ```
		// @return [Integer]
		Name: "length",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initilaizeInteger(len(str))
			}
		},
	},
	{
		// Returns a new String with reverse order of self
		//
		// ```Ruby
		// "reverse".reverse # => "esrever"
		// ```
		// @return [String]
		Name: "reverse",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				var revert string
				for i := len(str) - 1; i >= 0; i-- {
					revert += string(str[i])
				}

				return initializeString(revert)
			}
		},
	},
	{
		// Returns a new String with self value
		//
		// ```Ruby
		// "string".to_s # => "string"
		// ```
		// @return [String]
		Name: "to_s",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value

				return initializeString(str)
			}
		},
	},
	{
		// Returns the result of converting self to Integer
		//
		// ```Ruby
		// "123".to_i # => 123
		// "3d print".to_i # => 3
		// "some text".to_i # => 0
		// ```
		// @return [Integer]
		Name: "to_i",
		Fn: func(receiver Object) builtinMethodBody {
			return func(t *thread, args []Object, blockFrame *callFrame) Object {

				str := receiver.(*StringObject).Value
				parsedStr, err := strconv.ParseInt(str, 10, 0)

				if err == nil {
					return initilaizeInteger(int(parsedStr))
				}

				var digits string
				for _, char := range str {
					if unicode.IsDigit(char) {
						digits += string(char)
					} else {
						break
					}
				}

				if len(digits) > 0 {
					parsedStr, _ = strconv.ParseInt(digits, 10, 0)
					return initilaizeInteger(int(parsedStr))
				}

				return initilaizeInteger(0)
			}
		},
	},
}

func initString() {
	bc := &BaseClass{Name: "String", Methods: newEnvironment(), ClassMethods: newEnvironment(), Class: classClass, pseudoSuperClass: objectClass, superClass: objectClass}
	sc := &RString{BaseClass: bc}
	sc.setBuiltInMethods(builtinStringInstanceMethods, false)
	stringClass = sc
}
<|endoftext|>


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

package controller

import (
	"encoding/json"
	"fmt"
	"github.com/rodesousa/lantern/engine"
	log "github.com/rodesousa/lantern/logger"
	"github.com/rodesousa/lantern/mapper"
	"github.com/rodesousa/lantern/shard"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"net/http"
	"os"
	"strconv"
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "lantern",
	Short: "lantern is a tool for testing environments",
	Long: `===============================================================================
    __                         __                    __
   / /   __  __ ____          / /   ____ _   ____   / /_  ___    _____   ____
  / /   / / / //_  / ______  / /   / __  /  / __ \ / __/ / _ \  / ___/  / __ \
 / /___/ /_/ /  / /_/_____/ / /___/ /_/ /  / / / // /_  /  __/ / /     / / / /
/_____/\__,_/  /___/       /_____/\__,_/  /_/ /_/ \__/  \___/ /_/     /_/ /_/

===============================================================================

luz-lantern is a tool program used to test/check environments from development to production.

Please check down for help.

Copyright © 2016
Roberto De Sousa (https://github.com/rodesousa)
Patrick Tavares (https://github.com/ptavares)
`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	// Run: func(cmd *cobra.Command, args []string) { },

}

//Controller
type Controller struct {
	filename string
	result   string
}

var (
	cfgFile    string
	logFile    string
	debug      bool
	off        bool
	controller Controller
)

var runCmd = &cobra.Command{
	Use:     "run [yaml_file]",
	Short:   "launch the lantern program",
	Long:    `launch the lanter program with a yaml file that discribe all test to do`,
	Example: "lantern run tests.yaml",
	Run:     runLuz,
}

var serverCmd = &cobra.Command{
	Use:     "server [yaml_file]",
	Short:   "launch the lantern program like a server",
	Long:    `launch the lanter program with a yaml file that discribe all test to do`,
	Example: "lantern server tests.yaml",
	Run:     lanternServer,
}

func init() {
	cobra.OnInitialize(initFromCL)
	RootCmd.AddCommand(runCmd)
	RootCmd.AddCommand(serverCmd)

	RootCmd.PersistentFlags().StringVar(&logFile, "logfile", "", "log file output (default is current path)")
	RootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "show debug message")
	RootCmd.PersistentFlags().BoolVarP(&off, "off", "o", false, "disable out console log")
}

// initConfig reads in config file and ENV variables if set.
func initFromCL() {

	////mode server, disabled out console log
	//if server {
	//	off = true
	//}

	// initialize debug level
	log.Init(debug, !off, (logFile != ""), logFile)

	if cfgFile != "" {
		// enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".luz-lantern") // name of config file (without extension)
	viper.AddConfigPath("$HOME")        // adding home directory as first search path
	viper.AutomaticEnv()                // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		RootCmd.SetOutput(log.GetOutLogger())
		RootCmd.Help()
		os.Exit(-1)
	}
}

func lanternServer(cmd *cobra.Command, args []string) {
	runLuz(cmd, args)
	controller.runServer()
}

// Main funtion, launch when run command is invoked
func runLuz(cmd *cobra.Command, args []string) {
	// waiting for 1 arg -> show help in this case
	if len(args) == 0 {
		cmd.SetOutput(log.GetOutLogger())
		cmd.Help()
		os.Exit(1)
	} else {
		log.Info("Starting lantern with run command")
		log.DebugWithFields("run called", log.Fields{"args": args})

		// response json
		mapB, _ := json.Marshal(map[string]string{"status": "OK"})
		controller = Controller{args[0], string(mapB)}

		// Launch lantern in selected mode
		controller.launchLantern()
	}
	log.Info("End lantern")
}

// Main method of the lantern program
func (controller *Controller) launchLantern() {
	// Init the mapper
	shardsAsYaml, err := mapper.MappingYaml(controller.filename)
	if err == nil {
		// Get the shards from the Mapper
		shards := mapper.AnalyseShard(shardsAsYaml["cmd"])
		// Call the Engine with the shards
		engine.RunMultiThread(shards)

		koShards := shard.KoShards(shards)
		ko := len(koShards)
		ok := len(shards) - len(koShards)
		sOk := strconv.Itoa(ok) + "/" + strconv.Itoa(len(shards))
		sKo := strconv.Itoa(ko) + "/" + strconv.Itoa(len(shards))
		log.InfoWithFields("Test OK", log.Fields{"nbOk": sOk})

		// lantern find check ko
		if ko > 0 {
			err := fmt.Sprintf("Test KO nbKO=%s\n", sKo)
			mapResult := map[string]string{}
			mapResult["status"] = "KO"
			for i := range koShards {
				shard := koShards[i]
				err = fmt.Sprintf("%s - %s : %s", err, shard.Name, shard.Status.Err)
				mapResult[shard.Name] = shard.Status.Err
				mapB, _ := json.Marshal(mapResult)
				controller.result = string(mapB)
			}
			log.Info(err)
		}

	} else {
		//error in mapping yaml
		//TODO
	}
}

func (controller Controller) handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, controller.result)
}

func (controller Controller) runServer() {
	http.HandleFunc("/", controller.handler)
	http.ListenAndServe(":8080", nil)
}
<commit_msg>split run cmd<commit_after>// Copyright © 2016 Roberto De Sousa (https://github.com/rodesousa) / Patrick Tavares (https://github.com/ptavares)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package controller

import (
	"encoding/json"
	"fmt"
	"github.com/rodesousa/lantern/engine"
	log "github.com/rodesousa/lantern/logger"
	"github.com/rodesousa/lantern/mapper"
	"github.com/rodesousa/lantern/shard"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"net/http"
	"os"
	"strconv"
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "lantern",
	Short: "lantern is a tool for testing environments",
	Long: `===============================================================================
    __                         __                    __
   / /   __  __ ____          / /   ____ _   ____   / /_  ___    _____   ____
  / /   / / / //_  / ______  / /   / __  /  / __ \ / __/ / _ \  / ___/  / __ \
 / /___/ /_/ /  / /_/_____/ / /___/ /_/ /  / / / // /_  /  __/ / /     / / / /
/_____/\__,_/  /___/       /_____/\__,_/  /_/ /_/ \__/  \___/ /_/     /_/ /_/

===============================================================================

luz-lantern is a tool program used to test/check environments from development to production.

Please check down for help.

Copyright © 2016
Roberto De Sousa (https://github.com/rodesousa)
Patrick Tavares (https://github.com/ptavares)
`,
	// Uncomment the following line if your bare application
	// has an action associated with it:
	// Run: func(cmd *cobra.Command, args []string) { },

}

//Controller
type Controller struct {
	filename string
	result   string
}

var (
	cfgFile    string
	logFile    string
	debug      bool
	off        bool
	controller Controller
)

var runCmd = &cobra.Command{
	Use:     "run [yaml_file]",
	Short:   "launch the lantern program",
	Long:    `launch the lanter program with a yaml file that discribe all test to do`,
	Example: "lantern run tests.yaml",
	Run:     lantern,
}

var serverCmd = &cobra.Command{
	Use:     "server [yaml_file]",
	Short:   "launch the lantern program like a server",
	Long:    `launch the lanter program with a yaml file that discribe all test to do`,
	Example: "lantern server tests.yaml",
	Run:     lanternServer,
}

func init() {
	cobra.OnInitialize(initFromCL)
	RootCmd.AddCommand(runCmd)
	RootCmd.AddCommand(serverCmd)

	RootCmd.PersistentFlags().StringVar(&logFile, "logfile", "", "log file output (default is current path)")
	RootCmd.PersistentFlags().BoolVarP(&debug, "debug", "d", false, "show debug message")
	RootCmd.PersistentFlags().BoolVarP(&off, "off", "o", false, "disable out console log")
}

// initConfig reads in config file and ENV variables if set.
func initFromCL() {

	if cfgFile != "" {
		// enable ability to specify config file via flag
		viper.SetConfigFile(cfgFile)
	}

	viper.SetConfigName(".luz-lantern") // name of config file (without extension)
	viper.AddConfigPath("$HOME")        // adding home directory as first search path
	viper.AutomaticEnv()                // read in environment variables that match

	// If a config file is found, read it in.
	if err := viper.ReadInConfig(); err == nil {
		fmt.Println("Using config file:", viper.ConfigFileUsed())
	}
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	if err := RootCmd.Execute(); err != nil {
		RootCmd.SetOutput(log.GetOutLogger())
		RootCmd.Help()
		os.Exit(-1)
	}
}

func lanternServer(cmd *cobra.Command, args []string) {
	log.Init(debug, false, (logFile != ""), logFile)
	runLuz(cmd, args)
	controller.runServer()
}

func lantern(cmd *cobra.Command, args []string) {
	log.Init(debug, !off, (logFile != ""), logFile)
	runLuz(cmd, args)
}

// Main funtion, launch when run command is invoked
func runLuz(cmd *cobra.Command, args []string) {
	// waiting for 1 arg -> show help in this case
	if len(args) == 0 {
		cmd.SetOutput(log.GetOutLogger())
		cmd.Help()
		os.Exit(1)
	} else {
		log.Info("Starting lantern with run command")
		log.DebugWithFields("run called", log.Fields{"args": args})

		// response json
		mapB, _ := json.Marshal(map[string]string{"status": "OK"})
		controller = Controller{args[0], string(mapB)}

		// Launch lantern in selected mode
		controller.launchLantern()
	}
	log.Info("End lantern")
}

// Main method of the lantern program
func (controller *Controller) launchLantern() {
	// Init the mapper
	shardsAsYaml, err := mapper.MappingYaml(controller.filename)
	if err == nil {
		// Get the shards from the Mapper
		shards := mapper.AnalyseShard(shardsAsYaml["cmd"])
		// Call the Engine with the shards
		engine.RunMultiThread(shards)

		koShards := shard.KoShards(shards)
		ko := len(koShards)
		ok := len(shards) - len(koShards)
		sOk := strconv.Itoa(ok) + "/" + strconv.Itoa(len(shards))
		sKo := strconv.Itoa(ko) + "/" + strconv.Itoa(len(shards))
		log.InfoWithFields("Test OK", log.Fields{"nbOk": sOk})

		// lantern find check ko
		if ko > 0 {
			err := fmt.Sprintf("Test KO nbKO=%s\n", sKo)
			mapResult := map[string]string{}
			mapResult["status"] = "KO"
			for i := range koShards {
				shard := koShards[i]
				err = fmt.Sprintf("%s - %s : %s", err, shard.Name, shard.Status.Err)
				mapResult[shard.Name] = shard.Status.Err
				mapB, _ := json.Marshal(mapResult)
				controller.result = string(mapB)
			}
			log.Info(err)
		}

	} else {
		//error in mapping yaml
		//TODO
	}
}

func (controller Controller) handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, controller.result)
}

func (controller Controller) runServer() {
	http.HandleFunc("/", controller.handler)
	http.ListenAndServe(":8080", nil)
}
<|endoftext|>


<commit_before>package models

import (
	"errors"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"time"
)

type NotificationContent struct {
	Id int64 `json:"id"`
	// target of the activity (replied messageId, followed accountId etc.)
	TargetId     int64     `json:"targetId"   sql:"NOT NULL"`
	TypeConstant string    `json:"typeConstant"       sql:"NOT NULL"`
	CreatedAt    time.Time `json:"createdAt"`
}

const (
	NotificationContent_TYPE_LIKE     = "like"
	NotificationContent_TYPE_UPVOTE   = "upvote"
	NotificationContent_TYPE_DOWNVOTE = "downvote"
	NotificationContent_TYPE_COMMENT  = "comment"
	NotificationContent_TYPE_FOLLOW   = "follow"
	NotificationContent_TYPE_JOIN     = "join"
	NotificationContent_TYPE_LEFT     = "left"
)

func NewNotificationContent() *NotificationContent {
	return &NotificationContent{}
}

func (n *NotificationContent) GetId() int64 {
	return n.Id
}

func (n NotificationContent) TableName() string {
	return "api.notification_content"
}

func (n *NotificationContent) Create() error {
	s := map[string]interface{}{
		"type_constant": n.TypeConstant,
		"target_id":     n.TargetId,
	}
	q := bongo.NewQS(s)
	if err := n.One(q); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}
		return bongo.B.Create(n)
	}

	return nil
}

func (n *NotificationContent) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *NotificationContent) Fetch() error {
	return bongo.B.Fetch(n)
}

func CreateNotification(i Notifiable) error {
	// check for previous NotificationContent create if it does not exist (type:comment targetId:messageId)
	n := NewNotificationContent()
	n.TypeConstant = i.GetType()
	if n.TypeConstant == "" {
		return errors.New("Type must be set")
	}

	n.TargetId = i.GetTargetId()
	if n.TargetId == 0 {
		return errors.New("TargetId must be set")
	}

	if err := n.Create(); err != nil {
		return err
	}

	replierIds, err := i.GetNotifiedUsers()
	if err != nil {
		return err
	}

	// TODO check subscribers/unsubscribers

	return n.NotifyUsers(replierIds)
}

func (n *NotificationContent) NotifyUsers(recipients []int64) error {
	for i := 0; i < len(recipients); i++ {
		notification := NewNotification()
		notification.AccountId = recipients[i]
		notification.NotificationContentId = n.Id
		// TODO instead of interrupting the call send error messages to a queue
		if err := notification.Create(); err != nil {
			return err
		}
	}

	return nil
}

func (n *NotificationContent) FetchByIds(ids []int64) ([]NotificationContent, error) {
	notificationContents := make([]NotificationContent, 0)
	if err := bongo.B.FetchByIds(n, &notificationContents, ids); err != nil {
		return nil, err
	}
	return notificationContents, nil
}

func (n *NotificationContent) FetchMapByIds(ids []int64) (map[int64]NotificationContent, error) {
	ncList, err := n.FetchByIds(ids)
	if err != nil {
		return nil, err
	}

	ncMap := make(map[int64]NotificationContent, 0)
	for _, nc := range ncList {
		ncMap[nc.Id] = nc
	}

	return ncMap, nil
}

func (n *NotificationContent) GetEventType() string {
	switch n.TypeConstant {
	case NotificationContent_TYPE_LIKE:
		return "LikeIsAdded"
	case NotificationContent_TYPE_COMMENT:
		return "ReplyIsAdded"
	case NotificationContent_TYPE_FOLLOW:
		return "FollowHappened"
	case NotificationContent_TYPE_JOIN:
		return "GroupJoined"
	case NotificationContent_TYPE_LEFT:
		return "GroupLeft"
	case NotificationContent_TYPE_UPVOTE:
		return "undefined"
	case NotificationContent_TYPE_DOWNVOTE:
		return "undefined"
	default:
		return "undefined"
	}
}

func CreateNotificationType(notificationType string) (Notifiable, error) {
	switch notificationType {
	case "like":
		return NewInteractionNotification(notificationType), nil
	case "comment":
		return NewReplyNotification(), nil
	default:
		return nil, errors.New("undefined notification type")
	}

}

func (nc *NotificationContent) AfterCreate() {
	bongo.B.AfterCreate(nc)
}

func (nc *NotificationContent) AfterUpdate() {
	bongo.B.AfterUpdate(nc)
}

func (nc *NotificationContent) AfterDelete() {
	bongo.B.AfterDelete(nc)
}
<commit_msg>Social: follow notification is added to CreateNotificationType<commit_after>package models

import (
	"errors"
	"github.com/jinzhu/gorm"
	"github.com/koding/bongo"
	"time"
)

type NotificationContent struct {
	Id int64 `json:"id"`
	// target of the activity (replied messageId, followed accountId etc.)
	TargetId     int64     `json:"targetId"   sql:"NOT NULL"`
	TypeConstant string    `json:"typeConstant"       sql:"NOT NULL"`
	CreatedAt    time.Time `json:"createdAt"`
}

const (
	NotificationContent_TYPE_LIKE     = "like"
	NotificationContent_TYPE_UPVOTE   = "upvote"
	NotificationContent_TYPE_DOWNVOTE = "downvote"
	NotificationContent_TYPE_COMMENT  = "comment"
	NotificationContent_TYPE_FOLLOW   = "follow"
	NotificationContent_TYPE_JOIN     = "join"
	NotificationContent_TYPE_LEFT     = "left"
)

func NewNotificationContent() *NotificationContent {
	return &NotificationContent{}
}

func (n *NotificationContent) GetId() int64 {
	return n.Id
}

func (n NotificationContent) TableName() string {
	return "api.notification_content"
}

func (n *NotificationContent) Create() error {
	s := map[string]interface{}{
		"type_constant": n.TypeConstant,
		"target_id":     n.TargetId,
	}
	q := bongo.NewQS(s)
	if err := n.One(q); err != nil {
		if err != gorm.RecordNotFound {
			return err
		}
		return bongo.B.Create(n)
	}

	return nil
}

func (n *NotificationContent) One(q *bongo.Query) error {
	return bongo.B.One(n, n, q)
}

func (n *NotificationContent) Fetch() error {
	return bongo.B.Fetch(n)
}

func CreateNotification(i Notifiable) error {
	// check for previous NotificationContent create if it does not exist (type:comment targetId:messageId)
	n := NewNotificationContent()
	n.TypeConstant = i.GetType()
	if n.TypeConstant == "" {
		return errors.New("Type must be set")
	}

	n.TargetId = i.GetTargetId()
	if n.TargetId == 0 {
		return errors.New("TargetId must be set")
	}

	if err := n.Create(); err != nil {
		return err
	}

	replierIds, err := i.GetNotifiedUsers()
	if err != nil {
		return err
	}

	// TODO check subscribers/unsubscribers

	return n.NotifyUsers(replierIds)
}

func (n *NotificationContent) NotifyUsers(recipients []int64) error {
	for i := 0; i < len(recipients); i++ {
		notification := NewNotification()
		notification.AccountId = recipients[i]
		notification.NotificationContentId = n.Id
		// TODO instead of interrupting the call send error messages to a queue
		if err := notification.Create(); err != nil {
			return err
		}
	}

	return nil
}

func (n *NotificationContent) FetchByIds(ids []int64) ([]NotificationContent, error) {
	notificationContents := make([]NotificationContent, 0)
	if err := bongo.B.FetchByIds(n, &notificationContents, ids); err != nil {
		return nil, err
	}
	return notificationContents, nil
}

func (n *NotificationContent) FetchMapByIds(ids []int64) (map[int64]NotificationContent, error) {
	ncList, err := n.FetchByIds(ids)
	if err != nil {
		return nil, err
	}

	ncMap := make(map[int64]NotificationContent, 0)
	for _, nc := range ncList {
		ncMap[nc.Id] = nc
	}

	return ncMap, nil
}

func (n *NotificationContent) GetEventType() string {
	switch n.TypeConstant {
	case NotificationContent_TYPE_LIKE:
		return "LikeIsAdded"
	case NotificationContent_TYPE_COMMENT:
		return "ReplyIsAdded"
	case NotificationContent_TYPE_FOLLOW:
		return "FollowHappened"
	case NotificationContent_TYPE_JOIN:
		return "GroupJoined"
	case NotificationContent_TYPE_LEFT:
		return "GroupLeft"
	case NotificationContent_TYPE_UPVOTE:
		return "undefined"
	case NotificationContent_TYPE_DOWNVOTE:
		return "undefined"
	default:
		return "undefined"
	}
}

func CreateNotificationType(notificationType string) (Notifiable, error) {
	switch notificationType {
	case "like":
		return NewInteractionNotification(notificationType), nil
	case "comment":
		return NewReplyNotification(), nil
	case "follow":
		return NewFollowNotification(), nil
	default:
		return nil, errors.New("undefined notification type")
	}

}

func (nc *NotificationContent) AfterCreate() {
	bongo.B.AfterCreate(nc)
}

func (nc *NotificationContent) AfterUpdate() {
	bongo.B.AfterUpdate(nc)
}

func (nc *NotificationContent) AfterDelete() {
	bongo.B.AfterDelete(nc)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"socialapi/workers/common/runner"
	"socialapi/workers/followingfeed/followingfeed"
)

var (
	Name = "FollowingFeed"
)

func main() {
	r := runner.New(Name)
	if err := r.Init(); err != nil {
		fmt.Println(err)
		return
	}

	r.SetContext(followingfeed.New(r.Log))
	r.ListenFor("api.channel_message_created", (*followingfeed.Controller).MessageSaved)
	r.ListenFor("api.channel_message_update", (*followingfeed.Controller).MessageUpdated)
	r.ListenFor("api.channel_message_deleted", (*followingfeed.Controller).MessageDeleted)
	r.Listen()
	r.Wait()
}
<commit_msg>Social: ListerFor to Register<commit_after>package main

import (
	"fmt"
	"socialapi/models"
	"socialapi/workers/common/runner"
	"socialapi/workers/followingfeed/followingfeed"
)

var (
	Name = "FollowingFeed"
)

func main() {
	r := runner.New(Name)
	if err := r.Init(); err != nil {
		fmt.Println(err)
		return
	}

	r.SetContext(followingfeed.New(r.Log))
	r.Register(models.ChannelMessage{}).OnCreate().Handle((*followingfeed.Controller).MessageSaved)
	r.Register(models.ChannelMessage{}).OnUpdate().Handle((*followingfeed.Controller).MessageUpdated)
	r.Register(models.ChannelMessage{}).OnDelete().Handle((*followingfeed.Controller).MessageDeleted)
	r.Listen()
	r.Wait()
}
<|endoftext|>


<commit_before>package api

import (
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/handler"
	"socialapi/workers/common/mux"

	"golang.org/x/oauth2"
)

// AddHandlers adds handlers for slack integration
func AddHandlers(m *mux.Mux, config *config.Config) {
	s := &Slack{
		Hostname:          config.Hostname,
		Protocol:          config.Protocol,
		VerificationToken: config.Slack.VerificationToken,
		OAuthConf: &oauth2.Config{
			ClientID:     config.Slack.ClientId,
			ClientSecret: config.Slack.ClientSecret,
			RedirectURL:  config.Slack.RedirectUri,
			Endpoint: oauth2.Endpoint{
				AuthURL:  "https://slack.com/oauth/authorize",
				TokenURL: "https://slack.com/api/oauth.access",
			}, // https://slack.com/oauth/authorize
			Scopes: []string{
				// channels.info
				// channels.list
				"channels:read",

				// chat.postMessage
				"chat:write:bot",

				// groups.info
				// groups.list
				"groups:read",

				// im.list
				"im:read",

				// mpim.list
				"mpim:read",

				// team.info
				"team:read",

				// usergroups.list
				// usergroups.users.list
				"usergroups:read",

				// users.getPresence
				// users.info
				"users:read",

				// allows teams to easily install an incoming webhook that can
				// post from your app to a single Slack channel.
				"incoming-webhook",

				// allows teams to install slash commands bundled in your Slack
				// app.
				"commands",

				// includes bot user functionality. Unlike incoming-webhook and
				// commands, the bot scope grants your bot user access to a
				// subset of Web API methods.
				//
				// https://api.slack.com/bot-users#bot-methods
				"bot",
			},
		},
	}

	m.AddHandler(
		handler.Request{
			Handler:  s.Send,
			Name:     models.SlackOauthSend,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.Callback,
			Name:     models.SlackOauthCallback,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth/callback",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.Success,
			Name:     models.SlackOauthSuccess,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth/success",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.ListUsers,
			Name:     models.SlackListUsers,
			Type:     handler.GetRequest,
			Endpoint: "/slack/users",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.ListChannels,
			Name:     models.SlackListChannels,
			Type:     handler.GetRequest,
			Endpoint: "/slack/channels",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.TeamInfo,
			Name:     models.SlackTeamInfo,
			Type:     handler.GetRequest,
			Endpoint: "/slack/team",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.PostMessage,
			Name:     models.SlackPostMessage,
			Type:     handler.PostRequest,
			Endpoint: "/slack/message",
		},
	)

	m.AddUnscopedHandler(
		handler.Request{
			Handler:  s.SlashCommand,
			Name:     models.SlackSlashCommand,
			Type:     handler.PostRequest,
			Endpoint: "/slack/slash",
		},
	)
}
<commit_msg>socialapi: remove unnecessary oauth scopes<commit_after>package api

import (
	"socialapi/config"
	"socialapi/models"
	"socialapi/workers/common/handler"
	"socialapi/workers/common/mux"

	"golang.org/x/oauth2"
)

// AddHandlers adds handlers for slack integration
func AddHandlers(m *mux.Mux, config *config.Config) {
	s := &Slack{
		Hostname:          config.Hostname,
		Protocol:          config.Protocol,
		VerificationToken: config.Slack.VerificationToken,
		OAuthConf: &oauth2.Config{
			ClientID:     config.Slack.ClientId,
			ClientSecret: config.Slack.ClientSecret,
			RedirectURL:  config.Slack.RedirectUri,
			Endpoint: oauth2.Endpoint{
				AuthURL:  "https://slack.com/oauth/authorize",
				TokenURL: "https://slack.com/api/oauth.access",
			}, // https://slack.com/oauth/authorize
			Scopes: []string{
				// usergroups.list
				// usergroups.users.list
				"usergroups:read",

				// allows teams to install slash commands bundled in your Slack
				// app.
				"commands",

				// includes bot user functionality. Unlike incoming-webhook and
				// commands, the bot scope grants your bot user access to a
				// subset of Web API methods.
				//
				// https://api.slack.com/bot-users#bot-methods
				"bot",
			},
		},
	}

	m.AddHandler(
		handler.Request{
			Handler:  s.Send,
			Name:     models.SlackOauthSend,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.Callback,
			Name:     models.SlackOauthCallback,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth/callback",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.Success,
			Name:     models.SlackOauthSuccess,
			Type:     handler.GetRequest,
			Endpoint: "/slack/oauth/success",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.ListUsers,
			Name:     models.SlackListUsers,
			Type:     handler.GetRequest,
			Endpoint: "/slack/users",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.ListChannels,
			Name:     models.SlackListChannels,
			Type:     handler.GetRequest,
			Endpoint: "/slack/channels",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.TeamInfo,
			Name:     models.SlackTeamInfo,
			Type:     handler.GetRequest,
			Endpoint: "/slack/team",
		},
	)

	m.AddHandler(
		handler.Request{
			Handler:  s.PostMessage,
			Name:     models.SlackPostMessage,
			Type:     handler.PostRequest,
			Endpoint: "/slack/message",
		},
	)

	m.AddUnscopedHandler(
		handler.Request{
			Handler:  s.SlashCommand,
			Name:     models.SlackSlashCommand,
			Type:     handler.PostRequest,
			Endpoint: "/slack/slash",
		},
	)
}
<|endoftext|>


<commit_before>package gojunitreport

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

const testDataDir = "../../testdata/"

var matchTest = flag.String("match", "", "only test testdata matching this pattern")

var testConfigs = map[int]Config{
	5: {SkipXMLHeader: true},
	6: {SkipXMLHeader: true},
	7: {PackageName: "test/package"},
}

func TestRun(t *testing.T) {
	matchRegex := compileMatch(t)

	files, err := filepath.Glob(testDataDir + "*.txt")
	if err != nil {
		t.Fatalf("error finding files in testdata: %v", err)
	}

	for _, file := range files {
		if !matchRegex.MatchString(file) {
			continue
		}

		conf, reportFile, err := testFileConfig(strings.TrimPrefix(file, testDataDir))
		if err != nil {
			t.Errorf("testFileConfig error: %v", err)
			continue
		}

		t.Run(file, func(t *testing.T) {
			testRun(file, testDataDir+reportFile, conf, t)
		})
	}
}

func testRun(inputFile, reportFile string, config Config, t *testing.T) {
	input, err := os.Open(inputFile)
	if err != nil {
		t.Fatalf("error opening input file: %v", err)
	}
	defer input.Close()

	wantReport, err := os.ReadFile(reportFile)
	if os.IsNotExist(err) {
		t.Skipf("Skipping test with missing report file: %s", reportFile)
	} else if err != nil {
		t.Fatalf("error loading report file: %v", err)
	}

	config.Parser = "gotest"
	if strings.HasSuffix(inputFile, ".gojson.txt") {
		config.Parser = "gojson"
	}
	config.Hostname = "hostname"
	config.Properties = map[string]string{"go.version": "1.0"}
	config.TimestampFunc = func() time.Time {
		return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
	}

	var output bytes.Buffer
	if _, err := config.Run(input, &output); err != nil {
		t.Fatal(err)
	}

	if diff := cmp.Diff(output.String(), string(wantReport)); diff != "" {
		t.Errorf("Unexpected report diff (-want, +got):\n%v", diff)
	}
}

func testFileConfig(filename string) (config Config, reportFile string, err error) {
	var prefix string
	if idx := strings.IndexByte(filename, '-'); idx < 0 {
		return config, "", fmt.Errorf("testdata file does not contain a dash (-); expected name `{id}-{name}.txt` got `%s`", filename)
	} else {
		prefix = filename[:idx]
	}
	id, err := strconv.Atoi(prefix)
	if err != nil {
		return config, "", fmt.Errorf("testdata file did not start with a valid number: %w", err)
	}
	return testConfigs[id], fmt.Sprintf("%s-report.xml", prefix), nil
}

func compileMatch(t *testing.T) *regexp.Regexp {
	rx, err := regexp.Compile(*matchTest)
	if err != nil {
		t.Fatalf("Error compiling -match flag %q: %v", *matchTest, err)
	}
	return rx
}
<commit_msg>Strip testdata path from subtests name<commit_after>package gojunitreport

import (
	"bytes"
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

const testDataDir = "../../testdata/"

var matchTest = flag.String("match", "", "only test testdata matching this pattern")

var testConfigs = map[int]Config{
	5: {SkipXMLHeader: true},
	6: {SkipXMLHeader: true},
	7: {PackageName: "test/package"},
}

func TestRun(t *testing.T) {
	matchRegex := compileMatch(t)

	files, err := filepath.Glob(testDataDir + "*.txt")
	if err != nil {
		t.Fatalf("error finding files in testdata: %v", err)
	}

	for _, file := range files {
		if !matchRegex.MatchString(file) {
			continue
		}

		conf, reportFile, err := testFileConfig(strings.TrimPrefix(file, testDataDir))
		if err != nil {
			t.Errorf("testFileConfig error: %v", err)
			continue
		}

		t.Run(filepath.Base(file), func(t *testing.T) {
			testRun(file, testDataDir+reportFile, conf, t)
		})
	}
}

func testRun(inputFile, reportFile string, config Config, t *testing.T) {
	input, err := os.Open(inputFile)
	if err != nil {
		t.Fatalf("error opening input file: %v", err)
	}
	defer input.Close()

	wantReport, err := os.ReadFile(reportFile)
	if os.IsNotExist(err) {
		t.Skipf("Skipping test with missing report file: %s", reportFile)
	} else if err != nil {
		t.Fatalf("error loading report file: %v", err)
	}

	config.Parser = "gotest"
	if strings.HasSuffix(inputFile, ".gojson.txt") {
		config.Parser = "gojson"
	}
	config.Hostname = "hostname"
	config.Properties = map[string]string{"go.version": "1.0"}
	config.TimestampFunc = func() time.Time {
		return time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
	}

	var output bytes.Buffer
	if _, err := config.Run(input, &output); err != nil {
		t.Fatal(err)
	}

	if diff := cmp.Diff(output.String(), string(wantReport)); diff != "" {
		t.Errorf("Unexpected report diff (-want, +got):\n%v", diff)
	}
}

func testFileConfig(filename string) (config Config, reportFile string, err error) {
	var prefix string
	if idx := strings.IndexByte(filename, '-'); idx < 0 {
		return config, "", fmt.Errorf("testdata file does not contain a dash (-); expected name `{id}-{name}.txt` got `%s`", filename)
	} else {
		prefix = filename[:idx]
	}
	id, err := strconv.Atoi(prefix)
	if err != nil {
		return config, "", fmt.Errorf("testdata file did not start with a valid number: %w", err)
	}
	return testConfigs[id], fmt.Sprintf("%s-report.xml", prefix), nil
}

func compileMatch(t *testing.T) *regexp.Regexp {
	rx, err := regexp.Compile(*matchTest)
	if err != nil {
		t.Fatalf("Error compiling -match flag %q: %v", *matchTest, err)
	}
	return rx
}
<|endoftext|>


<commit_before>package server

import "errors"

// broadcastMessage processes messages and either broadcasts
// them to all connected users, or to a single user.
func (sc *signedConn) broadcastMessage() error {
	to := sc.message.GetPacket().GetTo().String()

	if to == "" {
		err := sc.broadcastAll()
		if err != nil {
			return err
		}
	} else {
		td := sc.tracker.getVerificationKeyData(to)
		if td == nil {
			return errors.New("peer disconnected")
		}

		err := writeMessage(td.conn, sc.message)
		if err != nil {
			return err
		}
	}

	return nil
}

// broadcastAll broadcasts to all participants.
func (sc *signedConn) broadcastAll() error {
	for conn := range sc.tracker.connections {
		if conn == sc.conn {
			continue
		}

		err := writeMessage(conn, sc.message)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Added new round broadcast<commit_after>package server

import (
	"errors"

	"github.com/cashshuffle/cashshuffle/message"
)

// broadcastMessage processes messages and either broadcasts
// them to all connected users, or to a single user.
func (sc *signedConn) broadcastMessage() error {
	to := sc.message.GetPacket().GetTo().String()

	if to == "" {
		err := sc.broadcastAll()
		if err != nil {
			sc.broadcastNewRound()

			// Don't disconnect, we broadcasted a new round.
			return nil
		}
	} else {
		td := sc.tracker.getVerificationKeyData(to)
		if td == nil {
			return errors.New("peer disconnected")
		}

		err := writeMessage(td.conn, sc.message)
		if err != nil {
			sc.broadcastNewRound()

			// Don't disconnect
			return nil
		}
	}

	return nil
}

// broadcastAll broadcasts to all participants.
func (sc *signedConn) broadcastAll() error {
	for conn := range sc.tracker.connections {
		if conn == sc.conn {
			continue
		}

		err := writeMessage(conn, sc.message)
		if err != nil {
			return err
		}
	}

	return nil
}

// broadcastNewRound broadcasts a new round.
func (sc *signedConn) broadcastNewRound() {
	for conn := range sc.tracker.connections {
		if conn == sc.conn {
			continue
		}

		td := sc.tracker.getTrackerData(conn)

		m := message.Signed{
			Packet: &message.Packet{
				Session: td.sessionID,
				Number:  td.number,
				Message: &message.Message{
					Str: "New round",
				},
			},
		}

		err := writeMessage(conn, &m)
		if err != nil {
			continue
		}
	}

	return
}
<|endoftext|>


<commit_before>package rio

type Block interface {
	Name() string
	Xfer(stream *Stream, op Operation, version int) error
	Version() uint
}
<commit_msg>block: add block definition for r/w on-disk data<commit_after>package rio

import (
	"bytes"
	"encoding"
	"reflect"
)

type Block interface {
	encoding.BinaryMarshaler
	encoding.BinaryUnmarshaler

	Name() string
	// Xfer(stream *Stream, op Operation, version int) error
	Version() uint32
}

// blockHeader describes the on-disk block data (header part)
type blockHeader struct {
	Len uint32 // length of this block
	Typ uint32 // block marker
}

// blockData describes the on-disk block data (payload part)
type blockData struct {
	Version uint32 // version of this block
	NameLen uint32 // length of the block name
}

type blockImpl struct {
	rv      reflect.Value
	rt      reflect.Type
	version uint32
	name    string
}

func (blk *blockImpl) Name() string {
	return blk.name
}

func (blk *blockImpl) Version() uint32 {
	return blk.version
}

func (blk *blockImpl) MarshalBinary() ([]byte, error) {
	var err error
	var buf bytes.Buffer

	err = bwrite(&buf, blk.rv.Interface())
	return buf.Bytes(), err
}

func (blk *blockImpl) UnmarshalBinary(data []byte) error {
	var err error
	buf := bytes.NewBuffer(data)
	err = bread(buf, blk.rv.Interface())
	data = data[len(data)-buf.Len():]
	return err
}
<|endoftext|>


<commit_before>package rel

import (
	"testing"
)

func TestAttributeNotEqSql(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" != 10"
	if sql != expected {
		t.Logf("TestAttributeNotEqSql sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEqAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" != 1 OR \"users\".\"id\" != 2)"
	if sql != expected {
		t.Logf("TestAttributeNotEqAny sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqNil(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEq(nil))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" IS NOT NULL"
	if sql != expected {
		t.Logf("TestAttributeNotEqNil sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEqAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" != 1 AND \"users\".\"id\" != 2)"
	if sql != expected {
		t.Logf("TestAttributeNotEqNil sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGt(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").Gt(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" > 10"
	if sql != expected {
		t.Logf("TestAttributeGt sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEq(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" >= 10"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEqAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEqAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" >= 1 OR \"users\".\"id\" >= 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEqAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEqAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" >= 1 AND \"users\".\"id\" >= 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" > 1 AND \"users\".\"id\" > 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" > 1 OR \"users\".\"id\" > 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeLt(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").Lt(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" < 10"
	if sql != expected {
		t.Logf("TestAttributeLt sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeLtEq(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").LtEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" <= 10"
	if sql != expected {
		t.Logf("TestAttributeLtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}
<commit_msg>Created test for AttributeNode#LtAny<commit_after>package rel

import (
	"testing"
)

func TestAttributeNotEqSql(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" != 10"
	if sql != expected {
		t.Logf("TestAttributeNotEqSql sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEqAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" != 1 OR \"users\".\"id\" != 2)"
	if sql != expected {
		t.Logf("TestAttributeNotEqAny sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqNil(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEq(nil))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" IS NOT NULL"
	if sql != expected {
		t.Logf("TestAttributeNotEqNil sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeNotEqAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").NotEqAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" != 1 AND \"users\".\"id\" != 2)"
	if sql != expected {
		t.Logf("TestAttributeNotEqNil sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGt(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").Gt(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" > 10"
	if sql != expected {
		t.Logf("TestAttributeGt sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEq(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" >= 10"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEqAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEqAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" >= 1 OR \"users\".\"id\" >= 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtEqAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtEqAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" >= 1 AND \"users\".\"id\" >= 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtAll(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtAll(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" > 1 AND \"users\".\"id\" > 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeGtAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").GtAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" > 1 OR \"users\".\"id\" > 2)"
	if sql != expected {
		t.Logf("TestAttributeGtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeLt(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").Lt(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" < 10"
	if sql != expected {
		t.Logf("TestAttributeLt sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeLtAny(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").LtAny(Sql(1), Sql(2)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE (\"users\".\"id\" < 1 OR \"users\".\"id\" < 2)"
	if sql != expected {
		t.Logf("TestAttributeLt sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}

func TestAttributeLtEq(t *testing.T) {
	users := NewTable("users")
	mgr := users.Select(users.Attr("id"))
	mgr.Where(users.Attr("id").LtEq(Sql(10)))
	sql := mgr.ToSql()
	expected := "SELECT \"users\".\"id\" FROM \"users\" WHERE \"users\".\"id\" <= 10"
	if sql != expected {
		t.Logf("TestAttributeLtEq sql: \n%s != \n%s", sql, expected)
		t.Fail()
	}
}
<|endoftext|>


<commit_before>package server

import (
	"net/rpc"
	"time"
)

type Multicast struct {
	ctrlChans   []chan bool
	doneChan    chan *rpc.Call
	timeoutChan chan bool
}

func NewMulticast(calls []*rpc.Call, timeoutMs int64) *Multicast {
	ret := &Multicast{
		ctrlChans:   make([]chan bool, 1),
		doneChan:    make(chan *rpc.Call, len(calls)),
		timeoutChan: make(chan bool, 1),
	}

	// Setup timeout channel.
	go func() {
		<-time.After(time.Duration(timeoutMs) * time.Millisecond)
		ret.timeoutChan <- true
	}()

	for _, call := range calls {
		if call != nil {
			exitChan := make(chan bool, 1)
			ret.ctrlChans = append(ret.ctrlChans, exitChan)

			go func() {
				for {
					select {
					case <-exitChan:
					case <-call.Done:
						ret.doneChan <- call
					}
					break
				}
			}()
		}
	}

	return ret
}

// Clean up resources: quit all pending go rotines.
func (m *Multicast) Close() {
	// Signal to quit all pending RPC go routines.
	for _, ch := range m.ctrlChans {
		ch <- true
	}
}

// Wait for all rpc finish, or timeout occurs, then return
// all available replies. The call also close the Multicast
// operation automatically.
func (m *Multicast) WaitAll() map[*rpc.Call]bool {
	// Process replies.
	replies := make(map[*rpc.Call]bool)
	for len(replies) < len(m.ctrlChans) {
		needsBreak := false
		select {
		case call := <-m.doneChan:
			replies[call] = true
		case <-m.timeoutChan:
			needsBreak = true
		default:
		}

		if needsBreak {
			break
		}
	}

	m.Close()

	return replies
}

// Wait for one pending rpc to finish. If there is no pending rpc or
// timeout has reached, return nil. The caller is responsible to
// close this Multicast object after it is no longer needed.
func (m *Multicast) WaitOne() *rpc.Call {
	for {
		select {
		case call := <-m.doneChan:
			return call
		case <-m.timeoutChan:
			return nil
		default:
		}
	}

	return nil
}
<commit_msg>Remove multicast<commit_after><|endoftext|>