<commit_before>package ldp

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/rdf"
	"ldpserver/textstore"
	"ldpserver/util"
	"log"
	"strings"
	"time"
)

var NodeNotFoundError = errors.New("Node not found")
var DuplicateNodeError = errors.New("Node already exists")

const metaFile = "meta.rdf"
const dataFile = "data.txt"

type Node struct {
	isRdf   bool
	uri     string
	headers map[string][]string
	graph   rdf.RdfGraph
	binary  string // should be []byte or reader

	settings Settings
	rootUri  string // http://localhost/
	store    textstore.Store

	isBasicContainer   bool
	isDirectContainer  bool
	membershipResource string
	hasMemberRelation  string
	// TODO isMemberOfRelation string
}

func (node Node) AddChild(child Node) error {
	triple := rdf.NewTriple("<"+node.uri+">", "<"+rdf.LdpContainsUri+">", "<"+child.uri+">")
	err := node.store.AppendToFile(metaFile, triple.StringLn())
	if err != nil {
		return err
	}

	if node.isDirectContainer {
		return node.addDirectContainerChild(child)
	}
	return nil
}

func (node Node) Content() string {
	if node.isRdf {
		return node.graph.String()
	}
	return node.binary
}

func (node Node) DebugString() string {
	if !node.isRdf {
		return fmt.Sprintf("Non-RDF: %s", node.uri)
	}

	triples := ""
	for i, triple := range node.graph {
		triples += fmt.Sprintf("%d %s\n", i, triple)
	}
	debugString := fmt.Sprintf("RDF: %s\n %s", node.uri, triples)
	return debugString
}

func (node *Node) Etag() string {
	subject := "<" + node.uri + ">"
	etagFound, etag := node.graph.GetObject(subject, "<"+rdf.ServerETagUri+">")
	if !etagFound {
		panic(fmt.Sprintf("No etag found for node %s", node.uri))
	}
	return removeQuotes(etag)
}

func (node Node) HasTriple(predicate, object string) bool {
	return node.graph.HasTriple("<"+node.uri+">", predicate, object)
}

func (node Node) Headers() map[string][]string {
	return node.headers
}

func (node Node) IsBasicContainer() bool {
	return node.isBasicContainer
}

func (node Node) IsDirectContainer() bool {
	return node.isDirectContainer
}

func (node Node) IsRdf() bool {
	return node.isRdf
}

func (node Node) Uri() string {
	return node.uri
}

func (node *Node) Patch(triples string) error {
	if !node.isRdf {
		return errors.New("Cannot PATCH non-RDF Source")
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return err
	}

	// This is pretty useless as-is since it does not allow to update
	// a triple. It always adds triples.
	// Also, there are some triples that can exist only once (e.g. direct container triples)
	// and this code does not validate them.
	node.graph.Append(graph)

	// write it to disk
	if err := node.writeToDisk(nil); err != nil {
		return err
	}

	return nil
}

func (node Node) Path() string {
	return util.PathFromUri(node.rootUri, node.uri)
}

func (node Node) String() string {
	return node.uri
}

func GetNode(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(true)
	return node, err
}

func GetHead(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(false)
	return node, err
}

func NewRdfNode(settings Settings, triples string, path string) (Node, error) {
	node := newNode(settings, path)
	return node, node.writeRdfToDisk(triples)
}

func ReplaceRdfNode(settings Settings, triples string, path string, etag string) (Node, error) {
	node, err := GetNode(settings, path)
	if err != nil {
		return Node{}, err
	}

	if !node.isRdf {
		return Node{}, errors.New("Cannot replace non-RDF source with an RDF source")
	}

	if etag == "" {
		return Node{}, errors.New("Cannot replace RDF source without an etag")
	}

	if node.Etag() != etag {
		return Node{}, fmt.Errorf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", node.Etag(), etag)
	}

	return node, node.writeRdfToDisk(triples)
}

func NewNonRdfNode(settings Settings, reader io.ReadCloser, parentPath string, newPath string) (Node, error) {
	path := util.UriConcat(parentPath, newPath)
	node := newNode(settings, path)
	graph := defaultGraphNonRdf(node.uri)
	node.setAsNonRdf(graph)
	err := node.writeToDisk(reader)
	return node, err
}

func (node Node) addDirectContainerChild(child Node) error {
	// TODO: account for isMemberOfRelation
	targetUri := removeAngleBrackets(node.membershipResource)
	targetPath := util.PathFromUri(node.rootUri, targetUri)

	targetNode, err := GetNode(node.settings, targetPath)
	if err != nil {
		log.Printf("Could not find target node %s.", targetPath)
		return err
	}

	tripleForTarget := rdf.NewTriple("<"+targetNode.uri+">", node.hasMemberRelation, "<"+child.uri+">")

	err = targetNode.store.AppendToFile(metaFile, tripleForTarget.StringLn())
	if err != nil {
		log.Printf("Error appending child %s to %s. %s", child.uri, targetNode.uri, err)
		return err
	}
	return nil
}

func (node *Node) loadNode(isIncludeBody bool) error {
	err := node.loadMeta()
	if err != nil {
		return err
	}

	if node.isRdf || isIncludeBody == false {
		return nil
	}

	return node.loadBinary()
}

func (node *Node) loadBinary() error {
	var err error
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) loadMeta() error {
	if !node.store.Exists() {
		return NodeNotFoundError
	}

	meta, err := node.store.ReadFile(metaFile)
	if err != nil {
		return err
	}

	graph, err := rdf.StringToGraph(meta, node.uri)
	if err != nil {
		return err
	}

	if graph.IsRdfSource("<" + node.uri + ">") {
		node.setAsRdf(graph)
	} else {
		node.setAsNonRdf(graph)
	}
	return nil
}

func (node *Node) writeRdfToDisk(triples string) error {
	userGraph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		log.Printf("== Triples \n%s\n==", triples)
		return err
	}

	graph := defaultGraph(node.uri)
	graph.Append(userGraph)
	node.setAsRdf(graph)
	return node.writeToDisk(nil)
}

func (node Node) writeToDisk(reader io.ReadCloser) error {
	// Write the RDF metadata
	err := node.store.SaveFile(metaFile, node.graph.String())
	if err != nil {
		return err
	}

	if node.isRdf {
		return nil
	}

	// Write the binary
	return node.store.SaveReader(dataFile, reader)
}

func (node *Node) setAsRdf(graph rdf.RdfGraph) {
	subject := "<" + node.uri + ">"
	node.isRdf = true
	node.graph = graph
	node.headers = make(map[string][]string)
	node.headers["Content-Type"] = []string{rdf.TurtleContentType}

	if graph.IsBasicContainer(subject) {
		// Is there a way to indicate that PUT is allowed
		// for creation only (and not to overwrite?)
		node.headers["Allow"] = []string{"GET, HEAD, POST, PUT, PATCH"}
	} else {
		node.headers["Allow"] = []string{"GET, HEAD, PUT, PATCH"}
	}
	node.headers["Accept-Post"] = []string{"text/turtle"}

	node.headers["Etag"] = []string{node.Etag()}

	links := make([]string, 0)
	links = append(links, rdf.LdpResourceLink)
	if graph.IsBasicContainer(subject) {
		node.isBasicContainer = true
		links = append(links, rdf.LdpContainerLink)
		links = append(links, rdf.LdpBasicContainerLink)
		// TODO: validate membershipResource is a sub-URI of rootURI
		node.membershipResource, node.hasMemberRelation, node.isDirectContainer = graph.GetDirectContainerInfo()
		if node.isDirectContainer {
			links = append(links, rdf.LdpDirectContainerLink)
		}
	}
	node.headers["Link"] = links
}

func (node *Node) setAsNonRdf(graph rdf.RdfGraph) {
	// TODO Figure out a way to pass the binary as a stream
	node.isRdf = false
	node.graph = graph
	node.binary = ""
	node.headers = make(map[string][]string)
	node.headers["Link"] = []string{rdf.LdpNonRdfSourceLink}
	node.headers["Allow"] = []string{"GET, HEAD, PUT"}
	node.headers["Content-Type"] = []string{"application/binary"}
	// TODO: guess the content-type from meta

	node.headers["Etag"] = []string{node.Etag()}
}

func calculateEtag() string {
	// TODO: Come up with a more precise value.
	return strings.Replace(time.Now().Format(time.RFC3339), ":", "_", -1)
}

func defaultGraph(uri string) rdf.RdfGraph {
	subject := "<" + uri + ">"
	// define the triples
	resource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">")
	rdfSource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpRdfSourceUri+">")
	// TODO: Not all RDFs resources should be containers
	basicContainer := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpBasicContainerUri+">")
	title := rdf.NewTriple(subject, "<"+rdf.DcTitleUri+">", "\"This is a new entry\"")
	nowString := "\"" + time.Now().Format(time.RFC3339) + "\""
	created := rdf.NewTriple(subject, "<"+rdf.DcCreatedUri+">", nowString)
	etag := rdf.NewTriple(subject, "<"+rdf.ServerETagUri+">", "\""+calculateEtag()+"\"")
	// create the graph
	graph := rdf.RdfGraph{resource, rdfSource, basicContainer, title, created, etag}
	return graph
}

func defaultGraphNonRdf(uri string) rdf.RdfGraph {
	subject := "<" + uri + ">"
	// define the triples
	resource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">")
	nonRdfSource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpNonRdfSourceUri+">")
	title := rdf.NewTriple(subject, "<"+rdf.DcTitleUri+">", "\"This is a new entry\"")
	nowString := "\"" + time.Now().Format(time.RFC3339) + "\""
	created := rdf.NewTriple(subject, "<"+rdf.DcCreatedUri+">", nowString)
	etag := rdf.NewTriple(subject, "<"+rdf.ServerETagUri+">", "\""+calculateEtag()+"\"")
	// create the graph
	graph := rdf.RdfGraph{resource, nonRdfSource, title, created, etag}
	return graph
}

func newNode(settings Settings, path string) Node {
	if strings.HasPrefix(path, "http://") {
		panic("newNode expects a path, received a URI: " + path)
	}
	var node Node
	node.settings = settings
	pathOnDisk := util.PathConcat(settings.dataPath, path)
	node.store = textstore.NewStore(pathOnDisk)
	node.rootUri = settings.RootUri()
	node.uri = util.UriConcat(node.rootUri, path)
	return node
}

func removeAngleBrackets(text string) string {
	if strings.HasPrefix(text, "<") && strings.HasSuffix(text, ">") {
		return text[1 : len(text)-1]
	}
	return text
}

func removeQuotes(text string) string {
	if strings.HasPrefix(text, "\"") && strings.HasSuffix(text, "\"") {
		return text[1 : len(text)-1]
	}
	return text
}
<commit_msg>Restore quotes to etags<commit_after>package ldp

import (
	"errors"
	"fmt"
	"io"
	"ldpserver/rdf"
	"ldpserver/textstore"
	"ldpserver/util"
	"log"
	"strings"
	"time"
)

var NodeNotFoundError = errors.New("Node not found")
var DuplicateNodeError = errors.New("Node already exists")

const metaFile = "meta.rdf"
const dataFile = "data.txt"

type Node struct {
	isRdf   bool
	uri     string
	headers map[string][]string
	graph   rdf.RdfGraph
	binary  string // should be []byte or reader

	settings Settings
	rootUri  string // http://localhost/
	store    textstore.Store

	isBasicContainer   bool
	isDirectContainer  bool
	membershipResource string
	hasMemberRelation  string
	// TODO isMemberOfRelation string
}

func (node Node) AddChild(child Node) error {
	triple := rdf.NewTriple("<"+node.uri+">", "<"+rdf.LdpContainsUri+">", "<"+child.uri+">")
	err := node.store.AppendToFile(metaFile, triple.StringLn())
	if err != nil {
		return err
	}

	if node.isDirectContainer {
		return node.addDirectContainerChild(child)
	}
	return nil
}

func (node Node) Content() string {
	if node.isRdf {
		return node.graph.String()
	}
	return node.binary
}

func (node Node) DebugString() string {
	if !node.isRdf {
		return fmt.Sprintf("Non-RDF: %s", node.uri)
	}

	triples := ""
	for i, triple := range node.graph {
		triples += fmt.Sprintf("%d %s\n", i, triple)
	}
	debugString := fmt.Sprintf("RDF: %s\n %s", node.uri, triples)
	return debugString
}

func (node *Node) Etag() string {
	subject := "<" + node.uri + ">"
	etagFound, etag := node.graph.GetObject(subject, "<"+rdf.ServerETagUri+">")
	if !etagFound {
		panic(fmt.Sprintf("No etag found for node %s", node.uri))
	}
	return etag
}

func (node Node) HasTriple(predicate, object string) bool {
	return node.graph.HasTriple("<"+node.uri+">", predicate, object)
}

func (node Node) Headers() map[string][]string {
	return node.headers
}

func (node Node) IsBasicContainer() bool {
	return node.isBasicContainer
}

func (node Node) IsDirectContainer() bool {
	return node.isDirectContainer
}

func (node Node) IsRdf() bool {
	return node.isRdf
}

func (node Node) Uri() string {
	return node.uri
}

func (node *Node) Patch(triples string) error {
	if !node.isRdf {
		return errors.New("Cannot PATCH non-RDF Source")
	}

	graph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		return err
	}

	// This is pretty useless as-is since it does not allow to update
	// a triple. It always adds triples.
	// Also, there are some triples that can exist only once (e.g. direct container triples)
	// and this code does not validate them.
	node.graph.Append(graph)

	// write it to disk
	if err := node.writeToDisk(nil); err != nil {
		return err
	}

	return nil
}

func (node Node) Path() string {
	return util.PathFromUri(node.rootUri, node.uri)
}

func (node Node) String() string {
	return node.uri
}

func GetNode(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(true)
	return node, err
}

func GetHead(settings Settings, path string) (Node, error) {
	node := newNode(settings, path)
	err := node.loadNode(false)
	return node, err
}

func NewRdfNode(settings Settings, triples string, path string) (Node, error) {
	node := newNode(settings, path)
	return node, node.writeRdfToDisk(triples)
}

func ReplaceRdfNode(settings Settings, triples string, path string, etag string) (Node, error) {
	node, err := GetNode(settings, path)
	if err != nil {
		return Node{}, err
	}

	if !node.isRdf {
		return Node{}, errors.New("Cannot replace non-RDF source with an RDF source")
	}

	if etag == "" {
		return Node{}, errors.New("Cannot replace RDF source without an etag")
	}

	nodeEtag = removeQuotes(node.Etag())
	if nodeEtag != etag {
		return Node{}, fmt.Errorf("Cannot replace RDF source. Etag mismatch. Expected: %s. Found: %s", nodeEtag, etag)
	}

	return node, node.writeRdfToDisk(triples)
}

func NewNonRdfNode(settings Settings, reader io.ReadCloser, parentPath string, newPath string) (Node, error) {
	path := util.UriConcat(parentPath, newPath)
	node := newNode(settings, path)
	graph := defaultGraphNonRdf(node.uri)
	node.setAsNonRdf(graph)
	err := node.writeToDisk(reader)
	return node, err
}

func (node Node) addDirectContainerChild(child Node) error {
	// TODO: account for isMemberOfRelation
	targetUri := removeAngleBrackets(node.membershipResource)
	targetPath := util.PathFromUri(node.rootUri, targetUri)

	targetNode, err := GetNode(node.settings, targetPath)
	if err != nil {
		log.Printf("Could not find target node %s.", targetPath)
		return err
	}

	tripleForTarget := rdf.NewTriple("<"+targetNode.uri+">", node.hasMemberRelation, "<"+child.uri+">")

	err = targetNode.store.AppendToFile(metaFile, tripleForTarget.StringLn())
	if err != nil {
		log.Printf("Error appending child %s to %s. %s", child.uri, targetNode.uri, err)
		return err
	}
	return nil
}

func (node *Node) loadNode(isIncludeBody bool) error {
	err := node.loadMeta()
	if err != nil {
		return err
	}

	if node.isRdf || isIncludeBody == false {
		return nil
	}

	return node.loadBinary()
}

func (node *Node) loadBinary() error {
	var err error
	node.binary, err = node.store.ReadFile(dataFile)
	return err
}

func (node *Node) loadMeta() error {
	if !node.store.Exists() {
		return NodeNotFoundError
	}

	meta, err := node.store.ReadFile(metaFile)
	if err != nil {
		return err
	}

	graph, err := rdf.StringToGraph(meta, node.uri)
	if err != nil {
		return err
	}

	if graph.IsRdfSource("<" + node.uri + ">") {
		node.setAsRdf(graph)
	} else {
		node.setAsNonRdf(graph)
	}
	return nil
}

func (node *Node) writeRdfToDisk(triples string) error {
	userGraph, err := rdf.StringToGraph(triples, "<"+node.uri+">")
	if err != nil {
		log.Printf("== Triples \n%s\n==", triples)
		return err
	}

	graph := defaultGraph(node.uri)
	graph.Append(userGraph)
	node.setAsRdf(graph)
	return node.writeToDisk(nil)
}

func (node Node) writeToDisk(reader io.ReadCloser) error {
	// Write the RDF metadata
	err := node.store.SaveFile(metaFile, node.graph.String())
	if err != nil {
		return err
	}

	if node.isRdf {
		return nil
	}

	// Write the binary
	return node.store.SaveReader(dataFile, reader)
}

func (node *Node) setAsRdf(graph rdf.RdfGraph) {
	subject := "<" + node.uri + ">"
	node.isRdf = true
	node.graph = graph
	node.headers = make(map[string][]string)
	node.headers["Content-Type"] = []string{rdf.TurtleContentType}

	if graph.IsBasicContainer(subject) {
		// Is there a way to indicate that PUT is allowed
		// for creation only (and not to overwrite?)
		node.headers["Allow"] = []string{"GET, HEAD, POST, PUT, PATCH"}
	} else {
		node.headers["Allow"] = []string{"GET, HEAD, PUT, PATCH"}
	}
	node.headers["Accept-Post"] = []string{"text/turtle"}

	node.headers["Etag"] = []string{node.Etag()}

	links := make([]string, 0)
	links = append(links, rdf.LdpResourceLink)
	if graph.IsBasicContainer(subject) {
		node.isBasicContainer = true
		links = append(links, rdf.LdpContainerLink)
		links = append(links, rdf.LdpBasicContainerLink)
		// TODO: validate membershipResource is a sub-URI of rootURI
		node.membershipResource, node.hasMemberRelation, node.isDirectContainer = graph.GetDirectContainerInfo()
		if node.isDirectContainer {
			links = append(links, rdf.LdpDirectContainerLink)
		}
	}
	node.headers["Link"] = links
}

func (node *Node) setAsNonRdf(graph rdf.RdfGraph) {
	// TODO Figure out a way to pass the binary as a stream
	node.isRdf = false
	node.graph = graph
	node.binary = ""
	node.headers = make(map[string][]string)
	node.headers["Link"] = []string{rdf.LdpNonRdfSourceLink}
	node.headers["Allow"] = []string{"GET, HEAD, PUT"}
	node.headers["Content-Type"] = []string{"application/binary"}
	// TODO: guess the content-type from meta

	node.headers["Etag"] = []string{node.Etag()}
}

func calculateEtag() string {
	// TODO: Come up with a more precise value.
	return strings.Replace(time.Now().Format(time.RFC3339), ":", "_", -1)
}

func defaultGraph(uri string) rdf.RdfGraph {
	subject := "<" + uri + ">"
	// define the triples
	resource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">")
	rdfSource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpRdfSourceUri+">")
	// TODO: Not all RDFs resources should be containers
	basicContainer := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpBasicContainerUri+">")
	title := rdf.NewTriple(subject, "<"+rdf.DcTitleUri+">", "\"This is a new entry\"")
	nowString := "\"" + time.Now().Format(time.RFC3339) + "\""
	created := rdf.NewTriple(subject, "<"+rdf.DcCreatedUri+">", nowString)
	etag := rdf.NewTriple(subject, "<"+rdf.ServerETagUri+">", "\""+calculateEtag()+"\"")
	// create the graph
	graph := rdf.RdfGraph{resource, rdfSource, basicContainer, title, created, etag}
	return graph
}

func defaultGraphNonRdf(uri string) rdf.RdfGraph {
	subject := "<" + uri + ">"
	// define the triples
	resource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpResourceUri+">")
	nonRdfSource := rdf.NewTriple(subject, "<"+rdf.RdfTypeUri+">", "<"+rdf.LdpNonRdfSourceUri+">")
	title := rdf.NewTriple(subject, "<"+rdf.DcTitleUri+">", "\"This is a new entry\"")
	nowString := "\"" + time.Now().Format(time.RFC3339) + "\""
	created := rdf.NewTriple(subject, "<"+rdf.DcCreatedUri+">", nowString)
	etag := rdf.NewTriple(subject, "<"+rdf.ServerETagUri+">", "\""+calculateEtag()+"\"")
	// create the graph
	graph := rdf.RdfGraph{resource, nonRdfSource, title, created, etag}
	return graph
}

func newNode(settings Settings, path string) Node {
	if strings.HasPrefix(path, "http://") {
		panic("newNode expects a path, received a URI: " + path)
	}
	var node Node
	node.settings = settings
	pathOnDisk := util.PathConcat(settings.dataPath, path)
	node.store = textstore.NewStore(pathOnDisk)
	node.rootUri = settings.RootUri()
	node.uri = util.UriConcat(node.rootUri, path)
	return node
}

func removeAngleBrackets(text string) string {
	if strings.HasPrefix(text, "<") && strings.HasSuffix(text, ">") {
		return text[1 : len(text)-1]
	}
	return text
}

func removeQuotes(text string) string {
	if strings.HasPrefix(text, "\"") && strings.HasSuffix(text, "\"") {
		return text[1 : len(text)-1]
	}
	return text
}
<|endoftext|>


<commit_before>package shout

import (
	"fmt"
	"unsafe"
)

/*
#cgo LDFLAGS: -lshout
#include <stdlib.h>
#include <shout/shout.h>
*/
import "C"

const (
	BUFFER_SIZE = 8192
)

const (
	// See shout.h
	SHOUTERR_SUCCESS     = 0
	SHOUTERR_INSANE      = -1
	SHOUTERR_NOCORRECT   = -2
	SHOUTERR_NOLOGIN     = -3
	SHOUTERR_SOCKET      = -4
	SHOUTERR_MALLOC      = -5
	SHOUTERR_METADATA    = -6
	SHOUTERR_CONNECTED   = -7
	SHOUTERR_UNCONNECTED = -8
	SHOUTERR_UNSUPPORTED = -9
	SHOUTERR_BUSY        = -10
)

const (
	FORMAT_OGG = 0
	FORMAT_MP3 = 1
	FORMAT_WEBM = 2
)

const (
	PROTOCOL_HTTP = iota
	PROTOCOL_XAUDIOCAST
	PROTOCOL_ICY
)

type ShoutError struct {
	Message string
	Code    int
}

func (e ShoutError) Error() string {
	return fmt.Sprintf("%s (%d)", e.Message, e.Code)
}

type Shout struct {
	Host     string
	Port     uint
	User     string
	Password string
	Mount    string
	Format   int
	Protocol int

	// wrap the native C struct
	struc *C.struct_shout

	stream chan []byte
}

func init() {
	C.shout_init()
}

func Shutdown() {
	C.shout_shutdown()
}

func Free(s *Shout) {
	C.shout_free(s.struc)
}

func (s *Shout) lazyInit() {
	if s.struc != nil {
		return
	}

	s.struc = C.shout_new()
	s.updateParameters()

	s.stream = make(chan []byte)
}

func (s *Shout) updateParameters() {
	// set hostname
	p := C.CString(s.Host)
	C.shout_set_host(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set port
	C.shout_set_port(s.struc, C.ushort(s.Port))

	// set username
	p = C.CString(s.User)
	C.shout_set_user(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set password
	p = C.CString(s.Password)
	C.shout_set_password(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set mount point
	p = C.CString(s.Mount)
	C.shout_set_mount(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set format
	C.shout_set_format(s.struc, C.uint(s.Format))

	// set protocol
	C.shout_set_protocol(s.struc, C.uint(s.Protocol))
}

func (s *Shout) GetError() string {
	s.lazyInit()
	err := C.shout_get_error(s.struc)
	return C.GoString(err)
}

func (s *Shout) Open() (chan<- []byte, error) {
	s.lazyInit()

	errcode := int(C.shout_open(s.struc))
	if errcode != C.SHOUTERR_SUCCESS {
		return nil, ShoutError{
			Code:    errcode,
			Message: s.GetError(),
		}
	}

	go s.handleStream()

	return s.stream, nil
}

func (s *Shout) Close() error {
	errcode := int(C.shout_close(s.struc))
	if errcode != C.SHOUTERR_SUCCESS {
		return ShoutError{
			Code:    errcode,
			Message: s.GetError(),
		}
	}

	return nil
}

func (s *Shout) send(buffer []byte) error {
	ptr := (*C.uchar)(&buffer[0])
	C.shout_send(s.struc, ptr, C.size_t(len(buffer)))

	errno := int(C.shout_get_errno(s.struc))
	if errno != C.SHOUTERR_SUCCESS {
		fmt.Println("something went wrong: %d", errno)
	}

	C.shout_sync(s.struc)
	return nil
}

func (s *Shout) handleStream() {
	for buf := range s.stream {
		s.send(buf)
	}
	fmt.Println("end handle")
}<commit_msg>add license file header<commit_after>/*
 * Copyright (c) $2013, Ömer Yildiz. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */
package shout

import (
	"fmt"
	"unsafe"
)

/*
#cgo LDFLAGS: -lshout
#include <stdlib.h>
#include <shout/shout.h>
*/
import "C"

const (
	BUFFER_SIZE = 8192
)

const (
	// See shout.h
	SHOUTERR_SUCCESS     = 0
	SHOUTERR_INSANE      = -1
	SHOUTERR_NOCORRECT   = -2
	SHOUTERR_NOLOGIN     = -3
	SHOUTERR_SOCKET      = -4
	SHOUTERR_MALLOC      = -5
	SHOUTERR_METADATA    = -6
	SHOUTERR_CONNECTED   = -7
	SHOUTERR_UNCONNECTED = -8
	SHOUTERR_UNSUPPORTED = -9
	SHOUTERR_BUSY        = -10
)

const (
	FORMAT_OGG = 0
	FORMAT_MP3 = 1
	FORMAT_WEBM = 2
)

const (
	PROTOCOL_HTTP = iota
	PROTOCOL_XAUDIOCAST
	PROTOCOL_ICY
)

type ShoutError struct {
	Message string
	Code    int
}

func (e ShoutError) Error() string {
	return fmt.Sprintf("%s (%d)", e.Message, e.Code)
}

type Shout struct {
	Host     string
	Port     uint
	User     string
	Password string
	Mount    string
	Format   int
	Protocol int

	// wrap the native C struct
	struc *C.struct_shout

	stream chan []byte
}

func init() {
	C.shout_init()
}

func Shutdown() {
	C.shout_shutdown()
}

func Free(s *Shout) {
	C.shout_free(s.struc)
}

func (s *Shout) lazyInit() {
	if s.struc != nil {
		return
	}

	s.struc = C.shout_new()
	s.updateParameters()

	s.stream = make(chan []byte)
}

func (s *Shout) updateParameters() {
	// set hostname
	p := C.CString(s.Host)
	C.shout_set_host(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set port
	C.shout_set_port(s.struc, C.ushort(s.Port))

	// set username
	p = C.CString(s.User)
	C.shout_set_user(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set password
	p = C.CString(s.Password)
	C.shout_set_password(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set mount point
	p = C.CString(s.Mount)
	C.shout_set_mount(s.struc, p)
	C.free(unsafe.Pointer(p))

	// set format
	C.shout_set_format(s.struc, C.uint(s.Format))

	// set protocol
	C.shout_set_protocol(s.struc, C.uint(s.Protocol))
}

func (s *Shout) GetError() string {
	s.lazyInit()
	err := C.shout_get_error(s.struc)
	return C.GoString(err)
}

func (s *Shout) Open() (chan<- []byte, error) {
	s.lazyInit()

	errcode := int(C.shout_open(s.struc))
	if errcode != C.SHOUTERR_SUCCESS {
		return nil, ShoutError{
			Code:    errcode,
			Message: s.GetError(),
		}
	}

	go s.handleStream()

	return s.stream, nil
}

func (s *Shout) Close() error {
	errcode := int(C.shout_close(s.struc))
	if errcode != C.SHOUTERR_SUCCESS {
		return ShoutError{
			Code:    errcode,
			Message: s.GetError(),
		}
	}

	return nil
}

func (s *Shout) send(buffer []byte) error {
	ptr := (*C.uchar)(&buffer[0])
	C.shout_send(s.struc, ptr, C.size_t(len(buffer)))

	errno := int(C.shout_get_errno(s.struc))
	if errno != C.SHOUTERR_SUCCESS {
		fmt.Println("something went wrong: %d", errno)
	}

	C.shout_sync(s.struc)
	return nil
}

func (s *Shout) handleStream() {
	for buf := range s.stream {
		s.send(buf)
	}
}<|endoftext|>


<commit_before>package haaasd

import (
	"bytes"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
)

func NewHaproxy(role string, properties *Config, application string, platform string, version string) *Haproxy {
	if version == "" {
		version = "1.4.22"
	}
	return &Haproxy{
		Role:             role,
		Application: application,
		Platform:    platform,
		properties:  properties,
		Version:     version,
	}
}

type Haproxy struct {
	Role        string
	Application string
	Platform    string
	Version     string
	properties  *Config
	State       int
}

const (
	SUCCESS int = iota
	UNCHANGED int = iota
	ERR_SYSLOG int = iota
	ERR_CONF int = iota
	ERR_RELOAD int = iota
)

// ApplyConfiguration write the new configuration and reload
// A rollback is called on failure
func (hap *Haproxy) ApplyConfiguration(data *EventMessage) (int, error) {
	hap.createSkeleton(data.Correlationid)

	newConf := data.Conf
	path := hap.confPath()

	// Check conf diff
	oldConf, err := ioutil.ReadFile(path)
	if log.GetLevel() == log.DebugLevel {
		hap.dumpConfiguration(hap.NewDebugPath(), newConf, data)
	}
	if bytes.Equal(oldConf, newConf) {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).Debug("Unchanged configuration")
		return UNCHANGED, nil
	}

	// Archive previous configuration
	archivePath := hap.confArchivePath()
	os.Rename(path, archivePath)
	log.WithFields(
		log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
			"archivePath": archivePath,
		}).Info("Old configuration saved")
	err = ioutil.WriteFile(path, newConf, 0644)
	if err != nil {
		return ERR_CONF, err
	}

	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"role": hap.Role,
		"application": data.Application,
		"plateform":   data.Platform,
		"path", path,
	}).Info("New configuration written")

	// Reload haproxy
	err = hap.reload(data.Correlationid)
	if err != nil {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Reload failed")
		hap.dumpConfiguration(hap.NewErrorPath(), newConf, data)
		err = hap.rollback(data.Correlationid)
		return ERR_RELOAD, err
	}
	// Write syslog fragment
	fragmentPath := hap.syslogFragmentPath()
	err = ioutil.WriteFile(fragmentPath, data.SyslogFragment, 0644)
	if err != nil {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Failed to write syslog fragment")
		// TODO Should we rollback on syslog error ?
		return ERR_SYSLOG, err
	}
	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"role": hap.Role,
		"application": data.Application,
		"plateform":   data.Platform,
		"content" : data.SyslogFragment,
		"filename": fragmentPath,
	}).Debug("Write syslog fragment")

	return SUCCESS, nil
}

// dumpConfiguration dumps the new configuration file with context for debugging purpose
func (hap *Haproxy) dumpConfiguration(filename string, newConf []byte, data *EventMessage) {

	f, err2 := os.Create(filename)
	defer f.Close()
	if err2 == nil {
		f.WriteString("================================================================\n")
		f.WriteString(fmt.Sprintf("application: %s\n", data.Application))
		f.WriteString(fmt.Sprintf("platform: %s\n", data.Platform))
		f.WriteString(fmt.Sprintf("correlationid: %s\n", data.Correlationid))
		f.WriteString("================================================================\n")
		f.Write(newConf)
		f.Sync()

		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"filename": filename,
			"application": data.Application,
			"platform": data.Platform,
		}).Info("Dump configuration")
	}
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) confPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/Config"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// confPath give the path of the archived configuration file given an application context
func (hap *Haproxy) confArchivePath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/version-1"
	// It returns the absolute path to the file
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// NewErrorPath gives a unique path the error file given the hap context
// It returns the full path to the file
func (hap *Haproxy) NewErrorPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/errors"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

func (hap *Haproxy) NewDebugPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/dump"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

// reload calls external shell script to reload haproxy
// It returns error if the reload fails
func (hap *Haproxy) reload(correlationId string) error {

	reloadScript := hap.getReloadScript()
	cmd, err := exec.Command("sh", reloadScript, "reload", "-y").Output()
	if err != nil {
		log.WithError(err).Error("Error reloading")
	}
	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"role": hap.Role,
		"application": hap.Application,
		"platform": hap.Platform,
		"reloadScript": reloadScript,
	}).WithField("cmd", cmd).Debug("Reload succeeded")
	return err
}

// rollbac reverts configuration files and call for reload
func (hap *Haproxy) rollback(correlationId string) error {
	lastConf := hap.confArchivePath()
	if _, err := os.Stat(lastConf); os.IsNotExist(err) {
		return errors.New("No configuration file to rollback")
	}
	os.Rename(lastConf, hap.confPath())
	hap.reload(correlationId)
	return nil
}

// createSkeleton creates the directory tree for a new haproxy context
func (hap *Haproxy) createSkeleton(correlationId string) error {
	baseDir := hap.properties.HapHome + "/" + hap.Application

	createDirectory(correlationId, baseDir + "/Config")
	createDirectory(correlationId, baseDir + "/logs/" + hap.Application + hap.Platform)
	createDirectory(correlationId, baseDir + "/scripts")
	createDirectory(correlationId, baseDir + "/version-1")

	updateSymlink(correlationId, hap.getHapctlFilename(), hap.getReloadScript())
	updateSymlink(correlationId, hap.getHapBinary(), baseDir + "/Config/haproxy")

	return nil
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) syslogFragmentPath() string {
	baseDir := hap.properties.HapHome + "/SYSLOG/Config/syslog.conf.d"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/syslog" + hap.Application + hap.Platform + ".conf"
}

// updateSymlink create or update a symlink
func updateSymlink(correlationId, oldname string, newname string) {
	newLink := true
	if _, err := os.Stat(newname); err == nil {
		os.Remove(newname)
		newLink = false
	}
	err := os.Symlink(oldname, newname)
	if err != nil {
		log.WithError(err).WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Error("Symlink failed")
	}

	if newLink {
		log.WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Info("Symlink created")
	}
}

// createDirectory recursively creates directory if it doesn't exists
func createDirectory(correlationId string, dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.WithError(err).WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Error("Failed to create")
		} else {
			log.WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Info("Directory created")
		}
	}
}

// getHapctlFilename return the path to the vsc hapctl shell script
// This script is provided
func (hap *Haproxy) getHapctlFilename() string {
	return "/HOME/uxwadm/scripts/hapctl_unif"
}

// getReloadScript calculates reload script path given the hap context
// It returns the full script path
func (hap *Haproxy) getReloadScript() string {
	return fmt.Sprintf("%s/%s/scripts/hapctl%s%s", hap.properties.HapHome, hap.Application, hap.Application, hap.Platform)
}

// getHapBinary calculates the haproxy binary to use given the expected version
// It returns the full path to the haproxy binary
func (hap *Haproxy) getHapBinary() string {
	return fmt.Sprintf("/export/product/haproxy/product/%s/bin/haproxy", hap.Version)
}
<commit_msg>Fix typo<commit_after>package haaasd

import (
	"bytes"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
)

func NewHaproxy(role string, properties *Config, application string, platform string, version string) *Haproxy {
	if version == "" {
		version = "1.4.22"
	}
	return &Haproxy{
		Role:             role,
		Application: application,
		Platform:    platform,
		properties:  properties,
		Version:     version,
	}
}

type Haproxy struct {
	Role        string
	Application string
	Platform    string
	Version     string
	properties  *Config
	State       int
}

const (
	SUCCESS int = iota
	UNCHANGED int = iota
	ERR_SYSLOG int = iota
	ERR_CONF int = iota
	ERR_RELOAD int = iota
)

// ApplyConfiguration write the new configuration and reload
// A rollback is called on failure
func (hap *Haproxy) ApplyConfiguration(data *EventMessage) (int, error) {
	hap.createSkeleton(data.Correlationid)

	newConf := data.Conf
	path := hap.confPath()

	// Check conf diff
	oldConf, err := ioutil.ReadFile(path)
	if log.GetLevel() == log.DebugLevel {
		hap.dumpConfiguration(hap.NewDebugPath(), newConf, data)
	}
	if bytes.Equal(oldConf, newConf) {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).Debug("Unchanged configuration")
		return UNCHANGED, nil
	}

	// Archive previous configuration
	archivePath := hap.confArchivePath()
	os.Rename(path, archivePath)
	log.WithFields(
		log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
			"archivePath": archivePath,
		}).Info("Old configuration saved")
	err = ioutil.WriteFile(path, newConf, 0644)
	if err != nil {
		return ERR_CONF, err
	}

	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"role": hap.Role,
		"application": data.Application,
		"plateform":   data.Platform,
		"path": path,
	}).Info("New configuration written")

	// Reload haproxy
	err = hap.reload(data.Correlationid)
	if err != nil {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Reload failed")
		hap.dumpConfiguration(hap.NewErrorPath(), newConf, data)
		err = hap.rollback(data.Correlationid)
		return ERR_RELOAD, err
	}
	// Write syslog fragment
	fragmentPath := hap.syslogFragmentPath()
	err = ioutil.WriteFile(fragmentPath, data.SyslogFragment, 0644)
	if err != nil {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Failed to write syslog fragment")
		// TODO Should we rollback on syslog error ?
		return ERR_SYSLOG, err
	}
	log.WithFields(log.Fields{
		"correlationId": data.Correlationid,
		"role": hap.Role,
		"application": data.Application,
		"plateform":   data.Platform,
		"content" : data.SyslogFragment,
		"filename": fragmentPath,
	}).Debug("Write syslog fragment")

	return SUCCESS, nil
}

// dumpConfiguration dumps the new configuration file with context for debugging purpose
func (hap *Haproxy) dumpConfiguration(filename string, newConf []byte, data *EventMessage) {

	f, err2 := os.Create(filename)
	defer f.Close()
	if err2 == nil {
		f.WriteString("================================================================\n")
		f.WriteString(fmt.Sprintf("application: %s\n", data.Application))
		f.WriteString(fmt.Sprintf("platform: %s\n", data.Platform))
		f.WriteString(fmt.Sprintf("correlationid: %s\n", data.Correlationid))
		f.WriteString("================================================================\n")
		f.Write(newConf)
		f.Sync()

		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"role": hap.Role,
			"filename": filename,
			"application": data.Application,
			"platform": data.Platform,
		}).Info("Dump configuration")
	}
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) confPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/Config"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// confPath give the path of the archived configuration file given an application context
func (hap *Haproxy) confArchivePath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/version-1"
	// It returns the absolute path to the file
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// NewErrorPath gives a unique path the error file given the hap context
// It returns the full path to the file
func (hap *Haproxy) NewErrorPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/errors"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

func (hap *Haproxy) NewDebugPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/dump"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

// reload calls external shell script to reload haproxy
// It returns error if the reload fails
func (hap *Haproxy) reload(correlationId string) error {

	reloadScript := hap.getReloadScript()
	cmd, err := exec.Command("sh", reloadScript, "reload", "-y").Output()
	if err != nil {
		log.WithError(err).Error("Error reloading")
	}
	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"role": hap.Role,
		"application": hap.Application,
		"platform": hap.Platform,
		"reloadScript": reloadScript,
	}).WithField("cmd", cmd).Debug("Reload succeeded")
	return err
}

// rollbac reverts configuration files and call for reload
func (hap *Haproxy) rollback(correlationId string) error {
	lastConf := hap.confArchivePath()
	if _, err := os.Stat(lastConf); os.IsNotExist(err) {
		return errors.New("No configuration file to rollback")
	}
	os.Rename(lastConf, hap.confPath())
	hap.reload(correlationId)
	return nil
}

// createSkeleton creates the directory tree for a new haproxy context
func (hap *Haproxy) createSkeleton(correlationId string) error {
	baseDir := hap.properties.HapHome + "/" + hap.Application

	createDirectory(correlationId, baseDir + "/Config")
	createDirectory(correlationId, baseDir + "/logs/" + hap.Application + hap.Platform)
	createDirectory(correlationId, baseDir + "/scripts")
	createDirectory(correlationId, baseDir + "/version-1")

	updateSymlink(correlationId, hap.getHapctlFilename(), hap.getReloadScript())
	updateSymlink(correlationId, hap.getHapBinary(), baseDir + "/Config/haproxy")

	return nil
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) syslogFragmentPath() string {
	baseDir := hap.properties.HapHome + "/SYSLOG/Config/syslog.conf.d"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/syslog" + hap.Application + hap.Platform + ".conf"
}

// updateSymlink create or update a symlink
func updateSymlink(correlationId, oldname string, newname string) {
	newLink := true
	if _, err := os.Stat(newname); err == nil {
		os.Remove(newname)
		newLink = false
	}
	err := os.Symlink(oldname, newname)
	if err != nil {
		log.WithError(err).WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Error("Symlink failed")
	}

	if newLink {
		log.WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Info("Symlink created")
	}
}

// createDirectory recursively creates directory if it doesn't exists
func createDirectory(correlationId string, dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.WithError(err).WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Error("Failed to create")
		} else {
			log.WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Info("Directory created")
		}
	}
}

// getHapctlFilename return the path to the vsc hapctl shell script
// This script is provided
func (hap *Haproxy) getHapctlFilename() string {
	return "/HOME/uxwadm/scripts/hapctl_unif"
}

// getReloadScript calculates reload script path given the hap context
// It returns the full script path
func (hap *Haproxy) getReloadScript() string {
	return fmt.Sprintf("%s/%s/scripts/hapctl%s%s", hap.properties.HapHome, hap.Application, hap.Application, hap.Platform)
}

// getHapBinary calculates the haproxy binary to use given the expected version
// It returns the full path to the haproxy binary
func (hap *Haproxy) getHapBinary() string {
	return fmt.Sprintf("/export/product/haproxy/product/%s/bin/haproxy", hap.Version)
}
<|endoftext|>


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

package gles

import (
	"context"
	"fmt"

	"github.com/google/gapid/core/data/binary"
	"github.com/google/gapid/gapis/api"
)

type drawCallIndices struct {
	indices  []uint32
	drawMode GLenum
	indexed  bool
}

// drawCall is the interface implemented by all GLES draw call atoms.
type drawCall interface {
	api.Cmd
	getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error)
}

func (a *GlDrawArrays) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	indices := make([]uint32, a.IndicesCount)
	for i := range indices {
		indices[i] = uint32(a.FirstIndex) + uint32(i)
	}
	return drawCallIndices{indices, a.DrawMode, false}, nil
}

func (a *GlDrawElements) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	return getIndices(ctx, c, s, a.IndicesType, a.DrawMode, 0, a.IndicesCount, a.Indices)
}

func (a *GlDrawRangeElements) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	return getIndices(ctx, c, s, a.IndicesType, a.DrawMode, 0, a.IndicesCount, a.Indices)
}

func getIndices(
	ctx context.Context,
	c *Context,
	s *api.GlobalState,
	ty, drawMode GLenum,
	first, count GLsizei,
	ptr IndicesPointer) (drawCallIndices, error) {

	indexSize := map[GLenum]uint64{
		GLenum_GL_UNSIGNED_BYTE:  1,
		GLenum_GL_UNSIGNED_SHORT: 2,
		GLenum_GL_UNSIGNED_INT:   4,
	}[ty]
	indexBuffer := c.Bound.VertexArray.ElementArrayBuffer
	size := uint64(count) * indexSize
	offset := uint64(first) * indexSize

	var reader binary.Reader
	if indexBuffer == nil {
		// Get the index buffer data from pointer
		reader = ptr.Slice(offset, size, s.MemoryLayout).Reader(ctx, s)
	} else {
		// Get the index buffer data from buffer, offset by the 'indices' pointer.
		offset += ptr.addr
		reader = indexBuffer.Data.Slice(offset, offset+size, s.MemoryLayout).Reader(ctx, s)
	}

	indices, err := decodeIndices(reader, ty)
	if err != nil {
		return drawCallIndices{}, err
	}
	return drawCallIndices{indices, drawMode, true}, err
}

// decodeIndices assumes little endian encoding
func decodeIndices(r binary.Reader, indicesType GLenum) ([]uint32, error) {
	var indices []uint32
	switch indicesType {
	case GLenum_GL_UNSIGNED_BYTE:
		for {
			if val := r.Uint8(); r.Error() == nil {
				indices = append(indices, uint32(val))
			} else {
				return indices, nil
			}
		}

	case GLenum_GL_UNSIGNED_SHORT:
		for {
			if val := r.Uint16(); r.Error() == nil {
				indices = append(indices, uint32(val))
			} else {
				return indices, nil
			}
		}

	case GLenum_GL_UNSIGNED_INT:
		for {
			if val := r.Uint32(); r.Error() == nil {
				indices = append(indices, val)
			} else {
				return indices, nil
			}
		}

	default:
		return nil, fmt.Errorf("Invalid index type: %v", indicesType)
	}
}

// The draw calls below are stubbed.
func (GlDrawArraysIndirect) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysIndirect.getIndices() not implemented")
}
func (GlDrawArraysInstanced) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstanced.getIndices() not implemented")
}
func (GlDrawArraysInstancedANGLE) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedANGLE.getIndices() not implemented")
}
func (GlDrawArraysInstancedBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawArraysInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedEXT.getIndices() not implemented")
}
func (GlDrawArraysInstancedNV) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedNV.getIndices() not implemented")
}
func (GlDrawElementsBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertex.getIndices() not implemented")
}
func (GlDrawElementsBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawElementsBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertexOES.getIndices() not implemented")
}
func (GlDrawElementsIndirect) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsIndirect.getIndices() not implemented")
}
func (GlDrawElementsInstanced) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstanced.getIndices() not implemented")
}
func (GlDrawElementsInstancedANGLE) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedANGLE.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertex.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexOES.getIndices() not implemented")
}
func (GlDrawElementsInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedNV) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedNV.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertex.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertexOES.getIndices() not implemented")
}
func (GlDrawTexfOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexfOES.getIndices() not implemented")
}
func (GlDrawTexfvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexfvOES.getIndices() not implemented")
}
func (GlDrawTexiOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexiOES.getIndices() not implemented")
}
func (GlDrawTexivOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexivOES.getIndices() not implemented")
}
func (GlDrawTexsOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexsOES.getIndices() not implemented")
}
func (GlDrawTexsvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexsvOES.getIndices() not implemented")
}
func (GlDrawTexxOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexxOES.getIndices() not implemented")
}
func (GlDrawTexxvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexxvOES.getIndices() not implemented")
}
func (GlDrawTransformFeedbackEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTransformFeedbackEXT.getIndices() not implemented")
}
func (GlDrawTransformFeedbackInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTransformFeedbackInstancedEXT.getIndices() not implemented")
}
<commit_msg>gles: Don't panic if vertex stream overflows buffer.<commit_after>// Copyright (C) 2017 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gles

import (
	"context"
	"fmt"

	"github.com/google/gapid/core/data/binary"
	"github.com/google/gapid/core/math/u64"
	"github.com/google/gapid/gapis/api"
)

type drawCallIndices struct {
	indices  []uint32
	drawMode GLenum
	indexed  bool
}

// drawCall is the interface implemented by all GLES draw call atoms.
type drawCall interface {
	api.Cmd
	getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error)
}

func (a *GlDrawArrays) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	indices := make([]uint32, a.IndicesCount)
	for i := range indices {
		indices[i] = uint32(a.FirstIndex) + uint32(i)
	}
	return drawCallIndices{indices, a.DrawMode, false}, nil
}

func (a *GlDrawElements) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	return getIndices(ctx, c, s, a.IndicesType, a.DrawMode, 0, a.IndicesCount, a.Indices)
}

func (a *GlDrawRangeElements) getIndices(ctx context.Context, c *Context, s *api.GlobalState) (drawCallIndices, error) {
	return getIndices(ctx, c, s, a.IndicesType, a.DrawMode, 0, a.IndicesCount, a.Indices)
}

func getIndices(
	ctx context.Context,
	c *Context,
	s *api.GlobalState,
	ty, drawMode GLenum,
	first, count GLsizei,
	ptr IndicesPointer) (drawCallIndices, error) {

	indexSize := map[GLenum]uint64{
		GLenum_GL_UNSIGNED_BYTE:  1,
		GLenum_GL_UNSIGNED_SHORT: 2,
		GLenum_GL_UNSIGNED_INT:   4,
	}[ty]
	indexBuffer := c.Bound.VertexArray.ElementArrayBuffer
	size := uint64(count) * indexSize
	offset := uint64(first) * indexSize

	var reader binary.Reader
	if indexBuffer == nil {
		// Get the index buffer data from pointer
		reader = ptr.Slice(offset, size, s.MemoryLayout).Reader(ctx, s)
	} else {
		// Get the index buffer data from buffer, offset by the 'indices' pointer.
		offset += ptr.addr
		start := u64.Min(offset, indexBuffer.Data.count)
		end := u64.Min(offset+size, indexBuffer.Data.count)
		reader = indexBuffer.Data.Slice(start, end, s.MemoryLayout).Reader(ctx, s)
	}

	indices, err := decodeIndices(reader, ty)
	if err != nil {
		return drawCallIndices{}, err
	}
	return drawCallIndices{indices, drawMode, true}, err
}

// decodeIndices assumes little endian encoding
func decodeIndices(r binary.Reader, indicesType GLenum) ([]uint32, error) {
	var indices []uint32
	switch indicesType {
	case GLenum_GL_UNSIGNED_BYTE:
		for {
			if val := r.Uint8(); r.Error() == nil {
				indices = append(indices, uint32(val))
			} else {
				return indices, nil
			}
		}

	case GLenum_GL_UNSIGNED_SHORT:
		for {
			if val := r.Uint16(); r.Error() == nil {
				indices = append(indices, uint32(val))
			} else {
				return indices, nil
			}
		}

	case GLenum_GL_UNSIGNED_INT:
		for {
			if val := r.Uint32(); r.Error() == nil {
				indices = append(indices, val)
			} else {
				return indices, nil
			}
		}

	default:
		return nil, fmt.Errorf("Invalid index type: %v", indicesType)
	}
}

// The draw calls below are stubbed.
func (GlDrawArraysIndirect) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysIndirect.getIndices() not implemented")
}
func (GlDrawArraysInstanced) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstanced.getIndices() not implemented")
}
func (GlDrawArraysInstancedANGLE) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedANGLE.getIndices() not implemented")
}
func (GlDrawArraysInstancedBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawArraysInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedEXT.getIndices() not implemented")
}
func (GlDrawArraysInstancedNV) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawArraysInstancedNV.getIndices() not implemented")
}
func (GlDrawElementsBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertex.getIndices() not implemented")
}
func (GlDrawElementsBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawElementsBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsBaseVertexOES.getIndices() not implemented")
}
func (GlDrawElementsIndirect) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsIndirect.getIndices() not implemented")
}
func (GlDrawElementsInstanced) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstanced.getIndices() not implemented")
}
func (GlDrawElementsInstancedANGLE) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedANGLE.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertex.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexBaseInstanceEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexBaseInstanceEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedBaseVertexOES.getIndices() not implemented")
}
func (GlDrawElementsInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedEXT.getIndices() not implemented")
}
func (GlDrawElementsInstancedNV) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawElementsInstancedNV.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertex) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertex.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertexEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertexEXT.getIndices() not implemented")
}
func (GlDrawRangeElementsBaseVertexOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawRangeElementsBaseVertexOES.getIndices() not implemented")
}
func (GlDrawTexfOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexfOES.getIndices() not implemented")
}
func (GlDrawTexfvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexfvOES.getIndices() not implemented")
}
func (GlDrawTexiOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexiOES.getIndices() not implemented")
}
func (GlDrawTexivOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexivOES.getIndices() not implemented")
}
func (GlDrawTexsOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexsOES.getIndices() not implemented")
}
func (GlDrawTexsvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexsvOES.getIndices() not implemented")
}
func (GlDrawTexxOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexxOES.getIndices() not implemented")
}
func (GlDrawTexxvOES) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTexxvOES.getIndices() not implemented")
}
func (GlDrawTransformFeedbackEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTransformFeedbackEXT.getIndices() not implemented")
}
func (GlDrawTransformFeedbackInstancedEXT) getIndices(context.Context, *Context, *api.GlobalState) (drawCallIndices, error) {
	return drawCallIndices{}, fmt.Errorf("GlDrawTransformFeedbackInstancedEXT.getIndices() not implemented")
}
<|endoftext|>


<commit_before>/*
   Copyright 2013 Juliano Martinez <juliano@martinez.io>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

      Based on http://github.com/nf/webfront

   @author: Juliano Martinez
*/

package http_server

import (
	"fmt"
	"github.com/fiorix/go-redis/redis"
	hpr_utils "github.com/ncode/hot-potato-router/utils"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	cfg = hpr_utils.NewConfig()
	rc  = redis.New(cfg.Options["redis"]["server_list"])
)

func xff(req *http.Request) string {
	remote_addr := strings.Split(req.RemoteAddr, ":")
	if len(remote_addr) == 0 {
		return ""
	}
	return remote_addr[0]
}

type Server struct {
	mu      sync.RWMutex
	last    time.Time
	proxy   map[string][]Proxy
	backend map[string]int
}

type Proxy struct {
	Connections int64
	Backend     string
	handler     http.Handler
}

func Listen(fd int, addr string) net.Listener {
	var l net.Listener
	var err error
	if fd >= 3 {
		l, err = net.FileListener(os.NewFile(uintptr(fd), "http"))
	} else {
		l, err = net.Listen("tcp", addr)
	}
	if err != nil {
		log.Fatal(err)
	}
	return l
}

func NewServer(probe time.Duration) (*Server, error) {
	s := new(Server)
	s.proxy = make(map[string][]Proxy)
	s.backend = make(map[string]int)
	go s.probe_backends(probe)
	return s, nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h := s.handler(r); h != nil {
		client := xff(r)
		hpr_utils.Log(fmt.Sprintf("Request from: %s Url: %s", client, r.Host))
		r.Header.Add("X-Forwarded-For", client)
		r.Header.Add("X-Real-IP", client)
		h.ServeHTTP(w, r)
		return
	}
	http.Error(w, "Not found.", http.StatusNotFound)
}

func (s *Server) handler(req *http.Request) http.Handler {
	h := req.Host
	if i := strings.Index(h, ":"); i >= 0 {
		h = h[:i]
	}

	_, ok := s.proxy[h]
	if !ok {
		fmt.Println(fmt.Sprintf("hpr-backends::%s", h))
		f, _ := rc.ZRange(fmt.Sprintf("hpr-backends::%s", h), 0, -1, true)
		if len(f) == 0 {
			return nil
		}
		s.mu.Lock()
		var url string
		for _, be := range f {
			count, err := strconv.Atoi(be)
			if err != nil {
				url = be
				continue
			}

			for r := 0; r >= count; r++ {
				log.Println(r)
				log.Println(count)
				log.Println(url)
				s.proxy[h] = append(s.proxy[h], Proxy{0, url, makeHandler(url)})
			}
		}
		s.mu.Unlock()
	}
	return s.Next(h)
}

/* TODO: Implement more balance algorithms */
func (s *Server) Next(h string) http.Handler {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.backend[h]++
	total := len(s.proxy[h])
	if s.backend[h] == total {
		s.backend[h] = 0
	}
	hpr_utils.Log(fmt.Sprintf("Using backend: %s Url: %s", s.proxy[h][s.backend[h]].Backend, h))
	return s.proxy[h][s.backend[h]].handler
}

func (s *Server) probe_backends(probe time.Duration) {
	for {
		time.Sleep(probe)
		// s.mu.Lock()
		for key, value := range s.proxy {
			hpr_utils.Log(fmt.Sprintf("Key: %s Value: %s", key, value))
		}
		// s.mu.Unlock()
	}
}

func makeHandler(f string) http.Handler {
	if f != "" {
		return &httputil.ReverseProxy{
			Director: func(req *http.Request) {
				req.URL.Scheme = "http"
				req.URL.Host = f
			},
		}
	}
	return nil
}
<commit_msg>lalala<commit_after>/*
   Copyright 2013 Juliano Martinez <juliano@martinez.io>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

      Based on http://github.com/nf/webfront

   @author: Juliano Martinez
*/

package http_server

import (
	"fmt"
	"github.com/fiorix/go-redis/redis"
	hpr_utils "github.com/ncode/hot-potato-router/utils"
	"log"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"
)

var (
	cfg = hpr_utils.NewConfig()
	rc  = redis.New(cfg.Options["redis"]["server_list"])
)

func xff(req *http.Request) string {
	remote_addr := strings.Split(req.RemoteAddr, ":")
	if len(remote_addr) == 0 {
		return ""
	}
	return remote_addr[0]
}

type Server struct {
	mu      sync.RWMutex
	last    time.Time
	proxy   map[string][]Proxy
	backend map[string]int
}

type Proxy struct {
	Connections int64
	Backend     string
	handler     http.Handler
}

func Listen(fd int, addr string) net.Listener {
	var l net.Listener
	var err error
	if fd >= 3 {
		l, err = net.FileListener(os.NewFile(uintptr(fd), "http"))
	} else {
		l, err = net.Listen("tcp", addr)
	}
	if err != nil {
		log.Fatal(err)
	}
	return l
}

func NewServer(probe time.Duration) (*Server, error) {
	s := new(Server)
	s.proxy = make(map[string][]Proxy)
	s.backend = make(map[string]int)
	go s.probe_backends(probe)
	return s, nil
}

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if h := s.handler(r); h != nil {
		client := xff(r)
		hpr_utils.Log(fmt.Sprintf("Request from: %s Url: %s", client, r.Host))
		r.Header.Add("X-Forwarded-For", client)
		r.Header.Add("X-Real-IP", client)
		h.ServeHTTP(w, r)
		return
	}
	http.Error(w, "Not found.", http.StatusNotFound)
}

func (s *Server) handler(req *http.Request) http.Handler {
	h := req.Host
	if i := strings.Index(h, ":"); i >= 0 {
		h = h[:i]
	}

	_, ok := s.proxy[h]
	if !ok {
		fmt.Println(fmt.Sprintf("hpr-backends::%s", h))
		f, _ := rc.ZRange(fmt.Sprintf("hpr-backends::%s", h), 0, -1, true)
		if len(f) == 0 {
			return nil
		}
		s.mu.Lock()
		var url string
		for _, be := range f {
			count, err := strconv.Atoi(be)
			log.Println(count)
			if err != nil {
				url = be
				log.Println(be)
				continue
			}

			for r := 0; r >= count; r++ {
				log.Println(r)
				log.Println(count)
				log.Println(url)
				s.proxy[h] = append(s.proxy[h], Proxy{0, url, makeHandler(url)})
			}
		}
		s.mu.Unlock()
	}
	return s.Next(h)
}

/* TODO: Implement more balance algorithms */
func (s *Server) Next(h string) http.Handler {
	s.mu.Lock()
	defer s.mu.Unlock()
	s.backend[h]++
	total := len(s.proxy[h])
	if s.backend[h] == total {
		s.backend[h] = 0
	}
	hpr_utils.Log(fmt.Sprintf("Using backend: %s Url: %s", s.proxy[h][s.backend[h]].Backend, h))
	return s.proxy[h][s.backend[h]].handler
}

func (s *Server) probe_backends(probe time.Duration) {
	for {
		time.Sleep(probe)
		// s.mu.Lock()
		for key, value := range s.proxy {
			hpr_utils.Log(fmt.Sprintf("Key: %s Value: %s", key, value))
		}
		// s.mu.Unlock()
	}
}

func makeHandler(f string) http.Handler {
	if f != "" {
		return &httputil.ReverseProxy{
			Director: func(req *http.Request) {
				req.URL.Scheme = "http"
				req.URL.Host = f
			},
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package httpservice

import (
	"net/http"

	"github.com/arjantop/saola"
	"golang.org/x/net/context"
)

type CancellableRoundTripper interface {
	http.RoundTripper
	CancelRequest(*http.Request)
}

type ClientRequest struct {
	Request  *http.Request
	Response *http.Response
}

type clientRequest struct{}

func withClientRequest(ctx context.Context, cr *ClientRequest) context.Context {
	return context.WithValue(ctx, clientRequest{}, cr)
}

func GetClientRequest(ctx context.Context) *ClientRequest {
	if r, ok := ctx.Value(clientRequest{}).(*ClientRequest); ok {
		return r
	}
	panic("missing client request")
}

type Client struct {
	Filter    saola.Filter
	service   saola.Service
	Transport CancellableRoundTripper
}

type result struct {
	Response *http.Response
	Error    error
}

func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error) {
	if c.service == nil {
		s := newClientService(c.Transport)
		if c.Filter != nil {
			c.service = saola.Apply(s, c.Filter)
		} else {
			c.service = s
		}
	}
	cr := &ClientRequest{Request: req}
	err := c.service.Do(withClientRequest(ctx, cr))
	return cr.Response, err
}

func newClientService(tr CancellableRoundTripper) saola.Service {
	return saola.FuncService(func(ctx context.Context) error {
		cr := GetClientRequest(ctx)
		client := http.Client{Transport: tr}
		r := make(chan result, 1)
		go func() {
			resp, err := client.Do(cr.Request)
			r <- result{resp, err}
		}()
		select {
		case <-ctx.Done():
			tr.CancelRequest(cr.Request)
			<-r
			return ctx.Err()
		case result := <-r:
			cr.Response = result.Response
			return result.Error
		}
	})
}
<commit_msg>Reuse the client for all requests.<commit_after>package httpservice

import (
	"net/http"

	"github.com/arjantop/saola"
	"golang.org/x/net/context"
)

type CancellableRoundTripper interface {
	http.RoundTripper
	CancelRequest(*http.Request)
}

type ClientRequest struct {
	Request  *http.Request
	Response *http.Response
}

type clientRequest struct{}

func withClientRequest(ctx context.Context, cr *ClientRequest) context.Context {
	return context.WithValue(ctx, clientRequest{}, cr)
}

func GetClientRequest(ctx context.Context) *ClientRequest {
	if r, ok := ctx.Value(clientRequest{}).(*ClientRequest); ok {
		return r
	}
	panic("missing client request")
}

type Client struct {
	Filter    saola.Filter
	service   saola.Service
	Transport CancellableRoundTripper
}

type result struct {
	Response *http.Response
	Error    error
}

func (c *Client) Do(ctx context.Context, req *http.Request) (*http.Response, error) {
	if c.service == nil {
		s := newClientService(c.Transport)
		if c.Filter != nil {
			c.service = saola.Apply(s, c.Filter)
		} else {
			c.service = s
		}
	}
	cr := &ClientRequest{Request: req}
	err := c.service.Do(withClientRequest(ctx, cr))
	return cr.Response, err
}

func newClientService(tr CancellableRoundTripper) saola.Service {
	client := http.Client{Transport: tr}
	return saola.FuncService(func(ctx context.Context) error {
		cr := GetClientRequest(ctx)
		r := make(chan result, 1)
		go func() {
			resp, err := client.Do(cr.Request)
			r <- result{resp, err}
		}()
		select {
		case <-ctx.Done():
			tr.CancelRequest(cr.Request)
			<-r
			return ctx.Err()
		case result := <-r:
			cr.Response = result.Response
			return result.Error
		}
	})
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

var (
	baseTemplate       *template.Template
	postTemplate       *template.Template
	indexTemplate      *template.Template
	categoryTemplate   *template.Template
	collectionTemplate *template.Template
	config             Config
	ConfigFile         string
	PublicDir          string
	PostsDir           string
	TemplatesDir       string
	RssURL             string

	specFiles = map[string]struct{}{
		"favicon.ico":          struct{}{},
		"robots.txt":           struct{}{},
		"humans.txt":           struct{}{},
		"apple-touch-icon.png": struct{}{},
	}

	funcs = template.FuncMap{
		"formattime": func(t time.Time, f string) string {
			return t.Format(f)
		},
	}
)

func init() {
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatal("FATAL", err)
	}
	PublicDir = filepath.Join(pwd, "public")
	PostsDir = filepath.Join(pwd, "post")
	TemplatesDir = filepath.Join(pwd, "template")
	ConfigFile = filepath.Join(pwd, "config.json")
	config = GetConfig(ConfigFile)
}

func storeRssURL() {
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		fmt.Errorf("Error parsing the baseurl: %s", err)
	}
	rss, err := base.Parse("/rss")
	if err != nil {
		fmt.Errorf("Error parsing the rss url: %s", err)
	}

	RssURL = rss.String()
}

type posts []*LongPost

func (p posts) Len() int           { return len(p) }
func (p posts) Less(i, j int) bool { return p[i].PublishDate.Before(p[j].PublishDate) }
func (p posts) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func filter(file []os.FileInfo) []os.FileInfo {
	for i := 0; i < len(file); {
		if file[i].IsDir() || filepath.Ext(file[i].Name()) != ".md" {
			file[i], file = file[len(file)-1], file[:len(file)-1]
		} else {
			i++
		}
	}

	return file
}

func clearPublishDir() error {
	files, err := ioutil.ReadDir(PublicDir)
	if err != nil {
		return fmt.Errorf("Error getting public directory files: %s", err)
	}

	for _, file := range files {
		if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
			if _, ok := specFiles[file.Name()]; !ok {
				err = os.Remove(filepath.Join(PublicDir, file.Name()))
				if err != nil {
					return fmt.Errorf("Error deleting file %s: %s", file.Name(), err)
				}
			}
		}
	}

	return nil
}

func getPosts(files []os.FileInfo) (allPosts []*LongPost, recentPosts []*LongPost) {
	allPosts = make([]*LongPost, 0, len(files))
	for _, file := range files {
		longPost, err := newLongPost(file)
		if err != nil {
			log.Printf("Post ignored: %s; Error: %s\n", file.Name(), err)
		} else {
			allPosts = append(allPosts, longPost)
		}
	}

	sort.Sort(sort.Reverse(posts(allPosts)))

	for i, _ := range allPosts {
		if i > 0 {
			allPosts[i].PrevSlug = allPosts[i-1].Slug
		}
		if i < len(allPosts)-1 {
			allPosts[i].NextSlug = allPosts[i+1].Slug
		}
	}
	recent := config.RecentPostsCount
	if length := len(allPosts); length < recent {
		recent = length
	}
	recentPosts = allPosts[:recent]
	return
}

func loadTemplates() {
	baseTemplate = template.Must(template.ParseFiles("template/base.html")).Funcs(funcs)
	postTemplate = template.Must(baseTemplate.Clone())
	postTemplate = template.Must(postTemplate.ParseFiles("template/post.html"))
	indexTemplate = template.Must(template.ParseFiles("template/index.html"))
	categoryTemplate = template.Must(template.ParseFiles("template/category.html"))
	collectionTemplate = template.Must(template.ParseFiles("template/collection.html"))
}

func GenerateSite() error {
	//TODO: format date in the template
	storeRssURL()
	loadTemplates()
	files, err := ioutil.ReadDir(PostsDir)
	if err != nil {
		return err
	}

	files = filter(files)

	allPosts, recentPosts := getPosts(files)
	collections := getCollection(allPosts)

	if err := clearPublishDir(); err != nil {
		return err
	}

	for i, p := range allPosts {
		pt := newPostTempalte(p, i, recentPosts, allPosts, config)
		if i == 0 {
			if err := generateIndexFile(pt); err != nil {
				return err
			}
		}
		if err := generatePostFile(pt); err != nil {
			return err
		}
		fmt.Println(i)
	}

	err = generateCategoryFile(collections)
	if err != nil {
		return err
	}

	for key, _ := range collections {
		if err := generateCollectionFile(key, collections[key]); err != nil {
			fmt.Println(err)
			return err
		}
		fmt.Println(key, collections[key])
	}

	pt := newPostTempalte(nil, 0, recentPosts, allPosts, config)

	err = generateJson(pt)
	if err != nil {
		return err
	}
	return generateRss(pt)
}

func generateRss(pt *PostTempalte) error {
	rss := NewRss(config.SiteName, config.Slogan, config.BaseURL, config.Author)
	fmt.Println(config.Author)
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		return fmt.Errorf("Error parsing base URL: %s", err)
	}

	for _, p := range pt.Recent {
		u, err := base.Parse(p.Slug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		rss.Channels[0].AppendItem(NewRssItem(p.Title, p.Description, u.String(), p.Author, "", p.PublishDate.Format("2006-01-02")))
	}

	return rss.WriteToFile(filepath.Join(PublicDir, "rss.xml"))
}

func generateJson(pt *PostTempalte) error {
	siteJson := NewSiteJson(config.SiteName)
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		return fmt.Errorf("Error parsing base URL: %s", err)
	}

	for _, p := range pt.All {
		slug, err := base.Parse(p.Slug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		prevSlug, err := base.Parse(p.PrevSlug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		nextSlug, err := base.Parse(p.NextSlug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		siteJson.AppendPostJson(NewPostJson(slug.String(), p.Author, p.Title, p.Description, p.Category, p.PublishDate.Format("2006-01-02"), p.ModifyDate.Format("2006-01-02"), p.ReadingTime, prevSlug.String(), nextSlug.String(), string(p.Content)))
	}
	return siteJson.WriteToFile(filepath.Join(PublicDir, "site.json"))
}

func generatePostFile(pt *PostTempalte) error {

	fileWriter, err := os.Create(filepath.Join(PublicDir, pt.Post.Slug))
	if err != nil {
		return fmt.Errorf("Error creating static file %s: %s", pt.Post.Slug, err)
	}
	defer fileWriter.Close()

	return postTemplate.ExecuteTemplate(fileWriter, "base", pt)
}

func generateIndexFile(pt *PostTempalte) error {

	indexWriter, err := os.Create(filepath.Join(PublicDir, "index.html"))
	if err != nil {
		return fmt.Errorf("Error creating static file index.html: %s", err)
	}
	defer indexWriter.Close()

	return indexTemplate.ExecuteTemplate(indexWriter, "index", pt)
}

func generateCategoryFile(c map[string][]*LongPost) error {

	categoryWriter, err := os.Create(filepath.Join(PublicDir, "category.html")) //TODO every category generate a html
	if err != nil {
		return fmt.Errorf("Error creating static file category.html: %s", err)
	}
	defer categoryWriter.Close()

	return categoryTemplate.ExecuteTemplate(categoryWriter, "category", c)
}

func generateCollectionFile(c string, posts []*LongPost) error { //TODO: init reposity first
	collectionWriter, err := os.Create(filepath.Join(PublicDir, "collection", c+".html"))
	if err != nil {
		return fmt.Errorf("Error creating static file in collection %s html: %s", c, err)
	}
	defer collectionWriter.Close()

	fmt.Println(posts)

	return collectionTemplate.ExecuteTemplate(collectionWriter, "collection", posts)
}
<commit_msg>remove comment<commit_after>package lib

import (
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"
)

var (
	baseTemplate       *template.Template
	postTemplate       *template.Template
	indexTemplate      *template.Template
	categoryTemplate   *template.Template
	collectionTemplate *template.Template
	config             Config
	ConfigFile         string
	PublicDir          string
	PostsDir           string
	TemplatesDir       string
	RssURL             string

	specFiles = map[string]struct{}{
		"favicon.ico":          struct{}{},
		"robots.txt":           struct{}{},
		"humans.txt":           struct{}{},
		"apple-touch-icon.png": struct{}{},
	}

	funcs = template.FuncMap{
		"formattime": func(t time.Time, f string) string {
			return t.Format(f)
		},
	}
)

func init() {
	pwd, err := os.Getwd()
	if err != nil {
		log.Fatal("FATAL", err)
	}
	PublicDir = filepath.Join(pwd, "public")
	PostsDir = filepath.Join(pwd, "post")
	TemplatesDir = filepath.Join(pwd, "template")
	ConfigFile = filepath.Join(pwd, "config.json")
	config = GetConfig(ConfigFile)
}

func storeRssURL() {
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		fmt.Errorf("Error parsing the baseurl: %s", err)
	}
	rss, err := base.Parse("/rss")
	if err != nil {
		fmt.Errorf("Error parsing the rss url: %s", err)
	}

	RssURL = rss.String()
}

type posts []*LongPost

func (p posts) Len() int           { return len(p) }
func (p posts) Less(i, j int) bool { return p[i].PublishDate.Before(p[j].PublishDate) }
func (p posts) Swap(i, j int)      { p[i], p[j] = p[j], p[i] }

func filter(file []os.FileInfo) []os.FileInfo {
	for i := 0; i < len(file); {
		if file[i].IsDir() || filepath.Ext(file[i].Name()) != ".md" {
			file[i], file = file[len(file)-1], file[:len(file)-1]
		} else {
			i++
		}
	}

	return file
}

func clearPublishDir() error {
	files, err := ioutil.ReadDir(PublicDir)
	if err != nil {
		return fmt.Errorf("Error getting public directory files: %s", err)
	}

	for _, file := range files {
		if !file.IsDir() && !strings.HasPrefix(file.Name(), ".") {
			if _, ok := specFiles[file.Name()]; !ok {
				err = os.Remove(filepath.Join(PublicDir, file.Name()))
				if err != nil {
					return fmt.Errorf("Error deleting file %s: %s", file.Name(), err)
				}
			}
		}
	}

	return nil
}

func getPosts(files []os.FileInfo) (allPosts []*LongPost, recentPosts []*LongPost) {
	allPosts = make([]*LongPost, 0, len(files))
	for _, file := range files {
		longPost, err := newLongPost(file)
		if err != nil {
			log.Printf("Post ignored: %s; Error: %s\n", file.Name(), err)
		} else {
			allPosts = append(allPosts, longPost)
		}
	}

	sort.Sort(sort.Reverse(posts(allPosts)))

	for i, _ := range allPosts {
		if i > 0 {
			allPosts[i].PrevSlug = allPosts[i-1].Slug
		}
		if i < len(allPosts)-1 {
			allPosts[i].NextSlug = allPosts[i+1].Slug
		}
	}
	recent := config.RecentPostsCount
	if length := len(allPosts); length < recent {
		recent = length
	}
	recentPosts = allPosts[:recent]
	return
}

func loadTemplates() {
	baseTemplate = template.Must(template.ParseFiles("template/base.html")).Funcs(funcs)
	postTemplate = template.Must(baseTemplate.Clone())
	postTemplate = template.Must(postTemplate.ParseFiles("template/post.html"))
	indexTemplate = template.Must(template.ParseFiles("template/index.html"))
	categoryTemplate = template.Must(template.ParseFiles("template/category.html"))
	collectionTemplate = template.Must(template.ParseFiles("template/collection.html"))
}

func GenerateSite() error {
	storeRssURL()
	loadTemplates()
	files, err := ioutil.ReadDir(PostsDir)
	if err != nil {
		return err
	}

	files = filter(files)

	allPosts, recentPosts := getPosts(files)
	collections := getCollection(allPosts)

	if err := clearPublishDir(); err != nil {
		return err
	}

	for i, p := range allPosts {
		pt := newPostTempalte(p, i, recentPosts, allPosts, config)
		if i == 0 {
			if err := generateIndexFile(pt); err != nil {
				return err
			}
		}
		if err := generatePostFile(pt); err != nil {
			return err
		}
		fmt.Println(i)
	}

	err = generateCategoryFile(collections)
	if err != nil {
		return err
	}

	for key, _ := range collections {
		if err := generateCollectionFile(key, collections[key]); err != nil {
			fmt.Println(err)
			return err
		}
		fmt.Println(key, collections[key])
	}

	pt := newPostTempalte(nil, 0, recentPosts, allPosts, config)

	err = generateJson(pt)
	if err != nil {
		return err
	}
	return generateRss(pt)
}

func generateRss(pt *PostTempalte) error {
	rss := NewRss(config.SiteName, config.Slogan, config.BaseURL, config.Author)
	fmt.Println(config.Author)
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		return fmt.Errorf("Error parsing base URL: %s", err)
	}

	for _, p := range pt.Recent {
		u, err := base.Parse(p.Slug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		rss.Channels[0].AppendItem(NewRssItem(p.Title, p.Description, u.String(), p.Author, "", p.PublishDate.Format("2006-01-02")))
	}

	return rss.WriteToFile(filepath.Join(PublicDir, "rss.xml"))
}

func generateJson(pt *PostTempalte) error {
	siteJson := NewSiteJson(config.SiteName)
	base, err := url.Parse(config.BaseURL)
	if err != nil {
		return fmt.Errorf("Error parsing base URL: %s", err)
	}

	for _, p := range pt.All {
		slug, err := base.Parse(p.Slug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		prevSlug, err := base.Parse(p.PrevSlug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		nextSlug, err := base.Parse(p.NextSlug)
		if err != nil {
			return fmt.Errorf("Error parsing post URL: %s", err)
		}
		siteJson.AppendPostJson(NewPostJson(slug.String(), p.Author, p.Title, p.Description, p.Category, p.PublishDate.Format("2006-01-02"), p.ModifyDate.Format("2006-01-02"), p.ReadingTime, prevSlug.String(), nextSlug.String(), string(p.Content)))
	}
	return siteJson.WriteToFile(filepath.Join(PublicDir, "site.json"))
}

func generatePostFile(pt *PostTempalte) error {

	fileWriter, err := os.Create(filepath.Join(PublicDir, pt.Post.Slug))
	if err != nil {
		return fmt.Errorf("Error creating static file %s: %s", pt.Post.Slug, err)
	}
	defer fileWriter.Close()

	return postTemplate.ExecuteTemplate(fileWriter, "base", pt)
}

func generateIndexFile(pt *PostTempalte) error {

	indexWriter, err := os.Create(filepath.Join(PublicDir, "index.html"))
	if err != nil {
		return fmt.Errorf("Error creating static file index.html: %s", err)
	}
	defer indexWriter.Close()

	return indexTemplate.ExecuteTemplate(indexWriter, "index", pt)
}

func generateCategoryFile(c map[string][]*LongPost) error {

	categoryWriter, err := os.Create(filepath.Join(PublicDir, "category.html")) //TODO every category generate a html
	if err != nil {
		return fmt.Errorf("Error creating static file category.html: %s", err)
	}
	defer categoryWriter.Close()

	return categoryTemplate.ExecuteTemplate(categoryWriter, "category", c)
}

func generateCollectionFile(c string, posts []*LongPost) error { //TODO: init reposity first
	collectionWriter, err := os.Create(filepath.Join(PublicDir, "collection", c+".html"))
	if err != nil {
		return fmt.Errorf("Error creating static file in collection %s html: %s", c, err)
	}
	defer collectionWriter.Close()

	fmt.Println(posts)

	return collectionTemplate.ExecuteTemplate(collectionWriter, "collection", posts)
}
<|endoftext|>


<commit_before>package jd

type voidNode struct{}

var _ JsonNode = voidNode{}

func isVoid(n JsonNode) bool {
	if n == nil {
		return false
	}
	if _, ok := n.(voidNode); ok {
		return true
	}
	return false
}

func (v voidNode) Json(metadata ...Metadata) string {
	return ""
}

func (v voidNode) Yaml(metadata ...Metadata) string {
	return ""
}

func (v voidNode) raw(metadata []Metadata) interface{} {
	return ""
}

func (v voidNode) Equals(n JsonNode, metadata ...Metadata) bool {
	switch n.(type) {
	case voidNode:
		return true
	default:
		return false
	}
}

func (v voidNode) hashCode(metadata []Metadata) [8]byte {
	return hash([]byte{0xF3, 0x97, 0x6B, 0x21, 0x91, 0x26, 0x8D, 0x96}) // Random bytes
}

func (v voidNode) Diff(n JsonNode, metadata ...Metadata) Diff {
	return v.diff(n, make(path, 0), metadata)
}

func (v voidNode) diff(n JsonNode, p path, metadata []Metadata) Diff {
	d := make(Diff, 0)
	if v.Equals(n) {
		return d
	}
	de := DiffElement{
		Path:      p.clone(),
		OldValues: nodeList(v),
		NewValues: nodeList(n),
	}
	return append(d, de)
}

func (v voidNode) Patch(d Diff) (JsonNode, error) {
	return patchAll(v, d)
}

func (v voidNode) patch(pathBehind, pathAhead path, oldValues, newValues []JsonNode) (JsonNode, error) {
	if len(pathAhead) != 0 {
		return patchErrExpectColl(v, pathBehind[0])
	}
	if len(oldValues) > 1 || len(newValues) > 1 {
		return patchErrNonSetDiff(oldValues, newValues, pathBehind)
	}
	oldValue := singleValue(oldValues)
	newValue := singleValue(newValues)
	if !v.Equals(oldValue) {
		return patchErrExpectValue(oldValue, v, pathBehind)
	}
	return newValue, nil
}
<commit_msg>Improve the error message for trying to patch through a void node.<commit_after>package jd

type voidNode struct{}

var _ JsonNode = voidNode{}

func isVoid(n JsonNode) bool {
	if n == nil {
		return false
	}
	if _, ok := n.(voidNode); ok {
		return true
	}
	return false
}

func (v voidNode) Json(metadata ...Metadata) string {
	return ""
}

func (v voidNode) Yaml(metadata ...Metadata) string {
	return ""
}

func (v voidNode) raw(metadata []Metadata) interface{} {
	return ""
}

func (v voidNode) Equals(n JsonNode, metadata ...Metadata) bool {
	switch n.(type) {
	case voidNode:
		return true
	default:
		return false
	}
}

func (v voidNode) hashCode(metadata []Metadata) [8]byte {
	return hash([]byte{0xF3, 0x97, 0x6B, 0x21, 0x91, 0x26, 0x8D, 0x96}) // Random bytes
}

func (v voidNode) Diff(n JsonNode, metadata ...Metadata) Diff {
	return v.diff(n, make(path, 0), metadata)
}

func (v voidNode) diff(n JsonNode, p path, metadata []Metadata) Diff {
	d := make(Diff, 0)
	if v.Equals(n) {
		return d
	}
	de := DiffElement{
		Path:      p.clone(),
		OldValues: nodeList(v),
		NewValues: nodeList(n),
	}
	return append(d, de)
}

func (v voidNode) Patch(d Diff) (JsonNode, error) {
	return patchAll(v, d)
}

func (v voidNode) patch(pathBehind, pathAhead path, oldValues, newValues []JsonNode) (JsonNode, error) {
	if len(pathAhead) != 0 {
		return patchErrExpectColl(v, pathBehind[len(pathBehind)-1])
	}
	if len(oldValues) > 1 || len(newValues) > 1 {
		return patchErrNonSetDiff(oldValues, newValues, pathBehind)
	}
	oldValue := singleValue(oldValues)
	newValue := singleValue(newValues)
	if !v.Equals(oldValue) {
		return patchErrExpectValue(oldValue, v, pathBehind)
	}
	return newValue, nil
}
<|endoftext|>