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

/*
 * Borrows ideas and code from https://github.com/bradfitz/gitbrute/blob/master/gitbrute.go
 */

package core

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/s3git/s3git-go/internal/cas"
	"github.com/s3git/s3git-go/internal/kv"
	"github.com/codahale/blake2"
	"strings"
	"runtime"
	"errors"
)

type prefixObject struct {
	coreObject
	S3gitFollowMe string `json:"s3gitFollowMe"`
	S3gitMagic    string `json:"s3gitMagic"`
}

func makePrefixObject(followMeHash string) *prefixObject {
	po := prefixObject{coreObject: coreObject{S3gitVersion: 1, S3gitType: kv.PREFIX}, S3gitFollowMe: followMeHash}
	return &po
}

var (
	//
	// With the additional constraint that objects are a multiple of 64 in size, this
	// adds 6 bits to the chance of getting a regular blob in the range, see table:
	//
	// PREFIX-SIZE | NR BITS | OCCURRENCE (MLD)
	//           5 |    20+6 |             0.1
	//           6 |    24+6 |             1.1
	//           7 |    28+6 |            17.2
	//           8 |    32+6 |             275
	//
	prefixChar  = '0'
	prefixNum   = 7
	prefixCheat = 3		// Number that is cheated in the prefix, like 0000xxx00000 -- will fail in file check mode
)

func Prefix() string {
	return strings.Repeat(string(prefixChar), prefixNum)
}

// Get prefix object based on hash
func GetPrefixObject(hash string) (*prefixObject, error) {

	s, err := readBlob(hash)
	if err != nil {
		return nil, err
	}

	return GetPrefixObjectFromString(s)
}

// Get prefix object from string contents
func GetPrefixObjectFromString(s string) (*prefixObject, error) {
	dec := json.NewDecoder(strings.NewReader(s))
	var po prefixObject
	if err := dec.Decode(&po); err != nil {
		return nil, err
	}

	return &po, nil
}

func StorePrefixObject(commitHash string) error {

	// Mine for proper start of the hash
	prefixJson, err := mine(commitHash)
	if err != nil {
		return err
	}

	// And save prefix object to disk
	cw := cas.MakeWriterInCheatMode(kv.PREFIX)
	fmt.Fprint(cw, prefixJson)

	// Flush the stream (and ignore hash)
	_, _, _, err = cw.Flush()
	if err != nil {
		return err
	}

	return nil
}

func mine(followMeHash string) (string, error) {

	prefixObject := makePrefixObject(followMeHash)

	buf := new(bytes.Buffer)

	encoder := json.NewEncoder(buf)
	if err := encoder.Encode(prefixObject); err != nil {
		return "", err
	}

	obj := buf.String()
	magiclength := cas.KeySize - (len(obj) % cas.KeySize)

	if magiclength < cas.KeySize*3/4 { // Round up size of magic to next multiple of KeySize in case it is too short
		magiclength += cas.KeySize
	}
	possibilities := make(chan try, 512)
	done := make(chan struct{})
	go explore(possibilities, magiclength, done)

	winner := make(chan solution)

	for i := 0; i < int(runtime.NumCPU()); i++ {
		go bruteForce(obj, winner, possibilities, done)
	}

	w := <-winner
	close(done)

	return string(w.blob[:]), nil
}

func bruteForce(obj string, winner chan<- solution, possibilities <-chan try, done <-chan struct{}) {

	// blob is the blob to mutate in-place repeatedly while testing
	// whether we have a match.

	if len(obj) > cas.ChunkSize {
		// TODO: Need to split up stream in chunks
		panic(errors.New("Commit object too long"))
	}
	leafHash := blake2.New(&blake2.Config{Size: 64, Tree: &blake2.Tree{Fanout: 0, MaxDepth: 2, LeafSize: cas.ChunkSize, NodeOffset: 0, NodeDepth: 0, InnerHashSize: 64, IsLastNode: true}})
	rootHash := blake2.New(&blake2.Config{Size: 64, Tree: &blake2.Tree{Fanout: 0, MaxDepth: 2, LeafSize: cas.ChunkSize, NodeOffset: 0, NodeDepth: 1, InnerHashSize: 64, IsLastNode: true}})
	wantHexPrefix := []byte(Prefix())[:prefixNum-prefixCheat]
	hexBuf := make([]byte, 0, cas.KeySizeHex)

	input := obj

	for t := range possibilities {
		select {
		case <-done:
			return
		default:
		// Round up blob size to multiple of 64 bytes
			blob := []byte(fmt.Sprintf("%s%s%s", input[:len(input)-3], t.magic, input[len(input)-3:]))

			leafHash.Reset()
			leafHash.Write(blob)
			digest := leafHash.Sum(hexBuf[:0])

			rootHash.Reset()
			rootHash.Write(digest)
			digest = rootHash.Sum(hexBuf[:0])

			hip := hexInPlace(digest)
			if !bytes.HasPrefix(hip, wantHexPrefix) ||
			rune(hip[prefixNum]) == prefixChar {			// Prevent accidentally gaining one 'free/by chance' extra level (so that we can safely distinguish between different lengths of prefixes)
				continue
			}

			winner <- solution{blob}
			return
		}
	}
}

// hexInPlace takes a slice of binary data and returns the same slice with double
// its length, hex-ified in-place.
func hexInPlace(v []byte) []byte {
	const hex = "0123456789abcdef"
	h := v[:len(v)*2]
	for i := len(v) - 1; i >= 0; i-- {
		b := v[i]
		h[i*2+0] = hex[b>>4]
		h[i*2+1] = hex[b&0xf]
	}
	return h
}

// try is a magic number looking for a matching prefix.
type try struct {
	magic string
}

// explore magic numbers
func explore(c chan<- try, magiclength int, done <-chan struct{}) {

	for magic := uint64(0); true; magic++ {
		select {
		case <-done:
			return
		default:
			c <- try{magicToStr(magic, magiclength)}
		}
	}
}

const letterBytes = "0123456789"

func magicToStr(magic uint64, n int) string {

	b := make([]byte, n)

	for i := n - 1; i >= 0; i-- {
		b[i] = letterBytes[int(magic%uint64(len(letterBytes)))]
		magic /= uint64(len(letterBytes))
	}

	return string(b[:])
}

type solution struct {
	blob []byte
}
<commit_msg>Remove TODO warning about computing prefixes for objects that are too large and add comment.<commit_after>/*
 * Copyright 2016 Frank Wessels <fwessels@xs4all.nl>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Borrows ideas and code from https://github.com/bradfitz/gitbrute/blob/master/gitbrute.go
 */

package core

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/s3git/s3git-go/internal/cas"
	"github.com/s3git/s3git-go/internal/kv"
	"github.com/codahale/blake2"
	"strings"
	"runtime"
	"errors"
)

type prefixObject struct {
	coreObject
	S3gitFollowMe string `json:"s3gitFollowMe"`
	S3gitMagic    string `json:"s3gitMagic"`
}

func makePrefixObject(followMeHash string) *prefixObject {
	po := prefixObject{coreObject: coreObject{S3gitVersion: 1, S3gitType: kv.PREFIX}, S3gitFollowMe: followMeHash}
	return &po
}

var (
	//
	// With the additional constraint that objects are a multiple of 64 in size, this
	// adds 6 bits to the chance of getting a regular blob in the range, see table:
	//
	// PREFIX-SIZE | NR BITS | OCCURRENCE (MLD)
	//           5 |    20+6 |             0.1
	//           6 |    24+6 |             1.1
	//           7 |    28+6 |            17.2
	//           8 |    32+6 |             275
	//
	prefixChar  = '0'
	prefixNum   = 7
	prefixCheat = 3		// Number that is cheated in the prefix, like 0000xxx00000 -- will fail in file check mode
)

func Prefix() string {
	return strings.Repeat(string(prefixChar), prefixNum)
}

// Get prefix object based on hash
func GetPrefixObject(hash string) (*prefixObject, error) {

	s, err := readBlob(hash)
	if err != nil {
		return nil, err
	}

	return GetPrefixObjectFromString(s)
}

// Get prefix object from string contents
func GetPrefixObjectFromString(s string) (*prefixObject, error) {
	dec := json.NewDecoder(strings.NewReader(s))
	var po prefixObject
	if err := dec.Decode(&po); err != nil {
		return nil, err
	}

	return &po, nil
}

func StorePrefixObject(commitHash string) error {

	// Mine for proper start of the hash
	prefixJson, err := mine(commitHash)
	if err != nil {
		return err
	}

	// And save prefix object to disk
	cw := cas.MakeWriterInCheatMode(kv.PREFIX)
	fmt.Fprint(cw, prefixJson)

	// Flush the stream (and ignore hash)
	_, _, _, err = cw.Flush()
	if err != nil {
		return err
	}

	return nil
}

func mine(followMeHash string) (string, error) {

	prefixObject := makePrefixObject(followMeHash)

	buf := new(bytes.Buffer)

	encoder := json.NewEncoder(buf)
	if err := encoder.Encode(prefixObject); err != nil {
		return "", err
	}

	obj := buf.String()
	magiclength := cas.KeySize - (len(obj) % cas.KeySize)

	if magiclength < cas.KeySize*3/4 { // Round up size of magic to next multiple of KeySize in case it is too short
		magiclength += cas.KeySize
	}
	possibilities := make(chan try, 512)
	done := make(chan struct{})
	go explore(possibilities, magiclength, done)

	winner := make(chan solution)

	for i := 0; i < int(runtime.NumCPU()); i++ {
		go bruteForce(obj, winner, possibilities, done)
	}

	w := <-winner
	close(done)

	return string(w.blob[:]), nil
}

func bruteForce(obj string, winner chan<- solution, possibilities <-chan try, done <-chan struct{}) {

	// blob is the blob to mutate in-place repeatedly while testing
	// whether we have a match.

	if len(obj) > cas.ChunkSize {
		// You don't want to compute prefixes for objects that are too large
		// (if so split off another object of different type and point to it)
		panic(errors.New("Object to prefix with hash is too large"))
	}
	leafHash := blake2.New(&blake2.Config{Size: 64, Tree: &blake2.Tree{Fanout: 0, MaxDepth: 2, LeafSize: cas.ChunkSize, NodeOffset: 0, NodeDepth: 0, InnerHashSize: 64, IsLastNode: true}})
	rootHash := blake2.New(&blake2.Config{Size: 64, Tree: &blake2.Tree{Fanout: 0, MaxDepth: 2, LeafSize: cas.ChunkSize, NodeOffset: 0, NodeDepth: 1, InnerHashSize: 64, IsLastNode: true}})
	wantHexPrefix := []byte(Prefix())[:prefixNum-prefixCheat]
	hexBuf := make([]byte, 0, cas.KeySizeHex)

	input := obj

	for t := range possibilities {
		select {
		case <-done:
			return
		default:
		// Round up blob size to multiple of 64 bytes
			blob := []byte(fmt.Sprintf("%s%s%s", input[:len(input)-3], t.magic, input[len(input)-3:]))

			leafHash.Reset()
			leafHash.Write(blob)
			digest := leafHash.Sum(hexBuf[:0])

			rootHash.Reset()
			rootHash.Write(digest)
			digest = rootHash.Sum(hexBuf[:0])

			hip := hexInPlace(digest)
			if !bytes.HasPrefix(hip, wantHexPrefix) ||
			rune(hip[prefixNum]) == prefixChar {			// Prevent accidentally gaining one 'free/by chance' extra level (so that we can safely distinguish between different lengths of prefixes)
				continue
			}

			winner <- solution{blob}
			return
		}
	}
}

// hexInPlace takes a slice of binary data and returns the same slice with double
// its length, hex-ified in-place.
func hexInPlace(v []byte) []byte {
	const hex = "0123456789abcdef"
	h := v[:len(v)*2]
	for i := len(v) - 1; i >= 0; i-- {
		b := v[i]
		h[i*2+0] = hex[b>>4]
		h[i*2+1] = hex[b&0xf]
	}
	return h
}

// try is a magic number looking for a matching prefix.
type try struct {
	magic string
}

// explore magic numbers
func explore(c chan<- try, magiclength int, done <-chan struct{}) {

	for magic := uint64(0); true; magic++ {
		select {
		case <-done:
			return
		default:
			c <- try{magicToStr(magic, magiclength)}
		}
	}
}

const letterBytes = "0123456789"

func magicToStr(magic uint64, n int) string {

	b := make([]byte, n)

	for i := n - 1; i >= 0; i-- {
		b[i] = letterBytes[int(magic%uint64(len(letterBytes)))]
		magic /= uint64(len(letterBytes))
	}

	return string(b[:])
}

type solution struct {
	blob []byte
}
<|endoftext|>


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

package relui

import (
	"context"
	"database/sql"
	"errors"
	"fmt"

	"github.com/golang-migrate/migrate/v4"
	dbpgx "github.com/golang-migrate/migrate/v4/database/pgx"
	"github.com/golang-migrate/migrate/v4/source/iofs"
	"github.com/jackc/pgx/v4"
)

var errDBNotExist = errors.New("database does not exist")

// InitDB creates and applies all migrations to the database specified
// in conn.
//
// If the database does not exist, one will be created using the
// credentials provided.
//
// Any key/value or URI string compatible with libpq is valid.
func InitDB(ctx context.Context, conn string) error {
	cfg, err := pgx.ParseConfig(conn)
	if err != nil {
		return fmt.Errorf("pgx.ParseConfig() = %w", err)
	}
	if err := CreateDBIfNotExists(ctx, cfg); err != nil {
		return err
	}
	if err := MigrateDB(conn, false); err != nil {
		return err
	}
	return nil
}

// MigrateDB applies all migrations to the database specified in conn.
//
// Any key/value or URI string compatible with libpq is a valid conn.
// If downUp is true, all migrations will be run, then the down and up
// migrations of the final migration are run.
func MigrateDB(conn string, downUp bool) error {
	cfg, err := pgx.ParseConfig(conn)
	if err != nil {
		return fmt.Errorf("pgx.ParseConfig() = %w", err)
	}
	db, err := sql.Open("pgx", conn)
	if err != nil {
		return fmt.Errorf("sql.Open(%q, _) = %v, %w", "pgx", db, err)
	}
	mcfg := &dbpgx.Config{
		MigrationsTable: "migrations",
		DatabaseName:    cfg.Database,
	}
	mdb, err := dbpgx.WithInstance(db, mcfg)
	if err != nil {
		return fmt.Errorf("dbpgx.WithInstance(_, %v) = %v, %w", mcfg, mdb, err)
	}
	mfs, err := iofs.New(migrations, "migrations")
	if err != nil {
		return fmt.Errorf("iofs.New(%v, %q) = %v, %w", migrations, "migrations", mfs, err)
	}
	m, err := migrate.NewWithInstance("iofs", mfs, "pgx", mdb)
	if err != nil {
		return fmt.Errorf("migrate.NewWithInstance(%q, %v, %q, %v) = %v, %w", "iofs", migrations, "pgx", mdb, m, err)
	}
	if err := m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
		return fmt.Errorf("m.Up() = %w", err)
	}
	if downUp {
		if err := m.Steps(-1); err != nil {
			return fmt.Errorf("m.Steps(%d) = %w", -1, err)
		}
		if err := m.Up(); err != nil {
			return fmt.Errorf("m.Up() = %w", err)
		}
	}
	db.Close()
	return nil
}

// ConnectMaintenanceDB connects to the maintenance database using the
// credentials from cfg. If maintDB is an empty string, the database
// with the name cfg.User will be used.
func ConnectMaintenanceDB(ctx context.Context, cfg *pgx.ConnConfig, maintDB string) (*pgx.Conn, error) {
	cfg = cfg.Copy()
	cfg.Database = maintDB
	return pgx.ConnectConfig(ctx, cfg)
}

// CreateDBIfNotExists checks whether the given dbName is an existing
// database, and creates one if not.
func CreateDBIfNotExists(ctx context.Context, cfg *pgx.ConnConfig) error {
	exists, err := checkIfDBExists(ctx, cfg)
	if err != nil || exists {
		return err
	}
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	createSQL := fmt.Sprintf("CREATE DATABASE %s", pgx.Identifier{cfg.Database}.Sanitize())
	if _, err := conn.Exec(ctx, createSQL); err != nil {
		return fmt.Errorf("conn.Exec(%q) = %w", createSQL, err)
	}
	return nil
}

// DropDB drops the database specified in cfg. An error returned if
// the database does not exist.
func DropDB(ctx context.Context, cfg *pgx.ConnConfig) error {
	exists, err := checkIfDBExists(ctx, cfg)
	if err != nil {
		return fmt.Errorf("p.checkIfDBExists() = %w", err)
	}
	if !exists {
		return errDBNotExist
	}
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	dropSQL := fmt.Sprintf("DROP DATABASE %s", pgx.Identifier{cfg.Database}.Sanitize())
	if _, err := conn.Exec(ctx, dropSQL); err != nil {
		return fmt.Errorf("conn.Exec(%q) = %w", dropSQL, err)
	}
	return nil
}

func checkIfDBExists(ctx context.Context, cfg *pgx.ConnConfig) (bool, error) {
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return false, fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	row := conn.QueryRow(ctx, "SELECT 1 from pg_database WHERE datname=$1 LIMIT 1", cfg.Database)
	var exists int
	if err := row.Scan(&exists); err != nil && err != pgx.ErrNoRows {
		return false, fmt.Errorf("row.Scan() = %w", err)
	}
	return exists == 1, nil
}
<commit_msg>internal/relui: default to postgres as maintdb<commit_after>// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package relui

import (
	"context"
	"database/sql"
	"errors"
	"fmt"

	"github.com/golang-migrate/migrate/v4"
	dbpgx "github.com/golang-migrate/migrate/v4/database/pgx"
	"github.com/golang-migrate/migrate/v4/source/iofs"
	"github.com/jackc/pgx/v4"
)

var errDBNotExist = errors.New("database does not exist")

// InitDB creates and applies all migrations to the database specified
// in conn.
//
// If the database does not exist, one will be created using the
// credentials provided.
//
// Any key/value or URI string compatible with libpq is valid.
func InitDB(ctx context.Context, conn string) error {
	cfg, err := pgx.ParseConfig(conn)
	if err != nil {
		return fmt.Errorf("pgx.ParseConfig() = %w", err)
	}
	if err := CreateDBIfNotExists(ctx, cfg); err != nil {
		return err
	}
	if err := MigrateDB(conn, false); err != nil {
		return err
	}
	return nil
}

// MigrateDB applies all migrations to the database specified in conn.
//
// Any key/value or URI string compatible with libpq is a valid conn.
// If downUp is true, all migrations will be run, then the down and up
// migrations of the final migration are run.
func MigrateDB(conn string, downUp bool) error {
	cfg, err := pgx.ParseConfig(conn)
	if err != nil {
		return fmt.Errorf("pgx.ParseConfig() = %w", err)
	}
	db, err := sql.Open("pgx", conn)
	if err != nil {
		return fmt.Errorf("sql.Open(%q, _) = %v, %w", "pgx", db, err)
	}
	mcfg := &dbpgx.Config{
		MigrationsTable: "migrations",
		DatabaseName:    cfg.Database,
	}
	mdb, err := dbpgx.WithInstance(db, mcfg)
	if err != nil {
		return fmt.Errorf("dbpgx.WithInstance(_, %v) = %v, %w", mcfg, mdb, err)
	}
	mfs, err := iofs.New(migrations, "migrations")
	if err != nil {
		return fmt.Errorf("iofs.New(%v, %q) = %v, %w", migrations, "migrations", mfs, err)
	}
	m, err := migrate.NewWithInstance("iofs", mfs, "pgx", mdb)
	if err != nil {
		return fmt.Errorf("migrate.NewWithInstance(%q, %v, %q, %v) = %v, %w", "iofs", migrations, "pgx", mdb, m, err)
	}
	if err := m.Up(); err != nil && !errors.Is(err, migrate.ErrNoChange) {
		return fmt.Errorf("m.Up() = %w", err)
	}
	if downUp {
		if err := m.Steps(-1); err != nil {
			return fmt.Errorf("m.Steps(%d) = %w", -1, err)
		}
		if err := m.Up(); err != nil {
			return fmt.Errorf("m.Up() = %w", err)
		}
	}
	db.Close()
	return nil
}

// ConnectMaintenanceDB connects to the maintenance database using the
// credentials from cfg. If maintDB is an empty string, the database
// with the name cfg.User will be used.
func ConnectMaintenanceDB(ctx context.Context, cfg *pgx.ConnConfig, maintDB string) (*pgx.Conn, error) {
	cfg = cfg.Copy()
	if maintDB == "" {
		maintDB = "postgres"
	}
	cfg.Database = maintDB
	return pgx.ConnectConfig(ctx, cfg)
}

// CreateDBIfNotExists checks whether the given dbName is an existing
// database, and creates one if not.
func CreateDBIfNotExists(ctx context.Context, cfg *pgx.ConnConfig) error {
	exists, err := checkIfDBExists(ctx, cfg)
	if err != nil || exists {
		return err
	}
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	createSQL := fmt.Sprintf("CREATE DATABASE %s", pgx.Identifier{cfg.Database}.Sanitize())
	if _, err := conn.Exec(ctx, createSQL); err != nil {
		return fmt.Errorf("conn.Exec(%q) = %w", createSQL, err)
	}
	return nil
}

// DropDB drops the database specified in cfg. An error returned if
// the database does not exist.
func DropDB(ctx context.Context, cfg *pgx.ConnConfig) error {
	exists, err := checkIfDBExists(ctx, cfg)
	if err != nil {
		return fmt.Errorf("p.checkIfDBExists() = %w", err)
	}
	if !exists {
		return errDBNotExist
	}
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	dropSQL := fmt.Sprintf("DROP DATABASE %s", pgx.Identifier{cfg.Database}.Sanitize())
	if _, err := conn.Exec(ctx, dropSQL); err != nil {
		return fmt.Errorf("conn.Exec(%q) = %w", dropSQL, err)
	}
	return nil
}

func checkIfDBExists(ctx context.Context, cfg *pgx.ConnConfig) (bool, error) {
	conn, err := ConnectMaintenanceDB(ctx, cfg, "")
	if err != nil {
		return false, fmt.Errorf("ConnectMaintenanceDB = %w", err)
	}
	row := conn.QueryRow(ctx, "SELECT 1 from pg_database WHERE datname=$1 LIMIT 1", cfg.Database)
	var exists int
	if err := row.Scan(&exists); err != nil && err != pgx.ErrNoRows {
		return false, fmt.Errorf("row.Scan() = %w", err)
	}
	return exists == 1, nil
}
<|endoftext|>


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

package setup

import (
	"context"
	"fmt"

	"github.com/google/exposure-notifications-server/internal/authorizedapp"
	"github.com/google/exposure-notifications-server/internal/database"
	"github.com/google/exposure-notifications-server/internal/envconfig"
	"github.com/google/exposure-notifications-server/internal/logging"
	"github.com/google/exposure-notifications-server/internal/metrics"
	"github.com/google/exposure-notifications-server/internal/secrets"
	"github.com/google/exposure-notifications-server/internal/serverenv"
	"github.com/google/exposure-notifications-server/internal/signing"
	"github.com/google/exposure-notifications-server/internal/storage"
)

// BlobstoreConfigProvider provides the information about current storage
// configuration.
type BlobstoreConfigProvider interface {
	BlobstoreConfig() *storage.Config
}

// DatabaseConfigProvider ensures that the environment config can provide a DB config.
// All binaries in this application connect to the database via the same method.
type DatabaseConfigProvider interface {
	DatabaseConfig() *database.Config
}

// AuthorizedAppConfigProvider signals that the config provided knows how to
// configure authorized apps.
type AuthorizedAppConfigProvider interface {
	AuthorizedAppConfig() *authorizedapp.Config
}

// KeyManagerConfigProvider is a marker interface indicating the key manager
// should be installed.
type KeyManagerConfigProvider interface {
	KeyManagerConfig() *signing.Config
}

// SecretManagerConfigProvider signals that the config knows how to configure a
// secret manager.
type SecretManagerConfigProvider interface {
	SecretManagerConfig() *secrets.Config
}

// Function returned from setup to be deferred until the caller exits.
type Defer func()

// Setup runs common initialization code for all servers.
func Setup(ctx context.Context, config DatabaseConfigProvider) (*serverenv.ServerEnv, Defer, error) {
	logger := logging.FromContext(ctx)

	// Load the secret manager - this needs to be loaded first because other
	// processors may require access to secrets.
	var sm secrets.SecretManager
	if provider, ok := config.(SecretManagerConfigProvider); ok {
		smConfig := provider.SecretManagerConfig()
		if err := envconfig.Process(ctx, smConfig, nil); err != nil {
			return nil, nil, fmt.Errorf("unable to process secret manager environment: %w", err)
		}

		var err error
		sm, err = secrets.SecretManagerFor(ctx, smConfig.SecretManagerType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to secret manager: %w", err)
		}

		// Enable caching, if provided.
		if ttl := smConfig.SecretCacheTTL; ttl > 0 {
			sm = secrets.WrapCacher(ctx, sm, ttl)
		}
	}

	// Process first round of environment variables.
	if err := envconfig.Process(ctx, config, sm); err != nil {
		return nil, nil, fmt.Errorf("error loading environment variables: %w", err)
	}
	logger.Infof("Effective environment variables: %+v", config)

	// Start building serverenv opts
	opts := []serverenv.Option{
		serverenv.WithSecretManager(sm),
		serverenv.WithMetricsExporter(metrics.NewLogsBasedFromContext),
	}

	// Configure key management for signing.
	if provider, ok := config.(KeyManagerConfigProvider); ok {
		kmConfig := provider.KeyManagerConfig()
		km, err := signing.KeyManagerFor(ctx, kmConfig.KeyManagerType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to key manager: %w", err)
		}
		opts = append(opts, serverenv.WithKeyManager(km))
	}

	// Configure blob storage.
	if provider, ok := config.(BlobstoreConfigProvider); ok {
		bsConfig := provider.BlobstoreConfig()
		blobStore, err := storage.BlobstoreFor(ctx, bsConfig.BlobstoreType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to storage system: %v", err)
		}
		blobStorage := serverenv.WithBlobStorage(blobStore)
		opts = append(opts, blobStorage)
	}

	// Setup the database connection.
	db, err := database.NewFromEnv(ctx, config.DatabaseConfig())
	if err != nil {
		return nil, nil, fmt.Errorf("unable to connect to database: %v", err)
	}
	{
		// Log the database config, but omit the password field.
		redactedDB := config.DatabaseConfig()
		redactedDB.Password = "<hidden>"
		logger.Infof("Effective DB config: %+v", redactedDB)
	}
	opts = append(opts, serverenv.WithDatabase(db))

	// AuthorizedApp must come after database setup due to the dependency.
	if typ, ok := config.(AuthorizedAppConfigProvider); ok {
		logger.Infof("Effective AuthorizedApp config: %+v", typ.AuthorizedAppConfig())
		provider, err := authorizedapp.NewDatabaseProvider(ctx, db, typ.AuthorizedAppConfig(), authorizedapp.WithSecretManager(sm))
		if err != nil {
			// Ensure the database is closed on an error.
			defer db.Close(ctx)
			return nil, nil, fmt.Errorf("unable to create AuthorizedApp provider: %v", err)
		}
		opts = append(opts, serverenv.WithAuthorizedAppProvider(provider))
	}

	return serverenv.New(ctx, opts...), func() { db.Close(ctx) }, nil
}
<commit_msg>Document why we process the envconfig (#500)<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package setup

import (
	"context"
	"fmt"

	"github.com/google/exposure-notifications-server/internal/authorizedapp"
	"github.com/google/exposure-notifications-server/internal/database"
	"github.com/google/exposure-notifications-server/internal/envconfig"
	"github.com/google/exposure-notifications-server/internal/logging"
	"github.com/google/exposure-notifications-server/internal/metrics"
	"github.com/google/exposure-notifications-server/internal/secrets"
	"github.com/google/exposure-notifications-server/internal/serverenv"
	"github.com/google/exposure-notifications-server/internal/signing"
	"github.com/google/exposure-notifications-server/internal/storage"
)

// BlobstoreConfigProvider provides the information about current storage
// configuration.
type BlobstoreConfigProvider interface {
	BlobstoreConfig() *storage.Config
}

// DatabaseConfigProvider ensures that the environment config can provide a DB config.
// All binaries in this application connect to the database via the same method.
type DatabaseConfigProvider interface {
	DatabaseConfig() *database.Config
}

// AuthorizedAppConfigProvider signals that the config provided knows how to
// configure authorized apps.
type AuthorizedAppConfigProvider interface {
	AuthorizedAppConfig() *authorizedapp.Config
}

// KeyManagerConfigProvider is a marker interface indicating the key manager
// should be installed.
type KeyManagerConfigProvider interface {
	KeyManagerConfig() *signing.Config
}

// SecretManagerConfigProvider signals that the config knows how to configure a
// secret manager.
type SecretManagerConfigProvider interface {
	SecretManagerConfig() *secrets.Config
}

// Function returned from setup to be deferred until the caller exits.
type Defer func()

// Setup runs common initialization code for all servers.
func Setup(ctx context.Context, config DatabaseConfigProvider) (*serverenv.ServerEnv, Defer, error) {
	logger := logging.FromContext(ctx)

	// Load the secret manager - this needs to be loaded first because other
	// processors may require access to secrets.
	var sm secrets.SecretManager
	if provider, ok := config.(SecretManagerConfigProvider); ok {
		// The environment configuration defines which secret manager to use, so we
		// need to process the envconfig in here. Once we know which secret manager
		// to use, we can load the secret manager and then re-process (later) any
		// secret:// references.
		//
		// NOTE: it is not possible to specify which secret manager to use via a
		// secret:// reference. This configuration option must always be the
		// plaintext string.
		smConfig := provider.SecretManagerConfig()
		if err := envconfig.Process(ctx, smConfig, nil); err != nil {
			return nil, nil, fmt.Errorf("unable to process secret manager environment: %w", err)
		}

		var err error
		sm, err = secrets.SecretManagerFor(ctx, smConfig.SecretManagerType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to secret manager: %w", err)
		}

		// Enable caching, if provided.
		if ttl := smConfig.SecretCacheTTL; ttl > 0 {
			sm = secrets.WrapCacher(ctx, sm, ttl)
		}
	}

	// Process first round of environment variables.
	if err := envconfig.Process(ctx, config, sm); err != nil {
		return nil, nil, fmt.Errorf("error loading environment variables: %w", err)
	}
	logger.Infof("Effective environment variables: %+v", config)

	// Start building serverenv opts
	opts := []serverenv.Option{
		serverenv.WithSecretManager(sm),
		serverenv.WithMetricsExporter(metrics.NewLogsBasedFromContext),
	}

	// Configure key management for signing.
	if provider, ok := config.(KeyManagerConfigProvider); ok {
		kmConfig := provider.KeyManagerConfig()
		km, err := signing.KeyManagerFor(ctx, kmConfig.KeyManagerType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to key manager: %w", err)
		}
		opts = append(opts, serverenv.WithKeyManager(km))
	}

	// Configure blob storage.
	if provider, ok := config.(BlobstoreConfigProvider); ok {
		bsConfig := provider.BlobstoreConfig()
		blobStore, err := storage.BlobstoreFor(ctx, bsConfig.BlobstoreType)
		if err != nil {
			return nil, nil, fmt.Errorf("unable to connect to storage system: %v", err)
		}
		blobStorage := serverenv.WithBlobStorage(blobStore)
		opts = append(opts, blobStorage)
	}

	// Setup the database connection.
	db, err := database.NewFromEnv(ctx, config.DatabaseConfig())
	if err != nil {
		return nil, nil, fmt.Errorf("unable to connect to database: %v", err)
	}
	{
		// Log the database config, but omit the password field.
		redactedDB := config.DatabaseConfig()
		redactedDB.Password = "<hidden>"
		logger.Infof("Effective DB config: %+v", redactedDB)
	}
	opts = append(opts, serverenv.WithDatabase(db))

	// AuthorizedApp must come after database setup due to the dependency.
	if typ, ok := config.(AuthorizedAppConfigProvider); ok {
		logger.Infof("Effective AuthorizedApp config: %+v", typ.AuthorizedAppConfig())
		provider, err := authorizedapp.NewDatabaseProvider(ctx, db, typ.AuthorizedAppConfig(), authorizedapp.WithSecretManager(sm))
		if err != nil {
			// Ensure the database is closed on an error.
			defer db.Close(ctx)
			return nil, nil, fmt.Errorf("unable to create AuthorizedApp provider: %v", err)
		}
		opts = append(opts, serverenv.WithAuthorizedAppProvider(provider))
	}

	return serverenv.New(ctx, opts...), func() { db.Close(ctx) }, nil
}
<|endoftext|>


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

package statsd

import (
	"crypto/x509"
	"io/ioutil"
	stdlog "log"
	"net"
	"regexp"

	"github.com/circonus-labs/circonus-agent/internal/config"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// New returns a statsd server definition
func New() (*Server, error) {
	s := Server{
		disabled:       viper.GetBool(config.KeyStatsdDisabled),
		logger:         log.With().Str("pkg", "statsd").Logger(),
		hostPrefix:     viper.GetString(config.KeyStatsdHostPrefix),
		hostCategory:   viper.GetString(config.KeyStatsdHostCategory),
		groupCID:       viper.GetString(config.KeyStatsdGroupCID),
		groupPrefix:    viper.GetString(config.KeyStatsdGroupPrefix),
		groupCounterOp: viper.GetString(config.KeyStatsdGroupCounters),
		groupGaugeOp:   viper.GetString(config.KeyStatsdGroupGauges),
		groupSetOp:     viper.GetString(config.KeyStatsdGroupSets),
		debugCGM:       viper.GetBool(config.KeyDebugCGM),
		apiKey:         viper.GetString(config.KeyAPITokenKey),
		apiApp:         viper.GetString(config.KeyAPITokenApp),
		apiURL:         viper.GetString(config.KeyAPIURL),
		apiCAFile:      viper.GetString(config.KeyAPICAFile),
	}

	port := viper.GetString(config.KeyStatsdPort)
	address := net.JoinHostPort("localhost", port)
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving address '%s'", address)
	}

	s.address = addr
	s.metricRegex = regexp.MustCompile(`^(?P<name>[^:\s]+):(?P<value>[^|\s]+)\|(?P<type>[a-z]+)(?:@(?P<sample>[0-9.]+))?$`)
	s.metricRegexGroupNames = s.metricRegex.SubexpNames()

	if !s.disabled {
		if ierr := s.initHostMetrics(); ierr != nil {
			return nil, errors.Wrap(ierr, "Initializing host metrics for StatsD")
		}

		if ierr := s.initGroupMetrics(); ierr != nil {
			return nil, errors.Wrap(ierr, "Initializing group metrics for StatsD")
		}
	}

	l, err := net.ListenUDP("udp", s.address)
	if err != nil {
		return nil, err
	}
	s.listener = l

	return &s, nil
}

// Start the StatsD listener
func (s *Server) Start() error {
	if s.disabled {
		s.logger.Info().Msg("disabled, not starting listener")
		return nil
	}

	s.t.Go(s.reader)
	s.t.Go(s.processor)

	return s.t.Wait()
}

// Stop the server
func (s *Server) Stop() error {
	if s.disabled {
		return nil
	}

	s.logger.Info().Msg("Stopping StatsD Server")

	if s.t.Alive() {
		s.t.Kill(nil)
	}

	if s.groupMetrics != nil {
		s.logger.Info().Msg("Flushing group metrics")
		s.groupMetricsmu.Lock()
		s.groupMetrics.Flush()
		s.groupMetricsmu.Unlock()
	}

	return nil
}

// Flush *host* metrics only
// NOTE: group metrics flush independently via circonus-gometrics to a different check
func (s *Server) Flush() *cgm.Metrics {
	if s.disabled {
		return nil
	}

	if s.hostMetrics == nil {
		return &cgm.Metrics{}
	}

	s.hostMetricsmu.Lock()
	defer s.hostMetricsmu.Unlock()
	return s.hostMetrics.FlushMetrics()
}

// initHostMetrics initializes the host metrics circonus-gometrics instance
func (s *Server) initHostMetrics() error {
	s.hostMetricsmu.Lock()
	defer s.hostMetricsmu.Unlock()

	cmc := &cgm.Config{
		Debug: s.debugCGM,
		Log:   stdlog.New(s.logger.With().Str("pkg", "statsd-host-check").Logger(), "", 0),
	}
	// put cgm into manual mode (no interval, no api key, invalid submission url)
	cmc.Interval = "0"                            // disable automatic flush
	cmc.CheckManager.Check.SubmissionURL = "none" // disable check management (create/update)

	hm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "statsd host check")
	}

	s.hostMetrics = hm

	s.logger.Info().Msg("host check initialized")
	return nil
}

// initGroupMetrics initializes the group metric circonus-gometrics instance
// NOTE: Group metrics are sent directly to circonus, to an existing HTTPTRAP
//       check created manually or by cosi - the group check is intended to be
//       used by multiple systems.
func (s *Server) initGroupMetrics() error {
	if s.groupCID == "" {
		s.logger.Info().Msg("group check disabled")
		return nil
	}

	s.groupMetricsmu.Lock()
	defer s.groupMetricsmu.Unlock()

	cmc := &cgm.Config{
		Debug: s.debugCGM,
		Log:   stdlog.New(s.logger.With().Str("pkg", "statsd-group-check").Logger(), "", 0),
	}
	cmc.CheckManager.API.TokenKey = s.apiKey
	cmc.CheckManager.API.TokenApp = s.apiApp
	cmc.CheckManager.API.URL = s.apiURL
	cmc.CheckManager.Check.ID = s.groupCID

	if s.apiCAFile != "" {
		cert, err := ioutil.ReadFile(s.apiCAFile)
		if err != nil {
			return err
		}

		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(cert) {
			return errors.Errorf("using api CA cert %#v", cert)
		}

		cmc.CheckManager.API.CACert = cp
	}

	gm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "statsd group check")
	}

	s.groupMetrics = gm

	s.logger.Info().Msg("group check initialized")
	return nil
}

// reader reads packets from the statsd listener, adds packets recevied to the queue
func (s *Server) reader() error {
	for {
		buff := make([]byte, maxPacketSize)
		n, err := s.listener.Read(buff)
		if s.shutdown() {
			return nil
		}
		if err != nil {
			return errors.Wrap(err, "reader")
		}
		if n > 0 {
			pkt := make([]byte, n)
			copy(pkt, buff[:n])
			s.packetCh <- pkt
		}
	}
}

// processor reads the packet queue and processes each packet
func (s *Server) processor() error {
	defer s.listener.Close()
	for {
		select {
		case <-s.t.Dying():
			return nil
		case pkt := <-s.packetCh:
			err := s.processPacket(pkt)
			if err != nil {
				return errors.Wrap(err, "processor")
			}
		}
	}
}

// shutdown checks whether tomb is dying
func (s *Server) shutdown() bool {
	select {
	case <-s.t.Dying():
		return true
	default:
		return false
	}
}
<commit_msg>reimplement packet channel initialization<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package statsd

import (
	"crypto/x509"
	"io/ioutil"
	stdlog "log"
	"net"
	"regexp"

	"github.com/circonus-labs/circonus-agent/internal/config"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

// New returns a statsd server definition
func New() (*Server, error) {
	s := Server{
		disabled:       viper.GetBool(config.KeyStatsdDisabled),
		logger:         log.With().Str("pkg", "statsd").Logger(),
		hostPrefix:     viper.GetString(config.KeyStatsdHostPrefix),
		hostCategory:   viper.GetString(config.KeyStatsdHostCategory),
		groupCID:       viper.GetString(config.KeyStatsdGroupCID),
		groupPrefix:    viper.GetString(config.KeyStatsdGroupPrefix),
		groupCounterOp: viper.GetString(config.KeyStatsdGroupCounters),
		groupGaugeOp:   viper.GetString(config.KeyStatsdGroupGauges),
		groupSetOp:     viper.GetString(config.KeyStatsdGroupSets),
		debugCGM:       viper.GetBool(config.KeyDebugCGM),
		apiKey:         viper.GetString(config.KeyAPITokenKey),
		apiApp:         viper.GetString(config.KeyAPITokenApp),
		apiURL:         viper.GetString(config.KeyAPIURL),
		apiCAFile:      viper.GetString(config.KeyAPICAFile),
		packetCh:       make(chan []byte, packetQueueSize),
	}

	port := viper.GetString(config.KeyStatsdPort)
	address := net.JoinHostPort("localhost", port)
	addr, err := net.ResolveUDPAddr("udp", address)
	if err != nil {
		return nil, errors.Wrapf(err, "resolving address '%s'", address)
	}

	s.address = addr
	s.metricRegex = regexp.MustCompile(`^(?P<name>[^:\s]+):(?P<value>[^|\s]+)\|(?P<type>[a-z]+)(?:@(?P<sample>[0-9.]+))?$`)
	s.metricRegexGroupNames = s.metricRegex.SubexpNames()

	if !s.disabled {
		if ierr := s.initHostMetrics(); ierr != nil {
			return nil, errors.Wrap(ierr, "Initializing host metrics for StatsD")
		}

		if ierr := s.initGroupMetrics(); ierr != nil {
			return nil, errors.Wrap(ierr, "Initializing group metrics for StatsD")
		}
	}

	l, err := net.ListenUDP("udp", s.address)
	if err != nil {
		return nil, err
	}
	s.listener = l

	return &s, nil
}

// Start the StatsD listener
func (s *Server) Start() error {
	if s.disabled {
		s.logger.Info().Msg("disabled, not starting listener")
		return nil
	}

	s.t.Go(s.reader)
	s.t.Go(s.processor)

	return s.t.Wait()
}

// Stop the server
func (s *Server) Stop() error {
	if s.disabled {
		return nil
	}

	s.logger.Info().Msg("Stopping StatsD Server")

	if s.t.Alive() {
		s.t.Kill(nil)
	}

	if s.groupMetrics != nil {
		s.logger.Info().Msg("Flushing group metrics")
		s.groupMetricsmu.Lock()
		s.groupMetrics.Flush()
		s.groupMetricsmu.Unlock()
	}

	return nil
}

// Flush *host* metrics only
// NOTE: group metrics flush independently via circonus-gometrics to a different check
func (s *Server) Flush() *cgm.Metrics {
	if s.disabled {
		return nil
	}

	if s.hostMetrics == nil {
		return &cgm.Metrics{}
	}

	s.hostMetricsmu.Lock()
	defer s.hostMetricsmu.Unlock()
	return s.hostMetrics.FlushMetrics()
}

// initHostMetrics initializes the host metrics circonus-gometrics instance
func (s *Server) initHostMetrics() error {
	s.hostMetricsmu.Lock()
	defer s.hostMetricsmu.Unlock()

	cmc := &cgm.Config{
		Debug: s.debugCGM,
		Log:   stdlog.New(s.logger.With().Str("pkg", "statsd-host-check").Logger(), "", 0),
	}
	// put cgm into manual mode (no interval, no api key, invalid submission url)
	cmc.Interval = "0"                            // disable automatic flush
	cmc.CheckManager.Check.SubmissionURL = "none" // disable check management (create/update)

	hm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "statsd host check")
	}

	s.hostMetrics = hm

	s.logger.Info().Msg("host check initialized")
	return nil
}

// initGroupMetrics initializes the group metric circonus-gometrics instance
// NOTE: Group metrics are sent directly to circonus, to an existing HTTPTRAP
//       check created manually or by cosi - the group check is intended to be
//       used by multiple systems.
func (s *Server) initGroupMetrics() error {
	if s.groupCID == "" {
		s.logger.Info().Msg("group check disabled")
		return nil
	}

	s.groupMetricsmu.Lock()
	defer s.groupMetricsmu.Unlock()

	cmc := &cgm.Config{
		Debug: s.debugCGM,
		Log:   stdlog.New(s.logger.With().Str("pkg", "statsd-group-check").Logger(), "", 0),
	}
	cmc.CheckManager.API.TokenKey = s.apiKey
	cmc.CheckManager.API.TokenApp = s.apiApp
	cmc.CheckManager.API.URL = s.apiURL
	cmc.CheckManager.Check.ID = s.groupCID

	if s.apiCAFile != "" {
		cert, err := ioutil.ReadFile(s.apiCAFile)
		if err != nil {
			return err
		}

		cp := x509.NewCertPool()
		if !cp.AppendCertsFromPEM(cert) {
			return errors.Errorf("using api CA cert %#v", cert)
		}

		cmc.CheckManager.API.CACert = cp
	}

	gm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "statsd group check")
	}

	s.groupMetrics = gm

	s.logger.Info().Msg("group check initialized")
	return nil
}

// reader reads packets from the statsd listener, adds packets recevied to the queue
func (s *Server) reader() error {
	for {
		buff := make([]byte, maxPacketSize)
		n, err := s.listener.Read(buff)
		if s.shutdown() {
			return nil
		}
		if err != nil {
			s.logger.Error().Err(err).Msg("reader")
			return errors.Wrap(err, "reader")
		}
		if n > 0 {
			pkt := make([]byte, n)
			copy(pkt, buff[:n])
			s.packetCh <- pkt
		}
	}
}

// processor reads the packet queue and processes each packet
func (s *Server) processor() error {
	defer s.listener.Close()
	for {
		select {
		case <-s.t.Dying():
			return nil
		case pkt := <-s.packetCh:
			err := s.processPacket(pkt)
			if err != nil {
				s.logger.Error().Err(err).Msg("processor")
				return errors.Wrap(err, "processor")
			}
		}
	}
}

// shutdown checks whether tomb is dying
func (s *Server) shutdown() bool {
	select {
	case <-s.t.Dying():
		return true
	default:
		return false
	}
}
<|endoftext|>


<commit_before>package client

import (
	"testing"

	p "github.com/firepear/petrel"
	ps "github.com/firepear/petrel/server"
)

func TestRawClientNewTCP(t *testing.T) {
	// instantiate TCP petrel
	asconf := &ps.ServerConfig{Sockname: "127.0.0.1:10298", Msglvl: Fatal}
	as, err := ps.TCPServer(asconf)
	if err != nil {
		t.Errorf("Failed to create petrel instance: %v", err)
	}
	as.Register("echo", "blob", hollaback)
	// and now a client
	cconf := &ClientConfig{Addr: "127.0.0.1:10298"}
	c, err := TCPClient(cconf)
	if err != nil {
		t.Errorf("Failed to create client: %v", err)
	}
	// and send a message
	messages := [][]byte{
		[]byte("echo just the one test"),
		[]byte("echo abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz"),
	}

	for mseq, m := range messages {
		c.Seq++
		xmission, interr, err := marshalXmission(m, nil, c.Seq)
		if err != nil {
			t.Errorf("marshalXmission returned error: %s - %s", interr, err)
		}
		resp, err := c.DispatchRaw(xmission)
		if err != nil {
			t.Errorf("Dispatch returned error: %v", err)
		}
		// check sequence
		xseq := []byte{uint8(mseq + 1), 0, 0, 0}
		rseq := resp[0:4]
		for i := 0; i < 4; i++ {
			if rseq[i] != xseq[i] {
				t.Errorf("Byte %d of received seq should be %d but is %d", i, xseq[i], rseq[i])
			}
		}
		// check plen
		xplen := []byte{uint8(len(m) - 5), 0, 0, 0}
		rplen := resp[4:8]
		for i := 0; i < 4; i++ {
			if rplen[i] != xplen[i] {
				t.Errorf("Byte %d of received plen should be %d but is %d", i, xplen[i], rplen[i])
			}
		}
		// check pver
		if resp[8] != Proto {
			t.Errorf("Received protover should be %d but is %d", Proto, resp[8])
		}
		// check payload
		if string(resp[9:]) != string([]byte(m)[5:]) {
			t.Errorf("Expected `%s` but got: `%v`", m[5:], string(resp))
		}
	}
	c.Quit()
	as.Quit()
}
<commit_msg>these things all get exercised via exported unit testing<commit_after><|endoftext|>


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

// Package Hacker View provides APIs to analyze Go projects and generate AST-based source code view.
package hv

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"path"
	"strings"

	"github.com/Unknwon/com"
)

// Render renders source code to HTML.
// Note that you have to call Build before you call this method.
func (w *Walker) Render() (map[string][]byte, error) {
	htmls := make(map[string][]byte)
	files := make(map[string]File)
	srcs := w.SrcFiles
	imports := make([]string, 0, 10)

	for name, src := range srcs {
		pkg, err := w.Build(&WalkRes{
			WalkDepth: WD_All,
			WalkType:  WT_Memory,
			WalkMode:  WM_NoReadme | WM_NoExample,
			Srcs:      []*Source{src},
			BuildAll:  true,
		})

		if err != nil {
			return nil, errors.New("hv.Walker.Render -> " + err.Error())
		}

		files[name] = pkg.File

		for _, v := range w.Pdoc.Imports {
			imports = com.AppendStr(imports, v)
		}
	}

	r := Render{
		Links:     GetLinks(w.Pdoc.ImportPath, imports, files),
		FilteList: make(map[string]bool),
	}

	for name := range srcs {
		htmls[name] = r.Render(name, srcs[name].Data())
		//com.SaveFile(name+".hmtl", htmls[name])
	}
	return htmls, nil
}

// A link describes the (HTML) link information for an identifier.
// The zero value of a link represents "no link".
type Link struct {
	Path, Name, Comment string
}

type localVar struct {
	name, tp string
}

// A Render describles a code render.
type Render struct {
	recv       localVar
	blockLevel int

	Links     []*Link
	FilteList map[string]bool
}

// Render highlights code.
func (r *Render) Render(name string, data []byte) []byte {
	if len(data) == 0 {
		return nil
	}

	code := string(data)
	l := len(code)

	buf := new(bytes.Buffer)
	//buf.WriteString("
")

	strTag := uint8(0)
	isComment := false
	isBlockComment := false
	isString := false
	isFuncDecl := false
	isFuncBlock := false
	isHasRecv := false
	isTypeDecl := false
	last := 0
	pos := 0

	for {
	CutWords:
		for {
			curChar := code[pos]
			if !com.IsLetter(curChar) {
				if isComment {
					// Comment.
					if isBlockComment {
						// Check if in end of block comment.
						if curChar == '/' && code[pos-1] == '*' {
							break CutWords
						}
					} else {
						// Check if in start of block comment.
						if curChar == '*' && code[pos-1] == '/' {
							isBlockComment = true
						} else if curChar == '\n' {
							break CutWords
						}
					}
				} else {
					// String.
					if curChar == '\'' || curChar == '"' || curChar == '`' {
						if !isString {
							// Set string tag.
							strTag = curChar
							isString = true
						} else {
							// Check if it is end of string or escaped character.
							if (code[pos-1] == '\\' && code[pos-2] == '\\') || code[pos-1] != '\\' {
								// Check string tag.
								if curChar == strTag {
									// Handle string highlight.
									break CutWords
								}
							}
						}
					}

					// FIXME: index out of range
					if len(code) <= pos {
						break CutWords
					}
					if !isString {
						switch {
						case curChar == '/' && (code[pos+1] == '/' || code[pos+1] == '*'):
							isComment = true
						case curChar > 47 && curChar < 58: // Ends with number.
						case curChar == '_' && code[pos-1] != ' ': // Underline: _.
						case (curChar != '.' || curChar == '\n'):
							break CutWords
						}
					}
				}
			}

			if pos == l-1 {
				break CutWords
			}
			pos++
		}

		seg := code[last : pos+1]
	CheckLink:
		switch {
		case isComment:
			isComment = false
			isBlockComment = false
			fmt.Fprintf(buf, `<span class="com">%s</span>`, seg)
		case isString:
			isString = false
			fmt.Fprintf(buf, `<span class="str">%s</span>`, template.HTMLEscapeString(seg))
		case seg == "\t":
			fmt.Fprintf(buf, `%s`, "    ")
		case seg == "{":
			if isFuncDecl {
				isFuncDecl = false
				isFuncBlock = true
			}

			if isFuncBlock {
				r.blockLevel++
			}
			fmt.Fprintf(buf, "%s", seg)
		case seg == "}":
			if isFuncBlock {
				r.blockLevel--
			}
			if r.blockLevel == 0 {
				isFuncBlock = false
				r.recv.name = ""
			}
			fmt.Fprintf(buf, "%s", seg)
		case isFuncDecl:
			if isHasRecv {
				if seg != "(" && seg != " " && seg != "*" {
					if len(r.recv.name) == 0 {
						r.recv.name = seg[:len(seg)-1]
					} else {
						r.recv.tp = seg[:len(seg)-1]
						isHasRecv = false
					}
				}
			} else if len(seg) > 1 && code[pos] == '(' {
				if len(r.recv.name) > 0 {
					fmt.Fprintf(buf, "%s(", r.recv.tp, seg[:len(seg)-1], seg[:len(seg)-1])
				} else {
					fmt.Fprintf(buf, "%s(", seg[:len(seg)-1], seg[:len(seg)-1])
				}
				break CheckLink
			}
			fallthrough
		case pos-last > 1:
			// Check if the last word of the paragraphy.
			l := len(seg)
			keyword := seg
			if !com.IsLetter(seg[l-1]) {
				keyword = seg[:l-1]
			} else {
				l++
			}

			// Check keywords.
			switch keyword {
			case "return", "break":
				fmt.Fprintf(buf, `<span class="ret">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			case "func":
				isFuncDecl = true
				if code[pos+1] == '(' {
					isHasRecv = true
				}
				fallthrough
			case "package", "import", "range", "for", "if", "else", "type", "struct", "select", "case", "var", "const", "switch", "default", "continue":
				if keyword == "type" {
					isTypeDecl = true
				}
				fmt.Fprintf(buf, `<span class="key">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			case "new", "append", "make", "panic", "recover", "len", "cap", "copy", "close", "delete", "defer":
				fmt.Fprintf(buf, `<span class="bui">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			}

			if isPredeclared(keyword) {
				fmt.Fprintf(buf, `<span class="boo">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			}

			// Check links.
			link, ok := r.findType(seg)
			if ok {
				switch {
				case strings.HasSuffix(link.Path, name) && len(link.Name) > 0: // Current file.
					fmt.Fprintf(buf, `<a class="int" title="%s" href="#%s">%s</a>%s`,
						link.Comment, link.Name, link.Name, seg[l-1:])
				case len(link.Path) > 0 && len(link.Name) > 0:
					if strings.HasPrefix(link.Path, "#") {
						fmt.Fprintf(buf, `<a class="ext" title="%s" href="%s">%s</a>%s`,
							link.Comment, link.Path, link.Name, seg[l-1:])
					} else {
						if strings.Index(link.Path, "#") > -1 {
							fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="%s">%s</a>%s`,
								link.Comment, link.Path, link.Name, seg[l-1:])
						} else {
							fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="/%s#%s">%s</a>%s`,
								link.Comment, link.Path, link.Name, link.Name, seg[l-1:])
						}
					}
				}
			} else if seg[len(seg)-1] == ' ' || seg[len(seg)-1] == '\n' {
				if isFuncDecl || isTypeDecl {
					isTypeDecl = false
					fmt.Fprintf(buf, "%s%s", seg[:len(seg)-1], seg[:len(seg)-1], seg[l-1:])
				} else {
					fmt.Fprintf(buf, "%s", seg)
				}
			} else {
				fmt.Fprintf(buf, "%s", seg)
			}
		default:
			fmt.Fprintf(buf, "%s", seg)
		}

		last = pos + 1
		pos++
		// End of code.
		if pos == l {
			fmt.Fprintf(buf, "%s", code[last:])
			break
		}
	}

	return buf.Bytes()
}

func (r *Render) findType(name string) (*Link, bool) {
	if !com.IsLetter(name[0]) {
		return nil, false
	}

	// We cannot deal with struct field now.
	if name[len(name)-1] == '[' || name[len(name)-1] == ']' ||
		name[len(name)-1] == ' ' || name[len(name)-1] == ':' {
		return nil, false
	}

	// We cannot deal with chain operation.
	if name[0] == '.' {
		return nil, false
	}

	name = name[:len(name)-1]

	// This is for functions and types from imported packages.
	i := strings.Index(name, ".")
	// We cannot deal with struct field or chain operation now.
	if i != strings.LastIndex(name, ".") {
		return nil, false
	}

	if filte := r.FilteList[name]; filte {
		return nil, false
	}

	var left, right string
	if i > -1 {
		left = name[:i+1]
		right = name[i+1:]
	}

	for _, l := range r.Links {
		if i == -1 {
			// Exported types and functions in current package.
			if l.Name == name {
				return l, true
			}
		} else {
			// Functions and types from imported packages.
			if l.Name == left {
				if len(l.Path) > 0 {
					return &Link{Name: name, Path: "/" + l.Path + "#" + right}, true
				} else {
					return &Link{Name: name, Path: "#" + right}, true
				}
			} else if r.recv.name == left[:len(left)-1] {
				// fmt.Println(r.recv.tp + "." + right)
				// fmt.Println(r.findType(r.recv.tp + "." + right))
				return nil, false
			}
		}
	}

	r.FilteList[name] = true
	return nil, false
}

// GetLinks returns objects with its jump link.
func GetLinks(prefix string, imports []string, files map[string]File) []*Link {
	prefix += "?f="
	links := make([]*Link, 0, len(imports)+len(files)*20+10)
	for fname, f := range files {
		// Get all types, functions and import packages.
		for _, t := range f.Types {
			links = append(links, &Link{
				Name:    t.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(t.Doc),
			})

			for _, f := range t.Funcs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}

			for _, f := range t.IFuncs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}
		}

		for _, t := range f.Itypes {
			links = append(links, &Link{
				Name:    t.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(t.Doc),
			})

			for _, f := range t.Funcs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}

			for _, f := range t.IFuncs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}
		}

		for _, f := range f.Funcs {
			links = append(links, &Link{
				Name:    f.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(f.Doc),
			})
		}

		for _, f := range f.Ifuncs {
			links = append(links, &Link{
				Name:    f.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(f.Doc),
			})
		}
	}

	for _, v := range imports {
		if v != "C" {
			links = append(links, &Link{
				Name: path.Base(v) + ".",
				Path: v,
			})
		}
	}
	return links
}

func isPredeclared(name string) bool {
	_, ok := predeclared[name]
	return ok
}
<commit_msg>Try to fix bug<commit_after>// Copyright 2013 Unknwon
//
// Licensed under the Apache License, Version 2.0 (the "License"): you may
// not use this file except in compliance with the License. You may obtain
// a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

// Package Hacker View provides APIs to analyze Go projects and generate AST-based source code view.
package hv

import (
	"bytes"
	"errors"
	"fmt"
	"html/template"
	"path"
	"strings"

	"github.com/Unknwon/com"
)

// Render renders source code to HTML.
// Note that you have to call Build before you call this method.
func (w *Walker) Render() (map[string][]byte, error) {
	htmls := make(map[string][]byte)
	files := make(map[string]File)
	srcs := w.SrcFiles
	imports := make([]string, 0, 10)

	for name, src := range srcs {
		pkg, err := w.Build(&WalkRes{
			WalkDepth: WD_All,
			WalkType:  WT_Memory,
			WalkMode:  WM_NoReadme | WM_NoExample,
			Srcs:      []*Source{src},
			BuildAll:  true,
		})

		if err != nil {
			return nil, errors.New("hv.Walker.Render -> " + err.Error())
		}

		files[name] = pkg.File

		for _, v := range w.Pdoc.Imports {
			imports = com.AppendStr(imports, v)
		}
	}

	r := Render{
		Links:     GetLinks(w.Pdoc.ImportPath, imports, files),
		FilteList: make(map[string]bool),
	}

	for name := range srcs {
		htmls[name] = r.Render(name, srcs[name].Data())
		//com.SaveFile(name+".hmtl", htmls[name])
	}
	return htmls, nil
}

// A link describes the (HTML) link information for an identifier.
// The zero value of a link represents "no link".
type Link struct {
	Path, Name, Comment string
}

type localVar struct {
	name, tp string
}

// A Render describles a code render.
type Render struct {
	recv       localVar
	blockLevel int

	Links     []*Link
	FilteList map[string]bool
}

// Render highlights code.
func (r *Render) Render(name string, data []byte) []byte {
	if len(data) == 0 {
		return nil
	}

	code := string(data)
	l := len(code)

	buf := new(bytes.Buffer)
	//buf.WriteString("
")

	strTag := uint8(0)
	isComment := false
	isBlockComment := false
	isString := false
	isFuncDecl := false
	isFuncBlock := false
	isHasRecv := false
	isTypeDecl := false
	last := 0
	pos := 0

	for {
	CutWords:
		for {
			curChar := code[pos]
			if !com.IsLetter(curChar) {
				if isComment {
					// Comment.
					if isBlockComment {
						// Check if in end of block comment.
						if curChar == '/' && code[pos-1] == '*' {
							break CutWords
						}
					} else {
						// Check if in start of block comment.
						if curChar == '*' && code[pos-1] == '/' {
							isBlockComment = true
						} else if curChar == '\n' {
							break CutWords
						}
					}
				} else {
					// String.
					if curChar == '\'' || curChar == '"' || curChar == '`' {
						if !isString {
							// Set string tag.
							strTag = curChar
							isString = true
						} else {
							// Check if it is end of string or escaped character.
							if (code[pos-1] == '\\' && code[pos-2] == '\\') || code[pos-1] != '\\' {
								// Check string tag.
								if curChar == strTag {
									// Handle string highlight.
									break CutWords
								}
							}
						}
					}

					// FIXME: index out of range
					if len(code) == pos+1 {
						break CutWords
					}
					if !isString {
						switch {
						case curChar == '/' && (code[pos+1] == '/' || code[pos+1] == '*'):
							isComment = true
						case curChar > 47 && curChar < 58: // Ends with number.
						case curChar == '_' && code[pos-1] != ' ': // Underline: _.
						case (curChar != '.' || curChar == '\n'):
							break CutWords
						}
					}
				}
			}

			if pos == l-1 {
				break CutWords
			}
			pos++
		}

		seg := code[last : pos+1]
	CheckLink:
		switch {
		case isComment:
			isComment = false
			isBlockComment = false
			fmt.Fprintf(buf, `<span class="com">%s</span>`, seg)
		case isString:
			isString = false
			fmt.Fprintf(buf, `<span class="str">%s</span>`, template.HTMLEscapeString(seg))
		case seg == "\t":
			fmt.Fprintf(buf, `%s`, "    ")
		case seg == "{":
			if isFuncDecl {
				isFuncDecl = false
				isFuncBlock = true
			}

			if isFuncBlock {
				r.blockLevel++
			}
			fmt.Fprintf(buf, "%s", seg)
		case seg == "}":
			if isFuncBlock {
				r.blockLevel--
			}
			if r.blockLevel == 0 {
				isFuncBlock = false
				r.recv.name = ""
			}
			fmt.Fprintf(buf, "%s", seg)
		case isFuncDecl:
			if isHasRecv {
				if seg != "(" && seg != " " && seg != "*" {
					if len(r.recv.name) == 0 {
						r.recv.name = seg[:len(seg)-1]
					} else {
						r.recv.tp = seg[:len(seg)-1]
						isHasRecv = false
					}
				}
			} else if len(seg) > 1 && code[pos] == '(' {
				if len(r.recv.name) > 0 {
					fmt.Fprintf(buf, "%s(", r.recv.tp, seg[:len(seg)-1], seg[:len(seg)-1])
				} else {
					fmt.Fprintf(buf, "%s(", seg[:len(seg)-1], seg[:len(seg)-1])
				}
				break CheckLink
			}
			fallthrough
		case pos-last > 1:
			// Check if the last word of the paragraphy.
			l := len(seg)
			keyword := seg
			if !com.IsLetter(seg[l-1]) {
				keyword = seg[:l-1]
			} else {
				l++
			}

			// Check keywords.
			switch keyword {
			case "return", "break":
				fmt.Fprintf(buf, `<span class="ret">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			case "func":
				isFuncDecl = true
				if code[pos+1] == '(' {
					isHasRecv = true
				}
				fallthrough
			case "package", "import", "range", "for", "if", "else", "type", "struct", "select", "case", "var", "const", "switch", "default", "continue":
				if keyword == "type" {
					isTypeDecl = true
				}
				fmt.Fprintf(buf, `<span class="key">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			case "new", "append", "make", "panic", "recover", "len", "cap", "copy", "close", "delete", "defer":
				fmt.Fprintf(buf, `<span class="bui">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			}

			if isPredeclared(keyword) {
				fmt.Fprintf(buf, `<span class="boo">%s</span>%s`, keyword, seg[l-1:])
				break CheckLink
			}

			// Check links.
			link, ok := r.findType(seg)
			if ok {
				switch {
				case strings.HasSuffix(link.Path, name) && len(link.Name) > 0: // Current file.
					fmt.Fprintf(buf, `<a class="int" title="%s" href="#%s">%s</a>%s`,
						link.Comment, link.Name, link.Name, seg[l-1:])
				case len(link.Path) > 0 && len(link.Name) > 0:
					if strings.HasPrefix(link.Path, "#") {
						fmt.Fprintf(buf, `<a class="ext" title="%s" href="%s">%s</a>%s`,
							link.Comment, link.Path, link.Name, seg[l-1:])
					} else {
						if strings.Index(link.Path, "#") > -1 {
							fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="%s">%s</a>%s`,
								link.Comment, link.Path, link.Name, seg[l-1:])
						} else {
							fmt.Fprintf(buf, `<a class="ext" title="%s" target="_blank" href="/%s#%s">%s</a>%s`,
								link.Comment, link.Path, link.Name, link.Name, seg[l-1:])
						}
					}
				}
			} else if seg[len(seg)-1] == ' ' || seg[len(seg)-1] == '\n' {
				if isFuncDecl || isTypeDecl {
					isTypeDecl = false
					fmt.Fprintf(buf, "%s%s", seg[:len(seg)-1], seg[:len(seg)-1], seg[l-1:])
				} else {
					fmt.Fprintf(buf, "%s", seg)
				}
			} else {
				fmt.Fprintf(buf, "%s", seg)
			}
		default:
			fmt.Fprintf(buf, "%s", seg)
		}

		last = pos + 1
		pos++
		// End of code.
		if pos == l {
			fmt.Fprintf(buf, "%s", code[last:])
			break
		}
	}

	return buf.Bytes()
}

func (r *Render) findType(name string) (*Link, bool) {
	if !com.IsLetter(name[0]) {
		return nil, false
	}

	// We cannot deal with struct field now.
	if name[len(name)-1] == '[' || name[len(name)-1] == ']' ||
		name[len(name)-1] == ' ' || name[len(name)-1] == ':' {
		return nil, false
	}

	// We cannot deal with chain operation.
	if name[0] == '.' {
		return nil, false
	}

	name = name[:len(name)-1]

	// This is for functions and types from imported packages.
	i := strings.Index(name, ".")
	// We cannot deal with struct field or chain operation now.
	if i != strings.LastIndex(name, ".") {
		return nil, false
	}

	if filte := r.FilteList[name]; filte {
		return nil, false
	}

	var left, right string
	if i > -1 {
		left = name[:i+1]
		right = name[i+1:]
	}

	for _, l := range r.Links {
		if i == -1 {
			// Exported types and functions in current package.
			if l.Name == name {
				return l, true
			}
		} else {
			// Functions and types from imported packages.
			if l.Name == left {
				if len(l.Path) > 0 {
					return &Link{Name: name, Path: "/" + l.Path + "#" + right}, true
				} else {
					return &Link{Name: name, Path: "#" + right}, true
				}
			} else if r.recv.name == left[:len(left)-1] {
				// fmt.Println(r.recv.tp + "." + right)
				// fmt.Println(r.findType(r.recv.tp + "." + right))
				return nil, false
			}
		}
	}

	r.FilteList[name] = true
	return nil, false
}

// GetLinks returns objects with its jump link.
func GetLinks(prefix string, imports []string, files map[string]File) []*Link {
	prefix += "?f="
	links := make([]*Link, 0, len(imports)+len(files)*20+10)
	for fname, f := range files {
		// Get all types, functions and import packages.
		for _, t := range f.Types {
			links = append(links, &Link{
				Name:    t.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(t.Doc),
			})

			for _, f := range t.Funcs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}

			for _, f := range t.IFuncs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}
		}

		for _, t := range f.Itypes {
			links = append(links, &Link{
				Name:    t.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(t.Doc),
			})

			for _, f := range t.Funcs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}

			for _, f := range t.IFuncs {
				links = append(links, &Link{
					Name:    f.Name,
					Path:    prefix + fname,
					Comment: template.HTMLEscapeString(f.Doc),
				})
			}
		}

		for _, f := range f.Funcs {
			links = append(links, &Link{
				Name:    f.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(f.Doc),
			})
		}

		for _, f := range f.Ifuncs {
			links = append(links, &Link{
				Name:    f.Name,
				Path:    prefix + fname,
				Comment: template.HTMLEscapeString(f.Doc),
			})
		}
	}

	for _, v := range imports {
		if v != "C" {
			links = append(links, &Link{
				Name: path.Base(v) + ".",
				Path: v,
			})
		}
	}
	return links
}

func isPredeclared(name string) bool {
	_, ok := predeclared[name]
	return ok
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
)

var separator = os.Getenv("BRONZE_SEPARATOR")
var thinSeparator = os.Getenv("BRONZE_THIN_SEPARATOR")
var iconMode = os.Getenv("BRONZE_ICONS")
var icons = map[string]string{}

func init() {
	// set defaults
	if separator == "" {
		separator = "\ue0b0" // Powerline
	}
	if thinSeparator == "" {
		thinSeparator = "\ue0b1" // Powerline
	}
	if iconMode == "" {
		iconMode = "nerd"
	}

	switch iconMode {
	case "nerd":
		initIcon("apple", "\uf179")      // Font Awesome; apple
		initIcon("arch", "\uf300")       // Font Linux
		initIcon("centOS", "\uf301")     // Font Linux
		initIcon("debian", "\uf302")     // Font Linux
		initIcon("fedora", "\uf303")     // Font Linux
		initIcon("mint", "\uf304")       // Font Linux
		initIcon("SUSE", "\uf308")       // Font Linux
		initIcon("ubuntu", "\uf30c")     // Font Linux
		initIcon("elementary", "\uf311") // Font Linux
		initIcon("linux", "\uf17c")      // Font Awesome; linux
		initIcon("bsd", "\uf30e")        // Font Linux
		initIcon("root", "\ue00a")       // Pomicons; lightning
		initIcon("readonly", "\uf023")   // Font Awesome; lock
		initIcon("failed", "\ue009")     // Pomicons; exclamation
		initIcon("job", "\ue615")        // Seti UI
		initIcon("package", "\uf187")    // Font Awesome; archive
		initIcon("rss", "\uf09e")        // Font Awesome; rss
		initIcon("home", "\uf015")       // Font Awesome; home
		initIcon("github", "\uf09b")     // Font Awesome; github
		initIcon("gitlab", "\uf296")     // Font Awesome; gitlab
		initIcon("bitbucket", "\uf171")  // Font Awesome; bitbucket
		initIcon("git", "\ue0a0")        // Powerline
		initIcon("stash", "\uf01c")      // Font Awesome; inbox
		initIcon("ahead", "\uf148")      // Font Awesome; level-up
		initIcon("behind", "\uf149")     // Font Awesome; level-down
		initIcon("modified", "\uf111")   // Unicode
		initIcon("staged", "\uf067")     // Unicode
	case "unicode":
		// TODO: test if it's possible to use \uf8ff on an Apple machine
		initIcon("apple", "\U0001f34e") // Emoji; red apple
		initIcon("arch", "")
		initIcon("centOS", "")
		initIcon("debian", "")
		initIcon("fedora", "")
		initIcon("mint", "")
		initIcon("SUSE", "")
		initIcon("ubuntu", "")
		initIcon("elementary", "")
		initIcon("linux", "\U0001f427")    // Emoji; penguin
		initIcon("bsd", "\U0001f608")      // Emoji; smiling face with horns
		initIcon("root", "\u26a1")         // Emoji; high voltage
		initIcon("readonly", "\U0001f512") // Emoji; locked
		initIcon("failed", "\u2757")       // Emoji; exclamation mark
		initIcon("job", "\u2699")          // Emoji; gear
		initIcon("package", "\U0001f4e6")  // Emoji; package
		initIcon("rss", "*")
		initIcon("home", "\U0001f3e0") // Emoji; house
		initIcon("github", "")
		initIcon("gitlab", "")
		initIcon("bitbucket", "")
		initIcon("git", "")
		initIcon("stash", "\U0001f4e5") // Emoji; inbox tray
		initIcon("ahead", "\u2191")     // Unicode
		initIcon("behind", "\u2193")    // Unicode
		initIcon("modified", "\u25cf")  // Unicode
		initIcon("staged", "\u271a")    // Unicode
	case "ascii":
		initIcon("apple", "")
		initIcon("arch", "")
		initIcon("centOS", "")
		initIcon("debian", "")
		initIcon("fedora", "")
		initIcon("mint", "")
		initIcon("SUSE", "")
		initIcon("ubuntu", "")
		initIcon("elementary", "")
		initIcon("linux", "")
		initIcon("bsd", "")
		initIcon("root", "#")
		initIcon("readonly", "@")
		initIcon("failed", "!")
		initIcon("job", "&")
		initIcon("package", "")
		initIcon("rss", "*")
		initIcon("home", "~")
		initIcon("github", "")
		initIcon("gitlab", "")
		initIcon("bitbucket", "")
		initIcon("git", "")
		initIcon("stash", "#")
		initIcon("ahead", ">")
		initIcon("behind", "<")
		initIcon("modified", "*")
		initIcon("staged", "+")
	}
}

func initIcon(name, defaultValue string) {
	env := os.Getenv("BRONZE_ICON_" + strings.ToUpper(name))
	if env == "" {
		icons[name] = defaultValue
	} else {
		icons[name] = env
	}
}
<commit_msg>fixed os code points<commit_after>package main

import (
	"os"
	"strings"
)

var separator = os.Getenv("BRONZE_SEPARATOR")
var thinSeparator = os.Getenv("BRONZE_THIN_SEPARATOR")
var iconMode = os.Getenv("BRONZE_ICONS")
var icons = map[string]string{}

func init() {
	// set defaults
	if separator == "" {
		separator = "\ue0b0" // Powerline
	}
	if thinSeparator == "" {
		thinSeparator = "\ue0b1" // Powerline
	}
	if iconMode == "" {
		iconMode = "nerd"
	}

	switch iconMode {
	case "nerd":
		initIcon("apple", "\uf179")      // Font Awesome; apple
		initIcon("arch", "\uf303")       // Font Linux
		initIcon("centOS", "\uf304")     // Font Linux
		initIcon("debian", "\uf306")     // Font Linux
		initIcon("fedora", "\uf30a")     // Font Linux
		initIcon("mint", "\uf30e")       // Font Linux
		initIcon("SUSE", "\uf314")       // Font Linux
		initIcon("ubuntu", "\uf31b")     // Font Linux
		initIcon("elementary", "\uf309") // Font Linux
		initIcon("linux", "\uf31a")      // Font Linux
		initIcon("bsd", "\uf30e")        // Font Linux
		initIcon("root", "\ue00a")       // Pomicons; lightning
		initIcon("readonly", "\uf023")   // Font Awesome; lock
		initIcon("failed", "\ue009")     // Pomicons; exclamation
		initIcon("job", "\ue615")        // Seti UI
		initIcon("package", "\uf187")    // Font Awesome; archive
		initIcon("rss", "\uf09e")        // Font Awesome; rss
		initIcon("home", "\uf015")       // Font Awesome; home
		initIcon("github", "\uf09b")     // Font Awesome; github
		initIcon("gitlab", "\uf296")     // Font Awesome; gitlab
		initIcon("bitbucket", "\uf171")  // Font Awesome; bitbucket
		initIcon("git", "\ue0a0")        // Powerline
		initIcon("stash", "\uf01c")      // Font Awesome; inbox
		initIcon("ahead", "\uf148")      // Font Awesome; level-up
		initIcon("behind", "\uf149")     // Font Awesome; level-down
		initIcon("modified", "\uf111")   // Unicode
		initIcon("staged", "\uf067")     // Unicode
	case "unicode":
		// TODO: test if it's possible to use \uf8ff on an Apple machine
		initIcon("apple", "\U0001f34e") // Emoji; red apple
		initIcon("arch", "")
		initIcon("centOS", "")
		initIcon("debian", "")
		initIcon("fedora", "")
		initIcon("mint", "")
		initIcon("SUSE", "")
		initIcon("ubuntu", "")
		initIcon("elementary", "")
		initIcon("linux", "\U0001f427")    // Emoji; penguin
		initIcon("bsd", "\U0001f608")      // Emoji; smiling face with horns
		initIcon("root", "\u26a1")         // Emoji; high voltage
		initIcon("readonly", "\U0001f512") // Emoji; locked
		initIcon("failed", "\u2757")       // Emoji; exclamation mark
		initIcon("job", "\u2699")          // Emoji; gear
		initIcon("package", "\U0001f4e6")  // Emoji; package
		initIcon("rss", "*")
		initIcon("home", "\U0001f3e0") // Emoji; house
		initIcon("github", "")
		initIcon("gitlab", "")
		initIcon("bitbucket", "")
		initIcon("git", "")
		initIcon("stash", "\U0001f4e5") // Emoji; inbox tray
		initIcon("ahead", "\u2191")     // Unicode
		initIcon("behind", "\u2193")    // Unicode
		initIcon("modified", "\u25cf")  // Unicode
		initIcon("staged", "\u271a")    // Unicode
	case "ascii":
		initIcon("apple", "")
		initIcon("arch", "")
		initIcon("centOS", "")
		initIcon("debian", "")
		initIcon("fedora", "")
		initIcon("mint", "")
		initIcon("SUSE", "")
		initIcon("ubuntu", "")
		initIcon("elementary", "")
		initIcon("linux", "")
		initIcon("bsd", "")
		initIcon("root", "#")
		initIcon("readonly", "@")
		initIcon("failed", "!")
		initIcon("job", "&")
		initIcon("package", "")
		initIcon("rss", "*")
		initIcon("home", "~")
		initIcon("github", "")
		initIcon("gitlab", "")
		initIcon("bitbucket", "")
		initIcon("git", "")
		initIcon("stash", "#")
		initIcon("ahead", ">")
		initIcon("behind", "<")
		initIcon("modified", "*")
		initIcon("staged", "+")
	}
}

func initIcon(name, defaultValue string) {
	env := os.Getenv("BRONZE_ICON_" + strings.ToUpper(name))
	if env == "" {
		icons[name] = defaultValue
	} else {
		icons[name] = env
	}
}
<|endoftext|>


<commit_before>package orgtracker

import (
	sl "github.com/djosephsen/slacker/slackerlib"
	"encoding/json"
	"regexp"
	"fmt"
	"strings"
)

const ORGS string = `OT:ORGS`

type Org struct{
	Name string
	Members	map[string]sl.User
}

func getOrgs(bot *sl.Sbot) (map[string]Org, error){
// load in the orgs struct from brain
 	orgs := make(map[string]Org)
	brain := *bot.Brain
	if orgson, err := brain.Get(ORGS); err != nil{
		//if notFound,_ := regexp.MatchString(
		if err.Error() != `key OT:ORGS was not found`{
			return orgs, err
		}
	}else if err := json.Unmarshal(orgson, &orgs); err != nil{
		return orgs, err
	}
	return orgs, nil
}

func setOrgs(bot *sl.Sbot, orgs map[string]Org) error{
// write the orgs struct to brain
	brain := *bot.Brain
	if orgson, err := json.Marshal(&orgs); err !=nil{
		return err
	}else if err = brain.Set(ORGS, orgson); err != nil{
		return err
	}
	return nil
}

var QueryPeeps = sl.MessageHandler{
	Name: `OrgTracker, Query Peeps`,
	Usage:`"<botname> who is from <orgname>" returns the users from <orgname>`,
	Method: `RESPOND`,
	Pattern: `(?i)who is from (\w+)`,
	Run:	func(e *sl.Event, match []string){
		orgName := match[1]
		orgID := strings.ToLower(orgName)
		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}

		if org, exists := orgs[orgID]; exists{
			users := fmt.Sprintf("Members from %s:\n",orgName)
			for _,peep := range org.Members{
				users = fmt.Sprintf("%s\n%s", users, peep.Name)
			}
			e.Respond(users)
			return
		}else{
			e.Respond(fmt.Sprintf("sorry, no org called: %s", orgName))
			return
		}
	},
}

var ManageOrg = sl.MessageHandler{
	Name: `OrgTracker: Manage Org`,
	Usage:`"<botname> (add|delete) org <orgname>" adds or deletes an org called <orgname>`,
	Method: `RESPOND`,
	Pattern: `(?i)(add|delete) org (\w+)`,
	Run:	func(e *sl.Event, match []string){
		cmd:=match[1]
		orgName:=match[2]
		orgID := strings.ToLower(orgName)
		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}

		if isAdd,_ := regexp.MatchString( `(?i)add`, cmd); isAdd{
			newOrg:=Org{
				Name:	strings.ToLower(orgName),
				Members: make(map[string]sl.User),
			}
			orgs[orgID] = newOrg
			if err := setOrgs(e.Sbot, orgs); err != nil{
				e.Reply(fmt.Sprintf("sorry I couldn't add %s because my brain says: %s", orgName, err))
				return
			}
			e.Reply(fmt.Sprintf("sure thing. Org: %s added", orgName))
		}

		if isDelete,_ := regexp.MatchString( `(?i)delete`, cmd); isDelete{
			delete(orgs,orgID)
			if err := setOrgs(e.Sbot, orgs); err != nil{
				e.Reply(fmt.Sprintf("blerg, couldn't delete %s because my brain says: %s", orgName, err))
				return
			}

			e.Reply(fmt.Sprintf("sure thing. Org: %s deleted", orgName))
		}
	},
}

var JoinOrg = sl.MessageHandler{
	Name: `OrgTracker: Join Org`,
	Usage:`"<botname> (join|leave) org <orgname>" adds or removes you to/from <orgname>`,
	Method: `RESPOND`,
	Pattern: `(?i)(join|leave) org (\w+)`,
	Run:	func(e *sl.Event, match []string){
		cmd:=match[1]
		orgName:=match[2]
		orgID := strings.ToLower(orgName)
		var org Org
		var exists bool

		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}

		if isJoin,_ := regexp.MatchString( `(?i)join`, cmd); isJoin{
			if org, exists = orgs[orgID]; !exists{
				e.Reply(fmt.Sprintf("(Creating new org: %s first)",orgName))
				org=Org{
					Name:	strings.ToLower(orgName),
					Members: make(map[string]sl.User),
				}
				orgs[orgID]=org
			}
			user:=e.Sbot.Meta.GetUser(e.User)
			if _,exists := org.Members[user.ID]; exists{
				e.Reply(fmt.Sprintf("user: %s already belongs to %s (sorry)",user.Name, orgName))
			}else{
				org.Members[user.ID] = *user
				if err := setOrgs(e.Sbot, orgs); err != nil{
					e.Reply(fmt.Sprintf("derp.. I couldn't add %s. Brain trouble: %s", user.Name, err))
					return
				}
				e.Reply(fmt.Sprintf("OK! user: %s now belongs to %s",user.Name, orgName))
				return
			}
		}
		if isLeave,_ := regexp.MatchString( `(?i)leave`, cmd); isLeave{
			if org,exists := orgs[orgID]; exists{
				user:=e.Sbot.Meta.GetUser(e.User)
				delete (org.Members,user.ID)
				e.Reply(fmt.Sprintf("OK! %s is no longer in %s",user.Name, orgName))
			}else{
				e.Reply(fmt.Sprintf("No such org: %s (sorry)",orgName))
			}
		}
	},
}

var ListOrgs = sl.MessageHandler{
	Name: `OrgTracker: List Orgs`,
	Usage:`"<botname> list orgs" lists the available orgs`,
	Method: `RESPOND`,
	Pattern: `(?i)list orgs`,
	Run:	func(e *sl.Event, match []string){
		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}
		reply:=`Existing Orgs:`
		for orgid,org := range orgs{
			reply=fmt.Sprintf("%s\n%s, (%d members)",reply, orgid, len(org.Members))
		}
		e.Reply(reply)
	},
}
<commit_msg>refactored to be 825% less dumb<commit_after>package orgtracker

import (
	sl "github.com/djosephsen/slacker/slackerlib"
	"encoding/json"
	"regexp"
	"fmt"
	"strings"
)

const ORGS string = `OT:ORGS`

type Org struct{
	Name string
	Members	map[string]sl.User
}

var OrgTracker = sl.MessageHandler{
	Name: `OrgTracker`,
	Usage:`<botname> [add|delete|join|leave|list] org <orgname>`,
	Method: `RESPOND`,
	Pattern: `(?i)(add|delete|join|leave|list) org *(\w*)`,
	Run:	func(e *sl.Event, match []string){
		var orgName, orgID string
		cmd := match[1]
		if len(match) >= 2{
			orgName = match[2]
			orgID = strings.ToLower(orgName)
		}
		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}
		if isAdd,_ := regexp.MatchString( `(?i)add`, cmd); isAdd{
			if err := addOrg(e.Sbot,orgs,orgID); err!=nil{
				e.Reply(fmt.Sprintf("sorry I couldn't add %s because: %s", orgName, err))
				return
			}
			e.Reply(fmt.Sprintf("sure thing. Org: %s added", orgName))
			return
		}else if isDel,_ := regexp.MatchString( `(?i)delete`, cmd); isDel{
			if err := deleteOrg(e.Sbot,orgs,orgID); err!=nil{
				e.Reply(fmt.Sprintf("gar. %s", err))
				return
			}
			e.Reply(fmt.Sprintf("Ok. Org: %s deleted", orgName))
			return
		}else if isJoin,_ := regexp.MatchString( `(?i)join`, cmd); isJoin{
			if _, exists := orgs[orgID]; !exists{
				e.Reply(fmt.Sprintf("(Creating new org: %s first)",orgName))
				if err := addOrg(e.Sbot,orgs,orgID); err!=nil{
					e.Reply(fmt.Sprintf("sorry I couldn't add %s because: %s", orgName, err))
					return
				}
			}
			if err := joinOrg(e,orgs,orgID); err!=nil{
					e.Reply(fmt.Sprintf("derp. %s", err))
					return
			}
			user:=e.Sbot.Meta.GetUser(e.User)
			e.Reply(fmt.Sprintf("OK! user: %s now belongs to %s",user.Name, orgName))
			return
		}else if isLeave,_ := regexp.MatchString( `(?i)leave`, cmd); isLeave{
			if err := leaveOrg(e,orgs,orgID); err!=nil{
					e.Reply(fmt.Sprintf("bleh. %s", err))
					return
			}
			user:=e.Sbot.Meta.GetUser(e.User)
			e.Reply(fmt.Sprintf("OK! removed user: %s from org: %s",user.Name, orgName))
			return
		}else if isList,_ := regexp.MatchString( `(?i)list`, cmd); isList{
			if reply, err := listOrg(orgs,orgID); err!=nil{
				e.Reply(fmt.Sprintf("sorry. %s", err))
				return
			}else{
				e.Reply(reply)
				return
			}
		}
	},
}

func listOrg(orgs map[string]Org, orgID string) (string, error){
	if orgID != ``{
		if org, exists := orgs[orgID]; exists{
			users := fmt.Sprintf("Members from %s:\n",orgID)
			for _,peep := range org.Members{
				users = fmt.Sprintf("%s\n%s", users, peep.Name)
			}
			return users, nil
		}else{
			return ``,fmt.Errorf("no org called: %s", orgID)
		}
	}else{
		reply:=`Existing Orgs:`
		for orgid,org := range orgs{
			reply=fmt.Sprintf("%s\n%s, (%d members)",reply, orgid, len(org.Members))
		}
		return reply,nil
	}
}

func addOrg(bot *sl.Sbot, orgs map[string]Org, orgID string) error{
	newOrg:=Org{
		Name:	orgID,
		Members: make(map[string]sl.User),
	}
	orgs[orgID] = newOrg
	if err := setOrgs(bot, orgs); err != nil{
		return fmt.Errorf("I couldn't add %s because my brain says: %s", orgID, err)
	}
	return nil
}

func deleteOrg(bot *sl.Sbot, orgs map[string]Org, orgID string) error{
	delete(orgs,orgID)
	if err := setOrgs(bot, orgs); err != nil{
		return fmt.Errorf("I couldn't delete %s because my brain says: %s", orgID, err)
	}
	return nil
}

func joinOrg(e *sl.Event, orgs map[string]Org, orgID string) error{
	user := e.Sbot.Meta.GetUser(e.User)
	org := orgs[orgID]
	if _,exists := org.Members[user.ID]; exists{
		return fmt.Errorf("user: %s already belongs to %s (sorry)",user.Name, orgID)
	}else{
		org.Members[user.ID] = *user
		if err := setOrgs(e.Sbot, orgs); err != nil{
			return fmt.Errorf("I couldn't add %s. Brain trouble: %s", user.Name, err)
		}
		return nil
	}
}

func leaveOrg(e *sl.Event, orgs map[string]Org, orgID string) error{
	if org,exists := orgs[orgID]; exists{
		user:=e.Sbot.Meta.GetUser(e.User)
		delete (org.Members,user.ID)
		if err := setOrgs(e.Sbot, orgs); err != nil{
			return fmt.Errorf("I couldn't delete %s. Brain trouble: %s", user.Name, err)
		}
		return nil
	}else{
		return fmt.Errorf("No such org: %s (sorry)",orgID)
	}
}

func getOrgs(bot *sl.Sbot) (map[string]Org, error){
// load in the orgs struct from brain
 	orgs := make(map[string]Org)
	brain := *bot.Brain
	if orgson, err := brain.Get(ORGS); err != nil{
		//if notFound,_ := regexp.MatchString(
		if err.Error() != `key OT:ORGS was not found`{
			return orgs, err
		}
	}else if err := json.Unmarshal(orgson, &orgs); err != nil{
		return orgs, err
	}
	return orgs, nil
}

func setOrgs(bot *sl.Sbot, orgs map[string]Org) error{
// write the orgs struct to brain
	brain := *bot.Brain
	if orgson, err := json.Marshal(&orgs); err !=nil{
		return err
	}else if err = brain.Set(ORGS, orgson); err != nil{
		return err
	}
	return nil
}

var WhoIsFrom = sl.MessageHandler{
	Name: `OrgTracker: Who-IS-FROM`,
	Usage:`"<botname> who is from <org>" :: lists members who are from org`,
	Method: `RESPOND`,
	Pattern: `(?i)who is from (\w*)`,
	Run:	func(e *sl.Event, match []string){
		orgName := match[1]
		orgID := strings.ToLower(orgName)
		orgs, err := getOrgs(e.Sbot)
		if err != nil{
			e.Respond(fmt.Sprintf("ack! I couldn't load my orgs struct! %s", err))
			sl.Logger.Debug(err)
			return
		}
		if reply, err := listOrg(orgs,orgID); err!=nil{
			e.Reply(fmt.Sprintf("sorry. %s", err))
			return
		}else{
			e.Reply(reply)
			return
		}
	},
}

<|endoftext|>


<commit_before>package core

import (
	"bytes"
	"fmt"
)

type GeneralRegs [13]uint32
type RegIndex uint8

type SPRegs [2]uint32
type SPType uint8

const (
	MSP SPType = 0
	PSP SPType = 1
)

type Apsr struct {
	N  bool
	Z  bool
	C  bool
	V  bool
	Q  bool
	GE uint8
}

type Ipsr struct {
	ExcpNum uint16
}

type Epsr struct {
	T   bool
	ICI uint16
	IT  uint16
}

type Mode uint8

const (
	MODE_THREAD Mode = iota
	MODE_HANDLER
)

type Control struct {
	Npriv bool
	Spsel SPType
	Fpca  bool
}

type Registers struct {
	r         GeneralRegs
	sp        SPRegs
	lr        uint32
	pc        uint32
	Apsr      Apsr
	Ipsr      Ipsr
	Epsr      Epsr
	Mode      Mode
	Primask   bool
	Faultmask bool
	Basepri   uint8
	Control   Control
}

/* Special registers in r13-15 */
const (
	SP RegIndex = 13
	LR RegIndex = 14
	PC RegIndex = 15
)

func (regs Registers) R(i RegIndex) uint32 {
	switch i {
	default:
		return regs.r[i]
	case SP:
		sp := regs.LookupSP()
		return regs.sp[sp]
	case LR:
		return regs.lr
	case PC:
		return regs.pc
	}
}

func (regs *Registers) SetR(i RegIndex, value uint32) {
	switch i {
	default:
		regs.r[i] = value
	case SP:
		sp := regs.LookupSP()
		regs.sp[sp] = value
	case LR:
		regs.lr = value
	case PC:
		regs.pc = value
	}
}

func (regs Registers) LookupSP() SPType {
	if regs.Control.Spsel == PSP && regs.Mode == MODE_THREAD {
		return PSP
	}

	return MSP
}

func (regs Registers) Lr() uint32 {
	return regs.R(LR)
}

func (regs Registers) Sp() uint32 {
	return regs.R(SP)
}

func (regs Registers) Msp() uint32 {
	return regs.sp[MSP]
}

func (regs Registers) Psp() uint32 {
	return regs.sp[PSP]
}

func (regs Registers) Pc() uint32 {
	return regs.R(PC)
}

func (regs Registers) InITBlock() bool {
	return (regs.Epsr.IT & 0xf) != 0
}

func (regs Registers) LastInITBlock() bool {
	return (regs.Epsr.IT & 0xf) == 0x8
}

func (regs *Registers) BranchTo(addr uint32) {
	regs.SetR(PC, addr)
}

func (regs *Registers) BranchWritePC(addr uint32) {
	addr &^= 0x1 // Clear the thumb bit

	regs.BranchTo(addr)
}

func (regs *Registers) ALUWritePC(addr uint32) {
	regs.BranchWritePC(addr)
}

func (regs Registers) Pretty() string {
	var b bytes.Buffer
	var i RegIndex

	for i = 0; i <= 12; i++ {
		if i != 0 {
			if (i % 4) == 0 {
				fmt.Fprintf(&b, "\n")
			} else {
				fmt.Fprintf(&b, "\t")
			}
		}
		fmt.Fprintf(&b, "R%-2d = %#x", i, regs.R(i))
	}

	fmt.Fprintf(&b, "\tSP (R13) = %#x", regs.R(SP))
	fmt.Fprintf(&b, "\tLR (R14) = %#x", regs.R(LR))
	fmt.Fprintf(&b, "\tPC (R15) = %#x\n", regs.R(PC))

	fmt.Fprintf(&b, "MSP = %#x\tPSP = %#x\n", regs.Msp(), regs.Psp())

	fmt.Fprintf(&b, "BASEPRI = %d\tPRIMASK = %d\tFAULTMASK = %d\n", regs.Basepri,
		booltou(regs.Primask), booltou(regs.Faultmask))

	fmt.Fprintf(&b, "CONTROL: nPRIV = %d SPSEL = %d FPCA = %d\n", booltou(regs.Control.Npriv),
		uint8(regs.Control.Spsel), booltou(regs.Control.Fpca))

	fmt.Fprintf(&b, "APSR: N = %d Z = %d C = %d V = %d Q = %d GE = %d\n",
		booltou(regs.Apsr.N), booltou(regs.Apsr.Z), booltou(regs.Apsr.C),
		booltou(regs.Apsr.V), booltou(regs.Apsr.Q), regs.Apsr.GE)

	fmt.Fprintf(&b, "EPSR: T = %d ICI = %#x IT = %#x\n", booltou(regs.Epsr.T),
		regs.Epsr.ICI, regs.Epsr.IT)

	fmt.Fprintf(&b, "IPSR: EXCPNUM = %d\n", regs.Ipsr.ExcpNum)

	return b.String()
}

func (regs Registers) Print() {
	fmt.Print(regs.Pretty())
}

func (i RegIndex) String() string {
	switch i {
	default:
		return fmt.Sprintf("r%d", i)
	case SP:
		return "sp"
	case LR:
		return "lr"
	case PC:
		return "pc"
	}
}
<commit_msg>Document special register bits<commit_after>package core

import (
	"bytes"
	"fmt"
)

type GeneralRegs [13]uint32
type RegIndex uint8

type SPRegs [2]uint32
type SPType uint8

const (
	MSP SPType = 0
	PSP SPType = 1
)

type Apsr struct {
	N  bool  // Negative
	Z  bool  // Zero
	C  bool  // Carry
	V  bool  // Overflow
	Q  bool  // Saturation
	GE uint8 // Greater than or equal flags
}

type Ipsr struct {
	ExcpNum uint16
}

type Epsr struct {
	T   bool   // Thumb bit
	ICI uint16 // Interrupt-continue
	IT  uint16 // IT block flags
}

type Mode uint8

const (
	MODE_THREAD Mode = iota
	MODE_HANDLER
)

type Control struct {
	Npriv bool   // NOT privilege
	Spsel SPType // SP select
	Fpca  bool   // FP extension enable
}

type Registers struct {
	r         GeneralRegs
	sp        SPRegs
	lr        uint32
	pc        uint32
	Apsr      Apsr
	Ipsr      Ipsr
	Epsr      Epsr
	Mode      Mode
	Primask   bool
	Faultmask bool
	Basepri   uint8
	Control   Control
}

/* Special registers in r13-15 */
const (
	SP RegIndex = 13
	LR RegIndex = 14
	PC RegIndex = 15
)

func (regs Registers) R(i RegIndex) uint32 {
	switch i {
	default:
		return regs.r[i]
	case SP:
		sp := regs.LookupSP()
		return regs.sp[sp]
	case LR:
		return regs.lr
	case PC:
		return regs.pc
	}
}

func (regs *Registers) SetR(i RegIndex, value uint32) {
	switch i {
	default:
		regs.r[i] = value
	case SP:
		sp := regs.LookupSP()
		regs.sp[sp] = value
	case LR:
		regs.lr = value
	case PC:
		regs.pc = value
	}
}

func (regs Registers) LookupSP() SPType {
	if regs.Control.Spsel == PSP && regs.Mode == MODE_THREAD {
		return PSP
	}

	return MSP
}

func (regs Registers) Lr() uint32 {
	return regs.R(LR)
}

func (regs Registers) Sp() uint32 {
	return regs.R(SP)
}

func (regs Registers) Msp() uint32 {
	return regs.sp[MSP]
}

func (regs Registers) Psp() uint32 {
	return regs.sp[PSP]
}

func (regs Registers) Pc() uint32 {
	return regs.R(PC)
}

func (regs Registers) InITBlock() bool {
	return (regs.Epsr.IT & 0xf) != 0
}

func (regs Registers) LastInITBlock() bool {
	return (regs.Epsr.IT & 0xf) == 0x8
}

func (regs *Registers) BranchTo(addr uint32) {
	regs.SetR(PC, addr)
}

func (regs *Registers) BranchWritePC(addr uint32) {
	addr &^= 0x1 // Clear the thumb bit

	regs.BranchTo(addr)
}

func (regs *Registers) ALUWritePC(addr uint32) {
	regs.BranchWritePC(addr)
}

func (regs Registers) Pretty() string {
	var b bytes.Buffer
	var i RegIndex

	for i = 0; i <= 12; i++ {
		if i != 0 {
			if (i % 4) == 0 {
				fmt.Fprintf(&b, "\n")
			} else {
				fmt.Fprintf(&b, "\t")
			}
		}
		fmt.Fprintf(&b, "R%-2d = %#x", i, regs.R(i))
	}

	fmt.Fprintf(&b, "\tSP (R13) = %#x", regs.R(SP))
	fmt.Fprintf(&b, "\tLR (R14) = %#x", regs.R(LR))
	fmt.Fprintf(&b, "\tPC (R15) = %#x\n", regs.R(PC))

	fmt.Fprintf(&b, "MSP = %#x\tPSP = %#x\n", regs.Msp(), regs.Psp())

	fmt.Fprintf(&b, "BASEPRI = %d\tPRIMASK = %d\tFAULTMASK = %d\n", regs.Basepri,
		booltou(regs.Primask), booltou(regs.Faultmask))

	fmt.Fprintf(&b, "CONTROL: nPRIV = %d SPSEL = %d FPCA = %d\n", booltou(regs.Control.Npriv),
		uint8(regs.Control.Spsel), booltou(regs.Control.Fpca))

	fmt.Fprintf(&b, "APSR: N = %d Z = %d C = %d V = %d Q = %d GE = %d\n",
		booltou(regs.Apsr.N), booltou(regs.Apsr.Z), booltou(regs.Apsr.C),
		booltou(regs.Apsr.V), booltou(regs.Apsr.Q), regs.Apsr.GE)

	fmt.Fprintf(&b, "EPSR: T = %d ICI = %#x IT = %#x\n", booltou(regs.Epsr.T),
		regs.Epsr.ICI, regs.Epsr.IT)

	fmt.Fprintf(&b, "IPSR: EXCPNUM = %d\n", regs.Ipsr.ExcpNum)

	return b.String()
}

func (regs Registers) Print() {
	fmt.Print(regs.Pretty())
}

func (i RegIndex) String() string {
	switch i {
	default:
		return fmt.Sprintf("r%d", i)
	case SP:
		return "sp"
	case LR:
		return "lr"
	case PC:
		return "pc"
	}
}
<|endoftext|>


<commit_before>// This program watches for recently changed stardew save files and publishes them to the
// Stardew Rocks AMQP server.
package main

import (
	"io/ioutil"
	"log"
	"os"
	"path"

	"path/filepath"

	"github.com/fsnotify/fsnotify"
)

func stardewFolder() string {
	return path.Join(os.Getenv("AppData"), "StardewValley/Saves")
}

func allSaveGameInfos() ([]string, error) {
	return filepath.Glob(stardewFolder() + "/*/SaveGameInfo")
}

func main() {

	ch, close, err := rabbitStart()
	if err != nil {
		log.Fatal(err)
	}
	defer close()

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					log.Println("modified file:", event.Name)
					content, err := ioutil.ReadFile(event.Name)
					if err != nil {
						log.Printf("could not read file %v: %v", event.Name, err)
						continue
					}
					if err := publishSavedGame(ch, content); err != nil {
						log.Print("could not publish new save game content:", err)
						continue
					}
					log.Print("New save game published")

				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()

	gameSaves, err := allSaveGameInfos()
	if err != nil {
		log.Fatal(err)
	}
	for _, save := range gameSaves {
		err = watcher.Add(save)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Watching %v", save)
	}

	select {}

}
<commit_msg>Make the file watcher work with the real game<commit_after>// This program watches for recently changed stardew save files and publishes them to the
// Stardew Rocks AMQP server.
package main

import (
	"io/ioutil"
	"log"
	"os"
	"path"
	"strings"

	"path/filepath"

	"github.com/fsnotify/fsnotify"
	"github.com/streadway/amqp"
)

func stardewFolder() string {
	return path.Join(os.Getenv("AppData"), "StardewValley/Saves")
}

func allSaveGameInfos() ([]string, error) {
	return filepath.Glob(stardewFolder() + "/*/SaveGameInfo")
}

func isDir(path string) bool {
	fileInfo, err := os.Stat(path)
	if err != nil {
		return false
	}
	return fileInfo.IsDir()
}

func watchAndPublish(topic *amqp.Channel) {

	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal(err)
	}
	defer watcher.Close()

	gameSaves, err := allSaveGameInfos()
	if err != nil {
		log.Fatal(err)
	}

	// Find new folders containing new saved games.
	err = watcher.Add(stardewFolder())
	if err != nil {
		log.Fatal(err)
	}

	watched := map[string]bool{}

	for _, save := range gameSaves {
		err = watcher.Add(save)
		if err != nil {
			log.Fatal(err)
		}
		log.Printf("Watching %v", save)
		watched[save] = true
	}

	done := make(chan bool)
	go func() {
		for {
			select {
			case event := <-watcher.Events:
				if event.Op&fsnotify.Write == fsnotify.Write {
					log.Println("Found modified file:", event.Name)
					switch {
					case isDir(event.Name):
						if !watched[event.Name] {
							watcher.Add(event.Name)
							watched[event.Name] = true
						}
						// The game is saved in a temporary file first (Directory/SaveGameInfo_STARDEWVALLEYTMP)
					case strings.Contains(path.Base(event.Name), "SaveGameInfo"):
						content, err := ioutil.ReadFile(event.Name)
						if err != nil {
							log.Printf("Could not read file %v: %v", event.Name, err)
							continue
						}
						if err := publishSavedGame(topic, content); err != nil {
							log.Print("could not publish new save game content:", err)
							continue
						}
						log.Print("New save game published")
					}
				}
			case err := <-watcher.Errors:
				log.Println("error:", err)
			}
		}
	}()
	<-done
}

func main() {

	topic, close, err := rabbitStart()
	if err != nil {
		log.Fatal(err)
	}
	defer close()

	watchAndPublish(topic)
}
<|endoftext|>