<commit_before>package rocsp

import (
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/jmhodges/clock"
	"github.com/letsencrypt/boulder/core"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/crypto/ocsp"
)

var ErrRedisNotFound = errors.New("redis key not found")

// Metadata represents information stored with the 'm' prefix in the Redis DB:
// information required to maintain or serve the response, but not the response
// itself.
type Metadata struct {
	ShortIssuerID byte
	// ThisUpdate contains the ThisUpdate time of the stored OCSP response.
	ThisUpdate time.Time
}

// String implements pretty-printing of Metadata
func (m Metadata) String() string {
	return fmt.Sprintf("shortIssuerID: 0x%x, updated at: %s", m.ShortIssuerID, m.ThisUpdate)
}

// Marshal turns a metadata into a slice of 9 bytes for writing into Redis.
// Storing these always as 9 bytes gives us some potential to change the
// storage format non-disruptively in the future, so long as we can distinguish
// on the length of the stored value.
func (m Metadata) Marshal() []byte {
	var output [9]byte
	output[0] = m.ShortIssuerID
	var epochSeconds uint64 = uint64(m.ThisUpdate.Unix())
	binary.LittleEndian.PutUint64(output[1:], epochSeconds)
	return output[:]
}

// UnmarshalMetadata takes data from Redis and turns it into a Metadata object.
func UnmarshalMetadata(input []byte) (Metadata, error) {
	if len(input) != 9 {
		return Metadata{}, fmt.Errorf("invalid metadata length %d", len(input))
	}
	var output Metadata
	output.ShortIssuerID = input[0]
	epochSeconds := binary.LittleEndian.Uint64(input[1:])
	output.ThisUpdate = time.Unix(int64(epochSeconds), 0).UTC()
	return output, nil
}

// MakeResponseKey generates a Redis key string under which a response with the
// given serial should be stored.
func MakeResponseKey(serial string) string {
	return fmt.Sprintf("r{%s}", serial)
}

// MakeMetadataKey generates a Redis key string under which metadata for the
// response with the given serial should be stored.
func MakeMetadataKey(serial string) string {
	return fmt.Sprintf("m{%s}", serial)
}

// Client represents a read-only Redis client.
type Client struct {
	rdb     *redis.ClusterClient
	timeout time.Duration
	clk     clock.Clock
	rdc     metricsCollector
}

// NewClient creates a Client. The timeout applies to all requests, though a shorter timeout can be
// applied on a per-request basis using context.Context.
func NewClient(rdb *redis.ClusterClient, timeout time.Duration, clk clock.Clock, stats prometheus.Registerer) *Client {
	dbc := metricsCollector{rdb: rdb}

	labels := prometheus.Labels{"address": rdb.Options().Addrs[0], "user": rdb.Options().Username}
	dbc.hits = prometheus.NewDesc(
		"redis_hits",
		"Number of times free connection was found in the pool.",
		nil, labels)
	dbc.misses = prometheus.NewDesc(
		"redis_misses",
		"Number of times free connection was NOT found in the pool.",
		nil, labels)
	dbc.timeouts = prometheus.NewDesc(
		"redis_timeouts",
		"Number of times a wait timeout occurred.",
		nil, labels)
	dbc.totalConns = prometheus.NewDesc(
		"redis_total_conns",
		"Number of total connections in the pool.",
		nil, labels)
	dbc.idleConns = prometheus.NewDesc(
		"redis_idle_conns",
		"Number of idle connections in the pool.",
		nil, labels)
	dbc.staleConns = prometheus.NewDesc(
		"redis_stale_conns",
		"Number of stale connections removed from the pool.",
		nil, labels)

	stats.MustRegister(dbc)

	return &Client{
		rdb:     rdb,
		timeout: timeout,
		clk:     clk,
		rdc:     dbc,
	}
}

// WritingClient represents a Redis client that can both read and write.
type WritingClient struct {
	*Client
}

// NewWritingClient creates a WritingClient.
func NewWritingClient(rdb *redis.ClusterClient, timeout time.Duration, clk clock.Clock, stats prometheus.Registerer) *WritingClient {
	return &WritingClient{NewClient(rdb, timeout, clk, stats)}
}

// StoreResponse parses the given bytes as an OCSP response, and stores it into
// Redis, updating both the metadata and response keys. ShortIssuerID is an
// arbitrarily assigned byte that unique identifies each issuer. Must be the
// same across OCSP components. Returns error if the OCSP response fails to
// parse.
func (c *WritingClient) StoreResponse(ctx context.Context, respBytes []byte, shortIssuerID byte, ttl time.Duration) error {
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	resp, err := ocsp.ParseResponse(respBytes, nil)
	if err != nil {
		return fmt.Errorf("parsing %d-byte response: %w", len(respBytes), err)
	}

	serial := core.SerialToString(resp.SerialNumber)

	responseKey := MakeResponseKey(serial)
	metadataKey := MakeMetadataKey(serial)

	metadataStruct := Metadata{
		ThisUpdate:    resp.ThisUpdate,
		ShortIssuerID: shortIssuerID,
	}
	metadataValue := metadataStruct.Marshal()

	err = c.rdb.Watch(ctx, func(tx *redis.Tx) error {
		err = tx.Set(ctx, responseKey, respBytes, ttl).Err()
		if err != nil {
			return fmt.Errorf("setting response: %w", err)
		}

		err = tx.Set(ctx, metadataKey, metadataValue, ttl).Err()
		if err != nil {
			return fmt.Errorf("setting metadata: %w", err)
		}

		return nil
	}, metadataKey, responseKey)
	if err != nil {
		return fmt.Errorf("transaction failed: %w", err)
	}

	return nil
}

// GetResponse fetches a response for the given serial number.
// Returns error if the OCSP response fails to parse.
// Does not check the metadata field.
func (c *Client) GetResponse(ctx context.Context, serial string) ([]byte, error) {
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	responseKey := MakeResponseKey(serial)

	resp, err := c.rdb.Get(ctx, responseKey).Result()
	if err != nil {
		// go-redis `Get` returns redis.Nil error when key does not exist. In
		// that case return a `ErrRedisNotFound` error.
		if errors.Is(err, redis.Nil) {
			return nil, ErrRedisNotFound
		}
		return nil, fmt.Errorf("getting response: %w", err)
	}

	return []byte(resp), nil
}

// GetMetadata fetches the metadata for the given serial number.
func (c *Client) GetMetadata(ctx context.Context, serial string) (*Metadata, error) {
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	metadataKey := MakeMetadataKey(serial)

	resp, err := c.rdb.Get(ctx, metadataKey).Result()
	if err != nil {
		// go-redis `Get` returns redis.Nil error when key does not exist. In
		// that case return a `ErrRedisNotFound` error.
		if errors.Is(err, redis.Nil) {
			return nil, ErrRedisNotFound
		}
		return nil, fmt.Errorf("getting metadata: %w", err)
	}
	metadata, err := UnmarshalMetadata([]byte(resp))
	if err != nil {
		return nil, fmt.Errorf("unmarshaling metadata: %w", err)
	}
	return &metadata, nil
}
<commit_msg>Add latency metrics to rocsp (#5865)<commit_after>package rocsp

import (
	"context"
	"encoding/binary"
	"errors"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/jmhodges/clock"
	"github.com/letsencrypt/boulder/core"
	"github.com/prometheus/client_golang/prometheus"
	"golang.org/x/crypto/ocsp"
)

var ErrRedisNotFound = errors.New("redis key not found")

// Metadata represents information stored with the 'm' prefix in the Redis DB:
// information required to maintain or serve the response, but not the response
// itself.
type Metadata struct {
	ShortIssuerID byte
	// ThisUpdate contains the ThisUpdate time of the stored OCSP response.
	ThisUpdate time.Time
}

// String implements pretty-printing of Metadata
func (m Metadata) String() string {
	return fmt.Sprintf("shortIssuerID: 0x%x, updated at: %s", m.ShortIssuerID, m.ThisUpdate)
}

// Marshal turns a metadata into a slice of 9 bytes for writing into Redis.
// Storing these always as 9 bytes gives us some potential to change the
// storage format non-disruptively in the future, so long as we can distinguish
// on the length of the stored value.
func (m Metadata) Marshal() []byte {
	var output [9]byte
	output[0] = m.ShortIssuerID
	var epochSeconds uint64 = uint64(m.ThisUpdate.Unix())
	binary.LittleEndian.PutUint64(output[1:], epochSeconds)
	return output[:]
}

// UnmarshalMetadata takes data from Redis and turns it into a Metadata object.
func UnmarshalMetadata(input []byte) (Metadata, error) {
	if len(input) != 9 {
		return Metadata{}, fmt.Errorf("invalid metadata length %d", len(input))
	}
	var output Metadata
	output.ShortIssuerID = input[0]
	epochSeconds := binary.LittleEndian.Uint64(input[1:])
	output.ThisUpdate = time.Unix(int64(epochSeconds), 0).UTC()
	return output, nil
}

// MakeResponseKey generates a Redis key string under which a response with the
// given serial should be stored.
func MakeResponseKey(serial string) string {
	return fmt.Sprintf("r{%s}", serial)
}

// MakeMetadataKey generates a Redis key string under which metadata for the
// response with the given serial should be stored.
func MakeMetadataKey(serial string) string {
	return fmt.Sprintf("m{%s}", serial)
}

// Client represents a read-only Redis client.
type Client struct {
	rdb        *redis.ClusterClient
	timeout    time.Duration
	clk        clock.Clock
	rdc        metricsCollector
	getLatency *prometheus.HistogramVec
}

// NewClient creates a Client. The timeout applies to all requests, though a shorter timeout can be
// applied on a per-request basis using context.Context.
func NewClient(
	rdb *redis.ClusterClient,
	timeout time.Duration,
	clk clock.Clock,
	stats prometheus.Registerer,
) *Client {
	dbc := metricsCollector{rdb: rdb}

	labels := prometheus.Labels{"address": rdb.Options().Addrs[0], "user": rdb.Options().Username}
	dbc.hits = prometheus.NewDesc(
		"redis_hits",
		"Number of times free connection was found in the pool.",
		nil, labels)
	dbc.misses = prometheus.NewDesc(
		"redis_misses",
		"Number of times free connection was NOT found in the pool.",
		nil, labels)
	dbc.timeouts = prometheus.NewDesc(
		"redis_timeouts",
		"Number of times a wait timeout occurred.",
		nil, labels)
	dbc.totalConns = prometheus.NewDesc(
		"redis_total_conns",
		"Number of total connections in the pool.",
		nil, labels)
	dbc.idleConns = prometheus.NewDesc(
		"redis_idle_conns",
		"Number of idle connections in the pool.",
		nil, labels)
	dbc.staleConns = prometheus.NewDesc(
		"redis_stale_conns",
		"Number of stale connections removed from the pool.",
		nil, labels)
	stats.MustRegister(dbc)
	getLatency := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "rocsp_get_latency",
			Help: "Histogram of latencies of rocsp.GetResponse and rocsp.GetMetadata calls with result and method labels",
		},
		[]string{"result", "method"},
	)
	stats.MustRegister(getLatency)

	return &Client{
		rdb:        rdb,
		timeout:    timeout,
		clk:        clk,
		rdc:        dbc,
		getLatency: getLatency,
	}
}

// WritingClient represents a Redis client that can both read and write.
type WritingClient struct {
	*Client
	storeResponseLatency *prometheus.HistogramVec
}

// NewWritingClient creates a WritingClient.
func NewWritingClient(rdb *redis.ClusterClient, timeout time.Duration, clk clock.Clock, stats prometheus.Registerer) *WritingClient {
	storeResponseLatency := prometheus.NewHistogramVec(
		prometheus.HistogramOpts{
			Name: "rocsp_store_response_latency",
			Help: "Histogram of latencies of rocsp.StoreResponse calls with result labels",
		},
		[]string{"result"},
	)
	stats.MustRegister(storeResponseLatency)
	return &WritingClient{NewClient(rdb, timeout, clk, stats), storeResponseLatency}
}

// StoreResponse parses the given bytes as an OCSP response, and stores it into
// Redis, updating both the metadata and response keys. ShortIssuerID is an
// arbitrarily assigned byte that unique identifies each issuer. Must be the
// same across OCSP components. Returns error if the OCSP response fails to
// parse.
func (c *WritingClient) StoreResponse(ctx context.Context, respBytes []byte, shortIssuerID byte, ttl time.Duration) error {
	start := c.clk.Now()
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	resp, err := ocsp.ParseResponse(respBytes, nil)
	if err != nil {
		return fmt.Errorf("parsing %d-byte response: %w", len(respBytes), err)
	}

	serial := core.SerialToString(resp.SerialNumber)

	responseKey := MakeResponseKey(serial)
	metadataKey := MakeMetadataKey(serial)

	metadataStruct := Metadata{
		ThisUpdate:    resp.ThisUpdate,
		ShortIssuerID: shortIssuerID,
	}
	metadataValue := metadataStruct.Marshal()

	err = c.rdb.Watch(ctx, func(tx *redis.Tx) error {
		err = tx.Set(ctx, responseKey, respBytes, ttl).Err()
		if err != nil {
			return fmt.Errorf("setting response: %w", err)
		}

		err = tx.Set(ctx, metadataKey, metadataValue, ttl).Err()
		if err != nil {
			return fmt.Errorf("setting metadata: %w", err)
		}

		return nil
	}, metadataKey, responseKey)
	if err != nil {
		state := "failed"
		if errors.Is(err, context.DeadlineExceeded) {
			state = "deadlineExceeded"
		} else if errors.Is(err, context.Canceled) {
			state = "canceled"
		}
		c.storeResponseLatency.With(prometheus.Labels{"result": state}).Observe(time.Since(start).Seconds())
		return fmt.Errorf("transaction failed: %w", err)
	}

	c.storeResponseLatency.With(prometheus.Labels{"result": "success"}).Observe(time.Since(start).Seconds())
	return nil
}

// GetResponse fetches a response for the given serial number.
// Returns error if the OCSP response fails to parse.
// Does not check the metadata field.
func (c *Client) GetResponse(ctx context.Context, serial string) ([]byte, error) {
	start := c.clk.Now()
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	responseKey := MakeResponseKey(serial)

	resp, err := c.rdb.Get(ctx, responseKey).Result()
	if err != nil {
		// go-redis `Get` returns redis.Nil error when key does not exist. In
		// that case return a `ErrRedisNotFound` error.
		if errors.Is(err, redis.Nil) {
			c.getLatency.With(prometheus.Labels{"result": "notFound", "method": "GetResponse"}).Observe(time.Since(start).Seconds())
			return nil, ErrRedisNotFound
		}

		state := "failed"
		if errors.Is(err, context.DeadlineExceeded) {
			state = "deadlineExceeded"
		} else if errors.Is(err, context.Canceled) {
			state = "canceled"
		}
		c.getLatency.With(prometheus.Labels{"result": state, "method": "GetResponse"}).Observe(time.Since(start).Seconds())
		return nil, fmt.Errorf("getting response: %w", err)
	}

	c.getLatency.With(prometheus.Labels{"result": "success", "method": "GetResponse"}).Observe(time.Since(start).Seconds())
	return []byte(resp), nil
}

// GetMetadata fetches the metadata for the given serial number.
func (c *Client) GetMetadata(ctx context.Context, serial string) (*Metadata, error) {
	start := c.clk.Now()
	ctx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	metadataKey := MakeMetadataKey(serial)

	resp, err := c.rdb.Get(ctx, metadataKey).Result()
	if err != nil {
		// go-redis `Get` returns redis.Nil error when key does not exist. In
		// that case return a `ErrRedisNotFound` error.
		if errors.Is(err, redis.Nil) {
			c.getLatency.With(prometheus.Labels{"result": "notFound", "method": "GetMetadata"}).Observe(time.Since(start).Seconds())
			return nil, ErrRedisNotFound
		}

		state := "failed"
		if errors.Is(err, context.DeadlineExceeded) {
			state = "deadlineExceeded"
		} else if errors.Is(err, context.Canceled) {
			state = "canceled"
		}
		c.getLatency.With(prometheus.Labels{"result": state, "method": "GetMetadata"}).Observe(time.Since(start).Seconds())
		return nil, fmt.Errorf("getting metadata: %w", err)
	}
	metadata, err := UnmarshalMetadata([]byte(resp))
	if err != nil {
		c.getLatency.With(prometheus.Labels{"result": "failed", "method": "GetMetadata"}).Observe(time.Since(start).Seconds())
		return nil, fmt.Errorf("unmarshaling metadata: %w", err)
	}

	c.getLatency.With(prometheus.Labels{"result": "success", "method": "GetMetadata"}).Observe(time.Since(start).Seconds())
	return &metadata, nil
}
<|endoftext|>


<commit_before>package geojson

import (
	"reflect"
	"testing"

	"github.com/terranodo/tegola/geom"
)

func TestClosePolygon(t *testing.T) {
	type tcase struct {
		geom     geom.Polygon
		expected geom.Polygon
	}

	fn := func(t *testing.T, tc tcase) {
		t.Parallel()

		closePolygon(tc.geom)

		if !reflect.DeepEqual(tc.expected, tc.geom) {
			t.Errorf("expected %v got %v", tc.expected, tc.geom)
			return
		}
	}

	tests := map[string]tcase{
		"needs closing": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
		},
		"already closed": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
		},
		"two point only polygon": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
		},
		"two point polygon 1": {
			geom: geom.Polygon{
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3, 4},
				},
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
		},
		"two point only polygon 2": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3, 4},
				},
			},
		},
	}

	for name, tc := range tests {
		tc := tc
		t.Run(name, func(t *testing.T) { fn(t, tc) })
	}
}

func BenchmarkClosePolygon(b *testing.B) {
	p := geom.Polygon{
		{
			geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
		},
	}

	// run the Fib function b.N times
	for n := 0; n < b.N; n++ {
		closePolygon(p)
	}
}
<commit_msg>removed comment left over from example code #327<commit_after>package geojson

import (
	"reflect"
	"testing"

	"github.com/terranodo/tegola/geom"
)

func TestClosePolygon(t *testing.T) {
	type tcase struct {
		geom     geom.Polygon
		expected geom.Polygon
	}

	fn := func(t *testing.T, tc tcase) {
		t.Parallel()

		closePolygon(tc.geom)

		if !reflect.DeepEqual(tc.expected, tc.geom) {
			t.Errorf("expected %v got %v", tc.expected, tc.geom)
			return
		}
	}

	tests := map[string]tcase{
		"needs closing": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
		},
		"already closed": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3.2, 4.3},
				},
			},
		},
		"two point only polygon": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
		},
		"two point polygon 1": {
			geom: geom.Polygon{
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3, 4},
				},
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
			},
		},
		"two point only polygon 2": {
			geom: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
				},
			},
			expected: geom.Polygon{
				{
					geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5},
				},
				{
					geom.Point{3, 4}, geom.Point{5, 7}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9}, geom.Point{3, 4},
				},
			},
		},
	}

	for name, tc := range tests {
		tc := tc
		t.Run(name, func(t *testing.T) { fn(t, tc) })
	}
}

func BenchmarkClosePolygon(b *testing.B) {
	p := geom.Polygon{
		{
			geom.Point{3.2, 4.3}, geom.Point{5.4, 6.5}, geom.Point{7.6, 8.7}, geom.Point{9.8, 10.9},
		},
	}

	for n := 0; n < b.N; n++ {
		closePolygon(p)
	}
}
<|endoftext|>


<commit_before>package emu

import (
	"math/rand"

	"github.com/valep27/GChip8/util"
)

// Decode maps an opcode to the function that implements it.
func Decode(opcode uint16) (instr OpcodeFunc, ok bool) {
	ok = true

	switch opcode & 0xF000 {
	case 0x0000:
		instr = Nop
	case 0x6000:
		instr = SetVxToImmediate
	default:
		ok = false
	}

	return
}

// Nop does nothing
// This is not an actual opcode, just a placeholder.
func Nop(c8 *Chip8) {
	c8.pc += 2
}

// SetVxToImmediate implements opcode 6XNN.
// It will set NN (8 bit immediate) to the register Vx.
func SetVxToImmediate(c8 *Chip8) {
	x := (c8.opcode & 0x0F00) >> 8
	nn := uint8(c8.opcode & 0x00FF)

	c8.V[x] = nn
	c8.pc += 2
}

// ClearScreen implements opcode 00E0.
// Resets the screen pixel values
func ClearScreen(c8 *Chip8) {
	for i := 0; i < len(c8.vram); i++ {
		c8.vram[i] = 0
	}
	c8.pc += 2
}

// ReturnFromSub implements opcode 00EE.
// Returns from a subroutine, meaning it will set the PC to the last stack value.
func ReturnFromSub(c8 *Chip8) {
	c8.pc = c8.stack[c8.sp]
	c8.sp++
}

// JumpAddr implements opcode 1NNN.
// Sets the program counter to NNN.
func JumpAddr(c8 *Chip8) {
	c8.pc = c8.opcode & 0x0FFF
}

// CallSubAtNNN implements opcode 2NNN.
// It will call the subroutine at address NNN, i.e. move the PC to it.
func CallSubAtNNN(c8 *Chip8) {
	c8.stack[c8.sp] = c8.pc
	c8.sp--
	c8.pc = c8.opcode & 0x0FFF
}

// SkipIfVxEqualToNN implements opcode 3XNN.
// It will skip the next instruction if Vx == NN.
func SkipIfVxEqualToNN(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := c8.opcode & 0x00FF

	if c8.V[x] == uint8(nn) {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SkipIfVxNotEqualToNN implements opcode 4XNN.
// It will skip the next instruction if Vx != NN.
func SkipIfVxNotEqualToNN(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := c8.opcode & 0x00FF

	if c8.V[x] != uint8(nn) {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SkipIfVxEqualToVy implements opcode 5XY0.
// It will skip the next instruction if Vx == Vy.
func SkipIfVxEqualToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	if c8.V[x] == c8.V[y] {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// AddNNToVx implements opcode 7XNN
// It will add NN to the Vx register
func AddNNToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := uint8(c8.opcode & 0x00FF)
	c8.V[x] += nn
	c8.pc += 2
}

// AssignVyToVx implements opcode 8XY0
// Assigns the value of Vy to Vx
func AssignVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[y]
	c8.pc += 2
}

// VxOrVy implements opcode 8XY1
// Assigns the value of Vx | Vy to Vx
func VxOrVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] | c8.V[y]
	c8.pc += 2
}

// VxAndVy implements opcode 8XY2
// Assigns the value of Vx & Vy to Vx
func VxAndVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] & c8.V[y]
	c8.pc += 2
}

// VxXorVy implements opcode 8XY3
// Assigns the value of Vx xor Vy to Vx
func VxXorVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] ^ c8.V[y]
	c8.pc += 2
}

// AddVyToVx implements opcode 8XY4
// Math	Vx += Vy	Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
func AddVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, carry := util.CheckedAdd(c8.V[x], c8.V[y])
	c8.V[0xF] = 0
	c8.V[x] = result

	if carry {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// SubVyToVx implements opcode 8XY5
// Math	Vx -= Vy	VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
func SubVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, borrow := util.CheckedSub(c8.V[x], c8.V[y])
	c8.V[0xF] = 0
	c8.V[x] = result

	if borrow {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// ShiftVxRight implements opcode 8XY6
// BitOp	Vx >> 1	Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.[2]
func ShiftVxRight(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F

	lsb := uint8(x) & 1
	c8.V[x] = c8.V[x] >> 1
	c8.V[0xF] = lsb

	c8.pc += 2
}

// SubVxToVy implements opcode 8XY7
// Math	Vx=Vy-Vx	Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
func SubVxToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, borrow := util.CheckedSub(c8.V[y], c8.V[x])
	c8.V[0xF] = 0
	c8.V[x] = result

	if borrow {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// ShiftVxLeft implements opcode 8XYE
// BitOp	Vx << 1	Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.[2]
func ShiftVxLeft(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F

	msb := uint8(x) & 0x80
	c8.V[x] = c8.V[x] << 1
	c8.V[0xF] = msb

	c8.pc += 2
}

// SkipIfVxNotEqualToVy implements opcode 9XY0
// Cond	if(Vx!=Vy)	Skips the next instruction if VX doesn't equal VY.
func SkipIfVxNotEqualToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	if c8.V[x] != c8.V[y] {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SetMemoryNNN implements opcode ANNN
// MEM	I = NNN	Sets I to the address NNN.
func SetMemoryNNN(c8 *Chip8) {
	c8.I = c8.opcode & 0x0FFF
	c8.pc += 2
}

// JumpAddrSum implements opcode BNNN
// Flow PC=V0+NNN	Jumps to the address NNN plus V0.
func JumpAddrSum(c8 *Chip8) {
	c8.pc = (c8.opcode & 0x0FFF) + uint16(c8.V[0])
}

// RandToVx implements opcode CXNN
// Rand Vx=rand()&NN	Sets VX to the result of a bitwise and operation on a random number (Typically: 0 to 255) and NN.
func RandToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := uint8(c8.opcode)

	c8.V[x] = uint8(rand.Intn(256)) & nn

	c8.pc += 2
}
<commit_msg>Add stubs for remaining opcodes<commit_after>package emu

import (
	"math/rand"

	"github.com/valep27/GChip8/util"
)

// Decode maps an opcode to the function that implements it.
func Decode(opcode uint16) (instr OpcodeFunc, ok bool) {
	ok = true

	switch opcode & 0xF000 {
	case 0x0000:
		instr = Nop
	case 0x6000:
		instr = SetVxToImmediate
	default:
		ok = false
	}

	return
}

// Nop does nothing
// This is not an actual opcode, just a placeholder.
func Nop(c8 *Chip8) {
	c8.pc += 2
}

// SetVxToImmediate implements opcode 6XNN.
// It will set NN (8 bit immediate) to the register Vx.
func SetVxToImmediate(c8 *Chip8) {
	x := (c8.opcode & 0x0F00) >> 8
	nn := uint8(c8.opcode & 0x00FF)

	c8.V[x] = nn
	c8.pc += 2
}

// ClearScreen implements opcode 00E0.
// Resets the screen pixel values
func ClearScreen(c8 *Chip8) {
	for i := 0; i < len(c8.vram); i++ {
		c8.vram[i] = 0
	}
	c8.pc += 2
}

// ReturnFromSub implements opcode 00EE.
// Returns from a subroutine, meaning it will set the PC to the last stack value.
func ReturnFromSub(c8 *Chip8) {
	c8.pc = c8.stack[c8.sp]
	c8.sp++
}

// JumpAddr implements opcode 1NNN.
// Sets the program counter to NNN.
func JumpAddr(c8 *Chip8) {
	c8.pc = c8.opcode & 0x0FFF
}

// CallSubAtNNN implements opcode 2NNN.
// It will call the subroutine at address NNN, i.e. move the PC to it.
func CallSubAtNNN(c8 *Chip8) {
	c8.stack[c8.sp] = c8.pc
	c8.sp--
	c8.pc = c8.opcode & 0x0FFF
}

// SkipIfVxEqualToNN implements opcode 3XNN.
// It will skip the next instruction if Vx == NN.
func SkipIfVxEqualToNN(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := c8.opcode & 0x00FF

	if c8.V[x] == uint8(nn) {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SkipIfVxNotEqualToNN implements opcode 4XNN.
// It will skip the next instruction if Vx != NN.
func SkipIfVxNotEqualToNN(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := c8.opcode & 0x00FF

	if c8.V[x] != uint8(nn) {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SkipIfVxEqualToVy implements opcode 5XY0.
// It will skip the next instruction if Vx == Vy.
func SkipIfVxEqualToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	if c8.V[x] == c8.V[y] {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// AddNNToVx implements opcode 7XNN
// It will add NN to the Vx register
func AddNNToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := uint8(c8.opcode & 0x00FF)
	c8.V[x] += nn
	c8.pc += 2
}

// AssignVyToVx implements opcode 8XY0
// Assigns the value of Vy to Vx
func AssignVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[y]
	c8.pc += 2
}

// VxOrVy implements opcode 8XY1
// Assigns the value of Vx | Vy to Vx
func VxOrVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] | c8.V[y]
	c8.pc += 2
}

// VxAndVy implements opcode 8XY2
// Assigns the value of Vx & Vy to Vx
func VxAndVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] & c8.V[y]
	c8.pc += 2
}

// VxXorVy implements opcode 8XY3
// Assigns the value of Vx xor Vy to Vx
func VxXorVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F
	c8.V[x] = c8.V[x] ^ c8.V[y]
	c8.pc += 2
}

// AddVyToVx implements opcode 8XY4
// Math	Vx += Vy	Adds VY to VX. VF is set to 1 when there's a carry, and to 0 when there isn't.
func AddVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, carry := util.CheckedAdd(c8.V[x], c8.V[y])
	c8.V[0xF] = 0
	c8.V[x] = result

	if carry {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// SubVyToVx implements opcode 8XY5
// Math	Vx -= Vy	VY is subtracted from VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
func SubVyToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, borrow := util.CheckedSub(c8.V[x], c8.V[y])
	c8.V[0xF] = 0
	c8.V[x] = result

	if borrow {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// ShiftVxRight implements opcode 8XY6
// BitOp	Vx >> 1	Shifts VX right by one. VF is set to the value of the least significant bit of VX before the shift.[2]
func ShiftVxRight(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F

	lsb := uint8(x) & 1
	c8.V[x] = c8.V[x] >> 1
	c8.V[0xF] = lsb

	c8.pc += 2
}

// SubVxToVy implements opcode 8XY7
// Math	Vx=Vy-Vx	Sets VX to VY minus VX. VF is set to 0 when there's a borrow, and 1 when there isn't.
func SubVxToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	result, borrow := util.CheckedSub(c8.V[y], c8.V[x])
	c8.V[0xF] = 0
	c8.V[x] = result

	if borrow {
		c8.V[0xF] = 1
	}

	c8.pc += 2
}

// ShiftVxLeft implements opcode 8XYE
// BitOp	Vx << 1	Shifts VX left by one. VF is set to the value of the most significant bit of VX before the shift.[2]
func ShiftVxLeft(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F

	msb := uint8(x) & 0x80
	c8.V[x] = c8.V[x] << 1
	c8.V[0xF] = msb

	c8.pc += 2
}

// SkipIfVxNotEqualToVy implements opcode 9XY0
// Cond	if(Vx!=Vy)	Skips the next instruction if VX doesn't equal VY.
func SkipIfVxNotEqualToVy(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	y := (c8.opcode >> 4) & 0x000F

	if c8.V[x] != c8.V[y] {
		c8.pc += 4
	} else {
		c8.pc += 2
	}
}

// SetMemoryNNN implements opcode ANNN
// MEM	I = NNN	Sets I to the address NNN.
func SetMemoryNNN(c8 *Chip8) {
	c8.I = c8.opcode & 0x0FFF
	c8.pc += 2
}

// JumpAddrSum implements opcode BNNN
// Flow PC=V0+NNN	Jumps to the address NNN plus V0.
func JumpAddrSum(c8 *Chip8) {
	c8.pc = (c8.opcode & 0x0FFF) + uint16(c8.V[0])
}

// RandToVx implements opcode CXNN
// Rand Vx=rand()&NN	Sets VX to the result of a bitwise and operation on a random number (Typically: 0 to 255) and NN.
func RandToVx(c8 *Chip8) {
	x := (c8.opcode >> 8) & 0x000F
	nn := uint8(c8.opcode)

	c8.V[x] = uint8(rand.Intn(256)) & nn

	c8.pc += 2
}

// Draw implements opcode DXYN
// Disp	draw(Vx,Vy,N)	Draws a sprite at coordinate (VX, VY)
func Draw(c8 *Chip8) {
	// TODO
}

// SkipIfKeyPressed implements opcode EX9E
// KeyOp	if(key()==Vx)	Skips the next instruction if the key stored in VX is pressed. (Usually the next instruction is a jump to skip a code block)
func SkipIfKeyPressed(c8 *Chip8) {
	// TODO
}

// SkipIfKeyNotPressed implements opcode EXA1
// KeyOp	if(key()!=Vx)	Skips the next instruction if the key stored in VX isn't pressed. (Usually the next instruction is a jump to skip a code block)
func SkipIfKeyNotPressed(c8 *Chip8) {
	// TODO
}

// SetVxToDelay implements opcode FX07
// Timer	Vx = get_delay()	Sets VX to the value of the delay timer.
func SetVxToDelay(c8 *Chip8) {
	// TODO
}

// WaitForKeyPress implements opcode FX0A
// KeyOp	Vx = get_key()	A key press is awaited, and then stored in VX. (Blocking Operation. All instruction halted until next key event)
func WaitForKeyPress(c8 *Chip8) {
	// TODO
}

// SetDelayToVx implements opcode FX15
// Timer	delay_timer(Vx)	Sets the delay timer to VX.
func SetDelayToVx(c8 *Chip8) {
	// TODO
}

// SetSoundToVx implements opcode FX18
// Sound	sound_timer(Vx)	Sets the sound timer to VX.
func SetSoundToVx(c8 *Chip8) {
	// TODO
}

// AddVxToI implements opcode FX1E
// MEM	I +=Vx	Adds VX to I.[3]
func AddVxToI(c8 *Chip8) {
	// TODO
}

// SetIToSpriteAddr implements opcode FX29
// MEM	I=sprite_addr[Vx]	Sets I to the location of the sprite for the character in VX. Characters 0-F (in hexadecimal) are represented by a 4x5 font.
func SetIToSpriteAddr(c8 *Chip8) {
	// TODO
}

// SetBCD implements opcode FX33
// BCD	set_BCD(Vx);
func SetBCD(c8 *Chip8) {
	// TODO
}

// DumpRegisters implements opcode FX55
// MEM	reg_dump(Vx,&I)	Stores V0 to VX (including VX) in memory starting at address I.[4]
func DumpRegisters(c8 *Chip8) {
	// TODO
}

// LoadRegisters implements opcode FX65
// MEM	reg_load(Vx,&I)	Fills V0 to VX (including VX) with values from memory starting at address I.[4]
func LoadRegisters(c8 *Chip8) {
	// TODO
}
<|endoftext|>


<commit_before>package yang

import (
	"fmt"
	"os"
	"strings"

	"github.com/openconfig/goyang/pkg/yang"
	"github.com/oshothebig/pbast"
)

var builtinMap = map[yang.TypeKind]pbast.Type{
	yang.Yint8:   pbast.Int32,
	yang.Yint16:  pbast.Int32,
	yang.Yint32:  pbast.Int32,
	yang.Yint64:  pbast.Int64,
	yang.Yuint8:  pbast.UInt32,
	yang.Yuint16: pbast.UInt32,
	yang.Yuint32: pbast.UInt32,
	yang.Yuint64: pbast.UInt64,
	yang.Ystring: pbast.String,
	yang.Ybool:   pbast.Bool,
	yang.Ybinary: pbast.Bytes,
}

var builtinTypes = stringSet{
	"int8":                struct{}{},
	"int16":               struct{}{},
	"int32":               struct{}{},
	"int64":               struct{}{},
	"uint8":               struct{}{},
	"uint16":              struct{}{},
	"unit32":              struct{}{},
	"uint64":              struct{}{},
	"string":              struct{}{},
	"boolean":             struct{}{},
	"enumeration":         struct{}{},
	"bits":                struct{}{},
	"binary":              struct{}{},
	"leafref":             struct{}{},
	"identityref":         struct{}{},
	"empty":               struct{}{},
	"union":               struct{}{},
	"instance-identifier": struct{}{},
}

type transformer struct {
	topScope           *scope
	decimal64          *pbast.Message
	leafRef            *pbast.Message
	identityRef        *pbast.Message
	instanceIdentifier *pbast.Message
	emptyNeeded        bool
}

// e must be YANG module
func Transform(e *yang.Entry) *pbast.File {
	if _, ok := e.Node.(*yang.Module); !ok {
		return nil
	}

	t := &transformer{
		topScope: newScope(),
	}

	return t.module(entry{e})
}

func (t *transformer) declare(m pbast.Type) {
	if err := t.topScope.addType(m); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

func (t *transformer) reflectTo(f *pbast.File) {
	if typ := t.topScope.types[pbast.Empty.TypeName()]; typ != nil {
		f.AddImport(pbast.NewImport("google/protobuf/empty.proto"))
	}
	t.topScope.reflectTo(f)
}

func (t *transformer) module(e entry) *pbast.File {
	namespace := e.Namespace().Name
	f := pbast.NewFile(pbast.NewPackageWithElements(guessElements(namespace)))

	f.Comment = t.moduleComment(e)

	root := t.buildMessage("Root", e)
	// Clear Root messgage comment because it overlaps with
	// the file level comment being generated from module description too
	root.Comment = nil
	// Child nodes are enclosed with Root message
	f.AddMessage(root)

	// RPCs
	s := t.rpcs(e)
	f.AddService(s)

	// Notifications
	n := t.notifications(e)
	f.AddService(n)

	t.reflectTo(f)

	return f
}

func (t *transformer) moduleComment(e entry) pbast.Comment {
	description := t.description(e)
	namespace := t.namespace(e)
	revisions := t.revisions(e)
	reference := t.reference(e)

	var comment []string
	comment = append(comment, description...)
	comment = append(comment, namespace...)
	comment = append(comment, revisions...)
	comment = append(comment, reference...)

	return comment
}

func (t *transformer) genericComments(e entry) pbast.Comment {
	description := t.description(e)
	reference := t.reference(e)

	comments := append(description, reference...)
	return comments
}

func (t *transformer) description(e entry) pbast.Comment {
	description := e.Description
	if e.Description == "" {
		return nil
	}

	lines := strings.Split(strings.TrimRight(description, "\n "), "\n")

	ret := make([]string, 0, len(lines)+1)
	ret = append(ret, "Description:")
	ret = append(ret, lines...)
	return ret
}

func (t *transformer) revisions(e entry) pbast.Comment {
	var lines []string
	if v := e.Extra["revision"]; len(v) > 0 {
		for _, rev := range v[0].([]*yang.Revision) {
			lines = append(lines, "Revision: "+rev.Name)
		}
	}

	return lines
}

func (t *transformer) namespace(e entry) pbast.Comment {
	namespace := e.Namespace().Name
	if namespace == "" {
		return nil
	}

	return []string{"Namespace: " + namespace}
}

func (t *transformer) reference(e entry) pbast.Comment {
	v := e.Extra["reference"]
	if len(v) == 0 {
		return nil
	}

	ref := v[0].(*yang.Value)
	if ref == nil {
		return nil
	}
	if ref.Name == "" {
		return nil
	}

	lines := strings.Split(strings.TrimRight(ref.Name, "\n "), "\n")

	ret := make([]string, 0, len(lines)+1)
	ret = append(ret, "Reference:")
	ret = append(ret, lines...)
	return ret
}

func (t *transformer) rpcs(e entry) *pbast.Service {
	rpcs := e.rpcs()
	if len(rpcs) == 0 {
		return nil
	}

	s := pbast.NewService(CamelCase(e.Name))
	for _, rpc := range rpcs {
		r := t.rpc(rpc)
		s.AddRPC(r)
	}

	return s
}

func (t *transformer) rpc(e entry) *pbast.RPC {
	method := CamelCase(e.Name)
	in := method + "Request"
	out := method + "Response"

	rpc := pbast.NewRPC(
		method,
		pbast.NewReturnType(in),
		pbast.NewReturnType(out),
	)
	rpc.Comment = t.genericComments(e)

	t.declare(t.buildMessage(in, entry{e.RPC.Input}))
	t.declare(t.buildMessage(out, entry{e.RPC.Output}))

	return rpc
}

func (t *transformer) notifications(e entry) *pbast.Service {
	notifications := e.notifications()
	if len(notifications) == 0 {
		return nil
	}

	s := pbast.NewService(CamelCase(e.Name + "Notification"))
	for _, notification := range notifications {
		n := t.notification(notification)
		n.Comment = t.genericComments(notification)
		s.AddRPC(n)
	}

	return s
}

func (t *transformer) notification(e entry) *pbast.RPC {
	const common = "Notification"
	method := CamelCase(e.Name)
	in := buildName(method, common, "Request")
	out := buildName(method, common, "Response")

	rpc := pbast.NewRPC(method, pbast.NewReturnType(in), pbast.NewReturnType(out))

	// notification statement doesn't have an input parameter equivalent,
	// then empty message is used for input as RPC
	t.declare(pbast.NewMessage(in))
	t.declare(t.buildMessage(out, e))

	return rpc
}

func (t *transformer) buildMessage(name string, e entry) *pbast.Message {
	if e.Entry == nil {
		return nil
	}

	msg := &pbast.Message{
		Name:    name,
		Comment: t.genericComments(e),
	}
	scope := newScope()
	for index, child := range e.children() {
		fieldNum := index + 1
		var field *pbast.MessageField
		if child.Type != nil {
			typ := t.leaf(child.Type, messageName(child))

			if needsInTopScope(typ) {
				t.declare(typ)
			} else {
				if err := scope.addType(typ); err != nil {
					fmt.Fprintln(os.Stderr, err)
				}
			}

			field = &pbast.MessageField{
				Repeated: child.ListAttr != nil,
				Type:     typ.TypeName(),
				Name:     underscoreCase(child.Name),
				Index:    fieldNum,
				Comment:  t.genericComments(child),
			}
		} else {
			field = t.directory(scope, child, fieldNum, child.ListAttr != nil)
		}
		msg.AddField(field)
	}

	scope.reflectTo(msg)

	return msg
}

func (t *transformer) leaf(typ *yang.YangType, name string) pbast.Type {
	if needsCustomType(typ.Kind) {
		return t.customType(typ, CamelCase(name))
	}

	if isBuiltinType(typ.Name) {
		return t.mapBuiltinType(typ.Kind)
	}

	inner := t.mapBuiltinType(typ.Kind)
	msg := pbast.NewMessage(CamelCase(name)).
		AddField(pbast.NewMessageField(inner, "value", 1))
	if needsInTopScope(inner) {
		t.declare(inner)
	} else {
		msg.AddType(inner)
	}
	return msg
}

func messageName(e entry) string {
	if isBuiltinType(e.Type.Name) {
		return e.Name
	}

	return e.Type.Name
}

func needsInTopScope(t pbast.Type) bool {
	switch t {
	case decimal64, leafRef, identityRef, instanceIdentifier, pbast.Empty:
		return true
	default:
		return false
	}
}

func (t *transformer) customType(typ *yang.YangType, name string) pbast.Type {
	switch typ.Kind {
	case yang.Yenum:
		return t.customEnum(name, typ.Enum)
	case yang.Ybits:
		return t.customBits(name, typ.Bit)
	case yang.Yunion:
		return t.customUnion(name, typ.Type)
	default:
		return nil
	}
}

func isBuiltinType(name string) bool {
	if _, ok := yang.TypeKindFromName[name]; !ok {
		return false
	}
	return true
}

func needsCustomType(kind yang.TypeKind) bool {
	switch kind {
	case yang.Yenum, yang.Ybits, yang.Yunion:
		return true
	default:
		return false
	}
}

func (t *transformer) mapBuiltinType(kind yang.TypeKind) pbast.Type {
	switch kind {
	case yang.Yint8, yang.Yint16, yang.Yint32:
		return pbast.Int32
	case yang.Yint64:
		return pbast.Int64
	case yang.Yuint8, yang.Yuint16, yang.Yuint32:
		return pbast.UInt32
	case yang.Yuint64:
		return pbast.UInt64
	case yang.Ystring:
		return pbast.String
	case yang.Ybool:
		return pbast.Bool
	case yang.Ybinary:
		return pbast.Bytes
	case yang.Yleafref:
		return leafRef
	case yang.Yidentityref:
		return identityRef
	case yang.Yempty:
		return pbast.Empty
	case yang.YinstanceIdentifier:
		return instanceIdentifier
	case yang.Ydecimal64:
		return decimal64
	default:
		return nil
	}
}

func (t *transformer) customBits(name string, bits *yang.EnumType) *pbast.Message {
	msg := pbast.NewMessage(name)
	for i, n := range bits.Names() {
		v := 1 << uint(bits.Values()[i])
		msg.AddField(pbast.NewMessageField(pbast.Bool, n, v))
	}

	return msg
}

func (t *transformer) customEnum(name string, e *yang.EnumType) *pbast.Message {
	enum := pbast.NewEnum("Value")
	for _, v := range e.Values() {
		n := e.ValueMap()[v]
		enum.AddField(pbast.NewEnumField(constantName(n), int(v)))
	}
	msg := pbast.NewMessage(name).
		AddEnum(enum).
		AddField(pbast.NewMessageField(enum, "value", 1))

	return msg
}

func (t *transformer) customUnion(name string, types []*yang.YangType) *pbast.Message {
	scope := newScope()
	pbTypes := t.unionFields(types, nil, scope)

	oneof := pbast.NewOneOf("value")
	for i, typ := range pbTypes {
		oneof.AddField(pbast.NewOneOfField(typ, underscoreCase(typ.TypeName()), i+1))
	}

	msg := pbast.NewMessage(name).AddOneOf(oneof)
	scope.reflectTo(msg)

	return msg
}

func (t *transformer) unionFields(types []*yang.YangType, pbTypes []pbast.Type, scope *scope) []pbast.Type {
	for _, typ := range types {
		if typ.Kind == yang.Yunion {
			pbTypes = t.unionFields(typ.Type, pbTypes, scope)
			continue
		}

		pbtype := t.leaf(typ, typ.Name)
		if pbtype == nil {
			continue
		}

		pbTypes = append(pbTypes, pbtype)
		scope.addType(pbtype)
	}

	return pbTypes
}

func (t *transformer) directory(scope *scope, e entry, index int, repeated bool) *pbast.MessageField {
	fieldName := underscoreCase(e.Name)
	typeName := CamelCase(e.Name)

	inner := t.buildMessage(typeName, e)
	field := &pbast.MessageField{
		Repeated: repeated,
		Type:     inner.TypeName(),
		Name:     fieldName,
		Index:    index,
		Comment:  t.genericComments(e),
	}

	if err := scope.addType(inner); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}

	return field
}
<commit_msg>Use well-know Empty type for messages for service and notification<commit_after>package yang

import (
	"fmt"
	"os"
	"strings"

	"github.com/openconfig/goyang/pkg/yang"
	"github.com/oshothebig/pbast"
)

var builtinMap = map[yang.TypeKind]pbast.Type{
	yang.Yint8:   pbast.Int32,
	yang.Yint16:  pbast.Int32,
	yang.Yint32:  pbast.Int32,
	yang.Yint64:  pbast.Int64,
	yang.Yuint8:  pbast.UInt32,
	yang.Yuint16: pbast.UInt32,
	yang.Yuint32: pbast.UInt32,
	yang.Yuint64: pbast.UInt64,
	yang.Ystring: pbast.String,
	yang.Ybool:   pbast.Bool,
	yang.Ybinary: pbast.Bytes,
}

var builtinTypes = stringSet{
	"int8":                struct{}{},
	"int16":               struct{}{},
	"int32":               struct{}{},
	"int64":               struct{}{},
	"uint8":               struct{}{},
	"uint16":              struct{}{},
	"unit32":              struct{}{},
	"uint64":              struct{}{},
	"string":              struct{}{},
	"boolean":             struct{}{},
	"enumeration":         struct{}{},
	"bits":                struct{}{},
	"binary":              struct{}{},
	"leafref":             struct{}{},
	"identityref":         struct{}{},
	"empty":               struct{}{},
	"union":               struct{}{},
	"instance-identifier": struct{}{},
}

type transformer struct {
	topScope           *scope
	decimal64          *pbast.Message
	leafRef            *pbast.Message
	identityRef        *pbast.Message
	instanceIdentifier *pbast.Message
	emptyNeeded        bool
}

// e must be YANG module
func Transform(e *yang.Entry) *pbast.File {
	if _, ok := e.Node.(*yang.Module); !ok {
		return nil
	}

	t := &transformer{
		topScope: newScope(),
	}

	return t.module(entry{e})
}

func (t *transformer) declare(m pbast.Type) {
	if err := t.topScope.addType(m); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
}

func (t *transformer) reflectTo(f *pbast.File) {
	if typ := t.topScope.types[pbast.Empty.TypeName()]; typ != nil {
		f.AddImport(pbast.NewImport("google/protobuf/empty.proto"))
	}
	t.topScope.reflectTo(f)
}

func (t *transformer) module(e entry) *pbast.File {
	namespace := e.Namespace().Name
	f := pbast.NewFile(pbast.NewPackageWithElements(guessElements(namespace)))

	f.Comment = t.moduleComment(e)

	root := t.buildMessage("Root", e)
	// Clear Root messgage comment because it overlaps with
	// the file level comment being generated from module description too
	root.Comment = nil
	// Child nodes are enclosed with Root message
	f.AddMessage(root)

	// RPCs
	s := t.rpcs(e)
	f.AddService(s)

	// Notifications
	n := t.notifications(e)
	f.AddService(n)

	t.reflectTo(f)

	return f
}

func (t *transformer) moduleComment(e entry) pbast.Comment {
	description := t.description(e)
	namespace := t.namespace(e)
	revisions := t.revisions(e)
	reference := t.reference(e)

	var comment []string
	comment = append(comment, description...)
	comment = append(comment, namespace...)
	comment = append(comment, revisions...)
	comment = append(comment, reference...)

	return comment
}

func (t *transformer) genericComments(e entry) pbast.Comment {
	description := t.description(e)
	reference := t.reference(e)

	comments := append(description, reference...)
	return comments
}

func (t *transformer) description(e entry) pbast.Comment {
	description := e.Description
	if e.Description == "" {
		return nil
	}

	lines := strings.Split(strings.TrimRight(description, "\n "), "\n")

	ret := make([]string, 0, len(lines)+1)
	ret = append(ret, "Description:")
	ret = append(ret, lines...)
	return ret
}

func (t *transformer) revisions(e entry) pbast.Comment {
	var lines []string
	if v := e.Extra["revision"]; len(v) > 0 {
		for _, rev := range v[0].([]*yang.Revision) {
			lines = append(lines, "Revision: "+rev.Name)
		}
	}

	return lines
}

func (t *transformer) namespace(e entry) pbast.Comment {
	namespace := e.Namespace().Name
	if namespace == "" {
		return nil
	}

	return []string{"Namespace: " + namespace}
}

func (t *transformer) reference(e entry) pbast.Comment {
	v := e.Extra["reference"]
	if len(v) == 0 {
		return nil
	}

	ref := v[0].(*yang.Value)
	if ref == nil {
		return nil
	}
	if ref.Name == "" {
		return nil
	}

	lines := strings.Split(strings.TrimRight(ref.Name, "\n "), "\n")

	ret := make([]string, 0, len(lines)+1)
	ret = append(ret, "Reference:")
	ret = append(ret, lines...)
	return ret
}

func (t *transformer) rpcs(e entry) *pbast.Service {
	rpcs := e.rpcs()
	if len(rpcs) == 0 {
		return nil
	}

	s := pbast.NewService(CamelCase(e.Name))
	for _, rpc := range rpcs {
		r := t.rpc(rpc)
		s.AddRPC(r)
	}

	return s
}

func (t *transformer) rpc(e entry) *pbast.RPC {
	method := CamelCase(e.Name)
	var in pbast.Type
	if e.RPC.Input == nil {
		in = pbast.Empty
	} else {
		in = t.buildMessage(method+"Request", entry{e.RPC.Input})
	}
	t.declare(in)

	var out pbast.Type
	if e.RPC.Output == nil {
		out = pbast.Empty
	} else {
		out = t.buildMessage(method+"Response", entry{e.RPC.Output})
	}
	t.declare(out)

	rpc := pbast.NewRPC(
		method,
		pbast.NewReturnType(in.TypeName()),
		pbast.NewReturnType(out.TypeName()),
	)
	rpc.Comment = t.genericComments(e)

	return rpc
}

func (t *transformer) notifications(e entry) *pbast.Service {
	notifications := e.notifications()
	if len(notifications) == 0 {
		return nil
	}

	s := pbast.NewService(CamelCase(e.Name + "Notification"))
	for _, notification := range notifications {
		n := t.notification(notification)
		n.Comment = t.genericComments(notification)
		s.AddRPC(n)
	}

	return s
}

func (t *transformer) notification(e entry) *pbast.RPC {
	const common = "Notification"
	method := CamelCase(e.Name)
	in := pbast.Empty
	t.declare(in)
	out := t.buildMessage(buildName(method, common, "Response"), e)
	t.declare(out)

	return pbast.NewRPC(method, pbast.NewReturnType(in.TypeName()), pbast.NewReturnType(out.TypeName()))
}

func (t *transformer) buildMessage(name string, e entry) *pbast.Message {
	if e.Entry == nil {
		return nil
	}

	msg := &pbast.Message{
		Name:    name,
		Comment: t.genericComments(e),
	}
	scope := newScope()
	for index, child := range e.children() {
		fieldNum := index + 1
		var field *pbast.MessageField
		if child.Type != nil {
			typ := t.leaf(child.Type, messageName(child))

			if needsInTopScope(typ) {
				t.declare(typ)
			} else {
				if err := scope.addType(typ); err != nil {
					fmt.Fprintln(os.Stderr, err)
				}
			}

			field = &pbast.MessageField{
				Repeated: child.ListAttr != nil,
				Type:     typ.TypeName(),
				Name:     underscoreCase(child.Name),
				Index:    fieldNum,
				Comment:  t.genericComments(child),
			}
		} else {
			field = t.directory(scope, child, fieldNum, child.ListAttr != nil)
		}
		msg.AddField(field)
	}

	scope.reflectTo(msg)

	return msg
}

func (t *transformer) leaf(typ *yang.YangType, name string) pbast.Type {
	if needsCustomType(typ.Kind) {
		return t.customType(typ, CamelCase(name))
	}

	if isBuiltinType(typ.Name) {
		return t.mapBuiltinType(typ.Kind)
	}

	inner := t.mapBuiltinType(typ.Kind)
	msg := pbast.NewMessage(CamelCase(name)).
		AddField(pbast.NewMessageField(inner, "value", 1))
	if needsInTopScope(inner) {
		t.declare(inner)
	} else {
		msg.AddType(inner)
	}
	return msg
}

func messageName(e entry) string {
	if isBuiltinType(e.Type.Name) {
		return e.Name
	}

	return e.Type.Name
}

func needsInTopScope(t pbast.Type) bool {
	switch t {
	case decimal64, leafRef, identityRef, instanceIdentifier, pbast.Empty:
		return true
	default:
		return false
	}
}

func (t *transformer) customType(typ *yang.YangType, name string) pbast.Type {
	switch typ.Kind {
	case yang.Yenum:
		return t.customEnum(name, typ.Enum)
	case yang.Ybits:
		return t.customBits(name, typ.Bit)
	case yang.Yunion:
		return t.customUnion(name, typ.Type)
	default:
		return nil
	}
}

func isBuiltinType(name string) bool {
	if _, ok := yang.TypeKindFromName[name]; !ok {
		return false
	}
	return true
}

func needsCustomType(kind yang.TypeKind) bool {
	switch kind {
	case yang.Yenum, yang.Ybits, yang.Yunion:
		return true
	default:
		return false
	}
}

func (t *transformer) mapBuiltinType(kind yang.TypeKind) pbast.Type {
	switch kind {
	case yang.Yint8, yang.Yint16, yang.Yint32:
		return pbast.Int32
	case yang.Yint64:
		return pbast.Int64
	case yang.Yuint8, yang.Yuint16, yang.Yuint32:
		return pbast.UInt32
	case yang.Yuint64:
		return pbast.UInt64
	case yang.Ystring:
		return pbast.String
	case yang.Ybool:
		return pbast.Bool
	case yang.Ybinary:
		return pbast.Bytes
	case yang.Yleafref:
		return leafRef
	case yang.Yidentityref:
		return identityRef
	case yang.Yempty:
		return pbast.Empty
	case yang.YinstanceIdentifier:
		return instanceIdentifier
	case yang.Ydecimal64:
		return decimal64
	default:
		return nil
	}
}

func (t *transformer) customBits(name string, bits *yang.EnumType) *pbast.Message {
	msg := pbast.NewMessage(name)
	for i, n := range bits.Names() {
		v := 1 << uint(bits.Values()[i])
		msg.AddField(pbast.NewMessageField(pbast.Bool, n, v))
	}

	return msg
}

func (t *transformer) customEnum(name string, e *yang.EnumType) *pbast.Message {
	enum := pbast.NewEnum("Value")
	for _, v := range e.Values() {
		n := e.ValueMap()[v]
		enum.AddField(pbast.NewEnumField(constantName(n), int(v)))
	}
	msg := pbast.NewMessage(name).
		AddEnum(enum).
		AddField(pbast.NewMessageField(enum, "value", 1))

	return msg
}

func (t *transformer) customUnion(name string, types []*yang.YangType) *pbast.Message {
	scope := newScope()
	pbTypes := t.unionFields(types, nil, scope)

	oneof := pbast.NewOneOf("value")
	for i, typ := range pbTypes {
		oneof.AddField(pbast.NewOneOfField(typ, underscoreCase(typ.TypeName()), i+1))
	}

	msg := pbast.NewMessage(name).AddOneOf(oneof)
	scope.reflectTo(msg)

	return msg
}

func (t *transformer) unionFields(types []*yang.YangType, pbTypes []pbast.Type, scope *scope) []pbast.Type {
	for _, typ := range types {
		if typ.Kind == yang.Yunion {
			pbTypes = t.unionFields(typ.Type, pbTypes, scope)
			continue
		}

		pbtype := t.leaf(typ, typ.Name)
		if pbtype == nil {
			continue
		}

		pbTypes = append(pbTypes, pbtype)
		scope.addType(pbtype)
	}

	return pbTypes
}

func (t *transformer) directory(scope *scope, e entry, index int, repeated bool) *pbast.MessageField {
	fieldName := underscoreCase(e.Name)
	typeName := CamelCase(e.Name)

	inner := t.buildMessage(typeName, e)
	field := &pbast.MessageField{
		Repeated: repeated,
		Type:     inner.TypeName(),
		Name:     fieldName,
		Index:    index,
		Comment:  t.genericComments(e),
	}

	if err := scope.addType(inner); err != nil {
		fmt.Fprintln(os.Stderr, err)
	}

	return field
}
<|endoftext|>


<commit_before>package dberr

import "errors"

var (
	ErrIDExists    = errors.New("hare: record with that id already exists")
	ErrNoRecord    = errors.New("hare: no record with that id found")
	ErrNoTable     = errors.New("hare: table with that name does not exist")
	ErrTableExists = errors.New("hare: table with that name already exists")
)
<commit_msg>add doc comments to error variables<commit_after>package dberr

import "errors"

var (
	// ErrIDExists error means a record with the specified id already exists in the table.
	ErrIDExists = errors.New("hare: record with that id already exists")

	// ErrNoRecord error means no record with the specified id was not found.
	ErrNoRecord = errors.New("hare: no record with that id found")

	// ErrNoTable error means a table that the specified name does not exist.
	ErrNoTable = errors.New("hare: table with that name does not exist")

	// ErrTableExists error means a table with the specified name already exists in the database.
	ErrTableExists = errors.New("hare: table with that name already exists")
)
<|endoftext|>


<commit_before>package handler

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strconv"

	"log"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/internal/apierr"
)

//copy of aws.SendHandler modified to use different http clients for timeouts
//on polling and heartbeating.
//to use, when constructing an swf.SWM
// swfClient.Service.Handlers.Send.Clear()
// swfClient.Service.Handlers.Send.PushBack(handler.SWFSendHandler(polling, heartbeat))
func SWFSendHandler(polling, heartbeat *http.Client) func(*aws.Request) {
	var reStatusCode = regexp.MustCompile(`^(\d+)`)

	return func(r *aws.Request) {
		client := r.Service.Config.HTTPClient
		if r.Service.ServiceName == "swf" {
			switch r.Operation.Name {
			case "PollForDecisionTask", "PollForActivityTask":
				log.Printf("using polling client %s %s", r.Service.ServiceName, r.Operation.Name)
				client = polling
			case "RecordActivityTaskHeartbeat":
				log.Printf("using heartbeat client %s %s", r.Service.ServiceName, r.Operation.Name)
				client = heartbeat
			default:
				log.Printf("using std client %s %s", r.Service.ServiceName, r.Operation.Name)
			}

		}
		var err error
		r.HTTPResponse, err = client.Do(r.HTTPRequest)
		if err != nil {
			// Capture the case where url.Error is returned for error processing
			// response. e.g. 301 without location header comes back as string
			// error and r.HTTPResponse is nil. Other url redirect errors will
			// comeback in a similar method.
			if e, ok := err.(*url.Error); ok {
				if s := reStatusCode.FindStringSubmatch(e.Error()); s != nil {
					code, _ := strconv.ParseInt(s[1], 10, 64)
					r.HTTPResponse = &http.Response{
						StatusCode: int(code),
						Status:     http.StatusText(int(code)),
						Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
					}
					return
				}
			}
			// Catch all other request errors.
			r.Error = apierr.New("RequestError", "send request failed", err)
			r.Retryable.Set(true) // network errors are retryable
		}
	}
}
<commit_msg>only log client usage if the logLevel > 0<commit_after>package handler

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strconv"

	"log"

	"github.com/awslabs/aws-sdk-go/aws"
	"github.com/awslabs/aws-sdk-go/internal/apierr"
)

//copy of aws.SendHandler modified to use different http clients for timeouts
//on polling and heartbeating.
//to use, when constructing an swf.SWM
// swfClient.Service.Handlers.Send.Clear()
// swfClient.Service.Handlers.Send.PushBack(handler.SWFSendHandler(polling, heartbeat))
func SWFSendHandler(polling, heartbeat *http.Client) func(*aws.Request) {
	var reStatusCode = regexp.MustCompile(`^(\d+)`)

	return func(r *aws.Request) {
		client := r.Service.Config.HTTPClient
		if r.Service.ServiceName == "swf" {
			switch r.Operation.Name {
			case "PollForDecisionTask", "PollForActivityTask":
				if r.Service.Config.LogLevel > 0 {
					log.Printf("using polling client %s %s", r.Service.ServiceName, r.Operation.Name)
				}
				client = polling
			case "RecordActivityTaskHeartbeat":
				if r.Service.Config.LogLevel > 0 {
					log.Printf("using heartbeat client %s %s", r.Service.ServiceName, r.Operation.Name)
				}
				client = heartbeat
			default:
				if r.Service.Config.LogLevel > 0 {
					log.Printf("using std client %s %s", r.Service.ServiceName, r.Operation.Name)
				}
			}

		}
		var err error
		r.HTTPResponse, err = client.Do(r.HTTPRequest)
		if err != nil {
			// Capture the case where url.Error is returned for error processing
			// response. e.g. 301 without location header comes back as string
			// error and r.HTTPResponse is nil. Other url redirect errors will
			// comeback in a similar method.
			if e, ok := err.(*url.Error); ok {
				if s := reStatusCode.FindStringSubmatch(e.Error()); s != nil {
					code, _ := strconv.ParseInt(s[1], 10, 64)
					r.HTTPResponse = &http.Response{
						StatusCode: int(code),
						Status:     http.StatusText(int(code)),
						Body:       ioutil.NopCloser(bytes.NewReader([]byte{})),
					}
					return
				}
			}
			// Catch all other request errors.
			r.Error = apierr.New("RequestError", "send request failed", err)
			r.Retryable.Set(true) // network errors are retryable
		}
	}
}
<|endoftext|>


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

package rog

import (
	"fmt"
	"github.com/ajhager/eng"
	"strings"
)

var bgRegion *eng.Region

type Console struct {
	bg, fg [][]*eng.Color
	ch     [][]rune
	w, h   int
}

func NewConsole(width, height int) *Console {
	bg := make([][]*eng.Color, height)
	fg := make([][]*eng.Color, height)
	ch := make([][]rune, height)

	for y := 0; y < height; y++ {
		bg[y] = make([]*eng.Color, width)
		fg[y] = make([]*eng.Color, width)
		ch[y] = make([]rune, width)
	}

	if bgRegion == nil {
		bgRegion = eng.NewRegion(eng.BlankTexture(), 0, 0, 1, 1)
	}

	con := &Console{bg, fg, ch, width, height}

	for x := 0; x < con.w; x++ {
		for y := 0; y < con.h; y++ {
			con.bg[y][x] = eng.NewColor(0, 0, 0)
			con.fg[y][x] = eng.NewColor(1, 1, 1)
			con.ch[y][x] = ' '
		}
	}

	return con
}

func (con *Console) put(x, y, i, t int, fg, bg eng.Blender, ch rune) {
	if x < 0 || x >= con.w || y < 0 || y >= con.h {
		return
	}

	if ch > 0 {
		con.ch[y][x] = ch
	}

	if bg != nil {
		con.bg[y][x] = bg.Blend(con.bg[y][x], i, t)
	}

	if fg != nil {
		con.fg[y][x] = fg.Blend(con.bg[y][x], i, t)
	}
}

func (con *Console) set(i, j, x, y, w, h int, fg, bg eng.Blender, data string, rest ...interface{}) {
	if len(rest) > 0 {
		data = fmt.Sprintf(data, rest...)
	}
	t := strings.Count(data, "") - 1
	if t > 0 {
		if h == 0 {
			h = con.h - y
		}
		k := 0
		for _, r := range data {
			if i == x+w {
				j += 1
				i = x
			}
			if j == y+h {
				break
			}
			con.put(i, j, k, t, fg, bg, r)
			i += 1
			k += 1
		}
	} else {
		con.put(i, j, 0, 0, fg, bg, -1)
	}
}

// Blit draws con onto this console with top left starting at x, y.
func (con *Console) Blit(o *Console, x, y int) {
	for i := 0; i < o.Width(); i++ {
		for j := 0; j < o.Height(); j++ {
			fg, bg, ch := o.Get(i, j)
			con.Set(x+i, y+j, fg, bg, string(ch))
		}
	}
}

// Clear is a short hand to fill the entire screen with the given colors and rune.
func (con *Console) Clear(fg, bg eng.Blender, ch rune) {
	con.Fill(0, 0, con.w, con.h, fg, bg, ch)
}

// Fill draws a rect on the root console using ch.
func (con *Console) Fill(x, y, w, h int, fg, bg eng.Blender, ch rune) {
	for i := x; i < x+w; i++ {
		for j := y; j < y+h; j++ {
			con.Set(i, j, fg, bg, string(ch))
		}
	}
}

// Get returns the fg, bg colors and rune of the cell.
func (con *Console) Get(x, y int) (*eng.Color, *eng.Color, rune) {
	return con.fg[y][x], con.bg[y][x], con.ch[y][x]
}

// Height returns the height of the console in cells.
func (con *Console) Height() int {
	return con.h
}

// Set draws a string starting at x,y onto the console, wrapping at the bounds if needed.
func (con *Console) Set(x, y int, fg, bg eng.Blender, data string, rest ...interface{}) {
	con.set(x, y, 0, 0, con.w, con.h, fg, bg, data, rest...)
}

// SetRect draws a string starting at x,y onto the console, wrapping at the bounds created by x, y, w, h if needed.
// If h is 0, the text will cut off at the bottom of the console, otherwise it will cut off after the y+h row.
func (con *Console) SetRect(x, y, w, h int, fg, bg eng.Blender, data string, rest ...interface{}) {
	con.set(x, y, x, y, w, h, fg, bg, data, rest...)
}

// Render takes a batch and font data, and renders the console using them.
func (con *Console) Render(batch *eng.Batch, offsetX, offsetY int, font *eng.Font, cellWidth, cellHeight int) {
	w := cellWidth
	h := cellHeight
	for x := 0; x < con.Width(); x++ {
		for y := 0; y < con.Height(); y++ {
			_, bg, _ := con.Get(x, y)
			batch.Draw(bgRegion, float32(offsetX+x*w), float32(offsetY+y*h), 0, 0, float32(w), float32(h), 0, bg)
		}
	}
	for x := 0; x < con.Width(); x++ {
		for y := 0; y < con.Height(); y++ {
			fg, _, ch := con.Get(x, y)
			font.Put(batch, ch, float32(offsetX+x*w), float32(offsetY+y*h), fg)
		}
	}
}

// Width returns the width of the console in cells.
func (con *Console) Width() int {
	return con.w
}
<commit_msg>Console now holds the font data<commit_after>// Copyright 2013 Joseph Hager. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rog

import (
	"fmt"
	"github.com/ajhager/eng"
	"strings"
)

var bgRegion *eng.Region

type Console struct {
	bg, fg     [][]*eng.Color
	ch         [][]rune
	w, h       int
	font       *eng.Font
	cellWidth  float32
	cellHeight float32
}

func NewConsole(width, height int, font *eng.Font, cellWidth, cellHeight float32) *Console {
	bg := make([][]*eng.Color, height)
	fg := make([][]*eng.Color, height)
	ch := make([][]rune, height)

	for y := 0; y < height; y++ {
		bg[y] = make([]*eng.Color, width)
		fg[y] = make([]*eng.Color, width)
		ch[y] = make([]rune, width)
	}

	if bgRegion == nil {
		bgRegion = eng.NewRegion(eng.BlankTexture(), 0, 0, 1, 1)
	}

	con := &Console{bg, fg, ch, width, height, font, cellWidth, cellHeight}

	for x := 0; x < con.w; x++ {
		for y := 0; y < con.h; y++ {
			con.bg[y][x] = eng.NewColor(0, 0, 0)
			con.fg[y][x] = eng.NewColor(1, 1, 1)
			con.ch[y][x] = ' '
		}
	}

	return con
}

func (con *Console) put(x, y, i, t int, fg, bg eng.Blender, ch rune) {
	if x < 0 || x >= con.w || y < 0 || y >= con.h {
		return
	}

	if ch > 0 {
		con.ch[y][x] = ch
	}

	if bg != nil {
		con.bg[y][x] = bg.Blend(con.bg[y][x], i, t)
	}

	if fg != nil {
		con.fg[y][x] = fg.Blend(con.bg[y][x], i, t)
	}
}

func (con *Console) set(i, j, x, y, w, h int, fg, bg eng.Blender, data string, rest ...interface{}) {
	if len(rest) > 0 {
		data = fmt.Sprintf(data, rest...)
	}
	t := strings.Count(data, "") - 1
	if t > 0 {
		if h == 0 {
			h = con.h - y
		}
		k := 0
		for _, r := range data {
			if i == x+w {
				j += 1
				i = x
			}
			if j == y+h {
				break
			}
			con.put(i, j, k, t, fg, bg, r)
			i += 1
			k += 1
		}
	} else {
		con.put(i, j, 0, 0, fg, bg, -1)
	}
}

// Blit draws con onto this console with top left starting at x, y.
func (con *Console) Blit(o *Console, x, y int) {
	for i := 0; i < o.Width(); i++ {
		for j := 0; j < o.Height(); j++ {
			fg, bg, ch := o.Get(i, j)
			con.Set(x+i, y+j, fg, bg, string(ch))
		}
	}
}

// Clear is a short hand to fill the entire screen with the given colors and rune.
func (con *Console) Clear(fg, bg eng.Blender, ch rune) {
	con.Fill(0, 0, con.w, con.h, fg, bg, ch)
}

// Fill draws a rect on the root console using ch.
func (con *Console) Fill(x, y, w, h int, fg, bg eng.Blender, ch rune) {
	for i := x; i < x+w; i++ {
		for j := y; j < y+h; j++ {
			con.Set(i, j, fg, bg, string(ch))
		}
	}
}

// Get returns the fg, bg colors and rune of the cell.
func (con *Console) Get(x, y int) (*eng.Color, *eng.Color, rune) {
	return con.fg[y][x], con.bg[y][x], con.ch[y][x]
}

// Height returns the height of the console in cells.
func (con *Console) Height() int {
	return con.h
}

// Set draws a string starting at x,y onto the console, wrapping at the bounds if needed.
func (con *Console) Set(x, y int, fg, bg eng.Blender, data string, rest ...interface{}) {
	con.set(x, y, 0, 0, con.w, con.h, fg, bg, data, rest...)
}

// SetFont sets the font data used to render the console.
func (con *Console) SetFont(font *eng.Font, cellWidth, cellHeight float32) {
	con.font = font
	con.cellWidth = cellWidth
	con.cellHeight = cellHeight
}

// SetRect draws a string starting at x,y onto the console, wrapping at the bounds created by x, y, w, h if needed.
// If h is 0, the text will cut off at the bottom of the console, otherwise it will cut off after the y+h row.
func (con *Console) SetRect(x, y, w, h int, fg, bg eng.Blender, data string, rest ...interface{}) {
	con.set(x, y, x, y, w, h, fg, bg, data, rest...)
}

// Render takes a batch and font data, and renders the console using them.
func (con *Console) Render(batch *eng.Batch, offsetX, offsetY float32) {
	w := con.cellWidth
	h := con.cellHeight
	for x := 0; x < con.Width(); x++ {
		for y := 0; y < con.Height(); y++ {
			_, bg, _ := con.Get(x, y)
			batch.Draw(bgRegion, offsetX+float32(x)*w, offsetY+float32(y)*h, 0, 0, w, h, 0, bg)
		}
	}
	for x := 0; x < con.Width(); x++ {
		for y := 0; y < con.Height(); y++ {
			fg, _, ch := con.Get(x, y)
			con.font.Put(batch, ch, offsetX+float32(x)*w, offsetY+float32(y)*h, fg)
		}
	}
}

// HitCell returns which cell is hit when the console is rendered at the offset.
func (con *Console) HitCell(x, y, offsetX, offsetY float32) (int, int) {
	return int((x - offsetX) / con.cellWidth), int((y - offsetY) / con.cellHeight)
}

// Width returns the width of the console in cells.
func (con *Console) Width() int {
	return con.w
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	gitRemoteSSH   = "git@github.com:parkr/merge-pr.git"
	gitRemoteHTTPS = "https://github.com/parkr/merge-pr.git"
	gitRemoteGit   = "git://github.com/parkr/merge-pr.git"
)

func TestCurrentBranch(t *testing.T) {
	branch := currentBranch()
	assert.NotEmpty(t, branch)
}

func TestIsAcceptableCurrentBranch(t *testing.T) {
	branch := currentBranch()
	err := isAcceptableCurrentBranch()
	switch branch {
	case "master":
		fallthrough
	case "staging":
		fallthrough
	case "dev":
		assert.NoError(t, err)
	default:
		assert.EqualError(t, err, fmt.Sprintf("Unacceptable local branch: %s", branch))
	}
}

func TestOriginRemote(t *testing.T) {
	assert.Regexp(t, GitRemoteRegexp, gitRemoteGit)
	assert.Regexp(t, GitRemoteRegexp, gitRemoteSSH)

	origin := gitOriginRemote()
	assert.Regexp(t, GitRemoteRegexp, origin)
}

func TestExtractOwnerAndRepoWithSSHUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteSSH)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithGitUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteGit)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithHTTPSUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteHTTPS)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithBadURL(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote("git@github.com:L!!!!RS/mars.git")
	assert.Equal(t, "", owner)
	assert.Equal(t, "", repo)
}

func TestFetchRepoOwnerAndName(t *testing.T) {
	owner, repo := fetchRepoOwnerAndName()
	assert.Contains(t, owner, "parkr")
	assert.Contains(t, repo, "merge-pr")
}
<commit_msg>Acceptable<commit_after>package main

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

var (
	gitRemoteSSH   = "git@github.com:parkr/merge-pr.git"
	gitRemoteHTTPS = "https://github.com/parkr/merge-pr.git"
	gitRemoteGit   = "git://github.com/parkr/merge-pr.git"
)

func TestCurrentBranch(t *testing.T) {
	branch := currentBranch()
	assert.NotEmpty(t, branch)
}

func TestIsAcceptableCurrentBranch(t *testing.T) {
	branch := currentBranch()
	err := isAcceptableCurrentBranch()
	switch branch {
	case "main", "master", "staging", "dev":
		assert.NoError(t, err)
	default:
		assert.EqualError(t, err, fmt.Sprintf("Unacceptable local branch: %s", branch))
	}
}

func TestOriginRemote(t *testing.T) {
	assert.Regexp(t, GitRemoteRegexp, gitRemoteGit)
	assert.Regexp(t, GitRemoteRegexp, gitRemoteSSH)

	origin := gitOriginRemote()
	assert.Regexp(t, GitRemoteRegexp, origin)
}

func TestExtractOwnerAndRepoWithSSHUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteSSH)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithGitUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteGit)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithHTTPSUrl(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote(gitRemoteHTTPS)
	assert.Equal(t, "parkr", owner)
	assert.Equal(t, "merge-pr", repo)
}

func TestExtractOwnerAndRepoWithBadURL(t *testing.T) {
	owner, repo := extractOwnerAndNameFromRemote("git@github.com:L!!!!RS/mars.git")
	assert.Equal(t, "", owner)
	assert.Equal(t, "", repo)
}

func TestFetchRepoOwnerAndName(t *testing.T) {
	owner, repo := fetchRepoOwnerAndName()
	assert.Contains(t, owner, "parkr")
	assert.Contains(t, repo, "merge-pr")
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/kardianos/service"
	"errors"
	"github.com/lukamicoder/ini-parser"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"regexp"
	"strings"
	"time"
	"log"
	"os"
)

var (
	interval = 3600
	services []IDdns
	logger service.Logger

	regex = regexp.MustCompile("(?m)[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}")
)

var urls = []string{
	"myipinfo.net",
	"myip.dnsomatic.com",
	"icanhazip.com",
	"checkip.dyndns.org",
	"www.myipnumber.com",
	"checkmyip.com",
	"myexternalip.com",
	"www.ipchicken.com",
	"ipecho.net/plain",
	"bot.whatismyipaddress.com",
	"ipv4.ipogre.com",
	"smart-ip.net/myip",
	"checkip.amazonaws.com",
	"www.checkip.org",
}

//Ddns represents a dynamic DNS service
type Ddns struct {
	Name     string
	Domain   string
	Account  string
	Password string
	Token    string
}

//IDdns represents an interface for a dynamic DNS service
type IDdns interface {
	updateIP() error
	getDomain() string
}

type program struct {
	exit chan struct{}
}

func (p *program) Start(s service.Service) error {
	p.exit = make(chan struct{})
	go p.run()
	return nil
}

func (p *program) run() error {
	update()
	ticker := time.NewTicker(time.Duration(interval) * time.Second)

	for {
		select {
		case <-ticker.C:
			update()
		case <-p.exit:
			ticker.Stop()
			return nil
		}
	}
}

func (p *program) Stop(s service.Service) error {
	logger.Info("Stopping service...")
	close(p.exit)
	return nil
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	svcConfig := &service.Config{
		Name:       "ddns-client",
		DisplayName: "DDNS Client",
		Description: "Dynamic DNS Client.",
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		log.Fatal(err)
	}

	logger, err = s.Logger(nil)
	if err != nil {
		log.Fatal(err)
	}

	err = loadConfig()
	if err != nil {
		log.Fatal(err)
	}

	if len(os.Args) > 1 {
		err := service.Control(s, os.Args[1])
		if err != nil {
			log.Printf("Valid actions: %q\n", service.ControlAction)
			log.Fatal(err)
		}

		return
	}

	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}

func loadConfig() error {
	var config iniparser.Config

	err := config.LoadFile("./config.ini")
	if err != nil {
		return err
	}

	sections := config.GetSections()
	if len(sections) < 2 {
		return errors.New("No services found in config file.\n")
	}

	for _, section := range sections {
		var err error
		var name = section.Name

		if name == "settings" {
			interval, err = config.GetInt(name, "interval")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
			}
			continue
		}

		t, err := config.GetString(name, "type")
		if err != nil {
			logger.Error(err)
			continue
		}
		switch strings.ToLower(t) {
		case "namecheap":
			service := new(nameCheapService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {

				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "noip":
		case "no-ip":
			service := new(noIPService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Account, err = config.GetString(name, "account")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "changeip":
			service := new(changeIPService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Account, err = config.GetString(name, "account")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "duckdns":
			service := new(duckDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "freedns":
			service := new(freeDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "system-ns":
		case "systemns":
			service := new(systemNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "ipdns":
			service := new(ipDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		}
	}

	if len(services) < 1 {
		return errors.New("No valid services found in config file.")
	}

	return nil
}

func update() {
	currentIP := getExternalIP()
	if currentIP == nil {
		return
	}

	for _, service := range services {
		addr, err := net.LookupHost(service.getDomain())
		if err != nil {
				logger.Errorf("%s - %s", service.getDomain(), err)
			continue
		}
		if len(addr) == 0 || addr[0] == "" {
			logger.Errorf("%s - Unable to get IP address", service.getDomain())
			continue
		}

		registeredIP := addr[0]

		if currentIP.String() == registeredIP {
			logger.Infof("%s - No update is necessary", service.getDomain())
		} else {
			err := service.updateIP()
			if err == nil {
				logger.Infof("%s - Successfully updated from %s to %s", service.getDomain(), registeredIP, currentIP.String())
			} else {
				logger.Errorf("%s - %s", service.getDomain(), err)
			}
		}
	}
}

func getExternalIP() net.IP {
	var currentIP net.IP
	for _, i := range rand.Perm(len(urls)) {
		url := "http://" + urls[i]

		content, err := GetResponse(url, "", "")
		if err != nil {
			logger.Errorf("%s - %s", url, err)
			continue
		}

		ip := regex.FindString(content)

		currentIP = net.ParseIP(ip)

		if currentIP != nil {
			return currentIP
		}
	}

	return currentIP
}

//GetResponse returns the content at the url address
func GetResponse(url string, login string, password string) (string, error) {
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}

	if login != "" && password != "" {
		request.SetBasicAuth(login, password)
	}

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return "", err
	}

	defer response.Body.Close()
	content, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}

	return string(content), nil
}
<commit_msg>Changed ddns interface name to DdnsService<commit_after>package main

import (
	"github.com/kardianos/service"
	"errors"
	"github.com/lukamicoder/ini-parser"
	"io/ioutil"
	"math/rand"
	"net"
	"net/http"
	"regexp"
	"strings"
	"time"
	"log"
	"os"
)

var (
	interval = 3600
	services []DdnsService
	logger service.Logger

	regex = regexp.MustCompile("(?m)[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}")
)

var urls = []string{
	"myipinfo.net",
	"myip.dnsomatic.com",
	"icanhazip.com",
	"checkip.dyndns.org",
	"www.myipnumber.com",
	"checkmyip.com",
	"myexternalip.com",
	"www.ipchicken.com",
	"ipecho.net/plain",
	"bot.whatismyipaddress.com",
	"ipv4.ipogre.com",
	"smart-ip.net/myip",
	"checkip.amazonaws.com",
	"www.checkip.org",
}

//Ddns represents a dynamic DNS service
type Ddns struct {
	Name     string
	Domain   string
	Account  string
	Password string
	Token    string
}

//DdnsService represents an interface for a dynamic DNS service
type DdnsService interface {
	updateIP() error
	getDomain() string
}

type program struct {
	exit chan struct{}
}

func (p *program) Start(s service.Service) error {
	p.exit = make(chan struct{})
	go p.run()
	return nil
}

func (p *program) run() error {
	update()
	ticker := time.NewTicker(time.Duration(interval) * time.Second)

	for {
		select {
		case <-ticker.C:
			update()
		case <-p.exit:
			ticker.Stop()
			return nil
		}
	}
}

func (p *program) Stop(s service.Service) error {
	logger.Info("Stopping service...")
	close(p.exit)
	return nil
}

func main() {
	rand.Seed(time.Now().UTC().UnixNano())

	svcConfig := &service.Config{
		Name:       "ddns-client",
		DisplayName: "DDNS Client",
		Description: "Dynamic DNS Client.",
	}

	prg := &program{}
	s, err := service.New(prg, svcConfig)
	if err != nil {
		log.Fatal(err)
	}

	logger, err = s.Logger(nil)
	if err != nil {
		log.Fatal(err)
	}

	err = loadConfig()
	if err != nil {
		log.Fatal(err)
	}

	if len(os.Args) > 1 {
		err := service.Control(s, os.Args[1])
		if err != nil {
			log.Printf("Valid actions: %q\n", service.ControlAction)
			log.Fatal(err)
		}

		return
	}

	err = s.Run()
	if err != nil {
		logger.Error(err)
	}
}

func loadConfig() error {
	var config iniparser.Config

	err := config.LoadFile("./config.ini")
	if err != nil {
		return err
	}

	sections := config.GetSections()
	if len(sections) < 2 {
		return errors.New("No services found in config file.\n")
	}

	for _, section := range sections {
		var err error
		var name = section.Name

		if name == "settings" {
			interval, err = config.GetInt(name, "interval")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
			}
			continue
		}

		t, err := config.GetString(name, "type")
		if err != nil {
			logger.Error(err)
			continue
		}
		switch strings.ToLower(t) {
		case "namecheap":
			service := new(nameCheapService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {

				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "noip":
		case "no-ip":
			service := new(noIPService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Account, err = config.GetString(name, "account")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "changeip":
			service := new(changeIPService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Account, err = config.GetString(name, "account")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "duckdns":
			service := new(duckDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "freedns":
			service := new(freeDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}

			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "system-ns":
		case "systemns":
			service := new(systemNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Token, err = config.GetString(name, "token")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		case "ipdns":
			service := new(ipDNSService)
			service.Name = name
			service.Domain, err = config.GetString(name, "domain")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			_, err := net.LookupHost(service.getDomain())
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			service.Password, err = config.GetString(name, "password")
			if err != nil {
				logger.Errorf("%s - %s", name, err)
				continue
			}
			services = append(services, service)
		}
	}

	if len(services) < 1 {
		return errors.New("No valid services found in config file.")
	}

	return nil
}

func update() {
	currentIP := getExternalIP()
	if currentIP == nil {
		return
	}

	for _, service := range services {
		addr, err := net.LookupHost(service.getDomain())
		if err != nil {
				logger.Errorf("%s - %s", service.getDomain(), err)
			continue
		}
		if len(addr) == 0 || addr[0] == "" {
			logger.Errorf("%s - Unable to get IP address", service.getDomain())
			continue
		}

		registeredIP := addr[0]

		if currentIP.String() == registeredIP {
			logger.Infof("%s - No update is necessary", service.getDomain())
		} else {
			err := service.updateIP()
			if err == nil {
				logger.Infof("%s - Successfully updated from %s to %s", service.getDomain(), registeredIP, currentIP.String())
			} else {
				logger.Errorf("%s - %s", service.getDomain(), err)
			}
		}
	}
}

func getExternalIP() net.IP {
	var currentIP net.IP
	for _, i := range rand.Perm(len(urls)) {
		url := "http://" + urls[i]

		content, err := GetResponse(url, "", "")
		if err != nil {
			logger.Errorf("%s - %s", url, err)
			continue
		}

		ip := regex.FindString(content)

		currentIP = net.ParseIP(ip)

		if currentIP != nil {
			return currentIP
		}
	}

	return currentIP
}

//GetResponse returns the content at the url address
func GetResponse(url string, login string, password string) (string, error) {
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", err
	}

	if login != "" && password != "" {
		request.SetBasicAuth(login, password)
	}

	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		return "", err
	}

	defer response.Body.Close()
	content, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return "", err
	}

	return string(content), nil
}
<|endoftext|>


<commit_before>// Copyright 2020 The SQLFlow Authors. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package experimental

import (
	"bytes"
	"text/template"

	pb "sqlflow.org/sqlflow/go/proto"
)

const normalStmtStepTmpl = `
def step_entry_{{.StepIndex}}():
    from runtime.dbapi import connect
    conn = connect("{{.DataSource}}")
    stmt = """{{.Stmt}}"""
    if conn.is_query(stmt):
        # Importing table_writer is slow. So only
        # import it when needed.
        from runtime.dbapi import table_writer
        rs = conn.query(stmt)  # Exception would raise if error
        tw = table_writer.ProtobufWriter(rs)
        lines = tw.dump_strings()
        for l in lines:
            print(l)
    else:
        conn.execute(stmt)  # Exception would raise if error
        print("OK")

    conn.close()
`

var normalStmtStepTemplate = template.Must(template.New("NormalStmtStep").Parse(normalStmtStepTmpl))

type normalStmtFiller struct {
	StepIndex  int
	DataSource string
	Stmt       string
}

// generateNormalStmtStep generate step Python code to run a normal SQL statement.
func generateNormalStmtStep(stmt string, stepIndex int, session *pb.Session) (string, error) {
	filler := &normalStmtFiller{
		StepIndex:  stepIndex,
		DataSource: session.DbConnStr,
		Stmt:       escapeSpecialRunesAndTrimSpace(stmt),
	}
	var program bytes.Buffer
	if err := normalStmtStepTemplate.Execute(&program, filler); err != nil {
		return "", err
	}
	return program.String(), nil
}
<commit_msg>remove log (#3096)<commit_after>// Copyright 2020 The SQLFlow Authors. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package experimental

import (
	"bytes"
	"text/template"

	pb "sqlflow.org/sqlflow/go/proto"
)

const normalStmtStepTmpl = `
def step_entry_{{.StepIndex}}():
    from runtime.dbapi import connect
    conn = connect("{{.DataSource}}")
    stmt = """{{.Stmt}}"""
    if conn.is_query(stmt):
        # Importing table_writer is slow. So only
        # import it when needed.
        from runtime.dbapi import table_writer
        rs = conn.query(stmt)  # Exception would raise if error
        tw = table_writer.ProtobufWriter(rs)
        lines = tw.dump_strings()
        for l in lines:
            print(l)
    else:
        conn.execute(stmt)  # Exception would raise if error

    conn.close()
`

var normalStmtStepTemplate = template.Must(template.New("NormalStmtStep").Parse(normalStmtStepTmpl))

type normalStmtFiller struct {
	StepIndex  int
	DataSource string
	Stmt       string
}

// generateNormalStmtStep generate step Python code to run a normal SQL statement.
func generateNormalStmtStep(stmt string, stepIndex int, session *pb.Session) (string, error) {
	filler := &normalStmtFiller{
		StepIndex:  stepIndex,
		DataSource: session.DbConnStr,
		Stmt:       escapeSpecialRunesAndTrimSpace(stmt),
	}
	var program bytes.Buffer
	if err := normalStmtStepTemplate.Execute(&program, filler); err != nil {
		return "", err
	}
	return program.String(), nil
}
<|endoftext|>