<commit_before>package spotify

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// User contains the basic, publicly available information about a Spotify user.
type User struct {
	// The name displayed on the user's profile.
	// Note: Spotify currently fails to populate
	// this field when querying for a playlist.
	DisplayName string `json:"display_name"`
	// Known public external URLs for the user.
	ExternalURLs map[string]string `json:"external_urls"`
	// Information about followers of the user.
	Followers Followers `json:"followers"`
	// A link to the Web API endpoint for this user.
	Endpoint string `json:"href"`
	// The Spotify user ID for the user.
	ID string `json:"id"`
	// The user's profile image.
	Images []Image `json:"images"`
	// The Spotify URI for the user.
	URI URI `json:"uri"`
}

// PrivateUser contains additional information about a user.
// This data is private and requires user authentication.
type PrivateUser struct {
	User
	// The country of the user, as set in the user's account profile.
	// An ISO 3166-1 alpha-2 country code.  This field is only available when the
	// current user has granted acess to the ScopeUserReadPrivate scope.
	Country string `json:"country"`
	// The user's email address, as entered by the user when creating their account.
	// Note: this email is UNVERIFIED - there is no proof that it actually
	// belongs to the user.  This field is only available when the current user
	// has granted access to the ScopeUserReadEmail scope.
	Email string `json:"email"`
	// The user's Spotify subscription level: "premium", "free", etc.
	// The subscription level "open" can be considered the same as "free".
	// This field is only available when the current user has granted access to
	// the ScopeUserReadPrivate scope.
	Product string `json:"product"`
	// The user's date of birth, in the format 'YYYY-MM-DD'.  You can use
	// the DateLayout constant to convert this to a time.Time value.
	// This field is only available when the current user has granted
	// access to the ScopeUserReadBirthdate scope.
	Birthdate string `json:"birthdate"`
}

// GetUsersPublicProfile gets public profile information about a
// Spotify User.  It does not require authentication.
func (c *Client) GetUsersPublicProfile(userID ID) (*User, error) {
	spotifyURL := c.baseURL + "users/" + string(userID)

	var user User

	err := c.get(spotifyURL, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// CurrentUser gets detailed profile information about the
// current user.
//
// Reading the user's email address requires that the application
// has the ScopeUserReadEmail scope.  Reading the country, display
// name, profile images, and product subscription level requires
// that the application has the ScopeUserReadPrivate scope.
//
// Warning: The email address in the response will be the address
// that was entered when the user created their spotify account.
// This email address is unverified - do not assume that Spotify has
// checked that the email address actually belongs to the user.
func (c *Client) CurrentUser() (*PrivateUser, error) {
	var result PrivateUser

	err := c.get(c.baseURL+"me", &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTracks gets a list of songs saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersTracks() (*SavedTrackPage, error) {
	return c.CurrentUsersTracksOpt(nil)
}

// CurrentUsersTracksOpt is like CurrentUsersTracks, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersTracksOpt(opt *Options) (*SavedTrackPage, error) {
	spotifyURL := c.baseURL + "me/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("country", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// FollowUser adds the current user as a follower of one or more
// spotify users, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowUser(ids ...ID) error {
	return c.modifyFollowers("user", true, ids...)
}

// FollowArtist adds the current user as a follower of one or more
// spotify artists, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", true, ids...)
}

// UnfollowUser removes the current user as a follower of one or more
// Spotify users.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowUser(ids ...ID) error {
	return c.modifyFollowers("user", false, ids...)
}

// UnfollowArtist removes the current user as a follower of one or more
// Spotify artists.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", false, ids...)
}

// CurrentUserFollows checks to see if the current user is following
// one or more artists or other Spotify Users.  This call requires
// ScopeUserFollowRead.
//
// The t argument indicates the type of the IDs, and must be either
// "user" or "artist".
//
// The result is returned as a slice of bool values in the same order
// in which the IDs were specified.
func (c *Client) CurrentUserFollows(t string, ids ...ID) ([]bool, error) {
	if l := len(ids); l == 0 || l > 50 {
		return nil, errors.New("spotify: UserFollows supports 1 to 50 IDs")
	}
	if t != "artist" && t != "user" {
		return nil, errors.New("spotify: t must be 'artist' or 'user'")
	}
	spotifyURL := fmt.Sprintf("%sme/following/contains?type=%s&ids=%s",
		c.baseURL, t, strings.Join(toStringSlice(ids), ","))

	var result []bool

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) modifyFollowers(usertype string, follow bool, ids ...ID) error {
	if l := len(ids); l == 0 || l > 50 {
		return errors.New("spotify: Follow/Unfollow supports 1 to 50 IDs")
	}
	v := url.Values{}
	v.Add("type", usertype)
	v.Add("ids", strings.Join(toStringSlice(ids), ","))
	spotifyURL := c.baseURL + "me/following?" + v.Encode()
	method := "PUT"
	if !follow {
		method = "DELETE"
	}
	req, err := http.NewRequest(method, spotifyURL, nil)
	if err != nil {
		return err
	}
	err = c.execute(req, nil, http.StatusNoContent)
	if err != nil {
		return err
	}
	return nil
}

// CurrentUsersFollowedArtists gets the current user's followed artists.
// This call requires that the user has granted the ScopeUserFollowRead scope.
func (c *Client) CurrentUsersFollowedArtists() (*FullArtistCursorPage, error) {
	return c.CurrentUsersFollowedArtistsOpt(-1, "")
}

// CurrentUsersFollowedArtistsOpt is like CurrentUsersFollowedArtists,
// but it accept the optional arguments limit and after.  Limit is the
// maximum number of items to return (1 <= limit <= 50), and after is
// the last artist ID retrieved from the previous request.  If you don't
// wish to specify either of the parameters, use -1 for limit and the empty
// string for after.
func (c *Client) CurrentUsersFollowedArtistsOpt(limit int, after string) (*FullArtistCursorPage, error) {
	spotifyURL := c.baseURL + "me/following"
	v := url.Values{}
	v.Set("type", "artist")
	if limit != -1 {
		v.Set("limit", strconv.Itoa(limit))
	}
	if after != "" {
		v.Set("after", after)
	}
	if params := v.Encode(); params != "" {
		spotifyURL += "?" + params
	}

	var result struct {
		A FullArtistCursorPage `json:"artists"`
	}

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result.A, nil
}

// CurrentUsersAlbums gets a list of albums saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersAlbums() (*SavedAlbumPage, error) {
	return c.CurrentUsersAlbumsOpt(nil)
}

// CurrentUsersAlbumsOpt is like CurrentUsersAlbums, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersAlbumsOpt(opt *Options) (*SavedAlbumPage, error) {
	spotifyURL := c.baseURL + "me/albums"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("market", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedAlbumPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersPlaylists gets a list of the playlists owned or followed by
// the current spotify user.
//
// Private playlists require the ScopePlaylistReadPrivate scope.  Note that
// this scope alone will not return collaborative playlists, even though
// they are always private.  In order to retrieve collaborative playlists
// the user must authorize the ScopePlaylistReadCollaborative scope.
func (c *Client) CurrentUsersPlaylists() (*SimplePlaylistPage, error) {
	return c.CurrentUsersPlaylistsOpt(nil)
}

// CurrentUsersPlaylistsOpt is like CurrentUsersPlaylists, but it accepts
// additional options for sorting and filtering the results.
func (c *Client) CurrentUsersPlaylistsOpt(opt *Options) (*SimplePlaylistPage, error) {
	spotifyURL := c.baseURL + "me/playlists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SimplePlaylistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtists gets a list of the top played artists in a given time
// range of the current Spotify user. It supports up to 50 artists in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopArtists(opt *Options) (*FullArtistPage, error) {
	spotifyURL := c.baseURL + "me/top/artists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullArtistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopTracks gets a list of the top played tracks in a given time
// range of the current Spotify user. It supports up to 50 tracks in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopTracks(opt *Options) (*FullTrackPage, error) {
	spotifyURL := c.baseURL + "me/top/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}
<commit_msg>Split Current User's Top Tracks/Artists functions into functions with defaults and functions with options<commit_after>package spotify

import (
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

// User contains the basic, publicly available information about a Spotify user.
type User struct {
	// The name displayed on the user's profile.
	// Note: Spotify currently fails to populate
	// this field when querying for a playlist.
	DisplayName string `json:"display_name"`
	// Known public external URLs for the user.
	ExternalURLs map[string]string `json:"external_urls"`
	// Information about followers of the user.
	Followers Followers `json:"followers"`
	// A link to the Web API endpoint for this user.
	Endpoint string `json:"href"`
	// The Spotify user ID for the user.
	ID string `json:"id"`
	// The user's profile image.
	Images []Image `json:"images"`
	// The Spotify URI for the user.
	URI URI `json:"uri"`
}

// PrivateUser contains additional information about a user.
// This data is private and requires user authentication.
type PrivateUser struct {
	User
	// The country of the user, as set in the user's account profile.
	// An ISO 3166-1 alpha-2 country code.  This field is only available when the
	// current user has granted acess to the ScopeUserReadPrivate scope.
	Country string `json:"country"`
	// The user's email address, as entered by the user when creating their account.
	// Note: this email is UNVERIFIED - there is no proof that it actually
	// belongs to the user.  This field is only available when the current user
	// has granted access to the ScopeUserReadEmail scope.
	Email string `json:"email"`
	// The user's Spotify subscription level: "premium", "free", etc.
	// The subscription level "open" can be considered the same as "free".
	// This field is only available when the current user has granted access to
	// the ScopeUserReadPrivate scope.
	Product string `json:"product"`
	// The user's date of birth, in the format 'YYYY-MM-DD'.  You can use
	// the DateLayout constant to convert this to a time.Time value.
	// This field is only available when the current user has granted
	// access to the ScopeUserReadBirthdate scope.
	Birthdate string `json:"birthdate"`
}

// GetUsersPublicProfile gets public profile information about a
// Spotify User.  It does not require authentication.
func (c *Client) GetUsersPublicProfile(userID ID) (*User, error) {
	spotifyURL := c.baseURL + "users/" + string(userID)

	var user User

	err := c.get(spotifyURL, &user)
	if err != nil {
		return nil, err
	}

	return &user, nil
}

// CurrentUser gets detailed profile information about the
// current user.
//
// Reading the user's email address requires that the application
// has the ScopeUserReadEmail scope.  Reading the country, display
// name, profile images, and product subscription level requires
// that the application has the ScopeUserReadPrivate scope.
//
// Warning: The email address in the response will be the address
// that was entered when the user created their spotify account.
// This email address is unverified - do not assume that Spotify has
// checked that the email address actually belongs to the user.
func (c *Client) CurrentUser() (*PrivateUser, error) {
	var result PrivateUser

	err := c.get(c.baseURL+"me", &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTracks gets a list of songs saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersTracks() (*SavedTrackPage, error) {
	return c.CurrentUsersTracksOpt(nil)
}

// CurrentUsersTracksOpt is like CurrentUsersTracks, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersTracksOpt(opt *Options) (*SavedTrackPage, error) {
	spotifyURL := c.baseURL + "me/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("country", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// FollowUser adds the current user as a follower of one or more
// spotify users, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowUser(ids ...ID) error {
	return c.modifyFollowers("user", true, ids...)
}

// FollowArtist adds the current user as a follower of one or more
// spotify artists, identified by their Spotify IDs.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) FollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", true, ids...)
}

// UnfollowUser removes the current user as a follower of one or more
// Spotify users.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowUser(ids ...ID) error {
	return c.modifyFollowers("user", false, ids...)
}

// UnfollowArtist removes the current user as a follower of one or more
// Spotify artists.
//
// Modifying the lists of artists or users the current user follows
// requires that the application has the ScopeUserFollowModify scope.
func (c *Client) UnfollowArtist(ids ...ID) error {
	return c.modifyFollowers("artist", false, ids...)
}

// CurrentUserFollows checks to see if the current user is following
// one or more artists or other Spotify Users.  This call requires
// ScopeUserFollowRead.
//
// The t argument indicates the type of the IDs, and must be either
// "user" or "artist".
//
// The result is returned as a slice of bool values in the same order
// in which the IDs were specified.
func (c *Client) CurrentUserFollows(t string, ids ...ID) ([]bool, error) {
	if l := len(ids); l == 0 || l > 50 {
		return nil, errors.New("spotify: UserFollows supports 1 to 50 IDs")
	}
	if t != "artist" && t != "user" {
		return nil, errors.New("spotify: t must be 'artist' or 'user'")
	}
	spotifyURL := fmt.Sprintf("%sme/following/contains?type=%s&ids=%s",
		c.baseURL, t, strings.Join(toStringSlice(ids), ","))

	var result []bool

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return result, nil
}

func (c *Client) modifyFollowers(usertype string, follow bool, ids ...ID) error {
	if l := len(ids); l == 0 || l > 50 {
		return errors.New("spotify: Follow/Unfollow supports 1 to 50 IDs")
	}
	v := url.Values{}
	v.Add("type", usertype)
	v.Add("ids", strings.Join(toStringSlice(ids), ","))
	spotifyURL := c.baseURL + "me/following?" + v.Encode()
	method := "PUT"
	if !follow {
		method = "DELETE"
	}
	req, err := http.NewRequest(method, spotifyURL, nil)
	if err != nil {
		return err
	}
	err = c.execute(req, nil, http.StatusNoContent)
	if err != nil {
		return err
	}
	return nil
}

// CurrentUsersFollowedArtists gets the current user's followed artists.
// This call requires that the user has granted the ScopeUserFollowRead scope.
func (c *Client) CurrentUsersFollowedArtists() (*FullArtistCursorPage, error) {
	return c.CurrentUsersFollowedArtistsOpt(-1, "")
}

// CurrentUsersFollowedArtistsOpt is like CurrentUsersFollowedArtists,
// but it accept the optional arguments limit and after.  Limit is the
// maximum number of items to return (1 <= limit <= 50), and after is
// the last artist ID retrieved from the previous request.  If you don't
// wish to specify either of the parameters, use -1 for limit and the empty
// string for after.
func (c *Client) CurrentUsersFollowedArtistsOpt(limit int, after string) (*FullArtistCursorPage, error) {
	spotifyURL := c.baseURL + "me/following"
	v := url.Values{}
	v.Set("type", "artist")
	if limit != -1 {
		v.Set("limit", strconv.Itoa(limit))
	}
	if after != "" {
		v.Set("after", after)
	}
	if params := v.Encode(); params != "" {
		spotifyURL += "?" + params
	}

	var result struct {
		A FullArtistCursorPage `json:"artists"`
	}

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result.A, nil
}

// CurrentUsersAlbums gets a list of albums saved in the current
// Spotify user's "Your Music" library.
func (c *Client) CurrentUsersAlbums() (*SavedAlbumPage, error) {
	return c.CurrentUsersAlbumsOpt(nil)
}

// CurrentUsersAlbumsOpt is like CurrentUsersAlbums, but it accepts additional
// options for sorting and filtering the results.
func (c *Client) CurrentUsersAlbumsOpt(opt *Options) (*SavedAlbumPage, error) {
	spotifyURL := c.baseURL + "me/albums"
	if opt != nil {
		v := url.Values{}
		if opt.Country != nil {
			v.Set("market", *opt.Country)
		}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SavedAlbumPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersPlaylists gets a list of the playlists owned or followed by
// the current spotify user.
//
// Private playlists require the ScopePlaylistReadPrivate scope.  Note that
// this scope alone will not return collaborative playlists, even though
// they are always private.  In order to retrieve collaborative playlists
// the user must authorize the ScopePlaylistReadCollaborative scope.
func (c *Client) CurrentUsersPlaylists() (*SimplePlaylistPage, error) {
	return c.CurrentUsersPlaylistsOpt(nil)
}

// CurrentUsersPlaylistsOpt is like CurrentUsersPlaylists, but it accepts
// additional options for sorting and filtering the results.
func (c *Client) CurrentUsersPlaylistsOpt(opt *Options) (*SimplePlaylistPage, error) {
	spotifyURL := c.baseURL + "me/playlists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Offset != nil {
			v.Set("offset", strconv.Itoa(*opt.Offset))
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result SimplePlaylistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtistsOpt gets a list of the top played artists in a given time
// range of the current Spotify user. It supports up to 50 artists in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopArtistsOpt(opt *Options) (*FullArtistPage, error) {
	spotifyURL := c.baseURL + "me/top/artists"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullArtistPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopArtists is like CurrentUsersTopArtistsOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopArtists() (*FullArtistPage, error) {
	return c.CurrentUsersTopArtistsOpt(nil)
}

// CurrentUsersTopTracksOpt gets a list of the top played tracks in a given time
// range of the current Spotify user. It supports up to 50 tracks in a single
// call. This call requires ScopeUserTopRead.
func (c *Client) CurrentUsersTopTracksOpt(opt *Options) (*FullTrackPage, error) {
	spotifyURL := c.baseURL + "me/top/tracks"
	if opt != nil {
		v := url.Values{}
		if opt.Limit != nil {
			v.Set("limit", strconv.Itoa(*opt.Limit))
		}
		if opt.Timerange != nil {
			v.Set("time_range", *opt.Timerange+"_term")
		}
		if params := v.Encode(); params != "" {
			spotifyURL += "?" + params
		}
	}

	var result FullTrackPage

	err := c.get(spotifyURL, &result)
	if err != nil {
		return nil, err
	}

	return &result, nil
}

// CurrentUsersTopTracks is like CurrentUsersTopTracksOpt but with
// sensible defaults. The default limit is 20 and the default timerange
// is medium_term.
func (c *Client) CurrentUsersTopTracks() (*FullTrackPage, error) {
	return c.CurrentUsersTopTracksOpt(nil)
}<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/events"
	"github.com/ninjasphere/go-zigbee"
	"github.com/ninjasphere/go-zigbee/gateway"
	"github.com/ninjasphere/go-zigbee/nwkmgr"
)

const (
	ClusterIDBasic    uint32 = 0x00
	ClusterIDOnOff    uint32 = 0x06
	ClusterIDTemp     uint32 = 0x402
	ClusterIDHumidity uint32 = 0x405
	ClusterIDPower    uint32 = 0x702
)

var (
	info = ninja.LoadModuleInfo("./package.json")
)

type ZStackConfig struct {
	Hostname       string
	OtasrvrPort    int
	GatewayPort    int
	NwkmgrPort     int
	StableFlagFile string
}

type DriverConfig struct {
}

type Driver struct {
	conn      *ninja.Connection
	userAgent *ninja.ServiceClient
	sendEvent func(event string, payload interface{}) error

	devices map[uint64]*Device

	NetworkReady chan bool

	nwkmgrConn  *zigbee.ZStackNwkMgr
	gatewayConn *zigbee.ZStackGateway
	otaConn     *zigbee.ZStackOta
	config      *DriverConfig
}

func NewDriver() *Driver {
	return &Driver{
		NetworkReady: make(chan bool),
	}
}

type Device struct {
	info       *model.Device
	driver     *Driver
	deviceInfo *nwkmgr.NwkDeviceInfoT
	channels   []Channel
	sendEvent  func(event string, payload interface{}) error
}

type Channel struct {
	device   *Device
	endpoint *nwkmgr.NwkSimpleDescriptorT
}

func (d *Driver) GetModuleInfo() *model.Module {
	return info
}

func (d *Driver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Driver) Reset(hard bool) error {
	return d.nwkmgrConn.Reset(hard)
}

func (d *Driver) Start(config *DriverConfig) error {
	d.config = config
	return d.sendEvent("config", config)
}

func (d *Driver) Stop() error {
	return fmt.Errorf("This driver does not support being stopped. YOU HAVE NO POWER HERE.")
}

func (d *Driver) StartPairing(period uint32) (*uint32, error) {
	err := d.PermitJoin(uint32(period))
	if err != nil {
		err = fmt.Errorf("permit Join failed: %v", err)
		log.Fatalf("%s", err)
	}
	return &period, err
}

func (d *Driver) OnPairingRequest(req *events.PairingRequest, topicKeys map[string]string) bool {
     log.Printf("Pairing request of %d seconds received from %s", req.Duration, topicKeys["deviceId"])
     d.PermitJoin(uint32(req.Duration))
     return true
}

func (d *Driver) PermitJoin(period uint32) error {

	log.Printf("Join window will open for %d seconds.", period)

	//joinTime := uint32(30)
	permitJoinRequest := &nwkmgr.NwkSetPermitJoinReq{
		PermitJoinTime: &period,
		PermitJoin:     nwkmgr.NwkPermitJoinTypeT_PERMIT_ALL.Enum(),
	}

	permitJoinResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	go func() {
		// logging the close of the join window is helpful when debugging
		// join behaviour.
		time.Sleep(time.Duration(period) * time.Second)
		log.Printf("Join window has closed after %d seconds.", period)
	}()
	err := d.nwkmgrConn.SendCommand(permitJoinRequest, permitJoinResponse)
	if err != nil {
		return fmt.Errorf("Failed to enable joining: %s", err)
	}
	if permitJoinResponse.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to enable joining: %s", permitJoinResponse.Status)
	}
	//log.Println("Permit join response: ")
	//outJSON(permitJoinResponse)

	return nil
}

func (d *Driver) Connect(cfg *ZStackConfig, networkReady chan bool) error {

	waitUntilZStackReady(config.StableFlagFile)

	d.devices = make(map[uint64]*Device)

	conn, err := ninja.Connect("com.ninjablocks.zigbee")
	if err != nil {
		return fmt.Errorf("Could not connect to MQTT: %s", err)
	}

	d.userAgent = conn.GetServiceClient("$device/:deviceId/channel/user-agent")
	err = d.userAgent.OnUnmarshalledEvent("pairing-requested", d.OnPairingRequest)
	if err != nil {
	       return fmt.Errorf("Failed register user-agent service client: %s", err)
	}

	err = conn.ExportDriver(d)
	if err != nil {
		return fmt.Errorf("Could not export driver: %s", err)
	}

	d.nwkmgrConn, err = zigbee.ConnectToNwkMgrServer(cfg.Hostname, cfg.NwkmgrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to nwkmgr %s", err)
	}
	d.nwkmgrConn.OnDeviceFound = func(deviceInfo *nwkmgr.NwkDeviceInfoT) {
		d.onDeviceFound(deviceInfo)
	}

	done := false
	d.nwkmgrConn.OnNetworkReady = func() {
		if !done {
			done = true
			networkReady <- true
		}
	}

	d.otaConn, err = zigbee.ConnectToOtaServer(cfg.Hostname, cfg.OtasrvrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to ota server %s", err)
	}

	d.gatewayConn, err = zigbee.ConnectToGatewayServer(cfg.Hostname, cfg.GatewayPort)
	if err != nil {
		return fmt.Errorf("Error connecting to gateway %s", err)
	}

	/*keyResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	keyRequest := &nwkmgr.NwkChangeNwkKeyReq{
		NewKey: []byte{0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39},
	}

	err = d.nwkmgrConn.SendCommand(keyRequest, keyResponse)
	if err != nil {
		return fmt.Errorf("Failed setting network key: %s", err)
	}

	spew.Dump(keyResponse)
	log.Println("Sleeping for 10 seconds after setting network key")

	time.Sleep(10 * time.Second)*/

	networkInfo := &nwkmgr.NwkZigbeeNwkInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkZigbeeNwkInfoReq{}, networkInfo)
	if err != nil {
		return fmt.Errorf("Failed getting network info: %s", err)
	}

	if *networkInfo.Status == nwkmgr.NwkNetworkStatusT_NWK_DOWN {
		return fmt.Errorf("The ZigBee network is down")
	}

	localDevice := &nwkmgr.NwkGetLocalDeviceInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetLocalDeviceInfoReq{}, localDevice)
	if err != nil {
		return fmt.Errorf("Failed getting local device info: %s", err)
	}

	spew.Dump("device info", localDevice.String())

	networkKey := &nwkmgr.NwkGetNwkKeyCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetNwkKeyReq{}, networkKey)
	if err != nil {
		return fmt.Errorf("Failed getting network key: %s", err)
	}

	spew.Dump(networkKey)

	log.Printf("Started coordinator. Channel:%d Pan ID:0x%X Key:% X", *networkInfo.NwkChannel, *networkInfo.PanId, networkKey.NewKey)

	return nil
}

func (d *Driver) FetchDevices() error {

	return d.nwkmgrConn.FetchDeviceList()

}

func (d *Device) GetDriver() ninja.Driver {
	return d.driver
}

func (d *Device) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *Device) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Device) getBasicInfo() error {

	log.Printf("Getting basic information from %X", *d.deviceInfo.IeeeAddress)

	cluster := ClusterIDBasic
	ManufacturerNameAttribute := uint32(0x004)
	ModelIdentifierAttribute := uint32(0x005)

	request := &gateway.GwReadDeviceAttributeReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    d.deviceInfo.IeeeAddress,
		},
		ClusterId:     &cluster,
		AttributeList: []uint32{ManufacturerNameAttribute, ModelIdentifierAttribute},
	}

	response := &gateway.GwReadDeviceAttributeRspInd{}
	err := d.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting basic device information state : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get basic device information. status: %s", response.Status.String())
	}

	sigs := make(map[string]string)

	manufacturerName := ""
	modelIdentifier := ""

	for _, attribute := range response.AttributeRecordList {

		switch *attribute.AttributeId {
		case ManufacturerNameAttribute:
			manufacturerName = string(attribute.AttributeValue)
		case ModelIdentifierAttribute:
			modelIdentifier = string(attribute.AttributeValue)
		default:
			log.Printf("Unknown attribute returned when finding basic info %s", *attribute.AttributeId)
		}
	}

	sigs["zigbee:ManufacturerName"] = manufacturerName
	sigs["zigbee:ModelIdentifier"] = modelIdentifier

	name := fmt.Sprintf("%s by %s", modelIdentifier, manufacturerName)
	id := fmt.Sprintf("%X", *d.deviceInfo.IeeeAddress)

	d.info.Name = &name

	d.info.Signatures = &sigs
	d.info.NaturalIDType = "zigbee"
	d.info.NaturalID = id
	d.info.Name = &name

	return nil
}

func (d *Driver) onDeviceFound(deviceInfo *nwkmgr.NwkDeviceInfoT) {

	fmt.Printf("\n\n")
	fmt.Printf("---- Found Device IEEE:%X ----\f", *deviceInfo.IeeeAddress)

	if d.devices[*deviceInfo.IeeeAddress] != nil {
		fmt.Printf("We've already seen this device")
	}

	/*sigs, _ := simplejson.NewJson([]byte(`{
	    "ninja:manufacturer": "Phillips",
	    "ninja:productName": "Hue",
	    "manufacturer:productModelId": "",
	    "ninja:productType": "Light",
	    "ninja:thingType": "light"
	}`))*/

	device := &Device{
		info:       &model.Device{},
		driver:     d,
		deviceInfo: deviceInfo,
	}

	err := device.getBasicInfo()
	if err != nil {
		log.Printf("Failed to get basic info: %s", err)
		return
	}

	spew.Dump(deviceInfo)

	err = d.conn.ExportDevice(device)

	log.Printf("Got device : %d", *deviceInfo.IeeeAddress)

	for _, endpoint := range deviceInfo.SimpleDescList {
		log.Printf("Got endpoint : %d", *endpoint.EndpointId)

		if containsUInt32(endpoint.InputClusters, ClusterIDOnOff) {
			log.Printf("This endpoint has on/off cluster")

			onOff := &OnOffChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := onOff.init()
			if err != nil {
				log.Printf("Failed initialising on/off channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDPower) {
			log.Printf("This endpoint has power cluster")

			power := &PowerChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := power.init()
			if err != nil {
				log.Printf("Failed initialising power channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDTemp) {
			log.Printf("This endpoint has temperature cluster")

			temp := &TempChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := temp.init()
			if err != nil {
				log.Printf("Failed initialising temp channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDHumidity) {
			log.Printf("This endpoint has humidity cluster")

			humidity := &HumidityChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := humidity.init()
			if err != nil {
				log.Printf("Failed initialising humidity channel: %s", err)
			}

		}

	}

	d.devices[*deviceInfo.IeeeAddress] = device

	fmt.Printf("---- Finished Device IEEE:%X ----\n", *deviceInfo.IeeeAddress)

}

func getCurDir() string {
	pwd, _ := os.Getwd()
	return pwd + "/"
}

func containsUInt32(hackstack []uint32, needle uint32) bool {
	for _, cluster := range hackstack {
		if cluster == needle {
			return true
		}
	}
	return false
}

func waitUntilZStackReady(checkFile string) {
	if checkFile == "" {
		return
	}

	// cooperate with zigbeeHAgw so that we don't start the zigbee driver
	// until we look somewhat stable.
	log.Printf("waiting until zigbeeHAgw writes %s", checkFile)
	for {
		if _, err := os.Stat(checkFile); err == nil {
			break
		} else {
			time.Sleep(time.Second)
		}
	}
	log.Printf("%s detected. start up continues...", checkFile)
}
<commit_msg>Use OnEvent which now supports unmarshalling.<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/events"
	"github.com/ninjasphere/go-zigbee"
	"github.com/ninjasphere/go-zigbee/gateway"
	"github.com/ninjasphere/go-zigbee/nwkmgr"
)

const (
	ClusterIDBasic    uint32 = 0x00
	ClusterIDOnOff    uint32 = 0x06
	ClusterIDTemp     uint32 = 0x402
	ClusterIDHumidity uint32 = 0x405
	ClusterIDPower    uint32 = 0x702
)

var (
	info = ninja.LoadModuleInfo("./package.json")
)

type ZStackConfig struct {
	Hostname       string
	OtasrvrPort    int
	GatewayPort    int
	NwkmgrPort     int
	StableFlagFile string
}

type DriverConfig struct {
}

type Driver struct {
	conn      *ninja.Connection
	userAgent *ninja.ServiceClient
	sendEvent func(event string, payload interface{}) error

	devices map[uint64]*Device

	NetworkReady chan bool

	nwkmgrConn  *zigbee.ZStackNwkMgr
	gatewayConn *zigbee.ZStackGateway
	otaConn     *zigbee.ZStackOta
	config      *DriverConfig
}

func NewDriver() *Driver {
	return &Driver{
		NetworkReady: make(chan bool),
	}
}

type Device struct {
	info       *model.Device
	driver     *Driver
	deviceInfo *nwkmgr.NwkDeviceInfoT
	channels   []Channel
	sendEvent  func(event string, payload interface{}) error
}

type Channel struct {
	device   *Device
	endpoint *nwkmgr.NwkSimpleDescriptorT
}

func (d *Driver) GetModuleInfo() *model.Module {
	return info
}

func (d *Driver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Driver) Reset(hard bool) error {
	return d.nwkmgrConn.Reset(hard)
}

func (d *Driver) Start(config *DriverConfig) error {
	d.config = config
	return d.sendEvent("config", config)
}

func (d *Driver) Stop() error {
	return fmt.Errorf("This driver does not support being stopped. YOU HAVE NO POWER HERE.")
}

func (d *Driver) StartPairing(period uint32) (*uint32, error) {
	err := d.PermitJoin(uint32(period))
	if err != nil {
		err = fmt.Errorf("permit Join failed: %v", err)
		log.Fatalf("%s", err)
	}
	return &period, err
}

func (d *Driver) OnPairingRequest(req *events.PairingRequest, topicKeys map[string]string) bool {
     log.Printf("Pairing request of %d seconds received from %s", req.Duration, topicKeys["deviceId"])
     d.PermitJoin(uint32(req.Duration))
     return true
}

func (d *Driver) PermitJoin(period uint32) error {

	log.Printf("Join window will open for %d seconds.", period)

	//joinTime := uint32(30)
	permitJoinRequest := &nwkmgr.NwkSetPermitJoinReq{
		PermitJoinTime: &period,
		PermitJoin:     nwkmgr.NwkPermitJoinTypeT_PERMIT_ALL.Enum(),
	}

	permitJoinResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	go func() {
		// logging the close of the join window is helpful when debugging
		// join behaviour.
		time.Sleep(time.Duration(period) * time.Second)
		log.Printf("Join window has closed after %d seconds.", period)
	}()
	err := d.nwkmgrConn.SendCommand(permitJoinRequest, permitJoinResponse)
	if err != nil {
		return fmt.Errorf("Failed to enable joining: %s", err)
	}
	if permitJoinResponse.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to enable joining: %s", permitJoinResponse.Status)
	}
	//log.Println("Permit join response: ")
	//outJSON(permitJoinResponse)

	return nil
}

func (d *Driver) Connect(cfg *ZStackConfig, networkReady chan bool) error {

	waitUntilZStackReady(config.StableFlagFile)

	d.devices = make(map[uint64]*Device)

	conn, err := ninja.Connect("com.ninjablocks.zigbee")
	if err != nil {
		return fmt.Errorf("Could not connect to MQTT: %s", err)
	}

	d.userAgent = conn.GetServiceClient("$device/:deviceId/channel/user-agent")
	err = d.userAgent.OnEvent("pairing-requested", d.OnPairingRequest)
	if err != nil {
	       return fmt.Errorf("Failed register user-agent service client: %s", err)
	}

	err = conn.ExportDriver(d)
	if err != nil {
		return fmt.Errorf("Could not export driver: %s", err)
	}

	d.nwkmgrConn, err = zigbee.ConnectToNwkMgrServer(cfg.Hostname, cfg.NwkmgrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to nwkmgr %s", err)
	}
	d.nwkmgrConn.OnDeviceFound = func(deviceInfo *nwkmgr.NwkDeviceInfoT) {
		d.onDeviceFound(deviceInfo)
	}

	done := false
	d.nwkmgrConn.OnNetworkReady = func() {
		if !done {
			done = true
			networkReady <- true
		}
	}

	d.otaConn, err = zigbee.ConnectToOtaServer(cfg.Hostname, cfg.OtasrvrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to ota server %s", err)
	}

	d.gatewayConn, err = zigbee.ConnectToGatewayServer(cfg.Hostname, cfg.GatewayPort)
	if err != nil {
		return fmt.Errorf("Error connecting to gateway %s", err)
	}

	/*keyResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	keyRequest := &nwkmgr.NwkChangeNwkKeyReq{
		NewKey: []byte{0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39},
	}

	err = d.nwkmgrConn.SendCommand(keyRequest, keyResponse)
	if err != nil {
		return fmt.Errorf("Failed setting network key: %s", err)
	}

	spew.Dump(keyResponse)
	log.Println("Sleeping for 10 seconds after setting network key")

	time.Sleep(10 * time.Second)*/

	networkInfo := &nwkmgr.NwkZigbeeNwkInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkZigbeeNwkInfoReq{}, networkInfo)
	if err != nil {
		return fmt.Errorf("Failed getting network info: %s", err)
	}

	if *networkInfo.Status == nwkmgr.NwkNetworkStatusT_NWK_DOWN {
		return fmt.Errorf("The ZigBee network is down")
	}

	localDevice := &nwkmgr.NwkGetLocalDeviceInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetLocalDeviceInfoReq{}, localDevice)
	if err != nil {
		return fmt.Errorf("Failed getting local device info: %s", err)
	}

	spew.Dump("device info", localDevice.String())

	networkKey := &nwkmgr.NwkGetNwkKeyCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetNwkKeyReq{}, networkKey)
	if err != nil {
		return fmt.Errorf("Failed getting network key: %s", err)
	}

	spew.Dump(networkKey)

	log.Printf("Started coordinator. Channel:%d Pan ID:0x%X Key:% X", *networkInfo.NwkChannel, *networkInfo.PanId, networkKey.NewKey)

	return nil
}

func (d *Driver) FetchDevices() error {

	return d.nwkmgrConn.FetchDeviceList()

}

func (d *Device) GetDriver() ninja.Driver {
	return d.driver
}

func (d *Device) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *Device) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Device) getBasicInfo() error {

	log.Printf("Getting basic information from %X", *d.deviceInfo.IeeeAddress)

	cluster := ClusterIDBasic
	ManufacturerNameAttribute := uint32(0x004)
	ModelIdentifierAttribute := uint32(0x005)

	request := &gateway.GwReadDeviceAttributeReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    d.deviceInfo.IeeeAddress,
		},
		ClusterId:     &cluster,
		AttributeList: []uint32{ManufacturerNameAttribute, ModelIdentifierAttribute},
	}

	response := &gateway.GwReadDeviceAttributeRspInd{}
	err := d.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting basic device information state : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get basic device information. status: %s", response.Status.String())
	}

	sigs := make(map[string]string)

	manufacturerName := ""
	modelIdentifier := ""

	for _, attribute := range response.AttributeRecordList {

		switch *attribute.AttributeId {
		case ManufacturerNameAttribute:
			manufacturerName = string(attribute.AttributeValue)
		case ModelIdentifierAttribute:
			modelIdentifier = string(attribute.AttributeValue)
		default:
			log.Printf("Unknown attribute returned when finding basic info %s", *attribute.AttributeId)
		}
	}

	sigs["zigbee:ManufacturerName"] = manufacturerName
	sigs["zigbee:ModelIdentifier"] = modelIdentifier

	name := fmt.Sprintf("%s by %s", modelIdentifier, manufacturerName)
	id := fmt.Sprintf("%X", *d.deviceInfo.IeeeAddress)

	d.info.Name = &name

	d.info.Signatures = &sigs
	d.info.NaturalIDType = "zigbee"
	d.info.NaturalID = id
	d.info.Name = &name

	return nil
}

func (d *Driver) onDeviceFound(deviceInfo *nwkmgr.NwkDeviceInfoT) {

	fmt.Printf("\n\n")
	fmt.Printf("---- Found Device IEEE:%X ----\f", *deviceInfo.IeeeAddress)

	if d.devices[*deviceInfo.IeeeAddress] != nil {
		fmt.Printf("We've already seen this device")
	}

	/*sigs, _ := simplejson.NewJson([]byte(`{
	    "ninja:manufacturer": "Phillips",
	    "ninja:productName": "Hue",
	    "manufacturer:productModelId": "",
	    "ninja:productType": "Light",
	    "ninja:thingType": "light"
	}`))*/

	device := &Device{
		info:       &model.Device{},
		driver:     d,
		deviceInfo: deviceInfo,
	}

	err := device.getBasicInfo()
	if err != nil {
		log.Printf("Failed to get basic info: %s", err)
		return
	}

	spew.Dump(deviceInfo)

	err = d.conn.ExportDevice(device)

	log.Printf("Got device : %d", *deviceInfo.IeeeAddress)

	for _, endpoint := range deviceInfo.SimpleDescList {
		log.Printf("Got endpoint : %d", *endpoint.EndpointId)

		if containsUInt32(endpoint.InputClusters, ClusterIDOnOff) {
			log.Printf("This endpoint has on/off cluster")

			onOff := &OnOffChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := onOff.init()
			if err != nil {
				log.Printf("Failed initialising on/off channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDPower) {
			log.Printf("This endpoint has power cluster")

			power := &PowerChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := power.init()
			if err != nil {
				log.Printf("Failed initialising power channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDTemp) {
			log.Printf("This endpoint has temperature cluster")

			temp := &TempChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := temp.init()
			if err != nil {
				log.Printf("Failed initialising temp channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDHumidity) {
			log.Printf("This endpoint has humidity cluster")

			humidity := &HumidityChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := humidity.init()
			if err != nil {
				log.Printf("Failed initialising humidity channel: %s", err)
			}

		}

	}

	d.devices[*deviceInfo.IeeeAddress] = device

	fmt.Printf("---- Finished Device IEEE:%X ----\n", *deviceInfo.IeeeAddress)

}

func getCurDir() string {
	pwd, _ := os.Getwd()
	return pwd + "/"
}

func containsUInt32(hackstack []uint32, needle uint32) bool {
	for _, cluster := range hackstack {
		if cluster == needle {
			return true
		}
	}
	return false
}

func waitUntilZStackReady(checkFile string) {
	if checkFile == "" {
		return
	}

	// cooperate with zigbeeHAgw so that we don't start the zigbee driver
	// until we look somewhat stable.
	log.Printf("waiting until zigbeeHAgw writes %s", checkFile)
	for {
		if _, err := os.Stat(checkFile); err == nil {
			break
		} else {
			time.Sleep(time.Second)
		}
	}
	log.Printf("%s detected. start up continues...", checkFile)
}
<|endoftext|>


<commit_before>package param

import (
	"fmt"
	"html/template"
	"strconv"
	"strings"
	"time"
)

const (
	EmptyHTML      = template.HTML(``)
	EmptyJS        = template.JS(``)
	EmptyCSS       = template.CSS(``)
	EmptyHTMLAttr  = template.HTMLAttr(``)
	DateTimeLayout = `2006-01-02 15:04:05`
	DateTimeShort  = `2006-01-02 15:04`
	DateLayout     = `2006-01-02`
	TimeLayout     = `15:04:05`
	DateMd         = `01-02`
	DateShort      = `06-01-02`
	TimeShort      = `15:04`
)

func AsString(val interface{}) string {
	switch v := val.(type) {
	case string:
		return v
	case nil:
		return ``
	default:
		return fmt.Sprint(val)
	}
}

func Split(val interface{}, sep string, limit ...int) StringSlice {
	str := AsString(val)
	if len(str) == 0 {
		return StringSlice{}
	}
	if len(limit) > 0 {
		return strings.SplitN(str, sep, limit[0])
	}
	return strings.Split(str, sep)
}

func Trim(val interface{}) String {
	return String(strings.TrimSpace(AsString(val)))
}

func AsHTML(val interface{}) template.HTML {
	switch v := val.(type) {
	case template.HTML:
		return v
	case string:
		return template.HTML(v)
	case nil:
		return EmptyHTML
	default:
		return template.HTML(fmt.Sprint(v))
	}
}

func AsHTMLAttr(val interface{}) template.HTMLAttr {
	switch v := val.(type) {
	case template.HTMLAttr:
		return v
	case string:
		return template.HTMLAttr(v)
	case nil:
		return EmptyHTMLAttr
	default:
		return template.HTMLAttr(fmt.Sprint(v))
	}
}

func AsJS(val interface{}) template.JS {
	switch v := val.(type) {
	case template.JS:
		return v
	case string:
		return template.JS(v)
	case nil:
		return EmptyJS
	default:
		return template.JS(fmt.Sprint(v))
	}
}

func AsCSS(val interface{}) template.CSS {
	switch v := val.(type) {
	case template.CSS:
		return v
	case string:
		return template.CSS(v)
	case nil:
		return EmptyCSS
	default:
		return template.CSS(fmt.Sprint(v))
	}
}

func AsBool(val interface{}) bool {
	switch v := val.(type) {
	case bool:
		return v
	case string:
		if len(v) > 0 {
			r, _ := strconv.ParseBool(v)
			return r
		}
		return false
	case nil:
		return false
	default:
		p := fmt.Sprint(v)
		if len(p) > 0 {
			r, _ := strconv.ParseBool(p)
			return r
		}
	}
	return false
}

func AsFloat64(val interface{}) float64 {
	switch v := val.(type) {
	case float64:
		return v
	case int64:
		return float64(v)
	case uint64:
		return float64(v)
	case float32:
		return float64(v)
	case int32:
		return float64(v)
	case uint32:
		return float64(v)
	case int:
		return float64(v)
	case uint:
		return float64(v)
	case string:
		i, _ := strconv.ParseFloat(v, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseFloat(s, 64)
		return i
	}
}

func AsFloat32(val interface{}) float32 {
	switch v := val.(type) {
	case float32:
		return v
	case int32:
		return float32(v)
	case uint32:
		return float32(v)
	case string:
		f, _ := strconv.ParseFloat(v, 32)
		return float32(f)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		f, _ := strconv.ParseFloat(s, 32)
		return float32(f)
	}
}

func AsInt8(val interface{}) int8 {
	switch v := val.(type) {
	case int8:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 8)
		return int8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		i, _ := strconv.ParseInt(s, 10, 8)
		return int8(i)
	}
}

func AsInt16(val interface{}) int16 {
	switch v := val.(type) {
	case int16:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 16)
		return int16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 16)
		return int16(i)
	}
}

func AsInt(val interface{}) int {
	switch v := val.(type) {
	case int:
		return v
	case string:
		i, _ := strconv.Atoi(v)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.Atoi(s)
		return i
	}
}

func AsInt32(val interface{}) int32 {
	switch v := val.(type) {
	case int32:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 32)
		return int32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 32)
		return int32(i)
	}
}

func AsInt64(val interface{}) int64 {
	switch v := val.(type) {
	case int64:
		return v
	case int32:
		return int64(v)
	case uint32:
		return int64(v)
	case int:
		return int64(v)
	case uint:
		return int64(v)
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 64)
		return i
	}
}

func Decr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v -= n
	return v
}

func Incr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v += n
	return v
}

func AsUint8(val interface{}) uint8 {
	switch v := val.(type) {
	case uint8:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 8)
		return uint8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 8)
		return uint8(i)
	}
}

func AsUint16(val interface{}) uint16 {
	switch v := val.(type) {
	case uint16:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 16)
		return uint16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 16)
		return uint16(i)
	}
}

func AsUint(val interface{}) uint {
	switch v := val.(type) {
	case uint:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint(i)
	}
}

func AsUint32(val interface{}) uint32 {
	switch v := val.(type) {
	case uint32:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint32(i)
	}
}

func AsUint64(val interface{}) uint64 {
	switch v := val.(type) {
	case uint64:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 64)
		return i
	}
}

func AsTimestamp(val interface{}) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		s := strings.SplitN(p, `.`, 2)
		var sec int64
		var nsec int64
		switch len(s) {
		case 2:
			nsec = String(s[1]).Int64()
			fallthrough
		case 1:
			sec = String(s[0]).Int64()
		}
		return time.Unix(sec, nsec)
	}
	return emptyTime
}

func AsDateTime(val interface{}, layouts ...string) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		layout := DateTimeLayout
		if len(layouts) > 0 {
			layout = layouts[0]
		}
		t, _ := time.Parse(layout, p)
		return t
	}
	return emptyTime
}
<commit_msg>update<commit_after>package param

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"html/template"
	"strconv"
	"strings"
	"time"
)

const (
	EmptyHTML      = template.HTML(``)
	EmptyJS        = template.JS(``)
	EmptyCSS       = template.CSS(``)
	EmptyHTMLAttr  = template.HTMLAttr(``)
	DateTimeLayout = `2006-01-02 15:04:05`
	DateTimeShort  = `2006-01-02 15:04`
	DateLayout     = `2006-01-02`
	TimeLayout     = `15:04:05`
	DateMd         = `01-02`
	DateShort      = `06-01-02`
	TimeShort      = `15:04`
)

func AsString(val interface{}) string {
	switch v := val.(type) {
	case string:
		return v
	case nil:
		return ``
	default:
		return fmt.Sprint(val)
	}
}

func AsBytes(val interface{}) []byte {
	switch v := val.(type) {
	case []byte:
		return v
	case nil:
		return nil
	case string:
		return []byte(v)
	default:
		var buf bytes.Buffer
		enc := gob.NewEncoder(&buf)
		err := enc.Encode(val)
		if err != nil {
			return nil
		}
		return buf.Bytes()
	}
}

func Split(val interface{}, sep string, limit ...int) StringSlice {
	str := AsString(val)
	if len(str) == 0 {
		return StringSlice{}
	}
	if len(limit) > 0 {
		return strings.SplitN(str, sep, limit[0])
	}
	return strings.Split(str, sep)
}

func Trim(val interface{}) String {
	return String(strings.TrimSpace(AsString(val)))
}

func AsHTML(val interface{}) template.HTML {
	switch v := val.(type) {
	case template.HTML:
		return v
	case string:
		return template.HTML(v)
	case nil:
		return EmptyHTML
	default:
		return template.HTML(fmt.Sprint(v))
	}
}

func AsHTMLAttr(val interface{}) template.HTMLAttr {
	switch v := val.(type) {
	case template.HTMLAttr:
		return v
	case string:
		return template.HTMLAttr(v)
	case nil:
		return EmptyHTMLAttr
	default:
		return template.HTMLAttr(fmt.Sprint(v))
	}
}

func AsJS(val interface{}) template.JS {
	switch v := val.(type) {
	case template.JS:
		return v
	case string:
		return template.JS(v)
	case nil:
		return EmptyJS
	default:
		return template.JS(fmt.Sprint(v))
	}
}

func AsCSS(val interface{}) template.CSS {
	switch v := val.(type) {
	case template.CSS:
		return v
	case string:
		return template.CSS(v)
	case nil:
		return EmptyCSS
	default:
		return template.CSS(fmt.Sprint(v))
	}
}

func AsBool(val interface{}) bool {
	switch v := val.(type) {
	case bool:
		return v
	case string:
		if len(v) > 0 {
			r, _ := strconv.ParseBool(v)
			return r
		}
		return false
	case nil:
		return false
	default:
		p := fmt.Sprint(v)
		if len(p) > 0 {
			r, _ := strconv.ParseBool(p)
			return r
		}
	}
	return false
}

func AsFloat64(val interface{}) float64 {
	switch v := val.(type) {
	case float64:
		return v
	case int64:
		return float64(v)
	case uint64:
		return float64(v)
	case float32:
		return float64(v)
	case int32:
		return float64(v)
	case uint32:
		return float64(v)
	case int:
		return float64(v)
	case uint:
		return float64(v)
	case string:
		i, _ := strconv.ParseFloat(v, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseFloat(s, 64)
		return i
	}
}

func AsFloat32(val interface{}) float32 {
	switch v := val.(type) {
	case float32:
		return v
	case int32:
		return float32(v)
	case uint32:
		return float32(v)
	case string:
		f, _ := strconv.ParseFloat(v, 32)
		return float32(f)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		f, _ := strconv.ParseFloat(s, 32)
		return float32(f)
	}
}

func AsInt8(val interface{}) int8 {
	switch v := val.(type) {
	case int8:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 8)
		return int8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(val)
		i, _ := strconv.ParseInt(s, 10, 8)
		return int8(i)
	}
}

func AsInt16(val interface{}) int16 {
	switch v := val.(type) {
	case int16:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 16)
		return int16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 16)
		return int16(i)
	}
}

func AsInt(val interface{}) int {
	switch v := val.(type) {
	case int:
		return v
	case string:
		i, _ := strconv.Atoi(v)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.Atoi(s)
		return i
	}
}

func AsInt32(val interface{}) int32 {
	switch v := val.(type) {
	case int32:
		return v
	case string:
		i, _ := strconv.ParseInt(v, 10, 32)
		return int32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 32)
		return int32(i)
	}
}

func AsInt64(val interface{}) int64 {
	switch v := val.(type) {
	case int64:
		return v
	case int32:
		return int64(v)
	case uint32:
		return int64(v)
	case int:
		return int64(v)
	case uint:
		return int64(v)
	case string:
		i, _ := strconv.ParseInt(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseInt(s, 10, 64)
		return i
	}
}

func Decr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v -= n
	return v
}

func Incr(val interface{}, n int64) int64 {
	v, _ := val.(int64)
	v += n
	return v
}

func AsUint8(val interface{}) uint8 {
	switch v := val.(type) {
	case uint8:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 8)
		return uint8(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 8)
		return uint8(i)
	}
}

func AsUint16(val interface{}) uint16 {
	switch v := val.(type) {
	case uint16:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 16)
		return uint16(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 16)
		return uint16(i)
	}
}

func AsUint(val interface{}) uint {
	switch v := val.(type) {
	case uint:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint(i)
	}
}

func AsUint32(val interface{}) uint32 {
	switch v := val.(type) {
	case uint32:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 32)
		return uint32(i)
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 32)
		return uint32(i)
	}
}

func AsUint64(val interface{}) uint64 {
	switch v := val.(type) {
	case uint64:
		return v
	case string:
		i, _ := strconv.ParseUint(v, 10, 64)
		return i
	case nil:
		return 0
	default:
		s := fmt.Sprint(v)
		i, _ := strconv.ParseUint(s, 10, 64)
		return i
	}
}

func AsTimestamp(val interface{}) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		s := strings.SplitN(p, `.`, 2)
		var sec int64
		var nsec int64
		switch len(s) {
		case 2:
			nsec = String(s[1]).Int64()
			fallthrough
		case 1:
			sec = String(s[0]).Int64()
		}
		return time.Unix(sec, nsec)
	}
	return emptyTime
}

func AsDateTime(val interface{}, layouts ...string) time.Time {
	p := AsString(val)
	if len(p) > 0 {
		layout := DateTimeLayout
		if len(layouts) > 0 {
			layout = layouts[0]
		}
		t, _ := time.Parse(layout, p)
		return t
	}
	return emptyTime
}
<|endoftext|>


<commit_before>package scraper

import (
	"encoding/json"
	"errors"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"time"

	"github.com/gorilla/feeds"
)

type InstagramSource struct {
	userId string
}

func NewInstagramSource(userId string) *InstagramSource {
	return &InstagramSource{
		userId: userId,
	}
}

func (s *InstagramSource) Scrape() (*feeds.Feed, error) {
	res, err := http.Get("https://www.instagram.com/" + s.userId)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	return s.ScrapeFromReader(res.Body)
}

var sharedDataRe = regexp.MustCompile(`window\._sharedData\s*=\s*({.+})[\s\n]*[;<]`)

type instagramData struct {
	EntryData struct {
		ProfilePage []struct {
			User struct {
				UserName  string `json:"username"`
				Id        string `json:"id"`
				Biography string `json:"biography"`
				FullName  string `json:"full_name"`
				Media     struct {
					Nodes []struct {
						Code        string `json:"code"`
						Date        int64  `json:"date"`
						Deimensions struct {
							Width  int `json:"width"`
							Height int `json:"height"`
						} `json:"dimensions"`
						Caption      string `json:"caption"`
						ThumbnailSrc string `json:"thumbnail_src"`
						IsVideo      bool   `json:"is_video"`
						Id           string `json:"id"`
						DisplaySrc   string `json:"display_src"`
					} `json:"nodes"`
				} `json:"media"`
			} `json:"user"`
		}
	} `json:"entry_data"`
}

func (s *InstagramSource) ScrapeFromReader(reader io.Reader) (*feeds.Feed, error) {
	src, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	sharedDataRe := sharedDataRe.Copy()
	submatches := sharedDataRe.FindSubmatch(src)
	if len(submatches) == 0 {
		return nil, errors.New("data not found")
	}

	var data instagramData
	if err := json.Unmarshal(submatches[1], &data); err != nil {
		return nil, err
	}

	if len(data.EntryData.ProfilePage) == 0 {
		return nil, errors.New("ProfilePage item not found")
	}

	user := data.EntryData.ProfilePage[0].User

	loc, err := time.LoadLocation("UTC")
	if err != nil {
		return nil, err
	}

	items := make([]*feeds.Item, 0, len(user.Media.Nodes))
	for _, node := range user.Media.Nodes {
		items = append(items, &feeds.Item{
			Title:       node.Caption,
			Created:     time.Unix(node.Date, 0).In(loc),
			Link:        &feeds.Link{Href: fmt.Sprintf("http://www.instagram.com/p/%s/", node.Code)},
			Description: fmt.Sprintf("%s
", html.EscapeString(node.Caption), node.DisplaySrc),
		})
	}

	return &feeds.Feed{
		Title:       user.FullName,
		Link:        &feeds.Link{Href: fmt.Sprintf("https://www.instagram.com/%s/", user.UserName)},
		Description: user.Biography,
		Items:       items,
	}, nil
}
<commit_msg>Remove emoji from caption<commit_after>package scraper

import (
	"encoding/json"
	"errors"
	"fmt"
	"html"
	"io"
	"io/ioutil"
	"net/http"
	"regexp"
	"time"

	"github.com/gorilla/feeds"
)

type InstagramSource struct {
	userId string
}

func NewInstagramSource(userId string) *InstagramSource {
	return &InstagramSource{
		userId: userId,
	}
}

func (s *InstagramSource) Scrape() (*feeds.Feed, error) {
	res, err := http.Get("https://www.instagram.com/" + s.userId)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()

	return s.ScrapeFromReader(res.Body)
}

var sharedDataRe = regexp.MustCompile(`window\._sharedData\s*=\s*({.+})[\s\n]*[;<]`)

type instagramData struct {
	EntryData struct {
		ProfilePage []struct {
			User struct {
				UserName  string `json:"username"`
				Id        string `json:"id"`
				Biography string `json:"biography"`
				FullName  string `json:"full_name"`
				Media     struct {
					Nodes []struct {
						Code        string `json:"code"`
						Date        int64  `json:"date"`
						Deimensions struct {
							Width  int `json:"width"`
							Height int `json:"height"`
						} `json:"dimensions"`
						Caption      string `json:"caption"`
						ThumbnailSrc string `json:"thumbnail_src"`
						IsVideo      bool   `json:"is_video"`
						Id           string `json:"id"`
						DisplaySrc   string `json:"display_src"`
					} `json:"nodes"`
				} `json:"media"`
			} `json:"user"`
		}
	} `json:"entry_data"`
}

var emojiRe = regexp.MustCompile(`[^\x{0000}-\x{ffff}]+`)

func (s *InstagramSource) ScrapeFromReader(reader io.Reader) (*feeds.Feed, error) {
	src, err := ioutil.ReadAll(reader)
	if err != nil {
		return nil, err
	}

	sharedDataRe := sharedDataRe.Copy()
	submatches := sharedDataRe.FindSubmatch(src)
	if len(submatches) == 0 {
		return nil, errors.New("data not found")
	}

	var data instagramData
	if err := json.Unmarshal(submatches[1], &data); err != nil {
		return nil, err
	}

	if len(data.EntryData.ProfilePage) == 0 {
		return nil, errors.New("ProfilePage item not found")
	}

	user := data.EntryData.ProfilePage[0].User

	loc, err := time.LoadLocation("UTC")
	if err != nil {
		return nil, err
	}

	items := make([]*feeds.Item, 0, len(user.Media.Nodes))
	for _, node := range user.Media.Nodes {
		caption := emojiRe.ReplaceAllString(node.Caption, "")
		items = append(items, &feeds.Item{
			Title:       caption,
			Created:     time.Unix(node.Date, 0).In(loc),
			Link:        &feeds.Link{Href: fmt.Sprintf("http://www.instagram.com/p/%s/", node.Code)},
			Description: fmt.Sprintf("%s
", html.EscapeString(caption), node.DisplaySrc),
		})
	}

	return &feeds.Feed{
		Title:       user.FullName,
		Link:        &feeds.Link{Href: fmt.Sprintf("https://www.instagram.com/%s/", user.UserName)},
		Description: user.Biography,
		Items:       items,
	}, nil
}
<|endoftext|>


<commit_before>package toml

import (
	"errors"
	"fmt"
	"github.com/pelletier/go-toml"
	"go/ast"
	"reflect"
	"time"
	"unicode"
)

var nilValue = reflect.ValueOf(nil)

func Load(v interface{}, file string, env string) error {
	tree, err := toml.LoadFile(file)
	if err != nil {
		return err
	}

	if v == nil {
		return fmt.Errorf("v must not be nil")
	}
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
		return fmt.Errorf("v must be a struct pointer")
	}

	rv = rv.Elem()
	rt := rv.Type()

	for i := 0; i < rt.NumField(); i++ {
		ft := rt.Field(i)
		fv := rv.Field(i)
		if !ast.IsExported(ft.Name) {
			continue
		}
		name := getFieldName(ft)
		value, err := getValue(fv.Type(), tree, name, env)
		if err != nil {
			return err
		}
		fv.Set(value)
	}
	return nil
}

func getValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	switch {
	case t == reflect.TypeOf(time.Time{}):
		return getBasicValue(t, tree, elem, env)
	case t.Kind() == reflect.Struct:
		return getStructValue(t, tree, elem, env)
	case t.Kind() == reflect.Map:
		return getMapValue(t, tree, elem, env)
	case t.Kind() == reflect.Array, t.Kind() == reflect.Slice:
		return getArrayValue(t, tree, elem, env)
	default:
		return getBasicValue(t, tree, elem, env)
	}
}

func getBasicValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	p, err := findPath(tree, elem, env)
	if err != nil {
		return nilValue, err
	}
	v := tree.Get(p)
	if reflect.TypeOf(v) != t {
		return nilValue, errors.New("invalid type")
	}
	return reflect.ValueOf(v), nil
}

func getArrayValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Array && t.Kind() != reflect.Slice {
		return nilValue, errors.New("invalid type")
	}
	p, err := findPath(tree, elem, env)
	if err != nil {
		return nilValue, err
	}
	v := tree.Get(p)
	et := t.Elem()
	rv := reflect.MakeSlice(t, 0, 0)

	switch ary := v.(type) {
	case []*toml.TomlTree:
		for _, childTree := range ary {
			ev, e := getValue(et, childTree, "", elem)
			if e != nil {
				return nilValue, e
			}
			rv = reflect.Append(rv, ev)
		}
	case []interface{}:
		for _, a := range ary {
			rv = reflect.Append(rv, reflect.ValueOf(a))
		}
	default:
		return nilValue, errors.New("Invalid type")
	}
	return rv, nil
}

func getMapValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Map || t.Key().Kind() != reflect.String {
		return nilValue, errors.New("invalid type")
	}
	target := tree
	// find env if elem defined
	if elem != "" {
		p, err := findPath(tree, elem, env)
		if err != nil {
			return nilValue, err
		}
		newTree := tree.Get(p)
		switch val := newTree.(type) {
		case *toml.TomlTree:
			target = val
		default:
			return nilValue, errors.New("invalud tree")
		}
	}
	// get map value from tree
	rv := reflect.MakeMap(t)
	for _, k := range target.Keys() {
		v, err := getValue(t.Elem(), target, k, env)
		if err != nil {
			continue
		}
		rv.SetMapIndex(reflect.ValueOf(k), v)
	}
	return rv, nil
}

func getStructValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Struct {
		return nilValue, errors.New("invalid type")
	}
	target := tree
	// find env if elem defined
	if elem != "" {
		p, err := findPath(tree, elem, env)
		if err != nil {
			return nilValue, err
		}
		newTree := tree.Get(p)
		switch val := newTree.(type) {
		case *toml.TomlTree:
			target = val
		default:
			return nilValue, errors.New("invalid tree")
		}
	}
	// get struct value from tree
	rv := reflect.New(t).Elem()
	for i := 0; i < t.NumField(); i++ {
		fv := rv.Field(i)
		ft := t.Field(i)
		if !ast.IsExported(ft.Name) {
			continue
		}
		name := getFieldName(ft)
		value, err := getValue(ft.Type, target, name, env)
		if err != nil {
			return nilValue, err
		}
		fv.Set(value)
	}
	return rv, nil
}

func getFieldName(f reflect.StructField) string {
	name := f.Tag.Get("toml")
	if name == "" {
		name = toSnake(f.Name)
	}
	return name
}

func toSnake(in string) string {
	runes := []rune(in)
	l := len(runes)

	var out []rune
	for i := 0; i < l; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < l && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func createPath(in ...string) string {
	out := ""
	for _, s := range in {
		if s != "" {
			if out == "" {
				out = s
			} else {
				out = out + "." + s
			}
		}
	}
	return out
}

func findPath(tree *toml.TomlTree, elem, env string) (string, error) {
	envPath := createPath(env, elem)
	elemPath := createPath(elem)
	if tree.Has(envPath) {
		return envPath, nil
	} else if tree.Has(elemPath) {
		return elemPath, nil
	} else {
		return "", errors.New("path not found")
	}
}
<commit_msg>use type assertion<commit_after>package toml

import (
	"errors"
	"fmt"
	"github.com/pelletier/go-toml"
	"go/ast"
	"reflect"
	"time"
	"unicode"
)

var nilValue = reflect.ValueOf(nil)

func Load(v interface{}, file string, env string) error {
	tree, err := toml.LoadFile(file)
	if err != nil {
		return err
	}

	if v == nil {
		return fmt.Errorf("v must not be nil")
	}
	rv := reflect.ValueOf(v)
	if rv.Kind() != reflect.Ptr || rv.Elem().Kind() != reflect.Struct {
		return fmt.Errorf("v must be a struct pointer")
	}

	rv = rv.Elem()
	rt := rv.Type()

	for i := 0; i < rt.NumField(); i++ {
		ft := rt.Field(i)
		fv := rv.Field(i)
		if !ast.IsExported(ft.Name) {
			continue
		}
		name := getFieldName(ft)
		value, err := getValue(fv.Type(), tree, name, env)
		if err != nil {
			return err
		}
		fv.Set(value)
	}
	return nil
}

func getValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	switch {
	case t == reflect.TypeOf(time.Time{}):
		return getBasicValue(t, tree, elem, env)
	case t.Kind() == reflect.Struct:
		return getStructValue(t, tree, elem, env)
	case t.Kind() == reflect.Map:
		return getMapValue(t, tree, elem, env)
	case t.Kind() == reflect.Array, t.Kind() == reflect.Slice:
		return getArrayValue(t, tree, elem, env)
	default:
		return getBasicValue(t, tree, elem, env)
	}
}

func getBasicValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	p, err := findPath(tree, elem, env)
	if err != nil {
		return nilValue, err
	}
	v := tree.Get(p)
	if reflect.TypeOf(v) != t {
		return nilValue, errors.New("invalid type")
	}
	return reflect.ValueOf(v), nil
}

func getArrayValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Array && t.Kind() != reflect.Slice {
		return nilValue, errors.New("invalid type")
	}
	p, err := findPath(tree, elem, env)
	if err != nil {
		return nilValue, err
	}
	v := tree.Get(p)
	et := t.Elem()
	rv := reflect.MakeSlice(t, 0, 0)

	switch ary := v.(type) {
	case []*toml.TomlTree:
		for _, childTree := range ary {
			ev, e := getValue(et, childTree, "", elem)
			if e != nil {
				return nilValue, e
			}
			rv = reflect.Append(rv, ev)
		}
	case []interface{}:
		for _, a := range ary {
			rv = reflect.Append(rv, reflect.ValueOf(a))
		}
	default:
		return nilValue, errors.New("Invalid type")
	}
	return rv, nil
}

func getMapValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Map || t.Key().Kind() != reflect.String {
		return nilValue, errors.New("invalid type")
	}
	target := tree
	// find env if elem defined
	if elem != "" {
		p, err := findPath(tree, elem, env)
		if err != nil {
			return nilValue, err
		}
		newTree := tree.Get(p)
		if val, ok := newTree.(*toml.TomlTree); ok {
			target = val
		} else {
			return nilValue, errors.New("invalud tree")
		}
	}
	// get map value from tree
	rv := reflect.MakeMap(t)
	for _, k := range target.Keys() {
		v, err := getValue(t.Elem(), target, k, env)
		if err != nil {
			continue
		}
		rv.SetMapIndex(reflect.ValueOf(k), v)
	}
	return rv, nil
}

func getStructValue(t reflect.Type, tree *toml.TomlTree, elem, env string) (reflect.Value, error) {
	if t.Kind() != reflect.Struct {
		return nilValue, errors.New("invalid type")
	}
	target := tree
	// find env if elem defined
	if elem != "" {
		p, err := findPath(tree, elem, env)
		if err != nil {
			return nilValue, err
		}
		newTree := tree.Get(p)
		if val, ok := newTree.(*toml.TomlTree); ok {
			target = val
		} else {
			return nilValue, errors.New("invalid tree")
		}
	}
	// get struct value from tree
	rv := reflect.New(t).Elem()
	for i := 0; i < t.NumField(); i++ {
		fv := rv.Field(i)
		ft := t.Field(i)
		if !ast.IsExported(ft.Name) {
			continue
		}
		name := getFieldName(ft)
		value, err := getValue(ft.Type, target, name, env)
		if err != nil {
			return nilValue, err
		}
		fv.Set(value)
	}
	return rv, nil
}

func getFieldName(f reflect.StructField) string {
	name := f.Tag.Get("toml")
	if name == "" {
		name = toSnake(f.Name)
	}
	return name
}

func toSnake(in string) string {
	runes := []rune(in)
	l := len(runes)

	var out []rune
	for i := 0; i < l; i++ {
		if i > 0 && unicode.IsUpper(runes[i]) && ((i+1 < l && unicode.IsLower(runes[i+1])) || unicode.IsLower(runes[i-1])) {
			out = append(out, '_')
		}
		out = append(out, unicode.ToLower(runes[i]))
	}

	return string(out)
}

func createPath(in ...string) string {
	out := ""
	for _, s := range in {
		if s != "" {
			if out == "" {
				out = s
			} else {
				out = out + "." + s
			}
		}
	}
	return out
}

func findPath(tree *toml.TomlTree, elem, env string) (string, error) {
	envPath := createPath(env, elem)
	elemPath := createPath(elem)
	if tree.Has(envPath) {
		return envPath, nil
	} else if tree.Has(elemPath) {
		return elemPath, nil
	} else {
		return "", errors.New("path not found")
	}
}
<|endoftext|>


<commit_before>package redis

import (
	"fmt"
	"reflect"
	"strconv"
	"time"
)

type (
	HashValue   map[string][]byte
	HashHash    map[string]HashValue
	HashSub     map[string][]*ChannelWriter
	HashBrStack map[string]*Stack
)

type Database struct {
	children map[int]*Database
	parent   *Database

	values  HashValue
	hvalues HashHash
	brstack HashBrStack

	sub HashSub
}

func NewDatabase(parent *Database) *Database {
	db := &Database{
		values:   make(HashValue),
		sub:      make(HashSub),
		brstack:  make(HashBrStack),
		children: map[int]*Database{},
		parent:   parent,
	}
	db.children[0] = db
	return db
}

type DefaultHandler struct {
	*Database
	currentDb int
	dbs       map[int]*Database
}

func (h *DefaultHandler) Rpush(key string, value []byte, values ...[]byte) (int, error) {
	values = append([][]byte{value}, values...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	for _, value := range values {
		h.brstack[key].PushBack(value)
	}
	return h.brstack[key].Len(), nil
}

func (h *DefaultHandler) Brpop(key string, keys ...string) (data [][]byte, err error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	if len(keys) == 0 {
		return nil, ErrParseTimeout
	}

	timeout, err := strconv.Atoi(keys[len(keys)-1])
	if err != nil {
		return nil, ErrParseTimeout
	}
	keys = keys[:len(keys)-1]

	var timeoutChan <-chan time.Time
	if timeout > 0 {
		timeoutChan = time.After(time.Duration(timeout) * time.Second)
	} else {
		timeoutChan = make(chan time.Time)
	}

	finishedChan := make(chan struct{})
	go func() {
		defer close(finishedChan)
		selectCases := []reflect.SelectCase{}
		for _, k := range keys {
			key := string(k)
			if _, exists := h.brstack[key]; !exists {
				h.brstack[key] = NewStack(k)
			}
			selectCases = append(selectCases, reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(h.brstack[key].Chan),
			})
		}
		_, recv, _ := reflect.Select(selectCases)
		s, ok := recv.Interface().(*Stack)
		if !ok {
			err = fmt.Errorf("Impossible to retrieve data. Wrong type.")
			return
		}
		data = [][]byte{[]byte(s.Key), s.PopBack()}
	}()

	select {
	case <-finishedChan:
		return data, err
	case <-timeoutChan:
		return nil, nil
	}
	return nil, nil
}

func (h *DefaultHandler) Lrange(key string, start, stop int) ([][]byte, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}

	if start < 0 {
		if start = h.brstack[key].Len() + start; start < 0 {
			start = 0
		}
	}

	var ret [][]byte
	for i := start; i <= stop; i++ {
		if val := h.brstack[key].GetIndex(i); val != nil {
			ret = append(ret, val)
		}
	}
	return ret, nil
}

func (h *DefaultHandler) Lindex(key string, index int) ([]byte, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	return h.brstack[key].GetIndex(index), nil
}

func (h *DefaultHandler) Lpush(key string, value []byte, values ...[]byte) (int, error) {
	values = append([][]byte{value}, values...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	for _, value := range values {
		h.brstack[key].PushFront(value)
	}
	return h.brstack[key].Len(), nil
}

func (h *DefaultHandler) Blpop(key string, keys ...string) (data [][]byte, err error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	if len(keys) == 0 {
		return nil, ErrParseTimeout
	}

	timeout, err := strconv.Atoi(keys[len(keys)-1])
	if err != nil {
		return nil, ErrParseTimeout
	}
	keys = keys[:len(keys)-1]

	var timeoutChan <-chan time.Time
	if timeout > 0 {
		timeoutChan = time.After(time.Duration(timeout) * time.Second)
	} else {
		timeoutChan = make(chan time.Time)
	}

	finishedChan := make(chan struct{})

	go func() {
		defer close(finishedChan)
		selectCases := []reflect.SelectCase{}
		for _, k := range keys {
			key := string(k)
			if _, exists := h.brstack[key]; !exists {
				h.brstack[key] = NewStack(k)
			}
			selectCases = append(selectCases, reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(h.brstack[key].Chan),
			})
		}
		_, recv, _ := reflect.Select(selectCases)
		s, ok := recv.Interface().(*Stack)
		if !ok {
			err = fmt.Errorf("Impossible to retrieve data. Wrong type.")
			return
		}
		data = [][]byte{[]byte(s.Key), s.PopFront()}
	}()

	select {
	case <-finishedChan:
		return data, err
	case <-timeoutChan:
		return nil, nil
	}
	return nil, nil
}

func (h *DefaultHandler) Hget(key, subkey string) ([]byte, error) {
	if h.Database == nil || h.hvalues == nil {
		return nil, nil
	}

	if v, exists := h.hvalues[key]; exists {
		if v, exists := v[subkey]; exists {
			return v, nil
		}
	}
	return nil, nil
}

func (h *DefaultHandler) Hset(key, subkey string, value []byte) (int, error) {
	ret := 0

	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.hvalues[key]; !exists {
		h.hvalues[key] = make(HashValue)
		ret = 1
	}

	if _, exists := h.hvalues[key][subkey]; !exists {
		ret = 1
	}

	h.hvalues[key][subkey] = value

	return ret, nil
}

func (h *DefaultHandler) Hgetall(key string) (HashValue, error) {
	if h.Database == nil || h.hvalues == nil {
		return nil, nil
	}
	return h.hvalues[key], nil
}

func (h *DefaultHandler) Get(key string) ([]byte, error) {
	if h.Database == nil || h.values == nil {
		return nil, nil
	}
	return h.values[key], nil
}

func (h *DefaultHandler) Set(key string, value []byte) error {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	h.values[key] = value
	return nil
}

func (h *DefaultHandler) Del(key string, keys ...string) (int, error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		return 0, nil
	}
	count := 0
	for _, k := range keys {
		if _, exists := h.values[k]; exists {
			delete(h.values, k)
			count++
		}
		if _, exists := h.hvalues[key]; exists {
			delete(h.hvalues, k)
			count++
		}
	}
	return count, nil
}

func (h *DefaultHandler) Ping() (*StatusReply, error) {
	return &StatusReply{code: "PONG"}, nil
}

func (h *DefaultHandler) Subscribe(channels ...[]byte) (*MultiChannelWriter, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	ret := &MultiChannelWriter{Chans: make([]*ChannelWriter, 0, len(channels))}
	for _, key := range channels {
		Debugf("SUBSCRIBE on %s\n", key)
		cw := &ChannelWriter{
			FirstReply: []interface{}{
				"subscribe",
				key,
				1,
			},
			Channel: make(chan []interface{}),
		}
		if h.sub[string(key)] == nil {
			h.sub[string(key)] = []*ChannelWriter{cw}
		} else {
			h.sub[string(key)] = append(h.sub[string(key)], cw)
		}
		ret.Chans = append(ret.Chans, cw)
	}
	return ret, nil
}

func (h *DefaultHandler) Publish(key string, value []byte) (int, error) {
	if h.Database == nil || h.sub == nil {
		return 0, nil
	}
	//	Debugf("Publishing %s on %s\n", value, key)
	v, exists := h.sub[key]
	if !exists {
		return 0, nil
	}
	i := 0
	for _, c := range v {
		select {
		case c.Channel <- []interface{}{
			"message",
			key,
			value,
		}:
			i++
		default:
		}
	}
	return i, nil
}

func (h *DefaultHandler) Select(key string) error {
	if h.dbs == nil {
		h.dbs = map[int]*Database{0: h.Database}
	}
	index, err := strconv.Atoi(key)
	if err != nil {
		return err
	}
	h.dbs[h.currentDb] = h.Database
	h.currentDb = index
	if _, exists := h.dbs[index]; !exists {
		println("DB not exits, create ", index)
		h.dbs[index] = NewDatabase(nil)
	}
	h.Database = h.dbs[index]
	return nil
}

func (h *DefaultHandler) Monitor() (*MonitorReply, error) {
	return &MonitorReply{}, nil
}

var lock = make(chan bool, 1)

func (h *DefaultHandler) Incr(key string) (int, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	lock <- true

	temp, _ := strconv.Atoi(string(h.values[key]))
	temp = temp + 1
	h.values[key] = []byte(strconv.Itoa(temp))

	<-lock

	return temp, nil
}

func (h *DefaultHandler) Decr(key string) (int, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	lock <- true

	temp, _ := strconv.Atoi(string(h.values[key]))
	temp = temp - 1
	h.values[key] = []byte(strconv.Itoa(temp))

	<-lock

	return temp, nil
}

func NewDefaultHandler() *DefaultHandler {
	db := NewDatabase(nil)
	ret := &DefaultHandler{
		Database:  db,
		currentDb: 0,
		dbs:       map[int]*Database{0: db},
	}
	return ret
}
<commit_msg>Add 'expire' method.<commit_after>package redis

import (
	"fmt"
	"reflect"
	"strconv"
	"time"
)

type (
	HashValue   map[string][]byte
	HashHash    map[string]HashValue
	HashSub     map[string][]*ChannelWriter
	HashBrStack map[string]*Stack
)

type Database struct {
	children map[int]*Database
	parent   *Database

	values  HashValue
	hvalues HashHash
	brstack HashBrStack

	sub HashSub
}

func NewDatabase(parent *Database) *Database {
	db := &Database{
		values:   make(HashValue),
		sub:      make(HashSub),
		brstack:  make(HashBrStack),
		children: map[int]*Database{},
		parent:   parent,
	}
	db.children[0] = db
	return db
}

type DefaultHandler struct {
	*Database
	currentDb int
	dbs       map[int]*Database
}

func (h *DefaultHandler) Rpush(key string, value []byte, values ...[]byte) (int, error) {
	values = append([][]byte{value}, values...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	for _, value := range values {
		h.brstack[key].PushBack(value)
	}
	return h.brstack[key].Len(), nil
}

func (h *DefaultHandler) Brpop(key string, keys ...string) (data [][]byte, err error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	if len(keys) == 0 {
		return nil, ErrParseTimeout
	}

	timeout, err := strconv.Atoi(keys[len(keys)-1])
	if err != nil {
		return nil, ErrParseTimeout
	}
	keys = keys[:len(keys)-1]

	var timeoutChan <-chan time.Time
	if timeout > 0 {
		timeoutChan = time.After(time.Duration(timeout) * time.Second)
	} else {
		timeoutChan = make(chan time.Time)
	}

	finishedChan := make(chan struct{})
	go func() {
		defer close(finishedChan)
		selectCases := []reflect.SelectCase{}
		for _, k := range keys {
			key := string(k)
			if _, exists := h.brstack[key]; !exists {
				h.brstack[key] = NewStack(k)
			}
			selectCases = append(selectCases, reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(h.brstack[key].Chan),
			})
		}
		_, recv, _ := reflect.Select(selectCases)
		s, ok := recv.Interface().(*Stack)
		if !ok {
			err = fmt.Errorf("Impossible to retrieve data. Wrong type.")
			return
		}
		data = [][]byte{[]byte(s.Key), s.PopBack()}
	}()

	select {
	case <-finishedChan:
		return data, err
	case <-timeoutChan:
		return nil, nil
	}
	return nil, nil
}

func (h *DefaultHandler) Lrange(key string, start, stop int) ([][]byte, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}

	if start < 0 {
		if start = h.brstack[key].Len() + start; start < 0 {
			start = 0
		}
	}

	var ret [][]byte
	for i := start; i <= stop; i++ {
		if val := h.brstack[key].GetIndex(i); val != nil {
			ret = append(ret, val)
		}
	}
	return ret, nil
}

func (h *DefaultHandler) Lindex(key string, index int) ([]byte, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	return h.brstack[key].GetIndex(index), nil
}

func (h *DefaultHandler) Lpush(key string, value []byte, values ...[]byte) (int, error) {
	values = append([][]byte{value}, values...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.brstack[key]; !exists {
		h.brstack[key] = NewStack(key)
	}
	for _, value := range values {
		h.brstack[key].PushFront(value)
	}
	return h.brstack[key].Len(), nil
}

func (h *DefaultHandler) Blpop(key string, keys ...string) (data [][]byte, err error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	if len(keys) == 0 {
		return nil, ErrParseTimeout
	}

	timeout, err := strconv.Atoi(keys[len(keys)-1])
	if err != nil {
		return nil, ErrParseTimeout
	}
	keys = keys[:len(keys)-1]

	var timeoutChan <-chan time.Time
	if timeout > 0 {
		timeoutChan = time.After(time.Duration(timeout) * time.Second)
	} else {
		timeoutChan = make(chan time.Time)
	}

	finishedChan := make(chan struct{})

	go func() {
		defer close(finishedChan)
		selectCases := []reflect.SelectCase{}
		for _, k := range keys {
			key := string(k)
			if _, exists := h.brstack[key]; !exists {
				h.brstack[key] = NewStack(k)
			}
			selectCases = append(selectCases, reflect.SelectCase{
				Dir:  reflect.SelectRecv,
				Chan: reflect.ValueOf(h.brstack[key].Chan),
			})
		}
		_, recv, _ := reflect.Select(selectCases)
		s, ok := recv.Interface().(*Stack)
		if !ok {
			err = fmt.Errorf("Impossible to retrieve data. Wrong type.")
			return
		}
		data = [][]byte{[]byte(s.Key), s.PopFront()}
	}()

	select {
	case <-finishedChan:
		return data, err
	case <-timeoutChan:
		return nil, nil
	}
	return nil, nil
}

func (h *DefaultHandler) Hget(key, subkey string) ([]byte, error) {
	if h.Database == nil || h.hvalues == nil {
		return nil, nil
	}

	if v, exists := h.hvalues[key]; exists {
		if v, exists := v[subkey]; exists {
			return v, nil
		}
	}
	return nil, nil
}

func (h *DefaultHandler) Hset(key, subkey string, value []byte) (int, error) {
	ret := 0

	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	if _, exists := h.hvalues[key]; !exists {
		h.hvalues[key] = make(HashValue)
		ret = 1
	}

	if _, exists := h.hvalues[key][subkey]; !exists {
		ret = 1
	}

	h.hvalues[key][subkey] = value

	return ret, nil
}

func (h *DefaultHandler) Hgetall(key string) (HashValue, error) {
	if h.Database == nil || h.hvalues == nil {
		return nil, nil
	}
	return h.hvalues[key], nil
}

func (h *DefaultHandler) Get(key string) ([]byte, error) {
	if h.Database == nil || h.values == nil {
		return nil, nil
	}
	return h.values[key], nil
}

func (h *DefaultHandler) Set(key string, value []byte) error {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	h.values[key] = value
	return nil
}

func (h *DefaultHandler) Del(key string, keys ...string) (int, error) {
	keys = append([]string{key}, keys...)
	if h.Database == nil {
		return 0, nil
	}
	count := 0
	for _, k := range keys {
		if _, exists := h.values[k]; exists {
			delete(h.values, k)
			count++
		}
		if _, exists := h.hvalues[key]; exists {
			delete(h.hvalues, k)
			count++
		}
	}
	return count, nil
}

func (h *DefaultHandler) Ping() (*StatusReply, error) {
	return &StatusReply{code: "PONG"}, nil
}

func (h *DefaultHandler) Subscribe(channels ...[]byte) (*MultiChannelWriter, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}
	ret := &MultiChannelWriter{Chans: make([]*ChannelWriter, 0, len(channels))}
	for _, key := range channels {
		Debugf("SUBSCRIBE on %s\n", key)
		cw := &ChannelWriter{
			FirstReply: []interface{}{
				"subscribe",
				key,
				1,
			},
			Channel: make(chan []interface{}),
		}
		if h.sub[string(key)] == nil {
			h.sub[string(key)] = []*ChannelWriter{cw}
		} else {
			h.sub[string(key)] = append(h.sub[string(key)], cw)
		}
		ret.Chans = append(ret.Chans, cw)
	}
	return ret, nil
}

func (h *DefaultHandler) Publish(key string, value []byte) (int, error) {
	if h.Database == nil || h.sub == nil {
		return 0, nil
	}
	//	Debugf("Publishing %s on %s\n", value, key)
	v, exists := h.sub[key]
	if !exists {
		return 0, nil
	}
	i := 0
	for _, c := range v {
		select {
		case c.Channel <- []interface{}{
			"message",
			key,
			value,
		}:
			i++
		default:
		}
	}
	return i, nil
}

func (h *DefaultHandler) Select(key string) error {
	if h.dbs == nil {
		h.dbs = map[int]*Database{0: h.Database}
	}
	index, err := strconv.Atoi(key)
	if err != nil {
		return err
	}
	h.dbs[h.currentDb] = h.Database
	h.currentDb = index
	if _, exists := h.dbs[index]; !exists {
		println("DB not exits, create ", index)
		h.dbs[index] = NewDatabase(nil)
	}
	h.Database = h.dbs[index]
	return nil
}

func (h *DefaultHandler) Monitor() (*MonitorReply, error) {
	return &MonitorReply{}, nil
}

var lock = make(chan bool, 1)

func (h *DefaultHandler) Incr(key string) (int, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	lock <- true

	temp, _ := strconv.Atoi(string(h.values[key]))
	temp = temp + 1
	h.values[key] = []byte(strconv.Itoa(temp))

	<-lock

	return temp, nil
}

func (h *DefaultHandler) Decr(key string) (int, error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	lock <- true

	temp, _ := strconv.Atoi(string(h.values[key]))
	temp = temp - 1
	h.values[key] = []byte(strconv.Itoa(temp))

	<-lock

	return temp, nil
}

func (h *DefaultHandler) Expire(key, after string) (error) {
	if h.Database == nil {
		h.Database = NewDatabase(nil)
	}

	d, _ := strconv.Atoi(after)

	time.AfterFunc(time.Duration(d) * time.Second, func() {
		h.Del(key)
	})

	return nil
}

func NewDefaultHandler() *DefaultHandler {
	db := NewDatabase(nil)
	ret := &DefaultHandler{
		Database:  db,
		currentDb: 0,
		dbs:       map[int]*Database{0: db},
	}
	return ret
}
<|endoftext|>