<commit_before>package kraken

import (
	"bytes"
	"encoding/json"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"log"
	"net"
	"net/http"
	"strconv"

	"github.com/vincent-petithory/kraken/fileserver"
)

type serverPoolAdminHandler struct {
	*ServerPool
	h      http.Handler
	router *mux.Router
}

const (
	routeServers                = "servers"
	routeServersSelf            = "servers.self"
	routeServersSelfAliases     = "servers.self.aliases"
	routeServersSelfAliasesSelf = "servers.self.aliases.self"
	routeFileservers            = "fileservers"
)

type adminApiErrorType string

const (
	apiErrTypeBadRequest  adminApiErrorType = "bad_request_error"
	apiErrTypeApiInternal                   = "api_internal_error"
)

type adminApiError struct {
	Type adminApiErrorType `json:"type"`
	Msg  string            `json:"msg"`
}

func (e *adminApiError) String() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

func (e *adminApiError) Error() string {
	return e.Msg
}

func NewServerPoolAdminHandler(serverPool *ServerPool) *serverPoolAdminHandler {
	spah := serverPoolAdminHandler{ServerPool: serverPool}
	r := mux.NewRouter()
	apiRouter := r.PathPrefix("/api/").Subrouter()
	apiRouter.Handle("/servers", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServers),
		"POST":   http.HandlerFunc(spah.createServerWithRandomPort),
		"DELETE": http.HandlerFunc(spah.removeServers),
	}).Name(routeServers)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServer),
		"PUT":    http.HandlerFunc(spah.createServer),
		"DELETE": http.HandlerFunc(spah.removeServer),
	}).Name(routeServersSelf)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}/aliases", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServerAliases),
		"DELETE": http.HandlerFunc(spah.removeServerAliases),
	}).Name(routeServersSelfAliases)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}/aliases/{alias}", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServerAlias),
		"PUT":    http.HandlerFunc(spah.createServerAlias),
		"DELETE": http.HandlerFunc(spah.removeServerAlias),
	}).Name(routeServersSelfAliasesSelf)
	apiRouter.Handle("/fileservers", handlers.MethodHandler{
		"GET": http.HandlerFunc(spah.getFileServers),
	}).Name(routeFileservers)
	spah.h = r
	spah.router = r
	return &spah
}

func (spah *serverPoolAdminHandler) writeLocation(w http.ResponseWriter, name string, params ...string) {
	var urlStr string
	if u, err := spah.router.GetRoute(name).URL(params...); err != nil {
		log.Print(err)
		urlStr = ""
	} else {
		urlStr = u.String()
	}
	w.Header().Set("Location", urlStr)
}

func (spah *serverPoolAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	spah.h.ServeHTTP(w, r)
}

type serverData struct {
	BindAddress string            `json:"bind_address"`
	Port        uint16            `json:"port"`
	Aliases     map[string]string `json:"aliases"`
}

func newServerDataFromServer(ds *Server) *serverData {
	aliases := ds.DirAliases.List()
	aliasesMap := make(map[string]string, len(aliases))
	for _, alias := range aliases {
		aliasesMap[alias] = ds.DirAliases.Get(alias)
	}
	host, _, _ := net.SplitHostPort(ds.Addr)
	return &serverData{
		BindAddress: host,
		Port:        ds.Port,
		Aliases:     aliasesMap,
	}
}

func (spah *serverPoolAdminHandler) serverOr404(w http.ResponseWriter, r *http.Request) *Server {
	sport := mux.Vars(r)["port"]
	port, err := strconv.Atoi(sport)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return nil
	}
	srv := spah.ServerPool.Get(uint16(port))
	if srv == nil {
		http.Error(w, fmt.Sprintf("server %d not found", port), http.StatusNotFound)
		return nil
	}
	return srv
}

func (spah *serverPoolAdminHandler) getServers(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	srvsData := make([]serverData, 0, len(spah.ServerPool.Srvs))
	for _, srv := range spah.ServerPool.Srvs {
		srvsData = append(srvsData, *newServerDataFromServer(srv))
	}
	if err := json.NewEncoder(w).Encode(srvsData); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) getServer(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

type createServerRequest struct {
	BindAddress string `json:"bind_address"`
}

func (spah *serverPoolAdminHandler) createServerWithRandomPort(w http.ResponseWriter, r *http.Request) {
	var req createServerRequest
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	addr := net.JoinHostPort(req.BindAddress, "0")
	srv, err := spah.ServerPool.Add(addr)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	// Wait for the server to be started
	<-srv.started
	spah.writeLocation(w, routeServersSelf, "port", strconv.Itoa(int(srv.Port)))
	w.WriteHeader(http.StatusCreated)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) createServer(w http.ResponseWriter, r *http.Request) {
	var req createServerRequest
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sport := mux.Vars(r)["port"]
	port, err := strconv.Atoi(sport)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	addr := net.JoinHostPort(req.BindAddress, strconv.Itoa(port))
	srv, err := spah.ServerPool.Add(addr)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	// Wait for the server to be started
	<-srv.started
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) removeServers(w http.ResponseWriter, r *http.Request) {
	errs := make([]error, 0)
	for _, srv := range spah.ServerPool.Srvs {
		if ok, err := spah.ServerPool.Remove(srv.Port); err != nil {
			errs = append(errs, err)
		} else if !ok {
			err := fmt.Errorf("error shutting down server on port %d", srv.Port)
			errs = append(errs, err)
		}
	}
	if len(errs) > 0 {
		var bufMsg bytes.Buffer
		for _, err := range errs {
			fmt.Fprintln(&bufMsg, err.Error())
		}
		apiErr := &adminApiError{apiErrTypeApiInternal, bufMsg.String()}
		w.WriteHeader(http.StatusInternalServerError)
		if err := json.NewEncoder(w).Encode(apiErr); err != nil {
			log.Print(err)
		}
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) removeServer(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	if ok, err := spah.ServerPool.Remove(srv.Port); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	} else if !ok {
		err := fmt.Errorf("error shutting down server on port %d", srv.Port)
		log.Print(err)
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getServerAliases(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	aliases := srv.DirAliases.List()
	aliasesMap := make(map[string]string, len(aliases))

	for _, alias := range aliases {
		aliasesMap[alias] = srv.DirAliases.Get(alias)
	}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(aliasesMap); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) removeServerAliases(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	aliases := srv.DirAliases.List()
	for _, alias := range aliases {
		srv.DirAliases.Delete(alias)
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	alias := mux.Vars(r)["alias"]
	aliasPath := srv.DirAliases.Get(alias)
	if aliasPath == "" {
		http.Error(w, fmt.Sprintf("server %d has no alias %q", srv.Port, alias), http.StatusNotFound)
		return
	}
	aliasesMap := map[string]string{alias: aliasPath}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(aliasesMap); err != nil {
		log.Print(err)
	}
}

type createServerAliasRequest struct {
	Path     string            `json:"path"`
	FsType   string            `json:"fs_type"`
	FsParams fileserver.Params `json:"fs_params"`
}

func (spah *serverPoolAdminHandler) createServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	alias := mux.Vars(r)["alias"]
	defer r.Body.Close()
	var req createServerAliasRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if _, err := srv.DirAliases.Put(alias, req.Path, req.FsType, req.FsParams); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) removeServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	alias := mux.Vars(r)["alias"]
	ok := srv.DirAliases.Delete(alias)
	if !ok {
		http.Error(w, fmt.Sprintf("server %d has no alias %q", srv.Port, alias), http.StatusNotFound)
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getFileServers(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(spah.ServerPool.fsf.Types()); err != nil {
		log.Print(err)
	}
}
<commit_msg>Update admin api to use extended aliases:<commit_after>package kraken

import (
	"bytes"
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
	"log"
	"net"
	"net/http"
	"strconv"

	"github.com/vincent-petithory/kraken/fileserver"
)

type serverPoolAdminHandler struct {
	*ServerPool
	h      http.Handler
	router *mux.Router
}

const (
	routeServers                = "servers"
	routeServersSelf            = "servers.self"
	routeServersSelfAliases     = "servers.self.aliases"
	routeServersSelfAliasesSelf = "servers.self.aliases.self"
	routeFileservers            = "fileservers"
)

type adminApiErrorType string

const (
	apiErrTypeBadRequest  adminApiErrorType = "bad_request_error"
	apiErrTypeApiInternal                   = "api_internal_error"
)

type adminApiError struct {
	Type adminApiErrorType `json:"type"`
	Msg  string            `json:"msg"`
}

func (e *adminApiError) String() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

func (e *adminApiError) Error() string {
	return e.Msg
}

func NewServerPoolAdminHandler(serverPool *ServerPool) *serverPoolAdminHandler {
	spah := serverPoolAdminHandler{ServerPool: serverPool}
	r := mux.NewRouter()
	apiRouter := r.PathPrefix("/api/").Subrouter()
	apiRouter.Handle("/servers", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServers),
		"POST":   http.HandlerFunc(spah.createServerWithRandomPort),
		"DELETE": http.HandlerFunc(spah.removeServers),
	}).Name(routeServers)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServer),
		"PUT":    http.HandlerFunc(spah.createServer),
		"DELETE": http.HandlerFunc(spah.removeServer),
	}).Name(routeServersSelf)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}/aliases", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServerAliases),
		"POST":   http.HandlerFunc(spah.createServerAlias),
		"DELETE": http.HandlerFunc(spah.removeServerAliases),
	}).Name(routeServersSelfAliases)
	apiRouter.Handle("/servers/{port:[0-9]{1,5}}/aliases/{alias}", handlers.MethodHandler{
		"GET":    http.HandlerFunc(spah.getServerAlias),
		"DELETE": http.HandlerFunc(spah.removeServerAlias),
	}).Name(routeServersSelfAliasesSelf)
	apiRouter.Handle("/fileservers", handlers.MethodHandler{
		"GET": http.HandlerFunc(spah.getFileServers),
	}).Name(routeFileservers)
	spah.h = r
	spah.router = r
	return &spah
}

func (spah *serverPoolAdminHandler) writeLocation(w http.ResponseWriter, name string, params ...string) {
	var urlStr string
	if u, err := spah.router.GetRoute(name).URL(params...); err != nil {
		log.Print(err)
		urlStr = ""
	} else {
		urlStr = u.String()
	}
	w.Header().Set("Location", urlStr)
}

func (spah *serverPoolAdminHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	spah.h.ServeHTTP(w, r)
}

type serverData struct {
	BindAddress string      `json:"bind_address"`
	Port        uint16      `json:"port"`
	Aliases     []aliasData `json:"aliases"`
}

func newServerDataFromServer(srv *Server) *serverData {
	aliasNames := srv.DirAliases.List()
	aliases := make([]aliasData, 0, len(aliasNames))
	for _, alias := range aliasNames {
		aliases = append(aliases, aliasData{
			ID:   aliasID(alias),
			Name: alias,
			Path: srv.DirAliases.Get(alias),
		})
	}
	host, _, _ := net.SplitHostPort(srv.Addr)
	return &serverData{
		BindAddress: host,
		Port:        srv.Port,
		Aliases:     aliases,
	}
}

type aliasData struct {
	ID   string `json:"id"`
	Name string `json:"name"`
	Path string `json:"path"`
}

func aliasID(alias string) string {
	h := sha1.New()
	h.Write([]byte(alias))
	b := h.Sum(nil)
	return fmt.Sprintf("%x", b)[0:7]
}

func (spah *serverPoolAdminHandler) serverOr404(w http.ResponseWriter, r *http.Request) *Server {
	sport := mux.Vars(r)["port"]
	port, err := strconv.Atoi(sport)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return nil
	}
	srv := spah.ServerPool.Get(uint16(port))
	if srv == nil {
		http.Error(w, fmt.Sprintf("server %d not found", port), http.StatusNotFound)
		return nil
	}
	return srv
}

func (spah *serverPoolAdminHandler) getServers(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	srvsData := make([]serverData, 0, len(spah.ServerPool.Srvs))
	for _, srv := range spah.ServerPool.Srvs {
		srvsData = append(srvsData, *newServerDataFromServer(srv))
	}
	if err := json.NewEncoder(w).Encode(srvsData); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) getServer(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

type createServerRequest struct {
	BindAddress string `json:"bind_address"`
}

func (spah *serverPoolAdminHandler) createServerWithRandomPort(w http.ResponseWriter, r *http.Request) {
	var req createServerRequest
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	addr := net.JoinHostPort(req.BindAddress, "0")
	srv, err := spah.ServerPool.Add(addr)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	// Wait for the server to be started
	<-srv.started
	spah.writeLocation(w, routeServersSelf, "port", strconv.Itoa(int(srv.Port)))
	w.WriteHeader(http.StatusCreated)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) createServer(w http.ResponseWriter, r *http.Request) {
	var req createServerRequest
	defer r.Body.Close()
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	sport := mux.Vars(r)["port"]
	port, err := strconv.Atoi(sport)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	addr := net.JoinHostPort(req.BindAddress, strconv.Itoa(port))
	srv, err := spah.ServerPool.Add(addr)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	// Wait for the server to be started
	<-srv.started
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(newServerDataFromServer(srv)); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) removeServers(w http.ResponseWriter, r *http.Request) {
	errs := make([]error, 0)
	for _, srv := range spah.ServerPool.Srvs {
		if ok, err := spah.ServerPool.Remove(srv.Port); err != nil {
			errs = append(errs, err)
		} else if !ok {
			err := fmt.Errorf("error shutting down server on port %d", srv.Port)
			errs = append(errs, err)
		}
	}
	if len(errs) > 0 {
		var bufMsg bytes.Buffer
		for _, err := range errs {
			fmt.Fprintln(&bufMsg, err.Error())
		}
		apiErr := &adminApiError{apiErrTypeApiInternal, bufMsg.String()}
		w.WriteHeader(http.StatusInternalServerError)
		if err := json.NewEncoder(w).Encode(apiErr); err != nil {
			log.Print(err)
		}
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) removeServer(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	if ok, err := spah.ServerPool.Remove(srv.Port); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	} else if !ok {
		err := fmt.Errorf("error shutting down server on port %d", srv.Port)
		log.Print(err)
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getServerAliases(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	aliasNames := srv.DirAliases.List()
	aliases := make([]aliasData, 0, len(aliasNames))
	for _, alias := range aliasNames {
		aliases = append(aliases, aliasData{
			ID:   aliasID(alias),
			Name: alias,
			Path: srv.DirAliases.Get(alias),
		})
	}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(aliases); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) removeServerAliases(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	aliases := srv.DirAliases.List()
	for _, alias := range aliases {
		srv.DirAliases.Delete(alias)
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	reqAliasID := mux.Vars(r)["alias"]
	var alias string
	for _, a := range srv.DirAliases.List() {
		if aliasID(a) == reqAliasID {
			alias = a
			break
		}
	}
	aliasPath := srv.DirAliases.Get(alias)
	if aliasPath == "" {
		http.Error(w, fmt.Sprintf("server %d has no alias %q", srv.Port, alias), http.StatusNotFound)
		return
	}

	aliasData := aliasData{
		ID:   aliasID(alias),
		Name: alias,
		Path: srv.DirAliases.Get(alias),
	}
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(aliasData); err != nil {
		log.Print(err)
	}
}

type createServerAliasRequest struct {
	Alias    string            `json:"alias"`
	Path     string            `json:"path"`
	FsType   string            `json:"fs_type"`
	FsParams fileserver.Params `json:"fs_params"`
}

func (spah *serverPoolAdminHandler) createServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	defer r.Body.Close()
	var req createServerAliasRequest
	if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	if _, err := srv.DirAliases.Put(req.Alias, req.Path, req.FsType, req.FsParams); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	aliasData := aliasData{
		ID:   aliasID(req.Alias),
		Name: req.Alias,
		Path: srv.DirAliases.Get(req.Alias),
	}
	spah.writeLocation(w, routeServersSelfAliasesSelf, "alias", aliasData.ID)
	w.WriteHeader(http.StatusCreated)
	if err := json.NewEncoder(w).Encode(aliasData); err != nil {
		log.Print(err)
	}
}

func (spah *serverPoolAdminHandler) removeServerAlias(w http.ResponseWriter, r *http.Request) {
	srv := spah.serverOr404(w, r)
	if srv == nil {
		return
	}
	reqAliasID := mux.Vars(r)["alias"]
	var alias string
	for _, a := range srv.DirAliases.List() {
		if aliasID(a) == reqAliasID {
			alias = a
			break
		}
	}
	ok := srv.DirAliases.Delete(alias)
	if !ok {
		http.Error(w, fmt.Sprintf("server %d has no alias %q", srv.Port, alias), http.StatusNotFound)
		return
	}
	w.WriteHeader(http.StatusOK)
}

func (spah *serverPoolAdminHandler) getFileServers(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	if err := json.NewEncoder(w).Encode(spah.ServerPool.fsf.Types()); err != nil {
		log.Print(err)
	}
}
<|endoftext|>


<commit_before>package cache

import (
	"context"
	"fmt"
	"go/token"

	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/memoize"
)

type tokenKey struct {
	file source.FileIdentity
}

type tokenHandle struct {
	handle *memoize.Handle
	file   source.FileHandle
}

type tokenData struct {
	memoize.NoCopy

	tok *token.File
	err error
}

func (c *cache) TokenHandle(fh source.FileHandle) source.TokenHandle {
	key := tokenKey{
		file: fh.Identity(),
	}
	h := c.store.Bind(key, func(ctx context.Context) interface{} {
		data := &tokenData{}
		data.tok, data.err = tokenFile(ctx, c, fh)
		return data
	})
	return &tokenHandle{
		handle: h,
	}
}

func (h *tokenHandle) File() source.FileHandle {
	return h.file
}

func (h *tokenHandle) Token(ctx context.Context) (*token.File, error) {
	v := h.handle.Get(ctx)
	if v == nil {
		return nil, ctx.Err()
	}
	data := v.(*tokenData)
	return data.tok, data.err
}

func tokenFile(ctx context.Context, c *cache, fh source.FileHandle) (*token.File, error) {
	// First, check if we already have a parsed AST for this file's handle.
	for _, mode := range []source.ParseMode{
		source.ParseHeader,
		source.ParseExported,
		source.ParseFull,
	} {
		pk := parseKey{
			file: fh.Identity(),
			mode: mode,
		}
		pd, ok := c.store.Cached(pk).(*parseGoData)
		if !ok {
			continue
		}
		if pd.ast == nil {
			continue
		}
		if !pd.ast.Pos().IsValid() {
			continue
		}
		return c.FileSet().File(pd.ast.Pos()), nil
	}
	// We have not yet parsed this file.
	buf, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	tok := c.FileSet().AddFile(fh.Identity().URI.Filename(), -1, len(buf))
	if tok == nil {
		return nil, fmt.Errorf("no token.File for %s", fh.Identity().URI)
	}
	return tok, nil
}
<commit_msg>internal/lsp: fix bug in creating token.Files<commit_after>package cache

import (
	"context"
	"fmt"
	"go/token"

	"golang.org/x/tools/internal/lsp/source"
	"golang.org/x/tools/internal/memoize"
)

type tokenKey struct {
	file source.FileIdentity
}

type tokenHandle struct {
	handle *memoize.Handle
	file   source.FileHandle
}

type tokenData struct {
	memoize.NoCopy

	tok *token.File
	err error
}

func (c *cache) TokenHandle(fh source.FileHandle) source.TokenHandle {
	key := tokenKey{
		file: fh.Identity(),
	}
	h := c.store.Bind(key, func(ctx context.Context) interface{} {
		data := &tokenData{}
		data.tok, data.err = tokenFile(ctx, c, fh)
		return data
	})
	return &tokenHandle{
		handle: h,
	}
}

func (h *tokenHandle) File() source.FileHandle {
	return h.file
}

func (h *tokenHandle) Token(ctx context.Context) (*token.File, error) {
	v := h.handle.Get(ctx)
	if v == nil {
		return nil, ctx.Err()
	}
	data := v.(*tokenData)
	return data.tok, data.err
}

func tokenFile(ctx context.Context, c *cache, fh source.FileHandle) (*token.File, error) {
	// First, check if we already have a parsed AST for this file's handle.
	for _, mode := range []source.ParseMode{
		source.ParseHeader,
		source.ParseExported,
		source.ParseFull,
	} {
		pk := parseKey{
			file: fh.Identity(),
			mode: mode,
		}
		pd, ok := c.store.Cached(pk).(*parseGoData)
		if !ok {
			continue
		}
		if pd.ast == nil {
			continue
		}
		if !pd.ast.Pos().IsValid() {
			continue
		}
		return c.FileSet().File(pd.ast.Pos()), nil
	}
	// We have not yet parsed this file.
	buf, _, err := fh.Read(ctx)
	if err != nil {
		return nil, err
	}
	tok := c.FileSet().AddFile(fh.Identity().URI.Filename(), -1, len(buf))
	if tok == nil {
		return nil, fmt.Errorf("no token.File for %s", fh.Identity().URI)
	}
	tok.SetLinesForContent(buf)
	return tok, nil
}
<|endoftext|>


<commit_before>package runtime

const Code = `
	let __defineProperty = Object.defineProperty
	let __hasOwnProperty = Object.prototype.hasOwnProperty
	let __getOwnPropertySymbols = Object.getOwnPropertySymbols
	let __getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
	let __propertyIsEnumerable = Object.prototype.propertyIsEnumerable

	export let __pow = Math.pow
	export let __assign = Object.assign

	// For object rest patterns
	export let __restKey = key => typeof key === 'symbol' ? key : key + ''
	export let __rest = (source, exclude) => {
		let target = {}
		for (let prop in source)
			if (__hasOwnProperty.call(source, prop) && exclude.indexOf(prop) < 0)
				target[prop] = source[prop]
		if (source != null && __getOwnPropertySymbols)
			for (let prop of __getOwnPropertySymbols(source))
				if (exclude.indexOf(prop) < 0 && __propertyIsEnumerable.call(source, prop))
					target[prop] = source[prop]
		return target
	}

	// Wraps a CommonJS closure and returns a require() function
	export let __commonJS = (callback, module) => () => {
		if (!module) {
			module = {exports: {}}
			callback(module.exports, module)
		}
		return module.exports
	}

	// Used to implement ES6 exports to CommonJS
	let __markAsModule = target => {
		return __defineProperty(target, '__esModule', { value: true })
	}
	export let __export = (target, all) => {
		__markAsModule(target)
		for (let name in all)
			__defineProperty(target, name, { get: all[name], enumerable: true })
	}
	export let __exportStar = (target, module) => {
		__markAsModule(target)
		for (let key in module)
			if (__hasOwnProperty.call(module, key) && !__hasOwnProperty.call(target, key) && key !== 'default')
				__defineProperty(target, key, { get: () => module[key], enumerable: true })
		return target
	}

	// Converts the module from CommonJS to ES6 if necessary
	export let __toModule = module => {
		if (module && module.__esModule)
			return module
		return __exportStar(
			__defineProperty({}, 'default', { value: module, enumerable: true }),
			module)
	}

	// For TypeScript decorators
	// - kind === undefined: class
	// - kind === 1: method, parameter
	// - kind === 2: field
	export let __decorate = (decorators, target, key, kind) => {
		var result = kind > 1 ? void 0 : kind ? __getOwnPropertyDescriptor(target, key) : target
		for (var i = decorators.length - 1, decorator; i >= 0; i--)
			if (decorator = decorators[i])
				result = (kind ? decorator(target, key, result) : decorator(result)) || result
		if (kind && result)
			__defineProperty(target, key, result)
		return result
	}
	export let __param = (index, decorator) => (target, key) => decorator(target, key, index)

	// For class members
	export let __publicField = (obj, key, value) => {
		if (key in obj) return __defineProperty(obj, key, {enumerable: true, configurable: true, writable: true, value})
		else return obj[key] = value
	}
	let __accessCheck = (obj, member, msg) => {
		if (!member.has(obj)) throw TypeError('Cannot ' + msg)
	}
	export let __privateGet = (obj, member, getter) => {
		__accessCheck(obj, member, 'read from private field')
		return getter ? getter.call(obj) : member.get(obj)
	}
	export let __privateSet = (obj, member, value, setter) => {
		__accessCheck(obj, member, 'write to private field')
		setter ? setter.call(obj, value) : member.set(obj, value)
		return value
	}
	export let __privateMethod = (obj, member, method) => {
		__accessCheck(obj, member, 'access private method')
		return method
	}

	// This helps for lowering async functions
	export let __async = (__this, __arguments, generator) => {
		return new Promise((resolve, reject) => {
			let fulfilled = value => {
				try {
					step(generator.next(value))
				} catch (e) {
					reject(e)
				}
			}
			let rejected = value => {
				try {
					step(generator.throw(value))
				} catch (e) {
					reject(e)
				}
			}
			let step = result => {
				return result.done ? resolve(result.value) : Promise.resolve(result.value).then(fulfilled, rejected)
			}
			step((generator = generator.apply(__this, __arguments)).next())
		})
	}
`

// The TypeScript decorator transform behaves similar to the official
// TypeScript compiler.
//
// One difference is that the "__decorate" function doesn't contain a reference
// to the non-existent "Reflect.decorate" function. This function was never
// standardized and checking for it is wasted code (as well as a potentially
// dangerous cause of unintentional behavior changes in the future).
//
// Another difference is that the "__decorate" function doesn't take in an
// optional property descriptor like it does in the official TypeScript
// compiler's support code. This appears to be a dead code path in the official
// support code that is only there for legacy reasons.
//
// Here are some examples of how esbuild's decorator transform works:
//
// ============================= Class decorator ==============================
//
//   // TypeScript                      // JavaScript
//   @dec                               let C = class {
//   class C {                          };
//   }                                  C = __decorate([
//                                        dec
//                                      ], C);
//
// ============================ Method decorator ==============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     @dec                               foo() {}
//     foo() {}                         }
//   }                                  __decorate([
//                                        dec
//                                      ], C.prototype, 'foo', 1);
//
// =========================== Parameter decorator ============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     foo(@dec bar) {}                   foo(bar) {}
//   }                                  }
//                                      __decorate([
//                                        __param(0, dec)
//                                      ], C.prototype, 'foo', 1);
//
// ============================= Field decorator ==============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     @dec                               constructor() {
//     foo = 123                            this.foo = 123
//   }                                    }
//                                      }
//                                      __decorate([
//                                        dec
//                                      ], C.prototype, 'foo', 2);
<commit_msg>optimize export star helper to avoid string keys<commit_after>package runtime

const Code = `
	let __defineProperty = Object.defineProperty
	let __hasOwnProperty = Object.prototype.hasOwnProperty
	let __getOwnPropertySymbols = Object.getOwnPropertySymbols
	let __getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
	let __propertyIsEnumerable = Object.prototype.propertyIsEnumerable

	export let __pow = Math.pow
	export let __assign = Object.assign

	// For object rest patterns
	export let __restKey = key => typeof key === 'symbol' ? key : key + ''
	export let __rest = (source, exclude) => {
		let target = {}
		for (let prop in source)
			if (__hasOwnProperty.call(source, prop) && exclude.indexOf(prop) < 0)
				target[prop] = source[prop]
		if (source != null && __getOwnPropertySymbols)
			for (let prop of __getOwnPropertySymbols(source))
				if (exclude.indexOf(prop) < 0 && __propertyIsEnumerable.call(source, prop))
					target[prop] = source[prop]
		return target
	}

	// Wraps a CommonJS closure and returns a require() function
	export let __commonJS = (callback, module) => () => {
		if (!module) {
			module = {exports: {}}
			callback(module.exports, module)
		}
		return module.exports
	}

	// Used to implement ES6 exports to CommonJS
	let __markAsModule = target => {
		return __defineProperty(target, '__esModule', { value: true })
	}
	export let __export = (target, all) => {
		__markAsModule(target)
		for (let name in all)
			__defineProperty(target, name, { get: all[name], enumerable: true })
	}
	export let __exportStar = (target, module) => {
		__markAsModule(target)
		if (typeof module === 'object' || typeof module === 'function')
			for (let key in module)
				if (__hasOwnProperty.call(module, key) && !__hasOwnProperty.call(target, key) && key !== 'default')
					__defineProperty(target, key, { get: () => module[key], enumerable: true })
		return target
	}

	// Converts the module from CommonJS to ES6 if necessary
	export let __toModule = module => {
		if (module && module.__esModule)
			return module
		return __exportStar(
			__defineProperty({}, 'default', { value: module, enumerable: true }),
			module)
	}

	// For TypeScript decorators
	// - kind === undefined: class
	// - kind === 1: method, parameter
	// - kind === 2: field
	export let __decorate = (decorators, target, key, kind) => {
		var result = kind > 1 ? void 0 : kind ? __getOwnPropertyDescriptor(target, key) : target
		for (var i = decorators.length - 1, decorator; i >= 0; i--)
			if (decorator = decorators[i])
				result = (kind ? decorator(target, key, result) : decorator(result)) || result
		if (kind && result)
			__defineProperty(target, key, result)
		return result
	}
	export let __param = (index, decorator) => (target, key) => decorator(target, key, index)

	// For class members
	export let __publicField = (obj, key, value) => {
		if (key in obj) return __defineProperty(obj, key, {enumerable: true, configurable: true, writable: true, value})
		else return obj[key] = value
	}
	let __accessCheck = (obj, member, msg) => {
		if (!member.has(obj)) throw TypeError('Cannot ' + msg)
	}
	export let __privateGet = (obj, member, getter) => {
		__accessCheck(obj, member, 'read from private field')
		return getter ? getter.call(obj) : member.get(obj)
	}
	export let __privateSet = (obj, member, value, setter) => {
		__accessCheck(obj, member, 'write to private field')
		setter ? setter.call(obj, value) : member.set(obj, value)
		return value
	}
	export let __privateMethod = (obj, member, method) => {
		__accessCheck(obj, member, 'access private method')
		return method
	}

	// This helps for lowering async functions
	export let __async = (__this, __arguments, generator) => {
		return new Promise((resolve, reject) => {
			let fulfilled = value => {
				try {
					step(generator.next(value))
				} catch (e) {
					reject(e)
				}
			}
			let rejected = value => {
				try {
					step(generator.throw(value))
				} catch (e) {
					reject(e)
				}
			}
			let step = result => {
				return result.done ? resolve(result.value) : Promise.resolve(result.value).then(fulfilled, rejected)
			}
			step((generator = generator.apply(__this, __arguments)).next())
		})
	}
`

// The TypeScript decorator transform behaves similar to the official
// TypeScript compiler.
//
// One difference is that the "__decorate" function doesn't contain a reference
// to the non-existent "Reflect.decorate" function. This function was never
// standardized and checking for it is wasted code (as well as a potentially
// dangerous cause of unintentional behavior changes in the future).
//
// Another difference is that the "__decorate" function doesn't take in an
// optional property descriptor like it does in the official TypeScript
// compiler's support code. This appears to be a dead code path in the official
// support code that is only there for legacy reasons.
//
// Here are some examples of how esbuild's decorator transform works:
//
// ============================= Class decorator ==============================
//
//   // TypeScript                      // JavaScript
//   @dec                               let C = class {
//   class C {                          };
//   }                                  C = __decorate([
//                                        dec
//                                      ], C);
//
// ============================ Method decorator ==============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     @dec                               foo() {}
//     foo() {}                         }
//   }                                  __decorate([
//                                        dec
//                                      ], C.prototype, 'foo', 1);
//
// =========================== Parameter decorator ============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     foo(@dec bar) {}                   foo(bar) {}
//   }                                  }
//                                      __decorate([
//                                        __param(0, dec)
//                                      ], C.prototype, 'foo', 1);
//
// ============================= Field decorator ==============================
//
//   // TypeScript                      // JavaScript
//   class C {                          class C {
//     @dec                               constructor() {
//     foo = 123                            this.foo = 123
//   }                                    }
//                                      }
//                                      __decorate([
//                                        dec
//                                      ], C.prototype, 'foo', 2);
<|endoftext|>


<commit_before>package storage

import (
	"github.com/rafaeljusto/toglacier/internal/archive"
	"github.com/rafaeljusto/toglacier/internal/cloud"
)

// Backup stores the cloud location of the backup and some extra information
// about the files of the backup.
type Backup struct {
	Backup cloud.Backup
	Info   archive.Info
}

// Backups represents a sorted list of backups that are ordered by creation
// date. It has the necessary methods so you could use the sort package of the
// standard library.
type Backups []Backup

// Len returns the number of backups.
func (b Backups) Len() int { return len(b) }

// Less compares two positions of the slice and verifies the preference. They
// are ordered from the newest backup to the oldest.
func (b Backups) Less(i, j int) bool { return b[i].Backup.CreatedAt.After(b[j].Backup.CreatedAt) }

// Swap change the backups position inside the slice.
func (b Backups) Swap(i, j int) { b[i], b[j] = b[j], b[i] }

// Storage represents all commands to manage backups information locally. After
// the backup is uploaded we must keep track of them locally to speed up
// recovery and cloud cleanup (remove old ones).
type Storage interface {
	// Save a backup information.
	Save(Backup) error

	// List all backup informations in the storage.
	List() (Backups, error)

	// Remove a specific backup information from the storage.
	Remove(id string) error
}
<commit_msg>Add reminder to improve the code<commit_after>package storage

import (
	"github.com/rafaeljusto/toglacier/internal/archive"
	"github.com/rafaeljusto/toglacier/internal/cloud"
)

// Backup stores the cloud location of the backup and some extra information
// about the files of the backup.
type Backup struct {
	Backup cloud.Backup // TODO: rename this attribute?
	Info   archive.Info
}

// Backups represents a sorted list of backups that are ordered by creation
// date. It has the necessary methods so you could use the sort package of the
// standard library.
type Backups []Backup

// Len returns the number of backups.
func (b Backups) Len() int { return len(b) }

// Less compares two positions of the slice and verifies the preference. They
// are ordered from the newest backup to the oldest.
func (b Backups) Less(i, j int) bool { return b[i].Backup.CreatedAt.After(b[j].Backup.CreatedAt) }

// Swap change the backups position inside the slice.
func (b Backups) Swap(i, j int) { b[i], b[j] = b[j], b[i] }

// Storage represents all commands to manage backups information locally. After
// the backup is uploaded we must keep track of them locally to speed up
// recovery and cloud cleanup (remove old ones).
type Storage interface {
	// Save a backup information.
	Save(Backup) error

	// List all backup informations in the storage.
	List() (Backups, error)

	// Remove a specific backup information from the storage.
	Remove(id string) error
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type animeList []anime

type anime struct {
	ID         string    `bson:"id"`
	Name       string    `bson:"name"`
	Href       string    `bson:"href"`
	Episode    int       `bson:"ep"`
	Subs       []string  `bson:"subs"`
	LastUpdate time.Time `bson:"lastUpdate"`
}

//LIMIT is a time constant for 22 Days (for removal of outdated db entries)
const LIMIT = 22 * 24 * time.Hour

//Gets every anime in animeList db and returns it as AnimeList type
func getAnimeList() (result animeList) {
	err := DBanimeList.Find(nil).Sort("lastUpdate").All(&result)
	if err != nil {
		log.Println("getAnimeList() => Find() error:\t", err)
	}
	return
}

//db maintanance process calls animeMaintanance function once on startup
//and after every interval time.Duration
func maintainAnimeListProcess(interval time.Duration) {
	maintainAnimeList()
	log.Println("Next maintanance in ", interval.String())

	for _ = range time.Tick(interval) {
		maintainAnimeList()
		log.Println("Next maintanance in ", interval.String())
	}
}

//db maintanance function called on app startup and after interval duration
//deletes entries over LIMIT days old
//gets urls for entries that don't have them
func maintainAnimeList() {
	removals, updates := 0, 0
	animeList := getAnimeList()
	now := time.Now()
	for _, a := range animeList {
		if len(a.Href) < 5 {
			log.Println("Getting href for ", a.Name)
			a.GetHref()
		}
		if now.Sub(a.LastUpdate) > LIMIT {
			a.Remove()
		}
	}
	log.Printf("AUTO-MAINTANANCE: animeList updated! (removed: %d | updated: %d)\n",
		removals, updates)
}

//Inserts a new anime entry to db
//Generates unique id if it doesent exist
func (a *anime) Insert() {
	if a.ID == "" {
		a.GenID()
	}
	a.LastUpdate = time.Now()
	DBanimeList.Insert(a)
}

//Remove anime from db by Anime.Name or Anime.Id
func (a anime) Remove() {
	if a.Name != "" {
		DBanimeList.Remove(bson.M{"name": a.Name})
	} else if a.ID != "" {
		DBanimeList.Remove(bson.M{"id": a.ID})
	}
}

//Updates the db entry with up-to-date episode number
func (a *anime) UpdateEp() {
	updateQuery := bson.M{
		"$set": bson.M{
			"ep":         a.Episode,
			"lastUpdate": time.Now(),
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"name": a.Name}).Apply(change, a)
}

//Adds new sub Name to the db entry of Anime.Id
func (a *anime) AddSub(sub string) {
	updateQuery := bson.M{
		"$addToSet": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Removes the sub from the db entry of Anime.Id
func (a *anime) RemoveSub(sub string) {
	updateQuery := bson.M{
		"$pull": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Generates a unique 3char alphanumeric ID
//Not case sensitive
func (a *anime) GenID() {
	var id, byteList string
	byteList = "0123456789abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 3; i++ {
		id += string(byteList[rand.Intn(len(byteList))])
	}

	if n, _ := DBanimeList.Find(bson.M{"id": id}).Count(); n == 0 {
		a.ID = id
	} else {
		a.GenID()
	}
}

//Gets href for Anime.Name
func (a *anime) GetHref() {
	doc, err := goquery.NewDocument("http://horriblesubs.info/current-season/")
	if err != nil {
		fmt.Println(err)
	} else {
		doc.Find(".ind-show.linkful").Each(func(i int, s *goquery.Selection) {
			name, _ := s.Find("a").Attr("title")
			fmt.Println(name)
			url, _ := s.Find("a").Attr("href")
			fmt.Println(url)
			if strings.ToLower(name) == strings.ToLower(a.Name) {
				newHref := fmt.Sprintf("http://horriblesubs.info%s", url)
				updateQuery := bson.M{
					"$set": bson.M{
						"href":       newHref,
						"lastUpdate": time.Now(),
					},
				}
				DBanimeList.Update(bson.M{"name": a.Name}, updateQuery)
				return
			}
		})
	}
}

//Checks if there is already an entry in db
//with the same id OR the same name
//returns true if it already exists
func (a anime) Exists() bool {
	query := bson.M{
		"$or": []interface{}{
			bson.M{"id": a.ID},
			bson.M{"name": a.Name},
		},
	}
	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//Checks if episode # already exists in db
//Returns true if episode in db is outdated and
//needs to be updated and false if db is already
//up to date
func (a anime) NewEpisode() bool {
	query := bson.M{
		"name": a.Name,
		"ep": bson.M{
			"$lt": a.Episode,
		},
	}

	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//returns length of AnimeList
//used for sort interface
func (a animeList) Len() int {
	return len(a)
}

//Checks if index i should sort before index j
//used for sort interface
func (a animeList) Less(i, j int) bool {
	if len(a[i].Subs) > len(a[j].Subs) {
		return true
	}
	return false
}

//Swaps the values of i and j indexes
//used for sort interface
func (a animeList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<commit_msg>scrapper test<commit_after>package main

import (
	"fmt"
	"log"
	"math/rand"
	"strings"
	"time"

	"github.com/PuerkitoBio/goquery"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type animeList []anime

type anime struct {
	ID         string    `bson:"id"`
	Name       string    `bson:"name"`
	Href       string    `bson:"href"`
	Episode    int       `bson:"ep"`
	Subs       []string  `bson:"subs"`
	LastUpdate time.Time `bson:"lastUpdate"`
}

//LIMIT is a time constant for 22 Days (for removal of outdated db entries)
const LIMIT = 22 * 24 * time.Hour

//Gets every anime in animeList db and returns it as AnimeList type
func getAnimeList() (result animeList) {
	err := DBanimeList.Find(nil).Sort("lastUpdate").All(&result)
	if err != nil {
		log.Println("getAnimeList() => Find() error:\t", err)
	}
	return
}

//db maintanance process calls animeMaintanance function once on startup
//and after every interval time.Duration
func maintainAnimeListProcess(interval time.Duration) {
	maintainAnimeList()
	log.Println("Next maintanance in ", interval.String())

	for _ = range time.Tick(interval) {
		maintainAnimeList()
		log.Println("Next maintanance in ", interval.String())
	}
}

//db maintanance function called on app startup and after interval duration
//deletes entries over LIMIT days old
//gets urls for entries that don't have them
func maintainAnimeList() {
	removals, updates := 0, 0
	animeList := getAnimeList()
	now := time.Now()
	for _, a := range animeList {
		if len(a.Href) < 5 {
			log.Println("Getting href for ", a.Name)
			a.GetHref()
		}
		if now.Sub(a.LastUpdate) > LIMIT {
			a.Remove()
		}
	}
	log.Printf("AUTO-MAINTANANCE: animeList updated! (removed: %d | updated: %d)\n",
		removals, updates)
}

//Inserts a new anime entry to db
//Generates unique id if it doesent exist
func (a *anime) Insert() {
	if a.ID == "" {
		a.GenID()
	}
	a.LastUpdate = time.Now()
	DBanimeList.Insert(a)
}

//Remove anime from db by Anime.Name or Anime.Id
func (a anime) Remove() {
	if a.Name != "" {
		DBanimeList.Remove(bson.M{"name": a.Name})
	} else if a.ID != "" {
		DBanimeList.Remove(bson.M{"id": a.ID})
	}
}

//Updates the db entry with up-to-date episode number
func (a *anime) UpdateEp() {
	updateQuery := bson.M{
		"$set": bson.M{
			"ep":         a.Episode,
			"lastUpdate": time.Now(),
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"name": a.Name}).Apply(change, a)
}

//Adds new sub Name to the db entry of Anime.Id
func (a *anime) AddSub(sub string) {
	updateQuery := bson.M{
		"$addToSet": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Removes the sub from the db entry of Anime.Id
func (a *anime) RemoveSub(sub string) {
	updateQuery := bson.M{
		"$pull": bson.M{
			"subs": sub,
		},
	}
	change := mgo.Change{
		Update:    updateQuery,
		Upsert:    false,
		Remove:    false,
		ReturnNew: true,
	}
	DBanimeList.Find(bson.M{"id": a.ID}).Apply(change, a)
}

//Generates a unique 3char alphanumeric ID
//Not case sensitive
func (a *anime) GenID() {
	var id, byteList string
	byteList = "0123456789abcdefghijklmnopqrstuvwxyz"
	for i := 0; i < 3; i++ {
		id += string(byteList[rand.Intn(len(byteList))])
	}

	if n, _ := DBanimeList.Find(bson.M{"id": id}).Count(); n == 0 {
		a.ID = id
	} else {
		a.GenID()
	}
}

//Gets href for Anime.Name
func (a *anime) GetHref() {
	scrapper, target :=
		"http://scraper-422.rhcloud.com/?href=",
		"http://horriblesubs.info/current-season/"
	doc, err := goquery.NewDocument(scrapper + target)
	if err != nil {
		fmt.Println(err)
	} else {
		doc.Find(".ind-show.linkful").Each(func(i int, s *goquery.Selection) {
			name, _ := s.Find("a").Attr("title")
			fmt.Println(name)
			url, _ := s.Find("a").Attr("href")
			fmt.Println(url)
			if strings.ToLower(name) == strings.ToLower(a.Name) {
				newHref := fmt.Sprintf("http://horriblesubs.info%s", url)
				updateQuery := bson.M{
					"$set": bson.M{
						"href":       newHref,
						"lastUpdate": time.Now(),
					},
				}
				DBanimeList.Update(bson.M{"name": a.Name}, updateQuery)
				return
			}
		})
	}
}

//Checks if there is already an entry in db
//with the same id OR the same name
//returns true if it already exists
func (a anime) Exists() bool {
	query := bson.M{
		"$or": []interface{}{
			bson.M{"id": a.ID},
			bson.M{"name": a.Name},
		},
	}
	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//Checks if episode # already exists in db
//Returns true if episode in db is outdated and
//needs to be updated and false if db is already
//up to date
func (a anime) NewEpisode() bool {
	query := bson.M{
		"name": a.Name,
		"ep": bson.M{
			"$lt": a.Episode,
		},
	}

	if n, _ := DBanimeList.Find(query).Count(); n == 0 {
		return false
	}
	return true
}

//returns length of AnimeList
//used for sort interface
func (a animeList) Len() int {
	return len(a)
}

//Checks if index i should sort before index j
//used for sort interface
func (a animeList) Less(i, j int) bool {
	if len(a[i].Subs) > len(a[j].Subs) {
		return true
	}
	return false
}

//Swaps the values of i and j indexes
//used for sort interface
func (a animeList) Swap(i, j int) {
	a[i], a[j] = a[j], a[i]
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"

	"github.com/hawx/aoide/cmd"
	"github.com/hawx/aoide/data"
	"github.com/hawx/aoide/tools/add"
	"github.com/hawx/aoide/tools/index"
	"github.com/hawx/aoide/tools/organise"
	"github.com/hawx/hadfield"
	"github.com/stvp/go-toml-config"
)

var (
	configFile = flag.String("config", "config.toml", "")
	help       = flag.Bool("help", false, "")

	musicDir    = config.String("musicDir", "")
	playlistDir = config.String("playlistDir", "")
	dbFile      = config.String("dbFile", "")
)

var templates = hadfield.Templates{
	Usage: `Usage: aoide [command] [arguments]

  A music library manager.

  Commands: {{range .}}
    {{.Name | printf "%-15s"}} # {{.Short}}{{end}}
`,
	Help: `usage: test {{.Usage}}
{{.Long}}
`,
}

func main() {
	flag.Parse()

	if *help {
		log.Println("Usage: aoide COMMAND [options]")
	}

	if err := config.Parse(*configFile); err != nil {
		log.Fatal("toml:", err)
	}

	if musicDir == nil || playlistDir == nil || dbFile == nil {
		log.Fatal("toml: must define musicDir, playlistDir and dbFile")
	}

	db, err := data.Open(*dbFile)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	adder := add.New(*musicDir, db)
	indexer := index.New(*musicDir, db)
	organiser := organise.New(*musicDir, *playlistDir, db)

	var commands = hadfield.Commands{
		cmd.Add(adder),
		cmd.Index(indexer),
		cmd.Organise(organiser),
	}

	hadfield.Run(commands, templates)
}
<commit_msg>Get config location using XDG_* vars<commit_after>package main

import (
	"flag"
	"log"
	"os"
	"strings"

	"github.com/hawx/aoide/cmd"
	"github.com/hawx/aoide/data"
	"github.com/hawx/aoide/tools/add"
	"github.com/hawx/aoide/tools/index"
	"github.com/hawx/aoide/tools/organise"
	"github.com/hawx/hadfield"
	"github.com/stvp/go-toml-config"
)

var (
	help = flag.Bool("help", false, "")

	musicDir    = config.String("musicDir", "")
	playlistDir = config.String("playlistDir", "")
	dbFile      = config.String("dbFile", "")
)

var templates = hadfield.Templates{
	Usage: `Usage: aoide [command] [arguments]

  A music library manager.

  By default will search for config at $XDG_CONFIG_HOME/aoide/rc, or in one of
  $XDG_CONFIG_DIRS called aoide/rc; otherwise tries to load ./config.toml. This
  configuartion file must include entries like:

    musicDir = "/path/to/music"
    playlistDir = /path/to/playlists"
    dbFile = "/path/to/db"

  Commands: {{range .}}
    {{.Name | printf "%-15s"}} # {{.Short}}{{end}}
`,
	Help: `usage: test {{.Usage}}
{{.Long}}
`,
}

func findConfigFile() string {
	const (
		configSuffix         = "/aoide/rc"
		defaultXdgConfigDirs = "/etc/xdg"
	)

	var (
		defaultXdgConfigHome = os.ExpandEnv("$HOME/.config")
		xdgConfigDirs        = os.Getenv("XDG_CONFIG_DIRS")
		xdgConfigHome        = os.Getenv("XDG_CONFIG_HOME")
	)

	if xdgConfigDirs == "" {
		xdgConfigDirs = defaultXdgConfigDirs
	}
	if xdgConfigHome == "" {
		xdgConfigHome = defaultXdgConfigHome
	}

	if _, err := os.Stat(xdgConfigHome + configSuffix); err == nil {
		return xdgConfigHome + configSuffix
	}

	for _, path := range strings.Split(xdgConfigDirs, ":") {
		if _, err := os.Stat(path + configSuffix); err == nil {
			return path + configSuffix
		}
	}

	return ""
}

func main() {
	flag.Parse()

	if *help {
		log.Println("Usage: aoide COMMAND [options]")
	}

	if err := config.Parse(findConfigFile()); err != nil {
		log.Fatal("toml:", err)
	}

	if musicDir == nil || playlistDir == nil || dbFile == nil {
		log.Fatal("toml: must define musicDir, playlistDir and dbFile")
	}

	db, err := data.Open(*dbFile)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	adder := add.New(*musicDir, db)
	indexer := index.New(*musicDir, db)
	organiser := organise.New(*musicDir, *playlistDir, db)

	var commands = hadfield.Commands{
		cmd.Add(adder),
		cmd.Index(indexer),
		cmd.Organise(organiser),
	}

	hadfield.Run(commands, templates)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"
)

const apiEndpoint string = "https://api.api.ai/v1/%s?v=%s"
const apiVersion string = "20150910"

var apiAccessToken = os.Getenv("APIAI_ACCESS_TOKEN")

//APIAIRequest : Incoming request format from APIAI
type APIAIRequest struct {
	ID        string    `json:"id"`
	Timestamp time.Time `json:"timestamp"`
	Result    struct {
		Source           string            `json:"source"`
		ResolvedQuery    string            `json:"resolvedQuery"`
		Action           string            `json:"action"`
		ActionIncomplete bool              `json:"actionIncomplete"`
		Parameters       map[string]string `json:"parameters"`
		Contexts         []struct {
			Name       string `json:"name"`
			Parameters struct {
				Name string `json:"name"`
			} `json:"parameters"`
			Lifespan int `json:"lifespan"`
		} `json:"contexts"`
		Metadata struct {
			IntentID   string `json:"intentId"`
			IntentName string `json:"intentName"`
		} `json:"metadata"`
		Fulfillment struct {
			Speech      string `json:"speech"`
			DisplayText string `json:"displayText"`
			Source      string `json:"source"`
		} `json:"fulfillment"`
	} `json:"result"`
	Status struct {
		Code      int    `json:"code"`
		ErrorType string `json:"errorType"`
	} `json:"status"`
}

//APIAIMessage : Response Message Structure
type APIAIMessage struct {
	Speech      string `json:"speech"`
	DisplayText string `json:"displayText"`
	Source      string `json:"source"`
}

// PerformRequestToAPIAi sends natural language text and information as query parameters to API.AI
func PerformRequestToAPIAi(text string) (APIAIRequest, error) {

	record := APIAIRequest{}
	myURL := fmt.Sprintf(apiEndpoint, "query", apiVersion)
	myURL = myURL + "&query=" + url.QueryEscape(text) + "&lang=en" + "&sessionId=1234567890"

	// Build the request
	req, err := http.NewRequest("GET", myURL, nil)
	if err != nil {
		log.Fatalf("The /GET request to %s failed", myURL)
		return record, err
	}

	// Replace authToken by your Client access token
	authValue := "Bearer " + apiAccessToken
	req.Header.Add("Authorization", authValue)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Do: ", err)
		return record, err
	}

	// Callers should defer the close of resp.Body when done reading from it
	defer resp.Body.Close()

	// Use json.Decode for reading streams of JSON data
	if err := json.NewDecoder(resp.Body).Decode(&record); err != nil {
		log.Println(err)
	}

	return record, nil
}

// ResolveEmoji converts the weather code into an emoji
func ResolveEmoji(weatherCode string) (emoji string) {

	switch weatherCode {
	case "11", "12":
		return "🌧☔"
	case "16":
		return "🌨❄"
	case "20":
		return "🌫"
	case "24":
		return "💨"
	case "25":
		return "🐧🐧"
	case "32":
		return "☀"
	case "36":
		return "🔥🔥"
	default:
		fmt.Printf("%s.", weatherCode)
		return ""
	}

}
<commit_msg>Added more emojis<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"time"
)

const apiEndpoint string = "https://api.api.ai/v1/%s?v=%s"
const apiVersion string = "20150910"

var apiAccessToken = os.Getenv("APIAI_ACCESS_TOKEN")

//APIAIRequest : Incoming request format from APIAI
type APIAIRequest struct {
	ID        string    `json:"id"`
	Timestamp time.Time `json:"timestamp"`
	Result    struct {
		Source           string            `json:"source"`
		ResolvedQuery    string            `json:"resolvedQuery"`
		Action           string            `json:"action"`
		ActionIncomplete bool              `json:"actionIncomplete"`
		Parameters       map[string]string `json:"parameters"`
		Contexts         []struct {
			Name       string `json:"name"`
			Parameters struct {
				Name string `json:"name"`
			} `json:"parameters"`
			Lifespan int `json:"lifespan"`
		} `json:"contexts"`
		Metadata struct {
			IntentID   string `json:"intentId"`
			IntentName string `json:"intentName"`
		} `json:"metadata"`
		Fulfillment struct {
			Speech      string `json:"speech"`
			DisplayText string `json:"displayText"`
			Source      string `json:"source"`
		} `json:"fulfillment"`
	} `json:"result"`
	Status struct {
		Code      int    `json:"code"`
		ErrorType string `json:"errorType"`
	} `json:"status"`
}

//APIAIMessage : Response Message Structure
type APIAIMessage struct {
	Speech      string `json:"speech"`
	DisplayText string `json:"displayText"`
	Source      string `json:"source"`
}

// PerformRequestToAPIAi sends natural language text and information as query parameters to API.AI
func PerformRequestToAPIAi(text string) (APIAIRequest, error) {

	record := APIAIRequest{}
	myURL := fmt.Sprintf(apiEndpoint, "query", apiVersion)
	myURL = myURL + "&query=" + url.QueryEscape(text) + "&lang=en" + "&sessionId=1234567890"

	// Build the request
	req, err := http.NewRequest("GET", myURL, nil)
	if err != nil {
		log.Fatalf("The /GET request to %s failed", myURL)
		return record, err
	}

	// Replace authToken by your Client access token
	authValue := "Bearer " + apiAccessToken
	req.Header.Add("Authorization", authValue)

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		log.Fatal("Do: ", err)
		return record, err
	}

	// Callers should defer the close of resp.Body when done reading from it
	defer resp.Body.Close()

	// Use json.Decode for reading streams of JSON data
	if err := json.NewDecoder(resp.Body).Decode(&record); err != nil {
		log.Println(err)
	}

	return record, nil
}

// ResolveEmoji converts the weather code into an emoji
func ResolveEmoji(weatherCode string) (emoji string) {

	switch weatherCode {
	case "4": // thunderstorms
		return "⛈⚡"
	case "11", "12":
		return "🌧☔"
	case "16":
		return "🌨❄"
	case "20":
		return "🌫"
	case "24":
		return "💨"
	case "25":
		return "🐧🐧"
	case "28": // mostly cloudy (day)
		return "☁"
		case "30": // partly cloudy (day)
		return "⛅"
	case "32":
		return "☀"
	case "36":
		return "🔥🔥"
	case "38", "39": // scattered thunderstorms
		return "⛈"
	default:
		fmt.Printf("%s.", weatherCode)
		return ""
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2014 The Kubernetes Authors.

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 get

import (
	"fmt"
	"io"
	"reflect"
	"sort"

	"k8s.io/klog/v2"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/client-go/util/jsonpath"
	"k8s.io/utils/integer"

	"github.com/fvbommel/sortorder"
)

// SortingPrinter sorts list types before delegating to another printer.
// Non-list types are simply passed through
type SortingPrinter struct {
	SortField string
	Delegate  printers.ResourcePrinter
	Decoder   runtime.Decoder
}

func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	if table, isTable := obj.(*metav1.Table); isTable && len(table.Rows) > 1 {
		parsedField, err := RelaxedJSONPathExpression(s.SortField)
		if err != nil {
			parsedField = s.SortField
		}

		if sorter, err := NewTableSorter(table, parsedField); err != nil {
			return err
		} else if err := sorter.Sort(); err != nil {
			return err
		}
		return s.Delegate.PrintObj(table, out)
	}

	if meta.IsListType(obj) {
		if err := s.sortObj(obj); err != nil {
			return err
		}
		return s.Delegate.PrintObj(obj, out)
	}

	return s.Delegate.PrintObj(obj, out)
}

func (s *SortingPrinter) sortObj(obj runtime.Object) error {
	objs, err := meta.ExtractList(obj)
	if err != nil {
		return err
	}
	if len(objs) == 0 {
		return nil
	}

	sorter, err := SortObjects(s.Decoder, objs, s.SortField)
	if err != nil {
		return err
	}

	switch list := obj.(type) {
	case *corev1.List:
		outputList := make([]runtime.RawExtension, len(objs))
		for ix := range objs {
			outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
		}
		list.Items = outputList
		return nil
	}
	return meta.SetList(obj, objs)
}

// SortObjects sorts the runtime.Object based on fieldInput and returns RuntimeSort that implements
// the golang sort interface
func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
	for ix := range objs {
		item := objs[ix]
		switch u := item.(type) {
		case *runtime.Unknown:
			var err error
			// decode runtime.Unknown to runtime.Unstructured for sorting.
			// we don't actually want the internal versions of known types.
			if objs[ix], _, err = decoder.Decode(u.Raw, nil, &unstructured.Unstructured{}); err != nil {
				return nil, err
			}
		}
	}

	field, err := RelaxedJSONPathExpression(fieldInput)
	if err != nil {
		return nil, err
	}

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	if err := parser.Parse(field); err != nil {
		return nil, err
	}

	// We don't do any model validation here, so we traverse all objects to be sorted
	// and, if the field is valid to at least one of them, we consider it to be a
	// valid field; otherwise error out.
	// Note that this requires empty fields to be considered later, when sorting.
	var fieldFoundOnce bool
	for _, obj := range objs {
		values, err := findJSONPathResults(parser, obj)
		if err != nil {
			return nil, err
		}
		if len(values) > 0 && len(values[0]) > 0 {
			fieldFoundOnce = true
			break
		}
	}
	if !fieldFoundOnce {
		return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
	}

	sorter := NewRuntimeSort(field, objs)
	sort.Sort(sorter)
	return sorter, nil
}

// RuntimeSort is an implementation of the golang sort interface that knows how to sort
// lists of runtime.Object
type RuntimeSort struct {
	field        string
	objs         []runtime.Object
	origPosition []int
}

// NewRuntimeSort creates a new RuntimeSort struct that implements golang sort interface
func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
	sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
	for ix := range objs {
		sorter.origPosition[ix] = ix
	}
	return sorter
}

func (r *RuntimeSort) Len() int {
	return len(r.objs)
}

func (r *RuntimeSort) Swap(i, j int) {
	r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
	r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
}

func isLess(i, j reflect.Value) (bool, error) {
	switch i.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return i.Int() < j.Int(), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return i.Uint() < j.Uint(), nil
	case reflect.Float32, reflect.Float64:
		return i.Float() < j.Float(), nil
	case reflect.String:
		return sortorder.NaturalLess(i.String(), j.String()), nil
	case reflect.Ptr:
		return isLess(i.Elem(), j.Elem())
	case reflect.Struct:
		// sort metav1.Time
		in := i.Interface()
		if t, ok := in.(metav1.Time); ok {
			time := j.Interface().(metav1.Time)
			return t.Before(&time), nil
		}
		// sort resource.Quantity
		if iQuantity, ok := in.(resource.Quantity); ok {
			jQuantity := j.Interface().(resource.Quantity)
			return iQuantity.Cmp(jQuantity) < 0, nil
		}
		// fallback to the fields comparison
		for idx := 0; idx < i.NumField(); idx++ {
			less, err := isLess(i.Field(idx), j.Field(idx))
			if err != nil || !less {
				return less, err
			}
		}
		return true, nil
	case reflect.Array, reflect.Slice:
		// note: the length of i and j may be different
		for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
			less, err := isLess(i.Index(idx), j.Index(idx))
			if err != nil || !less {
				return less, err
			}
		}
		return true, nil
	case reflect.Interface:
		if i.IsNil() && j.IsNil() {
			return false, nil
		} else if i.IsNil() {
			return true, nil
		} else if j.IsNil() {
			return false, nil
		}
		switch itype := i.Interface().(type) {
		case uint8:
			if jtype, ok := j.Interface().(uint8); ok {
				return itype < jtype, nil
			}
		case uint16:
			if jtype, ok := j.Interface().(uint16); ok {
				return itype < jtype, nil
			}
		case uint32:
			if jtype, ok := j.Interface().(uint32); ok {
				return itype < jtype, nil
			}
		case uint64:
			if jtype, ok := j.Interface().(uint64); ok {
				return itype < jtype, nil
			}
		case int8:
			if jtype, ok := j.Interface().(int8); ok {
				return itype < jtype, nil
			}
		case int16:
			if jtype, ok := j.Interface().(int16); ok {
				return itype < jtype, nil
			}
		case int32:
			if jtype, ok := j.Interface().(int32); ok {
				return itype < jtype, nil
			}
		case int64:
			if jtype, ok := j.Interface().(int64); ok {
				return itype < jtype, nil
			}
		case uint:
			if jtype, ok := j.Interface().(uint); ok {
				return itype < jtype, nil
			}
		case int:
			if jtype, ok := j.Interface().(int); ok {
				return itype < jtype, nil
			}
		case float32:
			if jtype, ok := j.Interface().(float32); ok {
				return itype < jtype, nil
			}
		case float64:
			if jtype, ok := j.Interface().(float64); ok {
				return itype < jtype, nil
			}
		case string:
			if jtype, ok := j.Interface().(string); ok {
				// check if it's a Quantity
				itypeQuantity, err := resource.ParseQuantity(itype)
				if err != nil {
					return sortorder.NaturalLess(itype, jtype), nil
				}
				jtypeQuantity, _ := resource.ParseQuantity(jtype)
				if err != nil {
					return sortorder.NaturalLess(itype, jtype), nil
				}
				// Both strings are quantity
				return itypeQuantity.Cmp(jtypeQuantity) < 0, nil
			}
		default:
			return false, fmt.Errorf("unsortable type: %T", itype)
		}
		return false, fmt.Errorf("unsortable interface: %v", i.Kind())

	default:
		return false, fmt.Errorf("unsortable type: %v", i.Kind())
	}
}

func (r *RuntimeSort) Less(i, j int) bool {
	iObj := r.objs[i]
	jObj := r.objs[j]

	var iValues [][]reflect.Value
	var jValues [][]reflect.Value
	var err error

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	err = parser.Parse(r.field)
	if err != nil {
		panic(err)
	}

	iValues, err = findJSONPathResults(parser, iObj)
	if err != nil {
		klog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
	}

	jValues, err = findJSONPathResults(parser, jObj)
	if err != nil {
		klog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
	}

	if len(iValues) == 0 || len(iValues[0]) == 0 {
		return true
	}
	if len(jValues) == 0 || len(jValues[0]) == 0 {
		return false
	}
	iField := iValues[0][0]
	jField := jValues[0][0]

	less, err := isLess(iField, jField)
	if err != nil {
		klog.Fatalf("Field %s in %T is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
	}
	return less
}

// OriginalPosition returns the starting (original) position of a particular index.
// e.g. If OriginalPosition(0) returns 5 than the
// the item currently at position 0 was at position 5 in the original unsorted array.
func (r *RuntimeSort) OriginalPosition(ix int) int {
	if ix < 0 || ix > len(r.origPosition) {
		return -1
	}
	return r.origPosition[ix]
}

type TableSorter struct {
	field      string
	obj        *metav1.Table
	parsedRows [][][]reflect.Value
}

func (t *TableSorter) Len() int {
	return len(t.obj.Rows)
}

func (t *TableSorter) Swap(i, j int) {
	t.obj.Rows[i], t.obj.Rows[j] = t.obj.Rows[j], t.obj.Rows[i]
	t.parsedRows[i], t.parsedRows[j] = t.parsedRows[j], t.parsedRows[i]
}

func (t *TableSorter) Less(i, j int) bool {
	iValues := t.parsedRows[i]
	jValues := t.parsedRows[j]

	if len(iValues) == 0 || len(iValues[0]) == 0 {
		return true
	}
	if len(jValues) == 0 || len(jValues[0]) == 0 {
		return false
	}

	iField := iValues[0][0]
	jField := jValues[0][0]

	less, err := isLess(iField, jField)
	if err != nil {
		klog.Fatalf("Field %s in %T is an unsortable type: %s, err: %v", t.field, t.parsedRows, iField.Kind().String(), err)
	}
	return less
}

func (t *TableSorter) Sort() error {
	sort.Sort(t)
	return nil
}

func NewTableSorter(table *metav1.Table, field string) (*TableSorter, error) {
	var parsedRows [][][]reflect.Value

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	err := parser.Parse(field)
	if err != nil {
		return nil, fmt.Errorf("sorting error: %v", err)
	}

	fieldFoundOnce := false
	for i := range table.Rows {
		parsedRow, err := findJSONPathResults(parser, table.Rows[i].Object.Object)
		if err != nil {
			return nil, fmt.Errorf("Failed to get values for %#v using %s (%#v)", parsedRow, field, err)
		}
		parsedRows = append(parsedRows, parsedRow)
		if len(parsedRow) > 0 && len(parsedRow[0]) > 0 {
			fieldFoundOnce = true
		}
	}

	if len(table.Rows) > 0 && !fieldFoundOnce {
		return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
	}

	return &TableSorter{
		obj:        table,
		field:      field,
		parsedRows: parsedRows,
	}, nil
}
func findJSONPathResults(parser *jsonpath.JSONPath, from runtime.Object) ([][]reflect.Value, error) {
	if unstructuredObj, ok := from.(*unstructured.Unstructured); ok {
		return parser.FindResults(unstructuredObj.Object)
	}
	return parser.FindResults(reflect.ValueOf(from).Elem().Interface())
}
<commit_msg>remove stack trace log when sorting with a bad unsortable field<commit_after>/*
Copyright 2014 The Kubernetes Authors.

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 get

import (
	"fmt"
	"io"
	"reflect"
	"sort"

	"k8s.io/klog/v2"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/meta"
	"k8s.io/apimachinery/pkg/api/resource"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/client-go/util/jsonpath"
	"k8s.io/utils/integer"

	"github.com/fvbommel/sortorder"
)

// SortingPrinter sorts list types before delegating to another printer.
// Non-list types are simply passed through
type SortingPrinter struct {
	SortField string
	Delegate  printers.ResourcePrinter
	Decoder   runtime.Decoder
}

func (s *SortingPrinter) PrintObj(obj runtime.Object, out io.Writer) error {
	if table, isTable := obj.(*metav1.Table); isTable && len(table.Rows) > 1 {
		parsedField, err := RelaxedJSONPathExpression(s.SortField)
		if err != nil {
			parsedField = s.SortField
		}

		if sorter, err := NewTableSorter(table, parsedField); err != nil {
			return err
		} else if err := sorter.Sort(); err != nil {
			return err
		}
		return s.Delegate.PrintObj(table, out)
	}

	if meta.IsListType(obj) {
		if err := s.sortObj(obj); err != nil {
			return err
		}
		return s.Delegate.PrintObj(obj, out)
	}

	return s.Delegate.PrintObj(obj, out)
}

func (s *SortingPrinter) sortObj(obj runtime.Object) error {
	objs, err := meta.ExtractList(obj)
	if err != nil {
		return err
	}
	if len(objs) == 0 {
		return nil
	}

	sorter, err := SortObjects(s.Decoder, objs, s.SortField)
	if err != nil {
		return err
	}

	switch list := obj.(type) {
	case *corev1.List:
		outputList := make([]runtime.RawExtension, len(objs))
		for ix := range objs {
			outputList[ix] = list.Items[sorter.OriginalPosition(ix)]
		}
		list.Items = outputList
		return nil
	}
	return meta.SetList(obj, objs)
}

// SortObjects sorts the runtime.Object based on fieldInput and returns RuntimeSort that implements
// the golang sort interface
func SortObjects(decoder runtime.Decoder, objs []runtime.Object, fieldInput string) (*RuntimeSort, error) {
	for ix := range objs {
		item := objs[ix]
		switch u := item.(type) {
		case *runtime.Unknown:
			var err error
			// decode runtime.Unknown to runtime.Unstructured for sorting.
			// we don't actually want the internal versions of known types.
			if objs[ix], _, err = decoder.Decode(u.Raw, nil, &unstructured.Unstructured{}); err != nil {
				return nil, err
			}
		}
	}

	field, err := RelaxedJSONPathExpression(fieldInput)
	if err != nil {
		return nil, err
	}

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	if err := parser.Parse(field); err != nil {
		return nil, err
	}

	// We don't do any model validation here, so we traverse all objects to be sorted
	// and, if the field is valid to at least one of them, we consider it to be a
	// valid field; otherwise error out.
	// Note that this requires empty fields to be considered later, when sorting.
	var fieldFoundOnce bool
	for _, obj := range objs {
		values, err := findJSONPathResults(parser, obj)
		if err != nil {
			return nil, err
		}
		if len(values) > 0 && len(values[0]) > 0 {
			fieldFoundOnce = true
			break
		}
	}
	if !fieldFoundOnce {
		return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
	}

	sorter := NewRuntimeSort(field, objs)
	sort.Sort(sorter)
	return sorter, nil
}

// RuntimeSort is an implementation of the golang sort interface that knows how to sort
// lists of runtime.Object
type RuntimeSort struct {
	field        string
	objs         []runtime.Object
	origPosition []int
}

// NewRuntimeSort creates a new RuntimeSort struct that implements golang sort interface
func NewRuntimeSort(field string, objs []runtime.Object) *RuntimeSort {
	sorter := &RuntimeSort{field: field, objs: objs, origPosition: make([]int, len(objs))}
	for ix := range objs {
		sorter.origPosition[ix] = ix
	}
	return sorter
}

func (r *RuntimeSort) Len() int {
	return len(r.objs)
}

func (r *RuntimeSort) Swap(i, j int) {
	r.objs[i], r.objs[j] = r.objs[j], r.objs[i]
	r.origPosition[i], r.origPosition[j] = r.origPosition[j], r.origPosition[i]
}

func isLess(i, j reflect.Value) (bool, error) {
	switch i.Kind() {
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return i.Int() < j.Int(), nil
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
		return i.Uint() < j.Uint(), nil
	case reflect.Float32, reflect.Float64:
		return i.Float() < j.Float(), nil
	case reflect.String:
		return sortorder.NaturalLess(i.String(), j.String()), nil
	case reflect.Ptr:
		return isLess(i.Elem(), j.Elem())
	case reflect.Struct:
		// sort metav1.Time
		in := i.Interface()
		if t, ok := in.(metav1.Time); ok {
			time := j.Interface().(metav1.Time)
			return t.Before(&time), nil
		}
		// sort resource.Quantity
		if iQuantity, ok := in.(resource.Quantity); ok {
			jQuantity := j.Interface().(resource.Quantity)
			return iQuantity.Cmp(jQuantity) < 0, nil
		}
		// fallback to the fields comparison
		for idx := 0; idx < i.NumField(); idx++ {
			less, err := isLess(i.Field(idx), j.Field(idx))
			if err != nil || !less {
				return less, err
			}
		}
		return true, nil
	case reflect.Array, reflect.Slice:
		// note: the length of i and j may be different
		for idx := 0; idx < integer.IntMin(i.Len(), j.Len()); idx++ {
			less, err := isLess(i.Index(idx), j.Index(idx))
			if err != nil || !less {
				return less, err
			}
		}
		return true, nil
	case reflect.Interface:
		if i.IsNil() && j.IsNil() {
			return false, nil
		} else if i.IsNil() {
			return true, nil
		} else if j.IsNil() {
			return false, nil
		}
		switch itype := i.Interface().(type) {
		case uint8:
			if jtype, ok := j.Interface().(uint8); ok {
				return itype < jtype, nil
			}
		case uint16:
			if jtype, ok := j.Interface().(uint16); ok {
				return itype < jtype, nil
			}
		case uint32:
			if jtype, ok := j.Interface().(uint32); ok {
				return itype < jtype, nil
			}
		case uint64:
			if jtype, ok := j.Interface().(uint64); ok {
				return itype < jtype, nil
			}
		case int8:
			if jtype, ok := j.Interface().(int8); ok {
				return itype < jtype, nil
			}
		case int16:
			if jtype, ok := j.Interface().(int16); ok {
				return itype < jtype, nil
			}
		case int32:
			if jtype, ok := j.Interface().(int32); ok {
				return itype < jtype, nil
			}
		case int64:
			if jtype, ok := j.Interface().(int64); ok {
				return itype < jtype, nil
			}
		case uint:
			if jtype, ok := j.Interface().(uint); ok {
				return itype < jtype, nil
			}
		case int:
			if jtype, ok := j.Interface().(int); ok {
				return itype < jtype, nil
			}
		case float32:
			if jtype, ok := j.Interface().(float32); ok {
				return itype < jtype, nil
			}
		case float64:
			if jtype, ok := j.Interface().(float64); ok {
				return itype < jtype, nil
			}
		case string:
			if jtype, ok := j.Interface().(string); ok {
				// check if it's a Quantity
				itypeQuantity, err := resource.ParseQuantity(itype)
				if err != nil {
					return sortorder.NaturalLess(itype, jtype), nil
				}
				jtypeQuantity, _ := resource.ParseQuantity(jtype)
				if err != nil {
					return sortorder.NaturalLess(itype, jtype), nil
				}
				// Both strings are quantity
				return itypeQuantity.Cmp(jtypeQuantity) < 0, nil
			}
		default:
			return false, fmt.Errorf("unsortable type: %T", itype)
		}
		return false, fmt.Errorf("unsortable interface: %v", i.Kind())

	default:
		return false, fmt.Errorf("unsortable type: %v", i.Kind())
	}
}

func (r *RuntimeSort) Less(i, j int) bool {
	iObj := r.objs[i]
	jObj := r.objs[j]

	var iValues [][]reflect.Value
	var jValues [][]reflect.Value
	var err error

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	err = parser.Parse(r.field)
	if err != nil {
		panic(err)
	}

	iValues, err = findJSONPathResults(parser, iObj)
	if err != nil {
		klog.Fatalf("Failed to get i values for %#v using %s (%#v)", iObj, r.field, err)
	}

	jValues, err = findJSONPathResults(parser, jObj)
	if err != nil {
		klog.Fatalf("Failed to get j values for %#v using %s (%v)", jObj, r.field, err)
	}

	if len(iValues) == 0 || len(iValues[0]) == 0 {
		return true
	}
	if len(jValues) == 0 || len(jValues[0]) == 0 {
		return false
	}
	iField := iValues[0][0]
	jField := jValues[0][0]

	less, err := isLess(iField, jField)
	if err != nil {
		klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", r.field, iObj, iField.Kind().String(), err)
	}
	return less
}

// OriginalPosition returns the starting (original) position of a particular index.
// e.g. If OriginalPosition(0) returns 5 than the
// the item currently at position 0 was at position 5 in the original unsorted array.
func (r *RuntimeSort) OriginalPosition(ix int) int {
	if ix < 0 || ix > len(r.origPosition) {
		return -1
	}
	return r.origPosition[ix]
}

type TableSorter struct {
	field      string
	obj        *metav1.Table
	parsedRows [][][]reflect.Value
}

func (t *TableSorter) Len() int {
	return len(t.obj.Rows)
}

func (t *TableSorter) Swap(i, j int) {
	t.obj.Rows[i], t.obj.Rows[j] = t.obj.Rows[j], t.obj.Rows[i]
	t.parsedRows[i], t.parsedRows[j] = t.parsedRows[j], t.parsedRows[i]
}

func (t *TableSorter) Less(i, j int) bool {
	iValues := t.parsedRows[i]
	jValues := t.parsedRows[j]

	if len(iValues) == 0 || len(iValues[0]) == 0 {
		return true
	}
	if len(jValues) == 0 || len(jValues[0]) == 0 {
		return false
	}

	iField := iValues[0][0]
	jField := jValues[0][0]

	less, err := isLess(iField, jField)
	if err != nil {
		klog.Exitf("Field %s in %T is an unsortable type: %s, err: %v", t.field, t.parsedRows, iField.Kind().String(), err)
	}
	return less
}

func (t *TableSorter) Sort() error {
	sort.Sort(t)
	return nil
}

func NewTableSorter(table *metav1.Table, field string) (*TableSorter, error) {
	var parsedRows [][][]reflect.Value

	parser := jsonpath.New("sorting").AllowMissingKeys(true)
	err := parser.Parse(field)
	if err != nil {
		return nil, fmt.Errorf("sorting error: %v", err)
	}

	fieldFoundOnce := false
	for i := range table.Rows {
		parsedRow, err := findJSONPathResults(parser, table.Rows[i].Object.Object)
		if err != nil {
			return nil, fmt.Errorf("Failed to get values for %#v using %s (%#v)", parsedRow, field, err)
		}
		parsedRows = append(parsedRows, parsedRow)
		if len(parsedRow) > 0 && len(parsedRow[0]) > 0 {
			fieldFoundOnce = true
		}
	}

	if len(table.Rows) > 0 && !fieldFoundOnce {
		return nil, fmt.Errorf("couldn't find any field with path %q in the list of objects", field)
	}

	return &TableSorter{
		obj:        table,
		field:      field,
		parsedRows: parsedRows,
	}, nil
}
func findJSONPathResults(parser *jsonpath.JSONPath, from runtime.Object) ([][]reflect.Value, error) {
	if unstructuredObj, ok := from.(*unstructured.Unstructured); ok {
		return parser.FindResults(unstructuredObj.Object)
	}
	return parser.FindResults(reflect.ValueOf(from).Elem().Interface())
}
<|endoftext|>