<commit_before>package linkedql

import (
	"context"
	"testing"

	"github.com/cayleygraph/cayley/graph/memstore"
	"github.com/cayleygraph/quad"
	"github.com/stretchr/testify/require"
)

var testCases = []struct {
	name    string
	data    []quad.Quad
	query   Step
	results []interface{}
}{
	{
		name: "All Vertices",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Vertex{Values: []quad.Value{}},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("likes"),
			quad.IRI("bob"),
		},
	},
	{
		name: "TagArray",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &As{
				Tags: []string{"liked"},
				From: &Out{
					From: &As{
						Tags: []string{"liker"},
						From: &Vertex{},
					},
					Via: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
				},
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"liker": quad.IRI("alice"),
				"liked": quad.IRI("bob"),
			},
		},
	},
	{
		name: "Back",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Back{
			From: &Out{
				From: &Vertex{
					Values: []quad.Value{quad.IRI("alice")},
				},
				Via: &Vertex{
					Values: []quad.Value{
						quad.IRI("likes"),
					},
				},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Both",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.MakeIRI("bob", "likes", "dan", ""),
		},
		query: &Both{
			From: &Vertex{
				Values: []quad.Value{quad.IRI("bob")},
			},
			Via: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("dan"),
		},
	},
	{
		name: "Count",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Count{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.Int(4),
		},
	},
	{
		name: "Except",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Except{
			From: &Vertex{
				Values: []quad.Value{quad.IRI("alice"), quad.IRI("likes")},
			},
			Excepted: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Filter",
		data: []quad.Quad{
			{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
		},
		query: &Filter{
			From:   &Vertex{Values: []quad.Value{}},
			Filter: &RegExp{Expression: "A"},
		},
		results: []interface{}{
			quad.String("Alice"),
		},
	},
	{
		name: "Has",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Has{
			From: &Vertex{
				Values: []quad.Value{},
			},
			Via: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
			Values: []quad.Value{quad.IRI("bob")},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "HasReverse",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &HasReverse{
			From: &Vertex{
				Values: []quad.Value{},
			},
			Via: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
			Values: []quad.Value{quad.IRI("alice")},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "In",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &In{
			From: &Vertex{Values: []quad.Value{}},
			Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "InPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &InPredicates{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.IRI("likes"),
		},
	},
	{
		name: "Intersect",
		data: []quad.Quad{
			quad.MakeIRI("bob", "likes", "alice", ""),
			quad.MakeIRI("dani", "likes", "alice", ""),
		},
		query: &Intersect{
			From: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("bob")}},
				Via: &Vertex{
					Values: []quad.Value{quad.IRI("likes")},
				},
			},
			Intersectee: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("bob")}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Is",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Is{
			Values: []quad.Value{quad.IRI("bob")},
			From: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("alice")}},
				Via: &Vertex{
					Values: []quad.Value{quad.IRI("likes")},
				},
			},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "In",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Limit{
			Limit: 2,
			From: &Vertex{
				Values: []quad.Value{},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("likes"),
		},
	},
	{
		name: "Out",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Out{
			From: &Vertex{Values: []quad.Value{}},
			Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "OutPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &OutPredicates{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.IRI("likes"),
		},
	},
	{
		name: "Save",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &Save{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
				Tag:  "likes",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"likes": quad.IRI("bob"),
			},
		},
	},
	{
		name: "SaveInPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &SaveInPredicates{
				From: &Vertex{Values: []quad.Value{}},
				Tag:  "predicate",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"predicate": quad.IRI("likes"),
			},
		},
	},
	{
		name: "SaveOptional",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
			quad.MakeIRI("bob", "likes", "alice", ""),
		},
		query: &TagArray{
			From: &SaveOptional{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("name")}},
				Tag:  "name",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"name": quad.String("Alice"),
			},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
		},
	},
	{
		name: "SaveOptionalReverse",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
			quad.MakeIRI("bob", "likes", "alice", ""),
		},
		query: &TagArray{
			From: &SaveOptionalReverse{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("name")}},
				Tag:  "name",
			},
		},
		results: []interface{}{
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{
				"name": quad.IRI("alice"),
			},
		},
	},
}

func TestLinkedQL(t *testing.T) {
	for _, c := range testCases {
		t.Run(c.name, func(t *testing.T) {
			store := memstore.New(c.data...)
			ctx := context.TODO()
			iterator := c.query.BuildIterator(store)
			var results []interface{}
			for iterator.Next(ctx) {
				results = append(results, iterator.Result())
			}
			require.NoError(t, iterator.Err())
			require.Equal(t, c.results, results)
		})
	}
}
<commit_msg>linkedql: Test SaveOutPredicates<commit_after>package linkedql

import (
	"context"
	"testing"

	"github.com/cayleygraph/cayley/graph/memstore"
	"github.com/cayleygraph/quad"
	"github.com/stretchr/testify/require"
)

var testCases = []struct {
	name    string
	data    []quad.Quad
	query   Step
	results []interface{}
}{
	{
		name: "All Vertices",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Vertex{Values: []quad.Value{}},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("likes"),
			quad.IRI("bob"),
		},
	},
	{
		name: "TagArray",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &As{
				Tags: []string{"liked"},
				From: &Out{
					From: &As{
						Tags: []string{"liker"},
						From: &Vertex{},
					},
					Via: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
				},
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"liker": quad.IRI("alice"),
				"liked": quad.IRI("bob"),
			},
		},
	},
	{
		name: "Back",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Back{
			From: &Out{
				From: &Vertex{
					Values: []quad.Value{quad.IRI("alice")},
				},
				Via: &Vertex{
					Values: []quad.Value{
						quad.IRI("likes"),
					},
				},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Both",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.MakeIRI("bob", "likes", "dan", ""),
		},
		query: &Both{
			From: &Vertex{
				Values: []quad.Value{quad.IRI("bob")},
			},
			Via: &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("dan"),
		},
	},
	{
		name: "Count",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Count{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.Int(4),
		},
	},
	{
		name: "Except",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Except{
			From: &Vertex{
				Values: []quad.Value{quad.IRI("alice"), quad.IRI("likes")},
			},
			Excepted: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Filter",
		data: []quad.Quad{
			{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
		},
		query: &Filter{
			From:   &Vertex{Values: []quad.Value{}},
			Filter: &RegExp{Expression: "A"},
		},
		results: []interface{}{
			quad.String("Alice"),
		},
	},
	{
		name: "Has",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Has{
			From: &Vertex{
				Values: []quad.Value{},
			},
			Via: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
			Values: []quad.Value{quad.IRI("bob")},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "HasReverse",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &HasReverse{
			From: &Vertex{
				Values: []quad.Value{},
			},
			Via: &Vertex{
				Values: []quad.Value{quad.IRI("likes")},
			},
			Values: []quad.Value{quad.IRI("alice")},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "In",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &In{
			From: &Vertex{Values: []quad.Value{}},
			Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "InPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &InPredicates{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.IRI("likes"),
		},
	},
	{
		name: "Intersect",
		data: []quad.Quad{
			quad.MakeIRI("bob", "likes", "alice", ""),
			quad.MakeIRI("dani", "likes", "alice", ""),
		},
		query: &Intersect{
			From: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("bob")}},
				Via: &Vertex{
					Values: []quad.Value{quad.IRI("likes")},
				},
			},
			Intersectee: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("bob")}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
		},
	},
	{
		name: "Is",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Is{
			Values: []quad.Value{quad.IRI("bob")},
			From: &Out{
				From: &Vertex{Values: []quad.Value{quad.IRI("alice")}},
				Via: &Vertex{
					Values: []quad.Value{quad.IRI("likes")},
				},
			},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "In",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Limit{
			Limit: 2,
			From: &Vertex{
				Values: []quad.Value{},
			},
		},
		results: []interface{}{
			quad.IRI("alice"),
			quad.IRI("likes"),
		},
	},
	{
		name: "Out",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &Out{
			From: &Vertex{Values: []quad.Value{}},
			Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
		},
		results: []interface{}{
			quad.IRI("bob"),
		},
	},
	{
		name: "OutPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &OutPredicates{
			From: &Vertex{Values: []quad.Value{}},
		},
		results: []interface{}{
			quad.IRI("likes"),
		},
	},
	{
		name: "Save",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &Save{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("likes")}},
				Tag:  "likes",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"likes": quad.IRI("bob"),
			},
		},
	},
	{
		name: "SaveInPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &SaveInPredicates{
				From: &Vertex{Values: []quad.Value{}},
				Tag:  "predicate",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"predicate": quad.IRI("likes"),
			},
		},
	},
	{
		name: "SaveOptional",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
			quad.MakeIRI("bob", "likes", "alice", ""),
		},
		query: &TagArray{
			From: &SaveOptional{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("name")}},
				Tag:  "name",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"name": quad.String("Alice"),
			},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
		},
	},
	{
		name: "SaveOptionalReverse",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
			quad.Quad{Subject: quad.IRI("alice"), Predicate: quad.IRI("name"), Object: quad.String("Alice"), Label: nil},
			quad.MakeIRI("bob", "likes", "alice", ""),
		},
		query: &TagArray{
			From: &SaveOptionalReverse{
				From: &Vertex{Values: []quad.Value{}},
				Via:  &Vertex{Values: []quad.Value{quad.IRI("name")}},
				Tag:  "name",
			},
		},
		results: []interface{}{
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{},
			map[string]quad.Value{
				"name": quad.IRI("alice"),
			},
		},
	},
	{
		name: "SaveOutPredicates",
		data: []quad.Quad{
			quad.MakeIRI("alice", "likes", "bob", ""),
		},
		query: &TagArray{
			From: &SaveOutPredicates{
				From: &Vertex{Values: []quad.Value{}},
				Tag:  "predicate",
			},
		},
		results: []interface{}{
			map[string]quad.Value{
				"predicate": quad.IRI("likes"),
			},
		},
	},
}

func TestLinkedQL(t *testing.T) {
	for _, c := range testCases {
		t.Run(c.name, func(t *testing.T) {
			store := memstore.New(c.data...)
			ctx := context.TODO()
			iterator := c.query.BuildIterator(store)
			var results []interface{}
			for iterator.Next(ctx) {
				results = append(results, iterator.Result())
			}
			require.NoError(t, iterator.Err())
			require.Equal(t, c.results, results)
		})
	}
}
<|endoftext|>


<commit_before>// +build linux

package peco

import (
	"os"
	"syscall"
	"unsafe"
)

func IsTty(fd uintptr) bool {
	var termios syscall.Termios
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
	return err == 0
}

func TtyReady() error {
	return nil
}

func TtyTerm() {
}
<commit_msg>remove unused var<commit_after>// +build linux

package peco

import (
	"syscall"
	"unsafe"
)

func IsTty(fd uintptr) bool {
	var termios syscall.Termios
	_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, uintptr(syscall.TCGETS), uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
	return err == 0
}

func TtyReady() error {
	return nil
}

func TtyTerm() {
}
<|endoftext|>


<commit_before>package device

import (
	"fmt"
	"strings"
	"time"

	"github.com/jmoiron/sqlx"

	"github.com/tapglue/snaas/platform/flake"
	"github.com/tapglue/snaas/platform/pg"
)

const (
	pgInsertDevice = `INSERT INTO
		%s.devices(deleted, device_id, disabled, endpoint_arn, id, language, platform, token, user_id, created_at, updated_at)
		VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`
	pgUpdateDevice = `
		UPDATE
			%s.devices
		SET
			deleted = $2,
			device_id = $3,
			disabled = $4,
			endpoint_arn = $5,
			language = $6,
			token = $7,
			user_id = $8,
			updated_at = $9
		WHERE
			id = $1`

	pgCountDevices = `SELECT count(*) FROM %s.devices
		%s`
	pgListDevices = `
		SELECT
			deleted, device_id, disabled, endpoint_arn, id, language, platform, token, user_id, created_at, updated_at
		FROM
			%s.devices
		%s`

	pgClauseDeleted      = `deleted = ?`
	pgClauseDeviceIDs    = `device_id IN (?)`
	pgClauseDisabled     = `disabled = ?`
	pgClauseEndpointARNs = `endpoint_arn IN (?)`
	pgClauseIDs          = `id IN (?)`
	pgClausePlatforms    = `platform IN (?)`
	pgClauseTokens       = `token IN (?)`
	pgClauseUserIDs      = `user_id IN (?)`

	pgOrderCreatedAt = `ORDER BY created_at DESC`

	pgIndexDeviceIDUserID = `
		CREATE INDEX
			%s
		ON
			%s.devices (device_id, user_id)
		WHERE
			deleted = false`
	pgIndexEndpointARN = `
		CREATE INDEX
			%s
		ON
			%s.devices(endpoint_arn)
		WHERE
			deleted = false`
	pgIndexID          = `CREATE INDEX %s ON %s.devices (id)`
	pgIndexUserDevices = `
		CREATE INDEX
			%s
		ON
			%s.devices(user_id)
		WHERE
			deleted = false
			AND disabled = false
			AND platform IN (1, 2, 3)`

	pgCreateSchema = `CREATE SCHEMA IF NOT EXISTS %s`
	pgCreateTable  = `CREATE TABLE IF NOT EXISTS %s.devices (
		deleted BOOL DEFAULT false,
		device_id TEXT NOT NULL,
		disabled BOOL DEFAULT false,
		endpoint_arn TEXT,
		id BIGINT NOT NULL,
		language TEXT NOT NULL,
		platform INT NOT NULL,
		token TEXT NOT NULL,
		user_id BIGINT NOT NULL,
		created_at TIMESTAMP NOT NULL,
		updated_at TIMESTAMP NOT NULL
	)`
	pgDropTable = `DROP TABLE IF EXISTS %s.devices`
)

type pgService struct {
	db *sqlx.DB
}

// PostgresService returns a Postgres based Service implementation.
func PostgresService(db *sqlx.DB) Service {
	return &pgService{
		db: db,
	}
}

func (s *pgService) Count(ns string, opts QueryOptions) (uint, error) {
	clauses, params, err := convertOpts(opts)
	if err != nil {
		return 0, err
	}

	count, err := s.countDevices(ns, clauses, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return 0, err
			}
		}

		count, err = s.countDevices(ns, clauses, params...)
	}

	return count, err
}

func (s *pgService) Put(ns string, d *Device) (*Device, error) {
	var (
		params []interface{}
		query  string
	)

	if err := d.Validate(); err != nil {
		return nil, err
	}

	if d.ID == 0 {
		if d.CreatedAt.IsZero() {
			d.CreatedAt = time.Now().UTC()
		}

		ts, err := time.Parse(pg.TimeFormat, d.CreatedAt.UTC().Format(pg.TimeFormat))
		if err != nil {
			return nil, err
		}

		d.CreatedAt = ts
		d.UpdatedAt = ts

		id, err := flake.NextID(flakeNamespace(ns))
		if err != nil {
			return nil, err
		}

		d.ID = id

		params = []interface{}{
			d.Deleted,
			d.DeviceID,
			d.Disabled,
			d.EndpointARN,
			d.ID,
			d.Language,
			d.Platform,
			d.Token,
			d.UserID,
			ts,
			ts,
		}
		query = fmt.Sprintf(pgInsertDevice, ns)
	} else {
		now, err := time.Parse(pg.TimeFormat, time.Now().UTC().Format(pg.TimeFormat))
		if err != nil {
			return nil, err
		}

		d.UpdatedAt = now

		params = []interface{}{
			d.ID,
			d.Deleted,
			d.DeviceID,
			d.Disabled,
			d.EndpointARN,
			d.Language,
			d.Token,
			d.UserID,
			d.UpdatedAt,
		}
		query = fmt.Sprintf(pgUpdateDevice, ns)
	}

	_, err := s.db.Exec(query, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return nil, err
			}

			_, err = s.db.Exec(query, params...)
		}
	}

	return d, err
}

func (s *pgService) Query(ns string, opts QueryOptions) (List, error) {
	clauses, params, err := convertOpts(opts)
	if err != nil {
		return nil, err
	}

	ds, err := s.listDevices(ns, clauses, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return nil, err
			}
		}

		ds, err = s.listDevices(ns, clauses, params...)
	}

	return ds, err
}

func (s *pgService) countDevices(
	ns string,
	clauses []string,
	params ...interface{},
) (uint, error) {
	c := strings.Join(clauses, "\nAND ")

	if len(clauses) > 0 {
		c = fmt.Sprintf("WHERE %s", c)
	}

	query := sqlx.Rebind(sqlx.DOLLAR, fmt.Sprintf(pgCountDevices, ns, c))

	var count uint

	err := s.db.Get(&count, query, params...)
	return count, err
}

func (s *pgService) listDevices(
	ns string,
	clauses []string,
	params ...interface{},
) (List, error) {
	c := strings.Join(clauses, "\nAND ")

	if len(clauses) > 0 {
		c = fmt.Sprintf("WHERE %s", c)
	}

	query := strings.Join([]string{
		fmt.Sprintf(pgListDevices, ns, c),
		pgOrderCreatedAt,
	}, "\n")

	query = sqlx.Rebind(sqlx.DOLLAR, query)

	rows, err := s.db.Query(query, params...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ds := List{}

	for rows.Next() {
		d := &Device{}

		err := rows.Scan(
			&d.Deleted,
			&d.DeviceID,
			&d.Disabled,
			&d.EndpointARN,
			&d.ID,
			&d.Language,
			&d.Platform,
			&d.Token,
			&d.UserID,
			&d.CreatedAt,
			&d.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		d.CreatedAt = d.CreatedAt.UTC()
		d.UpdatedAt = d.UpdatedAt.UTC()

		ds = append(ds, d)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return ds, nil
}

func (s *pgService) Setup(ns string) error {
	qs := []string{
		fmt.Sprintf(pgCreateSchema, ns),
		fmt.Sprintf(pgCreateTable, ns),
		pg.GuardIndex(ns, "device_device_id_user_id", pgIndexDeviceIDUserID),
		pg.GuardIndex(ns, "device_endpoint_arn", pgIndexEndpointARN),
		pg.GuardIndex(ns, "device_id", pgIndexID),
		pg.GuardIndex(ns, "device_user_devices", pgIndexUserDevices),
	}

	for _, q := range qs {
		_, err := s.db.Exec(q)
		if err != nil {
			return fmt.Errorf("setup (%s): %s", q, err)
		}
	}

	return nil
}

func (s *pgService) Teardown(ns string) error {
	qs := []string{
		fmt.Sprintf(pgDropTable, ns),
	}

	for _, q := range qs {
		_, err := s.db.Exec(q)
		if err != nil {
			return fmt.Errorf("teardown (%s): %s", q, err)
		}
	}

	return nil
}

func convertOpts(opts QueryOptions) ([]string, []interface{}, error) {
	var (
		clauses = []string{}
		params  = []interface{}{}
	)

	if opts.Deleted != nil {
		clause, _, err := sqlx.In(pgClauseDeleted, []interface{}{*opts.Deleted})
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, *opts.Deleted)
	}

	if len(opts.DeviceIDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.DeviceIDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseDeviceIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if opts.Disabled != nil {
		clause, _, err := sqlx.In(pgClauseDisabled, []interface{}{*opts.Disabled})
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, *opts.Disabled)
	}

	if len(opts.EndpointARNs) > 0 {
		ps := []interface{}{}

		for _, arn := range opts.EndpointARNs {
			ps = append(ps, arn)
		}

		clause, _, err := sqlx.In(pgClauseEndpointARNs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.IDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.IDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.Platforms) > 0 {
		ps := []interface{}{}

		for _, p := range opts.Platforms {
			ps = append(ps, p)
		}

		clause, _, err := sqlx.In(pgClausePlatforms, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.Tokens) > 0 {
		ps := []interface{}{}

		for _, p := range opts.Tokens {
			ps = append(ps, p)
		}

		clause, _, err := sqlx.In(pgClauseTokens, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.UserIDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.UserIDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseUserIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	return clauses, params, nil
}
<commit_msg>Update platform of device<commit_after>package device

import (
	"fmt"
	"strings"
	"time"

	"github.com/jmoiron/sqlx"

	"github.com/tapglue/snaas/platform/flake"
	"github.com/tapglue/snaas/platform/pg"
)

const (
	pgInsertDevice = `INSERT INTO
		%s.devices(deleted, device_id, disabled, endpoint_arn, id, language, platform, token, user_id, created_at, updated_at)
		VALUES($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)`
	pgUpdateDevice = `
		UPDATE
			%s.devices
		SET
			deleted = $2,
			device_id = $3,
			disabled = $4,
			endpoint_arn = $5,
			language = $6,
			platform = $7,
			token = $8,
			user_id = $9,
			updated_at = $10
		WHERE
			id = $1`

	pgCountDevices = `SELECT count(*) FROM %s.devices
		%s`
	pgListDevices = `
		SELECT
			deleted, device_id, disabled, endpoint_arn, id, language, platform, token, user_id, created_at, updated_at
		FROM
			%s.devices
		%s`

	pgClauseDeleted      = `deleted = ?`
	pgClauseDeviceIDs    = `device_id IN (?)`
	pgClauseDisabled     = `disabled = ?`
	pgClauseEndpointARNs = `endpoint_arn IN (?)`
	pgClauseIDs          = `id IN (?)`
	pgClausePlatforms    = `platform IN (?)`
	pgClauseTokens       = `token IN (?)`
	pgClauseUserIDs      = `user_id IN (?)`

	pgOrderCreatedAt = `ORDER BY created_at DESC`

	pgIndexDeviceIDUserID = `
		CREATE INDEX
			%s
		ON
			%s.devices (device_id, user_id)
		WHERE
			deleted = false`
	pgIndexEndpointARN = `
		CREATE INDEX
			%s
		ON
			%s.devices(endpoint_arn)
		WHERE
			deleted = false`
	pgIndexID          = `CREATE INDEX %s ON %s.devices (id)`
	pgIndexUserDevices = `
		CREATE INDEX
			%s
		ON
			%s.devices(user_id)
		WHERE
			deleted = false
			AND disabled = false
			AND platform IN (1, 2, 3)`

	pgCreateSchema = `CREATE SCHEMA IF NOT EXISTS %s`
	pgCreateTable  = `CREATE TABLE IF NOT EXISTS %s.devices (
		deleted BOOL DEFAULT false,
		device_id TEXT NOT NULL,
		disabled BOOL DEFAULT false,
		endpoint_arn TEXT,
		id BIGINT NOT NULL,
		language TEXT NOT NULL,
		platform INT NOT NULL,
		token TEXT NOT NULL,
		user_id BIGINT NOT NULL,
		created_at TIMESTAMP NOT NULL,
		updated_at TIMESTAMP NOT NULL
	)`
	pgDropTable = `DROP TABLE IF EXISTS %s.devices`
)

type pgService struct {
	db *sqlx.DB
}

// PostgresService returns a Postgres based Service implementation.
func PostgresService(db *sqlx.DB) Service {
	return &pgService{
		db: db,
	}
}

func (s *pgService) Count(ns string, opts QueryOptions) (uint, error) {
	clauses, params, err := convertOpts(opts)
	if err != nil {
		return 0, err
	}

	count, err := s.countDevices(ns, clauses, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return 0, err
			}
		}

		count, err = s.countDevices(ns, clauses, params...)
	}

	return count, err
}

func (s *pgService) Put(ns string, d *Device) (*Device, error) {
	var (
		params []interface{}
		query  string
	)

	if err := d.Validate(); err != nil {
		return nil, err
	}

	if d.ID == 0 {
		if d.CreatedAt.IsZero() {
			d.CreatedAt = time.Now().UTC()
		}

		ts, err := time.Parse(pg.TimeFormat, d.CreatedAt.UTC().Format(pg.TimeFormat))
		if err != nil {
			return nil, err
		}

		d.CreatedAt = ts
		d.UpdatedAt = ts

		id, err := flake.NextID(flakeNamespace(ns))
		if err != nil {
			return nil, err
		}

		d.ID = id

		params = []interface{}{
			d.Deleted,
			d.DeviceID,
			d.Disabled,
			d.EndpointARN,
			d.ID,
			d.Language,
			d.Platform,
			d.Token,
			d.UserID,
			ts,
			ts,
		}
		query = fmt.Sprintf(pgInsertDevice, ns)
	} else {
		now, err := time.Parse(pg.TimeFormat, time.Now().UTC().Format(pg.TimeFormat))
		if err != nil {
			return nil, err
		}

		d.UpdatedAt = now

		params = []interface{}{
			d.ID,
			d.Deleted,
			d.DeviceID,
			d.Disabled,
			d.EndpointARN,
			d.Language,
			d.Platform,
			d.Token,
			d.UserID,
			d.UpdatedAt,
		}
		query = fmt.Sprintf(pgUpdateDevice, ns)
	}

	_, err := s.db.Exec(query, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return nil, err
			}

			_, err = s.db.Exec(query, params...)
		}
	}

	return d, err
}

func (s *pgService) Query(ns string, opts QueryOptions) (List, error) {
	clauses, params, err := convertOpts(opts)
	if err != nil {
		return nil, err
	}

	ds, err := s.listDevices(ns, clauses, params...)
	if err != nil {
		if pg.IsRelationNotFound(pg.WrapError(err)) {
			if err := s.Setup(ns); err != nil {
				return nil, err
			}
		}

		ds, err = s.listDevices(ns, clauses, params...)
	}

	return ds, err
}

func (s *pgService) countDevices(
	ns string,
	clauses []string,
	params ...interface{},
) (uint, error) {
	c := strings.Join(clauses, "\nAND ")

	if len(clauses) > 0 {
		c = fmt.Sprintf("WHERE %s", c)
	}

	query := sqlx.Rebind(sqlx.DOLLAR, fmt.Sprintf(pgCountDevices, ns, c))

	var count uint

	err := s.db.Get(&count, query, params...)
	return count, err
}

func (s *pgService) listDevices(
	ns string,
	clauses []string,
	params ...interface{},
) (List, error) {
	c := strings.Join(clauses, "\nAND ")

	if len(clauses) > 0 {
		c = fmt.Sprintf("WHERE %s", c)
	}

	query := strings.Join([]string{
		fmt.Sprintf(pgListDevices, ns, c),
		pgOrderCreatedAt,
	}, "\n")

	query = sqlx.Rebind(sqlx.DOLLAR, query)

	rows, err := s.db.Query(query, params...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	ds := List{}

	for rows.Next() {
		d := &Device{}

		err := rows.Scan(
			&d.Deleted,
			&d.DeviceID,
			&d.Disabled,
			&d.EndpointARN,
			&d.ID,
			&d.Language,
			&d.Platform,
			&d.Token,
			&d.UserID,
			&d.CreatedAt,
			&d.UpdatedAt,
		)
		if err != nil {
			return nil, err
		}

		d.CreatedAt = d.CreatedAt.UTC()
		d.UpdatedAt = d.UpdatedAt.UTC()

		ds = append(ds, d)
	}

	if err := rows.Err(); err != nil {
		return nil, err
	}

	return ds, nil
}

func (s *pgService) Setup(ns string) error {
	qs := []string{
		fmt.Sprintf(pgCreateSchema, ns),
		fmt.Sprintf(pgCreateTable, ns),
		pg.GuardIndex(ns, "device_device_id_user_id", pgIndexDeviceIDUserID),
		pg.GuardIndex(ns, "device_endpoint_arn", pgIndexEndpointARN),
		pg.GuardIndex(ns, "device_id", pgIndexID),
		pg.GuardIndex(ns, "device_user_devices", pgIndexUserDevices),
	}

	for _, q := range qs {
		_, err := s.db.Exec(q)
		if err != nil {
			return fmt.Errorf("setup (%s): %s", q, err)
		}
	}

	return nil
}

func (s *pgService) Teardown(ns string) error {
	qs := []string{
		fmt.Sprintf(pgDropTable, ns),
	}

	for _, q := range qs {
		_, err := s.db.Exec(q)
		if err != nil {
			return fmt.Errorf("teardown (%s): %s", q, err)
		}
	}

	return nil
}

func convertOpts(opts QueryOptions) ([]string, []interface{}, error) {
	var (
		clauses = []string{}
		params  = []interface{}{}
	)

	if opts.Deleted != nil {
		clause, _, err := sqlx.In(pgClauseDeleted, []interface{}{*opts.Deleted})
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, *opts.Deleted)
	}

	if len(opts.DeviceIDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.DeviceIDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseDeviceIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if opts.Disabled != nil {
		clause, _, err := sqlx.In(pgClauseDisabled, []interface{}{*opts.Disabled})
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, *opts.Disabled)
	}

	if len(opts.EndpointARNs) > 0 {
		ps := []interface{}{}

		for _, arn := range opts.EndpointARNs {
			ps = append(ps, arn)
		}

		clause, _, err := sqlx.In(pgClauseEndpointARNs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.IDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.IDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.Platforms) > 0 {
		ps := []interface{}{}

		for _, p := range opts.Platforms {
			ps = append(ps, p)
		}

		clause, _, err := sqlx.In(pgClausePlatforms, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.Tokens) > 0 {
		ps := []interface{}{}

		for _, p := range opts.Tokens {
			ps = append(ps, p)
		}

		clause, _, err := sqlx.In(pgClauseTokens, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	if len(opts.UserIDs) > 0 {
		ps := []interface{}{}

		for _, id := range opts.UserIDs {
			ps = append(ps, id)
		}

		clause, _, err := sqlx.In(pgClauseUserIDs, ps)
		if err != nil {
			return nil, nil, err
		}

		clauses = append(clauses, clause)
		params = append(params, ps...)
	}

	return clauses, params, nil
}
<|endoftext|>


<commit_before>package backend

import (
	"reflect"
	"sort"
	"testing"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/state"
	"github.com/hashicorp/terraform/terraform"
)

// TestBackendConfig validates and configures the backend with the
// given configuration.
func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backend {
	// Get the proper config structure
	rc, err := config.NewRawConfig(c)
	if err != nil {
		t.Fatalf("bad: %s", err)
	}
	conf := terraform.NewResourceConfig(rc)

	// Validate
	warns, errs := b.Validate(conf)
	if len(warns) > 0 {
		t.Fatalf("warnings: %s", warns)
	}
	if len(errs) > 0 {
		t.Fatalf("errors: %s", errs)
	}

	// Configure
	if err := b.Configure(conf); err != nil {
		t.Fatalf("err: %s", err)
	}

	return b
}

// TestBackend will test the functionality of a Backend. The backend is
// assumed to already be configured. This will test state functionality.
// If the backend reports it doesn't support multi-state by returning the
// error ErrNamedStatesNotSupported, then it will not test that.
//
// If you want to test locking, two backends must be given. If b2 is nil,
// then state lockign won't be tested.
func TestBackend(t *testing.T, b1, b2 Backend) {
	testBackendStates(t, b1)

	if b2 != nil {
		testBackendStateLock(t, b1, b2)
	}
}

func testBackendStates(t *testing.T, b Backend) {
	states, err := b.States()
	if err == ErrNamedStatesNotSupported {
		t.Logf("TestBackend: named states not supported in %T, skipping", b)
		return
	}

	// Test it starts with only the default
	if len(states) != 1 || states[0] != DefaultStateName {
		t.Fatalf("should only have default to start: %#v", states)
	}

	// Create a couple states
	fooState, err := b.State("foo")
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := fooState.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}
	if v := fooState.State(); v.HasResources() {
		t.Fatalf("should be empty: %s", v)
	}

	barState, err := b.State("bar")
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := barState.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}
	if v := barState.State(); v.HasResources() {
		t.Fatalf("should be empty: %s", v)
	}

	// Verify they are distinct states
	{
		s := barState.State()
		if s == nil {
			s = terraform.NewState()
		}

		s.Lineage = "bar"
		if err := barState.WriteState(s); err != nil {
			t.Fatalf("bad: %s", err)
		}
		if err := barState.PersistState(); err != nil {
			t.Fatalf("bad: %s", err)
		}

		if err := fooState.RefreshState(); err != nil {
			t.Fatalf("bad: %s", err)
		}
		if v := fooState.State(); v != nil && v.Lineage == "bar" {
			t.Fatalf("bad: %#v", v)
		}
	}

	// Verify we can now list them
	{
		states, err := b.States()
		if err == ErrNamedStatesNotSupported {
			t.Logf("TestBackend: named states not supported in %T, skipping", b)
			return
		}

		sort.Strings(states)
		expected := []string{"bar", "default", "foo"}
		if !reflect.DeepEqual(states, expected) {
			t.Fatalf("bad: %#v", states)
		}
	}

	// Delete some states
	if err := b.DeleteState("foo"); err != nil {
		t.Fatalf("err: %s", err)
	}

	// Verify the default state can't be deleted
	if err := b.DeleteState(DefaultStateName); err == nil {
		t.Fatal("expected error")
	}

	// Verify deletion
	{
		states, err := b.States()
		if err == ErrNamedStatesNotSupported {
			t.Logf("TestBackend: named states not supported in %T, skipping", b)
			return
		}

		sort.Strings(states)
		expected := []string{"bar", "default"}
		if !reflect.DeepEqual(states, expected) {
			t.Fatalf("bad: %#v", states)
		}
	}
}

func testBackendStateLock(t *testing.T, b1, b2 Backend) {
	// Get the default state for each
	b1StateMgr, err := b1.State(DefaultStateName)
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := b1StateMgr.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}

	// Fast exit if this doesn't support locking at all
	if _, ok := b1StateMgr.(state.Locker); !ok {
		t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1)
		return
	}

	t.Logf("TestBackend: testing state locking for %T", b1)

	b2StateMgr, err := b2.State(DefaultStateName)
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := b2StateMgr.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}

	// Reassign so its obvious whats happening
	lockerA := b1StateMgr.(state.Locker)
	lockerB := b2StateMgr.(state.Locker)

	infoA := state.NewLockInfo()
	infoA.Operation = "test"
	infoA.Who = "clientA"

	infoB := state.NewLockInfo()
	infoB.Operation = "test"
	infoB.Who = "clientB"

	lockIDA, err := lockerA.Lock(infoA)
	if err != nil {
		t.Fatal("unable to get initial lock:", err)
	}

	// If the lock ID is blank, assume locking is disabled
	if lockIDA == "" {
		t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1)
		return
	}

	_, err = lockerB.Lock(infoB)
	if err == nil {
		lockerA.Unlock(lockIDA)
		t.Fatal("client B obtained lock while held by client A")
	}

	if err := lockerA.Unlock(lockIDA); err != nil {
		t.Fatal("error unlocking client A", err)
	}

	lockIDB, err := lockerB.Lock(infoB)
	if err != nil {
		t.Fatal("unable to obtain lock from client B")
	}

	if lockIDB == lockIDA {
		t.Fatalf("duplicate lock IDs: %q", lockIDB)
	}

	if err = lockerB.Unlock(lockIDB); err != nil {
		t.Fatal("error unlocking client B:", err)
	}

}
<commit_msg>test for proper state persistence<commit_after>package backend

import (
	"reflect"
	"sort"
	"testing"

	"github.com/hashicorp/terraform/config"
	"github.com/hashicorp/terraform/state"
	"github.com/hashicorp/terraform/terraform"
)

// TestBackendConfig validates and configures the backend with the
// given configuration.
func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backend {
	// Get the proper config structure
	rc, err := config.NewRawConfig(c)
	if err != nil {
		t.Fatalf("bad: %s", err)
	}
	conf := terraform.NewResourceConfig(rc)

	// Validate
	warns, errs := b.Validate(conf)
	if len(warns) > 0 {
		t.Fatalf("warnings: %s", warns)
	}
	if len(errs) > 0 {
		t.Fatalf("errors: %s", errs)
	}

	// Configure
	if err := b.Configure(conf); err != nil {
		t.Fatalf("err: %s", err)
	}

	return b
}

// TestBackend will test the functionality of a Backend. The backend is
// assumed to already be configured. This will test state functionality.
// If the backend reports it doesn't support multi-state by returning the
// error ErrNamedStatesNotSupported, then it will not test that.
//
// If you want to test locking, two backends must be given. If b2 is nil,
// then state lockign won't be tested.
func TestBackend(t *testing.T, b1, b2 Backend) {
	testBackendStates(t, b1)

	if b2 != nil {
		testBackendStateLock(t, b1, b2)
	}
}

func testBackendStates(t *testing.T, b Backend) {
	states, err := b.States()
	if err == ErrNamedStatesNotSupported {
		t.Logf("TestBackend: named states not supported in %T, skipping", b)
		return
	}

	// Test it starts with only the default
	if len(states) != 1 || states[0] != DefaultStateName {
		t.Fatalf("should only have default to start: %#v", states)
	}

	// Create a couple states
	foo, err := b.State("foo")
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := foo.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}
	if v := foo.State(); v.HasResources() {
		t.Fatalf("should be empty: %s", v)
	}

	bar, err := b.State("bar")
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := bar.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}
	if v := bar.State(); v.HasResources() {
		t.Fatalf("should be empty: %s", v)
	}

	// Verify they are distinct states that can be read back from storage
	{
		// start with a fresh state, and record the lineage being
		// written to "bar"
		barState := terraform.NewState()
		barLineage := barState.Lineage

		// the foo lineage should be distinct from bar, and unchanged after
		// modifying bar
		fooState := terraform.NewState()
		fooLineage := fooState.Lineage

		// write a known state to foo
		if err := foo.WriteState(fooState); err != nil {
			t.Fatal("error writing foo state:", err)
		}
		if err := foo.PersistState(); err != nil {
			t.Fatal("error persisting foo state:", err)
		}

		// write a distinct known state to bar
		if err := bar.WriteState(barState); err != nil {
			t.Fatalf("bad: %s", err)
		}
		if err := bar.PersistState(); err != nil {
			t.Fatalf("bad: %s", err)
		}

		// verify that foo is unchanged with the existing state manager
		if err := foo.RefreshState(); err != nil {
			t.Fatal("error refreshing foo:", err)
		}
		fooState = foo.State()
		switch {
		case fooState == nil:
			t.Fatal("nil state read from foo")
		case fooState.Lineage == barLineage:
			t.Fatalf("bar lineage read from foo: %#v", fooState)
		case fooState.Lineage != fooLineage:
			t.Fatal("foo lineage alterred")
		}

		// fetch foo again from the backend
		foo, err = b.State("foo")
		if err != nil {
			t.Fatal("error re-fetching state:", err)
		}
		if err := foo.RefreshState(); err != nil {
			t.Fatal("error refreshing foo:", err)
		}
		fooState = foo.State()
		switch {
		case fooState == nil:
			t.Fatal("nil state read from foo")
		case fooState.Lineage != fooLineage:
			t.Fatal("incorrect state returned from backend")
		}

		// fetch the bar  again from the backend
		bar, err = b.State("bar")
		if err != nil {
			t.Fatal("error re-fetching state:", err)
		}
		if err := bar.RefreshState(); err != nil {
			t.Fatal("error refreshing bar:", err)
		}
		barState = bar.State()
		switch {
		case barState == nil:
			t.Fatal("nil state read from bar")
		case barState.Lineage != barLineage:
			t.Fatal("incorrect state returned from backend")
		}
	}

	// Verify we can now list them
	{
		// we determined that named stated are supported earlier
		states, err := b.States()
		if err != nil {
			t.Fatal(err)
		}

		sort.Strings(states)
		expected := []string{"bar", "default", "foo"}
		if !reflect.DeepEqual(states, expected) {
			t.Fatalf("bad: %#v", states)
		}
	}

	// Delete some states
	if err := b.DeleteState("foo"); err != nil {
		t.Fatalf("err: %s", err)
	}

	// Verify the default state can't be deleted
	if err := b.DeleteState(DefaultStateName); err == nil {
		t.Fatal("expected error")
	}

	// Verify deletion
	{
		states, err := b.States()
		if err == ErrNamedStatesNotSupported {
			t.Logf("TestBackend: named states not supported in %T, skipping", b)
			return
		}

		sort.Strings(states)
		expected := []string{"bar", "default"}
		if !reflect.DeepEqual(states, expected) {
			t.Fatalf("bad: %#v", states)
		}
	}
}

func testBackendStateLock(t *testing.T, b1, b2 Backend) {
	// Get the default state for each
	b1StateMgr, err := b1.State(DefaultStateName)
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := b1StateMgr.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}

	// Fast exit if this doesn't support locking at all
	if _, ok := b1StateMgr.(state.Locker); !ok {
		t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1)
		return
	}

	t.Logf("TestBackend: testing state locking for %T", b1)

	b2StateMgr, err := b2.State(DefaultStateName)
	if err != nil {
		t.Fatalf("error: %s", err)
	}
	if err := b2StateMgr.RefreshState(); err != nil {
		t.Fatalf("bad: %s", err)
	}

	// Reassign so its obvious whats happening
	lockerA := b1StateMgr.(state.Locker)
	lockerB := b2StateMgr.(state.Locker)

	infoA := state.NewLockInfo()
	infoA.Operation = "test"
	infoA.Who = "clientA"

	infoB := state.NewLockInfo()
	infoB.Operation = "test"
	infoB.Who = "clientB"

	lockIDA, err := lockerA.Lock(infoA)
	if err != nil {
		t.Fatal("unable to get initial lock:", err)
	}

	// If the lock ID is blank, assume locking is disabled
	if lockIDA == "" {
		t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1)
		return
	}

	_, err = lockerB.Lock(infoB)
	if err == nil {
		lockerA.Unlock(lockIDA)
		t.Fatal("client B obtained lock while held by client A")
	}

	if err := lockerA.Unlock(lockIDA); err != nil {
		t.Fatal("error unlocking client A", err)
	}

	lockIDB, err := lockerB.Lock(infoB)
	if err != nil {
		t.Fatal("unable to obtain lock from client B")
	}

	if lockIDB == lockIDA {
		t.Fatalf("duplicate lock IDs: %q", lockIDB)
	}

	if err = lockerB.Unlock(lockIDB); err != nil {
		t.Fatal("error unlocking client B:", err)
	}

}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
	"container/list"
	js "encoding/json"

	"github.com/kurrik/oauth1a"
	"github.com/codingneo/twittergo"
	"github.com/kurrik/json"
	"github.com/robfig/cron"
	//"github.com/PuerkitoBio/goquery"
	"github.com/advancedlogic/GoOse"
	"github.com/codingneo/tweetsbot/ranking"
)

func LoadCredentials() (client *twittergo.Client, err error) {
	credentials, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		return
	}
	lines := strings.Split(string(credentials), "\n")
	config := &oauth1a.ClientConfig{
		ConsumerKey:    lines[0],
		ConsumerSecret: lines[1],
	}
	user := oauth1a.NewAuthorizedConfig(lines[2], lines[3])
	client = twittergo.NewClient(config, user, "stream.twitter.com")
	return
}

type Args struct {
	Track string
	Lang string
}

func parseArgs() *Args {
	a := &Args{}
	flag.StringVar(&a.Track, "track", "Data Science,Big Data,Machine Learning", "Keyword to look up")
	flag.StringVar(&a.Lang, "lang", "en", "Language to look up")
	flag.Parse()
	return a
}

func LoadExistingList(filename string) *list.List {
	result := list.New()

	data, err := ioutil.ReadFile(filename)
	if (err == nil) {
		// today's list exists, load existing list
		input := make(map[string][]ranking.Item)
		input["articles"] = make([]ranking.Item, 0)

		js.Unmarshal(data, &input)
		for item := range input["articles"] {
			result.PushBack(item)
		}
	}

	return result
}

type streamConn struct {
	client   *http.Client
	resp     *http.Response
	url      *url.URL
	stale    bool
	closed   bool
	mu       sync.Mutex
	// wait time before trying to reconnect, this will be
	// exponentially moved up until reaching maxWait, when
	// it will exit
	wait    int
	maxWait int
	connect func() (*http.Response, error)
}

func NewStreamConn(max int) streamConn {
	return streamConn{wait: 1, maxWait: max}
}

func (conn *streamConn) Close() {
	// Just mark the connection as stale, and let the connect() handler close after a read
	conn.mu.Lock()
	defer conn.mu.Unlock()
	conn.stale = true
	conn.closed = true
	if conn.resp != nil {
		conn.resp.Body.Close()
	}
}

func (conn *streamConn) isStale() bool {
	conn.mu.Lock()
	r := conn.stale
	conn.mu.Unlock()
	return r
}

func readStream(client *twittergo.Client, sc streamConn, path string, query url.Values,
				resp *twittergo.APIResponse, handler func([]byte), done chan bool) {

	var reader *bufio.Reader
	reader = bufio.NewReader(resp.Body)

	for {
		//we've been closed
		if sc.isStale() {
			sc.Close()
			fmt.Println("Connection closed, shutting down ")
			break
		}

		line, err := reader.ReadBytes('\n')

		if err != nil {
			if sc.isStale() {
				fmt.Println("conn stale, continue")
				continue
			}

			time.Sleep(time.Second * time.Duration(sc.wait))
			//try reconnecting, but exponentially back off until MaxWait is reached then exit?
			resp, err := Connect(client, path, query)
			if err != nil || resp == nil {
				fmt.Println(" Could not reconnect to source? sleeping and will retry ")
				if sc.wait < sc.maxWait {
					sc.wait = sc.wait * 2
				} else {
					fmt.Println("exiting, max wait reached")
					done <- true
					return
				}
				continue
			}
			if resp.StatusCode != 200 {
				fmt.Printf("resp.StatusCode = %d", resp.StatusCode)
				if sc.wait < sc.maxWait {
					sc.wait = sc.wait * 2
				}
				continue
			}

			reader = bufio.NewReader(resp.Body)
			continue
		} else if sc.wait != 1 {
			sc.wait = 1
		}
		line = bytes.TrimSpace(line)
		fmt.Println("Received a line ")

		if len(line) == 0 {
			continue
		}
		handler(line)
	}
}

func Connect(client *twittergo.Client, path string, query url.Values) (resp *twittergo.APIResponse, err error) {
	var (
		req 	*http.Request
	)

	url := fmt.Sprintf("%v?%v", path, query.Encode())
	req, err = http.NewRequest("GET", url, nil)
	if err != nil {
		err = fmt.Errorf("Could not parse request: %v\n", err)
		return
	}
	resp, err = client.SendRequest(req)
	if err != nil {
		err = fmt.Errorf("Could not send request: %v\n", err)
		return
	}

	fmt.Printf("resp.StatusCode=%d\n", resp.StatusCode)
	return
}

func filterStream(client *twittergo.Client, path string, query url.Values) (err error) {
	var (
		resp    *twittergo.APIResponse
	)

	sc := NewStreamConn(300)

	// Step 1: connect to twitter public stream endpoint
	resp, err = Connect(client, path, query)

	done := make(chan bool)
	stream := make(chan []byte, 1000)
	go func() {
		filename := "./data/toplist-" +
			time.Now().Local().Format("2006-01-02") +".json"

		//
		topList := LoadExistingList(filename)

		//Cron job to store toplist per hour
		c := cron.New()
		c.AddFunc("0 * * * * *",
			func() {
				fmt.Println("cron cron cron cron ............................")

				output := make(map[string]interface{})
				output["articles"] = make([]ranking.Item, 0)

				f, err := os.OpenFile(filename, os.O_RDWR, 0666)
				if (err == nil) {
					f.Close()
					err = os.Remove(filename)
				}

				f, err = os.Create(filename)
				if (err != nil) {
					fmt.Println("[Cron] File creation error")
				}

				tlist := make([]ranking.Item, 0)
				for e := topList.Front(); e != nil; e = e.Next() {
					fmt.Println("[Cron] Write url into file")
					//f.WriteString(e.Value.(ranking.Item).Url)
					//f.WriteString("\n")
					tlist = append(tlist, e.Value.(ranking.Item))
				}
				output["articles"] = tlist

				jsonstr, _ := js.Marshal(output)
				f.WriteString(string(jsonstr))
				f.Sync()
				f.Close()
			})
		c.Start()
		fmt.Println("cron job start")

		g := goose.New()
		for data := range stream {
			fmt.Println(string(data))
			tweet := &twittergo.Tweet{}
			err := json.Unmarshal(data, tweet)
			if (err == nil) {
				fmt.Printf("ID:                   %v\n", tweet.Id())
				fmt.Printf("User:                 %v\n", tweet.User().ScreenName())
				fmt.Printf("Tweet:                %v\n", tweet.Text())

				rs := tweet.RetweetedStatus()
				vote := 0
				createdAt := tweet.CreatedAt()
				if (rs != nil) {
					fmt.Printf("retweet_count:        %d\n", rs.RetweetCount())
					fmt.Printf("favorite_count:        %d\n", rs.FavoriteCount())
					vote += int(rs.RetweetCount()+rs.FavoriteCount())

					createdAt = rs.CreatedAt()
				}

				if (time.Now().UTC().Sub(createdAt).Hours() < 24.0) {
					e := tweet.Entities()
					if (e != nil) {
						fmt.Printf("url:        %v\n", e.FirstUrl().ExpandedUrl())

						// Form top item
						if (e.FirstUrl().ExpandedUrl()!="") {
							item := ranking.Item{}
							item.Vote = vote
							item.Url = e.FirstUrl().ExpandedUrl()

							// article extraction
							//doc, err := goquery.NewDocument(item.Url)
							article := g.ExtractFromUrl(item.Url)

							fmt.Println("title", article.Title)
	    				fmt.Println("description", article.MetaDescription)
	    				fmt.Println("top image", article.TopImage)

	    				if (article.Title != "") &&
	    					 (article.MetaDescription != "") {
								item.Title = article.Title
	    					item.Description = article.MetaDescription
	    					item.Image = article.TopImage

	    					ranking.Insert(topList, item)
	    				}

							//if err == nil {
							//	item.Title = doc.Find("title").Text()
							//	fmt.Printf("title:        %v\n", item.Title)
							//	ranking.Insert(topList, item)
							//}

							fmt.Println("**********************************")
							for e := topList.Front(); e != nil; e = e.Next() {
								fmt.Printf("%d: %v\n",e.Value.(ranking.Item).Vote, e.Value.(ranking.Item).Url)
							}
						}
					}
				}
			}
		}
	}()

	readStream(client, sc, path, query, resp,
		func(line []byte) {
			stream <- line
		}, done)

	return
}

func main() {
	var (
		err    error
		args   *Args
		client *twittergo.Client
	)

	args = parseArgs()
	if client, err = LoadCredentials(); err != nil {
		fmt.Printf("Could not parse CREDENTIALS file: %v\n", err)
		os.Exit(1)
	}
	fmt.Println(args.Track)
	query := url.Values{}
	query.Set("track", args.Track)
	query.Set("lang", args.Lang)

	fmt.Println("Printing everything about data science, big data and machine learning:")
	fmt.Printf("=========================================================\n")
	if err = filterStream(client, "/1.1/statuses/filter.json", query); err != nil {
		fmt.Println("Error: %v\n", err)
	}
	fmt.Printf("\n\n")

}
<commit_msg>Change daily update logic<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"sync"
	"time"
	"container/list"
	js "encoding/json"

	"github.com/kurrik/oauth1a"
	"github.com/codingneo/twittergo"
	"github.com/kurrik/json"
	"github.com/robfig/cron"
	//"github.com/PuerkitoBio/goquery"
	"github.com/advancedlogic/GoOse"
	"github.com/codingneo/tweetsbot/ranking"
)

func LoadCredentials() (client *twittergo.Client, err error) {
	credentials, err := ioutil.ReadFile("CREDENTIALS")
	if err != nil {
		return
	}
	lines := strings.Split(string(credentials), "\n")
	config := &oauth1a.ClientConfig{
		ConsumerKey:    lines[0],
		ConsumerSecret: lines[1],
	}
	user := oauth1a.NewAuthorizedConfig(lines[2], lines[3])
	client = twittergo.NewClient(config, user, "stream.twitter.com")
	return
}

type Args struct {
	Track string
	Lang string
}

func parseArgs() *Args {
	a := &Args{}
	flag.StringVar(&a.Track, "track", "Data Science,Big Data,Machine Learning", "Keyword to look up")
	flag.StringVar(&a.Lang, "lang", "en", "Language to look up")
	flag.Parse()
	return a
}

func LoadExistingList(filename string) *list.List {
	result := list.New()

	data, err := ioutil.ReadFile(filename)
	if (err == nil) {
		// today's list exists, load existing list
		input := make(map[string][]ranking.Item)
		input["articles"] = make([]ranking.Item, 0)

		js.Unmarshal(data, &input)
		for item := range input["articles"] {
			result.PushBack(item)
		}
	}

	return result
}

type streamConn struct {
	client   *http.Client
	resp     *http.Response
	url      *url.URL
	stale    bool
	closed   bool
	mu       sync.Mutex
	// wait time before trying to reconnect, this will be
	// exponentially moved up until reaching maxWait, when
	// it will exit
	wait    int
	maxWait int
	connect func() (*http.Response, error)
}

func NewStreamConn(max int) streamConn {
	return streamConn{wait: 1, maxWait: max}
}

func (conn *streamConn) Close() {
	// Just mark the connection as stale, and let the connect() handler close after a read
	conn.mu.Lock()
	defer conn.mu.Unlock()
	conn.stale = true
	conn.closed = true
	if conn.resp != nil {
		conn.resp.Body.Close()
	}
}

func (conn *streamConn) isStale() bool {
	conn.mu.Lock()
	r := conn.stale
	conn.mu.Unlock()
	return r
}

func readStream(client *twittergo.Client, sc streamConn, path string, query url.Values,
				resp *twittergo.APIResponse, handler func([]byte), done chan bool) {

	var reader *bufio.Reader
	reader = bufio.NewReader(resp.Body)

	for {
		//we've been closed
		if sc.isStale() {
			sc.Close()
			fmt.Println("Connection closed, shutting down ")
			break
		}

		line, err := reader.ReadBytes('\n')

		if err != nil {
			if sc.isStale() {
				fmt.Println("conn stale, continue")
				continue
			}

			time.Sleep(time.Second * time.Duration(sc.wait))
			//try reconnecting, but exponentially back off until MaxWait is reached then exit?
			resp, err := Connect(client, path, query)
			if err != nil || resp == nil {
				fmt.Println(" Could not reconnect to source? sleeping and will retry ")
				if sc.wait < sc.maxWait {
					sc.wait = sc.wait * 2
				} else {
					fmt.Println("exiting, max wait reached")
					done <- true
					return
				}
				continue
			}
			if resp.StatusCode != 200 {
				fmt.Printf("resp.StatusCode = %d", resp.StatusCode)
				if sc.wait < sc.maxWait {
					sc.wait = sc.wait * 2
				}
				continue
			}

			reader = bufio.NewReader(resp.Body)
			continue
		} else if sc.wait != 1 {
			sc.wait = 1
		}
		line = bytes.TrimSpace(line)
		fmt.Println("Received a line ")

		if len(line) == 0 {
			continue
		}
		handler(line)
	}
}

func Connect(client *twittergo.Client, path string, query url.Values) (resp *twittergo.APIResponse, err error) {
	var (
		req 	*http.Request
	)

	url := fmt.Sprintf("%v?%v", path, query.Encode())
	req, err = http.NewRequest("GET", url, nil)
	if err != nil {
		err = fmt.Errorf("Could not parse request: %v\n", err)
		return
	}
	resp, err = client.SendRequest(req)
	if err != nil {
		err = fmt.Errorf("Could not send request: %v\n", err)
		return
	}

	fmt.Printf("resp.StatusCode=%d\n", resp.StatusCode)
	return
}

func filterStream(client *twittergo.Client, path string, query url.Values) (err error) {
	var (
		resp    *twittergo.APIResponse
	)

	sc := NewStreamConn(300)

	// Step 1: connect to twitter public stream endpoint
	resp, err = Connect(client, path, query)

	done := make(chan bool)
	stream := make(chan []byte, 1000)
	go func() {
		startday := time.Now().UTC().Day()
		filename := "./data/toplist-" +
			time.Now().UTC().Format("2006-01-02") +".json"

		//
		topList := LoadExistingList(filename)

		//Cron job to store toplist per hour
		c := cron.New()
		c.AddFunc("0 * * * * *",
			func() {
				fmt.Println("cron cron cron cron ............................")

				output := make(map[string]interface{})
				output["articles"] = make([]ranking.Item, 0)

				f, err := os.OpenFile(filename, os.O_RDWR, 0666)
				if (err == nil) {
					f.Close()
					err = os.Remove(filename)
				}

				f, err = os.Create(filename)
				if (err != nil) {
					fmt.Println("[Cron] File creation error")
				}

				tlist := make([]ranking.Item, 0)
				for e := topList.Front(); e != nil; e = e.Next() {
					fmt.Println("[Cron] Write url into file")
					//f.WriteString(e.Value.(ranking.Item).Url)
					//f.WriteString("\n")
					tlist = append(tlist, e.Value.(ranking.Item))
				}
				output["articles"] = tlist

				jsonstr, _ := js.Marshal(output)
				f.WriteString(string(jsonstr))
				f.Sync()
				f.Close()
			})
		c.Start()
		fmt.Println("cron job start")

		g := goose.New()
		for data := range stream {
			if (time.Now().UTC().Day() != startday) {
				// Clear the top list
				var next *list.Element
				for e := topList.Front(); e != nil; e = next {
					next = e.Next()
					topList.Remove(e)
				}

				startday = time.Now().UTC().Day()
				filename = "./data/toplist-" +
					time.Now().UTC().Format("2006-01-02") +".json"
			}

			fmt.Println(string(data))
			tweet := &twittergo.Tweet{}
			err := json.Unmarshal(data, tweet)
			if (err == nil) {
				fmt.Printf("ID:                   %v\n", tweet.Id())
				fmt.Printf("User:                 %v\n", tweet.User().ScreenName())
				fmt.Printf("Tweet:                %v\n", tweet.Text())

				rs := tweet.RetweetedStatus()
				vote := 0
				createdAt := tweet.CreatedAt()
				if (rs != nil) {
					fmt.Printf("retweet_count:        %d\n", rs.RetweetCount())
					fmt.Printf("favorite_count:        %d\n", rs.FavoriteCount())
					vote += int(rs.RetweetCount()+rs.FavoriteCount())

					createdAt = rs.CreatedAt()
				}

				if (time.Now().UTC().Sub(createdAt).Hours() < 24.0) {
					e := tweet.Entities()
					if (e != nil) {
						fmt.Printf("url:        %v\n", e.FirstUrl().ExpandedUrl())

						// Form top item
						if (e.FirstUrl().ExpandedUrl()!="") {
							item := ranking.Item{}
							item.Vote = vote
							item.Url = e.FirstUrl().ExpandedUrl()

							// article extraction
							//doc, err := goquery.NewDocument(item.Url)
							article := g.ExtractFromUrl(item.Url)

							fmt.Println("title", article.Title)
	    				fmt.Println("description", article.MetaDescription)
	    				fmt.Println("top image", article.TopImage)

	    				if (article.Title != "") &&
	    					 (article.MetaDescription != "") {
								item.Title = article.Title
	    					item.Description = article.MetaDescription
	    					item.Image = article.TopImage

	    					ranking.Insert(topList, item)
	    				}

							//if err == nil {
							//	item.Title = doc.Find("title").Text()
							//	fmt.Printf("title:        %v\n", item.Title)
							//	ranking.Insert(topList, item)
							//}

							fmt.Println("**********************************")
							for e := topList.Front(); e != nil; e = e.Next() {
								fmt.Printf("%d: %v\n",e.Value.(ranking.Item).Vote, e.Value.(ranking.Item).Url)
							}
						}
					}
				}
			}
		}
	}()

	readStream(client, sc, path, query, resp,
		func(line []byte) {
			stream <- line
		}, done)

	return
}

func main() {
	var (
		err    error
		args   *Args
		client *twittergo.Client
	)

	args = parseArgs()
	if client, err = LoadCredentials(); err != nil {
		fmt.Printf("Could not parse CREDENTIALS file: %v\n", err)
		os.Exit(1)
	}
	fmt.Println(args.Track)
	query := url.Values{}
	query.Set("track", args.Track)
	query.Set("lang", args.Lang)

	fmt.Println("Printing everything about data science, big data and machine learning:")
	fmt.Printf("=========================================================\n")
	if err = filterStream(client, "/1.1/statuses/filter.json", query); err != nil {
		fmt.Println("Error: %v\n", err)
	}
	fmt.Printf("\n\n")

}
<|endoftext|>


<commit_before>package main

import (
	"board"
	"color"
	"fmt"
	"matrix"
)

func scanMove() (*matrix.Move, error) {
	var file byte
	var rank int

	_, err := fmt.Scanf("%c%d", &file, &rank)
	return matrix.NewMove(file, rank), err
}

func main() {
	chessboard := board.NewBoard()
	finish := false
	now := color.White

	for finish == false {
		success := false

		chessboard.Print()
		if chessboard.IsCheckMate(now) {
			fmt.Println("Checkmate")
			break
		} else if chessboard.IsChecked(now) {
			fmt.Println("Your king is checked")
		}

		for success == false {
			from, err := scanMove()
			if err != nil {
				finish = true
				break
			}
			to, err := scanMove()
			if err != nil {
				finish = true
				break
			}
			err = chessboard.Move(from.ToPoint(), to.ToPoint(), now)
			success = err == nil
			if success == false {
				fmt.Println(err)
			}
		}
		fmt.Println()
		now = now.Enemy()
	}
}
<commit_msg>Get from enemy when the currently player is black<commit_after>package main

import (
	"board"
	"color"
	"enemy"
	"fmt"
	"matrix"
)

func scanMove() (*matrix.Move, error) {
	var file byte
	var rank int

	_, err := fmt.Scanf("%c%d", &file, &rank)
	return matrix.NewMove(file, rank), err
}

func main() {
	chessboard := board.NewBoard()
	finish := false
	now := color.White

	for finish == false {
		success := false

		chessboard.Print()
		if chessboard.IsCheckMate(now) {
			fmt.Println("Checkmate")
			break
		} else if chessboard.IsChecked(now) {
			fmt.Println("Your king is checked")
		}

		for success == false {
			var from, to matrix.Point
			var err error
			if now == color.Black {
				from, to = enemy.NewEnemy(chessboard, now).RandomizedSelect()
			} else {
				var move *matrix.Move
				move, err = scanMove()
				if err != nil {
					finish = true
					break
				} else {
					from = move.ToPoint()
				}
				move, err = scanMove()
				if err != nil {
					finish = true
					break
				} else {
					to = move.ToPoint()
				}
			}
			err = chessboard.Move(from, to, now)
			success = err == nil
			if success == false {
				fmt.Println(err)
			}
		}
		fmt.Println()
		now = now.Enemy()
	}
}
<|endoftext|>


<commit_before>package cli

import (
	"fmt"
	"qiniu/api.v6/auth/digest"
	"qiniu/log"
	"qshell"
)

func GetBuckets(cmd string, params ...string) {
	if len(params) == 0 {
		gErr := accountS.Get()
		if gErr != nil {
			fmt.Println(gErr)
			return
		}
		mac := digest.Mac{
			accountS.AccessKey,
			[]byte(accountS.SecretKey),
		}
		buckets, err := qshell.GetBuckets(&mac)
		if err != nil {
			log.Error("Get buckets error,", err)
		} else {
			for _, bucket := range buckets {
				fmt.Println(bucket)
			}
		}
	} else {
		CmdHelp(cmd)
	}
}

func GetDomainsOfBucket(cmd string, params ...string) {
	if len(params) == 1 {
		bucket := params[0]
		gErr := accountS.Get()
		if gErr != nil {
			fmt.Println(gErr)
			return
		}
		mac := digest.Mac{
			accountS.AccessKey,
			[]byte(accountS.SecretKey),
		}
		domains, err := qshell.GetDomainsOfBucket(&mac, bucket)
		if err != nil {
			log.Error("Get domains error,", err)
		} else {
			for _, domain := range domains {
				fmt.Println(domain)
			}
		}
	} else {
		CmdHelp(cmd)
	}
}
<commit_msg>fix domains and domains of bucket code<commit_after>package cli

import (
	"fmt"
	"qiniu/api.v6/auth/digest"
	"qiniu/log"
	"qshell"
)

func GetBuckets(cmd string, params ...string) {
	if len(params) == 0 {
		gErr := accountS.Get()
		if gErr != nil {
			fmt.Println(gErr)
			return
		}
		mac := digest.Mac{
			accountS.AccessKey,
			[]byte(accountS.SecretKey),
		}
		buckets, err := qshell.GetBuckets(&mac)
		if err != nil {
			log.Error("Get buckets error,", err)
		} else {
			if len(buckets) == 0 {
				fmt.Println("No buckets found")
			} else {
				for _, bucket := range buckets {
					fmt.Println(bucket)
				}
			}
		}
	} else {
		CmdHelp(cmd)
	}
}

func GetDomainsOfBucket(cmd string, params ...string) {
	if len(params) == 1 {
		bucket := params[0]
		gErr := accountS.Get()
		if gErr != nil {
			fmt.Println(gErr)
			return
		}
		mac := digest.Mac{
			accountS.AccessKey,
			[]byte(accountS.SecretKey),
		}
		domains, err := qshell.GetDomainsOfBucket(&mac, bucket)
		if err != nil {
			log.Error("Get domains error,", err)
		} else {
			if len(domains) == 0 {
				fmt.Printf("No domains found for bucket `%s`\n", bucket)
			} else {
				for _, domain := range domains {
					fmt.Println(domain)
				}
			}
		}
	} else {
		CmdHelp(cmd)
	}
}
<|endoftext|>


<commit_before>package timestamp

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

// "interesting" dates are between years 1800 and 2200
const (
	SECONDS_PER_DAY  = 24 * 60 * 60
	SECONDS_PER_YEAR = (365 * SECONDS_PER_DAY) + (SECONDS_PER_DAY / 4)

	TS_SECONDS_MIN = (1800 - 1970) * SECONDS_PER_YEAR
	TS_SECONDS_MAX = (2200 - 1970) * SECONDS_PER_YEAR

	TS_MILLISECONDS_MIN = 1000 * TS_SECONDS_MIN
	TS_MILLISECONDS_MAX = 1000 * TS_SECONDS_MAX

	TS_MICROSECONDS_MIN = 1000 * TS_MILLISECONDS_MIN
	TS_MICROSECONDS_MAX = 1000 * TS_MILLISECONDS_MAX

	TS_NANOSECONDS_MIN = 1000 * TS_MICROSECONDS_MIN
	TS_NANOSECONDS_MAX = 1000 * TS_MICROSECONDS_MAX
)

// parseUnknown tries to guess the input format, and returns the
// parsed date.
func parseUnknown(s string) (time.Time, error) {
	// Is it a number?
	n, err := strconv.ParseInt(s, 10, 64)
	if err == nil {
		switch {
		case TS_SECONDS_MIN < n && n < TS_SECONDS_MAX:
			return time.Unix(n, 0), nil
		case TS_MILLISECONDS_MIN < n && n < TS_MILLISECONDS_MAX:
			return time.Unix(n/1000, n%1000), nil
		case TS_MICROSECONDS_MIN < n && n < TS_MICROSECONDS_MAX:
			return time.Unix(n/1000000, n%1000000), nil
		case TS_NANOSECONDS_MIN < n && n < TS_NANOSECONDS_MAX:
			return time.Unix(n/1000000000, n%1000000000), nil
		case 18000101 < n && n < 22000101:
			return time.Parse("20060102", s)
		case 180001010000 < n && n <= 220001012359:
			return time.Parse("200601021504", s)
		case 18000101000000 < n && n <= 22000101235959:
			return time.Parse("20060102150405", s)
		}
	}
	// Try removing useless chars
	stripped := strip(s)
	if stripped != s {
		tStripped, errStripped := parseUnknown(stripped)
		if errStripped == nil {
			return tStripped, nil
		}
	}
	return time.Time{}, fmt.Errorf("Could not determine format of input[%v]", s)
}

// Remove all non-digit chars
func strip(s string) string {
	return strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, s)
}
<commit_msg>Fix bug and priorities.<commit_after>package timestamp

import (
	"fmt"
	"strconv"
	"strings"
	"time"
)

// "interesting" dates are between years 1800 and 2200
const (
	SECONDS_PER_DAY  = 24 * 60 * 60
	SECONDS_PER_YEAR = (365 * SECONDS_PER_DAY) + (SECONDS_PER_DAY / 4)

	TS_SECONDS_MIN = (1800 - 1970) * SECONDS_PER_YEAR
	TS_SECONDS_MAX = (2200 - 1970) * SECONDS_PER_YEAR

	TS_MILLISECONDS_MIN = 1000 * TS_SECONDS_MIN
	TS_MILLISECONDS_MAX = 1000 * TS_SECONDS_MAX

	TS_MICROSECONDS_MIN = 1000 * TS_MILLISECONDS_MIN
	TS_MICROSECONDS_MAX = 1000 * TS_MILLISECONDS_MAX

	TS_NANOSECONDS_MIN = 1000 * TS_MICROSECONDS_MIN
	TS_NANOSECONDS_MAX = 1000 * TS_MICROSECONDS_MAX
)

// parseUnknown tries to guess the input format, and returns the
// parsed date.
func parseUnknown(s string) (time.Time, error) {
	// Is it a number?
	n, err := strconv.ParseInt(s, 10, 64)
	if err == nil {
		switch {
		// Date-like
		case 18000101 < n && n < 22000101:
			return time.Parse("20060102", s)
		case 180001010000 < n && n <= 220001012359:
			return time.Parse("200601021504", s)
		case 18000101000000 < n && n <= 22000101235959:
			return time.Parse("20060102150405", s)

		// Timestamp-like
		case TS_SECONDS_MIN < n && n < TS_SECONDS_MAX:
			return time.Unix(n, 0), nil
		case TS_MILLISECONDS_MIN < n && n < TS_MILLISECONDS_MAX:
			return time.Unix(n/1000, (n%1000)*1000000), nil
		case TS_MICROSECONDS_MIN < n && n < TS_MICROSECONDS_MAX:
			return time.Unix(n/1000000, (n%1000000)*1000), nil
		case TS_NANOSECONDS_MIN < n && n < TS_NANOSECONDS_MAX:
			return time.Unix(n/1000000000, n%1000000000), nil
		}
	}
	// Try removing useless chars
	stripped := strip(s)
	if stripped != s {
		tStripped, errStripped := parseUnknown(stripped)
		if errStripped == nil {
			return tStripped, nil
		}
	}
	return time.Time{}, fmt.Errorf("Could not determine format of input[%v]", s)
}

// Remove all non-digit chars
func strip(s string) string {
	return strings.Map(func(r rune) rune {
		if r >= '0' && r <= '9' {
			return r
		}
		return -1
	}, s)
}
<|endoftext|>


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

/*

6l is a modified version of the Plan 9 linker.  The original is documented at

	http://plan9.bell-labs.com/magic/man2html/1/2l

Its target architecture is the x86-64, referred to by these tools as amd64.
It reads files in .6 format generated by 6g, 6c, and 6a and emits
a binary called 6.out by default.

Major changes include:
	- support for ELF and Mach-O binary files
	- support for segmented stacks (this feature is implemented here, not in the compilers).

Original options are listed in the link above.

Options new in this version:

-d
	Elide the dynamic linking header.  With this option, the binary
	is statically linked and does not refer to dynld.  Without this option
	(the default), the binary's contents are identical but it is loaded with dynld.
-e
	Emit an extra ELF-compatible symbol table useful with tools such as
	nm, gdb, and oprofile. This option makes the binary file considerably larger.
-Hdarwin
	Write Apple Mach-O binaries (default when $GOOS is darwin)
-Hlinux
	Write Linux ELF binaries (default when $GOOS is linux)
-Hfreebsd
	Write FreeBSD ELF binaries (default when $GOOS is freebsd)
-Hopenbsd
	Write OpenBSD ELF binaries (default when $GOOS is openbsd)
-Hwindows
	Write Windows PE32+ binaries (default when $GOOS is windows)
-I interpreter
	Set the ELF dynamic linker to use.
-L dir1 -L dir2
	Search for libraries (package files) in dir1, dir2, etc.
	The default is the single location $GOROOT/pkg/$GOOS_amd64.
-r dir1:dir2:...
	Set the dynamic linker search path when using ELF.
-V
	Print the linker version.

*/
package documentation
<commit_msg>6l: remove mention of -e flag<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/*

6l is a modified version of the Plan 9 linker.  The original is documented at

	http://plan9.bell-labs.com/magic/man2html/1/2l

Its target architecture is the x86-64, referred to by these tools as amd64.
It reads files in .6 format generated by 6g, 6c, and 6a and emits
a binary called 6.out by default.

Major changes include:
	- support for ELF and Mach-O binary files
	- support for segmented stacks (this feature is implemented here, not in the compilers).

Original options are listed in the link above.

Options new in this version:

-d
	Elide the dynamic linking header.  With this option, the binary
	is statically linked and does not refer to dynld.  Without this option
	(the default), the binary's contents are identical but it is loaded with dynld.
-Hdarwin
	Write Apple Mach-O binaries (default when $GOOS is darwin)
-Hlinux
	Write Linux ELF binaries (default when $GOOS is linux)
-Hfreebsd
	Write FreeBSD ELF binaries (default when $GOOS is freebsd)
-Hopenbsd
	Write OpenBSD ELF binaries (default when $GOOS is openbsd)
-Hwindows
	Write Windows PE32+ binaries (default when $GOOS is windows)
-I interpreter
	Set the ELF dynamic linker to use.
-L dir1 -L dir2
	Search for libraries (package files) in dir1, dir2, etc.
	The default is the single location $GOROOT/pkg/$GOOS_amd64.
-r dir1:dir2:...
	Set the dynamic linker search path when using ELF.
-V
	Print the linker version.

*/
package documentation
<|endoftext|>


<commit_before>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

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

// A BuildScriptGeneratorError is sometimes used by the Generate method on a
// BuildScriptGenerator to return more metadata about an error.
type BuildScriptGeneratorError struct {
	error

	// true when this error can be recovered by retrying later
	Recover bool
}

// A BuildScriptGenerator generates a build script for a given job payload.
type BuildScriptGenerator interface {
	Generate(context.Context, JobPayload) ([]byte, error)
}

type webBuildScriptGenerator struct {
	URL string
}

// NewBuildScriptGenerator creates a generator backed by an HTTP API.
func NewBuildScriptGenerator(URL string) BuildScriptGenerator {
	return &webBuildScriptGenerator{URL: URL}
}

func (g *webBuildScriptGenerator) Generate(ctx context.Context, payload JobPayload) ([]byte, error) {
	b, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}

	buf := bytes.NewBuffer(b)
	resp, err := http.Post(g.URL, "application/json", buf)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 500 {
		return nil, BuildScriptGeneratorError{error: fmt.Errorf("server error: %v", body), Recover: true}
	} else if resp.StatusCode >= 400 {
		return nil, BuildScriptGeneratorError{error: fmt.Errorf("client error: %v", body), Recover: false}
	}

	return body, nil
}
<commit_msg>Format build script API errors as strings, not bytes<commit_after>package lib

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

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

// A BuildScriptGeneratorError is sometimes used by the Generate method on a
// BuildScriptGenerator to return more metadata about an error.
type BuildScriptGeneratorError struct {
	error

	// true when this error can be recovered by retrying later
	Recover bool
}

// A BuildScriptGenerator generates a build script for a given job payload.
type BuildScriptGenerator interface {
	Generate(context.Context, JobPayload) ([]byte, error)
}

type webBuildScriptGenerator struct {
	URL string
}

// NewBuildScriptGenerator creates a generator backed by an HTTP API.
func NewBuildScriptGenerator(URL string) BuildScriptGenerator {
	return &webBuildScriptGenerator{URL: URL}
}

func (g *webBuildScriptGenerator) Generate(ctx context.Context, payload JobPayload) ([]byte, error) {
	b, err := json.Marshal(payload)
	if err != nil {
		return nil, err
	}

	buf := bytes.NewBuffer(b)
	resp, err := http.Post(g.URL, "application/json", buf)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode >= 500 {
		return nil, BuildScriptGeneratorError{error: fmt.Errorf("server error: %q", string(body)), Recover: true}
	} else if resp.StatusCode >= 400 {
		return nil, BuildScriptGeneratorError{error: fmt.Errorf("client error: %q", string(body)), Recover: false}
	}

	return body, nil
}
<|endoftext|>


<commit_before>package krx

import (
	"github.com/ghts/ghts/lib"
	"time"
)

func F임시_지연_시간() time.Duration {
	if 지금 := time.Now(); 지금.Year() == 2021 && 지금.Month() == time.November && 지금.Day() == 18 {
		return time.Hour	// 2021년 11월 16일 수능으로 인해 1시간 순연.
	} else {
		return 0
	}
}

func F한국증시_정규_거래_시간임() bool {
	return f한국증시_거래시간_도우미(9, 0, 15, 20)
}

func F한국증시_동시호가_시간임() bool {
	return F한국증시_장전_동시호가_시간임() || F한국증시_장후_동시호가_시간임()
}

func F한국증시_장전_동시호가_시간임() bool {
	return f한국증시_거래시간_도우미(8, 40, 9, 0)
}

func F한국증시_장후_동시호가_시간임() bool {
	return f한국증시_거래시간_도우미(15, 20, 15, 30)
}

func F한국증시_장전_시간외_종가매매_시간임() bool {
	return f한국증시_거래시간_도우미(8, 30, 8, 40)
}

func F한국증시_장후_시간외_종가매매_시간임() bool {
	return f한국증시_거래시간_도우미(15, 40, 16, 0)
}

func F한국증시_시간외_단일가매매_시간임() bool {
	return f한국증시_거래시간_도우미(16, 0, 18, 0)
}

func F한국증시_정규경쟁대량매매_거래시간임() bool {
	return f한국증시_거래시간_도우미(9, 0, 15, 00)
}

func F한국증시_시간외_대량바스켓매매_거래시간임() bool {
	return f한국증시_거래시간_도우미(15, 40, 18, 0)
}

func F한국증시_ETF_LP_의무_호가제출_시간임() bool {
	// 규정상 9시 5분부터 LP 호가 제출이 시작되지만,
	// 9시 10분이 지나서도 NAV에서 벗어나 심하게 출렁이는 사례가 있으므로, 안전하게 9시 20분부터 시작하는 것으로 설정함.

	return f한국증시_거래시간_도우미(9, 20, 15, 20)
}

func F한국증시_폐장_시간임() bool {
	return !F한국증시_정규_거래_시간임() &&
		!F한국증시_동시호가_시간임() &&
		!F한국증시_장전_시간외_종가매매_시간임() &&
		!F한국증시_장후_시간외_종가매매_시간임() &&
		!F한국증시_시간외_단일가매매_시간임()
}

func f한국증시_거래시간_도우미(시작_시간, 시작_분, 종료_시간, 종료_분 int) bool {
	값 := lib.F금일()
	지금 := time.Now()
	로케일 := 지금.Location()

	시작_시각 := time.Date(값.Year(), 값.Month(), 값.Day(), 시작_시간, 시작_분, 0, 0, 로케일)
	종료_시각 := time.Date(값.Year(), 값.Month(), 값.Day(), 종료_시간, 종료_분, 0, 0, 로케일)

	if int64(F임시_지연_시간()) != 0 {
		시작_시각 = 시작_시각.Add(F임시_지연_시간())
		종료_시각 = 종료_시각.Add(F임시_지연_시간())
	}

	if 지금.After(시작_시각) && 지금.Before(종료_시각) {
		return true
	}

	return false
}

func F대기_한국_시각(시, 분, 초 int) {
	목표_시각 := lib.F2금일_한국_시각_단순형(시, 분, 초).Add(F임시_지연_시간())
	지금 := lib.F지금()

	if 목표_시각.After(지금) {
		대기_시간 := 목표_시각.Sub(지금)
		lib.F대기(대기_시간)
	}
}<commit_msg>krx.F금일_보정_시각() : 임시 지연 시간을 고려한 금일 시각<commit_after>package krx

import (
	"github.com/ghts/ghts/lib"
	"time"
)

func F임시_지연_시간() time.Duration {
	if 지금 := time.Now(); 지금.Year() == 2021 && 지금.Month() == time.November && 지금.Day() == 18 {
		return time.Hour	// 2021년 11월 16일 수능으로 인해 1시간 순연.
	} else {
		return 0
	}
}

func F한국증시_정규_거래_시간임() bool {
	return f한국증시_거래시간_도우미(9, 0, 15, 20)
}

func F한국증시_동시호가_시간임() bool {
	return F한국증시_장전_동시호가_시간임() || F한국증시_장후_동시호가_시간임()
}

func F한국증시_장전_동시호가_시간임() bool {
	return f한국증시_거래시간_도우미(8, 40, 9, 0)
}

func F한국증시_장후_동시호가_시간임() bool {
	return f한국증시_거래시간_도우미(15, 20, 15, 30)
}

func F한국증시_장전_시간외_종가매매_시간임() bool {
	return f한국증시_거래시간_도우미(8, 30, 8, 40)
}

func F한국증시_장후_시간외_종가매매_시간임() bool {
	return f한국증시_거래시간_도우미(15, 40, 16, 0)
}

func F한국증시_시간외_단일가매매_시간임() bool {
	return f한국증시_거래시간_도우미(16, 0, 18, 0)
}

func F한국증시_정규경쟁대량매매_거래시간임() bool {
	return f한국증시_거래시간_도우미(9, 0, 15, 00)
}

func F한국증시_시간외_대량바스켓매매_거래시간임() bool {
	return f한국증시_거래시간_도우미(15, 40, 18, 0)
}

func F한국증시_ETF_LP_의무_호가제출_시간임() bool {
	// 규정상 9시 5분부터 LP 호가 제출이 시작되지만,
	// 9시 10분이 지나서도 NAV에서 벗어나 심하게 출렁이는 사례가 있으므로, 안전하게 9시 20분부터 시작하는 것으로 설정함.

	return f한국증시_거래시간_도우미(9, 20, 15, 20)
}

func F한국증시_폐장_시간임() bool {
	return !F한국증시_정규_거래_시간임() &&
		!F한국증시_동시호가_시간임() &&
		!F한국증시_장전_시간외_종가매매_시간임() &&
		!F한국증시_장후_시간외_종가매매_시간임() &&
		!F한국증시_시간외_단일가매매_시간임()
}

func f한국증시_거래시간_도우미(시작_시간, 시작_분, 종료_시간, 종료_분 int) bool {
	값 := lib.F금일()
	지금 := time.Now()
	로케일 := 지금.Location()

	시작_시각 := time.Date(값.Year(), 값.Month(), 값.Day(), 시작_시간, 시작_분, 0, 0, 로케일)
	종료_시각 := time.Date(값.Year(), 값.Month(), 값.Day(), 종료_시간, 종료_분, 0, 0, 로케일)

	if int64(F임시_지연_시간()) != 0 {
		시작_시각 = 시작_시각.Add(F임시_지연_시간())
		종료_시각 = 종료_시각.Add(F임시_지연_시간())
	}

	if 지금.After(시작_시각) && 지금.Before(종료_시각) {
		return true
	}

	return false
}

func F대기_한국_시각(시, 분, 초 int) {
	목표_시각 := lib.F2금일_한국_시각_단순형(시, 분, 초).Add(F임시_지연_시간())
	지금 := lib.F지금()

	if 목표_시각.After(지금) {
		대기_시간 := 목표_시각.Sub(지금)
		lib.F대기(대기_시간)
	}
}

func F금일_보정_시각(시, 분, 초 int) time.Time {
	return lib.F금일().Add(F임시_지연_시간() + time.Duration(시)*lib.P1시간 + time.Duration(분)*lib.P1분 + time.Duration(초)*lib.P1초)
}<|endoftext|>


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

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
)

var (
	// Cursor is out next token pointer.
	// The language of this command doesn't require much more.
	cursor    int
	arg       []string
	whatIWant = "addr|route"
	l         = log.New(os.Stdout, "ip: ", 0)
)

// the pattern:
// at each level parse off arg[0]. If it matches, continue. If it does not, all error with how far you got, what arg you saw,
// and why it did not work out.

func usage() {
	log.Fatalf("This was fine: '%v', and this was left, '%v', and this was not understood, '%v'; only options are '%v'",
		arg[0:cursor], arg[cursor:], arg[cursor], whatIWant)
}

func adddelip(op, ip, dev string) error {
	addr, network, err := net.ParseCIDR(ip)
	if err != nil {
		l.Fatalf("%v is not in CIDR format: %v", ip, err)
	}
	iface, err := net.InterfaceByName(dev)
	if err != nil {
		l.Fatalf("%v not found: %v", dev, err)
		return err
	}

	switch op {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", ip, dev, err)
		}
	default:
		l.Fatalf("%v is not supported yet", op)
	}
	return nil

}

// in the ip command, turns out 'dev' is a noise word.
// The BNF is not right there either.
// Always make it optional.
func dev() *net.Interface {
	cursor++
	whatIWant = "dev|device name"
	if arg[cursor] == "dev" {
		cursor++
	}
	whatIWant = "device name"
	iface, err := net.InterfaceByName(arg[cursor])
	if err != nil {
		usage()
	}
	return iface
}

func showips() {
	ifaces, err := net.Interfaces()
	if err != nil {
		l.Fatalf("Can't enumerate interfaces? %v", err)
	}
	for _, v := range ifaces {
		addrs, err := v.Addrs()
		if err != nil {
			l.Printf("Can't enumerate addresses")
		}
		l.Printf("%v: %v", v, addrs)
	}
}
func addrip() {
	var err error
	var addr net.IP
	var network *net.IPNet
	if len(arg) == 1 {
		showips()
		return
	}
	cursor++
	whatIWant = "add"
	cmd := arg[cursor]

	switch cmd {
	case "add":
		cursor++
		whatIWant = "CIDR format address"
		addr, network, err = net.ParseCIDR(arg[cursor])
		if err != nil {
			usage()
		}
	default:
		usage()
	}
	iface := dev()
	switch cmd {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", arg[1], arg[2], err)
		}
	default:
		l.Fatalf("devip: arg[0] changed: can't happen")
	}
	return

}

func linkshow() {
	cursor++
	whatIWant = "<nothing>|<device name>"
	if len(arg[cursor:]) == 0 {
		showips()
	}
}

func linkset() {
	iface := dev()
	cursor++
	whatIWant="up|down"
	switch arg[cursor] {
		case "up":
		if err := NetworkLinkUp(iface); err != nil {
			l.Fatalf("%v can't make it up: %v", dev, err)
		}
		case "down":
		if err := NetworkLinkDown(iface); err != nil {
			l.Fatalf("%v can't make it down: %v", dev, err)
		}
		default:
			usage()
	}
}

func link() {
	cursor++
	whatIWant = "show|set"
	cmd := arg[cursor]

	switch cmd {
	case "show":
		linkshow()
	case "set":
		linkset()
	default:
		usage()
	}
	return

}
func main() {
	flag.Parse()
	arg = flag.Args()
	defer func() {
		switch err := recover().(type) {
		case nil:
		case error:
			if strings.Contains(err.Error(), "index out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else if strings.Contains(err.Error(), "slice bounds out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else {
				l.Fatalf("FUCK: %v", err.Error())
			}
			l.Fatalf("Bummer: %v", err)
		default:
			l.Fatalf("unexpected panic value: %T(%v)", err, err)
		}
	}()
	// The ip command doesn't actually follow the BNF it prints on error.
	// There are lots of handy shortcuts that people will expect.
	switch {
	case arg[cursor] == "addr":
		addrip()

	case arg[cursor] == "link":
		link()
	case len(arg) == 1 && arg[0] == "route":
		if b, err := ioutil.ReadFile("/proc/net/route"); err == nil {
			l.Printf("%s", string(b))
		} else {
			l.Fatalf("Route failed: %v", err)
		}
	case len(arg) == 7 && arg[0] == "route" && arg[1] == "add" && arg[2] == "default" && arg[3] == "via" && arg[5] == "dev":
		AddDefaultGw(arg[4], arg[6])
	default:
		l.Fatalf("We don't do this: %v; try addr or link or route", arg)
	}
}
<commit_msg>Well, really ugly bits are gone. Now there's just ugly bits.<commit_after>// Copyright 2012 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"flag"
	"io/ioutil"
	"log"
	"net"
	"os"
	"strings"
)

// you will notice that I suck at parsers. That said, here is the method to my madness.
// The language of ip is not super consistent and has lots of convenience shortcuts.
// The BNF it shows you doesn't show them.
// The inputs is just the set of args.
// It's very short.
// Each token is just a string and we need not produce terminals with them -- they can
// just be the terminals and we can switch on them.
// The cursor is always our current token pointer. We do a dumb recursive descent parser
// and accumulate information into a global set of variables. At any point we can see into the
// whole set of args and see where we are. We can indicate at each point what we're expecting so
// that in usage() or recover() we can tell the user exactly what we wanted, unlike IP,
// which just barfs a whole (incorrect) BNF at you when you do anything wrong.
// To handle errors in too few arguments, we just do a recover block. That lets us blindly
// reference the arg[] array without having to check the length everywhere.

var (
	// Cursor is out next token pointer.
	// The language of this command doesn't require much more.
	cursor    int
	arg       []string
	whatIWant = "addr|route|link"
	l         = log.New(os.Stdout, "ip: ", 0)
)

// the pattern:
// at each level parse off arg[0]. If it matches, continue. If it does not, all error with how far you got, what arg you saw,
// and why it did not work out.

func usage() {
	log.Fatalf("This was fine: '%v', and this was left, '%v', and this was not understood, '%v'; only options are '%v'",
		arg[0:cursor], arg[cursor:], arg[cursor], whatIWant)
}

func adddelip(op, ip, dev string) error {
	addr, network, err := net.ParseCIDR(ip)
	if err != nil {
		l.Fatalf("%v is not in CIDR format: %v", ip, err)
	}
	iface, err := net.InterfaceByName(dev)
	if err != nil {
		l.Fatalf("%v not found: %v", dev, err)
		return err
	}

	switch op {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", ip, dev, err)
		}
	default:
		l.Fatalf("%v is not supported yet", op)
	}
	return nil

}

// in the ip command, turns out 'dev' is a noise word.
// The BNF is not right there either.
// Always make it optional.
func dev() *net.Interface {
	cursor++
	whatIWant = "dev|device name"
	if arg[cursor] == "dev" {
		cursor++
	}
	whatIWant = "device name"
	iface, err := net.InterfaceByName(arg[cursor])
	if err != nil {
		usage()
	}
	return iface
}

func showips() {
	ifaces, err := net.Interfaces()
	if err != nil {
		l.Fatalf("Can't enumerate interfaces? %v", err)
	}
	for _, v := range ifaces {
		addrs, err := v.Addrs()
		if err != nil {
			l.Printf("Can't enumerate addresses")
		}
		l.Printf("%v: %v", v, addrs)
	}
}
func addrip() {
	var err error
	var addr net.IP
	var network *net.IPNet
	if len(arg) == 1 {
		showips()
		return
	}
	cursor++
	whatIWant = "add"
	cmd := arg[cursor]

	switch cmd {
	case "add":
		cursor++
		whatIWant = "CIDR format address"
		addr, network, err = net.ParseCIDR(arg[cursor])
		if err != nil {
			usage()
		}
	default:
		usage()
	}
	iface := dev()
	switch cmd {
	case "add":
		if err := NetworkLinkAddIp(iface, addr, network); err != nil {
			l.Fatalf("Adding %v to %v failed: %v", arg[1], arg[2], err)
		}
	default:
		l.Fatalf("devip: arg[0] changed: can't happen")
	}
	return

}

func linkshow() {
	cursor++
	whatIWant = "<nothing>|<device name>"
	if len(arg[cursor:]) == 0 {
		showips()
	}
}

func linkset() {
	iface := dev()
	cursor++
	whatIWant="up|down"
	switch arg[cursor] {
		case "up":
		if err := NetworkLinkUp(iface); err != nil {
			l.Fatalf("%v can't make it up: %v", dev, err)
		}
		case "down":
		if err := NetworkLinkDown(iface); err != nil {
			l.Fatalf("%v can't make it down: %v", dev, err)
		}
		default:
			usage()
	}
}

func link() {
	cursor++
	whatIWant = "show|set"
	cmd := arg[cursor]

	switch cmd {
	case "show":
		linkshow()
	case "set":
		linkset()
	default:
		usage()
	}
	return
}

func routeshow() {
	if b, err := ioutil.ReadFile("/proc/net/route"); err == nil {
		l.Printf("%s", string(b))
	} else {
		l.Fatalf("Route show failed: %v", err)
	}
}
func nodespec() string {
	cursor++
	whatIWant = "default|CIDR"
	return arg[cursor]
}

func nexthop() (string, string) {
	cursor++
	whatIWant = "via"
	if arg[cursor] != "via" {
		usage()
	}
	nh := arg[cursor]
	cursor++
	whatIWant = "Gateway CIDR"
	return nh, arg[cursor]
}

func routeadddefault() {
	nh,nhval := nexthop()
	// TODO: NHFLAGS.
	d := dev()
	switch nh {
	case "via":
		l.Printf("Add default route %v via %v", nhval, d)
		AddDefaultGw(nhval, d.Name)
	default:
		usage()
	}
}

func routeadd() {
	ns := nodespec()
	switch(ns) {
		case "default":
		routeadddefault()
	default:
		usage()
	}
}

func route() {
	cursor++
	if len(arg[cursor:]) == 0 {
		routeshow()
		return
	}

	whatIWant = "show|add"
	switch arg[cursor] {
		case "show":
			routeshow()
		case "add":
			routeadd()
		default:
			usage()
	}

}
func main() {
	flag.Parse()
	arg = flag.Args()
	defer func() {
		switch err := recover().(type) {
		case nil:
		case error:
			if strings.Contains(err.Error(), "index out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else if strings.Contains(err.Error(), "slice bounds out of range") {
				l.Fatalf("Args: %v, I got to arg %v, I wanted %v after that", arg, cursor, whatIWant)
			} else {
				l.Fatalf("FUCK: %v", err.Error())
			}
			l.Fatalf("Bummer: %v", err)
		default:
			l.Fatalf("unexpected panic value: %T(%v)", err, err)
		}
	}()
	// The ip command doesn't actually follow the BNF it prints on error.
	// There are lots of handy shortcuts that people will expect.
	switch arg[cursor]{
	case "addr":
		addrip()
	case "link":
		link()
	case "route":
		route()
	default:
		usage()
	}
}
<|endoftext|>