<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 提供对代码块的语法进行解析
package syntax

import (
	"log"

	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"

	"github.com/issue9/is"
)

// Input 输入的数据
type Input struct {
	File  string
	Line  int
	Data  []rune
	Error *log.Logger
	Warn  *log.Logger
}

// Parse 分析一段代码，并将结果保存到 d 中。
func Parse(d *types.Doc, input *Input) {
	l := newLexer(input)

	for {
		switch {
		case l.matchTag(vars.APIDoc):
			if !l.scanAPIDoc(d) {
				return
			}
		case l.matchTag(vars.API):
			if api, ok := l.scanAPI(); ok {
				if api == nil { // @apiIgnore
					return
				}
				d.NewAPI(api)
			} else {
				return
			}
		case l.match(vars.API):
			l.backup()
			word := l.readWord()
			if input.Warn != nil {
				err := &types.SyntaxError{
					File:    input.File,
					Line:    input.Line,
					Message: locale.Sprintf(locale.ErrUnknownTag, word),
				}
				input.Warn.Println(err)
			}
			l.readTag() // 指针移到下一个标签处
		default:
			if l.atEOF() {
				return
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) bool {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
		return false
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
		return false
	}
	if !t.atEOF() {
		l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
		return false
	}

	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
				return false
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
				return false
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
				return false
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				t.syntaxError(locale.ErrSecondArgMustURL)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
				return false
			}
		case l.matchTag(vars.APIContent):
			d.Content = l.readEnd()
		case l.match(vars.API): // 不认识的标签
			l.backup()
			l.syntaxError(locale.ErrUnknownTag, l.readWord())
			return false
		default:
			if l.atEOF() {
				return true
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI() (*types.API, bool) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
		return nil, false
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil, true
		case l.matchTag(vars.APIGroup):
			if !l.scanGroup(api) {
				return nil, false
			}
		case l.matchTag(vars.APIQuery):
			if !l.scanAPIQueries(api) {
				return nil, false
			}
		case l.matchTag(vars.APIParam):
			if !l.scanAPIParams(api) {
				return nil, false
			}
		case l.matchTag(vars.APIRequest):
			req, ok := l.scanAPIRequest()
			if !ok {
				return nil, false
			}
			api.Request = req
		case l.matchTag(vars.APIError):
			resp, ok := l.scanResponse(vars.APIError)
			if !ok {
				return nil, false
			}
			api.Error = resp
		case l.matchTag(vars.APISuccess):
			resp, ok := l.scanResponse(vars.APISuccess)
			if !ok {
				return nil, false
			}
			api.Success = resp
		case l.match(vars.API): // 不认识的标签
			l.backup()
			l.syntaxWarn(locale.ErrUnknownTag, l.readWord())
			l.readTag() // 指针移到下一个标签处
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for

	if api.Success == nil {
		l.syntaxError(locale.ErrSuccessNotEmpty)
		return nil, false
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	return api, true
}

func (l *lexer) scanGroup(api *types.API) bool {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
		return false
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
		return false
	}

	return true
}

func (l *lexer) scanAPIQueries(api *types.API) bool {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	if p, ok := l.scanAPIParam(vars.APIQuery); ok {
		api.Queries = append(api.Queries, p)
		return true
	}
	return false
}

func (l *lexer) scanAPIParams(api *types.API) bool {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	if p, ok := l.scanAPIParam(vars.APIParam); ok {
		api.Params = append(api.Params, p)
		return true
	}
	return false
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest() (*types.Request, bool) {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
		return nil, false
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
				return nil, false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
				return nil, false
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, ok := l.scanAPIParam(vars.APIParam)
			if !ok {
				return nil, false
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, ok := l.scanAPIExample()
			if !ok {
				return nil, false
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	return r, true
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, bool) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
		return nil, false
	}
	if !tag.atEOF() {
		tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
		return nil, false
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
				return nil, false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
				return nil, false
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, ok := l.scanAPIParam(vars.APIParam)
			if !ok {
				return nil, false
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, ok := l.scanAPIExample()
			if !ok {
				return nil, false
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, true
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, bool) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
		return nil, false
	}

	return example, true
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, bool) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
		return nil, false
	}
	return p, true
}
<commit_msg>复用 lexer.syntaxError 函数<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// Package syntax 负责对标签语法的解析操作。
package syntax

import (
	"log"

	"github.com/caixw/apidoc/locale"
	"github.com/caixw/apidoc/types"
	"github.com/caixw/apidoc/vars"

	"github.com/issue9/is"
)

// Input 由外界提供的与标签语法相关的内容。
type Input struct {
	File  string      // 该段代码所在的文件
	Line  int         // 该段代码在文件中的行号
	Data  []rune      // 需要解析的代码段
	Error *log.Logger // 出错时的输出通道
	Warn  *log.Logger // 警告信息的输出通道
}

// Parse 分析一段代码，并将结果保存到 d 中。
func Parse(d *types.Doc, input *Input) {
	l := newLexer(input)

	for {
		switch {
		case l.matchTag(vars.APIDoc):
			if !l.scanAPIDoc(d) {
				return
			}
		case l.matchTag(vars.API):
			if api, ok := l.scanAPI(); ok {
				if api == nil { // @apiIgnore
					return
				}
				d.NewAPI(api)
			} else {
				return
			}
		case l.match(vars.API):
			l.backup()
			l.syntaxWarn(locale.ErrUnknownTag, l.readWord())
			l.readTag() // 指针移到下一个标签处
		default:
			if l.atEOF() {
				return
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @apidoc 及其子标签
//
// @apidoc title of doc
// @apiVersion 2.0
// @apiBaseURL https://api.caixw.io
// @apiLicense MIT https://opensource.org/licenses/MIT
//
// @apiContent
// content1
// content2
func (l *lexer) scanAPIDoc(d *types.Doc) bool {
	if len(d.Title) > 0 || len(d.Version) > 0 {
		l.syntaxError(locale.ErrDuplicateTag, vars.APIDoc)
		return false
	}

	t := l.readTag()
	d.Title = t.readLine()
	if len(d.Title) == 0 {
		l.syntaxError(locale.ErrTagArgNotEnough, vars.APIDoc)
		return false
	}
	if !t.atEOF() {
		l.syntaxError(locale.ErrTagArgTooMuch, vars.APIDoc)
		return false
	}

	for {
		switch {
		case l.matchTag(vars.APIVersion):
			t := l.readTag()
			d.Version = t.readLine()
			if len(d.Version) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIVersion)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIVersion)
				return false
			}
		case l.matchTag(vars.APIBaseURL):
			t := l.readTag()
			d.BaseURL = t.readLine()
			if len(d.BaseURL) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIBaseURL)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIBaseURL)
				return false
			}
		case l.matchTag(vars.APILicense):
			t := l.readTag()
			d.LicenseName = t.readWord()
			d.LicenseURL = t.readLine()
			if len(d.LicenseName) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APILicense)
				return false
			}
			if len(d.LicenseURL) > 0 && !is.URL(d.LicenseURL) {
				t.syntaxError(locale.ErrSecondArgMustURL)
				return false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APILicense)
				return false
			}
		case l.matchTag(vars.APIContent):
			d.Content = l.readEnd()
		case l.match(vars.API): // 不认识的标签
			l.backup()
			l.syntaxError(locale.ErrUnknownTag, l.readWord())
			return false
		default:
			if l.atEOF() {
				return true
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for
}

// 解析 @api 及其子标签
func (l *lexer) scanAPI() (*types.API, bool) {
	api := &types.API{}
	t := l.readTag()
	api.Method = t.readWord()
	api.URL = t.readWord()
	api.Summary = t.readLine()

	if len(api.Method) == 0 || len(api.URL) == 0 || len(api.Summary) == 0 {
		t.syntaxError(locale.ErrTagArgNotEnough, vars.API)
		return nil, false
	}

	api.Description = t.readEnd()
LOOP:
	for {
		switch {
		case l.matchTag(vars.APIIgnore):
			return nil, true
		case l.matchTag(vars.APIGroup):
			if !l.scanGroup(api) {
				return nil, false
			}
		case l.matchTag(vars.APIQuery):
			if !l.scanAPIQueries(api) {
				return nil, false
			}
		case l.matchTag(vars.APIParam):
			if !l.scanAPIParams(api) {
				return nil, false
			}
		case l.matchTag(vars.APIRequest):
			req, ok := l.scanAPIRequest()
			if !ok {
				return nil, false
			}
			api.Request = req
		case l.matchTag(vars.APIError):
			resp, ok := l.scanResponse(vars.APIError)
			if !ok {
				return nil, false
			}
			api.Error = resp
		case l.matchTag(vars.APISuccess):
			resp, ok := l.scanResponse(vars.APISuccess)
			if !ok {
				return nil, false
			}
			api.Success = resp
		case l.match(vars.API): // 不认识的标签
			l.backup()
			l.syntaxWarn(locale.ErrUnknownTag, l.readWord())
			l.readTag() // 指针移到下一个标签处
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	} // end for

	if api.Success == nil {
		l.syntaxError(locale.ErrSuccessNotEmpty)
		return nil, false
	}

	if len(api.Group) == 0 {
		api.Group = vars.DefaultGroupName
	}

	return api, true
}

func (l *lexer) scanGroup(api *types.API) bool {
	t := l.readTag()

	api.Group = t.readWord()
	if len(api.Group) == 0 {
		t.syntaxError(locale.ErrTagArgNotEnough, vars.APIGroup)
		return false
	}

	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIGroup)
		return false
	}

	return true
}

func (l *lexer) scanAPIQueries(api *types.API) bool {
	if api.Queries == nil {
		api.Queries = make([]*types.Param, 0, 1)
	}

	if p, ok := l.scanAPIParam(vars.APIQuery); ok {
		api.Queries = append(api.Queries, p)
		return true
	}
	return false
}

func (l *lexer) scanAPIParams(api *types.API) bool {
	if api.Params == nil {
		api.Params = make([]*types.Param, 0, 1)
	}

	if p, ok := l.scanAPIParam(vars.APIParam); ok {
		api.Params = append(api.Params, p)
		return true
	}
	return false
}

// 解析 @apiRequest 及其子标签
func (l *lexer) scanAPIRequest() (*types.Request, bool) {
	t := l.readTag()
	r := &types.Request{
		Type:     t.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}
	if !t.atEOF() {
		t.syntaxError(locale.ErrTagArgTooMuch, vars.APIRequest)
		return nil, false
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
				return nil, false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
				return nil, false
			}
			r.Headers[string(key)] = string(val)
		case l.matchTag(vars.APIParam):
			p, ok := l.scanAPIParam(vars.APIParam)
			if !ok {
				return nil, false
			}
			r.Params = append(r.Params, p)
		case l.matchTag(vars.APIExample):
			e, ok := l.scanAPIExample()
			if !ok {
				return nil, false
			}
			r.Examples = append(r.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。

		} // end switch
	} // end for

	return r, true
}

// 解析 @apiSuccess 或是 @apiError 及其子标签。
func (l *lexer) scanResponse(tagName string) (*types.Response, bool) {
	tag := l.readTag()
	resp := &types.Response{
		Code:     tag.readWord(),
		Summary:  tag.readLine(),
		Headers:  map[string]string{},
		Params:   []*types.Param{},
		Examples: []*types.Example{},
	}

	if len(resp.Code) == 0 || len(resp.Summary) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
		return nil, false
	}
	if !tag.atEOF() {
		tag.syntaxError(locale.ErrTagArgTooMuch, tagName)
		return nil, false
	}

LOOP:
	for {
		switch {
		case l.matchTag(vars.APIHeader):
			t := l.readTag()
			key := t.readWord()
			val := t.readLine()
			if len(key) == 0 || len(val) == 0 {
				t.syntaxError(locale.ErrTagArgNotEnough, vars.APIHeader)
				return nil, false
			}
			if !t.atEOF() {
				t.syntaxError(locale.ErrTagArgTooMuch, vars.APIHeader)
				return nil, false
			}
			resp.Headers[key] = val
		case l.matchTag(vars.APIParam):
			p, ok := l.scanAPIParam(vars.APIParam)
			if !ok {
				return nil, false
			}
			resp.Params = append(resp.Params, p)
		case l.matchTag(vars.APIExample):
			e, ok := l.scanAPIExample()
			if !ok {
				return nil, false
			}
			resp.Examples = append(resp.Examples, e)
		case l.match(vars.API): // 其它 api*，退出。
			l.backup()
			break LOOP
		default:
			if l.atEOF() {
				break LOOP
			}
			l.pos++ // 去掉无用的字符。
		}
	}

	return resp, true
}

// 解析 @apiExample 标签
func (l *lexer) scanAPIExample() (*types.Example, bool) {
	tag := l.readTag()
	example := &types.Example{
		Type: tag.readWord(),
		Code: tag.readEnd(),
	}

	if len(example.Type) == 0 || len(example.Code) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, vars.APIExample)
		return nil, false
	}

	return example, true
}

// 解析 @apiParam 标签
func (l *lexer) scanAPIParam(tagName string) (*types.Param, bool) {
	p := &types.Param{}

	tag := l.readTag()
	p.Name = tag.readWord()
	p.Type = tag.readWord()
	p.Summary = tag.readEnd()
	if len(p.Name) == 0 || len(p.Type) == 0 || len(p.Summary) == 0 {
		tag.syntaxError(locale.ErrTagArgNotEnough, tagName)
		return nil, false
	}
	return p, true
}
<|endoftext|>


<commit_before>package mount

import (
	"github.com/hanwen/go-fuse/v2/fuse"
	sys "golang.org/x/sys/unix"
	"runtime"
	"strings"
	"syscall"
)

const (
	// https://man7.org/linux/man-pages/man7/xattr.7.html#:~:text=The%20VFS%20imposes%20limitations%20that,in%20listxattr(2)).
	MAX_XATTR_NAME_SIZE  = 255
	MAX_XATTR_VALUE_SIZE = 65536
	XATTR_PREFIX         = "xattr-" // same as filer
)

// GetXAttr reads an extended attribute, and should return the
// number of bytes. If the buffer is too small, return ERANGE,
// with the required buffer size.
func (wfs *WFS) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, dest []byte) (size uint32, code fuse.Status) {

	//validate attr name
	if len(attr) > MAX_XATTR_NAME_SIZE {
		if runtime.GOOS == "darwin" {
			return 0, fuse.EPERM
		} else {
			return 0, fuse.ERANGE
		}
	}
	if len(attr) == 0 {
		return 0, fuse.EINVAL
	}

	_, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return 0, status
	}
	if entry == nil {
		return 0, fuse.ENOENT
	}
	if entry.Extended == nil {
		return 0, fuse.ENOATTR
	}
	data, found := entry.Extended[XATTR_PREFIX+attr]
	if !found {
		return 0, fuse.ENOATTR
	}
	if len(dest) < len(data) {
		return uint32(len(data)), fuse.ERANGE
	}
	copy(dest, data)

	return uint32(len(data)), fuse.OK
}

// SetXAttr writes an extended attribute.
// https://man7.org/linux/man-pages/man2/setxattr.2.html
//        By default (i.e., flags is zero), the extended attribute will be
//       created if it does not exist, or the value will be replaced if
//       the attribute already exists.  To modify these semantics, one of
//       the following values can be specified in flags:
//
//       XATTR_CREATE
//              Perform a pure create, which fails if the named attribute
//              exists already.
//
//       XATTR_REPLACE
//              Perform a pure replace operation, which fails if the named
//              attribute does not already exist.
func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
	//validate attr name
	if len(attr) > MAX_XATTR_NAME_SIZE {
		if runtime.GOOS == "darwin" {
			return fuse.EPERM
		} else {
			return fuse.ERANGE
		}
	}
	if len(attr) == 0 {
		return fuse.EINVAL
	}
	//validate attr value
	if len(data) > MAX_XATTR_VALUE_SIZE {
		if runtime.GOOS == "darwin" {
			return fuse.Status(syscall.E2BIG)
		} else {
			return fuse.ERANGE
		}
	}

	path, _, entry, status := wfs.maybeReadEntry(input.NodeId)
	if status != fuse.OK {
		return status
	}
	if entry.Extended == nil {
		entry.Extended = make(map[string][]byte)
	}
	oldData, _ := entry.Extended[XATTR_PREFIX+attr]
	switch input.Flags {
	case sys.XATTR_CREATE:
		if len(oldData) > 0 {
			break
		}
		fallthrough
	case sys.XATTR_REPLACE:
		fallthrough
	default:
		entry.Extended[XATTR_PREFIX+attr] = data
	}

	return wfs.saveEntry(path, entry)

}

// ListXAttr lists extended attributes as '\0' delimited byte
// slice, and return the number of bytes. If the buffer is too
// small, return ERANGE, with the required buffer size.
func (wfs *WFS) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (n uint32, code fuse.Status) {
	_, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return 0, status
	}
	if entry == nil {
		return 0, fuse.ENOENT
	}
	if entry.Extended == nil {
		return 0, fuse.ENOATTR
	}

	var data []byte
	for k := range entry.Extended {
		if strings.HasPrefix(k, XATTR_PREFIX) {
			data = append(data, k[len(XATTR_PREFIX):]...)
			data = append(data, 0)
		}
	}
	if len(dest) < len(data) {
		return uint32(len(data)), fuse.ERANGE
	}

	copy(dest, data)

	return uint32(len(data)), fuse.OK
}

// RemoveXAttr removes an extended attribute.
func (wfs *WFS) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
	if len(attr) == 0 {
		return fuse.EINVAL
	}
	path, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return status
	}
	if entry.Extended == nil {
		return fuse.ENOATTR
	}
	_, found := entry.Extended[XATTR_PREFIX+attr]

	if !found {
		return fuse.ENOATTR
	}

	delete(entry.Extended, XATTR_PREFIX+attr)

	return wfs.saveEntry(path, entry)
}
<commit_msg>mount2: listXattr return ok if xattr is empty<commit_after>package mount

import (
	"github.com/hanwen/go-fuse/v2/fuse"
	sys "golang.org/x/sys/unix"
	"runtime"
	"strings"
	"syscall"
)

const (
	// https://man7.org/linux/man-pages/man7/xattr.7.html#:~:text=The%20VFS%20imposes%20limitations%20that,in%20listxattr(2)).
	MAX_XATTR_NAME_SIZE  = 255
	MAX_XATTR_VALUE_SIZE = 65536
	XATTR_PREFIX         = "xattr-" // same as filer
)

// GetXAttr reads an extended attribute, and should return the
// number of bytes. If the buffer is too small, return ERANGE,
// with the required buffer size.
func (wfs *WFS) GetXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string, dest []byte) (size uint32, code fuse.Status) {

	//validate attr name
	if len(attr) > MAX_XATTR_NAME_SIZE {
		if runtime.GOOS == "darwin" {
			return 0, fuse.EPERM
		} else {
			return 0, fuse.ERANGE
		}
	}
	if len(attr) == 0 {
		return 0, fuse.EINVAL
	}

	_, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return 0, status
	}
	if entry == nil {
		return 0, fuse.ENOENT
	}
	if entry.Extended == nil {
		return 0, fuse.ENOATTR
	}
	data, found := entry.Extended[XATTR_PREFIX+attr]
	if !found {
		return 0, fuse.ENOATTR
	}
	if len(dest) < len(data) {
		return uint32(len(data)), fuse.ERANGE
	}
	copy(dest, data)

	return uint32(len(data)), fuse.OK
}

// SetXAttr writes an extended attribute.
// https://man7.org/linux/man-pages/man2/setxattr.2.html
//        By default (i.e., flags is zero), the extended attribute will be
//       created if it does not exist, or the value will be replaced if
//       the attribute already exists.  To modify these semantics, one of
//       the following values can be specified in flags:
//
//       XATTR_CREATE
//              Perform a pure create, which fails if the named attribute
//              exists already.
//
//       XATTR_REPLACE
//              Perform a pure replace operation, which fails if the named
//              attribute does not already exist.
func (wfs *WFS) SetXAttr(cancel <-chan struct{}, input *fuse.SetXAttrIn, attr string, data []byte) fuse.Status {
	//validate attr name
	if len(attr) > MAX_XATTR_NAME_SIZE {
		if runtime.GOOS == "darwin" {
			return fuse.EPERM
		} else {
			return fuse.ERANGE
		}
	}
	if len(attr) == 0 {
		return fuse.EINVAL
	}
	//validate attr value
	if len(data) > MAX_XATTR_VALUE_SIZE {
		if runtime.GOOS == "darwin" {
			return fuse.Status(syscall.E2BIG)
		} else {
			return fuse.ERANGE
		}
	}

	path, _, entry, status := wfs.maybeReadEntry(input.NodeId)
	if status != fuse.OK {
		return status
	}
	if entry.Extended == nil {
		entry.Extended = make(map[string][]byte)
	}
	oldData, _ := entry.Extended[XATTR_PREFIX+attr]
	switch input.Flags {
	case sys.XATTR_CREATE:
		if len(oldData) > 0 {
			break
		}
		fallthrough
	case sys.XATTR_REPLACE:
		fallthrough
	default:
		entry.Extended[XATTR_PREFIX+attr] = data
	}

	return wfs.saveEntry(path, entry)

}

// ListXAttr lists extended attributes as '\0' delimited byte
// slice, and return the number of bytes. If the buffer is too
// small, return ERANGE, with the required buffer size.
func (wfs *WFS) ListXAttr(cancel <-chan struct{}, header *fuse.InHeader, dest []byte) (n uint32, code fuse.Status) {
	_, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return 0, status
	}
	if entry == nil {
		return 0, fuse.ENOENT
	}
	if entry.Extended == nil {
		return 0, fuse.OK
	}

	var data []byte
	for k := range entry.Extended {
		if strings.HasPrefix(k, XATTR_PREFIX) {
			data = append(data, k[len(XATTR_PREFIX):]...)
			data = append(data, 0)
		}
	}
	if len(dest) < len(data) {
		return uint32(len(data)), fuse.ERANGE
	}

	copy(dest, data)

	return uint32(len(data)), fuse.OK
}

// RemoveXAttr removes an extended attribute.
func (wfs *WFS) RemoveXAttr(cancel <-chan struct{}, header *fuse.InHeader, attr string) fuse.Status {
	if len(attr) == 0 {
		return fuse.EINVAL
	}
	path, _, entry, status := wfs.maybeReadEntry(header.NodeId)
	if status != fuse.OK {
		return status
	}
	if entry.Extended == nil {
		return fuse.ENOATTR
	}
	_, found := entry.Extended[XATTR_PREFIX+attr]

	if !found {
		return fuse.ENOATTR
	}

	delete(entry.Extended, XATTR_PREFIX+attr)

	return wfs.saveEntry(path, entry)
}
<|endoftext|>


<commit_before>package imageunpacker

const (
	StatusStreamIdle      = 0
	StatusStreamScanning  = 1
	StatusStreamScanned   = 2
	StatusStreamFetching  = 3
	StatusStreamUpdating  = 4
	StatusStreamPreparing = 5
)

type DeviceInfo struct {
	DeviceName string
	Size       uint64
	StreamName string
}

// The AddDevice() RPC is an exclusive transaction following this sequence:
// - Server sends string "\n" if Client should proceed with attaching a device
//   to the server, else it sends a string indicating an error
// - Client sends string containing the DeviceID that was just attached
// - Server sends "\n" if device was found, else an error message.
// - End of transaction. Method completes.

type GetStatusRequest struct{}

type GetStatusResponse struct {
	Devices      map[string]DeviceInfo      // Key: DeviceId.
	ImageStreams map[string]ImageStreamInfo // Key: StreamName.
}

type ImageStreamInfo struct {
	DeviceId string
	Status   StreamStatus
}

type PrepareForCaptureRequest struct {
	StreamName string
}

type PrepareForCaptureResponse struct{}

type PrepareForUnpackRequest struct {
	StreamName string
}

type PrepareForUnpackResponse struct{}

type StreamStatus uint

func (status StreamStatus) String() string {
	return status.string()
}

type UnpackImageRequest struct {
	StreamName    string
	ImageLeafName string
}

type UnpackImageResponse struct{}
<commit_msg>Add ImageUnpacker.AssociateStreamWithDevice SRPC protocol messages.<commit_after>package imageunpacker

const (
	StatusStreamIdle      = 0
	StatusStreamScanning  = 1
	StatusStreamScanned   = 2
	StatusStreamFetching  = 3
	StatusStreamUpdating  = 4
	StatusStreamPreparing = 5
)

type DeviceInfo struct {
	DeviceName string
	Size       uint64
	StreamName string
}

// The AddDevice() RPC is an exclusive transaction following this sequence:
// - Server sends string "\n" if Client should proceed with attaching a device
//   to the server, else it sends a string indicating an error
// - Client sends string containing the DeviceID that was just attached
// - Server sends "\n" if device was found, else an error message.
// - End of transaction. Method completes.

type AssociateStreamWithDeviceRequest struct {
	StreamName string
	DeviceId   string
}

type AssociateStreamWithDeviceResponse struct{}

type GetStatusRequest struct{}

type GetStatusResponse struct {
	Devices      map[string]DeviceInfo      // Key: DeviceId.
	ImageStreams map[string]ImageStreamInfo // Key: StreamName.
}

type ImageStreamInfo struct {
	DeviceId string
	Status   StreamStatus
}

type PrepareForCaptureRequest struct {
	StreamName string
}

type PrepareForCaptureResponse struct{}

type PrepareForUnpackRequest struct {
	StreamName string
}

type PrepareForUnpackResponse struct{}

type StreamStatus uint

func (status StreamStatus) String() string {
	return status.string()
}

type UnpackImageRequest struct {
	StreamName    string
	ImageLeafName string
}

type UnpackImageResponse struct{}
<|endoftext|>


<commit_before>package funscript

import (
	"fmt"
	"time"

	"github.com/funjack/launchcontrol/protocol"
)

const (
	// SpeedLimitMin is the slowest movement command possible. The Launch
	// crashes on very slow speeds.
	SpeedLimitMin = 20
	// SpeedLimitMax is the fasts movement command possible. The Launch
	// makes weird 'clicking' noises when moving at very fast speeds.
	SpeedLimitMax = 80
	// PositionMin is the lowest position possible.
	PositionMin = 5
	// PositionMax is the hight position possible.
	PositionMax = 95
	// Threshold is the minimum amount of time between actions.
	Threshold = 100 * time.Millisecond
)

// Script is the Funscript container type holding Launch data.
type Script struct {
	// Version of Launchscript
	Version string
	// Inverted causes up and down movement to be flipped.
	Inverted bool `json:"inverted,omitempty"`
	// Range is the percentage of a full stroke to use.
	Range Range `json:"range,omitempty"`
	// Actions are the timed moves.
	Actions []Action
}

// Action is a move at a specific time.
type Action struct {
	// At time in milliseconds the action should fire.
	At int64
	// Pos is the place in percent to move to.
	Pos int
}

// Range in percent.
type Range int

// Position returns the position for p limited within the range.
func (r Range) Position(p int) int {
	if r > 0 {
		return int(float64(p) / 100 * float64(r))
	}
	return p
}

// Stats when generated script.
type Stats struct {
	Count             uint64 // Amount of actions generated.
	DistanceTotal     uint64 // Total distance that will be traveled.
	SpeedTotal        uint64 // Accumulation of all commands speed param.
	SpeedOverrideFast int    // Number of times generated speed was too fast.
	SpeedOverrideSlow int    // Number of times generated speed was too slow.
	Delayed           int    // Number of actions that will be thresholded.
}

// String returns a formatted.
func (s Stats) String() string {
	var fastPct, slowPct float64
	overrideTotal := s.SpeedOverrideFast + s.SpeedOverrideSlow
	if overrideTotal > 0 {
		fastPct = float64(s.SpeedOverrideFast) /
			float64(overrideTotal) * 100
		slowPct = float64(s.SpeedOverrideSlow) /
			float64(overrideTotal) * 100
	}
	var avgSpeed uint64
	if s.Count > 0 {
		avgSpeed = s.SpeedTotal / s.Count
	}
	return fmt.Sprintf("actions=%d (avgspeed=%d%%), delayed=%d, "+
		"speedoverrides=%d (fast=%.2f%%,slow=%.2f%%)",
		s.Count, avgSpeed, s.Delayed, overrideTotal,
		fastPct, slowPct)
}

// TimedActions creates timed Launch actions from the Scripts timed positions.
// The minspd/maxspd arguments are Launch speed limits in percent. The
// minpos/maxpos specify the position limits in percent.
// The second return value are statistics on the script generation.
func (fs Script) TimedActions(minspd, maxspd, minpos, maxpos int) (s protocol.TimedActions, stat Stats) {
	if minspd < SpeedLimitMin {
		minspd = SpeedLimitMin
	}
	if maxspd > SpeedLimitMax {
		maxspd = SpeedLimitMax
	}
	if minpos < PositionMin {
		minpos = PositionMin
	}
	if maxpos > PositionMax {
		maxpos = PositionMax
	}
	r := Range(maxpos - minpos)
	if fs.Range != 0 && r > fs.Range {
		r = fs.Range
	}

	s = make(protocol.TimedActions, 1, len(fs.Actions)+1)
	s[0].Time = 0
	if fs.Inverted {
		s[0].Position = maxpos // Init at top
	} else {

		s[0].Position = minpos // Init at bottom
	}
	s[0].Speed = SpeedLimitMin

	previousPosition := s[0].Position
	previous := Action{
		At:  0,
		Pos: 0,
	}
	for _, e := range fs.Actions {
		if e.Pos == previous.Pos {
			previous = e
			continue
		}
		timediff := time.Duration(e.At-previous.At) * time.Millisecond
		if timediff < Threshold {
			stat.Delayed++
		}
		position := e.Pos
		if fs.Inverted {
			position = 100 - e.Pos
		}
		position = r.Position(position) + minpos
		distance := position - previousPosition
		if distance < 0 {
			distance = -distance
		}
		stat.DistanceTotal = stat.DistanceTotal + uint64(distance)
		speed := Speed(distance, timediff)
		if speed > maxspd {
			speed = maxspd
			stat.SpeedOverrideFast++
		} else if speed < minspd {
			speed = minspd
			stat.SpeedOverrideSlow++
		}
		stat.SpeedTotal = stat.SpeedTotal + uint64(speed)
		ta := protocol.TimedAction{
			Time: time.Duration(previous.At) * time.Millisecond,
			Action: protocol.Action{
				Position: position,
				Speed:    speed,
			},
		}
		s = append(s, ta)
		stat.Count++
		previous = e
		previousPosition = ta.Position
	}
	return
}
<commit_msg>Include json tags on funscript structs<commit_after>package funscript

import (
	"fmt"
	"time"

	"github.com/funjack/launchcontrol/protocol"
)

const (
	// SpeedLimitMin is the slowest movement command possible. The Launch
	// crashes on very slow speeds.
	SpeedLimitMin = 20
	// SpeedLimitMax is the fasts movement command possible. The Launch
	// makes weird 'clicking' noises when moving at very fast speeds.
	SpeedLimitMax = 80
	// PositionMin is the lowest position possible.
	PositionMin = 5
	// PositionMax is the hight position possible.
	PositionMax = 95
	// Threshold is the minimum amount of time between actions.
	Threshold = 100 * time.Millisecond
)

// Script is the Funscript container type holding Launch data.
type Script struct {
	// Version of Launchscript
	Version string `json:"version"`
	// Inverted causes up and down movement to be flipped.
	Inverted bool `json:"inverted,omitempty"`
	// Range is the percentage of a full stroke to use.
	Range Range `json:"range,omitempty"`
	// Actions are the timed moves.
	Actions []Action `json:"actions"`
}

// Action is a move at a specific time.
type Action struct {
	// At time in milliseconds the action should fire.
	At int64 `json:"at"`
	// Pos is the place in percent to move to.
	Pos int `json:"pos"`
}

// Range in percent.
type Range int

// Position returns the position for p limited within the range.
func (r Range) Position(p int) int {
	if r > 0 {
		return int(float64(p) / 100 * float64(r))
	}
	return p
}

// Stats when generated script.
type Stats struct {
	Count             uint64 // Amount of actions generated.
	DistanceTotal     uint64 // Total distance that will be traveled.
	SpeedTotal        uint64 // Accumulation of all commands speed param.
	SpeedOverrideFast int    // Number of times generated speed was too fast.
	SpeedOverrideSlow int    // Number of times generated speed was too slow.
	Delayed           int    // Number of actions that will be thresholded.
}

// String returns a formatted.
func (s Stats) String() string {
	var fastPct, slowPct float64
	overrideTotal := s.SpeedOverrideFast + s.SpeedOverrideSlow
	if overrideTotal > 0 {
		fastPct = float64(s.SpeedOverrideFast) /
			float64(overrideTotal) * 100
		slowPct = float64(s.SpeedOverrideSlow) /
			float64(overrideTotal) * 100
	}
	var avgSpeed uint64
	if s.Count > 0 {
		avgSpeed = s.SpeedTotal / s.Count
	}
	return fmt.Sprintf("actions=%d (avgspeed=%d%%), delayed=%d, "+
		"speedoverrides=%d (fast=%.2f%%,slow=%.2f%%)",
		s.Count, avgSpeed, s.Delayed, overrideTotal,
		fastPct, slowPct)
}

// TimedActions creates timed Launch actions from the Scripts timed positions.
// The minspd/maxspd arguments are Launch speed limits in percent. The
// minpos/maxpos specify the position limits in percent.
// The second return value are statistics on the script generation.
func (fs Script) TimedActions(minspd, maxspd, minpos, maxpos int) (s protocol.TimedActions, stat Stats) {
	if minspd < SpeedLimitMin {
		minspd = SpeedLimitMin
	}
	if maxspd > SpeedLimitMax {
		maxspd = SpeedLimitMax
	}
	if minpos < PositionMin {
		minpos = PositionMin
	}
	if maxpos > PositionMax {
		maxpos = PositionMax
	}
	r := Range(maxpos - minpos)
	if fs.Range != 0 && r > fs.Range {
		r = fs.Range
	}

	s = make(protocol.TimedActions, 1, len(fs.Actions)+1)
	s[0].Time = 0
	if fs.Inverted {
		s[0].Position = maxpos // Init at top
	} else {

		s[0].Position = minpos // Init at bottom
	}
	s[0].Speed = SpeedLimitMin

	previousPosition := s[0].Position
	previous := Action{
		At:  0,
		Pos: 0,
	}
	for _, e := range fs.Actions {
		if e.Pos == previous.Pos {
			previous = e
			continue
		}
		timediff := time.Duration(e.At-previous.At) * time.Millisecond
		if timediff < Threshold {
			stat.Delayed++
		}
		position := e.Pos
		if fs.Inverted {
			position = 100 - e.Pos
		}
		position = r.Position(position) + minpos
		distance := position - previousPosition
		if distance < 0 {
			distance = -distance
		}
		stat.DistanceTotal = stat.DistanceTotal + uint64(distance)
		speed := Speed(distance, timediff)
		if speed > maxspd {
			speed = maxspd
			stat.SpeedOverrideFast++
		} else if speed < minspd {
			speed = minspd
			stat.SpeedOverrideSlow++
		}
		stat.SpeedTotal = stat.SpeedTotal + uint64(speed)
		ta := protocol.TimedAction{
			Time: time.Duration(previous.At) * time.Millisecond,
			Action: protocol.Action{
				Position: position,
				Speed:    speed,
			},
		}
		s = append(s, ta)
		stat.Count++
		previous = e
		previousPosition = ta.Position
	}
	return
}
<|endoftext|>


<commit_before>// Copyright 2019 The Terraformer Authors.
//
// 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 aws

import (
	"context"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
	"regexp"
)

var cloudFormationAllowEmptyValues = []string{"tags."}

type CloudFormationGenerator struct {
	AWSService
}

func (g *CloudFormationGenerator) InitResources() error {
	config, e := g.generateConfig()
	if e != nil {
		return e
	}
	svc := cloudformation.New(config)
	p := cloudformation.NewListStacksPaginator(svc.ListStacksRequest(&cloudformation.ListStacksInput{}))
	for p.Next(context.Background()) {
		for _, stackSummary := range p.CurrentPage().StackSummaries {
			if stackSummary.StackStatus == cloudformation.StackStatusDeleteComplete {
				continue
			}
			g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
				*stackSummary.StackName,
				*stackSummary.StackName,
				"aws_cloudformation_stack",
				"aws",
				cloudFormationAllowEmptyValues,
			))
		}
	}
	if err := p.Err(); err != nil {
		return err
	}
	stackSets, err := svc.ListStackSetsRequest(&cloudformation.ListStackSetsInput{}).Send(context.Background())
	if err != nil {
		return err
	}
	for _, stackSetSummary := range stackSets.Summaries {
		g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
			*stackSetSummary.StackSetName,
			*stackSetSummary.StackSetName,
			"aws_cloudformation_stack_set",
			"aws",
			cloudFormationAllowEmptyValues,
		))

		stackSetInstances, err := svc.ListStackInstancesRequest(&cloudformation.ListStackInstancesInput{
			StackSetName: stackSetSummary.StackSetName,
		}).Send(context.Background())
		if err != nil {
			return err
		}
		for _, stackSetI := range stackSetInstances.Summaries {
			id := aws.StringValue(stackSetI.StackSetId) + "," + aws.StringValue(stackSetI.Account) + "," + aws.StringValue(stackSetI.Region)

			g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
				id,
				id,
				"aws_cloudformation_stack_set_instance",
				"aws",
				cloudFormationAllowEmptyValues,
			))
		}
	}

	return nil
}

func (g *CloudFormationGenerator) PostConvertHook() error {
	cfInterpolation := regexp.MustCompile(`(\${[0-9A-Za-z:]+})`)
	for _, resource := range g.Resources {
		if resource.InstanceInfo.Type == "aws_cloudformation_stack" {
			delete(resource.Item, "outputs")
			if templateBody, ok := resource.InstanceState.Attributes["template_body"]; ok {
				str := cfInterpolation.ReplaceAllString(templateBody, "$$$1")
				resource.Item["template_body"] = str
			}
		}
	}
	return nil
}
<commit_msg>#423 Bring back reference to CF resource by its identifier<commit_after>// Copyright 2019 The Terraformer Authors.
//
// 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 aws

import (
	"context"
	"github.com/GoogleCloudPlatform/terraformer/terraform_utils"
	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/cloudformation"
	"regexp"
)

var cloudFormationAllowEmptyValues = []string{"tags."}

type CloudFormationGenerator struct {
	AWSService
}

func (g *CloudFormationGenerator) InitResources() error {
	config, e := g.generateConfig()
	if e != nil {
		return e
	}
	svc := cloudformation.New(config)
	p := cloudformation.NewListStacksPaginator(svc.ListStacksRequest(&cloudformation.ListStacksInput{}))
	for p.Next(context.Background()) {
		for _, stackSummary := range p.CurrentPage().StackSummaries {
			if stackSummary.StackStatus == cloudformation.StackStatusDeleteComplete {
				continue
			}
			g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
				*stackSummary.StackId,
				*stackSummary.StackName,
				"aws_cloudformation_stack",
				"aws",
				cloudFormationAllowEmptyValues,
			))
		}
	}
	if err := p.Err(); err != nil {
		return err
	}
	stackSets, err := svc.ListStackSetsRequest(&cloudformation.ListStackSetsInput{}).Send(context.Background())
	if err != nil {
		return err
	}
	for _, stackSetSummary := range stackSets.Summaries {
		if stackSetSummary.Status == cloudformation.StackSetStatusDeleted {
			continue
		}
		g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
			*stackSetSummary.StackSetId,
			*stackSetSummary.StackSetName,
			"aws_cloudformation_stack_set",
			"aws",
			cloudFormationAllowEmptyValues,
		))

		stackSetInstances, err := svc.ListStackInstancesRequest(&cloudformation.ListStackInstancesInput{
			StackSetName: stackSetSummary.StackSetName,
		}).Send(context.Background())
		if err != nil {
			return err
		}
		for _, stackSetI := range stackSetInstances.Summaries {
			id := aws.StringValue(stackSetI.StackSetId) + "," + aws.StringValue(stackSetI.Account) + "," + aws.StringValue(stackSetI.Region)

			g.Resources = append(g.Resources, terraform_utils.NewSimpleResource(
				id,
				id,
				"aws_cloudformation_stack_set_instance",
				"aws",
				cloudFormationAllowEmptyValues,
			))
		}
	}

	return nil
}

func (g *CloudFormationGenerator) PostConvertHook() error {
	cfInterpolation := regexp.MustCompile(`(\${[0-9A-Za-z:]+})`)
	for _, resource := range g.Resources {
		if resource.InstanceInfo.Type == "aws_cloudformation_stack" {
			delete(resource.Item, "outputs")
			if templateBody, ok := resource.InstanceState.Attributes["template_body"]; ok {
				str := cfInterpolation.ReplaceAllString(templateBody, "$$$1")
				resource.Item["template_body"] = str
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package bundled

const epmElv = `use re

# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  bool ?(test -e (dest $pkg))
}

fn -package-domain [pkg]{
  splits &max=2 / $pkg | take 1
}

fn -package-without-domain [pkg]{
  splits &max=2 / $pkg | drop 1 | joins ''
}

# Merge two maps
fn -merge [a b]{
  keys $b | each [k]{ a[$k] = $b[$k] }
  put $a
}

# Uppercase first letter of a string
fn -first-upper [s]{
  put (echo $s[0] | tr '[:lower:]' '[:upper:]')$s[(count $s[0]):]
}

# Expand tilde at the beginning of a string to the home dir
fn -tilde-expand [p]{
  re:replace "^~" $E:HOME $p
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# received two arguments: package name and the domain config entry
#
# - Method 'git' requires the key 'protocol' in the domain config,
#   which has to be 'http' or 'https'
# - Method 'rsync' requires the key 'location' in the domain config,
#   which has to contain the directory where the domain files are
#   stored. It can be any source location understood by the rsync
#   command.
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      try {
        git -C $dest pull
      } except _ {
          -error "Something failed, please check error above and retry."
      }
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put (-tilde-expand $dom-cfg[location])/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

fn -write-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  mkdir -p (dirname $cfgfile)
  if (has-key $-default-domain-config $dom) {
    put $-default-domain-config[$dom] | to-json > $cfgfile
  } else {
    -error "No default config exists for domain "$dom"."
  }
}

# Returns the domain config for a given domain, as a JSON data
# structure. If the file does not exist but we have a built-in
# definition, then we return the default. Otherwise we return $false,
# so the result can always be checked with 'if'.
fn -domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  cfg = $false
  if ?(test -f $cfgfile) {
    # If the config file exists, read it...
    cfg = (cat $cfgfile | from-json)
    -debug "Read domain config for "$dom": "(to-string $cfg)
  } else {
    # ...otherwise check if we have a default config for the domain, and save it
    if (has-key $-default-domain-config $dom) {
      cfg = $-default-domain-config[$dom]
      -debug "No existing config for "$dom", using the default: "(to-string $cfg)
    } else {
      -debug "No existing config for "$dom" and no default available."
    }
  }
  put $cfg
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "Unknown operation '"$what"' for package "$pkg
      }
    } else {
      fail "Unknown method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  } else {
    -error "No config for domain '"$dom"'."
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  # Base metadata attributes
  res = [
    &name= $pkg
    &method= (-package-method $pkg)
    &src= (-package-op $pkg src)
    &dst= (dest $pkg)
    &installed= (is-installed $pkg)
  ]
  # Merge with package-specified attributes, if any
  file = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $file)) {
    res = (-merge (cat $file | from-json) $res)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  special-keys = [name method installed src dst]
  echo (styled "Package "$data[name] cyan)
  if $data[installed] {
    echo (styled "Installed at "$data[dst] green)
  } else {
    echo (styled "Not installed" red)
  }
  echo (styled "Source:" blue) $data[method] $data[src]
  keys $data | each [key]{
    if (not (has-value $special-keys $key)) {
      val = $data[$key]
      if (eq (kind-of $val) list) {
        val = (joins ", " $val)
      }
      echo (styled (-first-upper $key)":" blue) $val
    }
  }
}

# List installed packages
fn installed {
  put $-lib-dir/*[nomatch-ok] | each [dir]{
    dom = (replaces $-lib-dir/ '' $dir)
    cfg = (-domain-config $dom)
    # Only list domains for which we know the config, so that the user
    # can have his own non-package directories under ~/.elvish/lib
    # without conflicts.
    if $cfg {
      lvl = $cfg[levels]
      pat = '^\Q'$-lib-dir'/\E('(repeat (+ $lvl 1) '[^/]+' | joins '/')')/$'
      put (each [d]{ re:find $pat $d } [ $-lib-dir/$dom/**[nomatch-ok]/ ] )[groups][1][text]
    }
  }
}

# epm:list is an alias for epm:installed
fn list { installed }

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  if (eq $pkgs []) {
    -error "You must specify at least one package."
    return
  }
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
      # Check if there are any dependencies to install
      metadata = (metadata $pkg)
      if (has-key $metadata dependencies) {
        deps = $metadata[dependencies]
        -info "Installing dependencies: "(joins " " $deps)
        # If the installation of dependencies fails, uninstall the
        # target package (leave any already-installed dependencies in
        # place)
        try {
          install $@deps
        } except e {
          -error "Dependency installation failed. Uninstalling "$pkg", please check the errors above and try again."
          -uninstall-package $pkg
        }
      }
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    -error 'You must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<commit_msg>Fix typo in comments: received -> receives (#839)<commit_after>package bundled

const epmElv = `use re

# Verbosity configuration
debug-mode = $false

# Configuration for common domains
-default-domain-config = [
  &"github.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"bitbucket.org"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
  &"gitlab.com"= [
    &method= git
    &protocol= https
    &levels= 2
  ]
]

# Internal configuration
-data-dir = ~/.elvish
-lib-dir = $-data-dir/lib

# General utility functions

fn -debug [text]{
  if $debug-mode {
    print (styled '=> ' blue)
    echo $text
  }
}

fn -info [text]{
  print (styled '=> ' green)
  echo $text
}

fn -warn [text]{
  print (styled '=> ' yellow)
  echo $text
}

fn -error [text]{
  print (styled '=> ' red)
  echo $text
}

fn dest [pkg]{
  put $-lib-dir/$pkg
}

fn is-installed [pkg]{
  bool ?(test -e (dest $pkg))
}

fn -package-domain [pkg]{
  splits &max=2 / $pkg | take 1
}

fn -package-without-domain [pkg]{
  splits &max=2 / $pkg | drop 1 | joins ''
}

# Merge two maps
fn -merge [a b]{
  keys $b | each [k]{ a[$k] = $b[$k] }
  put $a
}

# Uppercase first letter of a string
fn -first-upper [s]{
  put (echo $s[0] | tr '[:lower:]' '[:upper:]')$s[(count $s[0]):]
}

# Expand tilde at the beginning of a string to the home dir
fn -tilde-expand [p]{
  re:replace "^~" $E:HOME $p
}

# Known method handlers. Each entry is indexed by method name (the
# value of the "method" key in the domain configs), and must contain
# two keys: install and upgrade, each one must be a closure that
# receives two arguments: package name and the domain config entry
#
# - Method 'git' requires the key 'protocol' in the domain config,
#   which has to be 'http' or 'https'
# - Method 'rsync' requires the key 'location' in the domain config,
#   which has to contain the directory where the domain files are
#   stored. It can be any source location understood by the rsync
#   command.
-method-handler = [
  &git= [
    &src= [pkg dom-cfg]{
      put $dom-cfg[protocol]"://"$pkg
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Installing "$pkg
      mkdir -p $dest
      git clone ($-method-handler[git][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      -info "Updating "$pkg
      try {
        git -C $dest pull
      } except _ {
          -error "Something failed, please check error above and retry."
      }
    }
  ]

  &rsync= [
    &src= [pkg dom-cfg]{
      put (-tilde-expand $dom-cfg[location])/(-package-without-domain $pkg)/
    }

    &install= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      -info "Installing "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }

    &upgrade= [pkg dom-cfg]{
      dest = (dest $pkg)
      pkgd = (-package-without-domain $pkg)
      if (not (is-installed $pkg)) {
        -error "Package "$pkg" is not installed."
        return
      }
      -info "Updating "$pkg
      rsync -av ($-method-handler[rsync][src] $pkg $dom-cfg) $dest
    }
  ]
]

# Return the filename of the domain config file for the given domain
# (regardless of whether it exists)
fn -domain-config-file [dom]{
  put $-lib-dir/$dom/epm-domain.cfg
}

# Return the filename of the metadata file for the given package
# (regardless of whether it exists)
fn -package-metadata-file [pkg]{
  put (dest $pkg)/metadata.json
}

fn -write-domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  mkdir -p (dirname $cfgfile)
  if (has-key $-default-domain-config $dom) {
    put $-default-domain-config[$dom] | to-json > $cfgfile
  } else {
    -error "No default config exists for domain "$dom"."
  }
}

# Returns the domain config for a given domain, as a JSON data
# structure. If the file does not exist but we have a built-in
# definition, then we return the default. Otherwise we return $false,
# so the result can always be checked with 'if'.
fn -domain-config [dom]{
  cfgfile = (-domain-config-file $dom)
  cfg = $false
  if ?(test -f $cfgfile) {
    # If the config file exists, read it...
    cfg = (cat $cfgfile | from-json)
    -debug "Read domain config for "$dom": "(to-string $cfg)
  } else {
    # ...otherwise check if we have a default config for the domain, and save it
    if (has-key $-default-domain-config $dom) {
      cfg = $-default-domain-config[$dom]
      -debug "No existing config for "$dom", using the default: "(to-string $cfg)
    } else {
      -debug "No existing config for "$dom" and no default available."
    }
  }
  put $cfg
}

# Return the method by which a package is installed
fn -package-method [pkg]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    put $cfg[method]
  } else {
    put $false
  }
}

# Invoke package operations defined in $-method-handler above
fn -package-op [pkg what]{
  dom = (-package-domain $pkg)
  cfg = (-domain-config $dom)
  if $cfg {
    method = $cfg[method]
    if (has-key $-method-handler $method) {
      if (has-key $-method-handler[$method] $what) {
        $-method-handler[$method][$what] $pkg $cfg
      } else {
        fail "Unknown operation '"$what"' for package "$pkg
      }
    } else {
      fail "Unknown method '"$method"', specified in in config file "(-domain-config-file $dom)
    }
  } else {
    -error "No config for domain '"$dom"'."
  }
}

# Uninstall a single package by removing its directory
fn -uninstall-package [pkg]{
  if (not (is-installed $pkg)) {
    -error "Package "$pkg" is not installed."
    return
  }
  dest = (dest $pkg)
  -info "Removing package "$pkg
  rm -rf $dest
}

######################################################################
# Main user-facing functions

# Read and parse the package metadata, if it exists
fn metadata [pkg]{
  # Base metadata attributes
  res = [
    &name= $pkg
    &method= (-package-method $pkg)
    &src= (-package-op $pkg src)
    &dst= (dest $pkg)
    &installed= (is-installed $pkg)
  ]
  # Merge with package-specified attributes, if any
  file = (-package-metadata-file $pkg)
  if (and (is-installed $pkg) ?(test -f $file)) {
    res = (-merge (cat $file | from-json) $res)
  }
  put $res
}

# Print out information about a package
fn query [pkg]{
  data = (metadata $pkg)
  special-keys = [name method installed src dst]
  echo (styled "Package "$data[name] cyan)
  if $data[installed] {
    echo (styled "Installed at "$data[dst] green)
  } else {
    echo (styled "Not installed" red)
  }
  echo (styled "Source:" blue) $data[method] $data[src]
  keys $data | each [key]{
    if (not (has-value $special-keys $key)) {
      val = $data[$key]
      if (eq (kind-of $val) list) {
        val = (joins ", " $val)
      }
      echo (styled (-first-upper $key)":" blue) $val
    }
  }
}

# List installed packages
fn installed {
  put $-lib-dir/*[nomatch-ok] | each [dir]{
    dom = (replaces $-lib-dir/ '' $dir)
    cfg = (-domain-config $dom)
    # Only list domains for which we know the config, so that the user
    # can have his own non-package directories under ~/.elvish/lib
    # without conflicts.
    if $cfg {
      lvl = $cfg[levels]
      pat = '^\Q'$-lib-dir'/\E('(repeat (+ $lvl 1) '[^/]+' | joins '/')')/$'
      put (each [d]{ re:find $pat $d } [ $-lib-dir/$dom/**[nomatch-ok]/ ] )[groups][1][text]
    }
  }
}

# epm:list is an alias for epm:installed
fn list { installed }

# Install and upgrade are method-specific, so we call the
# corresponding functions using -package-op
fn install [&silent-if-installed=$false @pkgs]{
  if (eq $pkgs []) {
    -error "You must specify at least one package."
    return
  }
  for pkg $pkgs {
    if (is-installed $pkg) {
      if (not $silent-if-installed) {
        -info "Package "$pkg" is already installed."
      }
    } else {
      -package-op $pkg install
      # Check if there are any dependencies to install
      metadata = (metadata $pkg)
      if (has-key $metadata dependencies) {
        deps = $metadata[dependencies]
        -info "Installing dependencies: "(joins " " $deps)
        # If the installation of dependencies fails, uninstall the
        # target package (leave any already-installed dependencies in
        # place)
        try {
          install $@deps
        } except e {
          -error "Dependency installation failed. Uninstalling "$pkg", please check the errors above and try again."
          -uninstall-package $pkg
        }
      }
    }
  }
}

fn upgrade [@pkgs]{
  if (eq $pkgs []) {
    pkgs = [(installed)]
    -info 'Upgrading all installed packages'
  }
  for pkg $pkgs {
    if (not (is-installed $pkg)) {
      -error "Package "$pkg" is not installed."
    } else {
      -package-op $pkg upgrade
    }
  }
}

# Uninstall is the same for everyone, just remove the directory
fn uninstall [@pkgs]{
  if (eq $pkgs []) {
    -error 'You must specify at least one package.'
    return
  }
  for pkg $pkgs {
    -uninstall-package $pkg
  }
}`
<|endoftext|>


<commit_before>package algo

import (
	"strings"
	"testing"
)

func assertMatch(t *testing.T, fun func(bool, bool, []rune, []rune) (int, int), caseSensitive bool, forward bool, input string, pattern string, sidx int, eidx int) {
	if !caseSensitive {
		pattern = strings.ToLower(pattern)
	}
	s, e := fun(caseSensitive, forward, []rune(input), []rune(pattern))
	if s != sidx {
		t.Errorf("Invalid start index: %d (expected: %d, %s / %s)", s, sidx, input, pattern)
	}
	if e != eidx {
		t.Errorf("Invalid end index: %d (expected: %d, %s / %s)", e, eidx, input, pattern)
	}
}

func TestFuzzyMatch(t *testing.T) {
	assertMatch(t, FuzzyMatch, false, true, "fooBarbaz", "oBZ", 2, 9)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "oBZ", -1, -1)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "oBz", 2, 9)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "fooBarbazz", -1, -1)
}

func TestFuzzyMatchBackward(t *testing.T) {
	assertMatch(t, FuzzyMatch, false, true, "foobar fb", "fb", 0, 4)
	assertMatch(t, FuzzyMatch, false, false, "foobar fb", "fb", 7, 9)
}

func TestExactMatchNaive(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, ExactMatchNaive, false, dir, "fooBarbaz", "oBA", 2, 5)
		assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "oBA", -1, -1)
		assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "fooBarbazz", -1, -1)
	}
}

func TestExactMatchNaiveBackward(t *testing.T) {
	assertMatch(t, FuzzyMatch, false, true, "foobar foob", "oo", 1, 3)
	assertMatch(t, FuzzyMatch, false, false, "foobar foob", "oo", 8, 10)
}

func TestPrefixMatch(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "Foo", 0, 3)
		assertMatch(t, PrefixMatch, true, dir, "fooBarbaz", "Foo", -1, -1)
		assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "baz", -1, -1)
	}
}

func TestSuffixMatch(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "Foo", -1, -1)
		assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "baz", 6, 9)
		assertMatch(t, SuffixMatch, true, dir, "fooBarbaz", "Baz", -1, -1)
	}
}

func TestEmptyPattern(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, FuzzyMatch, true, dir, "foobar", "", 0, 0)
		assertMatch(t, ExactMatchNaive, true, dir, "foobar", "", 0, 0)
		assertMatch(t, PrefixMatch, true, dir, "foobar", "", 0, 0)
		assertMatch(t, SuffixMatch, true, dir, "foobar", "", 6, 6)
	}
}
<commit_msg>Fix algorithm tests<commit_after>package algo

import (
	"strings"
	"testing"
)

func assertMatch(t *testing.T, fun func(bool, bool, []rune, []rune) (int, int), caseSensitive bool, forward bool, input string, pattern string, sidx int, eidx int) {
	if !caseSensitive {
		pattern = strings.ToLower(pattern)
	}
	s, e := fun(caseSensitive, forward, []rune(input), []rune(pattern))
	if s != sidx {
		t.Errorf("Invalid start index: %d (expected: %d, %s / %s)", s, sidx, input, pattern)
	}
	if e != eidx {
		t.Errorf("Invalid end index: %d (expected: %d, %s / %s)", e, eidx, input, pattern)
	}
}

func TestFuzzyMatch(t *testing.T) {
	assertMatch(t, FuzzyMatch, false, true, "fooBarbaz", "oBZ", 2, 9)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "oBZ", -1, -1)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "oBz", 2, 9)
	assertMatch(t, FuzzyMatch, true, true, "fooBarbaz", "fooBarbazz", -1, -1)
}

func TestFuzzyMatchBackward(t *testing.T) {
	assertMatch(t, FuzzyMatch, false, true, "foobar fb", "fb", 0, 4)
	assertMatch(t, FuzzyMatch, false, false, "foobar fb", "fb", 7, 9)
}

func TestExactMatchNaive(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, ExactMatchNaive, false, dir, "fooBarbaz", "oBA", 2, 5)
		assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "oBA", -1, -1)
		assertMatch(t, ExactMatchNaive, true, dir, "fooBarbaz", "fooBarbazz", -1, -1)
	}
}

func TestExactMatchNaiveBackward(t *testing.T) {
	assertMatch(t, ExactMatchNaive, false, true, "foobar foob", "oo", 1, 3)
	assertMatch(t, ExactMatchNaive, false, false, "foobar foob", "oo", 8, 10)
}

func TestPrefixMatch(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "Foo", 0, 3)
		assertMatch(t, PrefixMatch, true, dir, "fooBarbaz", "Foo", -1, -1)
		assertMatch(t, PrefixMatch, false, dir, "fooBarbaz", "baz", -1, -1)
	}
}

func TestSuffixMatch(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "Foo", -1, -1)
		assertMatch(t, SuffixMatch, false, dir, "fooBarbaz", "baz", 6, 9)
		assertMatch(t, SuffixMatch, true, dir, "fooBarbaz", "Baz", -1, -1)
	}
}

func TestEmptyPattern(t *testing.T) {
	for _, dir := range []bool{true, false} {
		assertMatch(t, FuzzyMatch, true, dir, "foobar", "", 0, 0)
		assertMatch(t, ExactMatchNaive, true, dir, "foobar", "", 0, 0)
		assertMatch(t, PrefixMatch, true, dir, "foobar", "", 0, 0)
		assertMatch(t, SuffixMatch, true, dir, "foobar", "", 6, 6)
	}
}
<|endoftext|>


<commit_before>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    Type: MMv1     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import "reflect"

const VertexAIFeaturestoreEntitytypeAssetType string = "{{region}}-aiplatform.googleapis.com/FeaturestoreEntitytype"

func resourceConverterVertexAIFeaturestoreEntitytype() ResourceConverter {
	return ResourceConverter{
		AssetType: VertexAIFeaturestoreEntitytypeAssetType,
		Convert:   GetVertexAIFeaturestoreEntitytypeCaiObject,
	}
}

func GetVertexAIFeaturestoreEntitytypeCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	name, err := assetName(d, config, "//{{region}}-aiplatform.googleapis.com/{{featurestore}}/entityTypes/{{name}}")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetVertexAIFeaturestoreEntitytypeApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: VertexAIFeaturestoreEntitytypeAssetType,
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/{{region}}-aiplatform/v1/rest",
				DiscoveryName:        "FeaturestoreEntitytype",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetVertexAIFeaturestoreEntitytypeApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	labelsProp, err := expandVertexAIFeaturestoreEntitytypeLabels(d.Get("labels"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
		obj["labels"] = labelsProp
	}
	monitoringConfigProp, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfig(d.Get("monitoring_config"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("monitoring_config"); !isEmptyValue(reflect.ValueOf(monitoringConfigProp)) && (ok || !reflect.DeepEqual(v, monitoringConfigProp)) {
		obj["monitoringConfig"] = monitoringConfigProp
	}

	return obj, nil
}

func expandVertexAIFeaturestoreEntitytypeLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) {
	if v == nil {
		return map[string]string{}, nil
	}
	m := make(map[string]string)
	for k, val := range v.(map[string]interface{}) {
		m[k] = val.(string)
	}
	return m, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedSnapshotAnalysis, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysis(original["snapshot_analysis"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSnapshotAnalysis); val.IsValid() && !isEmptyValue(val) {
		transformed["snapshotAnalysis"] = transformedSnapshotAnalysis
	}

	return transformed, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysis(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedDisabled, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysisDisabled(original["disabled"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) {
		transformed["disabled"] = transformedDisabled
	}

	return transformed, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysisDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<commit_msg>fix: added parameter region to google_vertex_ai_featurestore_entitytype (#6588) (#1089)<commit_after>// ----------------------------------------------------------------------------
//
//     ***     AUTO GENERATED CODE    ***    Type: MMv1     ***
//
// ----------------------------------------------------------------------------
//
//     This file is automatically generated by Magic Modules and manual
//     changes will be clobbered when the file is regenerated.
//
//     Please read more about how to change this file in
//     .github/CONTRIBUTING.md.
//
// ----------------------------------------------------------------------------

package google

import (
	"reflect"
	"regexp"
)

const VertexAIFeaturestoreEntitytypeAssetType string = "{{region}}-aiplatform.googleapis.com/FeaturestoreEntitytype"

func resourceConverterVertexAIFeaturestoreEntitytype() ResourceConverter {
	return ResourceConverter{
		AssetType: VertexAIFeaturestoreEntitytypeAssetType,
		Convert:   GetVertexAIFeaturestoreEntitytypeCaiObject,
	}
}

func GetVertexAIFeaturestoreEntitytypeCaiObject(d TerraformResourceData, config *Config) ([]Asset, error) {
	name, err := assetName(d, config, "//{{region}}-aiplatform.googleapis.com/{{featurestore}}/entityTypes/{{name}}")
	if err != nil {
		return []Asset{}, err
	}
	if obj, err := GetVertexAIFeaturestoreEntitytypeApiObject(d, config); err == nil {
		return []Asset{{
			Name: name,
			Type: VertexAIFeaturestoreEntitytypeAssetType,
			Resource: &AssetResource{
				Version:              "v1",
				DiscoveryDocumentURI: "https://www.googleapis.com/discovery/v1/apis/{{region}}-aiplatform/v1/rest",
				DiscoveryName:        "FeaturestoreEntitytype",
				Data:                 obj,
			},
		}}, nil
	} else {
		return []Asset{}, err
	}
}

func GetVertexAIFeaturestoreEntitytypeApiObject(d TerraformResourceData, config *Config) (map[string]interface{}, error) {
	obj := make(map[string]interface{})
	labelsProp, err := expandVertexAIFeaturestoreEntitytypeLabels(d.Get("labels"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("labels"); !isEmptyValue(reflect.ValueOf(labelsProp)) && (ok || !reflect.DeepEqual(v, labelsProp)) {
		obj["labels"] = labelsProp
	}
	monitoringConfigProp, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfig(d.Get("monitoring_config"), d, config)
	if err != nil {
		return nil, err
	} else if v, ok := d.GetOkExists("monitoring_config"); !isEmptyValue(reflect.ValueOf(monitoringConfigProp)) && (ok || !reflect.DeepEqual(v, monitoringConfigProp)) {
		obj["monitoringConfig"] = monitoringConfigProp
	}

	return resourceVertexAIFeaturestoreEntitytypeEncoder(d, config, obj)
}

func resourceVertexAIFeaturestoreEntitytypeEncoder(d TerraformResourceData, meta interface{}, obj map[string]interface{}) (map[string]interface{}, error) {
	if v, ok := d.GetOk("featurestore"); ok {
		re := regexp.MustCompile("projects/(.+)/locations/(.+)/featurestores/(.+)$")
		if parts := re.FindStringSubmatch(v.(string)); parts != nil {
			d.Set("region", parts[2])
		}
	}

	return obj, nil
}

func expandVertexAIFeaturestoreEntitytypeLabels(v interface{}, d TerraformResourceData, config *Config) (map[string]string, error) {
	if v == nil {
		return map[string]string{}, nil
	}
	m := make(map[string]string)
	for k, val := range v.(map[string]interface{}) {
		m[k] = val.(string)
	}
	return m, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedSnapshotAnalysis, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysis(original["snapshot_analysis"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedSnapshotAnalysis); val.IsValid() && !isEmptyValue(val) {
		transformed["snapshotAnalysis"] = transformedSnapshotAnalysis
	}

	return transformed, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysis(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	l := v.([]interface{})
	if len(l) == 0 || l[0] == nil {
		return nil, nil
	}
	raw := l[0]
	original := raw.(map[string]interface{})
	transformed := make(map[string]interface{})

	transformedDisabled, err := expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysisDisabled(original["disabled"], d, config)
	if err != nil {
		return nil, err
	} else if val := reflect.ValueOf(transformedDisabled); val.IsValid() && !isEmptyValue(val) {
		transformed["disabled"] = transformedDisabled
	}

	return transformed, nil
}

func expandVertexAIFeaturestoreEntitytypeMonitoringConfigSnapshotAnalysisDisabled(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
	return v, nil
}
<|endoftext|>