<commit_before>package main

import (
	"log"
	"os"
	"io/ioutil"
	"encoding/json"
	"github.com/emersion/go-kdeconnect/crypto"
	"github.com/emersion/go-kdeconnect/engine"
	"github.com/emersion/go-kdeconnect/plugin"
	"github.com/emersion/go-kdeconnect/network"
	"github.com/godbus/dbus"
	"github.com/esiqveland/notify"
	"github.com/emersion/go-mpris"
)

func getConfigDir() (configDir string, err error) {
	configHomeDir := os.Getenv("XDG_CONFIG_HOME")
	if configHomeDir == "" {
		homeDir := os.Getenv("HOME")
		if homeDir == "" {
			return
		}
		configHomeDir = homeDir+"/.config"
	}

	configDir = configHomeDir+"/gnomeconnect"
	err = os.MkdirAll(configDir, 0755)
	return
}

func loadPrivateKey() (priv *crypto.PrivateKey, err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	priv = &crypto.PrivateKey{}

	privateKeyFile := configDir+"/private.pem"
	raw, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		if err = priv.Generate(); err != nil {
			return
		}

		raw, err = priv.Marshal()
		if err != nil {
			return
		}

		err = ioutil.WriteFile(privateKeyFile, raw, 0644)
		return
	}

	err = priv.Unmarshal(raw)
	return
}

func loadKnownDevices() (knownDevices []*engine.KnownDevice, err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	knownDevicesFile, err := os.Open(configDir+"/known-devices.json")
	if err != nil {
		return
	}

	dec := json.NewDecoder(knownDevicesFile)
	err = dec.Decode(&knownDevices)
	return
}

func saveKnownDevices(knownDevices []*engine.KnownDevice) (err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	knownDevicesFile, err := os.Create(configDir+"/known-devices.json")
	if err != nil {
		return
	}

	enc := json.NewEncoder(knownDevicesFile)
	err = enc.Encode(knownDevices)
	return
}

func getDeviceIcon(device *network.Device) string {
	switch device.Type {
	case "phone":
		return "phone"
	case "tablet":
		return "pda" // TODO: find something better
	case "desktop":
		return "computer"
	default:
		return ""
	}
}

func newNotification() notify.Notification {
	return notify.Notification{
		AppName: "GNOMEConnect",
	}
}

func main() {
	config := engine.DefaultConfig()

 	priv, err := loadPrivateKey()
	if priv == nil {
		log.Fatal("Could not get private key:", err)
	}
	if err != nil {
		log.Println("Warning: error while loading private key:", err)
	}
	config.PrivateKey = priv

	knownDevices, err := loadKnownDevices()
	if err != nil {
		log.Println("Warning: error while loading known devices:", err)
	}
	config.KnownDevices = knownDevices

	battery := plugin.NewBattery()
	ping := plugin.NewPing()
	notification := plugin.NewNotification()
	mprisPlugin := plugin.NewMpris()

	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}

	notifier, err := notify.New(conn)
	if err != nil {
		panic(err)
	}

	go (func() {
		for {
			select {
			case event := <-ping.Incoming:
				log.Println("Ping:", event.Device.Name)

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Ping from "+event.Device.Name
				notifier.SendNotification(n)
			case event := <-battery.Incoming:
				log.Println("Battery:", event.Device.Name, event.BatteryBody)

				if event.ThresholdEvent == plugin.BatteryThresholdEventLow {
					n := newNotification()
					n.AppIcon = "battery-caution"
					n.Summary = event.Device.Name+" has low battery"
					notifier.SendNotification(n)
				}
				// TODO: remove notification when charging
			case event := <-notification.Incoming:
				log.Println("Notification:", event.Device.Name, event.NotificationBody)

				if event.IsCancel {
					// TODO: dismiss notification
					break
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Notification from "+event.AppName+" on "+event.Device.Name
				n.Body = event.Ticker
				notifier.SendNotification(n)

				// TODO: wait for notification dismiss and send message to remote
			case event := <-mprisPlugin.Incoming:
				log.Println("Mpris:", event.Device.Name, event.MprisBody)

				if event.RequestPlayerList {
					names, err := mpris.List(conn)
					if err != nil {
						log.Println("Warning: cannot list available MPRIS players", err)
						break
					}

					mprisPlugin.SendPlayerList(event.Device, names)
				}

				if event.Player != "" {
					player := mpris.New(conn, event.Player)

					event.RequestNowPlaying = true
					switch event.Action {
					case "Next":
						player.Next()
					case "Previous":
						player.Previous()
					case "Pause":
						player.Pause()
					case "PlayPause":
						player.PlayPause()
					case "Stop":
						player.Stop()
					case "Play":
						player.Play()
					default:
						event.RequestNowPlaying = false
					}

					if event.SetVolume != 0 {
						player.SetVolume(float64(event.SetVolume) / 100)
						event.RequestVolume = true
					}

					if event.RequestNowPlaying || event.RequestVolume {
						reply := &plugin.MprisBody{}
						if event.RequestNowPlaying {
							metadata := player.GetMetadata()
							reply.NowPlaying = metadata["xesam:title"].String()
							reply.IsPlaying = (player.GetPlaybackStatus() == "Playing")
							reply.Length = float64(metadata["mpris:length"].Value().(int64)) / 1000
							reply.Pos = float64(player.GetPosition()) / 1000
						}
						if event.RequestVolume {
							reply.Volume = int(player.GetVolume() * 100)
						}
						event.Device.Send(plugin.MprisType, reply)
					}
				}
			}
		}
	})()

	hdlr := plugin.NewHandler()
	hdlr.Register(battery)
	hdlr.Register(ping)
	hdlr.Register(notification)
	hdlr.Register(mprisPlugin)

	e := engine.New(hdlr, config)

	go (func() {
		devices := map[string]*network.Device{}
		notifications := map[string]int{}

		closed := notifier.NotificationClosed()
		actions := notifier.ActionInvoked()

		defer (func() {
			// Close all notifications
			for _, id := range notifications {
				notifier.CloseNotification(id)
			}
		})()

		getDeviceFromNotification := func (notificationId int) *network.Device {
			for deviceId, id := range notifications {
				if id == notificationId {
					if device, ok := devices[deviceId]; ok {
						return device
					} else {
						return nil
					}
				}
			}
			return nil
		}

		deviceAvailable := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "New device available"
			n.Hints = map[string]dbus.Variant{
				"category": dbus.MakeVariant("device"),
			}
			n.Actions = []string{"pair", "Pair device"}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		deviceRequestsPairing := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "New pair request"
			n.Hints = map[string]dbus.Variant{
				"category": dbus.MakeVariant("device"),
			}
			n.Actions = []string{"pair", "Accept", "unpair", "Reject"}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		deviceConnected := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "Device connected"
			n.Hints = map[string]dbus.Variant{
				"resident": dbus.MakeVariant(true),
				"category": dbus.MakeVariant("device.added"),
			}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		for {
			select {
			case device := <-e.Joins:
				if device.Id == "" {
					continue
				}

				devices[device.Id] = device

				if device.Paired {
					deviceConnected(device)
				} else {
					deviceAvailable(device)
				}
			case device := <-e.RequestsPairing:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				deviceRequestsPairing(device)
			case device := <-e.Paired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				err := saveKnownDevices(config.KnownDevices)
				if err != nil {
					log.Println("Cannot save known devices:", err)
				}

				deviceConnected(device)
			case device := <-e.Unpaired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
			case device := <-e.Leaves:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
				if _, ok := devices[device.Id]; ok {
					delete(devices, device.Id)
				}
			case signal := <-actions:
				device := getDeviceFromNotification(int(signal.Id))
				if device == nil {
					continue
				}

				log.Println(device.Name, signal.ActionKey)

				switch signal.ActionKey {
				case "pair":
					err := e.PairDevice(device)
					if err != nil {
						log.Println("Cannot pair device:", err)
					}
				case "unpair":
					err := e.UnpairDevice(device)
					if err != nil {
						log.Println("Cannot unpair device:", err)
					}
				}
			case signal := <-closed:
				device := getDeviceFromNotification(int(signal.Id))
				if device != nil {
					log.Println(device.Name, signal.Reason)

					if signal.Reason == notify.ReasonDismissedByUser {
						//device.Close()
					}
				}
			}
		}
	})()

	e.Listen()
}
<commit_msg>Adds telephony plugin support<commit_after>package main

import (
	"log"
	"os"
	"io/ioutil"
	"encoding/json"
	"github.com/emersion/go-kdeconnect/crypto"
	"github.com/emersion/go-kdeconnect/engine"
	"github.com/emersion/go-kdeconnect/plugin"
	"github.com/emersion/go-kdeconnect/network"
	"github.com/godbus/dbus"
	"github.com/esiqveland/notify"
	"github.com/emersion/go-mpris"
)

func getConfigDir() (configDir string, err error) {
	configHomeDir := os.Getenv("XDG_CONFIG_HOME")
	if configHomeDir == "" {
		homeDir := os.Getenv("HOME")
		if homeDir == "" {
			return
		}
		configHomeDir = homeDir+"/.config"
	}

	configDir = configHomeDir+"/gnomeconnect"
	err = os.MkdirAll(configDir, 0755)
	return
}

func loadPrivateKey() (priv *crypto.PrivateKey, err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	priv = &crypto.PrivateKey{}

	privateKeyFile := configDir+"/private.pem"
	raw, err := ioutil.ReadFile(privateKeyFile)
	if err != nil {
		if err = priv.Generate(); err != nil {
			return
		}

		raw, err = priv.Marshal()
		if err != nil {
			return
		}

		err = ioutil.WriteFile(privateKeyFile, raw, 0644)
		return
	}

	err = priv.Unmarshal(raw)
	return
}

func loadKnownDevices() (knownDevices []*engine.KnownDevice, err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	knownDevicesFile, err := os.Open(configDir+"/known-devices.json")
	if err != nil {
		return
	}

	dec := json.NewDecoder(knownDevicesFile)
	err = dec.Decode(&knownDevices)
	return
}

func saveKnownDevices(knownDevices []*engine.KnownDevice) (err error) {
	configDir, err := getConfigDir()
	if err != nil {
		return
	}

	knownDevicesFile, err := os.Create(configDir+"/known-devices.json")
	if err != nil {
		return
	}

	enc := json.NewEncoder(knownDevicesFile)
	err = enc.Encode(knownDevices)
	return
}

func getDeviceIcon(device *network.Device) string {
	switch device.Type {
	case "phone":
		return "phone"
	case "tablet":
		return "pda" // TODO: find something better
	case "desktop":
		return "computer"
	default:
		return ""
	}
}

func newNotification() notify.Notification {
	return notify.Notification{
		AppName: "GNOMEConnect",
	}
}

func main() {
	config := engine.DefaultConfig()

 	priv, err := loadPrivateKey()
	if priv == nil {
		log.Fatal("Could not get private key:", err)
	}
	if err != nil {
		log.Println("Warning: error while loading private key:", err)
	}
	config.PrivateKey = priv

	knownDevices, err := loadKnownDevices()
	if err != nil {
		log.Println("Warning: error while loading known devices:", err)
	}
	config.KnownDevices = knownDevices

	battery := plugin.NewBattery()
	ping := plugin.NewPing()
	notification := plugin.NewNotification()
	mprisPlugin := plugin.NewMpris()
	telephony := plugin.NewTelephony()

	conn, err := dbus.SessionBus()
	if err != nil {
		panic(err)
	}

	notifier, err := notify.New(conn)
	if err != nil {
		panic(err)
	}

	go (func() {
		notificationsMap := map[string]int{}

		for {
			select {
			case event := <-ping.Incoming:
				log.Println("Ping:", event.Device.Name)

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Ping from "+event.Device.Name
				notifier.SendNotification(n)
			case event := <-battery.Incoming:
				log.Println("Battery:", event.Device.Name, event.BatteryBody)

				if event.ThresholdEvent == plugin.BatteryThresholdEventLow {
					n := newNotification()
					n.AppIcon = "battery-caution"
					n.Summary = event.Device.Name+" has low battery"
					notifier.SendNotification(n)
				}
				// TODO: remove notification when charging
			case event := <-notification.Incoming:
				log.Println("Notification:", event.Device.Name, event.NotificationBody)

				id, exists := notificationsMap[event.NotificationBody.Id]

				if event.IsCancel {
					if exists {
						notifier.CloseNotification(id)
					}
					break
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Summary = "Notification from "+event.AppName+" on "+event.Device.Name
				n.Body = event.Ticker
				if exists {
					n.ReplacesID = uint32(id)
				}
				newId, _ := notifier.SendNotification(n)

				notificationsMap[event.NotificationBody.Id] = int(newId)

				// TODO: wait for notification dismiss and send message to remote
			case event := <-mprisPlugin.Incoming:
				log.Println("Mpris:", event.Device.Name, event.MprisBody)

				if event.RequestPlayerList {
					names, err := mpris.List(conn)
					if err != nil {
						log.Println("Warning: cannot list available MPRIS players", err)
						break
					}

					mprisPlugin.SendPlayerList(event.Device, names)
				}

				if event.Player != "" {
					player := mpris.New(conn, event.Player)

					event.RequestNowPlaying = true
					switch event.Action {
					case "Next":
						player.Next()
					case "Previous":
						player.Previous()
					case "Pause":
						player.Pause()
					case "PlayPause":
						player.PlayPause()
					case "Stop":
						player.Stop()
					case "Play":
						player.Play()
					default:
						event.RequestNowPlaying = false
					}

					if event.SetVolume != 0 {
						player.SetVolume(float64(event.SetVolume) / 100)
						event.RequestVolume = true
					}

					if event.RequestNowPlaying || event.RequestVolume {
						reply := &plugin.MprisBody{}
						if event.RequestNowPlaying {
							metadata := player.GetMetadata()
							reply.NowPlaying = metadata["xesam:title"].String()
							reply.IsPlaying = (player.GetPlaybackStatus() == "Playing")
							reply.Length = float64(metadata["mpris:length"].Value().(int64)) / 1000
							reply.Pos = float64(player.GetPosition()) / 1000
						}
						if event.RequestVolume {
							reply.Volume = int(player.GetVolume() * 100)
						}
						event.Device.Send(plugin.MprisType, reply)
					}
				}
			case event := <-telephony.Incoming:
				log.Println("Telephony:", event.Device.Name, event.TelephonyBody)

				contactName := event.PhoneNumber
				if contactName == "" {
					contactName = event.PhoneNumber
				}

				n := newNotification()
				n.AppIcon = getDeviceIcon(event.Device)
				n.Hints = map[string]dbus.Variant{
					"category": dbus.MakeVariant("im"),
				}

				switch event.TelephonyBody.Event {
				case plugin.TelephonySms:
					n.Hints = map[string]dbus.Variant{
						"category": dbus.MakeVariant("im.received"),
					}
					n.Summary = "SMS from "+contactName+" on "+event.Device.Name
					n.Body = event.MessageBody
				case plugin.TelephonyRinging:
					n.AppIcon = "call-start"
					n.Summary = "Call from "+contactName+" on "+event.Device.Name
				case plugin.TelephonyTalking:
					n.AppIcon = "call-start"
					n.Summary = "Calling "+contactName+" on "+event.Device.Name
				case plugin.TelephonyMissedCall:
					n.AppIcon = "call-stop"
					n.Summary = "Missed call from "+contactName+" on "+event.Device.Name
				}

				notifier.SendNotification(n)
			}
		}
	})()

	hdlr := plugin.NewHandler()
	hdlr.Register(battery)
	hdlr.Register(ping)
	hdlr.Register(notification)
	hdlr.Register(mprisPlugin)
	hdlr.Register(telephony)

	e := engine.New(hdlr, config)

	go (func() {
		devices := map[string]*network.Device{}
		notifications := map[string]int{}

		closed := notifier.NotificationClosed()
		actions := notifier.ActionInvoked()

		defer (func() {
			// Close all notifications
			for _, id := range notifications {
				notifier.CloseNotification(id)
			}
		})()

		getDeviceFromNotification := func (notificationId int) *network.Device {
			for deviceId, id := range notifications {
				if id == notificationId {
					if device, ok := devices[deviceId]; ok {
						return device
					} else {
						return nil
					}
				}
			}
			return nil
		}

		deviceAvailable := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "New device available"
			n.Hints = map[string]dbus.Variant{
				"category": dbus.MakeVariant("device"),
			}
			n.Actions = []string{"pair", "Pair device"}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		deviceRequestsPairing := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "New pair request"
			n.Hints = map[string]dbus.Variant{
				"category": dbus.MakeVariant("device"),
			}
			n.Actions = []string{"pair", "Accept", "unpair", "Reject"}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		deviceConnected := func(device *network.Device) {
			n := newNotification()
			n.AppIcon = getDeviceIcon(device)
			n.Summary = device.Name
			n.Body = "Device connected"
			n.Hints = map[string]dbus.Variant{
				"resident": dbus.MakeVariant(true),
				"category": dbus.MakeVariant("device.added"),
			}
			id, _ := notifier.SendNotification(n)

			notifications[device.Id] = int(id)
		}

		for {
			select {
			case device := <-e.Joins:
				if device.Id == "" {
					continue
				}

				devices[device.Id] = device

				if device.Paired {
					deviceConnected(device)
				} else {
					deviceAvailable(device)
				}
			case device := <-e.RequestsPairing:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				deviceRequestsPairing(device)
			case device := <-e.Paired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}

				err := saveKnownDevices(config.KnownDevices)
				if err != nil {
					log.Println("Cannot save known devices:", err)
				}

				deviceConnected(device)
			case device := <-e.Unpaired:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
			case device := <-e.Leaves:
				if id, ok := notifications[device.Id]; ok {
					notifier.CloseNotification(id)
				}
				if _, ok := devices[device.Id]; ok {
					delete(devices, device.Id)
				}
			case signal := <-actions:
				device := getDeviceFromNotification(int(signal.Id))
				if device == nil {
					continue
				}

				log.Println(device.Name, signal.ActionKey)

				switch signal.ActionKey {
				case "pair":
					err := e.PairDevice(device)
					if err != nil {
						log.Println("Cannot pair device:", err)
					}
				case "unpair":
					err := e.UnpairDevice(device)
					if err != nil {
						log.Println("Cannot unpair device:", err)
					}
				}
			case signal := <-closed:
				device := getDeviceFromNotification(int(signal.Id))
				if device != nil {
					log.Println(device.Name, signal.Reason)

					if signal.Reason == notify.ReasonDismissedByUser {
						//device.Close()
					}
				}
			}
		}
	})()

	e.Listen()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"net/http"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"bytes"
	"os"
	"os/signal"
	"syscall"
	"strings"
	"time"
	"code.google.com/p/go.net/websocket"
	"go-ari-library"
)

// Var config contains a Config struct to hold the proxy configuration file.
var (
	config Config						// main proxy configuration structure
	client = &http.Client{}				// connection for Commands to ARI
	proxyInstances *proxyInstanceMap	// maps the per-dialog proxy instances
)

// signalCatcher is a function to allows us to stop the application through an
// operating system signal.
func signalCatcher() {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	sig := <-ch
	log.Printf("Signal received: %v", sig)
	os.Exit(0)
}

// Init parses the configuration file by unmarshaling it into a Config struct.
func init() {
	var err error

	// parse the configuration file and get data from it
	configpath := flag.String("config", "./config.json", "Path to config file")

	flag.Parse()
	configfile, err := ioutil.ReadFile(*configpath)
	if err != nil {
		log.Fatal(err)
	}
	// read in the configuration file and unmarshal the json, storing it in 'config'
	json.Unmarshal(configfile, &config)
	fmt.Println(&config)
	proxyInstances = NewproxyInstanceMap()		// initialize a new proxy instance map
}

func main() {
	// Setup a new Event producer and Command consumer for every application
	// we've configured in the configuration file.
	ari.InitBus(config.MessageBus, config.BusConfig)
	for _, app := range config.Applications {
		/*
			Create a new producer which is responsible for the initial topic on the message bus which is used
			to signal the setup of per-application instances. All applications listen to this topic in order to
			be provided the information to setup the ownership of per dialog application instances.
		*/
		producer := ari.InitProducer(app)	// Initialize a new producer channel using the ari.IntProducer function.
		go runEventHandler(app, producer)	// create new websocket connection for every application and pass the producer channel
	}

	go signalCatcher()	// listen for os signal to stop the application
	select {}
}

// runEventHandler sets up a websocket connection to an ARI application.
func runEventHandler(s string, producer chan []byte) {
	// Connect to the websocket backend (ARI)
	var ariMessage string
	url := strings.Join([]string{config.Websocket_URL, "?app=", s, "&api_key=", config.WS_User, ":", config.WS_Password}, "")
	ws, err := websocket.Dial(url, "ari", config.Origin)
	if err != nil {
		log.Fatal(err)
	}

	// Start the producer loop. Every message received from the websocket is
	// passed to the PublishMessage() function.
	for {
		err = websocket.Message.Receive(ws, &ariMessage)		// accept the message from the websocket
		if err != nil {
			log.Fatal(err)
		}
		go PublishMessage(ariMessage, producer)	// publish message to the producer channel
	}
}

// PublishMessage takes an ARI event from the websocket and places it on the
// producer channel.
// Accepts two arguments:
// * a string containing the ARI message
// * a producer channel
func PublishMessage(ariMessage string, producer chan []byte) {
	// unmarshal into an ari.Event so we can append some extra information
	var info eventInfo
	var message ari.Event
	var pi *proxyInstance
	var exists bool = false
	json.Unmarshal([]byte(ariMessage), &message)
	json.Unmarshal([]byte(ariMessage), &info)
	message.ServerID = config.ServerID
	message.Timestamp = time.Now()
	message.ARI_Body = ariMessage
	switch  {
	case info.Type == "StasisStart":
		// since we're starting a new application instance, create the proxy side
		dialogID := ari.UUID()
		fmt.Println("Dialog ID is:", dialogID)
		as, err := json.Marshal(ari.AppStart{Application: info.Application, DialogID: dialogID})
		producer <- as

		// TODO: this sleep is required to allow the application time to spin up. In the future we likely want
		// to implement some sort of feedback mechanism in order to remove this sleep timer.
		time.Sleep(50 * time.Millisecond)
		if err != nil {
			return
		}
		pi = NewProxyInstance(dialogID)				// create new proxy instance for the dialog
		proxyInstances.Add(info.Channel.ID, pi)		// add the dialog to the proxyInstances map to track its life
		exists = true
	case info.Type == "StasisEnd":
		// on application end, perform clean up checks
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeAllObjects()
		}
	case info.Type == "BridgeDestroyed":
		pi, exists = proxyInstances.Get(info.Bridge.ID)
		if exists {
			pi.removeObject(info.Bridge.ID)
		}
	case info.Type == "ChannelDestroyed":
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeObject(info.Channel.ID)
		}
	case strings.HasPrefix(info.Type, "Channel"):
		pi, exists = proxyInstances.Get(info.Channel.ID)
	case strings.HasPrefix(message.Type, "Bridge"):
		pi, exists = proxyInstances.Get(info.Bridge.ID)
	case strings.HasPrefix(message.Type, "Playback"):
		pi, exists = proxyInstances.Get(info.Playback.ID)
	default:
		fmt.Println("No handler for event type")
		//pi, exists = proxyInstances[]
		// if not matching, then we need to perform checks against the
		// existing map to determine where to send this ARI message.
	}
	// marshal the message back into a string
	busMessage, err := json.Marshal(message)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Printf("[DEBUG] Bus Data:\n%s\n", busMessage)

	// push the busMessage onto the producer channel
	if exists {
		pi.Events <- busMessage
	}
}

// Add inserts a proxy instance into the global map of active proxy instances.
func (p *proxyInstanceMap) Add(id string, pi *proxyInstance) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	p.instanceMap[id] = pi
}

// Get returns a proxy instance from the global map of active proxy instances.
// returns nil if not found.
func (p *proxyInstanceMap) Get(id string) (*proxyInstance, bool) {
	p.mapLock.RLock()
	defer p.mapLock.RUnlock()
	pi, ok := p.instanceMap[id]
	if !ok {
		return nil, false
	}
	return pi, true
}

// Remove deletes an entry in the global proxyInstanceMap
func (p *proxyInstanceMap) Remove(id string) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	delete(p.instanceMap, id)
}

// shutDown closes the quit channel to signal all of a ProxyInstance's goroutines
// to return
func (p *proxyInstance) shutDown () {
	close(p.quit)
}

func (p *proxyInstance) addObject(id string) {
	for i:= range p.ariObjects {
		if p.ariObjects[i] == id {
			//object already is associated with this proxyInstance
			return
		}
	}
	p.ariObjects = append(p.ariObjects, id)
	proxyInstances.Add(id, p)

}

func (p *proxyInstance) removeObject(id string) {
	// remove an object from the map.
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			// rewrite the p.ariObjects string slice to append all values up to
			// the index value of 'i', and all values of 'i'+1 and later.
			p.ariObjects = append(p.ariObjects[:i], p.ariObjects[i+1:]...)
		}
	}

	// remove the instance from our tracking map
	proxyInstances.Remove(id)

	// if there are no more objects, shut'rdown
	if len(p.ariObjects) == 0 {
		p.shutDown()
	}
}

func (p *proxyInstance) removeAllObjects() {
	// remove all objects from the map as our application is shutting down.
	for _, obj := range p.ariObjects {
		proxyInstances.Remove(obj)
	}
	p.shutDown()	// destroy the application / proxy instance
}

// runCommandConsumer starts the consumer for accepting Commands from
// applications.
func (p *proxyInstance) runCommandConsumer(dialogID string) {
	commandTopic := strings.Join([]string{"commands", dialogID}, "_")
	responseTopic := strings.Join([]string{"responses", dialogID}, "_")
	fmt.Println("Topics are:", commandTopic, " ", responseTopic)
	p.responseChannel = ari.InitProducer(responseTopic)
	select {
	case <- ari.TopicExists(commandTopic):
		p.commandChannel = ari.InitConsumer(commandTopic)
	case <- time.After(10 * time.Second):
		p.removeAllObjects()
		return
	}

	for {
		select {
		case jsonCommand := <- p.commandChannel:
			go p.processCommand(jsonCommand, p.responseChannel)
		case <- p.quit:
			return
		}
	}
}

// processCommand processes commands from applications and submits them to the
// REST interface.
func (p *proxyInstance) processCommand(jsonCommand []byte, responseProducer chan []byte) {
	var c ari.Command
	var r ari.CommandResponse
	i := ID{ID:"", Name:""}
	fmt.Printf("jsonCommand is %s\n", string(jsonCommand))
	json.Unmarshal(jsonCommand, &c)
	fullURL := strings.Join([]string{config.Stasis_URL, c.URL, "?api_key=", config.WS_User, ":", config.WS_Password }, "")
	fmt.Println(fullURL)
	req, err := http.NewRequest(c.Method, fullURL, bytes.NewBufferString(c.Body))
	req.Header.Set("Content-Type", "application/json")
	res, err := client.Do(req)
	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Body)
	fmt.Printf("Response body is %s\n", buf.String())
	json.Unmarshal(buf.Bytes(), &i)
	if i.ID != "" {
		p.addObject(i.ID)
	} else if i.Name != "" {
		p.addObject(i.Name)
	}
	r.ResponseBody = buf.String()
	r.StatusCode = res.StatusCode
	sendJSON, err := json.Marshal(r)
	if err !=nil {
		fmt.Println(err)
	}
	fmt.Printf("sendJSON is %s\n", string(sendJSON))
	responseProducer <- sendJSON
}
<commit_msg>Clean up logging output.<commit_after>package main

import (
	"encoding/json"
	"net/http"
	"flag"
	"io"
	"io/ioutil"
	"log"
	"bytes"
	"os"
	"os/signal"
	"syscall"
	"strings"
	"time"
	"code.google.com/p/go.net/websocket"
	"go-ari-library"
)

// Var config contains a Config struct to hold the proxy configuration file.
var (
	config Config						// main proxy configuration structure
	client = &http.Client{}				// connection for Commands to ARI
	proxyInstances *proxyInstanceMap	// maps the per-dialog proxy instances
    Debug   *log.Logger
    Info    *log.Logger
    Warning *log.Logger
    Error   *log.Logger
)

// signalCatcher is a function to allows us to stop the application through an
// operating system signal.
func signalCatcher() {
	ch := make(chan os.Signal)
	signal.Notify(ch, syscall.SIGINT)
	sig := <-ch
	log.Printf("Signal received: %v", sig)
	os.Exit(0)
}

func InitializeLogging (debugHandle io.Writer, infoHandle io.Writer, warningHandle io.Writer, errorHandle io.Writer) {
    Debug = log.New(debugHandle,
        "DEBUG: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Info = log.New(infoHandle,
        "INFO: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Warning = log.New(warningHandle,
        "WARNING: ",
        log.Ldate|log.Ltime|log.Lshortfile)

    Error = log.New(errorHandle,
        "ERROR: ",
        log.Ldate|log.Ltime|log.Lshortfile)
}

// Init parses the configuration file by unmarshaling it into a Config struct.
func init() {
	var err error

	// initialize logging
	InitializeLogging(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr)

	// parse the configuration file and get data from it
	Info.Println("Loading configuration for proxy.")
	configpath := flag.String("config", "./config.json", "Path to config file")

	Debug.Println("Parsing the configuration file.")
	flag.Parse()
	Debug.Println("Reading in the configuration from the file.")
	configfile, err := ioutil.ReadFile(*configpath)
	if err != nil {
		Error.Fatal(err)
	}
	// read in the configuration file and unmarshal the json, storing it in 'config'
	Debug.Println("Unmarshaling proxy configuration.")
	json.Unmarshal(configfile, &config)
	Debug.Println(&config)
	Debug.Println("Initialize the proxy instance map.")
	proxyInstances = NewproxyInstanceMap()		// initialize a new proxy instance map
}

func main() {
	// Setup a new Event producer and Command consumer for every application
	// we've configured in the configuration file.
	Info.Println("Initializing the message bus.")
	ari.InitBus(config.MessageBus, config.BusConfig)
	for _, app := range config.Applications {
		/*
			Create a new producer which is responsible for the initial topic on the message bus which is used
			to signal the setup of per-application instances. All applications listen to this topic in order to
			be provided the information to setup the ownership of per dialog application instances.
		*/
		Info.Printf("Initializing signalling bus for application %s", app)
		producer := ari.InitProducer(app)	// Initialize a new producer channel using the ari.IntProducer function.
		Info.Printf("Starting event handler for application %s", app)
		go runEventHandler(app, producer)	// create new websocket connection for every application and pass the producer channel
	}

	go signalCatcher()	// listen for os signal to stop the application
	select {}
}

// runEventHandler sets up a websocket connection to an ARI application.
func runEventHandler(s string, producer chan []byte) {
	// Connect to the websocket backend (ARI)
	var ariMessage string
	url := strings.Join([]string{config.Websocket_URL, "?app=", s, "&api_key=", config.WS_User, ":", config.WS_Password}, "")

	Info.Printf("Attempting to connect to ARI websocket at: %s", url)
	ws, err := websocket.Dial(url, "ari", config.Origin)
	if err != nil {
		log.Fatal(err)
	}

	// Start the producer loop. Every message received from the websocket is
	// passed to the PublishMessage() function.
	Info.Printf("Starting producer loop for application %s", s)
	for {
		err = websocket.Message.Receive(ws, &ariMessage)		// accept the message from the websocket
		if err != nil {
			log.Fatal(err)
		}
		go PublishMessage(ariMessage, producer)	// publish message to the producer channel
	}
}

// PublishMessage takes an ARI event from the websocket and places it on the
// producer channel.
// Accepts two arguments:
// * a string containing the ARI message
// * a producer channel
func PublishMessage(ariMessage string, producer chan []byte) {
	// unmarshal into an ari.Event so we can append some extra information
	var info eventInfo
	var message ari.Event
	var pi *proxyInstance
	var exists bool = false
	json.Unmarshal([]byte(ariMessage), &message)
	json.Unmarshal([]byte(ariMessage), &info)
	message.ServerID = config.ServerID
	message.Timestamp = time.Now()
	message.ARI_Body = ariMessage
	switch  {
	case info.Type == "StasisStart":
		// since we're starting a new application instance, create the proxy side
		dialogID := ari.UUID()
		Info.Println("New StasisStart found. Created new dialogID of ", dialogID)
		as, err := json.Marshal(ari.AppStart{Application: info.Application, DialogID: dialogID})
		producer <- as

		// TODO: this sleep is required to allow the application time to spin up. In the future we likely want
		// to implement some sort of feedback mechanism in order to remove this sleep timer.
		time.Sleep(50 * time.Millisecond)
		if err != nil {
			return
		}

		Info.Printf("Created new proxy instance mapping for dialog '%s' and channel '%s'", dialogID, info.Channel.ID)
		pi = NewProxyInstance(dialogID)				// create new proxy instance for the dialog
		proxyInstances.Add(info.Channel.ID, pi)		// add the dialog to the proxyInstances map to track its life
		exists = true
	case info.Type == "StasisEnd":
		Info.Printf("Ending application instance for channel '%s'", info.Channel.ID)
		// on application end, perform clean up checks
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeAllObjects()
		}
	case info.Type == "BridgeDestroyed":
		pi, exists = proxyInstances.Get(info.Bridge.ID)
		if exists {
			pi.removeObject(info.Bridge.ID)
		}
	case info.Type == "ChannelDestroyed":
		pi, exists = proxyInstances.Get(info.Channel.ID)
		if exists {
			pi.removeObject(info.Channel.ID)
		}
	case strings.HasPrefix(info.Type, "Channel"):
		pi, exists = proxyInstances.Get(info.Channel.ID)
	case strings.HasPrefix(message.Type, "Bridge"):
		pi, exists = proxyInstances.Get(info.Bridge.ID)
	case strings.HasPrefix(message.Type, "Playback"):
		pi, exists = proxyInstances.Get(info.Playback.ID)
	default:
		Warning.Println("No handler for event type")
		//pi, exists = proxyInstances[]
		// if not matching, then we need to perform checks against the
		// existing map to determine where to send this ARI message.
	}
	// marshal the message back into a string
	busMessage, err := json.Marshal(message)
	if err != nil {
		Error.Println(err)
		return
	}
	Debug.Printf("Bus Data:\n%s\n", busMessage)

	// push the busMessage onto the producer channel
	if exists {
		pi.Events <- busMessage
	}
}

// Add inserts a proxy instance into the global map of active proxy instances.
func (p *proxyInstanceMap) Add(id string, pi *proxyInstance) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	p.instanceMap[id] = pi
}

// Get returns a proxy instance from the global map of active proxy instances.
// returns nil if not found.
func (p *proxyInstanceMap) Get(id string) (*proxyInstance, bool) {
	p.mapLock.RLock()
	defer p.mapLock.RUnlock()
	pi, ok := p.instanceMap[id]
	if !ok {
		return nil, false
	}
	return pi, true
}

// Remove deletes an entry in the global proxyInstanceMap
func (p *proxyInstanceMap) Remove(id string) {
	p.mapLock.Lock()
	defer p.mapLock.Unlock()
	delete(p.instanceMap, id)
}

// shutDown closes the quit channel to signal all of a ProxyInstance's goroutines
// to return
func (p *proxyInstance) shutDown () {
	close(p.quit)
}

func (p *proxyInstance) addObject(id string) {
	for i:= range p.ariObjects {
		if p.ariObjects[i] == id {
			//object already is associated with this proxyInstance
			return
		}
	}
	p.ariObjects = append(p.ariObjects, id)
	proxyInstances.Add(id, p)

}

func (p *proxyInstance) removeObject(id string) {
	// remove an object from the map.
	for i := range p.ariObjects {
		if p.ariObjects[i] == id {
			// rewrite the p.ariObjects string slice to append all values up to
			// the index value of 'i', and all values of 'i'+1 and later.
			p.ariObjects = append(p.ariObjects[:i], p.ariObjects[i+1:]...)
		}
	}

	// remove the instance from our tracking map
	proxyInstances.Remove(id)

	// if there are no more objects, shut'rdown
	if len(p.ariObjects) == 0 {
		p.shutDown()
	}
}

func (p *proxyInstance) removeAllObjects() {
	// remove all objects from the map as our application is shutting down.
	for _, obj := range p.ariObjects {
		proxyInstances.Remove(obj)
	}
	p.shutDown()	// destroy the application / proxy instance
}

// runCommandConsumer starts the consumer for accepting Commands from
// applications.
func (p *proxyInstance) runCommandConsumer(dialogID string) {
	commandTopic := strings.Join([]string{"commands", dialogID}, "_")
	responseTopic := strings.Join([]string{"responses", dialogID}, "_")
	Debug.Println("Topics are:", commandTopic, " ", responseTopic)
	p.responseChannel = ari.InitProducer(responseTopic)
	select {
	case <- ari.TopicExists(commandTopic):
		p.commandChannel = ari.InitConsumer(commandTopic)
	case <- time.After(10 * time.Second):
		p.removeAllObjects()
		return
	}

	for {
		select {
		case jsonCommand := <- p.commandChannel:
			go p.processCommand(jsonCommand, p.responseChannel)
		case <- p.quit:
			return
		}
	}
}

// processCommand processes commands from applications and submits them to the
// REST interface.
func (p *proxyInstance) processCommand(jsonCommand []byte, responseProducer chan []byte) {
	var c ari.Command
	var r ari.CommandResponse
	i := ID{ID:"", Name:""}
	Debug.Printf("jsonCommand is %s\n", string(jsonCommand))
	json.Unmarshal(jsonCommand, &c)
	fullURL := strings.Join([]string{config.Stasis_URL, c.URL, "?api_key=", config.WS_User, ":", config.WS_Password }, "")
	Debug.Printf("fullURL is %s\n", fullURL)
	req, err := http.NewRequest(c.Method, fullURL, bytes.NewBufferString(c.Body))
	req.Header.Set("Content-Type", "application/json")
	res, err := client.Do(req)
	buf := new(bytes.Buffer)
	buf.ReadFrom(res.Body)
	Debug.Printf("Response body is %s\n", buf.String())
	json.Unmarshal(buf.Bytes(), &i)
	if i.ID != "" {
		p.addObject(i.ID)
	} else if i.Name != "" {
		p.addObject(i.Name)
	}
	r.ResponseBody = buf.String()
	r.StatusCode = res.StatusCode
	sendJSON, err := json.Marshal(r)
	if err !=nil {
		Error.Println(err)
	}
	Debug.Printf("sendJSON is %s\n", string(sendJSON))
	responseProducer <- sendJSON
}
<|endoftext|>


<commit_before>// Package i3barjson provides a Go library for i3bar JSON protocol support.
package i3barjson

import (
    "encoding/json"
    "fmt"
    "io"
)

// Header represents the header of an i3bar message.
type Header struct {
    Version int         `json:"version"`
    Stop_signal int     `json:"stop_signal,omitempty"`
    Cont_signal int     `json:"cont_signal,omitempty"`
    Click_events bool   `json:"click_events,omitempty"`
}

// String pretty prints Header objects.
func (d *Header) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Block represents a single block of an i3bar message.
type Block struct {
    Full_text string            `json:"full_text"`
    Short_text string           `json:"short_text,omitempty"`
    Color string                `json:"color,omitempty"`
    Min_width int               `json:"min_width,omitempty"`
    Align string                `json:"align,omitempty"`
    Name string                 `json:"name,omitempty"`
    Instance string             `json:"instance,omitempty"`
    Urgent bool                 `json:"urgent,omitempty"`
    Separator bool              `json:"separator"`
    Separator_block_width int   `json:"separator_block_width,omitempty"`
    Markup string               `json:"markup,omitempty"`
}

// String pretty prints Block objects.
func (d *Block) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// StatusLine represents a full i3bar status line.
type StatusLine []Block

// String pretty prints StatusLine objects.
func (d *StatusLine) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Click represents an i3bar click event.
type Click struct {
    Name string     `json:"name"`
    Instance string `json:"instance"`
    X int           `json:"x"`
    Y int           `json:"y"`
    Button int      `json:"button"`
}

// jsonArrayEncoder is an object that streams an infinite JSON array.
type jsonArrayEncoder struct {
    count int
    w io.Writer
    e json.Encoder
}

// Encode streams an infinite JSON array.
// Each call adds another element to the array.
func (e *jsonArrayEncoder) Encode(v interface{}) error {
    linePrefix := ","
    if (e.count == 0) {
        linePrefix = "["
        e.count++
    }
    _, err := e.w.Write([]byte(linePrefix))
    if err != nil {
        return err
    }

    err = e.e.Encode(v)
    if err != nil {
        return err
    }

    return nil
}

// newJsonArrayEncoder returns a new jsonArrayEncoder that wraps w.
func newJsonArrayEncoder(w io.Writer) *jsonArrayEncoder {
    return &jsonArrayEncoder{0, w, *json.NewEncoder(w)}
}

// String pretty prints Click objects.
func (d *Click) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Init initializes the i3bar io.
// w is the io.Writer to write to (usually os.Stdout).
// r is the io.Reader to from (usually os.Stdin). TODO: implement
// q is a channel that will be closed when the write loop is finished.
// The returned channel can be used to write status lines to w.
func Init(h *Header, w io.Writer, r io.Reader, q chan bool) (chan StatusLine, error) {
    if (w == nil) {
        return nil, fmt.Errorf("error: Writer required")
    }
    var jsonWriter = newJsonArrayEncoder(w)
    // TODO: implement read loop
    //var jsonReader *json.Decoder
    if (r != nil) {
        //jsonReader = json.NewDecoder(r)
    }

    msg, err := json.Marshal(h)
    if err != nil {
        return nil, fmt.Errorf("error: couldn't parse Header")
    }
    _, err = fmt.Fprintln(w, string(msg))
    if err != nil {
        return nil, fmt.Errorf("error: couldn't write to Writer")
    }

    statusChan := make(chan StatusLine)
    go writeLoop(jsonWriter, statusChan, q)
    return statusChan, nil
}

// writeLoop continuosly writes status lines sent over c to e.
// q is closed when there are no more values to read from c.
func writeLoop(e *jsonArrayEncoder, c chan StatusLine, q chan bool) {
    for block := range c {
        // TODO: proper error handling
        err := e.Encode(block)
        if err != nil {
            fmt.Printf("%s\n", err)
        }
    }

    close(q)
}
<commit_msg>changed StatusLine to slice of Block pointers<commit_after>// Package i3barjson provides a Go library for i3bar JSON protocol support.
package i3barjson

import (
    "encoding/json"
    "fmt"
    "io"
)

// Header represents the header of an i3bar message.
type Header struct {
    Version int         `json:"version"`
    Stop_signal int     `json:"stop_signal,omitempty"`
    Cont_signal int     `json:"cont_signal,omitempty"`
    Click_events bool   `json:"click_events,omitempty"`
}

// String pretty prints Header objects.
func (d *Header) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Block represents a single block of an i3bar message.
type Block struct {
    Full_text string            `json:"full_text"`
    Short_text string           `json:"short_text,omitempty"`
    Color string                `json:"color,omitempty"`
    Min_width int               `json:"min_width,omitempty"`
    Align string                `json:"align,omitempty"`
    Name string                 `json:"name,omitempty"`
    Instance string             `json:"instance,omitempty"`
    Urgent bool                 `json:"urgent,omitempty"`
    Separator bool              `json:"separator"`
    Separator_block_width int   `json:"separator_block_width,omitempty"`
    Markup string               `json:"markup,omitempty"`
}

// String pretty prints Block objects.
func (d *Block) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// StatusLine represents a full i3bar status line.
type StatusLine []*Block

// String pretty prints StatusLine objects.
func (d *StatusLine) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Click represents an i3bar click event.
type Click struct {
    Name string     `json:"name"`
    Instance string `json:"instance"`
    X int           `json:"x"`
    Y int           `json:"y"`
    Button int      `json:"button"`
}

// jsonArrayEncoder is an object that streams an infinite JSON array.
type jsonArrayEncoder struct {
    count int
    w io.Writer
    e json.Encoder
}

// Encode streams an infinite JSON array.
// Each call adds another element to the array.
func (e *jsonArrayEncoder) Encode(v interface{}) error {
    linePrefix := ","
    if (e.count == 0) {
        linePrefix = "["
        e.count++
    }
    _, err := e.w.Write([]byte(linePrefix))
    if err != nil {
        return err
    }

    err = e.e.Encode(v)
    if err != nil {
        return err
    }

    return nil
}

// newJsonArrayEncoder returns a new jsonArrayEncoder that wraps w.
func newJsonArrayEncoder(w io.Writer) *jsonArrayEncoder {
    return &jsonArrayEncoder{0, w, *json.NewEncoder(w)}
}

// String pretty prints Click objects.
func (d *Click) String() string {
    str, err := json.MarshalIndent(d, "", "    ")
    if err != nil {
        return err.Error()
    }
    return string(str)
}

// Init initializes the i3bar io.
// w is the io.Writer to write to (usually os.Stdout).
// r is the io.Reader to from (usually os.Stdin). TODO: implement
// q is a channel that will be closed when the write loop is finished.
// The returned channel can be used to write status lines to w.
func Init(h *Header, w io.Writer, r io.Reader, q chan bool) (chan StatusLine, error) {
    if (w == nil) {
        return nil, fmt.Errorf("error: Writer required")
    }
    var jsonWriter = newJsonArrayEncoder(w)
    // TODO: implement read loop
    //var jsonReader *json.Decoder
    if (r != nil) {
        //jsonReader = json.NewDecoder(r)
    }

    msg, err := json.Marshal(h)
    if err != nil {
        return nil, fmt.Errorf("error: couldn't parse Header")
    }
    _, err = fmt.Fprintln(w, string(msg))
    if err != nil {
        return nil, fmt.Errorf("error: couldn't write to Writer")
    }

    statusChan := make(chan StatusLine)
    go writeLoop(jsonWriter, statusChan, q)
    return statusChan, nil
}

// writeLoop continuosly writes status lines sent over c to e.
// q is closed when there are no more values to read from c.
func writeLoop(e *jsonArrayEncoder, c chan StatusLine, q chan bool) {
    for block := range c {
        // TODO: proper error handling
        err := e.Encode(block)
        if err != nil {
            fmt.Printf("%s\n", err)
        }
    }

    close(q)
}
<|endoftext|>


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

// Package types declares the data types and implements
// the algorithms for type-checking of Go packages.
// Use Check and Config.Check to invoke the type-checker.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
// Use Info.Types[expr].Value for the results of constant folding.
//
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
//
package types

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/token"

	"code.google.com/p/go.tools/go/exact"
)

// Check type-checks a package and returns the resulting complete package
// object, or a nil package and the first error. The package is specified
// by a list of *ast.Files and corresponding file set, and the import path
// the package is identified with. The clean path must not be empty or dot (".").
//
// For more control over type-checking and results, use Config.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
	var conf Config
	pkg, err := conf.Check(path, fset, files, nil)
	if err != nil {
		return nil, err
	}
	return pkg, nil
}

// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
// behavior if ignored.
type Error struct {
	Fset *token.FileSet // file set for interpretation of Pos
	Pos  token.Pos      // error position
	Msg  string         // error message
	Soft bool           // if set, error is "soft"
}

// Error returns an error string formatted as follows:
// filename:line:column: message
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}

// An importer resolves import paths to Packages.
// The imports map records packages already known,
// indexed by package path. The type-checker
// will invoke Import with Config.Packages.
// An importer must determine the canonical package path and
// check imports to see if it is already present in the map.
// If so, the Importer can return the map entry.  Otherwise,
// the importer must load the package data for the given path
// into a new *Package, record it in imports map, and return
// the package.
// TODO(gri) Need to be clearer about requirements of completeness.
type Importer func(map[string]*Package, string) (*Package, error)

// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
	// If IgnoreFuncBodies is set, function bodies are not
	// type-checked.
	IgnoreFuncBodies bool

	// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
	// declares an empty "C" package and errors are omitted for qualified
	// identifiers referring to package C (which won't find an object).
	// This feature is intended for the standard library cmd/api tool.
	//
	// Caution: Effects may be unpredictable due to follow-up errors.
	//          Do not use casually!
	FakeImportC bool

	// Packages is used to look up (and thus canonicalize) packages by
	// package path. If Packages is nil, it is set to a new empty map.
	// During type-checking, imported packages are added to the map.
	Packages map[string]*Package

	// If Error != nil, it is called with each error found
	// during type checking; err has dynamic type Error.
	// Secondary errors (for instance, to enumerate all types
	// involved in an invalid recursive type declaration) have
	// error strings that start with a '\t' character.
	Error func(err error)

	// If Import != nil, it is called for each imported package.
	// Otherwise, DefaultImport is called.
	Import Importer

	// If Sizes != nil, it provides the sizing functions for package unsafe.
	// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
	Sizes Sizes
}

// DefaultImport is the default importer invoked if Config.Import == nil.
// The declaration:
//
//	import _ "code.google.com/p/go.tools/go/gcimporter"
//
// in a client of go/types will initialize DefaultImport to gcimporter.Import.
var DefaultImport Importer

// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
	// Types maps expressions to their types, and for constant
	// expressions, their values. Invalid expressions are omitted.
	//
	// For (possibly parenthesized) identifiers denoting built-in
	// functions, the recorded signatures are call-site specific:
	// if the call result is not a constant, the recorded type is
	// an argument-specific signature. Otherwise, the recorded type
	// is invalid.
	//
	// Identifiers on the lhs of declarations (i.e., the identifiers
	// which are being declared) are collected in the Defs map.
	// Identifiers denoting packages are collected in the Uses maps.
	Types map[ast.Expr]TypeAndValue

	// Defs maps identifiers to the objects they define (including
	// package names, dots "." of dot-imports, and blank "_" identifiers).
	// For identifiers that do not denote objects (e.g., the package name
	// in package clauses, or symbolic variables t in t := x.(type) of
	// type switch headers), the corresponding objects are nil.
	//
	// For an anonymous field, Defs returns the field *Var it defines.
	//
	// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
	Defs map[*ast.Ident]Object

	// Uses maps identifiers to the objects they denote.
	//
	// For an anonymous field, Uses returns the *TypeName it denotes.
	//
	// Invariant: Uses[id].Pos() != id.Pos()
	Uses map[*ast.Ident]Object

	// Implicits maps nodes to their implicitly declared objects, if any.
	// The following node and object types may appear:
	//
	//	node               declared object
	//
	//	*ast.ImportSpec    *PkgName for dot-imports and imports without renames
	//	*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)
	//      *ast.Field         anonymous struct field or parameter *Var
	//
	Implicits map[ast.Node]Object

	// Selections maps selector expressions (excluding qualified identifiers)
	// to their corresponding selections.
	Selections map[*ast.SelectorExpr]*Selection

	// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
	// associated with a specific node but with all files belonging to a package.
	// Thus, the package scope can be found in the type-checked Package object.
	// Scopes nest, with the Universe scope being the outermost scope, enclosing
	// the package scope, which contains (one or more) files scopes, which enclose
	// function scopes which in turn enclose statement and function literal scopes.
	// Note that even though package-level functions are declared in the package
	// scope, the function scopes are embedded in the file scope of the file
	// containing the function declaration.
	//
	// The following node types may appear in Scopes:
	//
	//	*ast.File
	//	*ast.FuncType
	//	*ast.BlockStmt
	//	*ast.IfStmt
	//	*ast.SwitchStmt
	//	*ast.TypeSwitchStmt
	//	*ast.CaseClause
	//	*ast.CommClause
	//	*ast.ForStmt
	//	*ast.RangeStmt
	//
	Scopes map[ast.Node]*Scope

	// InitOrder is the list of package-level initializers in the order in which
	// they must be executed. Initializers referring to variables related by an
	// initialization dependency appear in topological order, the others appear
	// in source order. Variables without an initialization expression do not
	// appear in this list.
	InitOrder []*Initializer
}

// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
//
func (info *Info) TypeOf(e ast.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, ok := e.(*ast.Ident); ok {
		return info.ObjectOf(id).Type()
	}
	return nil
}

// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
// it uses, not the type (*TypeName) it defines.
//
// Precondition: the Uses and Defs maps are populated.
//
func (info *Info) ObjectOf(id *ast.Ident) Object {
	if obj, ok := info.Defs[id]; ok {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue reports the type and value (for constants)
// of the corresponding expression.
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value exact.Value
}

// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
// "void" values as regular values but with the empty tuple type.

// IsVoid reports whether the corresponding expression
// is a function call without results.
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType reports whether the corresponding expression specifies a type.
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin reports whether the corresponding expression denotes
// a (possibly parenthesized) built-in function.
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue reports whether the corresponding expression is a value.
// Builtins are not considered values. Constant values have a non-
// nil Value.
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant, variable, mapindex, value, commaok:
		return true
	}
	return false
}

// IsNil reports whether the corresponding expression denotes the
// predeclared value nil.
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == value && tv.Type == Typ[UntypedNil]
}

// Addressable reports whether the corresponding expression
// is addressable (http://golang.org/ref/spec#Address_operators).
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable reports whether the corresponding expression
// is assignable to (provided a value of the right type).
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk reports whether the corresponding expression may be
// used on the lhs of a comma-ok assignment.
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
	Lhs []*Var // var Lhs = Rhs
	Rhs ast.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	WriteExpr(&buf, init.Rhs)
	return buf.String()
}

// Check type-checks a package and returns the resulting package object,
// the first error if any, and if info != nil, additional type information.
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}

// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
	m, _ := assertableTo(V, T)
	return m == nil
}

// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.assignableTo(nil, T) // config not needed for non-constant x
}

// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T) // config not needed for non-constant x
}

// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
	f, _ := MissingMethod(V, T, true)
	return f == nil
}
<commit_msg>go.tools/go/types: document Config.Error better<commit_after>// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package types declares the data types and implements
// the algorithms for type-checking of Go packages.
// Use Check and Config.Check to invoke the type-checker.
//
// Type-checking consists of several interdependent phases:
//
// Name resolution maps each identifier (ast.Ident) in the program to the
// language object (Object) it denotes.
// Use Info.{Defs,Uses,Implicits} for the results of name resolution.
//
// Constant folding computes the exact constant value (exact.Value) for
// every expression (ast.Expr) that is a compile-time constant.
// Use Info.Types[expr].Value for the results of constant folding.
//
// Type inference computes the type (Type) of every expression (ast.Expr)
// and checks for compliance with the language specification.
// Use Info.Types[expr].Type for the results of type inference.
//
package types

import (
	"bytes"
	"fmt"
	"go/ast"
	"go/token"

	"code.google.com/p/go.tools/go/exact"
)

// Check type-checks a package and returns the resulting complete package
// object, or a nil package and the first error. The package is specified
// by a list of *ast.Files and corresponding file set, and the import path
// the package is identified with. The clean path must not be empty or dot (".").
//
// For more control over type-checking and results, use Config.Check.
func Check(path string, fset *token.FileSet, files []*ast.File) (*Package, error) {
	var conf Config
	pkg, err := conf.Check(path, fset, files, nil)
	if err != nil {
		return nil, err
	}
	return pkg, nil
}

// An Error describes a type-checking error; it implements the error interface.
// A "soft" error is an error that still permits a valid interpretation of a
// package (such as "unused variable"); "hard" errors may lead to unpredictable
// behavior if ignored.
type Error struct {
	Fset *token.FileSet // file set for interpretation of Pos
	Pos  token.Pos      // error position
	Msg  string         // error message
	Soft bool           // if set, error is "soft"
}

// Error returns an error string formatted as follows:
// filename:line:column: message
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Fset.Position(err.Pos), err.Msg)
}

// An importer resolves import paths to Packages.
// The imports map records packages already known,
// indexed by package path. The type-checker
// will invoke Import with Config.Packages.
// An importer must determine the canonical package path and
// check imports to see if it is already present in the map.
// If so, the Importer can return the map entry.  Otherwise,
// the importer must load the package data for the given path
// into a new *Package, record it in imports map, and return
// the package.
// TODO(gri) Need to be clearer about requirements of completeness.
type Importer func(map[string]*Package, string) (*Package, error)

// A Config specifies the configuration for type checking.
// The zero value for Config is a ready-to-use default configuration.
type Config struct {
	// If IgnoreFuncBodies is set, function bodies are not
	// type-checked.
	IgnoreFuncBodies bool

	// If FakeImportC is set, `import "C"` (for packages requiring Cgo)
	// declares an empty "C" package and errors are omitted for qualified
	// identifiers referring to package C (which won't find an object).
	// This feature is intended for the standard library cmd/api tool.
	//
	// Caution: Effects may be unpredictable due to follow-up errors.
	//          Do not use casually!
	FakeImportC bool

	// Packages is used to look up (and thus canonicalize) packages by
	// package path. If Packages is nil, it is set to a new empty map.
	// During type-checking, imported packages are added to the map.
	Packages map[string]*Package

	// If Error != nil, it is called with each error found
	// during type checking; err has dynamic type Error.
	// Secondary errors (for instance, to enumerate all types
	// involved in an invalid recursive type declaration) have
	// error strings that start with a '\t' character.
	// If Error == nil, type-checking stops with the first
	// error found.
	Error func(err error)

	// If Import != nil, it is called for each imported package.
	// Otherwise, DefaultImport is called.
	Import Importer

	// If Sizes != nil, it provides the sizing functions for package unsafe.
	// Otherwise &StdSizes{WordSize: 8, MaxAlign: 8} is used instead.
	Sizes Sizes
}

// DefaultImport is the default importer invoked if Config.Import == nil.
// The declaration:
//
//	import _ "code.google.com/p/go.tools/go/gcimporter"
//
// in a client of go/types will initialize DefaultImport to gcimporter.Import.
var DefaultImport Importer

// Info holds result type information for a type-checked package.
// Only the information for which a map is provided is collected.
// If the package has type errors, the collected information may
// be incomplete.
type Info struct {
	// Types maps expressions to their types, and for constant
	// expressions, their values. Invalid expressions are omitted.
	//
	// For (possibly parenthesized) identifiers denoting built-in
	// functions, the recorded signatures are call-site specific:
	// if the call result is not a constant, the recorded type is
	// an argument-specific signature. Otherwise, the recorded type
	// is invalid.
	//
	// Identifiers on the lhs of declarations (i.e., the identifiers
	// which are being declared) are collected in the Defs map.
	// Identifiers denoting packages are collected in the Uses maps.
	Types map[ast.Expr]TypeAndValue

	// Defs maps identifiers to the objects they define (including
	// package names, dots "." of dot-imports, and blank "_" identifiers).
	// For identifiers that do not denote objects (e.g., the package name
	// in package clauses, or symbolic variables t in t := x.(type) of
	// type switch headers), the corresponding objects are nil.
	//
	// For an anonymous field, Defs returns the field *Var it defines.
	//
	// Invariant: Defs[id] == nil || Defs[id].Pos() == id.Pos()
	Defs map[*ast.Ident]Object

	// Uses maps identifiers to the objects they denote.
	//
	// For an anonymous field, Uses returns the *TypeName it denotes.
	//
	// Invariant: Uses[id].Pos() != id.Pos()
	Uses map[*ast.Ident]Object

	// Implicits maps nodes to their implicitly declared objects, if any.
	// The following node and object types may appear:
	//
	//	node               declared object
	//
	//	*ast.ImportSpec    *PkgName for dot-imports and imports without renames
	//	*ast.CaseClause    type-specific *Var for each type switch case clause (incl. default)
	//      *ast.Field         anonymous struct field or parameter *Var
	//
	Implicits map[ast.Node]Object

	// Selections maps selector expressions (excluding qualified identifiers)
	// to their corresponding selections.
	Selections map[*ast.SelectorExpr]*Selection

	// Scopes maps ast.Nodes to the scopes they define. Package scopes are not
	// associated with a specific node but with all files belonging to a package.
	// Thus, the package scope can be found in the type-checked Package object.
	// Scopes nest, with the Universe scope being the outermost scope, enclosing
	// the package scope, which contains (one or more) files scopes, which enclose
	// function scopes which in turn enclose statement and function literal scopes.
	// Note that even though package-level functions are declared in the package
	// scope, the function scopes are embedded in the file scope of the file
	// containing the function declaration.
	//
	// The following node types may appear in Scopes:
	//
	//	*ast.File
	//	*ast.FuncType
	//	*ast.BlockStmt
	//	*ast.IfStmt
	//	*ast.SwitchStmt
	//	*ast.TypeSwitchStmt
	//	*ast.CaseClause
	//	*ast.CommClause
	//	*ast.ForStmt
	//	*ast.RangeStmt
	//
	Scopes map[ast.Node]*Scope

	// InitOrder is the list of package-level initializers in the order in which
	// they must be executed. Initializers referring to variables related by an
	// initialization dependency appear in topological order, the others appear
	// in source order. Variables without an initialization expression do not
	// appear in this list.
	InitOrder []*Initializer
}

// TypeOf returns the type of expression e, or nil if not found.
// Precondition: the Types, Uses and Defs maps are populated.
//
func (info *Info) TypeOf(e ast.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, ok := e.(*ast.Ident); ok {
		return info.ObjectOf(id).Type()
	}
	return nil
}

// ObjectOf returns the object denoted by the specified id,
// or nil if not found.
//
// If id is an anonymous struct field, ObjectOf returns the field (*Var)
// it uses, not the type (*TypeName) it defines.
//
// Precondition: the Uses and Defs maps are populated.
//
func (info *Info) ObjectOf(id *ast.Ident) Object {
	if obj, ok := info.Defs[id]; ok {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue reports the type and value (for constants)
// of the corresponding expression.
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value exact.Value
}

// TODO(gri) Consider eliminating the IsVoid predicate. Instead, report
// "void" values as regular values but with the empty tuple type.

// IsVoid reports whether the corresponding expression
// is a function call without results.
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType reports whether the corresponding expression specifies a type.
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin reports whether the corresponding expression denotes
// a (possibly parenthesized) built-in function.
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue reports whether the corresponding expression is a value.
// Builtins are not considered values. Constant values have a non-
// nil Value.
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant, variable, mapindex, value, commaok:
		return true
	}
	return false
}

// IsNil reports whether the corresponding expression denotes the
// predeclared value nil.
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == value && tv.Type == Typ[UntypedNil]
}

// Addressable reports whether the corresponding expression
// is addressable (http://golang.org/ref/spec#Address_operators).
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable reports whether the corresponding expression
// is assignable to (provided a value of the right type).
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk reports whether the corresponding expression may be
// used on the lhs of a comma-ok assignment.
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// An Initializer describes a package-level variable, or a list of variables in case
// of a multi-valued initialization expression, and the corresponding initialization
// expression.
type Initializer struct {
	Lhs []*Var // var Lhs = Rhs
	Rhs ast.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	WriteExpr(&buf, init.Rhs)
	return buf.String()
}

// Check type-checks a package and returns the resulting package object,
// the first error if any, and if info != nil, additional type information.
// The package is marked as complete if no errors occurred, otherwise it is
// incomplete. See Config.Error for controlling behavior in the presense of
// errors.
//
// The package is specified by a list of *ast.Files and corresponding
// file set, and the package path the package is identified with.
// The clean path must not be empty or dot (".").
func (conf *Config) Check(path string, fset *token.FileSet, files []*ast.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, fset, pkg, info).Files(files)
}

// AssertableTo reports whether a value of type V can be asserted to have type T.
func AssertableTo(V *Interface, T Type) bool {
	m, _ := assertableTo(V, T)
	return m == nil
}

// AssignableTo reports whether a value of type V is assignable to a variable of type T.
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.assignableTo(nil, T) // config not needed for non-constant x
}

// ConvertibleTo reports whether a value of type V is convertible to a value of type T.
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T) // config not needed for non-constant x
}

// Implements reports whether type V implements interface T.
func Implements(V Type, T *Interface) bool {
	f, _ := MissingMethod(V, T, true)
	return f == nil
}
<|endoftext|>


<commit_before>package command

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type CurrencyQuery struct {
	XMLName xml.Name `xml:"query"`
	Rates   []struct {
		ID   string  `xml:"id,attr"`
		Rate float64 `xml:"Rate"`
	} `xml:"results>rate"`
}

type rateCache struct {
	rate      float64
	cached_at time.Time
}

var (
	command_re *regexp.Regexp       = regexp.MustCompile("(\\d+(?:\\.\\d+)?)\\s*(\\w{3})(\\s*=\\s*[?]?\\s*(\\w{3}))?")
	rate_cache map[string]rateCache = map[string]rateCache{}
)

func CurrencyCommand(req Request) *Response {
	ret := new(Response)
	ret.ResponseType = ephemeral

	matched := command_re.FindStringSubmatch(strings.TrimSpace(req.Text))
	if matched == nil {
		ret.Text = fmt.Sprintf("Format error. You typed \"%s\"", req.Text)
		return ret
	}

	original_value, _ := strconv.ParseFloat(matched[1], 64)
	if matched[3] == "" {
		matched[4] = "KRW"
	}
	key := fmt.Sprintf("%s%s", matched[2], matched[4])

	rate := 0.0
	if cached, ok := rate_cache[key]; ok {
		if time.Now().Sub(cached.cached_at).Minutes() < 10 {
			rate = cached.rate
		}
	}

	if rate == 0.0 {
		url := fmt.Sprintf("http://query.yahooapis.com/v1/public/yql?q=%s&env=%s",
			strings.Replace(
				url.QueryEscape(
					fmt.Sprintf("select * from yahoo.finance.xchange where pair in (\"%s\")", key)),
				"+", "%20", -1),
			url.QueryEscape("store://datatables.org/alltableswithkeys"))

		resp, err := http.Get(url)
		if err != nil {
			ret.Text = fmt.Sprintf("YQL error : %q", err)
			return ret
		}

		var query CurrencyQuery
		buf := new(bytes.Buffer)
		buf.ReadFrom(resp.Body)
		b := buf.Bytes()
		err = xml.Unmarshal(b, &query)
		if err != nil {
			ret.Text = fmt.Sprintf("Result parsing error : %q", err)
			return ret
		}

		for _, v := range query.Rates {
			if v.ID == key {
				rate = v.Rate
				break
			}
		}

		if rate != 0.0 {
			rate_cache[key] = rateCache{
				rate:      rate,
				cached_at: time.Now(),
			}
		}
	}

	ret.ResponseType = deffered_in_channel

	ret.Attachments = []Attachment{
		Attachment{
			Color:   Color{r: 33, g: 108, b: 42},
			Pretext: fmt.Sprintf("Rate : %f", rate),
			Text:    fmt.Sprintf("%.2f %s = %.2f %s", original_value, matched[2], original_value*rate, matched[4]),
		},
	}

	return ret
}
<commit_msg>Make force upper...<commit_after>package command

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"net/http"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
)

type CurrencyQuery struct {
	XMLName xml.Name `xml:"query"`
	Rates   []struct {
		ID   string  `xml:"id,attr"`
		Rate float64 `xml:"Rate"`
	} `xml:"results>rate"`
}

type rateCache struct {
	rate      float64
	cached_at time.Time
}

var (
	command_re *regexp.Regexp       = regexp.MustCompile("(\\d+(?:\\.\\d+)?)\\s*(\\w{3})(\\s*=\\s*[?]?\\s*(\\w{3}))?")
	rate_cache map[string]rateCache = map[string]rateCache{}
)

func CurrencyCommand(req Request) *Response {
	ret := new(Response)
	ret.ResponseType = ephemeral

	matched := command_re.FindStringSubmatch(strings.TrimSpace(req.Text))
	if matched == nil {
		ret.Text = fmt.Sprintf("Format error. You typed \"%s\"", req.Text)
		return ret
	}

	original_value, _ := strconv.ParseFloat(matched[1], 64)
	if matched[3] == "" {
		matched[4] = "KRW"
	}
	// make upper...
	matched[2] = strings.ToUpper(matched[2])
	matched[4] = strings.ToUpper(matched[4])
	key := fmt.Sprintf("%s%s", matched[2], matched[4])

	rate := 0.0
	if cached, ok := rate_cache[key]; ok {
		if time.Now().Sub(cached.cached_at).Minutes() < 10 {
			rate = cached.rate
		}
	}

	if rate == 0.0 {
		url := fmt.Sprintf("http://query.yahooapis.com/v1/public/yql?q=%s&env=%s",
			strings.Replace(
				url.QueryEscape(
					fmt.Sprintf("select * from yahoo.finance.xchange where pair in (\"%s\")", key)),
				"+", "%20", -1),
			url.QueryEscape("store://datatables.org/alltableswithkeys"))

		resp, err := http.Get(url)
		if err != nil {
			ret.Text = fmt.Sprintf("YQL error : %q", err)
			return ret
		}

		var query CurrencyQuery
		buf := new(bytes.Buffer)
		buf.ReadFrom(resp.Body)
		b := buf.Bytes()
		err = xml.Unmarshal(b, &query)
		if err != nil {
			ret.Text = fmt.Sprintf("Result parsing error : %q", err)
			return ret
		}

		for _, v := range query.Rates {
			if v.ID == key {
				rate = v.Rate
				break
			}
		}

		if rate != 0.0 {
			rate_cache[key] = rateCache{
				rate:      rate,
				cached_at: time.Now(),
			}
		}
	}

	ret.ResponseType = deffered_in_channel

	ret.Attachments = []Attachment{
		Attachment{
			Color:   Color{r: 33, g: 108, b: 42},
			Pretext: fmt.Sprintf("Rate : %f", rate),
			Text:    fmt.Sprintf("%.2f %s = %.2f %s", original_value, matched[2], original_value*rate, matched[4]),
		},
	}

	return ret
}
<|endoftext|>


<commit_before>package command

import (
	"testing"
  "github.com/mitchellh/cli"
  "os"
	"fmt"
)

func TestCreateNewProject(t *testing.T) {
  //use the haiku run command to make a new test blog
  ui := &cli.BasicUi{Writer: os.Stdout}
  cmd := NewCommand {
    Name: "new",
    Ui:   ui,
  }

  args := []string{
    "runtest",
  }
  cmd.Run(args)
  //test to see if these directories exist
  // runtest/config
  // runtest/content
  // runtest/style

  currentDir, err := os.Getwd()
  if err != nil {
    t.Error(err)
  }
  expectedDirs := []string {
    fmt.Sprintf("%s/runtest/config",  currentDir ),
    fmt.Sprintf("%s/runtest/content",  currentDir ),
    fmt.Sprintf("%s/runtest/style",  currentDir ),
  }

  for _, expectedDir := range expectedDirs {
    if fileInfo, err := os.Lstat(expectedDir); err == nil {
      if !fileInfo.IsDir() {
        t.Fail()
      }
    } else {
       t.Error(err)
    }
  }

  //cleanup by removeing all the directories we created (hopefully)
  for _, dir := range expectedDirs {
    if err := os.RemoveAll(dir); err != nil {
      t.Error(err)
    }
  }
}
<commit_msg>proper cleanup of dirs<commit_after>package command

import (
	"testing"
  "github.com/mitchellh/cli"
  "os"
	"fmt"
)

func TestCreateNewProject(t *testing.T) {
  //use the haiku run command to make a new test blog
  ui := &cli.BasicUi{Writer: os.Stdout}
  cmd := NewCommand {
    Name: "new",
    Ui:   ui,
  }

  args := []string{
    "runtest",
  }
  cmd.Run(args)
  //test to see if these directories exist
  // runtest/config
  // runtest/content
  // runtest/style

  currentDir, err := os.Getwd()
  if err != nil {
    t.Error(err)
  }
  expectedDirs := []string {
    fmt.Sprintf("%s/runtest/config",  currentDir ),
    fmt.Sprintf("%s/runtest/content",  currentDir ),
    fmt.Sprintf("%s/runtest/style",  currentDir ),
  }

  for _, expectedDir := range expectedDirs {
    if fileInfo, err := os.Lstat(expectedDir); err == nil {
      if !fileInfo.IsDir() {
        t.Fail()
      }
    } else {
       t.Error(err)
    }
  }

  //cleanup by removeing all the directories we created (hopefully)
  dir := fmt.Sprintf("%s/runtest",  currentDir )
  if err := os.RemoveAll(dir); err != nil {
    t.Error(err)
  }
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"

	"github.com/github/hub/github"
	"github.com/github/hub/utils"
)

var cmdCompare = &Command{
	Run:   compare,
	Usage: "compare [-u] [<USER>] [[<START>...]<END>]",
	Long: `Open a GitHub compare page in a web browser.

## Options:
	-u
		Print the URL instead of opening it.

	[<START>...]<END>
		Branch names, tag names, or commit SHAs specifying the range to compare.
		<END> defaults to the current branch name.

		If a range with two dots ('A..B') is given, it will be transformed into a
		range with three dots.

## Examples:
		$ hub compare refactor
		> open https://github.com/USER/REPO/compare/refactor

		$ hub compare v1.0..v1.1
		> open https://github.com/USER/REPO/compare/v1.0...v1.1

		$ hub compare -u jingweno feature
		> echo https://github.com/jingweno/REPO/compare/feature

## See also:

hub-browse(1), hub(1)
`,
}

var (
	flagCompareURLOnly bool
	flagCompareBase    string
)

func init() {
	cmdCompare.Flag.BoolVarP(&flagCompareURLOnly, "url-only", "u", false, "URL only")
	cmdCompare.Flag.StringVarP(&flagCompareBase, "base", "b", "", "BASE")

	CmdRunner.Use(cmdCompare)
}

func compare(command *Command, args *Args) {
	localRepo, err := github.LocalRepo()
	utils.Check(err)

	var (
		branch  *github.Branch
		project *github.Project
		r       string
	)

	branch, project, err = localRepo.RemoteBranchAndProject("", false)
	utils.Check(err)

	usageHelp := func() {
		utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"))
	}

	if args.IsParamsEmpty() {
		if branch == nil ||
			(branch.IsMaster() && flagCompareBase == "") ||
			(flagCompareBase == branch.ShortName()) {

			usageHelp()
		} else {
			r = branch.ShortName()
			if flagCompareBase != "" {
				r = parseCompareRange(flagCompareBase + "..." + r)
			}
		}
	} else {
		if flagCompareBase != "" {
			usageHelp()
		} else {
			r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
			project, err = localRepo.CurrentProject()
			if args.IsParamsEmpty() {
				utils.Check(err)
			} else {
				projectName := ""
				if err == nil {
					projectName = project.Name
				}
				project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), projectName, "")
				if project.Name == "" {
					utils.Check(fmt.Errorf("error: missing project name (owner: %q)\n", project.Owner))
				}
			}
		}
	}

	if project == nil {
		project, err = localRepo.CurrentProject()
		utils.Check(err)
	}

	subpage := utils.ConcatPaths("compare", rangeQueryEscape(r))
	url := project.WebURL("", "", subpage)

	args.NoForward()
	printBrowseOrCopy(args, url, !flagCompareURLOnly, false)
}

func parseCompareRange(r string) string {
	shaOrTag := fmt.Sprintf("((?:%s:)?\\w[\\w.-]+\\w)", OwnerRe)
	shaOrTagRange := fmt.Sprintf("^%s\\.\\.%s$", shaOrTag, shaOrTag)
	shaOrTagRangeRegexp := regexp.MustCompile(shaOrTagRange)
	return shaOrTagRangeRegexp.ReplaceAllString(r, "$1...$2")
}

// characters we want to allow unencoded in compare views
var compareUnescaper = strings.NewReplacer(
	"%2F", "/",
	"%3A", ":",
	"%5E", "^",
	"%7E", "~",
	"%2A", "*",
	"%21", "!",
)

func rangeQueryEscape(r string) string {
	if strings.Contains(r, "..") {
		return r
	} else {
		return compareUnescaper.Replace(url.QueryEscape(r))
	}
}
<commit_msg>document -b option for compare command<commit_after>package commands

import (
	"fmt"
	"net/url"
	"regexp"
	"strings"

	"github.com/github/hub/github"
	"github.com/github/hub/utils"
)

var cmdCompare = &Command{
	Run:   compare,
	Usage: "compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]",
	Long: `Open a GitHub compare page in a web browser.

## Options:
	-u
		Print the URL instead of opening it.

	-b <BASE>
		Base branch to compare.

	[<START>...]<END>
		Branch names, tag names, or commit SHAs specifying the range to compare.
		<END> defaults to the current branch name.

		If a range with two dots ('A..B') is given, it will be transformed into a
		range with three dots.

## Examples:
		$ hub compare refactor
		> open https://github.com/USER/REPO/compare/refactor

		$ hub compare v1.0..v1.1
		> open https://github.com/USER/REPO/compare/v1.0...v1.1

		$ hub compare -u jingweno feature
		> echo https://github.com/jingweno/REPO/compare/feature

## See also:

hub-browse(1), hub(1)
`,
}

var (
	flagCompareURLOnly bool
	flagCompareBase    string
)

func init() {
	cmdCompare.Flag.BoolVarP(&flagCompareURLOnly, "url-only", "u", false, "URL only")
	cmdCompare.Flag.StringVarP(&flagCompareBase, "base", "b", "", "BASE")

	CmdRunner.Use(cmdCompare)
}

func compare(command *Command, args *Args) {
	localRepo, err := github.LocalRepo()
	utils.Check(err)

	var (
		branch  *github.Branch
		project *github.Project
		r       string
	)

	branch, project, err = localRepo.RemoteBranchAndProject("", false)
	utils.Check(err)

	usageHelp := func() {
		utils.Check(fmt.Errorf("Usage: hub compare [-u] [-b <BASE>] [<USER>] [[<START>...]<END>]"))
	}

	if args.IsParamsEmpty() {
		if branch == nil ||
			(branch.IsMaster() && flagCompareBase == "") ||
			(flagCompareBase == branch.ShortName()) {

			usageHelp()
		} else {
			r = branch.ShortName()
			if flagCompareBase != "" {
				r = parseCompareRange(flagCompareBase + "..." + r)
			}
		}
	} else {
		if flagCompareBase != "" {
			usageHelp()
		} else {
			r = parseCompareRange(args.RemoveParam(args.ParamsSize() - 1))
			project, err = localRepo.CurrentProject()
			if args.IsParamsEmpty() {
				utils.Check(err)
			} else {
				projectName := ""
				if err == nil {
					projectName = project.Name
				}
				project = github.NewProject(args.RemoveParam(args.ParamsSize()-1), projectName, "")
				if project.Name == "" {
					utils.Check(fmt.Errorf("error: missing project name (owner: %q)\n", project.Owner))
				}
			}
		}
	}

	if project == nil {
		project, err = localRepo.CurrentProject()
		utils.Check(err)
	}

	subpage := utils.ConcatPaths("compare", rangeQueryEscape(r))
	url := project.WebURL("", "", subpage)

	args.NoForward()
	printBrowseOrCopy(args, url, !flagCompareURLOnly, false)
}

func parseCompareRange(r string) string {
	shaOrTag := fmt.Sprintf("((?:%s:)?\\w[\\w.-]+\\w)", OwnerRe)
	shaOrTagRange := fmt.Sprintf("^%s\\.\\.%s$", shaOrTag, shaOrTag)
	shaOrTagRangeRegexp := regexp.MustCompile(shaOrTagRange)
	return shaOrTagRangeRegexp.ReplaceAllString(r, "$1...$2")
}

// characters we want to allow unencoded in compare views
var compareUnescaper = strings.NewReplacer(
	"%2F", "/",
	"%3A", ":",
	"%5E", "^",
	"%7E", "~",
	"%2A", "*",
	"%21", "!",
)

func rangeQueryEscape(r string) string {
	if strings.Contains(r, "..") {
		return r
	} else {
		return compareUnescaper.Replace(url.QueryEscape(r))
	}
}
<|endoftext|>


<commit_before>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var CfgFile string

var RootCmd = &cobra.Command{
	Use:   "dagobah",
	Short: "Dagobah is an awesome planet style RSS aggregator",
	Long: `Dagobah provides planet style RSS aggregation. It
is inspired by python planet. It has a simple YAML configuration
and provides it's own webserver.`,
	Run: rootRun,
}

func rootRun(cmd *cobra.Command, args []string) {
	fmt.Println(viper.Get("feeds"))
	fmt.Println(viper.GetString("appname"))
}

func init() {
	cobra.InitializeConfig = initConfig
	RootCmd.PersistentFlags().StringVar(&CfgFile, "config", "", "config file (default is $HOME/dagobah/config.yaml)")
	RootCmd.PersistentFlags().StringP("dbname", "d", "dagobah", "name of the database")
	RootCmd.PersistentFlags().Int("dbport", 27017, "port to access mongoDB")
	RootCmd.PersistentFlags().String("dbhost", "localhost", "host where mongoDB is")
	RootCmd.PersistentFlags().String("dbusername", "", "username to connect to mongoDB with")
	RootCmd.PersistentFlags().String("dbpassword", "", "password to connect to mongoDB with")

	viper.BindPFlag("dbusername", RootCmd.PersistentFlags().Lookup("dbusername"))
	viper.BindPFlag("dbpassword", RootCmd.PersistentFlags().Lookup("dbpassword"))
	viper.BindPFlag("dbhost", RootCmd.PersistentFlags().Lookup("dbhost"))
	viper.BindPFlag("dbport", RootCmd.PersistentFlags().Lookup("dbport"))
	viper.BindPFlag("dbname", RootCmd.PersistentFlags().Lookup("dbname"))
}

func initConfig() {
	if CfgFile != "" {
		viper.SetConfigFile(CfgFile)
	}
	viper.SetConfigName("config")          // name of config file (without extension)
	viper.AddConfigPath("/etc/dagobah/")   // path to look for the config file in
	viper.AddConfigPath("$HOME/.dagobah/") // call multiple times to add many search paths
	viper.ReadInConfig()

	viper.SetDefault("feeds", []map[string]string{{"Name": "Hacking Management", "Url": "url:http://spf13.com/index.xml"}})
}

func addCommands() {
	RootCmd.AddCommand(fetchCmd)
	RootCmd.AddCommand(serverCmd)
}

func Execute() {
	addCommands()
	err := RootCmd.Execute()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	select {}
}
<commit_msg>Fixing typo in default url<commit_after>// Copyright © 2014 Steve Francia <spf@spf13.com>.
//
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package commands

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
)

var CfgFile string

var RootCmd = &cobra.Command{
	Use:   "dagobah",
	Short: "Dagobah is an awesome planet style RSS aggregator",
	Long: `Dagobah provides planet style RSS aggregation. It
is inspired by python planet. It has a simple YAML configuration
and provides it's own webserver.`,
	Run: rootRun,
}

func rootRun(cmd *cobra.Command, args []string) {
	fmt.Println(viper.Get("feeds"))
	fmt.Println(viper.GetString("appname"))
}

func init() {
	cobra.InitializeConfig = initConfig
	RootCmd.PersistentFlags().StringVar(&CfgFile, "config", "", "config file (default is $HOME/dagobah/config.yaml)")
	RootCmd.PersistentFlags().StringP("dbname", "d", "dagobah", "name of the database")
	RootCmd.PersistentFlags().Int("dbport", 27017, "port to access mongoDB")
	RootCmd.PersistentFlags().String("dbhost", "localhost", "host where mongoDB is")
	RootCmd.PersistentFlags().String("dbusername", "", "username to connect to mongoDB with")
	RootCmd.PersistentFlags().String("dbpassword", "", "password to connect to mongoDB with")

	viper.BindPFlag("dbusername", RootCmd.PersistentFlags().Lookup("dbusername"))
	viper.BindPFlag("dbpassword", RootCmd.PersistentFlags().Lookup("dbpassword"))
	viper.BindPFlag("dbhost", RootCmd.PersistentFlags().Lookup("dbhost"))
	viper.BindPFlag("dbport", RootCmd.PersistentFlags().Lookup("dbport"))
	viper.BindPFlag("dbname", RootCmd.PersistentFlags().Lookup("dbname"))
}

func initConfig() {
	if CfgFile != "" {
		viper.SetConfigFile(CfgFile)
	}
	viper.SetConfigName("config")          // name of config file (without extension)
	viper.AddConfigPath("/etc/dagobah/")   // path to look for the config file in
	viper.AddConfigPath("$HOME/.dagobah/") // call multiple times to add many search paths
	viper.ReadInConfig()

	viper.SetDefault("feeds", []map[string]string{{"Name": "Hacking Management", "Url": "http://spf13.com/index.xml"}})
}

func addCommands() {
	RootCmd.AddCommand(fetchCmd)
	RootCmd.AddCommand(serverCmd)
}

func Execute() {
	addCommands()
	err := RootCmd.Execute()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	select {}
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/steps"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/processors/env"
	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/display"
)

var (

	// DestroyCmd ...
	DestroyCmd = &cobra.Command{
		Use:   "destroy",
		Short: "Destroy the current project and remove it from Nanobox.",
		Long: `
Destroys the current project and removes it from Nanobox – destroying
the filesystem mount, associated dns aliases, and local app data.
		`,
		PreRun: steps.Run("start"),
		Run:    destroyFunc,
	}
)

// destroyFunc ...
func destroyFunc(ccmd *cobra.Command, args []string) {
	envModel, err := models.FindEnvByID(config.EnvID())
	if err != nil {
		fmt.Println("This project doesn't exist on nanobox.")
		return
	}
	display.CommandErr(env.Destroy(envModel))
}
<commit_msg>allow destruction to happen on a per app basis if desired fixes #375<commit_after>package commands

import (
	"fmt"
	"github.com/spf13/cobra"

	"github.com/nanobox-io/nanobox/commands/steps"
	"github.com/nanobox-io/nanobox/models"
	"github.com/nanobox-io/nanobox/helpers"
	"github.com/nanobox-io/nanobox/processors/env"
	"github.com/nanobox-io/nanobox/processors/app"
	"github.com/nanobox-io/nanobox/util/config"
	"github.com/nanobox-io/nanobox/util/display"
)

var (

	// DestroyCmd ...
	DestroyCmd = &cobra.Command{
		Use:   "destroy",
		Short: "Destroy the current project and remove it from Nanobox.",
		Long: `
Destroys the current project and removes it from Nanobox – destroying
the filesystem mount, associated dns aliases, and local app data.
		`,
		PreRun: steps.Run("start"),
		Run:    destroyFunc,
	}
)

// destroyFunc ...
func destroyFunc(ccmd *cobra.Command, args []string) {
	envModel, err := models.FindEnvByID(config.EnvID())
	if err != nil {
		fmt.Println("This project doesn't exist on nanobox.")
		return
	}

	if len(args) == 0 {
		display.CommandErr(env.Destroy(envModel))
	}

	_, _, name := helpers.Endpoint(envModel, args, 2)
	appModel, err := models.FindAppBySlug(envModel.ID, name)
	if err != nil {
		fmt.Println("Could not find the application")
	}

	display.CommandErr(app.Destroy(appModel))

}
<|endoftext|>


<commit_before>package commands

import (
	. "fmt"
)

const Version = "0.5.1"

type VersionCmdOpts struct { }

//Version command
func (opts *VersionCmdOpts) Execute(args []string) error {
	Println("hroot version", Version)
	return nil
}
<commit_msg>Mark version 0.5.2<commit_after>package commands

import (
	. "fmt"
)

const Version = "0.5.2"

type VersionCmdOpts struct { }

//Version command
func (opts *VersionCmdOpts) Execute(args []string) error {
	Println("hroot version", Version)
	return nil
}
<|endoftext|>