<commit_before>package configuration

import (
	"testing"

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

func TestEnvironmentVariabelsEmpty(t *testing.T) {
	// arrange
	conf := Config{}

	// act
	SetEnvironmentVariables(&conf)

	// assert
	assert.NotNil(t, conf, "Configuration should not be nil")
}

func TestEnvironmentVariabels(t *testing.T) {
	// arrange
	conf := Config{}
	server := "server"
	host := "host"
	port := "8080"
	portParsed, _ := strconv.Atoi(port)
	externalUri := "uri"
	maxEntities := "1"
	maxEntitiesParsed, _ := strconv.Atoi(maxEntities)
	indentJSON := "true"
	indentJSONParsed, _ := strconv.ParseBool(indentJSON)
	https := "true"
	httpsParsed, _ := strconv.ParseBool(https)
	httpsKey := "key"
	httpsCert := "cert"
	mqttEnabled := "true"
	mqttEnabledParsed, _ := strconv.ParseBool(mqttEnabled)
	mqttHost := "mqtt_host"
	mqttPort := "9001"
	mqttPortParsed, _ := strconv.Atoi(mqttPort)
	dbSSLEnabled := "true"
	dbSSLEnabledParsed, _ := strconv.ParseBool(dbSSLEnabled)
	dbHost := "db_host"
	dbPort := "5432"
	dbPortParsed, _ := strconv.Atoi(dbPort)
	dbUser := "user"
	dbPassword := "secret"
	dbDB := "gost"
	dbSchema := "v1"
	dbMaxIdleCons := "1"
	dbMaxIdleConsParsed, _ := strconv.Atoi(dbMaxIdleCons)
	dbMaxOpenCons := "1"
	dbMaxOpenConsParsed, _ := strconv.Atoi(dbMaxOpenCons)
	// act
	os.Setenv("GOST_SERVER_NAME", server)
	os.Setenv("GOST_SERVER_HOST", host)
	os.Setenv("GOST_SERVER_PORT", port)
	os.Setenv("GOST_SERVER_EXTERNAL_URI", externalUri)
	os.Setenv("GOST_SERVER_MAX_ENTITIES", maxEntities)
	os.Setenv("GOST_SERVER_INDENT_JSON", indentJSON)
	os.Setenv("GOST_SERVER_HTTPS", https)
	os.Setenv("GOST_SERVER_HTTPS_KEY", httpsKey)
	os.Setenv("GOST_SERVER_HTTPS_CERT", httpsCert)
	os.Setenv("GOST_MQTT_ENABLED", mqttEnabled)
	os.Setenv("GOST_MQTT_HOST", mqttHost)
	os.Setenv("GOST_MQTT_PORT", mqttPort)
	os.Setenv("GOST_DB_HOST", dbHost)
	os.Setenv("GOST_DB_PORT", dbPort)
	os.Setenv("GOST_DB_USER", dbUser)
	os.Setenv("GOST_DB_PASSWORD", dbPassword)
	os.Setenv("GOST_DB_DATABASE", dbDB)
	os.Setenv("GOST_DB_SCHEMA", dbSchema)
	os.Setenv("GOST_MQTT_SSL_ENABLED", dbSSLEnabled)
	os.Setenv("GOST_DB_MAX_IDLE_CONS", dbMaxIdleCons)
	os.Setenv("GOST_DB_MAX_OPEN_CONS", dbMaxOpenCons)

	SetEnvironmentVariables(&conf)

	// assert
	assert.NotNil(t, conf, "Configuration should not be nil")
	assert.Equal(t, server, conf.Server.Name)
	assert.Equal(t, externalUri, conf.Server.ExternalURI)
	assert.Equal(t, host, conf.Server.Host)
	assert.Equal(t, httpsParsed, conf.Server.HTTPS)
	assert.Equal(t, httpsCert, conf.Server.HTTPSCert)
	assert.Equal(t, httpsKey, conf.Server.HTTPSKey)
	assert.Equal(t, indentJSONParsed, conf.Server.IndentedJSON)
	assert.Equal(t, maxEntitiesParsed, conf.Server.MaxEntityResponse)
	assert.Equal(t, portParsed, conf.Server.Port)
	assert.Equal(t, mqttHost, conf.MQTT.Host)
	assert.Equal(t, mqttEnabledParsed, conf.MQTT.Enabled)
	assert.Equal(t, mqttPortParsed, conf.MQTT.Port)
	assert.Equal(t, dbDB, conf.Database.Database)
	assert.Equal(t, dbPortParsed, conf.Database.Port)
	assert.Equal(t, dbHost, conf.Database.Host)
	assert.Equal(t, dbMaxIdleConsParsed, conf.Database.MaxIdleConns)
	assert.Equal(t, dbMaxOpenConsParsed, conf.Database.MaxOpenConns)
	assert.Equal(t, dbPassword, conf.Database.Password)
	assert.Equal(t, dbSchema, conf.Database.Schema)
	assert.Equal(t, dbSSLEnabledParsed, conf.Database.SSL)
	assert.Equal(t, dbUser, conf.Database.User)
}
<commit_msg>Update environment_test.go<commit_after>package configuration

import (
	"testing"

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

func TestEnvironmentVariabelsEmpty(t *testing.T) {
	// arrange
	conf := Config{}

	// act
	SetEnvironmentVariables(&conf)

	// assert
	assert.NotNil(t, conf, "Configuration should not be nil")
}

func TestEnvironmentVariabels(t *testing.T) {
	// arrange
	conf := Config{}
	server := "server"
	host := "host"
	port := "8080"
	portParsed, _ := strconv.Atoi(port)
	externalUri := "uri"
	maxEntities := "1"
	maxEntitiesParsed, _ := strconv.Atoi(maxEntities)
	indentJSON := "true"
	indentJSONParsed, _ := strconv.ParseBool(indentJSON)
	https := "true"
	httpsParsed, _ := strconv.ParseBool(https)
	httpsKey := "key"
	httpsCert := "cert"
	mqttEnabled := "true"
	mqttEnabledParsed, _ := strconv.ParseBool(mqttEnabled)
	mqttHost := "mqtt_host"
	mqttPort := "9001"
	mqttPortParsed, _ := strconv.Atoi(mqttPort)
	dbSSLEnabled := "true"
	dbSSLEnabledParsed, _ := strconv.ParseBool(dbSSLEnabled)
	dbHost := "db_host"
	dbPort := "5432"
	dbPortParsed, _ := strconv.Atoi(dbPort)
	dbUser := "user"
	dbPassword := "secret"
	dbDB := "gost"
	dbSchema := "v1"
	dbMaxIdleCons := "1"
	dbMaxIdleConsParsed, _ := strconv.Atoi(dbMaxIdleCons)
	dbMaxOpenCons := "1"
	dbMaxOpenConsParsed, _ := strconv.Atoi(dbMaxOpenCons)
	// act
	os.Setenv("GOST_SERVER_NAME", server)
	os.Setenv("GOST_SERVER_HOST", host)
	os.Setenv("GOST_SERVER_PORT", port)
	os.Setenv("GOST_SERVER_EXTERNAL_URI", externalUri)
	os.Setenv("GOST_SERVER_MAX_ENTITIES", maxEntities)
	os.Setenv("GOST_SERVER_INDENT_JSON", indentJSON)
	os.Setenv("GOST_SERVER_HTTPS", https)
	os.Setenv("GOST_SERVER_HTTPS_KEY", httpsKey)
	os.Setenv("GOST_SERVER_HTTPS_CERT", httpsCert)
	os.Setenv("GOST_MQTT_ENABLED", mqttEnabled)
	os.Setenv("GOST_MQTT_HOST", mqttHost)
	os.Setenv("GOST_MQTT_PORT", mqttPort)
	os.Setenv("GOST_DB_HOST", dbHost)
	os.Setenv("GOST_DB_PORT", dbPort)
	os.Setenv("GOST_DB_USER", dbUser)
	os.Setenv("GOST_DB_PASSWORD", dbPassword)
	os.Setenv("GOST_DB_DATABASE", dbDB)
	os.Setenv("GOST_DB_SCHEMA", dbSchema)
	os.Setenv("GOST_DB_SSL_ENABLED", dbSSLEnabled)
	os.Setenv("GOST_DB_MAX_IDLE_CONS", dbMaxIdleCons)
	os.Setenv("GOST_DB_MAX_OPEN_CONS", dbMaxOpenCons)

	SetEnvironmentVariables(&conf)

	// assert
	assert.NotNil(t, conf, "Configuration should not be nil")
	assert.Equal(t, server, conf.Server.Name)
	assert.Equal(t, externalUri, conf.Server.ExternalURI)
	assert.Equal(t, host, conf.Server.Host)
	assert.Equal(t, httpsParsed, conf.Server.HTTPS)
	assert.Equal(t, httpsCert, conf.Server.HTTPSCert)
	assert.Equal(t, httpsKey, conf.Server.HTTPSKey)
	assert.Equal(t, indentJSONParsed, conf.Server.IndentedJSON)
	assert.Equal(t, maxEntitiesParsed, conf.Server.MaxEntityResponse)
	assert.Equal(t, portParsed, conf.Server.Port)
	assert.Equal(t, mqttHost, conf.MQTT.Host)
	assert.Equal(t, mqttEnabledParsed, conf.MQTT.Enabled)
	assert.Equal(t, mqttPortParsed, conf.MQTT.Port)
	assert.Equal(t, dbDB, conf.Database.Database)
	assert.Equal(t, dbPortParsed, conf.Database.Port)
	assert.Equal(t, dbHost, conf.Database.Host)
	assert.Equal(t, dbMaxIdleConsParsed, conf.Database.MaxIdleConns)
	assert.Equal(t, dbMaxOpenConsParsed, conf.Database.MaxOpenConns)
	assert.Equal(t, dbPassword, conf.Database.Password)
	assert.Equal(t, dbSchema, conf.Database.Schema)
	assert.Equal(t, dbSSLEnabledParsed, conf.Database.SSL)
	assert.Equal(t, dbUser, conf.Database.User)
}
<|endoftext|>


<commit_before>package main

// Generate errors.go which is used in returnedOnlyErrors() below.
//go:generate go run generrors.go

import (
	"database/sql"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/raft"
	"github.com/robustirc/robustirc/ircserver"
	"github.com/robustirc/robustirc/raft_store"
	"github.com/robustirc/robustirc/types"
	"github.com/sorcix/irc"
	"github.com/stapelberg/glog"

	_ "github.com/mattn/go-sqlite3"
)

// returnedOnlyErrors returns true if and only if |msgid| resulted in at least
// one error and not a single non-error IRC reply.
func returnedOnlyErrors(msgid types.RobustId) bool {
	vmsgs, _ := ircServer.Get(msgid)
	for _, msg := range vmsgs {
		ircmsg := irc.ParseMessage(msg.Data)
		if ircmsg == nil {
			glog.Errorf("Output message not parsable\n")
			continue
		}
		if !errorCodes[ircmsg.Command] {
			return false
		}
	}
	return len(vmsgs) > 0
}

type robustSnapshot struct {
	firstIndex            uint64
	lastIndex             uint64
	store                 *raft_store.LevelDBStore
	del                   map[uint64]types.RobustId
	servers               map[int64]bool
	idToIdx               map[int64]uint64
	compactionEnd         time.Time
	skipDeletionForCanary bool
}

func (s *robustSnapshot) Persist(sink raft.SnapshotSink) error {
	log.Printf("Filtering and writing up to %d indexes\n", s.lastIndex-s.firstIndex)

	// Get a timestamp and keep it constant, so that we only compact messages
	// older than n days from compactionStart. If we used time.Since, new
	// messages would pour into the window on every compaction round, possibly
	// making the compaction never converge.
	compactionStart := time.Now()
	log.Printf("compactionStart %s\n", compactionStart.String())
	if *canaryCompactionStart > 0 {
		compactionStart = time.Unix(0, *canaryCompactionStart)
		log.Printf("compactionStart %s (overridden with -canary_compaction_start)\n", compactionStart.String())
	}

	s.compactionEnd = compactionStart.Add(-7 * 24 * time.Hour)
	f, err := ioutil.TempFile(*raftDir, "temp-compaction.sqlite3")
	if err != nil {
		sink.Cancel()
		return err
	}
	tempfile := f.Name()
	f.Close()
	db, err := sql.Open("sqlite3", tempfile)
	if err != nil {
		sink.Cancel()
		return err
	}
	defer func() {
		db.Close()
		os.Remove(tempfile)
	}()
	// TODO: add an index over paramsJoin.msgid and paramsPart.msgid (cannot be primary key because it is not unique)
	const nonIrcCommandStmt = `
CREATE TABLE createSession (msgid integer not null unique primary key, session integer not null);
CREATE TABLE deleteSession (msgid integer not null unique primary key, session integer not null);
CREATE TABLE allMessages (msgid integer not null unique primary key, session integer not null);
CREATE VIEW allMessagesWin AS SELECT * FROM allMessages WHERE msgid < %d;
CREATE VIEW createSessionWin AS SELECT * FROM createSession WHERE msgid < %d;
CREATE VIEW deleteSessionWin AS SELECT * FROM deleteSession WHERE msgid < %d;
`
	if _, err := db.Exec(
		fmt.Sprintf(nonIrcCommandStmt,
			s.compactionEnd.UnixNano(),
			s.compactionEnd.UnixNano(),
			s.compactionEnd.UnixNano())); err != nil {
		sink.Cancel()
		return err
	}

	var allMessagesStmt, createSessionStmt, deleteSessionStmt *sql.Stmt
	allMessagesStmt, err = db.Prepare("INSERT INTO allMessages (msgid, session) VALUES (?, ?)")
	if err == nil {
		defer allMessagesStmt.Close()
		createSessionStmt, err = db.Prepare("INSERT INTO createSession (msgid, session) VALUES (?, ?)")
	}
	if err == nil {
		defer createSessionStmt.Close()
		deleteSessionStmt, err = db.Prepare("INSERT INTO deleteSession (msgid, session) VALUES (?, ?)")
	}
	if err == nil {
		defer deleteSessionStmt.Close()
	}
	if err != nil {
		sink.Cancel()
		return err
	}

	// Let each IRC command prepare their tables and prepared statements.
	stmts := make(map[string]*sql.Stmt)
	for name, cmd := range ircserver.Commands {
		if cmd.CompactionPrepareStmt == nil {
			continue
		}
		var err error
		stmts[name], err = cmd.CompactionPrepareStmt(db)
		if err == nil {
			defer stmts[name].Close()
			err = cmd.CompactionPrepareViews(db, s.compactionEnd)
		}
		if err != nil {
			sink.Cancel()
			return err
		}
	}

	// First pass: insert all messages into database
	iterator := s.store.GetBulkIterator(s.firstIndex, s.lastIndex+1)
	defer iterator.Release()
	available := iterator.First()
	for available {
		var nlog raft.Log
		if err := iterator.Error(); err != nil {
			glog.Errorf("Error while iterating through the log: %v", err)
			available = iterator.Next()
			continue
		}
		i := binary.BigEndian.Uint64(iterator.Key())
		value := iterator.Value()
		if err := json.Unmarshal(value, &nlog); err != nil {
			glog.Errorf("Skipping log entry %d because of a JSON unmarshaling error: %v", i, err)
			continue
		}
		available = iterator.Next()

		// TODO: compact raft messages as well, so that peer changes are not kept forever
		if nlog.Type != raft.LogCommand {
			continue
		}

		parsed := types.NewRobustMessageFromBytes(nlog.Data)
		if time.Unix(0, parsed.Id.Id).After(s.compactionEnd) {
			break
		}
		s.idToIdx[parsed.Id.Id] = i

		msgid := parsed.Id
		session := parsed.Session

		switch parsed.Type {
		case types.RobustMessageOfDeath:
			s.del[i] = parsed.Id
			continue

		case types.RobustConfig:
			// TODO: implement compaction for RobustConfig
			continue

		case types.RobustIRCToClient:
			fallthrough
		case types.RobustPing:
			fallthrough
		case types.RobustAny:
			glog.Errorf("Compaction: saw unexpected message type %v (log id %d, message id %v)\n", parsed.Type, i, parsed.Id)
			continue

		default:
			glog.Errorf("Compaction: saw message of unknown type %v (log id %d, message id %v)\n", parsed.Type, i, parsed.Id)
			continue

		case types.RobustCreateSession:
			session = parsed.Id
			if _, err := createSessionStmt.Exec(msgid.Id, session.Id); err != nil {
				sink.Cancel()
				return err
			}

		case types.RobustDeleteSession:
			if _, err := deleteSessionStmt.Exec(msgid.Id, session.Id); err != nil {
				sink.Cancel()
				return err
			}

		case types.RobustIRCFromClient:
			if returnedOnlyErrors(types.RobustId{Id: parsed.Id.Id}) {
				s.del[i] = parsed.Id
				continue
			}

			ircmsg := irc.ParseMessage(parsed.Data)
			if ircmsg == nil {
				s.del[i] = parsed.Id
				continue
			}
			irccmd := strings.ToUpper(ircmsg.Command)

			// Kind of a hack: we need to keep track of which sessions are
			// services connections and which are not, so that we can look at
			// the correct relevant-function (e.g. server_NICK vs. NICK).
			var serverPrefix string
			if irccmd == "SERVER" {
				s.servers[parsed.Session.Id] = true
			} else {
				if s.servers[session.Id] {
					serverPrefix = "server_"
				}
			}
			c, ok := ircserver.Commands[serverPrefix+irccmd]
			if !ok {
				// TODO: commands we don’t know should be okay to compact away, right?
				glog.Errorf("Could not find command %q", serverPrefix+irccmd)
				continue
			}
			if c.ImmediatelyCompactable {
				s.del[i] = parsed.Id
				continue
			}
			if c.CompactionInsert != nil {
				if err := c.CompactionInsert(msgid, session, ircmsg, stmts[serverPrefix+irccmd]); err != nil {
					sink.Cancel()
					return err
				}
			}
		}

		if _, err := allMessagesStmt.Exec(msgid.Id, session.Id); err != nil {
			sink.Cancel()
			return err
		}
	}

	// We repeatedly compact, since the result of one compaction can affect the
	// result of other compactions (see compaction_test.go for examples).
	changed := true
	pass := 0
	for changed {
		log.Printf("Compaction pass %d\n", pass)
		pass++
		var err error
		changed, err = s.compact(db)
		if err != nil {
			sink.Cancel()
			return err
		}
	}

	log.Printf("Copying non-deleted messages into snapshot\n")

	available = iterator.First()
	for available {
		if err := iterator.Error(); err != nil {
			sink.Cancel()
			return err
		}
		i := binary.BigEndian.Uint64(iterator.Key())
		value := iterator.Value()
		if _, ok := s.del[i]; !ok {
			if _, err := sink.Write(value); err != nil {
				sink.Cancel()
				return err
			}
		}
		available = iterator.Next()
	}

	sink.Close()

	if s.skipDeletionForCanary {
		log.Printf("Skipping deletion (canary mode)\n")
		return nil
	}

	log.Printf("Deleting compacted messages from disk\n")
	for idx, id := range s.del {
		if err := ircServer.Delete(id); err != nil {
			log.Panicf("Could not delete outputstream message: %v\n", err)
		}
		s.store.DeleteRange(idx, idx)
	}

	log.Printf("Snapshot done\n")

	return nil
}

func (s *robustSnapshot) Release() {
}

// compact invokes all irc command’s |Compact| callbacks and runs the
// compaction for createSession and deleteSession messages.
func (s *robustSnapshot) compact(db *sql.DB) (bool, error) {
	changed := false

	for _, cmd := range ircserver.Commands {
		if cmd.Compact == nil {
			continue
		}
		if err := cmd.Compact(db); err != nil {
			return changed, err
		}
		c, err := s.markResultForCompaction(db)
		if err != nil {
			return changed, err
		}
		changed = changed || c
	}

	const stmt = `
-- Delete all deleteSession messages immediately preceded by a createSession message.
CREATE TEMPORARY TABLE deleteIds AS
SELECT
    a.msgid AS msgid
FROM
    (
        SELECT
            d.msgid AS msgid,
            d.session AS session,
            a.msgid AS next_msgid
        FROM
            deleteSessionWin AS d
            INNER JOIN allMessagesWin AS a
            ON (
                d.session = a.session AND
                a.msgid < d.msgid
            )
        ORDER BY a.msgid DESC
        LIMIT 1
    ) AS a
    INNER JOIN createSessionWin AS c
    ON (
        a.session = c.session AND
        a.next_msgid = c.msgid
    );
DELETE FROM deleteSession WHERE msgid IN (SELECT msgid FROM deleteIds);

-- Delete all createSession messages for sessions which have no other message
INSERT INTO deleteIds
SELECT
    c.msgid
FROM
    createSession AS c
    LEFT JOIN allMessagesWin AS a
    ON (
        c.msgid <> a.msgid AND
        c.session = a.session
    )
WHERE
    a.msgid IS NULL;

DELETE FROM createSession WHERE msgid IN (SELECT msgid FROM deleteIds);
`

	_, err := db.Exec(stmt)
	if err != nil {
		return changed, err
	}

	c, err := s.markResultForCompaction(db)
	if err != nil {
		return changed, err
	}
	return changed || c, nil
}

// markResultForCompaction updates |s.del| with all messages whose ids are in
// the deleteIds table. It then removes these messages from the allMessages
// table and drops the deleteIds table.
func (s *robustSnapshot) markResultForCompaction(db *sql.DB) (bool, error) {
	changed := false
	rows, err := db.Query("SELECT msgid FROM deleteIds")
	if err != nil {
		return changed, err
	}
	defer rows.Close()
	for rows.Next() {
		var msgid int64
		if err := rows.Scan(&msgid); err != nil {
			return changed, err
		}
		idx, ok := s.idToIdx[msgid]
		if !ok {
			return changed, fmt.Errorf("msgid=%v not in idToIdx", msgid)
		}
		s.del[idx] = types.RobustId{Id: msgid}
		changed = true
	}
	if err := rows.Err(); err != nil {
		return changed, err
	}

	if _, err := db.Exec("DELETE FROM allMessages WHERE msgid IN (SELECT msgid FROM deleteIds)"); err != nil {
		return changed, err
	}

	_, err = db.Exec("DROP TABLE deleteIds")
	return changed, nil
}
<commit_msg>compaction: turn off synchronous mode in sqlite<commit_after>package main

// Generate errors.go which is used in returnedOnlyErrors() below.
//go:generate go run generrors.go

import (
	"database/sql"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"time"

	"github.com/hashicorp/raft"
	"github.com/robustirc/robustirc/ircserver"
	"github.com/robustirc/robustirc/raft_store"
	"github.com/robustirc/robustirc/types"
	"github.com/sorcix/irc"
	"github.com/stapelberg/glog"

	_ "github.com/mattn/go-sqlite3"
)

// returnedOnlyErrors returns true if and only if |msgid| resulted in at least
// one error and not a single non-error IRC reply.
func returnedOnlyErrors(msgid types.RobustId) bool {
	vmsgs, _ := ircServer.Get(msgid)
	for _, msg := range vmsgs {
		ircmsg := irc.ParseMessage(msg.Data)
		if ircmsg == nil {
			glog.Errorf("Output message not parsable\n")
			continue
		}
		if !errorCodes[ircmsg.Command] {
			return false
		}
	}
	return len(vmsgs) > 0
}

type robustSnapshot struct {
	firstIndex            uint64
	lastIndex             uint64
	store                 *raft_store.LevelDBStore
	del                   map[uint64]types.RobustId
	servers               map[int64]bool
	idToIdx               map[int64]uint64
	compactionEnd         time.Time
	skipDeletionForCanary bool
}

func (s *robustSnapshot) Persist(sink raft.SnapshotSink) error {
	log.Printf("Filtering and writing up to %d indexes\n", s.lastIndex-s.firstIndex)

	// Get a timestamp and keep it constant, so that we only compact messages
	// older than n days from compactionStart. If we used time.Since, new
	// messages would pour into the window on every compaction round, possibly
	// making the compaction never converge.
	compactionStart := time.Now()
	log.Printf("compactionStart %s\n", compactionStart.String())
	if *canaryCompactionStart > 0 {
		compactionStart = time.Unix(0, *canaryCompactionStart)
		log.Printf("compactionStart %s (overridden with -canary_compaction_start)\n", compactionStart.String())
	}

	s.compactionEnd = compactionStart.Add(-7 * 24 * time.Hour)
	f, err := ioutil.TempFile(*raftDir, "temp-compaction.sqlite3")
	if err != nil {
		sink.Cancel()
		return err
	}
	tempfile := f.Name()
	f.Close()
	db, err := sql.Open("sqlite3", tempfile)
	if err != nil {
		sink.Cancel()
		return err
	}
	defer func() {
		db.Close()
		os.Remove(tempfile)
	}()
	if _, err := db.Exec("pragma synchronous = off"); err != nil {
		sink.Cancel()
		return err
	}
	// TODO: add an index over paramsJoin.msgid and paramsPart.msgid (cannot be primary key because it is not unique)
	const nonIrcCommandStmt = `
CREATE TABLE createSession (msgid integer not null unique primary key, session integer not null);
CREATE TABLE deleteSession (msgid integer not null unique primary key, session integer not null);
CREATE TABLE allMessages (msgid integer not null unique primary key, session integer not null);
CREATE VIEW allMessagesWin AS SELECT * FROM allMessages WHERE msgid < %d;
CREATE VIEW createSessionWin AS SELECT * FROM createSession WHERE msgid < %d;
CREATE VIEW deleteSessionWin AS SELECT * FROM deleteSession WHERE msgid < %d;
`
	if _, err := db.Exec(
		fmt.Sprintf(nonIrcCommandStmt,
			s.compactionEnd.UnixNano(),
			s.compactionEnd.UnixNano(),
			s.compactionEnd.UnixNano())); err != nil {
		sink.Cancel()
		return err
	}

	var allMessagesStmt, createSessionStmt, deleteSessionStmt *sql.Stmt
	allMessagesStmt, err = db.Prepare("INSERT INTO allMessages (msgid, session) VALUES (?, ?)")
	if err == nil {
		defer allMessagesStmt.Close()
		createSessionStmt, err = db.Prepare("INSERT INTO createSession (msgid, session) VALUES (?, ?)")
	}
	if err == nil {
		defer createSessionStmt.Close()
		deleteSessionStmt, err = db.Prepare("INSERT INTO deleteSession (msgid, session) VALUES (?, ?)")
	}
	if err == nil {
		defer deleteSessionStmt.Close()
	}
	if err != nil {
		sink.Cancel()
		return err
	}

	// Let each IRC command prepare their tables and prepared statements.
	stmts := make(map[string]*sql.Stmt)
	for name, cmd := range ircserver.Commands {
		if cmd.CompactionPrepareStmt == nil {
			continue
		}
		var err error
		stmts[name], err = cmd.CompactionPrepareStmt(db)
		if err == nil {
			defer stmts[name].Close()
			err = cmd.CompactionPrepareViews(db, s.compactionEnd)
		}
		if err != nil {
			sink.Cancel()
			return err
		}
	}

	// First pass: insert all messages into database
	iterator := s.store.GetBulkIterator(s.firstIndex, s.lastIndex+1)
	defer iterator.Release()
	available := iterator.First()
	for available {
		var nlog raft.Log
		if err := iterator.Error(); err != nil {
			glog.Errorf("Error while iterating through the log: %v", err)
			available = iterator.Next()
			continue
		}
		i := binary.BigEndian.Uint64(iterator.Key())
		value := iterator.Value()
		if err := json.Unmarshal(value, &nlog); err != nil {
			glog.Errorf("Skipping log entry %d because of a JSON unmarshaling error: %v", i, err)
			continue
		}
		available = iterator.Next()

		// TODO: compact raft messages as well, so that peer changes are not kept forever
		if nlog.Type != raft.LogCommand {
			continue
		}

		parsed := types.NewRobustMessageFromBytes(nlog.Data)
		if time.Unix(0, parsed.Id.Id).After(s.compactionEnd) {
			break
		}
		s.idToIdx[parsed.Id.Id] = i

		msgid := parsed.Id
		session := parsed.Session

		switch parsed.Type {
		case types.RobustMessageOfDeath:
			s.del[i] = parsed.Id
			continue

		case types.RobustConfig:
			// TODO: implement compaction for RobustConfig
			continue

		case types.RobustIRCToClient:
			fallthrough
		case types.RobustPing:
			fallthrough
		case types.RobustAny:
			glog.Errorf("Compaction: saw unexpected message type %v (log id %d, message id %v)\n", parsed.Type, i, parsed.Id)
			continue

		default:
			glog.Errorf("Compaction: saw message of unknown type %v (log id %d, message id %v)\n", parsed.Type, i, parsed.Id)
			continue

		case types.RobustCreateSession:
			session = parsed.Id
			if _, err := createSessionStmt.Exec(msgid.Id, session.Id); err != nil {
				sink.Cancel()
				return err
			}

		case types.RobustDeleteSession:
			if _, err := deleteSessionStmt.Exec(msgid.Id, session.Id); err != nil {
				sink.Cancel()
				return err
			}

		case types.RobustIRCFromClient:
			if returnedOnlyErrors(types.RobustId{Id: parsed.Id.Id}) {
				s.del[i] = parsed.Id
				continue
			}

			ircmsg := irc.ParseMessage(parsed.Data)
			if ircmsg == nil {
				s.del[i] = parsed.Id
				continue
			}
			irccmd := strings.ToUpper(ircmsg.Command)

			// Kind of a hack: we need to keep track of which sessions are
			// services connections and which are not, so that we can look at
			// the correct relevant-function (e.g. server_NICK vs. NICK).
			var serverPrefix string
			if irccmd == "SERVER" {
				s.servers[parsed.Session.Id] = true
			} else {
				if s.servers[session.Id] {
					serverPrefix = "server_"
				}
			}
			c, ok := ircserver.Commands[serverPrefix+irccmd]
			if !ok {
				// TODO: commands we don’t know should be okay to compact away, right?
				glog.Errorf("Could not find command %q", serverPrefix+irccmd)
				continue
			}
			if c.ImmediatelyCompactable {
				s.del[i] = parsed.Id
				continue
			}
			if c.CompactionInsert != nil {
				if err := c.CompactionInsert(msgid, session, ircmsg, stmts[serverPrefix+irccmd]); err != nil {
					sink.Cancel()
					return err
				}
			}
		}

		if _, err := allMessagesStmt.Exec(msgid.Id, session.Id); err != nil {
			sink.Cancel()
			return err
		}
	}

	// We repeatedly compact, since the result of one compaction can affect the
	// result of other compactions (see compaction_test.go for examples).
	changed := true
	pass := 0
	for changed {
		log.Printf("Compaction pass %d\n", pass)
		pass++
		var err error
		changed, err = s.compact(db)
		if err != nil {
			sink.Cancel()
			return err
		}
	}

	log.Printf("Copying non-deleted messages into snapshot\n")

	available = iterator.First()
	for available {
		if err := iterator.Error(); err != nil {
			sink.Cancel()
			return err
		}
		i := binary.BigEndian.Uint64(iterator.Key())
		value := iterator.Value()
		if _, ok := s.del[i]; !ok {
			if _, err := sink.Write(value); err != nil {
				sink.Cancel()
				return err
			}
		}
		available = iterator.Next()
	}

	sink.Close()

	if s.skipDeletionForCanary {
		log.Printf("Skipping deletion (canary mode)\n")
		return nil
	}

	log.Printf("Deleting compacted messages from disk\n")
	for idx, id := range s.del {
		if err := ircServer.Delete(id); err != nil {
			log.Panicf("Could not delete outputstream message: %v\n", err)
		}
		s.store.DeleteRange(idx, idx)
	}

	log.Printf("Snapshot done\n")

	return nil
}

func (s *robustSnapshot) Release() {
}

// compact invokes all irc command’s |Compact| callbacks and runs the
// compaction for createSession and deleteSession messages.
func (s *robustSnapshot) compact(db *sql.DB) (bool, error) {
	changed := false

	for _, cmd := range ircserver.Commands {
		if cmd.Compact == nil {
			continue
		}
		if err := cmd.Compact(db); err != nil {
			return changed, err
		}
		c, err := s.markResultForCompaction(db)
		if err != nil {
			return changed, err
		}
		changed = changed || c
	}

	const stmt = `
-- Delete all deleteSession messages immediately preceded by a createSession message.
CREATE TEMPORARY TABLE deleteIds AS
SELECT
    a.msgid AS msgid
FROM
    (
        SELECT
            d.msgid AS msgid,
            d.session AS session,
            a.msgid AS next_msgid
        FROM
            deleteSessionWin AS d
            INNER JOIN allMessagesWin AS a
            ON (
                d.session = a.session AND
                a.msgid < d.msgid
            )
        ORDER BY a.msgid DESC
        LIMIT 1
    ) AS a
    INNER JOIN createSessionWin AS c
    ON (
        a.session = c.session AND
        a.next_msgid = c.msgid
    );
DELETE FROM deleteSession WHERE msgid IN (SELECT msgid FROM deleteIds);

-- Delete all createSession messages for sessions which have no other message
INSERT INTO deleteIds
SELECT
    c.msgid
FROM
    createSession AS c
    LEFT JOIN allMessagesWin AS a
    ON (
        c.msgid <> a.msgid AND
        c.session = a.session
    )
WHERE
    a.msgid IS NULL;

DELETE FROM createSession WHERE msgid IN (SELECT msgid FROM deleteIds);
`

	_, err := db.Exec(stmt)
	if err != nil {
		return changed, err
	}

	c, err := s.markResultForCompaction(db)
	if err != nil {
		return changed, err
	}
	return changed || c, nil
}

// markResultForCompaction updates |s.del| with all messages whose ids are in
// the deleteIds table. It then removes these messages from the allMessages
// table and drops the deleteIds table.
func (s *robustSnapshot) markResultForCompaction(db *sql.DB) (bool, error) {
	changed := false
	rows, err := db.Query("SELECT msgid FROM deleteIds")
	if err != nil {
		return changed, err
	}
	defer rows.Close()
	for rows.Next() {
		var msgid int64
		if err := rows.Scan(&msgid); err != nil {
			return changed, err
		}
		idx, ok := s.idToIdx[msgid]
		if !ok {
			return changed, fmt.Errorf("msgid=%v not in idToIdx", msgid)
		}
		s.del[idx] = types.RobustId{Id: msgid}
		changed = true
	}
	if err := rows.Err(); err != nil {
		return changed, err
	}

	if _, err := db.Exec("DELETE FROM allMessages WHERE msgid IN (SELECT msgid FROM deleteIds)"); err != nil {
		return changed, err
	}

	_, err = db.Exec("DROP TABLE deleteIds")
	return changed, nil
}
<|endoftext|>


<commit_before>package requirements

import (
	"cf/configuration"
	"cf/terminal"
	"errors"
	"fmt"
)

type SpaceRequirement struct {
	ui     terminal.UI
	config *configuration.Configuration
}

func NewSpaceRequirement(ui terminal.UI, config *configuration.Configuration) SpaceRequirement {
	return SpaceRequirement{ui, config}
}

func (req SpaceRequirement) Execute() (err error) {
	if !req.config.HasOrganization() {
		message := fmt.Sprintf("No org and space targeted. Use '%s' to target an org and space.", terminal.Yellow("cf target -o"))
		req.ui.Failed(message, nil)
		err = errors.New("No org and space targeted")
		return
	}

	if !req.config.HasSpace() {
		message := fmt.Sprintf("No space targeted. Use '%s' to target a space.", terminal.Yellow("cf target -s"))
		req.ui.Failed(message, nil)
		err = errors.New("No space targeted")
		return
	}

	return
}
<commit_msg>Change help text when app or space is not targeted<commit_after>package requirements

import (
	"cf/configuration"
	"cf/terminal"
	"errors"
	"fmt"
)

type SpaceRequirement struct {
	ui     terminal.UI
	config *configuration.Configuration
}

func NewSpaceRequirement(ui terminal.UI, config *configuration.Configuration) SpaceRequirement {
	return SpaceRequirement{ui, config}
}

func (req SpaceRequirement) Execute() (err error) {
	if !req.config.HasOrganization() {
		message := fmt.Sprintf("No org and space targeted. See '%s' to target an org and space.",
			terminal.Yellow("cf target --o ORGNAME --s SPACENAME"))
		req.ui.Failed(message, nil)
		err = errors.New("No org and space targeted")
		return
	}

	if !req.config.HasSpace() {
		message := fmt.Sprintf("No space targeted. Use '%s' to target a space.", terminal.Yellow("cf target -s"))
		req.ui.Failed(message, nil)
		err = errors.New("No space targeted")
		return
	}

	return
}
<|endoftext|>


<commit_before>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e2", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	fmt.Println("listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	fmt.Println("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not configured or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	destIndex := rand.Intn(len(route.Destinations))
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		fmt.Println("error!", err)
		panic(err)
	}
	fmt.Println("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		fmt.Println("Error proxying!", err)
		etype := reflect.TypeOf(err)
		fmt.Println("err type:", etype)
		w.WriteHeader(http.StatusInternalServerError)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			if len(route.Destinations) > 1 {
				fmt.Println("It's a network error, removing this destination from routing table.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				putRoute(route)
				fmt.Println("New route:", route)
				return
			} else {
				fmt.Println("It's a network error and no other destinations available so we're going to remove it and start new task.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				putRoute(route)
				fmt.Println("New route:", route)
			}
			// start new worker
			payload := map[string]interface{}{
				"token":      route.Token,
				"project_id": route.ProjectId,
				"code_name":  route.CodeName,
			}
			workerapi := worker.New()
			workerapi.Settings.UseConfigMap(payload)
			jsonPayload, err := json.Marshal(payload)
			if err != nil {
				fmt.Println("Couldn't marshal json!", err)
				return
			}
			timeout := time.Second*120
			task := worker.Task{
				CodeName: route.CodeName,
				Payload:  string(jsonPayload),
				Timeout:  &timeout, // let's have these die quickly while testing
			}
			tasks := make([]worker.Task, 1)
			tasks[0] = task
			taskIds, err := workerapi.TaskQueue(tasks...)
			fmt.Println("Tasks queued.", taskIds)
			if err != nil {
				fmt.Println("Couldn't queue up worker!", err)
				return
			}
		}
		// start new worker if it's a connection error
		return
	}
	fmt.Println("Served!")
	// todo: how to handle destination failures. I got this in log output when testing a bad endpoint:
	// 2012/12/26 23:22:08 http: proxy error: dial tcp 127.0.0.1:8082: connection refused
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	fmt.Println("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		fmt.Println("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		putRoute(route)
		fmt.Println("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		fmt.Println("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		fmt.Println("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		fmt.Println("ROUTE new:", route)
		putRoute(route)
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (Route, error) {
	rx, err := icache.Get(host)
	route := Route{}
	if err == nil {
		route = rx.(Route)
	}
	return route, err
}

func putRoute(route Route) {
	item := cache.Item{}
	item.Value = route
	icache.Put(route.Host, &item)
}
<commit_msg>logging<commit_after>/*

For keeping a minimum running, perhaps when doing a routing table update, if destination hosts are all
 expired or about to expire we start more.

*/

package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"github.com/iron-io/iron_go/cache"
	"github.com/iron-io/iron_go/worker"
	"github.com/iron-io/common"
	"log"
	"math/rand"
	// "net"
	"net/http"
	"net/url"
	"reflect"
	"strings"
	"time"
	"runtime"
	"flag"
//	"io/ioutil"
)

var config struct {
	Iron struct {
	Token      string `json:"token"`
	ProjectId  string `json:"project_id"`
} `json:"iron"`
	Logging       struct {
	To     string `json:"to"`
	Level  string `json:"level"`
	Prefix string `json:"prefix"`
}
}

//var routingTable = map[string]*Route{}
var icache = cache.New("routing-table")

func init() {

}

type Route struct {
	// TODO: Change destinations to a simple cache so it can expire entries after 55 minutes (the one we use in common?)
	Host         string `json:"host"`
	Destinations []string  `json:"destinations"`
	ProjectId    string  `json:"project_id"`
	Token        string  `json:"token"` // store this so we can queue up new workers on demand
	CodeName     string  `json:"code_name"`
}

// for adding new hosts
type Route2 struct {
	Host string `json:"host"`
	Dest string `json:"dest"`
}

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())
	log.Println("Running on", runtime.NumCPU(), "CPUs")

	var configFile string
	var env string
	flag.StringVar(&configFile, "c", "", "Config file name")
	// when this was e, it was erroring out.
	flag.StringVar(&env, "e2", "development", "environment")

	flag.Parse() // Scans the arg list and sets up flags

	// Deployer is now passing -c in since we're using upstart and it doesn't know what directory to run in
	if configFile == "" {
		configFile = "config_" + env + ".json"
	}

	common.LoadConfig("iron_mq", configFile, &config)
	common.SetLogLevel(config.Logging.Level)
	common.SetLogLocation(config.Logging.To, config.Logging.Prefix)

	icache.Settings.UseConfigMap(map[string]interface{}{"token": config.Iron.Token, "project_id": config.Iron.ProjectId})

	r := mux.NewRouter()
	s := r.Headers("Iron-Router", "").Subrouter()
	s.HandleFunc("/", AddWorker)
	r.HandleFunc("/addworker", AddWorker)

	r.HandleFunc("/", ProxyFunc)

	http.Handle("/", r)
	port := 80
	fmt.Println("listening and serving on port", port)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%v", port), nil))
}

func ProxyFunc(w http.ResponseWriter, req *http.Request) {
	fmt.Println("HOST:", req.Host)
	host := strings.Split(req.Host, ":")[0]

	// We look up the destinations in the routing table and there can be 3 possible scenarios:
	// 1) This host was never registered so we return 404
	// 2) This host has active workers so we do the proxy
	// 3) This host has no active workers so we queue one (or more) up and return a 503 or something with message that says "try again in a minute"
	//	route := routingTable[host]
 fmt.Println("getting route for host:", host)
	route, err := getRoute(host)
	// choose random dest
	if err != nil {
		common.SendError(w, 400, fmt.Sprintln("Host not configured or error!", err))
		return
	}
	//	if route == nil { // route.Host == "" {
	//		common.SendError(w, 400, fmt.Sprintln(w, "Host not configured!"))
	//		return
	//	}
	destIndex := rand.Intn(len(route.Destinations))
	destUrlString := route.Destinations[destIndex]
	// todo: should check if http:// already exists.
	destUrlString2 := "http://" + destUrlString
	destUrl, err := url.Parse(destUrlString2)
	if err != nil {
		fmt.Println("error!", err)
		panic(err)
	}
	fmt.Println("proxying to", destUrl)
	proxy := NewSingleHostReverseProxy(destUrl)
	err = proxy.ServeHTTP(w, req)
	if err != nil {
		fmt.Println("Error proxying!", err)
		etype := reflect.TypeOf(err)
		fmt.Println("err type:", etype)
		w.WriteHeader(http.StatusInternalServerError)
		// can't figure out how to compare types so comparing strings.... lame.
		if strings.Contains(etype.String(), "net.OpError") { // == reflect.TypeOf(net.OpError{}) { // couldn't figure out a better way to do this
			if len(route.Destinations) > 1 {
				fmt.Println("It's a network error, removing this destination from routing table.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				putRoute(route)
				fmt.Println("New route:", route)
				return
			} else {
				fmt.Println("It's a network error and no other destinations available so we're going to remove it and start new task.")
				route.Destinations = append(route.Destinations[:destIndex], route.Destinations[destIndex + 1:]...)
				putRoute(route)
				fmt.Println("New route:", route)
			}
			// start new worker
			payload := map[string]interface{}{
				"token":      route.Token,
				"project_id": route.ProjectId,
				"code_name":  route.CodeName,
			}
			workerapi := worker.New()
			workerapi.Settings.UseConfigMap(payload)
			jsonPayload, err := json.Marshal(payload)
			if err != nil {
				fmt.Println("Couldn't marshal json!", err)
				return
			}
			timeout := time.Second*120
			task := worker.Task{
				CodeName: route.CodeName,
				Payload:  string(jsonPayload),
				Timeout:  &timeout, // let's have these die quickly while testing
			}
			tasks := make([]worker.Task, 1)
			tasks[0] = task
			taskIds, err := workerapi.TaskQueue(tasks...)
			fmt.Println("Tasks queued.", taskIds)
			if err != nil {
				fmt.Println("Couldn't queue up worker!", err)
				return
			}
		}
		// start new worker if it's a connection error
		return
	}
	fmt.Println("Served!")
	// todo: how to handle destination failures. I got this in log output when testing a bad endpoint:
	// 2012/12/26 23:22:08 http: proxy error: dial tcp 127.0.0.1:8082: connection refused
}

// When a worker starts up, it calls this
func AddWorker(w http.ResponseWriter, req *http.Request) {
	log.Println("AddWorker called!")

//	s, err := ioutil.ReadAll(req.Body)
//	fmt.Println("req.body:", err, string(s))

	// get project id and token
	projectId := req.FormValue("project_id")
	token := req.FormValue("token")
	codeName := req.FormValue("code_name")
	fmt.Println("project_id:", projectId, "token:", token, "code_name:", codeName)

	// check header for what operation to perform
	routerHeader := req.Header.Get("Iron-Router")
	if routerHeader == "register" {
		route := Route{}
		if !common.ReadJSON(w, req, &route) {
			return
		}
		fmt.Println("body read into route:", route)
		route.ProjectId = projectId
		route.Token = token
		route.CodeName = codeName
		// todo: do we need to close body?
		putRoute(route)
		fmt.Println("registered route:", route)
		fmt.Fprintln(w, "Host registered successfully.")

	} else {
		r2 := Route2{}
		if !common.ReadJSON(w, req, &r2) {
			return
		}
		// todo: do we need to close body?
		fmt.Println("DECODED:", r2)
		route, err := getRoute(r2.Host)
		//		route := routingTable[r2.Host]
		if err != nil {
			common.SendError(w, 400, fmt.Sprintln("This host is not registered!", err))
			return
			//			route = &Route{}
		}
		fmt.Println("ROUTE:", route)
		route.Destinations = append(route.Destinations, r2.Dest)
		fmt.Println("ROUTE new:", route)
		putRoute(route)
		//		routingTable[r2.Host] = route
		//		fmt.Println("New routing table:", routingTable)
		fmt.Fprintln(w, "Worker added")
	}
}

func getRoute(host string) (Route, error) {
	rx, err := icache.Get(host)
	route := Route{}
	if err == nil {
		route = rx.(Route)
	}
	return route, err
}

func putRoute(route Route) {
	item := cache.Item{}
	item.Value = route
	icache.Put(route.Host, &item)
}
<|endoftext|>


<commit_before>package gorm

import (
	"errors"
	"go/ast"
	"reflect"
	"regexp"
	"time"
)

type Model struct {
	data          interface{}
	do            *Do
	_cache_fields map[string][]*Field
}

func (m *Model) reflectData() reflect.Value {
	return reflect.Indirect(reflect.ValueOf(m.data))
}

func (m *Model) primaryKeyZero() bool {
	return isBlank(reflect.ValueOf(m.primaryKeyValue()))
}

func (m *Model) primaryKeyValue() interface{} {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		if field := data.FieldByName(m.primaryKey()); field.IsValid() {
			return field.Interface()
		}
	}
	return 0
}

func (m *Model) primaryKey() string {
	return "Id"
}

func (m *Model) primaryKeyDb() string {
	return toSnake(m.primaryKey())
}

func (m *Model) fields(operation string) (fields []*Field) {
	indirect_value := m.reflectData()
	if !indirect_value.IsValid() {
		return
	}

	typ := indirect_value.Type()
	for i := 0; i < typ.NumField(); i++ {
		p := typ.Field(i)
		if !p.Anonymous && ast.IsExported(p.Name) {
			var field Field
			field.Name = p.Name
			field.dbName = toSnake(p.Name)
			field.isPrimaryKey = m.primaryKeyDb() == field.dbName
			value := indirect_value.FieldByName(p.Name)
			field.model = m

			if time_value, is_time := value.Interface().(time.Time); is_time {
				field.autoCreateTime = "created_at" == field.dbName
				field.autoUpdateTime = "updated_at" == field.dbName

				switch operation {
				case "create":
					if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() {
						value.Set(reflect.ValueOf(time.Now()))
					}
				case "update":
					if field.autoUpdateTime {
						value.Set(reflect.ValueOf(time.Now()))
					}
				}
			}

			field.structField = p
			field.reflectValue = value
			field.Value = value.Interface()
			field.parseAssociation()
			fields = append(fields, &field)
		}
	}
	return
}

func (m *Model) columnsHasValue(operation string) (fields []*Field) {
	for _, field := range m.fields(operation) {
		if !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}

func (m *Model) updatedColumnsAndValues(values map[string]interface{}, ignore_protected_attrs bool) (results map[string]interface{}, any_updated bool) {
	if m.data == nil {
		return values, true
	}

	data := m.reflectData()
	for key, value := range values {
		if field := data.FieldByName(snakeToUpperCamel(key)); field.IsValid() {
			if field.Interface() != value {
				switch field.Kind() {
				case reflect.Int, reflect.Int32, reflect.Int64:
					if field.Int() != reflect.ValueOf(value).Int() {
						any_updated = true
						field.SetInt(reflect.ValueOf(value).Int())
					}
				default:
					any_updated = true
					field.Set(reflect.ValueOf(value))
				}
			}
		}
	}

	if values["updated_at"] != nil && any_updated {
		setFieldValue(data.FieldByName("UpdatedAt"), time.Now())
	}
	return
}

func (m *Model) columnsAndValues(operation string) map[string]interface{} {
	results := map[string]interface{}{}

	for _, field := range m.fields(operation) {
		if !field.isPrimaryKey && len(field.sqlTag()) > 0 {
			results[field.dbName] = field.Value
		}
	}
	return results
}

func (m *Model) hasColumn(name string) bool {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		return data.FieldByName(name).IsValid()
	} else if data.Kind() == reflect.Slice {
		return reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
	}
	return false
}

func (m *Model) columnAndValue(name string) (has_column bool, is_slice bool, value interface{}) {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		if has_column = data.FieldByName(name).IsValid(); has_column {
			value = data.FieldByName(name).Interface()
		}
	} else if data.Kind() == reflect.Slice {
		has_column = reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
		is_slice = true
	}
	return
}

func (m *Model) typ() reflect.Type {
	typ := m.reflectData().Type()
	if typ.Kind() == reflect.Slice {
		return typ.Elem()
	} else {
		return typ
	}
}

func (m *Model) typeName() string {
	return m.typ().Name()
}

func (m *Model) tableName() (str string) {
	if m.data == nil {
		m.do.err(errors.New("Model haven't been set"))
		return
	}

	data := m.reflectData()
	if fm := data.MethodByName("TableName"); fm.IsValid() {
		if v := fm.Call([]reflect.Value{}); len(v) > 0 {
			if result, ok := v[0].Interface().(string); ok {
				return result
			}
		}
	}

	str = toSnake(m.typeName())

	if !m.do.db.parent.singularTable {
		pluralMap := map[string]string{"ch": "ches", "ss": "sses", "sh": "shes", "day": "days", "y": "ies", "x": "xes", "s?": "s"}
		for key, value := range pluralMap {
			reg := regexp.MustCompile(key + "$")
			if reg.MatchString(str) {
				return reg.ReplaceAllString(str, value)
			}
		}
	}

	return
}

func (m *Model) callMethod(method string) {
	if m.data == nil || m.do.db.hasError() {
		return
	}

	if fm := reflect.ValueOf(m.data).MethodByName(method); fm.IsValid() {
		if v := fm.Call([]reflect.Value{}); len(v) > 0 {
			if verr, ok := v[0].Interface().(error); ok {
				m.do.err(verr)
			}
		}
	}
	return
}

func (m *Model) setValueByColumn(name string, value interface{}, out interface{}) {
	data := reflect.Indirect(reflect.ValueOf(out))
	setFieldValue(data.FieldByName(snakeToUpperCamel(name)), value)
}

func (m *Model) beforeAssociations() (fields []*Field) {
	for _, field := range m.fields("null") {
		if field.beforeAssociation && !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}

func (m *Model) afterAssociations() (fields []*Field) {
	for _, field := range m.fields("null") {
		if field.afterAssociation && !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}
<commit_msg>Cache model struct for less GC<commit_after>package gorm

import (
	"errors"
	"go/ast"
	"reflect"
	"regexp"
	"time"
)

var modelFieldMap map[string][]reflect.StructField

func init() {
	modelFieldMap = map[string][]reflect.StructField{}
}

type Model struct {
	data          interface{}
	do            *Do
	_cache_fields map[string][]*Field
}

func (m *Model) reflectData() reflect.Value {
	return reflect.Indirect(reflect.ValueOf(m.data))
}

func (m *Model) primaryKeyZero() bool {
	return isBlank(reflect.ValueOf(m.primaryKeyValue()))
}

func (m *Model) primaryKeyValue() interface{} {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		if field := data.FieldByName(m.primaryKey()); field.IsValid() {
			return field.Interface()
		}
	}
	return 0
}

func (m *Model) primaryKey() string {
	return "Id"
}

func (m *Model) primaryKeyDb() string {
	return toSnake(m.primaryKey())
}

func getStructs(typ reflect.Type) (fs []reflect.StructField) {
	name := typ.Name()
	if fs = modelFieldMap[name]; fs != nil {
		return
	}

	for i := 0; i < typ.NumField(); i++ {
		p := typ.Field(i)
		if !p.Anonymous && ast.IsExported(p.Name) {
			fs = append(fs, p)
		}
	}

	modelFieldMap[name] = fs
	return
}

func (m *Model) fields(operation string) (fields []*Field) {
	indirect_value := m.reflectData()
	if !indirect_value.IsValid() {
		return
	}

	for _, filed_struct := range getStructs(indirect_value.Type()) {
		var field Field
		field.Name = filed_struct.Name
		field.dbName = toSnake(filed_struct.Name)
		field.isPrimaryKey = m.primaryKeyDb() == field.dbName
		value := indirect_value.FieldByName(filed_struct.Name)
		field.model = m

		if time_value, is_time := value.Interface().(time.Time); is_time {
			field.autoCreateTime = "created_at" == field.dbName
			field.autoUpdateTime = "updated_at" == field.dbName

			switch operation {
			case "create":
				if (field.autoCreateTime || field.autoUpdateTime) && time_value.IsZero() {
					value.Set(reflect.ValueOf(time.Now()))
				}
			case "update":
				if field.autoUpdateTime {
					value.Set(reflect.ValueOf(time.Now()))
				}
			}
		}

		field.structField = filed_struct
		field.reflectValue = value
		field.Value = value.Interface()
		field.parseAssociation()
		fields = append(fields, &field)
	}
	return
}

func (m *Model) columnsHasValue(operation string) (fields []*Field) {
	for _, field := range m.fields(operation) {
		if !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}

func (m *Model) updatedColumnsAndValues(values map[string]interface{}, ignore_protected_attrs bool) (results map[string]interface{}, any_updated bool) {
	if m.data == nil {
		return values, true
	}

	data := m.reflectData()
	for key, value := range values {
		if field := data.FieldByName(snakeToUpperCamel(key)); field.IsValid() {
			if field.Interface() != value {
				switch field.Kind() {
				case reflect.Int, reflect.Int32, reflect.Int64:
					if field.Int() != reflect.ValueOf(value).Int() {
						any_updated = true
						field.SetInt(reflect.ValueOf(value).Int())
					}
				default:
					any_updated = true
					field.Set(reflect.ValueOf(value))
				}
			}
		}
	}

	if values["updated_at"] != nil && any_updated {
		setFieldValue(data.FieldByName("UpdatedAt"), time.Now())
	}
	return
}

func (m *Model) columnsAndValues(operation string) map[string]interface{} {
	results := map[string]interface{}{}

	for _, field := range m.fields(operation) {
		if !field.isPrimaryKey && len(field.sqlTag()) > 0 {
			results[field.dbName] = field.Value
		}
	}
	return results
}

func (m *Model) hasColumn(name string) bool {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		return data.FieldByName(name).IsValid()
	} else if data.Kind() == reflect.Slice {
		return reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
	}
	return false
}

func (m *Model) columnAndValue(name string) (has_column bool, is_slice bool, value interface{}) {
	if data := m.reflectData(); data.Kind() == reflect.Struct {
		if has_column = data.FieldByName(name).IsValid(); has_column {
			value = data.FieldByName(name).Interface()
		}
	} else if data.Kind() == reflect.Slice {
		has_column = reflect.New(data.Type().Elem()).Elem().FieldByName(name).IsValid()
		is_slice = true
	}
	return
}

func (m *Model) typ() reflect.Type {
	typ := m.reflectData().Type()
	if typ.Kind() == reflect.Slice {
		return typ.Elem()
	} else {
		return typ
	}
}

func (m *Model) typeName() string {
	return m.typ().Name()
}

func (m *Model) tableName() (str string) {
	if m.data == nil {
		m.do.err(errors.New("Model haven't been set"))
		return
	}

	data := m.reflectData()
	if fm := data.MethodByName("TableName"); fm.IsValid() {
		if v := fm.Call([]reflect.Value{}); len(v) > 0 {
			if result, ok := v[0].Interface().(string); ok {
				return result
			}
		}
	}

	str = toSnake(m.typeName())

	if !m.do.db.parent.singularTable {
		pluralMap := map[string]string{"ch": "ches", "ss": "sses", "sh": "shes", "day": "days", "y": "ies", "x": "xes", "s?": "s"}
		for key, value := range pluralMap {
			reg := regexp.MustCompile(key + "$")
			if reg.MatchString(str) {
				return reg.ReplaceAllString(str, value)
			}
		}
	}

	return
}

func (m *Model) callMethod(method string) {
	if m.data == nil || m.do.db.hasError() {
		return
	}

	if fm := reflect.ValueOf(m.data).MethodByName(method); fm.IsValid() {
		if v := fm.Call([]reflect.Value{}); len(v) > 0 {
			if verr, ok := v[0].Interface().(error); ok {
				m.do.err(verr)
			}
		}
	}
	return
}

func (m *Model) setValueByColumn(name string, value interface{}, out interface{}) {
	data := reflect.Indirect(reflect.ValueOf(out))
	setFieldValue(data.FieldByName(snakeToUpperCamel(name)), value)
}

func (m *Model) beforeAssociations() (fields []*Field) {
	for _, field := range m.fields("null") {
		if field.beforeAssociation && !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}

func (m *Model) afterAssociations() (fields []*Field) {
	for _, field := range m.fields("null") {
		if field.afterAssociation && !field.isBlank() {
			fields = append(fields, field)
		}
	}
	return
}
<|endoftext|>


<commit_before>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import "strings"

// CMode represents a single step of a given mode change.
type CMode struct {
	add     bool   // if it's a +, or -.
	name    byte   // character representation of the given mode.
	setting bool   // if it's a setting (should be stored) or temporary (op/voice/etc).
	args    string // arguments to the mode, if arguments are supported.
}

// Short returns a short representation of a mode without arguments. E.g. "+a",
// or "-b".
func (c *CMode) Short() string {
	var status string
	if c.add {
		status = "+"
	} else {
		status = "-"
	}

	return status + string(c.name)
}

// String returns a string representation of a mode, including optional
// arguments. E.g. "+b user*!ident@host.*.com"
func (c *CMode) String() string {
	if len(c.args) == 0 {
		return c.Short()
	}

	return c.Short() + " " + c.args
}

// CModes is a representation of a set of modes. This may be the given state
// of a channel/user, or the given state changes to a given channel/user.
type CModes struct {
	raw           string // raw supported modes.
	modesListArgs string // modes that add/remove users from lists and support args.
	modesArgs     string // modes that support args.
	modesSetArgs  string // modes that support args ONLY when set.
	modesNoArgs   string // modes that do not support args.

	prefixes string  // user permission prefixes. these aren't a CMode.setting.
	modes    []CMode // the list of modes for this given state.
}

// String returns a complete set of modes for this given state (change?). For
// example, "+a-b+cde some-arg".
func (c *CModes) String() string {
	var out string
	var args string

	if len(c.modes) > 0 {
		out += "+"
	}

	for i := 0; i < len(c.modes); i++ {
		out += string(c.modes[i].name)

		if len(c.modes[i].args) > 0 {
			args += " " + c.modes[i].args
		}
	}

	return out + args
}

// hasArg checks to see if the mode supports arguments. What ones support this?:
//   A = Mode that adds or removes a nick or address to a list. Always has a parameter.
//   B = Mode that changes a setting and always has a parameter.
//   C = Mode that changes a setting and only has a parameter when set.
//   D = Mode that changes a setting and never has a parameter.
//   Note: Modes of type A return the list when there is no parameter present.
//   Note: Some clients assumes that any mode not listed is of type D.
//   Note: Modes in PREFIX are not listed but could be considered type B.
func (c *CModes) hasArg(set bool, mode byte) (hasArgs, isSetting bool) {
	if len(c.raw) < 1 {
		return false, true
	}

	if strings.IndexByte(c.modesListArgs, mode) > -1 {
		return true, false
	}

	if strings.IndexByte(c.modesArgs, mode) > -1 {
		return true, true
	}

	if strings.IndexByte(c.modesSetArgs, mode) > -1 {
		if set {
			return true, true
		}

		return false, true
	}

	if strings.IndexByte(c.prefixes, mode) > -1 {
		return true, false
	}

	return false, true
}

// apply merges two state changes, or one state change into a state of modes.
// For example, the latter would mean applying an incoming MODE with the modes
// stored for a channel.
func (c *CModes) apply(modes []CMode) {
	var new []CMode

	for j := 0; j < len(c.modes); j++ {
		isin := false
		for i := 0; i < len(modes); i++ {
			if !modes[i].setting {
				continue
			}
			if c.modes[j].name == modes[i].name && modes[i].add {
				new = append(new, modes[i])
				isin = true
				break
			}
		}

		if !isin {
			new = append(new, c.modes[j])
		}
	}

	for i := 0; i < len(modes); i++ {
		if !modes[i].setting || !modes[i].add {
			continue
		}

		isin := false
		for j := 0; j < len(new); j++ {
			if modes[i].name == new[j].name {
				isin = true
				break
			}
		}

		if !isin {
			new = append(new, modes[i])
		}
	}

	c.modes = new
}

// parse parses a set of flags and args, returning the necessary list of
// mappings for the mode flags.
func (c *CModes) parse(flags string, args []string) (out []CMode) {
	// add is the mode state we're currently in. Adding, or removing modes.
	add := true
	var argCount int

	for i := 0; i < len(flags); i++ {
		if flags[i] == 0x2B {
			add = true
			continue
		}
		if flags[i] == 0x2D {
			add = false
			continue
		}

		mode := CMode{
			name: flags[i],
			add:  add,
		}

		hasArgs, isSetting := c.hasArg(add, flags[i])
		if hasArgs && len(args) >= argCount+1 {
			mode.args = args[argCount]
			argCount++
		}
		mode.setting = isSetting

		out = append(out, mode)
	}

	return out
}

// newCModes returns a new CModes reference. channelModes and userPrefixes
// would be something you see from the server's "CHANMODES" and "PREFIX"
// ISUPPORT capability messages (alternatively, fall back to the standard)
// DefaultPrefixes and ModeDefaults.
func newCModes(channelModes, userPrefixes string) CModes {
	split := strings.SplitN(channelModes, ",", 4)
	if len(split) != 4 {
		for i := len(split); i < 4; i++ {
			split = append(split, "")
		}
	}

	return CModes{
		raw:           channelModes,
		modesListArgs: split[0],
		modesArgs:     split[1],
		modesSetArgs:  split[2],
		modesNoArgs:   split[3],

		prefixes: userPrefixes,
		modes:    []CMode{},
	}
}

// isValidChannelMode validates a channel mode (CHANMODES).
func isValidChannelMode(raw string) bool {
	if len(raw) < 1 {
		return false
	}

	for i := 0; i < len(raw); i++ {
		// Allowed are: ",", A-Z and a-z.
		if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) {
			return false
		}
	}

	return true
}

// isValidUserPrefix validates a list of ISUPPORT-style user prefixes (PREFIX).
func isValidUserPrefix(raw string) bool {
	if len(raw) < 1 {
		return false
	}

	if raw[0] != 0x28 { // (.
		return false
	}

	var keys, rep int
	var passedKeys bool

	// Skip the first one as we know it's (.
	for i := 1; i < len(raw); i++ {
		if raw[i] == 0x29 { // ).
			passedKeys = true
			continue
		}

		if passedKeys {
			rep++
		} else {
			keys++
		}
	}

	return keys == rep
}

// parsePrefixes parses the mode character mappings from the symbols of a
// ISUPPORT-style user prefixes list (PREFIX).
func parsePrefixes(raw string) (modes, prefixes string) {
	if !isValidUserPrefix(raw) {
		return modes, prefixes
	}

	i := strings.Index(raw, ")")
	if i < 1 {
		return modes, prefixes
	}

	return raw[1:i], raw[i+1:]
}

// handleMODE handles incoming MODE messages, and updates the tracking
// information for each channel, as well as if any of the modes affect user
// permissions.
func handleMODE(c *Client, e Event) {
	// Check if it's a RPL_CHANNELMODEIS.
	if e.Command == RPL_CHANNELMODEIS && len(e.Params) > 2 {
		// RPL_CHANNELMODEIS sends the user as the first param, skip it.
		e.Params = e.Params[1:]
	}
	// Should be at least MODE <target> <flags>, to be useful. As well, only
	// tracking channel modes at the moment.
	if len(e.Params) < 2 || !IsValidChannel(e.Params[0]) {
		return
	}

	c.state.mu.Lock()
	channel := c.state.lookupChannel(e.Params[0])
	if channel == nil {
		c.state.mu.Unlock()
		return
	}

	flags := e.Params[1]
	var args []string
	if len(e.Params) > 2 {
		args = append(args, e.Params[2:]...)
	}

	modes := channel.Modes.parse(flags, args)
	channel.Modes.apply(modes)

	// Loop through and update users modes as necessary.
	for i := 0; i < len(modes); i++ {
		if modes[i].setting || len(modes[i].args) == 0 {
			continue
		}

		users := c.state.lookupUsers("nick", modes[i].args)
		for j := 0; j < len(users); j++ {
			users[j].Perms.setFromMode(modes[i])
		}
	}

	c.state.mu.Unlock()
}

// chanModes returns the ISUPPORT list of server-supported channel modes,
// alternatively falling back to ModeDefaults.
func (s *state) chanModes() string {
	if modes, ok := s.serverOptions["CHANMODES"]; ok && isValidChannelMode(modes) {
		return modes
	}

	return ModeDefaults
}

// userPrefixes returns the ISUPPORT list of server-supported user prefixes.
// This includes mode characters, as well as user prefix symbols. Falls back
// to DefaultPrefixes if not server-supported.
func (s *state) userPrefixes() string {
	if prefix, ok := s.serverOptions["PREFIX"]; ok && isValidUserPrefix(prefix) {
		return prefix
	}

	return DefaultPrefixes
}

// UserPerms contains all channel-based user permissions. The minimum op, and
// voice should be supported on all networks. This also supports non-rfc
// Owner, Admin, and HalfOp, if the network has support for it.
type UserPerms struct {
	// Owner (non-rfc) indicates that the user has full permissions to the
	// channel. More than one user can have owner permission.
	Owner bool
	// Admin (non-rfc) is commonly given to users that are trusted enough
	// to manage channel permissions, as well as higher level service settings.
	Admin bool
	// Op is commonly given to trusted users who can manage a given channel
	// by kicking, and banning users.
	Op bool
	// HalfOp (non-rfc) is commonly used to give users permissions like the
	// ability to kick, without giving them greater abilities to ban all users.
	HalfOp bool
	// Voice indicates the user has voice permissions, commonly given to known
	// users, wih very light trust, or to indicate a user is active.
	Voice bool
}

// IsAdmin indicates that the user has banning abilities, and are likely a
// very trustable user (e.g. op+).
func (m UserPerms) IsAdmin() bool {
	if m.Owner || m.Admin || m.Op {
		return true
	}

	return false
}

// IsAdmin indicates that the user at least has modes set upon them, higher
// than a regular joining user.
func (m UserPerms) IsTrusted() bool {
	if m.IsAdmin() || m.HalfOp || m.Voice {
		return true
	}

	return false
}

// reset resets the modes of a user.
func (m *UserPerms) reset() {
	m.Owner = false
	m.Admin = false
	m.Op = false
	m.HalfOp = false
	m.Voice = false
}

// set translates raw prefix characters into proper permissions. Only
// use this function when you have a session lock.
func (m *UserPerms) set(prefix string, append bool) {
	if !append {
		m.reset()
	}

	for i := 0; i < len(prefix); i++ {
		switch string(prefix[i]) {
		case OwnerPrefix:
			m.Owner = true
		case AdminPrefix:
			m.Admin = true
		case OperatorPrefix:
			m.Op = true
		case HalfOperatorPrefix:
			m.HalfOp = true
		case VoicePrefix:
			m.Voice = true
		}
	}
}

// setFromMode sets user-permissions based on channel user mode chars. E.g.
// "o" being oper, "v" being voice, etc.
func (m *UserPerms) setFromMode(mode CMode) {
	switch string(mode.name) {
	case ModeOwner:
		m.Owner = mode.add
	case ModeAdmin:
		m.Admin = mode.add
	case ModeOperator:
		m.Op = mode.add
	case ModeHalfOperator:
		m.HalfOp = mode.add
	case ModeVoice:
		m.Voice = mode.add
	}
}

// parseUserPrefix parses a raw mode line, like "@user" or "@+user".
func parseUserPrefix(raw string) (modes, nick string, success bool) {
	for i := 0; i < len(raw); i++ {
		char := string(raw[i])

		if char == OwnerPrefix || char == AdminPrefix || char == HalfOperatorPrefix ||
			char == OperatorPrefix || char == VoicePrefix {
			modes += char
			continue
		}

		// Assume we've gotten to the nickname part.
		if !IsValidNick(raw[i:]) {
			return modes, nick, false
		}

		nick = raw[i:]

		return modes, nick, true
	}

	return
}
<commit_msg>implement CModes.HasMode()<commit_after>// Copyright (c) Liam Stanley <me@liamstanley.io>. All rights reserved. Use
// of this source code is governed by the MIT license that can be found in
// the LICENSE file.

package girc

import "strings"

// CMode represents a single step of a given mode change.
type CMode struct {
	add     bool   // if it's a +, or -.
	name    byte   // character representation of the given mode.
	setting bool   // if it's a setting (should be stored) or temporary (op/voice/etc).
	args    string // arguments to the mode, if arguments are supported.
}

// Short returns a short representation of a mode without arguments. E.g. "+a",
// or "-b".
func (c *CMode) Short() string {
	var status string
	if c.add {
		status = "+"
	} else {
		status = "-"
	}

	return status + string(c.name)
}

// String returns a string representation of a mode, including optional
// arguments. E.g. "+b user*!ident@host.*.com"
func (c *CMode) String() string {
	if len(c.args) == 0 {
		return c.Short()
	}

	return c.Short() + " " + c.args
}

// CModes is a representation of a set of modes. This may be the given state
// of a channel/user, or the given state changes to a given channel/user.
type CModes struct {
	raw           string // raw supported modes.
	modesListArgs string // modes that add/remove users from lists and support args.
	modesArgs     string // modes that support args.
	modesSetArgs  string // modes that support args ONLY when set.
	modesNoArgs   string // modes that do not support args.

	prefixes string  // user permission prefixes. these aren't a CMode.setting.
	modes    []CMode // the list of modes for this given state.
}

// String returns a complete set of modes for this given state (change?). For
// example, "+a-b+cde some-arg".
func (c *CModes) String() string {
	var out string
	var args string

	if len(c.modes) > 0 {
		out += "+"
	}

	for i := 0; i < len(c.modes); i++ {
		out += string(c.modes[i].name)

		if len(c.modes[i].args) > 0 {
			args += " " + c.modes[i].args
		}
	}

	return out + args
}

// HasMode checks if the CModes state has a given mode. E.g. "m", or "I".
func (c *CModes) HasMode(mode string) bool {
	for i := 0; i < len(c.modes); i++ {
		if string(c.modes[i].name) == mode {
			return true
		}
	}

	return false
}

// hasArg checks to see if the mode supports arguments. What ones support this?:
//   A = Mode that adds or removes a nick or address to a list. Always has a parameter.
//   B = Mode that changes a setting and always has a parameter.
//   C = Mode that changes a setting and only has a parameter when set.
//   D = Mode that changes a setting and never has a parameter.
//   Note: Modes of type A return the list when there is no parameter present.
//   Note: Some clients assumes that any mode not listed is of type D.
//   Note: Modes in PREFIX are not listed but could be considered type B.
func (c *CModes) hasArg(set bool, mode byte) (hasArgs, isSetting bool) {
	if len(c.raw) < 1 {
		return false, true
	}

	if strings.IndexByte(c.modesListArgs, mode) > -1 {
		return true, false
	}

	if strings.IndexByte(c.modesArgs, mode) > -1 {
		return true, true
	}

	if strings.IndexByte(c.modesSetArgs, mode) > -1 {
		if set {
			return true, true
		}

		return false, true
	}

	if strings.IndexByte(c.prefixes, mode) > -1 {
		return true, false
	}

	return false, true
}

// apply merges two state changes, or one state change into a state of modes.
// For example, the latter would mean applying an incoming MODE with the modes
// stored for a channel.
func (c *CModes) apply(modes []CMode) {
	var new []CMode

	for j := 0; j < len(c.modes); j++ {
		isin := false
		for i := 0; i < len(modes); i++ {
			if !modes[i].setting {
				continue
			}
			if c.modes[j].name == modes[i].name && modes[i].add {
				new = append(new, modes[i])
				isin = true
				break
			}
		}

		if !isin {
			new = append(new, c.modes[j])
		}
	}

	for i := 0; i < len(modes); i++ {
		if !modes[i].setting || !modes[i].add {
			continue
		}

		isin := false
		for j := 0; j < len(new); j++ {
			if modes[i].name == new[j].name {
				isin = true
				break
			}
		}

		if !isin {
			new = append(new, modes[i])
		}
	}

	c.modes = new
}

// parse parses a set of flags and args, returning the necessary list of
// mappings for the mode flags.
func (c *CModes) parse(flags string, args []string) (out []CMode) {
	// add is the mode state we're currently in. Adding, or removing modes.
	add := true
	var argCount int

	for i := 0; i < len(flags); i++ {
		if flags[i] == 0x2B {
			add = true
			continue
		}
		if flags[i] == 0x2D {
			add = false
			continue
		}

		mode := CMode{
			name: flags[i],
			add:  add,
		}

		hasArgs, isSetting := c.hasArg(add, flags[i])
		if hasArgs && len(args) >= argCount+1 {
			mode.args = args[argCount]
			argCount++
		}
		mode.setting = isSetting

		out = append(out, mode)
	}

	return out
}

// newCModes returns a new CModes reference. channelModes and userPrefixes
// would be something you see from the server's "CHANMODES" and "PREFIX"
// ISUPPORT capability messages (alternatively, fall back to the standard)
// DefaultPrefixes and ModeDefaults.
func newCModes(channelModes, userPrefixes string) CModes {
	split := strings.SplitN(channelModes, ",", 4)
	if len(split) != 4 {
		for i := len(split); i < 4; i++ {
			split = append(split, "")
		}
	}

	return CModes{
		raw:           channelModes,
		modesListArgs: split[0],
		modesArgs:     split[1],
		modesSetArgs:  split[2],
		modesNoArgs:   split[3],

		prefixes: userPrefixes,
		modes:    []CMode{},
	}
}

// isValidChannelMode validates a channel mode (CHANMODES).
func isValidChannelMode(raw string) bool {
	if len(raw) < 1 {
		return false
	}

	for i := 0; i < len(raw); i++ {
		// Allowed are: ",", A-Z and a-z.
		if raw[i] != 0x2C && (raw[i] < 0x41 || raw[i] > 0x5A) && (raw[i] < 0x61 || raw[i] > 0x7A) {
			return false
		}
	}

	return true
}

// isValidUserPrefix validates a list of ISUPPORT-style user prefixes (PREFIX).
func isValidUserPrefix(raw string) bool {
	if len(raw) < 1 {
		return false
	}

	if raw[0] != 0x28 { // (.
		return false
	}

	var keys, rep int
	var passedKeys bool

	// Skip the first one as we know it's (.
	for i := 1; i < len(raw); i++ {
		if raw[i] == 0x29 { // ).
			passedKeys = true
			continue
		}

		if passedKeys {
			rep++
		} else {
			keys++
		}
	}

	return keys == rep
}

// parsePrefixes parses the mode character mappings from the symbols of a
// ISUPPORT-style user prefixes list (PREFIX).
func parsePrefixes(raw string) (modes, prefixes string) {
	if !isValidUserPrefix(raw) {
		return modes, prefixes
	}

	i := strings.Index(raw, ")")
	if i < 1 {
		return modes, prefixes
	}

	return raw[1:i], raw[i+1:]
}

// handleMODE handles incoming MODE messages, and updates the tracking
// information for each channel, as well as if any of the modes affect user
// permissions.
func handleMODE(c *Client, e Event) {
	// Check if it's a RPL_CHANNELMODEIS.
	if e.Command == RPL_CHANNELMODEIS && len(e.Params) > 2 {
		// RPL_CHANNELMODEIS sends the user as the first param, skip it.
		e.Params = e.Params[1:]
	}
	// Should be at least MODE <target> <flags>, to be useful. As well, only
	// tracking channel modes at the moment.
	if len(e.Params) < 2 || !IsValidChannel(e.Params[0]) {
		return
	}

	c.state.mu.Lock()
	channel := c.state.lookupChannel(e.Params[0])
	if channel == nil {
		c.state.mu.Unlock()
		return
	}

	flags := e.Params[1]
	var args []string
	if len(e.Params) > 2 {
		args = append(args, e.Params[2:]...)
	}

	modes := channel.Modes.parse(flags, args)
	channel.Modes.apply(modes)

	// Loop through and update users modes as necessary.
	for i := 0; i < len(modes); i++ {
		if modes[i].setting || len(modes[i].args) == 0 {
			continue
		}

		users := c.state.lookupUsers("nick", modes[i].args)
		for j := 0; j < len(users); j++ {
			users[j].Perms.setFromMode(modes[i])
		}
	}

	c.state.mu.Unlock()
}

// chanModes returns the ISUPPORT list of server-supported channel modes,
// alternatively falling back to ModeDefaults.
func (s *state) chanModes() string {
	if modes, ok := s.serverOptions["CHANMODES"]; ok && isValidChannelMode(modes) {
		return modes
	}

	return ModeDefaults
}

// userPrefixes returns the ISUPPORT list of server-supported user prefixes.
// This includes mode characters, as well as user prefix symbols. Falls back
// to DefaultPrefixes if not server-supported.
func (s *state) userPrefixes() string {
	if prefix, ok := s.serverOptions["PREFIX"]; ok && isValidUserPrefix(prefix) {
		return prefix
	}

	return DefaultPrefixes
}

// UserPerms contains all channel-based user permissions. The minimum op, and
// voice should be supported on all networks. This also supports non-rfc
// Owner, Admin, and HalfOp, if the network has support for it.
type UserPerms struct {
	// Owner (non-rfc) indicates that the user has full permissions to the
	// channel. More than one user can have owner permission.
	Owner bool
	// Admin (non-rfc) is commonly given to users that are trusted enough
	// to manage channel permissions, as well as higher level service settings.
	Admin bool
	// Op is commonly given to trusted users who can manage a given channel
	// by kicking, and banning users.
	Op bool
	// HalfOp (non-rfc) is commonly used to give users permissions like the
	// ability to kick, without giving them greater abilities to ban all users.
	HalfOp bool
	// Voice indicates the user has voice permissions, commonly given to known
	// users, wih very light trust, or to indicate a user is active.
	Voice bool
}

// IsAdmin indicates that the user has banning abilities, and are likely a
// very trustable user (e.g. op+).
func (m UserPerms) IsAdmin() bool {
	if m.Owner || m.Admin || m.Op {
		return true
	}

	return false
}

// IsAdmin indicates that the user at least has modes set upon them, higher
// than a regular joining user.
func (m UserPerms) IsTrusted() bool {
	if m.IsAdmin() || m.HalfOp || m.Voice {
		return true
	}

	return false
}

// reset resets the modes of a user.
func (m *UserPerms) reset() {
	m.Owner = false
	m.Admin = false
	m.Op = false
	m.HalfOp = false
	m.Voice = false
}

// set translates raw prefix characters into proper permissions. Only
// use this function when you have a session lock.
func (m *UserPerms) set(prefix string, append bool) {
	if !append {
		m.reset()
	}

	for i := 0; i < len(prefix); i++ {
		switch string(prefix[i]) {
		case OwnerPrefix:
			m.Owner = true
		case AdminPrefix:
			m.Admin = true
		case OperatorPrefix:
			m.Op = true
		case HalfOperatorPrefix:
			m.HalfOp = true
		case VoicePrefix:
			m.Voice = true
		}
	}
}

// setFromMode sets user-permissions based on channel user mode chars. E.g.
// "o" being oper, "v" being voice, etc.
func (m *UserPerms) setFromMode(mode CMode) {
	switch string(mode.name) {
	case ModeOwner:
		m.Owner = mode.add
	case ModeAdmin:
		m.Admin = mode.add
	case ModeOperator:
		m.Op = mode.add
	case ModeHalfOperator:
		m.HalfOp = mode.add
	case ModeVoice:
		m.Voice = mode.add
	}
}

// parseUserPrefix parses a raw mode line, like "@user" or "@+user".
func parseUserPrefix(raw string) (modes, nick string, success bool) {
	for i := 0; i < len(raw); i++ {
		char := string(raw[i])

		if char == OwnerPrefix || char == AdminPrefix || char == HalfOperatorPrefix ||
			char == OperatorPrefix || char == VoicePrefix {
			modes += char
			continue
		}

		// Assume we've gotten to the nickname part.
		if !IsValidNick(raw[i:]) {
			return modes, nick, false
		}

		nick = raw[i:]

		return modes, nick, true
	}

	return
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
)

type InputNode struct {
	Id      string   `json:"id"`
	Parents []string `json:"parents"`
}

// Type:
// 0: |
// 1: ┘
// 2: ┐
// 3: ┌
type Point struct {
	X    int `json:"x"`
	Y    int `json:"y"`
	Type int `json:"type"`
}

type Path struct {
	Id   string  `json:"id"`
	Path []Point `json:"path"`
}

type OutputNode struct {
	Id                string             `json:"id"`
	Parents           []string           `json:"parents"`
	Column            int                `json:"column"`
	ParentsPaths      map[string][]Point `json:"-"`
	FinalParentsPaths []Path             `json:"parents_paths"`
	Idx               int                `json:"idx"`
	Children          []string           `json:"-"`
}

func serializeOutput(out []*OutputNode) ([]byte, error) {
	for _, node := range out {
		for parentId, path := range node.ParentsPaths {
			node.FinalParentsPaths = append(node.FinalParentsPaths, Path{parentId, path})
		}
	}
	treeBytes, err := json.Marshal(&out)
	return treeBytes, err
}

func getInputNodesFromJson(inputJson string) (nodes []InputNode, err error) {
	if err = json.Unmarshal([]byte(inputJson), &nodes); err != nil {
		return
	}
	return
}

func initNodes(inputNodes []InputNode) []*OutputNode {
	out := make([]*OutputNode, 0)
	for idx, node := range inputNodes {
		newNode := OutputNode{}
		newNode.Id = node.Id
		newNode.Parents = node.Parents
		newNode.Column = -1
		newNode.ParentsPaths = make(map[string][]Point)
		newNode.FinalParentsPaths = make([]Path, 0)
		newNode.Idx = idx
		newNode.Children = make([]string, 0)
		out = append(out, &newNode)
	}
	return out
}

func initIndex(nodes []*OutputNode) map[string]*OutputNode {
	index := make(map[string]*OutputNode)
	for _, node := range nodes {
		index[node.Id] = node
	}
	return index
}

func initChildren(nodes []*OutputNode, index map[string]*OutputNode) {
	for _, node := range nodes {
		for _, parentId := range node.Parents {
			index[parentId].Children = append(index[parentId].Children, node.Id)
		}
	}
}

func setColumns(nodes []*OutputNode, index map[string]*OutputNode) {
	nextColumn := 0
	for _, node := range nodes {
		if node.Column == -1 {
			node.Column = nextColumn
			nextColumn++
		}

		for _, childId := range node.Children {
			child := index[childId]
			if child.Column > node.Column {
				nextColumn--

			if !(child.Column > node.Column && child.Parents[0] == node.Id && len(child.Parents) > 1) {
				// Insert before the last element '-__-
				pos := len(child.ParentsPaths[node.Id]) - 1
				child.ParentsPaths[node.Id] = append(child.ParentsPaths[node.Id], Point{})
				copy(child.ParentsPaths[node.Id][pos+1:], child.ParentsPaths[node.Id][pos:])
				child.ParentsPaths[node.Id][pos] = Point{child.ParentsPaths[node.Id][pos-1].X, node.Idx, 1}
			}
			}
		}

		for parentIdx, parentId := range node.Parents {
			parent := index[parentId]

			node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{node.Column, node.Idx, 0})

			if parent.Column == -1 {
				if parentIdx == 0 || (parentIdx == 1 && index[node.Parents[0]].Column < node.Column) {
					parent.Column = node.Column
				} else {
					parent.Column = nextColumn
					node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, node.Idx, 2})
					nextColumn++
				}
			} else {
				if node.Column < parent.Column && parentIdx == 0 {
					for _, childId := range parent.Children {
						child := index[childId]
						idxRemove := len(child.ParentsPaths[parent.Id]) - 1
						if idxRemove > 0 {
							if child.ParentsPaths[parent.Id][idxRemove].Type != 2 {
								child.ParentsPaths[parent.Id] = append(child.ParentsPaths[parent.Id][:idxRemove], child.ParentsPaths[parent.Id][idxRemove+1:]...) // DELETE '-__-
							}
							child.ParentsPaths[parent.Id] = append(child.ParentsPaths[parent.Id], Point{node.Column, parent.Idx, 0})
						}
					}
					parent.Column = node.Column
				} else if node.Column > parent.Column {
					if node.Parents[0] == parent.Id && len(node.Parents) > 1 {
						node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, node.Idx, 3})
					}
				}
			}

			node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, parent.Idx, 0})

		}
	}
}

func buildTree(inputNodes []InputNode) ([]*OutputNode, error) {
	var nodes []*OutputNode = initNodes(inputNodes)
	var index map[string]*OutputNode = initIndex(nodes)

	initChildren(nodes, index)
	setColumns(nodes, index)

	return nodes, nil
}

func BuildTreeJson(inputJson string) (tree string, err error) {
	nodes, err := getInputNodesFromJson(inputJson)
	if err != nil {
		return
	}

	out, err := buildTree(nodes)
	if err != nil {
		return
	}

	treeBytes, err := serializeOutput(out)
	if err != nil {
		return
	}
	tree = string(treeBytes)
	return
}

func bootstrap(c *cli.Context) {
	var inputJson string
	jsonFlag := c.String("json")
	fileFlag := c.String("file")
	if jsonFlag != "" {
		inputJson = jsonFlag
	} else if fileFlag != "" {
		bytes, err := ioutil.ReadFile(fileFlag)
		if err != nil {
			fmt.Println(err)
			return
		}
		inputJson = string(bytes)
	} else {
		cli.ShowAppHelp(c)
		return
	}

	out, err := BuildTreeJson(inputJson)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(out)
}

func main() {
	var authors []cli.Author
	// Collaborators, add your name here :)
	authors = append(authors, cli.Author{"Alain Gilbert", "alain.gilbert.15@gmail.com"})

	app := cli.NewApp()
	app.Authors = authors
	app.Version = "0.0.0"
	app.Name = "git2graph"
	app.Usage = "Take a git tree, make a graph structure"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "f, file",
			Usage: "File",
		},
		cli.StringFlag{
			Name:  "j, json",
			Usage: "Json input",
		},
	}
	app.Action = bootstrap
	app.Run(os.Args)
}
<commit_msg>Reduce code complexity<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/codegangsta/cli"
	"io/ioutil"
	"os"
)

type InputNode struct {
	Id      string   `json:"id"`
	Parents []string `json:"parents"`
}

// Type:
// 0: |
// 1: ┘
// 2: ┐
// 3: ┌
type Point struct {
	X    int `json:"x"`
	Y    int `json:"y"`
	Type int `json:"type"`
}

type Path struct {
	Id   string  `json:"id"`
	Path []Point `json:"path"`
}

type OutputNode struct {
	Id                string             `json:"id"`
	Parents           []string           `json:"parents"`
	Column            int                `json:"column"`
	ParentsPaths      map[string][]Point `json:"-"`
	FinalParentsPaths []Path             `json:"parents_paths"`
	Idx               int                `json:"idx"`
	Children          []string           `json:"-"`
}

func serializeOutput(out []*OutputNode) ([]byte, error) {
	for _, node := range out {
		for parentId, path := range node.ParentsPaths {
			node.FinalParentsPaths = append(node.FinalParentsPaths, Path{parentId, path})
		}
	}
	treeBytes, err := json.Marshal(&out)
	return treeBytes, err
}

func getInputNodesFromJson(inputJson string) (nodes []InputNode, err error) {
	if err = json.Unmarshal([]byte(inputJson), &nodes); err != nil {
		return
	}
	return
}

func initNodes(inputNodes []InputNode) []*OutputNode {
	out := make([]*OutputNode, 0)
	for idx, node := range inputNodes {
		newNode := OutputNode{}
		newNode.Id = node.Id
		newNode.Parents = node.Parents
		newNode.Column = -1
		newNode.ParentsPaths = make(map[string][]Point)
		newNode.FinalParentsPaths = make([]Path, 0)
		newNode.Idx = idx
		newNode.Children = make([]string, 0)
		out = append(out, &newNode)
	}
	return out
}

func initIndex(nodes []*OutputNode) map[string]*OutputNode {
	index := make(map[string]*OutputNode)
	for _, node := range nodes {
		index[node.Id] = node
	}
	return index
}

func initChildren(nodes []*OutputNode, index map[string]*OutputNode) {
	for _, node := range nodes {
		for _, parentId := range node.Parents {
			index[parentId].Children = append(index[parentId].Children, node.Id)
		}
	}
}

func setColumns(nodes []*OutputNode, index map[string]*OutputNode) {
	nextColumn := 0
	for _, node := range nodes {
		if node.Column == -1 {
			node.Column = nextColumn
			nextColumn++
		}

		for _, childId := range node.Children {
			child := index[childId]
			if node.Column < child.Column {
				nextColumn--

			if child.Parents[0] != node.Id || len(child.Parents) <= 1 {
				// Insert before the last element '-__-
				pos := len(child.ParentsPaths[node.Id]) - 1
				child.ParentsPaths[node.Id] = append(child.ParentsPaths[node.Id], Point{})
				copy(child.ParentsPaths[node.Id][pos+1:], child.ParentsPaths[node.Id][pos:])
				child.ParentsPaths[node.Id][pos] = Point{child.ParentsPaths[node.Id][pos-1].X, node.Idx, 1}
			}
			}
		}

		for parentIdx, parentId := range node.Parents {
			parent := index[parentId]

			node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{node.Column, node.Idx, 0})

			if parent.Column == -1 {
				if parentIdx == 0 || (parentIdx == 1 && index[node.Parents[0]].Column < node.Column) {
					parent.Column = node.Column
				} else {
					parent.Column = nextColumn
					node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, node.Idx, 2})
					nextColumn++
				}
			} else {
				if node.Column < parent.Column && parentIdx == 0 {
					for _, childId := range parent.Children {
						child := index[childId]
						idxRemove := len(child.ParentsPaths[parent.Id]) - 1
						if idxRemove > 0 {
							if child.ParentsPaths[parent.Id][idxRemove].Type != 2 {
								child.ParentsPaths[parent.Id] = append(child.ParentsPaths[parent.Id][:idxRemove], child.ParentsPaths[parent.Id][idxRemove+1:]...) // DELETE '-__-
							}
							child.ParentsPaths[parent.Id] = append(child.ParentsPaths[parent.Id], Point{node.Column, parent.Idx, 0})
						}
					}
					parent.Column = node.Column
				} else if node.Column > parent.Column {
					if node.Parents[0] == parent.Id && len(node.Parents) > 1 {
						node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, node.Idx, 3})
					}
				}
			}

			node.ParentsPaths[parent.Id] = append(node.ParentsPaths[parent.Id], Point{parent.Column, parent.Idx, 0})

		}
	}
}

func buildTree(inputNodes []InputNode) ([]*OutputNode, error) {
	var nodes []*OutputNode = initNodes(inputNodes)
	var index map[string]*OutputNode = initIndex(nodes)

	initChildren(nodes, index)
	setColumns(nodes, index)

	return nodes, nil
}

func BuildTreeJson(inputJson string) (tree string, err error) {
	nodes, err := getInputNodesFromJson(inputJson)
	if err != nil {
		return
	}

	out, err := buildTree(nodes)
	if err != nil {
		return
	}

	treeBytes, err := serializeOutput(out)
	if err != nil {
		return
	}
	tree = string(treeBytes)
	return
}

func bootstrap(c *cli.Context) {
	var inputJson string
	jsonFlag := c.String("json")
	fileFlag := c.String("file")
	if jsonFlag != "" {
		inputJson = jsonFlag
	} else if fileFlag != "" {
		bytes, err := ioutil.ReadFile(fileFlag)
		if err != nil {
			fmt.Println(err)
			return
		}
		inputJson = string(bytes)
	} else {
		cli.ShowAppHelp(c)
		return
	}

	out, err := BuildTreeJson(inputJson)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(out)
}

func main() {
	var authors []cli.Author
	// Collaborators, add your name here :)
	authors = append(authors, cli.Author{"Alain Gilbert", "alain.gilbert.15@gmail.com"})

	app := cli.NewApp()
	app.Authors = authors
	app.Version = "0.0.0"
	app.Name = "git2graph"
	app.Usage = "Take a git tree, make a graph structure"
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:  "f, file",
			Usage: "File",
		},
		cli.StringFlag{
			Name:  "j, json",
			Usage: "Json input",
		},
	}
	app.Action = bootstrap
	app.Run(os.Args)
}
<|endoftext|>