<commit_before>package gqt_test

import (
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Properties", func() {
	var (
		client    *runner.RunningGarden
		container garden.Container
		props     garden.Properties
	)

	BeforeEach(func() {
		var err error
		client = startGarden()
		props = garden.Properties{"somename": "somevalue"}
		container, err = client.Create(garden.ContainerSpec{
			Properties: props,
		})
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	It("can get properties", func() {
		properties, err := container.Properties()
		Expect(err).NotTo(HaveOccurred())
		Expect(properties).To(HaveKeyWithValue("somename", "somevalue"))
	})

	It("can set a single property", func() {
		err := container.SetProperty("someothername", "someothervalue")
		Expect(err).NotTo(HaveOccurred())

		properties, err := container.Properties()
		Expect(err).NotTo(HaveOccurred())
		Expect(properties).To(HaveKeyWithValue("somename", "somevalue"))
		Expect(properties).To(HaveKeyWithValue("someothername", "someothervalue"))
	})

	It("can get a single property", func() {
		err := container.SetProperty("bing", "bong")
		Expect(err).NotTo(HaveOccurred())

		value, err := container.Property("bing")
		Expect(err).NotTo(HaveOccurred())
		Expect(value).To(Equal("bong"))
	})

	It("can remove a single property", func() {
		err := container.SetProperty("bing", "bong")
		Expect(err).NotTo(HaveOccurred())

		err = container.RemoveProperty("bing")
		Expect(err).NotTo(HaveOccurred())

		_, err = container.Property("bing")
		Expect(err).To(HaveOccurred())
	})

	It("can filter containers based on their properties", func() {
		_, err := client.Create(garden.ContainerSpec{
			Properties: garden.Properties{
				"somename": "wrongvalue",
			},
		})
		Expect(err).NotTo(HaveOccurred())

		containers, err := client.Containers(props)
		Expect(err).NotTo(HaveOccurred())
		Expect(containers).To(HaveLen(1))
		Expect(containers).To(ConsistOf(container))
	})
})
<commit_msg>Add regression for default container properties<commit_after>package gqt_test

import (
	"github.com/cloudfoundry-incubator/garden"
	"github.com/cloudfoundry-incubator/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("Properties", func() {
	var (
		client    *runner.RunningGarden
		container garden.Container
		props     garden.Properties
	)

	BeforeEach(func() {
		var err error
		client = startGarden()
		props = garden.Properties{"somename": "somevalue"}
		container, err = client.Create(garden.ContainerSpec{
			Properties: props,
		})
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	It("can get properties", func() {
		properties, err := container.Properties()
		Expect(err).NotTo(HaveOccurred())
		Expect(properties).To(HaveKeyWithValue("somename", "somevalue"))
	})

	It("can set a single property", func() {
		err := container.SetProperty("someothername", "someothervalue")
		Expect(err).NotTo(HaveOccurred())

		properties, err := container.Properties()
		Expect(err).NotTo(HaveOccurred())
		Expect(properties).To(HaveKeyWithValue("somename", "somevalue"))
		Expect(properties).To(HaveKeyWithValue("someothername", "someothervalue"))
	})

	It("can get a single property", func() {
		err := container.SetProperty("bing", "bong")
		Expect(err).NotTo(HaveOccurred())

		value, err := container.Property("bing")
		Expect(err).NotTo(HaveOccurred())
		Expect(value).To(Equal("bong"))
	})

	It("can remove a single property", func() {
		err := container.SetProperty("bing", "bong")
		Expect(err).NotTo(HaveOccurred())

		err = container.RemoveProperty("bing")
		Expect(err).NotTo(HaveOccurred())

		_, err = container.Property("bing")
		Expect(err).To(HaveOccurred())
	})

	It("can filter containers based on their properties", func() {
		_, err := client.Create(garden.ContainerSpec{
			Properties: garden.Properties{
				"somename": "wrongvalue",
			},
		})
		Expect(err).NotTo(HaveOccurred())

		containers, err := client.Containers(props)
		Expect(err).NotTo(HaveOccurred())
		Expect(containers).To(HaveLen(1))
		Expect(containers).To(ConsistOf(container))
	})

	It("can get the default properties", func() {
		container, err := client.Create(garden.ContainerSpec{})
		Expect(err).ToNot(HaveOccurred())

		props, err := container.Properties()
		Expect(err).ToNot(HaveOccurred())

		Expect(props).To(HaveLen(9))
		Expect(props).To(HaveKey("kawasaki.bridge-interface"))
		Expect(props).To(HaveKey("kawasaki.bridge-ip"))
		Expect(props).To(HaveKey("kawasaki.container-ip"))
		Expect(props).To(HaveKey("kawasaki.host-interface"))
		Expect(props).To(HaveKey("kawasaki.iptable-chain"))
		Expect(props).To(HaveKey("kawasaki.subnet-ip"))
		Expect(props).To(HaveKey("kawasaki.container-interface"))
		Expect(props).To(HaveKey("kawasaki.external-ip"))
		Expect(props).To(HaveKey("kawasaki.mtu"))
	})
})
<|endoftext|>


<commit_before>package core

import (
	"database/sql"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/mdlayher/wavepipe/data"

	"github.com/wtolson/go-taglib"
)

// fsFileSource represents a file source which indexes files in the local filesystem
type fsFileSource struct{}

// MediaScan scans for media files in the local filesystem
func (fsFileSource) MediaScan(mediaFolder string, verbose bool, walkCancelChan chan struct{}) error {
	// Halt walk if needed
	var mutex sync.RWMutex
	haltWalk := false
	go func() {
		// Wait for signal
		<-walkCancelChan

		// Halt!
		mutex.Lock()
		haltWalk = true
		mutex.Unlock()
	}()

	// Track metrics about the walk
	artistCount := 0
	albumCount := 0
	songCount := 0
	songUpdateCount := 0
	folderCount := 0
	startTime := time.Now()

	if verbose {
		log.Println("fs: beginning media scan:", mediaFolder)
	} else {
		log.Println("fs: scanning:", mediaFolder)
	}

	// Invoke a recursive file walk on the given media folder, passing closure variables into
	// walkFunc to enable additional functionality
	err := filepath.Walk(mediaFolder, func(currPath string, info os.FileInfo, err error) error {
		// Stop walking immediately if needed
		mutex.RLock()
		if haltWalk {
			return errors.New("media scan: halted by channel")
		}
		mutex.RUnlock()

		// Make sure path is actually valid
		if info == nil {
			return errors.New("media scan: invalid path: " + currPath)
		}

		// Check for an existing folder for this item
		folder := new(data.Folder)
		if info.IsDir() {
			// If directory, use this path
			folder.Path = currPath
		} else {
			// If file, use the directory path
			folder.Path = path.Dir(currPath)
		}

		// Attempt to load folder
		if err := folder.Load(); err != nil && err == sql.ErrNoRows {
			// Make sure items actually exist at this path
			files, err := ioutil.ReadDir(folder.Path)
			if err != nil {
				log.Println(err)
				return err
			}

			// No items, skip it
			if len(files) == 0 {
				return nil
			}

			// Set short title
			folder.Title = path.Base(folder.Path)

			// Check for a parent folder
			pFolder := new(data.Folder)

			// If scan is triggered by a file, we have to check the dir twice to get parent
			if info.IsDir() {
				pFolder.Path = path.Dir(currPath)
			} else {
				pFolder.Path = path.Dir(path.Dir(currPath))
			}

			// Load parent
			if err := pFolder.Load(); err != nil && err != sql.ErrNoRows {
				log.Println(err)
				return err
			}

			// Copy parent folder's ID
			folder.ParentID = pFolder.ID

			// Save new folder
			if err := folder.Save(); err != nil {
				log.Println(err)
				return err
			}

			// Continue traversal
			folderCount++
		}

		// Check for a valid media extension
		if !validSet.Has(path.Ext(currPath)) {
			return nil
		}

		// Attempt to scan media file with taglib
		file, err := taglib.Read(currPath)
		if err != nil {
			log.Println(err)
			return fmt.Errorf("%s: %s", currPath, err.Error())
		}
		defer file.Close()

		// Generate a song model from the TagLib file
		song, err := data.SongFromFile(file)
		if err != nil {
			log.Println(err)
			return err
		}

		// Populate filesystem-related struct fields using OS info
		song.FileName = currPath
		song.FileSize = info.Size()

		// Use this folder's ID
		song.FolderID = folder.ID

		// Extract type from the extension, capitalize it, drop the dot
		song.FileType = strings.ToUpper(path.Ext(info.Name()))[1:]
		song.LastModified = info.ModTime().Unix()

		// Generate an artist model from this song's metadata
		artist := data.ArtistFromSong(song)

		// Check for existing artist
		// Note: if the artist exists, this operation also loads necessary scanning information
		// such as their artist ID, for use in album and song generation
		if err := artist.Load(); err == sql.ErrNoRows {
			// Save new artist
			if err := artist.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				log.Printf("Artist: [#%05d] %s", artist.ID, artist.Title)
				artistCount++
			}
		}

		// Generate the album model from this song's metadata
		album := data.AlbumFromSong(song)
		album.ArtistID = artist.ID

		// Check for existing album
		// Note: if the album exists, this operation also loads necessary scanning information
		// such as the album ID, for use in song generation
		if err := album.Load(); err == sql.ErrNoRows {
			// Save album
			if err := album.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				log.Printf("  - Album: [#%05d] %s - %d - %s", album.ID, album.Artist, album.Year, album.Title)
				albumCount++
			}
		}

		// Add ID fields to song
		song.ArtistID = artist.ID
		song.AlbumID = album.ID

		// Make a duplicate song to check if song has been modified since last scan
		song2 := new(data.Song)
		song2.FileName = song.FileName

		// Check for existing song
		if err := song2.Load(); err == sql.ErrNoRows {
			// Save song (don't log these because they really slow things down)
			if err := song.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				songCount++
			}
		} else {
			// Song already existed.  Check if it's been updated
			if song.LastModified > song2.LastModified {
				// Update existing song
				song.ID = song2.ID
				if err := song.Update(); err != nil {
					log.Println(err)
				}

				songUpdateCount++
			}
		}

		// Successful media scan
		return nil
	})

	// Check for filesystem walk errors
	if err != nil {
		return err
	}

	// Print metrics
	if verbose {
		log.Printf("fs: media scan complete [time: %s]", time.Since(startTime).String())
		log.Printf("fs: added: [artists: %d] [albums: %d] [songs: %d] [folders: %d]", artistCount, albumCount, songCount, folderCount)
		log.Printf("fs: updated: [songs: %d]", songUpdateCount)
	}

	// No errors
	return nil
}

// OrphanScan scans for missing "orphaned" media files in the local filesystem
func (fsFileSource) OrphanScan(baseFolder string, subFolder string, verbose bool, orphanCancelChan chan struct{}) error {
	// Halt scan if needed
	var mutex sync.RWMutex
	haltOrphanScan := false
	go func() {
		// Wait for signal
		<-orphanCancelChan

		// Halt!
		mutex.Lock()
		haltOrphanScan = true
		mutex.Unlock()
	}()

	// Track metrics about the scan
	folderCount := 0
	songCount := 0
	startTime := time.Now()

	// Check if a baseFolder is set, meaning remove ANYTHING not under this base
	if baseFolder != "" {
		if verbose {
			log.Println("fs: orphan scanning base folder:", baseFolder)
		}

		// Scan for all songs NOT under the base folder
		songs, err := data.DB.SongsNotInPath(baseFolder)
		if err != nil {
			log.Println(err)
			return err
		}

		// Remove all songs which are not in this path
		for _, s := range songs {
			// Remove song from database
			if err := s.Delete(); err != nil {
				log.Println(err)
				return err
			}

			songCount++
		}

		// Scan for all folders NOT under the base folder
		folders, err := data.DB.FoldersNotInPath(baseFolder)
		if err != nil {
			log.Println(err)
			return err
		}

		// Remove all folders which are not in this path
		for _, f := range folders {
			// Remove folder from database
			if err := f.Delete(); err != nil {
				log.Println(err)
				return err
			}

			folderCount++
		}
	}

	// If no subfolder set, use the base folder to check file existence
	if subFolder == "" {
		subFolder = baseFolder
	}

	if verbose {
		log.Println("fs: orphan scanning subfolder:", subFolder)
	} else {
		log.Println("fs: removing:", subFolder)
	}

	// Scan for all songs in subfolder
	songs, err := data.DB.SongsInPath(subFolder)
	if err != nil {
		log.Println(err)
		return err
	}

	// Iterate all songs in this path
	for _, s := range songs {
		// Check that the song still exists in this place
		if _, err := os.Stat(s.FileName); os.IsNotExist(err) {
			// Remove song from database
			if err := s.Delete(); err != nil {
				log.Println(err)
				return err
			}

			songCount++
		}
	}

	// Scan for all folders in subfolder
	folders, err := data.DB.FoldersInPath(subFolder)
	if err != nil {
		return err
	}

	// Iterate all folders in this path
	for _, f := range folders {
		// Check that the folder still has items within it
		files, err := ioutil.ReadDir(f.Path)
		if err != nil && !os.IsNotExist(err) {
			log.Println(err)
			return err
		}

		// Delete any folders with 0 items
		if len(files) == 0 {
			if err := f.Delete(); err != nil {
				log.Println(err)
				return err
			}

			folderCount++
		}
	}

	// Now that songs have been purged, check for albums
	albumCount, err := data.DB.PurgeOrphanAlbums()
	if err != nil {
		log.Println(err)
		return err
	}

	// Check for artists
	artistCount, err := data.DB.PurgeOrphanArtists()
	if err != nil {
		log.Println(err)
		return err
	}

	// Print metrics
	if verbose {
		log.Printf("fs: orphan scan complete [time: %s]", time.Since(startTime).String())
		log.Printf("fs: removed: [artists: %d] [albums: %d] [songs: %d] [folders: %d]", artistCount, albumCount, songCount, folderCount)
	}
	return nil
}
<commit_msg>Cache folders, artists, albums on media scan<commit_after>package core

import (
	"database/sql"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mdlayher/wavepipe/data"

	"github.com/wtolson/go-taglib"
)

// fsFileSource represents a file source which indexes files in the local filesystem
type fsFileSource struct{}

// MediaScan scans for media files in the local filesystem
func (fsFileSource) MediaScan(mediaFolder string, verbose bool, walkCancelChan chan struct{}) error {
	// Halt walk if needed
	var mutex sync.RWMutex
	haltWalk := false
	go func() {
		// Wait for signal
		<-walkCancelChan

		// Halt!
		mutex.Lock()
		haltWalk = true
		mutex.Unlock()
	}()

	// Track metrics about the walk
	artistCount := 0
	albumCount := 0
	songCount := 0
	songUpdateCount := 0
	folderCount := 0
	startTime := time.Now()

	// Cache entries which have been seen previously, to reduce database load
	folderCache := map[string]*data.Folder{}
	artistCache := map[string]*data.Artist{}
	albumCache := map[string]*data.Album{}

	if verbose {
		log.Println("fs: beginning media scan:", mediaFolder)
	} else {
		log.Println("fs: scanning:", mediaFolder)
	}

	// Invoke a recursive file walk on the given media folder, passing closure variables into
	// walkFunc to enable additional functionality
	err := filepath.Walk(mediaFolder, func(currPath string, info os.FileInfo, err error) error {
		// Stop walking immediately if needed
		mutex.RLock()
		if haltWalk {
			return errors.New("media scan: halted by channel")
		}
		mutex.RUnlock()

		// Make sure path is actually valid
		if info == nil {
			return errors.New("media scan: invalid path: " + currPath)
		}

		// Check for an existing folder for this item
		folder := new(data.Folder)
		if info.IsDir() {
			// If directory, use this path
			folder.Path = currPath
		} else {
			// If file, use the directory path
			folder.Path = path.Dir(currPath)
		}

		// Check for a cached folder, or attempt to load it
		if tempFolder, ok := folderCache[folder.Path]; ok {
			folder = tempFolder
		} else if err := folder.Load(); err != nil && err == sql.ErrNoRows {
			// Make sure items actually exist at this path
			files, err := ioutil.ReadDir(folder.Path)
			if err != nil {
				log.Println(err)
				return err
			}

			// No items, skip it
			if len(files) == 0 {
				return nil
			}

			// Set short title
			folder.Title = path.Base(folder.Path)

			// Check for a parent folder
			pFolder := new(data.Folder)

			// If scan is triggered by a file, we have to check the dir twice to get parent
			if info.IsDir() {
				pFolder.Path = path.Dir(currPath)
			} else {
				pFolder.Path = path.Dir(path.Dir(currPath))
			}

			// Load parent
			if err := pFolder.Load(); err != nil && err != sql.ErrNoRows {
				log.Println(err)
				return err
			}

			// Copy parent folder's ID
			folder.ParentID = pFolder.ID

			// Save new folder
			if err := folder.Save(); err != nil {
				log.Println(err)
				return err
			}

			// Continue traversal
			folderCount++
		}

		// Cache this folder
		folderCache[folder.Path] = folder

		// Check for a valid media extension
		if !validSet.Has(path.Ext(currPath)) {
			return nil
		}

		// Attempt to scan media file with taglib
		file, err := taglib.Read(currPath)
		if err != nil {
			log.Println(err)
			return fmt.Errorf("%s: %s", currPath, err.Error())
		}
		defer file.Close()

		// Generate a song model from the TagLib file
		song, err := data.SongFromFile(file)
		if err != nil {
			log.Println(err)
			return err
		}

		// Populate filesystem-related struct fields using OS info
		song.FileName = currPath
		song.FileSize = info.Size()

		// Use this folder's ID
		song.FolderID = folder.ID

		// Extract type from the extension, capitalize it, drop the dot
		song.FileType = strings.ToUpper(path.Ext(info.Name()))[1:]
		song.LastModified = info.ModTime().Unix()

		// Generate an artist model from this song's metadata
		artist := data.ArtistFromSong(song)

		// Check for existing artist
		// Note: if the artist exists, this operation also loads necessary scanning information
		// such as their artist ID, for use in album and song generation
		if tempArtist, ok := artistCache[artist.Title]; ok {
			artist = tempArtist
		} else if err := artist.Load(); err == sql.ErrNoRows {
			// Save new artist
			if err := artist.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				log.Printf("Artist: [#%05d] %s", artist.ID, artist.Title)
				artistCount++
			}
		}

		// Cache this artist
		artistCache[artist.Title] = artist

		// Generate the album model from this song's metadata
		album := data.AlbumFromSong(song)
		album.ArtistID = artist.ID

		// Generate cache key
		albumCacheKey := strconv.Itoa(album.ArtistID) + "_" + album.Title

		// Check for existing album
		// Note: if the album exists, this operation also loads necessary scanning information
		// such as the album ID, for use in song generation
		if tempAlbum, ok := albumCache[albumCacheKey]; ok {
			album = tempAlbum
		} else if err := album.Load(); err == sql.ErrNoRows {
			// Save album
			if err := album.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				log.Printf("  - Album: [#%05d] %s - %d - %s", album.ID, album.Artist, album.Year, album.Title)
				albumCount++
			}
		}

		// Cache this album
		albumCache[albumCacheKey] = album

		// Add ID fields to song
		song.ArtistID = artist.ID
		song.AlbumID = album.ID

		// Make a duplicate song to check if song has been modified since last scan
		song2 := new(data.Song)
		song2.FileName = song.FileName

		// Check for existing song
		if err := song2.Load(); err == sql.ErrNoRows {
			// Save song (don't log these because they really slow things down)
			if err := song.Save(); err != nil {
				log.Println(err)
			} else if err == nil {
				songCount++
			}
		} else {
			// Song already existed.  Check if it's been updated
			if song.LastModified > song2.LastModified {
				// Update existing song
				song.ID = song2.ID
				if err := song.Update(); err != nil {
					log.Println(err)
				}

				songUpdateCount++
			}
		}

		// Successful media scan
		return nil
	})

	// Check for filesystem walk errors
	if err != nil {
		return err
	}

	// Print metrics
	if verbose {
		log.Printf("fs: media scan complete [time: %s]", time.Since(startTime).String())
		log.Printf("fs: added: [artists: %d] [albums: %d] [songs: %d] [folders: %d]", artistCount, albumCount, songCount, folderCount)
		log.Printf("fs: updated: [songs: %d]", songUpdateCount)
	}

	// No errors
	return nil
}

// OrphanScan scans for missing "orphaned" media files in the local filesystem
func (fsFileSource) OrphanScan(baseFolder string, subFolder string, verbose bool, orphanCancelChan chan struct{}) error {
	// Halt scan if needed
	var mutex sync.RWMutex
	haltOrphanScan := false
	go func() {
		// Wait for signal
		<-orphanCancelChan

		// Halt!
		mutex.Lock()
		haltOrphanScan = true
		mutex.Unlock()
	}()

	// Track metrics about the scan
	folderCount := 0
	songCount := 0
	startTime := time.Now()

	// Check if a baseFolder is set, meaning remove ANYTHING not under this base
	if baseFolder != "" {
		if verbose {
			log.Println("fs: orphan scanning base folder:", baseFolder)
		}

		// Scan for all songs NOT under the base folder
		songs, err := data.DB.SongsNotInPath(baseFolder)
		if err != nil {
			log.Println(err)
			return err
		}

		// Remove all songs which are not in this path
		for _, s := range songs {
			// Remove song from database
			if err := s.Delete(); err != nil {
				log.Println(err)
				return err
			}

			songCount++
		}

		// Scan for all folders NOT under the base folder
		folders, err := data.DB.FoldersNotInPath(baseFolder)
		if err != nil {
			log.Println(err)
			return err
		}

		// Remove all folders which are not in this path
		for _, f := range folders {
			// Remove folder from database
			if err := f.Delete(); err != nil {
				log.Println(err)
				return err
			}

			folderCount++
		}
	}

	// If no subfolder set, use the base folder to check file existence
	if subFolder == "" {
		subFolder = baseFolder
	}

	if verbose {
		log.Println("fs: orphan scanning subfolder:", subFolder)
	} else {
		log.Println("fs: removing:", subFolder)
	}

	// Scan for all songs in subfolder
	songs, err := data.DB.SongsInPath(subFolder)
	if err != nil {
		log.Println(err)
		return err
	}

	// Iterate all songs in this path
	for _, s := range songs {
		// Check that the song still exists in this place
		if _, err := os.Stat(s.FileName); os.IsNotExist(err) {
			// Remove song from database
			if err := s.Delete(); err != nil {
				log.Println(err)
				return err
			}

			songCount++
		}
	}

	// Scan for all folders in subfolder
	folders, err := data.DB.FoldersInPath(subFolder)
	if err != nil {
		return err
	}

	// Iterate all folders in this path
	for _, f := range folders {
		// Check that the folder still has items within it
		files, err := ioutil.ReadDir(f.Path)
		if err != nil && !os.IsNotExist(err) {
			log.Println(err)
			return err
		}

		// Delete any folders with 0 items
		if len(files) == 0 {
			if err := f.Delete(); err != nil {
				log.Println(err)
				return err
			}

			folderCount++
		}
	}

	// Now that songs have been purged, check for albums
	albumCount, err := data.DB.PurgeOrphanAlbums()
	if err != nil {
		log.Println(err)
		return err
	}

	// Check for artists
	artistCount, err := data.DB.PurgeOrphanArtists()
	if err != nil {
		log.Println(err)
		return err
	}

	// Print metrics
	if verbose {
		log.Printf("fs: orphan scan complete [time: %s]", time.Since(startTime).String())
		log.Printf("fs: removed: [artists: %d] [albums: %d] [songs: %d] [folders: %d]", artistCount, albumCount, songCount, folderCount)
	}
	return nil
}
<|endoftext|>


<commit_before>package gotana

import (
	"fmt"
	"strings"
)

const TYPE_CONTAINS = "contains"

type URLPattern struct {
	Type    string `required:"true"`
	Pattern string `required:"true"`
}

func (item URLPattern) String() (result string) {
	result = fmt.Sprintf("URL Pattern [%s]: %s", item.Type, item.Pattern)
	return
}

func (item *URLPattern) Validate(url string) (result bool) {
	switch {
	case item.Type == TYPE_CONTAINS:
		result = strings.Contains(url, item.Pattern)
	default:
		result = false
	}
	return
}

func NewURLPattern(kind string, pattern string) (instance URLPattern) {
	instance = URLPattern{
		Type:    kind,
		Pattern: pattern,
	}
	return
}
<commit_msg>Handle regexP<commit_after>package gotana

import (
	"fmt"
	"regexp"
	"strings"
)

const TYPE_CONTAINS = "contains"
const TYPE_REGEXP = "regexp"

type URLPattern struct {
	Type    string `required:"true"`
	Pattern string `required:"true"`
}

func (item URLPattern) String() (result string) {
	result = fmt.Sprintf("URL Pattern [%s]: %s", item.Type, item.Pattern)
	return
}

func (item *URLPattern) Validate(url string) (result bool) {
	switch {
	case item.Type == TYPE_CONTAINS:
		result = strings.Contains(url, item.Pattern)
	case item.Type == TYPE_REGEXP:
		matched, _ := regexp.MatchString(item.Pattern, url)
		result = matched
	default:
		result = false
	}
	return
}

func NewURLPattern(kind string, pattern string) (instance URLPattern) {
	instance = URLPattern{
		Type:    kind,
		Pattern: pattern,
	}
	return
}
<|endoftext|>


<commit_before>package mantle

import (
        "github.com/garyburd/redigo/redis"
        pool "github.com/vireshas/mantle/backends/redis/pool"
        "time"
        "fmt"
)

//redis struct
type Redis struct{
        Host string
        Port string
        Capacity int
        pool *pool.RedisPool
}

//set default values
func (r *Redis) SetDefaults(){
        if r.Host == "" { r.Host = "localHost" }
        if r.Port == "" { r.Port = "6379" }
        if r.Capacity == 0 { r.Capacity = 10 }
        r.pool = pool.NewPool( r.Host, r.Port, r.Capacity, r.Capacity, time.Minute )
}

//alias to SetDefaults
func (r *Redis) Configure(){
        r.SetDefaults()
}

//get a client from pool
func (r *Redis) GetClient() (*pool.RedisConn, error){
        return r.pool.GetConn()
}

//put a client back in pool
func (r *Redis) PutClient(c *pool.RedisConn){
        r.pool.PutConn(c)
}

//generic methods to execute any redis call
func (r *Redis) execute(cmd string, args ...interface{}) (interface{}, error){
        client, err := r.GetClient()
        if err != nil {
                return nil, err
        }
        defer r.PutClient(client)
        return client.Do(cmd, args...)
}

//wrapper around redis get
func (r *Redis) Get(key string) string{
        value, err := redis.String(r.execute("GET", key))
        if err != nil { return "Key not found!" }
        return value
}

//wrapper around redis set
func (r *Redis) Set(key string, value interface{}) bool{
        _, redis_err := r.execute("SET", key, value)
        if redis_err != nil { return false }
        return true
}

//wrapper around redis mget
func (r *Redis) MGet(keys ...interface{}) []string{
        values, err := redis.Strings(r.execute("MGET", keys...))
        if err != nil { return []string{} }
        return values
}
/*
func (r *Redis) Get(k_v_map map[interface{}]interface{}) bool{}
func (r *Redis) Expire(keys ...interface{}) bool{}
*/
<commit_msg>removing fmt<commit_after>package mantle

import (
        "github.com/garyburd/redigo/redis"
        pool "github.com/vireshas/mantle/backends/redis/pool"
        "time"
)

//redis struct
type Redis struct{
        Host string
        Port string
        Capacity int
        pool *pool.RedisPool
}

//set default values
func (r *Redis) SetDefaults(){
        if r.Host == "" { r.Host = "localHost" }
        if r.Port == "" { r.Port = "6379" }
        if r.Capacity == 0 { r.Capacity = 10 }
        r.pool = pool.NewPool( r.Host, r.Port, r.Capacity, r.Capacity, time.Minute )
}

//alias to SetDefaults
func (r *Redis) Configure(){
        r.SetDefaults()
}

//get a client from pool
func (r *Redis) GetClient() (*pool.RedisConn, error){
        return r.pool.GetConn()
}

//put a client back in pool
func (r *Redis) PutClient(c *pool.RedisConn){
        r.pool.PutConn(c)
}

//generic methods to execute any redis call
func (r *Redis) execute(cmd string, args ...interface{}) (interface{}, error){
        client, err := r.GetClient()
        if err != nil {
                return nil, err
        }
        defer r.PutClient(client)
        return client.Do(cmd, args...)
}

//wrapper around redis get
func (r *Redis) Get(key string) string{
        value, err := redis.String(r.execute("GET", key))
        if err != nil { return "Key not found!" }
        return value
}

//wrapper around redis set
func (r *Redis) Set(key string, value interface{}) bool{
        _, redis_err := r.execute("SET", key, value)
        if redis_err != nil { return false }
        return true
}

//wrapper around redis mget
func (r *Redis) MGet(keys ...interface{}) []string{
        values, err := redis.Strings(r.execute("MGET", keys...))
        if err != nil { return []string{} }
        return values
}
/*
func (r *Redis) Get(k_v_map map[interface{}]interface{}) bool{}
func (r *Redis) Expire(keys ...interface{}) bool{}
*/
<|endoftext|>


<commit_before>/*
 * The creature.
 *
 * The high-level behaviour of creatures.
 */

package creature

import (
	"github.com/DiscoViking/goBrains/brain"
	"github.com/DiscoViking/goBrains/genetics"
	"github.com/DiscoViking/goBrains/locationmanager"
	"image/color"
)

// Fixed values.
const (
	MaxVitality      = 600
	InitialVitality  = 300
	SpeedDegredation = 0.9
)

// Creatures always report a radius of zero, as they cannot be detected.
func (c *Creature) Radius() float64 {
	return 10
}

// Get the colour of the creature.
func (c *Creature) Color() color.RGBA {
	return c.color
}

// Creatures cannot consume each other.
func (c *Creature) Consume() float64 {
	return 0
}

// Manage vitality.
func (c *Creature) manageVitality() bool {
	if c.vitality <= 0 {
		c.lm.RemoveEntity(c)
		return true
	}

	// Decrement and cap vitality.
	c.vitality -= 0.1
	if c.vitality > MaxVitality {
		c.vitality = MaxVitality
	}

	return false
}

// Manage velocities.  Velocity must degrade, so that creatures can stop.
func (c *Creature) manageSpeed() {
	c.movement.move *= SpeedDegredation
	c.movement.rotate *= SpeedDegredation
}

// Do all updating and moving of a creature.
func (c *Creature) Work() {
	// Get all our inputs to charge appropriately.
	for _, in := range c.inputs {
		in.detect()
	}

	// Update the brain one cycle.
	c.brain.Work()

	// Update LM with the distance we are moving this check.
	c.lm.ChangeLocation(locationmanager.CoordDelta{c.movement.move,
		c.movement.rotate},
		c)
	c.manageSpeed()
}

// Check the status of the creature and update LM appropriately.
// Returns a boolean for whether teardown occured.
func (c *Creature) Check() bool {
	death := c.manageVitality()
	if death {
		return true
	}

	return false
}

// Breed a new creature from two existing ones.
func (cx *Creature) Breed(cy *Creature) *Creature {
	newC := NewSimple(cx.lm)
	newC.dna = cx.dna.Breed(cy.dna)
	newC.brain.Restore(newC.dna)
	return newC
}

// Clone an existing creature.
func (c *Creature) Clone() *Creature {
	newC := NewSimple(c.lm)
	newC.dna = c.dna.Clone()
	newC.brain.Restore(newC.dna)
	return newC
}

// Generates a new random DNA string for a creature and injects it into the brain.
// Must be called AFTER all outputs and inputs have been added.
func (c *Creature) Prepare() {
	n := c.brain.GenesNeeded()
	c.dna = genetics.NewDna()
	for i := 0; i < n; i++ {
		c.dna.AddGene(genetics.NewRandomGene())
	}
	c.brain.Restore(c.dna)
}

// Generate a basic creature.
func NewSimple(lm locationmanager.Detection) *Creature {
	c := New(lm)
	c.AddPulser()
	c.AddMouth()
	c.AddAntenna(AntennaLeft)
	c.AddAntenna(AntennaRight)
	c.AddBoosters()
	c.Prepare()
	return c
}

// Initialize a new creature object.
func New(lm locationmanager.Detection) *Creature {
	newC := &Creature{
		lm:       lm,
		dna:      genetics.NewDna(),
		brain:    brain.NewBrain(4),
		inputs:   make([]input, 0),
		color:    color.RGBA{200, 50, 50, 255},
		vitality: InitialVitality,
	}

	// Add the new creature to the location manager.
	lm.AddEntity(newC)
	return newC
}
<commit_msg>Moved creature movement into Check() to make collision detection threadsafe<commit_after>/*
 * The creature.
 *
 * The high-level behaviour of creatures.
 */

package creature

import (
	"image/color"

	"github.com/DiscoViking/goBrains/brain"
	"github.com/DiscoViking/goBrains/genetics"
	"github.com/DiscoViking/goBrains/locationmanager"
)

// Fixed values.
const (
	MaxVitality      = 600
	InitialVitality  = 300
	SpeedDegredation = 0.9
)

// Creatures always report a radius of zero, as they cannot be detected.
func (c *Creature) Radius() float64 {
	return 10
}

// Get the colour of the creature.
func (c *Creature) Color() color.RGBA {
	return c.color
}

// Creatures cannot consume each other.
func (c *Creature) Consume() float64 {
	return 0
}

// Manage vitality.
func (c *Creature) manageVitality() bool {
	if c.vitality <= 0 {
		c.lm.RemoveEntity(c)
		return true
	}

	// Decrement and cap vitality.
	c.vitality -= 0.1
	if c.vitality > MaxVitality {
		c.vitality = MaxVitality
	}

	return false
}

// Manage velocities.  Velocity must degrade, so that creatures can stop.
func (c *Creature) manageSpeed() {
	c.movement.move *= SpeedDegredation
	c.movement.rotate *= SpeedDegredation
}

// Do all updating and moving of a creature.
func (c *Creature) Work() {
	// Get all our inputs to charge appropriately.
	for _, in := range c.inputs {
		in.detect()
	}

	// Update the brain one cycle.
	c.brain.Work()
}

// Check the status of the creature and update LM appropriately.
// Returns a boolean for whether teardown occured.
func (c *Creature) Check() bool {
	death := c.manageVitality()
	if death {
		return true
	}

	// Update LM with the distance we are moving this check.
	c.lm.ChangeLocation(locationmanager.CoordDelta{c.movement.move,
		c.movement.rotate},
		c)
	c.manageSpeed()

	return false
}

// Breed a new creature from two existing ones.
func (cx *Creature) Breed(cy *Creature) *Creature {
	newC := NewSimple(cx.lm)
	newC.dna = cx.dna.Breed(cy.dna)
	newC.brain.Restore(newC.dna)
	return newC
}

// Clone an existing creature.
func (c *Creature) Clone() *Creature {
	newC := NewSimple(c.lm)
	newC.dna = c.dna.Clone()
	newC.brain.Restore(newC.dna)
	return newC
}

// Generates a new random DNA string for a creature and injects it into the brain.
// Must be called AFTER all outputs and inputs have been added.
func (c *Creature) Prepare() {
	n := c.brain.GenesNeeded()
	c.dna = genetics.NewDna()
	for i := 0; i < n; i++ {
		c.dna.AddGene(genetics.NewRandomGene())
	}
	c.brain.Restore(c.dna)
}

// Generate a basic creature.
func NewSimple(lm locationmanager.Detection) *Creature {
	c := New(lm)
	c.AddPulser()
	c.AddMouth()
	c.AddAntenna(AntennaLeft)
	c.AddAntenna(AntennaRight)
	c.AddBoosters()
	c.Prepare()
	return c
}

// Initialize a new creature object.
func New(lm locationmanager.Detection) *Creature {
	newC := &Creature{
		lm:       lm,
		dna:      genetics.NewDna(),
		brain:    brain.NewBrain(4),
		inputs:   make([]input, 0),
		color:    color.RGBA{200, 50, 50, 255},
		vitality: InitialVitality,
	}

	// Add the new creature to the location manager.
	lm.AddEntity(newC)
	return newC
}
<|endoftext|>


<commit_before>package webhooks

import (
	"time"
)

type PayloadBase struct {
	Sender     *User       `json:"sender"`
	Repository *Repository `json:"repository"`
}

type User struct {
	Login             string `json:"login"`
	Id                int64  `json:"id"`
	AvatarUrl         string `json:"avatar_url"`
	GravatarId        string `json:"gravatar_id"`
	Url               string `json:"url"`
	HtmlUrl           string `json:"html_url"`
	FollowersUrl      string `json:"followers_url"`
	FollowingUrl      string `json:"following_url"`
	GistsUrl          string `json:"gists_url"`
	StarredUrl        string `json:"starred_url"`
	SubscriptionsUrl  string `json:"subscriptions_url"`
	OrganizationsUrl  string `json:"organizations_url"`
	ReposUrl          string `json:"repos_url"`
	EventsUrl         string `json:"events_url"`
	ReceivedEventsUrl string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
}

type Repository struct {
	Id               int64     `json:"id"`
	Name             string    `json:"name"`
	FullName         string    `json:"full_name"`
	Owner            *User     `json:"owner"`
	Private          bool      `json:"private"`
	HtmlUrl          string    `json:"html_url"`
	Descriprion      string    `json:"description"`
	Fork             bool      `json:"fork"`
	Url              string    `json:"url"`
	ForksUrl         string    `json:"forks_url"`
	KeysUrl          string    `json:"keys_url"`
	CollaboratorsUrl string    `json:"collaborators_url"`
	TeamsUrl         string    `json:"teams_url"`
	HooksUrl         string    `json:"hooks_url"`
	IssueEventsUrl   string    `json:"issue_events_url"`
	EventsUrl        string    `json:"events_url"`
	AssigneesUrl     string    `json:"assignees_url"`
	BranchesUrl      string    `json:"branches_url"`
	TagsUrl          string    `json:"tags_url"`
	BlobsUrl         string    `json:"blobs_url"`
	GitTagsUrl       string    `json:"git_tags_url"`
	GitRefsUrl       string    `json:"git_refs_url"`
	TreesUrl         string    `json:"trees_url"`
	StatusesUrl      string    `json:"statuses_url"`
	LanguagesUrl     string    `json:"languages_url"`
	StargazersUrl    string    `json:"stargazers_url"`
	ContributorsUrl  string    `json:"contributors_url"`
	SubscribersUrl   string    `json:"subscribers_url"`
	SubscriptionUrl  string    `json:"subscription_url"`
	CommitsUrl       string    `json:"commits_url"`
	GitCommitsUrl    string    `json:"git_commits_url"`
	CommentsUrl      string    `json:"comments_url"`
	IssueCommentUrl  string    `json:"issue_comment_url"`
	ContentsUrl      string    `json:"contents_url"`
	CompareUrl       string    `json:"compare_url"`
	MergesUrl        string    `json:"merges_url"`
	ArchiveUrl       string    `json:"archive_url"`
	DownloadsUrl     string    `json:"downloads_url"`
	IssuesUrl        string    `json:"issues_url"`
	PullsUrl         string    `json:"pulls_url"`
	MilestonesUrl    string    `json:"milestones_url"`
	NotificationsUrl string    `json:"notifications_url"`
	LabelsUrl        string    `json:"labels_url"`
	ReleasesUrl      string    `json:"releases_url"`
	CreatedAt        time.Time `json:"created_at"`
	UpdatedAt        time.Time `json:"updated_at"`
	PushedAt         time.Time `json:"pushed_at"`
	GitUrl           string    `json:"git_url"`
	SshUrl           string    `json:"ssh_url"`
	CloneUrl         string    `json:"clone_url"`
	SvnUrl           string    `json:"svn_url"`
	Homepage         string    `json:"homepage"`
	Size             int       `json:"size"`
	StargazersCount  int       `json:"stargazers_count"`
	WatchersCount    int       `json:"watchers_count"`
	Language         string    `json:"language"`
	HasIssues        bool      `json:"has_issues"`
	HasDownloads     bool      `json:"has_downloads"`
	HasWiki          bool      `json:"has_wiki"`
	HasPages         bool      `json:"has_pages"`
	ForksCount       int       `json:"forks_count"`
	MirrorUrl        string    `json:"mirror_url"`
	OpenIssuesCount  int       `json:"open_issues_count"`
	Forks            int       `json:"forks"`
	OpenIssues       int       `json:"open_issues"`
	DefaultBranch    string    `json:"default_branch"`
}

type Comment struct {
	HtmlUrl   string    `json:"html_url"`
	Url       string    `json:"url"`
	Id        int64     `json:"id"`
	Body      string    `json:"body"`
	Path      string    `json:"path"`
	Position  int       `json:"position"`
	Line      int       `json:"line"`
	CommitId  string    `json:"commit_id"`
	User      *User     `json:"user"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

type Commit struct {
	Id        string     `json:"id"`
	Distinct  bool       `json:"distinct"`
	Message   string     `json:"message"`
	Timestamp time.Time  `json:"timestamp"`
	Url       string     `json:"url"`
	Author    *Committer `json:"author"`
	Committer *Committer `json:"committer"`
	Added     []string   `json:"added"`
	Removed   []string   `json:"removed"`
	Modified  []string   `json:"modified"`
}

type Hook struct {
	Id        int64     `json:"id"`
	Url       string    `json:"url"`
	TestUrl   string    `json:"test_url"`
	PingUrl   string    `json:"ping_url"`
	Name      string    `json:"name"`
	Events    []string  `json:"events"`
	Active    bool      `json:"active"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
	Config    struct {
		Url         string `json:"url"`
		ContentType string `json:"content_type"`
	} `json:"config"`
}

type Label struct {
	Url   string `json:"url"`
	Name  string `json:"name"`
	Color string `json:"color"`
}

type Issue struct {
	Url          string      `json:"url"`
	LabelsUrl    string      `json:"labels_url"`
	CommentsUrl  string      `json:"comments_url"`
	EventsUrl    string      `json:"events_url"`
	HtmlUrl      string      `json:"html_url"`
	Id           int64       `json:"id"`
	Number       int64       `json:"number"`
	Title        string      `json:"title"`
	User         *User       `json:"user"`
	Labels       []*Label    `json:"labels"`
	State        string      `json:"state"`
	Locked       bool        `json:"locked"`
	Assignee     *User       `json:"assignee"`
	Milestone    interface{} `json:"milestone"`
	CommentCount int64       `json:"comments"`
	CreatedAt    time.Time   `json:"created_at"`
	UpdatedAt    time.Time   `json:"updated_at"`
	ClosedAt     *time.Time  `json:"closed_at"`
	Body         string      `json:"body"`
}

type PullRequest struct {
	Issue
	DiffUrl            string      `json:"diff_url"`
	PatchUrl           string      `json:"patch_url"`
	IssueUrl           string      `json:"issue_url"`
	MergedAt           *time.Time  `json:"merged_at"`
	MergeCommitSha     string      `json:"merge_commit_sha"`
	CommitsUrl         string      `json:"commits_url"`
	ReviewCommentsUrl  string      `json:"review_comments_url"`
	ReviewCommentUrl   string      `json:"review_comment_url"`
	StatusesUrl        string      `json:"statuses_url"`
	Head               interface{} `json:"head"` // not sure what this type is quite yet
	Base               interface{} `json:"base"` // same type as Head
	Merged             bool        `json:"merged"`
	Mergeable          bool        `json:"mergable"`
	MergableState      string      `json:"mergable_state"`
	MergedBy           *User       `json:"merged_by"`
	ReviewCommentCount int64       `json:"review_comments"`
	CommitCount        int64       `json:"commits"`
	Additions          int64       `json:"additions"`
	Deletions          int64       `json:"deletions"`
	ChangedFiles       int64       `json:"changed_files"`
}

type Committer struct {
	Name     string `json:"name"`
	Email    string `json:"email"`
	Username string `json:"username"`
}

type CommitCommentEvent struct {
	PayloadBase
	Comment *Comment `json:"comment"`
}

type CreateEvent struct {
	PayloadBase
	RefType      string `json:"ref_type"`
	Ref          string `json:"ref"`
	MasterBranch string `json:"master_branch"`
	Description  string `json:"description"`
}

type DeleteEvent struct {
	PayloadBase
	RefType string `json:"ref_type"`
	Ref     string `json:"ref"`
}

type DeploymentEvent struct {
	PayloadBase
	Deployment struct {
		Url           string      `json:"url"`
		Id            int64       `json:"id"`
		Sha           string      `json:"sha"`
		Ref           string      `json:"ref"`
		Task          string      `json:"task"`
		Payload       interface{} `json:"payload"`
		Environment   string      `json:"environment"`
		Description   string      `json:"description"`
		Creator       *User       `json:"creator"`
		CreatedAt     time.Time   `json:"created_at"`
		UpdatedAt     time.Time   `json:"updated_at"`
		StatusesUrl   string      `json:"statuses_url"`
		RepositoryUrl string      `json:"repository_url"`
	} `json:"deployment"`
}

type IssueCommentEvent struct {
	PayloadBase
	Action  *Issue `json:"issue"`
	Comment string `json:"comment"`
}

type PingEvent struct {
	PayloadBase
	Zen    string `json:"zen"`
	HookId int64  `json:"hook_id"`
	Hook   *Hook  `json:"hook"`
}

type PullRequestEvent struct {
	PayloadBase
	Action      string       `json:"action"`
	Number      int64        `json:"number"`
	PullRequest *PullRequest `json:"pull_request"`
	Label       *Label       `json:"label"`
}

type PushEvent struct {
	PayloadBase
	Ref        string     `json:"ref"`
	Before     string     `json:"before"`
	After      string     `json:"after"`
	Created    bool       `json:"created"`
	Deleted    bool       `json:"deleted"`
	Forced     bool       `json:"forced"`
	BaseRef    string     `json:"base_ref"`
	Compare    string     `json:"compare"`
	Commits    []*Commit  `json:"commits"`
	HeadCommit *Commit    `json:"head_commit"`
	Pusher     *Committer `json:"pusher"`
}
<commit_msg>no idea<commit_after>package webhooks

import (
	"time"
)

type PayloadBase struct {
	Sender     *User       `json:"sender"`
	Repository *Repository `json:"repository"`
}

type User struct {
	Login             string `json:"login"`
	Id                int64  `json:"id"`
	AvatarUrl         string `json:"avatar_url"`
	GravatarId        string `json:"gravatar_id"`
	Url               string `json:"url"`
	HtmlUrl           string `json:"html_url"`
	FollowersUrl      string `json:"followers_url"`
	FollowingUrl      string `json:"following_url"`
	GistsUrl          string `json:"gists_url"`
	StarredUrl        string `json:"starred_url"`
	SubscriptionsUrl  string `json:"subscriptions_url"`
	OrganizationsUrl  string `json:"organizations_url"`
	ReposUrl          string `json:"repos_url"`
	EventsUrl         string `json:"events_url"`
	ReceivedEventsUrl string `json:"received_events_url"`
	Type              string `json:"type"`
	SiteAdmin         bool   `json:"site_admin"`
}

type Repository struct {
	Id               int64  `json:"id"`
	Name             string `json:"name"`
	FullName         string `json:"full_name"`
	Owner            *User  `json:"owner"`
	Private          bool   `json:"private"`
	HtmlUrl          string `json:"html_url"`
	Descriprion      string `json:"description"`
	Fork             bool   `json:"fork"`
	Url              string `json:"url"`
	ForksUrl         string `json:"forks_url"`
	KeysUrl          string `json:"keys_url"`
	CollaboratorsUrl string `json:"collaborators_url"`
	TeamsUrl         string `json:"teams_url"`
	HooksUrl         string `json:"hooks_url"`
	IssueEventsUrl   string `json:"issue_events_url"`
	EventsUrl        string `json:"events_url"`
	AssigneesUrl     string `json:"assignees_url"`
	BranchesUrl      string `json:"branches_url"`
	TagsUrl          string `json:"tags_url"`
	BlobsUrl         string `json:"blobs_url"`
	GitTagsUrl       string `json:"git_tags_url"`
	GitRefsUrl       string `json:"git_refs_url"`
	TreesUrl         string `json:"trees_url"`
	StatusesUrl      string `json:"statuses_url"`
	LanguagesUrl     string `json:"languages_url"`
	StargazersUrl    string `json:"stargazers_url"`
	ContributorsUrl  string `json:"contributors_url"`
	SubscribersUrl   string `json:"subscribers_url"`
	SubscriptionUrl  string `json:"subscription_url"`
	CommitsUrl       string `json:"commits_url"`
	GitCommitsUrl    string `json:"git_commits_url"`
	CommentsUrl      string `json:"comments_url"`
	IssueCommentUrl  string `json:"issue_comment_url"`
	ContentsUrl      string `json:"contents_url"`
	CompareUrl       string `json:"compare_url"`
	MergesUrl        string `json:"merges_url"`
	ArchiveUrl       string `json:"archive_url"`
	DownloadsUrl     string `json:"downloads_url"`
	IssuesUrl        string `json:"issues_url"`
	PullsUrl         string `json:"pulls_url"`
	MilestonesUrl    string `json:"milestones_url"`
	NotificationsUrl string `json:"notifications_url"`
	LabelsUrl        string `json:"labels_url"`
	ReleasesUrl      string `json:"releases_url"`
	CreatedAt        string `json:"created_at"`
	UpdatedAt        string `json:"updated_at"`
	PushedAt         string `json:"pushed_at"`
	GitUrl           string `json:"git_url"`
	SshUrl           string `json:"ssh_url"`
	CloneUrl         string `json:"clone_url"`
	SvnUrl           string `json:"svn_url"`
	Homepage         string `json:"homepage"`
	Size             int    `json:"size"`
	StargazersCount  int    `json:"stargazers_count"`
	WatchersCount    int    `json:"watchers_count"`
	Language         string `json:"language"`
	HasIssues        bool   `json:"has_issues"`
	HasDownloads     bool   `json:"has_downloads"`
	HasWiki          bool   `json:"has_wiki"`
	HasPages         bool   `json:"has_pages"`
	ForksCount       int    `json:"forks_count"`
	MirrorUrl        string `json:"mirror_url"`
	OpenIssuesCount  int    `json:"open_issues_count"`
	Forks            int    `json:"forks"`
	OpenIssues       int    `json:"open_issues"`
	DefaultBranch    string `json:"default_branch"`
}

type Comment struct {
	HtmlUrl   string     `json:"html_url"`
	Url       string     `json:"url"`
	Id        int64      `json:"id"`
	Body      string     `json:"body"`
	Path      string     `json:"path"`
	Position  int        `json:"position"`
	Line      int        `json:"line"`
	CommitId  string     `json:"commit_id"`
	User      *User      `json:"user"`
	CreatedAt *time.Time `json:"created_at"`
	UpdatedAt *time.Time `json:"updated_at"`
}

type Commit struct {
	Id        string   `json:"id"`
	Distinct  bool     `json:"distinct"`
	Message   string   `json:"message"`
	Timestamp string   `json:"timestamp"`
	Url       string   `json:"url"`
	Author    string   `json:"author"`
	Committer string   `json:"committer"`
	Added     []string `json:"added"`
	Removed   []string `json:"removed"`
	Modified  []string `json:"modified"`
}

type Hook struct {
	Id        int64    `json:"id"`
	Url       string   `json:"url"`
	TestUrl   string   `json:"test_url"`
	PingUrl   string   `json:"ping_url"`
	Name      string   `json:"name"`
	Events    []string `json:"events"`
	Active    bool     `json:"active"`
	CreatedAt string   `json:"created_at"`
	UpdatedAt string   `json:"updated_at"`
	Config    struct {
		Url         string `json:"url"`
		ContentType string `json:"content_type"`
	} `json:"config"`
}

type Label struct {
	Url   string `json:"url"`
	Name  string `json:"name"`
	Color string `json:"color"`
}

type Issue struct {
	Url          string      `json:"url"`
	LabelsUrl    string      `json:"labels_url"`
	CommentsUrl  string      `json:"comments_url"`
	EventsUrl    string      `json:"events_url"`
	HtmlUrl      string      `json:"html_url"`
	Id           int64       `json:"id"`
	Number       int64       `json:"number"`
	Title        string      `json:"title"`
	User         *User       `json:"user"`
	Labels       []*Label    `json:"labels"`
	State        string      `json:"state"`
	Locked       bool        `json:"locked"`
	Assignee     *User       `json:"assignee"`
	Milestone    interface{} `json:"milestone"`
	CommentCount int64       `json:"comments"`
	CreatedAt    string      `json:"created_at"`
	UpdatedAt    string      `json:"updated_at"`
	ClosedAt     string      `json:"closed_at"`
	Body         string      `json:"body"`
}

type PullRequest struct {
	Issue
	DiffUrl            string      `json:"diff_url"`
	PatchUrl           string      `json:"patch_url"`
	IssueUrl           string      `json:"issue_url"`
	MergedAt           string      `json:"merged_at"`
	MergeCommitSha     string      `json:"merge_commit_sha"`
	CommitsUrl         string      `json:"commits_url"`
	ReviewCommentsUrl  string      `json:"review_comments_url"`
	ReviewCommentUrl   string      `json:"review_comment_url"`
	StatusesUrl        string      `json:"statuses_url"`
	Head               interface{} `json:"head"` // not sure what this type is quite yet
	Base               interface{} `json:"base"` // same type as Head
	Merged             bool        `json:"merged"`
	Mergeable          bool        `json:"mergable"`
	MergableState      string      `json:"mergable_state"`
	MergedBy           *User       `json:"merged_by"`
	ReviewCommentCount int64       `json:"review_comments"`
	CommitCount        int64       `json:"commits"`
	Additions          int64       `json:"additions"`
	Deletions          int64       `json:"deletions"`
	ChangedFiles       int64       `json:"changed_files"`
}

type Committer struct {
	Name     string `json:"name"`
	Email    string `json:"email"`
	Username string `json:"username"`
}

type CommitCommentEvent struct {
	PayloadBase
	Comment *Comment `json:"comment"`
}

type CreateEvent struct {
	PayloadBase
	RefType      string `json:"ref_type"`
	Ref          string `json:"ref"`
	MasterBranch string `json:"master_branch"`
	Description  string `json:"description"`
}

type DeleteEvent struct {
	PayloadBase
	RefType string `json:"ref_type"`
	Ref     string `json:"ref"`
}

type DeploymentEvent struct {
	PayloadBase
	Deployment struct {
		Url           string      `json:"url"`
		Id            int64       `json:"id"`
		Sha           string      `json:"sha"`
		Ref           string      `json:"ref"`
		Task          string      `json:"task"`
		Payload       interface{} `json:"payload"`
		Environment   string      `json:"environment"`
		Description   string      `json:"description"`
		Creator       *User       `json:"creator"`
		CreatedAt     string      `json:"created_at"`
		UpdatedAt     string      `json:"updated_at"`
		StatusesUrl   string      `json:"statuses_url"`
		RepositoryUrl string      `json:"repository_url"`
	} `json:"deployment"`
}

type IssueCommentEvent struct {
	PayloadBase
	Action  *Issue `json:"issue"`
	Comment string `json:"comment"`
}

type PingEvent struct {
	PayloadBase
	Zen    string `json:"zen"`
	HookId int64  `json:"hook_id"`
	Hook   *Hook  `json:"hook"`
}

type PullRequestEvent struct {
	PayloadBase
	Action      string       `json:"action"`
	Number      int64        `json:"number"`
	PullRequest *PullRequest `json:"pull_request"`
	Label       *Label       `json:"label"`
}

type PushEvent struct {
	PayloadBase
	Ref        string     `json:"ref"`
	Before     string     `json:"before"`
	After      string     `json:"after"`
	Created    bool       `json:"created"`
	Deleted    bool       `json:"deleted"`
	Forced     bool       `json:"forced"`
	BaseRef    string     `json:"base_ref"`
	Compare    string     `json:"compare"`
	Commits    []*Commit  `json:"commits"`
	HeadCommit *Commit    `json:"head_commit"`
	Pusher     *Committer `json:"pusher"`
}
<|endoftext|>


<commit_before>package telebot

import "encoding/json"

type Currency struct {
	Code         string `json:"code"`
	Title        string `json:"title"`
	Symbol       string `json:"symbol"`
	Native       string `json:"native"`
	ThousandsSep string `json:"thousands_sep"`
	DecimalSep   string `json:"decimal_sep"`
	SymbolLeft   bool   `json:"symbol_left"`
	SpaceBetween bool   `json:"space_between"`
	Exp          int    `json:"exp"`
	MinAmount    string `json:"min_amount"`
	MaxAmount    string `json:"max_amount"`
}

var SupportedCurrencies = map[string]Currency{}

func init() {
	json.Unmarshal([]byte(dataSupportedCurrenciesJSON), &SupportedCurrencies)
}
<commit_msg>Fixed an apparent conflict in the currency JSON list<commit_after>package telebot

import "encoding/json"

type Currency struct {
	Code         string `json:"code"`
	Title        string `json:"title"`
	Symbol       string `json:"symbol"`
	Native       string `json:"native"`
	ThousandsSep string `json:"thousands_sep"`
	DecimalSep   string `json:"decimal_sep"`
	SymbolLeft   bool   `json:"symbol_left"`
	SpaceBetween bool   `json:"space_between"`
	Exp          int    `json:"exp"`
	MinAmount    interface{} `json:"min_amount"`
	MaxAmount    interface{} `json:"max_amount"`
}

var SupportedCurrencies = map[string]Currency{}

func init() {
	err := json.Unmarshal([]byte(dataSupportedCurrenciesJSON), &SupportedCurrencies)
	if err != nil {
		panic(err)
	}
}
<|endoftext|>


<commit_before>package mem_constrained_buffer

import (
	"bytes"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
)

const DEFAULT_MEMORY_SIZE int64 = 1 << 18
const FILE_NAME_PREFIX = "mem-buf-"

type MemoryConstrainedBuffer struct {
	b             *bytes.Buffer
	tmpfile       string
	max           int64
	size          int64
	removeOnClose bool
	file          multipart.File
}

func NewMemoryConstrainedBuffer(maxMemory int64, removeOnClose bool) *MemoryConstrainedBuffer {
	return &MemoryConstrainedBuffer{
		max:           maxMemory,
		removeOnClose: removeOnClose,
	}
}

func NewDefaultMemoryConstrainedBuffer() *MemoryConstrainedBuffer {
	return NewMemoryConstrainedBuffer(DEFAULT_MEMORY_SIZE, true)
}

func (m *MemoryConstrainedBuffer) Write(p []byte) (int, error) {
	n, err := m.ReadFrom(bytes.NewReader(p))
	return int(n), err
}

func (m *MemoryConstrainedBuffer) ReadFrom(r io.Reader) (int64, error) {
	var (
		n   int64
		err error
		buf bytes.Buffer
	)

	for {
		n, err = io.CopyN(&buf, r, m.max+1)
		if err != nil && err != io.EOF {
			return 0, err
		}

		m.size += n

		if err == io.EOF {
			err = nil
			break
		}

		if n > m.max {
			// too big, write to disk and flush buffer
			file, err := ioutil.TempFile("", FILE_NAME_PREFIX)
			if err != nil {
				return 0, err
			}
			defer file.Close()
			n, err = io.Copy(file, io.MultiReader(&buf, r))
			if err != nil {
				os.Remove(file.Name())
				return 0, err
			}
			m.tmpfile = file.Name()
			m.size = n
			break

		} else {
			m.max -= n
		}
	}

	if m.tmpfile == "" {
		m.b = &buf
	}

	return n, err
}

func (m *MemoryConstrainedBuffer) Len() int64 {
	return m.size
}

func (m *MemoryConstrainedBuffer) Remove() (err error) {
	if m.file == nil && m.tmpfile == "" {
		return nil
	}
	if m.file != nil {
		err = m.file.Close()
	}
	if m.tmpfile != "" {
		err = os.Remove(m.tmpfile)
	}
	return err
}

func (m *MemoryConstrainedBuffer) open() error {
	if m.file != nil {
		return nil
	}
	if b := m.b; b != nil {
		m.file = &sectionReadCloser{io.NewSectionReader(bytes.NewReader(m.b.Bytes()), 0, int64(b.Len()))}
		return nil
	}
	f, err := os.Open(m.tmpfile)
	m.file = f
	return err
}

func (m *MemoryConstrainedBuffer) Read(p []byte) (n int, err error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.Read(p)
}

func (m *MemoryConstrainedBuffer) ReadAt(p []byte, off int64) (n int, err error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.ReadAt(p, off)
}

func (m *MemoryConstrainedBuffer) Seek(offset int64, whence int) (int64, error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.Seek(offset, whence)
}

func (m *MemoryConstrainedBuffer) Close() error {
	if m.removeOnClose {
		defer m.Remove()
	}
	if m.file == nil {
		return nil
	}
	return m.file.Close()
}

type sectionReadCloser struct {
	*io.SectionReader
}

func (rc sectionReadCloser) Close() error {
	return nil
}
<commit_msg>Updates const block<commit_after>package mem_constrained_buffer

import (
	"bytes"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
)

const (
	DefaultMemorySize int64 = 1 << 18
	FilenamePrefix          = "mem-buf-"
)

type MemoryConstrainedBuffer struct {
	b             *bytes.Buffer
	tmpfile       string
	max           int64
	size          int64
	removeOnClose bool
	file          multipart.File
}

func NewMemoryConstrainedBuffer(maxMemory int64, removeOnClose bool) *MemoryConstrainedBuffer {
	return &MemoryConstrainedBuffer{
		max:           maxMemory,
		removeOnClose: removeOnClose,
	}
}

func NewDefaultMemoryConstrainedBuffer() *MemoryConstrainedBuffer {
	return NewMemoryConstrainedBuffer(DefaultMemorySize, true)
}

func (m *MemoryConstrainedBuffer) Write(p []byte) (int, error) {
	n, err := m.ReadFrom(bytes.NewReader(p))
	return int(n), err
}

func (m *MemoryConstrainedBuffer) ReadFrom(r io.Reader) (int64, error) {
	var (
		n   int64
		err error
		buf bytes.Buffer
	)

	for {
		n, err = io.CopyN(&buf, r, m.max+1)
		if err != nil && err != io.EOF {
			return 0, err
		}

		m.size += n

		if err == io.EOF {
			err = nil
			break
		}

		if n > m.max {
			// too big, write to disk and flush buffer
			file, err := ioutil.TempFile("", FilenamePrefix)
			if err != nil {
				return 0, err
			}
			defer file.Close()
			n, err = io.Copy(file, io.MultiReader(&buf, r))
			if err != nil {
				os.Remove(file.Name())
				return 0, err
			}
			m.tmpfile = file.Name()
			m.size = n
			break

		} else {
			m.max -= n
		}
	}

	if m.tmpfile == "" {
		m.b = &buf
	}

	return n, err
}

func (m *MemoryConstrainedBuffer) Len() int64 {
	return m.size
}

func (m *MemoryConstrainedBuffer) Remove() (err error) {
	if m.file == nil && m.tmpfile == "" {
		return nil
	}
	if m.file != nil {
		err = m.file.Close()
	}
	if m.tmpfile != "" {
		err = os.Remove(m.tmpfile)
	}
	return err
}

func (m *MemoryConstrainedBuffer) open() error {
	if m.file != nil {
		return nil
	}
	if b := m.b; b != nil {
		m.file = &sectionReadCloser{io.NewSectionReader(bytes.NewReader(m.b.Bytes()), 0, int64(b.Len()))}
		return nil
	}
	f, err := os.Open(m.tmpfile)
	m.file = f
	return err
}

func (m *MemoryConstrainedBuffer) Read(p []byte) (n int, err error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.Read(p)
}

func (m *MemoryConstrainedBuffer) ReadAt(p []byte, off int64) (n int, err error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.ReadAt(p, off)
}

func (m *MemoryConstrainedBuffer) Seek(offset int64, whence int) (int64, error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.Seek(offset, whence)
}

func (m *MemoryConstrainedBuffer) Close() error {
	if m.removeOnClose {
		defer m.Remove()
	}
	if m.file == nil {
		return nil
	}
	return m.file.Close()
}

type sectionReadCloser struct {
	*io.SectionReader
}

func (rc sectionReadCloser) Close() error {
	return nil
}
<|endoftext|>


<commit_before>package mem_constrained_buffer

import (
	"bytes"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path/filepath"
	"runtime/debug"
)

var (
	DefaultMemorySize int64 = 1 << 17 // 128K
	FilenamePrefix          = "mem-buf-"
)

type MemoryConstrainedBuffer struct {
	b             bytes.Buffer
	tmpfile       string
	max           int64
	size          int64
	removeOnClose bool
	file          multipart.File
}

func New() *MemoryConstrainedBuffer {
	return NewWithSize(DefaultMemorySize, true)
}

func NewWithSize(maxMemory int64, removeOnClose bool) *MemoryConstrainedBuffer {
	return &MemoryConstrainedBuffer{
		max:           maxMemory,
		removeOnClose: removeOnClose,
	}
}

func (m *MemoryConstrainedBuffer) Write(p []byte) (int, error) {
	n, err := m.ReadFrom(bytes.NewReader(p))
	return int(n), err
}

func (m *MemoryConstrainedBuffer) open() error {
	if m.file != nil {
		return nil
	}
	if m.tmpfile == "" {
		m.file = &sectionReadCloser{
			io.NewSectionReader(bytes.NewReader(m.b.Bytes()), 0, int64(m.b.Len()))}
		return nil
	}
	f, err := os.Open(m.tmpfile)
	m.file = f
	return err
}

func (m *MemoryConstrainedBuffer) Read(p []byte) (int, error) {
	if err := m.open(); err != nil {
		return 0, err
	}
	n, err := m.file.Read(p)
	return n, err
}

func (m *MemoryConstrainedBuffer) ReadAt(p []byte, off int64) (int, error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	return m.file.ReadAt(p, off)
}

func (m *MemoryConstrainedBuffer) ReadFrom(r io.Reader) (int64, error) {
	var (
		n   int64
		err error
	)

	for {
		n, err = io.CopyN(&m.b, r, m.max+1)
		if err != nil && err != io.EOF {
			return 0, err
		}

		m.size += n

		if err == io.EOF {
			err = nil
			break
		}

		if n > m.max {
			// too big, write to disk and flush buffer
			file, err := ioutil.TempFile("", FilenamePrefix)
			if err != nil {
				return 0, err
			}

			sfile, err := ioutil.TempFile("", filepath.Base(file.Name())+"-stack")
			if err != nil {
				return 0, err
			}
			sfile.Write(debug.Stack())
			sfile.Close()

			n, err = io.Copy(file, io.MultiReader(&m.b, r))
			if err != nil {
				os.Remove(file.Name())
				return 0, err
			}
			m.b.Reset()
			m.tmpfile = file.Name()
			m.file = file
			m.file.Seek(0, 0)
			m.size = n
			break

		} else {
			m.max -= n
		}
	}

	return m.size, err
}

func (m *MemoryConstrainedBuffer) Len() int64 {
	return m.size
}

func (m *MemoryConstrainedBuffer) Seek(offset int64, whence int) (int64, error) {
	if err := m.open(); err != nil {
		return 0, err
	}
	return m.file.Seek(offset, whence)
}

func (m *MemoryConstrainedBuffer) Remove() (err error) {
	if m.file == nil && m.tmpfile == "" {
		return nil
	}
	if m.file != nil {
		err = m.file.Close()
	}
	if m.tmpfile != "" {
		err = os.Remove(m.tmpfile)
	}
	return err
}

func (m *MemoryConstrainedBuffer) Close() error {
	m.b.Reset()
	if m.file == nil {
		return nil
	}
	err := m.file.Close()
	if m.removeOnClose {
		err = m.Remove()
	}
	return err
}

type sectionReadCloser struct {
	*io.SectionReader
}

func (rc sectionReadCloser) Close() error {
	return nil
}
<commit_msg>close the file so we don't have stale handles around<commit_after>package mem_constrained_buffer

import (
	"bytes"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path/filepath"
	"runtime/debug"
)

var (
	DefaultMemorySize int64 = 1 << 17 // 128K
	FilenamePrefix          = "mem-buf-"
)

type MemoryConstrainedBuffer struct {
	b             bytes.Buffer
	tmpfile       string
	max           int64
	size          int64
	removeOnClose bool
	file          multipart.File
}

func New() *MemoryConstrainedBuffer {
	return NewWithSize(DefaultMemorySize, true)
}

func NewWithSize(maxMemory int64, removeOnClose bool) *MemoryConstrainedBuffer {
	return &MemoryConstrainedBuffer{
		max:           maxMemory,
		removeOnClose: removeOnClose,
	}
}

func (m *MemoryConstrainedBuffer) Write(p []byte) (int, error) {
	n, err := m.ReadFrom(bytes.NewReader(p))
	return int(n), err
}

func (m *MemoryConstrainedBuffer) open() error {
	if m.file != nil {
		return nil
	}
	if m.tmpfile == "" {
		m.file = &sectionReadCloser{
			io.NewSectionReader(bytes.NewReader(m.b.Bytes()), 0, int64(m.b.Len()))}
		return nil
	}
	f, err := os.Open(m.tmpfile)
	m.file = f
	return err
}

func (m *MemoryConstrainedBuffer) close() error {
	if m.file == nil {
		return nil
	}
	err := m.file.Close()
	m.file = nil
	return err
}

func (m *MemoryConstrainedBuffer) Read(p []byte) (int, error) {
	if err := m.open(); err != nil {
		return 0, err
	}
	defer m.close()
	n, err := m.file.Read(p)
	return n, err
}

func (m *MemoryConstrainedBuffer) ReadAt(p []byte, off int64) (int, error) {
	if m.file == nil {
		if err := m.open(); err != nil {
			return 0, err
		}
	}
	defer m.close()
	return m.file.ReadAt(p, off)
}

func (m *MemoryConstrainedBuffer) ReadFrom(r io.Reader) (int64, error) {
	var (
		n   int64
		err error
	)

	for {
		n, err = io.CopyN(&m.b, r, m.max+1)
		if err != nil && err != io.EOF {
			return 0, err
		}

		m.size += n

		if err == io.EOF {
			err = nil
			break
		}

		if n > m.max {
			// too big, write to disk and flush buffer
			file, err := ioutil.TempFile("", FilenamePrefix)
			if err != nil {
				return 0, err
			}
			defer m.close()

			sfile, err := ioutil.TempFile("", filepath.Base(file.Name())+"-stack")
			if err != nil {
				return 0, err
			}
			sfile.Write(debug.Stack())
			sfile.Close()

			n, err = io.Copy(file, io.MultiReader(&m.b, r))
			if err != nil {
				os.Remove(file.Name())
				return 0, err
			}
			m.b.Reset()
			m.tmpfile = file.Name()
			m.file = file
			m.file.Seek(0, 0)
			m.size = n
			break

		} else {
			m.max -= n
		}
	}

	return m.size, err
}

func (m *MemoryConstrainedBuffer) Len() int64 {
	return m.size
}

func (m *MemoryConstrainedBuffer) Seek(offset int64, whence int) (int64, error) {
	if err := m.open(); err != nil {
		return 0, err
	}
	defer m.close()
	return m.file.Seek(offset, whence)
}

func (m *MemoryConstrainedBuffer) Remove() (err error) {
	if m.file == nil && m.tmpfile == "" {
		return nil
	}
	err = m.close()
	if m.tmpfile != "" {
		err = os.Remove(m.tmpfile)
	}
	return err
}

func (m *MemoryConstrainedBuffer) Close() error {
	m.b.Reset()
	err := m.close()
	if m.removeOnClose {
		err = m.Remove()
	}
	return err
}

type sectionReadCloser struct {
	*io.SectionReader
}

func (rc sectionReadCloser) Close() error {
	return nil
}
<|endoftext|>


<commit_before>// 24 may 2014
package main

import (
	"fmt"
	"os"
	"strings"
	"go/token"
	"go/ast"
	"go/parser"
	"sort"
)

func getPackage(path string) (pkg *ast.Package) {
	fileset := token.NewFileSet()		// parser.ParseDir() actually writes to this; not sure why it doesn't return one instead
	filter := func(i os.FileInfo) bool {
		return strings.HasSuffix(i.Name(), "_windows.go")
	}
	pkgs, err := parser.ParseDir(fileset, path, filter, parser.AllErrors)
	if err != nil {
		panic(err)
	}
	if len(pkgs) != 1 {
		panic("more than one package found")
	}
	for k, _ := range pkgs {		// get the sole key
		pkg = pkgs[k]
	}
	return pkg
}

type walker struct {
	desired	func(string) bool
}

var known = map[string]string{}
var unknown = map[string]struct{}{}

func (w *walker) Visit(node ast.Node) ast.Visitor {
	if n, ok := node.(*ast.Ident); ok {
		if w.desired(n.Name) {
			if n.Obj != nil {
				delete(unknown, n.Name)
				kind := n.Obj.Kind.String()
				if known[n.Name] != "" && known[n.Name] != kind {
					panic(n.Name + "(" + kind + ") already known to be a " + known[n.Name])
				}
				known[n.Name] = kind
			} else if _, ok := known[n.Name]; !ok {		// only if not known
				unknown[n.Name] = struct{}{}
			}
		}
	}
	return w
}

func gatherNames(pkg *ast.Package) {
	desired := func(name string) bool {
		if strings.HasPrefix(name, "_") && len(name) > 1 {
			return !strings.ContainsAny(name,
				"abcdefghijklmnopqrstuvwxyz")
		}
		return false
	}
	for _, f := range pkg.Files {
		for _, d := range f.Decls {
			ast.Walk(&walker{desired}, d)
		}
	}
}

func preamble(pkg string) string {
	return "// autogenerated by windowsconstgen; do not edit\n" +
		"package " + pkg + "\n"
}

func main() {
	if len(os.Args) != 2 {
		panic("usage: " + os.Args[0] + " path")
	}

	pkg := getPackage(os.Args[1])
	gatherNames(pkg)

	if len(unknown) > 0 {
		s := "error: the following are still unknown!"
		for k, _ := range unknown {
			s += "\n" + k
		}
		panic(s)
	}

	// keep sorted for git
	consts := make([]string, 0, len(known))

	for ident, kind := range known {
		if kind == "const" || kind == "var" {
			consts = append(consts, ident)
		}
	}
	sort.Strings(consts)

	fmt.Printf("%s" +
		"import \"fmt\"\n" +
		"// #include <windows.h>\n" +
		"import \"C\"\n" +
		"func main() {\n" +
		"	fmt.Println(%q)\n",
		preamble("main"), preamble("ui"))
	for _, ident := range consts {
		fmt.Printf("	fmt.Println(\"const %s =\", C.%s)\n", ident, ident[1:])
	}
	fmt.Printf("}\n")
}
<commit_msg>Updated the Windows constant generator again; this time it now works fully, and I just have to nail the variables and nuke the static constants...<commit_after>// 24 may 2014
package main

import (
	"fmt"
	"os"
	"strings"
	"go/token"
	"go/ast"
	"go/parser"
	"sort"
	"io/ioutil"
	"path/filepath"
	"os/exec"
)

func getPackage(path string) (pkg *ast.Package) {
	fileset := token.NewFileSet()		// parser.ParseDir() actually writes to this; not sure why it doesn't return one instead
	filter := func(i os.FileInfo) bool {
		return strings.HasSuffix(i.Name(), "_windows.go")
	}
	pkgs, err := parser.ParseDir(fileset, path, filter, parser.AllErrors)
	if err != nil {
		panic(err)
	}
	if len(pkgs) != 1 {
		panic("more than one package found")
	}
	for k, _ := range pkgs {		// get the sole key
		pkg = pkgs[k]
	}
	return pkg
}

type walker struct {
	desired	func(string) bool
}

var known = map[string]string{}
var unknown = map[string]struct{}{}

func (w *walker) Visit(node ast.Node) ast.Visitor {
	if n, ok := node.(*ast.Ident); ok {
		if w.desired(n.Name) {
			if n.Obj != nil {
				delete(unknown, n.Name)
				kind := n.Obj.Kind.String()
				if known[n.Name] != "" && known[n.Name] != kind {
					panic(n.Name + "(" + kind + ") already known to be a " + known[n.Name])
				}
				known[n.Name] = kind
			} else if _, ok := known[n.Name]; !ok {		// only if not known
				unknown[n.Name] = struct{}{}
			}
		}
	}
	return w
}

func gatherNames(pkg *ast.Package) {
	desired := func(name string) bool {
		if strings.HasPrefix(name, "_") && len(name) > 1 {
			return !strings.ContainsAny(name,
				"abcdefghijklmnopqrstuvwxyz")
		}
		return false
	}
	for _, f := range pkg.Files {
		for _, d := range f.Decls {
			ast.Walk(&walker{desired}, d)
		}
	}
}

func preamble(pkg string) string {
	return "// autogenerated by windowsconstgen; do not edit\n" +
		"package " + pkg + "\n"
}

func main() {
	if len(os.Args) != 3 {
		panic("usage: " + os.Args[0] + " path goarch")
	}
	pkgpath := os.Args[1]
	targetarch := os.Args[2]

	pkg := getPackage(pkgpath)
	gatherNames(pkg)

	if len(unknown) > 0 {
		s := "error: the following are still unknown!"
		for k, _ := range unknown {
			s += "\n" + k
		}
		panic(s)
	}

	// keep sorted for git
	consts := make([]string, 0, len(known))
	for ident, kind := range known {
		if kind == "const" || kind == "var" {
			consts = append(consts, ident)
		}
	}
	sort.Strings(consts)

	tmpdir, err := ioutil.TempDir("", "windowsconstgen")
	if err != nil {
		panic(err)
	}
	genoutname := filepath.Join(tmpdir, "gen.go")
	f, err := os.Create(genoutname)
	if err != nil {
		panic(err)
	}
	fmt.Fprintf(f, "%s" +
		"import \"fmt\"\n" +
		"// #include <windows.h>\n" +
		"// #include <commctrl.h>\n" +
		"import \"C\"\n" +
		"func main() {\n" +
		"	fmt.Println(%q)\n",
		preamble("main"), preamble("ui"))
	for _, ident := range consts {
		fmt.Fprintf(f, "	fmt.Println(\"const %s =\", C.%s)\n", ident, ident[1:])
	}
	fmt.Fprintf(f, "}\n")
	f.Close()

	cmd := exec.Command("go", "run", genoutname)
	f, err = os.Create(filepath.Join(pkgpath, "zconstants_windows_" + targetarch + ".go"))
	if err != nil {
		panic(err)
	}
	defer f.Close()
	cmd.Stdout = f
	cmd.Stderr = os.Stderr
	cmd.Env = append(cmd.Env, os.Environ()...)		// otherwise $PATH doesn't get carried over and things mysteriously fail
	cmd.Env = append(cmd.Env,
		"GOOS=windows",
		"GOARCH=" + targetarch,
		"CGO_ENABLED=1")		// needed as it's not set by default in cross-compiles
	err = cmd.Run()
	if err != nil {
		// TODO find a way to get the exit code
		os.Exit(1)
	}

	// TODO remove the temporary directory
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func makeRegisterCmd() *commander.Command {
	var registerCmd = &commander.Command{
		Run:       runRegisterCmd,
		UsageLine: "register [options]",
		Flag:      *flag.NewFlagSet("mezasi-register", flag.ExitOnError),
	}

	registerCmd.Flag.String("name", "", "vm name (* required)")
	registerCmd.Flag.String("base", "", "base image (*)")
	registerCmd.Flag.String("public-key", "", "path to ssh public key file (for root@vm)")
	registerCmd.Flag.String("user-data", "", "path to shell script executed on boot up")
	registerCmd.Flag.Bool("wait", false, "wait for vm boot up")
	return registerCmd
}

var listCmd = &commander.Command{
	Run:       runListCmd,
	UsageLine: "list",
}

var configCmd = &commander.Command{
	Run:       runConfigCmd,
	UsageLine: "config",
}

var infoCmd = &commander.Command{
	Run:       runInfoCmd,
	UsageLine: "info <name>",
}

var startCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "start <name>",
}

var stopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "stop <name>",
}

var forceStopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "force_stop <name>",
}

func makeRemoveCmd() *commander.Command {
	var removeCmd = &commander.Command{
		Run:       runRemoveCmd,
		UsageLine: "remove <name>",
		Flag:      *flag.NewFlagSet("mezasi-remove", flag.ExitOnError),
	}
	removeCmd.Flag.Bool("yes", false, "")
	return removeCmd
}

var publicKeyCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "public_key <name> [file]",
}

var userDataCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "user_data <name> [file]",
}

var sshCmd = &commander.Command{
	Run:       runSshCmd,
	UsageLine: "ssh <name> ...",
}

var portMapCmd = &commander.Command{
	Run:       runPostPortMapCmd,
	UsageLine: "port_map <name> <port>",
}

func runListCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/list", nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runConfigCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "config", nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runInfoCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", "vm/"+cmd.Name()+"/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRemoveCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	yes := cmd.Flag.Lookup("yes").Value.Get().(bool)
	name := args[0]

	if !yes {
		fmt.Printf("Really remove %s? [y/N]\n", name)
		var y string
		fmt.Scanf("%s", &y)
		if y != "y" && y != "Y" {
			os.Exit(1)
		}
	}
	req, err := client.NewRequest("POST", "vm/remove/"+name, nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRegisterCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	base := cmd.Flag.Lookup("base").Value.Get().(string)
	if name == "" || base == "" {
		return errors.New("Usage: " + cmd.UsageLine)
	}

	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("name", name); err != nil {
		return err
	}
	if err := w.WriteField("base", base); err != nil {
		return err
	}

	if publicKey := cmd.Flag.Lookup("public-key").Value.Get().(string); publicKey != "" {
		if err := writeFileField(w, "public_key", publicKey); err != nil {
			return err
		}
	}
	if userData := cmd.Flag.Lookup("user-data").Value.Get().(string); userData != "" {
		if err := writeFileField(w, "user_data", userData); err != nil {
			return err
		}
	}
	if err := w.Close(); err != nil {
		return err
	}

	req, err := client.NewRequest("POST", "vm/register", &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())

	if _, err := pp(client.Do(req)); err != nil {
		return err
	}

	wait := cmd.Flag.Lookup("wait").Value.Get().(bool)
	if !wait {
		return nil
	}
	log.Println("waiting for vm boot up...")

	req, err = client.NewRequest("GET", "notify/"+name, nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostFileHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	switch len(args) {
	case 1:
		req, err := client.NewRequest("GET", pathStr, nil)
		if err != nil {
			return err
		}
		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	case 2:
		var b bytes.Buffer
		w := multipart.NewWriter(&b)
		if err := writeFileField(w, cmdName, args[1]); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}

		req, err := client.NewRequest("POST", pathStr, &b)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", w.FormDataContentType())

		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	default:
		return errors.New("invalid arguments")
	}
	return nil
}

func runSshCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		pp(resp, err)
		return err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()

	m := make(map[string]interface{})
	if err := dec.Decode(&m); err != nil {
		return err
	}
	cmdPath, err := exec.LookPath("ssh")
	if err != nil {
		return err
	}
	execArgs := []string{m["ip_addr"].(string)}
	execArgs = append(execArgs, args[1:]...)

	execCmd := exec.Command(cmdPath, execArgs...)
	execCmd.Stdin = os.Stdin
	execCmd.Stdout = os.Stdout
	execCmd.Stderr = os.Stderr

	return execCmd.Run()
}

func runPostPortMapCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("port", args[1]); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", pathStr, &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func validateCmdArgs(cmd *commander.Command, args []string) error {
	mustVal := 0
	optionVal := 0
	must := regexp.MustCompile(`<\w+>`)
	option := regexp.MustCompile(`\[\w+\]`)

	for _, action := range strings.Split(cmd.UsageLine, " ") {
		if must.MatchString(action) {
			mustVal++
		}
		if option.MatchString(action) {
			optionVal++
		}
		// skip arguments validation
		if action == "..." {
			if mustVal <= len(args) {
				return nil
			}
		}
	}
	if mustVal <= len(args) && len(args) <= mustVal+optionVal {
		return nil
	}
	return errors.New("invalid argument\nUsage: " + cmd.UsageLine)
}

func writeFileField(w *multipart.Writer, fieldName string, filePath string) error {
	ff, err := w.CreateFormField(fieldName)
	if err != nil {
		return err
	}
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	if _, err := io.Copy(ff, file); err != nil {
		return err
	}
	return nil
}

func pp(resp *http.Response, err error) (*http.Response, error) {
	if err != nil {
		return resp, err
	}
	log.Println(resp.Status)

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

	index := strings.Index(resp.Header.Get("Content-Type"), "json")

	if index > 0 {
		var b bytes.Buffer
		err := json.Indent(&b, src, "", "    ")
		if err != nil {
			return resp, err
		}
		fmt.Println(b.String())
	} else {
		fmt.Println(string(src))
	}
	return resp, err
}
<commit_msg>add --host option<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/gonuts/commander"
	"github.com/gonuts/flag"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"strings"
)

func makeRegisterCmd() *commander.Command {
	var registerCmd = &commander.Command{
		Run:       runRegisterCmd,
		UsageLine: "register [options]",
		Flag:      *flag.NewFlagSet("mezasi-register", flag.ExitOnError),
	}

	registerCmd.Flag.String("name", "", "vm name (* required)")
	registerCmd.Flag.String("base", "", "base image (*)")
	registerCmd.Flag.String("public-key", "", "path to ssh public key file (for root@vm)")
	registerCmd.Flag.String("user-data", "", "path to shell script executed on boot up")
	registerCmd.Flag.String("host", "", "vm host (not specified, random choices)")
	registerCmd.Flag.Bool("wait", false, "wait for vm boot up")
	return registerCmd
}

var listCmd = &commander.Command{
	Run:       runListCmd,
	UsageLine: "list",
}

var configCmd = &commander.Command{
	Run:       runConfigCmd,
	UsageLine: "config",
}

var infoCmd = &commander.Command{
	Run:       runInfoCmd,
	UsageLine: "info <name>",
}

var startCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "start <name>",
}

var stopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "stop <name>",
}

var forceStopCmd = &commander.Command{
	Run:       runPostHandleNameCmd,
	UsageLine: "force_stop <name>",
}

func makeRemoveCmd() *commander.Command {
	var removeCmd = &commander.Command{
		Run:       runRemoveCmd,
		UsageLine: "remove <name>",
		Flag:      *flag.NewFlagSet("mezasi-remove", flag.ExitOnError),
	}
	removeCmd.Flag.Bool("yes", false, "")
	return removeCmd
}

var publicKeyCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "public_key <name> [file]",
}

var userDataCmd = &commander.Command{
	Run:       runPostFileHandleNameCmd,
	UsageLine: "user_data <name> [file]",
}

var sshCmd = &commander.Command{
	Run:       runSshCmd,
	UsageLine: "ssh <name> ...",
}

var portMapCmd = &commander.Command{
	Run:       runPostPortMapCmd,
	UsageLine: "port_map <name> <port>",
}

func runListCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/list", nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runConfigCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "config", nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runInfoCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", "vm/"+cmd.Name()+"/"+args[0], nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRemoveCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	yes := cmd.Flag.Lookup("yes").Value.Get().(bool)
	name := args[0]

	if !yes {
		fmt.Printf("Really remove %s? [y/N]\n", name)
		var y string
		fmt.Scanf("%s", &y)
		if y != "y" && y != "Y" {
			os.Exit(1)
		}
	}
	req, err := client.NewRequest("POST", "vm/remove/"+name, nil)
	if err != nil {
		return err
	}
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runRegisterCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	name := cmd.Flag.Lookup("name").Value.Get().(string)
	base := cmd.Flag.Lookup("base").Value.Get().(string)
	host := cmd.Flag.Lookup("host").Value.Get().(string)
	if name == "" || base == "" {
		return errors.New("Usage: " + cmd.UsageLine)
	}

	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("name", name); err != nil {
		return err
	}
	if err := w.WriteField("base", base); err != nil {
		return err
	}
	if err := w.WriteField("host", host); err != nil {
		return err
	}

	if publicKey := cmd.Flag.Lookup("public-key").Value.Get().(string); publicKey != "" {
		if err := writeFileField(w, "public_key", publicKey); err != nil {
			return err
		}
	}
	if userData := cmd.Flag.Lookup("user-data").Value.Get().(string); userData != "" {
		if err := writeFileField(w, "user_data", userData); err != nil {
			return err
		}
	}
	if err := w.Close(); err != nil {
		return err
	}

	req, err := client.NewRequest("POST", "vm/register", &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())

	if _, err := pp(client.Do(req)); err != nil {
		return err
	}

	wait := cmd.Flag.Lookup("wait").Value.Get().(bool)
	if !wait {
		return nil
	}
	log.Println("waiting for vm boot up...")

	req, err = client.NewRequest("GET", "notify/"+name, nil)
	if err != nil {
		return err
	}
	if _, err = pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func runPostFileHandleNameCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	switch len(args) {
	case 1:
		req, err := client.NewRequest("GET", pathStr, nil)
		if err != nil {
			return err
		}
		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	case 2:
		var b bytes.Buffer
		w := multipart.NewWriter(&b)
		if err := writeFileField(w, cmdName, args[1]); err != nil {
			return err
		}
		if err := w.Close(); err != nil {
			return err
		}

		req, err := client.NewRequest("POST", pathStr, &b)
		if err != nil {
			return err
		}
		req.Header.Set("Content-Type", w.FormDataContentType())

		if _, err := pp(client.Do(req)); err != nil {
			return err
		}
	default:
		return errors.New("invalid arguments")
	}
	return nil
}

func runSshCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	req, err := client.NewRequest("GET", "vm/info/"+args[0], nil)
	if err != nil {
		return err
	}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	if resp.StatusCode != 200 {
		pp(resp, err)
		return err
	}

	dec := json.NewDecoder(resp.Body)
	defer resp.Body.Close()

	m := make(map[string]interface{})
	if err := dec.Decode(&m); err != nil {
		return err
	}
	cmdPath, err := exec.LookPath("ssh")
	if err != nil {
		return err
	}
	execArgs := []string{m["ip_addr"].(string)}
	execArgs = append(execArgs, args[1:]...)

	execCmd := exec.Command(cmdPath, execArgs...)
	execCmd.Stdin = os.Stdin
	execCmd.Stdout = os.Stdout
	execCmd.Stderr = os.Stderr

	return execCmd.Run()
}

func runPostPortMapCmd(cmd *commander.Command, args []string) error {
	if err := validateCmdArgs(cmd, args); err != nil {
		return err
	}
	cmdName := cmd.Name()
	pathStr := cmdName + "/" + args[0]
	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	if err := w.WriteField("port", args[1]); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	req, err := client.NewRequest("POST", pathStr, &b)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	if _, err := pp(client.Do(req)); err != nil {
		return err
	}
	return nil
}

func validateCmdArgs(cmd *commander.Command, args []string) error {
	mustVal := 0
	optionVal := 0
	must := regexp.MustCompile(`<\w+>`)
	option := regexp.MustCompile(`\[\w+\]`)

	for _, action := range strings.Split(cmd.UsageLine, " ") {
		if must.MatchString(action) {
			mustVal++
		}
		if option.MatchString(action) {
			optionVal++
		}
		// skip arguments validation
		if action == "..." {
			if mustVal <= len(args) {
				return nil
			}
		}
	}
	if mustVal <= len(args) && len(args) <= mustVal+optionVal {
		return nil
	}
	return errors.New("invalid argument\nUsage: " + cmd.UsageLine)
}

func writeFileField(w *multipart.Writer, fieldName string, filePath string) error {
	ff, err := w.CreateFormField(fieldName)
	if err != nil {
		return err
	}
	file, err := os.Open(filePath)
	if err != nil {
		return err
	}
	if _, err := io.Copy(ff, file); err != nil {
		return err
	}
	return nil
}

func pp(resp *http.Response, err error) (*http.Response, error) {
	if err != nil {
		return resp, err
	}
	log.Println(resp.Status)

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

	index := strings.Index(resp.Header.Get("Content-Type"), "json")

	if index > 0 {
		var b bytes.Buffer
		err := json.Indent(&b, src, "", "    ")
		if err != nil {
			return resp, err
		}
		fmt.Println(b.String())
	} else {
		fmt.Println(string(src))
	}
	return resp, err
}
<|endoftext|>