<commit_before>package gorm

import (
	"bytes"
	"strings"
)

// Copied from golint
var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
var commonInitialismsReplacer *strings.Replacer

func init() {
	var commonInitialismsForReplacer []string
	for _, initialism := range commonInitialisms {
		commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
	}
	commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)

	smap = make(map[string]string)
}

var smap map[string]string

func ToDBName(name string) string {
	if v, ok := smap[name]; ok {
		return v
	}

	value := commonInitialismsReplacer.Replace(name)
	buf := bytes.NewBufferString("")
	for i, v := range value {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	s := strings.ToLower(buf.String())
	smap[name] = s
	return s
}

type expr struct {
	expr string
	args []interface{}
}

func Expr(expression string, args ...interface{}) *expr {
	return &expr{expr: expression, args: args}
}
<commit_msg>One more try to fix nil-pointer error<commit_after>package gorm

import (
	"bytes"
	"strings"
)

// Copied from golint
var commonInitialisms = []string{"API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", "SMTP", "SSH", "TLS", "TTL", "UI", "UID", "UUID", "URI", "URL", "UTF8", "VM", "XML", "XSRF", "XSS"}
var commonInitialismsReplacer *strings.Replacer

func init() {
	var commonInitialismsForReplacer []string
	for _, initialism := range commonInitialisms {
		commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
	}
	commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
}

var smap = make(map[string]string)

func ToDBName(name string) string {
	if v, ok := smap[name]; ok {
		return v
	}

	value := commonInitialismsReplacer.Replace(name)
	buf := bytes.NewBufferString("")
	for i, v := range value {
		if i > 0 && v >= 'A' && v <= 'Z' {
			buf.WriteRune('_')
		}
		buf.WriteRune(v)
	}

	s := strings.ToLower(buf.String())
	smap[name] = s
	return s
}

type expr struct {
	expr string
	args []interface{}
}

func Expr(expression string, args ...interface{}) *expr {
	return &expr{expr: expression, args: args}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"unicode"

	"github.com/fatih/color"
	"github.com/joho/godotenv"
)

const (
	logo = `
██╗   ██╗██████╗ ██╗ ██████╗████████╗
╚██╗ ██╔╝██╔══██╗██║██╔════╝╚══██╔══╝
 ╚████╔╝ ██║  ██║██║██║        ██║
  ╚██╔╝  ██║  ██║██║██║        ██║
   ██║   ██████╔╝██║╚██████╗   ██║
   ╚═╝   ╚═════╝ ╚═╝ ╚═════╝   ╚═╝

YDict V0.5
https://github.com/TimothyYe/ydict

`
)

func displayUsage() {
	color.Cyan(logo)
	color.Cyan("Usage: ydict <word to query>")
}

func isChinese(str string) bool {
	for _, r := range str {
		if unicode.Is(unicode.Scripts["Han"], r) {
			return true
		}
	}
	return false
}

func getExecutePath() string {
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}

	return filepath.Dir(ex)
}

func loadEnv() {
	exPath := getExecutePath()

	// if .env file doesn't exist, just return
	if _, err := os.Stat(fmt.Sprintf("%s/.env", exPath)); os.IsNotExist(err) {
		return
	}

	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading .env file")
		return
	}

	proxy = os.Getenv("SOCKS5")
}
<commit_msg>bug fix for env file<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"unicode"

	"github.com/fatih/color"
	"github.com/joho/godotenv"
)

const (
	logo = `
██╗   ██╗██████╗ ██╗ ██████╗████████╗
╚██╗ ██╔╝██╔══██╗██║██╔════╝╚══██╔══╝
 ╚████╔╝ ██║  ██║██║██║        ██║
  ╚██╔╝  ██║  ██║██║██║        ██║
   ██║   ██████╔╝██║╚██████╗   ██║
   ╚═╝   ╚═════╝ ╚═╝ ╚═════╝   ╚═╝

YDict V0.5
https://github.com/TimothyYe/ydict

`
)

func displayUsage() {
	color.Cyan(logo)
	color.Cyan("Usage: ydict <word to query>")
}

func isChinese(str string) bool {
	for _, r := range str {
		if unicode.Is(unicode.Scripts["Han"], r) {
			return true
		}
	}
	return false
}

func getExecutePath() string {
	ex, err := os.Executable()
	if err != nil {
		panic(err)
	}

	return filepath.Dir(ex)
}

func loadEnv() {
	exPath := getExecutePath()
	envPath := fmt.Sprintf("%s/.env", exPath)

	// if .env file doesn't exist, just return
	if _, err := os.Stat(fmt.Sprintf("%s/.env", exPath)); os.IsNotExist(err) {
		return
	}

	err := godotenv.Load(envPath)
	if err != nil {
		log.Fatal("Error loading .env file")
		return
	}

	proxy = os.Getenv("SOCKS5")
}
<|endoftext|>


<commit_before>package onecache

import (
	"bytes"
	"encoding/gob"
	"time"
)

//Converts an item into bytes
func (i *Item) Bytes() ([]byte, error) {

	var buf bytes.Buffer

	enc := gob.NewEncoder(&buf)

	if err := enc.Encode(i); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

//Helper method to check if an item is expired.
//Current usecase for this is for garbage collection
func (i *Item) IsExpired() bool {
	return time.Now().After(i.ExpiresAt)
}

//Decodes bytes into an item struct
func BytesToItem(data []byte) (*Item, error) {

	i := new(Item)

	err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(i)

	return i, err
}
<commit_msg>Utility function to increment integers of all types<commit_after>package onecache

import (
	"bytes"
	"encoding/gob"
	"time"
)

//Converts an item into bytes
func (i *Item) Bytes() ([]byte, error) {

	var buf bytes.Buffer

	enc := gob.NewEncoder(&buf)

	if err := enc.Encode(i); err != nil {
		return nil, err
	}

	return buf.Bytes(), nil
}

//Helper method to check if an item is expired.
//Current usecase for this is for garbage collection
func (i *Item) IsExpired() bool {
	return time.Now().After(i.ExpiresAt)
}

//Decodes bytes into an item struct
func BytesToItem(data []byte) (*Item, error) {

	i := new(Item)

	err := gob.NewDecoder(bytes.NewBuffer(data)).Decode(i)

	return i, err
}

func Increment(val interface{}, steps int) (interface{}, error) {

	var ret interface{}

	switch val.(type) {

	case int:
		ret = val.(int) + steps

	case int32:
		ret = val.(int32) + int32(steps)

	case int64:
		ret = val.(int64) + int64(steps)

	case uint:
		ret = val.(uint) + uint(steps)

	case uint8:
		ret = val.(uint8) + uint8(steps)

	case uint16:
		ret = val.(uint16) + uint16(steps)

	case uint32:
		ret = val.(uint32) + uint32(steps)

	case uint64:
		ret = val.(uint64) + uint64(steps)

	default:
		return 0, ErrCacheDataCannotBeIncreased
	}

	return ret, nil

}
<|endoftext|>


<commit_before>package scipipe

import (
	// "github.com/go-errors/errors"
	//"os"
	"os"
	"os/exec"
	re "regexp"
)

func ExecCmd(cmd string) string {
	Info.Println("Executing command: ", cmd)
	combOutput, err := exec.Command("bash", "-lc", cmd).CombinedOutput()
	if err != nil {
		Error.Println("Could not execute command `" + cmd + "`: " + string(combOutput))
		os.Exit(128)
	}
	return string(combOutput)
}

func Check(err error) {
	if err != nil {
		panic(err)
	}
}

func copyMapStrStr(m map[string]string) (nm map[string]string) {
	nm = make(map[string]string)
	for k, v := range m {
		nm[k] = v
	}
	return nm
}

// Return the regular expression used to parse the place-holder syntax for in-, out- and
// parameter ports, that can be used to instantiate a SciProcess.
func getShellCommandPlaceHolderRegex() *re.Regexp {
	r, err := re.Compile("{(o|os|i|is|p):([^{}:]+)}")
	Check(err)
	return r
}
<commit_msg>Remove un-used util method<commit_after>package scipipe

import (
	// "github.com/go-errors/errors"
	//"os"
	"os"
	"os/exec"
	re "regexp"
)

func ExecCmd(cmd string) string {
	Info.Println("Executing command: ", cmd)
	combOutput, err := exec.Command("bash", "-lc", cmd).CombinedOutput()
	if err != nil {
		Error.Println("Could not execute command `" + cmd + "`: " + string(combOutput))
		os.Exit(128)
	}
	return string(combOutput)
}

func Check(err error) {
	if err != nil {
		panic(err)
	}
}

// Return the regular expression used to parse the place-holder syntax for in-, out- and
// parameter ports, that can be used to instantiate a SciProcess.
func getShellCommandPlaceHolderRegex() *re.Regexp {
	r, err := re.Compile("{(o|os|i|is|p):([^{}:]+)}")
	Check(err)
	return r
}
<|endoftext|>


<commit_before>package main

import "regexp"

var ExpDetectDataURL = regexp.MustCompile(
	`(?i)^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$`)

// IsDataUrl returns true if passed string s is data url
func IsDataUrl(s string) bool {
	return ExpDetectDataURL.MatchString(s)
}

func IsBrowserImageContentType(contentType string) bool {
	switch contentType {
	case "image/jpeg", "image/jp2", "image/jpx", "image/jpm", "image/webp", "image/vnd.ms-photo",
		"image/jxr", "image/gif", "image/png", "image/tiff", "image/tiff-fx", "image/svg+xml",
		"image/x‑xbitmap", "image/x‑xbm", "image/bmp", "image/x-bmp", "image/x-icon":
		return true
	}

	return false
}
<commit_msg>created util funcs<commit_after>package main

import (
	"github.com/vincent-petithory/dataurl"
	"html/template"
	"net/http"
	"regexp"
)

var ExpDetectDataURL = regexp.MustCompile(
	`(?i)^\s*data:([a-z]+\/[a-z0-9\-\+]+(;[a-z\-]+\=[a-z0-9\-]+)?)?(;base64)?,[a-z0-9\!\$\&\'\,\(\)\*\+\,\;\=\-\.\_\~\:\@\/\?\%\s]*\s*$`)

// IsDataUrl returns true if passed string s is data url
func IsDataUrl(s string) bool {
	return ExpDetectDataURL.MatchString(s)
}

func IsBrowserImageContentType(contentType string) bool {
	switch contentType {
	case "image/jpeg", "image/jp2", "image/jpx", "image/jpm", "image/webp", "image/vnd.ms-photo",
		"image/jxr", "image/gif", "image/png", "image/tiff", "image/tiff-fx", "image/svg+xml",
		"image/x‑xbitmap", "image/x‑xbm", "image/bmp", "image/x-bmp", "image/x-icon":
		return true
	}

	return false
}

var ErrorPageTmpl = template.Must(template.New("error_page").Parse(`<!DOCTYPE html>

        <meta charset="utf-8">
        <title>Error</title>

Error

{{.}}

`))

// HTTPErrorHTML sends error message with specific status code
func HTTPErrorHTML(w http.ResponseWriter, error string, code int) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(code)
	ErrorPageTmpl.Execute(w, error)
}

var ImagesPageTmpl = template.Must(template.New("images_page").Parse(`<!DOCTYPE html>

        <meta charset="utf-8">
        <title>Images</title>

Images

        {{range .}}
        {{else}}No images{{end}}

`))

func ImagesHTMLRender(w http.ResponseWriter, dataURLChan <-chan *dataurl.DataURL) {
	w.Header().Set("Content-Type", "text/html; charset=utf-8")
	w.WriteHeader(http.StatusOK)
	ImagesPageTmpl.Execute(w, dataURLChan)
}

func MergeErrorChans(stopChan <-chan struct{}, errorChan1 <-chan error, errorChan2 <-chan error) <-chan error {
	errorChan := make(chan error)

	go func() {
		defer close(errorChan)

		select {
		case <-stopChan:
			return
		case errorChan <- <-errorChan1:
		case errorChan <- <-errorChan2:
		}
	}()

	return errorChan
}

func MergeDataURLChans(stopChan <-chan struct{}, dataURLChan1 <-chan *dataurl.DataURL, dataURLChan2 <-chan *dataurl.DataURL) <-chan *dataurl.DataURL {
	dataURLChan := make(chan *dataurl.DataURL)

	go func() {
		defer close(dataURLChan)

		select {
		case <-stopChan:
			return
		case dataURLChan <- <-dataURLChan1:
		case dataURLChan <- <-dataURLChan2:
		}
	}()

	return dataURLChan
}
<|endoftext|>


<commit_before>package youtube

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strconv"
	"time"
)

type Video struct {
	ID       string
	Streams  []Stream
	Title    string
	Author   string
	Duration time.Duration
}

type Stream struct {
	ItagNo   int    `json:"itag"`
	URL      string `json:"url"`
	MimeType string `json:"mimeType"`
	Quality  string `json:"quality"`
	Cipher   string `json:"signatureCipher"`
}

type Format struct {
	Stream
	Bitrate          int    `json:"bitrate"`
	Width            int    `json:"width"`
	Height           int    `json:"height"`
	LastModified     string `json:"lastModified"`
	ContentLength    string `json:"contentLength,omitempty"`
	QualityLabel     string `json:"qualityLabel"`
	ProjectionType   string `json:"projectionType"`
	AverageBitrate   int    `json:"averageBitrate,omitempty"`
	AudioQuality     string `json:"audioQuality"`
	ApproxDurationMs string `json:"approxDurationMs"`
	AudioSampleRate  string `json:"audioSampleRate"`
	AudioChannels    int    `json:"audioChannels"`
}

type AdaptiveFormat struct {
	Stream
	Bitrate   int `json:"bitrate"`
	Width     int `json:"width,omitempty"`
	Height    int `json:"height,omitempty"`
	InitRange struct {
		Start string `json:"start"`
		End   string `json:"end"`
	} `json:"initRange"`
	IndexRange struct {
		Start string `json:"start"`
		End   string `json:"end"`
	} `json:"indexRange"`
	LastModified     string `json:"lastModified"`
	ContentLength    string `json:"contentLength"`
	Fps              int    `json:"fps,omitempty"`
	QualityLabel     string `json:"qualityLabel,omitempty"`
	ProjectionType   string `json:"projectionType"`
	AverageBitrate   int    `json:"averageBitrate"`
	ApproxDurationMs string `json:"approxDurationMs"`
	ColorInfo        struct {
		Primaries               string `json:"primaries"`
		TransferCharacteristics string `json:"transferCharacteristics"`
		MatrixCoefficients      string `json:"matrixCoefficients"`
	} `json:"colorInfo,omitempty"`
	HighReplication bool   `json:"highReplication,omitempty"`
	AudioQuality    string `json:"audioQuality,omitempty"`
	AudioSampleRate string `json:"audioSampleRate,omitempty"`
	AudioChannels   int    `json:"audioChannels,omitempty"`
}

func (v *Video) FindStreamByQuality(quality string) *Stream {
	for i := range v.Streams {
		if v.Streams[i].Quality == quality {
			return &v.Streams[i]
		}
	}

	return nil
}

func (v *Video) FindStreamByItag(itagNo int) *Stream {
	for i := range v.Streams {
		if v.Streams[i].ItagNo == itagNo {
			return &v.Streams[i]
		}
	}
	return nil
}

func (v *Video) parseVideoInfo(info string) error {
	answer, err := url.ParseQuery(info)
	if err != nil {
		return err
	}

	status := answer.Get("status")
	if status != "ok" {
		return &ErrResponseStatus{
			Status: status,
			Reason: answer.Get("reason"),
		}
	}

	// read the streams map
	playerResponse := answer.Get("player_response")
	if playerResponse == "" {
		return errors.New("no player_response found in the server's answer")
	}

	var prData PlayerResponseData
	if err := json.Unmarshal([]byte(playerResponse), &prData); err != nil {
		return fmt.Errorf("unable to parse player response JSON: %w", err)
	}

	v.Title = prData.VideoDetails.Title
	v.Author = prData.VideoDetails.Author

	if seconds, _ := strconv.Atoi(prData.Microformat.PlayerMicroformatRenderer.LengthSeconds); seconds > 0 {
		v.Duration = time.Duration(seconds) * time.Second
	}

	// Check if video is downloadable
	if prData.PlayabilityStatus.Status != "OK" {
		return &ErrPlayabiltyStatus{
			Status: prData.PlayabilityStatus.Status,
			Reason: prData.PlayabilityStatus.Reason,
		}
	}

	// Get video download link
	streams, err := parseStreams(prData)
	if err != nil {
		return err
	}

	v.Streams = streams
	if len(v.Streams) == 0 {
		return errors.New("no Stream list found in the server's answer")
	}

	return nil
}

func parseStreams(prData PlayerResponseData) ([]Stream, error) {
	size := len(prData.StreamingData.Formats) + len(prData.StreamingData.AdaptiveFormats)
	streams := make([]Stream, 0, size)

	filterFormat := func(stream Stream) {
		if stream.MimeType == "" {
			// FIXME logging
			log.Printf("An error occurred while decoding one of the video's Stream's information: Stream %+v", stream)
			return
		}
		streams = append(streams, stream)
	}

	for _, format := range prData.StreamingData.Formats {
		filterFormat(format.Stream)
	}
	for _, format := range prData.StreamingData.AdaptiveFormats {
		filterFormat(format.Stream)
	}
	return streams, nil
}
<commit_msg>refactor parseStreams<commit_after>package youtube

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"strconv"
	"time"
)

type Video struct {
	ID              string
	Streams         []Stream
	Formats         []Format
	AdaptiveFormats []AdaptiveFormat
	Title           string
	Author          string
	Duration        time.Duration
}

type Stream struct {
	ItagNo   int    `json:"itag"`
	URL      string `json:"url"`
	MimeType string `json:"mimeType"`
	Quality  string `json:"quality"`
	Cipher   string `json:"signatureCipher"`
}

type Format struct {
	Stream
	Bitrate          int    `json:"bitrate"`
	Width            int    `json:"width"`
	Height           int    `json:"height"`
	LastModified     string `json:"lastModified"`
	ContentLength    string `json:"contentLength,omitempty"`
	QualityLabel     string `json:"qualityLabel"`
	ProjectionType   string `json:"projectionType"`
	AverageBitrate   int    `json:"averageBitrate,omitempty"`
	AudioQuality     string `json:"audioQuality"`
	ApproxDurationMs string `json:"approxDurationMs"`
	AudioSampleRate  string `json:"audioSampleRate"`
	AudioChannels    int    `json:"audioChannels"`
}

type AdaptiveFormat struct {
	Stream
	Bitrate   int `json:"bitrate"`
	Width     int `json:"width,omitempty"`
	Height    int `json:"height,omitempty"`
	InitRange struct {
		Start string `json:"start"`
		End   string `json:"end"`
	} `json:"initRange"`
	IndexRange struct {
		Start string `json:"start"`
		End   string `json:"end"`
	} `json:"indexRange"`
	LastModified     string `json:"lastModified"`
	ContentLength    string `json:"contentLength"`
	Fps              int    `json:"fps,omitempty"`
	QualityLabel     string `json:"qualityLabel,omitempty"`
	ProjectionType   string `json:"projectionType"`
	AverageBitrate   int    `json:"averageBitrate"`
	ApproxDurationMs string `json:"approxDurationMs"`
	ColorInfo        struct {
		Primaries               string `json:"primaries"`
		TransferCharacteristics string `json:"transferCharacteristics"`
		MatrixCoefficients      string `json:"matrixCoefficients"`
	} `json:"colorInfo,omitempty"`
	HighReplication bool   `json:"highReplication,omitempty"`
	AudioQuality    string `json:"audioQuality,omitempty"`
	AudioSampleRate string `json:"audioSampleRate,omitempty"`
	AudioChannels   int    `json:"audioChannels,omitempty"`
}

func (v *Video) FindStreamByQuality(quality string) *Stream {
	for i := range v.Streams {
		if v.Streams[i].Quality == quality {
			return &v.Streams[i]
		}
	}

	return nil
}

func (v *Video) FindStreamByItag(itagNo int) *Stream {
	for i := range v.Streams {
		if v.Streams[i].ItagNo == itagNo {
			return &v.Streams[i]
		}
	}
	return nil
}

func (v *Video) parseVideoInfo(info string) error {
	answer, err := url.ParseQuery(info)
	if err != nil {
		return err
	}

	status := answer.Get("status")
	if status != "ok" {
		return &ErrResponseStatus{
			Status: status,
			Reason: answer.Get("reason"),
		}
	}

	// read the streams map
	playerResponse := answer.Get("player_response")
	if playerResponse == "" {
		return errors.New("no player_response found in the server's answer")
	}

	var prData PlayerResponseData
	if err := json.Unmarshal([]byte(playerResponse), &prData); err != nil {
		return fmt.Errorf("unable to parse player response JSON: %w", err)
	}

	v.Title = prData.VideoDetails.Title
	v.Author = prData.VideoDetails.Author

	if seconds, _ := strconv.Atoi(prData.Microformat.PlayerMicroformatRenderer.LengthSeconds); seconds > 0 {
		v.Duration = time.Duration(seconds) * time.Second
	}

	// Check if video is downloadable
	if prData.PlayabilityStatus.Status != "OK" {
		return &ErrPlayabiltyStatus{
			Status: prData.PlayabilityStatus.Status,
			Reason: prData.PlayabilityStatus.Reason,
		}
	}

	// Get video download link
	if err := v.parseStreams(prData); err != nil {
		return err
	}

	if len(v.Streams) == 0 {
		return errors.New("no Stream list found in the server's answer")
	}

	return nil
}

func (v *Video) parseStreams(prData PlayerResponseData) error {
	v.Formats = prData.StreamingData.Formats
	v.AdaptiveFormats = prData.StreamingData.AdaptiveFormats
	size := len(v.Formats) + len(v.AdaptiveFormats)
	streams := make([]Stream, 0, size)

	filterFormat := func(stream Stream) {
		if stream.MimeType == "" {
			// FIXME logging
			log.Printf("An error occurred while decoding one of the video's Stream's information: Stream %+v", stream)
			return
		}
		streams = append(streams, stream)
	}

	for _, format := range v.Formats {
		filterFormat(format.Stream)
	}
	for _, format := range v.AdaptiveFormats {
		filterFormat(format.Stream)
	}
	v.Streams = streams
	return nil
}
<|endoftext|>


<commit_before>package wango

import (
	"fmt"
	"io"
	"math/rand"
	"regexp"
	"sync"
	"time"

	"github.com/ivahaev/go-logger"

	"github.com/pkg/errors"
	"golang.org/x/net/websocket"
)

const (
	msgWelcome = iota
	msgPrefix
	msgCall
	msgCallResult
	msgCallError
	msgSubscribe
	msgUnsubscribe
	msgPublish
	msgEvent
	msgSubscribed
	msgSubscribeError
	msgHeartbeat = 20
)

const (
	hbLimit            = 5
	hbTimeout          = time.Second * 5
	sendChanBufferSize = 50
	identity           = "wango"
)

var (
	msgIntTypes = map[int]string{
		0:  "WELCOME",
		1:  "PREFIX",
		2:  "CALL",
		3:  "CALLRESULT",
		4:  "CALLERROR",
		5:  "SUBSCRIBE",
		6:  "UNSUBSCRIBE",
		7:  "PUBLISH",
		8:  "EVENT",
		9:  "SUBSCRIBED",
		10: "SUBSCRIBEERROR",
		20: "HB",
	}
	msgTxtTypes = map[string]int{
		"WELCOME":        0,
		"PREFIX":         1,
		"CALL":           2,
		"CALLRESULT":     3,
		"CALLERROR":      4,
		"SUBSCRIBE":      5,
		"UNSUBSCRIBE":    6,
		"PUBLISH":        7,
		"EVENT":          8,
		"SUBSCRIBED":     9,
		"SUBSCRIBEERROR": 10,
		"HB":             20,
	}

	ErrHandlerAlreadyRegistered = errors.New("Handler already registered")
	ErrRPCNotRegistered         = errors.New("RPC not registered")
)

type callMsg struct {
	CallID string
	URI    string
	Args   []interface{}
}

type WS struct {
	connections       map[string]*conn
	connectionsLocker sync.RWMutex
	rpcHandlers       map[string]RPCHandler
	rpcRgxHandlers    map[*regexp.Regexp]RPCHandler
	// subHandlers       map[regexp]subHandler
	// subscribers       subscr
	openCB  func()
	closeCB func()
}

func New() *WS {
	server := new(WS)
	server.connections = map[string]*conn{}
	server.rpcHandlers = map[string]RPCHandler{}
	server.rpcRgxHandlers = map[*regexp.Regexp]RPCHandler{}
	return server
}

func (server *WS) RegisterRPCHandler(_uri interface{}, fn RPCHandler) error {
	switch _uri.(type) {
	case string:
		uri := _uri.(string)
		if _, ok := server.rpcHandlers[uri]; ok {
			return ErrHandlerAlreadyRegistered
		}
		server.rpcHandlers[uri] = fn
	case *regexp.Regexp:
		rgx := _uri.(*regexp.Regexp)
		if _, ok := server.rpcRgxHandlers[rgx]; ok {
			return ErrHandlerAlreadyRegistered
		}
		server.rpcRgxHandlers[rgx] = fn
	}

	return nil
}

func (server *WS) WampHandler(ws *websocket.Conn, extra interface{}) {
	c := server.addConnection(ws, extra)
	defer server.deleteConnection(c.id)

	go c.sender()

	server.receive(c)
}

func (server *WS) receive(c *conn) {
	var data string
	for {
		err := websocket.Message.Receive(c.connection, &data)
		if err != nil {
			if err != io.EOF {
				// Error receiving message
			}
			break
		}
		msgType, msg, err := parseMessage(data)
		if err != nil {
			// error parsing!!!
			logger.Error(err)
			continue
		}
		switch msgType {
		case msgCall:
			server.handleRPCCall(c, msg)
		case msgSubscribe:
		case msgUnsubscribe:
		case msgHeartbeat:
		}
	}
}

func (server *WS) handleRPCCall(c *conn, msg []interface{}) {
	rpcMessage, err := parseCallMessage(msg)
	if err != nil {
		logger.Error("Can't parse rpc message", err.Error())
		return
	}
	uri := rpcMessage.URI
	handler, ok := server.rpcHandlers[uri]
	if ok {
		res, err := handler(c.id, uri, rpcMessage.Args...)
		if err != nil {
			response, _ := createMessage(msgCallError, rpcMessage.CallID, err)
			// TODO: error handling
			c.send(response)
			return
		}
		response, _ := createMessage(msgCallResult, rpcMessage.CallID, res)
		c.send(response)
		return
	}
	for rgx, handler := range server.rpcRgxHandlers {
		if rgx.MatchString(uri) {
			res, err := handler(c.id, uri, rpcMessage.Args...)
			if err != nil {
				response, _ := createMessage(msgCallError, rpcMessage.CallID, err)
				c.send(response)
				return
			}
			response, _ := createMessage(msgCallResult, rpcMessage.CallID, res)
			c.send(response)
			return
		}
	}
	response, _ := createMessage(msgCallError, rpcMessage.CallID, ErrRPCNotRegistered)
	c.send(response)
}

func (c *conn) send(msg interface{}) {
	c.sendChan <- msg
}

func (c *conn) sender() {
	for msg := range c.sendChan {
		websocket.Message.Send(c.connection, msg)
	}
}

func (server *WS) addConnection(ws *websocket.Conn, extra interface{}) *conn {
	cn := new(conn)
	cn.connection = ws
	cn.id = newUUIDv4()
	cn.extra = extra
	cn.sendChan = make(chan interface{}, sendChanBufferSize)
	server.connectionsLocker.Lock()
	defer server.connectionsLocker.Unlock()
	server.connections[cn.id] = cn

	return cn
}

func (server *WS) getConnection(id string) (*conn, error) {
	server.connectionsLocker.RLock()
	defer server.connectionsLocker.RUnlock()
	cn, ok := server.connections[id]
	if !ok {
		return nil, errors.New("NOT FOUND")
	}

	return cn, nil
}

func (server *WS) deleteConnection(id string) {
	server.connectionsLocker.Lock()
	defer server.connectionsLocker.Unlock()
	delete(server.connections, id)
}

type conn struct {
	id         string
	connection *websocket.Conn
	extra      interface{}
	sendChan   chan interface{}
}

// RPCHandler describes func for handling RPC requests
type RPCHandler func(connID string, uri string, args ...interface{}) (interface{}, error)

func newUUIDv4() string {
	u := [16]byte{}
	rand.Read(u[:16])

	u[8] = (u[8] | 0x80) & 0xBf
	u[6] = (u[6] | 0x40) & 0x4f

	return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
}

func init() {

}
<commit_msg>handleRPCCall refactored<commit_after>package wango

import (
	"fmt"
	"io"
	"math/rand"
	"regexp"
	"sync"
	"time"

	"github.com/ivahaev/go-logger"

	"github.com/pkg/errors"
	"golang.org/x/net/websocket"
)

const (
	msgWelcome = iota
	msgPrefix
	msgCall
	msgCallResult
	msgCallError
	msgSubscribe
	msgUnsubscribe
	msgPublish
	msgEvent
	msgSubscribed
	msgSubscribeError
	msgHeartbeat = 20
)

const (
	hbLimit            = 5
	hbTimeout          = time.Second * 5
	sendChanBufferSize = 50
	identity           = "wango"
)

var (
	msgIntTypes = map[int]string{
		0:  "WELCOME",
		1:  "PREFIX",
		2:  "CALL",
		3:  "CALLRESULT",
		4:  "CALLERROR",
		5:  "SUBSCRIBE",
		6:  "UNSUBSCRIBE",
		7:  "PUBLISH",
		8:  "EVENT",
		9:  "SUBSCRIBED",
		10: "SUBSCRIBEERROR",
		20: "HB",
	}
	msgTxtTypes = map[string]int{
		"WELCOME":        0,
		"PREFIX":         1,
		"CALL":           2,
		"CALLRESULT":     3,
		"CALLERROR":      4,
		"SUBSCRIBE":      5,
		"UNSUBSCRIBE":    6,
		"PUBLISH":        7,
		"EVENT":          8,
		"SUBSCRIBED":     9,
		"SUBSCRIBEERROR": 10,
		"HB":             20,
	}

	ErrHandlerAlreadyRegistered = errors.New("Handler already registered")
	ErrRPCNotRegistered         = errors.New("RPC not registered")
)

type callMsg struct {
	CallID string
	URI    string
	Args   []interface{}
}

type WS struct {
	connections       map[string]*conn
	connectionsLocker sync.RWMutex
	rpcHandlers       map[string]RPCHandler
	rpcRgxHandlers    map[*regexp.Regexp]RPCHandler
	// subHandlers       map[regexp]subHandler
	// subscribers       subscr
	openCB  func()
	closeCB func()
}

func New() *WS {
	server := new(WS)
	server.connections = map[string]*conn{}
	server.rpcHandlers = map[string]RPCHandler{}
	server.rpcRgxHandlers = map[*regexp.Regexp]RPCHandler{}
	return server
}

func (server *WS) RegisterRPCHandler(_uri interface{}, fn RPCHandler) error {
	switch _uri.(type) {
	case string:
		uri := _uri.(string)
		if _, ok := server.rpcHandlers[uri]; ok {
			return ErrHandlerAlreadyRegistered
		}
		server.rpcHandlers[uri] = fn
	case *regexp.Regexp:
		rgx := _uri.(*regexp.Regexp)
		if _, ok := server.rpcRgxHandlers[rgx]; ok {
			return ErrHandlerAlreadyRegistered
		}
		server.rpcRgxHandlers[rgx] = fn
	}

	return nil
}

func (server *WS) WampHandler(ws *websocket.Conn, extra interface{}) {
	c := server.addConnection(ws, extra)
	defer server.deleteConnection(c.id)

	go c.sender()

	server.receive(c)
}

func (server *WS) receive(c *conn) {
	var data string
	for {
		err := websocket.Message.Receive(c.connection, &data)
		if err != nil {
			if err != io.EOF {
				// Error receiving message
			}
			break
		}
		msgType, msg, err := parseMessage(data)
		if err != nil {
			// error parsing!!!
			logger.Error(err)
			continue
		}
		switch msgType {
		case msgCall:
			server.handleRPCCall(c, msg)
		case msgSubscribe:
		case msgUnsubscribe:
		case msgHeartbeat:
		}
	}
}

func (server *WS) handleRPCCall(c *conn, msg []interface{}) {
	rpcMessage, err := parseCallMessage(msg)
	if err != nil {
		logger.Error("Can't parse rpc message", err.Error())
		return
	}

	uri := rpcMessage.URI
	handler, ok := server.rpcHandlers[uri]
	if !ok {
		var rgx *regexp.Regexp
		for rgx, handler = range server.rpcRgxHandlers {
			if rgx.MatchString(uri) {
				ok = true
				break
			}
		}
	}

	if ok {
		res, err := handler(c.id, uri, rpcMessage.Args...)
		if err != nil {
			response, _ := createMessage(msgCallError, rpcMessage.CallID, err)
			// TODO: error handling
			c.send(response)
			return
		}
		response, _ := createMessage(msgCallResult, rpcMessage.CallID, res)
		c.send(response)
		return
	}

	response, _ := createMessage(msgCallError, rpcMessage.CallID, ErrRPCNotRegistered)
	c.send(response)
}

func (c *conn) send(msg interface{}) {
	c.sendChan <- msg
}

func (c *conn) sender() {
	for msg := range c.sendChan {
		websocket.Message.Send(c.connection, msg)
	}
}

func (server *WS) addConnection(ws *websocket.Conn, extra interface{}) *conn {
	cn := new(conn)
	cn.connection = ws
	cn.id = newUUIDv4()
	cn.extra = extra
	cn.sendChan = make(chan interface{}, sendChanBufferSize)
	server.connectionsLocker.Lock()
	defer server.connectionsLocker.Unlock()
	server.connections[cn.id] = cn

	return cn
}

func (server *WS) getConnection(id string) (*conn, error) {
	server.connectionsLocker.RLock()
	defer server.connectionsLocker.RUnlock()
	cn, ok := server.connections[id]
	if !ok {
		return nil, errors.New("NOT FOUND")
	}

	return cn, nil
}

func (server *WS) deleteConnection(id string) {
	server.connectionsLocker.Lock()
	defer server.connectionsLocker.Unlock()
	delete(server.connections, id)
}

type conn struct {
	id         string
	connection *websocket.Conn
	extra      interface{}
	sendChan   chan interface{}
}

// RPCHandler describes func for handling RPC requests
type RPCHandler func(connID string, uri string, args ...interface{}) (interface{}, error)

func newUUIDv4() string {
	u := [16]byte{}
	rand.Read(u[:16])

	u[8] = (u[8] | 0x80) & 0xBf
	u[6] = (u[6] | 0x40) & 0x4f

	return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
}

func init() {

}
<|endoftext|>


<commit_before>package main

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

	"gopkg.in/fsnotify.v1"
)

// watch recursively watches changes in root and reports the filenames to names.
// It sends an error on the done chan.
// As an optimization, any dirs we encounter that meet the ExcludePrefix criteria of all reflexes can be
// ignored.
func watch(root string, watcher *fsnotify.Watcher, names chan<- string, done chan<- error, reflexes []*Reflex) {
	if err := filepath.Walk(root, walker(watcher, reflexes)); err != nil {
		infoPrintf(-1, "Error while walking path %s: %s", root, err)
	}

	for {
		select {
		case e := <-watcher.Events:
			if verbose {
				infoPrintln(-1, "fsnotify event:", e)
			}
			stat, err := os.Stat(e.Name)
			if err != nil {
				continue
			}
			path := normalize(e.Name, stat.IsDir())
			if e.Op&fsnotify.Chmod > 0 {
				continue
			}
			names <- path
			if e.Op&fsnotify.Create > 0 && stat.IsDir() {
				if err := filepath.Walk(path, walker(watcher, reflexes)); err != nil {
					infoPrintf(-1, "Error while walking path %s: %s", path, err)
				}
			}
			// TODO: Cannot currently remove fsnotify watches recursively, or for deleted files. See:
			// https://github.com/cespare/reflex/issues/13
			// https://github.com/go-fsnotify/fsnotify/issues/40
			// https://github.com/go-fsnotify/fsnotify/issues/41
		case err := <-watcher.Errors:
			done <- err
			return
		}
	}
}

func walker(watcher *fsnotify.Watcher, reflexes []*Reflex) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {
		if err != nil || !f.IsDir() {
			return nil
		}
		path = normalize(path, f.IsDir())
		ignore := true
		for _, r := range reflexes {
			if !r.matcher.ExcludePrefix(path) {
				ignore = false
				break
			}
		}
		if ignore {
			return filepath.SkipDir
		}
		if err := watcher.Add(path); err != nil {
			infoPrintf(-1, "Error while watching new path %s: %s", path, err)
		}
		return nil
	}
}

func normalize(path string, dir bool) string {
	path = strings.TrimPrefix(path, "./")
	if dir && !strings.HasSuffix(path, "/") {
		path = path + "/"
	}
	return path
}
<commit_msg>Only ignore events that are *only* CHMOD<commit_after>package main

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

	"gopkg.in/fsnotify.v1"
)

const chmodMask fsnotify.Op = ^fsnotify.Op(0) ^ fsnotify.Chmod

// watch recursively watches changes in root and reports the filenames to names.
// It sends an error on the done chan.
// As an optimization, any dirs we encounter that meet the ExcludePrefix criteria of all reflexes can be
// ignored.
func watch(root string, watcher *fsnotify.Watcher, names chan<- string, done chan<- error, reflexes []*Reflex) {
	if err := filepath.Walk(root, walker(watcher, reflexes)); err != nil {
		infoPrintf(-1, "Error while walking path %s: %s", root, err)
	}

	for {
		select {
		case e := <-watcher.Events:
			if verbose {
				infoPrintln(-1, "fsnotify event:", e)
			}
			stat, err := os.Stat(e.Name)
			if err != nil {
				continue
			}
			path := normalize(e.Name, stat.IsDir())
			if e.Op&chmodMask == 0 {
				continue
			}
			names <- path
			if e.Op&fsnotify.Create > 0 && stat.IsDir() {
				if err := filepath.Walk(path, walker(watcher, reflexes)); err != nil {
					infoPrintf(-1, "Error while walking path %s: %s", path, err)
				}
			}
			// TODO: Cannot currently remove fsnotify watches recursively, or for deleted files. See:
			// https://github.com/cespare/reflex/issues/13
			// https://github.com/go-fsnotify/fsnotify/issues/40
			// https://github.com/go-fsnotify/fsnotify/issues/41
		case err := <-watcher.Errors:
			done <- err
			return
		}
	}
}

func walker(watcher *fsnotify.Watcher, reflexes []*Reflex) filepath.WalkFunc {
	return func(path string, f os.FileInfo, err error) error {
		if err != nil || !f.IsDir() {
			return nil
		}
		path = normalize(path, f.IsDir())
		ignore := true
		for _, r := range reflexes {
			if !r.matcher.ExcludePrefix(path) {
				ignore = false
				break
			}
		}
		if ignore {
			return filepath.SkipDir
		}
		if err := watcher.Add(path); err != nil {
			infoPrintf(-1, "Error while watching new path %s: %s", path, err)
		}
		return nil
	}
}

func normalize(path string, dir bool) string {
	path = strings.TrimPrefix(path, "./")
	if dir && !strings.HasSuffix(path, "/") {
		path = path + "/"
	}
	return path
}
<|endoftext|>


<commit_before>package transloadit

import (
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type WatchOptions struct {
	// Directory to watch files in (only if Watch is true)
	Input string
	// Directoy to put result files in
	Output string
	// Watch the input directory or just compile files in input directory
	Watch bool
	// Template id to convert files with
	TemplateId string
	// Optional notify url for each assembly.
	NotifyUrl string
	// Instead of using templates you can define steps
	Steps map[string]map[string]interface{}
	// If true the original files will be copied in the output directoy with `-original_0_` prefix.
	// If false input files will be deleted.
	Preserve bool
}

type Watcher struct {
	client  *Client
	options *WatchOptions
	stopped bool
	// Listen for errors
	Error chan error
	// Listen for completed assemblies
	Done chan *AssemblyInfo
	// Listen for file changes (only if Watch == true)
	Change       chan string
	end          chan bool
	recentWrites map[string]time.Time
	blacklist    map[string]bool
}

// Watch a directory for changes and convert all changes files and download the result.
// It will create a new assembly for each file.
// If the directory already contains some they are all converted.
// See WatchOptions for possible configuration.
func (client *Client) Watch(options *WatchOptions) *Watcher {

	watcher := &Watcher{
		client:       client,
		options:      options,
		Error:        make(chan error),
		Done:         make(chan *AssemblyInfo),
		Change:       make(chan string),
		end:          make(chan bool),
		recentWrites: make(map[string]time.Time),
		blacklist:    make(map[string]bool),
	}

	watcher.start()

	return watcher

}

func (watcher *Watcher) start() {

	watcher.processDir()

	if watcher.options.Watch {
		go watcher.startWatcher()
	}

}

// Stop the watcher.
func (watcher *Watcher) Stop() {

	if watcher.stopped {
		return
	}

	watcher.stopped = true

	watcher.end <- true
	close(watcher.Done)
	close(watcher.Error)
	close(watcher.Change)
	close(watcher.end)
}

func (watcher *Watcher) processDir() {

	files, err := ioutil.ReadDir(watcher.options.Input)
	if err != nil {
		watcher.error(err)
		return
	}

	input := watcher.options.Input

	for _, file := range files {
		if !file.IsDir() {
			go watcher.processFile(path.Join(input, file.Name()))
		}
	}

}

func (watcher *Watcher) processFile(name string) {

	file, err := os.Open(name)
	if err != nil {
		watcher.error(err)
		return
	}

	// Add file to blacklist
	log.Printf("Adding to blacklist: '%s'", name)
	watcher.blacklist[name] = true

	assembly := watcher.client.CreateAssembly()

	if watcher.options.TemplateId != "" {
		assembly.TemplateId = watcher.options.TemplateId
	}

	if watcher.options.NotifyUrl != "" {
		assembly.NotifyUrl = watcher.options.NotifyUrl
	}

	for name, step := range watcher.options.Steps {
		assembly.AddStep(name, step)
	}

	assembly.Blocking = true

	assembly.AddReader("file", path.Base(name), file)

	info, err := assembly.Upload()
	if err != nil {
		watcher.error(err)
		return
	}

	if info.Error != "" {
		watcher.error(errors.New(info.Error))
		return
	}

	for stepName, results := range info.Results {
		for index, result := range results {
			go func() {
				watcher.downloadResult(stepName, index, result)
				watcher.handleOriginalFile(name)
				log.Printf("Removing from blacklist: '%s'", name)
				delete(watcher.blacklist, name)
				watcher.Done <- info
			}()
		}
	}
}

func (watcher *Watcher) downloadResult(stepName string, index int, result *FileInfo) {

	fileName := fmt.Sprintf("%s_%d_%s", stepName, index, result.Name)

	resp, err := http.Get(result.Url)
	if err != nil {
		watcher.error(err)
		return
	}

	defer resp.Body.Close()

	out, err := os.Create(path.Join(watcher.options.Output, fileName))
	if err != nil {
		watcher.error(err)
		return
	}

	defer out.Close()

	io.Copy(out, resp.Body)

}

func (watcher *Watcher) startWatcher() {

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		watcher.error(err)
	}

	defer fsWatcher.Close()

	if err = fsWatcher.Add(watcher.options.Input); err != nil {
		watcher.error(err)
	}

	go func() {
		for {

			if watcher.stopped {
				return
			}

			time.Sleep(time.Second)
			now := time.Now()

			for name, lastEvent := range watcher.recentWrites {
				diff := now.Sub(lastEvent)
				if diff > (time.Millisecond * 500) {
					delete(watcher.recentWrites, name)
					watcher.Change <- name
					go watcher.processFile(name)
				}
			}

		}
	}()

	for {
		select {
		case <-watcher.end:
			return
		case err := <-fsWatcher.Errors:
			watcher.error(err)
		case evt := <-fsWatcher.Events:
			// Ignore the event if the file is currently processed
			log.Printf("Checking blacklist: '%s'", evt.Name)
			if _, ok := watcher.blacklist[evt.Name]; ok == true {
				return
			}
			if evt.Op&fsnotify.Create == fsnotify.Create || evt.Op&fsnotify.Write == fsnotify.Write {
				watcher.recentWrites[evt.Name] = time.Now()
			}
		}
	}

}

func (watcher *Watcher) handleOriginalFile(name string) {

	var err error
	if watcher.options.Preserve {
		_, file := path.Split(name)
		err = os.Rename(name, watcher.options.Output+"/-original_0_"+basename(file))
	} else {
		err = os.Remove(name)
	}

	if err != nil {
		watcher.error(err)
	}

}

func (watcher *Watcher) error(err error) {
	watcher.Error <- err
}

func basename(name string) string {
	i := strings.LastIndex(name, string(os.PathSeparator))
	return name[i+1:]
}
<commit_msg>Don't stop the loop when hitting blacklist<commit_after>package transloadit

import (
	"errors"
	"fmt"
	"gopkg.in/fsnotify.v1"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strings"
	"time"
)

type WatchOptions struct {
	// Directory to watch files in (only if Watch is true)
	Input string
	// Directoy to put result files in
	Output string
	// Watch the input directory or just compile files in input directory
	Watch bool
	// Template id to convert files with
	TemplateId string
	// Optional notify url for each assembly.
	NotifyUrl string
	// Instead of using templates you can define steps
	Steps map[string]map[string]interface{}
	// If true the original files will be copied in the output directoy with `-original_0_` prefix.
	// If false input files will be deleted.
	Preserve bool
}

type Watcher struct {
	client  *Client
	options *WatchOptions
	stopped bool
	// Listen for errors
	Error chan error
	// Listen for completed assemblies
	Done chan *AssemblyInfo
	// Listen for file changes (only if Watch == true)
	Change       chan string
	end          chan bool
	recentWrites map[string]time.Time
	blacklist    map[string]bool
}

// Watch a directory for changes and convert all changes files and download the result.
// It will create a new assembly for each file.
// If the directory already contains some they are all converted.
// See WatchOptions for possible configuration.
func (client *Client) Watch(options *WatchOptions) *Watcher {

	watcher := &Watcher{
		client:       client,
		options:      options,
		Error:        make(chan error),
		Done:         make(chan *AssemblyInfo),
		Change:       make(chan string),
		end:          make(chan bool),
		recentWrites: make(map[string]time.Time),
		blacklist:    make(map[string]bool),
	}

	watcher.start()

	return watcher

}

func (watcher *Watcher) start() {

	watcher.processDir()

	if watcher.options.Watch {
		go watcher.startWatcher()
	}

}

// Stop the watcher.
func (watcher *Watcher) Stop() {

	if watcher.stopped {
		return
	}

	watcher.stopped = true

	watcher.end <- true
	close(watcher.Done)
	close(watcher.Error)
	close(watcher.Change)
	close(watcher.end)
}

func (watcher *Watcher) processDir() {

	files, err := ioutil.ReadDir(watcher.options.Input)
	if err != nil {
		watcher.error(err)
		return
	}

	input := watcher.options.Input

	for _, file := range files {
		if !file.IsDir() {
			go watcher.processFile(path.Join(input, file.Name()))
		}
	}

}

func (watcher *Watcher) processFile(name string) {

	file, err := os.Open(name)
	if err != nil {
		watcher.error(err)
		return
	}

	// Add file to blacklist
	log.Printf("Adding to blacklist: '%s'", name)
	watcher.blacklist[name] = true

	assembly := watcher.client.CreateAssembly()

	if watcher.options.TemplateId != "" {
		assembly.TemplateId = watcher.options.TemplateId
	}

	if watcher.options.NotifyUrl != "" {
		assembly.NotifyUrl = watcher.options.NotifyUrl
	}

	for name, step := range watcher.options.Steps {
		assembly.AddStep(name, step)
	}

	assembly.Blocking = true

	assembly.AddReader("file", path.Base(name), file)

	info, err := assembly.Upload()
	if err != nil {
		watcher.error(err)
		return
	}

	if info.Error != "" {
		watcher.error(errors.New(info.Error))
		return
	}

	for stepName, results := range info.Results {
		for index, result := range results {
			go func() {
				watcher.downloadResult(stepName, index, result)
				watcher.handleOriginalFile(name)
				log.Printf("Removing from blacklist: '%s'", name)
				delete(watcher.blacklist, name)
				watcher.Done <- info
			}()
		}
	}
}

func (watcher *Watcher) downloadResult(stepName string, index int, result *FileInfo) {

	fileName := fmt.Sprintf("%s_%d_%s", stepName, index, result.Name)

	resp, err := http.Get(result.Url)
	if err != nil {
		watcher.error(err)
		return
	}

	defer resp.Body.Close()

	out, err := os.Create(path.Join(watcher.options.Output, fileName))
	if err != nil {
		watcher.error(err)
		return
	}

	defer out.Close()

	io.Copy(out, resp.Body)

}

func (watcher *Watcher) startWatcher() {

	fsWatcher, err := fsnotify.NewWatcher()
	if err != nil {
		watcher.error(err)
	}

	defer fsWatcher.Close()

	if err = fsWatcher.Add(watcher.options.Input); err != nil {
		watcher.error(err)
	}

	go func() {
		for {

			if watcher.stopped {
				return
			}

			time.Sleep(time.Second)
			now := time.Now()

			for name, lastEvent := range watcher.recentWrites {
				diff := now.Sub(lastEvent)
				if diff > (time.Millisecond * 500) {
					delete(watcher.recentWrites, name)
					watcher.Change <- name
					go watcher.processFile(name)
				}
			}

		}
	}()

	for {
		select {
		case <-watcher.end:
			return
		case err := <-fsWatcher.Errors:
			watcher.error(err)
		case evt := <-fsWatcher.Events:
			// Ignore the event if the file is currently processed
			log.Printf("Checking blacklist: '%s'", evt.Name)
			if _, ok := watcher.blacklist[evt.Name]; ok == true {
				continue
			}
			if evt.Op&fsnotify.Create == fsnotify.Create || evt.Op&fsnotify.Write == fsnotify.Write {
				watcher.recentWrites[evt.Name] = time.Now()
			}
		}
	}

}

func (watcher *Watcher) handleOriginalFile(name string) {

	var err error
	if watcher.options.Preserve {
		_, file := path.Split(name)
		err = os.Rename(name, watcher.options.Output+"/-original_0_"+basename(file))
	} else {
		err = os.Remove(name)
	}

	if err != nil {
		watcher.error(err)
	}

}

func (watcher *Watcher) error(err error) {
	watcher.Error <- err
}

func basename(name string) string {
	i := strings.LastIndex(name, string(os.PathSeparator))
	return name[i+1:]
}
<|endoftext|>


<commit_before>package hermes

import (
	"bytes"
	"github.com/Masterminds/sprig"
	"github.com/imdario/mergo"
	"github.com/jaytaylor/html2text"
	"github.com/russross/blackfriday"
	"html/template"
)

// Hermes is an instance of the hermes email generator
type Hermes struct {
	Theme         Theme
	TextDirection TextDirection
	Product       Product
}

// Theme is an interface to implement when creating a new theme
type Theme interface {
	Name() string              // The name of the theme
	HTMLTemplate() string      // The golang template for HTML emails
	PlainTextTemplate() string // The golang templte for plain text emails (can be basic HTML)
}

// TextDirection of the text in HTML email
type TextDirection string

var templateFuncs = template.FuncMap{
	"url": func(s string) template.URL {
		return template.URL(s)
	},
}

// TDLeftToRight is the text direction from left to right (default)
const TDLeftToRight TextDirection = "ltr"

// TDRightToLeft is the text direction from right to left
const TDRightToLeft TextDirection = "rtl"

// Product represents your company product (brand)
// Appears in header & footer of e-mails
type Product struct {
	Name        string
	Link        string // e.g. https://matcornic.github.io
	Logo        string // e.g. https://matcornic.github.io/img/logo.png
	Copyright   string // Copyright © 2017 Hermes. All rights reserved.
	TroubleText string // TroubleText is the sentence at the end of the email for users having trouble with the button (default to `If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.`)
}

// Email is the email containing a body
type Email struct {
	Body Body
}

// Markdown is a HTML template (a string) representing Markdown content
// https://en.wikipedia.org/wiki/Markdown
type Markdown template.HTML

// Body is the body of the email, containing all interesting data
type Body struct {
	Name         string   // The name of the contacted person
	Intros       []string // Intro sentences, first displayed in the email
	Dictionary   []Entry  // A list of key+value (useful for displaying parameters/settings/personal info)
	Table        Table    // Table is an table where you can put data (pricing grid, a bill, and so on)
	Actions      []Action // Actions are a list of actions that the user will be able to execute via a button click
	Outros       []string // Outro sentences, last displayed in the email
	Greeting     string   // Greeting for the contacted person (default to 'Hi')
	Signature    string   // Signature for the contacted person (default to 'Yours truly')
	Title        string   // Title replaces the greeting+name when set
	FreeMarkdown Markdown // Free markdown content that replaces all content other than header and footer
}

// ToHTML converts Markdown to HTML
func (c Markdown) ToHTML() template.HTML {
	return template.HTML(blackfriday.MarkdownCommon([]byte(string(c))))
}

// Entry is a simple entry of a map
// Allows using a slice of entries instead of a map
// Because Golang maps are not ordered
type Entry struct {
	Key   string
	Value string
}

// Table is an table where you can put data (pricing grid, a bill, and so on)
type Table struct {
	Data    [][]Entry // Contains data
	Columns Columns   // Contains meta-data for display purpose (width, alignement)
}

// Columns contains meta-data for the different columns
type Columns struct {
	CustomWidth      map[string]string
	CustomAlignement map[string]string
}

// Action is an action the user can do on the email (click on a button)
type Action struct {
	Instructions string
	Button       Button
}

// Button defines an action to launch
type Button struct {
	Color string
	Text  string
	Link  string
}

// Template is the struct given to Golang templating
// Root object in a template is this struct
type Template struct {
	Hermes Hermes
	Email  Email
}

func setDefaultEmailValues(e *Email) error {
	// Default values of an email
	defaultEmail := Email{
		Body: Body{
			Intros:     []string{},
			Dictionary: []Entry{},
			Outros:     []string{},
			Signature:  "Yours truly",
			Greeting:   "Hi",
		},
	}
	// Merge the given email with default one
	// Default one overrides all zero values
	return mergo.Merge(e, defaultEmail)
}

// default values of the engine
func setDefaultHermesValues(h *Hermes) error {
	defaultTextDirection := TDLeftToRight
	defaultHermes := Hermes{
		Theme:         new(Default),
		TextDirection: defaultTextDirection,
		Product: Product{
			Name:        "Hermes",
			Copyright:   "Copyright © 2017 Hermes. All rights reserved.",
			TroubleText: "If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.",
		},
	}
	// Merge the given hermes engine configuration with default one
	// Default one overrides all zero values
	err := mergo.Merge(h, defaultHermes)
	if err != nil {
		return err
	}
	if h.TextDirection != TDLeftToRight && h.TextDirection != TDRightToLeft {
		h.TextDirection = defaultTextDirection
	}
	return nil
}

// GenerateHTML generates the email body from data to an HTML Reader
// This is for modern email clients
func (h *Hermes) GenerateHTML(email Email) (string, error) {
	err := setDefaultHermesValues(h)
	if err != nil {
		return "", err
	}
	return h.generateTemplate(email, h.Theme.HTMLTemplate())
}

// GeneratePlainText generates the email body from data
// This is for old email clients
// Note : this mode is not able to print Tables
func (h *Hermes) GeneratePlainText(email Email) (string, error) {
	err := setDefaultHermesValues(h)
	if err != nil {
		return "", err
	}
	template, err := h.generateTemplate(email, h.Theme.PlainTextTemplate())
	if err != nil {
		return "", err
	}
	return html2text.FromString(template, html2text.Options{PrettyTables: true})
}

func (h *Hermes) generateTemplate(email Email, tplt string) (string, error) {

	err := setDefaultEmailValues(&email)
	if err != nil {
		return "", err
	}

	// Generate the email from Golang template
	// Allow usage of simple function from sprig : https://github.com/Masterminds/sprig
	t, err := template.New("hermes").Funcs(sprig.FuncMap()).Funcs(templateFuncs).Parse(tplt)
	if err != nil {
		return "", err
	}
	var b bytes.Buffer
	t.Execute(&b, Template{*h, email})
	return b.String(), nil
}
<commit_msg>chore: remove wrong comment<commit_after>package hermes

import (
	"bytes"
	"github.com/Masterminds/sprig"
	"github.com/imdario/mergo"
	"github.com/jaytaylor/html2text"
	"github.com/russross/blackfriday"
	"html/template"
)

// Hermes is an instance of the hermes email generator
type Hermes struct {
	Theme         Theme
	TextDirection TextDirection
	Product       Product
}

// Theme is an interface to implement when creating a new theme
type Theme interface {
	Name() string              // The name of the theme
	HTMLTemplate() string      // The golang template for HTML emails
	PlainTextTemplate() string // The golang templte for plain text emails (can be basic HTML)
}

// TextDirection of the text in HTML email
type TextDirection string

var templateFuncs = template.FuncMap{
	"url": func(s string) template.URL {
		return template.URL(s)
	},
}

// TDLeftToRight is the text direction from left to right (default)
const TDLeftToRight TextDirection = "ltr"

// TDRightToLeft is the text direction from right to left
const TDRightToLeft TextDirection = "rtl"

// Product represents your company product (brand)
// Appears in header & footer of e-mails
type Product struct {
	Name        string
	Link        string // e.g. https://matcornic.github.io
	Logo        string // e.g. https://matcornic.github.io/img/logo.png
	Copyright   string // Copyright © 2017 Hermes. All rights reserved.
	TroubleText string // TroubleText is the sentence at the end of the email for users having trouble with the button (default to `If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.`)
}

// Email is the email containing a body
type Email struct {
	Body Body
}

// Markdown is a HTML template (a string) representing Markdown content
// https://en.wikipedia.org/wiki/Markdown
type Markdown template.HTML

// Body is the body of the email, containing all interesting data
type Body struct {
	Name         string   // The name of the contacted person
	Intros       []string // Intro sentences, first displayed in the email
	Dictionary   []Entry  // A list of key+value (useful for displaying parameters/settings/personal info)
	Table        Table    // Table is an table where you can put data (pricing grid, a bill, and so on)
	Actions      []Action // Actions are a list of actions that the user will be able to execute via a button click
	Outros       []string // Outro sentences, last displayed in the email
	Greeting     string   // Greeting for the contacted person (default to 'Hi')
	Signature    string   // Signature for the contacted person (default to 'Yours truly')
	Title        string   // Title replaces the greeting+name when set
	FreeMarkdown Markdown // Free markdown content that replaces all content other than header and footer
}

// ToHTML converts Markdown to HTML
func (c Markdown) ToHTML() template.HTML {
	return template.HTML(blackfriday.MarkdownCommon([]byte(string(c))))
}

// Entry is a simple entry of a map
// Allows using a slice of entries instead of a map
// Because Golang maps are not ordered
type Entry struct {
	Key   string
	Value string
}

// Table is an table where you can put data (pricing grid, a bill, and so on)
type Table struct {
	Data    [][]Entry // Contains data
	Columns Columns   // Contains meta-data for display purpose (width, alignement)
}

// Columns contains meta-data for the different columns
type Columns struct {
	CustomWidth      map[string]string
	CustomAlignement map[string]string
}

// Action is an action the user can do on the email (click on a button)
type Action struct {
	Instructions string
	Button       Button
}

// Button defines an action to launch
type Button struct {
	Color string
	Text  string
	Link  string
}

// Template is the struct given to Golang templating
// Root object in a template is this struct
type Template struct {
	Hermes Hermes
	Email  Email
}

func setDefaultEmailValues(e *Email) error {
	// Default values of an email
	defaultEmail := Email{
		Body: Body{
			Intros:     []string{},
			Dictionary: []Entry{},
			Outros:     []string{},
			Signature:  "Yours truly",
			Greeting:   "Hi",
		},
	}
	// Merge the given email with default one
	// Default one overrides all zero values
	return mergo.Merge(e, defaultEmail)
}

// default values of the engine
func setDefaultHermesValues(h *Hermes) error {
	defaultTextDirection := TDLeftToRight
	defaultHermes := Hermes{
		Theme:         new(Default),
		TextDirection: defaultTextDirection,
		Product: Product{
			Name:        "Hermes",
			Copyright:   "Copyright © 2017 Hermes. All rights reserved.",
			TroubleText: "If you’re having trouble with the button '{ACTION}', copy and paste the URL below into your web browser.",
		},
	}
	// Merge the given hermes engine configuration with default one
	// Default one overrides all zero values
	err := mergo.Merge(h, defaultHermes)
	if err != nil {
		return err
	}
	if h.TextDirection != TDLeftToRight && h.TextDirection != TDRightToLeft {
		h.TextDirection = defaultTextDirection
	}
	return nil
}

// GenerateHTML generates the email body from data to an HTML Reader
// This is for modern email clients
func (h *Hermes) GenerateHTML(email Email) (string, error) {
	err := setDefaultHermesValues(h)
	if err != nil {
		return "", err
	}
	return h.generateTemplate(email, h.Theme.HTMLTemplate())
}

// GeneratePlainText generates the email body from data
// This is for old email clients
func (h *Hermes) GeneratePlainText(email Email) (string, error) {
	err := setDefaultHermesValues(h)
	if err != nil {
		return "", err
	}
	template, err := h.generateTemplate(email, h.Theme.PlainTextTemplate())
	if err != nil {
		return "", err
	}
	return html2text.FromString(template, html2text.Options{PrettyTables: true})
}

func (h *Hermes) generateTemplate(email Email, tplt string) (string, error) {

	err := setDefaultEmailValues(&email)
	if err != nil {
		return "", err
	}

	// Generate the email from Golang template
	// Allow usage of simple function from sprig : https://github.com/Masterminds/sprig
	t, err := template.New("hermes").Funcs(sprig.FuncMap()).Funcs(templateFuncs).Parse(tplt)
	if err != nil {
		return "", err
	}
	var b bytes.Buffer
	t.Execute(&b, Template{*h, email})
	return b.String(), nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	"github.com/cloudfoundry-incubator/garden/warden"
	"github.com/codegangsta/cli"
	"github.com/concourse/atc/api/resources"
	"github.com/concourse/atc/api/routes"
	thijack "github.com/concourse/turbine/api/hijack"
	"github.com/kr/pty"
	"github.com/pkg/term"
	"github.com/tedsuo/rata"
)

func hijack(c *cli.Context) {
	atc := c.GlobalString("atcURL")
	insecure := c.GlobalBool("insecure")

	reqGenerator := rata.NewRequestGenerator(atc, routes.Routes)

	argv := c.Args()

	var path string
	var args []string

	switch len(argv) {
	case 0:
		path = "bash"
	case 1:
		path = argv[0]
	default:
		path = argv[0]
		args = argv[1:]
	}

	var ttySpec *warden.TTYSpec

	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		ttySpec = &warden.TTYSpec{
			WindowSize: &warden.WindowSize{
				Columns: cols,
				Rows:    rows,
			},
		}
	}

	spec := warden.ProcessSpec{
		Path: path,
		Args: args,
		Env:  []string{"TERM=" + os.Getenv("TERM")},

		Privileged: true,
		TTY:        ttySpec,
	}

	buildsReq, err := reqGenerator.CreateRequest(
		routes.ListBuilds,
		nil,
		nil,
	)
	if err != nil {
		log.Fatalln("failed to create request", err)
	}

	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
	}

	client := &http.Client{Transport: transport}

	buildsResp, err := client.Do(buildsReq)
	if err != nil {
		log.Fatalln("failed to get builds:", err)
	}

	if buildsResp.StatusCode != http.StatusOK {
		log.Println("bad response when getting builds:")
		buildsResp.Body.Close()
		buildsResp.Write(os.Stderr)
		os.Exit(1)
	}

	var builds []resources.Build
	err = json.NewDecoder(buildsResp.Body).Decode(&builds)
	if err != nil {
		log.Fatalln("failed to decode builds:", err)
	}

	if len(builds) == 0 {
		println("no builds to hijack")
		os.Exit(1)
	}

	build := builds[0]

	payload, err := json.Marshal(spec)
	if err != nil {
		log.Fatalln("failed to marshal build spec:", err)
	}

	hijackReq, err := reqGenerator.CreateRequest(
		routes.HijackBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		bytes.NewBuffer(payload),
	)
	if err != nil {
		log.Fatalln("failed to create hijack request:", err)
	}

	if hijackReq.URL.User != nil {
		hijackReq.Header.Add("Authorization", basicAuth(hijackReq.URL.User))
		hijackReq.URL.User = nil
	}

	conn, err := net.Dial("tcp", hijackReq.URL.Host)
	if err != nil {
		log.Fatalln("failed to dial hijack endpoint:", err)
	}

	clientConn := httputil.NewClientConn(conn, nil)

	resp, err := clientConn.Do(hijackReq)
	if err != nil {
		log.Fatalln("failed to hijack:", err)
	}

	if resp.StatusCode != http.StatusOK {
		log.Println("bad response when hijacking:")
		resp.Body.Close()
		resp.Write(os.Stderr)
		os.Exit(1)
	}

	cconn, cbr := clientConn.Hijack()

	term, err := term.Open(os.Stdin.Name())
	if err != nil {
		log.Fatalln("failed to open terminal:", err)
	}

	err = term.SetRaw()
	if err != nil {
		log.Fatalln("failed to set raw:", term)
	}

	defer term.Restore()

	encoder := gob.NewEncoder(cconn)

	resized := make(chan os.Signal, 10)
	signal.Notify(resized, syscall.SIGWINCH)

	go func() {
		for {
			<-resized
			sendSize(encoder)
		}
	}()

	go io.Copy(&stdinWriter{encoder}, term)

	io.Copy(os.Stdout, cbr)
}

func sendSize(enc *gob.Encoder) {
	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		enc.Encode(thijack.ProcessPayload{
			TTYSpec: &warden.TTYSpec{
				WindowSize: &warden.WindowSize{
					Columns: cols,
					Rows:    rows,
				},
			},
		})
	}
}

type stdinWriter struct {
	enc *gob.Encoder
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	err := w.enc.Encode(thijack.ProcessPayload{
		Stdin: d,
	})
	if err != nil {
		return 0, err
	}

	return len(d), nil
}

func basicAuth(user *url.Userinfo) string {
	username := user.Username()
	password, _ := user.Password()
	return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
}
<commit_msg>update garden api import<commit_after>package main

import (
	"bytes"
	"crypto/tls"
	"encoding/base64"
	"encoding/gob"
	"encoding/json"
	"io"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"os/signal"
	"strconv"
	"syscall"

	garden_api "github.com/cloudfoundry-incubator/garden/api"
	"github.com/codegangsta/cli"
	"github.com/concourse/atc/api/resources"
	"github.com/concourse/atc/api/routes"
	thijack "github.com/concourse/turbine/api/hijack"
	"github.com/kr/pty"
	"github.com/pkg/term"
	"github.com/tedsuo/rata"
)

func hijack(c *cli.Context) {
	atc := c.GlobalString("atcURL")
	insecure := c.GlobalBool("insecure")

	reqGenerator := rata.NewRequestGenerator(atc, routes.Routes)

	argv := c.Args()

	var path string
	var args []string

	switch len(argv) {
	case 0:
		path = "bash"
	case 1:
		path = argv[0]
	default:
		path = argv[0]
		args = argv[1:]
	}

	var ttySpec *garden_api.TTYSpec

	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		ttySpec = &garden_api.TTYSpec{
			WindowSize: &garden_api.WindowSize{
				Columns: cols,
				Rows:    rows,
			},
		}
	}

	spec := garden_api.ProcessSpec{
		Path: path,
		Args: args,
		Env:  []string{"TERM=" + os.Getenv("TERM")},

		Privileged: true,
		TTY:        ttySpec,
	}

	buildsReq, err := reqGenerator.CreateRequest(
		routes.ListBuilds,
		nil,
		nil,
	)
	if err != nil {
		log.Fatalln("failed to create request", err)
	}

	transport := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: insecure},
	}

	client := &http.Client{Transport: transport}

	buildsResp, err := client.Do(buildsReq)
	if err != nil {
		log.Fatalln("failed to get builds:", err)
	}

	if buildsResp.StatusCode != http.StatusOK {
		log.Println("bad response when getting builds:")
		buildsResp.Body.Close()
		buildsResp.Write(os.Stderr)
		os.Exit(1)
	}

	var builds []resources.Build
	err = json.NewDecoder(buildsResp.Body).Decode(&builds)
	if err != nil {
		log.Fatalln("failed to decode builds:", err)
	}

	if len(builds) == 0 {
		println("no builds to hijack")
		os.Exit(1)
	}

	build := builds[0]

	payload, err := json.Marshal(spec)
	if err != nil {
		log.Fatalln("failed to marshal build spec:", err)
	}

	hijackReq, err := reqGenerator.CreateRequest(
		routes.HijackBuild,
		rata.Params{"build_id": strconv.Itoa(build.ID)},
		bytes.NewBuffer(payload),
	)
	if err != nil {
		log.Fatalln("failed to create hijack request:", err)
	}

	if hijackReq.URL.User != nil {
		hijackReq.Header.Add("Authorization", basicAuth(hijackReq.URL.User))
		hijackReq.URL.User = nil
	}

	conn, err := net.Dial("tcp", hijackReq.URL.Host)
	if err != nil {
		log.Fatalln("failed to dial hijack endpoint:", err)
	}

	clientConn := httputil.NewClientConn(conn, nil)

	resp, err := clientConn.Do(hijackReq)
	if err != nil {
		log.Fatalln("failed to hijack:", err)
	}

	if resp.StatusCode != http.StatusOK {
		log.Println("bad response when hijacking:")
		resp.Body.Close()
		resp.Write(os.Stderr)
		os.Exit(1)
	}

	cconn, cbr := clientConn.Hijack()

	term, err := term.Open(os.Stdin.Name())
	if err != nil {
		log.Fatalln("failed to open terminal:", err)
	}

	err = term.SetRaw()
	if err != nil {
		log.Fatalln("failed to set raw:", term)
	}

	defer term.Restore()

	encoder := gob.NewEncoder(cconn)

	resized := make(chan os.Signal, 10)
	signal.Notify(resized, syscall.SIGWINCH)

	go func() {
		for {
			<-resized
			sendSize(encoder)
		}
	}()

	go io.Copy(&stdinWriter{encoder}, term)

	io.Copy(os.Stdout, cbr)
}

func sendSize(enc *gob.Encoder) {
	rows, cols, err := pty.Getsize(os.Stdin)
	if err == nil {
		enc.Encode(thijack.ProcessPayload{
			TTYSpec: &garden_api.TTYSpec{
				WindowSize: &garden_api.WindowSize{
					Columns: cols,
					Rows:    rows,
				},
			},
		})
	}
}

type stdinWriter struct {
	enc *gob.Encoder
}

func (w *stdinWriter) Write(d []byte) (int, error) {
	err := w.enc.Encode(thijack.ProcessPayload{
		Stdin: d,
	})
	if err != nil {
		return 0, err
	}

	return len(d), nil
}

func basicAuth(user *url.Userinfo) string {
	username := user.Username()
	password, _ := user.Password()
	return "Basic " + base64.StdEncoding.EncodeToString([]byte(username+":"+password))
}
<|endoftext|>


<commit_before>package ws

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"sync"
)

type Watcher interface {
	Watch(r *Res) error
	Close() error
}
type Op uint

const (
	Add Op = 1 << iota
	Change
	Remove
	_
	Create
	Modify
	Delete
	_
	WsMask Op = 0x0F
	FsMask Op = 0xF0
)

type Config struct {
	// CapHint hints the expected peak resource capacity.
	CapHint uint
	// Watcher returns a new watcher given workspace control.
	// Mounting a path results in a snapshot if no Watcher is configured.
	Watcher func(Controller) (Watcher, error)
	// Handler handles resource operation events if set.
	Handler func(Op, *Res)
	// Filter filters resources if set.
	// If filter returns true the resource is flagged with FlagIgnore but remains in the workspace.
	// Ignored directories are not read.
	Filter func(*Res) bool
}

func (c *Config) filldefaullts() {
	if c.Filter == nil {
		c.Filter = nilfilter
	}
	if c.Handler == nil {
		c.Handler = nilhandler
	}
}
func nilhandler(Op, *Res) {}
func nilfilter(*Res) bool { return false }

type Controller interface {
	Control(op Op, id Id, name string) error
}

type Ws struct {
	sync.RWMutex
	config  Config
	root    *Res
	all     map[Id]*Res
	watcher Watcher
}

// New creates a workspace configured with c.
func New(c Config) *Ws {
	c.filldefaullts()
	var name string
	if runtime.GOOS != "windows" {
		name = "/"
	}
	r := &Res{Id: NewId(name), Name: name}
	w := &Ws{config: c, root: r, all: make(map[Id]*Res, c.CapHint)}
	w.all[r.Id] = r
	return w
}

// Mount adds the directory tree at path to the workspace.
func (w *Ws) Mount(path string) (*Res, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("not a directory")
	}
	r, err := w.mount(path)
	if err != nil {
		return r, err
	}
	if w.config.Filter(r) {
		r.Flag |= FlagIgnore
		return r, nil
	}
	r.Lock()
	err = read(r, w.config.Filter)
	r.Unlock()
	if err != nil {
		return r, err
	}
	w.Lock()
	defer w.Unlock()
	w.addAllChildren(0, r)
	return r, nil
}
func (w *Ws) mount(path string) (*Res, error) {
	path = filepath.Clean(path)
	id := NewId(path)
	d, f := filepath.Split(path)
	w.Lock()
	defer w.Unlock()
	if w.watcher == nil && w.config.Watcher != nil {
		watcher, err := w.config.Watcher((*ctrl)(w))
		if err != nil {
			return nil, err
		}
		w.watcher = watcher
	}
	r, ok := w.all[id]
	if ok {
		return r, fmt.Errorf("duplicate")
	}
	r = &Res{Id: id, Name: f, Flag: FlagDir | FlagMount, Dir: &Dir{Path: path}}
	// add virtual parent
	r.Parent = w.logicalParent(d)
	w.all[id] = r
	w.config.Handler(Add, r)
	return r, nil
}

// Close closes the workspace.
// The workspace and all its resources are invalid afterwards.
func (w *Ws) Close() {
	w.Lock()
	defer w.Unlock()
	if w.watcher != nil {
		w.watcher.Close()
		w.watcher = nil
	}
	// scatter garbage
	for id, r := range w.all {
		r.Dir = nil
		delete(w.all, id)
	}
	w.all = nil
	w.root = nil
}
func (w *Ws) logicalParent(path string) *Res {
	parts := split(path)
	r := w.root
	for i := len(parts) - 1; i >= 0; i-- {
		if r.Dir == nil {
			r.Dir = &Dir{Path: r.Path()}
		} else if c := find(r.Children, parts[i]); c != nil {
			r = c
			continue
		}
		c := &Res{Name: parts[i], Parent: r, Flag: FlagDir | FlagLogical}
		p := c.Path()
		c.Dir = &Dir{Path: p}
		c.Id = NewId(p)
		r.Children = insert(r.Children, c)
		w.all[c.Id], r = c, c
	}
	return r
}
func split(path string) []string {
	parts := make([]string, 0, 8)
	dir, file := path, ""
	for dir != "" {
		if i := len(dir) - 1; dir[i] == os.PathSeparator {
			dir = dir[:i]
		}
		dir, file = filepath.Split(dir)
		if file != "" {
			parts = append(parts, file)
			continue
		}
		break
	}
	if dir != "" {
		return append(parts, dir)
	}
	return parts
}
func read(r *Res, filter func(*Res) bool) error {
	f, err := os.Open(r.Dir.Path)
	if err != nil {
		return err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return err
	}
	children := make([]*Res, 0, len(list))
	for _, fi := range list {
		children = append(children, newChild(r, fi))
	}
	sort.Sort(byTypeAndName(children))
	r.Children = children
	for _, c := range children {
		if filter(c) {
			c.Flag |= FlagIgnore
			continue
		}
		if c.Flag&FlagDir != 0 {
			if err := read(c, filter); err != nil {
				fmt.Println(err)
			}
		}
	}
	return nil
}
func (w *Ws) addAllChildren(fsop Op, r *Res) {
	for _, c := range r.Children {
		w.all[c.Id] = c
		w.config.Handler(fsop|Add, c)
		if c.Flag&(FlagDir|FlagIgnore) == FlagDir {
			w.addAllChildren(fsop, c)
		}
	}
	if w.watcher != nil {
		err := w.watcher.Watch(r)
		if err != nil {
			fmt.Println(err)
		}
	}
	w.config.Handler(fsop|Change, r)
}
<commit_msg>adds Res and Walk method to workspace<commit_after>package ws

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sort"
	"sync"
)

type Watcher interface {
	Watch(r *Res) error
	Close() error
}
type Op uint

const (
	Add Op = 1 << iota
	Change
	Remove
	_
	Create
	Modify
	Delete
	_
	WsMask Op = 0x0F
	FsMask Op = 0xF0
)

type Config struct {
	// CapHint hints the expected peak resource capacity.
	CapHint uint
	// Watcher returns a new watcher given workspace control.
	// Mounting a path results in a snapshot if no Watcher is configured.
	Watcher func(Controller) (Watcher, error)
	// Handler handles resource operation events if set.
	Handler func(Op, *Res)
	// Filter filters resources if set.
	// If filter returns true the resource is flagged with FlagIgnore but remains in the workspace.
	// Ignored directories are not read.
	Filter func(*Res) bool
}

func (c *Config) filldefaullts() {
	if c.Filter == nil {
		c.Filter = nilfilter
	}
	if c.Handler == nil {
		c.Handler = nilhandler
	}
}
func nilhandler(Op, *Res) {}
func nilfilter(*Res) bool { return false }

type Controller interface {
	Control(op Op, id Id, name string) error
}

type Ws struct {
	sync.RWMutex
	config  Config
	root    *Res
	all     map[Id]*Res
	watcher Watcher
}

// New creates a workspace configured with c.
func New(c Config) *Ws {
	c.filldefaullts()
	var name string
	if runtime.GOOS != "windows" {
		name = "/"
	}
	r := &Res{Id: NewId(name), Name: name}
	w := &Ws{config: c, root: r, all: make(map[Id]*Res, c.CapHint)}
	w.all[r.Id] = r
	return w
}

// Mount adds the directory tree at path to the workspace.
func (w *Ws) Mount(path string) (*Res, error) {
	fi, err := os.Stat(path)
	if err != nil {
		return nil, err
	}
	if !fi.IsDir() {
		return nil, fmt.Errorf("not a directory")
	}
	r, err := w.mount(path)
	if err != nil {
		return r, err
	}
	if w.config.Filter(r) {
		r.Flag |= FlagIgnore
		return r, nil
	}
	r.Lock()
	err = read(r, w.config.Filter)
	r.Unlock()
	if err != nil {
		return r, err
	}
	w.Lock()
	defer w.Unlock()
	w.addAllChildren(0, r)
	return r, nil
}
func (w *Ws) Res(id Id) *Res {
	w.RLock()
	defer w.RUnlock()
	return w.all[id]
}
func (w *Ws) Walk(l []*Res, f func(r *Res) error) error {
	w.RLock()
	defer w.RUnlock()
	return walk(l, f)
}
func (w *Ws) mount(path string) (*Res, error) {
	path = filepath.Clean(path)
	id := NewId(path)
	d, f := filepath.Split(path)
	w.Lock()
	defer w.Unlock()
	if w.watcher == nil && w.config.Watcher != nil {
		watcher, err := w.config.Watcher((*ctrl)(w))
		if err != nil {
			return nil, err
		}
		w.watcher = watcher
	}
	r, ok := w.all[id]
	if ok {
		return r, fmt.Errorf("duplicate")
	}
	r = &Res{Id: id, Name: f, Flag: FlagDir | FlagMount, Dir: &Dir{Path: path}}
	// add virtual parent
	r.Parent = w.logicalParent(d)
	w.all[id] = r
	w.config.Handler(Add, r)
	return r, nil
}

// Close closes the workspace.
// The workspace and all its resources are invalid afterwards.
func (w *Ws) Close() {
	w.Lock()
	defer w.Unlock()
	if w.watcher != nil {
		w.watcher.Close()
		w.watcher = nil
	}
	// scatter garbage
	for id, r := range w.all {
		r.Dir = nil
		delete(w.all, id)
	}
	w.all = nil
	w.root = nil
}
func (w *Ws) logicalParent(path string) *Res {
	parts := split(path)
	r := w.root
	for i := len(parts) - 1; i >= 0; i-- {
		if r.Dir == nil {
			r.Dir = &Dir{Path: r.Path()}
		} else if c := find(r.Children, parts[i]); c != nil {
			r = c
			continue
		}
		c := &Res{Name: parts[i], Parent: r, Flag: FlagDir | FlagLogical}
		p := c.Path()
		c.Dir = &Dir{Path: p}
		c.Id = NewId(p)
		r.Children = insert(r.Children, c)
		w.all[c.Id], r = c, c
	}
	return r
}
func split(path string) []string {
	parts := make([]string, 0, 8)
	dir, file := path, ""
	for dir != "" {
		if i := len(dir) - 1; dir[i] == os.PathSeparator {
			dir = dir[:i]
		}
		dir, file = filepath.Split(dir)
		if file != "" {
			parts = append(parts, file)
			continue
		}
		break
	}
	if dir != "" {
		return append(parts, dir)
	}
	return parts
}
func read(r *Res, filter func(*Res) bool) error {
	f, err := os.Open(r.Dir.Path)
	if err != nil {
		return err
	}
	list, err := f.Readdir(-1)
	f.Close()
	if err != nil {
		return err
	}
	children := make([]*Res, 0, len(list))
	for _, fi := range list {
		children = append(children, newChild(r, fi))
	}
	sort.Sort(byTypeAndName(children))
	r.Children = children
	for _, c := range children {
		if filter(c) {
			c.Flag |= FlagIgnore
			continue
		}
		if c.Flag&FlagDir != 0 {
			if err := read(c, filter); err != nil {
				fmt.Println(err)
			}
		}
	}
	return nil
}
func (w *Ws) addAllChildren(fsop Op, r *Res) {
	for _, c := range r.Children {
		w.all[c.Id] = c
		w.config.Handler(fsop|Add, c)
		if c.Flag&(FlagDir|FlagIgnore) == FlagDir {
			w.addAllChildren(fsop, c)
		}
	}
	if w.watcher != nil {
		err := w.watcher.Watch(r)
		if err != nil {
			fmt.Println(err)
		}
	}
	w.config.Handler(fsop|Change, r)
}
<|endoftext|>