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

package lsp

import (
	"context"

	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
	event := params.Event
	for _, folder := range event.Removed {
		view := s.session.View(folder.Name)
		if view != nil {
			view.Shutdown(ctx)
		} else {
			return errors.Errorf("view %s for %v not found", folder.Name, folder.URI)
		}
	}
	return s.addFolders(ctx, event.Added)
}

func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.Snapshot, func(), error) {
	s.stateMu.Lock()
	state := s.state
	s.stateMu.Unlock()
	if state < serverInitialized {
		return nil, func() {}, errors.Errorf("addView called before server initialized")
	}
	options := s.session.Options().Clone()
	if err := s.fetchConfig(ctx, name, uri, options); err != nil {
		return nil, func() {}, err
	}
	_, snapshot, release, err := s.session.NewView(ctx, name, uri, options)
	return snapshot, release, err
}

func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChangeConfigurationParams) error {
	// Apply any changes to the session-level settings.
	options := s.session.Options().Clone()
	semanticTokensRegistered := options.SemanticTokens
	if err := s.fetchConfig(ctx, "", "", options); err != nil {
		return err
	}
	s.session.SetOptions(options)

	// Go through each view, getting and updating its configuration.
	for _, view := range s.session.Views() {
		options := s.session.Options().Clone()
		if err := s.fetchConfig(ctx, view.Name(), view.Folder(), options); err != nil {
			return err
		}
		view, err := view.SetOptions(ctx, options)
		if err != nil {
			return err
		}
		go func() {
			snapshot, release := view.Snapshot(ctx)
			defer release()
			s.diagnoseDetached(snapshot)
		}()
	}

	// Update any session-specific registrations or unregistrations.
	if !semanticTokensRegistered && options.SemanticTokens {
		if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
			Registrations: semanticTokenRegistrations(),
		}); err != nil {
			return err
		}
	} else if semanticTokensRegistered && !options.SemanticTokens {
		var unregistrations []protocol.Unregistration
		for _, r := range semanticTokenRegistrations() {
			unregistrations = append(unregistrations, protocol.Unregistration{
				ID:     r.ID,
				Method: r.Method,
			})
		}
		if err := s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
			Unregisterations: unregistrations,
		}); err != nil {
			return err
		}
	}
	return nil
}

func semanticTokenRegistrations() []protocol.Registration {
	var registrations []protocol.Registration
	for _, method := range []string{
		"textDocument/semanticTokens/full",
		"textDocument/semanticTokens/full/delta",
		"textDocument/semanticTokens/range",
	} {
		registrations = append(registrations, protocol.Registration{
			ID:     method,
			Method: method,
			RegisterOptions: &protocol.SemanticTokensOptions{
				Legend: protocol.SemanticTokensLegend{
					// TODO(pjw): trim these to what we use (and an unused one
					// at position 0 of TokTypes, to catch typos)
					TokenTypes:     SemanticTypes(),
					TokenModifiers: SemanticModifiers(),
				},
			},
		})
	}
	return registrations
}
<commit_msg>internal/lsp: use the correct method name to register semantic tokens<commit_after>// Copyright 2019 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package lsp

import (
	"context"

	"golang.org/x/tools/internal/lsp/protocol"
	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/span"
	errors "golang.org/x/xerrors"
)

func (s *Server) didChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
	event := params.Event
	for _, folder := range event.Removed {
		view := s.session.View(folder.Name)
		if view != nil {
			view.Shutdown(ctx)
		} else {
			return errors.Errorf("view %s for %v not found", folder.Name, folder.URI)
		}
	}
	return s.addFolders(ctx, event.Added)
}

func (s *Server) addView(ctx context.Context, name string, uri span.URI) (source.Snapshot, func(), error) {
	s.stateMu.Lock()
	state := s.state
	s.stateMu.Unlock()
	if state < serverInitialized {
		return nil, func() {}, errors.Errorf("addView called before server initialized")
	}
	options := s.session.Options().Clone()
	if err := s.fetchConfig(ctx, name, uri, options); err != nil {
		return nil, func() {}, err
	}
	_, snapshot, release, err := s.session.NewView(ctx, name, uri, options)
	return snapshot, release, err
}

func (s *Server) didChangeConfiguration(ctx context.Context, _ *protocol.DidChangeConfigurationParams) error {
	// Apply any changes to the session-level settings.
	options := s.session.Options().Clone()
	semanticTokensRegistered := options.SemanticTokens
	if err := s.fetchConfig(ctx, "", "", options); err != nil {
		return err
	}
	s.session.SetOptions(options)

	// Go through each view, getting and updating its configuration.
	for _, view := range s.session.Views() {
		options := s.session.Options().Clone()
		if err := s.fetchConfig(ctx, view.Name(), view.Folder(), options); err != nil {
			return err
		}
		view, err := view.SetOptions(ctx, options)
		if err != nil {
			return err
		}
		go func() {
			snapshot, release := view.Snapshot(ctx)
			defer release()
			s.diagnoseDetached(snapshot)
		}()
	}

	// Update any session-specific registrations or unregistrations.
	if !semanticTokensRegistered && options.SemanticTokens {
		if err := s.client.RegisterCapability(ctx, &protocol.RegistrationParams{
			Registrations: semanticTokenRegistrations(),
		}); err != nil {
			return err
		}
	} else if semanticTokensRegistered && !options.SemanticTokens {
		var unregistrations []protocol.Unregistration
		for _, r := range semanticTokenRegistrations() {
			unregistrations = append(unregistrations, protocol.Unregistration{
				ID:     r.ID,
				Method: r.Method,
			})
		}
		if err := s.client.UnregisterCapability(ctx, &protocol.UnregistrationParams{
			Unregisterations: unregistrations,
		}); err != nil {
			return err
		}
	}
	return nil
}

// This is a work-around for
// https://github.com/microsoft/language-server-protocol/issues/1107. Once
// https://golang.org/cl/266497 has been released for ~1 month, we can probably
// remove this function and use the only correct method name, which is
// "textDocument/semanticTokens".
func semanticTokenRegistrations() []protocol.Registration {
	var registrations []protocol.Registration
	for _, method := range []string{
		"textDocument/semanticTokens",
		"textDocument/semanticTokens/full",
		"textDocument/semanticTokens/full/delta",
		"textDocument/semanticTokens/range",
	} {
		registrations = append(registrations, protocol.Registration{
			ID:     method,
			Method: method,
			RegisterOptions: &protocol.SemanticTokensOptions{
				Legend: protocol.SemanticTokensLegend{
					// TODO(pjw): trim these to what we use (and an unused one
					// at position 0 of TokTypes, to catch typos)
					TokenTypes:     SemanticTypes(),
					TokenModifiers: SemanticModifiers(),
				},
			},
		})
	}
	return registrations
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "github.com/stevedonovan/luar"
    "github.com/tsileo/blobstash/client/transaction"
)

const code = `
print(blobstash.Tx.Set('title', blobstash.Args.title))
return {
    ok = blobstash.Tx.Len()
}
`

func main() {
    L := luar.Init()
    defer L.Close()
    luar.Register(L,"",luar.Map{
        "blobstash": luar.Map{
            "Args": map[string]interface{}{"title": "ok"},
            "Tx": transaction.NewTransaction(),
        },
    })
    res := L.DoString(code)
    if res != nil {
        fmt.Println("Error:",res)
    }
    v := luar.CopyTableToMap(L,nil,-1)
    fmt.Println("returned map",v)
    m := v.(map[string]interface{})
    for k,v := range m {
        fmt.Println(k,v)
    }
}

<commit_msg>Added some TODOs for LUA scripting<commit_after>package main

import (
    "fmt"
    "time"
    "github.com/stevedonovan/luar"
    "github.com/dchest/blake2b"
    "github.com/tsileo/blobstash/client/transaction"
)

const code = `
print(blake2b('ok'))
print(now())
v1, v2 = two()
print(v1)
print(v2)
print(blobstash.Tx.Set('title', blobstash.Args.title))
return {
    ok = blobstash.Tx.Len()
}
`

func Hash(data string) string {
    return fmt.Sprintf("%x", blake2b.Sum256([]byte(data)))
}

func Now() int64 {
    return time.Now().UTC().Unix()
}

func TwoValue() (string, string) {
    return "ok1", "ok2"
}

func main() {
    // TODO set args the JSON from req.Body
    // TODO add blobstash.DB => but read-only => remove delete function and separate DBReader from DB
    // A way to temporary debug lua
    L := luar.Init()
    defer L.Close()
    luar.Register(L,"",luar.Map{
        "blake2b": Hash,
        "now": Now,
        "two": TwoValue,
        "blobstash": luar.Map{
            "Args": map[string]interface{}{"title": "ok"},
            "Tx": transaction.NewTransaction(),
        },
    })
    res := L.DoString(code)
    if res != nil {
        fmt.Println("Error:",res)
    }
    v := luar.CopyTableToMap(L,nil,-1)
    fmt.Println("returned map",v)
    m := v.(map[string]interface{})
    for k,v := range m {
        fmt.Println(k,v)
    }
    // TODO process the transaction
    // And output JSON
}

<|endoftext|>


<commit_before>// Copyright 2019 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package mtail

import (
	"context"
	"expvar"
	"fmt"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/internal/metrics"
	"github.com/google/mtail/internal/metrics/datum"
	"github.com/google/mtail/internal/testutil"
	"github.com/google/mtail/internal/waker"
)

const timeoutMultiplier = 3

const defaultDoOrTimeoutDeadline = 10 * time.Second

type TestServer struct {
	*Server

	waker  waker.Waker // for idle logstreams; others are polled explicitly in PollWatched
	awaken func(int)

	tb testing.TB

	cancel context.CancelFunc

	// Set this to change the poll deadline when using DoOrTimeout within this TestServer.
	DoOrTimeoutDeadline time.Duration
}

// TestMakeServer makes a new TestServer for use in tests, but does not start
// the server.  If an error occurs during creation, a testing.Fatal is issued.
func TestMakeServer(tb testing.TB, wakers int, pollInterval time.Duration, options ...Option) *TestServer {
	tb.Helper()

	// Reset counters when running multiple tests.  Tests that use expvar
	// helpers cannot be made parallel.
	glog.Info("resetting counters")
	expvar.Get("lines_total").(*expvar.Int).Set(0)
	expvar.Get("log_count").(*expvar.Int).Set(0)
	expvar.Get("log_lines_total").(*expvar.Map).Init()
	expvar.Get("log_opens_total").(*expvar.Map).Init()
	expvar.Get("file_truncates_total").(*expvar.Map).Init()
	expvar.Get("prog_loads_total").(*expvar.Map).Init()

	ctx, cancel := context.WithCancel(context.Background())
	waker, awaken := waker.NewTest(ctx, wakers)
	options = append(options,
		LogstreamPollWaker(waker),
	)
	m, err := New(ctx, metrics.NewStore(), options...)
	testutil.FatalIfErr(tb, err)
	return &TestServer{Server: m, waker: waker, awaken: awaken, tb: tb, cancel: cancel}
}

// TestStartServer creates a new TestServer and starts it running.  It
// returns the server, and a cleanup function.
func TestStartServer(tb testing.TB, wakers int, pollInterval time.Duration, options ...Option) (*TestServer, func()) {
	tb.Helper()
	ts := TestMakeServer(tb, wakers, pollInterval, options...)
	return ts, ts.Start()
}

// Start starts the TestServer and returns a cleanup function.
func (ts *TestServer) Start() func() {
	ts.tb.Helper()
	errc := make(chan error, 1)
	go func() {
		err := ts.Run()
		errc <- err
	}()

	return func() {
		ts.cancel()

		select {
		case err := <-errc:
			testutil.FatalIfErr(ts.tb, err)
		case <-time.After(6 * time.Second):
			buf := make([]byte, 1<<16)
			n := runtime.Stack(buf, true)
			fmt.Fprintf(os.Stderr, "%s", buf[0:n])
			ts.tb.Fatal("timeout waiting for shutdown")
		}
	}
}

// Poll all watched objects for updates.  The parameter n indicates how many logstreams to wait on before waking them.
func (ts *TestServer) PollWatched(n int) {
	glog.Info("Testserver starting poll")
	glog.Infof("TestServer polling filesystem patterns")
	if err := ts.t.Poll(); err != nil {
		glog.Info(err)
	}
	glog.Infof("TestServer reloading programs")
	if err := ts.l.LoadAllPrograms(); err != nil {
		glog.Info(err)
	}
	glog.Infof("TestServer tailer gcing")
	if err := ts.t.Gc(); err != nil {
		glog.Info(err)
	}
	glog.Info("TestServer waking idle routines")
	ts.awaken(n)
	glog.Info("Testserver finishing poll")
}

/// GetExpvar is a helper function on TestServer that acts like TestGetExpvar.
func (ts *TestServer) GetExpvar(name string) expvar.Var {
	ts.tb.Helper()
	return testutil.TestGetExpvar(ts.tb, name)
}

// ExpectExpvarDeltaWithDeadline returns a deferrable function which tests if the expvar metric with name has changed by delta within the given deadline, once the function begins.  Before returning, it fetches the original value for comparison.
func (ts *TestServer) ExpectExpvarDeltaWithDeadline(name string, want int64) func() {
	ts.tb.Helper()
	return testutil.ExpectExpvarDeltaWithDeadline(ts.tb, name, want)
}

// ExpectMapExpvarMetricDeltaWithDeadline returns a deferrable function which tests if the expvar map metric with name and key has changed by delta within the given deadline, once the function begins.  Before returning, it fetches the original value for comparison.
func (ts *TestServer) ExpectMapExpvarDeltaWithDeadline(name, key string, want int64) func() {
	ts.tb.Helper()
	return testutil.ExpectMapExpvarDeltaWithDeadline(ts.tb, name, key, want)
}

// GetProgramMetric fetches the datum of the program metric name.
func (ts *TestServer) GetProgramMetric(name string) datum.Datum {
	ts.tb.Helper()
	m := ts.store.Metrics[name]
	if len(m) != 1 || len(m[0].LabelValues) != 1 {
		ts.tb.Fatalf("Unexpected metric store content: expected a single metrics with no labels, but got %v", m)
		return nil
	}
	d, derr := m[0].GetDatum()
	testutil.FatalIfErr(ts.tb, derr)
	return d
}

// ExpectProgMetricDeltaWithDeadline tests that a given program metric increases by want within the deadline.  It assumes that the named metric is an Int type datum.Datum.
func (ts *TestServer) ExpectProgMetricDeltaWithDeadline(name string, want int64) func() {
	ts.tb.Helper()
	deadline := ts.DoOrTimeoutDeadline
	if deadline == 0 {
		deadline = defaultDoOrTimeoutDeadline
	}
	start := datum.GetInt(ts.GetProgramMetric(name))
	check := func() (bool, error) {
		ts.tb.Helper()
		now := datum.GetInt(ts.GetProgramMetric(name))
		return now-start == want, nil
	}
	return func() {
		ts.tb.Helper()
		ok, err := testutil.DoOrTimeout(check, deadline, 10*time.Millisecond)
		if err != nil {
			ts.tb.Fatal(err)
		}
		if !ok {
			now := datum.GetInt(ts.GetProgramMetric(name))
			delta := now - start
			ts.tb.Errorf("Did not see %s have delta by deadline: got %v - %v = %d, want %d", name, now, start, delta, want)
		}
	}
}
<commit_msg>TestServer doesn't return a cleanup function, it is used during tests to stop mtail.<commit_after>// Copyright 2019 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package mtail

import (
	"context"
	"expvar"
	"fmt"
	"os"
	"runtime"
	"testing"
	"time"

	"github.com/golang/glog"
	"github.com/google/mtail/internal/metrics"
	"github.com/google/mtail/internal/metrics/datum"
	"github.com/google/mtail/internal/testutil"
	"github.com/google/mtail/internal/waker"
)

const timeoutMultiplier = 3

const defaultDoOrTimeoutDeadline = 10 * time.Second

type TestServer struct {
	*Server

	waker  waker.Waker // for idle logstreams; others are polled explicitly in PollWatched
	awaken func(int)

	tb testing.TB

	cancel context.CancelFunc

	// Set this to change the poll deadline when using DoOrTimeout within this TestServer.
	DoOrTimeoutDeadline time.Duration
}

// TestMakeServer makes a new TestServer for use in tests, but does not start
// the server.  If an error occurs during creation, a testing.Fatal is issued.
func TestMakeServer(tb testing.TB, wakers int, pollInterval time.Duration, options ...Option) *TestServer {
	tb.Helper()

	// Reset counters when running multiple tests.  Tests that use expvar
	// helpers cannot be made parallel.
	glog.Info("resetting counters")
	expvar.Get("lines_total").(*expvar.Int).Set(0)
	expvar.Get("log_count").(*expvar.Int).Set(0)
	expvar.Get("log_lines_total").(*expvar.Map).Init()
	expvar.Get("log_opens_total").(*expvar.Map).Init()
	expvar.Get("file_truncates_total").(*expvar.Map).Init()
	expvar.Get("prog_loads_total").(*expvar.Map).Init()

	ctx, cancel := context.WithCancel(context.Background())
	waker, awaken := waker.NewTest(ctx, wakers)
	options = append(options,
		LogstreamPollWaker(waker),
	)
	m, err := New(ctx, metrics.NewStore(), options...)
	testutil.FatalIfErr(tb, err)
	return &TestServer{Server: m, waker: waker, awaken: awaken, tb: tb, cancel: cancel}
}

// TestStartServer creates a new TestServer and starts it running.  It
// returns the server, and a stop function.
func TestStartServer(tb testing.TB, wakers int, pollInterval time.Duration, options ...Option) (*TestServer, func()) {
	tb.Helper()
	ts := TestMakeServer(tb, wakers, pollInterval, options...)
	return ts, ts.Start()
}

// Start starts the TestServer and returns a stop function.
func (ts *TestServer) Start() func() {
	ts.tb.Helper()
	errc := make(chan error, 1)
	go func() {
		err := ts.Run()
		errc <- err
	}()

	return func() {
		ts.cancel()

		select {
		case err := <-errc:
			testutil.FatalIfErr(ts.tb, err)
		case <-time.After(6 * time.Second):
			buf := make([]byte, 1<<16)
			n := runtime.Stack(buf, true)
			fmt.Fprintf(os.Stderr, "%s", buf[0:n])
			ts.tb.Fatal("timeout waiting for shutdown")
		}
	}
}

// Poll all watched objects for updates.  The parameter n indicates how many logstreams to wait on before waking them.
func (ts *TestServer) PollWatched(n int) {
	glog.Info("Testserver starting poll")
	glog.Infof("TestServer polling filesystem patterns")
	if err := ts.t.Poll(); err != nil {
		glog.Info(err)
	}
	glog.Infof("TestServer reloading programs")
	if err := ts.l.LoadAllPrograms(); err != nil {
		glog.Info(err)
	}
	glog.Infof("TestServer tailer gcing")
	if err := ts.t.Gc(); err != nil {
		glog.Info(err)
	}
	glog.Info("TestServer waking idle routines")
	ts.awaken(n)
	glog.Info("Testserver finishing poll")
}

/// GetExpvar is a helper function on TestServer that acts like TestGetExpvar.
func (ts *TestServer) GetExpvar(name string) expvar.Var {
	ts.tb.Helper()
	return testutil.TestGetExpvar(ts.tb, name)
}

// ExpectExpvarDeltaWithDeadline returns a deferrable function which tests if the expvar metric with name has changed by delta within the given deadline, once the function begins.  Before returning, it fetches the original value for comparison.
func (ts *TestServer) ExpectExpvarDeltaWithDeadline(name string, want int64) func() {
	ts.tb.Helper()
	return testutil.ExpectExpvarDeltaWithDeadline(ts.tb, name, want)
}

// ExpectMapExpvarMetricDeltaWithDeadline returns a deferrable function which tests if the expvar map metric with name and key has changed by delta within the given deadline, once the function begins.  Before returning, it fetches the original value for comparison.
func (ts *TestServer) ExpectMapExpvarDeltaWithDeadline(name, key string, want int64) func() {
	ts.tb.Helper()
	return testutil.ExpectMapExpvarDeltaWithDeadline(ts.tb, name, key, want)
}

// GetProgramMetric fetches the datum of the program metric name.
func (ts *TestServer) GetProgramMetric(name string) datum.Datum {
	ts.tb.Helper()
	m := ts.store.Metrics[name]
	if len(m) != 1 || len(m[0].LabelValues) != 1 {
		ts.tb.Fatalf("Unexpected metric store content: expected a single metrics with no labels, but got %v", m)
		return nil
	}
	d, derr := m[0].GetDatum()
	testutil.FatalIfErr(ts.tb, derr)
	return d
}

// ExpectProgMetricDeltaWithDeadline tests that a given program metric increases by want within the deadline.  It assumes that the named metric is an Int type datum.Datum.
func (ts *TestServer) ExpectProgMetricDeltaWithDeadline(name string, want int64) func() {
	ts.tb.Helper()
	deadline := ts.DoOrTimeoutDeadline
	if deadline == 0 {
		deadline = defaultDoOrTimeoutDeadline
	}
	start := datum.GetInt(ts.GetProgramMetric(name))
	check := func() (bool, error) {
		ts.tb.Helper()
		now := datum.GetInt(ts.GetProgramMetric(name))
		return now-start == want, nil
	}
	return func() {
		ts.tb.Helper()
		ok, err := testutil.DoOrTimeout(check, deadline, 10*time.Millisecond)
		if err != nil {
			ts.tb.Fatal(err)
		}
		if !ok {
			now := datum.GetInt(ts.GetProgramMetric(name))
			delta := now - start
			ts.tb.Errorf("Did not see %s have delta by deadline: got %v - %v = %d, want %d", name, now, start, delta, want)
		}
	}
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package server

import "net/http"

func (s *Server) router(w http.ResponseWriter, r *http.Request) {

	s.logger.Info().
		Str("method", r.Method).
		Str("url", r.URL.String()).
		Msg("Request")

	switch r.Method {
	case "GET":
		if pluginPathRx.MatchString(r.URL.Path) { // run plugin(s)
			s.run(w, r)
		} else if inventoryPathRx.MatchString(r.URL.Path) { // plugin inventory
			s.inventory(w, r)
		} else {
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	case "POST":
		fallthrough
	case "PUT":
		if writePathRx.MatchString(r.URL.Path) {
			s.write(w, r)
		} else {
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
		return
	}

}
<commit_msg>remove redundant return<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package server

import "net/http"

func (s *Server) router(w http.ResponseWriter, r *http.Request) {

	s.logger.Info().
		Str("method", r.Method).
		Str("url", r.URL.String()).
		Msg("Request")

	switch r.Method {
	case "GET":
		if pluginPathRx.MatchString(r.URL.Path) { // run plugin(s)
			s.run(w, r)
		} else if inventoryPathRx.MatchString(r.URL.Path) { // plugin inventory
			s.inventory(w, r)
		} else {
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	case "POST":
		fallthrough
	case "PUT":
		if writePathRx.MatchString(r.URL.Path) {
			s.write(w, r)
		} else {
			s.logger.Warn().
				Str("method", r.Method).
				Str("url", r.URL.String()).
				Msg("Not found")
			http.NotFound(w, r)
		}
	default:
		http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
	}
}
<|endoftext|>


<commit_before>// This file defines all filters and convenience functions for filters.
// Should be functionally equivalent to stable gobgpdump
// Current Filter options:
// -Source AS number (NewASFilter("1,2,3", true))
// -Destination AS number (NewASFilter("1,2,3", false))

// TODO
// -Maybe a function to return an array of filters based on config
//	options?
// -Time filter
// -Filters based on other attributes (peers, prefixes seen, etc.)
package gobgpdump

import (
	"fmt"
	mrt "github.com/CSUNetSec/protoparse/protocol/mrt"
	radix "github.com/armon/go-radix"
	"net"
	"strconv"
	"strings"
)

type Filter func(mbs *mrt.MrtBufferStack) bool

type PrefixFilter struct {
	prefixes []string
	rt       *radix.Tree
}

func NewPrefixFilter(raw string) Filter {
	pf := PrefixFilter{}
	pf.rt = radix.New()
	prefstrings := strings.Split(raw, ",")
	for _, p := range prefstrings {
		parts := strings.Split(p, "/")
		if len(parts) != 2 {
			panic("malformed prefix string")
		}
		mask, err := maskstr2uint8(parts[1])
		if err != nil {
			panic(fmt.Sprintf("error parsing mask:%s err:%s", parts[1], err))
		}
		pf.rt.Insert(IpToRadixkey(net.ParseIP(parts[0]).To4(), mask), true)
	}
	pf.prefixes = prefstrings
	return pf.filterBySeen
}

func (pf PrefixFilter) filterBySeen(mbs *mrt.MrtBufferStack) bool {
	advPrefs, err := getAdvertizedPrefixes(mbs)
	if err == nil {
		for _, pref := range advPrefs {
			if _, _, found := pf.rt.LongestPrefix(IpToRadixkey(pref.IP, pref.Mask)); found {
				return true
			}
		}
	}

	wdnPrefs, err := getWithdrawnPrefixes(mbs)
	if err == nil {
		for _, pref := range wdnPrefs {
			if _, _, found := pf.rt.LongestPrefix(IpToRadixkey(pref.IP, pref.Mask)); found {
				return true
			}
		}
	}
	return false
}

func (pf PrefixFilter) matchesOne(pref string) bool {
	for _, comp := range pf.prefixes {
		if pref == comp {
			return true
		}
	}
	return false
}

type ASFilter struct {
	asList []uint32
}

// Returns an AS filter with the list of AS's in the form "1,2,3,4"
// If src is true, filters messages by source AS number
// otherwise filters by destination AS number
func NewASFilter(list string, src bool) (Filter, error) {
	aslist, err := parseASList(list)
	if err != nil {
		return nil, err
	}

	asf := ASFilter{aslist}
	if src {
		return asf.FilterBySource, nil
	} else {
		return asf.FilterByDest, nil
	}
}

func (asf ASFilter) FilterBySource(mbs *mrt.MrtBufferStack) bool {
	path, err := getASPath(mbs)
	if err != nil || len(path) < 1 {
		return false
	}

	return asf.matchesOne(path[len(path)-1])
}

func (asf ASFilter) FilterByDest(mbs *mrt.MrtBufferStack) bool {
	path, err := getASPath(mbs)
	if err != nil || len(path) < 1 {
		return false
	}

	return asf.matchesOne(path[0])
}

// Convenience function used by both FilterBySrc/Dest
func (asf ASFilter) matchesOne(comp uint32) bool {
	for _, asnum := range asf.asList {
		if asnum == comp {
			return true
		}
	}
	return false
}

func parseASList(str string) ([]uint32, error) {
	list := strings.Split(str, ",")
	aslist := make([]uint32, len(list))

	for i := 0; i < len(aslist); i++ {
		as, err := strconv.ParseUint(list[i], 10, 32)
		if err != nil {
			return nil, err
		}
		aslist[i] = uint32(as)
	}

	return aslist, nil
}

func filterAll(filters []Filter, mbs *mrt.MrtBufferStack) bool {
	for _, fil := range filters {
		if fil != nil && !fil(mbs) {
			return false
		}
	}
	return true
}
<commit_msg>removing unused prefix matching funtion<commit_after>// This file defines all filters and convenience functions for filters.
// Should be functionally equivalent to stable gobgpdump
// Current Filter options:
// -Source AS number (NewASFilter("1,2,3", true))
// -Destination AS number (NewASFilter("1,2,3", false))

// TODO
// -Maybe a function to return an array of filters based on config
//	options?
// -Time filter
// -Filters based on other attributes (peers, prefixes seen, etc.)
package gobgpdump

import (
	"fmt"
	mrt "github.com/CSUNetSec/protoparse/protocol/mrt"
	radix "github.com/armon/go-radix"
	"net"
	"strconv"
	"strings"
)

type Filter func(mbs *mrt.MrtBufferStack) bool

type PrefixFilter struct {
	prefixes []string
	rt       *radix.Tree
}

func NewPrefixFilter(raw string) Filter {
	pf := PrefixFilter{}
	pf.rt = radix.New()
	prefstrings := strings.Split(raw, ",")
	for _, p := range prefstrings {
		parts := strings.Split(p, "/")
		if len(parts) != 2 {
			panic("malformed prefix string")
		}
		mask, err := maskstr2uint8(parts[1])
		if err != nil {
			panic(fmt.Sprintf("error parsing mask:%s err:%s", parts[1], err))
		}
		pf.rt.Insert(IpToRadixkey(net.ParseIP(parts[0]).To4(), mask), true)
	}
	pf.prefixes = prefstrings
	return pf.filterBySeen
}

func (pf PrefixFilter) filterBySeen(mbs *mrt.MrtBufferStack) bool {
	advPrefs, err := getAdvertizedPrefixes(mbs)
	if err == nil {
		for _, pref := range advPrefs {
			if _, _, found := pf.rt.LongestPrefix(IpToRadixkey(pref.IP, pref.Mask)); found {
				return true
			}
		}
	}

	wdnPrefs, err := getWithdrawnPrefixes(mbs)
	if err == nil {
		for _, pref := range wdnPrefs {
			if _, _, found := pf.rt.LongestPrefix(IpToRadixkey(pref.IP, pref.Mask)); found {
				return true
			}
		}
	}
	return false
}

type ASFilter struct {
	asList []uint32
}

// Returns an AS filter with the list of AS's in the form "1,2,3,4"
// If src is true, filters messages by source AS number
// otherwise filters by destination AS number
func NewASFilter(list string, src bool) (Filter, error) {
	aslist, err := parseASList(list)
	if err != nil {
		return nil, err
	}

	asf := ASFilter{aslist}
	if src {
		return asf.FilterBySource, nil
	} else {
		return asf.FilterByDest, nil
	}
}

func (asf ASFilter) FilterBySource(mbs *mrt.MrtBufferStack) bool {
	path, err := getASPath(mbs)
	if err != nil || len(path) < 1 {
		return false
	}

	return asf.matchesOne(path[len(path)-1])
}

func (asf ASFilter) FilterByDest(mbs *mrt.MrtBufferStack) bool {
	path, err := getASPath(mbs)
	if err != nil || len(path) < 1 {
		return false
	}

	return asf.matchesOne(path[0])
}

// Convenience function used by both FilterBySrc/Dest
func (asf ASFilter) matchesOne(comp uint32) bool {
	for _, asnum := range asf.asList {
		if asnum == comp {
			return true
		}
	}
	return false
}

func parseASList(str string) ([]uint32, error) {
	list := strings.Split(str, ",")
	aslist := make([]uint32, len(list))

	for i := 0; i < len(aslist); i++ {
		as, err := strconv.ParseUint(list[i], 10, 32)
		if err != nil {
			return nil, err
		}
		aslist[i] = uint32(as)
	}

	return aslist, nil
}

func filterAll(filters []Filter, mbs *mrt.MrtBufferStack) bool {
	for _, fil := range filters {
		if fil != nil && !fil(mbs) {
			return false
		}
	}
	return true
}
<|endoftext|>


<commit_before>// Copyright 2017 Jan Christian Grünhage
//
// 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 gomatrixserverlib

import (
	"errors"
)

// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
// Specified by: https://matrix.org/docs/spec/client_server/r0.5.0.html#filtering
type Filter struct {
	EventFields []string    `json:"event_fields,omitempty"`
	EventFormat string      `json:"event_format,omitempty"`
	Presence    EventFilter `json:"presence,omitempty"`
	AccountData EventFilter `json:"account_data,omitempty"`
	Room        RoomFilter  `json:"room,omitempty"`
}

// EventFilter is used to define filtering rules for events
type EventFilter struct {
	Limit      int       `json:"limit,omitempty"`
	NotSenders *[]string `json:"not_senders,omitempty"`
	NotTypes   *[]string `json:"not_types,omitempty"`
	Senders    *[]string `json:"senders,omitempty"`
	Types      *[]string `json:"types,omitempty"`
}

// RoomFilter is used to define filtering rules for room-related events
type RoomFilter struct {
	NotRooms     *[]string       `json:"not_rooms,omitempty"`
	Rooms        *[]string       `json:"rooms,omitempty"`
	Ephemeral    RoomEventFilter `json:"ephemeral,omitempty"`
	IncludeLeave bool            `json:"include_leave,omitempty"`
	State        StateFilter     `json:"state,omitempty"`
	Timeline     RoomEventFilter `json:"timeline,omitempty"`
	AccountData  RoomEventFilter `json:"account_data,omitempty"`
}

// StateFilter is used to define filtering rules for state events
type StateFilter struct {
	Limit                   int       `json:"limit,omitempty"`
	NotSenders              *[]string `json:"not_senders,omitempty"`
	NotTypes                *[]string `json:"not_types,omitempty"`
	Senders                 *[]string `json:"senders,omitempty"`
	Types                   *[]string `json:"types,omitempty"`
	LazyLoadMembers         bool      `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool      `json:"include_redundant_members,omitempty"`
	NotRooms                *[]string `json:"not_rooms,omitempty"`
	Rooms                   *[]string `json:"rooms,omitempty"`
	ContainsURL             *bool     `json:"contains_url,omitempty"`
}

// RoomEventFilter is used to define filtering rules for events in rooms
type RoomEventFilter struct {
	Limit                   int       `json:"limit,omitempty"`
	NotSenders              *[]string `json:"not_senders,omitempty"`
	NotTypes                *[]string `json:"not_types,omitempty"`
	Senders                 *[]string `json:"senders,omitempty"`
	Types                   *[]string `json:"types,omitempty"`
	LazyLoadMembers         bool      `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool      `json:"include_redundant_members,omitempty"`
	NotRooms                *[]string `json:"not_rooms,omitempty"`
	Rooms                   *[]string `json:"rooms,omitempty"`
	ContainsURL             *bool     `json:"contains_url,omitempty"`
}

// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
	if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" {
		return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
	}
	return nil
}

// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in
// the request
func DefaultFilter() Filter {
	return Filter{
		AccountData: DefaultEventFilter(),
		EventFields: nil,
		EventFormat: "client",
		Presence:    DefaultEventFilter(),
		Room: RoomFilter{
			AccountData:  DefaultRoomEventFilter(),
			Ephemeral:    DefaultRoomEventFilter(),
			IncludeLeave: false,
			NotRooms:     nil,
			Rooms:        nil,
			State:        DefaultStateFilter(),
			Timeline:     DefaultRoomEventFilter(),
		},
	}
}

// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is
// provided in the request
func DefaultEventFilter() EventFilter {
	return EventFilter{
		Limit:      20,
		NotSenders: nil,
		NotTypes:   nil,
		Senders:    nil,
		Types:      nil,
	}
}

// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter
// is provided in the request
func DefaultStateFilter() StateFilter {
	return StateFilter{
		Limit:                   20,
		NotSenders:              nil,
		NotTypes:                nil,
		Senders:                 nil,
		Types:                   nil,
		LazyLoadMembers:         false,
		IncludeRedundantMembers: false,
		NotRooms:                nil,
		Rooms:                   nil,
		ContainsURL:             nil,
	}
}

// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no
// filter is provided in the request
func DefaultRoomEventFilter() RoomEventFilter {
	return RoomEventFilter{
		Limit:       20,
		NotSenders:  nil,
		NotTypes:    nil,
		Senders:     nil,
		Types:       nil,
		NotRooms:    nil,
		Rooms:       nil,
		ContainsURL: nil,
	}
}
<commit_msg>Remove `Limit` from `StateFilter`<commit_after>// Copyright 2017 Jan Christian Grünhage
//
// 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 gomatrixserverlib

import (
	"errors"
)

// Filter is used by clients to specify how the server should filter responses to e.g. sync requests
// Specified by: https://matrix.org/docs/spec/client_server/r0.5.0.html#filtering
type Filter struct {
	EventFields []string    `json:"event_fields,omitempty"`
	EventFormat string      `json:"event_format,omitempty"`
	Presence    EventFilter `json:"presence,omitempty"`
	AccountData EventFilter `json:"account_data,omitempty"`
	Room        RoomFilter  `json:"room,omitempty"`
}

// EventFilter is used to define filtering rules for events
type EventFilter struct {
	Limit      int       `json:"limit,omitempty"`
	NotSenders *[]string `json:"not_senders,omitempty"`
	NotTypes   *[]string `json:"not_types,omitempty"`
	Senders    *[]string `json:"senders,omitempty"`
	Types      *[]string `json:"types,omitempty"`
}

// RoomFilter is used to define filtering rules for room-related events
type RoomFilter struct {
	NotRooms     *[]string       `json:"not_rooms,omitempty"`
	Rooms        *[]string       `json:"rooms,omitempty"`
	Ephemeral    RoomEventFilter `json:"ephemeral,omitempty"`
	IncludeLeave bool            `json:"include_leave,omitempty"`
	State        StateFilter     `json:"state,omitempty"`
	Timeline     RoomEventFilter `json:"timeline,omitempty"`
	AccountData  RoomEventFilter `json:"account_data,omitempty"`
}

// StateFilter is used to define filtering rules for state events
type StateFilter struct {
	NotSenders              *[]string `json:"not_senders,omitempty"`
	NotTypes                *[]string `json:"not_types,omitempty"`
	Senders                 *[]string `json:"senders,omitempty"`
	Types                   *[]string `json:"types,omitempty"`
	LazyLoadMembers         bool      `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool      `json:"include_redundant_members,omitempty"`
	NotRooms                *[]string `json:"not_rooms,omitempty"`
	Rooms                   *[]string `json:"rooms,omitempty"`
	ContainsURL             *bool     `json:"contains_url,omitempty"`
}

// RoomEventFilter is used to define filtering rules for events in rooms
type RoomEventFilter struct {
	Limit                   int       `json:"limit,omitempty"`
	NotSenders              *[]string `json:"not_senders,omitempty"`
	NotTypes                *[]string `json:"not_types,omitempty"`
	Senders                 *[]string `json:"senders,omitempty"`
	Types                   *[]string `json:"types,omitempty"`
	LazyLoadMembers         bool      `json:"lazy_load_members,omitempty"`
	IncludeRedundantMembers bool      `json:"include_redundant_members,omitempty"`
	NotRooms                *[]string `json:"not_rooms,omitempty"`
	Rooms                   *[]string `json:"rooms,omitempty"`
	ContainsURL             *bool     `json:"contains_url,omitempty"`
}

// Validate checks if the filter contains valid property values
func (filter *Filter) Validate() error {
	if filter.EventFormat != "" && filter.EventFormat != "client" && filter.EventFormat != "federation" {
		return errors.New("Bad event_format value. Must be one of [\"client\", \"federation\"]")
	}
	return nil
}

// DefaultFilter returns the default filter used by the Matrix server if no filter is provided in
// the request
func DefaultFilter() Filter {
	return Filter{
		AccountData: DefaultEventFilter(),
		EventFields: nil,
		EventFormat: "client",
		Presence:    DefaultEventFilter(),
		Room: RoomFilter{
			AccountData:  DefaultRoomEventFilter(),
			Ephemeral:    DefaultRoomEventFilter(),
			IncludeLeave: false,
			NotRooms:     nil,
			Rooms:        nil,
			State:        DefaultStateFilter(),
			Timeline:     DefaultRoomEventFilter(),
		},
	}
}

// DefaultEventFilter returns the default event filter used by the Matrix server if no filter is
// provided in the request
func DefaultEventFilter() EventFilter {
	return EventFilter{
		Limit:      20,
		NotSenders: nil,
		NotTypes:   nil,
		Senders:    nil,
		Types:      nil,
	}
}

// DefaultStateFilter returns the default state event filter used by the Matrix server if no filter
// is provided in the request
func DefaultStateFilter() StateFilter {
	return StateFilter{
		NotSenders:              nil,
		NotTypes:                nil,
		Senders:                 nil,
		Types:                   nil,
		LazyLoadMembers:         false,
		IncludeRedundantMembers: false,
		NotRooms:                nil,
		Rooms:                   nil,
		ContainsURL:             nil,
	}
}

// DefaultRoomEventFilter returns the default room event filter used by the Matrix server if no
// filter is provided in the request
func DefaultRoomEventFilter() RoomEventFilter {
	return RoomEventFilter{
		Limit:       20,
		NotSenders:  nil,
		NotTypes:    nil,
		Senders:     nil,
		Types:       nil,
		NotRooms:    nil,
		Rooms:       nil,
		ContainsURL: nil,
	}
}
<|endoftext|>


<commit_before>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notPaths    []Matcher
	notNames    []Matcher
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.paths = append(f.paths, matcher)
	}
	return f
}

// NotPath excludes folders from the search using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) NotPath(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.notPaths = append(f.notPaths, matcher)
	}
	return f
}

func (f *Finder) path(p string) Matcher {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

var errNoMatch = errors.New("Item did not match")
var errSkipDir = filepath.SkipDir

func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return errNoMatch
	}
	var match error
	match = f.matchPaths(i)
	if match != nil {
		return match
	}
	match = f.matchNotPaths(i)
	if match != nil {
		return match
	}
	match = f.matchNames(i)
	if match != nil {
		return match
	}
	return f.matchNotNames(i)
}

func (f *Finder) matchPaths(i Item) error {
	if len(f.paths) == 0 {
		return nil
	}
	if i.IsDir() {
		for _, p := range f.paths {
			if !p(i) {
				return errSkipDir
			}
		}
		return nil
	}
	match := errNoMatch
	for _, p := range f.paths {
		if p(i) {
			match = nil
		}
	}
	return match
}

func (f *Finder) matchNotPaths(i Item) error {
	if len(f.notPaths) == 0 {
		return nil
	}
	if i.IsDir() {
		for _, p := range f.notPaths {
			if p(i) {
				return errSkipDir
			}
		}
		return nil
	}
	for _, p := range f.notPaths {
		if p(i) {
			return errNoMatch
		}
	}
	return nil
}

func (f *Finder) matchNames(i Item) error {
	if len(f.names) == 0 {
		return nil
	}
	for _, n := range f.names {
		if n(i) {
			return nil
		}
	}
	return errNoMatch
}

func (f *Finder) matchNotNames(i Item) error {
	if len(f.notNames) == 0 {
		return nil
	}
	for _, n := range f.notNames {
		if n(i) {
			return errNoMatch
		}
	}
	return nil
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case nil:
			fn(item)
			return nil
		case errNoMatch:
			return nil
		case errSkipDir:
			return errSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil && err != errSkipDir {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() (ItemSlice, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<commit_msg>Define isMatch error for accepted items<commit_after>package finder

import (
	"errors"
	"os"
	"path/filepath"
	"regexp"

	"github.com/gobwas/glob"
)

type itemType uint8

const (
	typeAll itemType = iota
	typeFile
	typeDir
)

// Finder contains options for a file/directory search.
type Finder struct {
	dirs        []string
	names       []Matcher
	paths       []Matcher
	notPaths    []Matcher
	notNames    []Matcher
	setupErrors []error
	itype       itemType
}

// Matcher checks whether an Item matches.
type Matcher func(Item) bool

// New returns a new finder.
//
// By default it will search for both files and directories.
func New() *Finder {
	return &Finder{}
}

// In searches in the given list of directories.
func (f *Finder) In(directories ...string) *Finder {
	f.dirs = append(f.dirs, directories...)
	return f
}

// Path narrows down the folders to be searched using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) Path(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.paths = append(f.paths, matcher)
	}
	return f
}

// NotPath excludes folders from the search using gobwas/glob
//
// p is matched against the item's RelPath()
// See https://github.com/gobwas/glob
func (f *Finder) NotPath(p string) *Finder {
	matcher := f.path(p)
	if matcher != nil {
		f.notPaths = append(f.notPaths, matcher)
	}
	return f
}

func (f *Finder) path(p string) Matcher {
	g, err := glob.Compile(p, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		if i.IsDir() {
			return g.Match(i.RelPath())
		}
		return g.Match(filepath.Dir(i.RelPath()))
	}
}

// Name matches a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) Name(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) name(n string) Matcher {
	g, err := glob.Compile(n, os.PathSeparator)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return g.Match(i.Name())
	}
}

// NameRegex matches a file or directory name using package regexp.
func (f *Finder) NameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.names = append(f.names, matcher)
	}
	return f
}

func (f *Finder) nameRegex(n string) Matcher {
	re, err := regexp.Compile(n)
	if err != nil {
		f.setupErrors = append(f.setupErrors, err)
		return nil
	}
	return func(i Item) bool {
		return re.MatchString(i.Name())
	}
}

// NotName excludes a file or directory name using gobwas/glob
//
// See https://github.com/gobwas/glob
func (f *Finder) NotName(n string) *Finder {
	matcher := f.name(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// NotNameRegex excludes a file or directory name using package regexp.
func (f *Finder) NotNameRegex(n string) *Finder {
	matcher := f.nameRegex(n)
	if matcher != nil {
		f.notNames = append(f.notNames, matcher)
	}
	return f
}

// Files makes the finder return files only.
func (f *Finder) Files() *Finder {
	f.itype = typeFile
	return f
}

// Dirs makes the finder return directories only.
func (f *Finder) Dirs() *Finder {
	f.itype = typeDir
	return f
}

var isMatch error = nil
var errNoMatch = errors.New("Item did not match")
var errSkipDir = filepath.SkipDir

func (f *Finder) match(i Item) error {
	if (f.itype == typeDir && !i.IsDir()) || (f.itype == typeFile && i.IsDir()) {
		return errNoMatch
	}
	var match error
	match = f.matchPaths(i)
	if match != isMatch {
		return match
	}
	match = f.matchNotPaths(i)
	if match != isMatch {
		return match
	}
	match = f.matchNames(i)
	if match != isMatch {
		return match
	}
	return f.matchNotNames(i)
}

func (f *Finder) matchPaths(i Item) error {
	if len(f.paths) == 0 {
		return isMatch
	}
	if i.IsDir() {
		for _, p := range f.paths {
			if !p(i) {
				return errSkipDir
			}
		}
		return isMatch
	}
	match := errNoMatch
	for _, p := range f.paths {
		if p(i) {
			match = isMatch
		}
	}
	return match
}

func (f *Finder) matchNotPaths(i Item) error {
	if len(f.notPaths) == 0 {
		return isMatch
	}
	if i.IsDir() {
		for _, p := range f.notPaths {
			if p(i) {
				return errSkipDir
			}
		}
		return isMatch
	}
	for _, p := range f.notPaths {
		if p(i) {
			return errNoMatch
		}
	}
	return isMatch
}

func (f *Finder) matchNames(i Item) error {
	if len(f.names) == 0 {
		return isMatch
	}
	for _, n := range f.names {
		if n(i) {
			return isMatch
		}
	}
	return errNoMatch
}

func (f *Finder) matchNotNames(i Item) error {
	if len(f.notNames) == 0 {
		return isMatch
	}
	for _, n := range f.notNames {
		if n(i) {
			return errNoMatch
		}
	}
	return isMatch
}

// Each calls func fn with each found item.
func (f *Finder) Each(fn func(Item)) []error {
	var errs []error
	var dir string
	walker := func(path string, info os.FileInfo, err error) error {
		if dir == path {
			return nil
		}
		if err != nil {
			return err
		}
		relDir, err := filepath.Rel(dir, path)
		if err != nil {
			return err
		}
		item := newItem(info, dir, relDir)
		match := f.match(item)
		switch match {
		case isMatch:
			fn(item)
			return nil
		case errNoMatch:
			return nil
		case errSkipDir:
			return errSkipDir
		}
		return nil
	}
	for _, dir = range f.dirs {
		err := filepath.Walk(dir, walker)
		if err != nil && err != errSkipDir {
			errs = append(errs, err)
		}
	}
	return append(f.setupErrors, errs...)
}

// ToSlice returns a slice of all found items.
func (f *Finder) ToSlice() (ItemSlice, []error) {
	var l []Item
	errs := f.Each(func(file Item) {
		l = append(l, file)
	})
	return l, errs
}
<|endoftext|>


<commit_before>package main

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

type (
	// Flower struct represents a container which is exposing commands.
	Flower struct {
		Container *Container
		Path      string
	}

	// FlowerData struct represents a YAML file defining commands.
	FlowerData struct {
		Commands []CommandData `yaml:"commands"`
	}

	// CommandData struct represents a section in the YAML file defining a command.
	CommandData struct {
		Name    string           `yaml:"name"`
		Bin     string           `yaml:"bin"`
		Context string           `yaml:"context,omitempty"`
		User    string           `yaml:"user,omitempty"`
		Usage   string           `yaml:"usage,omitempty"`
		Help    string           `yaml:"help,omitempty"`
		Sub     []CommandSubData `yaml:"sub,omitempty"`
	}

	// CommandSubData struct represents a section in the YAML file defining
	// option/value/sub-command of a command or another option/value/sub-command.
	CommandSubData struct {
		Name  string           `yaml:"name"`
		Usage string           `yaml:"usage,omitempty"`
		Help  string           `yaml:"help,omitempty"`
		Sub   []CommandSubData `yaml:"sub,omitempty"`
	}
)

// NewFlower function instantiates a Flower.
func NewFlower(container *Container, path string) *Flower {
	return &Flower{
		Container: container,
		Path:      path,
	}
}

// Parse function retrieves data contained in a YAML file
// which path has been defined in the FLOWER_PATH
// container's environment variable.
func (flower *Flower) Parse() (*FlowerData, error) {
	command := []string{"cat", flower.Path}
	captured, err := flower.Container.Exec(command, true)
	if err != nil {
		return nil, err
	}
	flowerData := &FlowerData{}
	err = yaml.Unmarshal([]byte(captured), flowerData)
	if err != nil {
		return nil, err
	}

	return flowerData, nil
}
<commit_msg>renaming Flower structs: more explicit<commit_after>package main

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

type (
	// Flower struct represents a container which is exposing commands.
	Flower struct {
		Container *Container
		Path      string
	}

	// FlowerData struct represents a YAML file defining commands.
	FlowerData struct {
		Commands []FlowerCommandData `yaml:"commands"`
	}

	// FlowerCommandData struct represents a section in the YAML file defining a command.
	FlowerCommandData struct {
		Name    string                 `yaml:"name"`
		Bin     string                 `yaml:"bin"`
		Context string                 `yaml:"context,omitempty"`
		User    string                 `yaml:"user,omitempty"`
		Usage   string                 `yaml:"usage,omitempty"`
		Help    string                 `yaml:"help,omitempty"`
		Sub     []FlowerCommandSubData `yaml:"sub,omitempty"`
	}

	// FlowerCommandSubData struct represents a section in the YAML file defining
	// option/value/sub-command of a command or another option/value/sub-command.
	FlowerCommandSubData struct {
		Name  string                 `yaml:"name"`
		Usage string                 `yaml:"usage,omitempty"`
		Help  string                 `yaml:"help,omitempty"`
		Sub   []FlowerCommandSubData `yaml:"sub,omitempty"`
	}
)

// NewFlower function instantiates a Flower.
func NewFlower(container *Container, path string) *Flower {
	return &Flower{
		Container: container,
		Path:      path,
	}
}

// Parse function retrieves data contained in a YAML file
// which path has been defined in the FLOWER_PATH
// container's environment variable.
func (flower *Flower) Parse() (*FlowerData, error) {
	command := []string{"cat", flower.Path}
	captured, err := flower.Container.Exec(command, true)
	if err != nil {
		return nil, err
	}
	flowerData := &FlowerData{}
	err = yaml.Unmarshal([]byte(captured), flowerData)
	if err != nil {
		return nil, err
	}

	return flowerData, nil
}
<|endoftext|>


<commit_before>package writer

import (
	"os"
	"bytes"
	"crypto/sha1"
	"fmt"
	"time"
	"io"
	"github.com/tsileo/silokv/rolling"
	"github.com/garyburd/redigo/redis"
)

func GetDbPool() (pool *redis.Pool, err error) {
	pool = &redis.Pool{
		MaxIdle:     50,
		MaxActive: 50,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", "localhost:9736")
			if err != nil {
				return nil, err
			}
			//if _, err := c.Do("AUTH", password); err != nil {
			//    c.Close()
			//    return nil, err
			//}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
	return
}

func SHA1(data []byte) string {
	h := sha1.New()
	h.Write(data)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func FileWriter(key, path string) (*WriteResult, error) {
	writeResult := &WriteResult{}
	window := 64
	rs := rolling.New(window)
	f, err := os.Open(path)
	if err != nil {
		return writeResult, err
	}
	rpool, _ := GetDbPool()
	con := rpool.Get()
	defer con.Close()
	var buf bytes.Buffer
	fullHash := sha1.New()
	eof := false
	for {
		b := make([]byte, 1)
		_, err := f.Read(b)
		if err == io.EOF {
			eof = true
		}
		rs.Write(b)
		buf.Write(b)
		onSplit := rs.OnSplit()
		if (onSplit && (buf.Len() > 64 << 10)) || buf.Len() >= 1 << 20 || eof {
			nsha := SHA1(buf.Bytes())
			ndata := string(buf.Bytes())
			fullHash.Write(buf.Bytes())
			exists, err := redis.Bool(con.Do("BEXISTS", nsha))
			if err != nil {
				panic(fmt.Sprintf("DB error: %v", err))
			}
			if !exists {
				rsha, err := redis.String(con.Do("BPUT", ndata))
				if err != nil {
					panic(fmt.Sprintf("DB error: %v", err))
				}
				writeResult.UploadedCnt++
				writeResult.UploadedSize += buf.Len()
				if rsha != nsha {
					panic(fmt.Sprintf("Corrupted data: %+v/%+v", rsha, nsha))
				}
			} else {
				writeResult.SkippedSize += buf.Len()
				writeResult.SkippedCnt++
			}
			con.Do("BPADD", key, writeResult.BlobsCnt, nsha)
			writeResult.Size += buf.Len()
			buf.Reset()
			writeResult.BlobsCnt++
		}
		if eof {
			break
		}
	}
	writeResult.Hash = fmt.Sprintf("%x", fullHash.Sum(nil))
	return writeResult, nil
}
<commit_msg>Bugfix filewriter EOF<commit_after>package writer

import (
	"os"
	"bytes"
	"crypto/sha1"
	"fmt"
	"time"
	"io"
	"bufio"
	"github.com/tsileo/silokv/rolling"
	"github.com/garyburd/redigo/redis"
	"log"
)

func GetDbPool() (pool *redis.Pool, err error) {
	pool = &redis.Pool{
		MaxIdle:     50,
		MaxActive: 50,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", "localhost:9736")
			if err != nil {
				return nil, err
			}
			//if _, err := c.Do("AUTH", password); err != nil {
			//    c.Close()
			//    return nil, err
			//}
			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}
	return
}

func SHA1(data []byte) string {
	h := sha1.New()
	h.Write(data)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func FullSHA1(path string) string {
	f, _ := os.Open(path)
	defer f.Close()
	//reader := bufio.NewReader(f)
	//log.Printf("%+v", reader)
	h := sha1.New()
	w, _ := io.Copy(h, f)
	//log.Printf("%+v", reader)
	log.Printf("w:%v", w)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func FileWriter(key, path string) (*WriteResult, error) {
	writeResult := &WriteResult{}
	window := 64
	rs := rolling.New(window)
	log.Printf("H:%v", FullSHA1(path))
	f, err := os.Open(path)
	defer f.Close()
	if err != nil {
		return writeResult, err
	}
	freader := bufio.NewReader(f)
	rpool, _ := GetDbPool()
	con := rpool.Get()
	defer con.Close()
	var buf bytes.Buffer
	buf.Reset()
	fullHash := sha1.New()
	eof := false
	for {
		b := make([]byte, 1)
		_, err := freader.Read(b)
		if err == io.EOF {
			eof = true
		} else {
			rs.Write(b)
			buf.Write(b)
		}
		onSplit := rs.OnSplit()
		if (onSplit && (buf.Len() > 64 << 10)) || buf.Len() >= 1 << 20 || eof {
			nsha := SHA1(buf.Bytes())
			ndata := string(buf.Bytes())
			fullHash.Write(buf.Bytes())
			exists, err := redis.Bool(con.Do("BEXISTS", nsha))
			if err != nil {
				panic(fmt.Sprintf("DB error: %v", err))
			}
			if !exists {
				rsha, err := redis.String(con.Do("BPUT", ndata))
				if err != nil {
					panic(fmt.Sprintf("DB error: %v", err))
				}
				writeResult.UploadedCnt++
				writeResult.UploadedSize += buf.Len()
				if rsha != nsha {
					panic(fmt.Sprintf("Corrupted data: %+v/%+v", rsha, nsha))
				}
			} else {
				writeResult.SkippedSize += buf.Len()
				writeResult.SkippedCnt++
			}
			con.Do("BPADD", key, writeResult.BlobsCnt, nsha)
			writeResult.Size += buf.Len()
			buf.Reset()
			writeResult.BlobsCnt++
		}
		if eof {
			break
		}
	}
	writeResult.Hash = fmt.Sprintf("%x", fullHash.Sum(nil))
	return writeResult, nil
}
<|endoftext|>


<commit_before>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	state           errorcode.ErrorCode
	reader          *bufio.Reader
	currentPosition *Position
	positionBuffer  []*Position

	currentLine   int
	currentColumn int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		state:          errorcode.E_OK,
		reader:         bufio.NewReader(r),
		positionBuffer: make([]*Position, 0),
		currentLine:    1,
		currentColumn:  0,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) NextToken() *token.Token {
	positions := NewPositions()

	pos := scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
		return positions.AsToken(token.NAME)
	case ch == '\n':
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			// Parse Number
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<commit_msg>add skeleton for async/await<commit_after>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	state           errorcode.ErrorCode
	reader          *bufio.Reader
	currentPosition *Position
	positionBuffer  []*Position

	currentLine   int
	currentColumn int

	asyncDef bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		state:          errorcode.E_OK,
		reader:         bufio.NewReader(r),
		positionBuffer: make([]*Position, 0),
		currentLine:    1,
		currentColumn:  0,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) NextToken() *token.Token {
	positions := NewPositions()

	pos := scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		// Check for async/await
		// literal := positions.String()
		// if literal == "async" || literal == "await" {
		// 	if scanner.asyncDef {
		// 		switch literal {
		// 		case "async":
		// 			return positions.AsToken(token.ASYNC)
		// 		case "await":
		// 			return positions.AsToken(token.AWAIT)
		// 		}
		// 	} else if literal == "async" {
		// 		nextToken := scanner.NextToken()
		// 		if nextToken.ID == token.NAME && nextToken.Literal == "def" {
		// 			scanner.asyncDef = true
		// 			return positions.AsToken(token.ASYNC)
		// 		}
		// 	}
		// }

		return positions.AsToken(token.NAME)
	case ch == '\n':
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			// Parse Number
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<|endoftext|>


<commit_before>package fridge

import (
	"errors"
	"github.com/shomali11/eventbus"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Refresh is when an item was restocked with a fresher one
	Refresh = "REFRESH"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	fridgeEventbusTopic   = "fridge_events"
	invalidDurationsError = "Invalid 'best by' and 'use by' durations"
)

// NewClient returns a client
func NewClient(redisClient *RedisClient, options ...DefaultsOption) *Client {
	client := &Client{
		defaults: newDefaults(options...),
		dao:      newDao(redisClient),
	}

	bus := eventbus.NewClient()
	bus.Subscribe(fridgeEventbusTopic, func(value interface{}) {
		event, ok := value.(*Event)
		if !ok {
			return
		}

		if client.handleEvent == nil {
			return
		}
		client.handleEvent(event)
	})

	client.bus = bus
	return client
}

// Fridge event
type Event struct {
	Key  string
	Type string
}

// Client fridge client
type Client struct {
	defaults    *Defaults
	dao         *Dao
	bus         *eventbus.Client
	handleEvent func(event *Event)
}

// Put an item
func (c *Client) Put(key string, value string, options ...StorageOption) error {
	storageDetails := newStorageDetails(c.defaults, options...)
	if storageDetails.BestBy > storageDetails.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return err
	}

	err = c.dao.Set(key, value, storageDetails.GetUseByInSeconds())
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...RetrievalOption) (string, bool, error) {
	retrievalDetails := newRetrievalDetails(options...)
	restock := retrievalDetails.Restock

	storageDetails, found, err := c.dao.GetStorageDetails(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, NotFound)
		return empty, false, err
	}

	cachedValue, found, err := c.dao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, Expired)
		return c.restockAndCompare(key, cachedValue, restock)
	}

	now := time.Now().UTC()
	if now.Before(storageDetails.Timestamp.Add(storageDetails.BestBy)) {
		c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(storageDetails.Timestamp.Add(storageDetails.UseBy)) {
		c.publish(key, Cold)
		go c.restockAndCompare(key, cachedValue, restock)
		return cachedValue, true, nil
	}

	c.publish(key, Expired)
	return c.restockAndCompare(key, cachedValue, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.dao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.dao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	c.bus.Close()
	return c.dao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.handleEvent = handleEvent
}

func (c *Client) publish(key string, eventType string) {
	c.bus.Publish(fridgeEventbusTopic, &Event{Key: key, Type: eventType})
}

func (c *Client) restockAndCompare(key string, cachedValue string, callback func() (string, error)) (string, bool, error) {
	newValue, found, err := c.restock(key, callback)
	if err != nil || !found {
		return empty, found, err
	}

	if newValue == cachedValue {
		go c.publish(key, Unchanged)
	}
	return newValue, true, nil
}

func (c *Client) restock(key string, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		go c.publish(key, OutOfStock)
		return empty, false, nil
	}

	result, err := callback()
	if err != nil {
		return empty, false, err
	}

	go c.publish(key, Refresh)

	bestBy, useBy := c.getDurations(key)
	err = c.Put(key, result, WithDurations(bestBy, useBy))
	if err != nil {
		return empty, false, err
	}
	return result, true, nil
}

func (c *Client) getDurations(key string) (time.Duration, time.Duration) {
	bestBy := c.defaults.BestBy
	useBy := c.defaults.UseBy

	itemConfig, found, err := c.dao.GetStorageDetails(key)
	if found && err == nil {
		bestBy = itemConfig.BestBy
		useBy = itemConfig.UseBy
	}
	return bestBy, useBy
}
<commit_msg>Clean up<commit_after>package fridge

import (
	"errors"
	"github.com/shomali11/eventbus"
	"time"
)

const (
	// Fresh is when an item has not passed its "Best By" duration
	Fresh = "FRESH"

	// Cold is when an item has passed its "Best By" duration but not its "Use By" one
	Cold = "COLD"

	// Expired is when an item has passed its "Use By" duration
	Expired = "EXPIRED"

	// NotFound is when an item was not found due to being removed or was never stored before
	NotFound = "NOT_FOUND"

	// Refresh is when an item was restocked with a fresher one
	Refresh = "REFRESH"

	// OutOfStock is when an item needs restocking, but no restocking function was provided
	OutOfStock = "OUT_OF_STOCK"

	// Unchanged is when the restocked item is not different from the version in the cache
	Unchanged = "UNCHANGED"
)

const (
	empty                 = ""
	eventsTopic           = "fridge_events"
	invalidDurationsError = "Invalid 'best by' and 'use by' durations"
)

// NewClient returns a client
func NewClient(redisClient *RedisClient, options ...DefaultsOption) *Client {
	client := &Client{
		defaults: newDefaults(options...),
		dao:      newDao(redisClient),
	}

	bus := eventbus.NewClient()
	bus.Subscribe(eventsTopic, func(value interface{}) {
		event, ok := value.(*Event)
		if !ok {
			return
		}

		if client.handleEvent == nil {
			return
		}
		client.handleEvent(event)
	})

	client.bus = bus
	return client
}

// Fridge event
type Event struct {
	Key  string
	Type string
}

// Client fridge client
type Client struct {
	defaults    *Defaults
	dao         *Dao
	bus         *eventbus.Client
	handleEvent func(event *Event)
}

// Put an item
func (c *Client) Put(key string, value string, options ...StorageOption) error {
	storageDetails := newStorageDetails(c.defaults, options...)
	if storageDetails.BestBy > storageDetails.UseBy {
		return errors.New(invalidDurationsError)
	}

	err := c.dao.SetStorageDetails(key, storageDetails)
	if err != nil {
		return err
	}

	err = c.dao.Set(key, value, storageDetails.GetUseByInSeconds())
	if err != nil {
		return err
	}
	return nil
}

// Get an item
func (c *Client) Get(key string, options ...RetrievalOption) (string, bool, error) {
	retrievalDetails := newRetrievalDetails(options...)
	restock := retrievalDetails.Restock

	storageDetails, found, err := c.dao.GetStorageDetails(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, NotFound)
		return empty, false, err
	}

	cachedValue, found, err := c.dao.Get(key)
	if err != nil {
		return empty, false, err
	}

	if !found {
		c.publish(key, Expired)
		return c.restockAndCompare(key, cachedValue, restock)
	}

	now := time.Now().UTC()
	if now.Before(storageDetails.Timestamp.Add(storageDetails.BestBy)) {
		c.publish(key, Fresh)
		return cachedValue, true, nil
	}

	if now.Before(storageDetails.Timestamp.Add(storageDetails.UseBy)) {
		c.publish(key, Cold)
		go c.restockAndCompare(key, cachedValue, restock)
		return cachedValue, true, nil
	}

	c.publish(key, Expired)
	return c.restockAndCompare(key, cachedValue, restock)
}

// Remove an item
func (c *Client) Remove(key string) error {
	return c.dao.Remove(key)
}

// Ping pings redis
func (c *Client) Ping() error {
	return c.dao.Ping()
}

// Close closes resources
func (c *Client) Close() error {
	c.bus.Close()
	return c.dao.Close()
}

// HandleEvent overrides the default handleEvent callback
func (c *Client) HandleEvent(handleEvent func(event *Event)) {
	c.handleEvent = handleEvent
}

func (c *Client) publish(key string, eventType string) {
	c.bus.Publish(eventsTopic, &Event{Key: key, Type: eventType})
}

func (c *Client) restockAndCompare(key string, cachedValue string, callback func() (string, error)) (string, bool, error) {
	newValue, found, err := c.restock(key, callback)
	if err != nil || !found {
		return empty, found, err
	}

	if newValue == cachedValue {
		go c.publish(key, Unchanged)
	}
	return newValue, true, nil
}

func (c *Client) restock(key string, callback func() (string, error)) (string, bool, error) {
	if callback == nil {
		go c.publish(key, OutOfStock)
		return empty, false, nil
	}

	result, err := callback()
	if err != nil {
		return empty, false, err
	}

	go c.publish(key, Refresh)

	bestBy, useBy := c.getDurations(key)
	err = c.Put(key, result, WithDurations(bestBy, useBy))
	if err != nil {
		return empty, false, err
	}
	return result, true, nil
}

func (c *Client) getDurations(key string) (time.Duration, time.Duration) {
	bestBy := c.defaults.BestBy
	useBy := c.defaults.UseBy

	itemConfig, found, err := c.dao.GetStorageDetails(key)
	if found && err == nil {
		bestBy = itemConfig.BestBy
		useBy = itemConfig.UseBy
	}
	return bestBy, useBy
}
<|endoftext|>


<commit_before>package routing_table_test

import (
	"github.com/cloudfoundry-incubator/receptor"
	. "github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("ByProcessGuid", func() {
	Describe("RoutesByProcessGuidFromDesireds", func() {
		It("should build a map of routes", func() {
			abcRoutes := cc_messages.NewRoutingInfo([]string{"foo.com", "bar.com"}, 8080)
			defRoutes := cc_messages.NewRoutingInfo([]string{"baz.com"}, 8080)

			routes := RoutesByProcessGuidFromDesireds([]receptor.DesiredLRPResponse{
				{Domain: "tests", ProcessGuid: "abc", Routes: abcRoutes, LogGuid: "abc-guid"},
				{Domain: "tests", ProcessGuid: "def", Routes: defRoutes, LogGuid: "def-guid"},
			})

			Ω(routes).Should(HaveLen(2))
			Ω(routes["abc"].URIs).Should(Equal([]string{"foo.com", "bar.com"}))
			Ω(routes["def"].URIs).Should(Equal([]string{"baz.com"}))
			Ω(routes["abc"].LogGuid).Should(Equal("abc-guid"))
			Ω(routes["def"].LogGuid).Should(Equal("def-guid"))
		})
	})

	Describe("ContainersByProcessGuidFromActuals", func() {
		It("should build a map of containers, ignoring those without ports", func() {
			containers := ContainersByProcessGuidFromActuals([]receptor.ActualLRPResponse{
				{ProcessGuid: "abc", Index: 1, Domain: "domain", Address: "1.1.1.1", Ports: []receptor.PortMapping{{HostPort: 11}}},
				{ProcessGuid: "abc", Index: 1, Domain: "domain", Address: "2.2.2.2", Ports: []receptor.PortMapping{{HostPort: 22}}},
				{ProcessGuid: "def", Index: 1, Domain: "domain", Address: "3.3.3.3", Ports: []receptor.PortMapping{{HostPort: 33}}},
				{ProcessGuid: "def", Index: 1, Domain: "domain", Address: "4.4.4.4", Ports: nil},
			})

			Ω(containers).Should(HaveLen(2))
			Ω(containers["abc"]).Should(HaveLen(2))
			Ω(containers["abc"]).Should(ContainElement(Container{Host: "1.1.1.1", Port: 11}))
			Ω(containers["abc"]).Should(ContainElement(Container{Host: "2.2.2.2", Port: 22}))
			Ω(containers["def"]).Should(HaveLen(1))
			Ω(containers["def"]).Should(ContainElement(Container{Host: "3.3.3.3", Port: 33}))
		})
	})
})
<commit_msg>Add test for nil routes in RoutingInfo<commit_after>package routing_table_test

import (
	"github.com/cloudfoundry-incubator/receptor"
	. "github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/cc_messages"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("ByProcessGuid", func() {
	Describe("RoutesByProcessGuidFromDesireds", func() {
		It("should build a map of routes", func() {
			abcRoutes := cc_messages.NewRoutingInfo([]string{"foo.com", "bar.com"}, 8080)
			defRoutes := cc_messages.NewRoutingInfo([]string{"baz.com"}, 8080)

			routes := RoutesByProcessGuidFromDesireds([]receptor.DesiredLRPResponse{
				{Domain: "tests", ProcessGuid: "abc", Routes: abcRoutes, LogGuid: "abc-guid"},
				{Domain: "tests", ProcessGuid: "def", Routes: defRoutes, LogGuid: "def-guid"},
			})

			Ω(routes).Should(HaveLen(2))
			Ω(routes["abc"].URIs).Should(Equal([]string{"foo.com", "bar.com"}))
			Ω(routes["def"].URIs).Should(Equal([]string{"baz.com"}))
			Ω(routes["abc"].LogGuid).Should(Equal("abc-guid"))
			Ω(routes["def"].LogGuid).Should(Equal("def-guid"))
		})

		Context("when the routing info is nil", func() {
			It("should return an empty hostname list", func() {
				routes := RoutesByProcessGuidFromDesireds([]receptor.DesiredLRPResponse{
					{Domain: "tests", ProcessGuid: "abc", Routes: nil, LogGuid: "abc-guid"},
				})
				Ω(routes).Should(HaveLen(1))
				Ω(routes["abc"].URIs).Should(BeNil())
			})
		})
	})

	Describe("ContainersByProcessGuidFromActuals", func() {
		It("should build a map of containers, ignoring those without ports", func() {
			containers := ContainersByProcessGuidFromActuals([]receptor.ActualLRPResponse{
				{ProcessGuid: "abc", Index: 1, Domain: "domain", Address: "1.1.1.1", Ports: []receptor.PortMapping{{HostPort: 11}}},
				{ProcessGuid: "abc", Index: 1, Domain: "domain", Address: "2.2.2.2", Ports: []receptor.PortMapping{{HostPort: 22}}},
				{ProcessGuid: "def", Index: 1, Domain: "domain", Address: "3.3.3.3", Ports: []receptor.PortMapping{{HostPort: 33}}},
				{ProcessGuid: "def", Index: 1, Domain: "domain", Address: "4.4.4.4", Ports: nil},
			})

			Ω(containers).Should(HaveLen(2))
			Ω(containers["abc"]).Should(HaveLen(2))
			Ω(containers["abc"]).Should(ContainElement(Container{Host: "1.1.1.1", Port: 11}))
			Ω(containers["abc"]).Should(ContainElement(Container{Host: "2.2.2.2", Port: 22}))
			Ω(containers["def"]).Should(HaveLen(1))
			Ω(containers["def"]).Should(ContainElement(Container{Host: "3.3.3.3", Port: 33}))
		})
	})
})
<|endoftext|>


<commit_before>package dispatcher

import (
	"errors"
	"log"

	"github.com/abhinavdahiya/go-messenger-bot"
)

type Storage interface {
	StoreState(mbotapi.User, State) error
	FetchState(mbotapi.User) (State, error)
}

type Dispatcher struct {
	States map[string]State
	Storage
	InitState string
	Debug     bool
}

func NewDispatcher() *Dispatcher {
	return &Dispatcher{
		States: make(map[string]State),
		Storage: &InMemory{
			Store: make(map[mbotapi.User]State),
		},
		InitState: "start",
		Debug:     false,
	}
}

func (d *Dispatcher) AddState(s State) {
	d.States[s.Name()] = s
}

func (d *Dispatcher) LoadState(name string) (State, error) {
	if s, ok := d.States[name]; ok {
		return s, nil
	}
	return nil, errors.New("Unknown State.")
}

func (d *Dispatcher) Process(c mbotapi.Callback, bot *mbotapi.BotAPI) error {
	// fetch the current state of the user
	curr, err := d.FetchState(c.Sender)
	if err != nil {
		tmp, _ := d.LoadState(d.InitState)
		d.StoreState(c.Sender, tmp)
		curr = tmp
	}

	if d.Debug {
		log.Printf("[DEBUG] %#v", curr)
		log.Printf("[DEBUG] [CTX] %#v", GetCTX(&curr))
	}

	var cLeave Action
	_, cLeave = curr.Actions()

	// exec Leave action for the state
	if cLeave != nil {
		err := cLeave(curr, c, bot)
		if err != nil {
			return err
		}
	}

	// load next state
	ns := curr.Next()

	// If next state is empty
	// move to initial state
	// Should you process the message through InitState??
	if ns == "" {
		tmp, _ := d.LoadState(d.InitState)
		var tLeave Action
		_, tLeave = tmp.Actions()

		if tLeave != nil {
			err := tLeave(curr, c, bot)
			if err != nil {
				return err
			}
		}
		ns = tmp.Next()
	}

	var next State
	next, err = d.LoadState(ns)
	if err != nil {
		return err
	}
	var nEnter Action
	nEnter, _ = next.Actions()

	// load the next state context
	if ctx := GetCTX(&curr); ctx != nil {
		SetCTX(&next, ctx)
	}
	next.Flush()

	if d.Debug {
		log.Printf("[DEBUG] %#v", next)
		log.Printf("[DEBUG] [CTX] %#v", GetCTX(&next))
	}

	if nEnter != nil {
		err = nEnter(next, c, bot)
		if err != nil {
			return err
		}
	}

	d.StoreState(c.Sender, next)

	if d.Debug {
		log.Printf("[DEBUG] %#v", d.Storage)
	}
	return nil
}
<commit_msg>debug<commit_after>package dispatcher

import (
	"errors"
	"log"

	"github.com/abhinavdahiya/go-messenger-bot"
)

type Storage interface {
	StoreState(mbotapi.User, State) error
	FetchState(mbotapi.User) (State, error)
}

type Dispatcher struct {
	States map[string]State
	Storage
	InitState string
	Debug     bool
}

func NewDispatcher() *Dispatcher {
	return &Dispatcher{
		States: make(map[string]State),
		Storage: &InMemory{
			Store: make(map[mbotapi.User]State),
		},
		InitState: "start",
		Debug:     false,
	}
}

func (d *Dispatcher) AddState(s State) {
	d.States[s.Name()] = s
}

func (d *Dispatcher) LoadState(name string) (State, error) {
	if s, ok := d.States[name]; ok {
		return s, nil
	}
	return nil, errors.New("Unknown State.")
}

func (d *Dispatcher) Process(c mbotapi.Callback, bot *mbotapi.BotAPI) error {
	// fetch the current state of the user
	curr, err := d.FetchState(c.Sender)
	if err != nil {
		tmp, _ := d.LoadState(d.InitState)
		d.StoreState(c.Sender, tmp)
		curr = tmp
	}

	if d.Debug {
		log.Printf("[DEBUG] %#v", curr)
		log.Printf("[DEBUG] [CTX] %#v", GetCTX(&curr))
	}

	var cLeave Action
	_, cLeave = curr.Actions()

	// exec Leave action for the state
	if cLeave != nil {
		err := cLeave(curr, c, bot)
		if err != nil {
			return err
		}
	}

	// load next state
	ns := curr.Next()
	if d.Debug {
		log.Printf("[DEBUG] Next State: %s", ns)
	}

	// If next state is empty
	// move to initial state
	// Should you process the message through InitState??
	if ns == "" {
		tmp, _ := d.LoadState(d.InitState)
		var tLeave Action
		_, tLeave = tmp.Actions()

		if tLeave != nil {
			err := tLeave(curr, c, bot)
			if err != nil {
				return err
			}
		}
		ns = tmp.Next()
		if d.Debug {
			log.Printf("[DEBUG] [Init] Next State: %s", ns)
		}
	}

	var next State
	next, err = d.LoadState(ns)
	if err != nil {
		return err
	}
	var nEnter Action
	nEnter, _ = next.Actions()

	// load the next state context
	if ctx := GetCTX(&curr); ctx != nil {
		SetCTX(&next, ctx)
	}
	next.Flush()

	if d.Debug {
		log.Printf("[DEBUG] %#v", next)
		log.Printf("[DEBUG] [CTX] %#v", GetCTX(&next))
	}

	if nEnter != nil {
		err = nEnter(next, c, bot)
		if err != nil {
			return err
		}
	}

	d.StoreState(c.Sender, next)

	if d.Debug {
		log.Printf("[DEBUG] %#v", d.Storage)
	}
	return nil
}
<|endoftext|>