<commit_before>package endly

import (
	"fmt"
	uuid "github.com/satori/go.uuid"
	"github.com/viant/endly/msg"
	"github.com/viant/neatly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/secret"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)

//Context represents a workflow session context/state
type Context struct {
	SessionID       string
	CLIEnabled      bool
	HasLogger       bool
	AsyncUnsafeKeys map[interface{}]bool
	Secrets         *secret.Service
	Wait            *sync.WaitGroup
	Listener        msg.Listener
	Source          *url.Resource
	state           data.Map
	toolbox.Context
	cloned []*Context
	closed int32
}

//Publish publishes event to listeners, it updates current run details like activity workflow name etc ...
func (c *Context) Publish(value interface{}) msg.Event {
	event, ok := value.(msg.Event)
	if !ok {
		event = msg.NewEvent(value)
	}
	if c.Listener != nil {
		c.Listener(event)
	}
	return event
}

//PublishWithStartEvent publishes event to listeners, it updates current run details like activity workflow name etc ...
func (c *Context) PublishWithStartEvent(value interface{}, init msg.Event) msg.Event {
	event := msg.NewEventWithInit(value, init)
	if c.Listener != nil {
		c.Listener(event)
	}
	return event
}

//SetListener sets context event Listener
func (c *Context) SetListener(listener msg.Listener) {
	c.Listener = listener
}

//IsClosed returns true if it is closed.
func (c *Context) IsClosed() bool {
	return atomic.LoadInt32(&c.closed) == 1
}

//Clone clones the context.
func (c *Context) Clone() *Context {
	if len(c.cloned) == 0 {
		c.cloned = make([]*Context, 0)
	}
	result := &Context{}
	result.Wait = &sync.WaitGroup{}
	result.Context = c.Context.Clone()
	result.state = NewDefaultState()
	result.state.Apply(c.state)
	result.SessionID = c.SessionID
	result.Listener = c.Listener
	result.CLIEnabled = c.CLIEnabled
	result.Secrets = c.Secrets
	result.AsyncUnsafeKeys = make(map[interface{}]bool)
	for k, v := range c.AsyncUnsafeKeys {
		result.AsyncUnsafeKeys[k] = v
	}
	c.cloned = append(c.cloned, result)
	return result
}

func (c *Context) parentURLCandidates() []string {
	var result = make([]string, 0)
	if c.Source != nil {
		baseURL, _ := toolbox.URLSplit(c.Source.URL)
		result = append(result, baseURL)
	}
	currentDirectory, err := os.Getwd()
	if err == nil {
		result = append(result, toolbox.FileSchema+currentDirectory)
	}

	return result
}

//ExpandResource substitutes any $ expression with the key value from the state map if it is present.
func (c *Context) ExpandResource(resource *url.Resource) (*url.Resource, error) {
	if resource == nil {
		return nil, msg.ReportError(fmt.Errorf("resource  was empty"))
	}
	if resource.URL == "" {
		return nil, msg.ReportError(fmt.Errorf("url was empty"))
	}

	if !strings.Contains(resource.URL, "://") {
		for _, parentCandidate := range c.parentURLCandidates() {
			service, err := storage.NewServiceForURL(parentCandidate, "")
			if err != nil {
				continue
			}
			var candidateURL = toolbox.URLPathJoin(parentCandidate, resource.URL)
			if exists, err := service.Exists(candidateURL); exists && err == nil {
				resource.URL = candidateURL
			}
		}
	}
	var result = url.NewResource(c.Expand(resource.URL), c.Expand(resource.Credentials))
	if result.ParsedURL == nil {
		return nil, fmt.Errorf("failed to parse URL %v", result.URL)
	}
	result.Cache = c.Expand(resource.Cache)
	result.CacheExpiryMs = resource.CacheExpiryMs
	return result, nil
}

//Manager returns workflow manager or error
func (c *Context) Manager() (Manager, error) {
	if c == nil {
		return nil, fmt.Errorf("context was nil")
	}
	var manager = &manager{}
	if !c.GetInto(serviceManagerKey, &manager) {
		return nil, msg.ReportError(fmt.Errorf("failed to lookup Manager"))
	}
	return manager, nil
}

//Service returns a service fo provided id or error.
func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

//Deffer add function to be executed if context closes. If returns currently registered functions.
func (c *Context) Deffer(functions ...func()) []func() {
	var result *[]func()
	if !c.Contains(deferFunctionsKey) {
		var functions = make([]func(), 0)
		result = &functions
		_ = c.Put(deferFunctionsKey, result)
	} else {
		c.GetInto(deferFunctionsKey, &result)
	}

	*result = append(*result, functions...)
	_ = c.Put(deferFunctionsKey, &result)
	return *result
}

//State returns a context state map.
func (c *Context) State() data.Map {
	if c.state == nil {
		c.state = NewDefaultState()
	}
	return c.state
}

//SetState sets a new state map
func (c *Context) SetState(state data.Map) {
	c.state = state
}

//Expand substitute $ expression if present in the text and state map.
func (c *Context) Expand(text string) string {
	state := c.State()
	return state.ExpandAsText(text)
}

//PublishAndRestore sets supplied value and returns func restoring original values
func (s *Context) PublishAndRestore(values map[string]interface{}) func() {
	var backup = map[string]interface{}{}
	for k, v := range values {
		if value, has := s.state.GetValue(k); has {
			backup[k] = value
		}
		s.state.SetValue(k, v)
	}
	return func() {
		for k, v := range backup {
			s.state.SetValue(k, v)
		}
	}
}

//NewRequest creates a new request for service and action
func (c *Context) NewRequest(serviceName, action string) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	route, err := service.Route(action)
	if err != nil {
		return nil, err
	}
	return route.RequestProvider(), nil
}

//AsRequest converts a source map into request for provided service and action.
func (c *Context) AsRequest(serviceName, action string, source map[string]interface{}) (request interface{}, err error) {
	if request, err = c.NewRequest(serviceName, action); err != nil {
		return request, err
	}
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("failed to create request, unable to cast %v into %T, %v", source, request, r)
		}
	}()
	expanded := c.state.Expand(source)
	source = toolbox.AsMap(expanded)
	err = toolbox.DefaultConverter.AssignConverted(request, source)
	return request, err
}

//Close closes this context, it executes all deferred function and set closed flag.
func (c *Context) Close() {
	atomic.StoreInt32(&c.closed, 1)
	for _, context := range c.cloned {
		context.Close()
	}
	for _, function := range c.Deffer() {
		function()
	}
}

//MakeAsyncSafe makes this contex async safe

func (c *Context) MakeAsyncSafe() *msg.Events {
	for k := range c.AsyncUnsafeKeys {
		c.Context.Remove(k)
	}
	result := msg.NewEvents()
	c.Listener = result.AsListener()
	return result
}

var yyyyMMDDLayout = toolbox.DateFormatToLayout("yyyy-MM-dd")
var yyyMMDDHHMMSSLayout = toolbox.DateFormatToLayout("yyyy-MM-dd hh:mm:ss")
var numberDateLayout = toolbox.DateFormatToLayout("yyyyMMddhhmmSSS")

/*
NewDefaultState returns a new default state.
It comes with the following registered keys:
	* rand - random int64
	* date -  current date formatted as yyyy-MM-dd
	* time - current time formatted as yyyy-MM-dd hh:mm:ss
	* ts - current timestamp formatted  as yyyyMMddhhmmSSS
	* timestamp.XXX - timestamp in ms where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* unix.XXX - timestamp in sec where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* tzTime.XXX - RFC3339 formatted time where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* tmpDir - temp directory
	* uuid.next - generate unique id
	* uuid.Get - returns previously generated unique id, or generate new
	*.env.XXX where XXX is the ID of the env variable to return
	* all UFD registry functions
*/
func NewDefaultState() data.Map {
	var result = data.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("rand", source.Int63())
	result.Put("date", now.Format(yyyyMMDDLayout))
	result.Put("time", now.Format(yyyMMDDHHMMSSLayout))
	result.Put("ts", now.Unix())

	result.Put("tmpDir", func(key string) interface{} {
		tempPath := path.Join(os.TempDir(), key)
		exec.Command("mkdir -p " + tempPath)
		return tempPath
	})

	var cachedUUID uuid.UUID
	result.Put("uuid", func(key string) interface{} {
		if key == "next" {
			var err error
			cachedUUID, err = uuid.NewV4()
			if err != nil {
				return ""
			}
		}
		if len(cachedUUID) > 0 {
			return cachedUUID.String()
		}
		return ""
	})

	//returns time in ms
	result.Put("timestamp", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return int(timeAt.Unix()+timeAt.UnixNano()) / 1000000
	})
	//return time in sec
	result.Put("unix", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return int(timeAt.Unix()+timeAt.UnixNano()) / 1000000000
	})

	//return formatted time with time.RFC3339 yyyy-MM-ddThh:mm:ss.SSS Z  i.e ${tzTime.4daysAgoInUTC}

	result.Put("tzTime", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return timeAt.Format(time.RFC3339)
	})

	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})

	neatly.AddStandardUdf(result)
	for k, v := range UdfRegistry {
		result.Put(k, v)
	}
	return result
}
<commit_msg>updated message in error handling<commit_after>package endly

import (
	"fmt"
	uuid "github.com/satori/go.uuid"
	"github.com/viant/endly/msg"
	"github.com/viant/neatly"
	"github.com/viant/toolbox"
	"github.com/viant/toolbox/data"
	"github.com/viant/toolbox/secret"
	"github.com/viant/toolbox/storage"
	"github.com/viant/toolbox/url"
	"math/rand"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

var serviceManagerKey = (*manager)(nil)
var deferFunctionsKey = (*[]func())(nil)

//Context represents a workflow session context/state
type Context struct {
	SessionID       string
	CLIEnabled      bool
	HasLogger       bool
	AsyncUnsafeKeys map[interface{}]bool
	Secrets         *secret.Service
	Wait            *sync.WaitGroup
	Listener        msg.Listener
	Source          *url.Resource
	state           data.Map
	toolbox.Context
	cloned []*Context
	closed int32
}

//Publish publishes event to listeners, it updates current run details like activity workflow name etc ...
func (c *Context) Publish(value interface{}) msg.Event {
	event, ok := value.(msg.Event)
	if !ok {
		event = msg.NewEvent(value)
	}
	if c.Listener != nil {
		c.Listener(event)
	}
	return event
}

//PublishWithStartEvent publishes event to listeners, it updates current run details like activity workflow name etc ...
func (c *Context) PublishWithStartEvent(value interface{}, init msg.Event) msg.Event {
	event := msg.NewEventWithInit(value, init)
	if c.Listener != nil {
		c.Listener(event)
	}
	return event
}

//SetListener sets context event Listener
func (c *Context) SetListener(listener msg.Listener) {
	c.Listener = listener
}

//IsClosed returns true if it is closed.
func (c *Context) IsClosed() bool {
	return atomic.LoadInt32(&c.closed) == 1
}

//Clone clones the context.
func (c *Context) Clone() *Context {
	if len(c.cloned) == 0 {
		c.cloned = make([]*Context, 0)
	}
	result := &Context{}
	result.Wait = &sync.WaitGroup{}
	result.Context = c.Context.Clone()
	result.state = NewDefaultState()
	result.state.Apply(c.state)
	result.SessionID = c.SessionID
	result.Listener = c.Listener
	result.CLIEnabled = c.CLIEnabled
	result.Secrets = c.Secrets
	result.AsyncUnsafeKeys = make(map[interface{}]bool)
	for k, v := range c.AsyncUnsafeKeys {
		result.AsyncUnsafeKeys[k] = v
	}
	c.cloned = append(c.cloned, result)
	return result
}

func (c *Context) parentURLCandidates() []string {
	var result = make([]string, 0)
	if c.Source != nil {
		baseURL, _ := toolbox.URLSplit(c.Source.URL)
		result = append(result, baseURL)
	}
	currentDirectory, err := os.Getwd()
	if err == nil {
		result = append(result, toolbox.FileSchema+currentDirectory)
	}

	return result
}

//ExpandResource substitutes any $ expression with the key value from the state map if it is present.
func (c *Context) ExpandResource(resource *url.Resource) (*url.Resource, error) {
	if resource == nil {
		return nil, msg.ReportError(fmt.Errorf("resource  was empty"))
	}
	if resource.URL == "" {
		return nil, msg.ReportError(fmt.Errorf("url was empty"))
	}

	if !strings.Contains(resource.URL, "://") {
		for _, parentCandidate := range c.parentURLCandidates() {
			service, err := storage.NewServiceForURL(parentCandidate, "")
			if err != nil {
				continue
			}
			var candidateURL = toolbox.URLPathJoin(parentCandidate, resource.URL)
			if exists, err := service.Exists(candidateURL); exists && err == nil {
				resource.URL = candidateURL
			}
		}
	}
	var result = url.NewResource(c.Expand(resource.URL), c.Expand(resource.Credentials))
	if result.ParsedURL == nil {
		return nil, fmt.Errorf("failed to parse URL %v", result.URL)
	}
	result.Cache = c.Expand(resource.Cache)
	result.CacheExpiryMs = resource.CacheExpiryMs
	return result, nil
}

//Manager returns workflow manager or error
func (c *Context) Manager() (Manager, error) {
	if c == nil {
		return nil, fmt.Errorf("context was nil")
	}
	var manager = &manager{}
	if !c.GetInto(serviceManagerKey, &manager) {
		return nil, msg.ReportError(fmt.Errorf("failed to lookup Manager"))
	}
	return manager, nil
}

//Service returns a service fo provided id or error.
func (c *Context) Service(name string) (Service, error) {
	manager, err := c.Manager()
	if err != nil {
		return nil, err
	}
	return manager.Service(name)
}

//Deffer add function to be executed if context closes. If returns currently registered functions.
func (c *Context) Deffer(functions ...func()) []func() {
	var result *[]func()
	if !c.Contains(deferFunctionsKey) {
		var functions = make([]func(), 0)
		result = &functions
		_ = c.Put(deferFunctionsKey, result)
	} else {
		c.GetInto(deferFunctionsKey, &result)
	}

	*result = append(*result, functions...)
	_ = c.Put(deferFunctionsKey, &result)
	return *result
}

//State returns a context state map.
func (c *Context) State() data.Map {
	if c.state == nil {
		c.state = NewDefaultState()
	}
	return c.state
}

//SetState sets a new state map
func (c *Context) SetState(state data.Map) {
	c.state = state
}

//Expand substitute $ expression if present in the text and state map.
func (c *Context) Expand(text string) string {
	state := c.State()
	return state.ExpandAsText(text)
}

//PublishAndRestore sets supplied value and returns func restoring original values
func (s *Context) PublishAndRestore(values map[string]interface{}) func() {
	var backup = map[string]interface{}{}
	for k, v := range values {
		if value, has := s.state.GetValue(k); has {
			backup[k] = value
		}
		s.state.SetValue(k, v)
	}
	return func() {
		for k, v := range backup {
			s.state.SetValue(k, v)
		}
	}
}

//NewRequest creates a new request for service and action
func (c *Context) NewRequest(serviceName, action string) (interface{}, error) {
	service, err := c.Service(serviceName)
	if err != nil {
		return nil, err
	}
	route, err := service.Route(action)
	if err != nil {
		return nil, err
	}
	return route.RequestProvider(), nil
}

//AsRequest converts a source map into request for provided service and action.
func (c *Context) AsRequest(serviceName, action string, source map[string]interface{}) (request interface{}, err error) {

	if request, err = c.NewRequest(serviceName, action); err != nil {
		return request, fmt.Errorf("unable to create %v request %v", serviceName+":"+action, err)
	}
	defer func() {

		if r := recover(); r != nil {
			var info  = toolbox.AsString(source)
			if JSONSource, err :=  toolbox.AsJSONText(source);err == nil {
				info = JSONSource
			}
			err = fmt.Errorf("unable to create %v request %v, request: %v", serviceName+":"+action, err, info)
		}
	}()
	expanded := c.state.Expand(source)
	source = toolbox.AsMap(expanded)
	err = toolbox.DefaultConverter.AssignConverted(request, source)
	return request, err
}

//Close closes this context, it executes all deferred function and set closed flag.
func (c *Context) Close() {
	atomic.StoreInt32(&c.closed, 1)
	for _, context := range c.cloned {
		context.Close()
	}
	for _, function := range c.Deffer() {
		function()
	}
}

//MakeAsyncSafe makes this contex async safe

func (c *Context) MakeAsyncSafe() *msg.Events {
	for k := range c.AsyncUnsafeKeys {
		c.Context.Remove(k)
	}
	result := msg.NewEvents()
	c.Listener = result.AsListener()
	return result
}

var yyyyMMDDLayout = toolbox.DateFormatToLayout("yyyy-MM-dd")
var yyyMMDDHHMMSSLayout = toolbox.DateFormatToLayout("yyyy-MM-dd hh:mm:ss")
var numberDateLayout = toolbox.DateFormatToLayout("yyyyMMddhhmmSSS")

/*
NewDefaultState returns a new default state.
It comes with the following registered keys:
	* rand - random int64
	* date -  current date formatted as yyyy-MM-dd
	* time - current time formatted as yyyy-MM-dd hh:mm:ss
	* ts - current timestamp formatted  as yyyyMMddhhmmSSS
	* timestamp.XXX - timestamp in ms where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* unix.XXX - timestamp in sec where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* tzTime.XXX - RFC3339 formatted time where XXX is time diff expression i.e 3DaysAgo, tomorrow, hourAhead
	* tmpDir - temp directory
	* uuid.next - generate unique id
	* uuid.Get - returns previously generated unique id, or generate new
	*.env.XXX where XXX is the ID of the env variable to return
	* all UFD registry functions
*/
func NewDefaultState() data.Map {
	var result = data.NewMap()
	var now = time.Now()
	source := rand.NewSource(now.UnixNano())
	result.Put("rand", source.Int63())
	result.Put("date", now.Format(yyyyMMDDLayout))
	result.Put("time", now.Format(yyyMMDDHHMMSSLayout))
	result.Put("ts", now.Unix())

	result.Put("tmpDir", func(key string) interface{} {
		tempPath := path.Join(os.TempDir(), key)
		exec.Command("mkdir -p " + tempPath)
		return tempPath
	})

	var cachedUUID uuid.UUID
	result.Put("uuid", func(key string) interface{} {
		if key == "next" {
			var err error
			cachedUUID, err = uuid.NewV4()
			if err != nil {
				return ""
			}
		}
		if len(cachedUUID) > 0 {
			return cachedUUID.String()
		}
		return ""
	})

	//returns time in ms
	result.Put("timestamp", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return int(timeAt.Unix()+timeAt.UnixNano()) / 1000000
	})
	//return time in sec
	result.Put("unix", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return int(timeAt.Unix()+timeAt.UnixNano()) / 1000000000
	})

	//return formatted time with time.RFC3339 yyyy-MM-ddThh:mm:ss.SSS Z  i.e ${tzTime.4daysAgoInUTC}

	result.Put("tzTime", func(key string) interface{} {
		timeAt, err := toolbox.TimeAt(key)
		if err != nil {
			return nil
		}
		return timeAt.Format(time.RFC3339)
	})

	result.Put("env", func(key string) interface{} {
		return os.Getenv(key)
	})

	neatly.AddStandardUdf(result)
	for k, v := range UdfRegistry {
		result.Put(k, v)
	}
	return result
}
<|endoftext|>


<commit_before>package app

import (
	"fmt"

	"github.com/murlokswarm/log"
	"github.com/murlokswarm/markup"
	"github.com/murlokswarm/uid"
)

var (
	contexts = map[uid.ID]Contexter{}
)

// Contexter represents the support where a component can be mounted.
// eg a window.
type Contexter interface {
	// The ID of the context.
	ID() uid.ID

	// Mounts the component and renders it in the context.
	Mount(c markup.Componer)

	// Renders an element.
	Render(elem *markup.Element)

	// If applicable, returns the position of the context.
	Position() (x float64, y float64)

	// If applicable, moves the context.
	Move(x float64, y float64)

	// If applicable, returns the size of the context.
	Size() (width float64, height float64)

	// If applicable, resizes the context.
	Resize(width float64, height float64)

	// If applicable, set the icon targeted by path.
	SetIcon(path string)

	// If applicable, set the badge with v.
	// Uses fmt.Sprint under the hood.
	SetBadge(v interface{})

	// If applicablex, closes the context.
	Close()
}

// Context returns the context of c.
// c must be mounted.
func Context(c markup.Componer) (ctx Contexter, err error) {
	var root *markup.Element

	if root, err = markup.ComponentRoot(c); err != nil {
		return
	}

	ctx, err = ContextByID(root.ContextID)
	return
}

// ContextByID returns the context registered under id.
func ContextByID(id uid.ID) (ctx Contexter, err error) {
	var registered bool

	if ctx, registered = contexts[id]; !registered {
		err = fmt.Errorf("context %v is not registered or has been closed", id)
	}

	return
}

// RegisterContext registers c.
// Should be used only in a driver implementation.
func RegisterContext(c Contexter) {
	if len(c.ID()) == 0 {
		log.Panicf("context %T is invalid. ID must be set", c)
	}

	if _, registered := contexts[c.ID()]; registered {
		log.Panicf("context %T with id %v is already registered", c, c.ID())
	}

	contexts[c.ID()] = c
}

// UnregisterContext unregisters c.
// Should be used only in a driver implementation.
func UnregisterContext(c Contexter) {
	delete(contexts, c.ID())
}

// ZeroContext is a placeholder context.
// It's used as a replacement for non available or non implemented features.
//
// Use of methods from a ZeroContext doesn't do anything.
type ZeroContext struct {
	id          uid.ID
	placeholder string
	root        markup.Componer
}

// NewZeroContext creates a ZeroContext.
func NewZeroContext(placeholder string) (ctx *ZeroContext) {
	ctx = &ZeroContext{
		id:          uid.Context(),
		placeholder: placeholder,
	}

	RegisterContext(ctx)
	return
}

// ID returns the ID of the context.
func (c *ZeroContext) ID() uid.ID {
	return c.id
}

// Mount is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Mount(component markup.Componer) {
	markup.Mount(component, c.ID())
	log.Infof("%T is mounted into %v (%v)", component, c.placeholder, c.ID())
}

// Render is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Render(elem *markup.Element) {
	log.Infof("rendering:\n\033[32m%v\033[00m", elem.HTML())
}

// Size is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Size() (width float64, height float64) {
	return
}

// Resize is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Resize(width float64, height float64) {
	log.Infof("%v (%v) simulates a resize of %v x %v", c.placeholder, c.ID(), width, height)
}

// Position is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Position() (x float64, y float64) {
	return
}

// Move is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Move(x float64, y float64) {
	log.Infof("%v (%v) simulates a move to (%v, %v)", c.placeholder, c.ID(), x, y)
}

// SetIcon is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetIcon(path string) {
	log.Infof("%v (%v) simulates set icon with %v", c.placeholder, c.ID(), path)
}

// SetBadge is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetBadge(v interface{}) {
	log.Infof("%v (%v) simulates set badge with %v", c.placeholder, c.ID(), v)

}

// Close is a closes the context.
func (c *ZeroContext) Close() {
	markup.Dismount(c.root)
	UnregisterContext(c)
	log.Infof("%v (%v) is closed", c.placeholder, c.ID())
}
<commit_msg>change badge description<commit_after>package app

import (
	"fmt"

	"github.com/murlokswarm/log"
	"github.com/murlokswarm/markup"
	"github.com/murlokswarm/uid"
)

var (
	contexts = map[uid.ID]Contexter{}
)

// Contexter represents the support where a component can be mounted.
// eg a window.
type Contexter interface {
	// The ID of the context.
	ID() uid.ID

	// Mounts the component and renders it in the context.
	Mount(c markup.Componer)

	// Renders an element.
	Render(elem *markup.Element)

	// If applicable, returns the position of the context.
	Position() (x float64, y float64)

	// If applicable, moves the context.
	Move(x float64, y float64)

	// If applicable, returns the size of the context.
	Size() (width float64, height float64)

	// If applicable, resizes the context.
	Resize(width float64, height float64)

	// If applicable, set the icon targeted by path.
	SetIcon(path string)

	// If applicable, set the badge with v.
	SetBadge(v interface{})

	// If applicablex, closes the context.
	Close()
}

// Context returns the context of c.
// c must be mounted.
func Context(c markup.Componer) (ctx Contexter, err error) {
	var root *markup.Element

	if root, err = markup.ComponentRoot(c); err != nil {
		return
	}

	ctx, err = ContextByID(root.ContextID)
	return
}

// ContextByID returns the context registered under id.
func ContextByID(id uid.ID) (ctx Contexter, err error) {
	var registered bool

	if ctx, registered = contexts[id]; !registered {
		err = fmt.Errorf("context %v is not registered or has been closed", id)
	}

	return
}

// RegisterContext registers c.
// Should be used only in a driver implementation.
func RegisterContext(c Contexter) {
	if len(c.ID()) == 0 {
		log.Panicf("context %T is invalid. ID must be set", c)
	}

	if _, registered := contexts[c.ID()]; registered {
		log.Panicf("context %T with id %v is already registered", c, c.ID())
	}

	contexts[c.ID()] = c
}

// UnregisterContext unregisters c.
// Should be used only in a driver implementation.
func UnregisterContext(c Contexter) {
	delete(contexts, c.ID())
}

// ZeroContext is a placeholder context.
// It's used as a replacement for non available or non implemented features.
//
// Use of methods from a ZeroContext doesn't do anything.
type ZeroContext struct {
	id          uid.ID
	placeholder string
	root        markup.Componer
}

// NewZeroContext creates a ZeroContext.
func NewZeroContext(placeholder string) (ctx *ZeroContext) {
	ctx = &ZeroContext{
		id:          uid.Context(),
		placeholder: placeholder,
	}

	RegisterContext(ctx)
	return
}

// ID returns the ID of the context.
func (c *ZeroContext) ID() uid.ID {
	return c.id
}

// Mount is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Mount(component markup.Componer) {
	markup.Mount(component, c.ID())
	log.Infof("%T is mounted into %v (%v)", component, c.placeholder, c.ID())
}

// Render is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Render(elem *markup.Element) {
	log.Infof("rendering:\n\033[32m%v\033[00m", elem.HTML())
}

// Size is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Size() (width float64, height float64) {
	return
}

// Resize is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Resize(width float64, height float64) {
	log.Infof("%v (%v) simulates a resize of %v x %v", c.placeholder, c.ID(), width, height)
}

// Position is a placeholder method to satisfy the Contexter interface.
func (c *ZeroContext) Position() (x float64, y float64) {
	return
}

// Move is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) Move(x float64, y float64) {
	log.Infof("%v (%v) simulates a move to (%v, %v)", c.placeholder, c.ID(), x, y)
}

// SetIcon is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetIcon(path string) {
	log.Infof("%v (%v) simulates set icon with %v", c.placeholder, c.ID(), path)
}

// SetBadge is a placeholder method to satisfy the Contexter interface.
// It does nothing.
func (c *ZeroContext) SetBadge(v interface{}) {
	log.Infof("%v (%v) simulates set badge with %v", c.placeholder, c.ID(), v)

}

// Close is a closes the context.
func (c *ZeroContext) Close() {
	markup.Dismount(c.root)
	UnregisterContext(c)
	log.Infof("%v (%v) is closed", c.placeholder, c.ID())
}
<|endoftext|>


<commit_before>package telebot

import (
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// HandlerFunc represents a handler function, which is
// used to handle actual endpoints.
type HandlerFunc func(Context) error

// Context wraps an update and represents the context of current event.
type Context interface {
	// Bot returns the bot instance.
	Bot() *Bot

	// Update returns the original update.
	Update() Update

	// Message returns stored message if such presented.
	Message() *Message

	// Media returns message media if such presented.
	Media() Media

	// Callback returns stored callback if such presented.
	Callback() *Callback

	// Query returns stored query if such presented.
	Query() *Query

	// InlineResult returns stored inline result if such presented.
	InlineResult() *InlineResult

	// ShippingQuery returns stored shipping query if such presented.
	ShippingQuery() *ShippingQuery

	// PreCheckoutQuery returns stored pre checkout query if such presented.
	PreCheckoutQuery() *PreCheckoutQuery

	// Poll returns stored poll if such presented.
	Poll() *Poll

	// PollAnswer returns stored poll answer if such presented.
	PollAnswer() *PollAnswer

	// ChatMember returns chat member changes.
	ChatMember() *ChatMemberUpdate

	// ChatJoinRequest returns cha
	ChatJoinRequest() *ChatJoinRequest

	// Migration returns both migration from and to chat IDs.
	Migration() (int64, int64)

	// Sender returns the current recipient, depending on the context type.
	// Returns nil if user is not presented.
	Sender() *User

	// Chat returns the current chat, depending on the context type.
	// Returns nil if chat is not presented.
	Chat() *Chat

	// Recipient combines both Sender and Chat functions. If there is no user
	// the chat will be returned. The native context cannot be without sender,
	// but it is useful in the case when the context created intentionally
	// by the NewContext constructor and have only Chat field inside.
	Recipient() Recipient

	// Text returns the message text, depending on the context type.
	// In the case when no related data presented, returns an empty string.
	Text() string

	// Data returns the current data, depending on the context type.
	// If the context contains command, returns its arguments string.
	// If the context contains payment, returns its payload.
	// In the case when no related data presented, returns an empty string.
	Data() string

	// Args returns a raw slice of command or callback arguments as strings.
	// The message arguments split by space, while the callback's ones by a "|" symbol.
	Args() []string

	// Send sends a message to the current recipient.
	// See Send from bot.go.
	Send(what interface{}, opts ...interface{}) error

	// SendAlbum sends an album to the current recipient.
	// See SendAlbum from bot.go.
	SendAlbum(a Album, opts ...interface{}) error

	// Reply replies to the current message.
	// See Reply from bot.go.
	Reply(what interface{}, opts ...interface{}) error

	// Forward forwards the given message to the current recipient.
	// See Forward from bot.go.
	Forward(msg Editable, opts ...interface{}) error

	// ForwardTo forwards the current message to the given recipient.
	// See Forward from bot.go
	ForwardTo(to Recipient, opts ...interface{}) error

	// Edit edits the current message.
	// See Edit from bot.go.
	Edit(what interface{}, opts ...interface{}) error

	// EditCaption edits the caption of the current message.
	// See EditCaption from bot.go.
	EditCaption(caption string, opts ...interface{}) error

	// EditOrSend edits the current message if the update is callback,
	// otherwise the content is sent to the chat as a separate message.
	EditOrSend(what interface{}, opts ...interface{}) error

	// EditOrReply edits the current message if the update is callback,
	// otherwise the content is replied as a separate message.
	EditOrReply(what interface{}, opts ...interface{}) error

	// Delete removes the current message.
	// See Delete from bot.go.
	Delete() error

	// DeleteAfter waits for the duration to elapse and then removes the
	// message. It handles an error automatically using b.OnError callback.
	// It returns a Timer that can be used to cancel the call using its Stop method.
	DeleteAfter(d time.Duration) *time.Timer

	// Notify updates the chat action for the current recipient.
	// See Notify from bot.go.
	Notify(action ChatAction) error

	// Ship replies to the current shipping query.
	// See Ship from bot.go.
	Ship(what ...interface{}) error

	// Accept finalizes the current deal.
	// See Accept from bot.go.
	Accept(errorMessage ...string) error

	// Answer sends a response to the current inline query.
	// See Answer from bot.go.
	Answer(resp *QueryResponse) error

	// Respond sends a response for the current callback query.
	// See Respond from bot.go.
	Respond(resp ...*CallbackResponse) error

	// Get retrieves data from the context.
	Get(key string) interface{}

	// Set saves data in the context.
	Set(key string, val interface{})
}

// nativeContext is a native implementation of the Context interface.
// "context" is taken by context package, maybe there is a better name.
type nativeContext struct {
	b     *Bot
	u     Update
	lock  sync.RWMutex
	store map[string]interface{}
}

func (c *nativeContext) Bot() *Bot {
	return c.b
}

func (c *nativeContext) Update() Update {
	return c.u
}

func (c *nativeContext) Message() *Message {
	switch {
	case c.u.Message != nil:
		return c.u.Message
	case c.u.Callback != nil:
		return c.u.Callback.Message
	case c.u.EditedMessage != nil:
		return c.u.EditedMessage
	case c.u.ChannelPost != nil:
		if c.u.ChannelPost.PinnedMessage != nil {
			return c.u.ChannelPost.PinnedMessage
		}
		return c.u.ChannelPost
	case c.u.EditedChannelPost != nil:
		return c.u.EditedChannelPost
	default:
		return nil
	}
}

func (c *nativeContext) Media() Media {
	m := c.Message()

	switch {
	case m.Photo != nil:
		return m.Photo
	case m.Voice != nil:
		return m.Voice
	case m.Audio != nil:
		return m.Audio
	case m.Animation != nil:
		return m.Animation
	case m.Document != nil:
		return m.Document
	case m.Video != nil:
		return m.Video
	case m.VideoNote != nil:
		return m.VideoNote
	default:
		return nil
	}
}

func (c *nativeContext) Callback() *Callback {
	return c.u.Callback
}

func (c *nativeContext) Query() *Query {
	return c.u.Query
}

func (c *nativeContext) InlineResult() *InlineResult {
	return c.u.InlineResult
}

func (c *nativeContext) ShippingQuery() *ShippingQuery {
	return c.u.ShippingQuery
}

func (c *nativeContext) PreCheckoutQuery() *PreCheckoutQuery {
	return c.u.PreCheckoutQuery
}

func (c *nativeContext) ChatMember() *ChatMemberUpdate {
	switch {
	case c.u.ChatMember != nil:
		return c.u.ChatMember
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember
	default:
		return nil
	}
}

func (c *nativeContext) ChatJoinRequest() *ChatJoinRequest {
	return c.u.ChatJoinRequest
}

func (c *nativeContext) Poll() *Poll {
	return c.u.Poll
}

func (c *nativeContext) PollAnswer() *PollAnswer {
	return c.u.PollAnswer
}

func (c *nativeContext) Migration() (int64, int64) {
	return c.u.Message.MigrateFrom, c.u.Message.MigrateTo
}

func (c *nativeContext) Sender() *User {
	switch {
	case c.u.Message != nil:
		return c.u.Message.Sender
	case c.u.Callback != nil:
		return c.u.Callback.Sender
	case c.u.Query != nil:
		return c.u.Query.Sender
	case c.u.InlineResult != nil:
		return c.u.InlineResult.Sender
	case c.u.ShippingQuery != nil:
		return c.u.ShippingQuery.Sender
	case c.u.PreCheckoutQuery != nil:
		return c.u.PreCheckoutQuery.Sender
	case c.u.PollAnswer != nil:
		return c.u.PollAnswer.Sender
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember.Sender
	case c.u.ChatMember != nil:
		return c.u.ChatMember.Sender
	case c.u.ChatJoinRequest != nil:
		return c.u.ChatJoinRequest.Sender
	default:
		return nil
	}
}

func (c *nativeContext) Chat() *Chat {
	switch {
	case c.u.Message != nil:
		return c.u.Message.Chat
	case c.u.Callback != nil && c.u.Callback.Message != nil:
		return c.u.Callback.Message.Chat
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember.Chat
	case c.u.ChatMember != nil:
		return c.u.ChatMember.Chat
	case c.u.ChatJoinRequest != nil:
		return c.u.ChatJoinRequest.Chat
	default:
		return nil
	}
}

func (c *nativeContext) Recipient() Recipient {
	chat := c.Chat()
	if chat != nil {
		return chat
	}
	return c.Sender()
}

func (c *nativeContext) Text() string {
	var m *Message

	switch {
	case c.u.Message != nil:
		m = c.u.Message
	case c.u.Callback != nil && c.u.Callback.Message != nil:
		m = c.u.Callback.Message
	default:
		return ""
	}

	if m.Caption != "" {
		return m.Caption
	}

	return m.Text
}

func (c *nativeContext) Data() string {
	switch {
	case c.u.Message != nil:
		return c.u.Message.Payload
	case c.u.Callback != nil:
		return c.u.Callback.Data
	case c.u.Query != nil:
		return c.u.Query.Text
	case c.u.InlineResult != nil:
		return c.u.InlineResult.Query
	case c.u.ShippingQuery != nil:
		return c.u.ShippingQuery.Payload
	case c.u.PreCheckoutQuery != nil:
		return c.u.PreCheckoutQuery.Payload
	default:
		return ""
	}
}

func (c *nativeContext) Args() []string {
	switch {
	case c.u.Message != nil:
		payload := strings.Trim(c.u.Message.Payload, " ")
		if payload != "" {
			return strings.Split(payload, " ")
		}
	case c.u.Callback != nil:
		return strings.Split(c.u.Callback.Data, "|")
	case c.u.Query != nil:
		return strings.Split(c.u.Query.Text, " ")
	case c.u.InlineResult != nil:
		return strings.Split(c.u.InlineResult.Query, " ")
	}
	return nil
}

func (c *nativeContext) Send(what interface{}, opts ...interface{}) error {
	_, err := c.b.Send(c.Recipient(), what, opts...)
	return err
}

func (c *nativeContext) SendAlbum(a Album, opts ...interface{}) error {
	_, err := c.b.SendAlbum(c.Recipient(), a, opts...)
	return err
}

func (c *nativeContext) Reply(what interface{}, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Reply(msg, what, opts...)
	return err
}

func (c *nativeContext) Forward(msg Editable, opts ...interface{}) error {
	_, err := c.b.Forward(c.Recipient(), msg, opts...)
	return err
}

func (c *nativeContext) ForwardTo(to Recipient, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Forward(to, msg, opts...)
	return err
}

func (c *nativeContext) Edit(what interface{}, opts ...interface{}) error {
	if c.u.InlineResult != nil {
		_, err := c.b.Edit(c.u.InlineResult, what, opts...)
		return err
	}
	if c.u.Callback != nil {
		_, err := c.b.Edit(c.u.Callback, what, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditCaption(caption string, opts ...interface{}) error {
	if c.u.InlineResult != nil {
		_, err := c.b.EditCaption(c.u.InlineResult, caption, opts...)
		return err
	}
	if c.u.Callback != nil {
		_, err := c.b.EditCaption(c.u.Callback, caption, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditOrSend(what interface{}, opts ...interface{}) error {
	err := c.Edit(what, opts...)
	if err == ErrBadContext {
		return c.Send(what, opts...)
	}
	return err
}

func (c *nativeContext) EditOrReply(what interface{}, opts ...interface{}) error {
	err := c.Edit(what, opts...)
	if err == ErrBadContext {
		return c.Reply(what, opts...)
	}
	return err
}

func (c *nativeContext) Delete() error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	return c.b.Delete(msg)
}

func (c *nativeContext) DeleteAfter(d time.Duration) *time.Timer {
	return time.AfterFunc(d, func() {
		if err := c.Delete(); err != nil {
			c.b.OnError(err, c)
		}
	})
}

func (c *nativeContext) Notify(action ChatAction) error {
	return c.b.Notify(c.Recipient(), action)
}

func (c *nativeContext) Ship(what ...interface{}) error {
	if c.u.ShippingQuery == nil {
		return errors.New("telebot: context shipping query is nil")
	}
	return c.b.Ship(c.u.ShippingQuery, what...)
}

func (c *nativeContext) Accept(errorMessage ...string) error {
	if c.u.PreCheckoutQuery == nil {
		return errors.New("telebot: context pre checkout query is nil")
	}
	return c.b.Accept(c.u.PreCheckoutQuery, errorMessage...)
}

func (c *nativeContext) Answer(resp *QueryResponse) error {
	if c.u.Query == nil {
		return errors.New("telebot: context inline query is nil")
	}
	return c.b.Answer(c.u.Query, resp)
}

func (c *nativeContext) Respond(resp ...*CallbackResponse) error {
	if c.u.Callback == nil {
		return errors.New("telebot: context callback is nil")
	}
	return c.b.Respond(c.u.Callback, resp...)
}

func (c *nativeContext) Set(key string, value interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = value
}

func (c *nativeContext) Get(key string) interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.store[key]
}
<commit_msg>context: check for Message properly<commit_after>package telebot

import (
	"strings"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// HandlerFunc represents a handler function, which is
// used to handle actual endpoints.
type HandlerFunc func(Context) error

// Context wraps an update and represents the context of current event.
type Context interface {
	// Bot returns the bot instance.
	Bot() *Bot

	// Update returns the original update.
	Update() Update

	// Message returns stored message if such presented.
	Message() *Message

	// Media returns message media if such presented.
	Media() Media

	// Callback returns stored callback if such presented.
	Callback() *Callback

	// Query returns stored query if such presented.
	Query() *Query

	// InlineResult returns stored inline result if such presented.
	InlineResult() *InlineResult

	// ShippingQuery returns stored shipping query if such presented.
	ShippingQuery() *ShippingQuery

	// PreCheckoutQuery returns stored pre checkout query if such presented.
	PreCheckoutQuery() *PreCheckoutQuery

	// Poll returns stored poll if such presented.
	Poll() *Poll

	// PollAnswer returns stored poll answer if such presented.
	PollAnswer() *PollAnswer

	// ChatMember returns chat member changes.
	ChatMember() *ChatMemberUpdate

	// ChatJoinRequest returns cha
	ChatJoinRequest() *ChatJoinRequest

	// Migration returns both migration from and to chat IDs.
	Migration() (int64, int64)

	// Sender returns the current recipient, depending on the context type.
	// Returns nil if user is not presented.
	Sender() *User

	// Chat returns the current chat, depending on the context type.
	// Returns nil if chat is not presented.
	Chat() *Chat

	// Recipient combines both Sender and Chat functions. If there is no user
	// the chat will be returned. The native context cannot be without sender,
	// but it is useful in the case when the context created intentionally
	// by the NewContext constructor and have only Chat field inside.
	Recipient() Recipient

	// Text returns the message text, depending on the context type.
	// In the case when no related data presented, returns an empty string.
	Text() string

	// Data returns the current data, depending on the context type.
	// If the context contains command, returns its arguments string.
	// If the context contains payment, returns its payload.
	// In the case when no related data presented, returns an empty string.
	Data() string

	// Args returns a raw slice of command or callback arguments as strings.
	// The message arguments split by space, while the callback's ones by a "|" symbol.
	Args() []string

	// Send sends a message to the current recipient.
	// See Send from bot.go.
	Send(what interface{}, opts ...interface{}) error

	// SendAlbum sends an album to the current recipient.
	// See SendAlbum from bot.go.
	SendAlbum(a Album, opts ...interface{}) error

	// Reply replies to the current message.
	// See Reply from bot.go.
	Reply(what interface{}, opts ...interface{}) error

	// Forward forwards the given message to the current recipient.
	// See Forward from bot.go.
	Forward(msg Editable, opts ...interface{}) error

	// ForwardTo forwards the current message to the given recipient.
	// See Forward from bot.go
	ForwardTo(to Recipient, opts ...interface{}) error

	// Edit edits the current message.
	// See Edit from bot.go.
	Edit(what interface{}, opts ...interface{}) error

	// EditCaption edits the caption of the current message.
	// See EditCaption from bot.go.
	EditCaption(caption string, opts ...interface{}) error

	// EditOrSend edits the current message if the update is callback,
	// otherwise the content is sent to the chat as a separate message.
	EditOrSend(what interface{}, opts ...interface{}) error

	// EditOrReply edits the current message if the update is callback,
	// otherwise the content is replied as a separate message.
	EditOrReply(what interface{}, opts ...interface{}) error

	// Delete removes the current message.
	// See Delete from bot.go.
	Delete() error

	// DeleteAfter waits for the duration to elapse and then removes the
	// message. It handles an error automatically using b.OnError callback.
	// It returns a Timer that can be used to cancel the call using its Stop method.
	DeleteAfter(d time.Duration) *time.Timer

	// Notify updates the chat action for the current recipient.
	// See Notify from bot.go.
	Notify(action ChatAction) error

	// Ship replies to the current shipping query.
	// See Ship from bot.go.
	Ship(what ...interface{}) error

	// Accept finalizes the current deal.
	// See Accept from bot.go.
	Accept(errorMessage ...string) error

	// Answer sends a response to the current inline query.
	// See Answer from bot.go.
	Answer(resp *QueryResponse) error

	// Respond sends a response for the current callback query.
	// See Respond from bot.go.
	Respond(resp ...*CallbackResponse) error

	// Get retrieves data from the context.
	Get(key string) interface{}

	// Set saves data in the context.
	Set(key string, val interface{})
}

// nativeContext is a native implementation of the Context interface.
// "context" is taken by context package, maybe there is a better name.
type nativeContext struct {
	b     *Bot
	u     Update
	lock  sync.RWMutex
	store map[string]interface{}
}

func (c *nativeContext) Bot() *Bot {
	return c.b
}

func (c *nativeContext) Update() Update {
	return c.u
}

func (c *nativeContext) Message() *Message {
	switch {
	case c.u.Message != nil:
		return c.u.Message
	case c.u.Callback != nil:
		return c.u.Callback.Message
	case c.u.EditedMessage != nil:
		return c.u.EditedMessage
	case c.u.ChannelPost != nil:
		if c.u.ChannelPost.PinnedMessage != nil {
			return c.u.ChannelPost.PinnedMessage
		}
		return c.u.ChannelPost
	case c.u.EditedChannelPost != nil:
		return c.u.EditedChannelPost
	default:
		return nil
	}
}

func (c *nativeContext) Media() Media {
	m := c.Message()

	switch {
	case m.Photo != nil:
		return m.Photo
	case m.Voice != nil:
		return m.Voice
	case m.Audio != nil:
		return m.Audio
	case m.Animation != nil:
		return m.Animation
	case m.Document != nil:
		return m.Document
	case m.Video != nil:
		return m.Video
	case m.VideoNote != nil:
		return m.VideoNote
	default:
		return nil
	}
}

func (c *nativeContext) Callback() *Callback {
	return c.u.Callback
}

func (c *nativeContext) Query() *Query {
	return c.u.Query
}

func (c *nativeContext) InlineResult() *InlineResult {
	return c.u.InlineResult
}

func (c *nativeContext) ShippingQuery() *ShippingQuery {
	return c.u.ShippingQuery
}

func (c *nativeContext) PreCheckoutQuery() *PreCheckoutQuery {
	return c.u.PreCheckoutQuery
}

func (c *nativeContext) ChatMember() *ChatMemberUpdate {
	switch {
	case c.u.ChatMember != nil:
		return c.u.ChatMember
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember
	default:
		return nil
	}
}

func (c *nativeContext) ChatJoinRequest() *ChatJoinRequest {
	return c.u.ChatJoinRequest
}

func (c *nativeContext) Poll() *Poll {
	return c.u.Poll
}

func (c *nativeContext) PollAnswer() *PollAnswer {
	return c.u.PollAnswer
}

func (c *nativeContext) Migration() (int64, int64) {
	return c.u.Message.MigrateFrom, c.u.Message.MigrateTo
}

func (c *nativeContext) Sender() *User {
	switch {
	case c.u.Callback != nil:
		return c.u.Callback.Sender
	case c.Message() != nil:
		return c.Message().Sender
	case c.u.Query != nil:
		return c.u.Query.Sender
	case c.u.InlineResult != nil:
		return c.u.InlineResult.Sender
	case c.u.ShippingQuery != nil:
		return c.u.ShippingQuery.Sender
	case c.u.PreCheckoutQuery != nil:
		return c.u.PreCheckoutQuery.Sender
	case c.u.PollAnswer != nil:
		return c.u.PollAnswer.Sender
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember.Sender
	case c.u.ChatMember != nil:
		return c.u.ChatMember.Sender
	case c.u.ChatJoinRequest != nil:
		return c.u.ChatJoinRequest.Sender
	default:
		return nil
	}
}

func (c *nativeContext) Chat() *Chat {
	switch {
	case c.Message() != nil:
		return c.Message().Chat
	case c.u.MyChatMember != nil:
		return c.u.MyChatMember.Chat
	case c.u.ChatMember != nil:
		return c.u.ChatMember.Chat
	case c.u.ChatJoinRequest != nil:
		return c.u.ChatJoinRequest.Chat
	default:
		return nil
	}
}

func (c *nativeContext) Recipient() Recipient {
	chat := c.Chat()
	if chat != nil {
		return chat
	}
	return c.Sender()
}

func (c *nativeContext) Text() string {
	m := c.Message()
	if m == nil {
		return ""
	}
	if m.Caption != "" {
		return m.Caption
	}
	return m.Text
}

func (c *nativeContext) Data() string {
	switch {
	case c.u.Message != nil:
		return c.u.Message.Payload
	case c.u.Callback != nil:
		return c.u.Callback.Data
	case c.u.Query != nil:
		return c.u.Query.Text
	case c.u.InlineResult != nil:
		return c.u.InlineResult.Query
	case c.u.ShippingQuery != nil:
		return c.u.ShippingQuery.Payload
	case c.u.PreCheckoutQuery != nil:
		return c.u.PreCheckoutQuery.Payload
	default:
		return ""
	}
}

func (c *nativeContext) Args() []string {
	switch {
	case c.u.Message != nil:
		payload := strings.Trim(c.u.Message.Payload, " ")
		if payload != "" {
			return strings.Split(payload, " ")
		}
	case c.u.Callback != nil:
		return strings.Split(c.u.Callback.Data, "|")
	case c.u.Query != nil:
		return strings.Split(c.u.Query.Text, " ")
	case c.u.InlineResult != nil:
		return strings.Split(c.u.InlineResult.Query, " ")
	}
	return nil
}

func (c *nativeContext) Send(what interface{}, opts ...interface{}) error {
	_, err := c.b.Send(c.Recipient(), what, opts...)
	return err
}

func (c *nativeContext) SendAlbum(a Album, opts ...interface{}) error {
	_, err := c.b.SendAlbum(c.Recipient(), a, opts...)
	return err
}

func (c *nativeContext) Reply(what interface{}, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Reply(msg, what, opts...)
	return err
}

func (c *nativeContext) Forward(msg Editable, opts ...interface{}) error {
	_, err := c.b.Forward(c.Recipient(), msg, opts...)
	return err
}

func (c *nativeContext) ForwardTo(to Recipient, opts ...interface{}) error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	_, err := c.b.Forward(to, msg, opts...)
	return err
}

func (c *nativeContext) Edit(what interface{}, opts ...interface{}) error {
	if c.u.InlineResult != nil {
		_, err := c.b.Edit(c.u.InlineResult, what, opts...)
		return err
	}
	if c.u.Callback != nil {
		_, err := c.b.Edit(c.u.Callback, what, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditCaption(caption string, opts ...interface{}) error {
	if c.u.InlineResult != nil {
		_, err := c.b.EditCaption(c.u.InlineResult, caption, opts...)
		return err
	}
	if c.u.Callback != nil {
		_, err := c.b.EditCaption(c.u.Callback, caption, opts...)
		return err
	}
	return ErrBadContext
}

func (c *nativeContext) EditOrSend(what interface{}, opts ...interface{}) error {
	err := c.Edit(what, opts...)
	if err == ErrBadContext {
		return c.Send(what, opts...)
	}
	return err
}

func (c *nativeContext) EditOrReply(what interface{}, opts ...interface{}) error {
	err := c.Edit(what, opts...)
	if err == ErrBadContext {
		return c.Reply(what, opts...)
	}
	return err
}

func (c *nativeContext) Delete() error {
	msg := c.Message()
	if msg == nil {
		return ErrBadContext
	}
	return c.b.Delete(msg)
}

func (c *nativeContext) DeleteAfter(d time.Duration) *time.Timer {
	return time.AfterFunc(d, func() {
		if err := c.Delete(); err != nil {
			c.b.OnError(err, c)
		}
	})
}

func (c *nativeContext) Notify(action ChatAction) error {
	return c.b.Notify(c.Recipient(), action)
}

func (c *nativeContext) Ship(what ...interface{}) error {
	if c.u.ShippingQuery == nil {
		return errors.New("telebot: context shipping query is nil")
	}
	return c.b.Ship(c.u.ShippingQuery, what...)
}

func (c *nativeContext) Accept(errorMessage ...string) error {
	if c.u.PreCheckoutQuery == nil {
		return errors.New("telebot: context pre checkout query is nil")
	}
	return c.b.Accept(c.u.PreCheckoutQuery, errorMessage...)
}

func (c *nativeContext) Answer(resp *QueryResponse) error {
	if c.u.Query == nil {
		return errors.New("telebot: context inline query is nil")
	}
	return c.b.Answer(c.u.Query, resp)
}

func (c *nativeContext) Respond(resp ...*CallbackResponse) error {
	if c.u.Callback == nil {
		return errors.New("telebot: context callback is nil")
	}
	return c.b.Respond(c.u.Callback, resp...)
}

func (c *nativeContext) Set(key string, value interface{}) {
	c.lock.Lock()
	defer c.lock.Unlock()

	if c.store == nil {
		c.store = make(map[string]interface{})
	}
	c.store[key] = value
}

func (c *nativeContext) Get(key string) interface{} {
	c.lock.RLock()
	defer c.lock.RUnlock()
	return c.store[key]
}
<|endoftext|>


<commit_before>//
// 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 (
	"fmt"
)

func runOCI2ACI(path string, flagDebug bool) error {
	if flagDebug {
		InitDebug()
	}
	if bValidate := validateOCIProc(path); bValidate != true {
                fmt.Println("Conversion stop.")
                return nil
        }

	buildACI(path)
	return nil
}
<commit_msg>Update<commit_after>//
// 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 (
	"fmt"
	"os"
	"io/ioutil"
	"path/filepath"
)

func runOCI2ACI(path string, flagDebug bool) error {
	if flagDebug {
		InitDebug()
	}
	if bValidate := validateOCIProc(path); bValidate != true {
                fmt.Println("Conversion stop.")
                return nil
        }

	dirWork := createWorkDir()

	convertProc(path, dirWork)

	buildACI(dirWork)

	return nil
}

func createWorkDir() string {
	idir, err := ioutil.TempDir("", "oci2aci")
        if err != nil {
                return ""
        }
        rootfs := filepath.Join(idir, "rootfs")
        os.MkdirAll(rootfs, 0755)

	data := []byte{}
	if err := ioutil.WriteFile(filepath.Join(idir, "manifest"), data, 0644); err != nil {
		return ""
	}
	return idir
}

func convertProc(srcPath, dstPath string) error {
	return nil
}
<|endoftext|>


<commit_before>package core

import (
	"strconv"
	"strings"
)

// SliceToString turns a slice of strings into a single string with space denotation.
func SliceToString(slices []string) string {
	var sl = ""
	for indx, val := range slices {
		if indx != len(slices)-1 {
			sl += val + " "
		} else {
			sl += val
		}
	}
	return sl
}

func SliceToHumanListing(slices []string) string {
	var sl = ""
	for indx, val := range slices {
		if indx == len(slices)-2 {
			sl += val + " and "
		} else if indx < len(slices)-2 {
			sl += val + ", "
		} else {
			sl += val
		}
	}
	return sl
}

func SanitizeArgs(slice []string) []string {
	var fslice []string = make([]string, 0)
	var current string
	var inquote bool
	for i := 0; i < len(slice); i++ {
		if strings.HasPrefix(slice[i], "\"") {
			current += slice[i][1:]
			inquote = true
			continue
		}

		if inquote {
			if strings.HasSuffix(slice[i], "\"") {
				current += slice[i][:len(slice[i])-1]
				fslice = append(fslice, slice[i])
				current = ""
				inquote = false
				continue
			}

			current += slice[i]
			continue
		}

		fslice = append(fslice, slice[i])
	}

	if current != "" {
		return slice
	}
	return fslice
}

// StringToSlice turns a string into slices based of space character denotation..
func StringToSlice(text string) (slices []string) {
	return strings.Split(text, " ")
}

func StringToInt64(inp string) (int64, error) {
	return strconv.ParseInt(inp, 10, 64)
}
<commit_msg>made some tweaks to the sanitizer<commit_after>package core

import (
	"strconv"
	"strings"
)

// SliceToString turns a slice of strings into a single string with space denotation.
func SliceToString(slices []string) string {
	var sl = ""
	for indx, val := range slices {
		if indx != len(slices)-1 {
			sl += val + " "
		} else {
			sl += val
		}
	}
	return sl
}

func SliceToHumanListing(slices []string) string {
	var sl = ""
	for indx, val := range slices {
		if indx == len(slices)-2 {
			sl += val + " and "
		} else if indx < len(slices)-2 {
			sl += val + ", "
		} else {
			sl += val
		}
	}
	return sl
}

func SanitizeArgs(slice []string) []string {
	var fslice []string = make([]string, 0)
	var current string
	var inquote bool
	for i := 0; i < len(slice); i++ {
		if strings.HasPrefix(slice[i], "\"") {
			current += slice[i][1:] + " "
			inquote = true
			continue
		}

		if inquote {
			if strings.HasSuffix(slice[i], "\"") {
				current += slice[i][:len(slice[i])-1]
				fslice = append(fslice, current)
				current = ""
				inquote = false
				continue
			}

			current += slice[i] + " "
			continue
		}

		fslice = append(fslice, slice[i])
	}

	if current == "" {
		return slice
	}
	return fslice
}

// StringToSlice turns a string into slices based of space character denotation..
func StringToSlice(text string) (slices []string) {
	return strings.Split(text, " ")
}

func StringToInt64(inp string) (int64, error) {
	return strconv.ParseInt(inp, 10, 64)
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative 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 main

import (
	"flag"
	"log"

	"github.com/kelseyhightower/envconfig"
	"github.com/knative/eventing-sources/contrib/gcppubsub/pkg/adapter"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"golang.org/x/net/context"
)

type envConfig struct {
	// Environment variable containing project id.
	Project string `envconfig:"GCPPUBSUB_PROJECT" required:"true"`

	// Environment variable containing the sink URI.
	Sink string `envconfig:"SINK_URI" required:"true"`

	// Environment variable containing the transformer URI.
	Transformer string `envconfig:"TRANSFORMER_URI" default:""`

	// Environment variable containing the GCP PubSub Topic being
	// subscribed to's name. In the form that is unique within the project. E.g. 'laconia', not
	// 'projects/my-gcp-project/topics/laconia'.
	Topic string `envconfig:"GCPPUBSUB_TOPIC" default:""`

	// Environment variable containing the name of the subscription to use.
	Subscription string `envconfig:"GCPPUBSUB_SUBSCRIPTION_ID" required:"true"`
}

func main() {
	flag.Parse()

	ctx := context.Background()
	logCfg := zap.NewProductionConfig()
	logCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	logger, err := logCfg.Build()
	if err != nil {
		log.Fatalf("Unable to create logger: %v", err)
	}

	var env envConfig
	if err := envconfig.Process("", &env); err != nil {
		logger.Fatal("Failed to process env var", zap.Error(err))
	}

	adapter := &gcppubsub.Adapter{
		ProjectID:      env.Project,
		TopicID:        env.Topic,
		SinkURI:        env.Sink,
		SubscriptionID: env.Subscription,
		TransformerURI: env.Transformer,
	}

	logger.Info("Starting GCP Pub/Sub Receive Adapter. %v", zap.Reflect("adapter", adapter))
	if err := adapter.Start(ctx); err != nil {
		logger.Fatal("failed to start adapter: ", zap.Error(err))
	}
}
<commit_msg>golang format tools (#440)<commit_after>/*
Copyright 2018 The Knative 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 main

import (
	"flag"
	"log"

	"github.com/kelseyhightower/envconfig"
	gcppubsub "github.com/knative/eventing-sources/contrib/gcppubsub/pkg/adapter"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"

	"golang.org/x/net/context"
)

type envConfig struct {
	// Environment variable containing project id.
	Project string `envconfig:"GCPPUBSUB_PROJECT" required:"true"`

	// Environment variable containing the sink URI.
	Sink string `envconfig:"SINK_URI" required:"true"`

	// Environment variable containing the transformer URI.
	Transformer string `envconfig:"TRANSFORMER_URI" default:""`

	// Environment variable containing the GCP PubSub Topic being
	// subscribed to's name. In the form that is unique within the project. E.g. 'laconia', not
	// 'projects/my-gcp-project/topics/laconia'.
	Topic string `envconfig:"GCPPUBSUB_TOPIC" default:""`

	// Environment variable containing the name of the subscription to use.
	Subscription string `envconfig:"GCPPUBSUB_SUBSCRIPTION_ID" required:"true"`
}

func main() {
	flag.Parse()

	ctx := context.Background()
	logCfg := zap.NewProductionConfig()
	logCfg.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
	logger, err := logCfg.Build()
	if err != nil {
		log.Fatalf("Unable to create logger: %v", err)
	}

	var env envConfig
	if err := envconfig.Process("", &env); err != nil {
		logger.Fatal("Failed to process env var", zap.Error(err))
	}

	adapter := &gcppubsub.Adapter{
		ProjectID:      env.Project,
		TopicID:        env.Topic,
		SinkURI:        env.Sink,
		SubscriptionID: env.Subscription,
		TransformerURI: env.Transformer,
	}

	logger.Info("Starting GCP Pub/Sub Receive Adapter. %v", zap.Reflect("adapter", adapter))
	if err := adapter.Start(ctx); err != nil {
		logger.Fatal("failed to start adapter: ", zap.Error(err))
	}
}
<|endoftext|>


<commit_before>package proposervm

import (
	"bytes"
	"crypto/tls"
	"errors"
	"testing"
	"time"

	"github.com/ava-labs/avalanchego/database/manager"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
	"github.com/ava-labs/avalanchego/staking"
)

var pTestCert *tls.Certificate = nil // package variable to init it only once

type TestConnectorProVM struct {
	block.TestVM
}

func (vm *TestConnectorProVM) Connected(validatorID ids.ShortID) (bool, error) {
	return false, nil
}

func (vm *TestConnectorProVM) Disconnected(validatorID ids.ShortID) (bool, error) {
	return true, nil
}

func TestProposerVMConnectorHandling(t *testing.T) {
	// setup
	noConnectorVM := &block.TestVM{}
	proVM := VM{
		ChainVM: noConnectorVM,
	}

	// test
	_, err := proVM.Connected(ids.ShortID{})
	if err != ErrInnerVMNotConnector {
		t.Fatal("Proposer VM should signal that it wraps a ChainVM not implementing Connector interface with ErrInnerVMNotConnector error")
	}

	_, err = proVM.Disconnected(ids.ShortID{})
	if err != ErrInnerVMNotConnector {
		t.Fatal("Proposer VM should signal that it wraps a ChainVM not implementing Connector interface with ErrInnerVMNotConnector error")
	}

	// setup
	proConnVM := TestConnectorProVM{
		TestVM: block.TestVM{},
	}

	// test
	_, err = proConnVM.Connected(ids.ShortID{})
	if err != nil {
		t.Fatal("Proposer VM should forward wrapped Connection state if this implements Connector interface")
	}

	_, err = proConnVM.Disconnected(ids.ShortID{})
	if err != nil {
		t.Fatal("Proposer VM should forward wrapped Disconnection state if this implements Connector interface")
	}
}

func TestInitializeRecordsGenesis(t *testing.T) {
	coreVM, proVM, genesisBlk := initTestProposerVM(t)

	// checks
	blkID, err := proVM.LastAccepted()
	if err != nil {
		t.Fatal("failed to retrieve last accepted block")
	}

	coreVM.CantGetBlock = false
	rtvdBlk, err := proVM.GetBlock(blkID)
	if err != nil {
		t.Fatal("Block should be returned without calling core vm")
	}

	proRtvdBlk, ok := rtvdBlk.(*ProposerBlock)
	if !ok {
		t.Fatal("retrieved block is not a proposer block")
	}

	if !bytes.Equal(proRtvdBlk.Block.Bytes(), genesisBlk.Bytes()) {
		t.Fatal("Stored block is not genesis")
	}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
func initTestProposerVM(t *testing.T) (*block.TestVM, VM, *snowman.TestBlock) {
	// setup
	coreGenesisBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.Empty.Prefix(2021),
			StatusV: choices.Unknown,
		},
		BytesV:  []byte{1},
		HeightV: 0,
	}

	coreVM := &block.TestVM{}
	coreVM.CantInitialize = true
	coreVM.InitializeF = func(*snow.Context, manager.Manager, []byte, []byte, []byte, chan<- common.Message, []*common.Fx) error {
		return nil
	}
	coreVM.LastAcceptedF = func() (ids.ID, error) { return coreGenesisBlk.ID(), nil }
	coreVM.GetBlockF = func(ids.ID) (snowman.Block, error) { return coreGenesisBlk, nil }

	tc := &testClock{
		setTime: time.Now(),
	}
	proVM := NewProVM(coreVM)
	proVM.clk = tc

	if pTestCert == nil {
		var err error
		pTestCert, _ = staking.NewTLSCert()
		if err != nil {
			t.Fatal("Could not generate dummy StakerCert")
		}
	}

	dummyCtx := &snow.Context{
		StakingKey: &pTestCert.PrivateKey,
	}
	dummyDBManager := manager.NewDefaultMemDBManager()
	if err := proVM.Initialize(dummyCtx, dummyDBManager, coreGenesisBlk.Bytes(), nil, nil, nil, nil); err != nil {
		t.Fatal("failed to initialize proposerVM")
	}
	return coreVM, proVM, coreGenesisBlk
}

func TestBuildBlockRecordsAndVerifiesBuiltBlock(t *testing.T) {
	// setup
	coreVM, proVM, coreGenBlk := initTestProposerVM(t)
	coreVM.CantBuildBlock = true
	coreBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.Empty.Prefix(2021),
			StatusV: choices.Processing,
		},
		ParentV: coreGenBlk,
		HeightV: coreGenBlk.HeightV + 1,
		VerifyV: nil,
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) { return coreBlk, nil }

	// test
	builtBlk, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("proposerVM could not build block")
	}
	if err := builtBlk.Verify(); err != nil {
		t.Fatal("built block should be verified")
	}

	// test
	coreVM.CantGetBlock = false // forbid calls to coreVM to show caching
	storedBlk, err := proVM.GetBlock(builtBlk.ID())
	if err != nil {
		t.Fatal("proposerVM has not cached built block")
	}
	if storedBlk != builtBlk {
		t.Fatal("proposerVM retrieved wrong block")
	}
}

func TestFirstProposerBlockIsBuiltOnTopOfGenesis(t *testing.T) {
	// setup
	coreVM, proVM, genesisBlk := initTestProposerVM(t)

	newBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.GenerateTestID(),
			StatusV: choices.Processing,
		},
		ParentV: genesisBlk,
		HeightV: 1,
		BytesV:  []byte{1},
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) { return newBlk, nil }

	// test
	snowBlock, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	// checks
	proBlock, ok := snowBlock.(*ProposerBlock)
	if !ok {
		t.Fatal("proposerVM.BuildBlock() does not return a proposervm.Block")
	}

	if proBlock.Block != newBlk {
		t.Fatal("different block was expected to be built")
	}

	if proBlock.Parent().ID() == genesisBlk.ID() {
		t.Fatal("first block not built on genesis")
	}
}

func TestParseBlockRecordsButDoesNotVerifyParsedBlock(t *testing.T) {
	coreVM, proVM, _ := initTestProposerVM(t)

	coreBlkDoesNotVerify := errors.New("coreBlk should not verify in this test")
	coreBlk := &snowman.TestBlock{
		BytesV:  []byte{1},
		VerifyV: coreBlkDoesNotVerify,
	}
	coreVM.CantParseBlock = true
	coreVM.ParseBlockF = func(b []byte) (snowman.Block, error) {
		if !bytes.Equal(b, coreBlk.Bytes()) {
			t.Fatalf("Wrong bytes")
		}
		return coreBlk, nil
	}

	proGenBlkID, _ := proVM.LastAccepted()
	proHdr := ProposerBlockHeader{
		PrntID:    proGenBlkID,
		Timestamp: time.Now().AddDate(0, 0, -1).Unix(),
		Height:    coreBlk.Height(),
	}
	proBlk, _ := NewProBlock(&proVM, proHdr, coreBlk, nil, false) // not signing block, cannot err

	// test
	parsedBlk, err := proVM.ParseBlock(proBlk.Bytes())
	if err != nil {
		t.Fatal("proposerVM could not parse block")
	}
	if err := parsedBlk.Verify(); err == nil {
		t.Fatal("parsed block should not necessarily verify upon parse")
	}

	// test
	coreVM.CantGetBlock = false // forbid calls to coreVM to show caching
	storedBlk, err := proVM.GetBlock(parsedBlk.ID())
	if err != nil {
		t.Fatal("proposerVM has not cached parsed block")
	}
	if storedBlk != parsedBlk {
		t.Fatal("proposerVM retrieved wrong block")
	}
}

func TestProposerVMCacheCanBeRebuiltFromDB(t *testing.T) {
	coreVM, proVM, genesisBlk := initTestProposerVM(t)

	// build two blocks on top of genesis
	coreBlk1 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: genesisBlk,
		HeightV: 1,
		BytesV:  []byte{1},
		VerifyV: nil,
	}
	coreBlk2 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: coreBlk1,
		HeightV: 2,
		BytesV:  []byte{2},
		VerifyV: nil,
	}
	coreBuildCalls := 0
	coreVM.BuildBlockF = func() (snowman.Block, error) {
		switch coreBuildCalls {
		case 0:
			coreBuildCalls++
			return coreBlk1, nil
		case 1:
			coreBuildCalls++
			return coreBlk2, nil
		default:
			t.Fatal("BuildBlock of coreVM called too many times")
		}
		return nil, nil
	}
	proBlk1, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}
	proBlk2, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	// while inner cache, as it would happen upon node shutdown
	proVM.state.wipeCache()

	// build a new block to show ops can resume smoothly
	coreBlk3 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: coreBlk2,
		HeightV: 3,
		BytesV:  []byte{3},
		VerifyV: nil,
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) {
		return coreBlk3, nil
	}
	coreVM.ParseBlockF = func(b []byte) (snowman.Block, error) {
		switch {
		case bytes.Equal(b, coreBlk1.BytesV):
			return coreBlk1, nil
		case bytes.Equal(b, coreBlk2.BytesV):
			return coreBlk2, nil
		default:
			t.Fatal("ParseBlock of coreVM called with unknown block")
		}
		return nil, nil
	}

	proBlk3, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	if proBlk3.Parent().ID() != proBlk2.ID() {
		t.Fatal("Error in building Block")
	}

	// check that getBlock still works on older blocks
	rtrvdProBlk2, err := proVM.GetBlock(proBlk2.ID())
	if err != nil {
		t.Fatal("Could not get block after whiping off proposerVM cache")
	}
	if rtrvdProBlk2.ID() != proBlk2.ID() {
		t.Fatal("blocks do not match following cache whiping")
	}
	if err = rtrvdProBlk2.Verify(); err != nil {
		t.Fatal("block retrieved after cache whiping does not verify")
	}

	rtrvdProBlk1, err := proVM.GetBlock(proBlk1.ID())
	if err != nil {
		t.Fatal("Could not get block after whiping off proposerVM cache")
	}
	if rtrvdProBlk1.ID() != proBlk1.ID() {
		t.Fatal("blocks do not match following cache whiping")
	}
	if err = rtrvdProBlk1.Verify(); err != nil {
		t.Fatal("block retrieved after cache whiping does not verify")
	}
}
<commit_msg>Minor proposerVM UT fix<commit_after>package proposervm

import (
	"bytes"
	"crypto/tls"
	"errors"
	"testing"
	"time"

	"github.com/ava-labs/avalanchego/database/manager"
	"github.com/ava-labs/avalanchego/ids"
	"github.com/ava-labs/avalanchego/snow"
	"github.com/ava-labs/avalanchego/snow/choices"
	"github.com/ava-labs/avalanchego/snow/consensus/snowman"
	"github.com/ava-labs/avalanchego/snow/engine/common"
	"github.com/ava-labs/avalanchego/snow/engine/snowman/block"
	"github.com/ava-labs/avalanchego/staking"
)

var pTestCert *tls.Certificate = nil // package variable to init it only once

type TestConnectorProVM struct {
	block.TestVM
}

func (vm *TestConnectorProVM) Connected(validatorID ids.ShortID) (bool, error) {
	return false, nil
}

func (vm *TestConnectorProVM) Disconnected(validatorID ids.ShortID) (bool, error) {
	return true, nil
}

func TestProposerVMConnectorHandling(t *testing.T) {
	// setup
	noConnectorVM := &block.TestVM{}
	proVM := VM{
		ChainVM: noConnectorVM,
	}

	// test
	_, err := proVM.Connected(ids.ShortID{})
	if err != ErrInnerVMNotConnector {
		t.Fatal("Proposer VM should signal that it wraps a ChainVM not implementing Connector interface with ErrInnerVMNotConnector error")
	}

	_, err = proVM.Disconnected(ids.ShortID{})
	if err != ErrInnerVMNotConnector {
		t.Fatal("Proposer VM should signal that it wraps a ChainVM not implementing Connector interface with ErrInnerVMNotConnector error")
	}

	// setup
	proConnVM := TestConnectorProVM{
		TestVM: block.TestVM{},
	}

	// test
	_, err = proConnVM.Connected(ids.ShortID{})
	if err != nil {
		t.Fatal("Proposer VM should forward wrapped Connection state if this implements Connector interface")
	}

	_, err = proConnVM.Disconnected(ids.ShortID{})
	if err != nil {
		t.Fatal("Proposer VM should forward wrapped Disconnection state if this implements Connector interface")
	}
}

func TestInitializeRecordsGenesis(t *testing.T) {
	coreVM, proVM, genesisBlk := initTestProposerVM(t)

	// checks
	blkID, err := proVM.LastAccepted()
	if err != nil {
		t.Fatal("failed to retrieve last accepted block")
	}

	coreVM.CantGetBlock = false
	rtvdBlk, err := proVM.GetBlock(blkID)
	if err != nil {
		t.Fatal("Block should be returned without calling core vm")
	}

	proRtvdBlk, ok := rtvdBlk.(*ProposerBlock)
	if !ok {
		t.Fatal("retrieved block is not a proposer block")
	}

	if !bytes.Equal(proRtvdBlk.Block.Bytes(), genesisBlk.Bytes()) {
		t.Fatal("Stored block is not genesis")
	}
}

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
func initTestProposerVM(t *testing.T) (*block.TestVM, VM, *snowman.TestBlock) {
	// setup
	coreGenesisBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.Empty.Prefix(2021),
			StatusV: choices.Unknown,
		},
		BytesV:  []byte{0},
		HeightV: 0,
	}

	coreVM := &block.TestVM{}
	coreVM.CantInitialize = true
	coreVM.InitializeF = func(*snow.Context, manager.Manager, []byte, []byte, []byte, chan<- common.Message, []*common.Fx) error {
		return nil
	}
	coreVM.LastAcceptedF = func() (ids.ID, error) { return coreGenesisBlk.ID(), nil }
	coreVM.GetBlockF = func(ids.ID) (snowman.Block, error) { return coreGenesisBlk, nil }

	tc := &testClock{
		setTime: time.Now(),
	}
	proVM := NewProVM(coreVM)
	proVM.clk = tc

	if pTestCert == nil {
		var err error
		pTestCert, _ = staking.NewTLSCert()
		if err != nil {
			t.Fatal("Could not generate dummy StakerCert")
		}
	}

	dummyCtx := &snow.Context{
		StakingKey: &pTestCert.PrivateKey,
	}
	dummyDBManager := manager.NewDefaultMemDBManager()
	if err := proVM.Initialize(dummyCtx, dummyDBManager, coreGenesisBlk.Bytes(), nil, nil, nil, nil); err != nil {
		t.Fatal("failed to initialize proposerVM")
	}
	return coreVM, proVM, coreGenesisBlk
}

func TestBuildBlockRecordsAndVerifiesBuiltBlock(t *testing.T) {
	// setup
	coreVM, proVM, coreGenBlk := initTestProposerVM(t)
	coreVM.CantBuildBlock = true
	coreBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.Empty.Prefix(2021),
			StatusV: choices.Processing,
		},
		ParentV: coreGenBlk,
		HeightV: coreGenBlk.HeightV + 1,
		VerifyV: nil,
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) { return coreBlk, nil }

	// test
	builtBlk, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("proposerVM could not build block")
	}
	if err := builtBlk.Verify(); err != nil {
		t.Fatal("built block should be verified")
	}

	// test
	coreVM.CantGetBlock = false // forbid calls to coreVM to show caching
	storedBlk, err := proVM.GetBlock(builtBlk.ID())
	if err != nil {
		t.Fatal("proposerVM has not cached built block")
	}
	if storedBlk != builtBlk {
		t.Fatal("proposerVM retrieved wrong block")
	}
}

func TestFirstProposerBlockIsBuiltOnTopOfGenesis(t *testing.T) {
	// setup
	coreVM, proVM, genesisBlk := initTestProposerVM(t)

	newBlk := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV:     ids.GenerateTestID(),
			StatusV: choices.Processing,
		},
		ParentV: genesisBlk,
		HeightV: 1,
		BytesV:  []byte{1},
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) { return newBlk, nil }

	// test
	snowBlock, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	// checks
	proBlock, ok := snowBlock.(*ProposerBlock)
	if !ok {
		t.Fatal("proposerVM.BuildBlock() does not return a proposervm.Block")
	}

	if proBlock.Block != newBlk {
		t.Fatal("different block was expected to be built")
	}

	if proBlock.Parent().ID() == genesisBlk.ID() {
		t.Fatal("first block not built on genesis")
	}
}

func TestParseBlockRecordsButDoesNotVerifyParsedBlock(t *testing.T) {
	coreVM, proVM, _ := initTestProposerVM(t)

	coreBlkDoesNotVerify := errors.New("coreBlk should not verify in this test")
	coreBlk := &snowman.TestBlock{
		BytesV:  []byte{1},
		VerifyV: coreBlkDoesNotVerify,
	}
	coreVM.CantParseBlock = true
	coreVM.ParseBlockF = func(b []byte) (snowman.Block, error) {
		if !bytes.Equal(b, coreBlk.Bytes()) {
			t.Fatalf("Wrong bytes")
		}
		return coreBlk, nil
	}

	proGenBlkID, _ := proVM.LastAccepted()
	proHdr := ProposerBlockHeader{
		PrntID:    proGenBlkID,
		Timestamp: time.Now().AddDate(0, 0, -1).Unix(),
		Height:    coreBlk.Height(),
	}
	proBlk, _ := NewProBlock(&proVM, proHdr, coreBlk, nil, false) // not signing block, cannot err

	// test
	parsedBlk, err := proVM.ParseBlock(proBlk.Bytes())
	if err != nil {
		t.Fatal("proposerVM could not parse block")
	}
	if err := parsedBlk.Verify(); err == nil {
		t.Fatal("parsed block should not necessarily verify upon parse")
	}

	// test
	coreVM.CantGetBlock = false // forbid calls to coreVM to show caching
	storedBlk, err := proVM.GetBlock(parsedBlk.ID())
	if err != nil {
		t.Fatal("proposerVM has not cached parsed block")
	}
	if storedBlk != parsedBlk {
		t.Fatal("proposerVM retrieved wrong block")
	}
}

func TestProposerVMCacheCanBeRebuiltFromDB(t *testing.T) {
	coreVM, proVM, coreGenBlk := initTestProposerVM(t)

	// build two blocks on top of genesis
	coreBlk1 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: coreGenBlk,
		HeightV: 1,
		BytesV:  []byte{1},
		VerifyV: nil,
	}
	coreBlk2 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: coreBlk1,
		HeightV: 2,
		BytesV:  []byte{2},
		VerifyV: nil,
	}
	coreBuildCalls := 0
	coreVM.BuildBlockF = func() (snowman.Block, error) {
		switch coreBuildCalls {
		case 0:
			coreBuildCalls++
			return coreBlk1, nil
		case 1:
			coreBuildCalls++
			return coreBlk2, nil
		default:
			t.Fatal("BuildBlock of coreVM called too many times")
		}
		return nil, nil
	}
	proBlk1, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}
	proBlk2, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	// while inner cache, as it would happen upon node shutdown
	proVM.state.wipeCache()

	// build a new block to show ops can resume smoothly
	coreBlk3 := &snowman.TestBlock{
		TestDecidable: choices.TestDecidable{
			IDV: ids.GenerateTestID(),
		},
		ParentV: coreBlk2,
		HeightV: 3,
		BytesV:  []byte{3},
		VerifyV: nil,
	}
	coreVM.BuildBlockF = func() (snowman.Block, error) {
		return coreBlk3, nil
	}
	coreVM.ParseBlockF = func(b []byte) (snowman.Block, error) {
		switch {
		case bytes.Equal(b, coreGenBlk.BytesV):
			return coreGenBlk, nil
		case bytes.Equal(b, coreBlk1.BytesV):
			return coreBlk1, nil
		case bytes.Equal(b, coreBlk2.BytesV):
			return coreBlk2, nil
		default:
			t.Fatal("ParseBlock of coreVM called with unknown block")
		}
		return nil, nil
	}

	proBlk3, err := proVM.BuildBlock()
	if err != nil {
		t.Fatal("Could not build block")
	}

	if proBlk3.Parent().ID() != proBlk2.ID() {
		t.Fatal("Error in building Block")
	}

	// check that getBlock still works on older blocks
	rtrvdProBlk2, err := proVM.GetBlock(proBlk2.ID())
	if err != nil {
		t.Fatal("Could not get block after whiping off proposerVM cache")
	}
	if rtrvdProBlk2.ID() != proBlk2.ID() {
		t.Fatal("blocks do not match following cache whiping")
	}
	if err = rtrvdProBlk2.Verify(); err != nil {
		t.Fatal("block retrieved after cache whiping does not verify")
	}

	rtrvdProBlk1, err := proVM.GetBlock(proBlk1.ID())
	if err != nil {
		t.Fatal("Could not get block after whiping off proposerVM cache")
	}
	if rtrvdProBlk1.ID() != proBlk1.ID() {
		t.Fatal("blocks do not match following cache whiping")
	}
	if err = rtrvdProBlk1.Verify(); err != nil {
		t.Fatal("block retrieved after cache whiping does not verify")
	}
}
<|endoftext|>


<commit_before>package router

import (
	"testing"
)

type cargs map[string]string

func TestStringConverter(t *testing.T) {
	var stringConverterTests = []struct {
		args   cargs
		regexp string
	}{
		{cargs{}, `[^/]{1,}`},
		{cargs{"minLength": "1"}, `[^/]{1,}`},
		{cargs{"minLength": "1", "maxLength": "4"}, `[^/]{1,4}`},
		{cargs{"minLength": "1", "maxLength": "2", "length": "4"}, `[^/]{4}`},
	}

	for _, tt := range stringConverterTests {
		c := NewStringConverter(tt.args)
		str := "test"

		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("StringConverter regexp expected `%v` but got `%v`",
				tt.regexp, regexp)
		}

		toGoResult, err := c.ToGo(str)
		if err != nil {
			t.Errorf("StringConverter ToGo(%q) unexpected error: %v", str, err)
		}
		if toGoResult != str {
			t.Errorf("StringConverter ToGo(%q)\nhave %q\nwant %q", str, toGoResult, str)
		}

		toUrlResult, err := c.ToUrl(str)
		if err != nil {
			t.Errorf("StringConverter ToUrl(%q) unexpected error: %v", str, err)
		}
		if toUrlResult != str {
			t.Errorf("StringConverter ToUrl(%q)\nhave %q\nwant %q", str, toUrlResult, str)
		}
	}
}

func TestPathConverter(t *testing.T) {
	var pathConverterTests = []string{
		"foo",
		"foo/bar",
	}

	args := cargs{}
	for _, path := range pathConverterTests {
		c := NewPathConverter(args)

		toGoResult, err := c.ToGo(path)
		if err != nil {
			t.Errorf("PathConverter ToGo(%q) unexpected error: %v", path, err)
		}
		if toGoResult != path {
			t.Errorf("PathConverter ToGo(%q)\nhave %q\nwant %q", path, toGoResult, path)
		}

		toUrlResult, err := c.ToUrl(path)
		if err != nil {
			t.Errorf("PathConverter ToUrl(%q) unexpected error: %v", path, err)
		}
		if toUrlResult != path {
			t.Errorf("PathConverter ToUrl(%q)\nhave %q\nwant %q", path, toUrlResult, path)
		}
	}
}

func TestPathConverterNil(t *testing.T) {
	args := cargs{"key": "value"}
	c := NewPathConverter(args)
	if c != nil {
		t.Errorf("NewPathConverter(%v) = %v, want <nil>", args, c)
	}
}

func TestPathConverterRegexp(t *testing.T) {
	args := cargs{}
	expectedRegexp := `[^/].*?`
	c := NewPathConverter(args)
	if regexp := c.Regexp(); regexp != expectedRegexp {
		t.Errorf("PathConverter regexp\nhave `%v`\nwant `%v`", regexp, expectedRegexp)
	}
}

func TestAnyConverter(t *testing.T) {
	var anyConverterTests = []struct {
		args   cargs
		regexp string
	}{
		{cargs{"items": "a"}, `(?:a)`},
		{cargs{"items": "a,b"}, `(?:a|b)`},
		{cargs{"items": "a,b,c"}, `(?:a|b|c)`},
	}

	for i, tt := range anyConverterTests {
		c := NewAnyConverter(tt.args)
		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("%d. AnyConverter(%v) regexp\nhave `%s`\nwant `%s`",
				i, tt.args, regexp, tt.regexp)
		}
	}
}

func TestAnyConverterNil(t *testing.T) {
	var anyConverterTests = []cargs{
		cargs{},
		cargs{"items": ""},
		cargs{"items": ","},
		cargs{"items": "a,"},
		cargs{"items": ",a"},
	}

	for i, args := range anyConverterTests {
		c := NewAnyConverter(args)
		if c != nil {
			t.Errorf("%d. NewPathConverter(%v) = %v, want <nil>", i, args, c)
		}
	}
}

func TestIntConverter(t *testing.T) {
	var intConverterTests = []struct {
		args        cargs
		regexp      string
		toGoParam   string
		toGoResult  int
		toUrlParam  int
		toUrlResult string
	}{
		{cargs{}, `\d+`, "4", 4, 4, "4"},
		{cargs{"digits": "2"}, `\d+`, "44", 44, 44, "44"},
		{cargs{"digits": "2"}, `\d+`, "04", 4, 4, "04"},
		{cargs{"digits": "2"}, `\d+`, "4", -1, 4, "04"},
		{cargs{"min": "3"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "5"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"max": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"max": "3"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"min": "3", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "4", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "5", "max": "5"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"min": "3", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "3", "max": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "3", "max": "3"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"digits": "2", "min": "3", "max": "4"}, `\d+`, "04", 4, 4, "04"},
		{cargs{"digits": "2", "min": "3", "max": "4"}, `\d+`, "05", -1, 5, "05"},
	}

	for _, tt := range intConverterTests {
		c := NewIntConverter(tt.args)
		if _, ok := c.(*IntConverter); !ok {
			t.Errorf("NewIntConverter(%v) got <nil>", tt.args)
			continue
		}

		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("IntConverter regexp\nhave `%v`\nwant `%v`", regexp, tt.regexp)
		}

		toGoResult, err := c.ToGo(tt.toGoParam)
		if err != nil && tt.toGoResult != -1 {
			t.Errorf("IntConverter ToGo(%q) unexpected error: %v",
				tt.toGoParam, err)
		}
		if toGoResult != tt.toGoResult {
			t.Errorf("IntConverter ToGo(%q)\nhave %v\nwant %v",
				tt.toGoParam, toGoResult, tt.toGoResult)
		}

		toUrlResult, err := c.ToUrl(tt.toUrlParam)
		if err != nil {
			t.Errorf("IntConverter ToUrl(%v) unexpected error: %v",
				tt.toUrlParam, err)
		}
		if toUrlResult != tt.toUrlResult {
			t.Errorf("IntConverter ToUrl(%v)\nhave %v\nwant %v",
				tt.toUrlParam, toUrlResult, tt.toUrlResult)
		}
	}
}
<commit_msg>Improve string converter tests.<commit_after>package router

import (
	"testing"
)

type cargs map[string]string

func TestStringConverter(t *testing.T) {
	var stringConverterTests = []struct {
		args   cargs
		regexp string
	}{
		{cargs{}, `[^/]{1,}`},
		{cargs{"minLength": "1"}, `[^/]{1,}`},
		{cargs{"minLength": "2"}, `[^/]{2,}`},
		{cargs{"minLength": "1", "maxLength": "4"}, `[^/]{1,4}`},
		{cargs{"minLength": "2", "maxLength": "4"}, `[^/]{2,4}`},
		{cargs{"minLength": "1", "maxLength": "2", "length": "4"}, `[^/]{4}`},
	}

	for i, tt := range stringConverterTests {
		c := NewStringConverter(tt.args)
		str := "test"

		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("%d. StringConverter(%v) regexp\nhave `%s`\nwant `%v`",
				i, tt.args, regexp, tt.regexp)
		}

		toGoResult, err := c.ToGo(str)
		if err != nil {
			t.Errorf("StringConverter ToGo(%q) unexpected error: %v", str, err)
		}
		if toGoResult != str {
			t.Errorf("StringConverter ToGo(%q)\nhave %q\nwant %q", str, toGoResult, str)
		}

		toUrlResult, err := c.ToUrl(str)
		if err != nil {
			t.Errorf("StringConverter ToUrl(%q) unexpected error: %v", str, err)
		}
		if toUrlResult != str {
			t.Errorf("StringConverter ToUrl(%q)\nhave %q\nwant %q", str, toUrlResult, str)
		}
	}
}

func TestPathConverter(t *testing.T) {
	var pathConverterTests = []string{
		"foo",
		"foo/bar",
	}

	args := cargs{}
	for _, path := range pathConverterTests {
		c := NewPathConverter(args)

		toGoResult, err := c.ToGo(path)
		if err != nil {
			t.Errorf("PathConverter ToGo(%q) unexpected error: %v", path, err)
		}
		if toGoResult != path {
			t.Errorf("PathConverter ToGo(%q)\nhave %q\nwant %q", path, toGoResult, path)
		}

		toUrlResult, err := c.ToUrl(path)
		if err != nil {
			t.Errorf("PathConverter ToUrl(%q) unexpected error: %v", path, err)
		}
		if toUrlResult != path {
			t.Errorf("PathConverter ToUrl(%q)\nhave %q\nwant %q", path, toUrlResult, path)
		}
	}
}

func TestPathConverterNil(t *testing.T) {
	args := cargs{"key": "value"}
	c := NewPathConverter(args)
	if c != nil {
		t.Errorf("NewPathConverter(%v) = %v, want <nil>", args, c)
	}
}

func TestPathConverterRegexp(t *testing.T) {
	args := cargs{}
	expectedRegexp := `[^/].*?`
	c := NewPathConverter(args)
	if regexp := c.Regexp(); regexp != expectedRegexp {
		t.Errorf("PathConverter regexp\nhave `%v`\nwant `%v`", regexp, expectedRegexp)
	}
}

func TestAnyConverter(t *testing.T) {
	var anyConverterTests = []struct {
		args   cargs
		regexp string
	}{
		{cargs{"items": "a"}, `(?:a)`},
		{cargs{"items": "a,b"}, `(?:a|b)`},
		{cargs{"items": "a,b,c"}, `(?:a|b|c)`},
	}

	for i, tt := range anyConverterTests {
		c := NewAnyConverter(tt.args)
		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("%d. AnyConverter(%v) regexp\nhave `%s`\nwant `%s`",
				i, tt.args, regexp, tt.regexp)
		}
	}
}

func TestAnyConverterNil(t *testing.T) {
	var anyConverterTests = []cargs{
		cargs{},
		cargs{"items": ""},
		cargs{"items": ","},
		cargs{"items": "a,"},
		cargs{"items": ",a"},
	}

	for i, args := range anyConverterTests {
		c := NewAnyConverter(args)
		if c != nil {
			t.Errorf("%d. NewPathConverter(%v) = %v, want <nil>", i, args, c)
		}
	}
}

func TestIntConverter(t *testing.T) {
	var intConverterTests = []struct {
		args        cargs
		regexp      string
		toGoParam   string
		toGoResult  int
		toUrlParam  int
		toUrlResult string
	}{
		{cargs{}, `\d+`, "4", 4, 4, "4"},
		{cargs{"digits": "2"}, `\d+`, "44", 44, 44, "44"},
		{cargs{"digits": "2"}, `\d+`, "04", 4, 4, "04"},
		{cargs{"digits": "2"}, `\d+`, "4", -1, 4, "04"},
		{cargs{"min": "3"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "5"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"max": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"max": "3"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"min": "3", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "4", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "5", "max": "5"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"min": "3", "max": "5"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "3", "max": "4"}, `\d+`, "4", 4, 4, "4"},
		{cargs{"min": "3", "max": "3"}, `\d+`, "4", -1, 4, "4"},
		{cargs{"digits": "2", "min": "3", "max": "4"}, `\d+`, "04", 4, 4, "04"},
		{cargs{"digits": "2", "min": "3", "max": "4"}, `\d+`, "05", -1, 5, "05"},
	}

	for _, tt := range intConverterTests {
		c := NewIntConverter(tt.args)
		if _, ok := c.(*IntConverter); !ok {
			t.Errorf("NewIntConverter(%v) got <nil>", tt.args)
			continue
		}

		if regexp := c.Regexp(); regexp != tt.regexp {
			t.Errorf("IntConverter regexp\nhave `%v`\nwant `%v`", regexp, tt.regexp)
		}

		toGoResult, err := c.ToGo(tt.toGoParam)
		if err != nil && tt.toGoResult != -1 {
			t.Errorf("IntConverter ToGo(%q) unexpected error: %v",
				tt.toGoParam, err)
		}
		if toGoResult != tt.toGoResult {
			t.Errorf("IntConverter ToGo(%q)\nhave %v\nwant %v",
				tt.toGoParam, toGoResult, tt.toGoResult)
		}

		toUrlResult, err := c.ToUrl(tt.toUrlParam)
		if err != nil {
			t.Errorf("IntConverter ToUrl(%v) unexpected error: %v",
				tt.toUrlParam, err)
		}
		if toUrlResult != tt.toUrlResult {
			t.Errorf("IntConverter ToUrl(%v)\nhave %v\nwant %v",
				tt.toUrlParam, toUrlResult, tt.toUrlResult)
		}
	}
}
<|endoftext|>