<commit_before>package models

import (
	"github.com/jinzhu/gorm"
	"time"
)

type Build struct {
	gorm.Model

	Uri string `form:"uri"`

	Jobs     []Job
	Branches []Branch

	Status     string
	StatusTime time.Time
}

func (b *Build) UpdateStatus() {
	var branches []Branch
	var status string
	var time time.Time

	dbHandle.Where("build_id = ?", b.ID).Find(&branches)
	if len(branches) > 0 {
		status = StatusPassed
		for _, branch := range branches {
			if status == StatusPassed {
				if time.Sub(branch.StatusTime).Seconds() < 0.0 {
					time = branch.StatusTime
				}

				if branch.Status == StatusBusy || branch.Status == StatusNew {
					status = branch.Status
					time = branch.StatusTime
				} else if branch.Status == StatusError || branch.Status == StatusFailed {
					status = branch.Status
					time = branch.StatusTime
				}
			}
		}
	} else {
		status = StatusUnknown
		time = b.CreatedAt
	}

	b.Status = status
	b.StatusTime = time
	dbHandle.Save(b)
}
<commit_msg>Busy and New always goes above failed/passed/error<commit_after>package models

import (
	"github.com/jinzhu/gorm"
	"time"
)

type Build struct {
	gorm.Model

	Uri string `form:"uri"`

	Jobs     []Job
	Branches []Branch

	Status     string
	StatusTime time.Time
}

func (b *Build) UpdateStatus() {
	var branches []Branch
	var status string
	var time time.Time

	dbHandle.Where("build_id = ?", b.ID).Find(&branches)
	if len(branches) > 0 {
		status = StatusPassed
		for _, branch := range branches {
			if branch.Status == StatusBusy || branch.Status == StatusNew {
				status = branch.Status
				time = branch.StatusTime
			}
			if status == StatusPassed {
				if time.Sub(branch.StatusTime).Seconds() < 0.0 {
					time = branch.StatusTime
				}

				if branch.Status == StatusError || branch.Status == StatusFailed {
					status = branch.Status
					time = branch.StatusTime
				}
			}
		}
	} else {
		status = StatusUnknown
		time = b.CreatedAt
	}

	b.Status = status
	b.StatusTime = time
	dbHandle.Save(b)
}
<|endoftext|>


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

// Core siegfried defaults
package config

import (
	"net/http"
	"path/filepath"
	"time"
)

var siegfried = struct {
	version   [3]int // Siegfried version (i.e. of the sf tool)
	home      string // Home directory used by both sf and roy tools
	signature string // Name of signature file
	magic     []byte // Magic bytes to ID signature file
	// Defaults for processing bytematcher signatures. These control the segmentation.
	distance  int // The acceptable distance between two frames before they will be segmented (default is 8192)
	rng       int // The acceptable range between two frames before they will be segmented (default is 0-2049)
	choices   int // The acceptable number of plain sequences generated from a single segment
	varLength int // The acceptable length of a variable byte sequence (longer the better to reduce false matches)
	// Config for using the update service.
	updateURL       string // URL for the update service (a JSON file that indicates whether update necessary and where can be found)
	updateTimeout   time.Duration
	updateTransport *http.Transport
	// Archivematica format policy registry service
	fpr string
	// DEBUG mode
	debug bool
}{
	version:         [3]int{1, 1, 0},
	signature:       "pronom.sig",
	magic:           []byte{'s', 'f', 0xFF, 0xFF},
	distance:        8192,
	rng:             512,
	choices:         64,
	varLength:       2,
	updateURL:       "http://www.itforarchivists.com/siegfried/update",
	updateTimeout:   30 * time.Second,
	updateTransport: &http.Transport{Proxy: http.ProxyFromEnvironment},
}

// GETTERS

func Version() [3]int {
	return siegfried.version
}

func Home() string {
	return siegfried.home
}

func Signature() string {
	if filepath.Dir(siegfried.signature) == "." {
		return filepath.Join(siegfried.home, siegfried.signature)
	}
	return siegfried.signature
}

func SignatureBase() string {
	return siegfried.signature
}

func Magic() []byte {
	return siegfried.magic
}

func Distance() int {
	return siegfried.distance
}

func Range() int {
	return siegfried.rng
}

func Choices() int {
	return siegfried.choices
}

func VarLength() int {
	return siegfried.varLength
}

func BMOptions() (int, int, int, int) {
	return siegfried.distance, siegfried.rng, siegfried.choices, siegfried.varLength
}

func UpdateOptions() (string, time.Duration, *http.Transport) {
	return siegfried.updateURL, siegfried.updateTimeout, siegfried.updateTransport
}

func Fpr() string {
	return siegfried.fpr
}

func Debug() bool {
	return siegfried.debug
}

// SETTERS

func SetHome(h string) {
	siegfried.home = h
}

func SetSignature(s string) {
	siegfried.signature = s
}

func SetDistance(i int) func() private {
	return func() private {
		siegfried.distance = i
		return private{}
	}
}

func SetRange(i int) func() private {
	return func() private {
		siegfried.rng = i
		return private{}
	}
}

func SetChoices(i int) func() private {
	return func() private {
		siegfried.choices = i
		return private{}
	}
}

func SetVarLength(i int) func() private {
	return func() private {
		siegfried.varLength = i
		return private{}
	}
}

func SetDebug() {
	siegfried.debug = true
}
<commit_msg>fpr now default<commit_after>// Copyright 2014 Richard Lehane. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Core siegfried defaults
package config

import (
	"net/http"
	"path/filepath"
	"time"
)

var siegfried = struct {
	version   [3]int // Siegfried version (i.e. of the sf tool)
	home      string // Home directory used by both sf and roy tools
	signature string // Name of signature file
	magic     []byte // Magic bytes to ID signature file
	// Defaults for processing bytematcher signatures. These control the segmentation.
	distance  int // The acceptable distance between two frames before they will be segmented (default is 8192)
	rng       int // The acceptable range between two frames before they will be segmented (default is 0-2049)
	choices   int // The acceptable number of plain sequences generated from a single segment
	varLength int // The acceptable length of a variable byte sequence (longer the better to reduce false matches)
	// Config for using the update service.
	updateURL       string // URL for the update service (a JSON file that indicates whether update necessary and where can be found)
	updateTimeout   time.Duration
	updateTransport *http.Transport
	// Archivematica format policy registry service
	fpr string
	// DEBUG mode
	debug bool
}{
	version:         [3]int{1, 1, 0},
	signature:       "pronom.sig",
	magic:           []byte{'s', 'f', 0xFF, 0xFF},
	distance:        8192,
	rng:             512,
	choices:         64,
	varLength:       2,
	updateURL:       "http://www.itforarchivists.com/siegfried/update",
	updateTimeout:   30 * time.Second,
	updateTransport: &http.Transport{Proxy: http.ProxyFromEnvironment},
	fpr:             "/tmp/siegfried",
}

// GETTERS

func Version() [3]int {
	return siegfried.version
}

func Home() string {
	return siegfried.home
}

func Signature() string {
	if filepath.Dir(siegfried.signature) == "." {
		return filepath.Join(siegfried.home, siegfried.signature)
	}
	return siegfried.signature
}

func SignatureBase() string {
	return siegfried.signature
}

func Magic() []byte {
	return siegfried.magic
}

func Distance() int {
	return siegfried.distance
}

func Range() int {
	return siegfried.rng
}

func Choices() int {
	return siegfried.choices
}

func VarLength() int {
	return siegfried.varLength
}

func BMOptions() (int, int, int, int) {
	return siegfried.distance, siegfried.rng, siegfried.choices, siegfried.varLength
}

func UpdateOptions() (string, time.Duration, *http.Transport) {
	return siegfried.updateURL, siegfried.updateTimeout, siegfried.updateTransport
}

func Fpr() string {
	return siegfried.fpr
}

func Debug() bool {
	return siegfried.debug
}

// SETTERS

func SetHome(h string) {
	siegfried.home = h
}

func SetSignature(s string) {
	siegfried.signature = s
}

func SetDistance(i int) func() private {
	return func() private {
		siegfried.distance = i
		return private{}
	}
}

func SetRange(i int) func() private {
	return func() private {
		siegfried.rng = i
		return private{}
	}
}

func SetChoices(i int) func() private {
	return func() private {
		siegfried.choices = i
		return private{}
	}
}

func SetVarLength(i int) func() private {
	return func() private {
		siegfried.varLength = i
		return private{}
	}
}

func SetDebug() {
	siegfried.debug = true
}
<|endoftext|>


<commit_before>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/persist"
	"github.com/NebulousLabs/Sia/types"
	"github.com/boltdb/bolt"
)

var meta = persist.Metadata{
	Version: "0.4.0",
	Header:  "Consensus Set Database",
}

var (
	errBadSetInsert = errors.New("attempting to add an already existing item to the consensus set")
	errNilBucket    = errors.New("using a bucket that does not exist")
	errNilItem      = errors.New("requested item does not exist")
	errNotGuarded   = errors.New("database modification not protected by guard")
)

// setDB is a wrapper around the persist bolt db which backs the
// consensus set
type setDB struct {
	*persist.BoltDatabase
	// The open flag is used to prevent reading from the database
	// after closing sia when the loading loop is still running
	open bool // DEPRECATED
}

// openDB loads the set database and populates it with the necessary buckets
func openDB(filename string) (*setDB, error) {
	db, err := persist.OpenDatabase(meta, filename)
	if err != nil {
		return nil, err
	}

	var buckets []string = []string{
		"Path",
		"BlockMap",
		"Metadata",
	}

	// Create buckets
	err = db.Update(func(tx *bolt.Tx) error {
		for _, bucketName := range buckets {
			_, err := tx.CreateBucketIfNotExists([]byte(bucketName))
			if err != nil {
				return err
			}
		}
		// Initilize the consistency guards
		b := tx.Bucket([]byte("Metadata"))
		err := b.Put([]byte("GuardA"), encoding.Marshal(0))
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardB"), encoding.Marshal(0))
	})
	return &setDB{db, true}, err
}

// startConsistencyGuard increments the first guard. If this is not
// equal to the second, a transaction is taking place in the database
func (db *setDB) startConsistencyGuard() {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var i int
		err := encoding.Unmarshal(b.Get([]byte("GuardA")), &i)
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardA"), encoding.Marshal(i+1))
	})
	if err != nil {
		panic(err)
	}
}

// startConsistencyGuard increments the first guard. If this is not
// equal to the second, a transaction is taking place in the database
func (db *setDB) stopConsistencyGuard() {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var i int
		err := encoding.Unmarshal(b.Get([]byte("GuardB")), &i)
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardB"), encoding.Marshal(i+1))
	})
	if err != nil {
		panic(err)
	}
}

// checkConsistencyGuard checks the two guards and returns true if
// they differ. This signifies that thaer there is a transaction
// taking place.
func (db *setDB) checkConsistencyGuard() bool {
	var guarded bool
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var x, y int
		err := encoding.Unmarshal(b.Get([]byte("GuardA")), &x)
		if err != nil {
			return err
		}
		err = encoding.Unmarshal(b.Get([]byte("GuardB")), &y)
		if err != nil {
			return err
		}
		guarded = x != y
		return nil
	})
	if err != nil {
		panic(err)
	}
	return guarded
}

// addItem should only be called from this file, and adds a new item
// to the database
//
// addItem and getItem are part of consensus due to stricter error
// conditions than a generic bolt implementation
func (db *setDB) addItem(bucket string, key, value interface{}) error {
	// Check that this transaction is guarded by consensusGuard.
	// However, allow direct database modifications when testing
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	v := encoding.Marshal(value)
	k := encoding.Marshal(key)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		// Sanity check: make sure the buckets exists and that
		// you are not inserting something that already exists
		if build.DEBUG {
			if b == nil {
				panic(errNilBucket)
			}
			i := b.Get(k)
			if i != nil {
				panic(errBadSetInsert)
			}
		}
		return b.Put(k, v)
	})
}

// getItem is a generic function to insert an item into the set database
func (db *setDB) getItem(bucket string, key interface{}) (item []byte, err error) {
	k := encoding.Marshal(key)
	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		// Sanity check to make sure the bucket exists.
		if build.DEBUG {
			if b == nil {
				panic(errNilBucket)
			}
		}
		item = b.Get(k)
		// Sanity check to make sure the item requested exists
		if build.DEBUG {
			if item == nil {
				panic(errNilItem)
			}
		}
		return nil
	})
	return item, err
}

// rmItem removes an item from a bucket
func (db *setDB) rmItem(bucket string, key interface{}) error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	k := encoding.Marshal(key)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		if build.DEBUG {
			// Sanity check to make sure the bucket exists.
			if b == nil {
				panic(errNilBucket)
			}
			// Sanity check to make sure you are deleting an item that exists
			item := b.Get(k)
			if item == nil {
				panic(errNilItem)
			}
		}
		return b.Delete(k)
	})
}

// inBucket checks if an item with the given key is in the bucket
func (db *setDB) inBucket(bucket string, key interface{}) bool {
	exists, err := db.Exists(bucket, encoding.Marshal(key))
	if build.DEBUG && err != nil {
		panic(err)
	}
	return exists
}

// pushPath inserts a block into the database at the "end" of the chain, i.e.
// the current height + 1.
func (db *setDB) pushPath(bid types.BlockID) error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	value := encoding.Marshal(bid)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Path"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN))
		return b.Put(key, value)
	})
}

// popPath removes a block from the "end" of the chain, i.e. the block
// with the largest height.
func (db *setDB) popPath() error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Path"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN - 1))
		return b.Delete(key)
	})
}

// getPath retreives the block id of a block at a given hegiht from the path
func (db *setDB) getPath(h types.BlockHeight) (id types.BlockID) {
	idBytes, err := db.getItem("Path", h)
	if err != nil {
		panic(err)
	}
	err = encoding.Unmarshal(idBytes, &id)
	if err != nil {
		panic(err)
	}
	return
}

// pathHeight returns the size of the current path
func (db *setDB) pathHeight() types.BlockHeight {
	h, err := db.BucketSize("Path")
	if err != nil {
		panic(err)
	}
	return types.BlockHeight(h)
}

// addBlockMap adds a processedBlock to the block map
// This will eventually take a processed block as an argument
func (db *setDB) addBlockMap(pb *processedBlock) error {
	return db.addItem("BlockMap", pb.Block.ID(), *pb)
}

// getBlockMap queries the set database to return a processedBlock
// with the given ID
func (db *setDB) getBlockMap(id types.BlockID) *processedBlock {
	bnBytes, err := db.getItem("BlockMap", id)
	if build.DEBUG && err != nil {
		panic(err)
	}
	var pb processedBlock
	err = encoding.Unmarshal(bnBytes, &pb)
	if build.DEBUG && err != nil {
		panic(err)
	}
	return &pb
}

// inBlockMap checks for the existance of a block with a given ID in
// the consensus set
func (db *setDB) inBlockMap(id types.BlockID) bool {
	return db.inBucket("BlockMap", id)
}

// rmBlockMap removes a processedBlock from the blockMap bucket
func (db *setDB) rmBlockMap(id types.BlockID) error {
	return db.rmItem("BlockMap", id)
}

// updateBlockMap is a wrapper function for modification of
func (db *setDB) updateBlockMap(pb *processedBlock) {
	// These errors will only be caused by an error by bolt
	// e.g. database being closed.
	err := db.rmBlockMap(pb.Block.ID())
	if build.DEBUG && err != nil {
		panic(err)
	}
	err = db.addBlockMap(pb)
	if build.DEBUG && err != nil {
		panic(err)
	}
}
<commit_msg>Make database functions for Siafund Outputs<commit_after>package consensus

import (
	"errors"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/persist"
	"github.com/NebulousLabs/Sia/types"
	"github.com/boltdb/bolt"
)

var meta = persist.Metadata{
	Version: "0.4.0",
	Header:  "Consensus Set Database",
}

var (
	errBadSetInsert = errors.New("attempting to add an already existing item to the consensus set")
	errNilBucket    = errors.New("using a bucket that does not exist")
	errNilItem      = errors.New("requested item does not exist")
	errNotGuarded   = errors.New("database modification not protected by guard")
)

// setDB is a wrapper around the persist bolt db which backs the
// consensus set
type setDB struct {
	*persist.BoltDatabase
	// The open flag is used to prevent reading from the database
	// after closing sia when the loading loop is still running
	open bool // DEPRECATED
}

// openDB loads the set database and populates it with the necessary buckets
func openDB(filename string) (*setDB, error) {
	db, err := persist.OpenDatabase(meta, filename)
	if err != nil {
		return nil, err
	}

	var buckets []string = []string{
		"Path",
		"BlockMap",
		"Metadata",
	}

	// Create buckets
	err = db.Update(func(tx *bolt.Tx) error {
		for _, bucketName := range buckets {
			_, err := tx.CreateBucketIfNotExists([]byte(bucketName))
			if err != nil {
				return err
			}
		}
		// Initilize the consistency guards
		b := tx.Bucket([]byte("Metadata"))
		err := b.Put([]byte("GuardA"), encoding.Marshal(0))
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardB"), encoding.Marshal(0))
	})
	return &setDB{db, true}, err
}

// startConsistencyGuard increments the first guard. If this is not
// equal to the second, a transaction is taking place in the database
func (db *setDB) startConsistencyGuard() {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var i int
		err := encoding.Unmarshal(b.Get([]byte("GuardA")), &i)
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardA"), encoding.Marshal(i+1))
	})
	if err != nil {
		panic(err)
	}
}

// startConsistencyGuard increments the first guard. If this is not
// equal to the second, a transaction is taking place in the database
func (db *setDB) stopConsistencyGuard() {
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var i int
		err := encoding.Unmarshal(b.Get([]byte("GuardB")), &i)
		if err != nil {
			return err
		}
		return b.Put([]byte("GuardB"), encoding.Marshal(i+1))
	})
	if err != nil {
		panic(err)
	}
}

// checkConsistencyGuard checks the two guards and returns true if
// they differ. This signifies that thaer there is a transaction
// taking place.
func (db *setDB) checkConsistencyGuard() bool {
	var guarded bool
	err := db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Metadata"))
		var x, y int
		err := encoding.Unmarshal(b.Get([]byte("GuardA")), &x)
		if err != nil {
			return err
		}
		err = encoding.Unmarshal(b.Get([]byte("GuardB")), &y)
		if err != nil {
			return err
		}
		guarded = x != y
		return nil
	})
	if err != nil {
		panic(err)
	}
	return guarded
}

// addItem should only be called from this file, and adds a new item
// to the database
//
// addItem and getItem are part of consensus due to stricter error
// conditions than a generic bolt implementation
func (db *setDB) addItem(bucket string, key, value interface{}) error {
	// Check that this transaction is guarded by consensusGuard.
	// However, allow direct database modifications when testing
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	v := encoding.Marshal(value)
	k := encoding.Marshal(key)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		// Sanity check: make sure the buckets exists and that
		// you are not inserting something that already exists
		if build.DEBUG {
			if b == nil {
				panic(errNilBucket)
			}
			i := b.Get(k)
			if i != nil {
				panic(errBadSetInsert)
			}
		}
		return b.Put(k, v)
	})
}

// getItem is a generic function to insert an item into the set database
func (db *setDB) getItem(bucket string, key interface{}) (item []byte, err error) {
	k := encoding.Marshal(key)
	err = db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		// Sanity check to make sure the bucket exists.
		if build.DEBUG {
			if b == nil {
				panic(errNilBucket)
			}
		}
		item = b.Get(k)
		// Sanity check to make sure the item requested exists
		if build.DEBUG {
			if item == nil {
				panic(errNilItem)
			}
		}
		return nil
	})
	return item, err
}

// rmItem removes an item from a bucket
func (db *setDB) rmItem(bucket string, key interface{}) error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	k := encoding.Marshal(key)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(bucket))
		if build.DEBUG {
			// Sanity check to make sure the bucket exists.
			if b == nil {
				panic(errNilBucket)
			}
			// Sanity check to make sure you are deleting an item that exists
			item := b.Get(k)
			if item == nil {
				panic(errNilItem)
			}
		}
		return b.Delete(k)
	})
}

// inBucket checks if an item with the given key is in the bucket
func (db *setDB) inBucket(bucket string, key interface{}) bool {
	exists, err := db.Exists(bucket, encoding.Marshal(key))
	if build.DEBUG && err != nil {
		panic(err)
	}
	return exists
}

// lenBucket is a simple wrapper for bucketSize that panics on error
func (db *setDB) lenBucket(bucket String) uint64 {
	s, err := db.bucketSize(bucket)
	if build.DEBUG && err != nil {
		panic(err)
	}
	return s
}

// pushPath inserts a block into the database at the "end" of the chain, i.e.
// the current height + 1.
func (db *setDB) pushPath(bid types.BlockID) error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	value := encoding.Marshal(bid)
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Path"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN))
		return b.Put(key, value)
	})
}

// popPath removes a block from the "end" of the chain, i.e. the block
// with the largest height.
func (db *setDB) popPath() error {
	if build.DEBUG && !db.checkConsistencyGuard() && build.Release != "testing" {
		panic(errNotGuarded)
	}
	return db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte("Path"))
		key := encoding.EncUint64(uint64(b.Stats().KeyN - 1))
		return b.Delete(key)
	})
}

// getPath retreives the block id of a block at a given hegiht from the path
func (db *setDB) getPath(h types.BlockHeight) (id types.BlockID) {
	idBytes, err := db.getItem("Path", h)
	if err != nil {
		panic(err)
	}
	err = encoding.Unmarshal(idBytes, &id)
	if err != nil {
		panic(err)
	}
	return
}

// pathHeight returns the size of the current path
func (db *setDB) pathHeight() types.BlockHeight {
	return db.lenBucket("Path")
}

// addBlockMap adds a processedBlock to the block map
// This will eventually take a processed block as an argument
func (db *setDB) addBlockMap(pb *processedBlock) error {
	return db.addItem("BlockMap", pb.Block.ID(), *pb)
}

// getBlockMap queries the set database to return a processedBlock
// with the given ID
func (db *setDB) getBlockMap(id types.BlockID) *processedBlock {
	bnBytes, err := db.getItem("BlockMap", id)
	if build.DEBUG && err != nil {
		panic(err)
	}
	var pb processedBlock
	err = encoding.Unmarshal(bnBytes, &pb)
	if build.DEBUG && err != nil {
		panic(err)
	}
	return &pb
}

// inBlockMap checks for the existance of a block with a given ID in
// the consensus set
func (db *setDB) inBlockMap(id types.BlockID) bool {
	return db.inBucket("BlockMap", id)
}

// rmBlockMap removes a processedBlock from the blockMap bucket
func (db *setDB) rmBlockMap(id types.BlockID) error {
	return db.rmItem("BlockMap", id)
}

// updateBlockMap is a wrapper function for modification of
func (db *setDB) updateBlockMap(pb *processedBlock) {
	// These errors will only be caused by an error by bolt
	// e.g. database being closed.
	err := db.rmBlockMap(pb.Block.ID())
	if build.DEBUG && err != nil {
		panic(err)
	}
	err = db.addBlockMap(pb)
	if build.DEBUG && err != nil {
		panic(err)
	}
}

// addSiafundOutputs is a wrapper around addItem for adding a siafundOutput.
func (db *setDB) addSiafundOutputs(id types.SiafundOutputID, output types.SiafundOutput) error {
	return db.addItem("SiafundOutputs", id, output)
}

// getSiafundOutputs is a wrapper around getItem which decodes the
// result into a siafundOutput
func (db *setDB) getSiafundOutputs(id types.SiafundOutputID) types.SiafundOutput {
	sfoBytes := db.getItem("SiafundOutputs", id)
	if build.DEBUG && err != nil {
		panic(err)
	}
	var sfo types.SiafundOutput
	err := encoding.Unmarshal(sfoBytes, &sfo)
	if build.DEBUG && err != nil {
		panic(err)
	}
	return sfo
}

// inSiafundOutputs is a wrapper around inBucket which returns a true
// if an output with the given id is in the database
func (db *setDB) inSiafundOutputs(id types.SiafundOutputID) bool {
	return db.inBucket("SiafundOutputs", id)
}

// nrmSiafundOutputs removes a siafund output from the database
func (db *setDB) rmSiafundOutputs(id types.SiafundOutputID) error {
	return db.rmItem("SiafundOutputs", id)
}

// lenSiafundOutputs returns the size of the siafundOutputs map
func (db *setDB) lenSiafundOutputs() uint64 {
	return lenBucket("SiafundOutputs")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"

	"github.com/julienschmidt/httprouter"
)

/* Read functions
 */
func ListView(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewReadHandler"].(somaViewReadHandler)
	handler.input <- somaViewRequest{
		action: "list",
		reply:  returnChannel,
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func ShowView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewReadHandler"].(somaViewReadHandler)
	handler.input <- somaViewRequest{
		action: "show",
		reply:  returnChannel,
		View: proto.View{
			Name: params.ByName("view"),
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

/* Write functions
 */
func AddView(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	cReq := proto.NewViewRequest()
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "add",
		reply:  returnChannel,
		View: proto.View{
			Name: cReq.View.Name,
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func DeleteView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "delete",
		reply:  returnChannel,
		View: proto.View{
			Name: params.ByName("view"),
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func RenameView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	cReq := proto.NewViewRequest()
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "rename",
		reply:  returnChannel,
		name:   params.ByName("view"),
		View: proto.View{
			Name: cReq.View.Name,
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

/* Utility
 */
func SendViewReply(w *http.ResponseWriter, r *somaResult) {
	result := proto.NewViewResult()
	if r.MarkErrors(&result) {
		goto dispatch
	}
	for _, i := range (*r).Views {
		*result.Views = append(*result.Views, i.View)
		if i.ResultError != nil {
			*result.Errors = append(*result.Errors, i.ResultError.Error())
		}
	}

dispatch:
	json, err := json.Marshal(result)
	if err != nil {
		DispatchInternalError(w, err)
		return
	}
	DispatchJsonReply(w, &json)
	return
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Enforce view name not containing . char<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strings"

	"github.com/julienschmidt/httprouter"
)

/* Read functions
 */
func ListView(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewReadHandler"].(somaViewReadHandler)
	handler.input <- somaViewRequest{
		action: "list",
		reply:  returnChannel,
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func ShowView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewReadHandler"].(somaViewReadHandler)
	handler.input <- somaViewRequest{
		action: "show",
		reply:  returnChannel,
		View: proto.View{
			Name: params.ByName("view"),
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

/* Write functions
 */
func AddView(w http.ResponseWriter, r *http.Request,
	_ httprouter.Params) {
	defer PanicCatcher(w)

	cReq := proto.NewViewRequest()
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}
	if strings.Contains(cReq.View.Name, `.`) {
		DispatchBadRequest(&w, fmt.Errorf(`Invalid view name containing . character`))
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "add",
		reply:  returnChannel,
		View: proto.View{
			Name: cReq.View.Name,
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func DeleteView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "delete",
		reply:  returnChannel,
		View: proto.View{
			Name: params.ByName("view"),
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

func RenameView(w http.ResponseWriter, r *http.Request,
	params httprouter.Params) {
	defer PanicCatcher(w)

	cReq := proto.NewViewRequest()
	err := DecodeJsonBody(r, &cReq)
	if err != nil {
		DispatchBadRequest(&w, err)
		return
	}

	returnChannel := make(chan somaResult)
	handler := handlerMap["viewWriteHandler"].(somaViewWriteHandler)
	handler.input <- somaViewRequest{
		action: "rename",
		reply:  returnChannel,
		name:   params.ByName("view"),
		View: proto.View{
			Name: cReq.View.Name,
		},
	}
	result := <-returnChannel
	SendViewReply(&w, &result)
}

/* Utility
 */
func SendViewReply(w *http.ResponseWriter, r *somaResult) {
	result := proto.NewViewResult()
	if r.MarkErrors(&result) {
		goto dispatch
	}
	for _, i := range (*r).Views {
		*result.Views = append(*result.Views, i.View)
		if i.ResultError != nil {
			*result.Errors = append(*result.Errors, i.ResultError.Error())
		}
	}

dispatch:
	json, err := json.Marshal(result)
	if err != nil {
		DispatchInternalError(w, err)
		return
	}
	DispatchJsonReply(w, &json)
	return
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>


<commit_before>package gitbackend

import (
	"bytes"
	"fmt"
	"github.com/libgit2/git2go"
	"io"
	"io/ioutil"
	"strings"
)

type FileStore struct {
	repo *git.Repository
}

func NewFileStore(path string, isBare bool) (fileStore FileStore, err error) {
	repo, err := git.InitRepository(path, isBare)
	if err != nil {
		return
	}
	fileStore.repo = repo
	return
}

func (this *FileStore) ReadDir(path string) (list []FileInfo, err error) {
	if strings.Trim(path, "/ ") == "" {
		return this.readRootDir()
	} else {
		return this.readSubDir(path)
	}
}

func (this *FileStore) readRootDir() (list []FileInfo, err error) {
	headCommitTree, err, noHead := this.headCommitTree()
	if err != nil {
		// return empty list for newly initialized repository without proper HEAD
		// usually the first commit sets a proper HEAD
		// this is only necessary for the root directory since there are no files after init
		if noHead {
			err = nil
		}
		return
	}
	list = this.listTree(headCommitTree)
	return
}

func (this *FileStore) readSubDir(path string) (list []FileInfo, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		return
	}

	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	tree, err := this.repo.LookupTree(entry.Id)
	if err != nil {
		return
	}

	list = this.listTree(tree)
	return
}

func (this *FileStore) listTree(tree *git.Tree) (list []FileInfo) {
	var i uint64
	for i = 0; i < tree.EntryCount(); i++ {
		entry := tree.EntryByIndex(i)
		isDir := entry.Type == git.ObjectTree
		list = append(list, FileInfo{entry.Name, isDir})
	}
	return
}

func (this *FileStore) Checksum(path string) (hexdigest string, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		fmt.Println(err)
		return
	}
	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	hexdigest = entry.Id.String()
	return
}

func (this *FileStore) ReadFile(path string) (reader io.Reader, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		fmt.Println(err)
		return
	}
	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	blob, err := this.repo.LookupBlob(entry.Id)
	if err != nil {
		return
	}
	reader = bytes.NewBuffer(blob.Contents())
	return
}

func (this *FileStore) CreateDir(path string, commitInfo *CommitInfo) (err error) {
	reader := strings.NewReader("")
	err = this.WriteFile(fmt.Sprintf("%s/.gitkeep", path), reader, commitInfo)
	return
}

func (this *FileStore) WriteFile(path string, reader io.Reader, commitInfo *CommitInfo) (err error) {
	blobOid, err := this.writeData(reader)

	if err != nil {
		fmt.Println(err)
		return
	}

	oldTree, _, _ := this.headCommitTree()
	newTreeId, err := this.updateTree(oldTree, path, blobOid)
	if err != nil {
		fmt.Println(err)
		return
	}

	tree, err := this.repo.LookupTree(newTreeId)
	if err != nil {
		fmt.Println(err)
		return
	}

	sig := &git.Signature{
		Name:  commitInfo.AuthorName(),
		Email: commitInfo.AuthorEmail(),
		When:  commitInfo.Time(),
	}

	commit, _, _ := this.headCommit()
	if commit == nil {
		_, err = this.repo.CreateCommit("HEAD", sig, sig, commitInfo.Message(), tree)

	} else {
		_, err = this.repo.CreateCommit("HEAD", sig, sig, commitInfo.Message(), tree, commit)

	}
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

func (this *FileStore) writeData(reader io.Reader) (blobOid *git.Oid, err error) {
	odb, err := this.repo.Odb()
	if err != nil {
		fmt.Println(err)
		return
	}

	data, err := ioutil.ReadAll(reader)
	if err != nil {
		fmt.Println(err)
		return
	}

	blobOid, err = odb.Write(data, git.ObjectBlob)
	return
}

func (this *FileStore) writeBlob(treebuilder *git.TreeBuilder, basename string, blobOid *git.Oid) (oid *git.Oid, err error) {
	err = treebuilder.Insert(basename, blobOid, int(git.FilemodeBlob))
	if err != nil {
		fmt.Println(err)
		return
	}
	return treebuilder.Write()
}

func (this *FileStore) writeTree(treebuilder *git.TreeBuilder, basename string, childsPath string, blobOid *git.Oid) (oid *git.Oid, err error) {
	newTreeOid, err := treebuilder.Write()
	if err != nil {
		fmt.Println(err)
		return
	}
	newTree, err := this.repo.LookupTree(newTreeOid)
	if err != nil {
		fmt.Println(err)
		return
	}

	var childTree *git.Tree

	oldChildTreeTreeEntry := newTree.EntryByName(basename)
	if oldChildTreeTreeEntry == nil {
		// no child tree entry found -> auto-create new sub tree
	} else {
		oldChildTree, err2 := this.repo.LookupTree(oldChildTreeTreeEntry.Id)
		if err2 != nil {
			fmt.Println(err2)
			err = err2
			return
		}
		childTree = oldChildTree
	}

	childTreeOid, err2 := this.updateTree(childTree, childsPath, blobOid)
	if err2 != nil {
		fmt.Println(err2)
		err = err2
		return
	}

	err = treebuilder.Insert(basename, childTreeOid, int(git.FilemodeTree))
	if err != nil {
		fmt.Println(err)
		return
	}

	oid, err = treebuilder.Write()
	return
}

func (this *FileStore) updateTree(oldParentTree *git.Tree, path string, blobOid *git.Oid) (oid *git.Oid, err error) {
	var treebuilder *git.TreeBuilder
	if oldParentTree == nil {
		treebuilder, err = this.repo.TreeBuilder()
	} else {
		treebuilder, err = this.repo.TreeBuilderFromTree(oldParentTree)
	}
	if err != nil {
		fmt.Println(err)
		return
	}
	parts := strings.SplitN(path, "/", 2)
	if len(parts) == 1 {
		return this.writeBlob(treebuilder, parts[0], blobOid)
	} else {
		return this.writeTree(treebuilder, parts[0], parts[1], blobOid)
	}
}

func (this *FileStore) headCommitTree() (tree *git.Tree, err error, noHead bool) {
	commit, err, noHead := this.headCommit()
	if err != nil {
		return
	}
	tree, err = commit.Tree()
	return
}

func (this *FileStore) headCommit() (commit *git.Commit, err error, noHead bool) {
	oid, err, noHead := this.headCommitId()
	if err != nil {
		return
	}
	commit, err = this.repo.LookupCommit(oid)
	return
}

func (this *FileStore) headCommitId() (oid *git.Oid, err error, noHead bool) {
	headRef, err := this.repo.LookupReference("HEAD")
	if err != nil {
		return
	}
	ref, err := headRef.Resolve()
	if err != nil {
		noHead = true
		return
	}
	oid = ref.Target()
	if oid == nil {
		err = fmt.Errorf("Could not get Target for HEAD(%s)\n", oid.String())
	}
	return
}
<commit_msg>Rename methods.<commit_after>package gitbackend

import (
	"bytes"
	"fmt"
	"github.com/libgit2/git2go"
	"io"
	"io/ioutil"
	"strings"
)

type FileStore struct {
	repo *git.Repository
}

func NewFileStore(path string, isBare bool) (fileStore FileStore, err error) {
	repo, err := git.InitRepository(path, isBare)
	if err != nil {
		return
	}
	fileStore.repo = repo
	return
}

func (this *FileStore) ReadDir(path string) (list []FileInfo, err error) {
	if strings.Trim(path, "/ ") == "" {
		return this.readRootDir()
	} else {
		return this.readSubDir(path)
	}
}

func (this *FileStore) readRootDir() (list []FileInfo, err error) {
	headCommitTree, err, noHead := this.headCommitTree()
	if err != nil {
		// return empty list for newly initialized repository without proper HEAD
		// usually the first commit sets a proper HEAD
		// this is only necessary for the root directory since there are no files after init
		if noHead {
			err = nil
		}
		return
	}
	list = this.listTree(headCommitTree)
	return
}

func (this *FileStore) readSubDir(path string) (list []FileInfo, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		return
	}

	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	tree, err := this.repo.LookupTree(entry.Id)
	if err != nil {
		return
	}

	list = this.listTree(tree)
	return
}

func (this *FileStore) listTree(tree *git.Tree) (list []FileInfo) {
	var i uint64
	for i = 0; i < tree.EntryCount(); i++ {
		entry := tree.EntryByIndex(i)
		isDir := entry.Type == git.ObjectTree
		list = append(list, FileInfo{entry.Name, isDir})
	}
	return
}

func (this *FileStore) Checksum(path string) (hexdigest string, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		fmt.Println(err)
		return
	}
	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	hexdigest = entry.Id.String()
	return
}

func (this *FileStore) ReadFile(path string) (reader io.Reader, err error) {
	headCommitTree, err, _ := this.headCommitTree()
	if err != nil {
		fmt.Println(err)
		return
	}
	entry, err := headCommitTree.EntryByPath(path)
	if err != nil {
		return
	}
	blob, err := this.repo.LookupBlob(entry.Id)
	if err != nil {
		return
	}
	reader = bytes.NewBuffer(blob.Contents())
	return
}

func (this *FileStore) CreateDir(path string, commitInfo *CommitInfo) (err error) {
	reader := strings.NewReader("")
	err = this.WriteFile(fmt.Sprintf("%s/.gitkeep", path), reader, commitInfo)
	return
}

func (this *FileStore) WriteFile(path string, reader io.Reader, commitInfo *CommitInfo) (err error) {
	blobOid, err := this.writeData(reader)

	if err != nil {
		fmt.Println(err)
		return
	}

	oldTree, _, _ := this.headCommitTree()
	newTreeId, err := this.updateTree(oldTree, path, blobOid)
	if err != nil {
		fmt.Println(err)
		return
	}

	tree, err := this.repo.LookupTree(newTreeId)
	if err != nil {
		fmt.Println(err)
		return
	}

	sig := &git.Signature{
		Name:  commitInfo.AuthorName(),
		Email: commitInfo.AuthorEmail(),
		When:  commitInfo.Time(),
	}

	commit, _, _ := this.headCommit()
	if commit == nil {
		_, err = this.repo.CreateCommit("HEAD", sig, sig, commitInfo.Message(), tree)

	} else {
		_, err = this.repo.CreateCommit("HEAD", sig, sig, commitInfo.Message(), tree, commit)

	}
	if err != nil {
		fmt.Println(err)
		return
	}
	return
}

func (this *FileStore) writeData(reader io.Reader) (blobOid *git.Oid, err error) {
	odb, err := this.repo.Odb()
	if err != nil {
		fmt.Println(err)
		return
	}

	data, err := ioutil.ReadAll(reader)
	if err != nil {
		fmt.Println(err)
		return
	}

	blobOid, err = odb.Write(data, git.ObjectBlob)
	return
}

func (this *FileStore) updateTreeBlob(treebuilder *git.TreeBuilder, basename string, blobOid *git.Oid) (oid *git.Oid, err error) {
	err = treebuilder.Insert(basename, blobOid, int(git.FilemodeBlob))
	if err != nil {
		fmt.Println(err)
		return
	}
	return treebuilder.Write()
}

func (this *FileStore) updateTreeTree(treebuilder *git.TreeBuilder, basename string, childsPath string, blobOid *git.Oid) (oid *git.Oid, err error) {
	newTreeOid, err := treebuilder.Write()
	if err != nil {
		fmt.Println(err)
		return
	}
	newTree, err := this.repo.LookupTree(newTreeOid)
	if err != nil {
		fmt.Println(err)
		return
	}

	var childTree *git.Tree

	oldChildTreeTreeEntry := newTree.EntryByName(basename)
	if oldChildTreeTreeEntry == nil {
		// no child tree entry found -> auto-create new sub tree
	} else {
		oldChildTree, err2 := this.repo.LookupTree(oldChildTreeTreeEntry.Id)
		if err2 != nil {
			fmt.Println(err2)
			err = err2
			return
		}
		childTree = oldChildTree
	}

	childTreeOid, err2 := this.updateTree(childTree, childsPath, blobOid)
	if err2 != nil {
		fmt.Println(err2)
		err = err2
		return
	}

	err = treebuilder.Insert(basename, childTreeOid, int(git.FilemodeTree))
	if err != nil {
		fmt.Println(err)
		return
	}

	oid, err = treebuilder.Write()
	return
}

func (this *FileStore) updateTree(oldParentTree *git.Tree, path string, blobOid *git.Oid) (oid *git.Oid, err error) {
	var treebuilder *git.TreeBuilder
	if oldParentTree == nil {
		treebuilder, err = this.repo.TreeBuilder()
	} else {
		treebuilder, err = this.repo.TreeBuilderFromTree(oldParentTree)
	}
	if err != nil {
		fmt.Println(err)
		return
	}
	parts := strings.SplitN(path, "/", 2)
	if len(parts) == 1 {
		return this.updateTreeBlob(treebuilder, parts[0], blobOid)
	} else {
		return this.updateTreeTree(treebuilder, parts[0], parts[1], blobOid)
	}
}

func (this *FileStore) headCommitTree() (tree *git.Tree, err error, noHead bool) {
	commit, err, noHead := this.headCommit()
	if err != nil {
		return
	}
	tree, err = commit.Tree()
	return
}

func (this *FileStore) headCommit() (commit *git.Commit, err error, noHead bool) {
	oid, err, noHead := this.headCommitId()
	if err != nil {
		return
	}
	commit, err = this.repo.LookupCommit(oid)
	return
}

func (this *FileStore) headCommitId() (oid *git.Oid, err error, noHead bool) {
	headRef, err := this.repo.LookupReference("HEAD")
	if err != nil {
		return
	}
	ref, err := headRef.Resolve()
	if err != nil {
		noHead = true
		return
	}
	oid = ref.Target()
	if oid == nil {
		err = fmt.Errorf("Could not get Target for HEAD(%s)\n", oid.String())
	}
	return
}
<|endoftext|>


<commit_before>// Name: copy.go
// Desc:
//   This portion of a package copies a file from one place to another.
package files

import (
	"errors"
	"os/exec"
)

// The copy function itself.
func Copy(src, dst string) error {
	cmd := exec.Command("cp", "-r", src, dst)

	err := cmd.Run()
	if err != nil {
		return errors.New("Could not copy " + src + " to " + dst)
	}

	return nil
}
<commit_msg>Fixed the recursive flag.<commit_after>// Name: copy.go
// Desc:
//   This portion of a package copies a file from one place to another.
package files

import (
	"errors"
	"os/exec"
)

// The copy function itself.
func Copy(src, dst string) error {
	cmd := exec.Command("cp", "-R", src, dst)

	err := cmd.Run()
	if err != nil {
		return errors.New("Could not copy " + src + " to " + dst)
	}

	return nil
}
<|endoftext|>


<commit_before>package util

import (
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"github.com/pkg/sftp"
	myssh "golang.org/x/crypto/ssh"
	"io/ioutil"
	"log"
	"regexp"
	"unsafe"
)

type SshClient interface {
	ExecCommand(username string, password string, ip string, command string) (string, error)
	UploadFile(username string, password string, ip string, srcFile string, destFile string) error
	DownloadFile(username string, password string, ip string, srcFile string, destFile string) error
}

type sshClientImpl struct{}

func IsIP(ip string) (b bool) {
	if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m {
		return false
	}
	return true
}

func IsDir(d string) (b bool) {
	if m, _ := regexp.MatchString("^/.*/$", d); !m {
		return false
	}
	return true
}

func (c *sshClientImpl) ExecCommand(username string, password string, ip string, command string) (string, error) {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	client, err := ssh.Dial("tcp", ip+":22", config)

	session, err := client.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()
	output, err := session.Output(command)

	return string(output), err
}

func (c *sshClientImpl) UploadFile(username string, password string, ip string, srcFile string, destFile string) error {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	if !IsIP(ip) {
		return errors.New("invalid IP address")
	}

	if IsDir(srcFile) || IsDir(destFile) {
		return errors.New("Is a directory")
	}
	client, err := ssh.Dial("tcp", ip+":22", config)
	if err != nil {
		log.Fatal(err)
		return err
	}

	conn := (*myssh.Client)(unsafe.Pointer(client))
	sftp, err := sftp.NewClient(conn)
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer sftp.Close()

	data, err := ioutil.ReadFile(srcFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	f, err := sftp.Create(destFile)
	if err != nil {
		log.Fatal(err)
		return err
	}
	if _, err := f.Write([]byte(data)); err != nil {
		log.Fatal(err)
		return err
	}

	if _, err = sftp.Lstat(destFile); err != nil {
		log.Fatal(err)
		return err
	}
	return nil
}

func (c *sshClientImpl) DownloadFile(username string, password string, ip string, srcFile string, destFile string) error {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	if !IsIP(ip) {
		return errors.New("invalid IP address")
	}

	if IsDir(srcFile) || IsDir(destFile) {
		return errors.New("Is a directory")
	}
	client, err := ssh.Dial("tcp", ip+":22", config)
	if err != nil {
		log.Fatal(err)
		return err
	}
	conn := (*myssh.Client)(unsafe.Pointer(client))
	sftp, err := sftp.NewClient(conn)
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer sftp.Close()

	pFile, err := sftp.Open(srcFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	data, err := ioutil.ReadAll(pFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	err = ioutil.WriteFile(destFile, data, 0755)
	if err != nil {
		log.Fatal(err)
		return err
	}

	return nil
}

func GetSshClient() SshClient {
	return &sshClientImpl{}
}
<commit_msg>Move f.Write Outside IF STATEMENTs<commit_after>package util

import (
	"code.google.com/p/go.crypto/ssh"
	"errors"
	"github.com/pkg/sftp"
	myssh "golang.org/x/crypto/ssh"
	"io/ioutil"
	"log"
	"regexp"
	"unsafe"
)

type SshClient interface {
	ExecCommand(username string, password string, ip string, command string) (string, error)
	UploadFile(username string, password string, ip string, srcFile string, destFile string) error
	DownloadFile(username string, password string, ip string, srcFile string, destFile string) error
}

type sshClientImpl struct{}

func IsIP(ip string) (b bool) {
	if m, _ := regexp.MatchString("^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}$", ip); !m {
		return false
	}
	return true
}

func IsDir(d string) (b bool) {
	if m, _ := regexp.MatchString("^/.*/$", d); !m {
		return false
	}
	return true
}

func (c *sshClientImpl) ExecCommand(username string, password string, ip string, command string) (string, error) {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	client, err := ssh.Dial("tcp", ip+":22", config)

	session, err := client.NewSession()
	if err != nil {
		return "", err
	}
	defer session.Close()
	output, err := session.Output(command)

	return string(output), err
}

func (c *sshClientImpl) UploadFile(username string, password string, ip string, srcFile string, destFile string) error {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	if !IsIP(ip) {
		return errors.New("invalid IP address")
	}

	if IsDir(srcFile) || IsDir(destFile) {
		return errors.New("Is a directory")
	}
	client, err := ssh.Dial("tcp", ip+":22", config)
	if err != nil {
		log.Fatal(err)
		return err
	}

	conn := (*myssh.Client)(unsafe.Pointer(client))
	sftp, err := sftp.NewClient(conn)
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer sftp.Close()

	data, err := ioutil.ReadFile(srcFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	f, err := sftp.Create(destFile)
	if err != nil {
		log.Fatal(err)
		return err
	}
	_, err = f.Write([]byte(data));
	if err != nil {
		log.Fatal(err)
		return err
	}

	_, err = sftp.Lstat(destFile);
	if err != nil {
		log.Fatal(err)
		return err
	}
	return nil
}

func (c *sshClientImpl) DownloadFile(username string, password string, ip string, srcFile string, destFile string) error {
	config := &ssh.ClientConfig{
		User: username,
		Auth: []ssh.AuthMethod{
			ssh.Password(password),
		},
	}
	if !IsIP(ip) {
		return errors.New("invalid IP address")
	}

	if IsDir(srcFile) || IsDir(destFile) {
		return errors.New("Is a directory")
	}
	client, err := ssh.Dial("tcp", ip+":22", config)
	if err != nil {
		log.Fatal(err)
		return err
	}
	conn := (*myssh.Client)(unsafe.Pointer(client))
	sftp, err := sftp.NewClient(conn)
	if err != nil {
		log.Fatal(err)
		return err
	}
	defer sftp.Close()

	pFile, err := sftp.Open(srcFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	data, err := ioutil.ReadAll(pFile)
	if err != nil {
		log.Fatal(err)
		return err
	}

	err = ioutil.WriteFile(destFile, data, 0755)
	if err != nil {
		log.Fatal(err)
		return err
	}

	return nil
}

func GetSshClient() SshClient {
	return &sshClientImpl{}
}
<|endoftext|>


<commit_before>package fleet

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	unitsPath = "/units"
)

type Unit struct {
	Name         string        `json:"name,omitempty"`
	Options      []*UnitOption `json:"options,omitempty"`
	DesiredState string        `json:"desiredState,omitempty"`
	CurrentState string        `json:"currentState,omitempty"`
	MachineID    string        `json:"machineID,omitempty"`
}

type UnitOption struct {
	Section string `json:"section,omitempty"`
	Name    string `json:"name,omitempty"`
	Value   string `json:"value,omitempty"`
}

type unitResponse struct {
	Units []Unit `json:"units,omitempty"`
}

func (c *Client) Units() ([]Unit, error) {
	var unitsRes unitResponse

	req, err := http.NewRequest("GET", c.URL+basePath+unitsPath, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	err = json.NewDecoder(res.Body).Decode(&unitsRes)
	if err != nil {
		return nil, err
	}

	return unitsRes.Units, nil
}

// createOrUpdateUnit creates or updates an unit
func (c *Client) createOrUpdateUnit(u Unit) error {
	switch u.DesiredState {
	case "inactive":
		break
	case "loaded":
		break
	case "launched":
		break
	default:
		return errors.New("Invalid desired state")
	}

	j, err := json.Marshal(u)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.URL+basePath+unitsPath+"/"+u.Name, bytes.NewReader(j))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 201:
		// Created successfully
		return nil
	case 204:
		// Modified successfully
		return nil
	case 400:
		// Attempting to create/modify an Unit with an invalid entity
		return errors.New("400 Bad Request")
	case 409:
		// Attempting to create an entity without options
		return errors.New("409 Conflict")
	default:
		message := fmt.Sprintf("%d Faild to create/update an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) deleteUnit(name string) error {
	req, err := http.NewRequest("DELETE", c.URL+basePath+unitsPath+"/"+name, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 204:
		// Deleted successfully
		return nil
	case 404:
		// The indicated Unit does not exist
		return errors.New("400 Bad Request")
	default:
		message := fmt.Sprintf("%d Faild to delete an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil

}

func (c *Client) Submit(name string, opts []*UnitOption, targetState string) error {
	unit := Unit{
		Name:         name,
		Options:      opts,
		DesiredState: targetState,
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Load(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Start(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "launched",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Stop(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Unload(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "inactive",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Destroy(name string) error {
	return c.deleteUnit(name)
}
<commit_msg>Remove blank line<commit_after>package fleet

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	unitsPath = "/units"
)

type Unit struct {
	Name         string        `json:"name,omitempty"`
	Options      []*UnitOption `json:"options,omitempty"`
	DesiredState string        `json:"desiredState,omitempty"`
	CurrentState string        `json:"currentState,omitempty"`
	MachineID    string        `json:"machineID,omitempty"`
}

type UnitOption struct {
	Section string `json:"section,omitempty"`
	Name    string `json:"name,omitempty"`
	Value   string `json:"value,omitempty"`
}

type unitResponse struct {
	Units []Unit `json:"units,omitempty"`
}

func (c *Client) Units() ([]Unit, error) {
	var unitsRes unitResponse

	req, err := http.NewRequest("GET", c.URL+basePath+unitsPath, nil)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	err = json.NewDecoder(res.Body).Decode(&unitsRes)
	if err != nil {
		return nil, err
	}

	return unitsRes.Units, nil
}

// createOrUpdateUnit creates or updates an unit
func (c *Client) createOrUpdateUnit(u Unit) error {
	switch u.DesiredState {
	case "inactive":
		break
	case "loaded":
		break
	case "launched":
		break
	default:
		return errors.New("Invalid desired state")
	}

	j, err := json.Marshal(u)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("PUT", c.URL+basePath+unitsPath+"/"+u.Name, bytes.NewReader(j))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 201:
		// Created successfully
		return nil
	case 204:
		// Modified successfully
		return nil
	case 400:
		// Attempting to create/modify an Unit with an invalid entity
		return errors.New("400 Bad Request")
	case 409:
		// Attempting to create an entity without options
		return errors.New("409 Conflict")
	default:
		message := fmt.Sprintf("%d Faild to create/update an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) deleteUnit(name string) error {
	req, err := http.NewRequest("DELETE", c.URL+basePath+unitsPath+"/"+name, nil)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	httpClient := http.Client{}
	res, err := httpClient.Do(req)
	if err != nil {
		return err
	}
	defer res.Body.Close()

	switch res.StatusCode {
	case 204:
		// Deleted successfully
		return nil
	case 404:
		// The indicated Unit does not exist
		return errors.New("400 Bad Request")
	default:
		message := fmt.Sprintf("%d Faild to delete an unit", res.StatusCode)
		return errors.New(message)
	}

	return nil
}

func (c *Client) Submit(name string, opts []*UnitOption, targetState string) error {
	unit := Unit{
		Name:         name,
		Options:      opts,
		DesiredState: targetState,
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Load(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Start(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "launched",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Stop(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "loaded",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Unload(name string) error {
	unit := Unit{
		Name:         name,
		DesiredState: "inactive",
	}

	return c.createOrUpdateUnit(unit)
}

func (c *Client) Destroy(name string) error {
	return c.deleteUnit(name)
}
<|endoftext|>


<commit_before>// Copyright 2014 Tamás Gulácsi. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.

package picago

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	neturl "net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	albumURL = "https://picasaweb.google.com/data/feed/api/user/{userID}?start-index={startIndex}"

	// imgmax=d is needed for original photo's download
	photoURL = "https://picasaweb.google.com/data/feed/api/user/{userID}/albumid/{albumID}?imgmax=d&start-index={startIndex}"
	userURL  = "https://picasaweb.google.com/data/feed/api/user/{userID}/contacts?kind=user"
)

var DebugDir = os.Getenv("PICAGO_DEBUG_DIR")

type User struct {
	ID, URI, Name, Thumbnail string
}

// An Album is a collection of Picasaweb or Google+ photos.
type Album struct {
	// ID is the stable identifier for an album.
	// e.g. "6041693388376552305"
	ID string

	// Name appears to be the Title, but with spaces removed. It
	// shows up in URLs but is not a stable
	// identifier. e.g. "BikingWithBlake"
	Name string

	// Title is the title of the album.
	// e.g. "Biking with Blake"
	Title string

	// Description is the Picasaweb "Description" field, and does
	// not appear available or shown in G+ Photos. It may be
	// contain newlines.
	Description string

	// Location is free-form location text. e.g. "San Bruno Mountain"
	Location string

	// URL is the main human-oriented (HTML) URL to the album.
	URL string

	// Published is the either the time the user actually created
	// and published the gallery or (in the case of Picasaweb at
	// least), the date that the user set on the gallery.  It will
	// be at day granularity, but the hour will be adjusted based
	// on whatever timezone the user is it. For instance, setting
	// July 21, 2014 while in California results in a time of
	// 2014-07-21T07:00:00.000Z since that was the UTC time at
	// which it became July 21st in US/Pacific on that day.
	Published time.Time

	// Updated is the server time any property of the gallery was
	// changed.  It appears to be at millisecond granularity.
	Updated time.Time

	AuthorName, AuthorURI string
}

// A Photo is a photo (or video) in a Picasaweb (or G+) gallery.
type Photo struct {
	// ID is the stable identifier for the photo.
	ID string

	// Filename is the image's filename from the Atom title field.
	Filename string

	// Description is the caption of the photo.
	Description string

	Keywords           []string
	Published, Updated time.Time

	// Latitude and Longitude optionally contain the GPS coordinates
	// of the photo.
	Latitude, Longitude float64

	// Location is free-form text describing the location of the
	// photo.
	Location string

	// URL is the URL of the photo or video.
	URL string

	// PageURL is the URL to the page showing just this image.
	PageURL string

	// Type is the Content-Type.
	Type string

	// Position is the 1-based position within a gallery.
	// It is zero if unknown.
	Position int

	Exif *Exif
}

// GetAlbums returns the list of albums of the given userID.
// If userID is empty, "default" is used.
func GetAlbums(client *http.Client, userID string) ([]Album, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(albumURL, "{userID}", userID, 1)

	var albums []Album
	var err error
	hasMore, startIndex := true, 1
	for hasMore {
		albums, hasMore, err = getAlbums(albums, client, url, startIndex)
		if !hasMore {
			break
		}
		startIndex = len(albums) + 1
	}
	return albums, err
}

func getAlbums(albums []Album, client *http.Client, url string, startIndex int) ([]Album, bool, error) {
	if startIndex <= 0 {
		startIndex = 1
	}
	feed, err := downloadAndParse(client,
		strings.Replace(url, "{startIndex}", strconv.Itoa(startIndex), 1))
	if err != nil {
		return nil, false, err
	}
	for _, entry := range feed.Entries {
		albums = append(albums, entry.album())
	}
	// since startIndex starts at 1, we need to compensate for this, just as we do for photos.
	return albums, startIndex+len(feed.Entries) <= feed.TotalResults, nil
}

func (e *Entry) album() Album {
	a := Album{
		ID:          e.ID,
		Name:        e.Name,
		Title:       e.Title,
		Location:    e.Location,
		AuthorName:  e.Author.Name,
		AuthorURI:   e.Author.URI,
		Published:   e.Published,
		Updated:     e.Updated,
		Description: e.Summary,
	}
	for _, link := range e.Links {
		if link.Rel == "alternate" && link.Type == "text/html" {
			a.URL = link.URL
			break
		}
	}
	if e.Media != nil {
		if a.Description == "" {
			a.Description = e.Media.Description
		}
	}
	return a
}

func GetPhotos(client *http.Client, userID, albumID string) ([]Photo, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(photoURL, "{userID}", userID, 1)
	url = strings.Replace(url, "{albumID}", albumID, 1)

	var photos []Photo
	var err error
	hasMore, startIndex := true, 1
	for hasMore {
		photos, hasMore, err = getPhotos(photos, client, url, startIndex)
		if !hasMore {
			break
		}
		startIndex = len(photos) + 1
	}
	return photos, err
}

func getPhotos(photos []Photo, client *http.Client, url string, startIndex int) ([]Photo, bool, error) {
	if startIndex <= 0 {
		startIndex = 1
	}
	feed, err := downloadAndParse(client,
		strings.Replace(url, "{startIndex}", strconv.Itoa(startIndex), 1))
	if err != nil {
		return nil, false, err
	}
	if len(feed.Entries) == 0 {
		return nil, false, nil
	}
	for i, entry := range feed.Entries {
		p, err := entry.photo()
		if err != nil {
			return nil, false, err
		}
		p.Position = startIndex + i
		photos = append(photos, p)
	}
	// startIndex starts with 1, we need to compensate for it.
	return photos, startIndex+len(feed.Entries) <= feed.NumPhotos, nil
}

func (e *Entry) photo() (p Photo, err error) {
	var lat, long float64
	i := strings.Index(e.Point, " ")
	if i >= 1 {
		lat, err = strconv.ParseFloat(e.Point[:i], 64)
		if err != nil {
			return p, fmt.Errorf("cannot parse %q as latitude: %v", e.Point[:i], err)
		}
		long, err = strconv.ParseFloat(e.Point[i+1:], 64)
		if err != nil {
			return p, fmt.Errorf("cannot parse %q as longitude: %v", e.Point[i+1:], err)
		}
	}
	if e.Point != "" && lat == 0 && long == 0 {
		return p, fmt.Errorf("point=%q but couldn't parse it as lat/long", e.Point)
	}
	p = Photo{
		ID:          e.ID,
		Exif:        e.Exif,
		Description: e.Summary,
		Filename:    e.Title,
		Location:    e.Location,
		Published:   e.Published,
		Updated:     e.Updated,
		Latitude:    lat,
		Longitude:   long,
	}
	for _, link := range e.Links {
		if link.Rel == "alternate" && link.Type == "text/html" {
			p.PageURL = link.URL
			break
		}
	}
	if e.Media != nil {
		for _, kw := range strings.Split(e.Media.Keywords, ",") {
			if kw := strings.TrimSpace(kw); kw != "" {
				p.Keywords = append(p.Keywords, kw)
			}
		}
		if p.Description == "" {
			p.Description = e.Media.Description
		}
		if mc, ok := e.Media.bestContent(); ok {
			p.URL, p.Type = mc.URL, mc.Type
		}
		if p.Filename == "" {
			p.Filename = e.Media.Title
		}
	}
	return p, nil
}

func (m *Media) bestContent() (ret MediaContent, ok bool) {
	// Find largest non-Flash video.
	var bestPixels int64
	for _, mc := range m.Content {
		thisPixels := int64(mc.Width) * int64(mc.Height)
		if mc.Medium == "video" && mc.Type != "application/x-shockwave-flash" && thisPixels > bestPixels {
			ret = mc
			ok = true
			bestPixels = thisPixels
		}
	}
	if ok {
		return
	}

	// Else, just find largest anything.
	bestPixels = 0
	for _, mc := range m.Content {
		thisPixels := int64(mc.Width) * int64(mc.Height)
		if thisPixels > bestPixels {
			ret = mc
			ok = true
			bestPixels = thisPixels
		}
	}
	return
}

func downloadAndParse(client *http.Client, url string) (*Atom, error) {
	resp, err := client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("downloadAndParse: get %q: %v", url, err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= http.StatusBadRequest {
		buf, _ := ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("downloadAndParse(%s) got %s (%s)", url, resp.Status, buf)
	}
	var r io.Reader = resp.Body
	if DebugDir != "" {
		fn := filepath.Join(DebugDir, neturl.QueryEscape(url)+".xml")
		xmlfh, err := os.Create(fn)
		if err != nil {
			return nil, fmt.Errorf("error creating debug filx %s: %v", fn, err)
		}
		defer xmlfh.Close()
		r = io.TeeReader(resp.Body, xmlfh)
	}
	return ParseAtom(r)
}

// DownloadPhoto returns an io.ReadCloser for reading the photo bytes
func DownloadPhoto(client *http.Client, url string) (io.ReadCloser, error) {
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode >= http.StatusBadRequest {
		buf, _ := ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("downloading %s: %s: %s", url, resp.Status, buf)
	}
	return resp.Body, nil
}

// GetUser returns the user's info
func GetUser(client *http.Client, userID string) (User, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(userURL, "{userID}", userID, 1)
	feed, err := downloadAndParse(client, url)
	if err != nil {
		return User{}, fmt.Errorf("GetUser: downloading %s: %v", url, err)
	}
	uri := feed.Author.URI
	id := uri
	i := strings.LastIndex(uri, "/")
	if i >= 0 {
		id = uri[i+1:]
	}
	return User{
		ID:        id,
		URI:       feed.Author.URI,
		Name:      feed.Author.Name,
		Thumbnail: feed.Thumbnail,
	}, nil
}
<commit_msg>My import of picasa was failing because the number of photos returned by the API was different (by one) than the number of photos claimed in the top-level NumPhotos attribute.<commit_after>// Copyright 2014 Tamás Gulácsi. All rights reserved.
// Use of this source code is governed by an Apache 2.0
// license that can be found in the LICENSE file.

package picago

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	neturl "net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"time"
)

const (
	albumURL = "https://picasaweb.google.com/data/feed/api/user/{userID}?start-index={startIndex}"

	// imgmax=d is needed for original photo's download
	photoURL = "https://picasaweb.google.com/data/feed/api/user/{userID}/albumid/{albumID}?imgmax=d&start-index={startIndex}"
	userURL  = "https://picasaweb.google.com/data/feed/api/user/{userID}/contacts?kind=user"
)

var DebugDir = os.Getenv("PICAGO_DEBUG_DIR")

type User struct {
	ID, URI, Name, Thumbnail string
}

// An Album is a collection of Picasaweb or Google+ photos.
type Album struct {
	// ID is the stable identifier for an album.
	// e.g. "6041693388376552305"
	ID string

	// Name appears to be the Title, but with spaces removed. It
	// shows up in URLs but is not a stable
	// identifier. e.g. "BikingWithBlake"
	Name string

	// Title is the title of the album.
	// e.g. "Biking with Blake"
	Title string

	// Description is the Picasaweb "Description" field, and does
	// not appear available or shown in G+ Photos. It may be
	// contain newlines.
	Description string

	// Location is free-form location text. e.g. "San Bruno Mountain"
	Location string

	// URL is the main human-oriented (HTML) URL to the album.
	URL string

	// Published is the either the time the user actually created
	// and published the gallery or (in the case of Picasaweb at
	// least), the date that the user set on the gallery.  It will
	// be at day granularity, but the hour will be adjusted based
	// on whatever timezone the user is it. For instance, setting
	// July 21, 2014 while in California results in a time of
	// 2014-07-21T07:00:00.000Z since that was the UTC time at
	// which it became July 21st in US/Pacific on that day.
	Published time.Time

	// Updated is the server time any property of the gallery was
	// changed.  It appears to be at millisecond granularity.
	Updated time.Time

	AuthorName, AuthorURI string
}

// A Photo is a photo (or video) in a Picasaweb (or G+) gallery.
type Photo struct {
	// ID is the stable identifier for the photo.
	ID string

	// Filename is the image's filename from the Atom title field.
	Filename string

	// Description is the caption of the photo.
	Description string

	Keywords           []string
	Published, Updated time.Time

	// Latitude and Longitude optionally contain the GPS coordinates
	// of the photo.
	Latitude, Longitude float64

	// Location is free-form text describing the location of the
	// photo.
	Location string

	// URL is the URL of the photo or video.
	URL string

	// PageURL is the URL to the page showing just this image.
	PageURL string

	// Type is the Content-Type.
	Type string

	// Position is the 1-based position within a gallery.
	// It is zero if unknown.
	Position int

	Exif *Exif
}

// GetAlbums returns the list of albums of the given userID.
// If userID is empty, "default" is used.
func GetAlbums(client *http.Client, userID string) ([]Album, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(albumURL, "{userID}", userID, 1)

	var albums []Album
	var err error
	hasMore, startIndex := true, 1
	for hasMore {
		albums, hasMore, err = getAlbums(albums, client, url, startIndex)
		if !hasMore {
			break
		}
		startIndex = len(albums) + 1
	}
	return albums, err
}

func getAlbums(albums []Album, client *http.Client, url string, startIndex int) ([]Album, bool, error) {
	if startIndex <= 0 {
		startIndex = 1
	}
	feed, err := downloadAndParse(client,
		strings.Replace(url, "{startIndex}", strconv.Itoa(startIndex), 1))
	if err != nil {
		return albums, false, err
	}
	if len(feed.Entries) == 0 {
		return albums, false, nil
	}
	for _, entry := range feed.Entries {
		albums = append(albums, entry.album())
	}
	return albums, true, nil
}

func (e *Entry) album() Album {
	a := Album{
		ID:          e.ID,
		Name:        e.Name,
		Title:       e.Title,
		Location:    e.Location,
		AuthorName:  e.Author.Name,
		AuthorURI:   e.Author.URI,
		Published:   e.Published,
		Updated:     e.Updated,
		Description: e.Summary,
	}
	for _, link := range e.Links {
		if link.Rel == "alternate" && link.Type == "text/html" {
			a.URL = link.URL
			break
		}
	}
	if e.Media != nil {
		if a.Description == "" {
			a.Description = e.Media.Description
		}
	}
	return a
}

func GetPhotos(client *http.Client, userID, albumID string) ([]Photo, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(photoURL, "{userID}", userID, 1)
	url = strings.Replace(url, "{albumID}", albumID, 1)

	var photos []Photo
	var err error
	hasMore, startIndex := true, 1
	for hasMore {
		photos, hasMore, err = getPhotos(photos, client, url, startIndex)
		if !hasMore {
			break
		}
		startIndex = len(photos) + 1
	}
	return photos, err
}

func getPhotos(photos []Photo, client *http.Client, url string, startIndex int) ([]Photo, bool, error) {
	if startIndex <= 0 {
		startIndex = 1
	}
	feed, err := downloadAndParse(client,
		strings.Replace(url, "{startIndex}", strconv.Itoa(startIndex), 1))
	if err != nil {
		return nil, false, err
	}
	if len(feed.Entries) == 0 {
		return photos, false, nil
	}
	for i, entry := range feed.Entries {
		p, err := entry.photo()
		if err != nil {
			return nil, false, err
		}
		p.Position = startIndex + i
		photos = append(photos, p)
	}

	// The number of photos can change while the import is happening. More
	// realistically, Aaron Boodman has observed feed.NumPhotos disagreeing with
	// len(feed.Entries). So to be on the safe side, just keep trying until we
	// get a response with zero entries.
	return photos, true, nil
}

func (e *Entry) photo() (p Photo, err error) {
	var lat, long float64
	i := strings.Index(e.Point, " ")
	if i >= 1 {
		lat, err = strconv.ParseFloat(e.Point[:i], 64)
		if err != nil {
			return p, fmt.Errorf("cannot parse %q as latitude: %v", e.Point[:i], err)
		}
		long, err = strconv.ParseFloat(e.Point[i+1:], 64)
		if err != nil {
			return p, fmt.Errorf("cannot parse %q as longitude: %v", e.Point[i+1:], err)
		}
	}
	if e.Point != "" && lat == 0 && long == 0 {
		return p, fmt.Errorf("point=%q but couldn't parse it as lat/long", e.Point)
	}
	p = Photo{
		ID:          e.ID,
		Exif:        e.Exif,
		Description: e.Summary,
		Filename:    e.Title,
		Location:    e.Location,
		Published:   e.Published,
		Updated:     e.Updated,
		Latitude:    lat,
		Longitude:   long,
	}
	for _, link := range e.Links {
		if link.Rel == "alternate" && link.Type == "text/html" {
			p.PageURL = link.URL
			break
		}
	}
	if e.Media != nil {
		for _, kw := range strings.Split(e.Media.Keywords, ",") {
			if kw := strings.TrimSpace(kw); kw != "" {
				p.Keywords = append(p.Keywords, kw)
			}
		}
		if p.Description == "" {
			p.Description = e.Media.Description
		}
		if mc, ok := e.Media.bestContent(); ok {
			p.URL, p.Type = mc.URL, mc.Type
		}
		if p.Filename == "" {
			p.Filename = e.Media.Title
		}
	}
	return p, nil
}

func (m *Media) bestContent() (ret MediaContent, ok bool) {
	// Find largest non-Flash video.
	var bestPixels int64
	for _, mc := range m.Content {
		thisPixels := int64(mc.Width) * int64(mc.Height)
		if mc.Medium == "video" && mc.Type != "application/x-shockwave-flash" && thisPixels > bestPixels {
			ret = mc
			ok = true
			bestPixels = thisPixels
		}
	}
	if ok {
		return
	}

	// Else, just find largest anything.
	bestPixels = 0
	for _, mc := range m.Content {
		thisPixels := int64(mc.Width) * int64(mc.Height)
		if thisPixels > bestPixels {
			ret = mc
			ok = true
			bestPixels = thisPixels
		}
	}
	return
}

func downloadAndParse(client *http.Client, url string) (*Atom, error) {
	resp, err := client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("downloadAndParse: get %q: %v", url, err)
	}
	defer resp.Body.Close()
	if resp.StatusCode >= http.StatusBadRequest {
		buf, _ := ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("downloadAndParse(%s) got %s (%s)", url, resp.Status, buf)
	}
	var r io.Reader = resp.Body
	if DebugDir != "" {
		fn := filepath.Join(DebugDir, neturl.QueryEscape(url)+".xml")
		xmlfh, err := os.Create(fn)
		if err != nil {
			return nil, fmt.Errorf("error creating debug filx %s: %v", fn, err)
		}
		defer xmlfh.Close()
		r = io.TeeReader(resp.Body, xmlfh)
	}
	return ParseAtom(r)
}

// DownloadPhoto returns an io.ReadCloser for reading the photo bytes
func DownloadPhoto(client *http.Client, url string) (io.ReadCloser, error) {
	resp, err := client.Get(url)
	if err != nil {
		return nil, err
	}
	if resp.StatusCode >= http.StatusBadRequest {
		buf, _ := ioutil.ReadAll(resp.Body)
		return nil, fmt.Errorf("downloading %s: %s: %s", url, resp.Status, buf)
	}
	return resp.Body, nil
}

// GetUser returns the user's info
func GetUser(client *http.Client, userID string) (User, error) {
	if userID == "" {
		userID = "default"
	}
	url := strings.Replace(userURL, "{userID}", userID, 1)
	feed, err := downloadAndParse(client, url)
	if err != nil {
		return User{}, fmt.Errorf("GetUser: downloading %s: %v", url, err)
	}
	uri := feed.Author.URI
	id := uri
	i := strings.LastIndex(uri, "/")
	if i >= 0 {
		id = uri[i+1:]
	}
	return User{
		ID:        id,
		URI:       feed.Author.URI,
		Name:      feed.Author.Name,
		Thumbnail: feed.Thumbnail,
	}, nil
}
<|endoftext|>


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

package templates

const documentTemplate = `

{{.Title}}

{{.Description}}

created {{if .Author.Name}}by {{ .Author.Name }}{{end}}{{ if .CreationDate }} on {{ .CreationDate }}{{ end }}

{{.Content}}

{{ if .Childs }}

{{end}}

{{ if .Locations }}

		Locations:

	{{range .Locations}}

		{{.Title}}
		{{ if .Description }}

{{.Description}}

		{{end}}

		{{ if .GeoLocation }}

		{{ if .GeoLocation.Address }}

{{ .GeoLocation.Address }}

		{{end}}

		{{ if .GeoLocation.Coordinates }}

			{{ .GeoLocation.Latitude }};
			{{ .GeoLocation.Longitude }}

		{{end}}

		{{ end }}

	{{end}}

{{end}}

{{ if .Tags }}

		Tags:

	{{range .Tags}}

		{{.Name}}

	{{end}}

{{end}}
`
<commit_msg>Default Theme Bug Fix: Hide the publisher information completely if neither Author nor CreationDate is set.<commit_after>// Copyright 2014 Andreas Koch. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package templates

const documentTemplate = `

{{.Title}}

{{.Description}}

{{if or .Author.Name .CreationDate }}
created {{if .Author.Name}}by {{ .Author.Name }}{{end}}{{ if .CreationDate }} on {{ .CreationDate }}{{ end }}
{{ end }}

{{.Content}}

{{ if .Childs }}

{{end}}

{{ if .Locations }}

		Locations:

	{{range .Locations}}

		{{.Title}}
		{{ if .Description }}

{{.Description}}

		{{end}}

		{{ if .GeoLocation }}

		{{ if .GeoLocation.Address }}

{{ .GeoLocation.Address }}

		{{end}}

		{{ if .GeoLocation.Coordinates }}

			{{ .GeoLocation.Latitude }};
			{{ .GeoLocation.Longitude }}

		{{end}}

		{{ end }}

	{{end}}

{{end}}

{{ if .Tags }}

		Tags:

	{{range .Tags}}

		{{.Name}}

	{{end}}

{{end}}
`
<|endoftext|>


<commit_before>package digraph

import (
	"container/list"
	"errors"
	"fmt"
)

var (
	// ErrCycle is returned when creating an edge between two vertices would result
	// in a cycle in the digraph
	ErrCycle = errors.New("digraph: cycle between edges")

	// ErrEdgeExists is returned when an edge between two vertices already exists
	ErrEdgeExists = errors.New("digraph: edge already exists")

	// ErrVertexExists is returned when a vertex with the same value already exists
	ErrVertexExists = errors.New("digraph: vertex already exists")

	// ErrVertexNotExists is returned when a vertex is used which does not exist
	ErrVertexNotExists = errors.New("digraph: vertex does not exist")
)

// Vertex represents a vertex or "node" in the digraph
type Vertex interface{}

// Digraph represents a "digraph", or directed graph data structure
type Digraph struct {
	adjList     map[Vertex]AdjacencyList
	edgeCount   int
	vertexCount int
}

// New creates a new acyclic Digraph, and initializes its adjacency list
func New() *Digraph {
	return &Digraph{
		adjList: map[Vertex]AdjacencyList{},
	}
}

// AddVertex tries to add a new vertex to the root of the adjacency list on the digraph
func (d *Digraph) AddVertex(vertex Vertex) error {
	// Check for a previous, identical vertex
	if _, found := d.adjList[vertex]; found {
		return ErrVertexExists
	}

	// Add the vertex to the adjacency list, initialize a new linked-list
	d.adjList[vertex] = AdjacencyList{list.New()}
	d.vertexCount++

	return nil
}

// AddEdge tries to add a new edge between two vertices on the adjacency list
func (d *Digraph) AddEdge(source Vertex, target Vertex) error {
	// Ensure vertices are not identical
	if source == target {
		return ErrCycle
	}

	// Add both vertices to the graph, ignoring if they already exist
	d.AddVertex(source)
	d.AddVertex(target)

	// Check if this digraph already has this edge
	if d.HasEdge(source, target) {
		// Return false, edge already exists
		return ErrEdgeExists
	}

	// Do a depth-first search from the target to the source to determine if a cycle will
	// result if this edge is created
	if d.DepthFirstSearch(target, source) {
		// Return false, a cycle will be created
		return ErrCycle
	}

	// Retrieve adjacency list
	adjList := d.adjList[source]

	// Target was not found, so add an edge between source and target
	adjList.list.PushBack(target)
	d.edgeCount++

	// Store adjacency list
	d.adjList[source] = adjList

	return nil
}

// discovered maps out which vertices have been discovered using Depth-First Search
var discovered map[Vertex]bool

// DepthFirstSearch searches the digraph for the target vertex, using the Depth-First
// Search algorithm, and returning true if a path to the target is found
func (d *Digraph) DepthFirstSearch(source Vertex, target Vertex) bool {
	// Clear discovery map
	discovered = map[Vertex]bool{}

	// Begin recursive Depth-First Search, looking for all vertices reachable from source
	d.dfs(source)

	// Check if target was discovered during Depth-First Search
	result := discovered[target]

	// Clear discovery map, return result
	discovered = map[Vertex]bool{}
	return result
}

// dfs implements a recursive Depth-First Search algorithm
func (d *Digraph) dfs(target Vertex) {
	// Get the adjacency list for this vertex
	adjList := d.adjList[target]

	// Check all adjacent vertices
	for _, v := range adjList.Adjacent() {
		// Check if vertex has not been discovered
		if !discovered[v] {
			// Mark it as discovered, recursively continue traversal
			discovered[v] = true
			d.dfs(v)
		}
	}
}

// EdgeCount returns the number of edges in the digraph
func (d *Digraph) EdgeCount() int {
	return d.edgeCount
}

// HasEdge determines if the digraph has an existing edge between source and target,
// returning true if it does, or false if it does not
func (d *Digraph) HasEdge(source Vertex, target Vertex) bool {
	// Check if the source vertex exists
	if _, found := d.adjList[source]; !found {
		return false
	}

	// Retrieve adjacency list for this source
	adjList := d.adjList[source]

	// Search for target vertex
	if v := adjList.Search(target); v != nil {
		// Vertex is adjacent, edge exists
		return true
	}

	// No result, edge does not exist
	return false
}

// Print displays a printed "tree" of the digraph to the console
func (d *Digraph) Print(root Vertex) (string, error) {
	// Check if the vertex actually exists
	if _, ok := d.adjList[root]; !ok {
		return "", ErrVertexNotExists
	}

	// Clear discovery map
	discovered = map[Vertex]bool{}

	// Begin recursive printing at the specified root vertex
	tree := d.printRecursive(root, "")

	// Clear discovery map
	discovered = map[Vertex]bool{}

	return tree, nil

}

// printRecursive handles the printing of each vertex in "tree" form
func (d *Digraph) printRecursive(vertex Vertex, prefix string) string {
	// Print the current vertex
	str := fmt.Sprintf("%s - %v\n", prefix, vertex)

	// Get the current adjacency list, get adjacent vertices
	adjList := d.adjList[vertex]
	adjacent := adjList.Adjacent()

	// Iterate all adjacent vertices
	for i, v := range adjacent {
		// Skip vertices which have already been discovered
		if discovered[v] {
			continue
		}

		// Mark new ones as discovered
		discovered[v] = true

		// If last iteration, don't add a pipe character
		if i == len(adjacent)-1 {
			str = str + d.printRecursive(v, prefix+"    ")
		} else {
			// Add pipe character to show multiple items belong to same parent
			str = str + d.printRecursive(v, prefix+"   |")
		}
	}

	return str
}

// VertexCount returns the number of vertices in the digraph
func (d *Digraph) VertexCount() int {
	return d.vertexCount
}
<commit_msg>Use a new 'printed' map for Print<commit_after>package digraph

import (
	"container/list"
	"errors"
	"fmt"
)

var (
	// ErrCycle is returned when creating an edge between two vertices would result
	// in a cycle in the digraph
	ErrCycle = errors.New("digraph: cycle between edges")

	// ErrEdgeExists is returned when an edge between two vertices already exists
	ErrEdgeExists = errors.New("digraph: edge already exists")

	// ErrVertexExists is returned when a vertex with the same value already exists
	ErrVertexExists = errors.New("digraph: vertex already exists")

	// ErrVertexNotExists is returned when a vertex is used which does not exist
	ErrVertexNotExists = errors.New("digraph: vertex does not exist")
)

// Vertex represents a vertex or "node" in the digraph
type Vertex interface{}

// Digraph represents a "digraph", or directed graph data structure
type Digraph struct {
	adjList     map[Vertex]AdjacencyList
	edgeCount   int
	vertexCount int
}

// New creates a new acyclic Digraph, and initializes its adjacency list
func New() *Digraph {
	return &Digraph{
		adjList: map[Vertex]AdjacencyList{},
	}
}

// AddVertex tries to add a new vertex to the root of the adjacency list on the digraph
func (d *Digraph) AddVertex(vertex Vertex) error {
	// Check for a previous, identical vertex
	if _, found := d.adjList[vertex]; found {
		return ErrVertexExists
	}

	// Add the vertex to the adjacency list, initialize a new linked-list
	d.adjList[vertex] = AdjacencyList{list.New()}
	d.vertexCount++

	return nil
}

// AddEdge tries to add a new edge between two vertices on the adjacency list
func (d *Digraph) AddEdge(source Vertex, target Vertex) error {
	// Ensure vertices are not identical
	if source == target {
		return ErrCycle
	}

	// Add both vertices to the graph, ignoring if they already exist
	d.AddVertex(source)
	d.AddVertex(target)

	// Check if this digraph already has this edge
	if d.HasEdge(source, target) {
		// Return false, edge already exists
		return ErrEdgeExists
	}

	// Do a depth-first search from the target to the source to determine if a cycle will
	// result if this edge is created
	if d.DepthFirstSearch(target, source) {
		// Return false, a cycle will be created
		return ErrCycle
	}

	// Retrieve adjacency list
	adjList := d.adjList[source]

	// Target was not found, so add an edge between source and target
	adjList.list.PushBack(target)
	d.edgeCount++

	// Store adjacency list
	d.adjList[source] = adjList

	return nil
}

// discovered maps out which vertices have been discovered using Depth-First Search
var discovered map[Vertex]bool

// DepthFirstSearch searches the digraph for the target vertex, using the Depth-First
// Search algorithm, and returning true if a path to the target is found
func (d *Digraph) DepthFirstSearch(source Vertex, target Vertex) bool {
	// Clear discovery map
	discovered = map[Vertex]bool{}

	// Begin recursive Depth-First Search, looking for all vertices reachable from source
	d.dfs(source)

	// Check if target was discovered during Depth-First Search
	result := discovered[target]

	// Clear discovery map, return result
	discovered = map[Vertex]bool{}
	return result
}

// dfs implements a recursive Depth-First Search algorithm
func (d *Digraph) dfs(target Vertex) {
	// Get the adjacency list for this vertex
	adjList := d.adjList[target]

	// Check all adjacent vertices
	for _, v := range adjList.Adjacent() {
		// Check if vertex has not been discovered
		if !discovered[v] {
			// Mark it as discovered, recursively continue traversal
			discovered[v] = true
			d.dfs(v)
		}
	}
}

// EdgeCount returns the number of edges in the digraph
func (d *Digraph) EdgeCount() int {
	return d.edgeCount
}

// HasEdge determines if the digraph has an existing edge between source and target,
// returning true if it does, or false if it does not
func (d *Digraph) HasEdge(source Vertex, target Vertex) bool {
	// Check if the source vertex exists
	if _, found := d.adjList[source]; !found {
		return false
	}

	// Retrieve adjacency list for this source
	adjList := d.adjList[source]

	// Search for target vertex
	if v := adjList.Search(target); v != nil {
		// Vertex is adjacent, edge exists
		return true
	}

	// No result, edge does not exist
	return false
}

// printed maps out which vertices have been printed already
var printed map[Vertex]bool

// Print displays a printed "tree" of the digraph to the console
func (d *Digraph) Print(root Vertex) (string, error) {
	// Check if the vertex actually exists
	if _, ok := d.adjList[root]; !ok {
		return "", ErrVertexNotExists
	}

	// Clear printed map
	printed = map[Vertex]bool{}

	// Begin recursive printing at the specified root vertex
	tree := d.printRecursive(root, "")

	// Clear printed map
	printed = map[Vertex]bool{}

	return tree, nil

}

// printRecursive handles the printing of each vertex in "tree" form
func (d *Digraph) printRecursive(vertex Vertex, prefix string) string {
	// Print the current vertex
	str := fmt.Sprintf("%s - %v\n", prefix, vertex)

	// Get the current adjacency list, get adjacent vertices
	adjList := d.adjList[vertex]
	adjacent := adjList.Adjacent()

	// Iterate all adjacent vertices
	for i, v := range adjacent {
		// Skip vertices which have already been printed
		if printed[v] {
			continue
		}

		// Mark new ones as printed
		printed[v] = true

		// If last iteration, don't add a pipe character
		if i == len(adjacent)-1 {
			str = str + d.printRecursive(v, prefix+"    ")
		} else {
			// Add pipe character to show multiple items belong to same parent
			str = str + d.printRecursive(v, prefix+"   |")
		}
	}

	return str
}

// VertexCount returns the number of vertices in the digraph
func (d *Digraph) VertexCount() int {
	return d.vertexCount
}
<|endoftext|>