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

package namespace

import (
	"github.com/astaxie/beego"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/autoscaler"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/deploy"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/deployclusterapplication"
	"github.com/cloudawan/cloudone_gui/controllers/identity"
	"github.com/cloudawan/cloudone_gui/controllers/notification/notifier"
	"github.com/cloudawan/cloudone_gui/controllers/utility/configuration"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/restclient"
	"strconv"
	"time"
)

type DeleteController struct {
	beego.Controller
}

func (c *DeleteController) Get() {
	guimessage := guimessagedisplay.GetGUIMessage(c)

	name := c.GetString("name")

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")
	kubeapiHost, kubeapiPort, err := configuration.GetAvailableKubeapiHostAndPort()
	if err != nil {
		// Error
		errorJsonMap := make(map[string]interface{})
		errorJsonMap["error"] = err.Error()
		c.Data["json"] = errorJsonMap
		c.ServeJSON()
		return
	}

	// Delete deploy
	url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/deploys/" + name

	deployInformationSlice := make([]deploy.DeployInformation, 0)

	tokenHeaderMap, _ := c.GetSession("tokenHeaderMap").(map[string]string)

	_, err = restclient.RequestGetWithStructure(url, &deployInformationSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, deployInformation := range deployInformationSlice {
			url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
				"/api/v1/deploys/" + name + "/" + deployInformation.ImageInformationName + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

			_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

			if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
				return
			}

			if err != nil {
				guimessage.AddDanger(err.Error())
			}
		}
	}

	// Delete third party service
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/deployclusterapplications/" + name

	deployClusterApplicationSlice := make([]deployclusterapplication.DeployClusterApplication, 0)
	_, err = restclient.RequestGetWithStructure(url, &deployClusterApplicationSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, deployClusterApplication := range deployClusterApplicationSlice {
			url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
				"/api/v1/deployclusterapplications/" + name + "/" + deployClusterApplication.Name + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

			_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

			if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
				return
			}

			if err != nil {
				guimessage.AddDanger(err.Error())
			}
		}
	}

	// Delete namespace
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/namespaces/" + name + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

	_, err = restclient.RequestDelete(url, nil, tokenHeaderMap, true)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		guimessage.AddSuccess("Namespace " + name + " is deleted")

		selectedNamespace := c.GetSession("namespace")
		if selectedNamespace.(string) == name {
			c.SetSession("namespace", "default")
		}
	}

	// Delete autoscaler
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/autoscalers/"

	replicationControllerAutoScalerSlice := make([]autoscaler.ReplicationControllerAutoScaler, 0)
	_, err = restclient.RequestGetWithStructure(url, &replicationControllerAutoScalerSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, replicationControllerAutoScaler := range replicationControllerAutoScalerSlice {
			if replicationControllerAutoScaler.Namespace == name {
				url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
					"/api/v1/autoscalers/" + name + "/" + replicationControllerAutoScaler.Kind + "/" + replicationControllerAutoScaler.Name

				_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

				if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
					return
				}

				if err != nil {
					guimessage.AddDanger(err.Error())
				}
			}
		}
	}

	// Delete notifier
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/notifiers/"

	replicationControllerNotifierSlice := make([]notifier.ReplicationControllerNotifier, 0)
	_, err = restclient.RequestGetWithStructure(url, &replicationControllerNotifierSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, replicationControllerNotifier := range replicationControllerNotifierSlice {
			if replicationControllerNotifier.Namespace == name {
				url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
					"/api/v1/notifiers/" + name + "/" + replicationControllerNotifier.Kind + "/" + replicationControllerNotifier.Name

				_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

				if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
					return
				}

				if err != nil {
					guimessage.AddDanger(err.Error())
				}
			}
		}
	}

	// Since the name deletion is asynchronized, wait for some time
	time.Sleep(time.Millisecond * 500)

	// Redirect to list
	c.Ctx.Redirect(302, "/gui/system/namespace/list")

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

package namespace

import (
	"github.com/astaxie/beego"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/autoscaler"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/deploy"
	"github.com/cloudawan/cloudone_gui/controllers/deploy/deployclusterapplication"
	"github.com/cloudawan/cloudone_gui/controllers/identity"
	"github.com/cloudawan/cloudone_gui/controllers/notification/notifier"
	"github.com/cloudawan/cloudone_gui/controllers/utility/configuration"
	"github.com/cloudawan/cloudone_gui/controllers/utility/guimessagedisplay"
	"github.com/cloudawan/cloudone_utility/restclient"
	"strconv"
	"time"
)

type DeleteController struct {
	beego.Controller
}

func (c *DeleteController) Get() {
	guimessage := guimessagedisplay.GetGUIMessage(c)

	name := c.GetString("name")

	cloudoneProtocol := beego.AppConfig.String("cloudoneProtocol")
	cloudoneHost := beego.AppConfig.String("cloudoneHost")
	cloudonePort := beego.AppConfig.String("cloudonePort")
	kubeapiHost, kubeapiPort, err := configuration.GetAvailableKubeapiHostAndPort()
	if err != nil {
		// Error
		c.Ctx.Redirect(302, "/gui/system/namespace/list")
		guimessage.AddDanger(err.Error())
		guimessage.RedirectMessage(c)
		return
	}

	// Delete deploy
	url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/deploys/" + name

	deployInformationSlice := make([]deploy.DeployInformation, 0)

	tokenHeaderMap, _ := c.GetSession("tokenHeaderMap").(map[string]string)

	_, err = restclient.RequestGetWithStructure(url, &deployInformationSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, deployInformation := range deployInformationSlice {
			url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
				"/api/v1/deploys/" + name + "/" + deployInformation.ImageInformationName + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

			_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

			if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
				return
			}

			if err != nil {
				guimessage.AddDanger(err.Error())
			}
		}
	}

	// Delete third party service
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/deployclusterapplications/" + name

	deployClusterApplicationSlice := make([]deployclusterapplication.DeployClusterApplication, 0)
	_, err = restclient.RequestGetWithStructure(url, &deployClusterApplicationSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, deployClusterApplication := range deployClusterApplicationSlice {
			url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
				"/api/v1/deployclusterapplications/" + name + "/" + deployClusterApplication.Name + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

			_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

			if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
				return
			}

			if err != nil {
				guimessage.AddDanger(err.Error())
			}
		}
	}

	// Delete namespace
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/namespaces/" + name + "?kubeapihost=" + kubeapiHost + "&kubeapiport=" + strconv.Itoa(kubeapiPort)

	_, err = restclient.RequestDelete(url, nil, tokenHeaderMap, true)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		guimessage.AddSuccess("Namespace " + name + " is deleted")

		selectedNamespace := c.GetSession("namespace")
		if selectedNamespace.(string) == name {
			c.SetSession("namespace", "default")
		}
	}

	// Delete autoscaler
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/autoscalers/"

	replicationControllerAutoScalerSlice := make([]autoscaler.ReplicationControllerAutoScaler, 0)
	_, err = restclient.RequestGetWithStructure(url, &replicationControllerAutoScalerSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, replicationControllerAutoScaler := range replicationControllerAutoScalerSlice {
			if replicationControllerAutoScaler.Namespace == name {
				url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
					"/api/v1/autoscalers/" + name + "/" + replicationControllerAutoScaler.Kind + "/" + replicationControllerAutoScaler.Name

				_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

				if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
					return
				}

				if err != nil {
					guimessage.AddDanger(err.Error())
				}
			}
		}
	}

	// Delete notifier
	url = cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
		"/api/v1/notifiers/"

	replicationControllerNotifierSlice := make([]notifier.ReplicationControllerNotifier, 0)
	_, err = restclient.RequestGetWithStructure(url, &replicationControllerNotifierSlice, tokenHeaderMap)

	if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
		return
	}

	if err != nil {
		// Error
		guimessage.AddDanger(err.Error())
	} else {
		for _, replicationControllerNotifier := range replicationControllerNotifierSlice {
			if replicationControllerNotifier.Namespace == name {
				url := cloudoneProtocol + "://" + cloudoneHost + ":" + cloudonePort +
					"/api/v1/notifiers/" + name + "/" + replicationControllerNotifier.Kind + "/" + replicationControllerNotifier.Name

				_, err := restclient.RequestDelete(url, nil, tokenHeaderMap, true)

				if identity.IsTokenInvalidAndRedirect(c, c.Ctx, err) {
					return
				}

				if err != nil {
					guimessage.AddDanger(err.Error())
				}
			}
		}
	}

	// Since the name deletion is asynchronized, wait for some time
	time.Sleep(time.Millisecond * 500)

	// Redirect to list
	c.Ctx.Redirect(302, "/gui/system/namespace/list")

	guimessage.RedirectMessage(c)
}
<|endoftext|>


<commit_before>package subsonic

import (
	"encoding/xml"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"

	"github.com/gorilla/context"
	"github.com/unrolled/render"
)

// MusicDirectoryContainer contains a list of emulated Subsonic music folders
type MusicDirectoryContainer struct {
	// Container name
	XMLName xml.Name `xml:"directory,omitempty"`

	// Attributes
	ID   string `xml:"id,attr"`
	Name string `xml:"name,attr"`

	Children []Child `xml:"child"`
}

type Child struct {
	// Container name
	XMLName xml.Name `xml:"child,omitempty"`

	// Attributes
	ID       string `xml:"id,attr"`
	Title    string `xml:"title,attr"`
	Album    string `xml:"album,attr"`
	Artist   string `xml:"artist,attr"`
	IsDir    bool   `xml:"isDir,attr"`
	CoverArt int    `xml:"coverArt,attr"`
	Created  string `xml:"created,attr"`
}

// GetMusicDirectory is used in Subsonic to return a list of filesystem items
// contained in a directory, including songs, folders, etc.
func GetMusicDirectory(res http.ResponseWriter, req *http.Request) {
	// Retrieve render
	r := context.Get(req, api.CtxRender).(*render.Render)

	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		r.XML(res, 200, ErrMissingParameter)
		return
	}

	// Parse prefix and ID in form prefix_id
	pair := strings.Split(pID, "_")
	if len(pair) < 2 {
		r.XML(res, 200, ErrMissingParameter)
		return
	}

	// Parse ID as integer
	prefix := pair[0]
	id, err := strconv.Atoi(pair[1])
	if err != nil {
		log.Println(err)
		r.XML(res, 200, ErrGeneric)
		return
	}

	// Title to relay to clients
	var outTitle string

	// Create list of children to relay to clients
	children := make([]Child, 0)

	// If an artist was passed, Subsonic probably wants albums
	if prefix == "artist" {
		// Load artist to get information
		artist := &data.Artist{ID: id}
		if err := artist.Load(); err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Use artist information for output
		outTitle = artist.Title

		// Load albums using artist ID
		albums, err := data.DB.AlbumsForArtist(id)
		if err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Add albums to children
		for _, a := range albums {
			children = append(children, Child{
				ID:     "album_" + strconv.Itoa(a.ID),
				Title:  a.Title,
				Album:  a.Title,
				Artist: a.Artist,
				IsDir:  true,
				//CoverArt: a.ArtID,
				//Created: time.Unix(a.LastModified, 0).Format("2006-01-02T15:04:05"),
			})
		}
	}

	// If an album was passed, Subsonic probably wants songs
	if prefix == "album" {
		// Load album to get information
		album := &data.Album{ID: id}
		if err := album.Load(); err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Use album information for output
		outTitle = album.Title

		// Load all contained songs for this album
		songs, err := data.DB.SongsForAlbum(id)
		if err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Iterate songs and add to children
		for _, s := range songs {
			children = append(children, Child{
				ID:       strconv.Itoa(s.ID),
				Title:    s.Title,
				Album:    s.Album,
				Artist:   s.Artist,
				IsDir:    false,
				CoverArt: s.ArtID,
				Created:  subTime(s.LastModified),
			})
		}
	}

	// Create a new response container
	c := newContainer()
	c.MusicDirectory = &MusicDirectoryContainer{
		ID:       pID,
		Name:     outTitle,
		Children: children,
	}

	// Write response
	r.XML(res, 200, c)
}
<commit_msg>subsonic/getMusicDirectory: add header comment on Child<commit_after>package subsonic

import (
	"encoding/xml"
	"log"
	"net/http"
	"strconv"
	"strings"

	"github.com/mdlayher/wavepipe/api"
	"github.com/mdlayher/wavepipe/data"

	"github.com/gorilla/context"
	"github.com/unrolled/render"
)

// MusicDirectoryContainer contains a list of emulated Subsonic music folders
type MusicDirectoryContainer struct {
	// Container name
	XMLName xml.Name `xml:"directory,omitempty"`

	// Attributes
	ID   string `xml:"id,attr"`
	Name string `xml:"name,attr"`

	Children []Child `xml:"child"`
}

// Child is any item displayed to Subsonic when browsing using getMusicDirectory
type Child struct {
	// Container name
	XMLName xml.Name `xml:"child,omitempty"`

	// Attributes
	ID       string `xml:"id,attr"`
	Title    string `xml:"title,attr"`
	Album    string `xml:"album,attr"`
	Artist   string `xml:"artist,attr"`
	IsDir    bool   `xml:"isDir,attr"`
	CoverArt int    `xml:"coverArt,attr"`
	Created  string `xml:"created,attr"`
}

// GetMusicDirectory is used in Subsonic to return a list of filesystem items
// contained in a directory, including songs, folders, etc.
func GetMusicDirectory(res http.ResponseWriter, req *http.Request) {
	// Retrieve render
	r := context.Get(req, api.CtxRender).(*render.Render)

	// Fetch ID parameter
	pID := req.URL.Query().Get("id")
	if pID == "" {
		r.XML(res, 200, ErrMissingParameter)
		return
	}

	// Parse prefix and ID in form prefix_id
	pair := strings.Split(pID, "_")
	if len(pair) < 2 {
		r.XML(res, 200, ErrMissingParameter)
		return
	}

	// Parse ID as integer
	prefix := pair[0]
	id, err := strconv.Atoi(pair[1])
	if err != nil {
		log.Println(err)
		r.XML(res, 200, ErrGeneric)
		return
	}

	// Title to relay to clients
	var outTitle string

	// Create list of children to relay to clients
	children := make([]Child, 0)

	// If an artist was passed, Subsonic probably wants albums
	if prefix == "artist" {
		// Load artist to get information
		artist := &data.Artist{ID: id}
		if err := artist.Load(); err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Use artist information for output
		outTitle = artist.Title

		// Load albums using artist ID
		albums, err := data.DB.AlbumsForArtist(id)
		if err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Add albums to children
		for _, a := range albums {
			children = append(children, Child{
				ID:     "album_" + strconv.Itoa(a.ID),
				Title:  a.Title,
				Album:  a.Title,
				Artist: a.Artist,
				IsDir:  true,
				//CoverArt: a.ArtID,
				//Created: time.Unix(a.LastModified, 0).Format("2006-01-02T15:04:05"),
			})
		}
	}

	// If an album was passed, Subsonic probably wants songs
	if prefix == "album" {
		// Load album to get information
		album := &data.Album{ID: id}
		if err := album.Load(); err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Use album information for output
		outTitle = album.Title

		// Load all contained songs for this album
		songs, err := data.DB.SongsForAlbum(id)
		if err != nil {
			log.Println(err)
			r.XML(res, 200, ErrGeneric)
			return
		}

		// Iterate songs and add to children
		for _, s := range songs {
			children = append(children, Child{
				ID:       strconv.Itoa(s.ID),
				Title:    s.Title,
				Album:    s.Album,
				Artist:   s.Artist,
				IsDir:    false,
				CoverArt: s.ArtID,
				Created:  subTime(s.LastModified),
			})
		}
	}

	// Create a new response container
	c := newContainer()
	c.MusicDirectory = &MusicDirectoryContainer{
		ID:       pID,
		Name:     outTitle,
		Children: children,
	}

	// Write response
	r.XML(res, 200, c)
}
<|endoftext|>


<commit_before>package telegram

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"

	"github.com/everpcpc/yubari/elasticsearch"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)

var (
	page = 5
)

func onSearch(b *Bot, message *tgbotapi.Message) {
	idx := getIndex(message)
	q := message.CommandArguments()

	exists, err := elasticsearch.CheckIndexExist(b.es, idx)
	if err != nil {
		b.logger.Errorf("check es index exists error: %+v", err)
		return
	}
	if !exists {
		msg := tgbotapi.NewMessage(message.Chat.ID, "还没有启用哦")
		msg.ReplyToMessageID = message.MessageID
		b.Client.Send(msg)
	}
	res, err := elasticsearch.SearchMessage(b.es, idx, q, 0, page)
	if err != nil {
		b.logger.Errorf("es search error: %+v", err)
		return
	}

	msg := tgbotapi.NewMessage(message.Chat.ID, buildSearchResponse(res, 0))
	msg.ParseMode = tgbotapi.ModeHTML
	msg.ReplyMarkup = buildSearchResponseButton(res, 0, q)

	_, err = b.Client.Send(msg)
	if err != nil {
		b.logger.Errorf("%+v", err)
	}
}

func getQueryCacheKey(msg *tgbotapi.Message) string {
	return fmt.Sprintf("search_query_%d", msg.MessageID)
}

func getIndex(message *tgbotapi.Message) string {
	return fmt.Sprintf("%s-%d", message.Chat.Type, message.Chat.ID)
}

func buildSearchResponse(res *elasticsearch.SearchResponse, from int) string {
	total := res.Hits.Total.Value
	respond := fmt.Sprintf("%d results：\n", total)
	for i, hit := range res.Hits.Hits {
		var content string
		if len(hit.Highlight.Content) == 0 {
			content = hit.Source.Content[:15]
		} else {
			content = hit.Highlight.Content[0]
		}
		respond += fmt.Sprintf("%d. %s\n", from+i+1, hit.Source.MessageID, content)
	}
	respond += fmt.Sprintf("duration %.3fs", float64(res.Took)/1000)
	return respond
}

func buildSearchResponseButton(res *elasticsearch.SearchResponse, from int, q string) tgbotapi.InlineKeyboardMarkup {
	total := res.Hits.Total.Value
	encodedQ := base64.StdEncoding.EncodeToString([]byte(q))
	row := tgbotapi.NewInlineKeyboardRow(
		tgbotapi.NewInlineKeyboardButtonData("⬅", fmt.Sprintf("search:%d:%s", max(int64(from-5), 0), encodedQ)),
		tgbotapi.NewInlineKeyboardButtonData("➡", fmt.Sprintf("search:%d:%s", min(int64(from+5), int64(total)), encodedQ)),
	)
	return tgbotapi.NewInlineKeyboardMarkup(row)
}

func onReactionSearch(b *Bot, callbackQuery *tgbotapi.CallbackQuery) {
	token := strings.Split(callbackQuery.Data, ":")
	if len(token) != 3 {
		b.logger.Errorf("react data error: %s", callbackQuery.Data)
		return
	}

	from, err := strconv.ParseInt(token[1], 10, 0)
	if err != nil {
		b.logger.Errorf("react search from error: %s", callbackQuery.Data)
	}
	q, err := base64.StdEncoding.DecodeString(token[2])
	if err != nil {
		b.logger.Errorf("react search q error: %s", callbackQuery.Data)
		return
	}

	idx := getIndex(callbackQuery.Message)

	res, err := elasticsearch.SearchMessage(b.es, idx, string(q), int(from), page)
	if err != nil {
		b.logger.Errorf("es search error: %+v", err)
		return
	}

	msg := tgbotapi.NewEditMessageText(
		callbackQuery.Message.Chat.ID,
		callbackQuery.Message.MessageID,
		buildSearchResponse(res, int(from)),
	)
	button := buildSearchResponseButton(res, int(from), string(q))
	msg.ParseMode = tgbotapi.ModeHTML
	msg.ReplyMarkup = &button

	_, err = b.Client.Send(msg)
	if err != nil {
		b.logger.Errorf("search reaction reply error: %+v", err)
	}
}
<commit_msg>Add delete button<commit_after>package telegram

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"

	"github.com/everpcpc/yubari/elasticsearch"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
)

var (
	page = 5
)

func onSearch(b *Bot, message *tgbotapi.Message) {
	idx := getIndex(message)
	q := message.CommandArguments()

	exists, err := elasticsearch.CheckIndexExist(b.es, idx)
	if err != nil {
		b.logger.Errorf("check es index exists error: %+v", err)
		return
	}
	if !exists {
		msg := tgbotapi.NewMessage(message.Chat.ID, "还没有启用哦")
		msg.ReplyToMessageID = message.MessageID
		b.Client.Send(msg)
	}
	res, err := elasticsearch.SearchMessage(b.es, idx, q, 0, page)
	if err != nil {
		b.logger.Errorf("es search error: %+v", err)
		return
	}

	msg := tgbotapi.NewMessage(message.Chat.ID, buildSearchResponse(res, 0))
	msg.ParseMode = tgbotapi.ModeHTML
	msg.ReplyMarkup = buildSearchResponseButton(res, 0, q)

	_, err = b.Client.Send(msg)
	if err != nil {
		b.logger.Errorf("%+v", err)
	}
}

func getQueryCacheKey(msg *tgbotapi.Message) string {
	return fmt.Sprintf("search_query_%d", msg.MessageID)
}

func getIndex(message *tgbotapi.Message) string {
	return fmt.Sprintf("%s-%d", message.Chat.Type, message.Chat.ID)
}

func buildSearchResponse(res *elasticsearch.SearchResponse, from int) string {
	total := res.Hits.Total.Value
	respond := fmt.Sprintf("%d results：\n", total)
	for i, hit := range res.Hits.Hits {
		var content string
		if len(hit.Highlight.Content) == 0 {
			content = hit.Source.Content[:15]
		} else {
			content = hit.Highlight.Content[0]
		}
		respond += fmt.Sprintf("%d. %s\n", from+i+1, hit.Source.MessageID, content)
	}
	respond += fmt.Sprintf("duration %.3fs", float64(res.Took)/1000)
	return respond
}

func buildSearchResponseButton(res *elasticsearch.SearchResponse, from int, q string) tgbotapi.InlineKeyboardMarkup {
	total := res.Hits.Total.Value
	encodedQ := base64.StdEncoding.EncodeToString([]byte(q))
	row := tgbotapi.NewInlineKeyboardRow(
		tgbotapi.NewInlineKeyboardButtonData("⬅", fmt.Sprintf("search:%d:%s", max(int64(from-5), 0), encodedQ)),
		tgbotapi.NewInlineKeyboardButtonData("❎", fmt.Sprintf("search:%d:%s", -1, encodedQ)),
		tgbotapi.NewInlineKeyboardButtonData("➡", fmt.Sprintf("search:%d:%s", min(int64(from+5), int64(total)), encodedQ)),
	)
	return tgbotapi.NewInlineKeyboardMarkup(row)
}

func onReactionSearch(b *Bot, callbackQuery *tgbotapi.CallbackQuery) {
	token := strings.Split(callbackQuery.Data, ":")
	if len(token) != 3 {
		b.logger.Errorf("react data error: %s", callbackQuery.Data)
		return
	}

	from, err := strconv.ParseInt(token[1], 10, 0)
	if err != nil {
		b.logger.Errorf("react search from error: %s", callbackQuery.Data)
	}
	if from < 0 {
		delMsg := tgbotapi.DeleteMessageConfig{
			ChatID:    callbackQuery.Message.Chat.ID,
			MessageID: callbackQuery.Message.MessageID,
		}
		_, err = b.Client.DeleteMessage(delMsg)
		if err != nil {
			b.logger.Errorf("delete search error: %s", callbackQuery.Data)
		}
		return
	}

	q, err := base64.StdEncoding.DecodeString(token[2])
	if err != nil {
		b.logger.Errorf("react search q error: %s", callbackQuery.Data)
		return
	}

	idx := getIndex(callbackQuery.Message)

	res, err := elasticsearch.SearchMessage(b.es, idx, string(q), int(from), page)
	if err != nil {
		b.logger.Errorf("es search error: %+v", err)
		return
	}

	msg := tgbotapi.NewEditMessageText(
		callbackQuery.Message.Chat.ID,
		callbackQuery.Message.MessageID,
		buildSearchResponse(res, int(from)),
	)
	button := buildSearchResponseButton(res, int(from), string(q))
	msg.ParseMode = tgbotapi.ModeHTML
	msg.ReplyMarkup = &button

	_, err = b.Client.Send(msg)
	if err != nil {
		b.logger.Errorf("search reaction reply error: %+v", err)
	}
}
<|endoftext|>


<commit_before>package apiv1

import (
	"encoding/json"

	"github.com/centrifugal/centrifugo/libcentrifugo/logger"
	"github.com/centrifugal/centrifugo/libcentrifugo/node"
	"github.com/centrifugal/centrifugo/libcentrifugo/proto"
)

// APICommandFromJSON tries to extract single APICommand encoded as JSON.
func APICommandFromJSON(msg []byte) (proto.APICommand, error) {
	var cmd proto.APICommand
	err := json.Unmarshal(msg, &cmd)
	if err != nil {
		return proto.APICommand{}, err
	}
	return cmd, nil
}

var (
	arrayJSONPrefix  byte = '['
	objectJSONPrefix byte = '{'
)

// APICommandsFromJSON tries to extract slice of APICommand encoded as JSON. This function understands both
// single object and array of commands JSON looking at first byte of msg.
func APICommandsFromJSON(msg []byte) ([]proto.APICommand, error) {
	var cmds []proto.APICommand

	if len(msg) == 0 {
		return cmds, nil
	}

	firstByte := msg[0]

	switch firstByte {
	case objectJSONPrefix:
		// single command request
		command, err := APICommandFromJSON(msg)
		if err != nil {
			return nil, err
		}
		cmds = append(cmds, command)
	case arrayJSONPrefix:
		// array of commands received
		err := json.Unmarshal(msg, &cmds)
		if err != nil {
			return nil, err
		}
	default:
		return nil, proto.ErrInvalidMessage
	}
	return cmds, nil
}

// ProcessAPIData is a helper method to extract commands from []byte data in APIv1 format,
// run commands and return JSON encoded response.
func ProcessAPIData(n *node.Node, data []byte) ([]byte, error) {

	commands, err := APICommandsFromJSON(data)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, proto.ErrInvalidMessage
	}

	mr := make(proto.MultiAPIResponse, len(commands))

	for i, command := range commands {
		resp, err := APICmd(n, command)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		mr[i] = resp
	}

	jsonResp, err := json.Marshal(mr)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, proto.ErrInternalServerError
	}
	return jsonResp, nil
}

// APICmd builds API command and dispatches it into correct handler method.
func APICmd(n *node.Node, cmd proto.APICommand) (proto.Response, error) {

	var err error
	var resp proto.Response

	method := cmd.Method
	params := cmd.Params

	switch method {
	case "publish":
		var cmd proto.PublishAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = PublishCmd(n, cmd)
	case "broadcast":
		var cmd proto.BroadcastAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = BroadcastCmd(n, cmd)
	case "unsubscribe":
		var cmd proto.UnsubscribeAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = UnsubscribeCmd(n, cmd)
	case "disconnect":
		var cmd proto.DisconnectAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = DisconnectCmd(n, cmd)
	case "presence":
		var cmd proto.PresenceAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = PresenceCmd(n, cmd)
	case "history":
		var cmd proto.HistoryAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = HistoryCmd(n, cmd)
	case "channels":
		resp, err = ChannelsCmd(n)
	case "stats":
		resp, err = StatsCmd(n)
	case "node":
		resp, err = NodeCmd(n)
	default:
		return nil, proto.ErrMethodNotFound
	}
	if err != nil {
		return nil, err
	}

	resp.SetUID(cmd.UID)

	return resp, nil
}

func makeErrChan(err error) <-chan error {
	ret := make(chan error, 1)
	ret <- err
	return ret
}

// PublishCmd publishes data into channel.
func PublishCmd(n *node.Node, cmd proto.PublishAPICommand) (proto.Response, error) {
	ch := cmd.Channel
	data := cmd.Data
	client := cmd.Client
	uid := cmd.UID

	if string(ch) == "" || len(data) == 0 {
		return nil, proto.ErrInvalidMessage
	}

	resp := proto.NewAPIPublishResponse()

	chOpts, err := n.ChannelOpts(ch)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceFix})
		return resp, nil
	}

	message := proto.NewMessageWithUid(ch, data, client, nil, uid)
	if chOpts.Watch {
		byteMessage, err := json.Marshal(message)
		if err != nil {
			logger.ERROR.Println(err)
		} else {
			n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
		}
	}

	err = <-n.Publish(message, &chOpts)

	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// PublishCmdAsync publishes data into channel without waiting for response.
func PublishCmdAsync(n *node.Node, cmd proto.PublishAPICommand) <-chan error {
	ch := cmd.Channel
	data := cmd.Data
	client := cmd.Client

	if string(ch) == "" || len(data) == 0 {
		return makeErrChan(proto.ErrInvalidMessage)
	}

	chOpts, err := n.ChannelOpts(ch)
	if err != nil {
		return makeErrChan(err)
	}

	message := proto.NewMessage(ch, data, client, nil)
	if chOpts.Watch {
		byteMessage, err := json.Marshal(message)
		if err != nil {
			logger.ERROR.Println(err)
		} else {
			n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
		}
	}

	return n.Publish(message, &chOpts)
}

// BroadcastCmd publishes data into multiple channels.
func BroadcastCmd(n *node.Node, cmd proto.BroadcastAPICommand) (proto.Response, error) {

	resp := proto.NewAPIBroadcastResponse()

	channels := cmd.Channels
	data := cmd.Data
	client := cmd.Client

	if len(channels) == 0 {
		logger.ERROR.Println("channels required for broadcast")
		resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
		return resp, nil
	}

	if len(data) == 0 {
		logger.ERROR.Println("empty data")
		resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
		return resp, nil
	}

	errs := make([]<-chan error, len(channels))
	for i, ch := range channels {

		if string(ch) == "" {
			resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
			return resp, nil
		}

		chOpts, err := n.ChannelOpts(ch)
		if err != nil {
			resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceFix})
			return resp, nil
		}

		message := proto.NewMessage(ch, data, client, nil)
		if chOpts.Watch {
			byteMessage, err := json.Marshal(message)
			if err != nil {
				logger.ERROR.Println(err)
			} else {
				n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
			}
		}

		errs[i] = n.Publish(message, &chOpts)
	}

	var firstErr error
	for i := range errs {
		err := <-errs[i]
		if err != nil {
			if firstErr == nil {
				firstErr = err
			}
			logger.ERROR.Printf("Error publishing into channel %s: %v", string(channels[i]), err.Error())
		}
	}
	if firstErr != nil {
		resp.SetErr(proto.ResponseError{firstErr, proto.ErrorAdviceNone})
	}
	return resp, nil
}

// BroadcastCmdAsync publishes data into multiple channels without waiting for response.
func BroadcastCmdAsync(n *node.Node, cmd proto.BroadcastAPICommand) <-chan error {

	channels := cmd.Channels
	data := cmd.Data
	client := cmd.Client

	if len(channels) == 0 {
		logger.ERROR.Println("channels required for broadcast")
		return makeErrChan(proto.ErrInvalidMessage)
	}

	if len(data) == 0 {
		logger.ERROR.Println("empty data")
		return makeErrChan(proto.ErrInvalidMessage)
	}

	for _, ch := range channels {

		if string(ch) == "" {
			return makeErrChan(proto.ErrInvalidMessage)
		}

		chOpts, err := n.ChannelOpts(ch)
		if err != nil {
			return makeErrChan(err)
		}

		message := proto.NewMessage(ch, data, client, nil)
		if chOpts.Watch {
			byteMessage, err := json.Marshal(message)
			if err != nil {
				logger.ERROR.Println(err)
			} else {
				n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
			}
		}

		n.Publish(message, &chOpts)
	}
	return makeErrChan(nil)
}

// UnsubscribeCmd unsubscribes project's user from channel and sends
// unsubscribe control message to other nodes.
func UnsubscribeCmd(n *node.Node, cmd proto.UnsubscribeAPICommand) (proto.Response, error) {

	resp := proto.NewAPIUnsubscribeResponse()

	user := cmd.User
	channel := cmd.Channel

	err := n.Unsubscribe(user, channel)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// DisconnectCmd disconnects user by its ID and sends disconnect
// control message to other nodes so they could also disconnect this user.
func DisconnectCmd(n *node.Node, cmd proto.DisconnectAPICommand) (proto.Response, error) {

	resp := proto.NewAPIDisconnectResponse()

	user := cmd.User

	err := n.Disconnect(user, false)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// PresenceCmd returns response with presence information for channel.
func PresenceCmd(n *node.Node, cmd proto.PresenceAPICommand) (proto.Response, error) {
	channel := cmd.Channel
	body := proto.PresenceBody{
		Channel: channel,
	}
	presence, err := n.Presence(channel)
	if err != nil {
		resp := proto.NewAPIPresenceResponse(body)
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = presence
	return proto.NewAPIPresenceResponse(body), nil
}

// HistoryCmd returns response with history information for channel.
func HistoryCmd(n *node.Node, cmd proto.HistoryAPICommand) (proto.Response, error) {
	channel := cmd.Channel
	body := proto.HistoryBody{
		Channel: channel,
	}
	history, err := n.History(channel)
	if err != nil {
		resp := proto.NewAPIHistoryResponse(body)
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = history
	return proto.NewAPIHistoryResponse(body), nil
}

// ChannelsCmd returns active channels.
func ChannelsCmd(n *node.Node) (proto.Response, error) {
	body := proto.ChannelsBody{}
	channels, err := n.Channels()
	if err != nil {
		logger.ERROR.Println(err)
		resp := proto.NewAPIChannelsResponse(body)
		resp.SetErr(proto.ResponseError{proto.ErrInternalServerError, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = channels
	return proto.NewAPIChannelsResponse(body), nil
}

// StatsCmd returns active node stats.
func StatsCmd(n *node.Node) (proto.Response, error) {
	body := proto.StatsBody{}
	body.Data = n.Stats()
	return proto.NewAPIStatsResponse(body), nil
}

// NodeCmd returns simple counter metrics which update in real time for the current node only.
func NodeCmd(n *node.Node) (proto.Response, error) {
	body := proto.NodeBody{}
	body.Data = n.Node()
	return proto.NewAPINodeResponse(body), nil
}
<commit_msg>set message uid in async publish  (#205)<commit_after>package apiv1

import (
	"encoding/json"

	"github.com/centrifugal/centrifugo/libcentrifugo/logger"
	"github.com/centrifugal/centrifugo/libcentrifugo/node"
	"github.com/centrifugal/centrifugo/libcentrifugo/proto"
)

// APICommandFromJSON tries to extract single APICommand encoded as JSON.
func APICommandFromJSON(msg []byte) (proto.APICommand, error) {
	var cmd proto.APICommand
	err := json.Unmarshal(msg, &cmd)
	if err != nil {
		return proto.APICommand{}, err
	}
	return cmd, nil
}

var (
	arrayJSONPrefix  byte = '['
	objectJSONPrefix byte = '{'
)

// APICommandsFromJSON tries to extract slice of APICommand encoded as JSON. This function understands both
// single object and array of commands JSON looking at first byte of msg.
func APICommandsFromJSON(msg []byte) ([]proto.APICommand, error) {
	var cmds []proto.APICommand

	if len(msg) == 0 {
		return cmds, nil
	}

	firstByte := msg[0]

	switch firstByte {
	case objectJSONPrefix:
		// single command request
		command, err := APICommandFromJSON(msg)
		if err != nil {
			return nil, err
		}
		cmds = append(cmds, command)
	case arrayJSONPrefix:
		// array of commands received
		err := json.Unmarshal(msg, &cmds)
		if err != nil {
			return nil, err
		}
	default:
		return nil, proto.ErrInvalidMessage
	}
	return cmds, nil
}

// ProcessAPIData is a helper method to extract commands from []byte data in APIv1 format,
// run commands and return JSON encoded response.
func ProcessAPIData(n *node.Node, data []byte) ([]byte, error) {

	commands, err := APICommandsFromJSON(data)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, proto.ErrInvalidMessage
	}

	mr := make(proto.MultiAPIResponse, len(commands))

	for i, command := range commands {
		resp, err := APICmd(n, command)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		mr[i] = resp
	}

	jsonResp, err := json.Marshal(mr)
	if err != nil {
		logger.ERROR.Println(err)
		return nil, proto.ErrInternalServerError
	}
	return jsonResp, nil
}

// APICmd builds API command and dispatches it into correct handler method.
func APICmd(n *node.Node, cmd proto.APICommand) (proto.Response, error) {

	var err error
	var resp proto.Response

	method := cmd.Method
	params := cmd.Params

	switch method {
	case "publish":
		var cmd proto.PublishAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = PublishCmd(n, cmd)
	case "broadcast":
		var cmd proto.BroadcastAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = BroadcastCmd(n, cmd)
	case "unsubscribe":
		var cmd proto.UnsubscribeAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = UnsubscribeCmd(n, cmd)
	case "disconnect":
		var cmd proto.DisconnectAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = DisconnectCmd(n, cmd)
	case "presence":
		var cmd proto.PresenceAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = PresenceCmd(n, cmd)
	case "history":
		var cmd proto.HistoryAPICommand
		err = json.Unmarshal(params, &cmd)
		if err != nil {
			logger.ERROR.Println(err)
			return nil, proto.ErrInvalidMessage
		}
		resp, err = HistoryCmd(n, cmd)
	case "channels":
		resp, err = ChannelsCmd(n)
	case "stats":
		resp, err = StatsCmd(n)
	case "node":
		resp, err = NodeCmd(n)
	default:
		return nil, proto.ErrMethodNotFound
	}
	if err != nil {
		return nil, err
	}

	resp.SetUID(cmd.UID)

	return resp, nil
}

func makeErrChan(err error) <-chan error {
	ret := make(chan error, 1)
	ret <- err
	return ret
}

// PublishCmd publishes data into channel.
func PublishCmd(n *node.Node, cmd proto.PublishAPICommand) (proto.Response, error) {
	ch := cmd.Channel
	data := cmd.Data
	client := cmd.Client
	uid := cmd.UID

	if string(ch) == "" || len(data) == 0 {
		return nil, proto.ErrInvalidMessage
	}

	resp := proto.NewAPIPublishResponse()

	chOpts, err := n.ChannelOpts(ch)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceFix})
		return resp, nil
	}

	message := proto.NewMessageWithUid(ch, data, client, nil, uid)
	if chOpts.Watch {
		byteMessage, err := json.Marshal(message)
		if err != nil {
			logger.ERROR.Println(err)
		} else {
			n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
		}
	}

	err = <-n.Publish(message, &chOpts)

	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// PublishCmdAsync publishes data into channel without waiting for response.
func PublishCmdAsync(n *node.Node, cmd proto.PublishAPICommand) <-chan error {
	ch := cmd.Channel
	data := cmd.Data
	client := cmd.Client
	uid := cmd.UID

	if string(ch) == "" || len(data) == 0 {
		return makeErrChan(proto.ErrInvalidMessage)
	}

	chOpts, err := n.ChannelOpts(ch)
	if err != nil {
		return makeErrChan(err)
	}

	message := proto.NewMessageWithUid(ch, data, client, nil, uid)
	if chOpts.Watch {
		byteMessage, err := json.Marshal(message)
		if err != nil {
			logger.ERROR.Println(err)
		} else {
			n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
		}
	}

	return n.Publish(message, &chOpts)
}

// BroadcastCmd publishes data into multiple channels.
func BroadcastCmd(n *node.Node, cmd proto.BroadcastAPICommand) (proto.Response, error) {

	resp := proto.NewAPIBroadcastResponse()

	channels := cmd.Channels
	data := cmd.Data
	client := cmd.Client

	if len(channels) == 0 {
		logger.ERROR.Println("channels required for broadcast")
		resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
		return resp, nil
	}

	if len(data) == 0 {
		logger.ERROR.Println("empty data")
		resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
		return resp, nil
	}

	errs := make([]<-chan error, len(channels))
	for i, ch := range channels {

		if string(ch) == "" {
			resp.SetErr(proto.ResponseError{proto.ErrInvalidMessage, proto.ErrorAdviceFix})
			return resp, nil
		}

		chOpts, err := n.ChannelOpts(ch)
		if err != nil {
			resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceFix})
			return resp, nil
		}

		message := proto.NewMessage(ch, data, client, nil)
		if chOpts.Watch {
			byteMessage, err := json.Marshal(message)
			if err != nil {
				logger.ERROR.Println(err)
			} else {
				n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
			}
		}

		errs[i] = n.Publish(message, &chOpts)
	}

	var firstErr error
	for i := range errs {
		err := <-errs[i]
		if err != nil {
			if firstErr == nil {
				firstErr = err
			}
			logger.ERROR.Printf("Error publishing into channel %s: %v", string(channels[i]), err.Error())
		}
	}
	if firstErr != nil {
		resp.SetErr(proto.ResponseError{firstErr, proto.ErrorAdviceNone})
	}
	return resp, nil
}

// BroadcastCmdAsync publishes data into multiple channels without waiting for response.
func BroadcastCmdAsync(n *node.Node, cmd proto.BroadcastAPICommand) <-chan error {

	channels := cmd.Channels
	data := cmd.Data
	client := cmd.Client

	if len(channels) == 0 {
		logger.ERROR.Println("channels required for broadcast")
		return makeErrChan(proto.ErrInvalidMessage)
	}

	if len(data) == 0 {
		logger.ERROR.Println("empty data")
		return makeErrChan(proto.ErrInvalidMessage)
	}

	for _, ch := range channels {

		if string(ch) == "" {
			return makeErrChan(proto.ErrInvalidMessage)
		}

		chOpts, err := n.ChannelOpts(ch)
		if err != nil {
			return makeErrChan(err)
		}

		message := proto.NewMessage(ch, data, client, nil)
		if chOpts.Watch {
			byteMessage, err := json.Marshal(message)
			if err != nil {
				logger.ERROR.Println(err)
			} else {
				n.PublishAdmin(proto.NewAdminMessage("message", byteMessage))
			}
		}

		n.Publish(message, &chOpts)
	}
	return makeErrChan(nil)
}

// UnsubscribeCmd unsubscribes project's user from channel and sends
// unsubscribe control message to other nodes.
func UnsubscribeCmd(n *node.Node, cmd proto.UnsubscribeAPICommand) (proto.Response, error) {

	resp := proto.NewAPIUnsubscribeResponse()

	user := cmd.User
	channel := cmd.Channel

	err := n.Unsubscribe(user, channel)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// DisconnectCmd disconnects user by its ID and sends disconnect
// control message to other nodes so they could also disconnect this user.
func DisconnectCmd(n *node.Node, cmd proto.DisconnectAPICommand) (proto.Response, error) {

	resp := proto.NewAPIDisconnectResponse()

	user := cmd.User

	err := n.Disconnect(user, false)
	if err != nil {
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	return resp, nil
}

// PresenceCmd returns response with presence information for channel.
func PresenceCmd(n *node.Node, cmd proto.PresenceAPICommand) (proto.Response, error) {
	channel := cmd.Channel
	body := proto.PresenceBody{
		Channel: channel,
	}
	presence, err := n.Presence(channel)
	if err != nil {
		resp := proto.NewAPIPresenceResponse(body)
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = presence
	return proto.NewAPIPresenceResponse(body), nil
}

// HistoryCmd returns response with history information for channel.
func HistoryCmd(n *node.Node, cmd proto.HistoryAPICommand) (proto.Response, error) {
	channel := cmd.Channel
	body := proto.HistoryBody{
		Channel: channel,
	}
	history, err := n.History(channel)
	if err != nil {
		resp := proto.NewAPIHistoryResponse(body)
		resp.SetErr(proto.ResponseError{err, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = history
	return proto.NewAPIHistoryResponse(body), nil
}

// ChannelsCmd returns active channels.
func ChannelsCmd(n *node.Node) (proto.Response, error) {
	body := proto.ChannelsBody{}
	channels, err := n.Channels()
	if err != nil {
		logger.ERROR.Println(err)
		resp := proto.NewAPIChannelsResponse(body)
		resp.SetErr(proto.ResponseError{proto.ErrInternalServerError, proto.ErrorAdviceNone})
		return resp, nil
	}
	body.Data = channels
	return proto.NewAPIChannelsResponse(body), nil
}

// StatsCmd returns active node stats.
func StatsCmd(n *node.Node) (proto.Response, error) {
	body := proto.StatsBody{}
	body.Data = n.Stats()
	return proto.NewAPIStatsResponse(body), nil
}

// NodeCmd returns simple counter metrics which update in real time for the current node only.
func NodeCmd(n *node.Node) (proto.Response, error) {
	body := proto.NodeBody{}
	body.Data = n.Node()
	return proto.NewAPINodeResponse(body), nil
}
<|endoftext|>


<commit_before>package libgobuster

import (
	"reflect"
	"testing"
)

func TestParseExtensions(t *testing.T) {
	t.Parallel()

	var tt = []struct {
		testName           string
		Extensions         string
		expectedExtensions stringSet
		expectedError      string
	}{
		{"Valid extensions", "php,asp,txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Spaces", "php, asp , txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Double extensions", "php,asp,txt,php,asp,txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Leading dot", ".php,asp,.txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Empty string", "", stringSet{Set: map[string]bool{}}, "invalid extension string provided"},
	}

	for _, x := range tt {
		t.Run(x.testName, func(t *testing.T) {
			o := NewOptions()
			o.Extensions = x.Extensions
			err := o.parseExtensions()
			if x.expectedError != "" {
				if err.Error() != x.expectedError {
					t.Fatalf("Expected error %q but got %q", x.expectedError, err.Error())
				}
			} else if !reflect.DeepEqual(x.expectedExtensions, o.ExtensionsParsed) {
				t.Fatalf("Expected %v but got %v", x.expectedExtensions, o.ExtensionsParsed)
			}
		})
	}
}

func TestParseStatusCodes(t *testing.T) {
	t.Parallel()

	var tt = []struct {
		testName      string
		stringCodes   string
		expectedCodes intSet
		expectedError string
	}{
		{"Valid codes", "200,100,202", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Spaces", "200, 100 , 202", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Double codes", "200, 100, 202, 100", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Invalid code", "200,AAA", intSet{Set: map[int]bool{}}, "invalid status code given: AAA"},
		{"Invalid integer", "2000000000000000000000000000000", intSet{Set: map[int]bool{}}, "invalid status code given: 2000000000000000000000000000000"},
	}

	for _, x := range tt {
		t.Run(x.testName, func(t *testing.T) {
			o := NewOptions()
			o.StatusCodes = x.stringCodes
			err := o.parseStatusCodes()
			if x.expectedError != "" {
				if err.Error() != x.expectedError {
					t.Fatalf("Expected error %q but got %q", x.expectedError, err.Error())
				}
			} else if !reflect.DeepEqual(x.expectedCodes, o.StatusCodesParsed) {
				t.Fatalf("Expected %v but got %v", x.expectedCodes, o.StatusCodesParsed)
			}
		})
	}
}
<commit_msg>one more test<commit_after>package libgobuster

import (
	"reflect"
	"testing"
)

func TestNewOptions(t *testing.T) {
	t.Parallel()

	o := NewOptions()
	if o.StatusCodesParsed.Set == nil {
		t.Fatalf("StatusCodesParsed not initialized")
	}

	if o.ExtensionsParsed.Set == nil {
		t.Fatalf("ExtensionsParsed not initialized")
	}
}

func TestParseExtensions(t *testing.T) {
	t.Parallel()

	var tt = []struct {
		testName           string
		Extensions         string
		expectedExtensions stringSet
		expectedError      string
	}{
		{"Valid extensions", "php,asp,txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Spaces", "php, asp , txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Double extensions", "php,asp,txt,php,asp,txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Leading dot", ".php,asp,.txt", stringSet{Set: map[string]bool{"php": true, "asp": true, "txt": true}}, ""},
		{"Empty string", "", stringSet{Set: map[string]bool{}}, "invalid extension string provided"},
	}

	for _, x := range tt {
		t.Run(x.testName, func(t *testing.T) {
			o := NewOptions()
			o.Extensions = x.Extensions
			err := o.parseExtensions()
			if x.expectedError != "" {
				if err.Error() != x.expectedError {
					t.Fatalf("Expected error %q but got %q", x.expectedError, err.Error())
				}
			} else if !reflect.DeepEqual(x.expectedExtensions, o.ExtensionsParsed) {
				t.Fatalf("Expected %v but got %v", x.expectedExtensions, o.ExtensionsParsed)
			}
		})
	}
}

func TestParseStatusCodes(t *testing.T) {
	t.Parallel()

	var tt = []struct {
		testName      string
		stringCodes   string
		expectedCodes intSet
		expectedError string
	}{
		{"Valid codes", "200,100,202", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Spaces", "200, 100 , 202", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Double codes", "200, 100, 202, 100", intSet{Set: map[int]bool{100: true, 200: true, 202: true}}, ""},
		{"Invalid code", "200,AAA", intSet{Set: map[int]bool{}}, "invalid status code given: AAA"},
		{"Invalid integer", "2000000000000000000000000000000", intSet{Set: map[int]bool{}}, "invalid status code given: 2000000000000000000000000000000"},
		{"Empty string", "", intSet{Set: map[int]bool{}}, "invalid status code string provided"},
	}

	for _, x := range tt {
		t.Run(x.testName, func(t *testing.T) {
			o := NewOptions()
			o.StatusCodes = x.stringCodes
			err := o.parseStatusCodes()
			if x.expectedError != "" {
				if err.Error() != x.expectedError {
					t.Fatalf("Expected error %q but got %q", x.expectedError, err.Error())
				}
			} else if !reflect.DeepEqual(x.expectedCodes, o.StatusCodesParsed) {
				t.Fatalf("Expected %v but got %v", x.expectedCodes, o.StatusCodesParsed)
			}
		})
	}
}
<|endoftext|>


<commit_before>// errchk $G $D/$F.go

// Copyright 2009 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 main

type T struct { a int }
var t *T

type I interface { M() }
var i I

func main() {
	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t;	// ERROR "missing"
	t = i;	// ERROR "missing"
}
<commit_msg>Recognize gccgo error messages:<commit_after>// errchk $G $D/$F.go

// Copyright 2009 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 main

type T struct { a int }
var t *T

type I interface { M() }
var i I

func main() {
	// neither of these can work,
	// because i has an extra method
	// that t does not, so i cannot contain a t.
	i = t;	// ERROR "missing|incompatible"
	t = i;	// ERROR "missing|incompatible"
}
<|endoftext|>


<commit_before>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package test

import (
	"bytes"
	"fmt"
	"strings"
	"runtime"
	"testing"
	"encoding/base64"
)

// Return short format caller info for printing errors, so errors don't all
// appear to come from test-tools.go.
func caller() string {
	_, file, line, _ := runtime.Caller(2)
	splits := strings.Split(file, "/")
	filename := splits[len(splits) - 1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

func Assert(t *testing.T, result bool, message string) {
	if !result {
		t.Error(caller(), message)
	}
}

func AssertNotError(t *testing.T, err error, message string) {
	if err != nil {
		t.Error(caller(), message, ":", err)
	}
}

func AssertError(t *testing.T, err error, message string) {
	if err == nil {
		t.Error(caller(), message, ":", err)
	}
}

func AssertEquals(t *testing.T, one string, two string) {
	if one != two {
		t.Errorf("%s String [%s] != [%s]", caller(), one, two)
	}
}

func AssertByteEquals(t *testing.T, one []byte, two []byte) {
	if !bytes.Equal(one, two) {
		t.Errorf("%s Byte [%s] != [%s]",
			caller(),
			base64.StdEncoding.EncodeToString(one),
			base64.StdEncoding.EncodeToString(two))
	}
}
func AssertContains(t *testing.T, haystack string, needle string) {
	if !strings.Contains(haystack, needle) {
		t.Errorf("%s String [%s] does not contain [%s]", caller(), haystack, needle)
	}
}

func AssertSeverity(t *testing.T, data string, severity int) {
	expected := fmt.Sprintf("\"severity\":%d", severity)
	AssertContains(t, data, expected)
}
<commit_msg>Incorporate JC changes to test-tools.go.<commit_after>// Copyright 2014 ISRG.  All rights reserved
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

package test

import (
	"bytes"
	"encoding/base64"
	"fmt"
	"runtime"
	"strings"
	"testing"
)

// Return short format caller info for printing errors, so errors don't all
// appear to come from test-tools.go.
func caller() string {
	_, file, line, _ := runtime.Caller(2)
	splits := strings.Split(file, "/")
	filename := splits[len(splits)-1]
	return fmt.Sprintf("%s:%d:", filename, line)
}

func Assert(t *testing.T, result bool, message string) {
	if !result {
		t.Error(caller(), message)
	}
}

func AssertNotNil(t *testing.T, obj interface{}, message string) {
	if obj == nil {
		t.Error(caller(), message)
	}
}

func AssertNotError(t *testing.T, err error, message string) {
	if err != nil {
		t.Error(caller(), message, ":", err)
	}
}

func AssertError(t *testing.T, err error, message string) {
	if err == nil {
		t.Error(caller(), message, ":", err)
	}
}

func AssertEquals(t *testing.T, one interface{}, two interface{}) {
	if one != two {
		t.Errorf("%s [%v] != [%v]", caller(), one, two)
	}
}

func AssertNotEquals(t *testing.T, one interface{}, two interface{}) {
	if one == two {
		t.Errorf("%s [%v] == [%v]", caller(), one, two)
	}
}

func AssertByteEquals(t *testing.T, one []byte, two []byte) {
	if !bytes.Equal(one, two) {
		t.Errorf("%s Byte [%s] != [%s]",
			caller(),
			base64.StdEncoding.EncodeToString(one),
			base64.StdEncoding.EncodeToString(two))
	}
}
func AssertContains(t *testing.T, haystack string, needle string) {
	if !strings.Contains(haystack, needle) {
		t.Errorf("%s String [%s] does not contain [%s]", caller(), haystack, needle)
	}
}

func AssertSeverity(t *testing.T, data string, severity int) {
	expected := fmt.Sprintf("\"severity\":%d", severity)
	AssertContains(t, data, expected)
}
<|endoftext|>


<commit_before>package lifecycle

import (
	"time"
	"testing"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	"github.com/vito/runtime-integration/config"
	. "github.com/vito/runtime-integration/helpers"
)

func Test(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Lifecycle")
}

var conf = config.Load()
var appName = RandomName()
var appPath = "../assets/dora"
var appUri = "http://" + appName + "." + conf.AppsDomain

var _ = Describe("Application", func() {
	Describe("pushing", func() {
		It("works", func() {
			push := Run("go-cf", "push", appName, "-p", appPath)
			Expect(push).To(SayWithTimeout("Started", 2 * time.Minute))
			Expect(push).To(ExitWith(0))
		})

		It("makes the app reachable via its bound routes", func() {
			curl := Run("curl", "-s", appUri)
			Expect(curl).To(Say("Hello, world!"))
			Expect(curl).To(ExitWith(0))
		})
	})

	Describe("stopping", func() {
		It("works", func() {
			del := Run("go-cf", "stop", appName)
			Expect(del).To(Say("OK"))
			Expect(del).To(ExitWith(0))
		})

		It("makes the app unreachable", func() {
			curl := Run("curl", "-s", appUri)
			Expect(curl).To(Say("404"))
			Expect(curl).To(ExitWith(0))
		})
	})

	Describe("starting", func() {
		It("works", func() {
			del := Run("go-cf", "start", appName)
			Expect(del).To(Say("OK"))
			Expect(del).To(ExitWith(0))
		})

		It("makes the app reachable again", func() {
			curl := Run("curl", "-s", appUri)
			Expect(curl).To(Say("Hello, world!"))
			Expect(curl).To(ExitWith(0))
		})
	})

	Describe("deleting", func() {
		It("works", func() {
			del := Run("go-cf", "delete", appName, "-f")
			Expect(del).To(Say("OK"))
			Expect(del).To(ExitWith(0))
		})

		It("removes the application", func() {
			app := Run("go-cf", "app", appName)
			Expect(app).To(Say("not found"))
			Expect(app).To(ExitWith(1))
		})

		It("makes the app unreachable", func() {
			curl := Run("curl", "-s", appUri)
			Expect(curl).To(Say("404"))
			Expect(curl).To(ExitWith(0))
		})
	})
})
<commit_msg>now runnable in parallel (-nodes=N)<commit_after>package lifecycle

import (
	"testing"
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/vito/cmdtest/matchers"

	"github.com/vito/runtime-integration/config"
	. "github.com/vito/runtime-integration/helpers"
)

func TestLifecycle(t *testing.T) {
	RegisterFailHandler(Fail)
	RunSpecs(t, "Application Lifecycle")
}

var conf = config.Load()

var appName = ""
var appPath = "../assets/dora"

func appUri() string {
	return "http://" + appName + "." + conf.AppsDomain
}

var _ = Describe("Application", func() {
	BeforeEach(func() {
		appName = RandomName()

		push := Run("go-cf", "push", appName, "-p", appPath)
		Expect(push).To(SayWithTimeout("Started", 2*time.Minute))
		Expect(push).To(ExitWith(0))
	})

	AfterEach(func() {
		del := Run("go-cf", "delete", appName, "-f")
		Expect(del).To(Say("OK"))
		Expect(del).To(ExitWith(0))
	})

	Describe("pushing", func() {
		It("makes the app reachable via its bound routes", func() {
			curl := Run("curl", "-s", appUri())
			Expect(curl).To(Say("Hello, world!"))
			Expect(curl).To(ExitWith(0))
		})
	})

	Describe("stopping", func() {
		BeforeEach(func() {
			del := Run("go-cf", "stop", appName)
			Expect(del).To(Say("OK"))
			Expect(del).To(ExitWith(0))
		})

		It("makes the app unreachable", func() {
			curl := Run("curl", "-s", appUri())
			Expect(curl).To(Say("404"))
			Expect(curl).To(ExitWith(0))
		})

		Describe("and then starting", func() {
			BeforeEach(func() {
				del := Run("go-cf", "start", appName)
				Expect(del).To(Say("OK"))
				Expect(del).To(ExitWith(0))
			})

			It("makes the app reachable again", func() {
				curl := Run("curl", "-s", appUri())
				Expect(curl).To(Say("Hello, world!"))
				Expect(curl).To(ExitWith(0))
			})
		})
	})

	Describe("deleting", func() {
		BeforeEach(func() {
			del := Run("go-cf", "delete", appName, "-f")
			Expect(del).To(Say("OK"))
			Expect(del).To(ExitWith(0))
		})

		It("removes the application", func() {
			app := Run("go-cf", "app", appName)
			Expect(app).To(Say("not found"))
			Expect(app).To(ExitWith(1))
		})

		It("makes the app unreachable", func() {
			curl := Run("curl", "-s", appUri())
			Expect(curl).To(Say("404"))
			Expect(curl).To(ExitWith(0))
		})
	})
})
<|endoftext|>


<commit_before>package chart

import (
	"fmt"
	"time"
)

// MarketHoursRange is a special type of range that compresses a time range into just the
// market (i.e. NYSE operating hours and days) range.
type MarketHoursRange struct {
	Min time.Time
	Max time.Time

	MarketOpen  time.Time
	MarketClose time.Time

	HolidayProvider HolidayProvider

	ValueFormatter ValueFormatter

	Descending bool
	Domain     int
}

// IsDescending returns if the range is descending.
func (mhr MarketHoursRange) IsDescending() bool {
	return mhr.Descending
}

// GetTimezone returns the timezone for the market hours range.
func (mhr MarketHoursRange) GetTimezone() *time.Location {
	return mhr.GetMarketOpen().Location()
}

// IsZero returns if the range is setup or not.
func (mhr MarketHoursRange) IsZero() bool {
	return mhr.Min.IsZero() && mhr.Max.IsZero()
}

// GetMin returns the min value.
func (mhr MarketHoursRange) GetMin() float64 {
	return Time.ToFloat64(mhr.Min)
}

// GetMax returns the max value.
func (mhr MarketHoursRange) GetMax() float64 {
	return Time.ToFloat64(mhr.GetEffectiveMax())
}

// GetEffectiveMax gets either the close on the max, or the max itself.
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
	maxClose := Date.On(mhr.MarketClose, mhr.Max)
	if maxClose.After(mhr.Max) {
		return maxClose
	}
	return mhr.Max
}

// SetMin sets the min value.
func (mhr *MarketHoursRange) SetMin(min float64) {
	mhr.Min = Time.FromFloat64(min)
	mhr.Min = mhr.Min.In(mhr.GetTimezone())
}

// SetMax sets the max value.
func (mhr *MarketHoursRange) SetMax(max float64) {
	mhr.Max = Time.FromFloat64(max)
	mhr.Max = mhr.Max.In(mhr.GetTimezone())
}

// GetDelta gets the delta.
func (mhr MarketHoursRange) GetDelta() float64 {
	min := mhr.GetMin()
	max := mhr.GetMax()
	return max - min
}

// GetDomain gets the domain.
func (mhr MarketHoursRange) GetDomain() int {
	return mhr.Domain
}

// SetDomain sets the domain.
func (mhr *MarketHoursRange) SetDomain(domain int) {
	mhr.Domain = domain
}

// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
	if mhr.HolidayProvider == nil {
		return defaultHolidayProvider
	}
	return mhr.HolidayProvider
}

// GetMarketOpen returns the market open time.
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
	if mhr.MarketOpen.IsZero() {
		return NYSEOpen
	}
	return mhr.MarketOpen
}

// GetMarketClose returns the market close time.
func (mhr MarketHoursRange) GetMarketClose() time.Time {
	if mhr.MarketClose.IsZero() {
		return NYSEClose
	}
	return mhr.MarketClose
}

// GetTicks returns the ticks for the range.
// This is to override the default continous ticks that would be generated for the range.
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
	times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth := mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	return GenerateContinuousTicks(r, mhr, false, defaults, vf)

}

func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
	defaults.GetTextOptions().WriteToRenderer(r)
	var total int
	for index, t := range times {
		timeLabel := vf(t)

		labelBox := r.MeasureText(timeLabel)
		total += labelBox.Width()
		if index > 0 {
			total += DefaultMinimumTickHorizontalSpacing
		}
	}
	return total
}

func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
	ticks := make([]Tick, len(times))
	for index, t := range times {
		ticks[index] = Tick{
			Value: Time.ToFloat64(t),
			Label: vf(t),
		}
	}
	return ticks
}

func (mhr MarketHoursRange) String() string {
	return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
}

// Translate maps a given value into the ContinuousRange space.
func (mhr MarketHoursRange) Translate(value float64) int {
	valueTime := Time.FromFloat64(value)
	valueTimeEastern := valueTime.In(Date.Eastern())
	totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))

	if r.IsDescending() {
		return r.Domain - translated
	}

	return translated
}
<commit_msg>typo.<commit_after>package chart

import (
	"fmt"
	"time"
)

// MarketHoursRange is a special type of range that compresses a time range into just the
// market (i.e. NYSE operating hours and days) range.
type MarketHoursRange struct {
	Min time.Time
	Max time.Time

	MarketOpen  time.Time
	MarketClose time.Time

	HolidayProvider HolidayProvider

	ValueFormatter ValueFormatter

	Descending bool
	Domain     int
}

// IsDescending returns if the range is descending.
func (mhr MarketHoursRange) IsDescending() bool {
	return mhr.Descending
}

// GetTimezone returns the timezone for the market hours range.
func (mhr MarketHoursRange) GetTimezone() *time.Location {
	return mhr.GetMarketOpen().Location()
}

// IsZero returns if the range is setup or not.
func (mhr MarketHoursRange) IsZero() bool {
	return mhr.Min.IsZero() && mhr.Max.IsZero()
}

// GetMin returns the min value.
func (mhr MarketHoursRange) GetMin() float64 {
	return Time.ToFloat64(mhr.Min)
}

// GetMax returns the max value.
func (mhr MarketHoursRange) GetMax() float64 {
	return Time.ToFloat64(mhr.GetEffectiveMax())
}

// GetEffectiveMax gets either the close on the max, or the max itself.
func (mhr MarketHoursRange) GetEffectiveMax() time.Time {
	maxClose := Date.On(mhr.MarketClose, mhr.Max)
	if maxClose.After(mhr.Max) {
		return maxClose
	}
	return mhr.Max
}

// SetMin sets the min value.
func (mhr *MarketHoursRange) SetMin(min float64) {
	mhr.Min = Time.FromFloat64(min)
	mhr.Min = mhr.Min.In(mhr.GetTimezone())
}

// SetMax sets the max value.
func (mhr *MarketHoursRange) SetMax(max float64) {
	mhr.Max = Time.FromFloat64(max)
	mhr.Max = mhr.Max.In(mhr.GetTimezone())
}

// GetDelta gets the delta.
func (mhr MarketHoursRange) GetDelta() float64 {
	min := mhr.GetMin()
	max := mhr.GetMax()
	return max - min
}

// GetDomain gets the domain.
func (mhr MarketHoursRange) GetDomain() int {
	return mhr.Domain
}

// SetDomain sets the domain.
func (mhr *MarketHoursRange) SetDomain(domain int) {
	mhr.Domain = domain
}

// GetHolidayProvider coalesces a userprovided holiday provider and the date.DefaultHolidayProvider.
func (mhr MarketHoursRange) GetHolidayProvider() HolidayProvider {
	if mhr.HolidayProvider == nil {
		return defaultHolidayProvider
	}
	return mhr.HolidayProvider
}

// GetMarketOpen returns the market open time.
func (mhr MarketHoursRange) GetMarketOpen() time.Time {
	if mhr.MarketOpen.IsZero() {
		return NYSEOpen
	}
	return mhr.MarketOpen
}

// GetMarketClose returns the market close time.
func (mhr MarketHoursRange) GetMarketClose() time.Time {
	if mhr.MarketClose.IsZero() {
		return NYSEClose
	}
	return mhr.MarketClose
}

// GetTicks returns the ticks for the range.
// This is to override the default continous ticks that would be generated for the range.
func (mhr *MarketHoursRange) GetTicks(r Renderer, defaults Style, vf ValueFormatter) []Tick {
	times := Sequence.MarketHours(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth := mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketHourQuarters(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayAlternateCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	times = Sequence.MarketDayMondayCloses(mhr.Min, mhr.Max, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.GetHolidayProvider())
	timesWidth = mhr.measureTimes(r, defaults, vf, times)
	if timesWidth <= mhr.Domain {
		return mhr.makeTicks(vf, times)
	}

	return GenerateContinuousTicks(r, mhr, false, defaults, vf)

}

func (mhr *MarketHoursRange) measureTimes(r Renderer, defaults Style, vf ValueFormatter, times []time.Time) int {
	defaults.GetTextOptions().WriteToRenderer(r)
	var total int
	for index, t := range times {
		timeLabel := vf(t)

		labelBox := r.MeasureText(timeLabel)
		total += labelBox.Width()
		if index > 0 {
			total += DefaultMinimumTickHorizontalSpacing
		}
	}
	return total
}

func (mhr *MarketHoursRange) makeTicks(vf ValueFormatter, times []time.Time) []Tick {
	ticks := make([]Tick, len(times))
	for index, t := range times {
		ticks[index] = Tick{
			Value: Time.ToFloat64(t),
			Label: vf(t),
		}
	}
	return ticks
}

func (mhr MarketHoursRange) String() string {
	return fmt.Sprintf("MarketHoursRange [%s, %s] => %d", mhr.Min.Format(time.RFC3339), mhr.Max.Format(time.RFC3339), mhr.Domain)
}

// Translate maps a given value into the ContinuousRange space.
func (mhr MarketHoursRange) Translate(value float64) int {
	valueTime := Time.FromFloat64(value)
	valueTimeEastern := valueTime.In(Date.Eastern())
	totalSeconds := Date.CalculateMarketSecondsBetween(mhr.Min, mhr.GetEffectiveMax(), mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	valueDelta := Date.CalculateMarketSecondsBetween(mhr.Min, valueTimeEastern, mhr.GetMarketOpen(), mhr.GetMarketClose(), mhr.HolidayProvider)
	translated := int((float64(valueDelta) / float64(totalSeconds)) * float64(mhr.Domain))

	if mhr.IsDescending() {
		return mhr.Domain - translated
	}

	return translated
}
<|endoftext|>


<commit_before>// Package fields содержит набор служебных функций для работы с полями Data объектов
package fields

import (

	// Helpers
	"MindAssistantBackend/helpers/errors"
	"MindAssistantBackend/helpers/response"
	"strings"
	// Interfaces
	"MindAssistantBackend/interfaces/data/fields"
	"MindAssistantBackend/interfaces/data/groups"
	// Queries
	"MindAssistantBackend/db/connect"
	"MindAssistantBackend/db/data/fields"
	// Packages
	"encoding/json"
	"net/http"
	// Libraries
	"github.com/gorilla/mux"
)

// Соединение с БД
var db = dbConnect.Init()

// List выводит список полей
func List(w http.ResponseWriter, r *http.Request, group *iFieldGroup.Model) []*iField.Model {
	// Сбор и анализ входных данных
	if group.ID <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return nil
	}

	// Выполнение запроса
	rows, err := dbFields.List(group.ID)
	errors.ErrorHandler(err, 500, w)
	defer rows.Close()

	// Сбор данных из БД в структуру
	list := make([]*iField.Model, 0)
	for rows.Next() {
		field := new(iField.Db)

		err := rows.Scan(&field.ID, &field.Type, &field.Order, &field.Value, &field.Group, &field.Title)
		errors.ErrorHandler(err, 500, w)

		output := &iField.Model{
			ID:    field.ID,
			Type:  field.Type,
			Order: field.Order,
			Group: field.Group,
			Title: field.Title,
			Value: strings.Split(field.Value, ";"),
		}

		// json.Unmarshal(field.Value, &output.Value)
		// fmt.Println(len(output.Value))

		// for v := 0; v < len(field.Value); v++ {
		// 	output.Value[v] = string(byte(field.Value[v]))
		// }
		// field.Value

		// json.Unmarshal(field.Value, &output.Value)

		if output.Value == nil {
			output.Value = make([]string, 1)
		}

		list = append(list, output)
	}
	errors.ErrorHandler(rows.Err(), 500, w)

	return list
}

// Update обновляет поле
func Update(w http.ResponseWriter, r *http.Request, field *iField.Model) *iField.Db {
	if field.ID <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return nil
	}

	// Подготовка запроса
	output := &iField.Db{
		ID:    field.ID,
		Type:  field.Type,
		Order: field.Order,
		Group: field.Group,
		Title: field.Title,
		Value: strings.Join(field.Value, ";"),
	}

	// Выполнение запросов
	result, err := dbFields.Update(output)
	errors.ErrorHandler(err, 500, w)

	rows, err := result.RowsAffected()
	errors.ErrorHandler(err, 500, w)

	// Проверка на успешность
	if rows > 0 {
		// Отображение результата
		return output
	}

	return nil
}

// Create создает поле
func Create(w http.ResponseWriter, r *http.Request) {
	// Сбор и анализ входных данных
	decoder := json.NewDecoder(r.Body)
	field := new(iField.Model)
	err := decoder.Decode(&field)
	errors.ErrorHandler(err, 500, w)
	defer r.Body.Close()

	if field.Group <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// Выполнение запроса
	row := dbFields.Create(field)
	err = row.Scan(&field.ID)
	errors.ErrorHandler(err, 500, w)

	// Отображение результата
	if field.ID > 0 {
		// Формирование ответа от сервера
		response := response.Set(true, "", field)

		// Подготовка выходных данных
		output, err := json.Marshal(response)
		errors.ErrorHandler(err, 500, w)

		// Отображение результата
		w.Write(output)
	}
}

// Delete удаляет поле
func Delete(w http.ResponseWriter, r *http.Request) {
	// Сбор и анализ входных данных
	params := mux.Vars(r)
	id := params["id"]
	if id == "" {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// Выполнение запросов
	result, err := dbFields.Delete(id)
	errors.ErrorHandler(err, 500, w)

	// Проверка на успешность
	rows, err := result.RowsAffected()
	errors.ErrorHandler(err, 500, w)

	if rows > 0 {
		// Формирование ответа от сервера
		response := response.Set(true, "", nil)

		// Подготовка выходных данных
		output, err := json.Marshal(response)
		errors.ErrorHandler(err, 500, w)

		// Отображение результата
		w.Write(output)
	}
}
<commit_msg>тепреь textarea и date можно добавлять группами<commit_after>// Package fields содержит набор служебных функций для работы с полями Data объектов
package fields

import (

	// Helpers
	"MindAssistantBackend/helpers/errors"
	"MindAssistantBackend/helpers/response"
	"strings"
	// Interfaces
	"MindAssistantBackend/interfaces/data/fields"
	"MindAssistantBackend/interfaces/data/groups"
	// Queries
	"MindAssistantBackend/db/connect"
	"MindAssistantBackend/db/data/fields"
	// Packages
	"encoding/json"
	"net/http"
	// Libraries
	"github.com/gorilla/mux"
)

// Соединение с БД
var db = dbConnect.Init()

// List выводит список полей
func List(w http.ResponseWriter, r *http.Request, group *iFieldGroup.Model) []*iField.Model {
	// Сбор и анализ входных данных
	if group.ID <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return nil
	}

	// Выполнение запроса
	rows, err := dbFields.List(group.ID)
	errors.ErrorHandler(err, 500, w)
	defer rows.Close()

	// Сбор данных из БД в структуру
	list := make([]*iField.Model, 0)
	for rows.Next() {
		field := new(iField.Db)

		err := rows.Scan(&field.ID, &field.Type, &field.Order, &field.Value, &field.Group, &field.Title)
		errors.ErrorHandler(err, 500, w)

		output := &iField.Model{
			ID:    field.ID,
			Type:  field.Type,
			Order: field.Order,
			Group: field.Group,
			Title: field.Title,
			Value: strings.Split(field.Value, ";"),
		}

		if output.Value == nil {
			output.Value = make([]string, 1)
		}

		list = append(list, output)
	}
	errors.ErrorHandler(rows.Err(), 500, w)

	return list
}

// Update обновляет поле
func Update(w http.ResponseWriter, r *http.Request, field *iField.Model) *iField.Db {
	if field.ID <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return nil
	}

	// Подготовка запроса
	output := &iField.Db{
		ID:    field.ID,
		Type:  field.Type,
		Order: field.Order,
		Group: field.Group,
		Title: field.Title,
	}

	if len(field.Value) > 1 {
		output.Value = strings.Join(field.Value, ";")
	} else {
		output.Value = field.Value[0]
	}

	// Выполнение запросов
	result, err := dbFields.Update(output)
	errors.ErrorHandler(err, 500, w)

	rows, err := result.RowsAffected()
	errors.ErrorHandler(err, 500, w)

	// Проверка на успешность
	if rows > 0 {
		// Отображение результата
		return output
	}

	return nil
}

// Create создает поле
func Create(w http.ResponseWriter, r *http.Request) {
	// Сбор и анализ входных данных
	decoder := json.NewDecoder(r.Body)
	field := new(iField.Model)
	err := decoder.Decode(&field)
	errors.ErrorHandler(err, 500, w)
	defer r.Body.Close()

	if field.Group <= 0 {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// Выполнение запроса
	row := dbFields.Create(field)
	err = row.Scan(&field.ID)
	errors.ErrorHandler(err, 500, w)

	// Отображение результата
	if field.ID > 0 {
		// Формирование ответа от сервера
		response := response.Set(true, "", field)

		// Подготовка выходных данных
		output, err := json.Marshal(response)
		errors.ErrorHandler(err, 500, w)

		// Отображение результата
		w.Write(output)
	}
}

// Delete удаляет поле
func Delete(w http.ResponseWriter, r *http.Request) {
	// Сбор и анализ входных данных
	params := mux.Vars(r)
	id := params["id"]
	if id == "" {
		http.Error(w, http.StatusText(400), 400)
		return
	}

	// Выполнение запросов
	result, err := dbFields.Delete(id)
	errors.ErrorHandler(err, 500, w)

	// Проверка на успешность
	rows, err := result.RowsAffected()
	errors.ErrorHandler(err, 500, w)

	if rows > 0 {
		// Формирование ответа от сервера
		response := response.Set(true, "", nil)

		// Подготовка выходных данных
		output, err := json.Marshal(response)
		errors.ErrorHandler(err, 500, w)

		// Отображение результата
		w.Write(output)
	}
}
<|endoftext|>


<commit_before>// +build js

package math

import (
	"github.com/gopherjs/gopherjs/js"
)

var math = js.Global.Get("Math")
var zero float64 = 0
var posInf = 1 / zero
var negInf = -1 / zero
var nan = 0 / zero

func Abs(x float64) float64 {
	return abs(x)
}

func Acos(x float64) float64 {
	return math.Call("acos", x).Float()
}

func Asin(x float64) float64 {
	return math.Call("asin", x).Float()
}

func Atan(x float64) float64 {
	return math.Call("atan", x).Float()
}

func Atan2(y, x float64) float64 {
	return math.Call("atan2", y, x).Float()
}

func Ceil(x float64) float64 {
	return math.Call("ceil", x).Float()
}

func Copysign(x, y float64) float64 {
	if (x < 0 || 1/x == negInf) != (y < 0 || 1/y == negInf) {
		return -x
	}
	return x
}

func Cos(x float64) float64 {
	return math.Call("cos", x).Float()
}

func Dim(x, y float64) float64 {
	return dim(x, y)
}

func Exp(x float64) float64 {
	return math.Call("exp", x).Float()
}

func Exp2(x float64) float64 {
	return math.Call("pow", 2, x).Float()
}

func Expm1(x float64) float64 {
	return expm1(x)
}

func Floor(x float64) float64 {
	return math.Call("floor", x).Float()
}

func Frexp(f float64) (frac float64, exp int) {
	return frexp(f)
}

func Hypot(p, q float64) float64 {
	return hypot(p, q)
}

func Inf(sign int) float64 {
	switch {
	case sign >= 0:
		return posInf
	default:
		return negInf
	}
}

func IsInf(f float64, sign int) bool {
	if f == posInf {
		return sign >= 0
	}
	if f == negInf {
		return sign <= 0
	}
	return false
}

func IsNaN(f float64) (is bool) {
	return f != f
}

func Ldexp(frac float64, exp int) float64 {
	return js.Global.Call("go$ldexp", frac, exp).Float()
}

func Log(x float64) float64 {
	return math.Call("log", x).Float()
}

func Log10(x float64) float64 {
	return log10(x)
}

func Log1p(x float64) float64 {
	return log1p(x)
}

func Log2(x float64) float64 {
	return log2(x)
}

func Max(x, y float64) float64 {
	return max(x, y)
}

func Min(x, y float64) float64 {
	return min(x, y)
}

func Mod(x, y float64) float64 {
	return js.Global.Call("go$mod", x, y).Float()
}

func Modf(f float64) (float64, float64) {
	if f == posInf || f == negInf {
		return f, nan
	}
	frac := Mod(f, 1)
	return f - frac, frac
}

func NaN() float64 {
	return nan
}

func Pow(x, y float64) float64 {
	if x == 1 || (x == -1 && (y == posInf || y == negInf)) {
		return 1
	}
	return math.Call("pow", x, y).Float()
}

func Remainder(x, y float64) float64 {
	return remainder(x, y)
}

func Signbit(x float64) bool {
	return x < 0 || 1/x == negInf
}

func Sin(x float64) float64 {
	return math.Call("sin", x).Float()
}

func Sincos(x float64) (sin, cos float64) {
	return Sin(x), Cos(x)
}

func Sqrt(x float64) float64 {
	return math.Call("sqrt", x).Float()
}

func Tan(x float64) float64 {
	return math.Call("tan", x).Float()
}

func Trunc(x float64) float64 {
	if x == posInf || x == negInf || x != x || 1/x == negInf {
		return x
	}
	return float64(int(x))
}

func Float32bits(f float32) uint32 {
	if f == 0 {
		if 1/f == float32(negInf) {
			return 1 << 31
		}
		return 0
	}
	if f != f { // NaN
		return 2143289344
	}

	s := uint32(0)
	if f < 0 {
		s = 1 << 31
		f = -f
	}

	e := uint32(127 + 23)
	for f >= 1<<24 {
		f /= 2
		if e == (1<<8)-1 {
			break
		}
		e++
	}
	for f < 1<<23 {
		e--
		if e == 0 {
			break
		}
		f *= 2
	}

	r := Mod(float64(f), 2)
	if (r > 0.5 && r < 1) || r >= 1.5 { // round to nearest even
		f++
	}

	return s | uint32(e)<<23 | (uint32(f) &^ (1 << 23))
}

func Float32frombits(b uint32) float32 {
	s := float32(+1)
	if b&(1<<31) != 0 {
		s = -1
	}
	e := (b >> 23) & (1<<8 - 1)
	m := b & (1<<23 - 1)

	if e == (1<<8)-1 {
		if m == 0 {
			return s / 0 // Inf
		}
		return float32(nan)
	}
	if e != 0 {
		m += 1 << 23
	}
	if e == 0 {
		e = 1
	}

	return float32(Ldexp(float64(m), int(e)-127-23)) * s
}

func Float64bits(f float64) uint64 {
	if f == 0 {
		if 1/f == negInf {
			return 1 << 63
		}
		return 0
	}
	if f != f { // NaN
		return 9221120237041090561
	}

	s := uint64(0)
	if f < 0 {
		s = 1 << 63
		f = -f
	}

	e := uint32(1023 + 52)
	for f >= 1<<53 {
		f /= 2
		if e == (1<<11)-1 {
			break
		}
		e++
	}
	for f < 1<<52 {
		e--
		if e == 0 {
			break
		}
		f *= 2
	}

	return s | uint64(e)<<52 | (uint64(f) &^ (1 << 52))
}

func Float64frombits(b uint64) float64 {
	s := float64(+1)
	if b&(1<<63) != 0 {
		s = -1
	}
	e := (b >> 52) & (1<<11 - 1)
	m := b & (1<<52 - 1)

	if e == (1<<11)-1 {
		if m == 0 {
			return s / 0
		}
		return nan
	}
	if e != 0 {
		m += 1 << 52
	}
	if e == 0 {
		e = 1
	}

	return Ldexp(float64(m), int(e)-1023-52) * s
}
<commit_msg>added workaround for optimizer bug in V8<commit_after>// +build js

package math

import (
	"github.com/gopherjs/gopherjs/js"
)

var math = js.Global.Get("Math")
var zero float64 = 0
var posInf = 1 / zero
var negInf = -1 / zero
var nan = 0 / zero

func Abs(x float64) float64 {
	return abs(x)
}

func Acos(x float64) float64 {
	return math.Call("acos", x).Float()
}

func Asin(x float64) float64 {
	return math.Call("asin", x).Float()
}

func Atan(x float64) float64 {
	return math.Call("atan", x).Float()
}

func Atan2(y, x float64) float64 {
	return math.Call("atan2", y, x).Float()
}

func Ceil(x float64) float64 {
	return math.Call("ceil", x).Float()
}

func Copysign(x, y float64) float64 {
	if (x < 0 || 1/x == negInf) != (y < 0 || 1/y == negInf) {
		return -x
	}
	return x
}

func Cos(x float64) float64 {
	return math.Call("cos", x).Float()
}

func Dim(x, y float64) float64 {
	return dim(x, y)
}

func Exp(x float64) float64 {
	return math.Call("exp", x).Float()
}

func Exp2(x float64) float64 {
	return math.Call("pow", 2, x).Float()
}

func Expm1(x float64) float64 {
	return expm1(x)
}

func Floor(x float64) float64 {
	return math.Call("floor", x).Float()
}

func Frexp(f float64) (frac float64, exp int) {
	return frexp(f)
}

func Hypot(p, q float64) float64 {
	return hypot(p, q)
}

func Inf(sign int) float64 {
	switch {
	case sign >= 0:
		return posInf
	default:
		return negInf
	}
}

func IsInf(f float64, sign int) bool {
	if f == posInf {
		return sign >= 0
	}
	if f == negInf {
		return sign <= 0
	}
	return false
}

func IsNaN(f float64) (is bool) {
	return f != f
}

func Ldexp(frac float64, exp int) float64 {
	return js.Global.Call("go$ldexp", frac, exp).Float()
}

func Log(x float64) float64 {
	if x != x { // workaround for optimizer bug in V8, remove at some point
		return nan
	}
	return math.Call("log", x).Float()
}

func Log10(x float64) float64 {
	return log10(x)
}

func Log1p(x float64) float64 {
	return log1p(x)
}

func Log2(x float64) float64 {
	return log2(x)
}

func Max(x, y float64) float64 {
	return max(x, y)
}

func Min(x, y float64) float64 {
	return min(x, y)
}

func Mod(x, y float64) float64 {
	return js.Global.Call("go$mod", x, y).Float()
}

func Modf(f float64) (float64, float64) {
	if f == posInf || f == negInf {
		return f, nan
	}
	frac := Mod(f, 1)
	return f - frac, frac
}

func NaN() float64 {
	return nan
}

func Pow(x, y float64) float64 {
	if x == 1 || (x == -1 && (y == posInf || y == negInf)) {
		return 1
	}
	return math.Call("pow", x, y).Float()
}

func Remainder(x, y float64) float64 {
	return remainder(x, y)
}

func Signbit(x float64) bool {
	return x < 0 || 1/x == negInf
}

func Sin(x float64) float64 {
	return math.Call("sin", x).Float()
}

func Sincos(x float64) (sin, cos float64) {
	return Sin(x), Cos(x)
}

func Sqrt(x float64) float64 {
	return math.Call("sqrt", x).Float()
}

func Tan(x float64) float64 {
	return math.Call("tan", x).Float()
}

func Trunc(x float64) float64 {
	if x == posInf || x == negInf || x != x || 1/x == negInf {
		return x
	}
	return float64(int(x))
}

func Float32bits(f float32) uint32 {
	if f == 0 {
		if 1/f == float32(negInf) {
			return 1 << 31
		}
		return 0
	}
	if f != f { // NaN
		return 2143289344
	}

	s := uint32(0)
	if f < 0 {
		s = 1 << 31
		f = -f
	}

	e := uint32(127 + 23)
	for f >= 1<<24 {
		f /= 2
		if e == (1<<8)-1 {
			break
		}
		e++
	}
	for f < 1<<23 {
		e--
		if e == 0 {
			break
		}
		f *= 2
	}

	r := Mod(float64(f), 2)
	if (r > 0.5 && r < 1) || r >= 1.5 { // round to nearest even
		f++
	}

	return s | uint32(e)<<23 | (uint32(f) &^ (1 << 23))
}

func Float32frombits(b uint32) float32 {
	s := float32(+1)
	if b&(1<<31) != 0 {
		s = -1
	}
	e := (b >> 23) & (1<<8 - 1)
	m := b & (1<<23 - 1)

	if e == (1<<8)-1 {
		if m == 0 {
			return s / 0 // Inf
		}
		return float32(nan)
	}
	if e != 0 {
		m += 1 << 23
	}
	if e == 0 {
		e = 1
	}

	return float32(Ldexp(float64(m), int(e)-127-23)) * s
}

func Float64bits(f float64) uint64 {
	if f == 0 {
		if 1/f == negInf {
			return 1 << 63
		}
		return 0
	}
	if f != f { // NaN
		return 9221120237041090561
	}

	s := uint64(0)
	if f < 0 {
		s = 1 << 63
		f = -f
	}

	e := uint32(1023 + 52)
	for f >= 1<<53 {
		f /= 2
		if e == (1<<11)-1 {
			break
		}
		e++
	}
	for f < 1<<52 {
		e--
		if e == 0 {
			break
		}
		f *= 2
	}

	return s | uint64(e)<<52 | (uint64(f) &^ (1 << 52))
}

func Float64frombits(b uint64) float64 {
	s := float64(+1)
	if b&(1<<63) != 0 {
		s = -1
	}
	e := (b >> 52) & (1<<11 - 1)
	m := b & (1<<52 - 1)

	if e == (1<<11)-1 {
		if m == 0 {
			return s / 0
		}
		return nan
	}
	if e != 0 {
		m += 1 << 52
	}
	if e == 0 {
		e = 1
	}

	return Ldexp(float64(m), int(e)-1023-52) * s
}
<|endoftext|>