<commit_before>/*
Quick and simple monitoring system

Usage:
 jsonmon config.yml
 jsonmon -v # Prints version to stdout and exits

Environment:
 HOST
  - defaults to localhost
  - the JSON API network interface
 PORT
  - defaults to 3000
  - the JSON API port
*/
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"regexp"
	"runtime"
	"strconv"
	"syscall"
	"time"
)

// Application version.
const Version = "1.2.9"

// This one is for internal use.
type ver struct {
	App  string `json:"jsonmon"`
	Go   string `json:"runtime"`
	Os   string `json:"os"`
	Arch string `json:"arch"`
}

var version ver

// Check details.
type Check struct {
	Name   string      `json:"name,omitempty" yaml:"name"`
	Web    string      `json:"web,omitempty" yaml:"web"`
	Shell  string      `json:"shell,omitempty" yaml:"shell"`
	Match  string      `json:"-" yaml:"match"`
	Return int         `json:"-" yaml:"return"`
	Notify interface{} `json:"-" yaml:"notify"`
	Alert  interface{} `json:"-", yaml:"alert`
	Tries  int         `json:"-" yaml:"tries"`
	Repeat int         `json:"-" yaml:"repeat"`
	Failed bool        `json:"failed" yaml:"-"`
	Since  string      `json:"since,omitempty" yaml:"-"`
}

// Global checks list. Need to share it with workers and Web UI.
var checks []Check

// Global last modified date for HTTP caching.
var modified string

// Construct the last modified string.
func etag() {
	modified = "W/\"" + strconv.FormatInt(time.Now().UnixNano(), 10) + "\""
}

// The main loop.
func main() {
	// Parse CLI args.
	usage := "Usage: " + path.Base(os.Args[0]) + " config.yml\n" +
		"Docs:  https://github.com/chillum/jsonmon/wiki"
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(1)
	}
	// -v for version.
	version.App = Version
	version.Go = runtime.Version()
	version.Os = runtime.GOOS
	version.Arch = runtime.GOARCH
	switch os.Args[1] {
	case "-h":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(0)
	case "-v":
		fallthrough
	case "-version":
		fallthrough
	case "--version":
		json, _ := json.MarshalIndent(&version, "", "  ")
		fmt.Println(string(json))
		os.Exit(0)
	}
	// Tune concurrency.
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	// Read config file or exit with error.
	config, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
		os.Exit(3)
	}
	err = yaml.Unmarshal(config, &checks)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", config[0], err)
		os.Exit(3)
	}
	// Exit with return code 0 on kill.
	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGTERM)
	go func() {
		<-done
		os.Exit(0)
	}()
	// Run checks.
	etag()
	for i := range checks {
		go worker(&checks[i])
	}
	// Launch the JSON API.
	host := os.Getenv("HOST")
	port := os.Getenv("PORT")
	if host == "" {
		host = "localhost"
	}
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/version", getVersion)
	http.HandleFunc("/", getChecks)
	err = http.ListenAndServe(host+":"+port, nil)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
	}
	os.Exit(4)
}

// Background worker.
func worker(check *Check) {
	for {
		if check.Repeat == 0 { // Set default timeout.
			check.Repeat = 60
		}
		if check.Tries == 0 { // Default to 1 attempt.
			check.Tries = 1
		}
		if check.Web != "" {
			web(check)
		}
		if check.Shell != "" {
			shell(check)
		}
		time.Sleep(time.Second * time.Duration(check.Repeat))
	}
}

// Shell worker.
func shell(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Shell
	}
	// Execute with shell in N attemps.
	var out []byte
	var err error
	for i := 0; i < check.Tries; i++ {
		out, err = exec.Command("/bin/sh", "-c", check.Shell).CombinedOutput()
		if err == nil {
			if check.Match != "" { // Match regexp.
				var regex *regexp.Regexp
				regex, err = regexp.Compile(check.Match)
				if err == nil && !regex.Match(out) {
					err = errors.New("Expected:\n" + check.Match + "\n\nGot:\n" + string(out))
				}
			}
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			check.Failed = false
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)
		}
	} else {
		if !check.Failed {
			check.Failed = true
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			msg := string(out) + err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Web worker.
func web(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Web
	}
	if check.Return == 0 { // Successful HTTP return code is 200.
		check.Return = 200
	}
	// Get the URL in N attempts.
	var err error
	for i := 0; i < check.Tries; i++ {
		err = fetch(check.Web, check.Match, check.Return)
		if err == nil {
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			check.Failed = false
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)

		}
	} else {
		if !check.Failed {
			check.Failed = true
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			msg := err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Check HTTP redirects.
func redirect(req *http.Request, via []*http.Request) error {
	// When redirects number > 10 probably there's a problem.
	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}
	// Redirects don't get User-Agent.
	req.Header.Set("User-Agent", "jsonmon")
	return nil
}

// The actual HTTP GET.
func fetch(url string, match string, code int) error {
	var err error
	var resp *http.Response
	var req *http.Request
	client := &http.Client{}
	client.CheckRedirect = redirect
	req, err = http.NewRequest("GET", url, nil)
	if err == nil {
		req.Header.Set("User-Agent", "jsonmon")
		resp, err = client.Do(req)
		if err == nil {
			if resp.StatusCode != code { // Check status code.
				err = errors.New(url + " returned " + strconv.Itoa(resp.StatusCode))
			} else { // Match regexp.
				if resp != nil && match != "" {
					var regex *regexp.Regexp
					regex, err = regexp.Compile(match)
					if err == nil {
						var body []byte
						body, _ = ioutil.ReadAll(resp.Body)
						if !regex.Match(body) {
							err = errors.New("Expected:\n" + match + "\n\nGot:\n" + string(body))
						}
					}
				}
			}
		}
	}
	if resp != nil {
		resp.Body.Close()
	}
	return err
}

// Logs and mail alerting.
func notify(mail interface{}, subject string, message *string) {
	// Log the alerts.
	if message == nil {
		fmt.Println(subject)
	} else {
		fmt.Println(subject + "\n" + *message)
	}
	// Mail the alerts.
	if mail != nil {
		// Make the message.
		var rcpt string
		var ok bool
		if rcpt, ok = mail.(string); !ok {
			for i, v := range mail.([]interface{}) {
				if i != 0 {
					rcpt += ", "
				}
				rcpt += v.(string)
			}
		}
		msg := "To: " + rcpt + "\nSubject: " + subject + "\nX-Mailer: jsonmon\n\n"
		if message != nil {
			msg += *message
		}
		msg += "\n.\n"
		// And send it.
		sendmail := exec.Command("/usr/sbin/sendmail", "-t")
		stdin, _ := sendmail.StdinPipe()
		err := sendmail.Start()
		if err != nil {
			fmt.Fprintln(os.Stderr, "ERROR:", err)
		}
		io.WriteString(stdin, msg)
		sendmail.Wait()
	}
}

// Executes callback. Passes args: true/false, check's name, message.
func alert(check *Check, name *string, msg *string) {
	if check.Alert != nil {
		plugin, ok := check.Alert.(string)
		if ok { // check.Alert is a string.
			out, err := exec.Command(plugin, strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
			if err != nil {
				fmt.Fprintln(os.Stderr, "ERROR:", string(out)+err.Error())
			}
		} else { // check.Alert is a list.
			for _, i := range check.Alert.([]interface{}) {
				out, err := exec.Command(i.(string), strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
				if err != nil {
					fmt.Fprintln(os.Stderr, "ERROR:", string(out)+err.Error())
				}
			}
		}
	}
}

// Display checks' details.
func getChecks(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" { // Serve for root page only, 404 otherwise.
		w.Header().Set("Server", "jsonmon")
		http.NotFound(w, r)
		return
	}
	displayJSON(w, r, &checks)
}

// Display application version.
func getVersion(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &version)
}

// Output JSON.
func displayJSON(w http.ResponseWriter, r *http.Request, data interface{}) {
	h := w.Header()
	h.Set("Server", "jsonmon")
	if r.Header.Get("If-None-Match") == modified {
		delete(h, "Content-Type")
		delete(h, "Content-Length")
		w.WriteHeader(http.StatusNotModified)
	} else {
		h.Set("Cache-Control", "no-cache")
		h.Set("ETag", modified)
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("Content-Type", "application/json; charset=utf-8")
		json, _ := json.MarshalIndent(&data, "", "  ")
		w.Write(json)
		fmt.Fprintln(w, "") // Trailing newline.
	}
}
<commit_msg>-v formatting<commit_after>/*
Quick and simple monitoring system

Usage:
 jsonmon config.yml
 jsonmon -v # Prints version to stdout and exits

Environment:
 HOST
  - defaults to localhost
  - the JSON API network interface
 PORT
  - defaults to 3000
  - the JSON API port
*/
package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"regexp"
	"runtime"
	"strconv"
	"syscall"
	"time"
)

// Application version.
const Version = "1.2.9"

// This one is for internal use.
type ver struct {
	App  string `json:"jsonmon"`
	Go   string `json:"runtime"`
	Os   string `json:"os"`
	Arch string `json:"arch"`
}

var version ver

// Check details.
type Check struct {
	Name   string      `json:"name,omitempty" yaml:"name"`
	Web    string      `json:"web,omitempty" yaml:"web"`
	Shell  string      `json:"shell,omitempty" yaml:"shell"`
	Match  string      `json:"-" yaml:"match"`
	Return int         `json:"-" yaml:"return"`
	Notify interface{} `json:"-" yaml:"notify"`
	Alert  interface{} `json:"-", yaml:"alert`
	Tries  int         `json:"-" yaml:"tries"`
	Repeat int         `json:"-" yaml:"repeat"`
	Failed bool        `json:"failed" yaml:"-"`
	Since  string      `json:"since,omitempty" yaml:"-"`
}

// Global checks list. Need to share it with workers and Web UI.
var checks []Check

// Global last modified date for HTTP caching.
var modified string

// Construct the last modified string.
func etag() {
	modified = "W/\"" + strconv.FormatInt(time.Now().UnixNano(), 10) + "\""
}

// The main loop.
func main() {
	// Parse CLI args.
	usage := "Usage: " + path.Base(os.Args[0]) + " config.yml\n" +
		"Docs:  https://github.com/chillum/jsonmon/wiki"
	if len(os.Args) != 2 {
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(1)
	}
	// -v for version.
	version.App = Version
	version.Go = runtime.Version()
	version.Os = runtime.GOOS
	version.Arch = runtime.GOARCH
	switch os.Args[1] {
	case "-h":
		fallthrough
	case "-help":
		fallthrough
	case "--help":
		fmt.Fprintln(os.Stderr, usage)
		os.Exit(0)
	case "-v":
		fallthrough
	case "-version":
		fallthrough
	case "--version":
		json, _ := json.Marshal(&version)
		fmt.Println(string(json))
		os.Exit(0)
	}
	// Tune concurrency.
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	// Read config file or exit with error.
	config, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
		os.Exit(3)
	}
	err = yaml.Unmarshal(config, &checks)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", config[0], err)
		os.Exit(3)
	}
	// Exit with return code 0 on kill.
	done := make(chan os.Signal, 1)
	signal.Notify(done, syscall.SIGTERM)
	go func() {
		<-done
		os.Exit(0)
	}()
	// Run checks.
	etag()
	for i := range checks {
		go worker(&checks[i])
	}
	// Launch the JSON API.
	host := os.Getenv("HOST")
	port := os.Getenv("PORT")
	if host == "" {
		host = "localhost"
	}
	if port == "" {
		port = "3000"
	}
	http.HandleFunc("/version", getVersion)
	http.HandleFunc("/", getChecks)
	err = http.ListenAndServe(host+":"+port, nil)
	if err != nil {
		fmt.Fprintln(os.Stderr, "ERROR:", err)
	}
	os.Exit(4)
}

// Background worker.
func worker(check *Check) {
	for {
		if check.Repeat == 0 { // Set default timeout.
			check.Repeat = 60
		}
		if check.Tries == 0 { // Default to 1 attempt.
			check.Tries = 1
		}
		if check.Web != "" {
			web(check)
		}
		if check.Shell != "" {
			shell(check)
		}
		time.Sleep(time.Second * time.Duration(check.Repeat))
	}
}

// Shell worker.
func shell(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Shell
	}
	// Execute with shell in N attemps.
	var out []byte
	var err error
	for i := 0; i < check.Tries; i++ {
		out, err = exec.Command("/bin/sh", "-c", check.Shell).CombinedOutput()
		if err == nil {
			if check.Match != "" { // Match regexp.
				var regex *regexp.Regexp
				regex, err = regexp.Compile(check.Match)
				if err == nil && !regex.Match(out) {
					err = errors.New("Expected:\n" + check.Match + "\n\nGot:\n" + string(out))
				}
			}
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			check.Failed = false
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)
		}
	} else {
		if !check.Failed {
			check.Failed = true
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			msg := string(out) + err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Web worker.
func web(check *Check) {
	// Set check's display name.
	var name string
	if check.Name != "" {
		name = check.Name
	} else {
		name = check.Web
	}
	if check.Return == 0 { // Successful HTTP return code is 200.
		check.Return = 200
	}
	// Get the URL in N attempts.
	var err error
	for i := 0; i < check.Tries; i++ {
		err = fetch(check.Web, check.Match, check.Return)
		if err == nil {
			break
		}
	}
	// Process results.
	if err == nil {
		if check.Failed {
			check.Failed = false
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			notify(check.Notify, "Fixed: "+name, nil)
			alert(check, &name, nil)

		}
	} else {
		if !check.Failed {
			check.Failed = true
			check.Since = time.Now().Format(time.RFC3339)
			etag()
			msg := err.Error()
			notify(check.Notify, "Failed: "+name, &msg)
			alert(check, &name, &msg)
		}
	}
}

// Check HTTP redirects.
func redirect(req *http.Request, via []*http.Request) error {
	// When redirects number > 10 probably there's a problem.
	if len(via) >= 10 {
		return errors.New("stopped after 10 redirects")
	}
	// Redirects don't get User-Agent.
	req.Header.Set("User-Agent", "jsonmon")
	return nil
}

// The actual HTTP GET.
func fetch(url string, match string, code int) error {
	var err error
	var resp *http.Response
	var req *http.Request
	client := &http.Client{}
	client.CheckRedirect = redirect
	req, err = http.NewRequest("GET", url, nil)
	if err == nil {
		req.Header.Set("User-Agent", "jsonmon")
		resp, err = client.Do(req)
		if err == nil {
			if resp.StatusCode != code { // Check status code.
				err = errors.New(url + " returned " + strconv.Itoa(resp.StatusCode))
			} else { // Match regexp.
				if resp != nil && match != "" {
					var regex *regexp.Regexp
					regex, err = regexp.Compile(match)
					if err == nil {
						var body []byte
						body, _ = ioutil.ReadAll(resp.Body)
						if !regex.Match(body) {
							err = errors.New("Expected:\n" + match + "\n\nGot:\n" + string(body))
						}
					}
				}
			}
		}
	}
	if resp != nil {
		resp.Body.Close()
	}
	return err
}

// Logs and mail alerting.
func notify(mail interface{}, subject string, message *string) {
	// Log the alerts.
	if message == nil {
		fmt.Println(subject)
	} else {
		fmt.Println(subject + "\n" + *message)
	}
	// Mail the alerts.
	if mail != nil {
		// Make the message.
		var rcpt string
		var ok bool
		if rcpt, ok = mail.(string); !ok {
			for i, v := range mail.([]interface{}) {
				if i != 0 {
					rcpt += ", "
				}
				rcpt += v.(string)
			}
		}
		msg := "To: " + rcpt + "\nSubject: " + subject + "\nX-Mailer: jsonmon\n\n"
		if message != nil {
			msg += *message
		}
		msg += "\n.\n"
		// And send it.
		sendmail := exec.Command("/usr/sbin/sendmail", "-t")
		stdin, _ := sendmail.StdinPipe()
		err := sendmail.Start()
		if err != nil {
			fmt.Fprintln(os.Stderr, "ERROR:", err)
		}
		io.WriteString(stdin, msg)
		sendmail.Wait()
	}
}

// Executes callback. Passes args: true/false, check's name, message.
func alert(check *Check, name *string, msg *string) {
	if check.Alert != nil {
		plugin, ok := check.Alert.(string)
		if ok { // check.Alert is a string.
			out, err := exec.Command(plugin, strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
			if err != nil {
				fmt.Fprintln(os.Stderr, "ERROR:", string(out)+err.Error())
			}
		} else { // check.Alert is a list.
			for _, i := range check.Alert.([]interface{}) {
				out, err := exec.Command(i.(string), strconv.FormatBool(check.Failed), *name, *msg).CombinedOutput()
				if err != nil {
					fmt.Fprintln(os.Stderr, "ERROR:", string(out)+err.Error())
				}
			}
		}
	}
}

// Display checks' details.
func getChecks(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != "/" { // Serve for root page only, 404 otherwise.
		w.Header().Set("Server", "jsonmon")
		http.NotFound(w, r)
		return
	}
	displayJSON(w, r, &checks)
}

// Display application version.
func getVersion(w http.ResponseWriter, r *http.Request) {
	displayJSON(w, r, &version)
}

// Output JSON.
func displayJSON(w http.ResponseWriter, r *http.Request, data interface{}) {
	h := w.Header()
	h.Set("Server", "jsonmon")
	if r.Header.Get("If-None-Match") == modified {
		delete(h, "Content-Type")
		delete(h, "Content-Length")
		w.WriteHeader(http.StatusNotModified)
	} else {
		h.Set("Cache-Control", "no-cache")
		h.Set("ETag", modified)
		h.Set("Access-Control-Allow-Origin", "*")
		h.Set("Content-Type", "application/json; charset=utf-8")
		json, _ := json.MarshalIndent(&data, "", "  ")
		w.Write(json)
		fmt.Fprintln(w, "") // Trailing newline.
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"sync"

	"github.com/zrob/boshler/archiver"
	"github.com/zrob/boshler/bosh_cli"
	"github.com/zrob/boshler/bosh_file"
	"github.com/zrob/boshler/boshio"
)

func main() {
	boshfile := parseBoshFile()
	displayCurrentTarget()
	archiveDir := getArchiveDir()

	var wg sync.WaitGroup
	wg.Add(len(boshfile.Releases) + len(boshfile.Stemcells))

	for _, release := range boshfile.Releases {
		go func(release bosh_file.Release) {
			defer wg.Done()

			cacheAndUploadRelease(release, archiveDir)
		}(release)
	}

	for _, stemcell := range boshfile.Stemcells {
		go func(stemcell bosh_file.Stemcell) {
			defer wg.Done()

			cacheAndUploadStemcell(stemcell, archiveDir)
		}(stemcell)
	}

	wg.Wait()
}

func cacheAndUploadRelease(release bosh_file.Release, archiveDir string) {
	fetcher := boshio.NewMetadataFetcher()
	archiver := archiver.NewArchiver(archiveDir)

	metadata, err := fetcher.FetchRelease(release)
	if err != nil {
		panic(err.Error())
	}

	releaseVersion := selectReleaseVersion(release, metadata)

	path, err := archiver.StoreRelease(releaseVersion)
	if err != nil {
		panic(err.Error())
	}

	fmt.Printf("Uploading %s %s\n", releaseVersion.ReleaseName(), releaseVersion.Version)
	err = bosh_cli.UploadRelease(path)
	if err != nil {
		println(err.Error())
		panic(err.Error())
	}
	fmt.Printf("\x1b[32;1mDone uploading %s %s\x1b[0m\n", releaseVersion.ReleaseName(), releaseVersion.Version)
}

func cacheAndUploadStemcell(stemcell bosh_file.Stemcell, archiveDir string) {
	fetcher := boshio.NewMetadataFetcher()
	archiver := archiver.NewArchiver(archiveDir)

	metadata, err := fetcher.FetchStemcell(stemcell.Name)
	if err != nil {
		panic(err.Error())
	}

	stemcellVersion := selectStemcellVersion(stemcell, metadata)

	path, err := archiver.StoreStemcell(stemcellVersion)
	if err != nil {
		panic(err.Error())
	}

	fmt.Printf("Uploading %s %s\n", stemcellVersion.Name, stemcellVersion.Version)
	err = bosh_cli.UploadStemcell(path)
	if err != nil {
		println(err.Error())
		panic(err.Error())
	}
	fmt.Printf("\x1b[32;1mDone uploading %s %s\x1b[0m\n", stemcellVersion.Name, stemcellVersion.Version)
}

func selectReleaseVersion(release bosh_file.Release, metadata boshio.ReleaseMetadata) boshio.ReleaseVersion {
	var releaseVersion boshio.ReleaseVersion

	if release.Version == "" {
		releaseVersion = metadata.Latest()
	} else {
		var err error
		releaseVersion, err = metadata.Version(release.Version)
		if err != nil {
			panic(err.Error())
		}
	}

	return releaseVersion
}

func selectStemcellVersion(stemcell bosh_file.Stemcell, metadata boshio.StemcellMetadata) boshio.StemcellVersion {
	var stemcellVersion boshio.StemcellVersion

	if stemcell.Version == "" {
		stemcellVersion = metadata.Latest()
	} else {
		var err error
		stemcellVersion, err = metadata.Version(stemcell.Version)
		if err != nil {
			panic(err.Error())
		}
	}

	return stemcellVersion
}

func displayCurrentTarget() {
	target, err := bosh_cli.GetTarget()
	if err != nil {
		panic(err.Error())
	}
	fmt.Println(target)
}

func parseBoshFile() bosh_file.BoshFile {
	boshfile, err := bosh_file.ParseFile("BOSHFILE")
	if strings.Contains(err.Error(), "no such file") {
		fmt.Println("Cannot find BOSHFILE. Are you sure you have it in your working directory?")
		os.Exit(1)
	}
	if err != nil {
		panic(err.Error())
	}
	return boshfile
}

func getArchiveDir() string {
	usr, err := user.Current()
	if err != nil {
		panic(err.Error())
	}
	return filepath.Join(usr.HomeDir, ".boshler")
}
<commit_msg>Check if err is nil before reading<commit_after>package main

import (
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"
	"sync"

	"github.com/zrob/boshler/archiver"
	"github.com/zrob/boshler/bosh_cli"
	"github.com/zrob/boshler/bosh_file"
	"github.com/zrob/boshler/boshio"
)

func main() {
	boshfile := parseBoshFile()
	displayCurrentTarget()
	archiveDir := getArchiveDir()

	var wg sync.WaitGroup
	wg.Add(len(boshfile.Releases) + len(boshfile.Stemcells))

	for _, release := range boshfile.Releases {
		go func(release bosh_file.Release) {
			defer wg.Done()

			cacheAndUploadRelease(release, archiveDir)
		}(release)
	}

	for _, stemcell := range boshfile.Stemcells {
		go func(stemcell bosh_file.Stemcell) {
			defer wg.Done()

			cacheAndUploadStemcell(stemcell, archiveDir)
		}(stemcell)
	}

	wg.Wait()
}

func cacheAndUploadRelease(release bosh_file.Release, archiveDir string) {
	fetcher := boshio.NewMetadataFetcher()
	archiver := archiver.NewArchiver(archiveDir)

	metadata, err := fetcher.FetchRelease(release)
	if err != nil {
		panic(err.Error())
	}

	releaseVersion := selectReleaseVersion(release, metadata)

	path, err := archiver.StoreRelease(releaseVersion)
	if err != nil {
		panic(err.Error())
	}

	fmt.Printf("Uploading %s %s\n", releaseVersion.ReleaseName(), releaseVersion.Version)
	err = bosh_cli.UploadRelease(path)
	if err != nil {
		println(err.Error())
		panic(err.Error())
	}
	fmt.Printf("\x1b[32;1mDone uploading %s %s\x1b[0m\n", releaseVersion.ReleaseName(), releaseVersion.Version)
}

func cacheAndUploadStemcell(stemcell bosh_file.Stemcell, archiveDir string) {
	fetcher := boshio.NewMetadataFetcher()
	archiver := archiver.NewArchiver(archiveDir)

	metadata, err := fetcher.FetchStemcell(stemcell.Name)
	if err != nil {
		panic(err.Error())
	}

	stemcellVersion := selectStemcellVersion(stemcell, metadata)

	path, err := archiver.StoreStemcell(stemcellVersion)
	if err != nil {
		panic(err.Error())
	}

	fmt.Printf("Uploading %s %s\n", stemcellVersion.Name, stemcellVersion.Version)
	err = bosh_cli.UploadStemcell(path)
	if err != nil {
		println(err.Error())
		panic(err.Error())
	}
	fmt.Printf("\x1b[32;1mDone uploading %s %s\x1b[0m\n", stemcellVersion.Name, stemcellVersion.Version)
}

func selectReleaseVersion(release bosh_file.Release, metadata boshio.ReleaseMetadata) boshio.ReleaseVersion {
	var releaseVersion boshio.ReleaseVersion

	if release.Version == "" {
		releaseVersion = metadata.Latest()
	} else {
		var err error
		releaseVersion, err = metadata.Version(release.Version)
		if err != nil {
			panic(err.Error())
		}
	}

	return releaseVersion
}

func selectStemcellVersion(stemcell bosh_file.Stemcell, metadata boshio.StemcellMetadata) boshio.StemcellVersion {
	var stemcellVersion boshio.StemcellVersion

	if stemcell.Version == "" {
		stemcellVersion = metadata.Latest()
	} else {
		var err error
		stemcellVersion, err = metadata.Version(stemcell.Version)
		if err != nil {
			panic(err.Error())
		}
	}

	return stemcellVersion
}

func displayCurrentTarget() {
	target, err := bosh_cli.GetTarget()
	if err != nil {
		panic(err.Error())
	}
	fmt.Println(target)
}

func parseBoshFile() bosh_file.BoshFile {
	boshfile, err := bosh_file.ParseFile("BOSHFILE")
	if err != nil {
		if strings.Contains(err.Error(), "no such file") {
			fmt.Println("Cannot find BOSHFILE. Are you sure you have it in your working directory?")
			os.Exit(1)
		} else {
			panic(err.Error())
		}
	}
	return boshfile
}

func getArchiveDir() string {
	usr, err := user.Current()
	if err != nil {
		panic(err.Error())
	}
	return filepath.Join(usr.HomeDir, ".boshler")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
)

// TODO: thorough spell checking run

// TODO: in our preprocess run, we should blank out sections that we shouldn't
//       check, like URL's. Maybe also strings in quotes?

// TODO: atom editor plugin for linter (should work with our JSON mode)

func processFile(filename string, report chan *Warning, maxWarnings int) int {
	fm, err := NewFileMap(filename)
	check(err)

	// We launch a goroutine per list of TextChecks and write any warnings to
	// our gather channel. Then we pump from the gather channel to our caller.
	// There are many things we COULD do as we gather the warnings, but
	// currently this is only used to handle the max warnings per file feature.
	wg := sync.WaitGroup{}
	gather := make(chan *Warning, 64)

	launch := func(checks []TextCheck) {
		wg.Add(1)

		go func(fm *FileMap, checks []TextCheck) {
			defer wg.Done()

			count := 0
			for _, c := range checks {
				for _, warn := range c.Match(fm) {
					gather <- warn
					count += 1
					if maxWarnings > 0 && count >= maxWarnings {
						// optimization: see below for actual maxWarnings work
						return
					}
				}
			}
		}(fm, checks)
	}

	launch(ShouldNotExist())
	launch(ShouldNotCliche())
	launch(ShouldNotRedundant())
	launch(ShouldNotProfane())
	launch(ShouldNotBias())
	launch(ShouldNotPassive())

	go func() {
		wg.Wait()
		close(gather)
	}()

	// Actually funnel warnings back to caller
	finalCount := 0

	for warn := range gather {
		report <- warn
		finalCount += 1
		if maxWarnings > 0 && finalCount >= maxWarnings {
			break
		}
	}

	return finalCount
}

func main() {
	flags := flag.NewFlagSet("tint", flag.ExitOnError)
	verbosePtr := flags.Bool("v", false, "Verbose output (written to stderr")
	sortPtr := flags.Bool("s", false, "Sort output (by file name, line number, column number")
	jsonPtr := flags.Bool("j", false, "Use JSON output instead of default")
	maxAllPtr := flags.Int("max", 0, "Maximum warnings to output")
	maxFilePtr := flags.Int("filemax", 0, "Maximum warnings per file")
	check(flags.Parse(os.Args[1:]))
	args := flags.Args()

	// If it should always be printed, we use log. If it should only be printed when
	// verbose=true, then we use verb
	var verb *log.Logger
	if *verbosePtr {
		verb = log.New(os.Stderr, "", 0)
	} else {
		verb = log.New(ioutil.Discard, "", 0)
	}

	verb.Printf("Verbose: %v\n", *verbosePtr)
	verb.Printf("Sort: %v\n", *sortPtr)

	wg := sync.WaitGroup{}
	report := make(chan *Warning, 64)

	for _, filename := range args {
		verb.Printf("FILE: %s\n", filename)
		wg.Add(1)
		go func(fn string) {
			defer wg.Done()
			processFile(fn, report, *maxFilePtr)
		}(filename)
	}

	go func() {
		wg.Wait()
		close(report)
	}()

	// Final output channel might be filtered by sort or count options
	output := report

	// Sort filter
	if *sortPtr {
		sortInput := output
		output = make(chan *Warning, 64)

		go func(out chan *Warning) {
			defer close(out)

			warnings := make([]*Warning, 0, 128)
			for warn := range sortInput {
				warnings = append(warnings, warn)
			}

			verb.Printf("Sorting %d warnings\n", len(warnings))
			sort.Sort(WarningDefSort(warnings))

			for _, warn := range warnings {
				out <- warn
			}
		}(output)
	}

	// Output all warnings
	var outputter func(w *Warning)
	if *jsonPtr {
		outputter = func(w *Warning) {
			fmt.Printf("%s\n", w.CreateJSON())
		}
	} else {
		outputter = func(w *Warning) {
			fmt.Printf("%s:%d:%d:warning: %s\n",
				w.Filename, w.Row+1, w.Col+1, msgEscape(w.Msg),
			)
		}
	}

	written := 0
	for warn := range output {
		outputter(warn)
		written += 1
		if *maxAllPtr > 0 && written >= *maxAllPtr {
			verb.Printf("Maximum warnings reached (%d)\n", written)
			break
		}
	}
}
<commit_msg>fix comment<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"sort"
	"sync"
)

// TODO: thorough spell checking run

// TODO: atom editor plugin for linter (should work with our JSON mode)

func processFile(filename string, report chan *Warning, maxWarnings int) int {
	fm, err := NewFileMap(filename)
	check(err)

	// We launch a goroutine per list of TextChecks and write any warnings to
	// our gather channel. Then we pump from the gather channel to our caller.
	// There are many things we COULD do as we gather the warnings, but
	// currently this is only used to handle the max warnings per file feature.
	wg := sync.WaitGroup{}
	gather := make(chan *Warning, 64)

	launch := func(checks []TextCheck) {
		wg.Add(1)

		go func(fm *FileMap, checks []TextCheck) {
			defer wg.Done()

			count := 0
			for _, c := range checks {
				for _, warn := range c.Match(fm) {
					gather <- warn
					count += 1
					if maxWarnings > 0 && count >= maxWarnings {
						// optimization: see below for actual maxWarnings work
						return
					}
				}
			}
		}(fm, checks)
	}

	launch(ShouldNotExist())
	launch(ShouldNotCliche())
	launch(ShouldNotRedundant())
	launch(ShouldNotProfane())
	launch(ShouldNotBias())
	launch(ShouldNotPassive())

	go func() {
		wg.Wait()
		close(gather)
	}()

	// Actually funnel warnings back to caller
	finalCount := 0

	for warn := range gather {
		report <- warn
		finalCount += 1
		if maxWarnings > 0 && finalCount >= maxWarnings {
			break
		}
	}

	return finalCount
}

func main() {
	flags := flag.NewFlagSet("tint", flag.ExitOnError)
	verbosePtr := flags.Bool("v", false, "Verbose output (written to stderr")
	sortPtr := flags.Bool("s", false, "Sort output (by file name, line number, column number")
	jsonPtr := flags.Bool("j", false, "Use JSON output instead of default")
	maxAllPtr := flags.Int("max", 0, "Maximum warnings to output")
	maxFilePtr := flags.Int("filemax", 0, "Maximum warnings per file")
	check(flags.Parse(os.Args[1:]))
	args := flags.Args()

	// If it should always be printed, we use log. If it should only be printed when
	// verbose=true, then we use verb
	var verb *log.Logger
	if *verbosePtr {
		verb = log.New(os.Stderr, "", 0)
	} else {
		verb = log.New(ioutil.Discard, "", 0)
	}

	verb.Printf("Verbose: %v\n", *verbosePtr)
	verb.Printf("Sort: %v\n", *sortPtr)

	wg := sync.WaitGroup{}
	report := make(chan *Warning, 64)

	for _, filename := range args {
		verb.Printf("FILE: %s\n", filename)
		wg.Add(1)
		go func(fn string) {
			defer wg.Done()
			processFile(fn, report, *maxFilePtr)
		}(filename)
	}

	go func() {
		wg.Wait()
		close(report)
	}()

	// Final output channel might be filtered by sort or count options
	output := report

	// Sort filter
	if *sortPtr {
		sortInput := output
		output = make(chan *Warning, 64)

		go func(out chan *Warning) {
			defer close(out)

			warnings := make([]*Warning, 0, 128)
			for warn := range sortInput {
				warnings = append(warnings, warn)
			}

			verb.Printf("Sorting %d warnings\n", len(warnings))
			sort.Sort(WarningDefSort(warnings))

			for _, warn := range warnings {
				out <- warn
			}
		}(output)
	}

	// Output all warnings
	var outputter func(w *Warning)
	if *jsonPtr {
		outputter = func(w *Warning) {
			fmt.Printf("%s\n", w.CreateJSON())
		}
	} else {
		outputter = func(w *Warning) {
			fmt.Printf("%s:%d:%d:warning: %s\n",
				w.Filename, w.Row+1, w.Col+1, msgEscape(w.Msg),
			)
		}
	}

	written := 0
	for warn := range output {
		outputter(warn)
		written += 1
		if *maxAllPtr > 0 && written >= *maxAllPtr {
			verb.Printf("Maximum warnings reached (%d)\n", written)
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
)

func main() {
	if len(os.Args) < 2 || os.Args[1][0] != '/' {
		printUsage()
		return
	}

	startPoint := os.Args[1]
	storage, err := extendScanWays(startPoint)
	//	fmt.Println("SCAN PLAN:")
	//	extendPrint(storage)
	//	fmt.Println()
	//	fmt.Println()
	//	fmt.Println()
	if err != nil {
		panic(err)
	}
	plan := extendPlan(storage)
	extendPrint(plan)

	if len(os.Args) > 2 && os.Args[2] == "--do" {
		if extendDo(plan) {
			fmt.Println("NEED REBOOT AND START ME ONCE AGAIN.")
		} else {
			fmt.Println("OK")
		}
	}
}

func printUsage() {
	fmt.Printf(`Usage: %v <start_point> [--do]
start_point - path to block device or file system to extend
--do - do extending. Without it - print extend plan only.

The program print to stdout:
OK - if extended compele.
NEED REBOOT AND START ME ONCE AGAIN. - if need reboot and run command with same parameters
`, os.Args[0])
}

func cmd(cmd string, args ...string) (stdout, errout string, err error) {
	log.Printf("CMD: '%v' '%v'", cmd, strings.Join(args, "' '"))
	bufStd := &bytes.Buffer{}
	bufErr := &bytes.Buffer{}
	command := exec.Command(cmd, args...)
	command.Stdout = bufStd
	command.Stderr = bufErr
	err = command.Run()
	return bufStd.String(), bufErr.String(), err
}

/*
execute command with args and return slice of strings.TrimSpace(line). Empty lines removed.
Возвращает stdout команды, разделенный на строки. У каждой строки пустые символы в начале/конце обрезаны, пустые строки
удалены. stderr и код ответа не учитываются
*/
func cmdTrimLines(command string, args ...string) []string {
	res, _, _ := cmd(command, args...)
	lines := make([]string, 0)
	for _, line := range strings.Split(res, "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		} else {
			lines = append(lines, line)
		}
	}
	return lines
}
<commit_msg>turn off log every command<commit_after>package main

import (
	"bytes"
	"fmt"
	"os"
	"os/exec"
	"strings"
)

func main() {
	if len(os.Args) < 2 || os.Args[1][0] != '/' {
		printUsage()
		return
	}

	startPoint := os.Args[1]
	storage, err := extendScanWays(startPoint)
	//	fmt.Println("SCAN PLAN:")
	//	extendPrint(storage)
	//	fmt.Println()
	//	fmt.Println()
	//	fmt.Println()
	if err != nil {
		panic(err)
	}
	plan := extendPlan(storage)
	extendPrint(plan)

	if len(os.Args) > 2 && os.Args[2] == "--do" {
		if extendDo(plan) {
			fmt.Println("NEED REBOOT AND START ME ONCE AGAIN.")
		} else {
			fmt.Println("OK")
		}
	}
}

func printUsage() {
	fmt.Printf(`Usage: %v <start_point> [--do]
start_point - path to block device or file system to extend
--do - do extending. Without it - print extend plan only.

The program print to stdout:
OK - if extended compele.
NEED REBOOT AND START ME ONCE AGAIN. - if need reboot and run command with same parameters
`, os.Args[0])
}

func cmd(cmd string, args ...string) (stdout, errout string, err error) {
	//log.Printf("CMD: '%v' '%v'", cmd, strings.Join(args, "' '"))
	bufStd := &bytes.Buffer{}
	bufErr := &bytes.Buffer{}
	command := exec.Command(cmd, args...)
	command.Stdout = bufStd
	command.Stderr = bufErr
	err = command.Run()
	return bufStd.String(), bufErr.String(), err
}

/*
execute command with args and return slice of strings.TrimSpace(line). Empty lines removed.
Возвращает stdout команды, разделенный на строки. У каждой строки пустые символы в начале/конце обрезаны, пустые строки
удалены. stderr и код ответа не учитываются
*/
func cmdTrimLines(command string, args ...string) []string {
	res, _, _ := cmd(command, args...)
	lines := make([]string, 0)
	for _, line := range strings.Split(res, "\n") {
		line = strings.TrimSpace(line)
		if line == "" {
			continue
		} else {
			lines = append(lines, line)
		}
	}
	return lines
}
<|endoftext|>


<commit_before>package main

import (
	"time"
	"encoding/json"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	//"io"
	"fmt"
	"database/sql"
	_ "github.com/lib/pq"
	"gopkg.in/antage/eventsource.v1"
)

var conn *sql.DB
var es eventsource.EventSource

func SetHeaders(w *http.ResponseWriter) {
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Content-Type", "application/json")
}

func GetBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM blog")
	data := []BlogPost{}
	for rows.Next() {
		post := BlogPost{}
		rows.Scan(&post.Id, &post.Titel, &post.Text, &post.Auteur, &post.Img_url, &post.Ctime, &post.Image)
		data = append(data, post)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT * FROM blog WHERE id = $1 LIMIT 1", ps.ByName("id"))
	data := BlogPost{}
	row.Scan(&data.Id, &data.Titel, &data.Text, &data.Auteur, &data.Img_url, &data.Ctime, &data.Image)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM spindata ORDER BY tijd DESC LIMIT 1")
	spin := SpinData{}
	rows.Next()
	rows.Scan(&spin.Id, &spin.Tijd, &spin.Mode, &spin.Hellingsgraad, &spin.Snelheid, &spin.Batterij, &spin.BallonCount)
	buf,_ := json.Marshal(spin)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinBatterij(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT batterij FROM spindata ORDER BY tijd DESC LIMIT 1")
	var data int
	row.Scan(&data)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinMode(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT mode FROM spindata ORDER BY tijd DESC LIMIT 1")
	var data string
	row.Scan(&data)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM spindata")
	data := []SpinData{}
	for rows.Next() {
		spin := SpinData{}
		rows.Scan(&spin.Id, &spin.Tijd, &spin.Mode, &spin.Hellingsgraad, &spin.Snelheid, &spin.Batterij, &spin.BallonCount)
		data = append(data, spin)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinBatterij(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT batterij FROM spindata")
	data := make([]int, 0)
	var scanInt int
	for rows.Next() {
		rows.Scan(&scanInt)
		data = append(data, scanInt)
	}
	buf,_ := json.Marshal(data)
	fmt.Printf(string(buf))
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinMode(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT mode FROM spindata")
	data := make([]string, 0)
	var scanStr string
	for rows.Next() {
		rows.Scan(&scanStr)
		data = append(data, scanStr)
	}
	buf,_ := json.Marshal(data)
	fmt.Printf(string(buf))
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT * FROM servodata ORDER BY tijd DESC LIMIT 1")
	servo := ServoData{}
	row.Scan(&servo.Id, &servo.ServoId, &servo.Tijd, &servo.Voltage, &servo.Positie, &servo.Load, &servo.Temperatuur)
	buf,_ := json.Marshal(servo)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM servodata")
	data := []ServoData{}
	for rows.Next() {
		servo := ServoData{}
		rows.Scan(&servo.Id, &servo.ServoId, &servo.Tijd, &servo.Voltage, &servo.Positie, &servo.Load, &servo.Temperatuur)
		data = append(data, servo)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLogs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM logs")
	data := []LogData{}
	for rows.Next() {
		log := LogData{}
		rows.Scan(&log.Id, &log.Log)
		data = append(data, log)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func Test(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	buf,_ := json.Marshal("test")
	SetHeaders(&w)
	w.Write(buf)
}

func PostBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	//r.ParseMultipartForm(32 << 20)
	/*file, handler, err := r.FormFile("uploadfile")
	defer file.Close()
	if err == nil {
		fmt.Fprintf(w, "%v", handler.Header)
		f, err := os.OpenFile("./img/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			w.WriteHeader(500)
			return
		}
		defer f.Close()
		io.Copy(f, file)
	}

	err = nil*/

	//_,err := conn.Query("INSERT INTO blog (titel, text, auteur, ctime, image) VALUES ($1, $2, $3, $4, $5)", r.FormValue("titel"), r.FormValue("text"), r.FormValue("auteur"), time.Now(), "http://idp-api.herokuapp.com/img/"+handler.Filename)
	_,err := conn.Query("INSERT INTO blog (titel, text, auteur, ctime) VALUES ($1, $2, $3, $4)", r.FormValue("onderwerp"), r.FormValue("bericht"), r.FormValue("naam"), time.Now())
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(201)
	w.Write([]byte("<meta http-equiv=\"refresh\" content=\"1; url=http://knightspider.herokuapp.com/#/blog\">successful"))
}

func PostSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	err := r.ParseForm()
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	mode := r.PostFormValue("mode")
	hellingsgraad := r.PostFormValue("hellingsgraad")
	if hellingsgraad == "" {
		hellingsgraad = "0"
	}
	snelheid := r.PostFormValue("snelheid")
	if snelheid == "" {
		snelheid = "0"
	}
	batterij := r.PostFormValue("batterij")
	if batterij == "" {
		batterij = "0"
	}
	balloncount := r.PostFormValue("ballonCount")
	if balloncount == "" {
		balloncount = "0"
	}
	_,err = conn.Query("INSERT INTO spindata (tijd, mode, hellingsgraad, snelheid, batterij, balloncount) VALUES ($1, $2, $3, $4, $5, $6)", time.Now(),
		mode, hellingsgraad, snelheid, batterij, balloncount)
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("mode = %s, hellingsgraad = %s, batterij = %s, balloncount = %s", mode, hellingsgraad, batterij, balloncount)))
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(201)
	w.Write([]byte(fmt.Sprintf("mode = %s, hellingsgraad = %s, batterij = %s, balloncount = %s", mode, hellingsgraad, batterij, balloncount)))
	w.Write([]byte("successful"))
}

func PostServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	_,err := conn.Query("INSERT INTO servodata (servo_id, tijd, voltage, positie, load, temperatuur) VALUES ($1, $2, $3, $4, $5, $6)",
		r.FormValue("servo_id"), time.Now(), r.FormValue("voltage"), r.FormValue("positie"), r.FormValue("load"), r.FormValue("Temperatuur"))
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(201)
	w.Write([]byte("successful"))
}

func PostLog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	_,err := conn.Query("INSERT INTO logs (log) VALUES ($1)",
		r.FormValue("log"))
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	es.SendEventMessage(r.FormValue("log"), "log", "")
	w.WriteHeader(201)
	w.Write([]byte(r.FormValue("log")))
}

func Head(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	SetHeaders(&w)
	w.WriteHeader(204)
}

func main() {
	conn,_ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
	defer conn.Close()

	es = eventsource.New(
		&eventsource.Settings{
			Timeout: 5 * time.Second,
			CloseOnTimeout: false,
			IdleTimeout: 30 * time.Minute,
		},
		func(req *http.Request) [][]byte {
			return [][]byte{
				[]byte("X-Accel-Buffering: no"),
				[]byte("Access-Control-Allow-Origin: *"),
			}
		},
	)
	defer es.Close()

	router := httprouter.New()
	router.HEAD("/*path", Head)
	router.GET("/test", Test)
	router.GET("/blog", GetBlog)
	router.GET("/blog/:id", GetPost)
	router.GET("/spin/latest", GetLatestSpinData)
	router.GET("/spin/latest/batterij", GetLatestSpinBatterij)
	router.GET("/spin/latest/mode", GetLatestSpinMode)
	router.GET("/spin/archive", GetArchivedSpinData)
	router.GET("/spin/archive/batterij", GetArchivedSpinBatterij)
	router.GET("/spin/archive/mode", GetArchivedSpinMode)
	router.GET("/servo/latest", GetLatestServoData)
	router.GET("/servo/archive", GetArchivedServoData)
	router.GET("/log", GetLogs)
	router.POST("/blog", PostBlog)
	router.POST("/spin", PostSpinData)
	router.POST("/servo", PostServoData)
	router.POST("/log", PostLog)

	http.Handle("/subscribe", es)
	http.Handle("/", router)

	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}<commit_msg>postformvalue<commit_after>package main

import (
	"time"
	"encoding/json"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"log"
	"os"
	//"io"
	"fmt"
	"database/sql"
	_ "github.com/lib/pq"
	"gopkg.in/antage/eventsource.v1"
)

var conn *sql.DB
var es eventsource.EventSource

func SetHeaders(w *http.ResponseWriter) {
	(*w).Header().Set("Access-Control-Allow-Origin", "*")
	(*w).Header().Set("Content-Type", "application/json")
}

func GetBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM blog")
	data := []BlogPost{}
	for rows.Next() {
		post := BlogPost{}
		rows.Scan(&post.Id, &post.Titel, &post.Text, &post.Auteur, &post.Img_url, &post.Ctime, &post.Image)
		data = append(data, post)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetPost(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT * FROM blog WHERE id = $1 LIMIT 1", ps.ByName("id"))
	data := BlogPost{}
	row.Scan(&data.Id, &data.Titel, &data.Text, &data.Auteur, &data.Img_url, &data.Ctime, &data.Image)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM spindata ORDER BY tijd DESC LIMIT 1")
	spin := SpinData{}
	rows.Next()
	rows.Scan(&spin.Id, &spin.Tijd, &spin.Mode, &spin.Hellingsgraad, &spin.Snelheid, &spin.Batterij, &spin.BallonCount)
	buf,_ := json.Marshal(spin)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinBatterij(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT batterij FROM spindata ORDER BY tijd DESC LIMIT 1")
	var data int
	row.Scan(&data)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestSpinMode(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT mode FROM spindata ORDER BY tijd DESC LIMIT 1")
	var data string
	row.Scan(&data)
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM spindata")
	data := []SpinData{}
	for rows.Next() {
		spin := SpinData{}
		rows.Scan(&spin.Id, &spin.Tijd, &spin.Mode, &spin.Hellingsgraad, &spin.Snelheid, &spin.Batterij, &spin.BallonCount)
		data = append(data, spin)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinBatterij(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT batterij FROM spindata")
	data := make([]int, 0)
	var scanInt int
	for rows.Next() {
		rows.Scan(&scanInt)
		data = append(data, scanInt)
	}
	buf,_ := json.Marshal(data)
	fmt.Printf(string(buf))
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedSpinMode(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT mode FROM spindata")
	data := make([]string, 0)
	var scanStr string
	for rows.Next() {
		rows.Scan(&scanStr)
		data = append(data, scanStr)
	}
	buf,_ := json.Marshal(data)
	fmt.Printf(string(buf))
	SetHeaders(&w)
	w.Write(buf)
}

func GetLatestServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	row := conn.QueryRow("SELECT * FROM servodata ORDER BY tijd DESC LIMIT 1")
	servo := ServoData{}
	row.Scan(&servo.Id, &servo.ServoId, &servo.Tijd, &servo.Voltage, &servo.Positie, &servo.Load, &servo.Temperatuur)
	buf,_ := json.Marshal(servo)
	SetHeaders(&w)
	w.Write(buf)
}

func GetArchivedServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM servodata")
	data := []ServoData{}
	for rows.Next() {
		servo := ServoData{}
		rows.Scan(&servo.Id, &servo.ServoId, &servo.Tijd, &servo.Voltage, &servo.Positie, &servo.Load, &servo.Temperatuur)
		data = append(data, servo)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func GetLogs(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	rows,_ := conn.Query("SELECT * FROM logs")
	data := []LogData{}
	for rows.Next() {
		log := LogData{}
		rows.Scan(&log.Id, &log.Log)
		data = append(data, log)
	}
	buf,_ := json.Marshal(data)
	SetHeaders(&w)
	w.Write(buf)
}

func Test(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	buf,_ := json.Marshal("test")
	SetHeaders(&w)
	w.Write(buf)
}

func PostBlog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	//r.ParseMultipartForm(32 << 20)
	/*file, handler, err := r.FormFile("uploadfile")
	defer file.Close()
	if err == nil {
		fmt.Fprintf(w, "%v", handler.Header)
		f, err := os.OpenFile("./img/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
		if err != nil {
			w.WriteHeader(500)
			return
		}
		defer f.Close()
		io.Copy(f, file)
	}

	err = nil*/

	//_,err := conn.Query("INSERT INTO blog (titel, text, auteur, ctime, image) VALUES ($1, $2, $3, $4, $5)", r.FormValue("titel"), r.FormValue("text"), r.FormValue("auteur"), time.Now(), "http://idp-api.herokuapp.com/img/"+handler.Filename)
	_,err := conn.Query("INSERT INTO blog (titel, text, auteur, ctime) VALUES ($1, $2, $3, $4)", r.FormValue("onderwerp"), r.FormValue("bericht"), r.FormValue("naam"), time.Now())
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	w.Header().Set("Content-Type", "text/html")
	w.WriteHeader(201)
	w.Write([]byte("<meta http-equiv=\"refresh\" content=\"1; url=http://knightspider.herokuapp.com/#/blog\">successful"))
}

func PostSpinData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	mode := r.PostFormValue("mode")
	hellingsgraad := r.PostFormValue("hellingsgraad")
	if hellingsgraad == "" {
		hellingsgraad = "0"
	}
	snelheid := r.PostFormValue("snelheid")
	if snelheid == "" {
		snelheid = "0"
	}
	batterij := r.PostFormValue("batterij")
	if batterij == "" {
		batterij = "0"
	}
	balloncount := r.PostFormValue("ballonCount")
	if balloncount == "" {
		balloncount = "0"
	}
	_,err = conn.Query("INSERT INTO spindata (tijd, mode, hellingsgraad, snelheid, batterij, balloncount) VALUES ($1, $2, $3, $4, $5, $6)", time.Now(),
		mode, hellingsgraad, snelheid, batterij, balloncount)
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(fmt.Sprintf("mode = %s, hellingsgraad = %s, batterij = %s, balloncount = %s", mode, hellingsgraad, batterij, balloncount)))
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(201)
	w.Write([]byte(fmt.Sprintf("mode = %s, hellingsgraad = %s, batterij = %s, balloncount = %s", mode, hellingsgraad, batterij, balloncount)))
	w.Write([]byte("successful"))
}

func PostServoData(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	_,err := conn.Query("INSERT INTO servodata (servo_id, tijd, voltage, positie, load, temperatuur) VALUES ($1, $2, $3, $4, $5, $6)",
		r.FormValue("servo_id"), time.Now(), r.FormValue("voltage"), r.FormValue("positie"), r.FormValue("load"), r.FormValue("Temperatuur"))
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	w.WriteHeader(201)
	w.Write([]byte("successful"))
}

func PostLog(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	_,err := conn.Query("INSERT INTO logs (log) VALUES ($1)",
		r.FormValue("log"))
	if err != nil {
		w.WriteHeader(500)
		w.Write([]byte(err.Error()))
		return
	}
	es.SendEventMessage(r.FormValue("log"), "log", "")
	w.WriteHeader(201)
	w.Write([]byte(r.FormValue("log")))
}

func Head(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
	SetHeaders(&w)
	w.WriteHeader(204)
}

func main() {
	conn,_ = sql.Open("postgres", os.Getenv("DATABASE_URL"))
	defer conn.Close()

	es = eventsource.New(
		&eventsource.Settings{
			Timeout: 5 * time.Second,
			CloseOnTimeout: false,
			IdleTimeout: 30 * time.Minute,
		},
		func(req *http.Request) [][]byte {
			return [][]byte{
				[]byte("X-Accel-Buffering: no"),
				[]byte("Access-Control-Allow-Origin: *"),
			}
		},
	)
	defer es.Close()

	router := httprouter.New()
	router.HEAD("/*path", Head)
	router.GET("/test", Test)
	router.GET("/blog", GetBlog)
	router.GET("/blog/:id", GetPost)
	router.GET("/spin/latest", GetLatestSpinData)
	router.GET("/spin/latest/batterij", GetLatestSpinBatterij)
	router.GET("/spin/latest/mode", GetLatestSpinMode)
	router.GET("/spin/archive", GetArchivedSpinData)
	router.GET("/spin/archive/batterij", GetArchivedSpinBatterij)
	router.GET("/spin/archive/mode", GetArchivedSpinMode)
	router.GET("/servo/latest", GetLatestServoData)
	router.GET("/servo/archive", GetArchivedServoData)
	router.GET("/log", GetLogs)
	router.POST("/blog", PostBlog)
	router.POST("/spin", PostSpinData)
	router.POST("/servo", PostServoData)
	router.POST("/log", PostLog)

	http.Handle("/subscribe", es)
	http.Handle("/", router)

	port := os.Getenv("PORT")
	if port == "" {
		port = "3000"
	}
	fmt.Printf("Starting server at localhost:%s...", port)
	log.Fatal(http.ListenAndServe(":"+port, nil))
}<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
)

const (
	VERSION = "0.1"
)

var options struct {
	path         string
	address      string
	port         int
	printVersion bool
}

func main() {
	flag.StringVar(&options.path, "path", "", "Optional path to serve static files from.")
	flag.StringVar(&options.address, "address", "127.0.0.1", "Address to listen on.")
	flag.IntVar(&options.port, "port", 3333, "Port to listen on.")
	flag.BoolVar(&options.printVersion, "version", false, "Print version and exit.")
	flag.Parse()

	if options.printVersion {
		fmt.Printf("rehearse v%s\n", VERSION)
		os.Exit(0)
	}

	stubHandler := NewStubHandler()
	if options.path != "" {
		stubHandler.fallbackHandler = http.FileServer(http.Dir(options.path))
	}
	http.Handle("/", stubHandler)

	var err error
	err = http.ListenAndServe(fmt.Sprintf("%s:%d", options.address, options.port), stubHandler)
	if err != nil {
		log.Fatalf("Unable to start server: %v\n", err)
	}
}
<commit_msg>Use 3 digit versioning<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
)

const (
	VERSION = "0.1.0"
)

var options struct {
	path         string
	address      string
	port         int
	printVersion bool
}

func main() {
	flag.StringVar(&options.path, "path", "", "Optional path to serve static files from.")
	flag.StringVar(&options.address, "address", "127.0.0.1", "Address to listen on.")
	flag.IntVar(&options.port, "port", 3333, "Port to listen on.")
	flag.BoolVar(&options.printVersion, "version", false, "Print version and exit.")
	flag.Parse()

	if options.printVersion {
		fmt.Printf("rehearse v%s\n", VERSION)
		os.Exit(0)
	}

	stubHandler := NewStubHandler()
	if options.path != "" {
		stubHandler.fallbackHandler = http.FileServer(http.Dir(options.path))
	}
	http.Handle("/", stubHandler)

	var err error
	err = http.ListenAndServe(fmt.Sprintf("%s:%d", options.address, options.port), stubHandler)
	if err != nil {
		log.Fatalf("Unable to start server: %v\n", err)
	}
}
<|endoftext|>


<commit_before>// http://en.wikipedia.org/wiki/Conway's_Game_of_Life

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	life "github.com/mdwhatcott/golife/life"
	"github.com/mdwhatcott/golife/ui"
	"github.com/smartystreets/configo"
)

func main() {
	reader := configo.NewReader(configo.FromCommandLineFlags().
		RegisterBool("console", "If 'true', run the simulation in the command line."))

	grid := life.New(gliderGun)

	if reader.Bool("console") {
		console(grid)
	} else {
		html(grid)
	}
}

func console(grid *life.Grid) {
	for {
		fmt.Print(clearScreen)
		fmt.Print(ui.Console(grid))
		grid.Scan()
		time.Sleep(time.Millisecond * 25)
	}
}

func html(grid *life.Grid) {
	http.HandleFunc("/", func(response http.ResponseWriter, _ *http.Request) {
		response.Header().Set("Content-Type", "text/html")
		fmt.Fprint(response, UI)
	})

	http.HandleFunc("/state", func(response http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(response, ui.HTML(grid))
		grid.Scan()
	})

	fmt.Println("Serving http at: ':8081'")
	if err := http.ListenAndServe(":8081", nil); err != nil {
		log.Fatal(err)
	}
}

const clearScreen = "\033[2J\033[H"

const gliderGun = `
------------------------x--------------------
----------------------x-x--------------------
------------xx------xx------------xx---------
-----------x---x----xx------------xx---------
xx--------x-----x---xx-----------------------
xx--------x---x-xx----x-x--------------------
----------x-----x-------x--------------------
-----------x---x-----------------------------
------------xx-------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
`

const UI = `<html>
<head>
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script type="text/javascript">
    jQuery(document).ready(function() {
      setInterval(function() {
        $.ajax("/state", {
          success: function(data) {
            $('body').html(data);
          }
        });
      }, 75)
    });
  </script>
</head>
<body style="font-family: monospace;">
</body>
</html>
`
<commit_msg>No need for an alias on the import statement.<commit_after>// http://en.wikipedia.org/wiki/Conway's_Game_of_Life

package main

import (
	"fmt"
	"log"
	"net/http"
	"time"

	"github.com/mdwhatcott/golife/life"
	"github.com/mdwhatcott/golife/ui"
	"github.com/smartystreets/configo"
)

func main() {
	reader := configo.NewReader(configo.FromCommandLineFlags().
		RegisterBool("console", "If 'true', run the simulation in the command line."))

	grid := life.New(gliderGun)

	if reader.Bool("console") {
		console(grid)
	} else {
		html(grid)
	}
}

func console(grid *life.Grid) {
	for {
		fmt.Print(clearScreen)
		fmt.Print(ui.Console(grid))
		grid.Scan()
		time.Sleep(time.Millisecond * 25)
	}
}

func html(grid *life.Grid) {
	http.HandleFunc("/", func(response http.ResponseWriter, _ *http.Request) {
		response.Header().Set("Content-Type", "text/html")
		fmt.Fprint(response, UI)
	})

	http.HandleFunc("/state", func(response http.ResponseWriter, _ *http.Request) {
		fmt.Fprint(response, ui.HTML(grid))
		grid.Scan()
	})

	fmt.Println("Serving http at: ':8081'")
	if err := http.ListenAndServe(":8081", nil); err != nil {
		log.Fatal(err)
	}
}

const clearScreen = "\033[2J\033[H"

const gliderGun = `
------------------------x--------------------
----------------------x-x--------------------
------------xx------xx------------xx---------
-----------x---x----xx------------xx---------
xx--------x-----x---xx-----------------------
xx--------x---x-xx----x-x--------------------
----------x-----x-------x--------------------
-----------x---x-----------------------------
------------xx-------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
---------------------------------------------
`

const UI = `<html>
<head>
  <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
  <script type="text/javascript">
    jQuery(document).ready(function() {
      setInterval(function() {
        $.ajax("/state", {
          success: function(data) {
            $('body').html(data);
          }
        });
      }, 75)
    });
  </script>
</head>
<body style="font-family: monospace;">
</body>
</html>
`
<|endoftext|>


<commit_before>package keystoneplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/iopred/bruxism"
)

type keystoneDungeonID int

const (
	BRH keystoneDungeonID = iota
	COS
	DHT
	EOA
	HOV
	MOS
	NL
	ARC
	VOW
)

type keystoneDungeon struct {
	Name    string
	Aliases []string
}

var dungeons map[keystoneDungeonID]*keystoneDungeon = map[keystoneDungeonID]*keystoneDungeon{
	BRH: &keystoneDungeon{
		Name:    "Black Rook Hold",
		Aliases: []string{"black rook hold", "brh", "black rook", "hold"},
	},
	COS: &keystoneDungeon{
		Name:    "Court of Stars",
		Aliases: []string{"court of stars", "cos", "court"},
	},
	DHT: &keystoneDungeon{
		Name:    "Darkheart Thicket",
		Aliases: []string{"darkheart thicket", "dht", "thicket", "darkheart"},
	},
	EOA: &keystoneDungeon{
		Name:    "Eye of Azshara",
		Aliases: []string{"eye of azshara", "eoa", "eye", "azshara"},
	},
	HOV: &keystoneDungeon{
		Name:    "Halls of Valor",
		Aliases: []string{"halls of valor", "hall of valor", "hov", "halls"},
	},
	MOS: &keystoneDungeon{
		Name:    "Maw of Souls",
		Aliases: []string{"maw of souls", "mos", "maw"},
	},
	NL: &keystoneDungeon{
		Name:    "Neltharion's Lair",
		Aliases: []string{"neltharion's lair", "nl", "neltharions lair", "nel", "nelth", "lair"},
	},
	ARC: &keystoneDungeon{
		Name:    "The Arcway",
		Aliases: []string{"the arcway", "arc", "arcway"},
	},
	VOW: &keystoneDungeon{
		Name:    "Vault of the Wardens",
		Aliases: []string{"vault of the wardens", "vow", "vault", "warden", "wardens"},
	},
}

type keystone struct {
	User      string
	Dungeon   keystoneDungeonID
	Level     int
	Depleted  bool
	Modifiers []string
}

func (k *keystone) String() string {
	str := fmt.Sprintf("Level %d **%s**", k.Level, dungeons[k.Dungeon].Name)
	if len(k.Modifiers) != 0 {
		str += " *(" + strings.Join(k.Modifiers, ", ") + ")*"
	}
	if k.Depleted {
		str += " - Depleted"
	}
	return str
}

type keystoneChannel struct {
	Users        map[string]*keystone
	LastModified time.Time
}

var location *time.Location

func init() {
	var err error
	location, err = time.LoadLocation("America/Los_Angeles")
	if err != nil {
		location = time.Now().Location()
	}
}

func lastTuesday(t time.Time) time.Time {
	year, month, day := t.Date()
	t = time.Date(year, month, day, 0, 0, 0, 0, location)
	for t.Weekday() != time.Tuesday {
		t = t.Add(-24 * time.Hour)
	}

	return t
}

func (c *keystoneChannel) check() {
	if time.Now().After(lastTuesday(c.LastModified).Add(24 * 7 * time.Hour)) {
		c.Users = map[string]*keystone{}
	}
}

func (c *keystoneChannel) add(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, userName, query string) bool {
	query = strings.ToLower(query)
	for dungeonID, dungeon := range dungeons {
		for _, alias := range dungeon.Aliases {
			if strings.Index(query, alias+" ") == 0 {
				query = query[len(alias)+1:]
				if len(query) == 0 {
					return false
				}

				parts := strings.Split(query, " ")

				level, err := strconv.Atoi(parts[0])
				if err != nil {
					return false
				}

				parts = parts[1:]

				depleted := false
				modifiers := []string{}

				for _, part := range parts {
					if strings.Index(part, "deplete") == 0 {
						depleted = true
					} else {
						modifiers = append(modifiers, part)
					}
				}

				c.Users[message.UserID()] = &keystone{
					User:      userName,
					Dungeon:   dungeonID,
					Level:     level,
					Depleted:  depleted,
					Modifiers: modifiers,
				}
				c.LastModified = time.Now()

				return true
			}
		}
	}
	return false
}

type keystoneList []*keystone

// Len is part of sort.Interface.
func (s keystoneList) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s keystoneList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s keystoneList) Less(i, j int) bool {
	if s[i].Level == s[j].Level {
		return dungeons[s[i].Dungeon].Name > dungeons[s[j].Dungeon].Name
	}
	return s[i].Level > s[j].Level
}

func (c *keystoneChannel) list(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if len(c.Users) == 0 {
		service.SendMessage(message.Channel(), "No keystones have been set this week.")
		return
	}

	keystones := keystoneList{}
	for _, keystone := range c.Users {
		keystones = append(keystones, keystone)
	}

	sort.Sort(keystones)

	content := ""
	for _, keystone := range keystones {
		if len(content) != 0 {
			content += "\n"
		}
		content += keystone.String()
		content += " - " + keystone.User
	}
	service.SendMessage(message.Channel(), content)
}

type keystonePlugin struct {
	sync.RWMutex
	Channels map[string]*keystoneChannel
}

// Name returns the name of the plugin.
func (p *keystonePlugin) Name() string {
	return "Keystone"
}

// Load will load plugin state from a byte array.
func (p *keystonePlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	if p.Channels == nil {
		p.Channels = make(map[string]*keystoneChannel)
	}

	return nil
}

// Save will save plugin state to a byte array.
func (p *keystonePlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *keystonePlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	if p.Channels[message.Channel()] == nil {
		if service.IsModerator(message) {
			return []string{
				"`keystone <start|stop>` - Start or stop WoW Mythic Keystones.",
			}
		}
		return nil
	}

	if !detailed {
		return []string{
			"`keystone <set <dungeon> <level> [modifiers]|list|deplete|undeplete>` - WoW Mythic Keystones.",
		}
	}

	return []string{
		"`keystone set<dungeon> <level> [modifiers]` - Set your keystone for this week.",
		"`keystone list` - Lists all this weeks keystones.",
		"`keystone deplete` - Depletes your keystone.",
		"Examples:",
		"`keystone set hov 5 teeming` - Adds a Level 5 Halls of Valor keystone with teeming.",
		"`keystone set eye of azshara 2 depleted` - Adds a depleted Level 2 Eye of Azshara keystone.",
	}
}

// Message handler.
func (p *keystonePlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if !service.IsMe(message) {
		messageChannel := message.Channel()

		var userName string
		discord, ok := service.(*bruxism.Discord)
		if ok {
			userName = discord.Nickname(message)
		} else {
			userName = message.UserName()
		}

		if service.IsModerator(message) && (bruxism.MatchesCommand(service, "start", message) || bruxism.MatchesCommand(service, "stop", message)) {
			p.Lock()
			defer p.Unlock()

			if bruxism.MatchesCommand(service, "start", message) {
				p.Channels[messageChannel] = &keystoneChannel{
					Users: map[string]*keystone{},
				}
				service.SendMessage(messageChannel, "This channel is now tracking mythic keystones.")
			} else {
				delete(p.Channels, messageChannel)
				service.SendMessage(messageChannel, "This channel is no longer tracking mythic keystones.")
			}
		} else if channel, ok := p.Channels[messageChannel]; ok {
			channel.check()

			if bruxism.MatchesCommand(service, "set", message) {
				query, parts := bruxism.ParseCommand(service, message)
				if len(parts) > 2 && channel.add(bot, service, message, userName, query) {
					channel.list(bot, service, message)
				} else {
					service.SendMessage(messageChannel, "Invalid keystone. Eg: `keystone set hall of valor 3 sanguine`")
				}
			} else if bruxism.MatchesCommand(service, "list", message) {
				channel.list(bot, service, message)
			} else if bruxism.MatchesCommand(service, "deplete", message) {
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = true
					keystone.User = userName
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone depleted: %s", keystone.String()))
				}
			} else if bruxism.MatchesCommand(service, "undeplete", message) {
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = false
					keystone.User = userName
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone undepleted: %s", keystone.String()))
				}
			}
		}
	}
}

// Stats will return the stats for a plugin.
func (p *keystonePlugin) Stats(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) []string {
	return nil
}

// New will create a new wormhole plugin.
func New() bruxism.Plugin {
	return &keystonePlugin{}
}
<commit_msg>Fix keystone plugin.<commit_after>package keystoneplugin

import (
	"encoding/json"
	"fmt"
	"log"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/iopred/bruxism"
)

type keystoneDungeonID int

const (
	BRH keystoneDungeonID = iota
	COS
	DHT
	EOA
	HOV
	MOS
	NL
	ARC
	VOW
)

type keystoneDungeon struct {
	Name    string
	Aliases []string
}

var dungeons map[keystoneDungeonID]*keystoneDungeon = map[keystoneDungeonID]*keystoneDungeon{
	BRH: &keystoneDungeon{
		Name:    "Black Rook Hold",
		Aliases: []string{"black rook hold", "brh", "black rook", "hold"},
	},
	COS: &keystoneDungeon{
		Name:    "Court of Stars",
		Aliases: []string{"court of stars", "cos", "court"},
	},
	DHT: &keystoneDungeon{
		Name:    "Darkheart Thicket",
		Aliases: []string{"darkheart thicket", "dht", "thicket", "darkheart"},
	},
	EOA: &keystoneDungeon{
		Name:    "Eye of Azshara",
		Aliases: []string{"eye of azshara", "eoa", "eye", "azshara"},
	},
	HOV: &keystoneDungeon{
		Name:    "Halls of Valor",
		Aliases: []string{"halls of valor", "hall of valor", "hov", "halls"},
	},
	MOS: &keystoneDungeon{
		Name:    "Maw of Souls",
		Aliases: []string{"maw of souls", "mos", "maw"},
	},
	NL: &keystoneDungeon{
		Name:    "Neltharion's Lair",
		Aliases: []string{"neltharion's lair", "nl", "neltharions lair", "nel", "nelth", "lair"},
	},
	ARC: &keystoneDungeon{
		Name:    "The Arcway",
		Aliases: []string{"the arcway", "arc", "arcway"},
	},
	VOW: &keystoneDungeon{
		Name:    "Vault of the Wardens",
		Aliases: []string{"vault of the wardens", "vow", "vault", "warden", "wardens"},
	},
}

type keystone struct {
	User      string
	Dungeon   keystoneDungeonID
	Level     int
	Depleted  bool
	Modifiers []string
}

func (k *keystone) String() string {
	str := fmt.Sprintf("Level %d **%s**", k.Level, dungeons[k.Dungeon].Name)
	if len(k.Modifiers) != 0 {
		str += " *(" + strings.Join(k.Modifiers, ", ") + ")*"
	}
	if k.Depleted {
		str += " - Depleted"
	}
	return str
}

type keystoneChannel struct {
	Users        map[string]*keystone
	LastModified time.Time
}

var location *time.Location

func init() {
	var err error
	location, err = time.LoadLocation("America/Los_Angeles")
	if err != nil {
		location = time.Now().Location()
	}
}

func lastTuesday(t time.Time) time.Time {
	year, month, day := t.Date()
	t = time.Date(year, month, day, 0, 0, 0, 0, location)
	for t.Weekday() != time.Tuesday {
		t = t.Add(-24 * time.Hour)
	}

	return t
}

func (c *keystoneChannel) check() {
	if time.Now().After(lastTuesday(c.LastModified).Add(24 * 7 * time.Hour)) {
		c.Users = map[string]*keystone{}
	}
}

func (c *keystoneChannel) add(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, userName, query string) bool {
	query = strings.ToLower(query)
	for dungeonID, dungeon := range dungeons {
		for _, alias := range dungeon.Aliases {
			if strings.Index(query, alias+" ") == 0 {
				query = query[len(alias)+1:]
				if len(query) == 0 {
					return false
				}

				parts := strings.Split(query, " ")

				level, err := strconv.Atoi(parts[0])
				if err != nil {
					return false
				}

				parts = parts[1:]

				depleted := false
				modifiers := []string{}

				for _, part := range parts {
					if strings.Index(part, "deplete") == 0 {
						depleted = true
					} else {
						modifiers = append(modifiers, part)
					}
				}

				c.Users[message.UserID()] = &keystone{
					User:      userName,
					Dungeon:   dungeonID,
					Level:     level,
					Depleted:  depleted,
					Modifiers: modifiers,
				}
				c.LastModified = time.Now()

				return true
			}
		}
	}
	return false
}

type keystoneList []*keystone

// Len is part of sort.Interface.
func (s keystoneList) Len() int {
	return len(s)
}

// Swap is part of sort.Interface.
func (s keystoneList) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s keystoneList) Less(i, j int) bool {
	if s[i].Level == s[j].Level {
		return dungeons[s[i].Dungeon].Name > dungeons[s[j].Dungeon].Name
	}
	return s[i].Level > s[j].Level
}

func (c *keystoneChannel) list(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	if len(c.Users) == 0 {
		service.SendMessage(message.Channel(), "No keystones have been set this week.")
		return
	}

	keystones := keystoneList{}
	for _, keystone := range c.Users {
		keystones = append(keystones, keystone)
	}

	sort.Sort(keystones)

	content := ""
	for _, keystone := range keystones {
		if len(content) != 0 {
			content += "\n"
		}
		content += keystone.String()
		content += " - " + keystone.User
	}
	service.SendMessage(message.Channel(), content)
}

type keystonePlugin struct {
	sync.RWMutex
	Channels map[string]*keystoneChannel
}

// Name returns the name of the plugin.
func (p *keystonePlugin) Name() string {
	return "Keystone"
}

// Load will load plugin state from a byte array.
func (p *keystonePlugin) Load(bot *bruxism.Bot, service bruxism.Service, data []byte) error {
	if data != nil {
		if err := json.Unmarshal(data, p); err != nil {
			log.Println("Error loading data", err)
		}
	}

	if p.Channels == nil {
		p.Channels = make(map[string]*keystoneChannel)
	}

	return nil
}

// Save will save plugin state to a byte array.
func (p *keystonePlugin) Save() ([]byte, error) {
	return json.Marshal(p)
}

// Help returns a list of help strings that are printed when the user requests them.
func (p *keystonePlugin) Help(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message, detailed bool) []string {
	help := []string{}

	if service.IsModerator(message) {
		help = append(help, []string{
			bruxism.CommandHelp(service, "start", "", "Starts keystone tracking in this channel.")[0],
			bruxism.CommandHelp(service, "stop", "", "Stops keystone tracking in this channel.")[0],
		}...)
	}

	ticks := ""
	if service.Name() == bruxism.DiscordServiceName {
		ticks = "`"
	}

	if p.Channels[message.Channel()] != nil {
		help = append(help, []string{
			bruxism.CommandHelp(service, "set", "<dungeon> <level> [modifiers]", "Set your keystone for this week.")[0],
			bruxism.CommandHelp(service, "list", "", "Lists all this weeks keystones.")[0],
			bruxism.CommandHelp(service, "deplete", "", "Depletes your keystone")[0],
			bruxism.CommandHelp(service, "undeplete", "", "Undepletes your keystone")[0],
		}...)
	}

	if detailed {
		help = append(help, []string{
			"Examples:",
			fmt.Sprintf("%s%sset hov 5 teeming%s - Adds a Level 5 Halls of Valor keystone with teeming.", ticks, service.CommandPrefix(), ticks),
			fmt.Sprintf("%s%sset eye of azshara 2 depleted%s - Adds a depleted Level 2 Eye of Azshara keystone.", ticks, service.CommandPrefix(), ticks),
		}...)
	}

	if len(help) == 0 {
		return nil
	}
	return help
}

// Message handler.
func (p *keystonePlugin) Message(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) {
	defer bruxism.MessageRecover()
	if !service.IsMe(message) {
		messageChannel := message.Channel()

		var userName string
		discord, ok := service.(*bruxism.Discord)
		if ok {
			userName = discord.Nickname(message)
		} else {
			userName = message.UserName()
		}

		if service.IsModerator(message) && (bruxism.MatchesCommand(service, "start", message) || bruxism.MatchesCommand(service, "stop", message)) {
			p.Lock()
			defer p.Unlock()

			if bruxism.MatchesCommand(service, "start", message) {
				p.Channels[messageChannel] = &keystoneChannel{
					Users: map[string]*keystone{},
				}
				service.SendMessage(messageChannel, "This channel is now tracking mythic keystones.")
			} else {
				delete(p.Channels, messageChannel)
				service.SendMessage(messageChannel, "This channel is no longer tracking mythic keystones.")
			}
		} else if channel, ok := p.Channels[messageChannel]; ok {
			channel.check()

			if bruxism.MatchesCommand(service, "set", message) {
				query, parts := bruxism.ParseCommand(service, message)
				if len(parts) > 1 && channel.add(bot, service, message, userName, query) {
					channel.list(bot, service, message)
				} else {
					service.SendMessage(messageChannel, "Invalid keystone. Eg: `keystone set hall of valor 3 sanguine`")
				}
			} else if bruxism.MatchesCommand(service, "list", message) {
				channel.list(bot, service, message)
			} else if bruxism.MatchesCommand(service, "deplete", message) {
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = true
					keystone.User = userName
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone depleted: %s", keystone.String()))
				}
			} else if bruxism.MatchesCommand(service, "undeplete", message) {
				keystone := channel.Users[message.UserID()]
				if keystone == nil {
					service.SendMessage(messageChannel, "You haven't set a keystone this week.")
				} else {
					keystone.Depleted = false
					keystone.User = userName
					service.SendMessage(messageChannel, fmt.Sprintf("Keystone undepleted: %s", keystone.String()))
				}
			}
		}
	}
}

// Stats will return the stats for a plugin.
func (p *keystonePlugin) Stats(bot *bruxism.Bot, service bruxism.Service, message bruxism.Message) []string {
	return nil
}

// New will create a new wormhole plugin.
func New() bruxism.Plugin {
	return &keystonePlugin{}
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/go-gl/gl"
	glfw "github.com/go-gl/glfw3"
)

func block() {
	gl.ShadeModel(gl.FLAT)
	gl.Normal3d(0.0, 0.0, 1.0)

	gl.Begin(gl.QUADS)

	gl.Vertex3f(-1, -1, 0)
	gl.Vertex3f(1, -1, 0)
	gl.Vertex3f(1, 1, 0)
	gl.Vertex3f(-1, 1, 0)

	gl.End()
}

var (
	rotationX float32 = 0.0
	rotationY float32 = 0.0
	rotationZ float32 = 0.0
	block1    uint
	angle     = 0.0
)

func draw() {
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

	gl.PushMatrix()
	gl.Rotatef(rotationX, 1.0, 0.0, 0.0)
	gl.Rotatef(rotationY, 0.0, 1.0, 0.0)
	gl.Rotatef(rotationZ, 0.0, 0.0, 1.0)

	gl.PushMatrix()
	gl.Translated(0.0, 0.0, 0.0)
	gl.CallList(block1)
	gl.PopMatrix()

	gl.PopMatrix()
}

// change view angle, exit upon ESC
func key(window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.Key(k) {
	case glfw.KeyZ:
		if mods&glfw.ModShift != 0 {
			rotationZ -= 5.0
		} else {
			rotationZ += 5.0
		}
	case glfw.KeyEscape:
		window.SetShouldClose(true)
	case glfw.KeyUp:
		rotationX += 5.0
	case glfw.KeyDown:
		rotationX -= 5.0
	case glfw.KeyLeft:
		rotationY += 5.0
	case glfw.KeyRight:
		rotationY -= 5.0
	default:
		return
	}
}

// new window size
func reshape(window *glfw.Window, width, height int) {
	h := float64(height) / float64(width)

	znear := 5.0
	zfar := 30.0
	xmax := znear * 0.5

	gl.Viewport(0, 0, width, height)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Frustum(-xmax, xmax, -xmax*h, xmax*h, znear, zfar)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()
	gl.Translated(0.0, 0.0, -20.0)
}

// program & OpenGL initialization
func Init() {
	pos := []float32{5.0, 5.0, 10.0, 0.0}
	red := []float32{0.8, 0.1, 0.0, 1.0}
	// green := []float32{0.0, 0.8, 0.2, 1.0}
	// blue := []float32{0.2, 0.2, 1.0, 1.0}

	gl.Lightfv(gl.LIGHT0, gl.POSITION, pos)
	gl.Enable(gl.CULL_FACE)
	gl.Enable(gl.LIGHTING)
	gl.Enable(gl.LIGHT0)
	gl.Enable(gl.DEPTH_TEST)

	// make the gears
	block1 = gl.GenLists(1)
	gl.NewList(block1, gl.COMPILE)
	gl.Materialfv(gl.FRONT, gl.AMBIENT_AND_DIFFUSE, red)
	block()
	gl.EndList()

	gl.Enable(gl.NORMALIZE)
}

func main() {
	if !glfw.Init() {
		panic("Failed to initialize GLFW")
	}

	defer glfw.Terminate()

	glfw.WindowHint(glfw.DepthBits, 16)

	window, err := glfw.CreateWindow(800, 600, "Voxel Party", nil, nil)
	if err != nil {
		panic(err)
	}

	// Set callback functions
	window.SetFramebufferSizeCallback(reshape)
	window.SetKeyCallback(key)

	window.MakeContextCurrent()
	glfw.SwapInterval(1)

	width, height := window.GetFramebufferSize()
	reshape(window, width, height)

	// Parse command-line options
	Init()

	// Main loop
	for !window.ShouldClose() {
		// Draw gears
		draw()

		// Swap buffers
		window.SwapBuffers()
		glfw.PollEvents()
	}
}
<commit_msg>refactor<commit_after>package main

import (
	"github.com/go-gl/gl"
	glfw "github.com/go-gl/glfw3"
)

const (
	Width  = 800
	Height = 600
	Title  = "Voxel Party"
)

func block() {
	gl.ShadeModel(gl.FLAT)
	gl.Normal3d(0.0, 0.0, 1.0)

	gl.Begin(gl.QUADS)

	gl.Vertex3f(-1, -1, 0)
	gl.Vertex3f(1, -1, 0)
	gl.Vertex3f(1, 1, 0)
	gl.Vertex3f(-1, 1, 0)

	gl.End()
}

var (
	positionX     float32 = 0.0
	positionY     float32 = 0.0
	positionZ     float32 = 0.0
	positionSpeed float32 = 1.0

	rotationX     float32 = 0.0
	rotationY     float32 = 0.0
	rotationZ     float32 = 0.0
	rotationSpeed float32 = 5.0
)

var (
	block1 uint
	angle  = 0.0
)

func draw() {
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

	gl.PushMatrix()
	gl.Rotatef(rotationX, 1.0, 0.0, 0.0)
	gl.Rotatef(rotationY, 0.0, 1.0, 0.0)
	gl.Rotatef(rotationZ, 0.0, 0.0, 1.0)

	gl.PushMatrix()
	gl.Translatef(positionX, positionY, positionZ)
	gl.CallList(block1)
	gl.PopMatrix()

	gl.PopMatrix()
}

// change view angle, exit upon ESC
func key(window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
	if action != glfw.Press {
		return
	}

	switch glfw.Key(k) {
	case glfw.KeyW:
		positionZ += positionSpeed
	case glfw.KeyS:
		positionZ -= positionSpeed
	case glfw.KeyA:
		positionX += positionSpeed
	case glfw.KeyD:
		positionX -= positionSpeed
	case glfw.KeyQ:
		positionY += positionSpeed
	case glfw.KeyE:
		positionY -= positionSpeed
	case glfw.KeyZ:
		if mods&glfw.ModShift != 0 {
			rotationZ -= rotationSpeed
		} else {
			rotationZ += rotationSpeed
		}
	case glfw.KeyEscape:
		window.SetShouldClose(true)
	case glfw.KeyUp:
		rotationX += rotationSpeed
	case glfw.KeyDown:
		rotationX -= rotationSpeed
	case glfw.KeyLeft:
		rotationY += rotationSpeed
	case glfw.KeyRight:
		rotationY -= rotationSpeed
	default:
		return
	}
}

// new window size
func reshape(window *glfw.Window, width, height int) {
	h := float64(height) / float64(width)

	znear := 5.0
	zfar := 30.0
	xmax := znear * 0.5

	gl.Viewport(0, 0, width, height)
	gl.MatrixMode(gl.PROJECTION)
	gl.LoadIdentity()
	gl.Frustum(-xmax, xmax, -xmax*h, xmax*h, znear, zfar)
	gl.MatrixMode(gl.MODELVIEW)
	gl.LoadIdentity()
	gl.Translated(0.0, 0.0, -20.0)
}

func Init() {
	pos := []float32{5.0, 5.0, 10.0, 0.0}
	red := []float32{0.8, 0.1, 0.0, 1.0}
	// green := []float32{0.0, 0.8, 0.2, 1.0}
	// blue := []float32{0.2, 0.2, 1.0, 1.0}

	gl.Lightfv(gl.LIGHT0, gl.POSITION, pos)
	gl.Enable(gl.CULL_FACE)
	gl.Enable(gl.LIGHTING)
	gl.Enable(gl.LIGHT0)
	gl.Enable(gl.DEPTH_TEST)

	// make the gears
	block1 = gl.GenLists(1)
	gl.NewList(block1, gl.COMPILE)
	gl.Materialfv(gl.FRONT, gl.AMBIENT_AND_DIFFUSE, red)
	block()
	gl.EndList()

	gl.Enable(gl.NORMALIZE)
}

func main() {
	if !glfw.Init() {
		panic("Failed to initialize GLFW")
	}

	defer glfw.Terminate()

	glfw.WindowHint(glfw.DepthBits, 16)

	window, err := glfw.CreateWindow(Width, Height, Title, nil, nil)

	if err != nil {
		panic(err)
	}

	// Set callback functions
	window.SetFramebufferSizeCallback(reshape)
	window.SetKeyCallback(key)

	window.MakeContextCurrent()
	glfw.SwapInterval(1)

	width, height := window.GetFramebufferSize()
	reshape(window, width, height)

	// Parse command-line options
	Init()

	// Main loop
	for !window.ShouldClose() {
		// Draw gears
		draw()

		// Swap buffers
		window.SwapBuffers()
		glfw.PollEvents()
	}
}
<|endoftext|>