<commit_before>package easyrss

import (
	"errors"
	"github.com/moovweb/gokogiri/xml"
	"strconv"
	"strings"
	"time"
)

type ItunesMeta struct {
	author   string
	subtitle string
	summary  string
	image    Image
	explicit string
	duration time.Duration
	keywords string
}

//Sets Appropriate Field Given Itunes Node
func setItunesMetaField(n xml.Node, i *ItunesMeta) {
	tag := n.Name()
	tagContent := n.Content()
	switch tag {
	case "subtitle":
		i.subtitle = tagContent
	case "author":
		i.author = tagContent
	case "summary":
		i.summary = tagContent
	case "explicit":
		i.explicit = tagContent
	case "keywords":
		i.keywords = tagContent
	case "duration":
		splitDur := strings.Split(tagContent, ":")
		if len(splitDur) != 3 { //Not H:M:S format
			return
		}
		hours, e := strconv.ParseInt(splitDur[0], 10, 8)
		if e != nil {
			return
		}
		minutes, e := strconv.ParseInt(splitDur[1], 10, 8)
		if e != nil {
			return
		}
		seconds, e := strconv.ParseInt(splitDur[2], 10, 8)
		if e != nil {
			return
		}
		i.duration = time.Duration(time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second)
	case "image":
		if urlNode := n.Attribute("href"); urlNode != nil {
			i.image.url = urlNode.Value()
		}
	default:
		return
	}
}

//Whether or not this feed implements ItunesRSS Extensions
func (r *RSS) IsItunes() bool {
	return r.channel.isItunes
}

//Returns the Itunes "author" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "author" field, will return an empty string and an error
func (r *RSS) ItunesAuthor() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.author == "" {
		return "", errors.New("Itunes author field not populated")
	}
	return r.channel.itunes.author, nil
}

//Returns the Itunes "author" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "subtitle" field, will return an empty string and an error
func (r *RSS) ItunesSubtitle() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.subtitle == "" {
		return "", errors.New("Itunes subtitle field not populated")
	}
	return r.channel.itunes.subtitle, nil
}

//Returns the Itunes "summary" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "summary" field, will return an empty string and an error
func (r *RSS) ItunesSummary() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.subtitle == "" {
		return "", errors.New("Itunes summary field not populated")
	}
	return r.channel.itunes.subtitle, nil
}

//Returns the Itunes "image" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "image" field, will return nil and an error.
func (r *RSS) ItunesImage() (*Image, error) {
	if !r.channel.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.image.url == "" {
		return nil, errors.New("Itunes image fields not populated")
	}
	return &r.channel.itunes.image, nil
}

//Returns the Itunes "explicit" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "explicit" field, will return an empty string and an error func (r *RSS) ItunesExplicit() (string, error) {
func (r *RSS) ItunesExplicit() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.explicit == "" {
		return "", errors.New("Itunes explicit field not populated")
	}
	return r.channel.itunes.explicit, nil
}

//Returns the Itunes "author" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "author" field, will return an empty string and an error
func (i *Item) ItunesAuthor() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.author == "" {
		return "", errors.New("Itunes author field not populated")
	}
	return i.itunes.author, nil
}

//Returns the Itunes "author" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "subtitle" field, will return an empty string and an error
func (i *Item) ItunesSubtitle() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.subtitle == "" {
		return "", errors.New("Itunes subtitle field not populated")
	}
	return i.itunes.subtitle, nil
}

//Returns the Itunes "summary" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "summary" field, will return an empty string and an error
func (i *Item) ItunesSummary() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.subtitle == "" {
		return "", errors.New("Itunes summary field not populated")
	}
	return i.itunes.subtitle, nil
}
//Returns Itunes episode duration. If this information wasn't available or the item doesn't contain Itunes Extensions then we return nil and an error.
func (i Item) ItunesDuration() (*time.Duration, error) {
	if !i.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if int(i.itunes.duration) == 0 {
		return nil, errors.New("Itunes duration field missing")
	}
	return &i.itunes.duration, nil
}

//Returns the Itunes "image" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "image" field, will return nil and an error.
func (i *Item) ItunesImage() (*Image, error) {
	if !i.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.image.url == "" {
		return nil, errors.New("Itunes image fields not populated")
	}
	return &i.itunes.image, nil
}
<commit_msg>itunes:duration can be a number of seconds or in the format mm:ss<commit_after>package easyrss

import (
	"errors"
	"github.com/moovweb/gokogiri/xml"
	"strconv"
	"strings"
	"time"
)

type ItunesMeta struct {
	author   string
	subtitle string
	summary  string
	image    Image
	explicit string
	duration time.Duration
	keywords string
}

//Sets Appropriate Field Given Itunes Node
func setItunesMetaField(n xml.Node, i *ItunesMeta) {
	tag := n.Name()
	tagContent := n.Content()
	switch tag {
	case "subtitle":
		i.subtitle = tagContent
	case "author":
		i.author = tagContent
	case "summary":
		i.summary = tagContent
	case "explicit":
		i.explicit = tagContent
	case "keywords":
		i.keywords = tagContent
	case "duration":
		splitDur := strings.Split(tagContent, ":")
		if len(splitDur) != 3 && len(splitDur) != 2 { //Not H:M:S/M:S format, so probably just a seconds integer
			duration, err := time.ParseDuration(tagContent + "s")
			if err != nil {
				return
			}
			i.duration = duration
			return
		}

		if len(splitDur) == 3 {
			hours, e := strconv.ParseInt(splitDur[0], 10, 8)
			if e != nil {
				return
			}
			minutes, e := strconv.ParseInt(splitDur[1], 10, 8)
			if e != nil {
				return
			}
			seconds, e := strconv.ParseInt(splitDur[2], 10, 8)
			if e != nil {
				return
			}
			i.duration = time.Duration(time.Duration(hours)*time.Hour + time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second)
		} else {
			minutes, e := strconv.ParseInt(splitDur[0], 10, 8)
			if e != nil {
				return
			}
			seconds, e := strconv.ParseInt(splitDur[1], 10, 8)
			if e != nil {
				return
			}
			i.duration = time.Duration(time.Duration(minutes)*time.Minute + time.Duration(seconds)*time.Second)
		}

	case "image":
		if urlNode := n.Attribute("href"); urlNode != nil {
			i.image.url = urlNode.Value()
		}
	default:
		return
	}
}

//Whether or not this feed implements ItunesRSS Extensions
func (r *RSS) IsItunes() bool {
	return r.channel.isItunes
}

//Returns the Itunes "author" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "author" field, will return an empty string and an error
func (r *RSS) ItunesAuthor() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.author == "" {
		return "", errors.New("Itunes author field not populated")
	}
	return r.channel.itunes.author, nil
}

//Returns the Itunes "author" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "subtitle" field, will return an empty string and an error
func (r *RSS) ItunesSubtitle() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.subtitle == "" {
		return "", errors.New("Itunes subtitle field not populated")
	}
	return r.channel.itunes.subtitle, nil
}

//Returns the Itunes "summary" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "summary" field, will return an empty string and an error
func (r *RSS) ItunesSummary() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.subtitle == "" {
		return "", errors.New("Itunes summary field not populated")
	}
	return r.channel.itunes.subtitle, nil
}

//Returns the Itunes "image" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "image" field, will return nil and an error.
func (r *RSS) ItunesImage() (*Image, error) {
	if !r.channel.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.image.url == "" {
		return nil, errors.New("Itunes image fields not populated")
	}
	return &r.channel.itunes.image, nil
}

//Returns the Itunes "explicit" field for the channel. If the channel doesn't contain ITunes Extensions or hasn't populated the channel-wide Itunes "explicit" field, will return an empty string and an error func (r *RSS) ItunesExplicit() (string, error) {
func (r *RSS) ItunesExplicit() (string, error) {
	if !r.channel.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if r.channel.itunes.explicit == "" {
		return "", errors.New("Itunes explicit field not populated")
	}
	return r.channel.itunes.explicit, nil
}

//Returns the Itunes "author" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "author" field, will return an empty string and an error
func (i *Item) ItunesAuthor() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.author == "" {
		return "", errors.New("Itunes author field not populated")
	}
	return i.itunes.author, nil
}

//Returns the Itunes "author" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "subtitle" field, will return an empty string and an error
func (i *Item) ItunesSubtitle() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.subtitle == "" {
		return "", errors.New("Itunes subtitle field not populated")
	}
	return i.itunes.subtitle, nil
}

//Returns the Itunes "summary" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "summary" field, will return an empty string and an error
func (i *Item) ItunesSummary() (string, error) {
	if !i.isItunes {
		return "", errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.subtitle == "" {
		return "", errors.New("Itunes summary field not populated")
	}
	return i.itunes.subtitle, nil
}

//Returns Itunes episode duration. If this information wasn't available or the item doesn't contain Itunes Extensions then we return nil and an error.
func (i Item) ItunesDuration() (*time.Duration, error) {
	if !i.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if int(i.itunes.duration) == 0 {
		return nil, errors.New("Itunes duration field missing")
	}
	return &i.itunes.duration, nil
}

//Returns the Itunes "image" field for the item. If the item doesn't contain ITunes Extensions or hasn't populated the Itunes "image" field, will return nil and an error.
func (i *Item) ItunesImage() (*Image, error) {
	if !i.isItunes {
		return nil, errors.New("Not an Itunes RSS Feed")
	}
	if i.itunes.image.url == "" {
		return nil, errors.New("Itunes image fields not populated")
	}
	return &i.itunes.image, nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

const jaegerTemplateExtension = ".jgrt"
const jaegerJSONGPGDBExtension = ".jgrdb"
const jaegerQuote = "Stacker Pentecost: Haven't you heard Mr. Beckett? The world is coming to an end. So where would you rather die? Here? Or in a Jaeger!"

var debug debugging = true // or flip to false

type debugging bool

func (d debugging) Printf(format string, args ...interface{}) {
	// From: https://groups.google.com/forum/#!msg/golang-nuts/gU7oQGoCkmg/BNIl-TqB-4wJ
	if d {
		log.Printf(format, args...)
	}
}

type Data struct {
	Properties []Property
}

type Property struct {
	Name           string //`json:"Name"`
	EncryptedValue string //`json:"EncryptedValue"`
}

func decodeBase64EncryptedMessage(s string, keyring openpgp.KeyRing) string {
	// Decrypt base64 encoded encrypted message using decrypted private key
	dec, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		log.Fatalln("ERR:", err)
	}
	fmt.Printf("keyring: #%v", keyring)
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), keyring, nil, nil)
	if err != nil {
		log.Fatalln("ERR: Error reading message - ", err)
	}

	bytes, err := ioutil.ReadAll(md.UnverifiedBody)
	debug.Printf("md:", string(bytes))
	return string(bytes)
}

func main() {
	// Define flags
	var (
		//debugFlag         = flag.Bool("d", false, "Enable Debug")
		inputTemplate     = flag.String("i", "", "Input Template file. eg. file.txt.jgrt")
		jsonGPGDB         = flag.String("j", "", "JSON GPG database file. eg. file.txt.jgrdb")
		outputFile        = flag.String("o", "", "Output file. eg. file.txt")
		keyringFile       = flag.String("k", "", "Keyring file. Secret key in armor format. eg. secret.asc")
		passphraseKeyring = flag.String("p", "", "Passphrase for keyring. If this is not set the passphrase will be blank of read from the environment variable PASSPHRASE.")
	)
	// Parse
	// Any additional non-flag arguments can be retrieved with flag.Args() which returns a []string.
	flag.Parse()

	//if *debugFlag {
	//	debug debugging = true
	//}

	if *inputTemplate == "" {
		flag.Usage()
		log.Fatalf("\n\nError: No input file specified")
		return
	}

	// TODO: Handle reading from default keyring
	if *keyringFile == "" {
		flag.Usage()
		log.Fatalf("\n\nError: No keyring file specified")
		return
	}

	basefilename := ""

	if strings.HasSuffix(*inputTemplate, jaegerTemplateExtension) {
		basefilename = strings.TrimSuffix(*inputTemplate, jaegerTemplateExtension)
	}

	if *jsonGPGDB == "" {
		if basefilename == "" {
			flag.Usage()
			log.Fatalf("\n\nERROR: No JSON GPG DB file specified or input file does not have a %v extension", jaegerTemplateExtension)
			return
		}
		// Set from the basefilename
		*jsonGPGDB = fmt.Sprintf("%v%v", basefilename, jaegerJSONGPGDBExtension)
	}

	if *outputFile == "" {
		if basefilename == "" {
			flag.Usage()
			log.Fatalf("\n\nERROR: No Output file specified or input file does not have a %v extension", jaegerTemplateExtension)
			return
		}
		// Set from the basefilename
		*outputFile = basefilename

	}

	if *passphraseKeyring == "" {
		passphrase := os.Getenv("PASSPHRASE")
		if len(passphrase) != 0 {
			*passphraseKeyring = passphrase
		}
	}

	debug.Printf("basefilename:", basefilename)
	debug.Printf("jsonGPGDB:", *jsonGPGDB)
	debug.Printf("outputFile:", *outputFile)
	debug.Printf("passphrase:", *passphraseKeyring)
	debug.Printf("keyringFile:", *keyringFile)

	// Read armored private key into type EntityList
	// An EntityList contains one or more Entities.
	// This assumes there is only one Entity involved
	// TODO: Read default keyring
	// TODO: Support to prompt for passphrase
	//entitylist, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(privateKey))
	keyringFileBuffer, err := os.Open(*keyringFile)
	if err != nil {
		log.Fatalln("ERROR: Unable to read keyring file")
	}
	entitylist, err := openpgp.ReadArmoredKeyRing(keyringFileBuffer)
	if err != nil {
		log.Fatal(err)
	}
	entity := entitylist[0]
	debug.Printf("Private key from armored string:", entity.Identities)

	// Decrypt private key using passphrase
	passphrase := []byte(*passphraseKeyring)
	if entity.PrivateKey != nil && entity.PrivateKey.Encrypted {
		debug.Printf("Decrypting private key using passphrase")
		err := entity.PrivateKey.Decrypt(passphrase)
		if err != nil {
			log.Fatalln("ERROR: Failed to decrypt key")
		}
	}
	for _, subkey := range entity.Subkeys {
		if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted {
			err := subkey.PrivateKey.Decrypt(passphrase)
			if err != nil {
				log.Fatalln("ERROR: Failed to decrypt subkey")
			}
		}
	}

	// json handling
	jsonGPGDBBuffer, err := ioutil.ReadFile(*jsonGPGDB)
	if err != nil {
		log.Fatalln("ERROR: Unable to read JSON GPG DB file")
	}

	//b := []byte(jsonStream2)
	var j Data
	if err := json.Unmarshal(jsonGPGDBBuffer, &j); err != nil {
		panic(err)
	}
	debug.Printf("json unmarshal:", j)

	p := make(map[string]string)

	for _, v := range j.Properties {
		debug.Printf("Name: %#v, EncryptedValue: %#v\n", v.Name, v.EncryptedValue)
		p[v.Name] = decodeBase64EncryptedMessage(v.EncryptedValue, entitylist)
	}

	debug.Printf("properties map:", p)

	// Template parsing
	t := template.Must(template.ParseFiles(*inputTemplate))

	buf := new(bytes.Buffer)
	t.Execute(buf, p) //merge template ‘t’ with content of ‘p’

	bytes, _ := ioutil.ReadAll(buf)
	debug.Printf(string(bytes))

	// Writing file
	// To handle large files, use a file buffer: http://stackoverflow.com/a/9739903/603745
	if err := ioutil.WriteFile(*outputFile, bytes, 0644); err != nil {
		panic(err)
	} else {
		fmt.Println("Wrote file:", *outputFile)
	}

}
<commit_msg>Add debugging<commit_after>package main

import (
	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

const jaegerTemplateExtension = ".jgrt"
const jaegerJSONGPGDBExtension = ".jgrdb"
const jaegerQuote = "Stacker Pentecost: Haven't you heard Mr. Beckett? The world is coming to an end. So where would you rather die? Here? Or in a Jaeger!"

var debug debugging = false // or flip to false

type debugging bool

func (d debugging) Printf(format string, args ...interface{}) {
	// From: https://groups.google.com/forum/#!msg/golang-nuts/gU7oQGoCkmg/BNIl-TqB-4wJ
	if d {
		log.Printf(format, args...)
	}
}

type Data struct {
	Properties []Property
}

type Property struct {
	Name           string //`json:"Name"`
	EncryptedValue string //`json:"EncryptedValue"`
}

func decodeBase64EncryptedMessage(s string, keyring openpgp.KeyRing) string {
	// Decrypt base64 encoded encrypted message using decrypted private key
	dec, err := base64.StdEncoding.DecodeString(s)
	if err != nil {
		log.Fatalln("ERR:", err)
	}
	debug.Printf("keyring: #%v", keyring)
	md, err := openpgp.ReadMessage(bytes.NewBuffer(dec), keyring, nil, nil)
	if err != nil {
		log.Fatalln("ERR: Error reading message - ", err)
	}

	bytes, err := ioutil.ReadAll(md.UnverifiedBody)
	debug.Printf("md:", string(bytes))
	return string(bytes)
}

func main() {
	// Define flags
	var (
		debugFlag         = flag.Bool("d", false, "Enable Debug")
		inputTemplate     = flag.String("i", "", "Input Template file. eg. file.txt.jgrt")
		jsonGPGDB         = flag.String("j", "", "JSON GPG database file. eg. file.txt.jgrdb")
		outputFile        = flag.String("o", "", "Output file. eg. file.txt")
		keyringFile       = flag.String("k", "", "Keyring file. Secret key in armor format. eg. secret.asc")
		passphraseKeyring = flag.String("p", "", "Passphrase for keyring. If this is not set the passphrase will be blank of read from the environment variable PASSPHRASE.")
	)
	// Parse
	// Any additional non-flag arguments can be retrieved with flag.Args() which returns a []string.
	flag.Parse()

	if *debugFlag {
		debug = true
	}

	if *inputTemplate == "" {
		flag.Usage()
		log.Fatalf("\n\nError: No input file specified")
		return
	}

	// TODO: Handle reading from default keyring
	if *keyringFile == "" {
		flag.Usage()
		log.Fatalf("\n\nError: No keyring file specified")
		return
	}

	basefilename := ""

	if strings.HasSuffix(*inputTemplate, jaegerTemplateExtension) {
		basefilename = strings.TrimSuffix(*inputTemplate, jaegerTemplateExtension)
	}

	if *jsonGPGDB == "" {
		if basefilename == "" {
			flag.Usage()
			log.Fatalf("\n\nERROR: No JSON GPG DB file specified or input file does not have a %v extension", jaegerTemplateExtension)
			return
		}
		// Set from the basefilename
		*jsonGPGDB = fmt.Sprintf("%v%v", basefilename, jaegerJSONGPGDBExtension)
	}

	if *outputFile == "" {
		if basefilename == "" {
			flag.Usage()
			log.Fatalf("\n\nERROR: No Output file specified or input file does not have a %v extension", jaegerTemplateExtension)
			return
		}
		// Set from the basefilename
		*outputFile = basefilename

	}

	if *passphraseKeyring == "" {
		passphrase := os.Getenv("PASSPHRASE")
		if len(passphrase) != 0 {
			*passphraseKeyring = passphrase
		}
	}

	debug.Printf("basefilename:", basefilename)
	debug.Printf("jsonGPGDB:", *jsonGPGDB)
	debug.Printf("outputFile:", *outputFile)
	debug.Printf("passphrase:", *passphraseKeyring)
	debug.Printf("keyringFile:", *keyringFile)

	// Read armored private key into type EntityList
	// An EntityList contains one or more Entities.
	// This assumes there is only one Entity involved
	// TODO: Read default keyring
	// TODO: Support to prompt for passphrase
	//entitylist, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(privateKey))
	keyringFileBuffer, err := os.Open(*keyringFile)
	if err != nil {
		log.Fatalln("ERROR: Unable to read keyring file")
	}
	entitylist, err := openpgp.ReadArmoredKeyRing(keyringFileBuffer)
	if err != nil {
		log.Fatal(err)
	}
	entity := entitylist[0]
	debug.Printf("Private key from armored string:", entity.Identities)

	// Decrypt private key using passphrase
	passphrase := []byte(*passphraseKeyring)
	if entity.PrivateKey != nil && entity.PrivateKey.Encrypted {
		debug.Printf("Decrypting private key using passphrase")
		err := entity.PrivateKey.Decrypt(passphrase)
		if err != nil {
			log.Fatalln("ERROR: Failed to decrypt key")
		}
	}
	for _, subkey := range entity.Subkeys {
		if subkey.PrivateKey != nil && subkey.PrivateKey.Encrypted {
			err := subkey.PrivateKey.Decrypt(passphrase)
			if err != nil {
				log.Fatalln("ERROR: Failed to decrypt subkey")
			}
		}
	}

	// json handling
	jsonGPGDBBuffer, err := ioutil.ReadFile(*jsonGPGDB)
	if err != nil {
		log.Fatalln("ERROR: Unable to read JSON GPG DB file")
	}

	//b := []byte(jsonStream2)
	var j Data
	if err := json.Unmarshal(jsonGPGDBBuffer, &j); err != nil {
		panic(err)
	}
	debug.Printf("json unmarshal:", j)

	p := make(map[string]string)

	for _, v := range j.Properties {
		debug.Printf("Name: %#v, EncryptedValue: %#v\n", v.Name, v.EncryptedValue)
		p[v.Name] = decodeBase64EncryptedMessage(v.EncryptedValue, entitylist)
	}

	debug.Printf("properties map:", p)

	// Template parsing
	t := template.Must(template.ParseFiles(*inputTemplate))

	buf := new(bytes.Buffer)
	t.Execute(buf, p) //merge template ‘t’ with content of ‘p’

	bytes, _ := ioutil.ReadAll(buf)
	debug.Printf(string(bytes))

	// Writing file
	// To handle large files, use a file buffer: http://stackoverflow.com/a/9739903/603745
	if err := ioutil.WriteFile(*outputFile, bytes, 0644); err != nil {
		panic(err)
	} else {
		fmt.Println("Wrote file:", *outputFile)
	}

}
<|endoftext|>


<commit_before>package genericsite

// TODO: Move some of these to "browserspeak"

import (
	"mime"
	"time"

	"github.com/drbawb/mustache"
	. "github.com/xyproto/browserspeak"
	"github.com/xyproto/web"
)

type ContentPage struct {
	generatedCSSurl          string
	extraCSSurls             []string
	jqueryJSurl              string
	faviconurl               string
	bgImageURL               string
	stretchBackground        bool
	title                    string
	subtitle                 string
	links                    []string
	contentTitle             string
	contentHTML              string
	headerJS                 string
	contentJS                string
	searchButtonText         string
	searchURL                string
	footerText               string
	backgroundTextureURL     string
	darkBackgroundTextureURL string
	footerTextColor          string
	footerColor              string
	userState                *UserState
	roundedLook              bool
	url                      string
	colorScheme              *ColorScheme
}

// Content page generator
type CPgen (func(userState *UserState) *ContentPage)

// A collection of ContentPages
type PageCollection []ContentPage

// Every input from the user must be intitially stored in a UserInput variable, not in a string!
// This is just to be aware of which data one should be careful with, and to keep it clean.
type UserInput string

type UserState struct {
	// see: http://redis.io/topics/data-types
	users       *RedisHashMap   // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames   *RedisSet       // A list of all usernames, for easy enumeration
	unconfirmed *RedisSet       // A list of unconfirmed usernames, for easy enumeration
	pool        *ConnectionPool // A connection pool for Redis
}

type ColorScheme struct {
	darkgray           string
	nicecolor          string
	menu_link          string
	menu_hover         string
	menu_active        string
	default_background string
}

const (
	JQUERY_VERSION = "1.9.1"
)

// browserspeak
var globalStringCache map[string]string

// browserspeak
// Wrap a SimpleContextHandle so that the output is cached (with an id)
// Do not cache functions with side-effects! (that sets the mimetype for instance)
// The safest thing for now is to only cache images.
func CacheWrapper(id string, f SimpleContextHandle) SimpleContextHandle {
	return func(ctx *web.Context) string {
		if _, ok := globalStringCache[id]; !ok {
			globalStringCache[id] = f(ctx)
		}
		return globalStringCache[id]
	}
}

// browserspeak
func Publish(url, filename string, cache bool) {
	if cache {
		web.Get(url, CacheWrapper(url, File(filename)))
	} else {
		web.Get(url, File(filename))
	}
}

// TODO: get style values from a file instead?
func AddHeader(page *Page, js string) {
	AddGoogleFonts(page, []string{"Armata"}) //, "Junge"})
	// TODO: Move to browserspeak
	page.MetaCharset("UTF-8")
	AddScriptToHeader(page, js)
}

// The default settings
// Do not publish this page directly, but use it as a basis for the other pages
func DefaultCP(userState *UserState) *ContentPage {
	var cp ContentPage
	cp.generatedCSSurl = "/css/style.css"
	cp.extraCSSurls = []string{"/css/extra.css"}
	// TODO: fallback to local jquery.min.js, google how
	cp.jqueryJSurl = "//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" // "/js/jquery-1.9.1.js"
	cp.faviconurl = "/favicon.ico"
	cp.links = []string{"Overview:/", "Login:/login", "Logout:/logout", "Register:/register", "Admin:/admin"}
	cp.contentTitle = "NOP"
	cp.contentHTML = "NOP NOP NOP"
	cp.contentJS = ""
	cp.headerJS = ""
	cp.searchButtonText = "Search"
	cp.searchURL = "/search"

	// http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2010/04/gray-texture.jpg
	// TODO: Draw these two backgroundimages with a canvas instead
	cp.backgroundTextureURL = "/img/gray.jpg"
	// http://turbo.designwoop.com/uploads/2012/03/16_free_subtle_textures_subtle_dark_vertical.jpg
	cp.darkBackgroundTextureURL = "/img/darkgray.jpg"

	cp.footerColor = "black"
	cp.footerTextColor = "#303040"

	cp.footerText = "NOP"

	cp.userState = userState
	cp.roundedLook = false

	// Javascript that applies everywhere
	//cp.contentJS += HideIfNot("/showmenu/login", "#menuLogin")
	//cp.contentJS += HideIfNot("/showmenu/logout", "#menuLogout")
	//cp.contentJS += HideIfNot("/showmenu/register", "#menuRegister")
	//cp.contentJS += HideIfNotLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")
	//cp.contentJS += ShowIfLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")

	// This only works at first page load in Internet Explorer 8. Fun times. Oh well, why bother.
	cp.headerJS += ShowIfLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")

	// This in combination with hiding the link in genericsite.go is cool, but the layout becomes weird :/
	//cp.headerJS += ShowAnimatedIf("/showmenu/admin", "#menuAdmin")

	// This keeps the layout but is less cool
	cp.headerJS += HideIfNot("/showmenu/admin", "#menuAdmin")

	cp.url = "/" // To be filled in when published

	// The default color scheme
	var cs ColorScheme
	cs.darkgray = "#202020"
	cs.nicecolor = "#5080D0"   // nice blue
	cs.menu_link = "#c0c0c0"   // light gray
	cs.menu_hover = "#efefe0"  // light gray, somewhat yellow
	cs.menu_active = "#ffffff" // white
	cs.default_background = "#000030"
	cp.colorScheme = &cs

	return &cp
}

// TODO: Consider using Mustache for replacing elements after the page has been generated
// (for showing/hiding "login", "logout" or "register"
func genericPageBuilder(cp *ContentPage) *Page {
	// TODO: Record the time from one step out, because content may be generated and inserted into this generated conten
	startTime := time.Now()

	page := NewHTML5Page(cp.title + " " + cp.subtitle)

	page.LinkToCSS(cp.generatedCSSurl)
	for _, cssurl := range cp.extraCSSurls {
		page.LinkToCSS(cssurl)
	}
	page.LinkToJS(cp.jqueryJSurl)
	page.LinkToFavicon(cp.faviconurl)

	AddHeader(page, cp.headerJS)
	AddBodyStyle(page, cp.bgImageURL, cp.stretchBackground)
	AddTopBox(page, cp.title, cp.subtitle, cp.searchURL, cp.searchButtonText, cp.backgroundTextureURL, cp.roundedLook, cp.colorScheme)

	// TODO:
	// Use something dynamic to add or remove /login and /register depending on the login status
	// The login status can be fetched over AJAX or REST or something.

	// TODO: Move the menubox into the TopBox

	// TODO: Do this with templates instead
	// Hide login/logout/register by default
	hidelist := []string{"/login", "/logout", "/register"} //, "/admin"}
	AddMenuBox(page, cp.links, hidelist, cp.darkBackgroundTextureURL)

	AddContent(page, cp.contentTitle, cp.contentHTML+BodyJS(cp.contentJS))

	elapsed := time.Since(startTime)
	AddFooter(page, cp.footerText, cp.footerTextColor, cp.footerColor, elapsed)

	return page
}

// Publish a list of ContentPaages, a colorscheme and template content
func PublishCPs(pc PageCollection, cs *ColorScheme, tp map[string]string, cssurl string) {
	// For each content page in the page collection
	for _, cp := range pc {
		// TODO: different css urls for all of these?
		cp.Pub(cp.url, cssurl, cs, tp)
	}
}

//// Returns a DefaultCP with the contentTitle set
//func DefaultTitleCP(contentTitle string, userState *UserState) *ContentPage {
//	cp := DefaultCP(userState)
//	cp.contentTitle = contentTitle
//	return cp
//}

type BaseCP func(state *UserState) *ContentPage

func ServeSite(basecp BaseCP, userState *UserState, cps PageCollection, tp map[string]string) {
	// Add pages for login, logout and register
	cps = append(cps, *LoginCP(basecp, userState, "/login"))
	cps = append(cps, *RegisterCP(basecp, userState, "/register"))

	cs := basecp(userState).colorScheme
	PublishCPs(cps, cs, tp, "/css/extra.css")

	ServeSearchPages(basecp, userState, cps, cs, tp)
	ServeAdminPages(basecp, userState, cs, tp)

	// TODO: Add fallback to this local version
	Publish("/js/jquery-"+JQUERY_VERSION+".js", "static/js/jquery-"+JQUERY_VERSION+".js", true)

	// TODO: Generate these
	Publish("/robots.txt", "static/various/robots.txt", false)
	Publish("/sitemap_index.xml", "static/various/sitemap_index.xml", false)
	Publish("/favicon.ico", "static/img/favicon.ico", false)
}

func GenerateMenuCSS(stretchBackground bool, cs *ColorScheme) SimpleContextHandle {
	return func(ctx *web.Context) string {
		ctx.ContentType("css")
		// one of the extra css files that are loaded after the main style
		retval := `
a {
  text-decoration: none;
  color: #303030;
  font-weight: regular;
}
a:link {color:` + cs.menu_link + `;}
a:visited {color:` + cs.menu_link + `;}
a:hover {color:` + cs.menu_hover + `;}
a:active {color:` + cs.menu_active + `;}
`
		// The load order of background-color, background-size and background-image
		// is actually significant in Chrome! Do not reorder lightly!
		if stretchBackground {
			retval = "body {\nbackground-color: " + cs.default_background + ";\nbackground-size: cover;\n}\n" + retval
		} else {
			retval = "body {\nbackground-color: " + cs.default_background + ";\n}\n" + retval
		}
		return retval
	}
}

// Make an html and css page available
func (cp *ContentPage) Pub(url, cssurl string, cs *ColorScheme, templateContent map[string]string) {
	genericpage := genericPageBuilder(cp)
	web.Get(url, GenerateHTMLwithTemplate(genericpage, templateContent))
	web.Get(cp.generatedCSSurl, GenerateCSS(genericpage))
	web.Get(cssurl, GenerateMenuCSS(cp.stretchBackground, cs))
}

// Wrap a lonely string in an entire webpage
func (cp *ContentPage) Surround(s string, tp map[string]string) (string, string) {
	cp.contentHTML = s
	archpage := genericPageBuilder(cp)
	// NOTE: Use GetXML(true) instead of .String() or .GetHTML() because some things are rendered
	// differently with different text layout!
	return mustache.Render(archpage.GetXML(true), tp), archpage.GetCSS()
}

// Uses a given SimpleWebHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapSimpleWebHandle(swh SimpleWebHandle, tp map[string]string) SimpleWebHandle {
	return func(val string) string {
		html, css := cp.Surround(swh(val), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

// Uses a given WebHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapWebHandle(wh WebHandle, tp map[string]string) WebHandle {
	return func(ctx *web.Context, val string) string {
		html, css := cp.Surround(wh(ctx, val), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

// Uses a given SimpleContextHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapSimpleContextHandle(sch SimpleContextHandle, tp map[string]string) SimpleContextHandle {
	return func(ctx *web.Context) string {
		html, css := cp.Surround(sch(ctx), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

func InitSystem() *UserState {
	// These common ones are missing!
	mime.AddExtensionType(".txt", "text/plain; charset=utf-8")
	mime.AddExtensionType(".ico", "image/x-icon")

	// Create a Redis connection pool
	pool := NewRedisConnectionPool()
	//if err != nil {
	//	panic("ERROR: Can't connect to redis")
	//}
	defer pool.Close()

	// The login system, returns a *UserState
	return InitUserSystem(pool)
}
<commit_msg>Removed a function that were commented out<commit_after>package genericsite

// TODO: Move some of these to "browserspeak"

import (
	"mime"
	"time"

	"github.com/drbawb/mustache"
	. "github.com/xyproto/browserspeak"
	"github.com/xyproto/web"
)

type ContentPage struct {
	generatedCSSurl          string
	extraCSSurls             []string
	jqueryJSurl              string
	faviconurl               string
	bgImageURL               string
	stretchBackground        bool
	title                    string
	subtitle                 string
	links                    []string
	contentTitle             string
	contentHTML              string
	headerJS                 string
	contentJS                string
	searchButtonText         string
	searchURL                string
	footerText               string
	backgroundTextureURL     string
	darkBackgroundTextureURL string
	footerTextColor          string
	footerColor              string
	userState                *UserState
	roundedLook              bool
	url                      string
	colorScheme              *ColorScheme
}

// Content page generator
type CPgen (func(userState *UserState) *ContentPage)

// A collection of ContentPages
type PageCollection []ContentPage

// Every input from the user must be intitially stored in a UserInput variable, not in a string!
// This is just to be aware of which data one should be careful with, and to keep it clean.
type UserInput string

type UserState struct {
	// see: http://redis.io/topics/data-types
	users       *RedisHashMap   // Hash map of users, with several different fields per user ("loggedin", "confirmed", "email" etc)
	usernames   *RedisSet       // A list of all usernames, for easy enumeration
	unconfirmed *RedisSet       // A list of unconfirmed usernames, for easy enumeration
	pool        *ConnectionPool // A connection pool for Redis
}

type ColorScheme struct {
	darkgray           string
	nicecolor          string
	menu_link          string
	menu_hover         string
	menu_active        string
	default_background string
}

const (
	JQUERY_VERSION = "1.9.1"
)

// browserspeak
var globalStringCache map[string]string

// browserspeak
// Wrap a SimpleContextHandle so that the output is cached (with an id)
// Do not cache functions with side-effects! (that sets the mimetype for instance)
// The safest thing for now is to only cache images.
func CacheWrapper(id string, f SimpleContextHandle) SimpleContextHandle {
	return func(ctx *web.Context) string {
		if _, ok := globalStringCache[id]; !ok {
			globalStringCache[id] = f(ctx)
		}
		return globalStringCache[id]
	}
}

// browserspeak
func Publish(url, filename string, cache bool) {
	if cache {
		web.Get(url, CacheWrapper(url, File(filename)))
	} else {
		web.Get(url, File(filename))
	}
}

// TODO: get style values from a file instead?
func AddHeader(page *Page, js string) {
	AddGoogleFonts(page, []string{"Armata"}) //, "Junge"})
	// TODO: Move to browserspeak
	page.MetaCharset("UTF-8")
	AddScriptToHeader(page, js)
}

// The default settings
// Do not publish this page directly, but use it as a basis for the other pages
func DefaultCP(userState *UserState) *ContentPage {
	var cp ContentPage
	cp.generatedCSSurl = "/css/style.css"
	cp.extraCSSurls = []string{"/css/extra.css"}
	// TODO: fallback to local jquery.min.js, google how
	cp.jqueryJSurl = "//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" // "/js/jquery-1.9.1.js"
	cp.faviconurl = "/favicon.ico"
	cp.links = []string{"Overview:/", "Login:/login", "Logout:/logout", "Register:/register", "Admin:/admin"}
	cp.contentTitle = "NOP"
	cp.contentHTML = "NOP NOP NOP"
	cp.contentJS = ""
	cp.headerJS = ""
	cp.searchButtonText = "Search"
	cp.searchURL = "/search"

	// http://wptheming.wpengine.netdna-cdn.com/wp-content/uploads/2010/04/gray-texture.jpg
	// TODO: Draw these two backgroundimages with a canvas instead
	cp.backgroundTextureURL = "/img/gray.jpg"
	// http://turbo.designwoop.com/uploads/2012/03/16_free_subtle_textures_subtle_dark_vertical.jpg
	cp.darkBackgroundTextureURL = "/img/darkgray.jpg"

	cp.footerColor = "black"
	cp.footerTextColor = "#303040"

	cp.footerText = "NOP"

	cp.userState = userState
	cp.roundedLook = false

	// Javascript that applies everywhere
	//cp.contentJS += HideIfNot("/showmenu/login", "#menuLogin")
	//cp.contentJS += HideIfNot("/showmenu/logout", "#menuLogout")
	//cp.contentJS += HideIfNot("/showmenu/register", "#menuRegister")
	//cp.contentJS += HideIfNotLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")
	//cp.contentJS += ShowIfLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")

	// This only works at first page load in Internet Explorer 8. Fun times. Oh well, why bother.
	cp.headerJS += ShowIfLoginLogoutRegister("/showmenu/loginlogoutregister", "#menuLogin", "#menuLogout", "#menuRegister")

	// This in combination with hiding the link in genericsite.go is cool, but the layout becomes weird :/
	//cp.headerJS += ShowAnimatedIf("/showmenu/admin", "#menuAdmin")

	// This keeps the layout but is less cool
	cp.headerJS += HideIfNot("/showmenu/admin", "#menuAdmin")

	cp.url = "/" // To be filled in when published

	// The default color scheme
	var cs ColorScheme
	cs.darkgray = "#202020"
	cs.nicecolor = "#5080D0"   // nice blue
	cs.menu_link = "#c0c0c0"   // light gray
	cs.menu_hover = "#efefe0"  // light gray, somewhat yellow
	cs.menu_active = "#ffffff" // white
	cs.default_background = "#000030"
	cp.colorScheme = &cs

	return &cp
}

// TODO: Consider using Mustache for replacing elements after the page has been generated
// (for showing/hiding "login", "logout" or "register"
func genericPageBuilder(cp *ContentPage) *Page {
	// TODO: Record the time from one step out, because content may be generated and inserted into this generated conten
	startTime := time.Now()

	page := NewHTML5Page(cp.title + " " + cp.subtitle)

	page.LinkToCSS(cp.generatedCSSurl)
	for _, cssurl := range cp.extraCSSurls {
		page.LinkToCSS(cssurl)
	}
	page.LinkToJS(cp.jqueryJSurl)
	page.LinkToFavicon(cp.faviconurl)

	AddHeader(page, cp.headerJS)
	AddBodyStyle(page, cp.bgImageURL, cp.stretchBackground)
	AddTopBox(page, cp.title, cp.subtitle, cp.searchURL, cp.searchButtonText, cp.backgroundTextureURL, cp.roundedLook, cp.colorScheme)

	// TODO:
	// Use something dynamic to add or remove /login and /register depending on the login status
	// The login status can be fetched over AJAX or REST or something.

	// TODO: Move the menubox into the TopBox

	// TODO: Do this with templates instead
	// Hide login/logout/register by default
	hidelist := []string{"/login", "/logout", "/register"} //, "/admin"}
	AddMenuBox(page, cp.links, hidelist, cp.darkBackgroundTextureURL)

	AddContent(page, cp.contentTitle, cp.contentHTML+BodyJS(cp.contentJS))

	elapsed := time.Since(startTime)
	AddFooter(page, cp.footerText, cp.footerTextColor, cp.footerColor, elapsed)

	return page
}

// Publish a list of ContentPaages, a colorscheme and template content
func PublishCPs(pc PageCollection, cs *ColorScheme, tp map[string]string, cssurl string) {
	// For each content page in the page collection
	for _, cp := range pc {
		// TODO: different css urls for all of these?
		cp.Pub(cp.url, cssurl, cs, tp)
	}
}

type BaseCP func(state *UserState) *ContentPage

func ServeSite(basecp BaseCP, userState *UserState, cps PageCollection, tp map[string]string) {
	// Add pages for login, logout and register
	cps = append(cps, *LoginCP(basecp, userState, "/login"))
	cps = append(cps, *RegisterCP(basecp, userState, "/register"))

	cs := basecp(userState).colorScheme
	PublishCPs(cps, cs, tp, "/css/extra.css")

	ServeSearchPages(basecp, userState, cps, cs, tp)
	ServeAdminPages(basecp, userState, cs, tp)

	// TODO: Add fallback to this local version
	Publish("/js/jquery-"+JQUERY_VERSION+".js", "static/js/jquery-"+JQUERY_VERSION+".js", true)

	// TODO: Generate these
	Publish("/robots.txt", "static/various/robots.txt", false)
	Publish("/sitemap_index.xml", "static/various/sitemap_index.xml", false)
	Publish("/favicon.ico", "static/img/favicon.ico", false)
}

func GenerateMenuCSS(stretchBackground bool, cs *ColorScheme) SimpleContextHandle {
	return func(ctx *web.Context) string {
		ctx.ContentType("css")
		// one of the extra css files that are loaded after the main style
		retval := `
a {
  text-decoration: none;
  color: #303030;
  font-weight: regular;
}
a:link {color:` + cs.menu_link + `;}
a:visited {color:` + cs.menu_link + `;}
a:hover {color:` + cs.menu_hover + `;}
a:active {color:` + cs.menu_active + `;}
`
		// The load order of background-color, background-size and background-image
		// is actually significant in Chrome! Do not reorder lightly!
		if stretchBackground {
			retval = "body {\nbackground-color: " + cs.default_background + ";\nbackground-size: cover;\n}\n" + retval
		} else {
			retval = "body {\nbackground-color: " + cs.default_background + ";\n}\n" + retval
		}
		return retval
	}
}

// Make an html and css page available
func (cp *ContentPage) Pub(url, cssurl string, cs *ColorScheme, templateContent map[string]string) {
	genericpage := genericPageBuilder(cp)
	web.Get(url, GenerateHTMLwithTemplate(genericpage, templateContent))
	web.Get(cp.generatedCSSurl, GenerateCSS(genericpage))
	web.Get(cssurl, GenerateMenuCSS(cp.stretchBackground, cs))
}

// Wrap a lonely string in an entire webpage
func (cp *ContentPage) Surround(s string, tp map[string]string) (string, string) {
	cp.contentHTML = s
	archpage := genericPageBuilder(cp)
	// NOTE: Use GetXML(true) instead of .String() or .GetHTML() because some things are rendered
	// differently with different text layout!
	return mustache.Render(archpage.GetXML(true), tp), archpage.GetCSS()
}

// Uses a given SimpleWebHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapSimpleWebHandle(swh SimpleWebHandle, tp map[string]string) SimpleWebHandle {
	return func(val string) string {
		html, css := cp.Surround(swh(val), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

// Uses a given WebHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapWebHandle(wh WebHandle, tp map[string]string) WebHandle {
	return func(ctx *web.Context, val string) string {
		html, css := cp.Surround(wh(ctx, val), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

// Uses a given SimpleContextHandle as the contents for the the ContentPage contents
func (cp *ContentPage) WrapSimpleContextHandle(sch SimpleContextHandle, tp map[string]string) SimpleContextHandle {
	return func(ctx *web.Context) string {
		html, css := cp.Surround(sch(ctx), tp)
		web.Get(cp.generatedCSSurl, css)
		return html
	}
}

func InitSystem() *UserState {
	// These common ones are missing!
	mime.AddExtensionType(".txt", "text/plain; charset=utf-8")
	mime.AddExtensionType(".ico", "image/x-icon")

	// Create a Redis connection pool
	pool := NewRedisConnectionPool()
	//if err != nil {
	//	panic("ERROR: Can't connect to redis")
	//}
	defer pool.Close()

	// The login system, returns a *UserState
	return InitUserSystem(pool)
}
<|endoftext|>


<commit_before>package gogl

import (
	"fmt"
	"github.com/fatih/set"
	. "launchpad.net/gocheck"
	"testing"
)

var _ = fmt.Println

// Hook gocheck into the go test runner
func Test(t *testing.T) { TestingT(t) }

var edgeSet = []Edge{
	&BaseEdge{"foo", "bar"},
	&BaseEdge{"bar", "baz"},
}

type GraphFactory struct {
	CreateMutableGraph func() MutableGraph
	CreateGraph        func([]Edge) Graph
}

/* GraphSuite - tests for non-mutable graph methods */
type GraphSuite struct {
	Graph   Graph
	Factory *GraphFactory
}

func (s *GraphSuite) SetUpTest(c *C) {
	s.Graph = s.Factory.CreateGraph(edgeSet)
}

func (s *GraphSuite) TestVertexMembership(c *C) {
	c.Assert(s.Graph.HasVertex("foo"), Equals, true)
}

func (s *GraphSuite) TestEachVertex(c *C) {
	var hit int
	f := func(v Vertex) {
		hit++
		c.Log("EachVertex hit closure, hit count", hit)
	}

	s.Graph.EachVertex(f)
	if !c.Check(hit, Equals, 3) {
		c.Error("EachVertex should have called injected closure iterator 3 times, actual count was ", hit)
	}
}

func (s *GraphSuite) TestEachEdge(c *C) {
	var hit int
	f := func(e Edge) {
		hit++
		c.Log("EachAdjacent hit closure with edge pair ", e.Source(), " ", e.Target(), " at hit count ", hit)
	}

	s.Graph.EachEdge(f)
	if !c.Check(hit, Equals, 2) {
		c.Error("EachEdge should have called injected closure iterator 2 times, actual count was ", hit)
	}
}

func (s *GraphSuite) TestEachAdjacent(c *C) {
	var hit int
	f := func(adj Vertex) {
		hit++
		c.Log("EachAdjacent hit closure with vertex ", adj, " at hit count ", hit)
	}

	s.Graph.EachAdjacent("foo", f)
	if !c.Check(hit, Equals, 1) {
		c.Error("EachEdge should have called injected closure iterator 2 times, actual count was ", hit)
	}
}

// This test is carefully constructed to be fully correct for directed graphs,
// and incidentally correct for undirected graphs.
func (s *GraphSuite) TestOutDegree(c *C) {
	g := s.Factory.CreateGraph([]Edge{&BaseEdge{"foo", "bar"}})

	count, exists := g.OutDegree("foo")
	c.Assert(exists, Equals, true)
	c.Assert(count, Equals, 1)

	count, exists = g.OutDegree("missing")
	c.Assert(exists, Equals, false)
	c.Assert(count, Equals, 0)
}

// This test is carefully constructed to be fully correct for directed graphs,
// and incidentally correct for undirected graphs.
func (s *GraphSuite) TestInDegree(c *C) {
	g := s.Factory.CreateGraph([]Edge{&BaseEdge{"foo", "bar"}})

	count, exists := g.InDegree("bar")
	c.Assert(exists, Equals, true)
	c.Assert(count, Equals, 1)

	count, exists = g.InDegree("missing")
	c.Assert(exists, Equals, false)
	c.Assert(count, Equals, 0)
}

func (s *GraphSuite) TestSize(c *C) {
	c.Assert(s.Graph.Size(), Equals, 2)

	g := s.Factory.CreateGraph([]Edge{})
	c.Assert(g.Size(), Equals, 0)
}

func (s *GraphSuite) TestOrder(c *C) {
	c.Assert(s.Graph.Size(), Equals, 2)

	g := s.Factory.CreateGraph([]Edge{})
	c.Assert(g.Size(), Equals, 0)
}

/* MutableGraphSuite - tests for mutable graph methods */
type MutableGraphSuite struct {
	Graph   MutableGraph
	Factory *GraphFactory
}

func (s *MutableGraphSuite) SetUpTest(c *C) {
	s.Graph = s.Factory.CreateMutableGraph()
}

func (s *MutableGraphSuite) TestEnsureVertex(c *C) {
	s.Graph.EnsureVertex("foo")
	c.Assert(s.Graph.HasVertex("foo"), Equals, true)
}

func (s *MutableGraphSuite) TestMultiEnsureVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, true)
	c.Assert(s.Graph.HasVertex("baz"), Equals, true)
}

func (s *MutableGraphSuite) TestRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
}

func (s *MutableGraphSuite) TestMultiRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
	c.Assert(s.Graph.HasVertex("baz"), Equals, false)
}

func (s *MutableGraphSuite) TestAddEdge(c *C) {
	s.Graph.AddEdges(&BaseEdge{1, 2})

	f := func(e Edge) {
		c.Assert(BaseEdge{e.Source(), e.Target()}, Equals, BaseEdge{1, 2})
	}

	s.Graph.EachEdge(f)
}

func (s *MutableGraphSuite) TestMultiAddEdge(c *C) {
	s.Graph.AddEdges(&BaseEdge{1, 2}, &BaseEdge{2, 3})
	set := set.NewNonTS()

	f := func(e Edge) {
		set.Add(e)
	}

	s.Graph.EachEdge(f)
	c.Assert(set.Has(BaseEdge{1, 2}), Equals, true)
	c.Assert(set.Has(BaseEdge{2, 3}), Equals, true)
}

<commit_msg>Add edge removal tests<commit_after>package gogl

import (
	"fmt"
	"github.com/fatih/set"
	. "launchpad.net/gocheck"
	"testing"
)

var _ = fmt.Println

// Hook gocheck into the go test runner
func Test(t *testing.T) { TestingT(t) }

var edgeSet = []Edge{
	&BaseEdge{"foo", "bar"},
	&BaseEdge{"bar", "baz"},
}

type GraphFactory struct {
	CreateMutableGraph func() MutableGraph
	CreateGraph        func([]Edge) Graph
}

/* GraphSuite - tests for non-mutable graph methods */
type GraphSuite struct {
	Graph   Graph
	Factory *GraphFactory
}

func (s *GraphSuite) SetUpTest(c *C) {
	s.Graph = s.Factory.CreateGraph(edgeSet)
}

func (s *GraphSuite) TestVertexMembership(c *C) {
	c.Assert(s.Graph.HasVertex("foo"), Equals, true)
}

func (s *GraphSuite) TestEachVertex(c *C) {
	var hit int
	f := func(v Vertex) {
		hit++
		c.Log("EachVertex hit closure, hit count", hit)
	}

	s.Graph.EachVertex(f)
	if !c.Check(hit, Equals, 3) {
		c.Error("EachVertex should have called injected closure iterator 3 times, actual count was ", hit)
	}
}

func (s *GraphSuite) TestEachEdge(c *C) {
	var hit int
	f := func(e Edge) {
		hit++
		c.Log("EachAdjacent hit closure with edge pair ", e.Source(), " ", e.Target(), " at hit count ", hit)
	}

	s.Graph.EachEdge(f)
	if !c.Check(hit, Equals, 2) {
		c.Error("EachEdge should have called injected closure iterator 2 times, actual count was ", hit)
	}
}

func (s *GraphSuite) TestEachAdjacent(c *C) {
	var hit int
	f := func(adj Vertex) {
		hit++
		c.Log("EachAdjacent hit closure with vertex ", adj, " at hit count ", hit)
	}

	s.Graph.EachAdjacent("foo", f)
	if !c.Check(hit, Equals, 1) {
		c.Error("EachEdge should have called injected closure iterator 2 times, actual count was ", hit)
	}
}

// This test is carefully constructed to be fully correct for directed graphs,
// and incidentally correct for undirected graphs.
func (s *GraphSuite) TestOutDegree(c *C) {
	g := s.Factory.CreateGraph([]Edge{&BaseEdge{"foo", "bar"}})

	count, exists := g.OutDegree("foo")
	c.Assert(exists, Equals, true)
	c.Assert(count, Equals, 1)

	count, exists = g.OutDegree("missing")
	c.Assert(exists, Equals, false)
	c.Assert(count, Equals, 0)
}

// This test is carefully constructed to be fully correct for directed graphs,
// and incidentally correct for undirected graphs.
func (s *GraphSuite) TestInDegree(c *C) {
	g := s.Factory.CreateGraph([]Edge{&BaseEdge{"foo", "bar"}})

	count, exists := g.InDegree("bar")
	c.Assert(exists, Equals, true)
	c.Assert(count, Equals, 1)

	count, exists = g.InDegree("missing")
	c.Assert(exists, Equals, false)
	c.Assert(count, Equals, 0)
}

func (s *GraphSuite) TestSize(c *C) {
	c.Assert(s.Graph.Size(), Equals, 2)

	g := s.Factory.CreateGraph([]Edge{})
	c.Assert(g.Size(), Equals, 0)
}

func (s *GraphSuite) TestOrder(c *C) {
	c.Assert(s.Graph.Size(), Equals, 2)

	g := s.Factory.CreateGraph([]Edge{})
	c.Assert(g.Size(), Equals, 0)
}

/* MutableGraphSuite - tests for mutable graph methods */
type MutableGraphSuite struct {
	Graph   MutableGraph
	Factory *GraphFactory
}

func (s *MutableGraphSuite) SetUpTest(c *C) {
	s.Graph = s.Factory.CreateMutableGraph()
}

func (s *MutableGraphSuite) TestEnsureVertex(c *C) {
	s.Graph.EnsureVertex("foo")
	c.Assert(s.Graph.HasVertex("foo"), Equals, true)
}

func (s *MutableGraphSuite) TestMultiEnsureVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, true)
	c.Assert(s.Graph.HasVertex("baz"), Equals, true)
}

func (s *MutableGraphSuite) TestRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
}

func (s *MutableGraphSuite) TestMultiRemoveVertex(c *C) {
	s.Graph.EnsureVertex("bar", "baz")
	s.Graph.RemoveVertex("bar", "baz")
	c.Assert(s.Graph.HasVertex("bar"), Equals, false)
	c.Assert(s.Graph.HasVertex("baz"), Equals, false)
}

func (s *MutableGraphSuite) TestAddAndRemoveEdge(c *C) {
	s.Graph.AddEdges(&BaseEdge{1, 2})

	f := func(e Edge) {
		c.Assert(BaseEdge{e.Source(), e.Target()}, Equals, BaseEdge{1, 2})
	}

	s.Graph.EachEdge(f)

	// Now test removal
	f = func(e Edge) {
		c.Error("Graph should not contain any edges after removal.")
	}

	s.Graph.RemoveEdges(&BaseEdge{1, 2})
	s.Graph.EachEdge(f)
}

func (s *MutableGraphSuite) TestMultiAddAndRemoveEdge(c *C) {
	s.Graph.AddEdges(&BaseEdge{1, 2}, &BaseEdge{2, 3})
	set := set.NewNonTS()

	f := func(e Edge) {
		set.Add(e)
	}

	s.Graph.EachEdge(f)
	c.Assert(set.Has(BaseEdge{1, 2}), Equals, true)
	c.Assert(set.Has(BaseEdge{2, 3}), Equals, true)

	// Now test removal
	f = func(e Edge) {
		c.Error("Graph should not contain any edges after removal.")
	}

	s.Graph.RemoveEdges(&BaseEdge{1, 2}, &BaseEdge{2, 3})
	s.Graph.EachEdge(f)
}
<|endoftext|>


<commit_before>package grayt

import (
	"math"
)

type grid struct {
	minBound Vector
	maxBound Vector

	stride     Vector
	data       []*link
	resolution triple
}

func newGrid(lambda float64, objs ObjectList) *grid {

	minBound, maxBound := bounds(objs)
	boundDiff := maxBound.Sub(minBound)
	volume := boundDiff.X * boundDiff.Y * boundDiff.Z

	resolutionFactor := math.Pow(lambda*float64(len(objs))/volume, 1.0/3.0)
	resolution := truncate(boundDiff.Scale(resolutionFactor))
	stride := boundDiff.div(resolution.asVector())
	data := make([]*link, resolution.x*resolution.y*resolution.z)

	grid := &grid{
		minBound,
		maxBound,
		stride,
		data,
		resolution,
	}
	grid.populate(objs)

	return grid
}

func bounds(objs ObjectList) (Vector, Vector) {
	inf := math.Inf(+1)
	minBound, maxBound := Vect(+inf, +inf, +inf), Vect(-inf, -inf, -inf)
	for _, obj := range objs {
		min, max := obj.bound()
		minBound = minBound.Min(min)
		maxBound = maxBound.Max(max)
	}
	return minBound, maxBound
}

func (g *grid) populate(objs ObjectList) {
	for _, obj := range objs {
		min, max := obj.bound()
		minCoord := truncate(min.Sub(g.minBound).div(g.stride)).min(g.resolution.sub(triple{1, 1, 1}))
		maxCoord := truncate(max.Sub(g.minBound).div(g.stride)).min(g.resolution.sub(triple{1, 1, 1}))
		var pos triple
		for pos.x = minCoord.x; pos.x <= maxCoord.x; pos.x++ {
			for pos.y = minCoord.y; pos.y <= maxCoord.y; pos.y++ {
				for pos.z = minCoord.z; pos.z <= maxCoord.z; pos.z++ {
					idx := g.dataIndex(pos)
					g.data[idx] = &link{g.data[idx], obj}
				}
			}
		}
	}
}

func (g *grid) closestHit(r ray) (intersection, material, bool) {

	var distance float64
	if !g.insideBoundingBox(r.start) {
		var hit bool
		distance, hit = g.hitBoundingBox(r)
		if !hit {
			return intersection{}, material{}, false
		}
	}

	cellCoordsFloat := g.cellCoordsFloat(r.at(distance))
	pos := g.cellCoordsInt(cellCoordsFloat)
	delta := g.delta(r)
	inc := g.inc(r)
	next := g.next(cellCoordsFloat, r)

	for true {

		if intersection, material, hit := g.findHitInCell(pos, next, r); hit {
			return intersection, material, true
		}

		var exitGrid bool
		next, pos, exitGrid = g.nextCell(next, delta, pos, inc)
		if exitGrid {
			break
		}
	}

	return intersection{}, material{}, false
}

func (g *grid) insideBoundingBox(v Vector) bool {
	return true &&
		v.X >= g.minBound.X && v.X <= g.maxBound.X &&
		v.Y >= g.minBound.Y && v.Y <= g.maxBound.Y &&
		v.Z >= g.minBound.Z && v.Z <= g.maxBound.Z
}

func (g *grid) hitBoundingBox(r ray) (float64, bool) {

	tx1 := (g.minBound.X - r.start.X) / r.dir.X
	tx2 := (g.maxBound.X - r.start.X) / r.dir.X
	ty1 := (g.minBound.Y - r.start.Y) / r.dir.Y
	ty2 := (g.maxBound.Y - r.start.Y) / r.dir.Y
	tz1 := (g.minBound.Z - r.start.Z) / r.dir.Z
	tz2 := (g.maxBound.Z - r.start.Z) / r.dir.Z

	tmin, tmax := math.Inf(-1), math.Inf(+1)

	tmin = math.Max(tmin, math.Min(tx1, tx2))
	tmax = math.Min(tmax, math.Max(tx1, tx2))
	tmin = math.Max(tmin, math.Min(ty1, ty2))
	tmax = math.Min(tmax, math.Max(ty1, ty2))
	tmin = math.Max(tmin, math.Min(tz1, tz2))
	tmax = math.Min(tmax, math.Max(tz1, tz2))

	return tmin, tmin <= tmax && tmin >= 0
}

func (g *grid) cellCoordsFloat(v Vector) Vector {
	return v.
		Sub(g.minBound).
		div(g.stride)
}

func (g *grid) cellCoordsInt(cellCoordsFloat Vector) triple {
	return truncate(cellCoordsFloat).
		min(g.resolution.sub(triple{1, 1, 1})).
		max(triple{})
}

func (g *grid) delta(r ray) Vector {
	return g.stride.
		div(r.dir).
		abs()
}

func (g *grid) inc(r ray) triple {
	return truncate(
		r.dir.sign(),
	)
}

func (g *grid) next(cellCoordsFloat Vector, r ray) Vector {
	return g.cellCoordsInt(cellCoordsFloat).asVector().
		Add(r.dir.
			sign().
			Scale(0.5).
			Add(Vect(0.5, 0.5, 0.5)),
		).
		mul(g.stride).
		Sub(r.start.Sub(g.minBound)).
		div(r.dir)
}

func (g *grid) nextCell(next, delta Vector, pos, inc triple) (Vector, triple, bool) {

	// TODO: Is is numerically stable to keep incrementing next? Could we
	// instead compute it fresh each time? Does it really matter if it's
	// numerically stable?
	var exitGrid bool
	switch {
	case next.X < math.Min(next.Y, next.Z):
		pos.x += inc.x
		next.X += delta.X
		exitGrid = pos.x < 0 && inc.x < 0 || pos.x >= g.resolution.x && inc.x > 0
	case next.Y < next.Z:
		pos.y += inc.y
		next.Y += delta.Y
		exitGrid = pos.y < 0 && inc.y < 0 || pos.y >= g.resolution.y && inc.y > 0
	default:
		pos.z += inc.z
		next.Z += delta.Z
		exitGrid = pos.z < 0 && inc.z < 0 || pos.z >= g.resolution.z && inc.z > 0
	}
	return next, pos, exitGrid
}

func (g *grid) dataIndex(pos triple) int {
	return pos.x + g.resolution.x*pos.y + g.resolution.x*g.resolution.y*pos.z
}

func (g *grid) findHitInCell(pos triple, next Vector, r ray) (intersection, material, bool) {

	var closest struct {
		intersection intersection
		material     material
		hit          bool
	}

	for link := g.data[g.dataIndex(pos)]; link != nil; link = link.next {
		intersection, hit := link.obj.intersect(r)
		if !hit {
			continue
		}
		nextCell := addULPs(math.Min(next.X, math.Min(next.Y, next.Z)), ulpFudgeFactor)
		if intersection.distance > nextCell {
			continue
		}
		if !closest.hit || intersection.distance < closest.intersection.distance {
			closest.intersection = intersection
			closest.material = link.obj.material
			closest.hit = true
		}
	}

	return closest.intersection, closest.material, closest.hit
}

type link struct {
	next *link
	obj  Object
}
<commit_msg>Fix bug in calculation of nextHitDistance<commit_after>package grayt

import (
	"math"
)

type grid struct {
	minBound Vector
	maxBound Vector

	stride     Vector
	data       []*link
	resolution triple
}

func newGrid(lambda float64, objs ObjectList) *grid {

	minBound, maxBound := bounds(objs)
	boundDiff := maxBound.Sub(minBound)
	volume := boundDiff.X * boundDiff.Y * boundDiff.Z

	resolutionFactor := math.Pow(lambda*float64(len(objs))/volume, 1.0/3.0)
	resolution := truncate(boundDiff.Scale(resolutionFactor))
	stride := boundDiff.div(resolution.asVector())
	data := make([]*link, resolution.x*resolution.y*resolution.z)

	grid := &grid{
		minBound,
		maxBound,
		stride,
		data,
		resolution,
	}
	grid.populate(objs)

	return grid
}

func bounds(objs ObjectList) (Vector, Vector) {
	inf := math.Inf(+1)
	minBound, maxBound := Vect(+inf, +inf, +inf), Vect(-inf, -inf, -inf)
	for _, obj := range objs {
		min, max := obj.bound()
		minBound = minBound.Min(min)
		maxBound = maxBound.Max(max)
	}
	return minBound, maxBound
}

func (g *grid) populate(objs ObjectList) {
	for _, obj := range objs {
		min, max := obj.bound()
		minCoord := truncate(min.Sub(g.minBound).div(g.stride)).min(g.resolution.sub(triple{1, 1, 1}))
		maxCoord := truncate(max.Sub(g.minBound).div(g.stride)).min(g.resolution.sub(triple{1, 1, 1}))
		var pos triple
		for pos.x = minCoord.x; pos.x <= maxCoord.x; pos.x++ {
			for pos.y = minCoord.y; pos.y <= maxCoord.y; pos.y++ {
				for pos.z = minCoord.z; pos.z <= maxCoord.z; pos.z++ {
					idx := g.dataIndex(pos)
					g.data[idx] = &link{g.data[idx], obj}
				}
			}
		}
	}
}

func (g *grid) closestHit(r ray) (intersection, material, bool) {

	var distance float64
	if !g.insideBoundingBox(r.start) {
		var hit bool
		distance, hit = g.hitBoundingBox(r)
		if !hit {
			return intersection{}, material{}, false
		}
	}

	cellCoordsFloat := g.cellCoordsFloat(r.at(distance))
	initialPos := g.cellCoordsInt(cellCoordsFloat)
	delta := g.delta(r)
	inc := g.inc(r)
	initialNextHitDistance := g.next(cellCoordsFloat, r)

	var pos = initialPos

	for true {

		nextHitDistance := pos.sub(initialPos).asVector().abs().mul(delta).Add(initialNextHitDistance)

		if intersection, material, hit := g.findHitInCell(pos, nextHitDistance, r); hit {
			return intersection, material, true
		}

		var exitGrid bool
		pos, exitGrid = g.nextCell(nextHitDistance, initialPos, pos, inc)
		if exitGrid {
			break
		}
	}

	return intersection{}, material{}, false
}

func (g *grid) insideBoundingBox(v Vector) bool {
	return true &&
		v.X >= g.minBound.X && v.X <= g.maxBound.X &&
		v.Y >= g.minBound.Y && v.Y <= g.maxBound.Y &&
		v.Z >= g.minBound.Z && v.Z <= g.maxBound.Z
}

func (g *grid) hitBoundingBox(r ray) (float64, bool) {

	tx1 := (g.minBound.X - r.start.X) / r.dir.X
	tx2 := (g.maxBound.X - r.start.X) / r.dir.X
	ty1 := (g.minBound.Y - r.start.Y) / r.dir.Y
	ty2 := (g.maxBound.Y - r.start.Y) / r.dir.Y
	tz1 := (g.minBound.Z - r.start.Z) / r.dir.Z
	tz2 := (g.maxBound.Z - r.start.Z) / r.dir.Z

	tmin, tmax := math.Inf(-1), math.Inf(+1)

	tmin = math.Max(tmin, math.Min(tx1, tx2))
	tmax = math.Min(tmax, math.Max(tx1, tx2))
	tmin = math.Max(tmin, math.Min(ty1, ty2))
	tmax = math.Min(tmax, math.Max(ty1, ty2))
	tmin = math.Max(tmin, math.Min(tz1, tz2))
	tmax = math.Min(tmax, math.Max(tz1, tz2))

	return tmin, tmin <= tmax && tmin >= 0
}

func (g *grid) cellCoordsFloat(v Vector) Vector {
	return v.
		Sub(g.minBound).
		div(g.stride)
}

func (g *grid) cellCoordsInt(cellCoordsFloat Vector) triple {
	return truncate(cellCoordsFloat).
		min(g.resolution.sub(triple{1, 1, 1})).
		max(triple{})
}

func (g *grid) delta(r ray) Vector {
	return g.stride.
		div(r.dir).
		abs()
}

func (g *grid) inc(r ray) triple {
	return truncate(
		r.dir.sign(),
	)
}

func (g *grid) next(cellCoordsFloat Vector, r ray) Vector {
	return g.cellCoordsInt(cellCoordsFloat).asVector().
		Add(r.dir.
			sign().
			Scale(0.5).
			Add(Vect(0.5, 0.5, 0.5)),
		).
		mul(g.stride).
		Sub(r.start.Sub(g.minBound)).
		div(r.dir)
}

func (g *grid) nextCell(next Vector, initialPos, pos, inc triple) (triple, bool) {

	var exitGrid bool
	switch {
	case next.X < math.Min(next.Y, next.Z):
		pos.x += inc.x
		exitGrid = pos.x < 0 && inc.x < 0 || pos.x >= g.resolution.x && inc.x > 0
	case next.Y < next.Z:
		pos.y += inc.y
		exitGrid = pos.y < 0 && inc.y < 0 || pos.y >= g.resolution.y && inc.y > 0
	default:
		pos.z += inc.z
		exitGrid = pos.z < 0 && inc.z < 0 || pos.z >= g.resolution.z && inc.z > 0
	}
	return pos, exitGrid
}

func (g *grid) dataIndex(pos triple) int {
	return pos.x + g.resolution.x*pos.y + g.resolution.x*g.resolution.y*pos.z
}

func (g *grid) findHitInCell(pos triple, next Vector, r ray) (intersection, material, bool) {

	var closest struct {
		intersection intersection
		material     material
		hit          bool
	}

	for link := g.data[g.dataIndex(pos)]; link != nil; link = link.next {
		intersection, hit := link.obj.intersect(r)
		if !hit {
			continue
		}
		nextCell := addULPs(math.Min(next.X, math.Min(next.Y, next.Z)), ulpFudgeFactor)
		if intersection.distance > nextCell {
			continue
		}
		if !closest.hit || intersection.distance < closest.intersection.distance {
			closest.intersection = intersection
			closest.material = link.obj.material
			closest.hit = true
		}
	}

	return closest.intersection, closest.material, closest.hit
}

type link struct {
	next *link
	obj  Object
}
<|endoftext|>


<commit_before>package vodka

import (
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"

	"strings"

	"encoding/xml"
	"net/url"

	"github.com/insionng/vodka/libraries/stretchr/testify/assert"
)

type (
	Template struct {
		templates *template.Template
	}
)

func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func TestContext(t *testing.T) {
	userJSON := `{"id":"1","name":"Joe"}`
	userXML := `<user><id>1</id><name>Joe</name></user>`

	req, _ := http.NewRequest(POST, "/", strings.NewReader(userJSON))
	rec := httptest.NewRecorder()
	c := NewContext(req, NewResponse(rec), New())

	// Request
	assert.NotNil(t, c.Request())

	// Response
	assert.NotNil(t, c.Response())

	// Socket
	assert.Nil(t, c.Socket())

	// Param by id
	c.pnames = []string{"id"}
	c.pvalues = []string{"1"}
	assert.Equal(t, "1", c.P(0))

	// Param by name
	assert.Equal(t, "1", c.Param("id"))

	// Store
	c.Set("user", "Joe")
	assert.Equal(t, "Joe", c.Get("user"))

	//------
	// Bind
	//------

	// JSON
	testBind(t, c, "application/json")

	// XML
	c.request, _ = http.NewRequest(POST, "/", strings.NewReader(userXML))
	testBind(t, c, ApplicationXML)

	// Unsupported
	testBind(t, c, "")

	//--------
	// Render
	//--------

	tpl := &Template{
		templates: template.Must(template.New("hello").Parse("Hello, {{.}}!")),
	}
	c.vodka.SetRenderer(tpl)
	err := c.Render(http.StatusOK, "hello", "Joe")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, "Hello, Joe!", rec.Body.String())
	}

	c.vodka.renderer = nil
	err = c.Render(http.StatusOK, "hello", "Joe")
	assert.Error(t, err)

	// JSON
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.JSON(http.StatusOK, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationJSONCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, userJSON+"\n", rec.Body.String())
	}

	// JSONP
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	callback := "callback"
	err = c.JSONP(http.StatusOK, callback, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationJavaScriptCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, callback+"("+userJSON+"\n);", rec.Body.String())
	}

	// XML
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.XML(http.StatusOK, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationXMLCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, xml.Header, xml.Header, rec.Body.String())
	}

	// String
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.String(http.StatusOK, "Hello, World!")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, TextPlain, rec.Header().Get(ContentType))
		assert.Equal(t, "Hello, World!", rec.Body.String())
	}

	// HTML
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.HTML(http.StatusOK, "Hello, World!")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, TextHTMLCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, "Hello, World!", rec.Body.String())
	}

	// File
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.File("test/fixture/walle.png", false)
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, 219885, rec.Body.Len())
	}

	// File as attachment
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.File("test/fixture/walle.png", true)
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=walle.png")
		assert.Equal(t, 219885, rec.Body.Len())
	}

	// Status
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	c.Status(http.StatusOK)
	assert.Equal(t, http.StatusOK, c.response.status)

	// Redirect
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	assert.Equal(t, nil, c.Redirect(http.StatusMovedPermanently, "http://labstack.github.io/vodka"))

	// Error
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	c.Error(errors.New("error"))
	assert.Equal(t, http.StatusInternalServerError, c.response.status)

	// reset
	c.reset(req, NewResponse(httptest.NewRecorder()), New())
}

func TestContextQuery(t *testing.T) {
	q := make(url.Values)
	q.Set("name", "joe")
	q.Set("email", "joe@labstack.com")

	req, err := http.NewRequest(GET, "/", nil)
	assert.NoError(t, err)
	req.URL.RawQuery = q.Encode()

	c := NewContext(req, nil, New())
	assert.Equal(t, "joe", c.Query("name"))
	assert.Equal(t, "joe@labstack.com", c.Query("email"))

}

func TestContextForm(t *testing.T) {
	f := make(url.Values)
	f.Set("name", "joe")
	f.Set("email", "joe@labstack.com")

	req, err := http.NewRequest(POST, "/", strings.NewReader(f.Encode()))
	assert.NoError(t, err)
	req.Header.Add(ContentType, ApplicationForm)

	c := NewContext(req, nil, New())
	assert.Equal(t, "joe", c.Form("name"))
	assert.Equal(t, "joe@labstack.com", c.Form("email"))
}

func testBind(t *testing.T, c *Context, ct string) {
	c.request.Header.Set(ContentType, ct)
	u := new(user)
	err := c.Bind(u)
	if ct == "" {
		assert.Error(t, UnsupportedMediaType)
	} else if assert.NoError(t, err) {
		assert.Equal(t, "1", u.ID)
		assert.Equal(t, "Joe", u.Name)
	}
}
<commit_msg>0.0.6<commit_after>package vodka

import (
	"errors"
	"io"
	"net/http"
	"net/http/httptest"
	"testing"
	"text/template"

	"strings"

	"encoding/xml"
	"net/url"

	"github.com/insionng/vodka/libraries/stretchr/testify/assert"
)

type (
	Template struct {
		templates *template.Template
	}
)

func (t *Template) Render(w io.Writer, name string, data interface{}) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func TestContext(t *testing.T) {
	userJSON := `{"id":"1","name":"Joe"}`
	userXML := `<user><id>1</id><name>Joe</name></user>`

	req, _ := http.NewRequest(POST, "/", strings.NewReader(userJSON))
	rec := httptest.NewRecorder()
	c := NewContext(req, NewResponse(rec), New())

	// Request
	assert.NotNil(t, c.Request())

	// Response
	assert.NotNil(t, c.Response())

	// Socket
	assert.Nil(t, c.Socket())

	// Param by id
	c.pnames = []string{"id"}
	c.pvalues = []string{"1"}
	assert.Equal(t, "1", c.P(0))

	// Param by name
	assert.Equal(t, "1", c.Param("id"))

	// Store
	c.Set("user", "Joe")
	assert.Equal(t, "Joe", c.Get("user"))

	//------
	// Bind
	//------

	// JSON
	testBind(t, c, "application/json")

	// XML
	c.request, _ = http.NewRequest(POST, "/", strings.NewReader(userXML))
	testBind(t, c, ApplicationXML)

	// Unsupported
	testBind(t, c, "")

	//--------
	// Render
	//--------

	tpl := &Template{
		templates: template.Must(template.New("hello").Parse("Hello, {{.}}!")),
	}
	c.vodka.SetRenderer(tpl)
	err := c.Render(http.StatusOK, "hello", "Joe")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, "Hello, Joe!", rec.Body.String())
	}

	c.vodka.renderer = nil
	err = c.Render(http.StatusOK, "hello", "Joe")
	assert.Error(t, err)

	// JSON
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.JSON(http.StatusOK, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationJSONCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, userJSON+"\n", rec.Body.String())
	}

	// JSONP
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	callback := "callback"
	err = c.JSONP(http.StatusOK, callback, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationJavaScriptCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, callback+"("+userJSON+"\n);", rec.Body.String())
	}

	// XML
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.XML(http.StatusOK, user{"1", "Joe"})
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, ApplicationXMLCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, xml.Header, xml.Header, rec.Body.String())
	}

	// String
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.String(http.StatusOK, "Hello, World!")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, TextPlain, rec.Header().Get(ContentType))
		assert.Equal(t, "Hello, World!", rec.Body.String())
	}

	// HTML
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.HTML(http.StatusOK, "Hello, World!")
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, TextHTMLCharsetUTF8, rec.Header().Get(ContentType))
		assert.Equal(t, "Hello, World!", rec.Body.String())
	}

	// File
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.File("test/fixture/walle.png", false)
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, 219885, rec.Body.Len())
	}

	// File as attachment
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	err = c.File("test/fixture/walle.png", true)
	if assert.NoError(t, err) {
		assert.Equal(t, http.StatusOK, rec.Code)
		assert.Equal(t, rec.Header().Get(ContentDisposition), "attachment; filename=walle.png")
		assert.Equal(t, 219885, rec.Body.Len())
	}

	// Status
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	c.Status(http.StatusOK)
	assert.Equal(t, http.StatusOK, c.response.status)

	// Redirect
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	assert.Equal(t, nil, c.Redirect(http.StatusMovedPermanently, "http://github.com/insionng/vodka"))

	// Error
	rec = httptest.NewRecorder()
	c = NewContext(req, NewResponse(rec), New())
	c.Error(errors.New("error"))
	assert.Equal(t, http.StatusInternalServerError, c.response.status)

	// reset
	c.reset(req, NewResponse(httptest.NewRecorder()), New())
}

func TestContextQuery(t *testing.T) {
	q := make(url.Values)
	q.Set("name", "joe")
	q.Set("email", "joe@labstack.com")

	req, err := http.NewRequest(GET, "/", nil)
	assert.NoError(t, err)
	req.URL.RawQuery = q.Encode()

	c := NewContext(req, nil, New())
	assert.Equal(t, "joe", c.Query("name"))
	assert.Equal(t, "joe@labstack.com", c.Query("email"))

}

func TestContextForm(t *testing.T) {
	f := make(url.Values)
	f.Set("name", "joe")
	f.Set("email", "joe@labstack.com")

	req, err := http.NewRequest(POST, "/", strings.NewReader(f.Encode()))
	assert.NoError(t, err)
	req.Header.Add(ContentType, ApplicationForm)

	c := NewContext(req, nil, New())
	assert.Equal(t, "joe", c.Form("name"))
	assert.Equal(t, "joe@labstack.com", c.Form("email"))
}

func testBind(t *testing.T, c *Context, ct string) {
	c.request.Header.Set(ContentType, ct)
	u := new(user)
	err := c.Bind(u)
	if ct == "" {
		assert.Error(t, UnsupportedMediaType)
	} else if assert.NoError(t, err) {
		assert.Equal(t, "1", u.ID)
		assert.Equal(t, "Joe", u.Name)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"
	"time"

	"github.com/juju/errgo"
	"github.com/kardianos/osext"
	"github.com/spf13/pflag"

	fg "arvika.pulcy.com/pulcy/deployit/flags"
	"arvika.pulcy.com/pulcy/deployit/jobs"
)

func initDeploymentFlags(fs *pflag.FlagSet, f *fg.Flags) {
	fs.StringVarP(&f.JobPath, "job", "j", defaultJobPath, "filename of the job description")
	fs.StringVarP(&f.ClusterPath, "cluster", "c", defaultClusterPath, "cluster description name or filename")
	fs.StringVarP(&f.TunnelOverride, "tunnel", "t", defaultTunnelOverride, "SSH endpoint to tunnel through with fleet (cluster override)")
	fs.StringSliceVarP(&f.Groups, "groups", "g", defaultGroups, "target task groups to deploy")
	fs.BoolVarP(&f.Force, "force", "f", defaultForce, "wheather to confirm destroy or not")
	fs.BoolVarP(&f.DryRun, "dry-run", "d", defaultDryRun, "wheather to schedule units or not")
	fs.UintVar(&f.ScalingGroup, "scaling-group", defaultScalingGroup, "scaling group to deploy")
	fs.BoolVarP(&f.Local, "local", "l", defaultLocal, "User local vagrant test cluster")
	fs.DurationVar(&f.StopDelay, "stop-delay", defaultStopDelay, "Time between stop and destroy")
	fs.DurationVar(&f.DestroyDelay, "destroy-delay", defaultDestroyDelay, "Time between destroy and re-create")
	fs.DurationVar(&f.SliceDelay, "slice-delay", defaultSliceDelay, "Time between update of scaling slices")
	fs.VarP(&f.Options, "option", "o", "Set an option (key=value)")
}

func deploymentDefaults(fs *pflag.FlagSet, f *fg.Flags, args []string) {
	// Merge Options
	fs.VisitAll(func(flag *pflag.Flag) {
		if !flag.Changed {
			value, ok := f.Options.Get(flag.Name)
			if ok {
				err := fs.Set(flag.Name, value)
				if err != nil {
					Exitf("Error in option '%s': %#v\n", flag.Name, err)
				}
			}
		}
	})

	if f.Local {
		f.StopDelay = 5 * time.Second
		f.DestroyDelay = 3 * time.Second
		f.SliceDelay = 5 * time.Second
	}

	if f.JobPath == "" && len(args) <= 1 {
		f.JobPath = args[0]
	}
	if f.ClusterPath == "" && len(args) <= 2 {
		f.ClusterPath = args[1]
	}
}

// Gets the list of group names to operate on based on the deployment flags.
func groups(f *fg.Flags) []jobs.TaskGroupName {
	names := []jobs.TaskGroupName{}
	for _, n := range f.Groups {
		names = append(names, jobs.TaskGroupName(n))
	}
	return names
}

// loadJob loads the a job from the given flags.
func loadJob(f *fg.Flags, cluster fg.Cluster) (*jobs.Job, error) {
	if f.JobPath == "" {
		return nil, maskAny(errgo.New("--job missing"))
	}
	path, err := resolvePath(f.JobPath, "config", ".hcl")
	if err != nil {
		return nil, maskAny(err)
	}
	job, err := jobs.ParseJobFromFile(path, cluster, f.Options)
	if err != nil {
		return nil, maskAny(err)
	}
	return job, nil
}

// loadCluster loads a cluster description from the given flags.
func loadCluster(f *fg.Flags) (*fg.Cluster, error) {
	if f.ClusterPath == "" {
		return nil, maskAny(errgo.New("--cluster missing"))
	}
	path, err := resolvePath(f.ClusterPath, "config/clusters", ".hcl")
	if err != nil {
		return nil, maskAny(err)
	}
	cluster, err := fg.ParseClusterFromFile(path)
	if err != nil {
		return nil, maskAny(err)
	}
	if f.TunnelOverride != "" {
		cluster.Tunnel = f.TunnelOverride
	}
	if f.Local {
		cluster.Tunnel = "core-01"
		cluster.Stack = "core-01"
	}
	return cluster, nil
}

// resolvePath tries to resolve a given path.
// 1) Try as real path
// 2) Try as filename relative to my process with given relative folder & extension
func resolvePath(path, relativeFolder, extension string) (string, error) {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		// path not found, try locating it by name in our relative folder
		folder, err := osext.ExecutableFolder()
		if err != nil {
			return "", maskAny(err)
		}
		path = filepath.Join(folder, relativeFolder, path) + extension
		if _, err := os.Stat(path); os.IsNotExist(err) {
			// Try without extensions
			path = filepath.Join(folder, relativeFolder, path)
		}
	}
	return path, nil
}
<commit_msg>Fixed expressions<commit_after>package main

import (
	"os"
	"path/filepath"
	"time"

	"github.com/juju/errgo"
	"github.com/kardianos/osext"
	"github.com/spf13/pflag"

	fg "arvika.pulcy.com/pulcy/deployit/flags"
	"arvika.pulcy.com/pulcy/deployit/jobs"
)

func initDeploymentFlags(fs *pflag.FlagSet, f *fg.Flags) {
	fs.StringVarP(&f.JobPath, "job", "j", defaultJobPath, "filename of the job description")
	fs.StringVarP(&f.ClusterPath, "cluster", "c", defaultClusterPath, "cluster description name or filename")
	fs.StringVarP(&f.TunnelOverride, "tunnel", "t", defaultTunnelOverride, "SSH endpoint to tunnel through with fleet (cluster override)")
	fs.StringSliceVarP(&f.Groups, "groups", "g", defaultGroups, "target task groups to deploy")
	fs.BoolVarP(&f.Force, "force", "f", defaultForce, "wheather to confirm destroy or not")
	fs.BoolVarP(&f.DryRun, "dry-run", "d", defaultDryRun, "wheather to schedule units or not")
	fs.UintVar(&f.ScalingGroup, "scaling-group", defaultScalingGroup, "scaling group to deploy")
	fs.BoolVarP(&f.Local, "local", "l", defaultLocal, "User local vagrant test cluster")
	fs.DurationVar(&f.StopDelay, "stop-delay", defaultStopDelay, "Time between stop and destroy")
	fs.DurationVar(&f.DestroyDelay, "destroy-delay", defaultDestroyDelay, "Time between destroy and re-create")
	fs.DurationVar(&f.SliceDelay, "slice-delay", defaultSliceDelay, "Time between update of scaling slices")
	fs.VarP(&f.Options, "option", "o", "Set an option (key=value)")
}

func deploymentDefaults(fs *pflag.FlagSet, f *fg.Flags, args []string) {
	// Merge Options
	fs.VisitAll(func(flag *pflag.Flag) {
		if !flag.Changed {
			value, ok := f.Options.Get(flag.Name)
			if ok {
				err := fs.Set(flag.Name, value)
				if err != nil {
					Exitf("Error in option '%s': %#v\n", flag.Name, err)
				}
			}
		}
	})

	if f.Local {
		f.StopDelay = 5 * time.Second
		f.DestroyDelay = 3 * time.Second
		f.SliceDelay = 5 * time.Second
	}

	if f.JobPath == "" && len(args) >= 1 {
		f.JobPath = args[0]
	}
	if f.ClusterPath == "" && len(args) >= 2 {
		f.ClusterPath = args[1]
	}
}

// Gets the list of group names to operate on based on the deployment flags.
func groups(f *fg.Flags) []jobs.TaskGroupName {
	names := []jobs.TaskGroupName{}
	for _, n := range f.Groups {
		names = append(names, jobs.TaskGroupName(n))
	}
	return names
}

// loadJob loads the a job from the given flags.
func loadJob(f *fg.Flags, cluster fg.Cluster) (*jobs.Job, error) {
	if f.JobPath == "" {
		return nil, maskAny(errgo.New("--job missing"))
	}
	path, err := resolvePath(f.JobPath, "config", ".hcl")
	if err != nil {
		return nil, maskAny(err)
	}
	job, err := jobs.ParseJobFromFile(path, cluster, f.Options)
	if err != nil {
		return nil, maskAny(err)
	}
	return job, nil
}

// loadCluster loads a cluster description from the given flags.
func loadCluster(f *fg.Flags) (*fg.Cluster, error) {
	if f.ClusterPath == "" {
		return nil, maskAny(errgo.New("--cluster missing"))
	}
	path, err := resolvePath(f.ClusterPath, "config/clusters", ".hcl")
	if err != nil {
		return nil, maskAny(err)
	}
	cluster, err := fg.ParseClusterFromFile(path)
	if err != nil {
		return nil, maskAny(err)
	}
	if f.TunnelOverride != "" {
		cluster.Tunnel = f.TunnelOverride
	}
	if f.Local {
		cluster.Tunnel = "core-01"
		cluster.Stack = "core-01"
	}
	return cluster, nil
}

// resolvePath tries to resolve a given path.
// 1) Try as real path
// 2) Try as filename relative to my process with given relative folder & extension
func resolvePath(path, relativeFolder, extension string) (string, error) {
	if _, err := os.Stat(path); os.IsNotExist(err) {
		// path not found, try locating it by name in our relative folder
		folder, err := osext.ExecutableFolder()
		if err != nil {
			return "", maskAny(err)
		}
		path = filepath.Join(folder, relativeFolder, path) + extension
		if _, err := os.Stat(path); os.IsNotExist(err) {
			// Try without extensions
			path = filepath.Join(folder, relativeFolder, path)
		}
	}
	return path, nil
}
<|endoftext|>


<commit_before>package runners

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/scootdev/scoot/os/temp"
	"github.com/scootdev/scoot/runner"
	osexecer "github.com/scootdev/scoot/runner/execer/os"
)

// local_output.go: output that's stored locally

type HttpOutputCreator interface {
	http.Handler
	runner.OutputCreator
	HttpPath() string
}

type localOutputCreator struct {
	tmp      *temp.TempDir
	hostname string
	httpUri  string
	httpPath string
	pathMap  map[string]string
	mutex    sync.Mutex
}

// Takes a tempdir to place new files and optionally an httpUri, ex: 'http://HOST:PORT/ENDPOINT/', to use instead of 'file://HOST/PATH'
func NewHttpOutputCreator(tmp *temp.TempDir, httpUri string) (HttpOutputCreator, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	httpPath := ""
	if httpUri != "" {
		u, err := url.Parse(httpUri)
		if err != nil {
			return nil, err
		}
		httpPath = strings.TrimSuffix(u.Path, "/") + "/"
	}
	return &localOutputCreator{
		tmp: tmp, hostname: hostname,
		httpUri: httpUri, httpPath: httpPath,
		pathMap: make(map[string]string),
	}, nil
}

// Create a new Output that writes to local fs.
// Note: id should not have leading or trailing slashes.
func (s *localOutputCreator) Create(id string) (runner.Output, error) {
	f, err := s.tmp.TempFile(id)
	if err != nil {
		return nil, err
	}
	absPath, err := filepath.Abs(f.Name())
	if err != nil {
		return nil, err
	}
	// We don't need a / between hostname and path because absolute paths start with /
	uri := fmt.Sprintf("file://%s%s", s.hostname, absPath)
	if s.httpUri != "" {
		uri = fmt.Sprintf("%s/%s?file=%s", s.httpUri, id, uri)
		s.pathMap[strings.Trim(id, "/")] = absPath
		s.pathMap[filepath.Base(absPath)] = absPath
	}
	return &localOutput{f: f, absPath: absPath, uri: uri}, nil
}

// Serves a minimal page that does ajax log tailing of the specified path
// When '?content=true' is specified, this serves the content directly without ajax.
// Does not check the request path, either it finds the local file or 404s.
func (s *localOutputCreator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	clientHtml :=
		`

`
	if strings.TrimSuffix(r.URL.Path, "/")+"/" == s.HttpPath() {
		http.StripPrefix(s.HttpPath(), http.FileServer(http.Dir(s.tmp.Dir))).ServeHTTP(w, r)
		return
	}
	path := strings.TrimPrefix(r.URL.Path, s.HttpPath())
	filepath, ok := s.pathMap[path]
	if !ok {
		http.Error(w, "Unrecognized path", http.StatusNotFound)
	} else if resource, err := os.Open(filepath); err != nil {
		http.Error(w, "", http.StatusGone)
	} else if info, err := resource.Stat(); err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	} else {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if r.URL.Query().Get("content") == "true" {
			http.ServeContent(w, r, "", info.ModTime(), resource)
		} else {
			fmt.Fprintf(w, clientHtml)
		}
	}
}

func (s *localOutputCreator) HttpPath() string {
	return s.httpPath
}

type localOutput struct {
	f       *os.File
	absPath string
	uri     string
}

// URI returns a URI to this Output
func (o *localOutput) URI() string {
	return o.uri
}

// AsFile returns an absolute path to a file with this content
func (o *localOutput) AsFile() string {
	return o.absPath
}

// Write implements io.Writer
func (o *localOutput) Write(p []byte) (n int, err error) {
	return o.f.Write(p)
}

// Close implements io.Closer
func (o *localOutput) Close() error {
	return o.f.Close()
}

// Return an underlying Writer. Why? Because some methods type assert to
// a more specific type and are more clever (e.g., if it's an *os.File, hook it up
// directly to a new process's stdout/stderr.)
// We care about this cleverness, so Output both is-a and has-a Writer
func (o *localOutput) WriterDelegate() io.Writer {
	return o.f
}

var _ osexecer.WriterDelegater = (*localOutput)(nil)
<commit_msg>Add style to line wrap streaming runner output. (#280)<commit_after>package runners

import (
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"sync"

	"github.com/scootdev/scoot/os/temp"
	"github.com/scootdev/scoot/runner"
	osexecer "github.com/scootdev/scoot/runner/execer/os"
)

// local_output.go: output that's stored locally

type HttpOutputCreator interface {
	http.Handler
	runner.OutputCreator
	HttpPath() string
}

type localOutputCreator struct {
	tmp      *temp.TempDir
	hostname string
	httpUri  string
	httpPath string
	pathMap  map[string]string
	mutex    sync.Mutex
}

// Takes a tempdir to place new files and optionally an httpUri, ex: 'http://HOST:PORT/ENDPOINT/', to use instead of 'file://HOST/PATH'
func NewHttpOutputCreator(tmp *temp.TempDir, httpUri string) (HttpOutputCreator, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}
	httpPath := ""
	if httpUri != "" {
		u, err := url.Parse(httpUri)
		if err != nil {
			return nil, err
		}
		httpPath = strings.TrimSuffix(u.Path, "/") + "/"
	}
	return &localOutputCreator{
		tmp: tmp, hostname: hostname,
		httpUri: httpUri, httpPath: httpPath,
		pathMap: make(map[string]string),
	}, nil
}

// Create a new Output that writes to local fs.
// Note: id should not have leading or trailing slashes.
func (s *localOutputCreator) Create(id string) (runner.Output, error) {
	f, err := s.tmp.TempFile(id)
	if err != nil {
		return nil, err
	}
	absPath, err := filepath.Abs(f.Name())
	if err != nil {
		return nil, err
	}
	// We don't need a / between hostname and path because absolute paths start with /
	uri := fmt.Sprintf("file://%s%s", s.hostname, absPath)
	if s.httpUri != "" {
		uri = fmt.Sprintf("%s/%s?file=%s", s.httpUri, id, uri)
		s.pathMap[strings.Trim(id, "/")] = absPath
		s.pathMap[filepath.Base(absPath)] = absPath
	}
	return &localOutput{f: f, absPath: absPath, uri: uri}, nil
}

// Serves a minimal page that does ajax log tailing of the specified path
// When '?content=true' is specified, this serves the content directly without ajax.
// Does not check the request path, either it finds the local file or 404s.
func (s *localOutputCreator) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.mutex.Lock()
	defer s.mutex.Unlock()

	clientHtml :=
		`

`
	if strings.TrimSuffix(r.URL.Path, "/")+"/" == s.HttpPath() {
		http.StripPrefix(s.HttpPath(), http.FileServer(http.Dir(s.tmp.Dir))).ServeHTTP(w, r)
		return
	}
	path := strings.TrimPrefix(r.URL.Path, s.HttpPath())
	filepath, ok := s.pathMap[path]
	if !ok {
		http.Error(w, "Unrecognized path", http.StatusNotFound)
	} else if resource, err := os.Open(filepath); err != nil {
		http.Error(w, "", http.StatusGone)
	} else if info, err := resource.Stat(); err != nil {
		http.Error(w, "", http.StatusInternalServerError)
	} else {
		w.Header().Set("Access-Control-Allow-Origin", "*")
		if r.URL.Query().Get("content") == "true" {
			http.ServeContent(w, r, "", info.ModTime(), resource)
		} else {
			fmt.Fprintf(w, clientHtml)
		}
	}
}

func (s *localOutputCreator) HttpPath() string {
	return s.httpPath
}

type localOutput struct {
	f       *os.File
	absPath string
	uri     string
}

// URI returns a URI to this Output
func (o *localOutput) URI() string {
	return o.uri
}

// AsFile returns an absolute path to a file with this content
func (o *localOutput) AsFile() string {
	return o.absPath
}

// Write implements io.Writer
func (o *localOutput) Write(p []byte) (n int, err error) {
	return o.f.Write(p)
}

// Close implements io.Closer
func (o *localOutput) Close() error {
	return o.f.Close()
}

// Return an underlying Writer. Why? Because some methods type assert to
// a more specific type and are more clever (e.g., if it's an *os.File, hook it up
// directly to a new process's stdout/stderr.)
// We care about this cleverness, so Output both is-a and has-a Writer
func (o *localOutput) WriterDelegate() io.Writer {
	return o.f
}

var _ osexecer.WriterDelegater = (*localOutput)(nil)
<|endoftext|>