<commit_before>package repo

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/schema"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

const RepoVersion = "15"

var log = logging.MustGetLogger("repo")
var ErrRepoExists = errors.New("IPFS configuration file exists. Reinitializing would overwrite your keys. Use -f to force overwrite.")

func DoInit(repoRoot string, nBitsForKeypair int, testnet bool, password string, mnemonic string, creationDate time.Time, dbInit func(string, []byte, string, time.Time) error) error {
	nodeSchema, err := schema.NewCustomSchemaManager(schema.SchemaContext{
		DataPath:        repoRoot,
		TestModeEnabled: testnet,
		Mnemonic:        mnemonic,
	})
	if err != nil {
		return err
	}
	if nodeSchema.BuildSchemaDirectories(); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		err := MigrateUp(repoRoot, password, testnet)
		if err != nil {
			return err
		}
		return ErrRepoExists
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf := schema.MustDefaultConfig()

	if mnemonic == "" {
		mnemonic, err = createMnemonic(bip39.NewEntropy, bip39.NewMnemonic)
		if err != nil {
			return err
		}
	}
	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("Generating Ed25519 keypair...")
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("Done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("Initializing OpenBazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password, creationDate); err != nil {
		return err
	}

	f, err := os.Create(path.Join(repoRoot, "repover"))
	if err != nil {
		return err
	}
	_, werr := f.Write([]byte(RepoVersion))
	if werr != nil {
		return werr
	}
	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// Directory exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("Unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// Directory does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("Cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	var (
		ws = schema.WalletsConfig{
			BTC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://btc.bloqapi.net/insight-api",
				APITestnet:       "https://test-insight.bitpay.com/api",
				MaxFee:           200,
				FeeAPI:           "https://btc.fees.openbazaar.org",
				HighFeeDefault:   50,
				MediumFeeDefault: 10,
				LowFeeDefault:    1,
			},
			BCH: &schema.CoinConfig{
				Type:             "API",
				API:              "https://bch-insight.bitpay.com/api",
				APITestnet:       "https://test-bch-insight.bitpay.com/api",
				MaxFee:           200,
				HighFeeDefault:   10,
				MediumFeeDefault: 5,
				LowFeeDefault:    1,
			},
			LTC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://insight.litecore.io/api",
				APITestnet:       "https://testnet.litecore.io/api",
				MaxFee:           200,
				HighFeeDefault:   20,
				MediumFeeDefault: 10,
				LowFeeDefault:    5,
			},
			ZEC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://zcashnetwork.info/api",
				APITestnet:       "https://explorer.testnet.z.cash/api",
				MaxFee:           200,
				HighFeeDefault:   20,
				MediumFeeDefault: 10,
				LowFeeDefault:    5,
			},
			ETH: &schema.CoinConfig{
				Type:             "API",
				API:              "https://rinkeby.infura.io",
				APITestnet:       "https://rinkeby.infura.io",
				MaxFee:           200,
				HighFeeDefault:   30,
				MediumFeeDefault: 15,
				LowFeeDefault:    7,
				CustomConfig: map[string]string{
					"RegistryAddress":        "0xab8dd0e05b73529b440d9c9df00b5f490c8596ff",
					"RinkebyRegistryAddress": "0xab8dd0e05b73529b440d9c9df00b5f490c8596ff",
					"RopstenRegistryAddress": "0x029d6a0cd4ce98315690f4ea52945545d9c0f460",
				},
			},
		}

		a = schema.APIConfig{
			Enabled:     true,
			AllowedIPs:  []string{},
			HTTPHeaders: nil,
		}

		ds = schema.DataSharing{
			AcceptStoreRequests: false,
			PushTo:              schema.DataPushNodes,
		}

		t = schema.TorConfig{}

		resolvers = schema.ResolverConfig{
			Id: "https://resolver.onename.com/",
		}
	)
	if err := r.SetConfigKey("Wallets", ws); err != nil {
		return err
	}
	if err := r.SetConfigKey("DataSharing", ds); err != nil {
		return err
	}
	if err := r.SetConfigKey("Resolvers", resolvers); err != nil {
		return err
	}
	if err := r.SetConfigKey("Bootstrap-testnet", schema.BootstrapAddressesTestnet); err != nil {
		return err
	}
	if err := r.SetConfigKey("Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := r.SetConfigKey("RepublishInterval", "24h"); err != nil {
		return err
	}
	if err := r.SetConfigKey("JSON-API", a); err != nil {
		return err
	}
	if err := r.SetConfigKey("Tor-config", t); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic(newEntropy func(int) ([]byte, error), newMnemonic func([]byte) (string, error)) (string, error) {
	entropy, err := newEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := newMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}

/* Returns the directory to store repo data in.
   It depends on the OS and whether or not we are on testnet. */
func GetRepoPath(isTestnet bool) (string, error) {
	paths, err := schema.NewCustomSchemaManager(schema.SchemaContext{
		TestModeEnabled: isTestnet,
	})
	if err != nil {
		return "", err
	}
	return paths.DataPath(), nil
}
<commit_msg>change registry address<commit_after>package repo

import (
	"context"
	"errors"
	"fmt"
	"os"
	"path"
	"time"

	"github.com/OpenBazaar/openbazaar-go/ipfs"
	"github.com/OpenBazaar/openbazaar-go/schema"
	"github.com/ipfs/go-ipfs/core"
	"github.com/ipfs/go-ipfs/namesys"
	"github.com/ipfs/go-ipfs/repo/fsrepo"
	"github.com/op/go-logging"
	"github.com/tyler-smith/go-bip39"
)

const RepoVersion = "15"

var log = logging.MustGetLogger("repo")
var ErrRepoExists = errors.New("IPFS configuration file exists. Reinitializing would overwrite your keys. Use -f to force overwrite.")

func DoInit(repoRoot string, nBitsForKeypair int, testnet bool, password string, mnemonic string, creationDate time.Time, dbInit func(string, []byte, string, time.Time) error) error {
	nodeSchema, err := schema.NewCustomSchemaManager(schema.SchemaContext{
		DataPath:        repoRoot,
		TestModeEnabled: testnet,
		Mnemonic:        mnemonic,
	})
	if err != nil {
		return err
	}
	if nodeSchema.BuildSchemaDirectories(); err != nil {
		return err
	}

	if fsrepo.IsInitialized(repoRoot) {
		err := MigrateUp(repoRoot, password, testnet)
		if err != nil {
			return err
		}
		return ErrRepoExists
	}

	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

	conf := schema.MustDefaultConfig()

	if mnemonic == "" {
		mnemonic, err = createMnemonic(bip39.NewEntropy, bip39.NewMnemonic)
		if err != nil {
			return err
		}
	}
	seed := bip39.NewSeed(mnemonic, "Secret Passphrase")
	fmt.Printf("Generating Ed25519 keypair...")
	identityKey, err := ipfs.IdentityKeyFromSeed(seed, nBitsForKeypair)
	if err != nil {
		return err
	}
	fmt.Printf("Done\n")

	identity, err := ipfs.IdentityFromKey(identityKey)
	if err != nil {
		return err
	}

	log.Infof("Initializing OpenBazaar node at %s\n", repoRoot)
	if err := fsrepo.Init(repoRoot, conf); err != nil {
		return err
	}
	conf.Identity = identity

	if err := addConfigExtensions(repoRoot); err != nil {
		return err
	}

	if err := dbInit(mnemonic, identityKey, password, creationDate); err != nil {
		return err
	}

	f, err := os.Create(path.Join(repoRoot, "repover"))
	if err != nil {
		return err
	}
	_, werr := f.Write([]byte(RepoVersion))
	if werr != nil {
		return werr
	}
	return initializeIpnsKeyspace(repoRoot, identityKey)
}

func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// Directory exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("Unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// Directory does not exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("Cannot write to %s, incorrect permissions", err)
	}

	return err
}

func initializeIpnsKeyspace(repoRoot string, privKeyBytes []byte) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	cfg, err := r.Config()
	if err != nil {
		log.Error(err)
		return err
	}
	identity, err := ipfs.IdentityFromKey(privKeyBytes)
	if err != nil {
		return err
	}

	cfg.Identity = identity
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

	return namesys.InitializeKeyspace(ctx, nd.Namesys, nd.Pinning, nd.PrivateKey)
}

func addConfigExtensions(repoRoot string) error {
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
		return err
	}
	var (
		ws = schema.WalletsConfig{
			BTC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://btc.bloqapi.net/insight-api",
				APITestnet:       "https://test-insight.bitpay.com/api",
				MaxFee:           200,
				FeeAPI:           "https://btc.fees.openbazaar.org",
				HighFeeDefault:   50,
				MediumFeeDefault: 10,
				LowFeeDefault:    1,
			},
			BCH: &schema.CoinConfig{
				Type:             "API",
				API:              "https://bch-insight.bitpay.com/api",
				APITestnet:       "https://test-bch-insight.bitpay.com/api",
				MaxFee:           200,
				HighFeeDefault:   10,
				MediumFeeDefault: 5,
				LowFeeDefault:    1,
			},
			LTC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://insight.litecore.io/api",
				APITestnet:       "https://testnet.litecore.io/api",
				MaxFee:           200,
				HighFeeDefault:   20,
				MediumFeeDefault: 10,
				LowFeeDefault:    5,
			},
			ZEC: &schema.CoinConfig{
				Type:             "API",
				API:              "https://zcashnetwork.info/api",
				APITestnet:       "https://explorer.testnet.z.cash/api",
				MaxFee:           200,
				HighFeeDefault:   20,
				MediumFeeDefault: 10,
				LowFeeDefault:    5,
			},
			ETH: &schema.CoinConfig{
				Type:             "API",
				API:              "https://rinkeby.infura.io",
				APITestnet:       "https://rinkeby.infura.io",
				MaxFee:           200,
				HighFeeDefault:   30,
				MediumFeeDefault: 15,
				LowFeeDefault:    7,
				CustomConfig: map[string]string{
					"RegistryAddress":        "0x403d907982474cdd51687b09a8968346159378f3",
					"RinkebyRegistryAddress": "0x403d907982474cdd51687b09a8968346159378f3",
					"RopstenRegistryAddress": "0x403d907982474cdd51687b09a8968346159378f3",
				},
			},
		}

		a = schema.APIConfig{
			Enabled:     true,
			AllowedIPs:  []string{},
			HTTPHeaders: nil,
		}

		ds = schema.DataSharing{
			AcceptStoreRequests: false,
			PushTo:              schema.DataPushNodes,
		}

		t = schema.TorConfig{}

		resolvers = schema.ResolverConfig{
			Id: "https://resolver.onename.com/",
		}
	)
	if err := r.SetConfigKey("Wallets", ws); err != nil {
		return err
	}
	if err := r.SetConfigKey("DataSharing", ds); err != nil {
		return err
	}
	if err := r.SetConfigKey("Resolvers", resolvers); err != nil {
		return err
	}
	if err := r.SetConfigKey("Bootstrap-testnet", schema.BootstrapAddressesTestnet); err != nil {
		return err
	}
	if err := r.SetConfigKey("Dropbox-api-token", ""); err != nil {
		return err
	}
	if err := r.SetConfigKey("RepublishInterval", "24h"); err != nil {
		return err
	}
	if err := r.SetConfigKey("JSON-API", a); err != nil {
		return err
	}
	if err := r.SetConfigKey("Tor-config", t); err != nil {
		return err
	}
	if err := r.Close(); err != nil {
		return err
	}
	return nil
}

func createMnemonic(newEntropy func(int) ([]byte, error), newMnemonic func([]byte) (string, error)) (string, error) {
	entropy, err := newEntropy(128)
	if err != nil {
		return "", err
	}
	mnemonic, err := newMnemonic(entropy)
	if err != nil {
		return "", err
	}
	return mnemonic, nil
}

/* Returns the directory to store repo data in.
   It depends on the OS and whether or not we are on testnet. */
func GetRepoPath(isTestnet bool) (string, error) {
	paths, err := schema.NewCustomSchemaManager(schema.SchemaContext{
		TestModeEnabled: isTestnet,
	})
	if err != nil {
		return "", err
	}
	return paths.DataPath(), nil
}
<|endoftext|>


<commit_before>package goness

// Editor resources.

// Templates are used to create scans or policies with predefined parameters.
type Template struct {
	// The uuid for the template.
	UUID string `json:"uuid"`
	// The short name of the template.
	Name string `json:"name"`
	// The long name of the template.
	Title string `json:"title"`
	// The description of the template.
	Desc string `json:"description"`
	// If true, template is only available on the cloud.
	CloudOnly bool `json:"cloud_only"`
	// If true, the template is only available for subscribers.
	SubscriptionOnly bool `json:"subscription_only"`
	// An external URL to link the template to.
	MoreInfo string `json:"more_info"`
}

type TemplateFormInput struct {
	ID      string   `json:"id"`
	Type    string   `json:"type"`
	Label   string   `json:"label"`
	Default string   `json:"default"`
	Options []string `json:"options"`
}

type TemplateDisplayGroup struct {
	Name     string   `json:"name"`
	Title    string   `json:"title"`
	Inputs   []string `json:"inputs"`
	Sections []string `json:"sections"`
}

type TemplateSection struct {
	Name   string   `json:"name"`
	Title  string   `json:"title"`
	Inputs []string `json:"inputs"`
}

type TemplateMode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Desc string `json:"desc"`
}

type TemplatePluginFamily struct {
	ID     int64  `json:"id"`
	Count  int64  `json:"count"`
	Status string `json:"status"`
}

type Filter struct {
	Name         string           `json:"name"`
	ReadableName string           `json:"readable_name"`
	Operators    []string         `json:"operators"`
	Controls     []FilterControls `json:"controls"`
}

type FilterControls struct {
	Type          string   `json:"type"`
	ReadableRegex string   `json:"readable_regest"`
	Regex         string   `json:"regex"`
	Ooptions      []string `json:"options"`
}

// Folders resources.

type Folder struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Type        string `json:"type"`
	DefaultTag  string `json:"default_tag"`
	Custom      int64  `json:"custom"`
	UnreadCount int64  `json:"unread_count"`
}

// Groups resources.

type Group struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	permissions int64  `json:"permissions"`
	UserCount   int64  `json:"user_count"`
}

// Permissions resources.

type Permission struct {
	Owner       int64  `json:"owner"`
	Type        string `json:"type"`
	Permissions int64  `json:"permissions"`
	ID          int64  `json:"id"`
	Name        string `json:"name"`
}

// Plugins resources.

type PluginAttr struct {
	Name string `json:"attribute_name"`
	Val  string `json:"attribute_value"`
}

type PluginFamily struct {
	ID    int64  `json:"id"`
	Name  string `json:"name"`
	Count int64  `json:"count"`
}

type Plugin struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

// Plugin-rules resources.

type Rule struct {
	ID       int64  `json:"id"`
	PluginID int64  `json:"plugin_id"`
	Date     string `json:"date"`
	Host     string `json:"host"`
	Type     string `json:"type"`
	Owner    string `json:"owner"`
	OwnerID  int64  `json:"owner_id"`
}

// Policies resources.

type Rule struct {
	ID                   int64  `json:"id"`
	TemplateUUID         string `json:"template_uuid"`
	Name                 string `json:"uuid"`
	Desc                 string `json:"description"`
	OwnerID              string `json:"owner_id"`
	Owner                string `json:"owner"`
	Shared               int64  `json:"shared"`
	UserPerms            int64  `json:"user_permissions"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
	Visibility           int64  `json:"visibility"`
	NoTarget             bool   `json:"no_target"`
}

// Scanners resources.

type Scanner struct {
	ID               int64  `json:"id"`
	UUID             string `json:"uuid"`
	Name             string `json:"name"`
	Type             string `json:"type"`
	Status           string `json:"status"`
	ScanCount        int64  `json:"scan_count"`
	EngineVersion    string `json:"engine_version"`
	Platform         string `json:"platform"`
	LoadedPluginSet  string `json:"loaded_plugin_set"`
	RegistrationCode string `json:"registration_code"`
	Owner            string `json:"owner"`
}

// Scans resources.

type Scan struct {
	ID                   int64  `json:"id"`
	UUID                 string `json:"uuid"`
	Name                 string `json:"name"`
	Owner                string `json:"owner"`
	FolderID             string `json:"folder_id"`
	Read                 bool   `json:"read"`
	Status               string `json:"status"`
	Shared               bool   `json:"shared"`
	UserPerms            int64  `json:"user_permissions"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
	Control              bool   `json:"control"`
	StartTime            string `json:"starttime"`
	TimeZone             string `json:"timezone"`
	RRules               string `json:"rrules"`
}

type Host struct {
	HostID                int64  `json:"host_id"`
	HostIdx               string `json:"host_index"`
	Hostname              string `json:"hostname"`
	Progress              string `json:"progress"`
	Critical              int64  `json:"critical"`
	High                  int64  `json:"high"`
	Medium                int64  `json:"medium"`
	Low                   int64  `json:"low"`
	Info                  int64  `json:"info"`
	TotalChecksConsidered int64  `json:"totalchecksconsidered"`
	NumChecksConsidered   int64  `json:"numchecksconsidered"`
	ScanProgressTotal     int64  `json:"scanprogresstotal"`
	ScanProgressCurrent   int64  `json:"scanprogresscurrent"`
	Score                 int64  `json:"score"`
}

type Note struct {
	Title    string `json:"title"`
	Message  string `json:"message"`
	Severity int64  `json:"severity"`
}

type Remediation struct {
	Value       string `json:"value"`
	Remediation string `json:"remediation"`
	NumHosts    int64  `json:"hosts"`
	NumVulns    string `json:"vulns"`
}

type History struct {
	HistoryID            int64  `json:"history_id"`
	UUID                 string `json:"uuid"`
	OwnerID              int64  `json:"owner_id"`
	Status               string `json:"status"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
}

type Vulnerability struct {
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	VulnIdx      int64  `json:"vuln_index"`
	SeverityIdx  int64  `json:"severity_index"`
}

type HostVulnerability struct {
	HostID       int64  `json:"host_id"`
	Hostname     string `json:"hostname"`
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	VulnIdx      int64  `json:"vuln_index"`
	SeverityIdx  int64  `json:"severity_index"`
	Severity     int64  `json:"severity"`
}

type HostCompliance struct {
	HostID       int64  `json:"host_id"`
	Hostname     string `json:"hostname"`
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	SeverityIdx  int64  `json:"severity_index"`
	Severity     int64  `json:"severity"`
}

type PluginOutput struct {
	PluginOutput string   `json:"plugin_output"`
	Hosts        string   `json:"hosts"`
	Severity     int64    `json:"severity"`
	Ports        []string `json:"ports"`
}

type TimeZone struct {
	Name string `json:"name"`
	Val  string `json:"value"`
}

// Sessions resources.

type Session struct {
	ID          string   `json:"id"`
	Username    string   `json:"username"`
	Email       string   `json:"email"`
	Name        string   `json:"name"`
	Type        string   `json:"type"`
	Perms       int64    `json:"permissions"`
	LastLogin   int64    `json:"last_login"`
	ContainerID int64    `json:"container_id"`
	Groups      []string `json:"groups"`
}

type User struct {
	ID          int    `json:"id"`
	Username    string `json:"username"`
	Name        string `json:"name"`
	Email       string `json:"email"`
	Permissions int    `json:"permissions"`
	LastLogin   int    `json:"lastlogin"`
	Type        string `json:"type"`
}
<commit_msg>change package name and fix duplicate rule type<commit_after>package nessie

// Editor resources.

// Templates are used to create scans or policies with predefined parameters.
type Template struct {
	// The uuid for the template.
	UUID string `json:"uuid"`
	// The short name of the template.
	Name string `json:"name"`
	// The long name of the template.
	Title string `json:"title"`
	// The description of the template.
	Desc string `json:"description"`
	// If true, template is only available on the cloud.
	CloudOnly bool `json:"cloud_only"`
	// If true, the template is only available for subscribers.
	SubscriptionOnly bool `json:"subscription_only"`
	// An external URL to link the template to.
	MoreInfo string `json:"more_info"`
}

type TemplateFormInput struct {
	ID      string   `json:"id"`
	Type    string   `json:"type"`
	Label   string   `json:"label"`
	Default string   `json:"default"`
	Options []string `json:"options"`
}

type TemplateDisplayGroup struct {
	Name     string   `json:"name"`
	Title    string   `json:"title"`
	Inputs   []string `json:"inputs"`
	Sections []string `json:"sections"`
}

type TemplateSection struct {
	Name   string   `json:"name"`
	Title  string   `json:"title"`
	Inputs []string `json:"inputs"`
}

type TemplateMode struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Desc string `json:"desc"`
}

type TemplatePluginFamily struct {
	ID     int64  `json:"id"`
	Count  int64  `json:"count"`
	Status string `json:"status"`
}

type Filter struct {
	Name         string           `json:"name"`
	ReadableName string           `json:"readable_name"`
	Operators    []string         `json:"operators"`
	Controls     []FilterControls `json:"controls"`
}

type FilterControls struct {
	Type          string   `json:"type"`
	ReadableRegex string   `json:"readable_regest"`
	Regex         string   `json:"regex"`
	Ooptions      []string `json:"options"`
}

// Folders resources.

type Folder struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	Type        string `json:"type"`
	DefaultTag  string `json:"default_tag"`
	Custom      int64  `json:"custom"`
	UnreadCount int64  `json:"unread_count"`
}

// Groups resources.

type Group struct {
	ID          int64  `json:"id"`
	Name        string `json:"name"`
	permissions int64  `json:"permissions"`
	UserCount   int64  `json:"user_count"`
}

// Permissions resources.

type Permission struct {
	Owner       int64  `json:"owner"`
	Type        string `json:"type"`
	Permissions int64  `json:"permissions"`
	ID          int64  `json:"id"`
	Name        string `json:"name"`
}

// Plugins resources.

type PluginAttr struct {
	Name string `json:"attribute_name"`
	Val  string `json:"attribute_value"`
}

type PluginFamily struct {
	ID    int64  `json:"id"`
	Name  string `json:"name"`
	Count int64  `json:"count"`
}

type Plugin struct {
	ID   int64  `json:"id"`
	Name string `json:"name"`
}

// Plugin-rules resources.

type Rule struct {
	ID       int64  `json:"id"`
	PluginID int64  `json:"plugin_id"`
	Date     string `json:"date"`
	Host     string `json:"host"`
	Type     string `json:"type"`
	Owner    string `json:"owner"`
	OwnerID  int64  `json:"owner_id"`
}

// Policies resources.

type Policy struct {
	ID                   int64  `json:"id"`
	TemplateUUID         string `json:"template_uuid"`
	Name                 string `json:"uuid"`
	Desc                 string `json:"description"`
	OwnerID              string `json:"owner_id"`
	Owner                string `json:"owner"`
	Shared               int64  `json:"shared"`
	UserPerms            int64  `json:"user_permissions"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
	Visibility           int64  `json:"visibility"`
	NoTarget             bool   `json:"no_target"`
}

// Scanners resources.

type Scanner struct {
	ID               int64  `json:"id"`
	UUID             string `json:"uuid"`
	Name             string `json:"name"`
	Type             string `json:"type"`
	Status           string `json:"status"`
	ScanCount        int64  `json:"scan_count"`
	EngineVersion    string `json:"engine_version"`
	Platform         string `json:"platform"`
	LoadedPluginSet  string `json:"loaded_plugin_set"`
	RegistrationCode string `json:"registration_code"`
	Owner            string `json:"owner"`
}

// Scans resources.

type Scan struct {
	ID                   int64  `json:"id"`
	UUID                 string `json:"uuid"`
	Name                 string `json:"name"`
	Owner                string `json:"owner"`
	FolderID             string `json:"folder_id"`
	Read                 bool   `json:"read"`
	Status               string `json:"status"`
	Shared               bool   `json:"shared"`
	UserPerms            int64  `json:"user_permissions"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
	Control              bool   `json:"control"`
	StartTime            string `json:"starttime"`
	TimeZone             string `json:"timezone"`
	RRules               string `json:"rrules"`
}

type Host struct {
	HostID                int64  `json:"host_id"`
	HostIdx               string `json:"host_index"`
	Hostname              string `json:"hostname"`
	Progress              string `json:"progress"`
	Critical              int64  `json:"critical"`
	High                  int64  `json:"high"`
	Medium                int64  `json:"medium"`
	Low                   int64  `json:"low"`
	Info                  int64  `json:"info"`
	TotalChecksConsidered int64  `json:"totalchecksconsidered"`
	NumChecksConsidered   int64  `json:"numchecksconsidered"`
	ScanProgressTotal     int64  `json:"scanprogresstotal"`
	ScanProgressCurrent   int64  `json:"scanprogresscurrent"`
	Score                 int64  `json:"score"`
}

type Note struct {
	Title    string `json:"title"`
	Message  string `json:"message"`
	Severity int64  `json:"severity"`
}

type Remediation struct {
	Value       string `json:"value"`
	Remediation string `json:"remediation"`
	NumHosts    int64  `json:"hosts"`
	NumVulns    string `json:"vulns"`
}

type History struct {
	HistoryID            int64  `json:"history_id"`
	UUID                 string `json:"uuid"`
	OwnerID              int64  `json:"owner_id"`
	Status               string `json:"status"`
	CreationDate         int64  `json:"creation_date"`
	LastModificationDate int64  `json:"last_modification_date"`
}

type Vulnerability struct {
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	VulnIdx      int64  `json:"vuln_index"`
	SeverityIdx  int64  `json:"severity_index"`
}

type HostVulnerability struct {
	HostID       int64  `json:"host_id"`
	Hostname     string `json:"hostname"`
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	VulnIdx      int64  `json:"vuln_index"`
	SeverityIdx  int64  `json:"severity_index"`
	Severity     int64  `json:"severity"`
}

type HostCompliance struct {
	HostID       int64  `json:"host_id"`
	Hostname     string `json:"hostname"`
	PluginID     int64  `json:"plugin_id"`
	PluginName   string `json:"plugin_name"`
	PluginFamily string `json:"plugin_family"`
	Count        int64  `json:"count"`
	SeverityIdx  int64  `json:"severity_index"`
	Severity     int64  `json:"severity"`
}

type PluginOutput struct {
	PluginOutput string   `json:"plugin_output"`
	Hosts        string   `json:"hosts"`
	Severity     int64    `json:"severity"`
	Ports        []string `json:"ports"`
}

type TimeZone struct {
	Name string `json:"name"`
	Val  string `json:"value"`
}

// Sessions resources.

type Session struct {
	ID          string   `json:"id"`
	Username    string   `json:"username"`
	Email       string   `json:"email"`
	Name        string   `json:"name"`
	Type        string   `json:"type"`
	Perms       int64    `json:"permissions"`
	LastLogin   int64    `json:"last_login"`
	ContainerID int64    `json:"container_id"`
	Groups      []string `json:"groups"`
}

type User struct {
	ID          int    `json:"id"`
	Username    string `json:"username"`
	Name        string `json:"name"`
	Email       string `json:"email"`
	Permissions int    `json:"permissions"`
	LastLogin   int    `json:"lastlogin"`
	Type        string `json:"type"`
}
<|endoftext|>


<commit_before>// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package parse

import (
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/stretchr/testify/assert"
)

func TestParse(t *testing.T) {
	samples := []struct {
		text   string
		rule   Rule
		tree   *Tree
		err    error
		parser *Parser
	}{

		// Terminal

		{
			"",
			NewTerminal(""),
			nil,
			NewErrEmptyRule(NewTerminal("")),
			DefaultParser,
		},
		{
			"foo",
			NewTerminal("foo"),
			&Tree{
				Rule:  NewTerminal("foo"),
				Data:  []byte("foo"),
				Start: 0,
				End:   3,
			},
			nil,
			DefaultParser,
		},
		{
			"bar",
			NewTerminal("foo"),
			nil,
			NewErrUnexpectedToken(
				[]byte("b"),
				1,
				NewTerminal("foo"),
			),
			DefaultParser,
		},
		{
			"foobar",
			NewTerminal("foo"),
			nil,
			NewErrUnexpectedToken(
				[]byte("b"),
				4,
				NewTerminal("foo"),
			),
			DefaultParser,
		},

		// Chain

		{
			"",
			NewChain(),
			nil,
			NewErrEmptyRule(NewChain()),
			DefaultParser,
		},
		{
			"foo bar",
			NewChain(
				NewTerminal("foo"),
				NewTerminal(" "),
				NewTerminal("bar"),
			),
			&Tree{
				Rule: NewChain(
					NewTerminal("foo"),
					NewTerminal(" "),
					NewTerminal("bar"),
				),
				Data:  []byte("foo bar"),
				Start: 0,
				End:   7,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("foo"),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
					},
					{
						Rule:  NewTerminal(" "),
						Data:  []byte(" "),
						Start: 3,
						End:   4,
					},
					{
						Rule:  NewTerminal("bar"),
						Data:  []byte("bar"),
						Start: 4,
						End:   7,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"foo",
			NewChain(
				NewChain(
					NewChain(
						NewChain(
							NewTerminal("foo"),
						),
					),
				),
			),
			nil,
			NewErrNestingTooDeep(4, 1),
			NewParser(3),
		},

		// Either

		{
			"",
			NewEither(),
			nil,
			NewErrEmptyRule(NewEither()),
			DefaultParser,
		},
		{
			"1",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
				Data:  []byte("1"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("1"),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"1",
			NewEither(
				NewTerminal("3"),
				NewTerminal("2"),
				NewTerminal("1"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("3"),
					NewTerminal("2"),
					NewTerminal("1"),
				),
				Data:  []byte("1"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("1"),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"2",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
				Data:  []byte("2"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("2"),
						Data:  []byte("2"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"4",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			nil,
			NewErrUnexpectedToken(
				[]byte("4"),
				1,
				NewTerminal("3"),
			),
			DefaultParser,
		},

		// Repetition

		{
			"123",
			NewRepetition(
				NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
			),
			&Tree{
				Rule: NewRepetition(
					NewEither(
						NewTerminal("1"),
						NewTerminal("2"),
						NewTerminal("3"),
					),
				),
				Data:  []byte("123"),
				Start: 0,
				End:   3,
				Childs: []*Tree{
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("1"),
								Data:  []byte("1"),
								Start: 0,
								End:   1,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("2"),
						Start: 1,
						End:   2,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("2"),
								Data:  []byte("2"),
								Start: 1,
								End:   2,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("3"),
						Start: 2,
						End:   3,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("3"),
								Data:  []byte("3"),
								Start: 2,
								End:   3,
							},
						},
					},
				},
			},
			nil,
			DefaultParser,
		},

		// mixed

		{
			"foobar",
			NewChain(
				NewEither(
					NewTerminal("foo"),
					NewTerminal("bar"),
				),
				NewEither(
					NewTerminal("foo"),
					NewTerminal("bar"),
				),
			),
			&Tree{
				Rule: NewChain(
					NewEither(
						NewTerminal("foo"),
						NewTerminal("bar"),
					),
					NewEither(
						NewTerminal("foo"),
						NewTerminal("bar"),
					),
				),
				Data:  []byte("foobar"),
				Start: 0,
				End:   6,
				Childs: []*Tree{
					{
						Rule: NewEither(
							NewTerminal("foo"),
							NewTerminal("bar"),
						),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("foo"),
								Data:  []byte("foo"),
								Start: 0,
								End:   3,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("foo"),
							NewTerminal("bar"),
						),
						Data:  []byte("bar"),
						Start: 3,
						End:   6,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("bar"),
								Data:  []byte("bar"),
								Start: 3,
								End:   6,
							},
						},
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"foo(1234)",
			NewChain(
				NewTerminal("foo"),
				NewTerminal("("),
				NewRepetition(
					NewEither(
						NewTerminal("4"),
						NewTerminal("3"),
						NewTerminal("2"),
						NewTerminal("1"),
					),
				),
				NewTerminal(")"),
			),
			&Tree{
				Rule: NewChain(
					NewTerminal("foo"),
					NewTerminal("("),
					NewRepetition(
						NewEither(
							NewTerminal("4"),
							NewTerminal("3"),
							NewTerminal("2"),
							NewTerminal("1"),
						),
					),
					NewTerminal(")"),
				),
				Data:  []byte("foo(1234)"),
				Start: 0,
				End:   9,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("foo"),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
					},
					{
						Rule:  NewTerminal("("),
						Data:  []byte("("),
						Start: 3,
						End:   4,
					},
					{
						Rule: NewRepetition(
							NewEither(
								NewTerminal("4"),
								NewTerminal("3"),
								NewTerminal("2"),
								NewTerminal("1"),
							),
						),
						Data:  []byte("1234"),
						Start: 4,
						End:   8,
						Childs: []*Tree{
							{
								Rule: NewEither(
									NewTerminal("4"),
									NewTerminal("3"),
									NewTerminal("2"),
									NewTerminal("1"),
								),
								Data:  []byte("1234"),
								Start: 4,
								End:   8,
								Childs: []*Tree{
									{
										Rule:  NewTerminal("1"),
										Data:  []byte("1"),
										Start: 4,
										End:   5,
									},
									{
										Rule:  NewTerminal("2"),
										Data:  []byte("2"),
										Start: 5,
										End:   6,
									},
									{
										Rule:  NewTerminal("3"),
										Data:  []byte("3"),
										Start: 6,
										End:   7,
									},
									{
										Rule:  NewTerminal("4"),
										Data:  []byte("4"),
										Start: 7,
										End:   8,
									},
								},
							},
						},
					},
					{
						Rule:  NewTerminal(")"),
						Data:  []byte(")"),
						Start: 8,
						End:   9,
					},
				},
			},
			nil,
			DefaultParser,
		},
	}

	for k, sample := range samples {
		tree, err := sample.parser.Parse(
			sample.rule,
			[]byte(sample.text),
		)
		msg := spew.Sdump(
			k,
			sample.rule,
			sample.text,
		)
		assert.EqualValues(t, sample.err, err, msg)
		assert.EqualValues(t, sample.tree, tree, msg)
	}
}

// func TestTemporary(t *testing.T) {
// 	number := NewEither(
// 		NewTerminal("0"),
// 		NewTerminal("1"),
// 		NewTerminal("2"),
// 		NewTerminal("3"),
// 		NewTerminal("4"),
// 		NewTerminal("5"),
// 		NewTerminal("6"),
// 		NewTerminal("7"),
// 		NewTerminal("8"),
// 		NewTerminal("9"),
// 	)
// 	leftBracket := NewTerminal("(")
// 	rightBracket := NewTerminal(")")

// 	typeDefinition := NewEither()

// 	wrap := NewChain(
// 		NewTerminal("Wrap"),
// 		leftBracket,
// 		typeDefinition,
// 		rightBracket,
// 	)
// 	node := NewChain(
// 		NewTerminal("Node"),
// 		leftBracket,
// 		typeDefinition,
// 		rightBracket,
// 	)
// 	integer := NewChain(
// 		NewTerminal("Int"),
// 		leftBracket,
// 		NewRepetition(number),
// 		rightBracket,
// 	)

// 	*typeDefinition = append(*typeDefinition, wrap)
// 	*typeDefinition = append(*typeDefinition, node)
// 	*typeDefinition = append(*typeDefinition, integer)

// 	spew.Dump(
// 		DefaultParser.Parse(
// 			typeDefinition,
// 			[]byte("Wrap(Node(Int(5)))"),
// 		),
// 	)
// }
<commit_msg>Implemented repetition<commit_after>// Copyright © 2017 Dmitry Moskowski
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package parse

import (
	"testing"

	"github.com/davecgh/go-spew/spew"
	"github.com/stretchr/testify/assert"
)

func TestParse(t *testing.T) {
	samples := []struct {
		text   string
		rule   Rule
		tree   *Tree
		err    error
		parser *Parser
	}{

		// Terminal

		{
			"",
			NewTerminal(""),
			nil,
			NewErrEmptyRule(NewTerminal("")),
			DefaultParser,
		},
		{
			"foo",
			NewTerminal("foo"),
			&Tree{
				Rule:  NewTerminal("foo"),
				Data:  []byte("foo"),
				Start: 0,
				End:   3,
			},
			nil,
			DefaultParser,
		},
		{
			"bar",
			NewTerminal("foo"),
			nil,
			NewErrUnexpectedToken(
				[]byte("b"),
				1,
				NewTerminal("foo"),
			),
			DefaultParser,
		},
		{
			"foobar",
			NewTerminal("foo"),
			nil,
			NewErrUnexpectedToken(
				[]byte("b"),
				4,
				NewTerminal("foo"),
			),
			DefaultParser,
		},

		// Chain

		{
			"",
			NewChain(),
			nil,
			NewErrEmptyRule(NewChain()),
			DefaultParser,
		},
		{
			"foo bar",
			NewChain(
				NewTerminal("foo"),
				NewTerminal(" "),
				NewTerminal("bar"),
			),
			&Tree{
				Rule: NewChain(
					NewTerminal("foo"),
					NewTerminal(" "),
					NewTerminal("bar"),
				),
				Data:  []byte("foo bar"),
				Start: 0,
				End:   7,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("foo"),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
					},
					{
						Rule:  NewTerminal(" "),
						Data:  []byte(" "),
						Start: 3,
						End:   4,
					},
					{
						Rule:  NewTerminal("bar"),
						Data:  []byte("bar"),
						Start: 4,
						End:   7,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"foo",
			NewChain(
				NewChain(
					NewChain(
						NewChain(
							NewTerminal("foo"),
						),
					),
				),
			),
			nil,
			NewErrNestingTooDeep(4, 1),
			NewParser(3),
		},

		// Either

		{
			"",
			NewEither(),
			nil,
			NewErrEmptyRule(NewEither()),
			DefaultParser,
		},
		{
			"1",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
				Data:  []byte("1"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("1"),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"1",
			NewEither(
				NewTerminal("3"),
				NewTerminal("2"),
				NewTerminal("1"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("3"),
					NewTerminal("2"),
					NewTerminal("1"),
				),
				Data:  []byte("1"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("1"),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"2",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			&Tree{
				Rule: NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
				Data:  []byte("2"),
				Start: 0,
				End:   1,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("2"),
						Data:  []byte("2"),
						Start: 0,
						End:   1,
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"4",
			NewEither(
				NewTerminal("1"),
				NewTerminal("2"),
				NewTerminal("3"),
			),
			nil,
			NewErrUnexpectedToken(
				[]byte("4"),
				1,
				NewTerminal("3"),
			),
			DefaultParser,
		},

		// Repetition

		{
			"123",
			NewRepetition(
				NewEither(
					NewTerminal("1"),
					NewTerminal("2"),
					NewTerminal("3"),
				),
			),
			&Tree{
				Rule: NewRepetition(
					NewEither(
						NewTerminal("1"),
						NewTerminal("2"),
						NewTerminal("3"),
					),
				),
				Data:  []byte("123"),
				Start: 0,
				End:   3,
				Childs: []*Tree{
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("1"),
						Start: 0,
						End:   1,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("1"),
								Data:  []byte("1"),
								Start: 0,
								End:   1,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("2"),
						Start: 1,
						End:   2,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("2"),
								Data:  []byte("2"),
								Start: 1,
								End:   2,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("1"),
							NewTerminal("2"),
							NewTerminal("3"),
						),
						Data:  []byte("3"),
						Start: 2,
						End:   3,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("3"),
								Data:  []byte("3"),
								Start: 2,
								End:   3,
							},
						},
					},
				},
			},
			nil,
			DefaultParser,
		},

		// mixed

		{
			"foobar",
			NewChain(
				NewEither(
					NewTerminal("foo"),
					NewTerminal("bar"),
				),
				NewEither(
					NewTerminal("foo"),
					NewTerminal("bar"),
				),
			),
			&Tree{
				Rule: NewChain(
					NewEither(
						NewTerminal("foo"),
						NewTerminal("bar"),
					),
					NewEither(
						NewTerminal("foo"),
						NewTerminal("bar"),
					),
				),
				Data:  []byte("foobar"),
				Start: 0,
				End:   6,
				Childs: []*Tree{
					{
						Rule: NewEither(
							NewTerminal("foo"),
							NewTerminal("bar"),
						),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("foo"),
								Data:  []byte("foo"),
								Start: 0,
								End:   3,
							},
						},
					},
					{
						Rule: NewEither(
							NewTerminal("foo"),
							NewTerminal("bar"),
						),
						Data:  []byte("bar"),
						Start: 3,
						End:   6,
						Childs: []*Tree{
							{
								Rule:  NewTerminal("bar"),
								Data:  []byte("bar"),
								Start: 3,
								End:   6,
							},
						},
					},
				},
			},
			nil,
			DefaultParser,
		},
		{
			"foo(1234)",
			NewChain(
				NewTerminal("foo"),
				NewTerminal("("),
				NewRepetition(
					NewEither(
						NewTerminal("4"),
						NewTerminal("3"),
						NewTerminal("2"),
						NewTerminal("1"),
					),
				),
				NewTerminal(")"),
			),
			&Tree{
				Rule: NewChain(
					NewTerminal("foo"),
					NewTerminal("("),
					NewRepetition(
						NewEither(
							NewTerminal("4"),
							NewTerminal("3"),
							NewTerminal("2"),
							NewTerminal("1"),
						),
					),
					NewTerminal(")"),
				),
				Data:  []byte("foo(1234)"),
				Start: 0,
				End:   9,
				Childs: []*Tree{
					{
						Rule:  NewTerminal("foo"),
						Data:  []byte("foo"),
						Start: 0,
						End:   3,
					},
					{
						Rule:  NewTerminal("("),
						Data:  []byte("("),
						Start: 3,
						End:   4,
					},
					{
						Rule: NewRepetition(
							NewEither(
								NewTerminal("4"),
								NewTerminal("3"),
								NewTerminal("2"),
								NewTerminal("1"),
							),
						),
						Data:  []byte("1234"),
						Start: 4,
						End:   8,
						Childs: []*Tree{
							{
								Rule: NewEither(
									NewTerminal("4"),
									NewTerminal("3"),
									NewTerminal("2"),
									NewTerminal("1"),
								),
								Data:  []byte("1"),
								Start: 4,
								End:   5,
								Childs: []*Tree{
									{
										Rule:  NewTerminal("1"),
										Data:  []byte("1"),
										Start: 4,
										End:   5,
									},
								},
							},
							{
								Rule: NewEither(
									NewTerminal("4"),
									NewTerminal("3"),
									NewTerminal("2"),
									NewTerminal("1"),
								),
								Data:  []byte("2"),
								Start: 5,
								End:   6,
								Childs: []*Tree{
									{
										Rule:  NewTerminal("2"),
										Data:  []byte("2"),
										Start: 5,
										End:   6,
									},
								},
							},
							{
								Rule: NewEither(
									NewTerminal("4"),
									NewTerminal("3"),
									NewTerminal("2"),
									NewTerminal("1"),
								),
								Data:  []byte("3"),
								Start: 6,
								End:   7,
								Childs: []*Tree{
									{
										Rule:  NewTerminal("3"),
										Data:  []byte("3"),
										Start: 6,
										End:   7,
									},
								},
							},
							{
								Rule: NewEither(
									NewTerminal("4"),
									NewTerminal("3"),
									NewTerminal("2"),
									NewTerminal("1"),
								),
								Data:  []byte("4"),
								Start: 7,
								End:   8,
								Childs: []*Tree{
									{
										Rule:  NewTerminal("4"),
										Data:  []byte("4"),
										Start: 7,
										End:   8,
									},
								},
							},
						},
					},
					{
						Rule:  NewTerminal(")"),
						Data:  []byte(")"),
						Start: 8,
						End:   9,
					},
				},
			},
			nil,
			DefaultParser,
		},
	}

	for k, sample := range samples {
		tree, err := sample.parser.Parse(
			sample.rule,
			[]byte(sample.text),
		)
		msg := spew.Sdump(
			k,
			sample.rule,
			sample.text,
		)
		assert.EqualValues(t, sample.err, err, msg)
		assert.EqualValues(t, sample.tree, tree, msg)
	}
}
<|endoftext|>


<commit_before>package ftp

import (
	"time"
	"testing"
)

var thisYear, _, _ = time.Now().Date()

type line struct {
	line      string
	name      string
	size      uint64
	entryType EntryType
	time      time.Time
}

var listTests = []line{
	// UNIX ls -l style
	line{"drwxr-xr-x    3 110      1002            3 Dec 02  2009 pub", "pub", 0, EntryTypeFolder, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"drwxr-xr-x    3 110      1002            3 Dec 02  2009 p u b", "p u b", 0, EntryTypeFolder, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"-rwxr-xr-x    3 110      1002            1234567 Dec 02  2009 fileName", "fileName", 1234567, EntryTypeFile, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"lrwxrwxrwx   1 root     other          7 Jan 25 00:17 bin -> usr/bin", "bin -> usr/bin", 0, EntryTypeLink, time.Date(thisYear, time.January, 25, 0, 17, 0, 0, time.UTC)},
	// Microsoft's FTP servers for Windows
	line{"----------   1 owner    group         1803128 Jul 10 10:18 ls-lR.Z", "ls-lR.Z", 1803128, EntryTypeFile, time.Date(thisYear, time.July, 10, 10, 18, 0, 0, time.UTC)},
	line{"d---------   1 owner    group               0 May  9 19:45 Softlib", "Softlib", 0, EntryTypeFolder, time.Date(thisYear, time.May, 9, 19, 45, 0, 0, time.UTC)},
	// WFTPD for MSDOS
	line{"-rwxrwxrwx   1 noone    nogroup      322 Aug 19  1996 message.ftp", "message.ftp", 322, EntryTypeFile, time.Date(1996, time.August, 19, 0, 0, 0, 0, time.UTC)},
}

// Not supported, at least we should properly return failure
var listTestsFail = []line{
	line{"d [R----F--] supervisor            512       Jan 16 18:53 login", "login", 0, EntryTypeFolder, time.Date(thisYear, time.January, 16, 18, 53, 0, 0, time.UTC)},
	line{"- [R----F--] rhesus             214059       Oct 20 15:27 cx.exe", "cx.exe", 0, EntryTypeFile, time.Date(thisYear, time.October, 20, 15, 27, 0, 0, time.UTC)},
}

func TestParseListLine(t *testing.T) {
	for _, lt := range listTests {
		entry, err := parseListLine(lt.line)
		if err != nil {
			t.Errorf("parseListLine(%v) returned err = %v", lt.line, err)
			continue
		}
		if entry.Name != lt.name {
			t.Errorf("parseListLine(%v).Name = '%v', want '%v'", lt.line, entry.Name, lt.name)
		}
		if entry.Type != lt.entryType {
			t.Errorf("parseListLine(%v).EntryType = %v, want %v", lt.line, entry.Type, lt.entryType)
		}
		if entry.Size != lt.size {
			t.Errorf("parseListLine(%v).Size = %v, want %v", lt.line, entry.Size, lt.size)
		}
		if entry.Time.Unix() != lt.time.Unix() {
			t.Errorf("parseListLine(%v).Time = %v, want %v", lt.line, entry.Time, lt.time)
		}
	}
	for _, lt := range listTestsFail {
		_, err := parseListLine(lt.line)
		if err == nil {
			t.Errorf("parseListLine(%v) expected to fail", lt.line)
		}
	}
}
<commit_msg>gofmt parse_test.go<commit_after>package ftp

import (
	"testing"
	"time"
)

var thisYear, _, _ = time.Now().Date()

type line struct {
	line      string
	name      string
	size      uint64
	entryType EntryType
	time      time.Time
}

var listTests = []line{
	// UNIX ls -l style
	line{"drwxr-xr-x    3 110      1002            3 Dec 02  2009 pub", "pub", 0, EntryTypeFolder, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"drwxr-xr-x    3 110      1002            3 Dec 02  2009 p u b", "p u b", 0, EntryTypeFolder, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"-rwxr-xr-x    3 110      1002            1234567 Dec 02  2009 fileName", "fileName", 1234567, EntryTypeFile, time.Date(2009, time.December, 2, 0, 0, 0, 0, time.UTC)},
	line{"lrwxrwxrwx   1 root     other          7 Jan 25 00:17 bin -> usr/bin", "bin -> usr/bin", 0, EntryTypeLink, time.Date(thisYear, time.January, 25, 0, 17, 0, 0, time.UTC)},
	// Microsoft's FTP servers for Windows
	line{"----------   1 owner    group         1803128 Jul 10 10:18 ls-lR.Z", "ls-lR.Z", 1803128, EntryTypeFile, time.Date(thisYear, time.July, 10, 10, 18, 0, 0, time.UTC)},
	line{"d---------   1 owner    group               0 May  9 19:45 Softlib", "Softlib", 0, EntryTypeFolder, time.Date(thisYear, time.May, 9, 19, 45, 0, 0, time.UTC)},
	// WFTPD for MSDOS
	line{"-rwxrwxrwx   1 noone    nogroup      322 Aug 19  1996 message.ftp", "message.ftp", 322, EntryTypeFile, time.Date(1996, time.August, 19, 0, 0, 0, 0, time.UTC)},
}

// Not supported, at least we should properly return failure
var listTestsFail = []line{
	line{"d [R----F--] supervisor            512       Jan 16 18:53 login", "login", 0, EntryTypeFolder, time.Date(thisYear, time.January, 16, 18, 53, 0, 0, time.UTC)},
	line{"- [R----F--] rhesus             214059       Oct 20 15:27 cx.exe", "cx.exe", 0, EntryTypeFile, time.Date(thisYear, time.October, 20, 15, 27, 0, 0, time.UTC)},
}

func TestParseListLine(t *testing.T) {
	for _, lt := range listTests {
		entry, err := parseListLine(lt.line)
		if err != nil {
			t.Errorf("parseListLine(%v) returned err = %v", lt.line, err)
			continue
		}
		if entry.Name != lt.name {
			t.Errorf("parseListLine(%v).Name = '%v', want '%v'", lt.line, entry.Name, lt.name)
		}
		if entry.Type != lt.entryType {
			t.Errorf("parseListLine(%v).EntryType = %v, want %v", lt.line, entry.Type, lt.entryType)
		}
		if entry.Size != lt.size {
			t.Errorf("parseListLine(%v).Size = %v, want %v", lt.line, entry.Size, lt.size)
		}
		if entry.Time.Unix() != lt.time.Unix() {
			t.Errorf("parseListLine(%v).Time = %v, want %v", lt.line, entry.Time, lt.time)
		}
	}
	for _, lt := range listTestsFail {
		_, err := parseListLine(lt.line)
		if err == nil {
			t.Errorf("parseListLine(%v) expected to fail", lt.line)
		}
	}
}
<|endoftext|>


<commit_before>package vm

import (
	"errors"
	"fmt"
	"math"

	"github.com/Zac-Garby/pluto/ast"
	"github.com/Zac-Garby/pluto/bytecode"
	"github.com/Zac-Garby/pluto/object"
)

// Effector is a function which performs a particular instruction
type Effector func(f *Frame, i bytecode.Instruction)

var effectors map[byte]Effector

// Effectors returns a map of bytecodes and their
// effectors. Wrapped in a function to stop an
// initialization loop.
func Effectors() map[byte]Effector {
	if len(effectors) == 0 {
		effectors = map[byte]Effector{
			bytecode.Pop: bytePop,
			bytecode.Dup: byteDup,

			bytecode.LoadConst: byteLoadConst,
			bytecode.LoadName:  byteLoadName,
			bytecode.StoreName: byteStoreName,

			bytecode.UnaryInvert: bytePrefix,
			bytecode.UnaryNegate: bytePrefix,
			bytecode.UnaryNoOp:   bytePrefix,

			bytecode.BinaryAdd:      byteInfix,
			bytecode.BinarySubtract: byteInfix,
			bytecode.BinaryMultiply: byteInfix,
			bytecode.BinaryDivide:   byteInfix,
			bytecode.BinaryExponent: byteInfix,
			bytecode.BinaryFloorDiv: byteInfix,
			bytecode.BinaryMod:      byteInfix,
			bytecode.BinaryBitOr:    byteInfix,
			bytecode.BinaryBitAnd:   byteInfix,
			bytecode.BinaryEquals:   byteEquals,
			bytecode.BinaryNotEqual: byteNotEqual,
			bytecode.BinaryLessThan: bincmp,
			bytecode.BinaryMoreThan: bincmp,
			bytecode.BinaryLessEq:   bincmp,
			bytecode.BinaryMoreEq:   bincmp,

			bytecode.Call:   byteCall,
			bytecode.Return: byteReturn,

			bytecode.Print:   bytePrint,
			bytecode.Println: bytePrintln,

			bytecode.Jump:        byteJump,
			bytecode.JumpIfTrue:  byteJumpIfTrue,
			bytecode.JumpIfFalse: byteJumpIfFalse,

			bytecode.MakeArray: byteMakeArray,
			bytecode.MakeTuple: byteMakeTuple,
			bytecode.MakeMap:   byteMakeMap,
		}
	}

	return effectors
}

func bytePop(f *Frame, i bytecode.Instruction) {
	f.stack.pop()
}

func byteDup(f *Frame, i bytecode.Instruction) {
	f.stack.dup()
}

func byteLoadConst(f *Frame, i bytecode.Instruction) {
	f.stack.push(f.constants[i.Arg])
}

func byteLoadName(f *Frame, i bytecode.Instruction) {
	name, ok := f.getName(i.Arg)
	if !ok {
		f.vm.Error = errors.New("evaluation: internal: name not found")
		return
	}

	val, ok := f.searchName(name)
	if !ok {
		f.vm.Error = fmt.Errorf("evaluation: name %s not found in the current scope", name)
		return
	}

	f.stack.push(val)
}

func byteStoreName(f *Frame, i bytecode.Instruction) {
	name, ok := f.getName(i.Arg)
	if !ok {
		f.vm.Error = errors.New("evaluation: internal: name not found")
		return
	}

	f.locals.Define(name, f.stack.pop())
}

func bytePrefix(f *Frame, i bytecode.Instruction) {
	right := f.stack.pop()

	if i.Code == bytecode.UnaryInvert {
		f.stack.push(object.BoolObj(!object.IsTruthy(right)))
		return
	}

	if n, ok := right.(object.Numeric); ok {
		val := n.Float64()
		f.stack.push(numPrefix(i.Code, val))
	} else {
		f.vm.Error = errors.New("evaluation: prefix r-value of invalid type")
	}
}

func numPrefix(opcode byte, val float64) object.Object {
	switch opcode {
	case bytecode.UnaryNegate:
		val = -val
	case bytecode.UnaryNoOp:
		// val = val
	}

	return &object.Number{Value: val}
}

func byteInfix(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()

	if n, ok := left.(object.Numeric); ok {
		if m, ok := right.(object.Numeric); ok {
			f.stack.push(numInfix(f, i.Code, n.Float64(), m.Float64()))
		} else if m, ok := right.(object.Collection); ok {
			f.stack.push(numColInfix(f, i.Code, n.Float64(), m))
		} else {
			f.vm.Error = errors.New("evaluation: infix r-value of invalid type when l-value is <number>")
			return
		}
	} else if n, ok := left.(object.Collection); ok {
		if m, ok := right.(object.Numeric); ok {
			f.stack.push(numColInfix(f, i.Code, m.Float64(), n))
		} else if m, ok := right.(object.Collection); ok {
			f.stack.push(colInfix(f, i.Code, n, m))
		} else {
			f.vm.Error = errors.New("evaluation: infix r-value of invalid type when l-value is a collection")
		}
	} else {
		f.vm.Error = errors.New("evaluation: infix l-value of invalid type")
		return
	}
}

func numInfix(f *Frame, opcode byte, left, right float64) object.Object {
	var val float64

	switch opcode {
	case bytecode.BinaryAdd:
		val = left + right
	case bytecode.BinarySubtract:
		val = left - right
	case bytecode.BinaryMultiply:
		val = left * right
	case bytecode.BinaryDivide:
		val = left / right
	case bytecode.BinaryExponent:
		val = math.Pow(left, right)
	case bytecode.BinaryFloorDiv:
		val = math.Floor(left / right)
	case bytecode.BinaryMod:
		val = math.Mod(left, right)
	case bytecode.BinaryBitOr:
		val = float64(int64(left) | int64(right))
	case bytecode.BinaryBitAnd:
		val = float64(int64(left) & int64(right))
	default:
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for two numbers", op)
	}

	return &object.Number{Value: val}
}

func numColInfix(f *Frame, opcode byte, left float64, right object.Collection) object.Object {
	var (
		result   []object.Object
		elements = right.Elements()
	)

	if opcode == bytecode.BinaryMultiply {
		for i := 0; i < int(left); i++ {
			result = append(result, elements...)
		}
	} else {
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for a collection and a number", op)
	}

	col, _ := object.MakeCollection(right.Type(), result)
	return col
}

func colInfix(f *Frame, opcode byte, left, right object.Collection) object.Object {
	var (
		lefts  = left.Elements()
		rights = right.Elements()
		elems  []object.Object
	)

	switch opcode {
	case bytecode.BinaryAdd:
		elems = append(lefts, rights...)
	case bytecode.BinarySubtract:
		for _, el := range lefts {
			for _, rel := range rights {
				if el.Equals(rel) {
					goto next
				}
			}

			elems = append(elems, el)
		next:
		}
	case bytecode.BinaryBitOr:
		for _, el := range append(lefts, rights...) {
			unique := true

			for _, rel := range elems {
				if el.Equals(rel) {
					unique = false
					break
				}
			}

			if unique {
				elems = append(elems, el)
			}
		}
	case bytecode.BinaryBitAnd:
		for _, el := range lefts {
			both := false

			for _, rel := range rights {
				if el.Equals(rel) {
					both = true
					break
				}
			}

			if both {
				elems = append(elems, el)
			}
		}
	default:
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for two collections", op)
	}

	col, _ := object.MakeCollection(left.Type(), elems)
	return col
}

func bincmp(f *Frame, i bytecode.Instruction) {
	f.byteToInstructionIndex(int(i.Arg))

	b, a := f.stack.pop(), f.stack.pop()

	n, ok := a.(object.Numeric)
	if !ok {
		f.vm.Error = errors.New("evaluation: non-numeric value in numeric binary expression")
		return
	}

	m, ok := b.(object.Numeric)
	if !ok {
		f.vm.Error = errors.New("evaluation: non-numeric value in numeric binary expression")
		return
	}

	lval := n.Float64()
	rval := m.Float64()

	var result bool

	switch i.Code {
	case bytecode.BinaryLessThan:
		result = lval < rval
	case bytecode.BinaryMoreThan:
		result = lval > rval
	case bytecode.BinaryLessEq:
		result = lval <= rval
	case bytecode.BinaryMoreEq:
		result = lval >= rval
	}

	f.stack.push(&object.Boolean{Value: result})
}

func byteEquals(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()
	eq := left.Equals(right)

	f.stack.push(object.BoolObj(eq))
}

func byteNotEqual(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()
	eq := left.Equals(right)

	f.stack.push(object.BoolObj(!eq))
}

func byteCall(f *Frame, i bytecode.Instruction) {
	pattern := f.locals.Patterns[i.Arg]

	fn := f.locals.Functions.SearchString(pattern)
	if fn == nil {
		f.vm.Error = fmt.Errorf("evaluation: function '%s' not found in the current scope", pattern)
		return
	}

	locals := f.locals

	for _, item := range fn.Pattern {
		if param, ok := item.(*ast.Parameter); ok {
			// Found a parameter

			locals.Define(param.Name, f.stack.pop())
		}
	}

	// Create the function's frame
	fnFrame := &Frame{
		code:      fn.Body,
		constants: fn.Constants,
		locals:    locals,
		offset:    0,
		previous:  f,
		stack:     newStack(),
		vm:        f.vm,
	}

	// Push and execute the function's frame
	f.vm.pushFrame(fnFrame)
	f.vm.runFrame(fnFrame)

	// Push the returned value
	f.stack.push(fnFrame.stack.pop())
}

func byteReturn(f *Frame, i bytecode.Instruction) {
	f.offset = len(f.code) - 1
}

func bytePrint(f *Frame, i bytecode.Instruction) {
	fmt.Print(f.stack.pop())
}

func bytePrintln(f *Frame, i bytecode.Instruction) {
	fmt.Println(f.stack.pop())
}

func byteJump(f *Frame, i bytecode.Instruction) {
	f.offset = f.byteToInstructionIndex(int(i.Arg))
}

func byteJumpIfTrue(f *Frame, i bytecode.Instruction) {
	obj := f.stack.pop()

	if object.IsTruthy(obj) {
		f.offset = f.byteToInstructionIndex(int(i.Arg))
	}
}

func byteJumpIfFalse(f *Frame, i bytecode.Instruction) {
	obj := f.stack.pop()

	if !object.IsTruthy(obj) {
		f.offset = f.byteToInstructionIndex(int(i.Arg))
	}
}

func byteMakeArray(f *Frame, i bytecode.Instruction) {
	elems := make([]object.Object, i.Arg)

	for n := int(i.Arg) - 1; n >= 0; n-- {
		elems[n] = f.stack.pop()
	}

	f.stack.push(&object.Array{
		Value: elems,
	})
}

func byteMakeTuple(f *Frame, i bytecode.Instruction) {
	elems := make([]object.Object, i.Arg)

	for n := int(i.Arg) - 1; n >= 0; n-- {
		elems[n] = f.stack.pop()
	}

	f.stack.push(&object.Tuple{
		Value: elems,
	})
}

func byteMakeMap(f *Frame, i bytecode.Instruction) {
	keys := make(map[string]object.Object, i.Arg)
	values := make(map[string]object.Object, i.Arg)

	for n := 0; n < int(i.Arg); n++ {
		val, key := f.stack.pop(), f.stack.pop()

		hasher, ok := key.(object.Hasher)
		if !ok {
			f.vm.Error = fmt.Errorf("evaluation: non-hashable type as map key: %s", key.Type())
			return
		}

		hash := hasher.Hash()

		keys[hash] = key
		values[hash] = val
	}

	obj := &object.Map{
		Keys:   keys,
		Values: values,
	}

	f.stack.push(obj)
}
<commit_msg>Implement index operator evaluation<commit_after>package vm

import (
	"errors"
	"fmt"
	"math"

	"github.com/Zac-Garby/pluto/ast"
	"github.com/Zac-Garby/pluto/bytecode"
	"github.com/Zac-Garby/pluto/object"
)

// Effector is a function which performs a particular instruction
type Effector func(f *Frame, i bytecode.Instruction)

var effectors map[byte]Effector

// Effectors returns a map of bytecodes and their
// effectors. Wrapped in a function to stop an
// initialization loop.
func Effectors() map[byte]Effector {
	if len(effectors) == 0 {
		effectors = map[byte]Effector{
			bytecode.Pop: bytePop,
			bytecode.Dup: byteDup,

			bytecode.LoadConst: byteLoadConst,
			bytecode.LoadName:  byteLoadName,
			bytecode.StoreName: byteStoreName,
			bytecode.LoadField: byteLoadField,

			bytecode.UnaryInvert: bytePrefix,
			bytecode.UnaryNegate: bytePrefix,
			bytecode.UnaryNoOp:   bytePrefix,

			bytecode.BinaryAdd:      byteInfix,
			bytecode.BinarySubtract: byteInfix,
			bytecode.BinaryMultiply: byteInfix,
			bytecode.BinaryDivide:   byteInfix,
			bytecode.BinaryExponent: byteInfix,
			bytecode.BinaryFloorDiv: byteInfix,
			bytecode.BinaryMod:      byteInfix,
			bytecode.BinaryBitOr:    byteInfix,
			bytecode.BinaryBitAnd:   byteInfix,
			bytecode.BinaryEquals:   byteEquals,
			bytecode.BinaryNotEqual: byteNotEqual,
			bytecode.BinaryLessThan: bincmp,
			bytecode.BinaryMoreThan: bincmp,
			bytecode.BinaryLessEq:   bincmp,
			bytecode.BinaryMoreEq:   bincmp,

			bytecode.Call:   byteCall,
			bytecode.Return: byteReturn,

			bytecode.Print:   bytePrint,
			bytecode.Println: bytePrintln,

			bytecode.Jump:        byteJump,
			bytecode.JumpIfTrue:  byteJumpIfTrue,
			bytecode.JumpIfFalse: byteJumpIfFalse,

			bytecode.MakeArray: byteMakeArray,
			bytecode.MakeTuple: byteMakeTuple,
			bytecode.MakeMap:   byteMakeMap,
		}
	}

	return effectors
}

func bytePop(f *Frame, i bytecode.Instruction) {
	f.stack.pop()
}

func byteDup(f *Frame, i bytecode.Instruction) {
	f.stack.dup()
}

func byteLoadConst(f *Frame, i bytecode.Instruction) {
	f.stack.push(f.constants[i.Arg])
}

func byteLoadName(f *Frame, i bytecode.Instruction) {
	name, ok := f.getName(i.Arg)
	if !ok {
		f.vm.Error = errors.New("evaluation: internal: name not found")
		return
	}

	val, ok := f.searchName(name)
	if !ok {
		f.vm.Error = fmt.Errorf("evaluation: name %s not found in the current scope", name)
		return
	}

	f.stack.push(val)
}

func byteStoreName(f *Frame, i bytecode.Instruction) {
	name, ok := f.getName(i.Arg)
	if !ok {
		f.vm.Error = errors.New("evaluation: internal: name not found")
		return
	}

	f.locals.Define(name, f.stack.pop())
}

func byteLoadField(f *Frame, i bytecode.Instruction) {
	field, obj := f.stack.pop(), f.stack.pop()

	var val object.Object

	if col, ok := obj.(object.Collection); ok {
		if index, ok := field.(object.Numeric); ok {
			idx := int(index.Float64())

			val = col.GetIndex(idx)
		} else {
			f.vm.Error = fmt.Errorf("evaluation: non-numeric type %s used to index a collection", field.Type())
			return
		}
	} else if cont, ok := obj.(object.Container); ok {
		val = cont.Get(field)
	} else {
		f.vm.Error = fmt.Errorf("evaluation: cannot index type %s", obj.Type())
	}

	f.stack.push(val)
}

func bytePrefix(f *Frame, i bytecode.Instruction) {
	right := f.stack.pop()

	if i.Code == bytecode.UnaryInvert {
		f.stack.push(object.BoolObj(!object.IsTruthy(right)))
		return
	}

	if n, ok := right.(object.Numeric); ok {
		val := n.Float64()
		f.stack.push(numPrefix(i.Code, val))
	} else {
		f.vm.Error = errors.New("evaluation: prefix r-value of invalid type")
	}
}

func numPrefix(opcode byte, val float64) object.Object {
	switch opcode {
	case bytecode.UnaryNegate:
		val = -val
	case bytecode.UnaryNoOp:
		// val = val
	}

	return &object.Number{Value: val}
}

func byteInfix(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()

	if n, ok := left.(object.Numeric); ok {
		if m, ok := right.(object.Numeric); ok {
			f.stack.push(numInfix(f, i.Code, n.Float64(), m.Float64()))
		} else if m, ok := right.(object.Collection); ok {
			f.stack.push(numColInfix(f, i.Code, n.Float64(), m))
		} else {
			f.vm.Error = errors.New("evaluation: infix r-value of invalid type when l-value is <number>")
			return
		}
	} else if n, ok := left.(object.Collection); ok {
		if m, ok := right.(object.Numeric); ok {
			f.stack.push(numColInfix(f, i.Code, m.Float64(), n))
		} else if m, ok := right.(object.Collection); ok {
			f.stack.push(colInfix(f, i.Code, n, m))
		} else {
			f.vm.Error = errors.New("evaluation: infix r-value of invalid type when l-value is a collection")
		}
	} else {
		f.vm.Error = errors.New("evaluation: infix l-value of invalid type")
		return
	}
}

func numInfix(f *Frame, opcode byte, left, right float64) object.Object {
	var val float64

	switch opcode {
	case bytecode.BinaryAdd:
		val = left + right
	case bytecode.BinarySubtract:
		val = left - right
	case bytecode.BinaryMultiply:
		val = left * right
	case bytecode.BinaryDivide:
		val = left / right
	case bytecode.BinaryExponent:
		val = math.Pow(left, right)
	case bytecode.BinaryFloorDiv:
		val = math.Floor(left / right)
	case bytecode.BinaryMod:
		val = math.Mod(left, right)
	case bytecode.BinaryBitOr:
		val = float64(int64(left) | int64(right))
	case bytecode.BinaryBitAnd:
		val = float64(int64(left) & int64(right))
	default:
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for two numbers", op)
	}

	return &object.Number{Value: val}
}

func numColInfix(f *Frame, opcode byte, left float64, right object.Collection) object.Object {
	var (
		result   []object.Object
		elements = right.Elements()
	)

	if opcode == bytecode.BinaryMultiply {
		for i := 0; i < int(left); i++ {
			result = append(result, elements...)
		}
	} else {
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for a collection and a number", op)
	}

	col, _ := object.MakeCollection(right.Type(), result)
	return col
}

func colInfix(f *Frame, opcode byte, left, right object.Collection) object.Object {
	var (
		lefts  = left.Elements()
		rights = right.Elements()
		elems  []object.Object
	)

	switch opcode {
	case bytecode.BinaryAdd:
		elems = append(lefts, rights...)
	case bytecode.BinarySubtract:
		for _, el := range lefts {
			for _, rel := range rights {
				if el.Equals(rel) {
					goto next
				}
			}

			elems = append(elems, el)
		next:
		}
	case bytecode.BinaryBitOr:
		for _, el := range append(lefts, rights...) {
			unique := true

			for _, rel := range elems {
				if el.Equals(rel) {
					unique = false
					break
				}
			}

			if unique {
				elems = append(elems, el)
			}
		}
	case bytecode.BinaryBitAnd:
		for _, el := range lefts {
			both := false

			for _, rel := range rights {
				if el.Equals(rel) {
					both = true
					break
				}
			}

			if both {
				elems = append(elems, el)
			}
		}
	default:
		op := bytecode.Instructions[opcode].Name[7:]
		f.vm.Error = fmt.Errorf("evaluation: operator %s not supported for two collections", op)
	}

	col, _ := object.MakeCollection(left.Type(), elems)
	return col
}

func bincmp(f *Frame, i bytecode.Instruction) {
	f.byteToInstructionIndex(int(i.Arg))

	b, a := f.stack.pop(), f.stack.pop()

	n, ok := a.(object.Numeric)
	if !ok {
		f.vm.Error = errors.New("evaluation: non-numeric value in numeric binary expression")
		return
	}

	m, ok := b.(object.Numeric)
	if !ok {
		f.vm.Error = errors.New("evaluation: non-numeric value in numeric binary expression")
		return
	}

	lval := n.Float64()
	rval := m.Float64()

	var result bool

	switch i.Code {
	case bytecode.BinaryLessThan:
		result = lval < rval
	case bytecode.BinaryMoreThan:
		result = lval > rval
	case bytecode.BinaryLessEq:
		result = lval <= rval
	case bytecode.BinaryMoreEq:
		result = lval >= rval
	}

	f.stack.push(&object.Boolean{Value: result})
}

func byteEquals(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()
	eq := left.Equals(right)

	f.stack.push(object.BoolObj(eq))
}

func byteNotEqual(f *Frame, i bytecode.Instruction) {
	right, left := f.stack.pop(), f.stack.pop()
	eq := left.Equals(right)

	f.stack.push(object.BoolObj(!eq))
}

func byteCall(f *Frame, i bytecode.Instruction) {
	pattern := f.locals.Patterns[i.Arg]

	fn := f.locals.Functions.SearchString(pattern)
	if fn == nil {
		f.vm.Error = fmt.Errorf("evaluation: function '%s' not found in the current scope", pattern)
		return
	}

	locals := f.locals

	for _, item := range fn.Pattern {
		if param, ok := item.(*ast.Parameter); ok {
			// Found a parameter

			locals.Define(param.Name, f.stack.pop())
		}
	}

	// Create the function's frame
	fnFrame := &Frame{
		code:      fn.Body,
		constants: fn.Constants,
		locals:    locals,
		offset:    0,
		previous:  f,
		stack:     newStack(),
		vm:        f.vm,
	}

	// Push and execute the function's frame
	f.vm.pushFrame(fnFrame)
	f.vm.runFrame(fnFrame)

	// Push the returned value
	f.stack.push(fnFrame.stack.pop())
}

func byteReturn(f *Frame, i bytecode.Instruction) {
	f.offset = len(f.code) - 1
}

func bytePrint(f *Frame, i bytecode.Instruction) {
	fmt.Print(f.stack.pop())
}

func bytePrintln(f *Frame, i bytecode.Instruction) {
	fmt.Println(f.stack.pop())
}

func byteJump(f *Frame, i bytecode.Instruction) {
	f.offset = f.byteToInstructionIndex(int(i.Arg))
}

func byteJumpIfTrue(f *Frame, i bytecode.Instruction) {
	obj := f.stack.pop()

	if object.IsTruthy(obj) {
		f.offset = f.byteToInstructionIndex(int(i.Arg))
	}
}

func byteJumpIfFalse(f *Frame, i bytecode.Instruction) {
	obj := f.stack.pop()

	if !object.IsTruthy(obj) {
		f.offset = f.byteToInstructionIndex(int(i.Arg))
	}
}

func byteMakeArray(f *Frame, i bytecode.Instruction) {
	elems := make([]object.Object, i.Arg)

	for n := int(i.Arg) - 1; n >= 0; n-- {
		elems[n] = f.stack.pop()
	}

	f.stack.push(&object.Array{
		Value: elems,
	})
}

func byteMakeTuple(f *Frame, i bytecode.Instruction) {
	elems := make([]object.Object, i.Arg)

	for n := int(i.Arg) - 1; n >= 0; n-- {
		elems[n] = f.stack.pop()
	}

	f.stack.push(&object.Tuple{
		Value: elems,
	})
}

func byteMakeMap(f *Frame, i bytecode.Instruction) {
	keys := make(map[string]object.Object, i.Arg)
	values := make(map[string]object.Object, i.Arg)

	for n := 0; n < int(i.Arg); n++ {
		val, key := f.stack.pop(), f.stack.pop()

		hasher, ok := key.(object.Hasher)
		if !ok {
			f.vm.Error = fmt.Errorf("evaluation: non-hashable type as map key: %s", key.Type())
			return
		}

		hash := hasher.Hash()

		keys[hash] = key
		values[hash] = val
	}

	obj := &object.Map{
		Keys:   keys,
		Values: values,
	}

	f.stack.push(obj)
}
<|endoftext|>


<commit_before>package gost

import (
	"bufio"
	"crypto/tls"
	"net"
	"net/url"
	"time"

	"github.com/ginuerzh/gosocks4"
	"github.com/ginuerzh/gosocks5"
	"github.com/go-log/log"
)

// Handler is a proxy server handler
type Handler interface {
	Init(options ...HandlerOption)
	Handle(net.Conn)
}

// HandlerOptions describes the options for Handler.
type HandlerOptions struct {
	Addr          string
	Chain         *Chain
	Users         []*url.Userinfo
	Authenticator Authenticator
	TLSConfig     *tls.Config
	Whitelist     *Permissions
	Blacklist     *Permissions
	Strategy      Strategy
	Bypass        *Bypass
	Retries       int
	Timeout       time.Duration
	Resolver      Resolver
	Hosts         *Hosts
	ProbeResist   string
	Node          Node
	Host          string
}

// HandlerOption allows a common way to set handler options.
type HandlerOption func(opts *HandlerOptions)

// AddrHandlerOption sets the Addr option of HandlerOptions.
func AddrHandlerOption(addr string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Addr = addr
	}
}

// ChainHandlerOption sets the Chain option of HandlerOptions.
func ChainHandlerOption(chain *Chain) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Chain = chain
	}
}

// UsersHandlerOption sets the Users option of HandlerOptions.
func UsersHandlerOption(users ...*url.Userinfo) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Users = users
	}
}

// AuthenticatorHandlerOption sets the Authenticator option of HandlerOptions.
func AuthenticatorHandlerOption(au Authenticator) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Authenticator = au
	}
}

// TLSConfigHandlerOption sets the TLSConfig option of HandlerOptions.
func TLSConfigHandlerOption(config *tls.Config) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.TLSConfig = config
	}
}

// WhitelistHandlerOption sets the Whitelist option of HandlerOptions.
func WhitelistHandlerOption(whitelist *Permissions) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Whitelist = whitelist
	}
}

// BlacklistHandlerOption sets the Blacklist option of HandlerOptions.
func BlacklistHandlerOption(blacklist *Permissions) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Blacklist = blacklist
	}
}

// BypassHandlerOption sets the bypass option of HandlerOptions.
func BypassHandlerOption(bypass *Bypass) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Bypass = bypass
	}
}

// StrategyHandlerOption sets the strategy option of HandlerOptions.
func StrategyHandlerOption(strategy Strategy) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Strategy = strategy
	}
}

// RetryHandlerOption sets the retry option of HandlerOptions.
func RetryHandlerOption(retries int) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Retries = retries
	}
}

// TimeoutHandlerOption sets the timeout option of HandlerOptions.
func TimeoutHandlerOption(timeout time.Duration) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Timeout = timeout
	}
}

// ResolverHandlerOption sets the resolver option of HandlerOptions.
func ResolverHandlerOption(resolver Resolver) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Resolver = resolver
	}
}

// HostsHandlerOption sets the Hosts option of HandlerOptions.
func HostsHandlerOption(hosts *Hosts) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Hosts = hosts
	}
}

// ProbeResistHandlerOption adds the probe resistance for HTTP proxy.
func ProbeResistHandlerOption(pr string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.ProbeResist = pr
	}
}

// NodeHandlerOption set the server node for server handler.
func NodeHandlerOption(node Node) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Node = node
	}
}

// HostHandlerOption sets the target host for SNI proxy.
func HostHandlerOption(host string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Host = host
	}
}

type autoHandler struct {
	options *HandlerOptions
}

// AutoHandler creates a server Handler for auto proxy server.
func AutoHandler(opts ...HandlerOption) Handler {
	h := &autoHandler{}
	h.Init(opts...)
	return h
}

func (h *autoHandler) Init(options ...HandlerOption) {
	if h.options == nil {
		h.options = &HandlerOptions{}
	}
	for _, opt := range options {
		opt(h.options)
	}
}

func (h *autoHandler) Handle(conn net.Conn) {
	br := bufio.NewReader(conn)
	b, err := br.Peek(1)
	if err != nil {
		log.Logf("[auto] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		conn.Close()
		return
	}

	cc := &bufferdConn{Conn: conn, br: br}
	var handler Handler
	switch b[0] {
	case gosocks4.Ver4:
		// SOCKS4(a) does not suppport authentication method,
		// so we ignore it when credentials are specified for security reason.
		if len(h.options.Users) > 0 {
			cc.Close()
			return
		}
		handler = &socks4Handler{options: h.options}
	case gosocks5.Ver5: // socks5
		handler = &socks5Handler{options: h.options}
	default: // http
		handler = &httpHandler{options: h.options}
	}
	handler.Init()
	handler.Handle(cc)
}

type bufferdConn struct {
	net.Conn
	br *bufio.Reader
}

func (c *bufferdConn) Read(b []byte) (int, error) {
	return c.br.Read(b)
}
<commit_msg>fix test cases<commit_after>package gost

import (
	"bufio"
	"crypto/tls"
	"net"
	"net/url"
	"time"

	"github.com/ginuerzh/gosocks4"
	"github.com/ginuerzh/gosocks5"
	"github.com/go-log/log"
)

// Handler is a proxy server handler
type Handler interface {
	Init(options ...HandlerOption)
	Handle(net.Conn)
}

// HandlerOptions describes the options for Handler.
type HandlerOptions struct {
	Addr          string
	Chain         *Chain
	Users         []*url.Userinfo
	Authenticator Authenticator
	TLSConfig     *tls.Config
	Whitelist     *Permissions
	Blacklist     *Permissions
	Strategy      Strategy
	Bypass        *Bypass
	Retries       int
	Timeout       time.Duration
	Resolver      Resolver
	Hosts         *Hosts
	ProbeResist   string
	Node          Node
	Host          string
}

// HandlerOption allows a common way to set handler options.
type HandlerOption func(opts *HandlerOptions)

// AddrHandlerOption sets the Addr option of HandlerOptions.
func AddrHandlerOption(addr string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Addr = addr
	}
}

// ChainHandlerOption sets the Chain option of HandlerOptions.
func ChainHandlerOption(chain *Chain) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Chain = chain
	}
}

// UsersHandlerOption sets the Users option of HandlerOptions.
func UsersHandlerOption(users ...*url.Userinfo) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Users = users

		kvs := make(map[string]string)
		for _, u := range users {
			if u != nil {
				kvs[u.Username()], _ = u.Password()
			}
		}
		if len(kvs) > 0 {
			opts.Authenticator = NewLocalAuthenticator(kvs)
		}
	}
}

// AuthenticatorHandlerOption sets the Authenticator option of HandlerOptions.
func AuthenticatorHandlerOption(au Authenticator) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Authenticator = au
	}
}

// TLSConfigHandlerOption sets the TLSConfig option of HandlerOptions.
func TLSConfigHandlerOption(config *tls.Config) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.TLSConfig = config
	}
}

// WhitelistHandlerOption sets the Whitelist option of HandlerOptions.
func WhitelistHandlerOption(whitelist *Permissions) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Whitelist = whitelist
	}
}

// BlacklistHandlerOption sets the Blacklist option of HandlerOptions.
func BlacklistHandlerOption(blacklist *Permissions) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Blacklist = blacklist
	}
}

// BypassHandlerOption sets the bypass option of HandlerOptions.
func BypassHandlerOption(bypass *Bypass) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Bypass = bypass
	}
}

// StrategyHandlerOption sets the strategy option of HandlerOptions.
func StrategyHandlerOption(strategy Strategy) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Strategy = strategy
	}
}

// RetryHandlerOption sets the retry option of HandlerOptions.
func RetryHandlerOption(retries int) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Retries = retries
	}
}

// TimeoutHandlerOption sets the timeout option of HandlerOptions.
func TimeoutHandlerOption(timeout time.Duration) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Timeout = timeout
	}
}

// ResolverHandlerOption sets the resolver option of HandlerOptions.
func ResolverHandlerOption(resolver Resolver) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Resolver = resolver
	}
}

// HostsHandlerOption sets the Hosts option of HandlerOptions.
func HostsHandlerOption(hosts *Hosts) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Hosts = hosts
	}
}

// ProbeResistHandlerOption adds the probe resistance for HTTP proxy.
func ProbeResistHandlerOption(pr string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.ProbeResist = pr
	}
}

// NodeHandlerOption set the server node for server handler.
func NodeHandlerOption(node Node) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Node = node
	}
}

// HostHandlerOption sets the target host for SNI proxy.
func HostHandlerOption(host string) HandlerOption {
	return func(opts *HandlerOptions) {
		opts.Host = host
	}
}

type autoHandler struct {
	options *HandlerOptions
}

// AutoHandler creates a server Handler for auto proxy server.
func AutoHandler(opts ...HandlerOption) Handler {
	h := &autoHandler{}
	h.Init(opts...)
	return h
}

func (h *autoHandler) Init(options ...HandlerOption) {
	if h.options == nil {
		h.options = &HandlerOptions{}
	}
	for _, opt := range options {
		opt(h.options)
	}
}

func (h *autoHandler) Handle(conn net.Conn) {
	br := bufio.NewReader(conn)
	b, err := br.Peek(1)
	if err != nil {
		log.Logf("[auto] %s - %s: %s", conn.RemoteAddr(), conn.LocalAddr(), err)
		conn.Close()
		return
	}

	cc := &bufferdConn{Conn: conn, br: br}
	var handler Handler
	switch b[0] {
	case gosocks4.Ver4:
		// SOCKS4(a) does not suppport authentication method,
		// so we ignore it when credentials are specified for security reason.
		if len(h.options.Users) > 0 {
			cc.Close()
			return
		}
		handler = &socks4Handler{options: h.options}
	case gosocks5.Ver5: // socks5
		handler = &socks5Handler{options: h.options}
	default: // http
		handler = &httpHandler{options: h.options}
	}
	handler.Init()
	handler.Handle(cc)
}

type bufferdConn struct {
	net.Conn
	br *bufio.Reader
}

func (c *bufferdConn) Read(b []byte) (int, error) {
	return c.br.Read(b)
}
<|endoftext|>


<commit_before>package fire

import "time"

// A Callback is called during the request processing flow of a controller.
type Callback struct {
	// The matcher that decides whether the callback should be run.
	Matcher Matcher

	// The handler handler that gets executed with the context.
	//
	// If returned errors are marked with Safe() they will be included in the
	// returned JSON-API error.
	Handler Handler
}

// L is a short-hand type to create a list of callbacks.
type L []*Callback

// C is a short-hand function to construct a callback. It will also add tracing
// code around the execution of the callback.
func C(name string, m Matcher, h Handler) *Callback {
	// panic if matcher or handler is not set
	if m == nil || h == nil {
		panic("fire: missing matcher or handler")
	}

	return &Callback{
		Matcher: m,
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// An Action defines a collection or resource action.
type Action struct {
	// The allowed methods for this action.
	Methods []string

	// BodyLimit defines the maximum allowed size of the request body. The
	// serve.DataSize helper can be used to set the value.
	//
	// Default: 8M.
	BodyLimit uint64

	// Timeout defines the time after which the context is cancelled and
	// processing of the action should be stopped.
	//
	// Default: 30s.
	Timeout time.Duration

	// The handler handler that gets executed with the context.
	//
	// If returned errors are marked with Safe() they will be included in the
	// returned JSON-API error.
	Handler Handler
}

// M is a short-hand type to create a map of actions.
type M map[string]*Action

// A is a short-hand function to construct an action.
func A(name string, methods []string, bodyLimit uint64, h Handler) *Action {
	// panic if methods or handler is not set
	if len(methods) == 0 || h == nil {
		panic("fire: missing methods or handler")
	}

	return &Action{
		Methods:   methods,
		BodyLimit: bodyLimit,
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// Handler is function that takes a context, mutates is to modify the behaviour
// and response or return an error.
type Handler func(*Context) error

// Matcher is a function that makes an assessment of a context and decides whether
// a modification should be applied in the future.
type Matcher func(*Context) bool

// All will match all contexts.
func All() Matcher {
	return func(ctx *Context) bool {
		return true
	}
}

// Only will match if the operation is present in the provided list.
func Only(ops ...Operation) Matcher {
	return func(ctx *Context) bool {
		// allow if operation is listed
		for _, op := range ops {
			if op == ctx.Operation {
				return true
			}
		}

		return false
	}
}

// Except will match if the operation is not present in the provided list.
func Except(ops ...Operation) Matcher {
	return func(ctx *Context) bool {
		// disallow if operation is listed
		for _, op := range ops {
			if op == ctx.Operation {
				return false
			}
		}

		return true
	}
}
<commit_msg>use type aliasing<commit_after>package fire

import "time"

// A Callback is called during the request processing flow of a controller.
type Callback struct {
	// The matcher that decides whether the callback should be run.
	Matcher Matcher

	// The handler handler that gets executed with the context.
	//
	// If returned errors are marked with Safe() they will be included in the
	// returned JSON-API error.
	Handler Handler
}

// L is a short-hand type to create a list of callbacks.
type L = []*Callback

// C is a short-hand function to construct a callback. It will also add tracing
// code around the execution of the callback.
func C(name string, m Matcher, h Handler) *Callback {
	// panic if matcher or handler is not set
	if m == nil || h == nil {
		panic("fire: missing matcher or handler")
	}

	return &Callback{
		Matcher: m,
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// An Action defines a collection or resource action.
type Action struct {
	// The allowed methods for this action.
	Methods []string

	// BodyLimit defines the maximum allowed size of the request body. The
	// serve.DataSize helper can be used to set the value.
	//
	// Default: 8M.
	BodyLimit uint64

	// Timeout defines the time after which the context is cancelled and
	// processing of the action should be stopped.
	//
	// Default: 30s.
	Timeout time.Duration

	// The handler handler that gets executed with the context.
	//
	// If returned errors are marked with Safe() they will be included in the
	// returned JSON-API error.
	Handler Handler
}

// M is a short-hand type to create a map of actions.
type M = map[string]*Action

// A is a short-hand function to construct an action.
func A(name string, methods []string, bodyLimit uint64, h Handler) *Action {
	// panic if methods or handler is not set
	if len(methods) == 0 || h == nil {
		panic("fire: missing methods or handler")
	}

	return &Action{
		Methods:   methods,
		BodyLimit: bodyLimit,
		Handler: func(ctx *Context) error {
			// begin trace
			ctx.Tracer.Push(name)

			// call handler
			err := h(ctx)
			if err != nil {
				return err
			}

			// finish trace
			ctx.Tracer.Pop()

			return nil
		},
	}
}

// Handler is function that takes a context, mutates is to modify the behaviour
// and response or return an error.
type Handler func(*Context) error

// Matcher is a function that makes an assessment of a context and decides whether
// a modification should be applied in the future.
type Matcher func(*Context) bool

// All will match all contexts.
func All() Matcher {
	return func(ctx *Context) bool {
		return true
	}
}

// Only will match if the operation is present in the provided list.
func Only(ops ...Operation) Matcher {
	return func(ctx *Context) bool {
		// allow if operation is listed
		for _, op := range ops {
			if op == ctx.Operation {
				return true
			}
		}

		return false
	}
}

// Except will match if the operation is not present in the provided list.
func Except(ops ...Operation) Matcher {
	return func(ctx *Context) bool {
		// disallow if operation is listed
		for _, op := range ops {
			if op == ctx.Operation {
				return false
			}
		}

		return true
	}
}
<|endoftext|>