<commit_before>package main

import (
	"bytes"
	"flag"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unicode"
	"unicode/utf8"
)

const (
	zenrpcComment       = "//zenrpc"
	zenrpcService       = "zenrpc.Service"
	contextTypeName     = "context.Context"
	generateFilePostfix = "_zenrpc.go"
)

func main() {
	args := flag.Args()
	var filename string
	if len(args) == 0 {
		filename = os.Getenv("GOFILE")
	} else {
		filename = args[0]
	}

	log.Printf("Entrypoint: %s", filename)
	structData, err := parse(filename)
	die(err)

	dir, err := filepath.Abs(filepath.Dir(filename))
	die(err)

	outputFileName := filepath.Join(dir, structData.PackageName+generateFilePostfix)
	file, err := os.Create(outputFileName)
	die(err)
	defer file.Close()

	output := new(bytes.Buffer)
	die(serviceTemplate.Execute(output, structData))

	source, err := format.Source(output.Bytes())
	die(err)

	_, err = file.Write(source)
	die(err)

	log.Printf("Generated: %s", outputFileName)
}

func die(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// StructData represents struct info for XXX_zenrpc.go file generation.
type StructData struct {
	PackageName string
	Services    map[string]Service
}

type Service struct {
	GenDecl *ast.GenDecl
	Name    string
	Methods map[string]*Method
}

type Method struct {
	FuncDecl      *ast.FuncType
	Name          string
	LowerCaseName string
	HasContext    bool
	Args          []Arg
}

type Arg struct {
	Name        string
	Type        string
	CapitalName string
	JsonName    string
}

func parse(filename string) (StructData, error) {
	data := StructData{}
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
	if err != nil {
		return data, err
	}
	//ast.Print(fset, f) // TODO remove

	data.PackageName = f.Name.Name

	// get structs for zenrpc
	data.Services = make(map[string]Service)
	for _, decl := range f.Decls {
		gdecl, ok := decl.(*ast.GenDecl)
		if !ok || gdecl.Tok != token.TYPE {
			continue
		}

		for _, spec := range gdecl.Specs {
			spec, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}

			if !ast.IsExported(spec.Name.Name) {
				continue
			}

			structType, ok := spec.Type.(*ast.StructType)
			if !ok {
				continue
			}

			// check that struct is our zenrpc struct
			if hasZenrpcComment(spec) || hasZenrpcService(structType) {
				data.Services[spec.Name.Name] = Service{gdecl, spec.Name.Name, make(map[string]*Method)}
			}
		}
	}

	// get funcs for structs
	for _, decl := range f.Decls {
		fdecl, ok := decl.(*ast.FuncDecl)
		if !ok || fdecl.Recv == nil {
			continue
		}

		method := Method{
			FuncDecl:      fdecl.Type,
			Name:          fdecl.Name.Name,
			LowerCaseName: strings.ToLower(fdecl.Name.Name),
			Args:          []Arg{},
		}

		for _, field := range fdecl.Recv.List {
			// field can be pointer or not
			var ident *ast.Ident
			if starExpr, ok := field.Type.(*ast.StarExpr); ok {
				if ident, ok = starExpr.X.(*ast.Ident); !ok {
					continue
				}
			} else if ident, ok = field.Type.(*ast.Ident); !ok {
				continue
			}

			// find service in our service list
			// method can be in several services
			if _, ok := data.Services[ident.Name]; !ok {
				continue
			}

			if !ast.IsExported(fdecl.Name.Name) {
				continue
			}

			data.Services[ident.Name].Methods[fdecl.Name.Name] = &method
		}

		// parse arguments
		if fdecl.Type.Params == nil || fdecl.Type.Params.List == nil {
			continue
		}

		for _, field := range fdecl.Type.Params.List {
			if field.Names == nil {
				continue
			}

			// parse type
			typeName := ""
			switch v := field.Type.(type) {
			case *ast.StarExpr:
				// pointer
				typeName += "*" // TODO not implemented
			case *ast.SelectorExpr:
				// struct
				x, ok := v.X.(*ast.Ident)
				if ok && v.Sel != nil { // TODO check it
					typeName = x.Name + "." + v.Sel.Name
				} else {
					continue
				}
			case *ast.Ident:
				// basic types
				typeName = v.Name
			default:
				continue
			}

			if typeName == contextTypeName {
				method.HasContext = true
				continue // not add context to arg list
			}

			// parse names
			for _, name := range field.Names {
				method.Args = append(method.Args, Arg{
					Name:        name.Name,
					Type:        typeName,
					CapitalName: strings.Title(name.Name),
					JsonName:    lowerFirst(name.Name),
				})
			}
		}
	}

	return data, nil
}

func hasZenrpcComment(spec *ast.TypeSpec) bool {
	if spec.Comment != nil && len(spec.Comment.List) > 0 && spec.Comment.List[0].Text == zenrpcComment {
		return true
	}

	return false
}

func hasZenrpcService(structType *ast.StructType) bool {
	if structType.Fields.List == nil {
		return false
	}

	for _, field := range structType.Fields.List {
		selectorExpr, ok := field.Type.(*ast.SelectorExpr)
		if !ok {
			continue
		}

		x, ok := selectorExpr.X.(*ast.Ident)
		if ok && selectorExpr.Sel != nil && x.Name+"."+selectorExpr.Sel.Name == zenrpcService {
			return true
		}
	}

	return false
}

func lowerFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}
<commit_msg>Change reading of arguments in zenrpc util<commit_after>package main

import (
	"bytes"
	"go/ast"
	"go/format"
	"go/parser"
	"go/token"
	"log"
	"os"
	"path/filepath"
	"strings"
	"unicode"
	"unicode/utf8"
)

const (
	zenrpcComment       = "//zenrpc"
	zenrpcService       = "zenrpc.Service"
	contextTypeName     = "context.Context"
	generateFilePostfix = "_zenrpc.go"
)

func main() {
	var filename string
	if len(os.Args) > 1 {
		filename = os.Args[len(os.Args)-1]
	} else {
		filename = os.Getenv("GOFILE")
	}

	log.Printf("Entrypoint: %s", filename)
	structData, err := parse(filename)
	die(err)

	dir, err := filepath.Abs(filepath.Dir(filename))
	die(err)

	outputFileName := filepath.Join(dir, structData.PackageName+generateFilePostfix)
	file, err := os.Create(outputFileName)
	die(err)
	defer file.Close()

	output := new(bytes.Buffer)
	die(serviceTemplate.Execute(output, structData))

	source, err := format.Source(output.Bytes())
	die(err)

	_, err = file.Write(source)
	die(err)

	log.Printf("Generated: %s", outputFileName)
}

func die(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// StructData represents struct info for XXX_zenrpc.go file generation.
type StructData struct {
	PackageName string
	Services    map[string]Service
}

type Service struct {
	GenDecl *ast.GenDecl
	Name    string
	Methods map[string]*Method
}

type Method struct {
	FuncDecl      *ast.FuncType
	Name          string
	LowerCaseName string
	HasContext    bool
	Args          []Arg
}

type Arg struct {
	Name        string
	Type        string
	CapitalName string
	JsonName    string
}

func parse(filename string) (StructData, error) {
	data := StructData{}
	fset := token.NewFileSet()
	f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments)
	if err != nil {
		return data, err
	}
	//ast.Print(fset, f) // TODO remove

	data.PackageName = f.Name.Name

	// get structs for zenrpc
	data.Services = make(map[string]Service)
	for _, decl := range f.Decls {
		gdecl, ok := decl.(*ast.GenDecl)
		if !ok || gdecl.Tok != token.TYPE {
			continue
		}

		for _, spec := range gdecl.Specs {
			spec, ok := spec.(*ast.TypeSpec)
			if !ok {
				continue
			}

			if !ast.IsExported(spec.Name.Name) {
				continue
			}

			structType, ok := spec.Type.(*ast.StructType)
			if !ok {
				continue
			}

			// check that struct is our zenrpc struct
			if hasZenrpcComment(spec) || hasZenrpcService(structType) {
				data.Services[spec.Name.Name] = Service{gdecl, spec.Name.Name, make(map[string]*Method)}
			}
		}
	}

	// get funcs for structs
	for _, decl := range f.Decls {
		fdecl, ok := decl.(*ast.FuncDecl)
		if !ok || fdecl.Recv == nil {
			continue
		}

		method := Method{
			FuncDecl:      fdecl.Type,
			Name:          fdecl.Name.Name,
			LowerCaseName: strings.ToLower(fdecl.Name.Name),
			Args:          []Arg{},
		}

		for _, field := range fdecl.Recv.List {
			// field can be pointer or not
			var ident *ast.Ident
			if starExpr, ok := field.Type.(*ast.StarExpr); ok {
				if ident, ok = starExpr.X.(*ast.Ident); !ok {
					continue
				}
			} else if ident, ok = field.Type.(*ast.Ident); !ok {
				continue
			}

			// find service in our service list
			// method can be in several services
			if _, ok := data.Services[ident.Name]; !ok {
				continue
			}

			if !ast.IsExported(fdecl.Name.Name) {
				continue
			}

			data.Services[ident.Name].Methods[fdecl.Name.Name] = &method
		}

		// parse arguments
		if fdecl.Type.Params == nil || fdecl.Type.Params.List == nil {
			continue
		}

		for _, field := range fdecl.Type.Params.List {
			if field.Names == nil {
				continue
			}

			// parse type
			typeName := ""
			switch v := field.Type.(type) {
			case *ast.StarExpr:
				// pointer
				typeName += "*" // TODO not implemented
			case *ast.SelectorExpr:
				// struct
				x, ok := v.X.(*ast.Ident)
				if ok && v.Sel != nil { // TODO check it
					typeName = x.Name + "." + v.Sel.Name
				} else {
					continue
				}
			case *ast.Ident:
				// basic types
				typeName = v.Name
			default:
				continue
			}

			if typeName == contextTypeName {
				method.HasContext = true
				continue // not add context to arg list
			}

			// parse names
			for _, name := range field.Names {
				method.Args = append(method.Args, Arg{
					Name:        name.Name,
					Type:        typeName,
					CapitalName: strings.Title(name.Name),
					JsonName:    lowerFirst(name.Name),
				})
			}
		}
	}

	return data, nil
}

func hasZenrpcComment(spec *ast.TypeSpec) bool {
	if spec.Comment != nil && len(spec.Comment.List) > 0 && spec.Comment.List[0].Text == zenrpcComment {
		return true
	}

	return false
}

func hasZenrpcService(structType *ast.StructType) bool {
	if structType.Fields.List == nil {
		return false
	}

	for _, field := range structType.Fields.List {
		selectorExpr, ok := field.Type.(*ast.SelectorExpr)
		if !ok {
			continue
		}

		x, ok := selectorExpr.X.(*ast.Ident)
		if ok && selectorExpr.Sel != nil && x.Name+"."+selectorExpr.Sel.Name == zenrpcService {
			return true
		}
	}

	return false
}

func lowerFirst(s string) string {
	if s == "" {
		return ""
	}
	r, n := utf8.DecodeRuneInString(s)
	return string(unicode.ToLower(r)) + s[n:]
}
<|endoftext|>


<commit_before>// Command line pagerank
// ---------------------
//
//    $ cat example.in
//    0   1
//    1   2
//    2   3   4   5
//    4   2
//    5   1
//
//    $ pagerank example.in 2> /dev/null | sort -k2,2 -nr
//    2   0.33477170103317816
//    1   0.20154082325712538
//    5   0.13963495553328562
//    4   0.13963495553328562
//    3   0.13963495553328562
//    0   0.0447826091098397
package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "math"
    "os"
    "strconv"
    "strings"
)

type Graph struct {
    Nodes []GraphNode
}

type GraphNode struct {
    OutboundNeighbors []int
}

// Taken from https://github.com/cosbynator/WikiRank
// Written by Thomas Dimson (tdimson@cs.stanford.edu)
func pageRankGraph(g Graph, walkProbability float64, convergenceCriteron float64) []float64 {
    beta, epsilon := walkProbability, convergenceCriteron
    log.Printf("Ranking with beta='%f', epsilon='%f'", beta, epsilon)
    n := len(g.Nodes)
    lastRank := make([]float64, n)
    thisRank := make([]float64, n)

    for iteration, lastChange := 1, math.MaxFloat64; lastChange > epsilon; iteration++ {
        thisRank, lastRank = lastRank, thisRank
        if iteration > 1 {
            // Clear out old values
            for i := 0; i < n; i++ {
                thisRank[i] = 0.0
            }
        } else {
            // Base case: everything uniform
            for i := 0; i < n; i++ {
                lastRank[i] = 1.0 / float64(n)
            }
        }

        // Single power iteration
        for i := 0; i < n; i++ {
            contribution := beta * lastRank[i] / float64(len(g.Nodes[i].OutboundNeighbors))
            for _, linkId := range g.Nodes[i].OutboundNeighbors {
                // fmt.Println(linkId)
                thisRank[linkId] += contribution
            }
        }

        // Reinsert leaked probability
        S := float64(0.0)
        for i := 0; i < n; i++ {
            S += thisRank[i]
        }
        leakedRank := (1.0 - S) / float64(n)
        lastChange = 0.0 // and calculate L1-difference too
        for i := 0; i < n; i++ {
            thisRank[i] += leakedRank
            lastChange += math.Abs(thisRank[i] - lastRank[i])
        }

        log.Printf("Pagerank iteration #%d delta=%f", iteration, lastChange)
    }

    return thisRank
}

func main() {

    log.SetOutput(os.Stderr)

    walkProbability := flag.Float64("w", 0.85, "walk probability")
    convergenceCriteron := flag.Float64("c", 0.0001, "convergence criteron")

    var PrintUsage = func() {
        fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] FILE\n", os.Args[0])
        fmt.Fprintf(os.Stderr, "File format: TSV (NODE OUTBOUND [OUTBOUND, ...])\n")
        flag.PrintDefaults()
    }

    flag.Parse()

    if flag.NArg() < 1 {
        PrintUsage()
        os.Exit(1)
    }

    fileName := flag.Args()[0]

    var in *os.File
    if fileName == "-" {
      in = os.Stdin
    } else {
      handle, err := os.Open(fileName)
      in = handle
      if err != nil {
          fmt.Printf("%s\n", err)
          os.Exit(1)
      }
      defer func() {
          if err := handle.Close(); err != nil {
              panic(err)
          }
      }()
    }
    scanner := bufio.NewScanner(in)
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    // intermediate store of outbound links, that are actually defined
    defined := make(map[int][]int)
    maximum := 0

    for scanner.Scan() {
        fields := strings.Fields(scanner.Text())
        head, _ := strconv.Atoi(fields[0])
        if head > maximum {
            maximum = head
        }
        rest := fields[1:]
        nodelist := make([]int, len(rest))
        for i, value := range rest {
            converted, _ := strconv.Atoi(value)
            if converted > maximum {
                maximum = converted
            }
            nodelist[i] = converted
        }
        defined[head] = nodelist
    }

    log.Printf("%d nodes\n", maximum)
    nodes := make([]GraphNode, maximum+1)

    for i, _ := range nodes {
        cached, present := defined[i]
        if present {
            nodes[i] = GraphNode{OutboundNeighbors: cached}
        } else {
            nodes[i] = GraphNode{}
        }
    }

    g := Graph{Nodes: nodes}
    rankVector := pageRankGraph(g, *walkProbability, *convergenceCriteron)

    for i, value := range rankVector {
        fmt.Printf("%d\t%s\n", i, strconv.FormatFloat(value, 'f', -1, 64))
    }
}
<commit_msg>Remove useless 'handle' variable<commit_after>// Command line pagerank
// ---------------------
//
//    $ cat example.in
//    0   1
//    1   2
//    2   3   4   5
//    4   2
//    5   1
//
//    $ pagerank example.in 2> /dev/null | sort -k2,2 -nr
//    2   0.33477170103317816
//    1   0.20154082325712538
//    5   0.13963495553328562
//    4   0.13963495553328562
//    3   0.13963495553328562
//    0   0.0447826091098397
package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "math"
    "os"
    "strconv"
    "strings"
)

type Graph struct {
    Nodes []GraphNode
}

type GraphNode struct {
    OutboundNeighbors []int
}

// Taken from https://github.com/cosbynator/WikiRank
// Written by Thomas Dimson (tdimson@cs.stanford.edu)
func pageRankGraph(g Graph, walkProbability float64, convergenceCriteron float64) []float64 {
    beta, epsilon := walkProbability, convergenceCriteron
    log.Printf("Ranking with beta='%f', epsilon='%f'", beta, epsilon)
    n := len(g.Nodes)
    lastRank := make([]float64, n)
    thisRank := make([]float64, n)

    for iteration, lastChange := 1, math.MaxFloat64; lastChange > epsilon; iteration++ {
        thisRank, lastRank = lastRank, thisRank
        if iteration > 1 {
            // Clear out old values
            for i := 0; i < n; i++ {
                thisRank[i] = 0.0
            }
        } else {
            // Base case: everything uniform
            for i := 0; i < n; i++ {
                lastRank[i] = 1.0 / float64(n)
            }
        }

        // Single power iteration
        for i := 0; i < n; i++ {
            contribution := beta * lastRank[i] / float64(len(g.Nodes[i].OutboundNeighbors))
            for _, linkId := range g.Nodes[i].OutboundNeighbors {
                // fmt.Println(linkId)
                thisRank[linkId] += contribution
            }
        }

        // Reinsert leaked probability
        S := float64(0.0)
        for i := 0; i < n; i++ {
            S += thisRank[i]
        }
        leakedRank := (1.0 - S) / float64(n)
        lastChange = 0.0 // and calculate L1-difference too
        for i := 0; i < n; i++ {
            thisRank[i] += leakedRank
            lastChange += math.Abs(thisRank[i] - lastRank[i])
        }

        log.Printf("Pagerank iteration #%d delta=%f", iteration, lastChange)
    }

    return thisRank
}

func main() {

    log.SetOutput(os.Stderr)

    walkProbability := flag.Float64("w", 0.85, "walk probability")
    convergenceCriteron := flag.Float64("c", 0.0001, "convergence criteron")

    var PrintUsage = func() {
        fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] FILE\n", os.Args[0])
        fmt.Fprintf(os.Stderr, "File format: TSV (NODE OUTBOUND [OUTBOUND, ...])\n")
        flag.PrintDefaults()
    }

    flag.Parse()

    if flag.NArg() < 1 {
        PrintUsage()
        os.Exit(1)
    }

    fileName := flag.Args()[0]

    var in *os.File
    if fileName == "-" {
      in = os.Stdin
    } else {
      in, err := os.Open(fileName)
      if err != nil {
          fmt.Printf("%s\n", err)
          os.Exit(1)
      }
      defer func() {
          if err := in.Close(); err != nil {
              panic(err)
          }
      }()
    }
    scanner := bufio.NewScanner(in)
    if err := scanner.Err(); err != nil {
        log.Fatal(err)
    }

    // intermediate store of outbound links, that are actually defined
    defined := make(map[int][]int)
    maximum := 0

    for scanner.Scan() {
        fields := strings.Fields(scanner.Text())
        head, _ := strconv.Atoi(fields[0])
        if head > maximum {
            maximum = head
        }
        rest := fields[1:]
        nodelist := make([]int, len(rest))
        for i, value := range rest {
            converted, _ := strconv.Atoi(value)
            if converted > maximum {
                maximum = converted
            }
            nodelist[i] = converted
        }
        defined[head] = nodelist
    }

    log.Printf("%d nodes\n", maximum)
    nodes := make([]GraphNode, maximum+1)

    for i, _ := range nodes {
        cached, present := defined[i]
        if present {
            nodes[i] = GraphNode{OutboundNeighbors: cached}
        } else {
            nodes[i] = GraphNode{}
        }
    }

    g := Graph{Nodes: nodes}
    rankVector := pageRankGraph(g, *walkProbability, *convergenceCriteron)

    for i, value := range rankVector {
        fmt.Printf("%d\t%s\n", i, strconv.FormatFloat(value, 'f', -1, 64))
    }
}
<|endoftext|>


<commit_before>package main

import (
	"container/heap"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/tj/go-dropbox"
	"golang.org/x/text/unicode/norm"
	"os"
	"path/filepath"
)

/* TODO
- Clean up output formatting
- Parallelize local/remote file listing
*/

// File stores the result of either Dropbox API or local file listing
type File struct {
	Path        string
	ContentHash string
}

// FileHeap is a list of Files sorted by path
type FileHeap []*File

func (h FileHeap) Len() int           { return len(h) }
func (h FileHeap) Less(i, j int) bool { return h[i].Path < h[j].Path }
func (h FileHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

// Push a File onto the heap
func (h *FileHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(*File))
}

// Pop a File off the heap
func (h *FileHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// PopOrNil pops a File off the heap or returns nil if there's nothing left
func (h *FileHeap) PopOrNil() *File {
	if h.Len() > 0 {
		return heap.Pop(h).(*File)
	}
	return nil
}

// ManifestComparison records the relative paths that differ between remote and
// local versions of a directory
type ManifestComparison struct {
	OnlyRemote      []string
	OnlyLocal       []string
	ContentMismatch []string
	Matches         int
	Misses          int
}

func main() {
	token := os.Getenv("DROPBOX_ACCESS_TOKEN")
	if token == "" {
		fmt.Fprintln(os.Stderr, "Missing Dropbox OAuth token! Please set the DROPBOX_ACCESS_TOKEN environment variable.")
		os.Exit(1)
	}

	var opts struct {
		Verbose          bool   `short:"v" long:"verbose" description:"Show verbose debug information"`
		RemoteRoot       string `short:"r" long:"remote" description:"Directory in Dropbox to verify" default:"/"`
		LocalRoot        string `short:"l" long:"local" description:"Local directory to compare to Dropbox contents" default:"."`
		CheckContentHash bool   `long:"check" description:"Check content hash of local files"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// Dropbox API uses empty string for root, but for figuring out relative
	// paths of the returned entries it's easier to use "/". Conversion is
	// handled before the API call.
	if opts.RemoteRoot == "" {
		opts.RemoteRoot = "/"
	}
	if opts.RemoteRoot[0] != '/' {
		opts.RemoteRoot = "/" + opts.RemoteRoot
	}

	localRoot, _ := filepath.Abs(opts.LocalRoot)

	dbxClient := dropbox.New(dropbox.NewConfig(token))

	fmt.Printf("Comparing Dropbox directory \"%v\" to local directory \"%v\"\n", opts.RemoteRoot, localRoot)
	if opts.CheckContentHash {
		fmt.Println("Checking content hashes.")
	}
	fmt.Println("")

	dropboxManifest, err := getDropboxManifest(dbxClient, opts.RemoteRoot)
	if err != nil {
		panic(err)
	}

	localManifest, err := getLocalManifest(localRoot, opts.CheckContentHash)
	if err != nil {
		panic(err)
	}

	manifestComparison := compareManifests(dropboxManifest, localManifest)

	printFileList(manifestComparison.OnlyRemote, "Files only in remote")
	printFileList(manifestComparison.OnlyLocal, "Files only in local")
	printFileList(manifestComparison.ContentMismatch, "Files whose contents don't match")
	total := manifestComparison.Matches + manifestComparison.Misses
	fmt.Println("SUMMARY:")
	fmt.Printf("Files matched: %d/%d\n", manifestComparison.Matches, total)
	fmt.Printf("Files not matched: %d/%d\n", manifestComparison.Misses, total)
}

func getDropboxManifest(dbxClient *dropbox.Client, rootPath string) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)
	cursor := ""
	keepGoing := true

	for keepGoing {
		var resp *dropbox.ListFolderOutput
		if cursor != "" {
			arg := &dropbox.ListFolderContinueInput{Cursor: cursor}
			resp, err = dbxClient.Files.ListFolderContinue(arg)
		} else {
			apiPath := rootPath
			if apiPath == "/" {
				apiPath = ""
			}
			arg := &dropbox.ListFolderInput{
				Path:             apiPath,
				Recursive:        true,
				IncludeMediaInfo: false,
				IncludeDeleted:   false,
			}
			resp, err = dbxClient.Files.ListFolder(arg)
		}
		if err != nil {
			return
		}
		for _, entry := range resp.Entries {
			if entry.Tag == "file" {

				var relPath string
				relPath, err = normalizePath(rootPath, entry.PathDisplay)
				if err != nil {
					return
				}
				heap.Push(manifest, &File{
					Path:        relPath,
					ContentHash: entry.ContentHash,
				})
			}
		}

		cursor = resp.Cursor
		keepGoing = resp.HasMore
	}

	return
}

func getLocalManifest(localRoot string, contentHash bool) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)

	err = filepath.Walk(localRoot, func(entryPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() {
			relPath, err := normalizePath(localRoot, entryPath)
			if err != nil {
				return err
			}

			hash := ""
			if contentHash {
				hash, err = dropbox.FileContentHash(entryPath)
				if err != nil {
					return err
				}
			}

			heap.Push(manifest, &File{
				Path:        relPath,
				ContentHash: hash,
			})
		}

		return nil
	})

	return
}

func normalizePath(root string, entryPath string) (string, error) {
	relPath, err := filepath.Rel(root, entryPath)
	if err != nil {
		return "", err
	}

	// Normalize Unicode combining characters
	relPath = norm.NFC.String(relPath)
	return relPath, nil
}

func compareManifests(remoteManifest, localManifest *FileHeap) *ManifestComparison {
	// 1. Pop a path off both remote and local manifests.
	// 2. While remote & local are both not nil:
	//    Compare remote & local:
	//    a. If local is nil or local > remote, this file is only in remote. Record and pop remote again.
	//    b. If remote is nil or local < remote, this file is only in local. Record and pop local again.
	//    c. If local == remote, check for content mismatch. Record if necessary and pop both again.
	comparison := &ManifestComparison{}
	local := localManifest.PopOrNil()
	remote := remoteManifest.PopOrNil()
	for local != nil || remote != nil {
		if local == nil || local.Path > remote.Path {
			comparison.OnlyRemote = append(comparison.OnlyRemote, remote.Path)
			comparison.Misses++
			remote = remoteManifest.PopOrNil()
		} else if remote == nil || local.Path < remote.Path {
			comparison.OnlyLocal = append(comparison.OnlyLocal, local.Path)
			comparison.Misses++
			local = localManifest.PopOrNil()
		} else if remote.Path == local.Path {
			if !compareFileContents(remote, local) {
				comparison.ContentMismatch = append(comparison.ContentMismatch, local.Path)
				comparison.Misses++
			} else {
				comparison.Matches++
			}
			local = localManifest.PopOrNil()
			remote = remoteManifest.PopOrNil()
		} else {
			// This should be unreachable...
			panic(fmt.Sprintf("Unexpected condition! Local: %v, remote: %v", local, remote))
		}
	}
	return comparison
}

func compareFileContents(remote, local *File) bool {
	if remote.ContentHash == "" || local.ContentHash == "" {
		// Missing content hash for one of the files, possibly intentionally,
		// so can't compare. Assume that presence of both is enough to
		// validate.
		return true
	}
	return remote.ContentHash == local.ContentHash
}

func printFileList(files []string, description string) {
	fmt.Printf("%s: %d\n\n", description, len(files))
	for _, path := range files {
		fmt.Println(path)
	}
	if len(files) > 0 {
		fmt.Print("\n\n")
	}
}
<commit_msg>Add clunky basic progress reporting<commit_after>package main

import (
	"container/heap"
	"fmt"
	"github.com/jessevdk/go-flags"
	"github.com/tj/go-dropbox"
	"golang.org/x/text/unicode/norm"
	"os"
	"path/filepath"
)

/* TODO
- Clean up output formatting
- Parallelize local/remote file listing
- Add progress printing - maybe collect progress from remote/local listing through channels
*/

// File stores the result of either Dropbox API or local file listing
type File struct {
	Path        string
	ContentHash string
}

// FileHeap is a list of Files sorted by path
type FileHeap []*File

func (h FileHeap) Len() int           { return len(h) }
func (h FileHeap) Less(i, j int) bool { return h[i].Path < h[j].Path }
func (h FileHeap) Swap(i, j int)      { h[i], h[j] = h[j], h[i] }

// Push a File onto the heap
func (h *FileHeap) Push(x interface{}) {
	// Push and Pop use pointer receivers because they modify the slice's length,
	// not just its contents.
	*h = append(*h, x.(*File))
}

// Pop a File off the heap
func (h *FileHeap) Pop() interface{} {
	old := *h
	n := len(old)
	x := old[n-1]
	*h = old[0 : n-1]
	return x
}

// PopOrNil pops a File off the heap or returns nil if there's nothing left
func (h *FileHeap) PopOrNil() *File {
	if h.Len() > 0 {
		return heap.Pop(h).(*File)
	}
	return nil
}

// ManifestComparison records the relative paths that differ between remote and
// local versions of a directory
type ManifestComparison struct {
	OnlyRemote      []string
	OnlyLocal       []string
	ContentMismatch []string
	Matches         int
	Misses          int
}

func main() {
	token := os.Getenv("DROPBOX_ACCESS_TOKEN")
	if token == "" {
		fmt.Fprintln(os.Stderr, "Missing Dropbox OAuth token! Please set the DROPBOX_ACCESS_TOKEN environment variable.")
		os.Exit(1)
	}

	var opts struct {
		Verbose          bool   `short:"v" long:"verbose" description:"Show verbose debug information"`
		RemoteRoot       string `short:"r" long:"remote" description:"Directory in Dropbox to verify" default:"/"`
		LocalRoot        string `short:"l" long:"local" description:"Local directory to compare to Dropbox contents" default:"."`
		CheckContentHash bool   `long:"check" description:"Check content hash of local files"`
	}

	_, err := flags.Parse(&opts)
	if err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	// Dropbox API uses empty string for root, but for figuring out relative
	// paths of the returned entries it's easier to use "/". Conversion is
	// handled before the API call.
	if opts.RemoteRoot == "" {
		opts.RemoteRoot = "/"
	}
	if opts.RemoteRoot[0] != '/' {
		opts.RemoteRoot = "/" + opts.RemoteRoot
	}

	localRoot, _ := filepath.Abs(opts.LocalRoot)

	dbxClient := dropbox.New(dropbox.NewConfig(token))

	fmt.Printf("Comparing Dropbox directory \"%v\" to local directory \"%v\"\n", opts.RemoteRoot, localRoot)
	if opts.CheckContentHash {
		fmt.Println("Checking content hashes.")
	}
	fmt.Println("")

	dropboxManifest, err := getDropboxManifest(dbxClient, opts.RemoteRoot)
	if err != nil {
		panic(err)
	}

	localManifest, err := getLocalManifest(localRoot, opts.CheckContentHash)
	if err != nil {
		panic(err)
	}

	manifestComparison := compareManifests(dropboxManifest, localManifest)

	printFileList(manifestComparison.OnlyRemote, "Files only in remote")
	printFileList(manifestComparison.OnlyLocal, "Files only in local")
	printFileList(manifestComparison.ContentMismatch, "Files whose contents don't match")
	total := manifestComparison.Matches + manifestComparison.Misses
	fmt.Println("SUMMARY:")
	fmt.Printf("Files matched: %d/%d\n", manifestComparison.Matches, total)
	fmt.Printf("Files not matched: %d/%d\n", manifestComparison.Misses, total)
}

func getDropboxManifest(dbxClient *dropbox.Client, rootPath string) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)
	cursor := ""
	keepGoing := true

	for keepGoing {
		var resp *dropbox.ListFolderOutput
		if cursor != "" {
			arg := &dropbox.ListFolderContinueInput{Cursor: cursor}
			resp, err = dbxClient.Files.ListFolderContinue(arg)
		} else {
			apiPath := rootPath
			if apiPath == "/" {
				apiPath = ""
			}
			arg := &dropbox.ListFolderInput{
				Path:             apiPath,
				Recursive:        true,
				IncludeMediaInfo: false,
				IncludeDeleted:   false,
			}
			resp, err = dbxClient.Files.ListFolder(arg)
		}
		if err != nil {
			return
		}
		for _, entry := range resp.Entries {
			if entry.Tag == "file" {

				var relPath string
				relPath, err = normalizePath(rootPath, entry.PathDisplay)
				if err != nil {
					return
				}
				heap.Push(manifest, &File{
					Path:        relPath,
					ContentHash: entry.ContentHash,
				})
			}
		}

		cursor = resp.Cursor
		keepGoing = resp.HasMore

		// DEBUG info
		fmt.Printf("%d dropbox files listed          \r", manifest.Len())
	}

	return
}

func getLocalManifest(localRoot string, contentHash bool) (manifest *FileHeap, err error) {
	manifest = &FileHeap{}
	heap.Init(manifest)

	err = filepath.Walk(localRoot, func(entryPath string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}

		if info.Mode().IsRegular() {
			relPath, err := normalizePath(localRoot, entryPath)
			if err != nil {
				return err
			}

			hash := ""
			if contentHash {
				hash, err = dropbox.FileContentHash(entryPath)
				if err != nil {
					return err
				}
			}

			heap.Push(manifest, &File{
				Path:        relPath,
				ContentHash: hash,
			})

			// DEBUG info
			fmt.Printf("%d local files listed          \r", manifest.Len())
		}

		return nil
	})

	return
}

func normalizePath(root string, entryPath string) (string, error) {
	relPath, err := filepath.Rel(root, entryPath)
	if err != nil {
		return "", err
	}

	// Normalize Unicode combining characters
	relPath = norm.NFC.String(relPath)
	return relPath, nil
}

func compareManifests(remoteManifest, localManifest *FileHeap) *ManifestComparison {
	// 1. Pop a path off both remote and local manifests.
	// 2. While remote & local are both not nil:
	//    Compare remote & local:
	//    a. If local is nil or local > remote, this file is only in remote. Record and pop remote again.
	//    b. If remote is nil or local < remote, this file is only in local. Record and pop local again.
	//    c. If local == remote, check for content mismatch. Record if necessary and pop both again.
	comparison := &ManifestComparison{}
	local := localManifest.PopOrNil()
	remote := remoteManifest.PopOrNil()
	for local != nil || remote != nil {
		if local == nil || local.Path > remote.Path {
			comparison.OnlyRemote = append(comparison.OnlyRemote, remote.Path)
			comparison.Misses++
			remote = remoteManifest.PopOrNil()
		} else if remote == nil || local.Path < remote.Path {
			comparison.OnlyLocal = append(comparison.OnlyLocal, local.Path)
			comparison.Misses++
			local = localManifest.PopOrNil()
		} else if remote.Path == local.Path {
			if !compareFileContents(remote, local) {
				comparison.ContentMismatch = append(comparison.ContentMismatch, local.Path)
				comparison.Misses++
			} else {
				comparison.Matches++
			}
			local = localManifest.PopOrNil()
			remote = remoteManifest.PopOrNil()
		} else {
			// This should be unreachable...
			panic(fmt.Sprintf("Unexpected condition! Local: %v, remote: %v", local, remote))
		}
	}
	return comparison
}

func compareFileContents(remote, local *File) bool {
	if remote.ContentHash == "" || local.ContentHash == "" {
		// Missing content hash for one of the files, possibly intentionally,
		// so can't compare. Assume that presence of both is enough to
		// validate.
		return true
	}
	return remote.ContentHash == local.ContentHash
}

func printFileList(files []string, description string) {
	fmt.Printf("%s: %d\n\n", description, len(files))
	for _, path := range files {
		fmt.Println(path)
	}
	if len(files) > 0 {
		fmt.Print("\n\n")
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Andrea Masi. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE.txt file.

// Package parallel simplifies use of parallel
// (as not concurrent) workers that run on their own core.
// Number of workers is adjusted at runtime in base of numbers of cores.
// This paradigm is particulary uselfull in presence of heavy,
// indipended tasks.
//
// Go's high level functionalities are used to implement parallelism
// with concurrency (channel, no locks).
package parallel

// NOTE Usefull for debugging on Linux: pidstat -tu  -C '<pid-name>'  1

import (
	"errors"
	"os"
	"os/signal"
	"runtime"

	"github.com/eraclitux/trace"
)

// Tasker interface models an heavy task that have to be
// executed from a worker.
type Tasker interface {
	Execute()
}

// ErrTasksNotCompleted says that not all tasks where completed.
var ErrTasksNotCompleted = errors.New("SIGINT received, not all tasks have been completed")

var workersNumber int = runtime.NumCPU()
var jobsQueue chan Tasker
var doneChan chan struct{}

func populateQueue(jobsQueue chan<- Tasker, jobs []Tasker, prematureEnd chan<- struct{}) {
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	for _, t := range jobs {
		select {
		case <-signalChan:
			// Abort jobs queue evaluation.
			// Taskers already sended will be finished
			// and an error will be returned.
			trace.Traceln("parallel: received SIGINT")
			prematureEnd <- struct{}{}
			close(jobsQueue)
			return
		default:
			jobsQueue <- t
		}
	}
	trace.Traceln("close jobsQueue")
	close(jobsQueue)
}

// parallelizeWorkers creates a goroutine for every worker
// which will call Execute() method.
func parallelizeWorkers(jobsQueue <-chan Tasker, doneChan chan<- struct{}) {
	for i := 0; i < workersNumber; i++ {
		go evaluateQueue(jobsQueue, doneChan)
	}
}

// evaluateQueue does jobs in sequence on its own goroutine
// on a single core.
func evaluateQueue(jobsQueue <-chan Tasker, doneChan chan<- struct{}) {
	for j := range jobsQueue {
		j.Execute()
	}
	doneChan <- struct{}{}
}

func init() {
	// Use all cores.
	// FIXME default in 1.5?
	runtime.GOMAXPROCS(WorkersNumber)
	// TODO Timeout a public accessible time out setting.
}

// RunBlocking starts the goroutines that will execute Taskers.
// It is intended to run blocking in the main goroutine.
// []T does not convert to []Tasker implicitly even is T implements
// Tasker. We need to iterate on []Tasker making an explicit cast.
// http://golang.org/doc/faq#convert_slice_of_interface
func RunBlocking(jobs []Tasker) (err error) {
	trace.Traceln("WorkersNumber:", workersNumber)
	prematureEnd := make(chan struct{})
	jobsQueue := make(chan Tasker, workersNumber)
	doneChan := make(chan struct{}, workersNumber)
	var totalDone int
	go populateQueue(jobsQueue, jobs, prematureEnd)
	go parallelizeWorkers(jobsQueue, doneChan)
	for {
		select {
		// TODO case timeout, returns error.
		case <-doneChan:
			totalDone++
		case <-prematureEnd:
			err = ErrTasksNotCompleted
		}
		if totalDone == workersNumber {
			// We can assume that jobsQueue is closed and
			// that no goroutine is operating on []Tasker.
			break
		}
	}
	return
}

// TODO has a non blocking version a sense (API semplification, performance etc.)? Es:
// When using RunBlocking one must wait that all tasks are done
// and put separate results togherther in the end. RunNonBlocking avoids that.
// func RunNonBlocking(jobs <-chan Tasker) (results chan<- Resulter) {
//code
//code
// Comunicate to callers that we are done.
// close(results)
//}
<commit_msg>Remove GOMAXPROCS setup<commit_after>// Copyright (c) 2015 Andrea Masi. All rights reserved.
// Use of this source code is governed by a MIT license
// that can be found in the LICENSE.txt file.

// Package parallel simplifies use of parallel
// (as not concurrent) workers that run on their own core.
// Number of workers is adjusted at runtime in base of numbers of cores.
// This paradigm is particulary uselfull in presence of heavy,
// indipended tasks.
//
// Go's high level functionalities are used to implement parallelism
// with concurrency (channel, no locks).
package parallel

// NOTE Usefull for debugging on Linux: pidstat -tu  -C '<pid-name>'  1

import (
	"errors"
	"os"
	"os/signal"
	"runtime"

	"github.com/eraclitux/trace"
)

// Tasker interface models an heavy task that have to be
// executed from a worker.
type Tasker interface {
	Execute()
}

// ErrTasksNotCompleted says that not all tasks where completed.
var ErrTasksNotCompleted = errors.New("SIGINT received, not all tasks have been completed")

var workersNumber int = runtime.NumCPU()
var jobsQueue chan Tasker
var doneChan chan struct{}

func populateQueue(jobsQueue chan<- Tasker, jobs []Tasker, prematureEnd chan<- struct{}) {
	signalChan := make(chan os.Signal, 1)
	signal.Notify(signalChan, os.Interrupt)
	for _, t := range jobs {
		select {
		case <-signalChan:
			// Abort jobs queue evaluation.
			// Taskers already sended will be finished
			// and an error will be returned.
			trace.Traceln("parallel: received SIGINT")
			prematureEnd <- struct{}{}
			close(jobsQueue)
			return
		default:
			jobsQueue <- t
		}
	}
	trace.Traceln("close jobsQueue")
	close(jobsQueue)
}

// parallelizeWorkers creates a goroutine for every worker
// which will call Execute() method.
func parallelizeWorkers(jobsQueue <-chan Tasker, doneChan chan<- struct{}) {
	for i := 0; i < workersNumber; i++ {
		go evaluateQueue(jobsQueue, doneChan)
	}
}

// evaluateQueue does jobs in sequence on its own goroutine
// on a single core.
func evaluateQueue(jobsQueue <-chan Tasker, doneChan chan<- struct{}) {
	for j := range jobsQueue {
		j.Execute()
	}
	doneChan <- struct{}{}
}

// RunBlocking starts the goroutines that will execute Taskers.
// It is intended to run blocking in the main goroutine.
// []T does not convert to []Tasker implicitly even is T implements
// Tasker. We need to iterate on []Tasker making an explicit cast.
// http://golang.org/doc/faq#convert_slice_of_interface
func RunBlocking(jobs []Tasker) (err error) {
	trace.Traceln("WorkersNumber:", workersNumber)
	prematureEnd := make(chan struct{})
	jobsQueue := make(chan Tasker, workersNumber)
	doneChan := make(chan struct{}, workersNumber)
	var totalDone int
	go populateQueue(jobsQueue, jobs, prematureEnd)
	go parallelizeWorkers(jobsQueue, doneChan)
	for {
		select {
		// TODO case timeout, returns error.
		case <-doneChan:
			totalDone++
		case <-prematureEnd:
			err = ErrTasksNotCompleted
		}
		if totalDone == workersNumber {
			// We can assume that jobsQueue is closed and
			// that no goroutine is operating on []Tasker.
			break
		}
	}
	return
}

// TODO has a non blocking version a sense (API semplification, performance etc.)? Es:
// When using RunBlocking one must wait that all tasks are done
// and put separate results togherther in the end. RunNonBlocking avoids that.
// func RunNonBlocking(jobs <-chan Tasker) (results chan<- Resulter) {
//code
//code
// Comunicate to callers that we are done.
// close(results)
//}
<|endoftext|>


<commit_before>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"flag"
	"os"

	"github.com/keybase/kbfs/env"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/libpages"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	fProd          bool
	fDiskCertCache bool
)

func init() {
	flag.BoolVar(&fProd, "prod", false, "disable development mode")
	flag.BoolVar(&fDiskCertCache, "use-disk-cert-cache", false, "cache cert on disk")
}

func main() {
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	var logger *zap.Logger
	var err error
	if fProd {
		logger, err = zap.NewProduction()
	} else {
		loggerConfig := zap.NewDevelopmentConfig()
		loggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
		loggerConfig.DisableStacktrace = true
		logger, err = loggerConfig.Build()
	}
	if err != nil {
		panic(err)
	}

	// Hack to make libkbfs.Init connect to prod {md,b}server all the time.
	os.Setenv("KEYBASE_RUN_MODE", "prod")

	kbCtx := env.NewContext()
	params := libkbfs.DefaultInitParams(kbCtx)
	params.EnableJournal = false
	params.Debug = true
	kbfsLog, err := libkbfs.InitLog(params, kbCtx)
	if err != nil {
		logger.Panic("libkbfs.InitLog", zap.Error(err))
	}
	kbConfig, err := libkbfs.Init(
		ctx, kbCtx, params, nil, cancel, kbfsLog)
	if err != nil {
		logger.Panic("libkbfs.Init", zap.Error(err))
	}

	serverConfig := libpages.ServerConfig{
		// Connect to staging Let's Encrypt server while we are testing since
		// the rate-limit is way higher.
		UseStaging:       true,
		Logger:           logger,
		UseDiskCertCache: fDiskCertCache,
	}
	libpages.ListenAndServe(ctx, serverConfig, kbConfig)
}
<commit_msg>enable prod with CLI flag<commit_after>// Copyright 2017 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package main

import (
	"context"
	"flag"
	"os"

	"github.com/keybase/kbfs/env"
	"github.com/keybase/kbfs/libkbfs"
	"github.com/keybase/kbfs/libpages"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
)

var (
	fProd          bool
	fDiskCertCache bool
)

func init() {
	flag.BoolVar(&fProd, "prod", false, "disable development mode")
	flag.BoolVar(&fDiskCertCache, "use-disk-cert-cache", false, "cache cert on disk")
}

func main() {
	flag.Parse()

	ctx, cancel := context.WithCancel(context.Background())
	var logger *zap.Logger
	var err error
	if fProd {
		logger, err = zap.NewProduction()
	} else {
		loggerConfig := zap.NewDevelopmentConfig()
		loggerConfig.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
		loggerConfig.DisableStacktrace = true
		logger, err = loggerConfig.Build()
	}
	if err != nil {
		panic(err)
	}

	// Hack to make libkbfs.Init connect to prod {md,b}server all the time.
	os.Setenv("KEYBASE_RUN_MODE", "prod")

	kbCtx := env.NewContext()
	params := libkbfs.DefaultInitParams(kbCtx)
	params.EnableJournal = false
	params.Debug = true
	kbfsLog, err := libkbfs.InitLog(params, kbCtx)
	if err != nil {
		logger.Panic("libkbfs.InitLog", zap.Error(err))
	}
	kbConfig, err := libkbfs.Init(
		ctx, kbCtx, params, nil, cancel, kbfsLog)
	if err != nil {
		logger.Panic("libkbfs.Init", zap.Error(err))
	}

	serverConfig := libpages.ServerConfig{
		// Connect to staging Let's Encrypt server while we are testing since
		// the rate-limit is way higher.
		UseStaging:       !fProd,
		Logger:           logger,
		UseDiskCertCache: fDiskCertCache,
	}
	libpages.ListenAndServe(ctx, serverConfig, kbConfig)
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package key

import (
	"encoding/base64"
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
	"unicode/utf8"
)

type keyStringer interface {
	KeyString() string
}

// StringKey generates a String suitable to be used as a key in a
// string index by calling k.StringKey, if available, otherwise it
// calls k.String. StringKey returns the same results as
// StringifyInterface(k.Key()) and should be preferred over
// StringifyInterface.
func StringKey(k Key) string {
	if ks, ok := k.(keyStringer); ok {
		return ks.KeyString()
	}
	return k.String()
}

// StringifyInterface transforms an arbitrary interface into a string
// representation suitable to be used as a key, such as in a JSON
// object, or as a path element.
//
// Deprecated: Use StringKey instead.
func StringifyInterface(key interface{}) (string, error) {
	var str string
	switch key := key.(type) {
	case nil:
		return "<nil>", nil
	case bool:
		str = strconv.FormatBool(key)
	case uint8:
		str = strconv.FormatUint(uint64(key), 10)
	case uint16:
		str = strconv.FormatUint(uint64(key), 10)
	case uint32:
		str = strconv.FormatUint(uint64(key), 10)
	case uint64:
		str = strconv.FormatUint(key, 10)
	case int8:
		str = strconv.FormatInt(int64(key), 10)
	case int16:
		str = strconv.FormatInt(int64(key), 10)
	case int32:
		str = strconv.FormatInt(int64(key), 10)
	case int64:
		str = strconv.FormatInt(key, 10)
	case float32:
		str = "f" + strconv.FormatInt(int64(math.Float32bits(key)), 10)
	case float64:
		str = "f" + strconv.FormatInt(int64(math.Float64bits(key)), 10)
	case string:
		str = escape(key)
	case map[string]interface{}:
		keys := SortedKeys(key)
		for i, k := range keys {
			v := key[k]
			keys[i] = stringify(v)
		}
		str = strings.Join(keys, "_")
	case *map[string]interface{}:
		return StringifyInterface(*key)
	case Map:
		return key.KeyString(), nil
	case *Map:
		return key.KeyString(), nil
	case map[Key]interface{}:
		m := make(map[string]interface{}, len(key))
		for k, v := range key {
			m[k.String()] = v
		}
		keys := SortedKeys(m)
		for i, k := range keys {
			keys[i] = escape(k) + "=" + stringify(m[k])
		}
		str = strings.Join(keys, "_")
	case *map[Key]interface{}:
		return StringifyInterface(*key)
	case []interface{}:
		elements := make([]string, len(key))
		for i, element := range key {
			elements[i] = stringify(element)
		}
		str = strings.Join(elements, ",")
	case Pointer:
		return "{" + key.Pointer().String() + "}", nil
	case Path:
		return "[" + key.String() + "]", nil
	case keyStringer:
		return key.KeyString(), nil
	case fmt.Stringer:
		return key.String(), nil
	default:
		panic(fmt.Errorf("Unable to stringify type %T: %#v", key, key))
	}

	return str, nil
}

// escape checks if the string is a valid utf-8 string.
// If it is, it will return the string as is.
// If it is not, it will return the base64 representation of the byte array string
func escape(str string) string {
	if utf8.ValidString(str) {
		return str
	}
	return base64.StdEncoding.EncodeToString([]byte(str))
}

func stringify(key interface{}) string {
	s, err := StringifyInterface(key)
	if err != nil {
		panic(err)
	}
	return s
}

// StringifyCollection safely returns a string representation of a
// map[Key]interface{} that is similar in form to the standard
// stringification of a map, "map[k1:v1, k2:v2]". This differs from
// StringifyInterface's handling of a map which emits a string to be
// used as key in contexts such as JSON objects.
func StringifyCollection(m map[Key]interface{}) string {
	type kv struct {
		key string
		val string
	}
	var length int
	kvs := make([]kv, 0, len(m))
	for k, v := range m {
		element := kv{
			key: stringifyCollectionHelper(k.Key()),
			val: stringifyCollectionHelper(v),
		}
		kvs = append(kvs, element)
		length += len(element.key) + len(element.val)
	}
	sort.Slice(kvs, func(i, j int) bool {
		return kvs[i].key < kvs[j].key
	})
	var buf strings.Builder
	buf.Grow(length + len("map[]") + 2*len(kvs) /* room for seperators: ", :" */)
	buf.WriteString("map[")
	for i, kv := range kvs {
		if i > 0 {
			buf.WriteString(" ")
		}
		buf.WriteString(kv.key)
		buf.WriteByte(':')
		buf.WriteString(kv.val)
	}
	buf.WriteByte(']')
	return buf.String()
}

// stringifyCollectionHelper is similar to StringifyInterface, but
// optimizes for human readability instead of making a unique string
// key suitable for JSON.
func stringifyCollectionHelper(val interface{}) string {
	switch val := val.(type) {
	case string:
		return escape(val)
	case map[string]interface{}:
		keys := SortedKeys(val)
		for i, k := range keys {
			v := val[k]
			s := stringifyCollectionHelper(v)
			keys[i] = k + ":" + s
		}
		return "map[" + strings.Join(keys, " ") + "]"
	case map[Key]interface{}:
		return StringifyCollection(val)
	case []interface{}:
		elements := make([]string, len(val))
		for i, element := range val {
			elements[i] = stringifyCollectionHelper(element)
		}
		return strings.Join(elements, ",")
	case Pointer:
		return "{" + val.Pointer().String() + "}"
	case Path:
		return "[" + val.String() + "]"
	case Key:
		return stringifyCollectionHelper(val.Key())
	}

	return fmt.Sprint(val)
}
<commit_msg>key: Move StringifyInterface implementation into stringify<commit_after>// Copyright (c) 2015 Arista Networks, Inc.
// Use of this source code is governed by the Apache License 2.0
// that can be found in the COPYING file.

package key

import (
	"encoding/base64"
	"fmt"
	"math"
	"sort"
	"strconv"
	"strings"
	"unicode/utf8"
)

type keyStringer interface {
	KeyString() string
}

// StringKey generates a String suitable to be used as a key in a
// string index by calling k.StringKey, if available, otherwise it
// calls k.String. StringKey returns the same results as
// StringifyInterface(k.Key()) and should be preferred over
// StringifyInterface.
func StringKey(k Key) string {
	if ks, ok := k.(keyStringer); ok {
		return ks.KeyString()
	}
	return k.String()
}

// StringifyInterface transforms an arbitrary interface into a string
// representation suitable to be used as a key, such as in a JSON
// object, or as a path element.
//
// Deprecated: Use StringKey instead.
func StringifyInterface(key interface{}) (string, error) {
	return stringify(key), nil
}

// escape checks if the string is a valid utf-8 string.
// If it is, it will return the string as is.
// If it is not, it will return the base64 representation of the byte array string
func escape(str string) string {
	if utf8.ValidString(str) {
		return str
	}
	return base64.StdEncoding.EncodeToString([]byte(str))
}

func stringify(key interface{}) string {
	switch key := key.(type) {
	case nil:
		return "<nil>"
	case bool:
		return strconv.FormatBool(key)
	case uint8:
		return strconv.FormatUint(uint64(key), 10)
	case uint16:
		return strconv.FormatUint(uint64(key), 10)
	case uint32:
		return strconv.FormatUint(uint64(key), 10)
	case uint64:
		return strconv.FormatUint(key, 10)
	case int8:
		return strconv.FormatInt(int64(key), 10)
	case int16:
		return strconv.FormatInt(int64(key), 10)
	case int32:
		return strconv.FormatInt(int64(key), 10)
	case int64:
		return strconv.FormatInt(key, 10)
	case float32:
		return "f" + strconv.FormatInt(int64(math.Float32bits(key)), 10)
	case float64:
		return "f" + strconv.FormatInt(int64(math.Float64bits(key)), 10)
	case string:
		return escape(key)
	case map[string]interface{}:
		keys := SortedKeys(key)
		for i, k := range keys {
			v := key[k]
			keys[i] = stringify(v)
		}
		return strings.Join(keys, "_")
	case *map[string]interface{}:
		return stringify(*key)
	case Map:
		return key.KeyString()
	case *Map:
		return key.KeyString()
	case map[Key]interface{}:
		m := make(map[string]interface{}, len(key))
		for k, v := range key {
			m[k.String()] = v
		}
		keys := SortedKeys(m)
		for i, k := range keys {
			keys[i] = escape(k) + "=" + stringify(m[k])
		}
		return strings.Join(keys, "_")
	case *map[Key]interface{}:
		return stringify(*key)
	case []interface{}:
		elements := make([]string, len(key))
		for i, element := range key {
			elements[i] = stringify(element)
		}
		return strings.Join(elements, ",")
	case Pointer:
		return "{" + key.Pointer().String() + "}"
	case Path:
		return "[" + key.String() + "]"
	case keyStringer:
		return key.KeyString()
	case fmt.Stringer:
		return key.String()
	default:
		panic(fmt.Errorf("Unable to stringify type %T: %#v", key, key))
	}
}

// StringifyCollection safely returns a string representation of a
// map[Key]interface{} that is similar in form to the standard
// stringification of a map, "map[k1:v1, k2:v2]". This differs from
// StringifyInterface's handling of a map which emits a string to be
// used as key in contexts such as JSON objects.
func StringifyCollection(m map[Key]interface{}) string {
	type kv struct {
		key string
		val string
	}
	var length int
	kvs := make([]kv, 0, len(m))
	for k, v := range m {
		element := kv{
			key: stringifyCollectionHelper(k.Key()),
			val: stringifyCollectionHelper(v),
		}
		kvs = append(kvs, element)
		length += len(element.key) + len(element.val)
	}
	sort.Slice(kvs, func(i, j int) bool {
		return kvs[i].key < kvs[j].key
	})
	var buf strings.Builder
	buf.Grow(length + len("map[]") + 2*len(kvs) /* room for seperators: ", :" */)
	buf.WriteString("map[")
	for i, kv := range kvs {
		if i > 0 {
			buf.WriteString(" ")
		}
		buf.WriteString(kv.key)
		buf.WriteByte(':')
		buf.WriteString(kv.val)
	}
	buf.WriteByte(']')
	return buf.String()
}

// stringifyCollectionHelper is similar to StringifyInterface, but
// optimizes for human readability instead of making a unique string
// key suitable for JSON.
func stringifyCollectionHelper(val interface{}) string {
	switch val := val.(type) {
	case string:
		return escape(val)
	case map[string]interface{}:
		keys := SortedKeys(val)
		for i, k := range keys {
			v := val[k]
			s := stringifyCollectionHelper(v)
			keys[i] = k + ":" + s
		}
		return "map[" + strings.Join(keys, " ") + "]"
	case map[Key]interface{}:
		return StringifyCollection(val)
	case []interface{}:
		elements := make([]string, len(val))
		for i, element := range val {
			elements[i] = stringifyCollectionHelper(element)
		}
		return strings.Join(elements, ",")
	case Pointer:
		return "{" + val.Pointer().String() + "}"
	case Path:
		return "[" + val.String() + "]"
	case Key:
		return stringifyCollectionHelper(val.Key())
	}

	return fmt.Sprint(val)
}
<|endoftext|>


<commit_before>package ts

import (
	"testing"
)

// validator is a function type used by tests to verify that a given storage
// backend is active and working correctly. Normally, functions implementing
// this interface should perform a simple write/read cycle using the backend to
// verify its operation.
type validator func(Store) error

// newStore is a common type implemented by the constructors for all provided
// storage backends. It's useful for internal testing, but not particularly for
// use by clients, so it's not exported.
type newStore func() (Store, error)

// testStore is a common function used for testing all available storage
// backends, it provides common setup and teardown functionality and runs tests
// common to all backends.
func testStore(t *testing.T, ns newStore, v validator) {

	store, err := ns()
	if err != nil {
		t.Error(err.Error())
		return
	}
	defer store.Shutdown()

	err = v(store)
	if err != nil {
		t.Error(err.Error())
		return
	}

}
<commit_msg>horrible hack to work around an issue with the way ts waits for datastores to start up (temporary)<commit_after>package ts

import (
	"testing"
	"time"
)

// validator is a function type used by tests to verify that a given storage
// backend is active and working correctly. Normally, functions implementing
// this interface should perform a simple write/read cycle using the backend to
// verify its operation.
type validator func(Store) error

// newStore is a common type implemented by the constructors for all provided
// storage backends. It's useful for internal testing, but not particularly for
// use by clients, so it's not exported.
type newStore func() (Store, error)

// testStore is a common function used for testing all available storage
// backends, it provides common setup and teardown functionality and runs tests
// common to all backends.
func testStore(t *testing.T, ns newStore, v validator) {

	store, err := ns()
	if err != nil {
		t.Error(err.Error())
		return
	}
	defer store.Shutdown()

	// temporary hack until i have a better method of waiting for postgres to
	// start up. This should be removed at the earliest opportunity.
	time.Sleep(time.Second * time.Duration(3))

	err = v(store)
	if err != nil {
		t.Error(err.Error())
		return
	}

}
<|endoftext|>


<commit_before>package web

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/StackExchange/bosun/_third_party/github.com/MiniProfiler/go/miniprofiler"
	"github.com/StackExchange/bosun/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/bosun/conf"
	"github.com/StackExchange/bosun/expr"
	"github.com/StackExchange/bosun/sched"
)

func Expr(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	e, err := expr.New(r.FormValue("q"))
	if err != nil {
		return nil, err
	}
	now, err := getTime(r)
	if err != nil {
		return nil, err
	}
	res, queries, err := e.Execute(opentsdb.NewCache(schedule.Conf.TsdbHost, schedule.Conf.ResponseLimit), t, now, 0, false, schedule.Search, schedule.Lookups)
	if err != nil {
		return nil, err
	}
	for _, r := range res.Results {
		if r.Computations == nil {
			r.Computations = make(expr.Computations, 0)
		}
	}
	ret := struct {
		Type    string
		Results []*expr.Result
		Queries map[string]opentsdb.Request
	}{
		e.Tree.Root.Return().String(),
		res.Results,
		make(map[string]opentsdb.Request),
	}
	for _, q := range queries {
		if e, err := url.QueryUnescape(q.String()); err == nil {
			ret.Queries[e] = q
		}
	}
	return ret, nil
}

func getTime(r *http.Request) (now time.Time, err error) {
	now = time.Now().UTC()
	if fd := r.FormValue("date"); len(fd) > 0 {
		if ft := r.FormValue("time"); len(ft) > 0 {
			fd += " " + ft
		} else {
			fd += " " + now.Format("15:04")
		}
		now, err = time.Parse("2006-01-02 15:04", fd)
	}
	return
}

type Res struct {
	*sched.Event
	Key expr.AlertKey
}

func Rule(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "tsdbHost = %s\n", schedule.Conf.TsdbHost)
	fmt.Fprintf(&buf, "smtpHost = %s\n", schedule.Conf.SmtpHost)
	fmt.Fprintf(&buf, "emailFrom = %s\n", schedule.Conf.EmailFrom)
	fmt.Fprintf(&buf, "responseLimit = %d\n", schedule.Conf.ResponseLimit)
	for k, v := range schedule.Conf.Vars {
		if strings.HasPrefix(k, "$") {
			fmt.Fprintf(&buf, "%s=%s\n", k, v)
		}
	}
	for _, v := range schedule.Conf.Notifications {
		fmt.Fprintln(&buf, v.Def)
	}
	fmt.Fprintf(&buf, "%s\n", r.FormValue("template"))
	fmt.Fprintf(&buf, "%s\n", r.FormValue("alert"))
	c, err := conf.New("Test Config", buf.String())
	if err != nil {
		return nil, err
	}
	if len(c.Alerts) != 1 {
		return nil, fmt.Errorf("exactly one alert must be defined")
	}
	s := &sched.Schedule{}
	now, err := getTime(r)
	if err != nil {
		return nil, err
	}
	s.CheckStart = now
	s.Init(c)
	s.Search = schedule.Search
	rh := make(sched.RunHistory)
	var a *conf.Alert
	for _, a = range c.Alerts {
	}
	if _, err := s.CheckExpr(rh, a, a.Warn, sched.StWarning, nil); err != nil {
		return nil, err
	}
	if _, err := s.CheckExpr(rh, a, a.Crit, sched.StCritical, nil); err != nil {
		return nil, err
	}
	i := 0
	if len(rh) < 1 {
		return nil, fmt.Errorf("no results returned")
	}
	keys := make(expr.AlertKeys, len(rh))
	for k, v := range rh {
		v.Time = now
		keys[i] = k
		i++
	}
	sort.Sort(keys)
	instance := s.Status(keys[0])
	instance.History = []sched.Event{*rh[keys[0]]}
	body := new(bytes.Buffer)
	subject := new(bytes.Buffer)
	var warning []string
	if err := s.ExecuteBody(body, a, instance); err != nil {
		warning = append(warning, err.Error())
	}
	if err := s.ExecuteSubject(subject, a, instance); err != nil {
		warning = append(warning, err.Error())
	}
	b, _ := ioutil.ReadAll(body)
	sub, _ := ioutil.ReadAll(subject)
	return struct {
		Body    string
		Subject string
		Data    interface{}
		Result  sched.RunHistory
		Warning []string
		Time    int64
	}{
		string(b),
		string(sub),
		s.Data(instance, a),
		rh,
		warning,
		now.Unix(),
	}, nil
}
<commit_msg>Allow no template data to be specified<commit_after>package web

import (
	"bytes"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"

	"github.com/StackExchange/bosun/_third_party/github.com/MiniProfiler/go/miniprofiler"
	"github.com/StackExchange/bosun/_third_party/github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/bosun/conf"
	"github.com/StackExchange/bosun/expr"
	"github.com/StackExchange/bosun/sched"
)

func Expr(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	e, err := expr.New(r.FormValue("q"))
	if err != nil {
		return nil, err
	}
	now, err := getTime(r)
	if err != nil {
		return nil, err
	}
	res, queries, err := e.Execute(opentsdb.NewCache(schedule.Conf.TsdbHost, schedule.Conf.ResponseLimit), t, now, 0, false, schedule.Search, schedule.Lookups)
	if err != nil {
		return nil, err
	}
	for _, r := range res.Results {
		if r.Computations == nil {
			r.Computations = make(expr.Computations, 0)
		}
	}
	ret := struct {
		Type    string
		Results []*expr.Result
		Queries map[string]opentsdb.Request
	}{
		e.Tree.Root.Return().String(),
		res.Results,
		make(map[string]opentsdb.Request),
	}
	for _, q := range queries {
		if e, err := url.QueryUnescape(q.String()); err == nil {
			ret.Queries[e] = q
		}
	}
	return ret, nil
}

func getTime(r *http.Request) (now time.Time, err error) {
	now = time.Now().UTC()
	if fd := r.FormValue("date"); len(fd) > 0 {
		if ft := r.FormValue("time"); len(ft) > 0 {
			fd += " " + ft
		} else {
			fd += " " + now.Format("15:04")
		}
		now, err = time.Parse("2006-01-02 15:04", fd)
	}
	return
}

type Res struct {
	*sched.Event
	Key expr.AlertKey
}

func Rule(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	var buf bytes.Buffer
	fmt.Fprintf(&buf, "tsdbHost = %s\n", schedule.Conf.TsdbHost)
	fmt.Fprintf(&buf, "smtpHost = %s\n", schedule.Conf.SmtpHost)
	fmt.Fprintf(&buf, "emailFrom = %s\n", schedule.Conf.EmailFrom)
	fmt.Fprintf(&buf, "responseLimit = %d\n", schedule.Conf.ResponseLimit)
	for k, v := range schedule.Conf.Vars {
		if strings.HasPrefix(k, "$") {
			fmt.Fprintf(&buf, "%s=%s\n", k, v)
		}
	}
	for _, v := range schedule.Conf.Notifications {
		fmt.Fprintln(&buf, v.Def)
	}
	fmt.Fprintf(&buf, "%s\n", r.FormValue("template"))
	fmt.Fprintf(&buf, "%s\n", r.FormValue("alert"))
	c, err := conf.New("Test Config", buf.String())
	if err != nil {
		return nil, err
	}
	if len(c.Alerts) != 1 {
		return nil, fmt.Errorf("exactly one alert must be defined")
	}
	s := &sched.Schedule{}
	now, err := getTime(r)
	if err != nil {
		return nil, err
	}
	s.CheckStart = now
	s.Init(c)
	s.Search = schedule.Search
	rh := make(sched.RunHistory)
	var a *conf.Alert
	for _, a = range c.Alerts {
	}
	if _, err := s.CheckExpr(rh, a, a.Warn, sched.StWarning, nil); err != nil {
		return nil, err
	}
	if _, err := s.CheckExpr(rh, a, a.Crit, sched.StCritical, nil); err != nil {
		return nil, err
	}
	i := 0
	if len(rh) < 1 {
		return nil, fmt.Errorf("no results returned")
	}
	keys := make(expr.AlertKeys, len(rh))
	for k, v := range rh {
		v.Time = now
		keys[i] = k
		i++
	}
	sort.Sort(keys)
	instance := s.Status(keys[0])
	instance.History = []sched.Event{*rh[keys[0]]}
	body := new(bytes.Buffer)
	subject := new(bytes.Buffer)
	var data interface{}
	warning := make([]string, 0)
	if r.FormValue("notemplate") != "" {
		if err := s.ExecuteBody(body, a, instance); err != nil {
			warning = append(warning, err.Error())
		}
		if err := s.ExecuteSubject(subject, a, instance); err != nil {
			warning = append(warning, err.Error())
		}
		data = s.Data(instance, a)
	}
	return struct {
		Body    string      `json:",omitempty"`
		Subject string      `json:",omitempty"`
		Data    interface{} `json:",omitempty"`
		Result  sched.RunHistory
		Warning []string `json:",omitempty"`
		Time    int64
	}{
		body.String(),
		subject.String(),
		data,
		rh,
		warning,
		now.Unix(),
	}, nil
}
<|endoftext|>


<commit_before>// In the name of Allah

// Copyright (c) 2015 Navid Fathollahzade
// This source code is licensed under MIT license that can be found in the LICENSE file.

// Package ptime provides functionality for implementation of Persian (Jalali) Calendar.
package pcalendar

import "errors"

// A PersianMonth specifies a month of the year in Persian calendar starting from 1.
type PersianMonth int

// A GregorianMonth specifies a month of the year in Gregorian calendar starting from 1.
type GregorianMonth int

// A PersianWeekday specifies a day of the week in Persian calendar starting from 0.
type PersianWeekday int

// A GregorianWeekday specifies a day of the week in Gregorian calendar starting from 0.
type GregorianWeekday int

// A PersianCalendar represents a day in Persian (Jalali) Calendar.
type PersianCalendar struct {
	p_year int
	p_month PersianMonth
	p_month_day int
	p_year_day int
	p_week_day PersianWeekday
	g_year int
	g_month GregorianMonth
	g_month_day int
	g_year_day int
	g_week_day GregorianWeekday
}

const (
	Farvardin PersianMonth = 1 + iota
	Ordibehesht
	Khordad
	Tir
	Mordad
	Shahrivar
	Mehr
	Aban
	Azar
	Dey
	Bahman
	Esfand
)

const (
	January GregorianMonth = 1 + iota
	February
	March
	April
	May
	June
	July
	August
	September
	October
	November
	December
)

const (
	Shanbe PersianWeekday = iota
	Yekshanbe
	Doshanbe
	Seshanbe
	Chaharshanbe
	Panjshanbe
	Jome
)

const (
	Saturday GregorianWeekday = iota
	Sunday
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
)

const (
	persian_epoch = 226899

	month_count_normal = 0
	month_count_leap = 1
	month_count_normal_before = 2
	month_count_leap_before = 3
)

var p_months = [...]string{
	"فروردین",
	"اردیبهشت",
	"خرداد",
	"تیر",
	"مرداد",
	"شهریور",
	"مهر",
	"آبان",
	"آذر",
	"دی",
	"بهمن",
	"اسفند",
}

var g_months = [...]string{
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December",
}

var p_days = [...]string{
	"شنبه",
	"یک‌شنبه",
	"دوشنبه",
	"سه‌شنبه",
	"چهارشنبه",
	"پنج‌شنبه",
	"جمعه",
}

var g_days = [...]string{
	"Saturday",
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
}

var p_month_count = [...][...]int {
	{31,     31,      0},       // Farvardin
	{31,     31,      31},      // Ordibehesht
	{31,     31,      62},      // Khordad
	{31,     31,      93},      // Tir
	{31,     31,      124},     // Mordad
	{31,     31,      155},     // Shahrivar
	{30,     30,      186},     // Mehr
	{30,     30,      216},     // Aban
	{30,     30,      246},     // Azar
	{30,     30,      276},     // Dey
	{30,     30,      306},     // Bahman
	{29,     30,      336},     // Esfand
}

var g_month_count = [...][...]int {
	{31,     31,      0,        0},       // Jan
	{28,     29,      31,       31},      // Feb
	{31,     31,      59,       60},      // Mar
	{30,     30,      90,       91},      // Apr
	{31,     31,      120,      121},     // May
	{30,     30,      151,      152},     // Jun
	{31,     31,      181,      182},     // Jul
	{31,     31,      212,      213},     // Aug
	{30,     30,      243,      244},     // Sep
	{31,     31,      273,      274},     // Oct
	{30,     30,      304,      305},     // Nov
	{31,     31,      334,      335},     // Dec
}

// Returns the Persian name of the month.
func (m PersianMonth) String() string {
	return p_months[m - 1]
}

// Returns the English name of the month.
func (m GregorianMonth) String() string {
	return g_months[m - 1]
}

// Returns the Persian name of the day in week.
func (d PersianWeekday) String() string {
	return p_days[d]
}

// Returns the English name of the day in week.
func (d GregorianWeekday) String() string {
	return g_days[d]
}

// Returns new instance of PersianCalendar using Persian date.
func NewFromPersianDate(year int, month PersianMonth, day int) (PersianCalendar, errors) {
	err := checkPersianDate(year, month, day)
	if err != nil {
		return nil, err
	}

	gy, gm, gmd, gyd, gwd := toGregorian(year, month, day)
	pyd, pwd := calculatePersianDays(month, day)

	return PersianCalendar {
		year,   month,  day,    pyd,    pwd,
		gy,     gm,     gmd,    gyd,    gwd,
	}, nil
}

// Returns new instance of PersianCalendar using Gregorian date.
func NewFromGregorianDate(year int, month GregorianMonth, day int) (PersianCalendar, errors) {
	// TODO
	return nil, nil
}

// Changes an instance of PersianCalendar using Persian date.
func (pc PersianCalendar) SetPersianDate(year int, month PersianMonth, day int) errors {
	// TODO
	return nil
}

// Changes an instance of PersianCalendar using Gregorian date.
func (pc PersianCalendar) SetGregorianDate(year int, month GregorianMonth, day int) errors {
	// TODO
	return nil
}

// Checks Persian date inputs to be a correct day.
func checkPersianDate(year int, month PersianMonth, day int) errors {
	// TODO
	return nil
}

// Checks Gregorian date inputs to be a correct day.
func checkGregorianDate(year int, month GregorianMonth, day int) errors {
	// TODO
	return nil
}

// Converts Gregorian date to Persian date (year, month, month_day, year_day, week_day)
func toPersian(year int, month GregorianMonth, day int) (y int, m PersianMonth, md int, yd int, wd PersianWeekday) {
	// TODO
	return
}

// Converts Persian date to Gregorian date (year, month, month_day, year_day, week_day)
func toGregorian(year int, month PersianMonth, day int) (y int, m GregorianMonth, md int, yd int, wd GregorianWeekday) {
	// TODO
	return
}

// Calculates the year day and weekday of Persian date.
func calculatePersianDays(month PersianMonth, day int) (yd int, wd PersianWeekday) {
	// TODO
	return
}

// Calculates the year day and weekday of Gregorian date.
func calculateGregorianDays(month PersianMonth, day int) (yd int, wd GregorianWeekday) {
	// TODO
	return
}<commit_msg>debug year day calculator<commit_after>// In the name of Allah

// Copyright (c) 2015 Navid Fathollahzade
// This source code is licensed under MIT license that can be found in the LICENSE file.

// Package ptime provides functionality for implementation of Persian (Jalali) Calendar.
package pcalendar

import "errors"

// A PersianMonth specifies a month of the year in Persian calendar starting from 1.
type PersianMonth int

// A GregorianMonth specifies a month of the year in Gregorian calendar starting from 1.
type GregorianMonth int

// A PersianWeekday specifies a day of the week in Persian calendar starting from 0.
type PersianWeekday int

// A GregorianWeekday specifies a day of the week in Gregorian calendar starting from 0.
type GregorianWeekday int

// A PersianCalendar represents a day in Persian (Jalali) Calendar.
type PersianCalendar struct {
	p_year int
	p_month PersianMonth
	p_month_day int
	p_year_day int
	p_week_day PersianWeekday
	g_year int
	g_month GregorianMonth
	g_month_day int
	g_year_day int
	g_week_day GregorianWeekday
}

const (
	Farvardin PersianMonth = 1 + iota
	Ordibehesht
	Khordad
	Tir
	Mordad
	Shahrivar
	Mehr
	Aban
	Azar
	Dey
	Bahman
	Esfand
)

const (
	January GregorianMonth = 1 + iota
	February
	March
	April
	May
	June
	July
	August
	September
	October
	November
	December
)

const (
	Shanbe PersianWeekday = iota
	Yekshanbe
	Doshanbe
	Seshanbe
	Chaharshanbe
	Panjshanbe
	Jome
)

const (
	Saturday GregorianWeekday = iota
	Sunday
	Monday
	Tuesday
	Wednesday
	Thursday
	Friday
)

const (
	persian_epoch = 226899

	month_count_normal = 0
	month_count_leap = 1
	month_count_normal_before = 2
	month_count_leap_before = 3
)

var p_months = [...]string{
	"فروردین",
	"اردیبهشت",
	"خرداد",
	"تیر",
	"مرداد",
	"شهریور",
	"مهر",
	"آبان",
	"آذر",
	"دی",
	"بهمن",
	"اسفند",
}

var g_months = [...]string{
	"January",
	"February",
	"March",
	"April",
	"May",
	"June",
	"July",
	"August",
	"September",
	"October",
	"November",
	"December",
}

var p_days = [...]string{
	"شنبه",
	"یک‌شنبه",
	"دوشنبه",
	"سه‌شنبه",
	"چهارشنبه",
	"پنج‌شنبه",
	"جمعه",
}

var g_days = [...]string{
	"Saturday",
	"Sunday",
	"Monday",
	"Tuesday",
	"Wednesday",
	"Thursday",
	"Friday",
}

var p_month_count = [...][...]int {
	{31,     31,      0},       // Farvardin
	{31,     31,      31},      // Ordibehesht
	{31,     31,      62},      // Khordad
	{31,     31,      93},      // Tir
	{31,     31,      124},     // Mordad
	{31,     31,      155},     // Shahrivar
	{30,     30,      186},     // Mehr
	{30,     30,      216},     // Aban
	{30,     30,      246},     // Azar
	{30,     30,      276},     // Dey
	{30,     30,      306},     // Bahman
	{29,     30,      336},     // Esfand
}

var g_month_count = [...][...]int {
	{31,     31,      0,        0},       // Jan
	{28,     29,      31,       31},      // Feb
	{31,     31,      59,       60},      // Mar
	{30,     30,      90,       91},      // Apr
	{31,     31,      120,      121},     // May
	{30,     30,      151,      152},     // Jun
	{31,     31,      181,      182},     // Jul
	{31,     31,      212,      213},     // Aug
	{30,     30,      243,      244},     // Sep
	{31,     31,      273,      274},     // Oct
	{30,     30,      304,      305},     // Nov
	{31,     31,      334,      335},     // Dec
}

// Returns the Persian name of the month.
func (m PersianMonth) String() string {
	return p_months[m - 1]
}

// Returns the English name of the month.
func (m GregorianMonth) String() string {
	return g_months[m - 1]
}

// Returns the Persian name of the day in week.
func (d PersianWeekday) String() string {
	return p_days[d]
}

// Returns the English name of the day in week.
func (d GregorianWeekday) String() string {
	return g_days[d]
}

// Returns new instance of PersianCalendar using Persian date.
func NewPersianDate(year int, month PersianMonth, day int) (PersianCalendar, errors) {
	err := checkPersianDate(year, month, day)
	if err != nil {
		return nil, err
	}

	gy, gm, gmd, gyd, gwd := toGregorian(year, month, day)
	pyd := persianYearDay(year, month, day)

	return PersianCalendar {
		year,   month,  day,    pyd,    PersianWeekday(gwd),
		gy,     gm,     gmd,    gyd,    gwd,
	}, nil
}

// Returns new instance of PersianCalendar using Gregorian date.
func NewGregorianDate(year int, month GregorianMonth, day int) (PersianCalendar, errors) {
	err := checkGregorianDate(year, month, day)
	if err != nil {
		return nil, err
	}

	py, pm, pmd, pyd, pwd := toPersian(year, month, day)
	gyd := gregorianYearDay(year, month, day)

	return PersianCalendar {
		py,     pm,     pmd,    pyd,    pwd,
		year,   month,  day,    gyd,    GregorianWeekday(pwd),
	}, nil
}

// Changes the instance of PersianCalendar (pc) using Persian date.
func (pc PersianCalendar) SetPersianDate(year int, month PersianMonth, day int) errors {
	err := checkPersianDate(year, month, day)
	if err != nil {
		return err
	}

	pc.p_year = year
	pc.p_month = month
	pc.p_month_day = day

	pc.g_year, pc.g_month, pc.g_month_day, pc.g_year_day, pc.g_week_day = toGregorian(year, month, day)
	pc.p_year_day = persianYearDay(year, month, day)
	pc.p_week_day = PersianWeekday(pc.g_week_day)

	return nil
}

// Changes the instance of PersianCalendar (pc) using Gregorian date.
func (pc PersianCalendar) SetGregorianDate(year int, month GregorianMonth, day int) errors {
	err := checkGregorianDate(year, month, day)
	if err != nil {
		return err
	}

	pc.g_year = year
	pc.g_month = month
	pc.g_month_day = day

	pc.p_year, pc.p_month, pc.p_month_day, pc.p_year_day, pc.p_week_day = toPersian(year, month, day)
	pc.g_year_day = gregorianYearDay(year, month, day)
	pc.g_week_day = GregorianWeekday(pc.p_week_day)

	return nil
}

// Checks Persian date inputs to be a correct day.
func checkPersianDate(year int, month PersianMonth, day int) errors {
	// TODO
	return nil
}

// Checks Gregorian date inputs to be a correct day.
func checkGregorianDate(year int, month GregorianMonth, day int) errors {
	// TODO
	return nil
}

// Converts Gregorian date to Persian date (year, month, month_day, year_day, week_day)
func toPersian(year int, month GregorianMonth, day int) (y int, m PersianMonth, md int, yd int, wd PersianWeekday) {
	// TODO
	return
}

// Converts Persian date to Gregorian date (year, month, month_day, year_day, week_day)
func toGregorian(year int, month PersianMonth, day int) (y int, m GregorianMonth, md int, yd int, wd GregorianWeekday) {
	// TODO
	return
}

// Returns the year day of Persian date.
func persianYearDay(year int, month PersianMonth, day int) (yd int) {
	// TODO
	return
}

// Returns the year day of Gregorian date.
func gregorianYearDay(year int, month GregorianMonth, day int) (yd int) {
	// TODO
	return
}<|endoftext|>


<commit_before>package backendutil

import (
	"net/textproto"
	"strings"
	"testing"
	"time"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-message"
)

var matchTests = []struct {
	criteria *imap.SearchCriteria
	res      bool
}{
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"From": {"Mitsuha"}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"To": {"Mitsuha"}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{Before: testDate.Add(48 * time.Hour)},
		res:      true,
	},
	{
		criteria: &imap.SearchCriteria{
			Not: []*imap.SearchCriteria{{Since: testDate.Add(48 * time.Hour)}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Not: []*imap.SearchCriteria{{Body: []string{"name"}}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Message-Id": {"43@example.org"}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Message-Id": {""}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Larger: 10,
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Smaller: 10,
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Subject": {"your"}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Subject": {"Taki"}},
		},
		res: false,
	},
}

func TestMatch(t *testing.T) {
	for i, test := range matchTests {
		e, err := message.Read(strings.NewReader(testMailString))
		if err != nil {
			t.Fatal("Expected no error while reading entity, got:", err)
		}

		ok, err := Match(e, test.criteria)
		if err != nil {
			t.Fatal("Expected no error while matching entity, got:", err)
		}

		if test.res && !ok {
			t.Errorf("Expected #%v to match search criteria", i+1)
		}
		if !test.res && ok {
			t.Errorf("Expected #%v not to match search criteria", i+1)
		}
	}
}

var flagsTests = []struct {
	flags    []string
	criteria *imap.SearchCriteria
	res      bool
}{
	{
		flags: []string{imap.SeenFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.SeenFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: true,
	},
	{
		flags: []string{imap.SeenFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.DraftFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: false,
	},
	{
		flags: []string{imap.SeenFlag, imap.FlaggedFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.SeenFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: false,
	},
}

func TestMatchFlags(t *testing.T) {
	for i, test := range flagsTests {
		ok := MatchFlags(test.flags, test.criteria)
		if test.res && !ok {
			t.Errorf("Expected #%v to match search criteria", i+1)
		}
		if !test.res && ok {
			t.Errorf("Expected #%v not to match search criteria", i+1)
		}
	}
}

func TestMatchSeqNumAndUid(t *testing.T) {
	seqNum := uint32(42)
	uid := uint32(69)

	c := &imap.SearchCriteria{
		Or: [][2]*imap.SearchCriteria{{
			{
				Uid: new(imap.SeqSet),
				Not: []*imap.SearchCriteria{{SeqNum: new(imap.SeqSet)}},
			},
			{
				SeqNum: new(imap.SeqSet),
			},
		}},
	}

	if MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected not to match criteria")
	}

	c.Or[0][0].Uid.AddNum(uid)
	if !MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected to match criteria")
	}

	c.Or[0][0].Not[0].SeqNum.AddNum(seqNum)
	if MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected not to match criteria")
	}

	c.Or[0][1].SeqNum.AddNum(seqNum)
	if !MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected to match criteria")
	}
}
<commit_msg>backendutil: fixes useless test<commit_after>package backendutil

import (
	"net/textproto"
	"strings"
	"testing"
	"time"

	"github.com/emersion/go-imap"
	"github.com/emersion/go-message"
)

var matchTests = []struct {
	criteria *imap.SearchCriteria
	res      bool
}{
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"From": {"Mitsuha"}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"To": {"Mitsuha"}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{SentBefore: testDate.Add(48 * time.Hour)},
		res:      true,
	},
	{
		criteria: &imap.SearchCriteria{
			Not: []*imap.SearchCriteria{{SentSince: testDate.Add(48 * time.Hour)}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Not: []*imap.SearchCriteria{{Body: []string{"name"}}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Message-Id": {"43@example.org"}},
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Message-Id": {""}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Larger: 10,
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Smaller: 10,
		},
		res: false,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Subject": {"your"}},
		},
		res: true,
	},
	{
		criteria: &imap.SearchCriteria{
			Header: textproto.MIMEHeader{"Subject": {"Taki"}},
		},
		res: false,
	},
}

func TestMatch(t *testing.T) {
	for i, test := range matchTests {
		e, err := message.Read(strings.NewReader(testMailString))
		if err != nil {
			t.Fatal("Expected no error while reading entity, got:", err)
		}

		ok, err := Match(e, test.criteria)
		if err != nil {
			t.Fatal("Expected no error while matching entity, got:", err)
		}

		if test.res && !ok {
			t.Errorf("Expected #%v to match search criteria", i+1)
		}
		if !test.res && ok {
			t.Errorf("Expected #%v not to match search criteria", i+1)
		}
	}
}

var flagsTests = []struct {
	flags    []string
	criteria *imap.SearchCriteria
	res      bool
}{
	{
		flags: []string{imap.SeenFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.SeenFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: true,
	},
	{
		flags: []string{imap.SeenFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.DraftFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: false,
	},
	{
		flags: []string{imap.SeenFlag, imap.FlaggedFlag},
		criteria: &imap.SearchCriteria{
			WithFlags:    []string{imap.SeenFlag},
			WithoutFlags: []string{imap.FlaggedFlag},
		},
		res: false,
	},
}

func TestMatchFlags(t *testing.T) {
	for i, test := range flagsTests {
		ok := MatchFlags(test.flags, test.criteria)
		if test.res && !ok {
			t.Errorf("Expected #%v to match search criteria", i+1)
		}
		if !test.res && ok {
			t.Errorf("Expected #%v not to match search criteria", i+1)
		}
	}
}

func TestMatchSeqNumAndUid(t *testing.T) {
	seqNum := uint32(42)
	uid := uint32(69)

	c := &imap.SearchCriteria{
		Or: [][2]*imap.SearchCriteria{{
			{
				Uid: new(imap.SeqSet),
				Not: []*imap.SearchCriteria{{SeqNum: new(imap.SeqSet)}},
			},
			{
				SeqNum: new(imap.SeqSet),
			},
		}},
	}

	if MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected not to match criteria")
	}

	c.Or[0][0].Uid.AddNum(uid)
	if !MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected to match criteria")
	}

	c.Or[0][0].Not[0].SeqNum.AddNum(seqNum)
	if MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected not to match criteria")
	}

	c.Or[0][1].SeqNum.AddNum(seqNum)
	if !MatchSeqNumAndUid(seqNum, uid, c) {
		t.Error("Expected to match criteria")
	}
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"log"

	"github.com/lhz/considerate/hvsc"
	"github.com/lhz/considerate/player"
	"github.com/nsf/termbox-go"
)

var w, h int
var listOffset, listPos int

func Setup() {
	err := termbox.Init()
	if err != nil {
		log.Panicln(err)
	}

	w, h = termbox.Size()
	listOffset = 0
	listPos = 0

	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	draw()
}

func Run() {
	defer termbox.Close()

	for quit := false; !quit; {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyCtrlC, termbox.KeyEsc:
				quit = true
			case termbox.KeyPgup:
				pageUp()
			case termbox.KeyPgdn:
				pageDown()
			case termbox.KeyArrowUp:
				moveUp()
			case termbox.KeyArrowDown:
				moveDown()
			case termbox.KeyEnter:
				selectTune()
			}
			draw()
		case termbox.EventResize:
			w, h = ev.Width, ev.Height
			draw()
		case termbox.EventError:
			panic(ev.Err)
		}
	}
}

func moveUp() {
	listPos--
	if listPos < 0 {
		listPos = h - 1
		pageUp()
	}
}

func moveDown() {
	listPos++
	if listPos >= h {
		listPos = 0
		pageDown()
	}
}

func pageUp() {
	listOffset -= h
	if listOffset < 0 {
		listOffset = 0
	}
}

func pageDown() {
	listOffset += h
	if listOffset > hvsc.NumTunes-1 {
		listOffset = hvsc.NumTunes - 1
	}
}

func selectTune() {
	player.Play(hvsc.Tunes[listOffset+listPos].FullPath(), 1)
}

func draw() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	for y := 0; y < h; y++ {
		tune := hvsc.Tunes[y+listOffset]
		bg := termbox.ColorDefault
		if y == listPos {
			bg = termbox.ColorBlack
		}
		writeAt(0, y, fmt.Sprintf("%05d", y+listOffset+1), termbox.ColorRed, termbox.ColorDefault)
		writeAt(12, y, tune.Header.Name, termbox.ColorBlue, bg)
		writeAt(45, y, tune.Header.Author, termbox.ColorGreen, bg)
		writeAt(78, y, tune.Header.Released, termbox.ColorYellow, bg)
		writeAt(112, y, tune.Path, termbox.ColorDefault, bg)
	}
	termbox.Flush()
}

func writeAt(x, y int, value string, fg, bg termbox.Attribute) {
	i := 0
	for _, c := range value {
		termbox.SetCell(x+i, y, c, fg, bg)
		i++
	}
}
<commit_msg>Added header and footer.<commit_after>package ui

import (
	"fmt"
	"log"

	"github.com/lhz/considerate/hvsc"
	"github.com/lhz/considerate/player"
	"github.com/nsf/termbox-go"
)

var w, h int
var listOffset, listPos, lh, ly int

func Setup() {
	err := termbox.Init()
	if err != nil {
		log.Panicln(err)
	}

	w, h = termbox.Size()
	listOffset = 0
	listPos = 0
	ly = 1
	lh = h - 2

	termbox.SetInputMode(termbox.InputEsc | termbox.InputMouse)
	draw()
}

func Run() {
	defer termbox.Close()

	for quit := false; !quit; {
		switch ev := termbox.PollEvent(); ev.Type {
		case termbox.EventKey:
			switch ev.Key {
			case termbox.KeyCtrlC, termbox.KeyEsc:
				quit = true
			case termbox.KeyPgup:
				pageUp()
			case termbox.KeyPgdn:
				pageDown()
			case termbox.KeyArrowUp:
				moveUp()
			case termbox.KeyArrowDown:
				moveDown()
			case termbox.KeyEnter:
				selectTune()
			}
			draw()
		case termbox.EventResize:
			w, h = ev.Width, ev.Height
			draw()
		case termbox.EventError:
			panic(ev.Err)
		}
	}
}

func moveUp() {
	listPos--
	if listPos < 0 {
		if listOffset > 0 {
			listPos = lh - 1
			pageUp()
		} else {
			listPos = 0
		}
	}
}

func moveDown() {
	listPos++
	if listPos >= lh {
		listPos = 0
		pageDown()
	}
}

func pageUp() {
	listOffset -= lh
	if listOffset < 0 {
		listOffset = 0
	}
}

func pageDown() {
	listOffset += lh
	if listOffset > hvsc.NumTunes-1 {
		listOffset -= lh
	}
}

func selectTune() {
	player.Play(hvsc.Tunes[listOffset+listPos].FullPath(), 1)
}

func draw() {
	termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)
	drawHeader()
	drawList()
	drawFooter()
	termbox.Flush()
}

func drawHeader() {
	header := fmt.Sprintf(fmt.Sprintf("%%%ds", w), "")
	writeAt(0, 0, header, termbox.ColorWhite, termbox.ColorBlack)
}

func drawFooter() {
	footer := fmt.Sprintf("%d/%d", listOffset+listPos+1, hvsc.NumTunes)
	footer = fmt.Sprintf(fmt.Sprintf("%%s%%%ds", w-len(footer)), footer, "")
	writeAt(0, h-1, footer, termbox.ColorWhite, termbox.ColorBlack)
}

func drawList() {
	for y := 0; y < lh; y++ {
		tune := hvsc.Tunes[y+listOffset]
		fg := termbox.ColorDefault
		bg := termbox.ColorDefault
		if y == listPos {
			fg = termbox.ColorDefault | termbox.AttrBold
			bg = termbox.ColorBlue
		}
		writeAt(0, ly+y, tune.Path, fg, bg)
	}
}

func writeAt(x, y int, value string, fg, bg termbox.Attribute) {
	i := 0
	for _, c := range value {
		termbox.SetCell(x+i, y, c, fg, bg)
		i++
	}
}
<|endoftext|>


<commit_before>package philarios

type TargetPhrase struct {
  Pretarget string
  Target string
  Posttarget string
}

func (t *TargetPhrase) AlternativeTargets() ([]string) {
  // TODO: implement this
}
<commit_msg>Starting to work on philarios alternative words.<commit_after>package philarios

import (
  "database/sql"
)

type WordVector struct {
  Word string
  Score float64
}

/*
This method returns alternative words that can be used in place of the current
target in the target phrase.
*/
func AlternativeWords(word string) ([]string, error) {
  var alternativeWords []string

  targetVectors, err := TargetVectors(word)
  if err != nil {
    return alternativeWords, err
  }

  var potentialWords map[string]float64
  for _, wordVector := range targetVectors {
    potentialWords[wordVector.Word] = wordVector.Score
  }

  synonyms, err := Synonyms(word)
  if err != nil {
    return alternativeWords, err
  }

  for _, synonym := range synonyms {
    synonymVectors, err := TargetVectors(synonym)
    if err != nil {
      return alternativeWords, err
    }

    for _, synonymVector := range synonymVectors {
      potentialWords[synonymVector.Word] = SynonymScore(synonymVector.Score)
    }
  }

  // TODO: find the best n alternative words.
}

func SynonymScore(score float64) (float64) {
  return score
}

func TargetVectors(word string) ([]WordVector, error) {
  var wordVectors []WordVector

  // TODO: figure out a way to get pre and post target vectors
}

func Synonyms(word string) ([]string, error) {
  // TODO: implement
}
<|endoftext|>