<commit_before>package config

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"
	"time"

	"golang.org/x/crypto/scrypt"

	"github.com/lxc/lxd/lxd/config"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared/validate"
)

// Config holds cluster-wide configuration values.
type Config struct {
	tx *db.ClusterTx // DB transaction the values in this config are bound to.
	m  config.Map    // Low-level map holding the config values.
}

// Load loads a new Config object with the current cluster configuration
// values fetched from the database.
func Load(tx *db.ClusterTx) (*Config, error) {
	// Load current raw values from the database, any error is fatal.
	values, err := tx.Config()
	if err != nil {
		return nil, fmt.Errorf("cannot fetch node config from database: %w", err)
	}

	m, err := config.SafeLoad(ConfigSchema, values)
	if err != nil {
		return nil, fmt.Errorf("failed to load node config: %w", err)
	}

	return &Config{tx: tx, m: m}, nil
}

// MetricsAuthentication checks whether metrics API requires authentication.
func (c *Config) MetricsAuthentication() bool {
	return c.m.GetBool("core.metrics_authentication")
}

// BGPASN returns the BGP ASN setting.
func (c *Config) BGPASN() int64 {
	return c.m.GetInt64("core.bgp_asn")
}

// HTTPSAllowedHeaders returns the relevant CORS setting.
func (c *Config) HTTPSAllowedHeaders() string {
	return c.m.GetString("core.https_allowed_headers")
}

// HTTPSAllowedMethods returns the relevant CORS setting.
func (c *Config) HTTPSAllowedMethods() string {
	return c.m.GetString("core.https_allowed_methods")
}

// HTTPSAllowedOrigin returns the relevant CORS setting.
func (c *Config) HTTPSAllowedOrigin() string {
	return c.m.GetString("core.https_allowed_origin")
}

// HTTPSAllowedCredentials returns the relevant CORS setting.
func (c *Config) HTTPSAllowedCredentials() bool {
	return c.m.GetBool("core.https_allowed_credentials")
}

// TrustPassword returns the LXD trust password for authenticating clients.
func (c *Config) TrustPassword() string {
	return c.m.GetString("core.trust_password")
}

// TrustCACertificates returns whether client certificates are checked
// against a CA.
func (c *Config) TrustCACertificates() bool {
	return c.m.GetBool("core.trust_ca_certificates")
}

// CandidServer returns all the Candid settings needed to connect to a server.
func (c *Config) CandidServer() (string, string, int64, string) {
	return c.m.GetString("candid.api.url"),
		c.m.GetString("candid.api.key"),
		c.m.GetInt64("candid.expiry"),
		c.m.GetString("candid.domains")
}

// RBACServer returns all the Candid settings needed to connect to a server.
func (c *Config) RBACServer() (string, string, int64, string, string, string, string) {
	return c.m.GetString("rbac.api.url"),
		c.m.GetString("rbac.api.key"),
		c.m.GetInt64("rbac.expiry"),
		c.m.GetString("rbac.agent.url"),
		c.m.GetString("rbac.agent.username"),
		c.m.GetString("rbac.agent.private_key"),
		c.m.GetString("rbac.agent.public_key")
}

// ProxyHTTPS returns the configured HTTPS proxy, if any.
func (c *Config) ProxyHTTPS() string {
	return c.m.GetString("core.proxy_https")
}

// ProxyHTTP returns the configured HTTP proxy, if any.
func (c *Config) ProxyHTTP() string {
	return c.m.GetString("core.proxy_http")
}

// ProxyIgnoreHosts returns the configured ignore-hosts proxy setting, if any.
func (c *Config) ProxyIgnoreHosts() string {
	return c.m.GetString("core.proxy_ignore_hosts")
}

// HTTPSTrustedProxy returns the configured HTTPS trusted proxy setting, if any.
func (c *Config) HTTPSTrustedProxy() string {
	return c.m.GetString("core.https_trusted_proxy")
}

// MAASController the configured MAAS url and key, if any.
func (c *Config) MAASController() (string, string) {
	url := c.m.GetString("maas.api.url")
	key := c.m.GetString("maas.api.key")
	return url, key
}

// OfflineThreshold returns the configured heartbeat threshold, i.e. the
// number of seconds before after which an unresponsive node is considered
// offline..
func (c *Config) OfflineThreshold() time.Duration {
	n := c.m.GetInt64("cluster.offline_threshold")
	return time.Duration(n) * time.Second
}

// ImagesMinimalReplica returns the numbers of nodes for cluster images replication.
func (c *Config) ImagesMinimalReplica() int64 {
	return c.m.GetInt64("cluster.images_minimal_replica")
}

// MaxVoters returns the maximum number of members in a cluster that will be
// assigned the voter role.
func (c *Config) MaxVoters() int64 {
	return c.m.GetInt64("cluster.max_voters")
}

// MaxStandBy returns the maximum number of standby members in a cluster that
// will be assigned the stand-by role.
func (c *Config) MaxStandBy() int64 {
	return c.m.GetInt64("cluster.max_standby")
}

// ShutdownTimeout returns the number of minutes to wait for running operation to complete
// before LXD server shut down.
func (c *Config) ShutdownTimeout() time.Duration {
	n := c.m.GetInt64("core.shutdown_timeout")
	return time.Duration(n) * time.Minute
}

// ImagesDefaultArchitecture returns the default architecture.
func (c *Config) ImagesDefaultArchitecture() string {
	return c.m.GetString("images.default_architecture")
}

// InstancesNICHostname returns hostname mode to use for instance NICs.
func (c *Config) InstancesNICHostname() string {
	return c.m.GetString("instances.nic.host_name")
}

// Dump current configuration keys and their values. Keys with values matching
// their defaults are omitted.
func (c *Config) Dump() map[string]any {
	return c.m.Dump()
}

// Replace the current configuration with the given values.
//
// Return what has actually changed.
func (c *Config) Replace(values map[string]any) (map[string]string, error) {
	return c.update(values)
}

// Patch changes only the configuration keys in the given map.
//
// Return what has actually changed.
func (c *Config) Patch(patch map[string]any) (map[string]string, error) {
	values := c.Dump() // Use current values as defaults
	for name, value := range patch {
		values[name] = value
	}

	return c.update(values)
}

func (c *Config) update(values map[string]any) (map[string]string, error) {
	changed, err := c.m.Change(values)
	if err != nil {
		return nil, err
	}

	err = c.tx.UpdateClusterConfig(changed)
	if err != nil {
		return nil, fmt.Errorf("cannot persist configuration changes: %w", err)
	}

	return changed, nil
}

// GetString is a convenience for loading the cluster configuration and
// returning the value of a particular key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetString(cluster *db.Cluster, key string) (string, error) {
	config, err := configGet(cluster)
	if err != nil {
		return "", err
	}

	return config.m.GetString(key), nil
}

// GetBool is a convenience for loading the cluster configuration and
// returning the value of a particular boolean key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetBool(cluster *db.Cluster, key string) (bool, error) {
	config, err := configGet(cluster)
	if err != nil {
		return false, err
	}

	return config.m.GetBool(key), nil
}

// GetInt64 is a convenience for loading the cluster configuration and
// returning the value of a particular key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetInt64(cluster *db.Cluster, key string) (int64, error) {
	config, err := configGet(cluster)
	if err != nil {
		return 0, err
	}

	return config.m.GetInt64(key), nil
}

func configGet(cluster *db.Cluster) (*Config, error) {
	var config *Config
	err := cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		var err error
		config, err = Load(tx)
		return err
	})
	return config, err
}

// ConfigSchema defines available server configuration keys.
var ConfigSchema = config.Schema{
	"backups.compression_algorithm":  {Default: "gzip", Validator: validate.IsCompressionAlgorithm},
	"cluster.offline_threshold":      {Type: config.Int64, Default: offlineThresholdDefault(), Validator: offlineThresholdValidator},
	"cluster.images_minimal_replica": {Type: config.Int64, Default: "3", Validator: imageMinimalReplicaValidator},
	"cluster.max_voters":             {Type: config.Int64, Default: "3", Validator: maxVotersValidator},
	"cluster.max_standby":            {Type: config.Int64, Default: "2", Validator: maxStandByValidator},
	"core.metrics_authentication":    {Type: config.Bool, Default: "true"},
	"core.bgp_asn":                   {Type: config.Int64, Default: "0", Validator: validate.Optional(validate.IsInRange(0, 4294967294))},
	"core.https_allowed_headers":     {},
	"core.https_allowed_methods":     {},
	"core.https_allowed_origin":      {},
	"core.https_allowed_credentials": {Type: config.Bool},
	"core.https_trusted_proxy":       {},
	"core.proxy_http":                {},
	"core.proxy_https":               {},
	"core.proxy_ignore_hosts":        {},
	"core.shutdown_timeout":          {Type: config.Int64, Default: "5"},
	"core.trust_password":            {Hidden: true, Setter: passwordSetter},
	"core.trust_ca_certificates":     {Type: config.Bool},
	"candid.api.key":                 {},
	"candid.api.url":                 {},
	"candid.domains":                 {},
	"candid.expiry":                  {Type: config.Int64, Default: "3600"},
	"images.auto_update_cached":      {Type: config.Bool, Default: "true"},
	"images.auto_update_interval":    {Type: config.Int64, Default: "6"},
	"images.compression_algorithm":   {Default: "gzip", Validator: validate.IsCompressionAlgorithm},
	"images.default_architecture":    {Validator: validate.Optional(validate.IsArchitecture)},
	"images.remote_cache_expiry":     {Type: config.Int64, Default: "10"},
	"instances.nic.host_name":        {Validator: validate.Optional(validate.IsOneOf("random", "mac"))},
	"maas.api.key":                   {},
	"maas.api.url":                   {},
	"rbac.agent.url":                 {},
	"rbac.agent.username":            {},
	"rbac.agent.private_key":         {},
	"rbac.agent.public_key":          {},
	"rbac.api.expiry":                {Type: config.Int64, Default: "3600"},
	"rbac.api.key":                   {},
	"rbac.api.url":                   {},
	"rbac.expiry":                    {Type: config.Int64, Default: "3600"},

	// OVN networking global keys.
	"network.ovn.integration_bridge":    {Default: "br-int"},
	"network.ovn.northbound_connection": {Default: "unix:/var/run/ovn/ovnnb_db.sock"},
}

func offlineThresholdDefault() string {
	return strconv.Itoa(db.DefaultOfflineThreshold)
}

func offlineThresholdValidator(value string) error {
	minThreshold := 10

	// Ensure that the given value is greater than the heartbeat interval,
	// which is the lower bound granularity of the offline check.
	threshold, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Offline threshold is not a number")
	}

	if threshold <= minThreshold {
		return fmt.Errorf("Value must be greater than '%d'", minThreshold)
	}

	return nil
}

func imageMinimalReplicaValidator(value string) error {
	count, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Minimal image replica count is not a number")
	}

	if count < 1 && count != -1 {
		return fmt.Errorf("Invalid value for image replica count")
	}

	return nil
}

func maxVotersValidator(value string) error {
	n, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Value is not a number")
	}

	if n < 3 || n%2 != 1 {
		return fmt.Errorf("Value must be an odd number equal to or higher than 3")
	}

	return nil
}

func maxStandByValidator(value string) error {
	n, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Value is not a number")
	}

	if n < 0 || n > 5 {
		return fmt.Errorf("Value must be between 0 and 5")
	}

	return nil
}

func passwordSetter(value string) (string, error) {
	// Nothing to do on unset
	if value == "" {
		return value, nil
	}

	// Hash the password
	buf := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		return "", err
	}

	hash, err := scrypt.Key([]byte(value), buf, 1<<14, 8, 1, 64)
	if err != nil {
		return "", err
	}

	buf = append(buf, hash...)
	value = hex.EncodeToString(buf)

	return value, nil
}
<commit_msg>lxd/cluster/config/config: Adds BackupsCompressionAlgorithm function<commit_after>package config

import (
	"context"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"io"
	"strconv"
	"time"

	"golang.org/x/crypto/scrypt"

	"github.com/lxc/lxd/lxd/config"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/shared/validate"
)

// Config holds cluster-wide configuration values.
type Config struct {
	tx *db.ClusterTx // DB transaction the values in this config are bound to.
	m  config.Map    // Low-level map holding the config values.
}

// Load loads a new Config object with the current cluster configuration
// values fetched from the database.
func Load(tx *db.ClusterTx) (*Config, error) {
	// Load current raw values from the database, any error is fatal.
	values, err := tx.Config()
	if err != nil {
		return nil, fmt.Errorf("cannot fetch node config from database: %w", err)
	}

	m, err := config.SafeLoad(ConfigSchema, values)
	if err != nil {
		return nil, fmt.Errorf("failed to load node config: %w", err)
	}

	return &Config{tx: tx, m: m}, nil
}

// BackupsCompressionAlgorithm returns the compression algorithm to use for backups.
func (c *Config) BackupsCompressionAlgorithm() string {
	return c.m.GetString("backups.compression_algorithm")
}

// MetricsAuthentication checks whether metrics API requires authentication.
func (c *Config) MetricsAuthentication() bool {
	return c.m.GetBool("core.metrics_authentication")
}

// BGPASN returns the BGP ASN setting.
func (c *Config) BGPASN() int64 {
	return c.m.GetInt64("core.bgp_asn")
}

// HTTPSAllowedHeaders returns the relevant CORS setting.
func (c *Config) HTTPSAllowedHeaders() string {
	return c.m.GetString("core.https_allowed_headers")
}

// HTTPSAllowedMethods returns the relevant CORS setting.
func (c *Config) HTTPSAllowedMethods() string {
	return c.m.GetString("core.https_allowed_methods")
}

// HTTPSAllowedOrigin returns the relevant CORS setting.
func (c *Config) HTTPSAllowedOrigin() string {
	return c.m.GetString("core.https_allowed_origin")
}

// HTTPSAllowedCredentials returns the relevant CORS setting.
func (c *Config) HTTPSAllowedCredentials() bool {
	return c.m.GetBool("core.https_allowed_credentials")
}

// TrustPassword returns the LXD trust password for authenticating clients.
func (c *Config) TrustPassword() string {
	return c.m.GetString("core.trust_password")
}

// TrustCACertificates returns whether client certificates are checked
// against a CA.
func (c *Config) TrustCACertificates() bool {
	return c.m.GetBool("core.trust_ca_certificates")
}

// CandidServer returns all the Candid settings needed to connect to a server.
func (c *Config) CandidServer() (string, string, int64, string) {
	return c.m.GetString("candid.api.url"),
		c.m.GetString("candid.api.key"),
		c.m.GetInt64("candid.expiry"),
		c.m.GetString("candid.domains")
}

// RBACServer returns all the Candid settings needed to connect to a server.
func (c *Config) RBACServer() (string, string, int64, string, string, string, string) {
	return c.m.GetString("rbac.api.url"),
		c.m.GetString("rbac.api.key"),
		c.m.GetInt64("rbac.expiry"),
		c.m.GetString("rbac.agent.url"),
		c.m.GetString("rbac.agent.username"),
		c.m.GetString("rbac.agent.private_key"),
		c.m.GetString("rbac.agent.public_key")
}

// ProxyHTTPS returns the configured HTTPS proxy, if any.
func (c *Config) ProxyHTTPS() string {
	return c.m.GetString("core.proxy_https")
}

// ProxyHTTP returns the configured HTTP proxy, if any.
func (c *Config) ProxyHTTP() string {
	return c.m.GetString("core.proxy_http")
}

// ProxyIgnoreHosts returns the configured ignore-hosts proxy setting, if any.
func (c *Config) ProxyIgnoreHosts() string {
	return c.m.GetString("core.proxy_ignore_hosts")
}

// HTTPSTrustedProxy returns the configured HTTPS trusted proxy setting, if any.
func (c *Config) HTTPSTrustedProxy() string {
	return c.m.GetString("core.https_trusted_proxy")
}

// MAASController the configured MAAS url and key, if any.
func (c *Config) MAASController() (string, string) {
	url := c.m.GetString("maas.api.url")
	key := c.m.GetString("maas.api.key")
	return url, key
}

// OfflineThreshold returns the configured heartbeat threshold, i.e. the
// number of seconds before after which an unresponsive node is considered
// offline..
func (c *Config) OfflineThreshold() time.Duration {
	n := c.m.GetInt64("cluster.offline_threshold")
	return time.Duration(n) * time.Second
}

// ImagesMinimalReplica returns the numbers of nodes for cluster images replication.
func (c *Config) ImagesMinimalReplica() int64 {
	return c.m.GetInt64("cluster.images_minimal_replica")
}

// MaxVoters returns the maximum number of members in a cluster that will be
// assigned the voter role.
func (c *Config) MaxVoters() int64 {
	return c.m.GetInt64("cluster.max_voters")
}

// MaxStandBy returns the maximum number of standby members in a cluster that
// will be assigned the stand-by role.
func (c *Config) MaxStandBy() int64 {
	return c.m.GetInt64("cluster.max_standby")
}

// ShutdownTimeout returns the number of minutes to wait for running operation to complete
// before LXD server shut down.
func (c *Config) ShutdownTimeout() time.Duration {
	n := c.m.GetInt64("core.shutdown_timeout")
	return time.Duration(n) * time.Minute
}

// ImagesDefaultArchitecture returns the default architecture.
func (c *Config) ImagesDefaultArchitecture() string {
	return c.m.GetString("images.default_architecture")
}

// InstancesNICHostname returns hostname mode to use for instance NICs.
func (c *Config) InstancesNICHostname() string {
	return c.m.GetString("instances.nic.host_name")
}

// Dump current configuration keys and their values. Keys with values matching
// their defaults are omitted.
func (c *Config) Dump() map[string]any {
	return c.m.Dump()
}

// Replace the current configuration with the given values.
//
// Return what has actually changed.
func (c *Config) Replace(values map[string]any) (map[string]string, error) {
	return c.update(values)
}

// Patch changes only the configuration keys in the given map.
//
// Return what has actually changed.
func (c *Config) Patch(patch map[string]any) (map[string]string, error) {
	values := c.Dump() // Use current values as defaults
	for name, value := range patch {
		values[name] = value
	}

	return c.update(values)
}

func (c *Config) update(values map[string]any) (map[string]string, error) {
	changed, err := c.m.Change(values)
	if err != nil {
		return nil, err
	}

	err = c.tx.UpdateClusterConfig(changed)
	if err != nil {
		return nil, fmt.Errorf("cannot persist configuration changes: %w", err)
	}

	return changed, nil
}

// GetString is a convenience for loading the cluster configuration and
// returning the value of a particular key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetString(cluster *db.Cluster, key string) (string, error) {
	config, err := configGet(cluster)
	if err != nil {
		return "", err
	}

	return config.m.GetString(key), nil
}

// GetBool is a convenience for loading the cluster configuration and
// returning the value of a particular boolean key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetBool(cluster *db.Cluster, key string) (bool, error) {
	config, err := configGet(cluster)
	if err != nil {
		return false, err
	}

	return config.m.GetBool(key), nil
}

// GetInt64 is a convenience for loading the cluster configuration and
// returning the value of a particular key.
//
// It's a deprecated API meant to be used by call sites that are not
// interacting with the database in a transactional way.
func GetInt64(cluster *db.Cluster, key string) (int64, error) {
	config, err := configGet(cluster)
	if err != nil {
		return 0, err
	}

	return config.m.GetInt64(key), nil
}

func configGet(cluster *db.Cluster) (*Config, error) {
	var config *Config
	err := cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		var err error
		config, err = Load(tx)
		return err
	})
	return config, err
}

// ConfigSchema defines available server configuration keys.
var ConfigSchema = config.Schema{
	"backups.compression_algorithm":  {Default: "gzip", Validator: validate.IsCompressionAlgorithm},
	"cluster.offline_threshold":      {Type: config.Int64, Default: offlineThresholdDefault(), Validator: offlineThresholdValidator},
	"cluster.images_minimal_replica": {Type: config.Int64, Default: "3", Validator: imageMinimalReplicaValidator},
	"cluster.max_voters":             {Type: config.Int64, Default: "3", Validator: maxVotersValidator},
	"cluster.max_standby":            {Type: config.Int64, Default: "2", Validator: maxStandByValidator},
	"core.metrics_authentication":    {Type: config.Bool, Default: "true"},
	"core.bgp_asn":                   {Type: config.Int64, Default: "0", Validator: validate.Optional(validate.IsInRange(0, 4294967294))},
	"core.https_allowed_headers":     {},
	"core.https_allowed_methods":     {},
	"core.https_allowed_origin":      {},
	"core.https_allowed_credentials": {Type: config.Bool},
	"core.https_trusted_proxy":       {},
	"core.proxy_http":                {},
	"core.proxy_https":               {},
	"core.proxy_ignore_hosts":        {},
	"core.shutdown_timeout":          {Type: config.Int64, Default: "5"},
	"core.trust_password":            {Hidden: true, Setter: passwordSetter},
	"core.trust_ca_certificates":     {Type: config.Bool},
	"candid.api.key":                 {},
	"candid.api.url":                 {},
	"candid.domains":                 {},
	"candid.expiry":                  {Type: config.Int64, Default: "3600"},
	"images.auto_update_cached":      {Type: config.Bool, Default: "true"},
	"images.auto_update_interval":    {Type: config.Int64, Default: "6"},
	"images.compression_algorithm":   {Default: "gzip", Validator: validate.IsCompressionAlgorithm},
	"images.default_architecture":    {Validator: validate.Optional(validate.IsArchitecture)},
	"images.remote_cache_expiry":     {Type: config.Int64, Default: "10"},
	"instances.nic.host_name":        {Validator: validate.Optional(validate.IsOneOf("random", "mac"))},
	"maas.api.key":                   {},
	"maas.api.url":                   {},
	"rbac.agent.url":                 {},
	"rbac.agent.username":            {},
	"rbac.agent.private_key":         {},
	"rbac.agent.public_key":          {},
	"rbac.api.expiry":                {Type: config.Int64, Default: "3600"},
	"rbac.api.key":                   {},
	"rbac.api.url":                   {},
	"rbac.expiry":                    {Type: config.Int64, Default: "3600"},

	// OVN networking global keys.
	"network.ovn.integration_bridge":    {Default: "br-int"},
	"network.ovn.northbound_connection": {Default: "unix:/var/run/ovn/ovnnb_db.sock"},
}

func offlineThresholdDefault() string {
	return strconv.Itoa(db.DefaultOfflineThreshold)
}

func offlineThresholdValidator(value string) error {
	minThreshold := 10

	// Ensure that the given value is greater than the heartbeat interval,
	// which is the lower bound granularity of the offline check.
	threshold, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Offline threshold is not a number")
	}

	if threshold <= minThreshold {
		return fmt.Errorf("Value must be greater than '%d'", minThreshold)
	}

	return nil
}

func imageMinimalReplicaValidator(value string) error {
	count, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Minimal image replica count is not a number")
	}

	if count < 1 && count != -1 {
		return fmt.Errorf("Invalid value for image replica count")
	}

	return nil
}

func maxVotersValidator(value string) error {
	n, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Value is not a number")
	}

	if n < 3 || n%2 != 1 {
		return fmt.Errorf("Value must be an odd number equal to or higher than 3")
	}

	return nil
}

func maxStandByValidator(value string) error {
	n, err := strconv.Atoi(value)
	if err != nil {
		return fmt.Errorf("Value is not a number")
	}

	if n < 0 || n > 5 {
		return fmt.Errorf("Value must be between 0 and 5")
	}

	return nil
}

func passwordSetter(value string) (string, error) {
	// Nothing to do on unset
	if value == "" {
		return value, nil
	}

	// Hash the password
	buf := make([]byte, 32)
	_, err := io.ReadFull(rand.Reader, buf)
	if err != nil {
		return "", err
	}

	hash, err := scrypt.Key([]byte(value), buf, 1<<14, 8, 1, 64)
	if err != nil {
		return "", err
	}

	buf = append(buf, hash...)
	value = hex.EncodeToString(buf)

	return value, nil
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/pkg/errors"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
)

// Info represents information about a network driver.
type Info struct {
	Projects           bool // Indicates if driver can be used in network enabled projects.
	NodeSpecificConfig bool // Whether driver has cluster node specific config as a prerequisite for creation.
}

// common represents a generic LXD network.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	project     string
	name        string
	description string
	config      map[string]string
	status      string
	nodes       map[int64]db.NetworkNode
}

// init initialise internal variables.
func (n *common) init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode) {
	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netInfo.Type, "network": netInfo.Name})
	n.id = id
	n.project = projectName
	n.name = netInfo.Name
	n.config = netInfo.Config
	n.state = state
	n.description = netInfo.Description
	n.status = netInfo.Status
	n.nodes = netNodes
}

// FillConfig fills requested config with any default values, by default this is a no-op.
func (n *common) FillConfig(config map[string]string) error {
	return nil
}

// validationRules returns a map of config rules common to all drivers.
func (n *common) validationRules() map[string]func(string) error {
	return map[string]func(string) error{}
}

// validate a network config against common rules and optional driver specific rules.
func (n *common) validate(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := n.validationRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for network %q option %q", n.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		return fmt.Errorf("Invalid option for network %q option %q", n.name, k)
	}

	return nil
}

// ValidateName validates network name.
func (n *common) ValidateName(name string) error {
	err := validate.IsURLSegmentSafe(name)
	if err != nil {
		return err
	}

	if strings.Contains(name, ":") {
		return fmt.Errorf("Cannot contain %q", ":")
	}

	return nil
}

// ID returns the network ID.
func (n *common) ID() int64 {
	return n.id
}

// Name returns the network name.
func (n *common) Name() string {
	return n.name
}

// Description returns the network description.
func (n *common) Description() string {
	return n.description
}

// Status returns the network status.
func (n *common) Status() string {
	return n.status
}

// LocalStatus returns network status of the local cluster member.
func (n *common) LocalStatus() string {
	node, exists := n.nodes[n.state.Cluster.GetNodeID()]
	if !exists {
		return api.NetworkStatusUnknown
	}

	return db.NetworkStateToAPIStatus(node.State)
}

// Config returns the network config.
func (n *common) Config() map[string]string {
	return n.config
}

// Config returns the common network driver info.
func (n *common) Info() Info {
	return Info{
		Projects:           false,
		NodeSpecificConfig: true,
	}
}

// IsUsed returns whether the network is used by any instances or profiles.
func (n *common) IsUsed() (bool, error) {
	usedBy, err := UsedBy(n.state, n.project, n.name, true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// DHCPv4Subnet returns nil always.
func (n *common) DHCPv4Subnet() *net.IPNet {
	return nil
}

// DHCPv6Subnet returns nil always.
func (n *common) DHCPv6Subnet() *net.IPNet {
	return nil
}

// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
func (n *common) DHCPv4Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv4.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To4(),
					End:   endIP.To4(),
				})
			}
		}
	}

	return dhcpRanges
}

// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
func (n *common) DHCPv6Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv6.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To16(),
					End:   endIP.To16(),
				})
			}
		}
	}

	return dhcpRanges
}

// update the internal config variables, and if not cluster notification, notifies all nodes and updates database.
func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clientType cluster.ClientType) error {
	// Update internal config before database has been updated (so that if update is a notification we apply
	// the config being supplied and not that in the database).
	n.description = applyNetwork.Description
	n.config = applyNetwork.Config

	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
	// update the database.
	if clientType != cluster.ClientTypeNotifier {
		if targetNode == "" {
			// Notify all other nodes to update the network if no target specified.
			notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
			if err != nil {
				return err
			}

			sendNetwork := applyNetwork
			sendNetwork.Config = make(map[string]string)
			for k, v := range applyNetwork.Config {
				// Don't forward node specific keys (these will be merged in on recipient node).
				if shared.StringInSlice(k, db.NodeSpecificNetworkConfig) {
					continue
				}

				sendNetwork.Config[k] = v
			}

			err = notifier(func(client lxd.InstanceServer) error {
				return client.UseProject(n.project).UpdateNetwork(n.name, sendNetwork, "")
			})
			if err != nil {
				return err
			}
		}

		// Update the database.
		err := n.state.Cluster.UpdateNetwork(n.project, n.name, applyNetwork.Description, applyNetwork.Config)
		if err != nil {
			return err
		}
	}

	return nil
}

// configChanged compares supplied new config with existing config. Returns a boolean indicating if differences in
// the config or description were found (and the database record needs updating), and a list of non-user config
// keys that have changed, and a copy of the current internal network config that can be used to revert if needed.
func (n *common) configChanged(newNetwork api.NetworkPut) (bool, []string, api.NetworkPut, error) {
	// Backup the current state.
	oldNetwork := api.NetworkPut{
		Description: n.description,
		Config:      map[string]string{},
	}

	err := shared.DeepCopy(&n.config, &oldNetwork.Config)
	if err != nil {
		return false, nil, oldNetwork, err
	}

	// Diff the configurations.
	changedKeys := []string{}
	dbUpdateNeeded := false

	if newNetwork.Description != n.description {
		dbUpdateNeeded = true
	}

	for k, v := range oldNetwork.Config {
		if v != newNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	for k, v := range newNetwork.Config {
		if v != oldNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	return dbUpdateNeeded, changedKeys, oldNetwork, nil
}

// rename the network directory, update database record and update internal variables.
func (n *common) rename(newName string) error {
	// Clear new directory if exists.
	if shared.PathExists(shared.VarPath("networks", newName)) {
		os.RemoveAll(shared.VarPath("networks", newName))
	}

	// Rename directory to new name.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		err := os.Rename(shared.VarPath("networks", n.name), shared.VarPath("networks", newName))
		if err != nil {
			return err
		}
	}

	// Rename the database entry.
	err := n.state.Cluster.RenameNetwork(n.project, n.name, newName)
	if err != nil {
		return err
	}

	// Reinitialise internal name variable and logger context with new name.
	n.name = newName

	return nil
}

// delete the network from the database if clusterNotification is false.
func (n *common) delete(clientType cluster.ClientType) error {
	// Only delete database record if not cluster notification.
	if clientType != cluster.ClientTypeNotifier {
		// Notify all other nodes. If any node is down, an error will be returned.
		notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
		if err != nil {
			return err
		}
		err = notifier(func(client lxd.InstanceServer) error {
			return client.UseProject(n.project).DeleteNetwork(n.name)
		})
		if err != nil {
			return err
		}

		// Remove the network from the database.
		err = n.state.Cluster.DeleteNetwork(n.project, n.name)
		if err != nil {
			return err
		}
	}

	// Cleanup storage.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		os.RemoveAll(shared.VarPath("networks", n.name))
	}

	return nil
}

// Create is a no-op.
func (n *common) Create(clientType cluster.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	return nil
}

// HandleHeartbeat is a no-op.
func (n *common) HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error {
	return nil
}
<commit_msg>lxd/network/driver/common: Adds IsManaged function and associated internal variable<commit_after>package network

import (
	"fmt"
	"net"
	"os"
	"strings"

	"github.com/pkg/errors"

	lxd "github.com/lxc/lxd/client"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
	"github.com/lxc/lxd/shared/validate"
)

// Info represents information about a network driver.
type Info struct {
	Projects           bool // Indicates if driver can be used in network enabled projects.
	NodeSpecificConfig bool // Whether driver has cluster node specific config as a prerequisite for creation.
}

// common represents a generic LXD network.
type common struct {
	logger      logger.Logger
	state       *state.State
	id          int64
	project     string
	name        string
	description string
	config      map[string]string
	status      string
	managed     bool
	nodes       map[int64]db.NetworkNode
}

// init initialise internal variables.
func (n *common) init(state *state.State, id int64, projectName string, netInfo *api.Network, netNodes map[int64]db.NetworkNode) {
	n.logger = logging.AddContext(logger.Log, log.Ctx{"project": projectName, "driver": netInfo.Type, "network": netInfo.Name})
	n.id = id
	n.project = projectName
	n.name = netInfo.Name
	n.config = netInfo.Config
	n.state = state
	n.description = netInfo.Description
	n.status = netInfo.Status
	n.managed = netInfo.Managed
	n.nodes = netNodes
}

// FillConfig fills requested config with any default values, by default this is a no-op.
func (n *common) FillConfig(config map[string]string) error {
	return nil
}

// validationRules returns a map of config rules common to all drivers.
func (n *common) validationRules() map[string]func(string) error {
	return map[string]func(string) error{}
}

// validate a network config against common rules and optional driver specific rules.
func (n *common) validate(config map[string]string, driverRules map[string]func(value string) error) error {
	checkedFields := map[string]struct{}{}

	// Get rules common for all drivers.
	rules := n.validationRules()

	// Merge driver specific rules into common rules.
	for field, validator := range driverRules {
		rules[field] = validator
	}

	// Run the validator against each field.
	for k, validator := range rules {
		checkedFields[k] = struct{}{} //Mark field as checked.
		err := validator(config[k])
		if err != nil {
			return errors.Wrapf(err, "Invalid value for network %q option %q", n.name, k)
		}
	}

	// Look for any unchecked fields, as these are unknown fields and validation should fail.
	for k := range config {
		_, checked := checkedFields[k]
		if checked {
			continue
		}

		// User keys are not validated.
		if strings.HasPrefix(k, "user.") {
			continue
		}

		return fmt.Errorf("Invalid option for network %q option %q", n.name, k)
	}

	return nil
}

// ValidateName validates network name.
func (n *common) ValidateName(name string) error {
	err := validate.IsURLSegmentSafe(name)
	if err != nil {
		return err
	}

	if strings.Contains(name, ":") {
		return fmt.Errorf("Cannot contain %q", ":")
	}

	return nil
}

// ID returns the network ID.
func (n *common) ID() int64 {
	return n.id
}

// Name returns the network name.
func (n *common) Name() string {
	return n.name
}

// Description returns the network description.
func (n *common) Description() string {
	return n.description
}

// Status returns the network status.
func (n *common) Status() string {
	return n.status
}

// LocalStatus returns network status of the local cluster member.
func (n *common) LocalStatus() string {
	node, exists := n.nodes[n.state.Cluster.GetNodeID()]
	if !exists {
		return api.NetworkStatusUnknown
	}

	return db.NetworkStateToAPIStatus(node.State)
}

// Config returns the network config.
func (n *common) Config() map[string]string {
	return n.config
}

func (n *common) IsManaged() bool {
	return n.managed
}

// Config returns the common network driver info.
func (n *common) Info() Info {
	return Info{
		Projects:           false,
		NodeSpecificConfig: true,
	}
}

// IsUsed returns whether the network is used by any instances or profiles.
func (n *common) IsUsed() (bool, error) {
	usedBy, err := UsedBy(n.state, n.project, n.name, true)
	if err != nil {
		return false, err
	}

	return len(usedBy) > 0, nil
}

// DHCPv4Subnet returns nil always.
func (n *common) DHCPv4Subnet() *net.IPNet {
	return nil
}

// DHCPv6Subnet returns nil always.
func (n *common) DHCPv6Subnet() *net.IPNet {
	return nil
}

// DHCPv4Ranges returns a parsed set of DHCPv4 ranges for this network.
func (n *common) DHCPv4Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv4.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv4.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To4(),
					End:   endIP.To4(),
				})
			}
		}
	}

	return dhcpRanges
}

// DHCPv6Ranges returns a parsed set of DHCPv6 ranges for this network.
func (n *common) DHCPv6Ranges() []shared.IPRange {
	dhcpRanges := make([]shared.IPRange, 0)
	if n.config["ipv6.dhcp.ranges"] != "" {
		for _, r := range strings.Split(n.config["ipv6.dhcp.ranges"], ",") {
			parts := strings.SplitN(strings.TrimSpace(r), "-", 2)
			if len(parts) == 2 {
				startIP := net.ParseIP(parts[0])
				endIP := net.ParseIP(parts[1])
				dhcpRanges = append(dhcpRanges, shared.IPRange{
					Start: startIP.To16(),
					End:   endIP.To16(),
				})
			}
		}
	}

	return dhcpRanges
}

// update the internal config variables, and if not cluster notification, notifies all nodes and updates database.
func (n *common) update(applyNetwork api.NetworkPut, targetNode string, clientType cluster.ClientType) error {
	// Update internal config before database has been updated (so that if update is a notification we apply
	// the config being supplied and not that in the database).
	n.description = applyNetwork.Description
	n.config = applyNetwork.Config

	// If this update isn't coming via a cluster notification itself, then notify all nodes of change and then
	// update the database.
	if clientType != cluster.ClientTypeNotifier {
		if targetNode == "" {
			// Notify all other nodes to update the network if no target specified.
			notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
			if err != nil {
				return err
			}

			sendNetwork := applyNetwork
			sendNetwork.Config = make(map[string]string)
			for k, v := range applyNetwork.Config {
				// Don't forward node specific keys (these will be merged in on recipient node).
				if shared.StringInSlice(k, db.NodeSpecificNetworkConfig) {
					continue
				}

				sendNetwork.Config[k] = v
			}

			err = notifier(func(client lxd.InstanceServer) error {
				return client.UseProject(n.project).UpdateNetwork(n.name, sendNetwork, "")
			})
			if err != nil {
				return err
			}
		}

		// Update the database.
		err := n.state.Cluster.UpdateNetwork(n.project, n.name, applyNetwork.Description, applyNetwork.Config)
		if err != nil {
			return err
		}
	}

	return nil
}

// configChanged compares supplied new config with existing config. Returns a boolean indicating if differences in
// the config or description were found (and the database record needs updating), and a list of non-user config
// keys that have changed, and a copy of the current internal network config that can be used to revert if needed.
func (n *common) configChanged(newNetwork api.NetworkPut) (bool, []string, api.NetworkPut, error) {
	// Backup the current state.
	oldNetwork := api.NetworkPut{
		Description: n.description,
		Config:      map[string]string{},
	}

	err := shared.DeepCopy(&n.config, &oldNetwork.Config)
	if err != nil {
		return false, nil, oldNetwork, err
	}

	// Diff the configurations.
	changedKeys := []string{}
	dbUpdateNeeded := false

	if newNetwork.Description != n.description {
		dbUpdateNeeded = true
	}

	for k, v := range oldNetwork.Config {
		if v != newNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	for k, v := range newNetwork.Config {
		if v != oldNetwork.Config[k] {
			dbUpdateNeeded = true

			// Add non-user changed key to list of changed keys.
			if !strings.HasPrefix(k, "user.") && !shared.StringInSlice(k, changedKeys) {
				changedKeys = append(changedKeys, k)
			}
		}
	}

	return dbUpdateNeeded, changedKeys, oldNetwork, nil
}

// rename the network directory, update database record and update internal variables.
func (n *common) rename(newName string) error {
	// Clear new directory if exists.
	if shared.PathExists(shared.VarPath("networks", newName)) {
		os.RemoveAll(shared.VarPath("networks", newName))
	}

	// Rename directory to new name.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		err := os.Rename(shared.VarPath("networks", n.name), shared.VarPath("networks", newName))
		if err != nil {
			return err
		}
	}

	// Rename the database entry.
	err := n.state.Cluster.RenameNetwork(n.project, n.name, newName)
	if err != nil {
		return err
	}

	// Reinitialise internal name variable and logger context with new name.
	n.name = newName

	return nil
}

// delete the network from the database if clusterNotification is false.
func (n *common) delete(clientType cluster.ClientType) error {
	// Only delete database record if not cluster notification.
	if clientType != cluster.ClientTypeNotifier {
		// Notify all other nodes. If any node is down, an error will be returned.
		notifier, err := cluster.NewNotifier(n.state, n.state.Endpoints.NetworkCert(), cluster.NotifyAll)
		if err != nil {
			return err
		}
		err = notifier(func(client lxd.InstanceServer) error {
			return client.UseProject(n.project).DeleteNetwork(n.name)
		})
		if err != nil {
			return err
		}

		// Remove the network from the database.
		err = n.state.Cluster.DeleteNetwork(n.project, n.name)
		if err != nil {
			return err
		}
	}

	// Cleanup storage.
	if shared.PathExists(shared.VarPath("networks", n.name)) {
		os.RemoveAll(shared.VarPath("networks", n.name))
	}

	return nil
}

// Create is a no-op.
func (n *common) Create(clientType cluster.ClientType) error {
	n.logger.Debug("Create", log.Ctx{"clientType": clientType, "config": n.config})

	return nil
}

// HandleHeartbeat is a no-op.
func (n *common) HandleHeartbeat(heartbeatData *cluster.APIHeartbeat) error {
	return nil
}
<|endoftext|>


<commit_before>package tripeg

import (
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/fatih/color"
)

//Hole struct that contains information
//about a hole in the board, its location
//and whether or not it has a peg in it.
type Hole struct {
	Row    int //max of 5
	Col    int //max of 9
	Peg    bool
	Status int
}

const (
	//Dormant short hand for a row/col location that's not involved in a jump move
	Dormant = iota
	//Source shorthand for the source peg row/col for a jump move
	Source
	//Target the empty row/col the source peg will land in for a jump move.
	Target
)

func (b Board) showMove(m, o, t Hole) Board {
	result := Board{}
	result.Rows = b.Rows
	for k, v := range b.Holes {
		b.Holes[k].Status = Dormant
		if v.Row == m.Row && v.Col == m.Col {
			b.Holes[k].Status = Source
		}
		if v.Row == t.Row && v.Col == t.Col {
			b.Holes[k].Status = Target
		}
	}
	result.MoveLog = b.MoveLog
	result.Holes = b.Holes
	return result

}

//Jump from the Board struct type
func (b Board) Jump(m, o Hole) (Board, Hole, error) {
	result := Board{}
	result.SolveMoves = b.SolveMoves
	result.Rows = b.Rows
	thole := Hole{}
	for _, r := range b.Holes {
		result.Holes = append(result.Holes, r)
	}
	if !m.Peg {
		//If there is no peg in the moveHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in move hole %d,%d\n", o.Row, o.Col)
	}
	if !o.Peg {
		//If there is no peg in the overHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in over hole %d,%d\n", o.Row, o.Col)
	}
	rDif := m.Row - o.Row
	cDif := o.Col - m.Col
	if cDif == 0 && rDif == 0 {
		//Holes are the same, not valid
		return result, thole, fmt.Errorf("Jump peg and over hole are the same\n")
	}
	if math.Abs(float64(rDif)) > 1 {
		//You can't jump over more than 1 row horizontally
		return result, thole, fmt.Errorf("Invalid horizonal movement %d\n", rDif)
	}
	if rDif > 0 && math.Abs(float64(cDif)) > 1 {
		//You can't jump over more than 1 col vertically
		return result, thole, fmt.Errorf("Invalid vertical movement %d\n", cDif)
	}
	if rDif == 0 && math.Abs(float64(cDif)) > 2 {
		return result, thole, fmt.Errorf("Invalid horizantal movement %d\n", rDif)
		//You can't jump more than 2 cols horizontally
	}
	targetR := 0
	targetC := 0
	if rDif == 0 {
		//This is a horizontal jump
		targetR = m.Row
	}
	if rDif > 0 {
		targetR = o.Row - 1
		//This is an up jump
	}
	if rDif < 0 {
		targetR = o.Row + 1
		//This is a jump down
	}
	if cDif < 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col - x
		//This is a jump left
	}
	if cDif > 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col + x
		//This is a jump right
	}
	targetHole, err := b.GetHole(targetR, targetC)
	if err != nil {
		return result, thole, fmt.Errorf("Target hole(%d,%d) does not exist\n", targetR, targetC)
	}
	if targetHole.Peg {
		return result, thole, fmt.Errorf("Target hole(%d,%d) has a peg in it\n", targetHole.Row, targetHole.Col)
	}
	for k, bh := range result.Holes {
		if bh.Row == m.Row && bh.Col == m.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == o.Row && bh.Col == o.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == targetHole.Row && bh.Col == targetHole.Col {
			result.Holes[k].Peg = true
		}
	}
	return result, targetHole, nil
}

//Board contains all the holes that contain the pegs
type Board struct {
	Holes      []Hole
	MoveLog    []string //TODO: Remove the movelog.
	MoveChart  []string
	SolveMoves int
	Rows       int
}

//GetHole gets a pointer to a hole based on the row,col coordinates
func (b Board) GetHole(r, c int) (Hole, error) {
	if r < 0 || r > b.Rows+1 || c < 0 || c > b.Rows+(b.Rows-1) {
		return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
	}
	for _, v := range b.Holes {
		if v.Col == c && v.Row == r {
			return v, nil
		}
	}
	return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
}

//BuildBoard makes a board of peg holes.
//All holes have a peg except one.
func BuildBoard(rows, empty int) (Board, error) {
	var b Board
	if rows < 5 {
		return b, fmt.Errorf("Invalid rows valid %d, it must be greater than 4\n", rows)
	}
	if rows > 6 {
		return b, fmt.Errorf("We're going to need a better algorithm before we get to %d... rows\n", rows)
	}
	max := 0
	for i := 1; i < rows+1; i++ {
		max += i
	}
	b.SolveMoves = max - 2
	b.Rows = rows

	if empty < 0 || empty > max {
		return b, fmt.Errorf("1st parameter must be >=0 or <=%d, you supplied %d", empty, max)
	}
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	if empty == 0 {
		empty = r2.Intn(max)
	} else {
		empty--
	}
	for r := 1; r < rows+1; r++ {
		for c := 1; c < r+1; c++ {
			offset := 1
			col := rows + (c * 2) - offset - r
			h := Hole{Row: r, Col: col, Peg: true}
			if empty == len(b.Holes) {
				h.Peg = false
			}
			b.Holes = append(b.Holes, h)
		}
	}
	return b, nil
}

type move struct {
	H Hole
	O Hole
	T Hole
}

func (m move) String() string {
	return fmt.Sprintf("[%d,%d] over [%d,%d] to [%d,%d]", m.H.Row, m.H.Col, m.O.Row, m.O.Col, m.T.Row, m.T.Col)
}

//ErrorArray an array of errors that also implements the error interface
type ErrorArray struct {
	Errors []error
}

func (ea ErrorArray) Error() string {
	r := ""
	m := len(ea.Errors)
	c := m
	if m > 11 {
		m = 11
		ea.Errors[10] = fmt.Errorf("Too many errors! Count: %v", c-1)
	}
	for _, v := range ea.Errors[0:m] {
		r += v.Error() + "\n"
	}
	return r[0 : len(r)-1]
}

//Add takes an argument of the error interface and adds it to the array
func (ea *ErrorArray) Add(err error) {
	ea.Errors = append(ea.Errors, err)
}

//Solve does a brute force solving of the game
func (b *Board) Solve() []error {
	high := 0
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	var newBoard = b
	var solved = false
	var solveErrors = ErrorArray{}
	validMove := 0
	for {
		func() {
			aMoves := []move{}
			o := Hole{}
			var err error
			for _, v := range newBoard.Holes {
				/*
					Go through all of the holes on the board.
					If the hole doesn't have a peg, it can't
					have a legal move, so skip it.
					If it doesn't have a peg, just to see if it has
					a legal move by jumping left, right, up left, up right, down left or down right.
					If any of these moves are legal, add it to the array of available moves.
					Do this for each hole on the board.
					Randomly select a legal move, color the board and return the new color coded board.
					Keep doing this until we've done SolveMoves legal moves or we run out of availaable moves.
					If no legal moves left, start over and hope for the best.
					If SolveMoves legal moves, then we've solved it, return out of here.
				*/
				if v.Peg {
					//upleft
					o, err = newBoard.GetHole(v.Row-1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//upright
					o, err = newBoard.GetHole(v.Row-1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//left
					o, err = newBoard.GetHole(v.Row, v.Col-2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
					//right
					o, err = newBoard.GetHole(v.Row, v.Col+2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downleft
					o, err = newBoard.GetHole(v.Row+1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downright
					o, err = newBoard.GetHole(v.Row+1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
				}
			}
			if len(aMoves) == 0 {
				//No legal moves left
				newBoard = b
				validMove = 0
				b.MoveLog = []string{}
				b.MoveChart = []string{}
				return
			}
			available := r2.Intn(len(aMoves))
			avs := aMoves[available].H
			avo := aMoves[available].O
			cBoard, th, errN := newBoard.Jump(avs, avo)
			cBoard.Rows = b.Rows
			if errN != nil {
				solveErrors.Add(errN)
			}
			validMove++
			if validMove > high {
				high = validMove
				//fmt.Println(b.SolveMoves, high, b.SolveMoves-high)
			}
			b.MoveChart = append(b.MoveChart, fmt.Sprintf("%v", newBoard.showMove(avs, avo, th)))
			b.MoveLog = append(b.MoveLog, fmt.Sprintf("%v", aMoves[available]))

			newBoard = &cBoard
			if validMove == b.SolveMoves {
				solved = true
				return
			}
		}()
		if solved {
			break
		}
	}
	return nil
}

func (b Board) String() string {
	result := "\n"
	tar := color.New(color.FgRed).SprintFunc()
	src := color.New(color.FgGreen).SprintFunc()
	dor := color.New(color.FgWhite).SprintFunc()
	offset := 1
	for r := 1; r < b.Rows+1; r++ {
		for c := 1; c < b.Rows*2+offset; c++ {
			h, err := b.GetHole(r, c)
			mark := " "
			if err == nil {
				mark = "O"
				if h.Peg {
					mark = "*"
				}
			}
			switch h.Status {
			case Source:
				result += fmt.Sprintf("%s", src(mark))
			case Target:
				result += fmt.Sprintf("%s", tar(mark))
			case Dormant:
				result += fmt.Sprintf("%s", dor(mark))
			}
		}
		result += "\n"
	}
	return result
}
<commit_msg>remove move log, don't need it<commit_after>package tripeg

import (
	"fmt"
	"math"
	"math/rand"
	"time"

	"github.com/fatih/color"
)

//Hole struct that contains information
//about a hole in the board, its location
//and whether or not it has a peg in it.
type Hole struct {
	Row    int //max of 5
	Col    int //max of 9
	Peg    bool
	Status int
}

const (
	//Dormant short hand for a row/col location that's not involved in a jump move
	Dormant = iota
	//Source shorthand for the source peg row/col for a jump move
	Source
	//Target the empty row/col the source peg will land in for a jump move.
	Target
)

func (b Board) showMove(m, o, t Hole) Board {
	result := Board{}
	result.Rows = b.Rows
	for k, v := range b.Holes {
		b.Holes[k].Status = Dormant
		if v.Row == m.Row && v.Col == m.Col {
			b.Holes[k].Status = Source
		}
		if v.Row == t.Row && v.Col == t.Col {
			b.Holes[k].Status = Target
		}
	}
	result.Holes = b.Holes
	return result

}

//Jump from the Board struct type
func (b Board) Jump(m, o Hole) (Board, Hole, error) {
	result := Board{}
	result.SolveMoves = b.SolveMoves
	result.Rows = b.Rows
	thole := Hole{}
	for _, r := range b.Holes {
		result.Holes = append(result.Holes, r)
	}
	if !m.Peg {
		//If there is no peg in the moveHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in move hole %d,%d\n", o.Row, o.Col)
	}
	if !o.Peg {
		//If there is no peg in the overHole, no jump possible
		return result, thole, fmt.Errorf("No Peg in over hole %d,%d\n", o.Row, o.Col)
	}
	rDif := m.Row - o.Row
	cDif := o.Col - m.Col
	if cDif == 0 && rDif == 0 {
		//Holes are the same, not valid
		return result, thole, fmt.Errorf("Jump peg and over hole are the same\n")
	}
	if math.Abs(float64(rDif)) > 1 {
		//You can't jump over more than 1 row horizontally
		return result, thole, fmt.Errorf("Invalid horizonal movement %d\n", rDif)
	}
	if rDif > 0 && math.Abs(float64(cDif)) > 1 {
		//You can't jump over more than 1 col vertically
		return result, thole, fmt.Errorf("Invalid vertical movement %d\n", cDif)
	}
	if rDif == 0 && math.Abs(float64(cDif)) > 2 {
		return result, thole, fmt.Errorf("Invalid horizantal movement %d\n", rDif)
		//You can't jump more than 2 cols horizontally
	}
	targetR := 0
	targetC := 0
	if rDif == 0 {
		//This is a horizontal jump
		targetR = m.Row
	}
	if rDif > 0 {
		targetR = o.Row - 1
		//This is an up jump
	}
	if rDif < 0 {
		targetR = o.Row + 1
		//This is a jump down
	}
	if cDif < 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col - x
		//This is a jump left
	}
	if cDif > 0 {
		x := 1
		if rDif == 0 {
			x = 2
		}
		targetC = o.Col + x
		//This is a jump right
	}
	targetHole, err := b.GetHole(targetR, targetC)
	if err != nil {
		return result, thole, fmt.Errorf("Target hole(%d,%d) does not exist\n", targetR, targetC)
	}
	if targetHole.Peg {
		return result, thole, fmt.Errorf("Target hole(%d,%d) has a peg in it\n", targetHole.Row, targetHole.Col)
	}
	for k, bh := range result.Holes {
		if bh.Row == m.Row && bh.Col == m.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == o.Row && bh.Col == o.Col {
			result.Holes[k].Peg = false
		}
		if bh.Row == targetHole.Row && bh.Col == targetHole.Col {
			result.Holes[k].Peg = true
		}
	}
	return result, targetHole, nil
}

//Board contains all the holes that contain the pegs
type Board struct {
	Holes      []Hole
	MoveChart  []string
	SolveMoves int
	Rows       int
}

//GetHole gets a pointer to a hole based on the row,col coordinates
func (b Board) GetHole(r, c int) (Hole, error) {
	if r < 0 || r > b.Rows+1 || c < 0 || c > b.Rows+(b.Rows-1) {
		return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
	}
	for _, v := range b.Holes {
		if v.Col == c && v.Row == r {
			return v, nil
		}
	}
	return Hole{}, fmt.Errorf("Hole %d,%d does not exist\n", r, c)
}

//BuildBoard makes a board of peg holes.
//All holes have a peg except one.
func BuildBoard(rows, empty int) (Board, error) {
	var b Board
	if rows < 5 {
		return b, fmt.Errorf("Invalid rows valid %d, it must be greater than 4\n", rows)
	}
	if rows > 6 {
		return b, fmt.Errorf("We're going to need a better algorithm before we get to %d rows...\n", rows)
	}
	max := 0
	for i := 1; i < rows+1; i++ {
		max += i
	}
	b.SolveMoves = max - 2
	b.Rows = rows

	if empty < 0 || empty > max {
		return b, fmt.Errorf("1st parameter must be >=0 or <=%d, you supplied %d", empty, max)
	}
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	if empty == 0 {
		empty = r2.Intn(max)
	} else {
		empty--
	}
	for r := 1; r < rows+1; r++ {
		for c := 1; c < r+1; c++ {
			offset := 1
			col := rows + (c * 2) - offset - r
			h := Hole{Row: r, Col: col, Peg: true}
			if empty == len(b.Holes) {
				h.Peg = false
			}
			b.Holes = append(b.Holes, h)
		}
	}
	return b, nil
}

type move struct {
	H Hole
	O Hole
	T Hole
}

func (m move) String() string {
	return fmt.Sprintf("[%d,%d] over [%d,%d] to [%d,%d]", m.H.Row, m.H.Col, m.O.Row, m.O.Col, m.T.Row, m.T.Col)
}

//ErrorArray an array of errors that also implements the error interface
type ErrorArray struct {
	Errors []error
}

func (ea ErrorArray) Error() string {
	r := ""
	m := len(ea.Errors)
	c := m
	if m > 11 {
		m = 11
		ea.Errors[10] = fmt.Errorf("Too many errors! Count: %v", c-1)
	}
	for _, v := range ea.Errors[0:m] {
		r += v.Error() + "\n"
	}
	return r[0 : len(r)-1]
}

//Add takes an argument of the error interface and adds it to the array
func (ea *ErrorArray) Add(err error) {
	ea.Errors = append(ea.Errors, err)
}

//Solve does a brute force solving of the game
func (b *Board) Solve() []error {
	high := 0
	s2 := rand.NewSource(time.Now().UnixNano())
	r2 := rand.New(s2)
	var newBoard = b
	var solved = false
	var solveErrors = ErrorArray{}
	validMove := 0
	for {
		func() {
			aMoves := []move{}
			o := Hole{}
			var err error
			for _, v := range newBoard.Holes {
				/*
					Go through all of the holes on the board.
					If the hole doesn't have a peg, it can't
					have a legal move, so skip it.
					If it doesn't have a peg, just to see if it has
					a legal move by jumping left, right, up left, up right, down left or down right.
					If any of these moves are legal, add it to the array of available moves.
					Do this for each hole on the board.
					Randomly select a legal move, color the board and return the new color coded board.
					Keep doing this until we've done SolveMoves legal moves or we run out of availaable moves.
					If no legal moves left, start over and hope for the best.
					If SolveMoves legal moves, then we've solved it, return out of here.
				*/
				if v.Peg {
					//upleft
					o, err = newBoard.GetHole(v.Row-1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//upright
					o, err = newBoard.GetHole(v.Row-1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//left
					o, err = newBoard.GetHole(v.Row, v.Col-2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
					//right
					o, err = newBoard.GetHole(v.Row, v.Col+2)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downleft
					o, err = newBoard.GetHole(v.Row+1, v.Col-1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}

					//downright
					o, err = newBoard.GetHole(v.Row+1, v.Col+1)
					if err == nil {
						_, t, errJ := newBoard.Jump(v, o)
						if errJ == nil {
							aMoves = append(aMoves, move{H: v, O: o, T: t})
						}
					}
				}
			}
			if len(aMoves) == 0 {
				//No legal moves left
				newBoard = b
				validMove = 0
				b.MoveChart = []string{}
				return
			}
			available := r2.Intn(len(aMoves))
			avs := aMoves[available].H
			avo := aMoves[available].O
			cBoard, th, errN := newBoard.Jump(avs, avo)
			cBoard.Rows = b.Rows
			if errN != nil {
				solveErrors.Add(errN)
			}
			validMove++
			if validMove > high {
				high = validMove
				//fmt.Println(b.SolveMoves, high, b.SolveMoves-high)
			}
			b.MoveChart = append(b.MoveChart, fmt.Sprintf("%v", newBoard.showMove(avs, avo, th)))

			newBoard = &cBoard
			if validMove == b.SolveMoves {
				solved = true
				return
			}
		}()
		if solved {
			break
		}
	}
	return nil
}

func (b Board) String() string {
	result := "\n"
	tar := color.New(color.FgRed).SprintFunc()
	src := color.New(color.FgGreen).SprintFunc()
	dor := color.New(color.FgWhite).SprintFunc()
	offset := 1
	for r := 1; r < b.Rows+1; r++ {
		for c := 1; c < b.Rows*2+offset; c++ {
			h, err := b.GetHole(r, c)
			mark := " "
			if err == nil {
				mark = "O"
				if h.Peg {
					mark = "*"
				}
			}
			switch h.Status {
			case Source:
				result += fmt.Sprintf("%s", src(mark))
			case Target:
				result += fmt.Sprintf("%s", tar(mark))
			case Dormant:
				result += fmt.Sprintf("%s", dor(mark))
			}
		}
		result += "\n"
	}
	return result
}
<|endoftext|>


<commit_before>package simplexml

import (
	"fmt"
	"html"
	"strings"
)

const (
	DefaultDeclaration = "<?xml version=\"1.0\" encding=\"UTF-8\"?>"
)

type Element interface {
	// String returns the elements string repesentation, including the elements wrapping markup, usually with HTML encoding
	String() string

	// Value returns the inner value of an Element, without the elements wrapping markup and without HTML encoding
	Value() (string, error)
}

// Value is a string representation of XML CharData
type Value string

// String implements the Stringer interface. String returns the html escaped value of Value.
func (v Value) String() string {
	return html.EscapeString(string(v))
}

// Value implements the Stringer interface. String returns the html escaped value of Value.
func (v Value) Value() (string, error) {
	return string(v), nil
}

// CDATA is a string representation of XML CDATA without the '<![CDATA[' and ']]>' markup.
type CDATA string

// String implements the Stringer interface. String returns the html escaped value of Value wrapped the CDATA markup.
func (c CDATA) String() string {
	return fmt.Sprintf("<![CDATA[%s]]>", html.EscapeString(string(c)))
}

// String implements the Stringer interface. String returns the html escaped value of Value wrapped the CDATA markup.
func (c CDATA) Value() (string, error) {
	return string(c), nil
}

// Comments is a string representation of an XML comment without the '<!--' and '-->' markup.
type Comment string

// String implements the Stringer interface. String returns the value of Comment with the comment markup. String()
// does not html encode the value, as it is not considered part of the document.
func (c Comment) String() string {
	return fmt.Sprintf("<!--%s-->", string(c))
}

func (c Comment) Value() (string, error) {
	return string(c), nil
}

// NewComment returns a pointer to a new Comment
func NewComment(s string) *Comment {
	c := new(Comment)
	*c = Comment(s)
	return c
}

// NewCDATA returns a pointer to a new CDATA
func NewCDATA(s string) *CDATA {
	c := new(CDATA)
	*c = CDATA(s)
	return c
}

// NewValue returns a pointer to a new CDATA
func NewValue(s string) *Value {
	c := new(Value)
	*c = Value(s)
	return c
}

// NeedCDATA parses a string and returns true if it contains any XML markup or other characters that would require it to be repesented as CDATA
func NeedCDATA(s string) bool {
	if strings.Contains(s, "<") || strings.Contains(s, ">") {
		return true
	}
	return false
}

// Attribute is a simple representations of an XML attrbiute, consiting of a prefix, name and value.
type Attribute struct {
	Prefix string
	Name   string
	Value  string
}

// IsNamespace returns true if it's prefix = 'xmlns' (not case sensitive)
func (a Attribute) IsNamespace() bool {
	if strings.ToLower(a.Prefix) == "xmlns" {
		return true
	}
	return false
}

// String returns a format for use within String() of Tag
func (a Attribute) String() string {
	if a.Prefix != "" {
		return fmt.Sprintf("%s:%s=\"%s\"", a.Prefix, a.Name, a.Value)
	}
	return fmt.Sprintf("%s=\"%s\"", a.Name, a.Value)
}

// XPath is a slice of string (of Tag names)
type XPath []string

// String returns the string repesenation of an XPATH ('/foo/bar')
func (x XPath) String() string {
	var s string
	for _, v := range x {
		s = s + "/" + v
	}
	return s
}
<commit_msg>not escaping cdata<commit_after>package simplexml

import (
	"fmt"
	"html"
	"strings"
)

const (
	DefaultDeclaration = "<?xml version=\"1.0\" encding=\"UTF-8\"?>"
)

type Element interface {
	// String returns the elements string repesentation, including the elements wrapping markup, usually with HTML encoding
	String() string

	// Value returns the inner value of an Element, without the elements wrapping markup and without HTML encoding
	Value() (string, error)
}

// Value is a string representation of XML CharData
type Value string

// String implements the Stringer interface. String returns the html escaped value of Value.
func (v Value) String() string {
	return html.EscapeString(string(v))
}

// Value implements the Stringer interface. String returns the html escaped value of Value.
func (v Value) Value() (string, error) {
	return string(v), nil
}

// CDATA is a string representation of XML CDATA without the '<![CDATA[' and ']]>' markup.
type CDATA string

// String implements the Stringer interface. String returns the html escaped value of Value wrapped the CDATA markup.
func (c CDATA) String() string {
	return fmt.Sprintf("<![CDATA[%s]]>", string(c))
}

// String implements the Stringer interface. String returns the html escaped value of Value wrapped the CDATA markup.
func (c CDATA) Value() (string, error) {
	return string(c), nil
}

// Comments is a string representation of an XML comment without the '<!--' and '-->' markup.
type Comment string

// String implements the Stringer interface. String returns the value of Comment with the comment markup. String()
// does not html encode the value, as it is not considered part of the document.
func (c Comment) String() string {
	return fmt.Sprintf("<!--%s-->", string(c))
}

func (c Comment) Value() (string, error) {
	return string(c), nil
}

// NewComment returns a pointer to a new Comment
func NewComment(s string) *Comment {
	c := new(Comment)
	*c = Comment(s)
	return c
}

// NewCDATA returns a pointer to a new CDATA
func NewCDATA(s string) *CDATA {
	c := new(CDATA)
	*c = CDATA(s)
	return c
}

// NewValue returns a pointer to a new CDATA
func NewValue(s string) *Value {
	c := new(Value)
	*c = Value(s)
	return c
}

// NeedCDATA parses a string and returns true if it contains any XML markup or other characters that would require it to be repesented as CDATA
func NeedCDATA(s string) bool {
	if strings.Contains(s, "<") || strings.Contains(s, ">") {
		return true
	}
	return false
}

// Attribute is a simple representations of an XML attrbiute, consiting of a prefix, name and value.
type Attribute struct {
	Prefix string
	Name   string
	Value  string
}

// IsNamespace returns true if it's prefix = 'xmlns' (not case sensitive)
func (a Attribute) IsNamespace() bool {
	if strings.ToLower(a.Prefix) == "xmlns" {
		return true
	}
	return false
}

// String returns a format for use within String() of Tag
func (a Attribute) String() string {
	if a.Prefix != "" {
		return fmt.Sprintf("%s:%s=\"%s\"", a.Prefix, a.Name, a.Value)
	}
	return fmt.Sprintf("%s=\"%s\"", a.Name, a.Value)
}

// XPath is a slice of string (of Tag names)
type XPath []string

// String returns the string repesenation of an XPATH ('/foo/bar')
func (x XPath) String() string {
	var s string
	for _, v := range x {
		s = s + "/" + v
	}
	return s
}
<|endoftext|>


<commit_before>package cloudstack

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"code.google.com/p/go-uuid/uuid"
)

type APIParameter interface{}

type Resource interface {
	// refresh information about the resource
	Refresh() error
	// update the resource with specified args
	Update(args map[string]interface{}) error
	// delete the resource
	Delete() error
	// set client for this resource
	setClient(*Client)
}

type ResourceBase struct {
	client *Client
}

func (*ResourceBase) Refresh() error {
	return errors.New("Not Implemented")
}

func (*ResourceBase) Update(map[string]interface{}) error {
	return errors.New("Not Implemented")
}

func (*ResourceBase) Delete() error {
	return errors.New("Not Implemented")
}

func (rb *ResourceBase) setClient(client *Client) {
	rb.client = new(Client)
	*rb.client = *client
}

type Nullable interface {
	Value() interface{}
	String() string
	IsNil() bool
}

type Setter interface {
	Set(interface{}) error
}

func unmarshalJSON(s Setter, b []byte) error {
	var v interface{}

	// initialize by nil
	if err := s.Set(nil); err != nil {
		return err
	}

	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.UseNumber()
	if err := decoder.Decode(&v); err != nil {
		return err
	}

	return s.Set(v)
}

// Base struct of Nullable
type NullBase struct {
	valid bool
	value interface{}
}

func (n NullBase) MarshalJSON() ([]byte, error) {
	if n.IsNil() {
		return json.Marshal(nil)
	}
	return json.Marshal(n.value)
}

func (nb *NullBase) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nb, b)
}

// Set value. If nil is given, value is cleared.
func (nb *NullBase) Set(value interface{}) error {

	nb.valid = false
	nb.value = nil

	if value == nil {
		return nil
	}

	nb.valid = true
	nb.value = value

	return nil
}

// Return Value. If no value is set, return nil.
func (nb NullBase) Value() interface{} {
	if nb.IsNil() {
		return nil
	}
	return nb.value
}

// Return Value as String. If no value is set, return "null".
func (nb NullBase) String() string {
	if nb.IsNil() {
		return "null"
	}
	return fmt.Sprint(nb.value)
}

// Check if value is nil.
func (nb NullBase) IsNil() bool {
	return !nb.valid
}

// Nullable Bool
type NullBool struct {
	NullBase
}

func (nb *NullBool) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nb, b)
}

// Set Value. Value is converted by strconv.ParseBool
func (nb *NullBool) Set(value interface{}) error {

	nb.valid = false
	nb.value = false

	if value == nil {
		return nil
	}

	b, err := strconv.ParseBool(fmt.Sprint(value))
	if err != nil {
		return err
	}

	nb.valid = true
	nb.value = b

	return nil
}

// Return Value as bool
func (nb NullBool) Bool() bool {
	return nb.value.(bool)
}

// Nullable String
type NullString struct {
	NullBase
}

func (ns *NullString) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(ns, b)
}

// Set Value. Value is converted by fmt.Sprint
func (ns *NullString) Set(value interface{}) error {

	ns.valid = false
	ns.value = ""

	if value == nil {
		return nil
	}

	ns.valid = true
	ns.value = fmt.Sprint(value)

	return nil
}

// Nullable Number
// Value is stored as string.
type NullNumber struct {
	NullString
}

func (nn *NullNumber) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nn, b)
}

func (nn NullNumber) MarshalJSON() ([]byte, error) {
	return []byte(nn.String()), nil
}

// Set Value. Value is converted to string by fmt.Sprint
func (nn *NullNumber) Set(value interface{}) error {

	nn.valid = false
	nn.value = ""

	if value == nil {
		return nil
	}

	s := fmt.Sprint(value)
	_, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return err
	}

	nn.valid = true
	nn.value = s

	return nil
}

// Return Value as int64
func (nn NullNumber) Int64() (int64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseInt(nn.value.(string), 10, 64)
}

// Return Value as uint64
func (nn NullNumber) UInt64() (uint64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseUint(nn.value.(string), 10, 64)
}

// Return Value as float64
func (nn NullNumber) Float64() (float64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseFloat(nn.value.(string), 64)
}

// UUID or Integer ID
type ID struct {
	NullString
}

func (id *ID) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(id, b)
}

// Set Value
func (id *ID) Set(value interface{}) error {

	id.valid = false
	id.value = ""

	if value == nil {
		return nil
	}

	s := fmt.Sprint(value)
	uuid := uuid.Parse(s)
	_, err := strconv.ParseFloat(s, 64)

	if uuid != nil || err == nil {
		id.valid = true
		id.value = s
	}

	return nil
}

func (id ID) UUID() uuid.UUID {
	return uuid.Parse(id.String())
}
<commit_msg>Update types.go<commit_after>package cloudstack

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strconv"

	"github.com/pborman/uuid"
)

type APIParameter interface{}

type Resource interface {
	// refresh information about the resource
	Refresh() error
	// update the resource with specified args
	Update(args map[string]interface{}) error
	// delete the resource
	Delete() error
	// set client for this resource
	setClient(*Client)
}

type ResourceBase struct {
	client *Client
}

func (*ResourceBase) Refresh() error {
	return errors.New("Not Implemented")
}

func (*ResourceBase) Update(map[string]interface{}) error {
	return errors.New("Not Implemented")
}

func (*ResourceBase) Delete() error {
	return errors.New("Not Implemented")
}

func (rb *ResourceBase) setClient(client *Client) {
	rb.client = new(Client)
	*rb.client = *client
}

type Nullable interface {
	Value() interface{}
	String() string
	IsNil() bool
}

type Setter interface {
	Set(interface{}) error
}

func unmarshalJSON(s Setter, b []byte) error {
	var v interface{}

	// initialize by nil
	if err := s.Set(nil); err != nil {
		return err
	}

	decoder := json.NewDecoder(bytes.NewReader(b))
	decoder.UseNumber()
	if err := decoder.Decode(&v); err != nil {
		return err
	}

	return s.Set(v)
}

// Base struct of Nullable
type NullBase struct {
	valid bool
	value interface{}
}

func (n NullBase) MarshalJSON() ([]byte, error) {
	if n.IsNil() {
		return json.Marshal(nil)
	}
	return json.Marshal(n.value)
}

func (nb *NullBase) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nb, b)
}

// Set value. If nil is given, value is cleared.
func (nb *NullBase) Set(value interface{}) error {

	nb.valid = false
	nb.value = nil

	if value == nil {
		return nil
	}

	nb.valid = true
	nb.value = value

	return nil
}

// Return Value. If no value is set, return nil.
func (nb NullBase) Value() interface{} {
	if nb.IsNil() {
		return nil
	}
	return nb.value
}

// Return Value as String. If no value is set, return "null".
func (nb NullBase) String() string {
	if nb.IsNil() {
		return "null"
	}
	return fmt.Sprint(nb.value)
}

// Check if value is nil.
func (nb NullBase) IsNil() bool {
	return !nb.valid
}

// Nullable Bool
type NullBool struct {
	NullBase
}

func (nb *NullBool) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nb, b)
}

// Set Value. Value is converted by strconv.ParseBool
func (nb *NullBool) Set(value interface{}) error {

	nb.valid = false
	nb.value = false

	if value == nil {
		return nil
	}

	b, err := strconv.ParseBool(fmt.Sprint(value))
	if err != nil {
		return err
	}

	nb.valid = true
	nb.value = b

	return nil
}

// Return Value as bool
func (nb NullBool) Bool() bool {
	return nb.value.(bool)
}

// Nullable String
type NullString struct {
	NullBase
}

func (ns *NullString) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(ns, b)
}

// Set Value. Value is converted by fmt.Sprint
func (ns *NullString) Set(value interface{}) error {

	ns.valid = false
	ns.value = ""

	if value == nil {
		return nil
	}

	ns.valid = true
	ns.value = fmt.Sprint(value)

	return nil
}

// Nullable Number
// Value is stored as string.
type NullNumber struct {
	NullString
}

func (nn *NullNumber) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(nn, b)
}

func (nn NullNumber) MarshalJSON() ([]byte, error) {
	return []byte(nn.String()), nil
}

// Set Value. Value is converted to string by fmt.Sprint
func (nn *NullNumber) Set(value interface{}) error {

	nn.valid = false
	nn.value = ""

	if value == nil {
		return nil
	}

	s := fmt.Sprint(value)
	_, err := strconv.ParseFloat(s, 64)
	if err != nil {
		return err
	}

	nn.valid = true
	nn.value = s

	return nil
}

// Return Value as int64
func (nn NullNumber) Int64() (int64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseInt(nn.value.(string), 10, 64)
}

// Return Value as uint64
func (nn NullNumber) UInt64() (uint64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseUint(nn.value.(string), 10, 64)
}

// Return Value as float64
func (nn NullNumber) Float64() (float64, error) {
	if nn.IsNil() {
		return 0, errors.New("NullNumber is nil")
	}
	return strconv.ParseFloat(nn.value.(string), 64)
}

// UUID or Integer ID
type ID struct {
	NullString
}

func (id *ID) UnmarshalJSON(b []byte) error {
	return unmarshalJSON(id, b)
}

// Set Value
func (id *ID) Set(value interface{}) error {

	id.valid = false
	id.value = ""

	if value == nil {
		return nil
	}

	s := fmt.Sprint(value)
	uuid := uuid.Parse(s)
	_, err := strconv.ParseFloat(s, 64)

	if uuid != nil || err == nil {
		id.valid = true
		id.value = s
	}

	return nil
}

func (id ID) UUID() uuid.UUID {
	return uuid.Parse(id.String())
}
<|endoftext|>


<commit_before>package datasource

import (
	"database/sql/driver"
	"fmt"
	"net/url"
	"time"

	u "github.com/araddon/gou"
	"github.com/araddon/qlbridge/expr"
	"github.com/araddon/qlbridge/value"
)

var (
	_ expr.ContextWriter = (*ContextSimple)(nil)
	_ expr.ContextReader = (*ContextSimple)(nil)
	_ expr.ContextWriter = (*ContextUrlValues)(nil)
	_ expr.ContextReader = (*ContextUrlValues)(nil)
	_                    = u.EMPTY
)

// represents a message routable by the topology. The Key() method
// is used to route the message in certain topologies. Body() is used
// to express something user specific.
// see  "https://github.com/mdmarek/topo" AND http://github.com/lytics/grid
type Message interface {
	Key() uint64
	Body() interface{}
}

type SqlDriverMessage struct {
	Vals []driver.Value
	Id   uint64
}

func (m *SqlDriverMessage) Key() uint64       { return m.Id }
func (m *SqlDriverMessage) Body() interface{} { return m.Vals }

type SqlDriverMessageMap struct {
	Vals map[string]driver.Value
	Id   uint64
}

func NewSqlDriverMessageMap() *SqlDriverMessageMap {
	return &SqlDriverMessageMap{Vals: make(map[string]driver.Value)}
}

func (m *SqlDriverMessageMap) Key() uint64       { return m.Id }
func (m *SqlDriverMessageMap) Body() interface{} { return m.Vals }

type ValueContextWrapper struct {
	*SqlDriverMessage
	cols map[string]*expr.Column
}

func NewValueContextWrapper(msg *SqlDriverMessage, cols map[string]*expr.Column) *ValueContextWrapper {
	return &ValueContextWrapper{msg, cols}
}
func (m *ValueContextWrapper) Get(key string) (value.Value, bool) {
	if col, ok := m.cols[key]; ok {
		if col.Index <= len(m.Vals) {
			return value.NewValue(m.Vals[col.Index]), true
		}
		u.Warnf("could not find index?: %v col.idx:%v   len(vals)=%v", key, col.Index, len(m.Vals))
	} else {
		u.Warnf("could not find key: %v", key)
	}
	return value.ErrValue, false
}
func (m *ValueContextWrapper) Row() map[string]value.Value {
	row := make(map[string]value.Value)
	for _, col := range m.cols {
		if col.Index <= len(m.Vals) {
			row[col.Key()] = value.NewValue(m.Vals[col.Index])
		}
	}
	return row
}
func (m *ValueContextWrapper) Ts() time.Time { return time.Time{} }

type UrlValuesMsg struct {
	id   uint64
	body *ContextUrlValues
}

func NewUrlValuesMsg(id uint64, body *ContextUrlValues) *UrlValuesMsg {
	return &UrlValuesMsg{id, body}
}

func (m *UrlValuesMsg) Key() uint64       { return m.id }
func (m *UrlValuesMsg) Body() interface{} { return m.body }
func (m *UrlValuesMsg) String() string    { return m.body.String() }

type ContextSimple struct {
	Data map[string]value.Value
	//Rows   []map[string]value.Value
	ts     time.Time
	cursor int
	keyval uint64
}

func NewContextSimple() *ContextSimple {
	return &ContextSimple{Data: make(map[string]value.Value), ts: time.Now(), cursor: 0}
}
func NewContextSimpleData(data map[string]value.Value) *ContextSimple {
	return &ContextSimple{Data: data, ts: time.Now(), cursor: 0}
}
func NewContextSimpleTs(data map[string]value.Value, ts time.Time) *ContextSimple {
	return &ContextSimple{Data: data, ts: ts, cursor: 0}
}

func (m *ContextSimple) All() map[string]value.Value { return m.Data }
func (m *ContextSimple) Row() map[string]value.Value { return m.Data }
func (m *ContextSimple) Body() interface{}           { return m }
func (m *ContextSimple) Key() uint64                 { return m.keyval }
func (m *ContextSimple) Ts() time.Time               { return m.ts }
func (m ContextSimple) Get(key string) (value.Value, bool) {
	val, ok := m.Data[key]
	return val, ok
}

func (m *ContextSimple) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	//u.Infof("put context:  %v %T:%v", col.Key(), v, v)
	m.Data[col.Key()] = v
	return nil
}
func (m *ContextSimple) Commit(rowInfo []expr.SchemaInfo, row expr.RowWriter) error {
	//m.Rows = append(m.Rows, m.Data)
	//m.Data = make(map[string]value.Value)
	return nil
}
func (m *ContextSimple) Delete(row map[string]value.Value) error {
	return nil
}

type ContextWriterEmpty struct{}

func (m *ContextWriterEmpty) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	return nil
}
func (m *ContextWriterEmpty) Delete(delRow map[string]value.Value) error { return nil }

type ContextUrlValues struct {
	Data url.Values
	ts   time.Time
}

func NewContextUrlValues(uv url.Values) *ContextUrlValues {
	return &ContextUrlValues{uv, time.Now()}
}
func NewContextUrlValuesTs(uv url.Values, ts time.Time) *ContextUrlValues {
	return &ContextUrlValues{uv, ts}
}
func (m *ContextUrlValues) String() string {
	if m == nil || len(m.Data) == 0 {
		return ""
	}
	return m.Data.Encode()
}
func (m ContextUrlValues) Get(key string) (value.Value, bool) {
	vals, ok := m.Data[key]
	if ok {
		if len(vals) == 1 {
			return value.NewValue(vals[0]), true
		}
		return value.NewValue(vals), true
	}
	return value.EmptyStringValue, true
}
func (m ContextUrlValues) Row() map[string]value.Value {
	mi := make(map[string]value.Value)
	for k, v := range m.Data {
		if len(v) == 1 {
			mi[k] = value.NewValue(v[0])
		} else if len(v) > 1 {
			mi[k] = value.NewStringsValue(v)
		}
	}
	return mi
}
func (m *ContextUrlValues) Delete(delRow map[string]value.Value) error {
	return fmt.Errorf("Not implemented")
}
func (m ContextUrlValues) Ts() time.Time {
	return m.ts
}

func (m ContextUrlValues) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	key := col.Key()
	switch typedValue := v.(type) {
	case value.StringValue:
		m.Data.Set(key, typedValue.ToString())
	case value.NumberValue:
		m.Data.Set(key, typedValue.ToString())
	}
	return nil
}
<commit_msg>revert context false value<commit_after>package datasource

import (
	"database/sql/driver"
	"fmt"
	"net/url"
	"time"

	u "github.com/araddon/gou"
	"github.com/araddon/qlbridge/expr"
	"github.com/araddon/qlbridge/value"
)

var (
	_ expr.ContextWriter = (*ContextSimple)(nil)
	_ expr.ContextReader = (*ContextSimple)(nil)
	_ expr.ContextWriter = (*ContextUrlValues)(nil)
	_ expr.ContextReader = (*ContextUrlValues)(nil)
	_                    = u.EMPTY
)

// represents a message routable by the topology. The Key() method
// is used to route the message in certain topologies. Body() is used
// to express something user specific.
// see  "https://github.com/mdmarek/topo" AND http://github.com/lytics/grid
type Message interface {
	Key() uint64
	Body() interface{}
}

type SqlDriverMessage struct {
	Vals []driver.Value
	Id   uint64
}

func (m *SqlDriverMessage) Key() uint64       { return m.Id }
func (m *SqlDriverMessage) Body() interface{} { return m.Vals }

type SqlDriverMessageMap struct {
	Vals map[string]driver.Value
	Id   uint64
}

func NewSqlDriverMessageMap() *SqlDriverMessageMap {
	return &SqlDriverMessageMap{Vals: make(map[string]driver.Value)}
}

func (m *SqlDriverMessageMap) Key() uint64       { return m.Id }
func (m *SqlDriverMessageMap) Body() interface{} { return m.Vals }

type ValueContextWrapper struct {
	*SqlDriverMessage
	cols map[string]*expr.Column
}

func NewValueContextWrapper(msg *SqlDriverMessage, cols map[string]*expr.Column) *ValueContextWrapper {
	return &ValueContextWrapper{msg, cols}
}
func (m *ValueContextWrapper) Get(key string) (value.Value, bool) {
	if col, ok := m.cols[key]; ok {
		if col.Index <= len(m.Vals) {
			return value.NewValue(m.Vals[col.Index]), true
		}
		u.Warnf("could not find index?: %v col.idx:%v   len(vals)=%v", key, col.Index, len(m.Vals))
	} else {
		u.Warnf("could not find key: %v", key)
	}
	return value.ErrValue, false
}
func (m *ValueContextWrapper) Row() map[string]value.Value {
	row := make(map[string]value.Value)
	for _, col := range m.cols {
		if col.Index <= len(m.Vals) {
			row[col.Key()] = value.NewValue(m.Vals[col.Index])
		}
	}
	return row
}
func (m *ValueContextWrapper) Ts() time.Time { return time.Time{} }

type UrlValuesMsg struct {
	id   uint64
	body *ContextUrlValues
}

func NewUrlValuesMsg(id uint64, body *ContextUrlValues) *UrlValuesMsg {
	return &UrlValuesMsg{id, body}
}

func (m *UrlValuesMsg) Key() uint64       { return m.id }
func (m *UrlValuesMsg) Body() interface{} { return m.body }
func (m *UrlValuesMsg) String() string    { return m.body.String() }

type ContextSimple struct {
	Data map[string]value.Value
	//Rows   []map[string]value.Value
	ts     time.Time
	cursor int
	keyval uint64
}

func NewContextSimple() *ContextSimple {
	return &ContextSimple{Data: make(map[string]value.Value), ts: time.Now(), cursor: 0}
}
func NewContextSimpleData(data map[string]value.Value) *ContextSimple {
	return &ContextSimple{Data: data, ts: time.Now(), cursor: 0}
}
func NewContextSimpleTs(data map[string]value.Value, ts time.Time) *ContextSimple {
	return &ContextSimple{Data: data, ts: ts, cursor: 0}
}

func (m *ContextSimple) All() map[string]value.Value { return m.Data }
func (m *ContextSimple) Row() map[string]value.Value { return m.Data }
func (m *ContextSimple) Body() interface{}           { return m }
func (m *ContextSimple) Key() uint64                 { return m.keyval }
func (m *ContextSimple) Ts() time.Time               { return m.ts }
func (m ContextSimple) Get(key string) (value.Value, bool) {
	val, ok := m.Data[key]
	return val, ok
}

func (m *ContextSimple) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	//u.Infof("put context:  %v %T:%v", col.Key(), v, v)
	m.Data[col.Key()] = v
	return nil
}
func (m *ContextSimple) Commit(rowInfo []expr.SchemaInfo, row expr.RowWriter) error {
	//m.Rows = append(m.Rows, m.Data)
	//m.Data = make(map[string]value.Value)
	return nil
}
func (m *ContextSimple) Delete(row map[string]value.Value) error {
	return nil
}

type ContextWriterEmpty struct{}

func (m *ContextWriterEmpty) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	return nil
}
func (m *ContextWriterEmpty) Delete(delRow map[string]value.Value) error { return nil }

type ContextUrlValues struct {
	Data url.Values
	ts   time.Time
}

func NewContextUrlValues(uv url.Values) *ContextUrlValues {
	return &ContextUrlValues{uv, time.Now()}
}
func NewContextUrlValuesTs(uv url.Values, ts time.Time) *ContextUrlValues {
	return &ContextUrlValues{uv, ts}
}
func (m *ContextUrlValues) String() string {
	if m == nil || len(m.Data) == 0 {
		return ""
	}
	return m.Data.Encode()
}
func (m ContextUrlValues) Get(key string) (value.Value, bool) {
	vals, ok := m.Data[key]
	if ok {
		if len(vals) == 1 {
			return value.NewValue(vals[0]), true
		}
		return value.NewValue(vals), true
	}
	return value.EmptyStringValue, false
}
func (m ContextUrlValues) Row() map[string]value.Value {
	mi := make(map[string]value.Value)
	for k, v := range m.Data {
		if len(v) == 1 {
			mi[k] = value.NewValue(v[0])
		} else if len(v) > 1 {
			mi[k] = value.NewStringsValue(v)
		}
	}
	return mi
}
func (m *ContextUrlValues) Delete(delRow map[string]value.Value) error {
	return fmt.Errorf("Not implemented")
}
func (m ContextUrlValues) Ts() time.Time {
	return m.ts
}

func (m ContextUrlValues) Put(col expr.SchemaInfo, rctx expr.ContextReader, v value.Value) error {
	key := col.Key()
	switch typedValue := v.(type) {
	case value.StringValue:
		m.Data.Set(key, typedValue.ToString())
	case value.NumberValue:
		m.Data.Set(key, typedValue.ToString())
	}
	return nil
}
<|endoftext|>