<commit_before>package content

import (
	"encoding/json"
	"time"

	"github.com/Financial-Times/neo-utils-go/neoutils"
	tid "github.com/Financial-Times/transactionid-utils-go"
	"github.com/jmcvetta/neoism"
	"github.com/Financial-Times/go-logger"
)

var contentTypesWithNoBody = map[string]bool{
	"Content": true,
	"Article": true,
	"Video":   true,
	"Graphic": true,
	"Audio":   true,
}

// CypherDriver - CypherDriver
type service struct {
	conn neoutils.NeoConnection
}

//NewCypherDriver instantiate driver
func NewCypherContentService(cypherRunner neoutils.NeoConnection) service {
	return service{cypherRunner}
}

//Initialise ensure constraint on content uuid
func (cd service) Initialise() error {

	return cd.conn.EnsureConstraints(map[string]string{
		"Content": "uuid"})
}

// Check - Feeds into the Healthcheck and checks whether we can connect to Neo and that the datastore isn't empty and
// also checks if we are connected to leader/writable node
func (cd service) Check() error {
	writableErr := neoutils.CheckWritable(cd.conn)
	if writableErr != nil {
		return writableErr
	}

	return neoutils.Check(cd.conn)
}

// Read - reads a content given a UUID
func (cd service) Read(uuid string, transId string) (interface{}, bool, error) {
	var results []struct {
		content
	}

	query := &neoism.CypherQuery{
		Statement: `MATCH (n:Content {uuid:{uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(n)
			OPTIONAL MATCH (n)-[rel2:CONTAINS]->(cp:Thing)
			WITH n,sp,cp
			return  n.uuid as uuid,
				n.title as title,
				n.publishedDate as publishedDate,
				sp.uuid as storyPackage,
				cp.uuid as contentPackage`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &results,
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return content{}, false, err
	}

	if len(results) == 0 {
		return content{}, false, nil
	}

	result := results[0]

	contentItem := content{
		UUID:           result.UUID,
		Title:          result.Title,
		PublishedDate:  result.PublishedDate,
		StoryPackage:   result.StoryPackage,
		ContentPackage: result.ContentPackage,
	}
	return contentItem, true, nil
}

//Write - Writes a content node
func (cd service) Write(thing interface{}, transId string) error {
	c := thing.(content)

	// Letting through only articles (which have body), live blogs, content packages, graphics, videos and audios (which don't have a body)
	if c.Body == "" && !contentTypesWithNoBody[c.Type] {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is no body with this content item therefore assuming is it not an Article: %v", c.UUID)
		return nil
	}

	params := map[string]interface{}{
		"uuid": c.UUID,
	}

	if c.Title != "" {
		params["title"] = c.Title
		params["prefLabel"] = c.Title
	}

	if c.PublishedDate != "" {
		params["publishedDate"] = c.PublishedDate
		datetimeEpoch, err := time.Parse(time.RFC3339, c.PublishedDate)

		if err != nil {
			return err
		}

		params["publishedDateEpoch"] = datetimeEpoch.Unix()
	}

	deleteEntityRelationshipsQuery := &neoism.CypherQuery{
		Statement: `MATCH (t:Thing {uuid:{uuid}})
				OPTIONAL MATCH (c:Thing)-[rel1:IS_CURATED_FOR]->(t)
				OPTIONAL MATCH (cp:Thing)<-[rel2:CONTAINS]-(t)
				DELETE rel1, rel2`,
		Parameters: map[string]interface{}{
			"uuid": c.UUID,
		},
	}

	queries := []*neoism.CypherQuery{deleteEntityRelationshipsQuery}

	labels := `:Content`

	if c.StoryPackage != "" {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is a story package with uuid=%v attached to Article with uuid=%v", c.StoryPackage, c.UUID)
		addStoryPackageRelationQuery := addStoryPackageRelationQuery(c.UUID, c.StoryPackage)
		queries = append(queries, addStoryPackageRelationQuery)
	}

	if c.ContentPackage != "" {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is a content package with uuid=%v attached to Article with uuid=%v", c.ContentPackage, c.UUID)
		addContentPackageRelationQuery := addContentPackageRelationQuery(c.UUID, c.ContentPackage)
		queries = append(queries, addContentPackageRelationQuery)
		labels = labels + `:ContentPackage`
	}

	statement := `MERGE (n:Thing {uuid: {uuid}})
		      set n={allprops}
		      set n ` + labels

	writeContentQuery := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"uuid":     c.UUID,
			"allprops": params,
		},
	}

	queries = append(queries, writeContentQuery)
	err := cd.conn.CypherBatch(queries)
	if err != nil {
		logger.WithMonitoringEvent("SaveNeo4j", transId, c.Type).WithError(err).Errorf("error: the query could not be executed")
	} else {
		logger.WithMonitoringEvent("SaveNeo4j", transId, c.Type).Info("the query was successfully executed")
	}
	return err
}

func addStoryPackageRelationQuery(articleUUID, packageUUID string) *neoism.CypherQuery {
	statement := `	MERGE(sp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)<-[rel:IS_CURATED_FOR]-(sp)`

	query := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
	return query
}

func addContentPackageRelationQuery(articleUUID, packageUUID string) *neoism.CypherQuery {
	statement := `	MERGE(cp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)-[rel:CONTAINS]->(cp)`

	query := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
	return query
}

//Delete - Deletes a content
func (cd service) Delete(uuid string, transId string) (bool, error) {
	clearNode := &neoism.CypherQuery{
		Statement: `
			MATCH (p:Thing {uuid: {uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(p)
			OPTIONAL MATCH (p)-[rel2:CONTAINS]->(contained_cp:Thing)
			OPTIONAL MATCH (containing_cp:Thing)-[rel3:CONTAINS]->(p)
			REMOVE p:Content
			DELETE rel1, rel2, rel3
			SET p={props}
		`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
			"props": map[string]interface{}{
				"uuid": uuid,
			},
		},
		IncludeStats: true,
	}

	removeNodeIfUnused := &neoism.CypherQuery{
		Statement: `
			MATCH (p:Thing {uuid: {uuid}})
			OPTIONAL MATCH (p)-[a]-(x)
			WITH p, count(a) AS relCount
			WHERE relCount = 0
			DELETE p
		`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{clearNode, removeNodeIfUnused})
	if err != nil {
		logger.WithMonitoringEvent("SaveNeo4j", transId, "").WithError(err).Error("error: the delete query could not be executed")
		return false, err
	}

	s1, err := clearNode.Stats()
	if err != nil {
		return false, err
	}

	var deleted bool
	if s1.ContainsUpdates && s1.LabelsRemoved > 0 {
		deleted = true
	}
	logger.WithMonitoringEvent("SaveNeo4j", transId, "").Info("the delete query was successfully executed")
	return deleted, err
}

// DecodeJSON - Decodes JSON into content
func (cd service) DecodeJSON(dec *json.Decoder) (interface{}, string, error) {
	c := content{}
	err := dec.Decode(&c)
	return c, c.UUID, err

}

// Count - Returns a count of the number of content in this Neo instance
func (cd service) Count() (int, error) {

	var results []struct {
		Count int `json:"c"`
	}

	query := &neoism.CypherQuery{
		Statement: `MATCH (n:Content) return count(n) as c`,
		Result:    &results,
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}
<commit_msg>added uuid field to log<commit_after>package content

import (
	"encoding/json"
	"time"

	"github.com/Financial-Times/neo-utils-go/neoutils"
	tid "github.com/Financial-Times/transactionid-utils-go"
	"github.com/jmcvetta/neoism"
	"github.com/Financial-Times/go-logger"
)

var contentTypesWithNoBody = map[string]bool{
	"Content": true,
	"Article": true,
	"Video":   true,
	"Graphic": true,
	"Audio":   true,
}

// CypherDriver - CypherDriver
type service struct {
	conn neoutils.NeoConnection
}

//NewCypherDriver instantiate driver
func NewCypherContentService(cypherRunner neoutils.NeoConnection) service {
	return service{cypherRunner}
}

//Initialise ensure constraint on content uuid
func (cd service) Initialise() error {

	return cd.conn.EnsureConstraints(map[string]string{
		"Content": "uuid"})
}

// Check - Feeds into the Healthcheck and checks whether we can connect to Neo and that the datastore isn't empty and
// also checks if we are connected to leader/writable node
func (cd service) Check() error {
	writableErr := neoutils.CheckWritable(cd.conn)
	if writableErr != nil {
		return writableErr
	}

	return neoutils.Check(cd.conn)
}

// Read - reads a content given a UUID
func (cd service) Read(uuid string, transId string) (interface{}, bool, error) {
	var results []struct {
		content
	}

	query := &neoism.CypherQuery{
		Statement: `MATCH (n:Content {uuid:{uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(n)
			OPTIONAL MATCH (n)-[rel2:CONTAINS]->(cp:Thing)
			WITH n,sp,cp
			return  n.uuid as uuid,
				n.title as title,
				n.publishedDate as publishedDate,
				sp.uuid as storyPackage,
				cp.uuid as contentPackage`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
		Result: &results,
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return content{}, false, err
	}

	if len(results) == 0 {
		return content{}, false, nil
	}

	result := results[0]

	contentItem := content{
		UUID:           result.UUID,
		Title:          result.Title,
		PublishedDate:  result.PublishedDate,
		StoryPackage:   result.StoryPackage,
		ContentPackage: result.ContentPackage,
	}
	return contentItem, true, nil
}

//Write - Writes a content node
func (cd service) Write(thing interface{}, transId string) error {
	c := thing.(content)

	// Letting through only articles (which have body), live blogs, content packages, graphics, videos and audios (which don't have a body)
	if c.Body == "" && !contentTypesWithNoBody[c.Type] {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is no body with this content item therefore assuming is it not an Article: %v", c.UUID)
		return nil
	}

	params := map[string]interface{}{
		"uuid": c.UUID,
	}

	if c.Title != "" {
		params["title"] = c.Title
		params["prefLabel"] = c.Title
	}

	if c.PublishedDate != "" {
		params["publishedDate"] = c.PublishedDate
		datetimeEpoch, err := time.Parse(time.RFC3339, c.PublishedDate)

		if err != nil {
			return err
		}

		params["publishedDateEpoch"] = datetimeEpoch.Unix()
	}

	deleteEntityRelationshipsQuery := &neoism.CypherQuery{
		Statement: `MATCH (t:Thing {uuid:{uuid}})
				OPTIONAL MATCH (c:Thing)-[rel1:IS_CURATED_FOR]->(t)
				OPTIONAL MATCH (cp:Thing)<-[rel2:CONTAINS]-(t)
				DELETE rel1, rel2`,
		Parameters: map[string]interface{}{
			"uuid": c.UUID,
		},
	}

	queries := []*neoism.CypherQuery{deleteEntityRelationshipsQuery}

	labels := `:Content`

	if c.StoryPackage != "" {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is a story package with uuid=%v attached to Article with uuid=%v", c.StoryPackage, c.UUID)
		addStoryPackageRelationQuery := addStoryPackageRelationQuery(c.UUID, c.StoryPackage)
		queries = append(queries, addStoryPackageRelationQuery)
	}

	if c.ContentPackage != "" {
		logger.WithField(tid.TransactionIDKey, transId).
			Infof("There is a content package with uuid=%v attached to Article with uuid=%v", c.ContentPackage, c.UUID)
		addContentPackageRelationQuery := addContentPackageRelationQuery(c.UUID, c.ContentPackage)
		queries = append(queries, addContentPackageRelationQuery)
		labels = labels + `:ContentPackage`
	}

	statement := `MERGE (n:Thing {uuid: {uuid}})
		      set n={allprops}
		      set n ` + labels

	writeContentQuery := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"uuid":     c.UUID,
			"allprops": params,
		},
	}

	queries = append(queries, writeContentQuery)
	err := cd.conn.CypherBatch(queries)
	if err != nil {
		logger.WithMonitoringEvent("SaveNeo4j", transId, c.Type).WithField("uuid", c.UUID).WithError(err).Errorf("error: the query could not be executed")
	} else {
		logger.WithMonitoringEvent("SaveNeo4j", transId, c.Type).WithField("uuid", c.UUID).Info("the query was successfully executed")
	}
	return err
}

func addStoryPackageRelationQuery(articleUUID, packageUUID string) *neoism.CypherQuery {
	statement := `	MERGE(sp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)<-[rel:IS_CURATED_FOR]-(sp)`

	query := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
	return query
}

func addContentPackageRelationQuery(articleUUID, packageUUID string) *neoism.CypherQuery {
	statement := `	MERGE(cp:Thing{uuid:{packageUuid}})
			MERGE(c:Thing{uuid:{contentUuid}})
			MERGE(c)-[rel:CONTAINS]->(cp)`

	query := &neoism.CypherQuery{
		Statement: statement,
		Parameters: map[string]interface{}{
			"packageUuid": packageUUID,
			"contentUuid": articleUUID,
		},
	}
	return query
}

//Delete - Deletes a content
func (cd service) Delete(uuid string, transId string) (bool, error) {
	clearNode := &neoism.CypherQuery{
		Statement: `
			MATCH (p:Thing {uuid: {uuid}})
			OPTIONAL MATCH (sp:Thing)-[rel1:IS_CURATED_FOR]->(p)
			OPTIONAL MATCH (p)-[rel2:CONTAINS]->(contained_cp:Thing)
			OPTIONAL MATCH (containing_cp:Thing)-[rel3:CONTAINS]->(p)
			REMOVE p:Content
			DELETE rel1, rel2, rel3
			SET p={props}
		`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
			"props": map[string]interface{}{
				"uuid": uuid,
			},
		},
		IncludeStats: true,
	}

	removeNodeIfUnused := &neoism.CypherQuery{
		Statement: `
			MATCH (p:Thing {uuid: {uuid}})
			OPTIONAL MATCH (p)-[a]-(x)
			WITH p, count(a) AS relCount
			WHERE relCount = 0
			DELETE p
		`,
		Parameters: map[string]interface{}{
			"uuid": uuid,
		},
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{clearNode, removeNodeIfUnused})
	if err != nil {
		logger.WithMonitoringEvent("SaveNeo4j", transId, "").WithField("uuid", uuid).WithError(err).Error("error: the delete query could not be executed")
		return false, err
	}

	s1, err := clearNode.Stats()
	if err != nil {
		return false, err
	}

	var deleted bool
	if s1.ContainsUpdates && s1.LabelsRemoved > 0 {
		deleted = true
	}
	logger.WithMonitoringEvent("SaveNeo4j", transId, "").WithField("uuid", uuid).Info("the delete query was successfully executed")
	return deleted, err
}

// DecodeJSON - Decodes JSON into content
func (cd service) DecodeJSON(dec *json.Decoder) (interface{}, string, error) {
	c := content{}
	err := dec.Decode(&c)
	return c, c.UUID, err

}

// Count - Returns a count of the number of content in this Neo instance
func (cd service) Count() (int, error) {

	var results []struct {
		Count int `json:"c"`
	}

	query := &neoism.CypherQuery{
		Statement: `MATCH (n:Content) return count(n) as c`,
		Result:    &results,
	}

	err := cd.conn.CypherBatch([]*neoism.CypherQuery{query})

	if err != nil {
		return 0, err
	}

	return results[0].Count, nil
}
<|endoftext|>


<commit_before>package ast

import (
	"bytes"
	"monkey/token"
)

type Node interface {
	TokenLiteral() string
	String() string
}

type Statement interface {
	Node
	statementNode()
}

type Expression interface {
	Node
	expressionNode()
}

type Program struct {
	Statements []Statement
}

func (p *Program) TokenLiteral() string {
	if len(p.Statements) > 0 {
		return p.Statements[0].TokenLiteral()
	} else {
		return ""
	}
}
func (p *Program) String() string {
	var out bytes.Buffer

	for _, s := range p.Statements {
		out.WriteString(s.String())
	}

	return out.String()
}

type LetStatement struct {
	Token token.Token
	Name  *Identifier
	Value Expression
}

func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string {
	return ls.Token.Literal
}
func (ls *LetStatement) String() string {
	var out bytes.Buffer

	out.WriteString(ls.TokenLiteral() + " ")
	out.WriteString(ls.Name.String())
	out.WriteString(" = ")

	if ls.Value != nil {
		out.WriteString(ls.Value.String())
	}

	out.WriteString(";")

	return out.String()
}

type Identifier struct {
	Token token.Token
	Value string
}

func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string {
	return i.Token.Literal
}
func (i *Identifier) String() string {
	return i.Value
}

type ReturnStatement struct {
	Token       token.Token
	ReturnValue Expression
}

func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string {
	return rs.Token.Literal
}
func (rs *ReturnStatement) String() string {
	var out bytes.Buffer

	out.WriteString(rs.TokenLiteral() + " ")

	if rs.ReturnValue != nil {
		out.WriteString(rs.ReturnValue.String())
	}

	out.WriteString(";")

	return out.String()
}

type ExpressionStatement struct {
	Token      token.Token
	Expression Expression
}

func (es *ExpressionStatement) statementNode() {}
func (es *ExpressionStatement) TokenLiteral() string {
	return es.Token.Literal
}
func (es *ExpressionStatement) String() string {
	if es.Expression != nil {
		return es.Expression.String()
	}
	return ""
}

type IntegerLiteral struct {
	Token token.Token
	Value int64
}

func (il *IntegerLiteral) expressionNode() {}
func (il *IntegerLiteral) TokenLiteral() string {
	return il.Token.Literal
}
func (il *IntegerLiteral) String() string {
	return il.Token.Literal
}

type PrefixExpression struct {
	Token    token.Token
	Operator string
	Right    Expression
}

func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string {
	return pe.Token.Literal
}
func (pe *PrefixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(pe.Operator)
	out.WriteString(pe.Right.String())
	out.WriteString(")")

	return out.String()
}

type InfixExpression struct {
	Token    token.Token
	Left     Expression
	Operator string
	Right    Expression
}

func (ie *InfixExpression) expressionNode() {}
func (ie *InfixExpression) TokenLiteral() string {
	return ie.Token.Literal
}
func (ie *InfixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.String())
	out.WriteString(" " + ie.Operator + " ")
	out.WriteString(ie.Right.String())
	out.WriteString(")")

	return out.String()
}

type Boolean struct {
	Token token.Token
	Value bool
}

func (b *Boolean) expressionNode() {}
func (b *Boolean) TokenLiteral() string {
	return b.Token.Literal
}
func (b *Boolean) String() string {
	return b.Token.Literal
}

type IfExpression struct {
	token       token.Token
	Condition   Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

func (ie *IfExpression) expressionNode() {}
func (ie *IfExpression) TokenLiteral() string {
	return ie.token.Literal
}
func (ie *IfExpression) String() string {
	var out bytes.Buffer

	out.WriteString("if")
	out.WriteString(ie.Condition.String())
	out.WriteString(" ")
	out.WriteString(ie.Consequence.String())

	if ie.Alternative != nil {
		out.WriteString("else")
		out.WriteString(ie.Alternative.String())
	}

	return out.String()
}
<commit_msg>add block statement<commit_after>package ast

import (
	"bytes"
	"monkey/token"
)

type Node interface {
	TokenLiteral() string
	String() string
}

type Statement interface {
	Node
	statementNode()
}

type Expression interface {
	Node
	expressionNode()
}

type Program struct {
	Statements []Statement
}

func (p *Program) TokenLiteral() string {
	if len(p.Statements) > 0 {
		return p.Statements[0].TokenLiteral()
	} else {
		return ""
	}
}
func (p *Program) String() string {
	var out bytes.Buffer

	for _, s := range p.Statements {
		out.WriteString(s.String())
	}

	return out.String()
}

type LetStatement struct {
	Token token.Token
	Name  *Identifier
	Value Expression
}

func (ls *LetStatement) statementNode() {}
func (ls *LetStatement) TokenLiteral() string {
	return ls.Token.Literal
}
func (ls *LetStatement) String() string {
	var out bytes.Buffer

	out.WriteString(ls.TokenLiteral() + " ")
	out.WriteString(ls.Name.String())
	out.WriteString(" = ")

	if ls.Value != nil {
		out.WriteString(ls.Value.String())
	}

	out.WriteString(";")

	return out.String()
}

type Identifier struct {
	Token token.Token
	Value string
}

func (i *Identifier) expressionNode() {}
func (i *Identifier) TokenLiteral() string {
	return i.Token.Literal
}
func (i *Identifier) String() string {
	return i.Value
}

type ReturnStatement struct {
	Token       token.Token
	ReturnValue Expression
}

func (rs *ReturnStatement) statementNode() {}
func (rs *ReturnStatement) TokenLiteral() string {
	return rs.Token.Literal
}
func (rs *ReturnStatement) String() string {
	var out bytes.Buffer

	out.WriteString(rs.TokenLiteral() + " ")

	if rs.ReturnValue != nil {
		out.WriteString(rs.ReturnValue.String())
	}

	out.WriteString(";")

	return out.String()
}

type ExpressionStatement struct {
	Token      token.Token
	Expression Expression
}

func (es *ExpressionStatement) statementNode() {}
func (es *ExpressionStatement) TokenLiteral() string {
	return es.Token.Literal
}
func (es *ExpressionStatement) String() string {
	if es.Expression != nil {
		return es.Expression.String()
	}
	return ""
}

type IntegerLiteral struct {
	Token token.Token
	Value int64
}

func (il *IntegerLiteral) expressionNode() {}
func (il *IntegerLiteral) TokenLiteral() string {
	return il.Token.Literal
}
func (il *IntegerLiteral) String() string {
	return il.Token.Literal
}

type PrefixExpression struct {
	Token    token.Token
	Operator string
	Right    Expression
}

func (pe *PrefixExpression) expressionNode() {}
func (pe *PrefixExpression) TokenLiteral() string {
	return pe.Token.Literal
}
func (pe *PrefixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(pe.Operator)
	out.WriteString(pe.Right.String())
	out.WriteString(")")

	return out.String()
}

type InfixExpression struct {
	Token    token.Token
	Left     Expression
	Operator string
	Right    Expression
}

func (ie *InfixExpression) expressionNode() {}
func (ie *InfixExpression) TokenLiteral() string {
	return ie.Token.Literal
}
func (ie *InfixExpression) String() string {
	var out bytes.Buffer

	out.WriteString("(")
	out.WriteString(ie.Left.String())
	out.WriteString(" " + ie.Operator + " ")
	out.WriteString(ie.Right.String())
	out.WriteString(")")

	return out.String()
}

type Boolean struct {
	Token token.Token
	Value bool
}

func (b *Boolean) expressionNode() {}
func (b *Boolean) TokenLiteral() string {
	return b.Token.Literal
}
func (b *Boolean) String() string {
	return b.Token.Literal
}

type IfExpression struct {
	Token       token.Token
	Condition   Expression
	Consequence *BlockStatement
	Alternative *BlockStatement
}

func (ie *IfExpression) expressionNode() {}
func (ie *IfExpression) TokenLiteral() string {
	return ie.Token.Literal
}
func (ie *IfExpression) String() string {
	var out bytes.Buffer

	out.WriteString("if")
	out.WriteString(ie.Condition.String())
	out.WriteString(" ")
	out.WriteString(ie.Consequence.String())

	if ie.Alternative != nil {
		out.WriteString("else")
		out.WriteString(ie.Alternative.String())
	}

	return out.String()
}

type BlockStatement struct {
	Token      token.Token
	Statements []Statement
}

func (bs *BlockStatement) statementNode() {}
func (bs *BlockStatement) TokenLiteral() string {
	return bs.Token.Literal
}
func (bs *BlockStatement) String() string {
	var out bytes.Buffer

	for _, s := range bs.Statements {
		out.WriteString(s.String())
	}

	return out.String()
}
<|endoftext|>


<commit_before>package utils_test

import (
	"math/rand"
	"testing"

	"github.com/Konstantin8105/GoFea/utils"
)

func TestUniqueInt1(t *testing.T) {
	array := []int{1, 2, 3, 4, 5}
	result := []int{1, 2, 3, 4, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt2(t *testing.T) {
	array := []int{1, 1, 1, 1, 5}
	result := []int{1, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt3(t *testing.T) {
	array := []int{1, 2, 2, 2, 5}
	result := []int{1, 2, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt4(t *testing.T) {
	array := []int{1, 2, 2, 2, 2}
	result := []int{1, 2}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt5(t *testing.T) {
	array := []int{2, 2, 2, 2, 2}
	result := []int{2}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt6(t *testing.T) {
	array := []int{20, 200, 2, 2, 5}
	result := []int{2, 5, 20, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt7(t *testing.T) {
	array := []int{1, 200, 2, 2, 5}
	result := []int{1, 2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt8(t *testing.T) {
	array := []int{200, 200, 2, 2, 5}
	result := []int{2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt9(t *testing.T) {
	array := []int{20, 5, 2, 2, 5}
	result := []int{2, 5, 20}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt10(t *testing.T) {
	array := []int{-1, 200, 2, 2, 5}
	result := []int{-1, 2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func Compare(t *testing.T, a, b []int) {
	if len(a) != len(b) {
		t.Errorf("Wrong")
	}
	for i := range a {
		if a[i] != b[i] {
			t.Errorf("Wrong")
		}
	}
}

// Benchmark
// Result:
// go test -bench=.  -benchtime=10s -cpu 2
// 21 may 2015:
// Benchmark1000-2   	  100000	    147824 ns/op	      32 B/op	       1 allocs/op

func Benchmark1000(b *testing.B) {
	size := 1000
	array := make([]int, size, size)
	for i := 0; i < size; i++ {
		array[i] = rand.Intn(size)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c := make([]int, size, size)
		copy(c, array)
		b.StartTimer()
		utils.UniqueInt(&c)
		b.StopTimer()
	}
	b.ReportAllocs()
}
<commit_msg>simplification<commit_after>package utils_test

import (
	"math/rand"
	"testing"

	"github.com/Konstantin8105/GoFea/utils"
)

func TestUniqueInt1(t *testing.T) {
	array := []int{1, 2, 3, 4, 5}
	result := []int{1, 2, 3, 4, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt2(t *testing.T) {
	array := []int{1, 1, 1, 1, 5}
	result := []int{1, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt3(t *testing.T) {
	array := []int{1, 2, 2, 2, 5}
	result := []int{1, 2, 5}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt4(t *testing.T) {
	array := []int{1, 2, 2, 2, 2}
	result := []int{1, 2}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt5(t *testing.T) {
	array := []int{2, 2, 2, 2, 2}
	result := []int{2}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt6(t *testing.T) {
	array := []int{20, 200, 2, 2, 5}
	result := []int{2, 5, 20, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt7(t *testing.T) {
	array := []int{1, 200, 2, 2, 5}
	result := []int{1, 2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt8(t *testing.T) {
	array := []int{200, 200, 2, 2, 5}
	result := []int{2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt9(t *testing.T) {
	array := []int{20, 5, 2, 2, 5}
	result := []int{2, 5, 20}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func TestUniqueInt10(t *testing.T) {
	array := []int{-1, 200, 2, 2, 5}
	result := []int{-1, 2, 5, 200}

	utils.UniqueInt(&array)
	Compare(t, array, result)
}

func Compare(t *testing.T, a, b []int) {
	if len(a) != len(b) {
		t.Errorf("Wrong")
	}
	for i := range a {
		if a[i] != b[i] {
			t.Errorf("Wrong")
		}
	}
}

// Benchmark
// Result:
// go test -bench=.  -benchtime=10s -cpu 2
// 21 may 2015:
// Benchmark1000-2   	  100000	    147824 ns/op	      32 B/op	       1 allocs/op

func Benchmark1000(b *testing.B) {
	size := 1000
	array := make([]int, size)
	for i := 0; i < size; i++ {
		array[i] = rand.Intn(size)
	}
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		c := make([]int, size)
		copy(c, array)
		b.StartTimer()
		utils.UniqueInt(&c)
		b.StopTimer()
	}
	b.ReportAllocs()
}
<|endoftext|>


<commit_before>/* Copyright 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

    https://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 xpath provides utility functions to parse gnmi xpath.
package xpath

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	idPattern = `[a-zA-Z_][a-zA-Z\d\_\-\.]*`
	// YANG identifiers must follow RFC 6020:
	// https://tools.ietf.org/html/rfc6020#section-6.2.
	idRe = regexp.MustCompile(`^` + idPattern + `$`)
	// The sting representation of List key value pairs must follow the
	// following pattern: [key=value], where key is the List key leaf name,
	// and value is the string representation of key leaf value.
	kvRe = regexp.MustCompile(`^\[` +
		// Key leaf name must be a valid YANG identifier.
		idPattern + `=` +
		// Key leaf value must be a non-empty string, which may contain
		// newlines. Use (?s) to turn on s flag to match newlines.
		`((?s).+)` +
		`\]$`)
)

// splitPath splits a string representation of path into []string. Path
// elements are separated by '/'. String splitting scans from left to right. A
// '[' marks the beginning of a List key value pair substring. A List key value
// pair string ends at the first ']' encountered. Neither an escaped '[', i.e.,
// `\[`, nor an escaped ']', i.e., `\]`, serves as the boundary of a List key
// value pair string.
//
// Within a List key value string, '/', '[' and ']' are treated differently:
//
//	1. A '/' does not act as a separator, and is allowed to be part of a
//	List key leaf value.
//
//	2. A '[' is allowed within a List key value. '[' and `\[` are
//	equivalent within a List key value.
//
//	3. If a ']' needs to be part of a List key value, it must be escaped as
//	'\]'. The first unescaped ']' terminates a List key value string.
//
// Outside of any List key value pair string:
//
//	1. A ']' without a matching '[' does not generate any error in this
//	API. This error is caught later by another API.
//
//	2. A '[' without an closing ']' is treated as an error, because it
//	indicates an incomplete List key leaf value string.
//
// For example, "/a/b/c" is split into []string{"a", "b", "c"}.
// "/a/b[k=eth1/1]/c" is split into []string{"a", "b[k=eth1/1]", "c"}.
// `/a/b/[k=v\]]/c` is split into []string{"a", "b", `[k=v\]]`, "c"}.
// "a/b][k=v]/c" is split into []string{"a", "b][k=v]", "c"}. The invalid List
// name "b]" error will be caught later by another API. "/a/b[k=v/c" generates
// an error because of incomplete List key value pair string.
func splitPath(str string) ([]string, error) {
	var path []string
	str += "/"
	// insideBrackets is true when at least one '[' has been found and no
	// ']' has been found. It is false when a closing ']' has been found.
	insideBrackets := false
	// begin marks the beginning of a path element, which is separated by
	// '/' unclosed between '[' and ']'.
	begin := 0
	// end marks the end of a path element, which is separated by '/'
	// unclosed between '[' and ']'.
	end := 0

	// Split the given string using unescaped '/'.
	for end < len(str) {
		switch str[end] {
		case '/':
			if !insideBrackets {
				// Current '/' is a valid path element
				// separator.
				if end > begin {
					path = append(path, str[begin:end])
				}
				end++
				begin = end
			} else {
				// Current '/' must be part of a List key value
				// string.
				end++
			}
		case '[':
			if (end == 0 || str[end-1] != '\\') && !insideBrackets {
				// Current '[' is unescacped, and is the
				// beginning of List key-value pair(s) string.
				insideBrackets = true
			}
			end++
		case ']':
			if (end == 0 || str[end-1] != '\\') && insideBrackets {
				// Current ']' is unescacped, and is the end of
				// List key-value pair(s) string.
				insideBrackets = false
			}
			end++
		default:
			end++
		}
	}

	if insideBrackets {
		return nil, fmt.Errorf("missing ] in path string: %s", str)
	}
	return path, nil
}

// parseKeyValueString parses a List key-value pair, and returns a
// map[string]string whose key is the List key leaf name and whose value is the
// string representation of List key leaf value. The input path-valur pairs are
// encoded using the following pattern: [k1=v1][k2=v2]..., where k1 and k2 must be
// valid YANG identifiers, v1 and v2 can be any non-empty strings where any ']'
// must be escapced by an '\'. Any malformed key-value pair generates an error.
// For example, given
//	"[k1=v1][k2=v2]",
// this API returns
//	map[string]string{"k1": "v1", "k2": "v2"}.
func parseKeyValueString(str string) (map[string]string, error) {
	keyValuePairs := make(map[string]string)
	// begin marks the beginning of a key-value pair.
	begin := 0
	// end marks the end of a key-value pair.
	end := 0
	// insideBrackets is true when at least one '[' has been found and no
	// ']' has been found. It is false when a closing ']' has been found.
	insideBrackets := false

	for end < len(str) {
		switch str[end] {
		case '[':
			if (end == 0 || str[end-1] != '\\') && !insideBrackets {
				insideBrackets = true
			}
			end++
		case ']':
			if (end == 0 || str[end-1] != '\\') && insideBrackets {
				insideBrackets = false
				keyValue := str[begin : end+1]
				// Key-value pair string must have the
				// following pattern: [k=v], where k is a valid
				// YANG identifier, and v can be any non-empty
				// string.
				if !kvRe.MatchString(keyValue) {
					return nil, fmt.Errorf("malformed List key-value pair string: %s, in: %s", keyValue, str)
				}
				keyValue = keyValue[1 : len(keyValue)-1]
				i := strings.Index(keyValue, "=")
				key, val := keyValue[:i], keyValue[i+1:]
				// Recover escaped '[' and ']'.
				val = strings.Replace(val, `\]`, `]`, -1)
				val = strings.Replace(val, `\[`, `[`, -1)
				keyValuePairs[key] = val
				begin = end + 1
			}
			end++
		default:
			end++
		}
	}

	if begin < end {
		return nil, fmt.Errorf("malformed List key-value pair string: %s", str)
	}

	return keyValuePairs, nil
}

// parseElement parses a split path element, and returns the parsed elements.
// Two types of path elements are supported:
//
// 1. Non-List schema node names which must be valid YANG identifiers. A valid
// schema node name is returned as it is. For example, given "abc", this API
// returns []interface{"abc"}.
//
// 2. List elements following this pattern: list-name[k1=v1], where list-name
// is the substring from the beginning of the input string to the first '[', k1
// is the substring from the letter after '[' to the first '=', and v1 is the
// substring from the letter after '=' to the first unescaped ']'. list-name
// and k1 must be valid YANG identifier, and v1 can be any non-empty string
// where ']' is escaped by '\'. A List element is parsed into two parts: List
// name and List key value pair(s). List key value pairs are saved in a
// map[string]string whose key is List key leaf name and whose value is the
// string representation of List key leaf value. For example, given
//	"list-name[k1=v1]",
// this API returns
//	[]interface{}{"list-name", map[string]string{"k1": "v1"}}.
// Multi-key List elements follow a similar pattern:
//	list-name[k1=v1]...[kN=vN].
func parseElement(elem string) ([]interface{}, error) {
	i := strings.Index(elem, "[")
	if i < 0 {
		if !idRe.MatchString(elem) {
			return nil, fmt.Errorf("invalid node name: %q", elem)
		}
		return []interface{}{elem}, nil
	}

	listName := elem[:i]
	if !idRe.MatchString(listName) {
		return nil, fmt.Errorf("invalid List name: %q, in: %s", listName, elem)
	}
	keyValuePairs, err := parseKeyValueString(elem[i:])
	if err != nil {
		return nil, fmt.Errorf("invalid path element %s: %v", elem, err)
	}
	return []interface{}{listName, keyValuePairs}, nil
}

// ParseStringPath parses a string path and produces a []interface{} of parsed
// path elements. Path elements in a string path are separated by '/'. Each
// path element can either be a schema node name or a List path element. Schema
// node names must be valid YANG identifiers. A List path element is encoded
// using the following pattern: list-name[key1=value1]...[keyN=valueN]. Each
// List path element generates two parsed path elements: List name and a
// map[string]string containing List key-value pairs with value(s) in string
// representation. A '/' within a List key value pair string, i.e., between a
// pair of '[' and ']', does not serve as a path separator, and is allowed to be
// part of a List key leaf value. For example, given a string path:
//	"/a/list-name[k=v/v]/c",
// this API returns:
//	[]interface{}{"a", "list-name", map[string]string{"k": "v/v"}, "c"}.
//
// String path parsing consists of two passes. In the first pass, the input
// string is split into []string using valid separator '/'. An incomplete List
// key value string, i.e, a '[' which starts a List key value string without a
// closing ']', in input string generates an error. In the above example, this
// pass produces:
//	[]string{"a", "list-name[k=v/v]", "c"}.
// In the second pass, each element in split []string is parsed checking syntax
// and pattern correctness. Errors are generated for invalid YANG identifiers,
// malformed List key-value string, etc.. In the above example, the second pass
// produces:
//	[]interface{}{"a", "list-name", map[string]string{"k": "v/v"}, "c"}.
func ParseStringPath(stringPath string) ([]interface{}, error) {
	elems, err := splitPath(stringPath)
	if err != nil {
		return nil, err
	}

	var path []interface{}
	// Check whether each path element is valid. Parse List key value
	// pairs.
	for _, elem := range elems {
		parts, err := parseElement(elem)
		if err != nil {
			return nil, fmt.Errorf("invalid string path %s: %v", stringPath, err)
		}
		path = append(path, parts...)
	}

	return path, nil
}
<commit_msg>allow for SONiC style path root (#304)<commit_after>/* Copyright 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

    https://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 xpath provides utility functions to parse gnmi xpath.
package xpath

import (
	"fmt"
	"regexp"
	"strings"
)

var (
	idPattern = `[a-zA-Z_][a-zA-Z\d\_\-\.]*`
	// YANG identifiers must follow RFC 6020:
	// https://tools.ietf.org/html/rfc6020#section-6.2.
	idRe = regexp.MustCompile(`^(?:`+idPattern+`:)?` + idPattern + `$`)
	// The sting representation of List key value pairs must follow the
	// following pattern: [key=value], where key is the List key leaf name,
	// and value is the string representation of key leaf value.
	kvRe = regexp.MustCompile(`^\[` +
		// Key leaf name must be a valid YANG identifier.
		idPattern + `=` +
		// Key leaf value must be a non-empty string, which may contain
		// newlines. Use (?s) to turn on s flag to match newlines.
		`((?s).+)` +
		`\]$`)
)

// splitPath splits a string representation of path into []string. Path
// elements are separated by '/'. String splitting scans from left to right. A
// '[' marks the beginning of a List key value pair substring. A List key value
// pair string ends at the first ']' encountered. Neither an escaped '[', i.e.,
// `\[`, nor an escaped ']', i.e., `\]`, serves as the boundary of a List key
// value pair string.
//
// Within a List key value string, '/', '[' and ']' are treated differently:
//
//	1. A '/' does not act as a separator, and is allowed to be part of a
//	List key leaf value.
//
//	2. A '[' is allowed within a List key value. '[' and `\[` are
//	equivalent within a List key value.
//
//	3. If a ']' needs to be part of a List key value, it must be escaped as
//	'\]'. The first unescaped ']' terminates a List key value string.
//
// Outside of any List key value pair string:
//
//	1. A ']' without a matching '[' does not generate any error in this
//	API. This error is caught later by another API.
//
//	2. A '[' without an closing ']' is treated as an error, because it
//	indicates an incomplete List key leaf value string.
//
// For example, "/a/b/c" is split into []string{"a", "b", "c"}.
// "/a/b[k=eth1/1]/c" is split into []string{"a", "b[k=eth1/1]", "c"}.
// `/a/b/[k=v\]]/c` is split into []string{"a", "b", `[k=v\]]`, "c"}.
// "a/b][k=v]/c" is split into []string{"a", "b][k=v]", "c"}. The invalid List
// name "b]" error will be caught later by another API. "/a/b[k=v/c" generates
// an error because of incomplete List key value pair string.
func splitPath(str string) ([]string, error) {
	var path []string
	str += "/"
	// insideBrackets is true when at least one '[' has been found and no
	// ']' has been found. It is false when a closing ']' has been found.
	insideBrackets := false
	// begin marks the beginning of a path element, which is separated by
	// '/' unclosed between '[' and ']'.
	begin := 0
	// end marks the end of a path element, which is separated by '/'
	// unclosed between '[' and ']'.
	end := 0

	// Split the given string using unescaped '/'.
	for end < len(str) {
		switch str[end] {
		case '/':
			if !insideBrackets {
				// Current '/' is a valid path element
				// separator.
				if end > begin {
					path = append(path, str[begin:end])
				}
				end++
				begin = end
			} else {
				// Current '/' must be part of a List key value
				// string.
				end++
			}
		case '[':
			if (end == 0 || str[end-1] != '\\') && !insideBrackets {
				// Current '[' is unescacped, and is the
				// beginning of List key-value pair(s) string.
				insideBrackets = true
			}
			end++
		case ']':
			if (end == 0 || str[end-1] != '\\') && insideBrackets {
				// Current ']' is unescacped, and is the end of
				// List key-value pair(s) string.
				insideBrackets = false
			}
			end++
		default:
			end++
		}
	}

	if insideBrackets {
		return nil, fmt.Errorf("missing ] in path string: %s", str)
	}
	return path, nil
}

// parseKeyValueString parses a List key-value pair, and returns a
// map[string]string whose key is the List key leaf name and whose value is the
// string representation of List key leaf value. The input path-valur pairs are
// encoded using the following pattern: [k1=v1][k2=v2]..., where k1 and k2 must be
// valid YANG identifiers, v1 and v2 can be any non-empty strings where any ']'
// must be escapced by an '\'. Any malformed key-value pair generates an error.
// For example, given
//	"[k1=v1][k2=v2]",
// this API returns
//	map[string]string{"k1": "v1", "k2": "v2"}.
func parseKeyValueString(str string) (map[string]string, error) {
	keyValuePairs := make(map[string]string)
	// begin marks the beginning of a key-value pair.
	begin := 0
	// end marks the end of a key-value pair.
	end := 0
	// insideBrackets is true when at least one '[' has been found and no
	// ']' has been found. It is false when a closing ']' has been found.
	insideBrackets := false

	for end < len(str) {
		switch str[end] {
		case '[':
			if (end == 0 || str[end-1] != '\\') && !insideBrackets {
				insideBrackets = true
			}
			end++
		case ']':
			if (end == 0 || str[end-1] != '\\') && insideBrackets {
				insideBrackets = false
				keyValue := str[begin : end+1]
				// Key-value pair string must have the
				// following pattern: [k=v], where k is a valid
				// YANG identifier, and v can be any non-empty
				// string.
				if !kvRe.MatchString(keyValue) {
					return nil, fmt.Errorf("malformed List key-value pair string: %s, in: %s", keyValue, str)
				}
				keyValue = keyValue[1 : len(keyValue)-1]
				i := strings.Index(keyValue, "=")
				key, val := keyValue[:i], keyValue[i+1:]
				// Recover escaped '[' and ']'.
				val = strings.Replace(val, `\]`, `]`, -1)
				val = strings.Replace(val, `\[`, `[`, -1)
				keyValuePairs[key] = val
				begin = end + 1
			}
			end++
		default:
			end++
		}
	}

	if begin < end {
		return nil, fmt.Errorf("malformed List key-value pair string: %s", str)
	}

	return keyValuePairs, nil
}

// parseElement parses a split path element, and returns the parsed elements.
// Two types of path elements are supported:
//
// 1. Non-List schema node names which must be valid YANG identifiers. A valid
// schema node name is returned as it is. For example, given "abc", this API
// returns []interface{"abc"}.
//
// 2. List elements following this pattern: list-name[k1=v1], where list-name
// is the substring from the beginning of the input string to the first '[', k1
// is the substring from the letter after '[' to the first '=', and v1 is the
// substring from the letter after '=' to the first unescaped ']'. list-name
// and k1 must be valid YANG identifier, and v1 can be any non-empty string
// where ']' is escaped by '\'. A List element is parsed into two parts: List
// name and List key value pair(s). List key value pairs are saved in a
// map[string]string whose key is List key leaf name and whose value is the
// string representation of List key leaf value. For example, given
//	"list-name[k1=v1]",
// this API returns
//	[]interface{}{"list-name", map[string]string{"k1": "v1"}}.
// Multi-key List elements follow a similar pattern:
//	list-name[k1=v1]...[kN=vN].
func parseElement(elem string) ([]interface{}, error) {
	i := strings.Index(elem, "[")
	if i < 0 {
		if !idRe.MatchString(elem) {
			return nil, fmt.Errorf("invalid node name: %q", elem)
		}
		return []interface{}{elem}, nil
	}

	listName := elem[:i]
	if !idRe.MatchString(listName) {
		return nil, fmt.Errorf("invalid List name: %q, in: %s", listName, elem)
	}
	keyValuePairs, err := parseKeyValueString(elem[i:])
	if err != nil {
		return nil, fmt.Errorf("invalid path element %s: %v", elem, err)
	}
	return []interface{}{listName, keyValuePairs}, nil
}

// ParseStringPath parses a string path and produces a []interface{} of parsed
// path elements. Path elements in a string path are separated by '/'. Each
// path element can either be a schema node name or a List path element. Schema
// node names must be valid YANG identifiers. A List path element is encoded
// using the following pattern: list-name[key1=value1]...[keyN=valueN]. Each
// List path element generates two parsed path elements: List name and a
// map[string]string containing List key-value pairs with value(s) in string
// representation. A '/' within a List key value pair string, i.e., between a
// pair of '[' and ']', does not serve as a path separator, and is allowed to be
// part of a List key leaf value. For example, given a string path:
//	"/a/list-name[k=v/v]/c",
// this API returns:
//	[]interface{}{"a", "list-name", map[string]string{"k": "v/v"}, "c"}.
//
// String path parsing consists of two passes. In the first pass, the input
// string is split into []string using valid separator '/'. An incomplete List
// key value string, i.e, a '[' which starts a List key value string without a
// closing ']', in input string generates an error. In the above example, this
// pass produces:
//	[]string{"a", "list-name[k=v/v]", "c"}.
// In the second pass, each element in split []string is parsed checking syntax
// and pattern correctness. Errors are generated for invalid YANG identifiers,
// malformed List key-value string, etc.. In the above example, the second pass
// produces:
//	[]interface{}{"a", "list-name", map[string]string{"k": "v/v"}, "c"}.
func ParseStringPath(stringPath string) ([]interface{}, error) {
	elems, err := splitPath(stringPath)
	if err != nil {
		return nil, err
	}

	var path []interface{}
	// Check whether each path element is valid. Parse List key value
	// pairs.
	for _, elem := range elems {
		parts, err := parseElement(elem)
		if err != nil {
			return nil, fmt.Errorf("invalid string path %s: %v", stringPath, err)
		}
		path = append(path, parts...)
	}

	return path, nil
}
<|endoftext|>


<commit_before>package context

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/http"
	"sync"
)

// ResponseWriter interface is used by the context to serve an HTTP handler to
// construct an HTTP response.
//
// Note: Only this ResponseWriter is an interface in order to be able
// for developers to change the response writer of the Context via `context.ResetResponseWriter`.
// The rest of the response writers implementations (ResponseRecorder & CompressResponseWriter)
// are coupled to the internal ResponseWriter implementation(*responseWriter).
//
// A ResponseWriter may not be used after the Handler
// has returned.
type ResponseWriter interface {
	http.ResponseWriter

	// Naive returns the simple, underline and original http.ResponseWriter
	// that backends this response writer.
	Naive() http.ResponseWriter
	// SetWriter sets the underline http.ResponseWriter
	// that this responseWriter should write on.
	SetWriter(underline http.ResponseWriter)
	// BeginResponse receives an http.ResponseWriter
	// and initialize or reset the response writer's field's values.
	BeginResponse(http.ResponseWriter)
	// EndResponse is the last function which is called right before the server sent the final response.
	//
	// Here is the place which we can make the last checks or do a cleanup.
	EndResponse()

	// IsHijacked reports whether this response writer's connection is hijacked.
	IsHijacked() bool

	// StatusCode returns the status code header value.
	StatusCode() int

	// Written should returns the total length of bytes that were being written to the client.
	// In addition iris provides some variables to help low-level actions:
	// NoWritten, means that nothing were written yet and the response writer is still live.
	// StatusCodeWritten, means that status code was written but no other bytes are written to the client, response writer may closed.
	// > 0 means that the reply was written and it's the total number of bytes were written.
	Written() int

	// SetWritten sets manually a value for written, it can be
	// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
	SetWritten(int)

	// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client.
	SetBeforeFlush(cb func())
	// GetBeforeFlush returns (not execute) the before flush callback, or nil if not set by SetBeforeFlush.
	GetBeforeFlush() func()
	// FlushResponse should be called only once before EndResponse.
	// it tries to send the status code if not sent already
	// and calls the  before flush callback, if any.
	//
	// FlushResponse can be called before EndResponse, but it should
	// be the last call of this response writer.
	FlushResponse()

	// clone returns a clone of this response writer
	// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.
	Clone() ResponseWriter

	// CopyTo writes a response writer (temp: status code, headers and body) to another response writer
	CopyTo(ResponseWriter)

	// Flusher indicates if `Flush` is supported by the client.
	//
	// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
	// support Flusher, but ResponseWriter wrappers may not. Handlers
	// should always test for this ability at runtime.
	//
	// Note that even for ResponseWriters that support Flush,
	// if the client is connected through an HTTP proxy,
	// the buffered data may not reach the client until the response
	// completes.
	Flusher() (http.Flusher, bool)
	// Flush sends any buffered data to the client.
	Flush() // required by compress writer.
}

// ResponseWriterBodyReseter can be implemented by
// response writers that supports response body overriding
// (e.g. recorder and compressed).
type ResponseWriterBodyReseter interface {
	// ResetBody should reset the body and reports back if it could reset successfully.
	ResetBody()
}

// ResponseWriterDisabler can be implemented
// by response writers that can be disabled and restored to their previous state
// (e.g. compressed).
type ResponseWriterDisabler interface {
	// Disable should disable this type of response writer and fallback to the default one.
	Disable()
}

// ResponseWriterReseter can be implemented
// by response writers that can clear the whole response
// so a new handler can write into this from the beginning.
// E.g. recorder, compressed (full) and common writer (status code and headers).
type ResponseWriterReseter interface {
	// Reset should reset the whole response and reports
	// whether it could reset successfully.
	Reset() bool
}

// ResponseWriterWriteTo can be implemented
// by response writers that needs a special
// encoding before writing to their buffers.
// E.g. a custom recorder that wraps a custom compressed one.
//
// Not used by the framework itself.
type ResponseWriterWriteTo interface {
	WriteTo(dest io.Writer, p []byte)
}

//  +------------------------------------------------------------+
//  | Response Writer Implementation                             |
//  +------------------------------------------------------------+

var rpool = sync.Pool{New: func() interface{} { return &responseWriter{} }}

// AcquireResponseWriter returns a new *ResponseWriter from the pool.
// Releasing is done automatically when request and response is done.
func AcquireResponseWriter() ResponseWriter {
	return rpool.Get().(*responseWriter)
}

func releaseResponseWriter(w ResponseWriter) {
	rpool.Put(w)
}

// ResponseWriter is the basic response writer,
// it writes directly to the underline http.ResponseWriter
type responseWriter struct {
	http.ResponseWriter

	statusCode int // the saved status code which will be used from the cache service
	// statusCodeSent bool // reply header has been (logically) written | no needed any more as we have a variable to catch total len of written bytes
	written int // the total size of bytes were written
	// yes only one callback, we need simplicity here because on FireStatusCode the beforeFlush events should NOT be cleared
	// but the response is cleared.
	// Sometimes is useful to keep the event,
	// so we keep one func only and let the user decide when he/she wants to override it with an empty func before the FireStatusCode (context's behavior)
	beforeFlush func()
}

var _ ResponseWriter = (*responseWriter)(nil)

const (
	defaultStatusCode = http.StatusOK
	// NoWritten !=-1 => when nothing written before
	NoWritten = -1
	// StatusCodeWritten != 0 =>  when only status code written
	StatusCodeWritten = 0
)

// Naive returns the simple, underline and original http.ResponseWriter
// that backends this response writer.
func (w *responseWriter) Naive() http.ResponseWriter {
	return w.ResponseWriter
}

// BeginResponse receives an http.ResponseWriter
// and initialize or reset the response writer's field's values.
func (w *responseWriter) BeginResponse(underline http.ResponseWriter) {
	w.beforeFlush = nil
	w.written = NoWritten
	w.statusCode = defaultStatusCode
	w.SetWriter(underline)
}

// SetWriter sets the underline http.ResponseWriter
// that this responseWriter should write on.
func (w *responseWriter) SetWriter(underline http.ResponseWriter) {
	w.ResponseWriter = underline
}

// EndResponse is the last function which is called right before the server sent the final response.
//
// Here is the place which we can make the last checks or do a cleanup.
func (w *responseWriter) EndResponse() {
	releaseResponseWriter(w)
}

// Reset clears headers, sets the status code to 200
// and clears the cached body.
//
// Implements the `ResponseWriterReseter`.
func (w *responseWriter) Reset() bool {
	if w.written > 0 {
		return false // if already written we can't reset this type of response writer.
	}

	h := w.Header()
	for k := range h {
		h[k] = nil
	}

	w.written = NoWritten
	w.statusCode = defaultStatusCode
	return true
}

// SetWritten sets manually a value for written, it can be
// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
func (w *responseWriter) SetWritten(n int) {
	if n >= NoWritten && n <= StatusCodeWritten {
		w.written = n
	}
}

// Written should returns the total length of bytes that were being written to the client.
// In addition iris provides some variables to help low-level actions:
// NoWritten, means that nothing were written yet and the response writer is still live.
// StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed.
// > 0 means that the reply was written and it's the total number of bytes were written.
func (w *responseWriter) Written() int {
	return w.written
}

// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
func (w *responseWriter) WriteHeader(statusCode int) {
	w.statusCode = statusCode
}

func (w *responseWriter) tryWriteHeader() {
	if w.written == NoWritten { // before write, once.
		w.written = StatusCodeWritten
		w.ResponseWriter.WriteHeader(w.statusCode)
	}
}

// IsHijacked reports whether this response writer's connection is hijacked.
func (w *responseWriter) IsHijacked() bool {
	// Note:
	// A zero-byte `ResponseWriter.Write` on a hijacked connection will
	// return `http.ErrHijacked` without any other side effects.
	_, err := w.ResponseWriter.Write(nil)
	return err == http.ErrHijacked
}

// Write writes to the client
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType.
//
// Depending on the HTTP protocol version and the client, calling
// Write or WriteHeader may prevent future reads on the
// Request.Body. For HTTP/1.x requests, handlers should read any
// needed request body data before writing the response. Once the
// headers have been flushed (due to either an explicit Flusher.Flush
// call or writing enough data to trigger a flush), the request body
// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
// handlers to continue to read the request body while concurrently
// writing the response. However, such behavior may not be supported
// by all HTTP/2 clients. Handlers should read before writing if
// possible to maximize compatibility.
func (w *responseWriter) Write(contents []byte) (int, error) {
	w.tryWriteHeader()
	n, err := w.ResponseWriter.Write(contents)
	w.written += n
	return n, err
}

// StatusCode returns the status code header value
func (w *responseWriter) StatusCode() int {
	return w.statusCode
}

func (w *responseWriter) GetBeforeFlush() func() {
	return w.beforeFlush
}

// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client
func (w *responseWriter) SetBeforeFlush(cb func()) {
	w.beforeFlush = cb
}

func (w *responseWriter) FlushResponse() {
	if w.beforeFlush != nil {
		w.beforeFlush()
	}

	w.tryWriteHeader()
}

// Clone returns a clone of this response writer
// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.
func (w *responseWriter) Clone() ResponseWriter {
	wc := &responseWriter{}
	wc.ResponseWriter = w.ResponseWriter
	wc.statusCode = w.statusCode
	wc.beforeFlush = w.beforeFlush
	wc.written = w.written
	return wc
}

// CopyTo writes a response writer (temp: status code, headers and body) to another response writer.
func (w *responseWriter) CopyTo(to ResponseWriter) {
	// set the status code, failure status code are first class
	if w.statusCode >= 400 {
		to.WriteHeader(w.statusCode)
	}

	// append the headers
	if w.Header() != nil {
		for k, values := range w.Header() {
			for _, v := range values {
				if to.Header().Get(v) == "" {
					to.Header().Add(k, v)
				}
			}
		}
	}
	// the body is not copied, this writer doesn't support recording
}

// ErrHijackNotSupported is returned by the Hijack method to
// indicate that Hijack feature is not available.
var ErrHijackNotSupported = errors.New("hijack is not supported by this ResponseWriter")

// Hijack lets the caller take over the connection.
// After a call to Hijack(), the HTTP server library
// will not do anything else with the connection.
//
// It becomes the caller's responsibility to manage
// and close the connection.
//
// The returned net.Conn may have read or write deadlines
// already set, depending on the configuration of the
// Server. It is the caller's responsibility to set
// or clear those deadlines as needed.
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if h, isHijacker := w.ResponseWriter.(http.Hijacker); isHijacker {
		w.written = StatusCodeWritten
		return h.Hijack()
	}

	return nil, nil, ErrHijackNotSupported
}

// Flusher indicates if `Flush` is supported by the client.
//
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
// support Flusher, but ResponseWriter wrappers may not. Handlers
// should always test for this ability at runtime.
//
// Note that even for ResponseWriters that support Flush,
// if the client is connected through an HTTP proxy,
// the buffered data may not reach the client until the response
// completes.
func (w *responseWriter) Flusher() (http.Flusher, bool) {
	flusher, canFlush := w.ResponseWriter.(http.Flusher)
	return flusher, canFlush
}

// Flush sends any buffered data to the client.
func (w *responseWriter) Flush() {
	if flusher, ok := w.Flusher(); ok {
		flusher.Flush()
	}
}
<commit_msg>minor<commit_after>package context

import (
	"bufio"
	"errors"
	"io"
	"net"
	"net/http"
	"sync"
)

// ResponseWriter interface is used by the context to serve an HTTP handler to
// construct an HTTP response.
//
// Note: Only this ResponseWriter is an interface in order to be able
// for developers to change the response writer of the Context via `context.ResetResponseWriter`.
// The rest of the response writers implementations (ResponseRecorder & CompressResponseWriter)
// are coupled to the internal ResponseWriter implementation(*responseWriter).
//
// A ResponseWriter may not be used after the Handler
// has returned.
type ResponseWriter interface {
	http.ResponseWriter

	// Naive returns the simple, underline and original http.ResponseWriter
	// that backends this response writer.
	Naive() http.ResponseWriter
	// SetWriter sets the underline http.ResponseWriter
	// that this responseWriter should write on.
	SetWriter(underline http.ResponseWriter)
	// BeginResponse receives an http.ResponseWriter
	// and initialize or reset the response writer's field's values.
	BeginResponse(http.ResponseWriter)
	// EndResponse is the last function which is called right before the server sent the final response.
	//
	// Here is the place which we can make the last checks or do a cleanup.
	EndResponse()

	// IsHijacked reports whether this response writer's connection is hijacked.
	IsHijacked() bool

	// StatusCode returns the status code header value.
	StatusCode() int

	// Written should returns the total length of bytes that were being written to the client.
	// In addition iris provides some variables to help low-level actions:
	// NoWritten, means that nothing were written yet and the response writer is still live.
	// StatusCodeWritten, means that status code was written but no other bytes are written to the client, response writer may closed.
	// > 0 means that the reply was written and it's the total number of bytes were written.
	Written() int

	// SetWritten sets manually a value for written, it can be
	// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
	SetWritten(int)

	// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client.
	SetBeforeFlush(cb func())
	// GetBeforeFlush returns (not execute) the before flush callback, or nil if not set by SetBeforeFlush.
	GetBeforeFlush() func()
	// FlushResponse should be called only once before EndResponse.
	// it tries to send the status code if not sent already
	// and calls the  before flush callback, if any.
	//
	// FlushResponse can be called before EndResponse, but it should
	// be the last call of this response writer.
	FlushResponse()

	// clone returns a clone of this response writer
	// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.
	Clone() ResponseWriter

	// CopyTo writes a response writer (temp: status code, headers and body) to another response writer
	CopyTo(ResponseWriter)

	// Flusher indicates if `Flush` is supported by the client.
	//
	// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
	// support Flusher, but ResponseWriter wrappers may not. Handlers
	// should always test for this ability at runtime.
	//
	// Note that even for ResponseWriters that support Flush,
	// if the client is connected through an HTTP proxy,
	// the buffered data may not reach the client until the response
	// completes.
	Flusher() (http.Flusher, bool)
	// Flush sends any buffered data to the client.
	Flush() // required by compress writer.
}

// ResponseWriterBodyReseter can be implemented by
// response writers that supports response body overriding
// (e.g. recorder and compressed).
type ResponseWriterBodyReseter interface {
	// ResetBody should reset the body and reports back if it could reset successfully.
	ResetBody()
}

// ResponseWriterDisabler can be implemented
// by response writers that can be disabled and restored to their previous state
// (e.g. compressed).
type ResponseWriterDisabler interface {
	// Disable should disable this type of response writer and fallback to the default one.
	Disable()
}

// ResponseWriterReseter can be implemented
// by response writers that can clear the whole response
// so a new handler can write into this from the beginning.
// E.g. recorder, compressed (full) and common writer (status code and headers).
type ResponseWriterReseter interface {
	// Reset should reset the whole response and reports
	// whether it could reset successfully.
	Reset() bool
}

// ResponseWriterWriteTo can be implemented
// by response writers that needs a special
// encoding before writing to their buffers.
// E.g. a custom recorder that wraps a custom compressed one.
//
// Not used by the framework itself.
type ResponseWriterWriteTo interface {
	WriteTo(dest io.Writer, p []byte)
}

//  +------------------------------------------------------------+
//  | Response Writer Implementation                             |
//  +------------------------------------------------------------+

var rpool = sync.Pool{New: func() interface{} { return &responseWriter{} }}

// AcquireResponseWriter returns a new *ResponseWriter from the pool.
// Releasing is done automatically when request and response is done.
func AcquireResponseWriter() ResponseWriter {
	return rpool.Get().(*responseWriter)
}

func releaseResponseWriter(w ResponseWriter) {
	rpool.Put(w)
}

// ResponseWriter is the basic response writer,
// it writes directly to the underline http.ResponseWriter
type responseWriter struct {
	http.ResponseWriter

	statusCode int // the saved status code which will be used from the cache service
	// statusCodeSent bool // reply header has been (logically) written | no needed any more as we have a variable to catch total len of written bytes
	written int // the total size of bytes were written
	// yes only one callback, we need simplicity here because on FireStatusCode the beforeFlush events should NOT be cleared
	// but the response is cleared.
	// Sometimes is useful to keep the event,
	// so we keep one func only and let the user decide when he/she wants to override it with an empty func before the FireStatusCode (context's behavior)
	beforeFlush func()
}

var _ ResponseWriter = (*responseWriter)(nil)

const (
	defaultStatusCode = http.StatusOK
	// NoWritten !=-1 => when nothing written before
	NoWritten = -1
	// StatusCodeWritten != 0 =>  when only status code written
	StatusCodeWritten = 0
)

// Naive returns the simple, underline and original http.ResponseWriter
// that backends this response writer.
func (w *responseWriter) Naive() http.ResponseWriter {
	return w.ResponseWriter
}

// BeginResponse receives an http.ResponseWriter
// and initialize or reset the response writer's field's values.
func (w *responseWriter) BeginResponse(underline http.ResponseWriter) {
	w.beforeFlush = nil
	w.written = NoWritten
	w.statusCode = defaultStatusCode
	w.SetWriter(underline)
}

// SetWriter sets the underline http.ResponseWriter
// that this responseWriter should write on.
func (w *responseWriter) SetWriter(underline http.ResponseWriter) {
	w.ResponseWriter = underline
}

// EndResponse is the last function which is called right before the server sent the final response.
//
// Here is the place which we can make the last checks or do a cleanup.
func (w *responseWriter) EndResponse() {
	releaseResponseWriter(w)
}

// Reset clears headers, sets the status code to 200
// and clears the cached body.
//
// Implements the `ResponseWriterReseter`.
func (w *responseWriter) Reset() bool {
	if w.written > 0 {
		return false // if already written we can't reset this type of response writer.
	}

	h := w.Header()
	for k := range h {
		h[k] = nil
	}

	w.written = NoWritten
	w.statusCode = defaultStatusCode
	return true
}

// SetWritten sets manually a value for written, it can be
// NoWritten(-1) or StatusCodeWritten(0), > 0 means body length which is useless here.
func (w *responseWriter) SetWritten(n int) {
	if n >= NoWritten && n <= StatusCodeWritten {
		w.written = n
	}
}

// Written should returns the total length of bytes that were being written to the client.
// In addition iris provides some variables to help low-level actions:
// NoWritten, means that nothing were written yet and the response writer is still live.
// StatusCodeWritten, means that status code were written but no other bytes are written to the client, response writer may closed.
// > 0 means that the reply was written and it's the total number of bytes were written.
func (w *responseWriter) Written() int {
	return w.written
}

// WriteHeader sends an HTTP response header with status code.
// If WriteHeader is not called explicitly, the first call to Write
// will trigger an implicit WriteHeader(http.StatusOK).
// Thus explicit calls to WriteHeader are mainly used to
// send error codes.
func (w *responseWriter) WriteHeader(statusCode int) {
	w.statusCode = statusCode
}

func (w *responseWriter) tryWriteHeader() {
	if w.written == NoWritten { // before write, once.
		w.written = StatusCodeWritten
		w.ResponseWriter.WriteHeader(w.statusCode)
	}
}

// IsHijacked reports whether this response writer's connection is hijacked.
func (w *responseWriter) IsHijacked() bool {
	// Note:
	// A zero-byte `ResponseWriter.Write` on a hijacked connection will
	// return `http.ErrHijacked` without any other side effects.
	_, err := w.ResponseWriter.Write(nil)
	return err == http.ErrHijacked
}

// Write writes to the client
// If WriteHeader has not yet been called, Write calls
// WriteHeader(http.StatusOK) before writing the data. If the Header
// does not contain a Content-Type line, Write adds a Content-Type set
// to the result of passing the initial 512 bytes of written data to
// DetectContentType.
//
// Depending on the HTTP protocol version and the client, calling
// Write or WriteHeader may prevent future reads on the
// Request.Body. For HTTP/1.x requests, handlers should read any
// needed request body data before writing the response. Once the
// headers have been flushed (due to either an explicit Flusher.Flush
// call or writing enough data to trigger a flush), the request body
// may be unavailable. For HTTP/2 requests, the Go HTTP server permits
// handlers to continue to read the request body while concurrently
// writing the response. However, such behavior may not be supported
// by all HTTP/2 clients. Handlers should read before writing if
// possible to maximize compatibility.
func (w *responseWriter) Write(contents []byte) (int, error) {
	w.tryWriteHeader()
	n, err := w.ResponseWriter.Write(contents)
	w.written += n
	return n, err
}

// StatusCode returns the status code header value
func (w *responseWriter) StatusCode() int {
	return w.statusCode
}

func (w *responseWriter) GetBeforeFlush() func() {
	return w.beforeFlush
}

// SetBeforeFlush registers the unique callback which called exactly before the response is flushed to the client
func (w *responseWriter) SetBeforeFlush(cb func()) {
	w.beforeFlush = cb
}

func (w *responseWriter) FlushResponse() {
	if w.beforeFlush != nil {
		w.beforeFlush()
	}

	w.tryWriteHeader()
}

// Clone returns a clone of this response writer
// it copies the header, status code, headers and the beforeFlush finally  returns a new ResponseRecorder.
func (w *responseWriter) Clone() ResponseWriter {
	wc := &responseWriter{}
	wc.ResponseWriter = w.ResponseWriter
	wc.statusCode = w.statusCode
	wc.beforeFlush = w.beforeFlush
	wc.written = w.written
	return wc
}

// CopyTo writes a response writer (temp: status code, headers and body) to another response writer.
func (w *responseWriter) CopyTo(to ResponseWriter) {
	// set the status code, failure status code are first class
	if w.statusCode >= 400 {
		to.WriteHeader(w.statusCode)
	}

	// append the headers
	if w.Header() != nil {
		for k, values := range w.Header() {
			for _, v := range values {
				if to.Header().Get(v) == "" {
					to.Header().Add(k, v)
				}
			}
		}
	}
	// the body is not copied, this writer doesn't support recording
}

// ErrHijackNotSupported is returned by the Hijack method to
// indicate that Hijack feature is not available.
var ErrHijackNotSupported = errors.New("hijack is not supported by this ResponseWriter")

// Hijack lets the caller take over the connection.
// After a call to Hijack(), the HTTP server library
// will not do anything else with the connection.
//
// It becomes the caller's responsibility to manage
// and close the connection.
//
// The returned net.Conn may have read or write deadlines
// already set, depending on the configuration of the
// Server. It is the caller's responsibility to set
// or clear those deadlines as needed.
func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
	if h, isHijacker := w.ResponseWriter.(http.Hijacker); isHijacker {
		w.written = StatusCodeWritten
		return h.Hijack()
	}

	return nil, nil, ErrHijackNotSupported
}

// Flusher indicates if `Flush` is supported by the client.
//
// The default HTTP/1.x and HTTP/2 ResponseWriter implementations
// support Flusher, but ResponseWriter wrappers may not. Handlers
// should always test for this ability at runtime.
//
// Note that even for ResponseWriters that support Flush,
// if the client is connected through an HTTP proxy,
// the buffered data may not reach the client until the response
// completes.
func (w *responseWriter) Flusher() (http.Flusher, bool) {
	flusher, canFlush := w.ResponseWriter.(http.Flusher)
	return flusher, canFlush
}

// Flush sends any buffered data to the client.
func (w *responseWriter) Flush() {
	if flusher, ok := w.Flusher(); ok {
		// Flow: WriteHeader -> Flush -> Write -> Write -> Write....
		w.tryWriteHeader()

		flusher.Flush()
	}
}
<|endoftext|>


<commit_before>package constants

const (
	HOST = "0.0.0.0" // All host names
	PORT = "9000"

	LENGTH_HEADER_SIZE = 3 // Measured in bytes

	// Packet constants for plug communication
	HEAD   = "@H\n"
	ACK    = "\x06\n"
	OKAY   = "@K\n"
	CONFIG = "@SLITIsEP00000000500000C00001\n"
	//"@SLITVsEP00000000500000C00001\n"
	//"@SLITWsEP00000000500000C00001\n"
	//"@SLITTsEP00000000500000C00001\n"

	READ_TIME_LIMIT = 5 * 60 // Measured in seconds
	DB_TIME_LIMIT   = 10     // Measured in seconds

	HEADER_REGEX = "^(?:THS)(\\d+)(?:t)(\\d+)(?X)$"

	// Database
	DB_SOCKET   = "/var/run/postgresql"
	DB_PORT     = 5432
	DB_USER     = "landingzone"
	DB_NAME     = "seads"
	DB_PASSWORD = ""
)
<commit_msg>Fixed broken regex.<commit_after>package constants

const (
	HOST = "0.0.0.0" // All host names
	PORT = "9000"

	LENGTH_HEADER_SIZE = 3 // Measured in bytes

	// Packet constants for plug communication
	HEAD   = "@H\n"
	ACK    = "\x06\n"
	OKAY   = "@K\n"
	CONFIG = "@SLITIsEP00000000500000C00001\n"
	//"@SLITVsEP00000000500000C00001\n"
	//"@SLITWsEP00000000500000C00001\n"
	//"@SLITTsEP00000000500000C00001\n"

	READ_TIME_LIMIT = 5 * 60 // Measured in seconds
	DB_TIME_LIMIT   = 10     // Measured in seconds

	HEADER_REGEX = "^(?:THS)(\\d+)(?:t)(\\d+)(?:X)$"

	// Database
	DB_SOCKET   = "/var/run/postgresql"
	DB_PORT     = 5432
	DB_USER     = "landingzone"
	DB_NAME     = "seads"
	DB_PASSWORD = ""
)
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/draw"
	"image/png"
	"io"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8
	HelmWidth  = 8
	HelmHeight = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

func (skin *mcSkin) GetHead() error {
	img, err := cropHead(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = img
	return nil
}

func (skin *mcSkin) GetHelm() error {
	// check if helm is solid colour - if so, it counts as transparent
	isSolidColour := true
	baseColour := skin.Image.At(HelmX, HelmY)
	for checkX := HelmX; checkX < HelmX+HelmWidth; checkX++ {
		for checkY := HelmY; checkY < HelmY+HelmHeight; checkY++ {
			checkColour := skin.Image.At(checkX, checkY)
			if checkColour != baseColour {
				isSolidColour = false
				break
			}
		}
	}

	headImg, err := cropHead(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = headImg

	if isSolidColour {
		return nil
	}

	headImgRGBA := headImg.(*image.RGBA)

	helmImg, err := cropImage(skin.Image, image.Rect(HelmX, HelmY, HelmX+HelmWidth, HelmY+HelmHeight))
	if err != nil {
		return err
	}

	sr := helmImg.Bounds()
	draw.Draw(headImgRGBA, sr, helmImg, sr.Min, draw.Over)
	return nil
}

func (skin *mcSkin) GetBody() error {
	// Check if 1.8 skin (the max Y bound should be 64)
	render18Skin := true
	bounds := skin.Image.Bounds()
	if bounds.Max.Y != 64 {
		render18Skin = false
	}

	err := skin.GetHelm()
	if err != nil {
		return err
	}
	helmImg := skin.Processed

	torsoImg, err := cropImage(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	if err != nil {
		return err
	}

	raImg, err := cropImage(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+RaHeight))
	if err != nil {
		return err
	}

	rlImg, err := cropImage(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))
	if err != nil {
		return err
	}

	var laImg, llImg image.Image

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	if render18Skin {
		laImg, err = cropImage(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+LaHeight))
		if err != nil {
			return err
		}

		llImg, err = cropImage(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
		if err != nil {
			return err
		}
	} else {
		laImg = imaging.FlipH(raImg)

		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on
	bodyImg := image.NewRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight+LlHeight))
	// Helm
	draw.Draw(bodyImg, image.Rect(LaWidth, 0, LaWidth+HelmWidth, HelmHeight), helmImg, image.Pt(0, 0), draw.Src)
	// Torso
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight, LaWidth+TorsoWidth, HelmHeight+TorsoHeight), torsoImg, image.Pt(0, 0), draw.Src)
	// Left Arm
	draw.Draw(bodyImg, image.Rect(0, HelmHeight, LaWidth, HelmHeight+LaHeight), laImg, image.Pt(0, 0), draw.Src)
	// Right Arm
	draw.Draw(bodyImg, image.Rect(LaWidth+TorsoWidth, HelmHeight, LaWidth+TorsoWidth+RaWidth, HelmHeight+RaHeight), raImg, image.Pt(0, 0), draw.Src)
	// Left Leg
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth, HelmHeight+TorsoHeight+LlHeight), llImg, image.Pt(0, 0), draw.Src)
	// Right Leg
	draw.Draw(bodyImg, image.Rect(LaWidth+LlWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth+RlWidth, HelmHeight+TorsoHeight+RlHeight), rlImg, image.Pt(0, 0), draw.Src)

	skin.Processed = bodyImg
	return nil
}

func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

func (skin *mcSkin) Resize(width uint) {
	skin.Processed = imaging.Resize(skin.Processed, int(width), 0, imaging.NearestNeighbor)
}

func cropHead(img image.Image) (image.Image, error) {
	return cropImage(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

func cropImage(i image.Image, d image.Rectangle) (image.Image, error) {
	bounds := i.Bounds()
	if bounds.Min.X > d.Min.X || bounds.Min.Y > d.Min.Y || bounds.Max.X < d.Max.X || bounds.Max.Y < d.Max.Y {
		return nil, errors.New("Bounds invalid for crop")
	}

	dims := d.Size()
	outIm := image.NewRGBA(image.Rect(0, 0, dims.X, dims.Y))
	for x := 0; x < dims.X; x++ {
		for y := 0; y < dims.Y; y++ {
			outIm.Set(x, y, i.At(d.Min.X+x, d.Min.Y+y))
		}
	}
	return outIm, nil
}
<commit_msg>Make getHelm nicer<commit_after>package main

import (
	"errors"
	"github.com/disintegration/imaging"
	"github.com/minotar/minecraft"
	"image"
	"image/draw"
	"image/png"
	"io"
)

const (
	HeadX      = 8
	HeadY      = 8
	HeadWidth  = 8
	HeadHeight = 8

	HelmX      = 40
	HelmY      = 8
	HelmWidth  = 8
	HelmHeight = 8

	TorsoX      = 20
	TorsoY      = 20
	TorsoWidth  = 8
	TorsoHeight = 12

	RaX      = 44
	RaY      = 20
	RaWidth  = 4
	RaHeight = 12

	RlX      = 4
	RlY      = 20
	RlWidth  = 4
	RlHeight = 12

	LaX      = 36
	LaY      = 52
	LaWidth  = 4
	LaHeight = 12

	LlX      = 20
	LlY      = 52
	LlWidth  = 4
	LlHeight = 12
)

type mcSkin struct {
	Processed image.Image
	minecraft.Skin
}

func (skin *mcSkin) GetHead() error {
	img, err := cropHead(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = img
	return nil
}

func (skin *mcSkin) GetHelm() error {
	helm, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	skin.Processed = helm
	return nil
}

func (skin *mcSkin) GetBody() error {
	// Check if 1.8 skin (the max Y bound should be 64)
	render18Skin := true
	bounds := skin.Image.Bounds()
	if bounds.Max.Y != 64 {
		render18Skin = false
	}

	helmImg, err := cropHelm(skin.Image)
	if err != nil {
		return err
	}

	torsoImg, err := cropImage(skin.Image, image.Rect(TorsoX, TorsoY, TorsoX+TorsoWidth, TorsoY+TorsoHeight))
	if err != nil {
		return err
	}

	raImg, err := cropImage(skin.Image, image.Rect(RaX, RaY, RaX+RaWidth, RaY+RaHeight))
	if err != nil {
		return err
	}

	rlImg, err := cropImage(skin.Image, image.Rect(RlX, RlY, RlX+RlWidth, RlY+RlHeight))
	if err != nil {
		return err
	}

	var laImg, llImg image.Image

	// If the skin is 1.8 then we will use the left arms and legs, otherwise flip the right ones and use them.
	if render18Skin {
		laImg, err = cropImage(skin.Image, image.Rect(LaX, LaY, LaX+LaWidth, LaY+LaHeight))
		if err != nil {
			return err
		}

		llImg, err = cropImage(skin.Image, image.Rect(LlX, LlY, LlX+LlWidth, LlY+LlHeight))
		if err != nil {
			return err
		}
	} else {
		laImg = imaging.FlipH(raImg)

		llImg = imaging.FlipH(rlImg)
	}

	// Create a blank canvas for us to draw our body on
	bodyImg := image.NewRGBA(image.Rect(0, 0, LaWidth+TorsoWidth+RaWidth, HeadHeight+TorsoHeight+LlHeight))
	// Helm
	draw.Draw(bodyImg, image.Rect(LaWidth, 0, LaWidth+HelmWidth, HelmHeight), helmImg, image.Pt(0, 0), draw.Src)
	// Torso
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight, LaWidth+TorsoWidth, HelmHeight+TorsoHeight), torsoImg, image.Pt(0, 0), draw.Src)
	// Left Arm
	draw.Draw(bodyImg, image.Rect(0, HelmHeight, LaWidth, HelmHeight+LaHeight), laImg, image.Pt(0, 0), draw.Src)
	// Right Arm
	draw.Draw(bodyImg, image.Rect(LaWidth+TorsoWidth, HelmHeight, LaWidth+TorsoWidth+RaWidth, HelmHeight+RaHeight), raImg, image.Pt(0, 0), draw.Src)
	// Left Leg
	draw.Draw(bodyImg, image.Rect(LaWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth, HelmHeight+TorsoHeight+LlHeight), llImg, image.Pt(0, 0), draw.Src)
	// Right Leg
	draw.Draw(bodyImg, image.Rect(LaWidth+LlWidth, HelmHeight+TorsoHeight, LaWidth+LlWidth+RlWidth, HelmHeight+TorsoHeight+RlHeight), rlImg, image.Pt(0, 0), draw.Src)

	skin.Processed = bodyImg
	return nil
}

func (skin *mcSkin) WritePNG(w io.Writer) error {
	return png.Encode(w, skin.Processed)
}

func (skin *mcSkin) Resize(width uint) {
	skin.Processed = imaging.Resize(skin.Processed, int(width), 0, imaging.NearestNeighbor)
}

func cropHead(img image.Image) (image.Image, error) {
	return cropImage(img, image.Rect(HeadX, HeadY, HeadX+HeadWidth, HeadY+HeadHeight))
}

func cropHelm(img image.Image) (image.Image, error) {
	// check if helm is solid colour - if so, it counts as transparent
	isSolidColour := true
	baseColour := img.At(HelmX, HelmY)
	for checkX := HelmX; checkX < HelmX+HelmWidth; checkX++ {
		for checkY := HelmY; checkY < HelmY+HelmHeight; checkY++ {
			checkColour := img.At(checkX, checkY)
			if checkColour != baseColour {
				isSolidColour = false
				break
			}
		}
	}

	headImg, err := cropHead(img)

	if err != nil {
		return nil, err
	}

	if !isSolidColour {
		headImgRGBA := headImg.(*image.RGBA)

		helmImg, err := cropImage(img, image.Rect(HelmX, HelmY, HelmX+HelmWidth, HelmY+HelmHeight))
		if err != nil {
			return nil, err
		}

		sr := helmImg.Bounds()
		draw.Draw(headImgRGBA, sr, helmImg, sr.Min, draw.Over)
	}

	return headImg, nil
}

func cropImage(i image.Image, d image.Rectangle) (image.Image, error) {
	bounds := i.Bounds()
	if bounds.Min.X > d.Min.X || bounds.Min.Y > d.Min.Y || bounds.Max.X < d.Max.X || bounds.Max.Y < d.Max.Y {
		return nil, errors.New("Bounds invalid for crop")
	}

	dims := d.Size()
	outIm := image.NewRGBA(image.Rect(0, 0, dims.X, dims.Y))
	for x := 0; x < dims.X; x++ {
		for y := 0; y < dims.Y; y++ {
			outIm.Set(x, y, i.At(d.Min.X+x, d.Min.Y+y))
		}
	}
	return outIm, nil
}
<|endoftext|>


<commit_before>package immortal

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"syscall"
	"time"
)

// Process interface
type Process interface {
	Kill() error
	Pid() int
	Signal(sig syscall.Signal) error
	Start() (*process, error)
}

type process struct {
	*Config
	Logger
	LoggerStderr Logger
	cmd          *exec.Cmd
	errch        chan error
	quit         chan struct{}
	sTime, eTime time.Time
}

// SetEnv set environment variables - If the Cmd.Env contains duplicate
// environment keys, only the last value in the slice for each duplicate
// key is used.
func (p *process) SetEnv(env []string) {
	if p.Env != nil {
		for k, v := range p.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
		p.cmd.Env = env
	}
}

// SetsysProcAttr - set process owner (run on behalf)
func (p *process) SetsysProcAttr() error {
	sysProcAttr := &syscall.SysProcAttr{
		Setpgid: true, // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
		Pgid:    0,    // Child's process group ID if Setpgid.
	}

	// set owner
	if p.user != nil {
		uid, err := strconv.Atoi(p.user.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(p.user.Gid)
		if err != nil {
			return err
		}
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// set the attributes
	p.cmd.SysProcAttr = sysProcAttr

	return nil
}

// Start runs the command
func (p *process) Start() (*process, error) {
	// command obtained from Config parent
	p.cmd = exec.Command(p.command[0], p.command[1:]...)

	// change working directory
	if p.Cwd != "" {
		p.cmd.Dir = p.Cwd
	}

	// set environment variables
	p.SetEnv(os.Environ())

	// set sysProcAttr
	if err := p.SetsysProcAttr(); err != nil {
		return nil, err
	}

	var (
		prStdout, prStderr, pwStdout, pwStderr *os.File
		e                                      error
	)
	// log only if are available loggers
	if p.Logger.IsLogging() && p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			go p.Logger.Log(prStdout)
		}
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	} else if p.Logger.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			p.cmd.Stderr = pwStdout
			go p.Logger.Log(prStdout)
		}
	} else if p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	}

	// Start the process
	if err := p.cmd.Start(); err != nil {
		return nil, err
	}

	// set start time
	p.sTime = time.Now()

	// wait process to finish in a goroutine
	go p.Wait(pwStdout, pwStderr)

	return p, nil
}

// Wait - wait process to finish
func (p *process) Wait(stdout, stderr *os.File) {
	err := p.cmd.Wait()
	if stdout != nil {
		stdout.Close()
		close(p.quit)
	}
	if stderr != nil {
		stderr.Close()
	}
	p.errch <- err
}

// Kill the entire Process group.
func (p *process) Kill() error {
	processGroup := 0 - p.cmd.Process.Pid
	return syscall.Kill(processGroup, syscall.SIGKILL)
}

// Pid return Process PID
func (p *process) Pid() int {
	if p.cmd == nil || p.cmd.Process == nil {
		return 0
	}
	return p.cmd.Process.Pid
}

// Signal sends a signal to the Process
func (p *process) Signal(sig syscall.Signal) error {
	return syscall.Kill(p.cmd.Process.Pid, sig)
}

// NewProcess return process instance
func NewProcess(cfg *Config) *process {
	qch := make(chan struct{})
	return &process{
		Config: cfg,
		Logger: &LogWriter{
			logger: NewLogger(cfg, qch),
		},
		LoggerStderr: &LogWriter{
			logger: NewStderrLogger(cfg),
		},
		errch: make(chan error, 1),
		quit:  qch,
	}
}
<commit_msg>comment<commit_after>package immortal

import (
	"fmt"
	"os"
	"os/exec"
	"strconv"
	"syscall"
	"time"
)

// Process interface
type Process interface {
	Kill() error
	Pid() int
	Signal(sig syscall.Signal) error
	Start() (*process, error)
}

type process struct {
	*Config
	Logger
	LoggerStderr Logger
	cmd          *exec.Cmd
	errch        chan error
	quit         chan struct{}
	sTime, eTime time.Time
}

// SetEnv set environment variables - If the Cmd.Env contains duplicate
// environment keys, only the last value in the slice for each duplicate
// key is used.
func (p *process) SetEnv(env []string) {
	if p.Env != nil {
		for k, v := range p.Env {
			env = append(env, fmt.Sprintf("%s=%s", k, v))
		}
		p.cmd.Env = env
	}
}

// SetsysProcAttr - set process group ID and owner (run on behalf)
func (p *process) SetsysProcAttr() error {
	sysProcAttr := &syscall.SysProcAttr{
		Setpgid: true, // Set process group ID to Pgid, or, if Pgid == 0, to new pid.
		Pgid:    0,    // Child's process group ID if Setpgid.
	}

	// set owner
	if p.user != nil {
		uid, err := strconv.Atoi(p.user.Uid)
		if err != nil {
			return err
		}
		gid, err := strconv.Atoi(p.user.Gid)
		if err != nil {
			return err
		}
		sysProcAttr.Credential = &syscall.Credential{
			Uid: uint32(uid),
			Gid: uint32(gid),
		}
	}

	// set the attributes
	p.cmd.SysProcAttr = sysProcAttr

	return nil
}

// Start runs the command
func (p *process) Start() (*process, error) {
	// command obtained from Config parent
	p.cmd = exec.Command(p.command[0], p.command[1:]...)

	// change working directory
	if p.Cwd != "" {
		p.cmd.Dir = p.Cwd
	}

	// set environment variables
	p.SetEnv(os.Environ())

	// set sysProcAttr
	if err := p.SetsysProcAttr(); err != nil {
		return nil, err
	}

	var (
		prStdout, prStderr, pwStdout, pwStderr *os.File
		e                                      error
	)
	// log only if are available loggers
	if p.Logger.IsLogging() && p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			go p.Logger.Log(prStdout)
		}
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	} else if p.Logger.IsLogging() {
		// create the pipes for Stdout
		prStdout, pwStdout, e = os.Pipe()
		if e == nil {
			p.cmd.Stdout = pwStdout
			p.cmd.Stderr = pwStdout
			go p.Logger.Log(prStdout)
		}
	} else if p.LoggerStderr.IsLogging() {
		// create the pipes for Stdout
		prStderr, pwStderr, e = os.Pipe()
		if e == nil {
			p.cmd.Stderr = pwStderr
			go p.LoggerStderr.Log(prStderr)
		}
	}

	// Start the process
	if err := p.cmd.Start(); err != nil {
		return nil, err
	}

	// set start time
	p.sTime = time.Now()

	// wait process to finish in a goroutine
	go p.Wait(pwStdout, pwStderr)

	return p, nil
}

// Wait - wait process to finish
func (p *process) Wait(stdout, stderr *os.File) {
	err := p.cmd.Wait()
	if stdout != nil {
		stdout.Close()
		close(p.quit)
	}
	if stderr != nil {
		stderr.Close()
	}
	p.errch <- err
}

// Kill the entire Process group.
func (p *process) Kill() error {
	processGroup := 0 - p.cmd.Process.Pid
	return syscall.Kill(processGroup, syscall.SIGKILL)
}

// Pid return Process PID
func (p *process) Pid() int {
	if p.cmd == nil || p.cmd.Process == nil {
		return 0
	}
	return p.cmd.Process.Pid
}

// Signal sends a signal to the Process
func (p *process) Signal(sig syscall.Signal) error {
	return syscall.Kill(p.cmd.Process.Pid, sig)
}

// NewProcess return process instance
func NewProcess(cfg *Config) *process {
	qch := make(chan struct{})
	return &process{
		Config: cfg,
		Logger: &LogWriter{
			logger: NewLogger(cfg, qch),
		},
		LoggerStderr: &LogWriter{
			logger: NewStderrLogger(cfg),
		},
		errch: make(chan error, 1),
		quit:  qch,
	}
}
<|endoftext|>