<commit_before>// Copyright 2012-2014 Apcera Inc. All rights reserved.

// A Go client for the NATS messaging system (https://nats.io).
package nats

import (
	"fmt"
)

type msgArg struct {
	subject []byte
	reply   []byte
	sid     int64
	size    int
}

const MAX_CONTROL_LINE_SIZE = 1024

type parseState struct {
	state   int
	as      int
	drop    int
	ma      msgArg
	argBuf  []byte
	msgBuf  []byte
	scratch [MAX_CONTROL_LINE_SIZE]byte
}

const (
	OP_START = iota
	OP_PLUS
	OP_PLUS_O
	OP_PLUS_OK
	OP_MINUS
	OP_MINUS_E
	OP_MINUS_ER
	OP_MINUS_ERR
	OP_MINUS_ERR_SPC
	MINUS_ERR_ARG
	OP_C
	OP_CO
	OP_CON
	OP_CONN
	OP_CONNE
	OP_CONNEC
	OP_CONNECT
	CONNECT_ARG
	OP_M
	OP_MS
	OP_MSG
	OP_MSG_SPC
	MSG_ARG
	MSG_PAYLOAD
	MSG_END
	OP_P
	OP_PI
	OP_PIN
	OP_PING
	OP_PO
	OP_PON
	OP_PONG
)

// parse is the fast protocol parser engine.
func (nc *Conn) parse(buf []byte) error {
	var i int
	var b byte

	for i, b = range buf {
		switch nc.ps.state {
		case OP_START:
			switch b {
			case 'M', 'm':
				nc.ps.state = OP_M
			case 'C', 'c':
				nc.ps.state = OP_C
			case 'P', 'p':
				nc.ps.state = OP_P
			case '+':
				nc.ps.state = OP_PLUS
			case '-':
				nc.ps.state = OP_MINUS
			default:
				goto parseErr
			}
		case OP_M:
			switch b {
			case 'S', 's':
				nc.ps.state = OP_MS
			default:
				goto parseErr
			}
		case OP_MS:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_MSG
			default:
				goto parseErr
			}
		case OP_MSG:
			switch b {
			case ' ', '\t':
				nc.ps.state = OP_MSG_SPC
			default:
				goto parseErr
			}
		case OP_MSG_SPC:
			switch b {
			case ' ', '\t':
				continue
			default:
				nc.ps.state = MSG_ARG
				nc.ps.as = i
			}
		case MSG_ARG:
			switch b {
			case '\r':
				nc.ps.drop = 1
			case '\n':
				var arg []byte
				if nc.ps.argBuf != nil {
					arg = nc.ps.argBuf
				} else {
					arg = buf[nc.ps.as : i-nc.ps.drop]
				}
				if err := nc.processMsgArgs(arg); err != nil {
					return err
				}
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, MSG_PAYLOAD
			default:
				if nc.ps.argBuf != nil {
					nc.ps.argBuf = append(nc.ps.argBuf, b)
				}
			}
		case MSG_PAYLOAD:
			if nc.ps.msgBuf != nil {
				if len(nc.ps.msgBuf) >= nc.ps.ma.size {
					nc.processMsg(nc.ps.msgBuf)
					nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
				} else {
					nc.ps.msgBuf = append(nc.ps.msgBuf, b)
				}
			} else if i-nc.ps.as >= nc.ps.ma.size {
				nc.processMsg(buf[nc.ps.as:i])
				nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
			}
		case MSG_END:
			switch b {
			case '\n':
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
			default:
				continue
			}
		case OP_PLUS:
			switch b {
			case 'O', 'o':
				nc.ps.state = OP_PLUS_O
			default:
				goto parseErr
			}
		case OP_PLUS_O:
			switch b {
			case 'K', 'k':
				nc.ps.state = OP_PLUS_OK
			default:
				goto parseErr
			}
		case OP_PLUS_OK:
			switch b {
			case '\n':
				nc.processOK()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		case OP_MINUS:
			switch b {
			case 'E', 'e':
				nc.ps.state = OP_MINUS_E
			default:
				goto parseErr
			}
		case OP_MINUS_E:
			switch b {
			case 'R', 'r':
				nc.ps.state = OP_MINUS_ER
			default:
				goto parseErr
			}
		case OP_MINUS_ER:
			switch b {
			case 'R', 'r':
				nc.ps.state = OP_MINUS_ERR
			default:
				goto parseErr
			}
		case OP_MINUS_ERR:
			switch b {
			case ' ', '\t':
				nc.ps.state = OP_MINUS_ERR_SPC
			default:
				goto parseErr
			}
		case OP_MINUS_ERR_SPC:
			switch b {
			case ' ', '\t':
				continue
			default:
				nc.ps.state = MINUS_ERR_ARG
				nc.ps.as = i
			}
		case MINUS_ERR_ARG:
			switch b {
			case '\r':
				nc.ps.drop = 1
			case '\n':
				var arg []byte
				if nc.ps.argBuf != nil {
					arg = nc.ps.argBuf
					nc.ps.argBuf = nil
				} else {
					arg = buf[nc.ps.as : i-nc.ps.drop]
				}
				nc.processErr(string(arg))
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
			default:
				if nc.ps.argBuf != nil {
					nc.ps.argBuf = append(nc.ps.argBuf, b)
				}
			}
		case OP_P:
			switch b {
			case 'I', 'i':
				nc.ps.state = OP_PI
			case 'O', 'o':
				nc.ps.state = OP_PO
			default:
				goto parseErr
			}
		case OP_PO:
			switch b {
			case 'N', 'n':
				nc.ps.state = OP_PON
			default:
				goto parseErr
			}
		case OP_PON:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_PONG
			default:
				goto parseErr
			}
		case OP_PONG:
			switch b {
			case '\n':
				nc.processPong()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		case OP_PI:
			switch b {
			case 'N', 'n':
				nc.ps.state = OP_PIN
			default:
				goto parseErr
			}
		case OP_PIN:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_PING
			default:
				goto parseErr
			}
		case OP_PING:
			switch b {
			case '\n':
				nc.processPing()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		default:
			goto parseErr
		}
	}
	// Check for split buffer scenarios
	if (nc.ps.state == MSG_ARG || nc.ps.state == MINUS_ERR_ARG) && nc.ps.argBuf == nil {
		nc.ps.argBuf = nc.ps.scratch[:0]
		nc.ps.argBuf = append(nc.ps.argBuf, buf[nc.ps.as:(i+1)-nc.ps.drop]...)
		// FIXME, check max len
	}
	// Check for split msg
	if nc.ps.state == MSG_PAYLOAD && nc.ps.msgBuf == nil {
		// We need to clone the msgArg if it is still referencing the
		// read buffer and we are not able to process the msg.
		if nc.ps.argBuf == nil {
			nc.cloneMsgArg()
		}
		// FIXME: copy better here? Make whole buf if large?
		nc.ps.msgBuf = nc.ps.scratch[len(nc.ps.argBuf):len(nc.ps.argBuf)]
		nc.ps.msgBuf = append(nc.ps.msgBuf, (buf[nc.ps.as:])...)
	}

	return nil

parseErr:
	return fmt.Errorf("nats: Parse Error [%d]: '%s'", nc.ps.state, buf[i:])
}

// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
// we need to hold onto it into the next read.
func (nc *Conn) cloneMsgArg() {
	nc.ps.argBuf = nc.ps.scratch[:0]
	nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.subject...)
	nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.reply...)
	nc.ps.ma.subject = nc.ps.argBuf[:len(nc.ps.ma.subject)]
	if nc.ps.ma.reply != nil {
		nc.ps.ma.reply = nc.ps.argBuf[len(nc.ps.ma.subject):]
	}
}

const argsLenMax = 4

func (nc *Conn) processMsgArgs(arg []byte) error {
	// Unroll splitArgs to avoid runtime/heap issues
	a := [argsLenMax][]byte{}
	args := a[:0]
	start := -1
	for i, b := range arg {
		switch b {
		case ' ', '\t', '\r', '\n':
			if start >= 0 {
				args = append(args, arg[start:i])
				start = -1
			}
		default:
			if start < 0 {
				start = i
			}
		}
	}
	if start >= 0 {
		args = append(args, arg[start:])
	}

	switch len(args) {
	case 3:
		nc.ps.ma.subject = args[0]
		nc.ps.ma.sid = parseInt64(args[1])
		nc.ps.ma.reply = nil
		nc.ps.ma.size = int(parseInt64(args[2]))
	case 4:
		nc.ps.ma.subject = args[0]
		nc.ps.ma.sid = parseInt64(args[1])
		nc.ps.ma.reply = args[2]
		nc.ps.ma.size = int(parseInt64(args[3]))
	default:
		return fmt.Errorf("nats: processMsgArgs Parse Error: '%s'", arg)
	}
	if nc.ps.ma.size < 0 {
		return fmt.Errorf("nats: processMsgArgs Bad or Missing Size: '%s'", arg)
	}
	return nil
}

// Ascii numbers 0-9
const (
	ascii_0 = 48
	ascii_9 = 57
)

// parseInt64 expects decimal positive numbers. We
// return -1 to signal error
func parseInt64(d []byte) (n int64) {
	if len(d) == 0 {
		return -1
	}
	for _, dec := range d {
		if dec < ascii_0 || dec > ascii_9 {
			return -1
		}
		n = n*10 + (int64(dec) - ascii_0)
	}
	return n
}
<commit_msg>parser: rm unused OP_CONNECT & CONNECT_ARG.<commit_after>// Copyright 2012-2014 Apcera Inc. All rights reserved.

// A Go client for the NATS messaging system (https://nats.io).
package nats

import (
	"fmt"
)

type msgArg struct {
	subject []byte
	reply   []byte
	sid     int64
	size    int
}

const MAX_CONTROL_LINE_SIZE = 1024

type parseState struct {
	state   int
	as      int
	drop    int
	ma      msgArg
	argBuf  []byte
	msgBuf  []byte
	scratch [MAX_CONTROL_LINE_SIZE]byte
}

const (
	OP_START = iota
	OP_PLUS
	OP_PLUS_O
	OP_PLUS_OK
	OP_MINUS
	OP_MINUS_E
	OP_MINUS_ER
	OP_MINUS_ERR
	OP_MINUS_ERR_SPC
	MINUS_ERR_ARG
	OP_M
	OP_MS
	OP_MSG
	OP_MSG_SPC
	MSG_ARG
	MSG_PAYLOAD
	MSG_END
	OP_P
	OP_PI
	OP_PIN
	OP_PING
	OP_PO
	OP_PON
	OP_PONG
)

// parse is the fast protocol parser engine.
func (nc *Conn) parse(buf []byte) error {
	var i int
	var b byte

	for i, b = range buf {
		switch nc.ps.state {
		case OP_START:
			switch b {
			case 'M', 'm':
				nc.ps.state = OP_M
			case 'P', 'p':
				nc.ps.state = OP_P
			case '+':
				nc.ps.state = OP_PLUS
			case '-':
				nc.ps.state = OP_MINUS
			default:
				goto parseErr
			}
		case OP_M:
			switch b {
			case 'S', 's':
				nc.ps.state = OP_MS
			default:
				goto parseErr
			}
		case OP_MS:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_MSG
			default:
				goto parseErr
			}
		case OP_MSG:
			switch b {
			case ' ', '\t':
				nc.ps.state = OP_MSG_SPC
			default:
				goto parseErr
			}
		case OP_MSG_SPC:
			switch b {
			case ' ', '\t':
				continue
			default:
				nc.ps.state = MSG_ARG
				nc.ps.as = i
			}
		case MSG_ARG:
			switch b {
			case '\r':
				nc.ps.drop = 1
			case '\n':
				var arg []byte
				if nc.ps.argBuf != nil {
					arg = nc.ps.argBuf
				} else {
					arg = buf[nc.ps.as : i-nc.ps.drop]
				}
				if err := nc.processMsgArgs(arg); err != nil {
					return err
				}
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, MSG_PAYLOAD
			default:
				if nc.ps.argBuf != nil {
					nc.ps.argBuf = append(nc.ps.argBuf, b)
				}
			}
		case MSG_PAYLOAD:
			if nc.ps.msgBuf != nil {
				if len(nc.ps.msgBuf) >= nc.ps.ma.size {
					nc.processMsg(nc.ps.msgBuf)
					nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
				} else {
					nc.ps.msgBuf = append(nc.ps.msgBuf, b)
				}
			} else if i-nc.ps.as >= nc.ps.ma.size {
				nc.processMsg(buf[nc.ps.as:i])
				nc.ps.argBuf, nc.ps.msgBuf, nc.ps.state = nil, nil, MSG_END
			}
		case MSG_END:
			switch b {
			case '\n':
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
			default:
				continue
			}
		case OP_PLUS:
			switch b {
			case 'O', 'o':
				nc.ps.state = OP_PLUS_O
			default:
				goto parseErr
			}
		case OP_PLUS_O:
			switch b {
			case 'K', 'k':
				nc.ps.state = OP_PLUS_OK
			default:
				goto parseErr
			}
		case OP_PLUS_OK:
			switch b {
			case '\n':
				nc.processOK()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		case OP_MINUS:
			switch b {
			case 'E', 'e':
				nc.ps.state = OP_MINUS_E
			default:
				goto parseErr
			}
		case OP_MINUS_E:
			switch b {
			case 'R', 'r':
				nc.ps.state = OP_MINUS_ER
			default:
				goto parseErr
			}
		case OP_MINUS_ER:
			switch b {
			case 'R', 'r':
				nc.ps.state = OP_MINUS_ERR
			default:
				goto parseErr
			}
		case OP_MINUS_ERR:
			switch b {
			case ' ', '\t':
				nc.ps.state = OP_MINUS_ERR_SPC
			default:
				goto parseErr
			}
		case OP_MINUS_ERR_SPC:
			switch b {
			case ' ', '\t':
				continue
			default:
				nc.ps.state = MINUS_ERR_ARG
				nc.ps.as = i
			}
		case MINUS_ERR_ARG:
			switch b {
			case '\r':
				nc.ps.drop = 1
			case '\n':
				var arg []byte
				if nc.ps.argBuf != nil {
					arg = nc.ps.argBuf
					nc.ps.argBuf = nil
				} else {
					arg = buf[nc.ps.as : i-nc.ps.drop]
				}
				nc.processErr(string(arg))
				nc.ps.drop, nc.ps.as, nc.ps.state = 0, i+1, OP_START
			default:
				if nc.ps.argBuf != nil {
					nc.ps.argBuf = append(nc.ps.argBuf, b)
				}
			}
		case OP_P:
			switch b {
			case 'I', 'i':
				nc.ps.state = OP_PI
			case 'O', 'o':
				nc.ps.state = OP_PO
			default:
				goto parseErr
			}
		case OP_PO:
			switch b {
			case 'N', 'n':
				nc.ps.state = OP_PON
			default:
				goto parseErr
			}
		case OP_PON:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_PONG
			default:
				goto parseErr
			}
		case OP_PONG:
			switch b {
			case '\n':
				nc.processPong()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		case OP_PI:
			switch b {
			case 'N', 'n':
				nc.ps.state = OP_PIN
			default:
				goto parseErr
			}
		case OP_PIN:
			switch b {
			case 'G', 'g':
				nc.ps.state = OP_PING
			default:
				goto parseErr
			}
		case OP_PING:
			switch b {
			case '\n':
				nc.processPing()
				nc.ps.drop, nc.ps.state = 0, OP_START
			}
		default:
			goto parseErr
		}
	}
	// Check for split buffer scenarios
	if (nc.ps.state == MSG_ARG || nc.ps.state == MINUS_ERR_ARG) && nc.ps.argBuf == nil {
		nc.ps.argBuf = nc.ps.scratch[:0]
		nc.ps.argBuf = append(nc.ps.argBuf, buf[nc.ps.as:(i+1)-nc.ps.drop]...)
		// FIXME, check max len
	}
	// Check for split msg
	if nc.ps.state == MSG_PAYLOAD && nc.ps.msgBuf == nil {
		// We need to clone the msgArg if it is still referencing the
		// read buffer and we are not able to process the msg.
		if nc.ps.argBuf == nil {
			nc.cloneMsgArg()
		}
		// FIXME: copy better here? Make whole buf if large?
		nc.ps.msgBuf = nc.ps.scratch[len(nc.ps.argBuf):len(nc.ps.argBuf)]
		nc.ps.msgBuf = append(nc.ps.msgBuf, (buf[nc.ps.as:])...)
	}

	return nil

parseErr:
	return fmt.Errorf("nats: Parse Error [%d]: '%s'", nc.ps.state, buf[i:])
}

// cloneMsgArg is used when the split buffer scenario has the pubArg in the existing read buffer, but
// we need to hold onto it into the next read.
func (nc *Conn) cloneMsgArg() {
	nc.ps.argBuf = nc.ps.scratch[:0]
	nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.subject...)
	nc.ps.argBuf = append(nc.ps.argBuf, nc.ps.ma.reply...)
	nc.ps.ma.subject = nc.ps.argBuf[:len(nc.ps.ma.subject)]
	if nc.ps.ma.reply != nil {
		nc.ps.ma.reply = nc.ps.argBuf[len(nc.ps.ma.subject):]
	}
}

const argsLenMax = 4

func (nc *Conn) processMsgArgs(arg []byte) error {
	// Unroll splitArgs to avoid runtime/heap issues
	a := [argsLenMax][]byte{}
	args := a[:0]
	start := -1
	for i, b := range arg {
		switch b {
		case ' ', '\t', '\r', '\n':
			if start >= 0 {
				args = append(args, arg[start:i])
				start = -1
			}
		default:
			if start < 0 {
				start = i
			}
		}
	}
	if start >= 0 {
		args = append(args, arg[start:])
	}

	switch len(args) {
	case 3:
		nc.ps.ma.subject = args[0]
		nc.ps.ma.sid = parseInt64(args[1])
		nc.ps.ma.reply = nil
		nc.ps.ma.size = int(parseInt64(args[2]))
	case 4:
		nc.ps.ma.subject = args[0]
		nc.ps.ma.sid = parseInt64(args[1])
		nc.ps.ma.reply = args[2]
		nc.ps.ma.size = int(parseInt64(args[3]))
	default:
		return fmt.Errorf("nats: processMsgArgs Parse Error: '%s'", arg)
	}
	if nc.ps.ma.size < 0 {
		return fmt.Errorf("nats: processMsgArgs Bad or Missing Size: '%s'", arg)
	}
	return nil
}

// Ascii numbers 0-9
const (
	ascii_0 = 48
	ascii_9 = 57
)

// parseInt64 expects decimal positive numbers. We
// return -1 to signal error
func parseInt64(d []byte) (n int64) {
	if len(d) == 0 {
		return -1
	}
	for _, dec := range d {
		if dec < ascii_0 || dec > ascii_9 {
			return -1
		}
		n = n*10 + (int64(dec) - ascii_0)
	}
	return n
}
<|endoftext|>


<commit_before>/*
Copyright 2015 - Olivier Wulveryck

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

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

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

package toscalib

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"path/filepath"

	"golang.org/x/tools/godoc/vfs"
	"golang.org/x/tools/godoc/vfs/zipfs"
	"gopkg.in/yaml.v2"
)

// GetNodeTemplate returns a pointer to a node template given its name
// its returns nil if not found
func (t *ServiceTemplateDefinition) GetNodeTemplate(nodeName string) *NodeTemplate {
	for name, nodeTemplate := range t.TopologyTemplate.NodeTemplates {
		if name == nodeName {
			return &nodeTemplate
		}
	}
	return nil
}

// merge copies all the elements of t into s and returns the result
func merge(s, t ServiceTemplateDefinition) ServiceTemplateDefinition {
	// Repositories
	rep := make(map[string]RepositoryDefinition, len(s.Repositories)+len(t.Repositories))
	for key, val := range t.Repositories {
		rep[key] = val
	}
	for key, val := range s.Repositories {
		rep[key] = val
	}
	s.Repositories = rep
	// DataTypes
	dat := make(map[string]DataType, len(s.DataTypes)+len(t.DataTypes))
	for key, val := range t.DataTypes {
		dat[key] = val
	}
	for key, val := range s.DataTypes {
		dat[key] = val
	}
	s.DataTypes = dat
	// NodeTypes
	nt := make(map[string]NodeType, len(s.NodeTypes)+len(t.NodeTypes))
	for key, val := range t.NodeTypes {
		nt[key] = val
	}
	for key, val := range s.NodeTypes {
		nt[key] = val
	}
	s.NodeTypes = nt
	// ArtifactType
	arti := make(map[string]ArtifactType, len(s.ArtifactTypes)+len(t.ArtifactTypes))
	for key, val := range t.ArtifactTypes {
		arti[key] = val
	}
	for key, val := range s.ArtifactTypes {
		arti[key] = val
	}
	s.ArtifactTypes = arti
	// RelationshipType
	rel := make(map[string]RelationshipType, len(s.RelationshipTypes)+len(t.RelationshipTypes))
	for key, val := range t.RelationshipTypes {
		rel[key] = val
	}
	for key, val := range s.RelationshipTypes {
		rel[key] = val
	}
	s.RelationshipTypes = rel
	// CapabilityType
	capa := make(map[string]CapabilityType, len(s.CapabilityTypes)+len(t.CapabilityTypes))
	for key, val := range t.CapabilityTypes {
		capa[key] = val
	}
	for key, val := range s.CapabilityTypes {
		capa[key] = val
	}
	s.CapabilityTypes = capa
	// InterfaceType
	intf := make(map[string]InterfaceType, len(s.InterfaceTypes)+len(t.InterfaceTypes))
	for key, val := range t.InterfaceTypes {
		intf[key] = val
	}
	for key, val := range s.InterfaceTypes {
		intf[key] = val
	}
	s.InterfaceTypes = intf
	// PolicyType
	pt := make(map[string]PolicyType, len(s.PolicyTypes)+len(t.PolicyTypes))
	for key, val := range t.PolicyTypes {
		pt[key] = val
	}
	for key, val := range s.PolicyTypes {
		pt[key] = val
	}
	s.PolicyTypes = pt
	return s
}

// ParseCsar handles open and parse the CSAR file
func (t *ServiceTemplateDefinition) ParseCsar(zipfile string) error {

	type meta struct {
		Version         string `yaml:"TOSCA-Meta-File-Version"`
		CsarVersion     string `yaml:"CSAR-Version"`
		CreatedBy       string `yaml:"Created-By"`
		EntryDefinition string `yaml:"Entry-Definitions"`
	}

	rc, err := zip.OpenReader(zipfile)
	if err != nil {
		return err
	}
	defer rc.Close()
	fs := zipfs.New(rc, zipfile)
	out, err := vfs.ReadFile(fs, "/TOSCA-Metadata/TOSCA.meta")
	if err != nil {
		return err
	}
	var m meta
	err = yaml.Unmarshal(out, &m)
	if err != nil {
		return err
	}
	dirname := fmt.Sprintf("/%v", filepath.Dir(m.EntryDefinition))
	base := filepath.Base(m.EntryDefinition)
	ns := vfs.NameSpace{}
	ns.Bind("/", fs, dirname, vfs.BindReplace)
	// Now read the yaml
	rsc, err := ns.Open(base)
	if err != nil {
		return err

	}
	var std ServiceTemplateDefinition
	data, err := ioutil.ReadAll(rsc)
	if err != nil {
		return err
	}

	// Unmarshal the data in an interface
	err = yaml.Unmarshal(data, &std)
	if err != nil {
		return err
	}
	// Import de normative types by default
	for _, normType := range []string{"interface_types", "relationship_types", "node_types", "capability_types"} {
		data, err := Asset(normType)
		if err != nil {
			return err
		}
		var tt ServiceTemplateDefinition
		err = yaml.Unmarshal(data, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}
	for _, im := range std.Imports {
		rsc, err := ns.Open(im)
		if err != nil {
			return err
		}
		var tt ServiceTemplateDefinition
		data, err := ioutil.ReadAll(rsc)
		if err != nil {
			return err
		}

		// Unmarshal the data in an interface
		err = yaml.Unmarshal(data, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}
	// Free the imports
	std.Imports = []string{}
	*t = std
	for name, node := range t.TopologyTemplate.NodeTemplates {
		node.fillInterface(*t)
		node.setRefs(t)
		node.setName(name)
		t.TopologyTemplate.NodeTemplates[name] = node
	}

	return nil
}

// ParseRemote a TOSCA document and fill in the structure using specified Resolver function
// to retrieve remote imports.
func (t *ServiceTemplateDefinition) ParseRemote(r io.Reader, resolver Resolver) error {
	var std ServiceTemplateDefinition
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	// Unmarshal the data in an interface
	err = yaml.Unmarshal(data, &std)
	if err != nil {
		return err
	}
	// Import de normative types by default
	for _, normType := range []string{"interface_types", "relationship_types", "node_types", "capability_types"} {
		data, err := Asset(normType)
		if err != nil {
			log.Panic("Normative type not found")
			return err
		}
		var tt ServiceTemplateDefinition
		err = yaml.Unmarshal(data, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}
	for _, im := range std.Imports {
		r, err := resolver(im)
		if err != nil {
			return err
		}

		var tt ServiceTemplateDefinition

		err = yaml.Unmarshal(r, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}
	// Free the imports
	std.Imports = []string{}
	*t = std
	for name, node := range t.TopologyTemplate.NodeTemplates {
		node.fillInterface(*t)
		node.setRefs(t)
		node.setName(name)
		t.TopologyTemplate.NodeTemplates[name] = node
	}

	return nil

}

// Parse a TOSCA document and fill in the structure
func (t *ServiceTemplateDefinition) Parse(r io.Reader) error {
	return t.ParseRemote(r, defaultResolver)
}
<commit_msg>Refactor parser to remove duplicate code<commit_after>/*
Copyright 2015 - Olivier Wulveryck

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

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

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

package toscalib

import (
	"archive/zip"
	"fmt"
	"io"
	"io/ioutil"
	"path/filepath"

	"golang.org/x/tools/godoc/vfs"
	"golang.org/x/tools/godoc/vfs/zipfs"
	"gopkg.in/yaml.v2"
)

// GetNodeTemplate returns a pointer to a node template given its name
// its returns nil if not found
func (t *ServiceTemplateDefinition) GetNodeTemplate(nodeName string) *NodeTemplate {
	for name, nodeTemplate := range t.TopologyTemplate.NodeTemplates {
		if name == nodeName {
			return &nodeTemplate
		}
	}
	return nil
}

// merge copies all the elements of t into s and returns the result
func merge(s, t ServiceTemplateDefinition) ServiceTemplateDefinition {
	// Repositories
	rep := make(map[string]RepositoryDefinition, len(s.Repositories)+len(t.Repositories))
	for key, val := range t.Repositories {
		rep[key] = val
	}
	for key, val := range s.Repositories {
		rep[key] = val
	}
	s.Repositories = rep
	// DataTypes
	dat := make(map[string]DataType, len(s.DataTypes)+len(t.DataTypes))
	for key, val := range t.DataTypes {
		dat[key] = val
	}
	for key, val := range s.DataTypes {
		dat[key] = val
	}
	s.DataTypes = dat
	// NodeTypes
	nt := make(map[string]NodeType, len(s.NodeTypes)+len(t.NodeTypes))
	for key, val := range t.NodeTypes {
		nt[key] = val
	}
	for key, val := range s.NodeTypes {
		nt[key] = val
	}
	s.NodeTypes = nt
	// ArtifactType
	arti := make(map[string]ArtifactType, len(s.ArtifactTypes)+len(t.ArtifactTypes))
	for key, val := range t.ArtifactTypes {
		arti[key] = val
	}
	for key, val := range s.ArtifactTypes {
		arti[key] = val
	}
	s.ArtifactTypes = arti
	// RelationshipType
	rel := make(map[string]RelationshipType, len(s.RelationshipTypes)+len(t.RelationshipTypes))
	for key, val := range t.RelationshipTypes {
		rel[key] = val
	}
	for key, val := range s.RelationshipTypes {
		rel[key] = val
	}
	s.RelationshipTypes = rel
	// CapabilityType
	capa := make(map[string]CapabilityType, len(s.CapabilityTypes)+len(t.CapabilityTypes))
	for key, val := range t.CapabilityTypes {
		capa[key] = val
	}
	for key, val := range s.CapabilityTypes {
		capa[key] = val
	}
	s.CapabilityTypes = capa
	// InterfaceType
	intf := make(map[string]InterfaceType, len(s.InterfaceTypes)+len(t.InterfaceTypes))
	for key, val := range t.InterfaceTypes {
		intf[key] = val
	}
	for key, val := range s.InterfaceTypes {
		intf[key] = val
	}
	s.InterfaceTypes = intf
	// PolicyType
	pt := make(map[string]PolicyType, len(s.PolicyTypes)+len(t.PolicyTypes))
	for key, val := range t.PolicyTypes {
		pt[key] = val
	}
	for key, val := range s.PolicyTypes {
		pt[key] = val
	}
	s.PolicyTypes = pt
	return s
}

// ParseCsar handles open and parse the CSAR file
func (t *ServiceTemplateDefinition) ParseCsar(zipfile string) error {

	type meta struct {
		Version         string `yaml:"TOSCA-Meta-File-Version"`
		CsarVersion     string `yaml:"CSAR-Version"`
		CreatedBy       string `yaml:"Created-By"`
		EntryDefinition string `yaml:"Entry-Definitions"`
	}

	rc, err := zip.OpenReader(zipfile)
	if err != nil {
		return err
	}
	defer rc.Close()
	fs := zipfs.New(rc, zipfile)
	out, err := vfs.ReadFile(fs, "/TOSCA-Metadata/TOSCA.meta")
	if err != nil {
		return err
	}
	var m meta
	err = yaml.Unmarshal(out, &m)
	if err != nil {
		return err
	}
	dirname := fmt.Sprintf("/%v", filepath.Dir(m.EntryDefinition))
	base := filepath.Base(m.EntryDefinition)
	ns := vfs.NameSpace{}
	ns.Bind("/", fs, dirname, vfs.BindReplace)
	// Now read the yaml
	rsc, err := ns.Open(base)
	if err != nil {
		return err
	}

	// pass in a resolver that has the context of the virtual filespace
	// of the archive file to handle resolving imports
	return t.ParseRemote(rsc, func(l string) ([]byte, error) {
		var r []byte
		rsc, err := ns.Open(l)
		if err != nil {
			return r, err
		}
		r, err = ioutil.ReadAll(rsc)
		if err != nil {
			return r, err
		}
		return r, nil
	})
}

// ParseRemote a TOSCA document and fill in the structure using specified Resolver function
// to retrieve remote imports.
func (t *ServiceTemplateDefinition) ParseRemote(r io.Reader, resolver Resolver) error {
	var std ServiceTemplateDefinition
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	// Unmarshal the data in an interface
	err = yaml.Unmarshal(data, &std)
	if err != nil {
		return err
	}

	// Import the normative types by default
	for _, normType := range AssetNames() {
		// the normType comes from the defined list so this will
		// always be successful, if not then panic is the correct
		// approach for this kind of parsing.
		data := MustAsset(normType)

		var tt ServiceTemplateDefinition
		err = yaml.Unmarshal(data, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}

	for _, im := range std.Imports {
		r, err := resolver(im)
		if err != nil {
			return err
		}

		var tt ServiceTemplateDefinition
		err = yaml.Unmarshal(r, &tt)
		if err != nil {
			return err
		}
		std = merge(std, tt)
	}
	// Free the imports
	// TODO(kenjones): Does dropping the Imports list really have any impact?
	std.Imports = []string{}

	// update the initial context with the freshly loaded context
	*t = std

	// make sure any references are fulfilled
	for name, node := range t.TopologyTemplate.NodeTemplates {
		node.fillInterface(*t)
		node.setRefs(t)
		node.setName(name)
		t.TopologyTemplate.NodeTemplates[name] = node
	}

	return nil

}

// Parse a TOSCA document and fill in the structure
func (t *ServiceTemplateDefinition) Parse(r io.Reader) error {
	return t.ParseRemote(r, defaultResolver)
}
<|endoftext|>


<commit_before>package vdf

import (
	"bytes"
	"fmt"
	"io"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		tok Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// scan returns the next token from the underlying scanner.
// If a token has been unscanned then read that instead.
func (p *Parser) scan(respectWhitespace bool) (Token, string) {
	// If we have a token on the buffer, then return it.
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	// Otherwise read the next token from the scanner.
	tok, lit := p.s.Scan(respectWhitespace)

	// Save it to the buffer in case we unscan later.
	p.buf.tok, p.buf.lit = tok, lit

	return tok, lit
}

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWSAndComments ignores whitespace, end-of-line and comment tokens
// during scanning. It returns the next token + string that is not WS, EOL or CommentDoubleSlash
func (p *Parser) scanIgnoreWSAndComments() (Token, string) {
	tok, lit := p.scan(false)

	// If we have a whitespace, just continue scanning until the next token appears
	for tok == WS || tok == EOL {
		tok, lit = p.scan(false)
	}

	// If we have a comment, we need to drop the complete line, because
	// the text would be detect as an ident. But the text after a "//"
	// is part of the comment. So we ignore the complete line.
	if tok == CommentDoubleSlash {
		// Scan until the next line ending
		for {
			tok, _ = p.scan(true)
			if tok == EOL {
				break
			}
		}
		return p.scanIgnoreWSAndComments()
	}

	return tok, lit
}

// Parse is the main entry point of the vdf parser.
// If parsed the complete VDF content and returns
// a map as a key / value pair.
// The value is a string (normal value) or a map[string]interface{}
// again if there is a nested structure.
func (p *Parser) Parse() (map[string]interface{}, error) {
	m := make(map[string]interface{})
	key := ""

	// The first part is a simple ident (as a main map key)
	tok, lit, err := p.scanMapKey()
	if err != nil {
		return nil, err
	}
	if tok != Ident {
		return nil, fmt.Errorf("found %q, expected an ident as a first part", lit)
	}
	key = lit

	tok, lit = p.scanIgnoreWSAndComments()
	if tok != CurlyBraceOpen {
		return nil, fmt.Errorf("found %q, expected an ident as a first part", lit)
	}

	p.unscan()
	m[key], err = p.parseMap()
	if err != nil {
		return nil, err
	}
	return m, nil
}

func (p *Parser) scanMapKey() (Token, string, error) {
	tok, lit := p.scanIgnoreWSAndComments()

	// Get the key
	if tok == QuotationMark {
		return p.scanIdentSurroundedQuotationMark()
	} else if tok == Ident {
		return tok, lit, nil
	}

	return Illegal, lit, nil
}

func (p *Parser) parseMap() (map[string]interface{}, error) {
	m := make(map[string]interface{})
	key := ""

	// The first part should be a open curly brace
	tok, _ := p.scanIgnoreWSAndComments()
	if tok != CurlyBraceOpen {
		return m, nil
	}
	var err error
	for {
		// At first: A key
		tok, lit := p.scanIgnoreWSAndComments()
		switch tok {
		case QuotationMark:
			_, key, err = p.scanIdentSurroundedQuotationMark()
			if err != nil {
				return nil, err
			}
		case Ident:
			key = lit

			// The default statement would also cover tokens like:
			//	- CommentDoubleSlash
			//		|-> Should in theory never happen, because comments
			//			are handled by scanIgnoreWSAndComments() already
			//	- CurlyBraceClose
		default:
			return m, nil
		}

		// After this: A value or a map again
		tok, lit = p.scanIgnoreWSAndComments()
		switch tok {
		case QuotationMark:
			_, m[key], err = p.scanIdentSurroundedQuotationMark()
			if err != nil {
				return nil, err
			}
		case Ident:
			m[key] = lit
		case CurlyBraceOpen:
			p.unscan()
			m1, err := p.parseMap()
			if err != nil {
				return nil, err
			}
			mergeMap(m, m1, key)

			// The default statement would also cover tokens like:
			//	- CurlyBraceClose
		default:
			return m, nil
		}
	}
}

func (p *Parser) scanIdentSurroundedQuotationMark() (Token, string, error) {
	var buf bytes.Buffer
	escaped := false
	terminated := false
	for {
		tok, lit := p.scan(true)
		if tok == EOF && !terminated {
			return tok, "", ErrNotValidFormat
		}
		if tok == QuotationMark && !escaped {
			terminated = true
			// We don`t unscan here, because
			// we don`t need this quotation mark anymore.
			break
		}

		// If the current character is escaped and it is NOT an escape sequence
		// set character handling back to normal.
		if escaped && tok != EscapeSequence {
			escaped = false
		}

		// If we have an escape sequence and the current state is not escaped
		// mark the next character as escaped.
		if tok == EscapeSequence && !escaped {
			escaped = true
			continue
		}

		buf.WriteString(lit)

		// If the current character is escaped and it is a backslash
		// reset the character handing.
		// This is only triggered if you want to add a \ into a key or a value.
		// Then you have to add "\\".
		if escaped && tok == EscapeSequence {
			escaped = false
		}
	}

	return Ident, buf.String(), nil
}

// VDF files can contain duplicates of keys, when this occurs we need to merge the existing map and the returned map
func mergeMap(m, r map[string]interface{}, key string) map[string]interface{} {
	if _, ok := m[key]; !ok {
		m[key] = r
	} else {
		for k, v := range r {
			m[key].(map[string]interface{})[k] = v
		}
	}

	return m
}
<commit_msg>Fixed missleading error message: We expect a curly brace as a second token, not an ident<commit_after>package vdf

import (
	"bytes"
	"fmt"
	"io"
)

// Parser represents a parser.
type Parser struct {
	s   *Scanner
	buf struct {
		tok Token  // last read token
		lit string // last read literal
		n   int    // buffer size (max=1)
	}
}

// NewParser returns a new instance of Parser.
func NewParser(r io.Reader) *Parser {
	return &Parser{s: NewScanner(r)}
}

// scan returns the next token from the underlying scanner.
// If a token has been unscanned then read that instead.
func (p *Parser) scan(respectWhitespace bool) (Token, string) {
	// If we have a token on the buffer, then return it.
	if p.buf.n != 0 {
		p.buf.n = 0
		return p.buf.tok, p.buf.lit
	}

	// Otherwise read the next token from the scanner.
	tok, lit := p.s.Scan(respectWhitespace)

	// Save it to the buffer in case we unscan later.
	p.buf.tok, p.buf.lit = tok, lit

	return tok, lit
}

// unscan pushes the previously read token back onto the buffer.
func (p *Parser) unscan() { p.buf.n = 1 }

// scanIgnoreWSAndComments ignores whitespace, end-of-line and comment tokens
// during scanning. It returns the next token + string that is not WS, EOL or CommentDoubleSlash
func (p *Parser) scanIgnoreWSAndComments() (Token, string) {
	tok, lit := p.scan(false)

	// If we have a whitespace, just continue scanning until the next token appears
	for tok == WS || tok == EOL {
		tok, lit = p.scan(false)
	}

	// If we have a comment, we need to drop the complete line, because
	// the text would be detect as an ident. But the text after a "//"
	// is part of the comment. So we ignore the complete line.
	if tok == CommentDoubleSlash {
		// Scan until the next line ending
		for {
			tok, _ = p.scan(true)
			if tok == EOL {
				break
			}
		}
		return p.scanIgnoreWSAndComments()
	}

	return tok, lit
}

// Parse is the main entry point of the vdf parser.
// If parsed the complete VDF content and returns
// a map as a key / value pair.
// The value is a string (normal value) or a map[string]interface{}
// again if there is a nested structure.
func (p *Parser) Parse() (map[string]interface{}, error) {
	m := make(map[string]interface{})
	key := ""

	// The first part is a simple ident (as a main map key)
	tok, lit, err := p.scanMapKey()
	if err != nil {
		return nil, err
	}
	if tok != Ident {
		return nil, fmt.Errorf("found %q, expected an ident as a first part", lit)
	}
	key = lit

	tok, lit = p.scanIgnoreWSAndComments()
	if tok != CurlyBraceOpen {
		return nil, fmt.Errorf("found %q, expected a curly brace as second part (to open up the first level)", lit)
	}

	p.unscan()
	m[key], err = p.parseMap()
	if err != nil {
		return nil, err
	}
	return m, nil
}

func (p *Parser) scanMapKey() (Token, string, error) {
	tok, lit := p.scanIgnoreWSAndComments()

	// Get the key
	if tok == QuotationMark {
		return p.scanIdentSurroundedQuotationMark()
	} else if tok == Ident {
		return tok, lit, nil
	}

	return Illegal, lit, nil
}

func (p *Parser) parseMap() (map[string]interface{}, error) {
	m := make(map[string]interface{})
	key := ""

	// The first part should be a open curly brace
	tok, _ := p.scanIgnoreWSAndComments()
	if tok != CurlyBraceOpen {
		return m, nil
	}
	var err error
	for {
		// At first: A key
		tok, lit := p.scanIgnoreWSAndComments()
		switch tok {
		case QuotationMark:
			_, key, err = p.scanIdentSurroundedQuotationMark()
			if err != nil {
				return nil, err
			}
		case Ident:
			key = lit

			// The default statement would also cover tokens like:
			//	- CommentDoubleSlash
			//		|-> Should in theory never happen, because comments
			//			are handled by scanIgnoreWSAndComments() already
			//	- CurlyBraceClose
		default:
			return m, nil
		}

		// After this: A value or a map again
		tok, lit = p.scanIgnoreWSAndComments()
		switch tok {
		case QuotationMark:
			_, m[key], err = p.scanIdentSurroundedQuotationMark()
			if err != nil {
				return nil, err
			}
		case Ident:
			m[key] = lit
		case CurlyBraceOpen:
			p.unscan()
			m1, err := p.parseMap()
			if err != nil {
				return nil, err
			}
			mergeMap(m, m1, key)

			// The default statement would also cover tokens like:
			//	- CurlyBraceClose
		default:
			return m, nil
		}
	}
}

func (p *Parser) scanIdentSurroundedQuotationMark() (Token, string, error) {
	var buf bytes.Buffer
	escaped := false
	terminated := false
	for {
		tok, lit := p.scan(true)
		if tok == EOF && !terminated {
			return tok, "", ErrNotValidFormat
		}
		if tok == QuotationMark && !escaped {
			terminated = true
			// We don`t unscan here, because
			// we don`t need this quotation mark anymore.
			break
		}

		// If the current character is escaped and it is NOT an escape sequence
		// set character handling back to normal.
		if escaped && tok != EscapeSequence {
			escaped = false
		}

		// If we have an escape sequence and the current state is not escaped
		// mark the next character as escaped.
		if tok == EscapeSequence && !escaped {
			escaped = true
			continue
		}

		buf.WriteString(lit)

		// If the current character is escaped and it is a backslash
		// reset the character handing.
		// This is only triggered if you want to add a \ into a key or a value.
		// Then you have to add "\\".
		if escaped && tok == EscapeSequence {
			escaped = false
		}
	}

	return Ident, buf.String(), nil
}

// VDF files can contain duplicates of keys, when this occurs we need to merge the existing map and the returned map
func mergeMap(m, r map[string]interface{}, key string) map[string]interface{} {
	if _, ok := m[key]; !ok {
		m[key] = r
	} else {
		for k, v := range r {
			m[key].(map[string]interface{})[k] = v
		}
	}

	return m
}
<|endoftext|>


<commit_before>package toscalib

import (
	"errors"
	"fmt"
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
	"log"
)

// Parse a TOSCA document and fill in the structure
// If the structure already contains data
func (toscaStructure *ToscaDefinition) Parse(r io.Reader) error {
	var tempStruct ToscaDefinition
	tempStruct.NodeTypes = make(map[string]NodeType)
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	// Unmarshal the data in an interface
	err = yaml.Unmarshal(data, &tempStruct)
	if err != nil {
		return err
	}
	// for each node, add its corresponding notetype definition to the structure
	// if not present yet

	// index is the node name and nodeTemplate is the corresponding NodeTemplate
	for _, nodeTemplate := range tempStruct.TopologyTemplate.NodeTemplates {
		// nodeType is he node type of the current NodeTemplate
		nodeType := nodeTemplate.Type
		if _, typeIsPresent := tempStruct.NodeTypes[nodeType]; typeIsPresent == false {
			// Get the corresponding asset and add it to the global structure
			data, err := Asset(nodeType)
			if err != nil {
				//  For debuging purpode
				log.Printf("DEBUG: Cannot find the NodeType definition for %v", nodeType)
			}
			var nt map[string]NodeType
			// Unmarshal the data in an interface
			err = yaml.Unmarshal(data, &nt)
			if err != nil {
				return errors.New(fmt.Sprintf("cannot unmarshal %v (%v)", nodeType, err))
			}
			tempStruct.NodeTypes[nodeType] = nt[nodeType]
		}
	}
	// TODO: deal with the import files
	*toscaStructure = tempStruct
	return nil

}
<commit_msg>Added some documentation about the lifecycle. It will be usefull to construct the adjacency matrix for the graph of execution<commit_after>package toscalib

import (
	"gopkg.in/yaml.v2"
	"io"
	"io/ioutil"
)

// Parse a TOSCA document and fill in the structure
// If the structure already contains data
func (toscaStructure *ToscaDefinition) Parse(r io.Reader) error {
	var tempStruct ToscaDefinition
	tempStruct.NodeTypes = make(map[string]NodeType)
	data, err := ioutil.ReadAll(r)
	if err != nil {
		return err
	}

	// Unmarshal the data in an interface
	err = yaml.Unmarshal(data, &tempStruct)
	if err != nil {
		return err
	}
	/*
		// for each node, add its corresponding notetype definition to the structure
		// if not present yet

		// index is the node name and nodeTemplate is the corresponding NodeTemplate
		for _, nodeTemplate := range tempStruct.TopologyTemplate.NodeTemplates {
			// nodeType is he node type of the current NodeTemplate
			nodeType := nodeTemplate.Type
			if _, typeIsPresent := tempStruct.NodeTypes[nodeType]; typeIsPresent == false {
				// Get the corresponding asset and add it to the global structure
				data, err := Asset(nodeType)
				if err != nil {
					//  For debuging purpode
					log.Printf("Cannot find the NodeType definition for %v", nodeType)
				}
				var nt map[string]NodeType
				// Unmarshal the data in an interface
				err = yaml.Unmarshal(data, &nt)
				if err != nil {
					return errors.New(fmt.Sprintf("cannot unmarshal %v (%v)", nodeType, err))
				}
				tempStruct.NodeTypes[nodeType] = nt[nodeType]
			}
		}
	*/
	// TODO: deal with the import files
	// Create the adjacency matrix
	// Every node state must be represented in the matrix
	// Suppose nodeA and nodeB where nodeA requires nodeB
	// nodeB must be setup prior to nodeA
	// There are the following states defined:
	// - initial
	// - creating
	// - created
	// - configuring
	// - configured
	// - starting
	// - started
	// - stopping
	// - deleting
	// - error

	// if nodeA has an artifact for one of the following interface:
	// pre_configure_soure
	// post_configure_source
	// or if nodeB has an artifcat for one of the following interface:
	// pre_configure_target
	// post_configure_target
	// then the workflow is:
	// digraph WorkflowStart {
	//     nodeB:Create() -> nodeA:Create()
	//     nodeA:Create() -> nodeA:PreConfigureSource()
	//     nodeA:PreConfigureSource -> nodeB:PreConfigureTarget()
	//     nodeB:PreConfigureTarget -> nodeA:Configure()
	//     nodeB:PreConfigureTarget -> nodeB:Configure()
	//     nodeA:Configure() -> nodeA:PostConfigureSource()
	//     nodeB:Configure() -> nodeB:PostConfigureTarget()
	//     nodeA:PostConfigureSource() -> nodeA:Start()
	//     nodeB:PostConfigureTarget() -> nodeB:Start()
	//     nodeA:Start() -> nodeB:Start()
	// }
	// otherwise the workflow is
	// digraph WorkflowStart {
	// nodeB:Create() -> nodeB:Configure() -> nodeB:Start() -> nodeA:Create() -> nodeA:Configure() -> nodeA:Start()
	// }
	// FIXME: By now, only the second case is implemented

	*toscaStructure = tempStruct
	return nil

}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "os"
  "os/exec"
  "syscall"
  "io/ioutil"
  "path/filepath"
  "strings"
  "sort"
  "encoding/json"
  "github.com/Masterminds/semver"
)

const pathSep = string(os.PathSeparator)

func main() {
  // Prerequisites

  if "" == os.Getenv("NODIST_PREFIX") {
    fmt.Println("Please set the path to the nodist directory in the NODIST_PREFIX environment variable.")
    os.Exit(40)
  }

  // Determine version spec

  var spec string = ""
  if v, err := getTargetEngine(); err == nil && strings.Trim(string(v), " \r\n") != "" {
    spec = v
    //fmt.Println("Target engine found:'", spec, "'")
  } else
  if v := os.Getenv("NODE_VERSION"); v != "" {
    spec = v
    //fmt.Println("NODE_VERSION found:'", spec, "'")
  } else
  if v = os.Getenv("NODIST_VERSION"); v != "" {
    spec = v
    //fmt.Println("NODIST_VERSION found:'", spec, "'")
  } else
  if v, _, err := getLocalVersion(); err == nil && strings.Trim(string(v), " \r\n") != "" {
    spec = string(v)
    //fmt.Println("Local file found:'", spec, "' @ ", localFile)
  } else
  if v, err := ioutil.ReadFile(os.Getenv("NODIST_PREFIX")+"\\.node-version"); err == nil {
    spec = string(v)
    //fmt.Println("Global file found:'", spec, "'")
  }

  spec = strings.Trim(spec, "v \r\n")

  if spec == "" {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't decide which node version to use. Please set a version.")
    os.Exit(41)
  }

  constraint, err := semver.NewConstraint(spec)

  if err != nil {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't decide which node version to use. Malformatted version spec ", spec, " . Please set a new version.")
    os.Exit(43)
  }

  // Find an installed version matching the spec...

  installed, err := getInstalledVersions()

  if err != nil {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't list installed versions.")
    os.Exit(44)
  }

  version := ""

  for _, v := range installed {
    if constraint.Check(v) {
      version = "v"+v.String()
      break
    }
  }

  if version == "" {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't find an installed version that matches version spec ", spec)
    os.Exit(45)
  }

  // Determine architecture

  x64 := false

  if wantX64 := os.Getenv("NODIST_X64"); wantX64 != "" {
    x64 = (wantX64 == "1")
  }

  // Set up binary path

  var path string
  var nodebin string

  path = os.Getenv("NODIST_PREFIX")+"/v"

  if x64 {
    path += "-x64"
  }

  path = path+"/"+version
  nodebin = path+"/node.exe"

  // Run node!

  cmd := exec.Command(nodebin, os.Args[1:]...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Stdin = os.Stdin
  err = cmd.Run()

  if err != nil {
    exitError, isExitError := err.(*(exec.ExitError))
    if isExitError {
      // You know it. Black Magic...
      os.Exit(exitError.Sys().(syscall.WaitStatus).ExitStatus())
    } else {
      fmt.Println("Sorry, there's a problem with nodist.")
      fmt.Println("Error: ", err)
      os.Exit(42)
    }
  }
}

func getLocalVersion() (version string, file string, error error) {
  dir, err := os.Getwd()

  if err != nil {
    error = err
    return
  }

  dirSlice := strings.Split(dir, pathSep) // D:\Programme\nodist => [D:, Programme, nodist]

  for len(dirSlice) != 1 {
    dir = strings.Join(dirSlice, pathSep)
    file = dir+"\\.node-version"
    v, err := ioutil.ReadFile(file);

    if err == nil {
      version = string(v)
      return
    }

    if !os.IsNotExist(err) {
      error = err // some other error.. bad luck.
      return
    }

    // `$ cd ..`
    dirSlice = dirSlice[:len(dirSlice)-1] // pop the last dir
  }

  version = ""
  return
}

func getInstalledVersions() (versions []*semver.Version, error error) {
  // Determine architecture
  x64 := false
  if wantX64 := os.Getenv("NODIST_X64"); wantX64 != "" {
    x64 = (wantX64 == "1")
  }
  // construct path to version dir
  path := os.Getenv("NODIST_PREFIX")+"/v"
  if x64 {
    path += "-x64"
  }

  dirs, err := ioutil.ReadDir(path)
  if err != nil {
    error = err
    return
  }

  versions = make([]*semver.Version, len(dirs))
  for i, dir := range dirs {
    v, err := semver.NewVersion(dir.Name())
    if err == nil {
      versions[i] = v
    }
  }

  sort.Sort(semver.Collection(versions))

  return
}

func getTargetEngine() (spec string, error error) {
  if len(os.Args) < 2 {
    return
  }

  targetFile := os.Args[1]

  dir := filepath.Dir(targetFile)
  if !filepath.IsAbs(dir) {
    cwd, err := os.Getwd()
    if err != nil {
      error = err
      return
    }
    dir = filepath.Join(cwd, dir)
  }

  //fmt.Println("getTargetEngine: targetDir:", dir)

  dirSlice := strings.Split(dir, pathSep) // D:\Programme\nodist => [D:, Programme, nodist]

  spec = ""

  for len(dirSlice) != 1 {
    dir = strings.Join(dirSlice, pathSep)
    file := dir+"\\package.json"
    rawPackageJSON, err := ioutil.ReadFile(file);
    //fmt.Println("getTargetEngine: ReadFile ", file)
    if err == nil {
      // no error handling for parsing, cause we don't want to use a different package.json if we've already found one
      spec, error = getVerSpecFromPackageJSON(rawPackageJSON)
      return
    }

    if !os.IsNotExist(err) {
      error = err // some other error.. bad luck.
      return
    }

    // `$ cd ..`
    dirSlice = dirSlice[:len(dirSlice)-1] // pop the last dir
  }

  return
}

func getVerSpecFromPackageJSON(rawPackageJSON []byte) (spec string, err error) {
  type PackageJSON struct {
    Engines struct {
      Node string
    }
  }
  var packageJSON PackageJSON
  err = json.Unmarshal(rawPackageJSON, &packageJSON)

  if err == nil {
    spec = packageJSON.Engines.Node
    //fmt.Println("getVerSpecFromPackageJSON: %+v", packageJSON)
    return
  }

  //fmt.Println("getVerSpecFromPackageJSON: error:", err.Error())

  // incorrect JSON -- bad luck
  return
}<commit_msg>Fix local switching: Use the target script's dir as the base dir<commit_after>package main

import (
  "fmt"
  "os"
  "os/exec"
  "syscall"
  "io/ioutil"
  "path/filepath"
  "strings"
  "sort"
  "encoding/json"
  "github.com/Masterminds/semver"
)

const pathSep = string(os.PathSeparator)

func main() {
  // Prerequisites

  if "" == os.Getenv("NODIST_PREFIX") {
    fmt.Println("Please set the path to the nodist directory in the NODIST_PREFIX environment variable.")
    os.Exit(40)
  }

  // Determine version spec

  var spec string = ""
  if v := os.Getenv("NODE_VERSION"); v != "" {
    spec = v
    //fmt.Println("NODE_VERSION found:'", spec, "'")
  } else
  if v = os.Getenv("NODIST_VERSION"); v != "" {
    spec = v
    //fmt.Println("NODIST_VERSION found:'", spec, "'")
  } else
  if v, err := getTargetEngine(); err == nil && strings.Trim(string(v), " \r\n"$
    spec = v
    //fmt.Println("Target engine found:'", spec, "'")
  } else
  if v, _, err := getLocalVersion(); err == nil && strings.Trim(string(v), " \r\n") != "" {
    spec = string(v)
    //fmt.Println("Local file found:'", spec, "' @ ", localFile)
  } else
  if v, err := ioutil.ReadFile(os.Getenv("NODIST_PREFIX")+"\\.node-version"); err == nil {
    spec = string(v)
    //fmt.Println("Global file found:'", spec, "'")
  }

  spec = strings.Trim(spec, "v \r\n")

  if spec == "" {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't decide which node version to use. Please set a version.")
    os.Exit(41)
  }

  constraint, err := semver.NewConstraint(spec)

  if err != nil {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't decide which node version to use. Malformatted version spec ", spec, " . Please set a new version.")
    os.Exit(43)
  }

  // Find an installed version matching the spec...

  installed, err := getInstalledVersions()

  if err != nil {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't list installed versions.")
    os.Exit(44)
  }

  version := ""

  for _, v := range installed {
    if constraint.Check(v) {
      version = "v"+v.String()
      break
    }
  }

  if version == "" {
    fmt.Println("Sorry, there's a problem with nodist. Couldn't find an installed version that matches version spec ", spec)
    os.Exit(45)
  }

  // Determine architecture

  x64 := false

  if wantX64 := os.Getenv("NODIST_X64"); wantX64 != "" {
    x64 = (wantX64 == "1")
  }

  // Set up binary path

  var path string
  var nodebin string

  path = os.Getenv("NODIST_PREFIX")+"/v"

  if x64 {
    path += "-x64"
  }

  path = path+"/"+version
  nodebin = path+"/node.exe"

  // Run node!

  cmd := exec.Command(nodebin, os.Args[1:]...)
  cmd.Stdout = os.Stdout
  cmd.Stderr = os.Stderr
  cmd.Stdin = os.Stdin
  err = cmd.Run()

  if err != nil {
    exitError, isExitError := err.(*(exec.ExitError))
    if isExitError {
      // You know it. Black Magic...
      os.Exit(exitError.Sys().(syscall.WaitStatus).ExitStatus())
    } else {
      fmt.Println("Sorry, there's a problem with nodist.")
      fmt.Println("Error: ", err)
      os.Exit(42)
    }
  }
}

func getLocalVersion() (version string, file string, error error) {
  if len(os.Args) < 2 {
    dir, err := os.Getwd()
    if err != nil {
      error = err
      return
    }
  }else{
    targetFile := os.Args[1]
    dir := filepath.Dir(targetFile)

    if !filepath.IsAbs(dir) {
      cwd, err := os.Getwd()
      if err != nil {
        error = err
        return
      }
      dir = filepath.Join(cwd, dir)
    }
  }

  dirSlice := strings.Split(dir, pathSep) // D:\Programme\nodist => [D:, Programme, nodist]

  for len(dirSlice) != 1 {
    dir = strings.Join(dirSlice, pathSep)
    file = dir+"\\.node-version"
    v, err := ioutil.ReadFile(file);

    if err == nil {
      version = string(v)
      return
    }

    if !os.IsNotExist(err) {
      error = err // some other error.. bad luck.
      return
    }

    // `$ cd ..`
    dirSlice = dirSlice[:len(dirSlice)-1] // pop the last dir
  }

  version = ""
  return
}

func getInstalledVersions() (versions []*semver.Version, error error) {
  // Determine architecture
  x64 := false
  if wantX64 := os.Getenv("NODIST_X64"); wantX64 != "" {
    x64 = (wantX64 == "1")
  }
  // construct path to version dir
  path := os.Getenv("NODIST_PREFIX")+"/v"
  if x64 {
    path += "-x64"
  }

  dirs, err := ioutil.ReadDir(path)
  if err != nil {
    error = err
    return
  }

  versions = make([]*semver.Version, len(dirs))
  for i, dir := range dirs {
    v, err := semver.NewVersion(dir.Name())
    if err == nil {
      versions[i] = v
    }
  }

  sort.Sort(semver.Collection(versions))

  return
}

func getTargetEngine() (spec string, error error) {
  if len(os.Args) < 2 {
    return
  }

  targetFile := os.Args[1]

  dir := filepath.Dir(targetFile)
  if !filepath.IsAbs(dir) {
    cwd, err := os.Getwd()
    if err != nil {
      error = err
      return
    }
    dir = filepath.Join(cwd, dir)
  }

  //fmt.Println("getTargetEngine: targetDir:", dir)

  dirSlice := strings.Split(dir, pathSep) // D:\Programme\nodist => [D:, Programme, nodist]

  spec = ""

  for len(dirSlice) != 1 {
    dir = strings.Join(dirSlice, pathSep)
    file := dir+"\\package.json"
    rawPackageJSON, err := ioutil.ReadFile(file);
    //fmt.Println("getTargetEngine: ReadFile ", file)
    if err == nil {
      // no error handling for parsing, cause we don't want to use a different package.json if we've already found one
      spec, error = getVerSpecFromPackageJSON(rawPackageJSON)
      return
    }

    if !os.IsNotExist(err) {
      error = err // some other error.. bad luck.
      return
    }

    // `$ cd ..`
    dirSlice = dirSlice[:len(dirSlice)-1] // pop the last dir
  }

  return
}

func getVerSpecFromPackageJSON(rawPackageJSON []byte) (spec string, err error) {
  type PackageJSON struct {
    Engines struct {
      Node string
    }
  }
  var packageJSON PackageJSON
  err = json.Unmarshal(rawPackageJSON, &packageJSON)

  if err == nil {
    spec = packageJSON.Engines.Node
    //fmt.Println("getVerSpecFromPackageJSON: %+v", packageJSON)
    return
  }

  //fmt.Println("getVerSpecFromPackageJSON: error:", err.Error())

  // incorrect JSON -- bad luck
  return
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

import (
	"errors"
	"fmt"
	"reflect"
)

const (
	IfStmtName        = "IF"
	SwitchStmtName    = "SWITCH"
	LoopStmtName      = "LOOP"
	RangeLoopStmtName = "RANGE_LOOP"
	AssignStmtName    = "ASSIGN"
	DeclStmtName      = "DECL"
	ReturnStmtName    = "RETURN"
	ExprStmtName      = "EXPR"
	TryStmtName       = "TRY"
	ThrowStmtName     = "THROW"
	OtherStmtName     = "OTHER"
)

type Stmt interface{}

func newStmt(m map[string]interface{}) (Stmt, error) {
	errPrefix := "src/stmt"

	typ, ok := m["statement_name"]
	if !ok {
		return nil, addDebugInfo(fmt.Errorf(
			"%s: field 'type' does not exist", errPrefix))
	}

	switch typ {
	case IfStmtName:
		return newIfStmt(m)
	case SwitchStmtName:
		return newSwitchStmt(m)
	case LoopStmtName:
		return newLoopStmt(m)
	case AssignStmtName:
		return newAssignStmt(m)
	case DeclStmtName:
		return newDeclStmt(m)
	case ReturnStmtName:
		return newReturnStmt(m)
	case ExprStmtName:
		return newExprStmt(m)
	case TryStmtName:
		return newTryStmt(m)
	case ThrowStmtName:
		return newThrowStmt(m)
	case OtherStmtName:
		return newOtherStmt(m)
	}

	return nil, addDebugInfo(errors.New("unknown statement type"))
}

func newStmtsSlice(key, errPrefix string, m map[string]interface{}) ([]Stmt, error) {
	var err error
	var s reflect.Value

	stmtsMap, ok := m[key]
	if !ok || stmtsMap == nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	}

	if s = reflect.ValueOf(stmtsMap); s.Kind() != reflect.Slice {
		return nil, addDebugInfo(fmt.Errorf(
			"%s: field '%s' is supposed to be a slice", errPrefix, key))
	}

	stmts := make([]Stmt, s.Len(), s.Len())
	for i := 0; i < s.Len(); i++ {
		stmt := s.Index(i).Interface()
		if stmt == nil {
			continue
		}

		switch stmt.(type) {
		case map[string]interface{}:
			if stmts[i], err = newStmt(stmt.(map[string]interface{})); err != nil {
				return nil, addDebugInfo(err)
			}
		default:
			return nil, addDebugInfo(fmt.Errorf(
				"%s: '%s' must be a map[string]interface{}, found %v",
				errPrefix, key, reflect.TypeOf(stmt)))
		}
	}

	return stmts, nil
}
<commit_msg>src: add missing statement in stmt.go<commit_after>// Copyright 2014-2015 The DevMine Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package src

import (
	"errors"
	"fmt"
	"reflect"
)

const (
	IfStmtName        = "IF"
	SwitchStmtName    = "SWITCH"
	LoopStmtName      = "LOOP"
	RangeLoopStmtName = "RANGE_LOOP"
	AssignStmtName    = "ASSIGN"
	DeclStmtName      = "DECL"
	ReturnStmtName    = "RETURN"
	ExprStmtName      = "EXPR"
	TryStmtName       = "TRY"
	ThrowStmtName     = "THROW"
	OtherStmtName     = "OTHER"
)

type Stmt interface{}

func newStmt(m map[string]interface{}) (Stmt, error) {
	errPrefix := "src/stmt"

	typ, ok := m["statement_name"]
	if !ok {
		return nil, addDebugInfo(fmt.Errorf(
			"%s: field 'type' does not exist", errPrefix))
	}

	switch typ {
	case IfStmtName:
		return newIfStmt(m)
	case SwitchStmtName:
		return newSwitchStmt(m)
	case LoopStmtName:
		return newLoopStmt(m)
	case RangeLoopStmtName:
		return newRangeLoopStmt(m)
	case AssignStmtName:
		return newAssignStmt(m)
	case DeclStmtName:
		return newDeclStmt(m)
	case ReturnStmtName:
		return newReturnStmt(m)
	case ExprStmtName:
		return newExprStmt(m)
	case TryStmtName:
		return newTryStmt(m)
	case ThrowStmtName:
		return newThrowStmt(m)
	case OtherStmtName:
		return newOtherStmt(m)
	}

	return nil, addDebugInfo(errors.New("unknown statement type"))
}

func newStmtsSlice(key, errPrefix string, m map[string]interface{}) ([]Stmt, error) {
	var err error
	var s reflect.Value

	stmtsMap, ok := m[key]
	if !ok || stmtsMap == nil {
		// XXX It is not possible to add debug info on this error because it is
		// required that this error be en "errNotExist".
		return nil, errNotExist
	}

	if s = reflect.ValueOf(stmtsMap); s.Kind() != reflect.Slice {
		return nil, addDebugInfo(fmt.Errorf(
			"%s: field '%s' is supposed to be a slice", errPrefix, key))
	}

	stmts := make([]Stmt, s.Len(), s.Len())
	for i := 0; i < s.Len(); i++ {
		stmt := s.Index(i).Interface()
		if stmt == nil {
			continue
		}

		switch stmt.(type) {
		case map[string]interface{}:
			if stmts[i], err = newStmt(stmt.(map[string]interface{})); err != nil {
				return nil, addDebugInfo(err)
			}
		default:
			return nil, addDebugInfo(fmt.Errorf(
				"%s: '%s' must be a map[string]interface{}, found %v",
				errPrefix, key, reflect.TypeOf(stmt)))
		}
	}

	return stmts, nil
}
<|endoftext|>


<commit_before>package main

import "sort"

type View struct {
	height int
	query  string
	lines  []string
	index  int
}

func (v *View) Index() int {
	return v.index
}

func (v *View) Selected() string {
	return v.lines[v.index]
}

func (v *View) Query() string {
	return v.query
}

func (v *View) Down() *View {
	if v.index < len(v.lines)-1 {
		v.index++
	}
	return v
}

func (v *View) Up() {
	if v.index > 0 {
		v.index--
	}
}

type Picker struct {
	all     []string
	visible int
}

func NewPicker(candidates []string, visible int) *Picker {
	return &Picker{
		all:     candidates,
		visible: visible,
	}
}

type lessfn func(int, int) bool

type sortableCandidates struct {
	list []string
	by   lessfn
}

func (c sortableCandidates) Len() int           { return len(c.list) }
func (c sortableCandidates) Swap(i, j int)      { c.list[i], c.list[j] = c.list[j], c.list[i] }
func (c sortableCandidates) Less(i, j int) bool { return c.by(i, j) }

func scoreByQuery(candidates []string, query string) lessfn {
	return func(i, j int) bool {
		return Score(candidates[i], query) > Score(candidates[j], query)
	}
}

func fisrtZero(c []string, query string) int {
	for i, str := range c {
		if Score(str, query) == 0.0 {
			return i
		}
	}
	return len(c)
}

func (p *Picker) Answer(query string) *View {
	candidates := &sortableCandidates{
		list: p.all,
		by:   scoreByQuery(p.all, query),
	}
	sort.Sort(candidates)

	//XXX: We are rescoring all the candidates
	spread := fisrtZero(p.all, query)
	if spread > p.visible {
		spread = p.visible
	}

	return &View{
		index:  0,
		height: p.visible,
		lines:  p.all[:spread],
		query:  query,
	}

	// score `all`
	// sort `all`
	// pick best `n` candidates
	// restore `index` to 0
}
<commit_msg>Introduce Candidate type inside Picker<commit_after>package main

import "sort"

type View struct {
	height int
	query  string
	lines  []string
	index  int
}

func (v *View) Index() int {
	return v.index
}

func (v *View) Selected() string {
	return v.lines[v.index]
}

func (v *View) Query() string {
	return v.query
}

func (v *View) Down() *View {
	if v.index < len(v.lines)-1 {
		v.index++
	}
	return v
}

func (v *View) Up() {
	if v.index > 0 {
		v.index--
	}
}

type Picker struct {
	all     []Candidate
	visible int
}

func NewPicker(candidates []string, visible int) *Picker {

	all := make([]Candidate, len(candidates))
	for i, c := range candidates {
		all[i] = NewCandidate(c)
	}

	return &Picker{
		all:     all,
		visible: visible,
	}
}

type Candidate struct {
	value string
	score float64
}

func NewCandidate(s string) Candidate {
	return Candidate{
		value: s,
		score: 1.0,
	}
}

type CandidateSlice []Candidate

func (cs CandidateSlice) Len() int           { return len(cs) }
func (cs CandidateSlice) Swap(i, j int)      { cs[i], cs[j] = cs[j], cs[i] }
func (cs CandidateSlice) Less(i, j int) bool { return cs[i].score > cs[j].score }

func (p *Picker) Answer(query string) *View {
	for i, c := range p.all {
		p.all[i].score = Score(c.value, query)
	}
	sort.Sort(CandidateSlice(p.all))

	lines := []string{}
	for i := 0; i < p.visible; i++ {
		if p.all[i].score == 0.0 {
			break
		}
		lines = append(lines, p.all[i].value)
	}

	return &View{
		index:  0,
		height: p.visible,
		lines:  lines,
		query:  query,
	}
}
<|endoftext|>


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

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

// A PIndex represents a "physical" index or a index "partition".

const PINDEX_META_FILENAME string = "PINDEX_META"
const pindexPathSuffix string = ".pindex"

type PIndex struct {
	Name             string     `json:"name"`
	UUID             string     `json:"uuid"`
	IndexType        string     `json:"indexType"`
	IndexName        string     `json:"indexName"`
	IndexUUID        string     `json:"indexUUID"`
	IndexParams      string     `json:"indexParams"`
	SourceType       string     `json:"sourceType"`
	SourceName       string     `json:"sourceName"`
	SourceUUID       string     `json:"sourceUUID"`
	SourceParams     string     `json:"sourceParams"`
	SourcePartitions string     `json:"sourcePartitions"`
	Path             string     `json:"-"` // Transient, not persisted.
	Impl             PIndexImpl `json:"-"` // Transient, not persisted.
	Dest             Dest       `json:"-"` // Transient, not persisted.
}

func (p *PIndex) Close(remove bool) error {
	err := p.Impl.Close()
	if err != nil {
		return err
	}

	if remove {
		os.RemoveAll(p.Path)
	}

	return nil
}

func NewPIndex(mgr *Manager, name, uuid,
	indexType, indexName, indexUUID, indexParams,
	sourceType, sourceName, sourceUUID, sourceParams, sourcePartitions string,
	path string) (*PIndex, error) {
	var pindex *PIndex

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := NewPIndexImpl(indexType, indexParams, path, restart)
	if err != nil {
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: new indexType: %s, indexParams: %s,"+
			" path: %s, err: %s", indexType, indexParams, path, err)
	}

	pindex = &PIndex{
		Name:             name,
		UUID:             uuid,
		IndexType:        indexType,
		IndexName:        indexName,
		IndexUUID:        indexUUID,
		IndexParams:      indexParams,
		SourceType:       sourceType,
		SourceName:       sourceName,
		SourceUUID:       sourceUUID,
		SourceParams:     sourceParams,
		SourcePartitions: sourcePartitions,
		Path:             path,
		Impl:             impl,
		Dest:             dest,
	}
	buf, err := json.Marshal(pindex)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+PINDEX_META_FILENAME,
		buf, 0600)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: could not save PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	return pindex, nil
}

// NOTE: Path argument must be a directory.
func OpenPIndex(mgr *Manager, path string) (*PIndex, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + PINDEX_META_FILENAME)
	if err != nil {
		return nil, fmt.Errorf("error: could not load PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	pindex := &PIndex{}
	err = json.Unmarshal(buf, pindex)
	if err != nil {
		return nil, fmt.Errorf("error: could not parse pindex json,"+
			" path: %s, err: %v", path, err)
	}

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := OpenPIndexImpl(pindex.IndexType, path, restart)
	if err != nil {
		return nil, fmt.Errorf("error: could not open indexType: %s, path: %s, err: %v",
			pindex.IndexType, path, err)
	}

	pindex.Path = path
	pindex.Impl = impl
	pindex.Dest = dest

	return pindex, nil
}

func PIndexPath(dataDir, pindexName string) string {
	// TODO: path security checks / mapping here; ex: "../etc/pswd"
	return dataDir + string(os.PathSeparator) + pindexName + pindexPathSuffix
}

func ParsePIndexPath(dataDir, pindexPath string) (string, bool) {
	if !strings.HasSuffix(pindexPath, pindexPathSuffix) {
		return "", false
	}
	prefix := dataDir + string(os.PathSeparator)
	if !strings.HasPrefix(pindexPath, prefix) {
		return "", false
	}
	pindexName := pindexPath[len(prefix):]
	pindexName = pindexName[0 : len(pindexName)-len(pindexPathSuffix)]
	return pindexName, true
}

// ---------------------------------------------------------

type RemotePlanPIndex struct {
	PlanPIndex *PlanPIndex
	NodeDef    *NodeDef
}

// Returns a non-overlapping, disjoint set (or cut) of PIndexes
// (either local or remote) that cover all the partitons of an index
// so that the caller can perform scatter/gather queries, etc.  Only
// PlanPIndexes on wanted nodes that pass the wantNode filter will be
// returned.
//
// TODO: Perhaps need a tighter check around indexUUID, as the current
// implementation might have a race where old pindexes with a matching
// (but outdated) indexUUID might be chosen.
//
// TODO: This implementation currently always favors the local node's
// pindex, but should it?  Perhaps a remote node is more up-to-date
// than the local pindex?
//
// TODO: We should favor the most up-to-date node rather than
// the first one that we run into here?  But, perhaps the most
// up-to-date node is also the most overloaded?  Or, perhaps
// the planner may be trying to rebalance away the most
// up-to-date node and hitting it with load just makes the
// rebalance take longer?
func (mgr *Manager) CoveringPIndexes(indexName, indexUUID string,
	wantNode func(*PlanPIndexNode) bool) (
	localPIndexes []*PIndex, remotePlanPIndexes []*RemotePlanPIndex, err error) {
	nodeDefs, _, err := CfgGetNodeDefs(mgr.Cfg(), NODE_DEFS_WANTED)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve wanted nodeDefs, err: %v", err)
	}

	// Returns true if the node has the "pindex" tag.
	nodeDoesPIndexes := func(nodeUUID string) (*NodeDef, bool) {
		for _, nodeDef := range nodeDefs.NodeDefs {
			if nodeDef.UUID == nodeUUID {
				if len(nodeDef.Tags) <= 0 {
					return nodeDef, true
				}
				for _, tag := range nodeDef.Tags {
					if tag == "pindex" {
						return nodeDef, true
					}
				}
			}
		}
		return nil, false
	}

	_, allPlanPIndexes, err := mgr.GetPlanPIndexes(false)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve allPlanPIndexes, err: %v", err)
	}

	planPIndexes, exists := allPlanPIndexes[indexName]
	if !exists || len(planPIndexes) <= 0 {
		return nil, nil, fmt.Errorf("no planPIndexes for indexName: %s", indexName)
	}

	localPIndexes = make([]*PIndex, 0)
	remotePlanPIndexes = make([]*RemotePlanPIndex, 0)

	_, pindexes := mgr.CurrentMaps()

	selfUUID := mgr.UUID()
	_, selfDoesPIndexes := nodeDoesPIndexes(selfUUID)

build_alias_loop:
	for _, planPIndex := range planPIndexes {
		// First check whether this local node serves that planPIndex.
		if selfDoesPIndexes &&
			wantNode(planPIndex.Nodes[selfUUID]) {
			localPIndex, exists := pindexes[planPIndex.Name]
			if exists &&
				localPIndex != nil &&
				localPIndex.Name == planPIndex.Name &&
				localPIndex.IndexName == indexName &&
				(indexUUID == "" || localPIndex.IndexUUID == indexUUID) {
				localPIndexes = append(localPIndexes, localPIndex)
				continue build_alias_loop
			}
		}

		// Otherwise, look for a remote node that serves that planPIndex.
		for nodeUUID, planPIndexNode := range planPIndex.Nodes {
			if nodeUUID != selfUUID {
				nodeDef, ok := nodeDoesPIndexes(nodeUUID)
				if ok && wantNode(planPIndexNode) {
					remotePlanPIndexes = append(remotePlanPIndexes, &RemotePlanPIndex{
						PlanPIndex: planPIndex,
						NodeDef:    nodeDef,
					})
					continue build_alias_loop
				}
			}
		}

		return nil, nil, fmt.Errorf("no node covers planPIndex: %#v", planPIndex)
	}

	return localPIndexes, remotePlanPIndexes, nil
}

// --------------------------------------------------------

type ConsistencyParams struct {
	Level string `json:"level"`

	// Keyed by indexName, value is {partition => seqNum}.
	Vectors map[string]map[string]uint64 `json:"vectors"`

	// TODO: what about changes to vbucket UUID?
}

func (p *PIndex) ConsistencyWaitForAtLeast(
	consistencyVectors map[string]uint64) error {
	return nil // TODO.
}
<commit_msg>placeholder for ConsistencyWaitForAtLeast()<commit_after>//  Copyright (c) 2014 Couchbase, Inc.
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the
//  License. You may obtain a copy of the License at
//    http://www.apache.org/licenses/LICENSE-2.0
//  Unless required by applicable law or agreed to in writing,
//  software distributed under the License is distributed on an "AS
//  IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//  express or implied. See the License for the specific language
//  governing permissions and limitations under the License.

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
)

// A PIndex represents a "physical" index or a index "partition".

const PINDEX_META_FILENAME string = "PINDEX_META"
const pindexPathSuffix string = ".pindex"

type PIndex struct {
	Name             string     `json:"name"`
	UUID             string     `json:"uuid"`
	IndexType        string     `json:"indexType"`
	IndexName        string     `json:"indexName"`
	IndexUUID        string     `json:"indexUUID"`
	IndexParams      string     `json:"indexParams"`
	SourceType       string     `json:"sourceType"`
	SourceName       string     `json:"sourceName"`
	SourceUUID       string     `json:"sourceUUID"`
	SourceParams     string     `json:"sourceParams"`
	SourcePartitions string     `json:"sourcePartitions"`
	Path             string     `json:"-"` // Transient, not persisted.
	Impl             PIndexImpl `json:"-"` // Transient, not persisted.
	Dest             Dest       `json:"-"` // Transient, not persisted.
}

func (p *PIndex) Close(remove bool) error {
	err := p.Impl.Close()
	if err != nil {
		return err
	}

	if remove {
		os.RemoveAll(p.Path)
	}

	return nil
}

func NewPIndex(mgr *Manager, name, uuid,
	indexType, indexName, indexUUID, indexParams,
	sourceType, sourceName, sourceUUID, sourceParams, sourcePartitions string,
	path string) (*PIndex, error) {
	var pindex *PIndex

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := NewPIndexImpl(indexType, indexParams, path, restart)
	if err != nil {
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: new indexType: %s, indexParams: %s,"+
			" path: %s, err: %s", indexType, indexParams, path, err)
	}

	pindex = &PIndex{
		Name:             name,
		UUID:             uuid,
		IndexType:        indexType,
		IndexName:        indexName,
		IndexUUID:        indexUUID,
		IndexParams:      indexParams,
		SourceType:       sourceType,
		SourceName:       sourceName,
		SourceUUID:       sourceUUID,
		SourceParams:     sourceParams,
		SourcePartitions: sourcePartitions,
		Path:             path,
		Impl:             impl,
		Dest:             dest,
	}
	buf, err := json.Marshal(pindex)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, err
	}

	err = ioutil.WriteFile(path+string(os.PathSeparator)+PINDEX_META_FILENAME,
		buf, 0600)
	if err != nil {
		impl.Close()
		os.RemoveAll(path)
		return nil, fmt.Errorf("error: could not save PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	return pindex, nil
}

// NOTE: Path argument must be a directory.
func OpenPIndex(mgr *Manager, path string) (*PIndex, error) {
	buf, err := ioutil.ReadFile(path + string(os.PathSeparator) + PINDEX_META_FILENAME)
	if err != nil {
		return nil, fmt.Errorf("error: could not load PINDEX_META_FILENAME,"+
			" path: %s, err: %v", path, err)
	}

	pindex := &PIndex{}
	err = json.Unmarshal(buf, pindex)
	if err != nil {
		return nil, fmt.Errorf("error: could not parse pindex json,"+
			" path: %s, err: %v", path, err)
	}

	restart := func() {
		go func() {
			mgr.ClosePIndex(pindex)
			mgr.Kick("restart-pindex")
		}()
	}

	impl, dest, err := OpenPIndexImpl(pindex.IndexType, path, restart)
	if err != nil {
		return nil, fmt.Errorf("error: could not open indexType: %s, path: %s, err: %v",
			pindex.IndexType, path, err)
	}

	pindex.Path = path
	pindex.Impl = impl
	pindex.Dest = dest

	return pindex, nil
}

func PIndexPath(dataDir, pindexName string) string {
	// TODO: path security checks / mapping here; ex: "../etc/pswd"
	return dataDir + string(os.PathSeparator) + pindexName + pindexPathSuffix
}

func ParsePIndexPath(dataDir, pindexPath string) (string, bool) {
	if !strings.HasSuffix(pindexPath, pindexPathSuffix) {
		return "", false
	}
	prefix := dataDir + string(os.PathSeparator)
	if !strings.HasPrefix(pindexPath, prefix) {
		return "", false
	}
	pindexName := pindexPath[len(prefix):]
	pindexName = pindexName[0 : len(pindexName)-len(pindexPathSuffix)]
	return pindexName, true
}

// ---------------------------------------------------------

type RemotePlanPIndex struct {
	PlanPIndex *PlanPIndex
	NodeDef    *NodeDef
}

// Returns a non-overlapping, disjoint set (or cut) of PIndexes
// (either local or remote) that cover all the partitons of an index
// so that the caller can perform scatter/gather queries, etc.  Only
// PlanPIndexes on wanted nodes that pass the wantNode filter will be
// returned.
//
// TODO: Perhaps need a tighter check around indexUUID, as the current
// implementation might have a race where old pindexes with a matching
// (but outdated) indexUUID might be chosen.
//
// TODO: This implementation currently always favors the local node's
// pindex, but should it?  Perhaps a remote node is more up-to-date
// than the local pindex?
//
// TODO: We should favor the most up-to-date node rather than
// the first one that we run into here?  But, perhaps the most
// up-to-date node is also the most overloaded?  Or, perhaps
// the planner may be trying to rebalance away the most
// up-to-date node and hitting it with load just makes the
// rebalance take longer?
func (mgr *Manager) CoveringPIndexes(indexName, indexUUID string,
	wantNode func(*PlanPIndexNode) bool) (
	localPIndexes []*PIndex, remotePlanPIndexes []*RemotePlanPIndex, err error) {
	nodeDefs, _, err := CfgGetNodeDefs(mgr.Cfg(), NODE_DEFS_WANTED)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve wanted nodeDefs, err: %v", err)
	}

	// Returns true if the node has the "pindex" tag.
	nodeDoesPIndexes := func(nodeUUID string) (*NodeDef, bool) {
		for _, nodeDef := range nodeDefs.NodeDefs {
			if nodeDef.UUID == nodeUUID {
				if len(nodeDef.Tags) <= 0 {
					return nodeDef, true
				}
				for _, tag := range nodeDef.Tags {
					if tag == "pindex" {
						return nodeDef, true
					}
				}
			}
		}
		return nil, false
	}

	_, allPlanPIndexes, err := mgr.GetPlanPIndexes(false)
	if err != nil {
		return nil, nil, fmt.Errorf("could not retrieve allPlanPIndexes, err: %v", err)
	}

	planPIndexes, exists := allPlanPIndexes[indexName]
	if !exists || len(planPIndexes) <= 0 {
		return nil, nil, fmt.Errorf("no planPIndexes for indexName: %s", indexName)
	}

	localPIndexes = make([]*PIndex, 0)
	remotePlanPIndexes = make([]*RemotePlanPIndex, 0)

	_, pindexes := mgr.CurrentMaps()

	selfUUID := mgr.UUID()
	_, selfDoesPIndexes := nodeDoesPIndexes(selfUUID)

build_alias_loop:
	for _, planPIndex := range planPIndexes {
		// First check whether this local node serves that planPIndex.
		if selfDoesPIndexes &&
			wantNode(planPIndex.Nodes[selfUUID]) {
			localPIndex, exists := pindexes[planPIndex.Name]
			if exists &&
				localPIndex != nil &&
				localPIndex.Name == planPIndex.Name &&
				localPIndex.IndexName == indexName &&
				(indexUUID == "" || localPIndex.IndexUUID == indexUUID) {
				localPIndexes = append(localPIndexes, localPIndex)
				continue build_alias_loop
			}
		}

		// Otherwise, look for a remote node that serves that planPIndex.
		for nodeUUID, planPIndexNode := range planPIndex.Nodes {
			if nodeUUID != selfUUID {
				nodeDef, ok := nodeDoesPIndexes(nodeUUID)
				if ok && wantNode(planPIndexNode) {
					remotePlanPIndexes = append(remotePlanPIndexes, &RemotePlanPIndex{
						PlanPIndex: planPIndex,
						NodeDef:    nodeDef,
					})
					continue build_alias_loop
				}
			}
		}

		return nil, nil, fmt.Errorf("no node covers planPIndex: %#v", planPIndex)
	}

	return localPIndexes, remotePlanPIndexes, nil
}

// --------------------------------------------------------

type ConsistencyParams struct {
	Level string `json:"level"`

	// Keyed by indexName, value is {partition => seqNum}.
	Vectors map[string]map[string]uint64 `json:"vectors"`

	// TODO: Can user specify certain partition UUID (like vbucket UUID)?
}

func (p *PIndex) ConsistencyWaitForAtLeast(
	consistencyVectors map[string]uint64) error {
	if len(consistencyVectors) <= 0 {
		return nil
	}
	// TODO.
	return nil
}
<|endoftext|>


<commit_before>package pinyin

import (
	"regexp"
	"strings"
)

// Meta
const (
	Version   = "0.6.0"
	Author    = "mozillazg, 闲耘"
	License   = "MIT"
	Copyright = "Copyright (c) 2016 mozillazg, 闲耘"
)

// 拼音风格(推荐)
const (
	Normal      = 0 // 普通风格，不带声调（默认风格）。如： zhong guo
	Tone        = 1 // 声调风格1，拼音声调在韵母第一个字母上。如： zhōng guó
	Tone2       = 2 // 声调风格2，即拼音声调在各个拼音之后，用数字 [0-4] 进行表示。如： zho1ng guo2
	Initials    = 3 // 声母风格，只返回各个拼音的声母部分。如： zh g
	FirstLetter = 4 // 首字母风格，只返回拼音的首字母部分。如： z g
	Finals      = 5 // 韵母风格1，只返回各个拼音的韵母部分，不带声调。如： ong uo
	FinalsTone  = 6 // 韵母风格2，带声调，声调在韵母第一个字母上。如： ōng uó
	FinalsTone2 = 7 // 韵母风格2，带声调，声调在各个拼音之后，用数字 [0-4] 进行表示。如： o1ng uo2
)

// 拼音风格(兼容之前的版本)
const (
	NORMAL       = Normal
	TONE         = Tone
	TONE2        = Tone2
	INITIALS     = Initials
	FIRST_LETTER = FirstLetter
	FINALS       = Finals
	FINALS_TONE  = FinalsTone
	FINALS_TONE2 = FinalsTone2
)

// 声母表
var initialArray = strings.Split(
	"b,p,m,f,d,t,n,l,g,k,h,j,q,x,r,zh,ch,sh,z,c,s",
	",",
)

// 所有带声调的字符
var rePhoneticSymbolSource = func(m map[string]string) string {
	s := ""
	for k := range m {
		s = s + k
	}
	return s
}(phoneticSymbol)

// 匹配带声调字符的正则表达式
var rePhoneticSymbol = regexp.MustCompile("[" + rePhoneticSymbolSource + "]")

// 匹配使用数字标识声调的字符的正则表达式
var reTone2 = regexp.MustCompile("([aeoiuvnm])([0-4])$")

// Args 配置信息
type Args struct {
	Style     int    // 拼音风格（默认： Normal)
	Heteronym bool   // 是否启用多音字模式（默认：禁用）
	Separator string // Slug 中使用的分隔符（默认：-)

	// 处理没有拼音的字符（默认忽略没有拼音的字符）
	// 函数返回的 slice 的长度为0 则表示忽略这个字符
	Fallback func(r rune, a Args) []string
}

// Style 默认配置：风格
var Style = Normal

// Heteronym 默认配置：是否启用多音字模式
var Heteronym = false

// Separator 默认配置： `Slug` 中 Join 所用的分隔符
var Separator = "-"

// Fallback 默认配置: 如何处理没有拼音的字符(忽略这个字符)
var Fallback = func(r rune, a Args) []string {
	return []string{}
}

var finalExceptionsMap = map[string]string{
	"ū": "ǖ",
	"ú": "ǘ",
	"ǔ": "ǚ",
	"ù": "ǜ",
}
var reFinalExceptions = regexp.MustCompile("^(j|q|x)(ū|ú|ǔ|ù)$")
var reFinal2Exceptions = regexp.MustCompile("^(j|q|x)u(\\d?)$")

// NewArgs 返回包含默认配置的 `Args`
func NewArgs() Args {
	return Args{Style, Heteronym, Separator, Fallback}
}

// 获取单个拼音中的声母
func initial(p string) string {
	s := ""
	for _, v := range initialArray {
		if strings.HasPrefix(p, v) {
			s = v
			break
		}
	}
	return s
}

// 获取单个拼音中的韵母
func final(p string) string {
	n := initial(p)
	if n == "" {
		return handleYW(p)
	}

	// 特例 j/q/x
	matches := reFinalExceptions.FindStringSubmatch(p)
	// jū -> jǖ
	if len(matches) == 3 && matches[1] != "" && matches[2] != "" {
		v, _ := finalExceptionsMap[matches[2]]
		return v
	}
	// ju -> jv, ju1 -> jv1
	p = reFinal2Exceptions.ReplaceAllString(p, "${1}v$2")
	return strings.Join(strings.SplitN(p, n, 2), "")
}

// 处理 y, w
func handleYW(p string) string {
	// 特例 y/w
	if strings.HasPrefix(p, "yu") {
		p = "v" + p[2:] // yu -> v
	} else if strings.HasPrefix(p, "yi") {
		p = p[1:] // yi -> i
	} else if strings.HasPrefix(p, "y") {
		p = "i" + p[1:] // y -> i
	} else if strings.HasPrefix(p, "wu") {
		p = p[1:] // wu -> u
	} else if strings.HasPrefix(p, "w") {
		p = "u" + p[1:] // w -> u
	}
	return p
}

func toFixed(p string, a Args) string {
	if a.Style == Initials {
		return initial(p)
	}
	origP := p

	// 替换拼音中的带声调字符
	py := rePhoneticSymbol.ReplaceAllStringFunc(p, func(m string) string {
		symbol, _ := phoneticSymbol[m]
		switch a.Style {
		// 不包含声调
		case Normal, FirstLetter, Finals:
			// 去掉声调: a1 -> a
			m = reTone2.ReplaceAllString(symbol, "$1")
		case Tone2, FinalsTone2:
			// 返回使用数字标识声调的字符
			m = symbol
		default:
			// 声调在头上
		}
		return m
	})

	switch a.Style {
	// 首字母
	case FirstLetter:
		py = py[:1]
	// 韵母
	case Finals, FinalsTone, FinalsTone2:
		// 转换为 []rune unicode 编码用于获取第一个拼音字符
		// 因为 string 是 utf-8 编码不方便获取第一个拼音字符
		rs := []rune(origP)
		switch string(rs[0]) {
		// 因为鼻音没有声母所以不需要去掉声母部分
		case "ḿ", "ń", "ň", "ǹ":
		default:
			py = final(py)
		}
	}
	return py
}

func applyStyle(p []string, a Args) []string {
	newP := []string{}
	for _, v := range p {
		newP = append(newP, toFixed(v, a))
	}
	return newP
}

// SinglePinyin 把单个 `rune` 类型的汉字转换为拼音.
func SinglePinyin(r rune, a Args) []string {
	if a.Fallback == nil {
		a.Fallback = Fallback
	}
	value, ok := PinyinDict[int(r)]
	pys := []string{}
	if ok {
		pys = strings.Split(value, ",")
	} else {
		pys = a.Fallback(r, a)
	}
	if len(pys) > 0 {
		if !a.Heteronym {
			pys = pys[:1]
		}
		return applyStyle(pys, a)
	}
	return pys
}

// Pinyin 汉字转拼音，支持多音字模式.
func Pinyin(s string, a Args) [][]string {
	hans := []rune(s)
	pys := [][]string{}
	for _, r := range hans {
		py := SinglePinyin(r, a)
		if len(py) > 0 {
			pys = append(pys, py)
		}
	}
	return pys
}

// LazyPinyin 汉字转拼音，与 `Pinyin` 的区别是：
// 返回值类型不同，并且不支持多音字模式，每个汉字只取第一个音.
func LazyPinyin(s string, a Args) []string {
	a.Heteronym = false
	pys := []string{}
	for _, v := range Pinyin(s, a) {
		pys = append(pys, v[0])
	}
	return pys
}

// Slug join `LazyPinyin` 的返回值.
func Slug(s string, a Args) string {
	separator := a.Separator
	return strings.Join(LazyPinyin(s, a), separator)
}
<commit_msg>clean up code<commit_after>package pinyin

import (
	"regexp"
	"strings"
)

// Meta
const (
	Version   = "0.6.0"
	Author    = "mozillazg, 闲耘"
	License   = "MIT"
	Copyright = "Copyright (c) 2016 mozillazg, 闲耘"
)

// 拼音风格(推荐)
const (
	Normal      = 0 // 普通风格，不带声调（默认风格）。如： zhong guo
	Tone        = 1 // 声调风格1，拼音声调在韵母第一个字母上。如： zhōng guó
	Tone2       = 2 // 声调风格2，即拼音声调在各个拼音之后，用数字 [0-4] 进行表示。如： zho1ng guo2
	Initials    = 3 // 声母风格，只返回各个拼音的声母部分。如： zh g
	FirstLetter = 4 // 首字母风格，只返回拼音的首字母部分。如： z g
	Finals      = 5 // 韵母风格1，只返回各个拼音的韵母部分，不带声调。如： ong uo
	FinalsTone  = 6 // 韵母风格2，带声调，声调在韵母第一个字母上。如： ōng uó
	FinalsTone2 = 7 // 韵母风格2，带声调，声调在各个拼音之后，用数字 [0-4] 进行表示。如： o1ng uo2
)

// 拼音风格(兼容之前的版本)
const (
	NORMAL       = Normal
	TONE         = Tone
	TONE2        = Tone2
	INITIALS     = Initials
	FIRST_LETTER = FirstLetter
	FINALS       = Finals
	FINALS_TONE  = FinalsTone
	FINALS_TONE2 = FinalsTone2
)

// 声母表
var initialArray = strings.Split(
	"b,p,m,f,d,t,n,l,g,k,h,j,q,x,r,zh,ch,sh,z,c,s",
	",",
)

// 所有带声调的字符
var rePhoneticSymbolSource = func(m map[string]string) string {
	s := ""
	for k := range m {
		s = s + k
	}
	return s
}(phoneticSymbol)

// 匹配带声调字符的正则表达式
var rePhoneticSymbol = regexp.MustCompile("[" + rePhoneticSymbolSource + "]")

// 匹配使用数字标识声调的字符的正则表达式
var reTone2 = regexp.MustCompile("([aeoiuvnm])([0-4])$")

// Args 配置信息
type Args struct {
	Style     int    // 拼音风格（默认： Normal)
	Heteronym bool   // 是否启用多音字模式（默认：禁用）
	Separator string // Slug 中使用的分隔符（默认：-)

	// 处理没有拼音的字符（默认忽略没有拼音的字符）
	// 函数返回的 slice 的长度为0 则表示忽略这个字符
	Fallback func(r rune, a Args) []string
}

// Style 默认配置：风格
var Style = Normal

// Heteronym 默认配置：是否启用多音字模式
var Heteronym = false

// Separator 默认配置： `Slug` 中 Join 所用的分隔符
var Separator = "-"

// Fallback 默认配置: 如何处理没有拼音的字符(忽略这个字符)
var Fallback = func(r rune, a Args) []string {
	return []string{}
}

var finalExceptionsMap = map[string]string{
	"ū": "ǖ",
	"ú": "ǘ",
	"ǔ": "ǚ",
	"ù": "ǜ",
}
var reFinalExceptions = regexp.MustCompile("^(j|q|x)(ū|ú|ǔ|ù)$")
var reFinal2Exceptions = regexp.MustCompile("^(j|q|x)u(\\d?)$")

// NewArgs 返回包含默认配置的 `Args`
func NewArgs() Args {
	return Args{Style, Heteronym, Separator, Fallback}
}

// 获取单个拼音中的声母
func initial(p string) string {
	s := ""
	for _, v := range initialArray {
		if strings.HasPrefix(p, v) {
			s = v
			break
		}
	}
	return s
}

// 获取单个拼音中的韵母
func final(p string) string {
	n := initial(p)
	if n == "" {
		return handleYW(p)
	}

	// 特例 j/q/x
	matches := reFinalExceptions.FindStringSubmatch(p)
	// jū -> jǖ
	if len(matches) == 3 && matches[1] != "" && matches[2] != "" {
		v, _ := finalExceptionsMap[matches[2]]
		return v
	}
	// ju -> jv, ju1 -> jv1
	p = reFinal2Exceptions.ReplaceAllString(p, "${1}v$2")
	return strings.Join(strings.SplitN(p, n, 2), "")
}

// 处理 y, w
func handleYW(p string) string {
	// 特例 y/w
	if strings.HasPrefix(p, "yu") {
		p = "v" + p[2:] // yu -> v
	} else if strings.HasPrefix(p, "yi") {
		p = p[1:] // yi -> i
	} else if strings.HasPrefix(p, "y") {
		p = "i" + p[1:] // y -> i
	} else if strings.HasPrefix(p, "wu") {
		p = p[1:] // wu -> u
	} else if strings.HasPrefix(p, "w") {
		p = "u" + p[1:] // w -> u
	}
	return p
}

func toFixed(p string, a Args) string {
	if a.Style == Initials {
		return initial(p)
	}
	origP := p

	// 替换拼音中的带声调字符
	py := rePhoneticSymbol.ReplaceAllStringFunc(p, func(m string) string {
		symbol, _ := phoneticSymbol[m]
		switch a.Style {
		// 不包含声调
		case Normal, FirstLetter, Finals:
			// 去掉声调: a1 -> a
			m = reTone2.ReplaceAllString(symbol, "$1")
		case Tone2, FinalsTone2:
			// 返回使用数字标识声调的字符
			m = symbol
		default:
			// 声调在头上
		}
		return m
	})

	switch a.Style {
	// 首字母
	case FirstLetter:
		py = py[:1]
	// 韵母
	case Finals, FinalsTone, FinalsTone2:
		// 转换为 []rune unicode 编码用于获取第一个拼音字符
		// 因为 string 是 utf-8 编码不方便获取第一个拼音字符
		rs := []rune(origP)
		switch string(rs[0]) {
		// 因为鼻音没有声母所以不需要去掉声母部分
		case "ḿ", "ń", "ň", "ǹ":
		default:
			py = final(py)
		}
	}
	return py
}

func applyStyle(p []string, a Args) []string {
	newP := []string{}
	for _, v := range p {
		newP = append(newP, toFixed(v, a))
	}
	return newP
}

// SinglePinyin 把单个 `rune` 类型的汉字转换为拼音.
func SinglePinyin(r rune, a Args) []string {
	if a.Fallback == nil {
		a.Fallback = Fallback
	}
	value, ok := PinyinDict[int(r)]
	pys := []string{}
	if ok {
		pys = strings.Split(value, ",")
	} else {
		pys = a.Fallback(r, a)
	}
	if len(pys) > 0 {
		if !a.Heteronym {
			pys = pys[:1]
		}
		return applyStyle(pys, a)
	}
	return pys
}

// Pinyin 汉字转拼音，支持多音字模式.
func Pinyin(s string, a Args) [][]string {
	pys := [][]string{}
	for _, r := range s {
		py := SinglePinyin(r, a)
		if len(py) > 0 {
			pys = append(pys, py)
		}
	}
	return pys
}

// LazyPinyin 汉字转拼音，与 `Pinyin` 的区别是：
// 返回值类型不同，并且不支持多音字模式，每个汉字只取第一个音.
func LazyPinyin(s string, a Args) []string {
	a.Heteronym = false
	pys := []string{}
	for _, v := range Pinyin(s, a) {
		pys = append(pys, v[0])
	}
	return pys
}

// Slug join `LazyPinyin` 的返回值.
func Slug(s string, a Args) string {
	separator := a.Separator
	return strings.Join(LazyPinyin(s, a), separator)
}
<|endoftext|>