<commit_before>package hipchat

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

// RoomService gives access to the room related methods of the API.
type RoomService struct {
	client *Client
}

// Rooms represents a HipChat room list.
type Rooms struct {
	Items      []Room    `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Room represents a HipChat room.
type Room struct {
	ID                int            `json:"id"`
	Links             RoomLinks      `json:"links"`
	Name              string         `json:"name"`
	XmppJid           string         `json:"xmpp_jid"`
	Statistics        RoomStatistics `json:"statistics"`
	Created           string         `json:"created"`
	IsArchived        bool           `json:"is_archived"`
	Privacy           string         `json:"privacy"`
	IsGuestAccessible bool           `json:"is_guess_accessible"`
	Topic             string         `json:"topic"`
	Participants      []User         `json:"participants"`
	Owner             User           `json:"owner"`
	GuestAccessURL    string         `json:"guest_access_url"`
}

// RoomStatistics represents the HipChat room statistics.
type RoomStatistics struct {
	Links Links `json:"links"`
}

// CreateRoomRequest represents a HipChat room creation request.
type CreateRoomRequest struct {
	Topic       string `json:"topic,omitempty"`
	GuestAccess bool   `json:"guest_access,omitempty"`
	Name        string `json:"name,omitempty"`
	OwnerUserID string `json:"owner_user_id,omitempty"`
	Privacy     string `json:"privacy,omitempty"`
}

// UpdateRoomRequest represents a HipChat room update request.
type UpdateRoomRequest struct {
	Name          string `json:"name"`
	Topic         string `json:"topic"`
	IsGuestAccess bool   `json:"is_guest_access"`
	IsArchived    bool   `json:"is_archived"`
	Privacy       string `json:"privacy"`
	Owner         ID     `json:"owner"`
}

// RoomLinks represents the HipChat room links.
type RoomLinks struct {
	Links
	Webhooks     string `json:"webhooks"`
	Members      string `json:"members"`
	Participants string `json:"participants"`
}

// NotificationRequest represents a HipChat room notification request.
type NotificationRequest struct {
	Color         string `json:"color,omitempty"`
	Message       string `json:"message,omitempty"`
	Notify        bool   `json:"notify,omitempty"`
	MessageFormat string `json:"message_format,omitempty"`
	Card          Card   `json:"card,omitempty"`
}

// Card represents a HipChat card
type Card struct {
	Style       string      `json:"style"`
	ID          string      `json:"id"`
	URL         string      `json:"url"`
	Title       string      `json:"title"`
	Description string      `json:"description"`
	Site        string      `json:"site,omitempty"`
	Icon        Icon        `json:"icon,omitempty"`
	Date        uint32      `json:"date,omitempty"`
	Attributes  []Attribute `json:"attributes,omitempty"`
	Activity    Activity    `json:"activity,omitempty"`
}

// Icon represents an icon
type Icon struct {
	URL string `json:"url"`
}

// Attribute represents an attribute on a card
type Attribute struct {
	Label string         `json:"label,omitempty"`
	Value AttributeValue `json:"value"`
}

// AttributeValue represents the value of an attribute
type AttributeValue struct {
	Icon  string `json:"icon,omitempty"`
	Label string `json:"label,omitempty"`
	URL   string `json:"url,omitempty"`
}

// Activity represents an activity that occurred
type Activity struct {
	Icon string `json:"icon,omitempty"`
	HTML string `json:"html,omitempty"`
}

// ShareFileRequest represents a HipChat room file share request.
type ShareFileRequest struct {
	Path     string `json:"path"`
	Filename string `json:"filename,omitempty"`
	Message  string `json:"message,omitempty"`
}

// HistoryRequest represents a HipChat room chat history request.
type HistoryRequest struct {
	Date       string `json:"date"`
	Timezone   string `json:"timezone"`
	StartIndex int    `json:"start-index"`
	MaxResults int    `json:"max-results"`
	Reverse    bool   `json:"reverse"`
}

// History represents a HipChat room chat history.
type History struct {
	Items      []Message `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Message represents a HipChat message.
type Message struct {
	Date          string      `json:"date"`
	From          interface{} `json:"from"` // string | obj <- weak
	Id            string      `json:"id"`
	Mentions      []User      `json:"mentions"`
	Message       string      `json:"message"`
	MessageFormat string      `json:"message_format"`
	Type          string      `json:"type"`
}

// SetTopicRequest represents a hipchat update topic request
type SetTopicRequest struct {
	Topic string `json:"topic"`
}

// InviteRequest represents a hipchat invite to room request
type InviteRequest struct {
	Reason string `json:"reason"`
}

// AddAttribute adds an attribute to a Card
func (c *Card) AddAttribute(mainLabel, subLabel, url, iconURL string) {
	attr := Attribute{Label: mainLabel}
	attr.Value = AttributeValue{Label: subLabel, URL: url, Icon: iconURL}

	c.Attributes = append(c.Attributes, attr)
}

// List returns all the rooms authorized.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_all_rooms
func (r *RoomService) List() (*Rooms, *http.Response, error) {
	req, err := r.client.NewRequest("GET", "room", nil)
	if err != nil {
		return nil, nil, err
	}

	rooms := new(Rooms)
	resp, err := r.client.Do(req, rooms)
	if err != nil {
		return nil, resp, err
	}
	return rooms, resp, nil
}

// Get returns the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_room
func (r *RoomService) Get(id string) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("GET", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Notification sends a notification to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/send_room_notification
func (r *RoomService) Notification(id string, notifReq *NotificationRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/notification", id), notifReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// ShareFile sends a file to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/share_file_with_room
func (r *RoomService) ShareFile(id string, shareFileReq *ShareFileRequest) (*http.Response, error) {
	req, err := r.client.NewFileUploadRequest("POST", fmt.Sprintf("room/%s/share/file", id), shareFileReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Create creates a new room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/create_room
func (r *RoomService) Create(roomReq *CreateRoomRequest) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("POST", "room", roomReq)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Delete deletes an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/delete_room
func (r *RoomService) Delete(id string) (*http.Response, error) {
	req, err := r.client.NewRequest("DELETE", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Update updates an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/update_room
func (r *RoomService) Update(id string, roomReq *UpdateRoomRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s", id), roomReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// History fetches a room's chat history.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/view_room_history
func (r *RoomService) History(id string, roomReq *HistoryRequest) (*History, *http.Response, error) {
	u := fmt.Sprintf("room/%s/history", id)
	// Form query parameters
	if roomReq != nil {
		p := url.Values{}
		if roomReq.Date != "" {
			p.Add("date", roomReq.Date)
		}
		if roomReq.Timezone != "" {
			p.Add("timezone", roomReq.Timezone)
		}
		if roomReq.StartIndex != 0 {
			p.Add("start-index", strconv.FormatInt(int64(roomReq.StartIndex), 10))
		}
		if roomReq.MaxResults != 0 {
			p.Add("max-results", strconv.FormatInt(int64(roomReq.MaxResults), 10))
		}
		// There's no way to tell whether caller set a boolean or not. We have to always set
		// it.
		p.Add("reverse", strconv.FormatBool(roomReq.Reverse))
		u += "?" + p.Encode()
	}
	req, err := r.client.NewRequest("GET", u, nil)
	h := new(History)
	resp, err := r.client.Do(req, &h)
	if err != nil {
		return nil, resp, err
	}
	return h, resp, nil
}

// Set Room topic.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/set_topic
func (r *RoomService) SetTopic(id string, topic string) (*http.Response, error) {
	topicReq := &SetTopicRequest{Topic: topic}

	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s/topic", id), topicReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Invite someone to the Room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/invite_user
func (r *RoomService) Invite(room string, user string, reason string) (*http.Response, error) {
	reasonReq := &InviteRequest{Reason: reason}

	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/invite/%s", room, user), reasonReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}
<commit_msg>More stuff<commit_after>package hipchat

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
)

// RoomService gives access to the room related methods of the API.
type RoomService struct {
	client *Client
}

// Rooms represents a HipChat room list.
type Rooms struct {
	Items      []Room    `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Room represents a HipChat room.
type Room struct {
	ID                int            `json:"id"`
	Links             RoomLinks      `json:"links"`
	Name              string         `json:"name"`
	XmppJid           string         `json:"xmpp_jid"`
	Statistics        RoomStatistics `json:"statistics"`
	Created           string         `json:"created"`
	IsArchived        bool           `json:"is_archived"`
	Privacy           string         `json:"privacy"`
	IsGuestAccessible bool           `json:"is_guess_accessible"`
	Topic             string         `json:"topic"`
	Participants      []User         `json:"participants"`
	Owner             User           `json:"owner"`
	GuestAccessURL    string         `json:"guest_access_url"`
}

// RoomStatistics represents the HipChat room statistics.
type RoomStatistics struct {
	Links Links `json:"links"`
}

// CreateRoomRequest represents a HipChat room creation request.
type CreateRoomRequest struct {
	Topic       string `json:"topic,omitempty"`
	GuestAccess bool   `json:"guest_access,omitempty"`
	Name        string `json:"name,omitempty"`
	OwnerUserID string `json:"owner_user_id,omitempty"`
	Privacy     string `json:"privacy,omitempty"`
}

// UpdateRoomRequest represents a HipChat room update request.
type UpdateRoomRequest struct {
	Name          string `json:"name"`
	Topic         string `json:"topic"`
	IsGuestAccess bool   `json:"is_guest_access"`
	IsArchived    bool   `json:"is_archived"`
	Privacy       string `json:"privacy"`
	Owner         ID     `json:"owner"`
}

// RoomLinks represents the HipChat room links.
type RoomLinks struct {
	Links
	Webhooks     string `json:"webhooks"`
	Members      string `json:"members"`
	Participants string `json:"participants"`
}

// NotificationRequest represents a HipChat room notification request.
type NotificationRequest struct {
	Color         string `json:"color,omitempty"`
	Message       string `json:"message,omitempty"`
	Notify        bool   `json:"notify,omitempty"`
	MessageFormat string `json:"message_format,omitempty"`
	Card          Card   `json:"card,omitempty"`
}

// Card represents a HipChat card
type Card struct {
	Style       string      `json:"style"`
	ID          string      `json:"id"`
	URL         string      `json:"url"`
	Title       string      `json:"title"`
	Description string      `json:"description"`
	Site        string      `json:"site,omitempty"`
	Icon        Icon        `json:"icon,omitempty"`
	Date        uint32      `json:"date,omitempty"`
	Attributes  []Attribute `json:"attributes,omitempty"`
	Activity    Activity    `json:"activity,omitempty"`
}

// Icon represents an icon
type Icon struct {
	URL string `json:"url"`
}

// Attribute represents an attribute on a card
type Attribute struct {
	Label string         `json:"label,omitempty"`
	Value AttributeValue `json:"value"`
}

// AttributeValue represents the value of an attribute
type AttributeValue struct {
	Icon  string `json:"icon,omitempty"`
	Label string `json:"label,omitempty"`
	URL   string `json:"url,omitempty"`
}

// Activity represents an activity that occurred
type Activity struct {
	Icon string          `json:"icon,omitempty"`
	HTML json.RawMessage `json:"html,omitempty"`
}

// ShareFileRequest represents a HipChat room file share request.
type ShareFileRequest struct {
	Path     string `json:"path"`
	Filename string `json:"filename,omitempty"`
	Message  string `json:"message,omitempty"`
}

// HistoryRequest represents a HipChat room chat history request.
type HistoryRequest struct {
	Date       string `json:"date"`
	Timezone   string `json:"timezone"`
	StartIndex int    `json:"start-index"`
	MaxResults int    `json:"max-results"`
	Reverse    bool   `json:"reverse"`
}

// History represents a HipChat room chat history.
type History struct {
	Items      []Message `json:"items"`
	StartIndex int       `json:"startIndex"`
	MaxResults int       `json:"maxResults"`
	Links      PageLinks `json:"links"`
}

// Message represents a HipChat message.
type Message struct {
	Date          string      `json:"date"`
	From          interface{} `json:"from"` // string | obj <- weak
	Id            string      `json:"id"`
	Mentions      []User      `json:"mentions"`
	Message       string      `json:"message"`
	MessageFormat string      `json:"message_format"`
	Type          string      `json:"type"`
}

// SetTopicRequest represents a hipchat update topic request
type SetTopicRequest struct {
	Topic string `json:"topic"`
}

// InviteRequest represents a hipchat invite to room request
type InviteRequest struct {
	Reason string `json:"reason"`
}

// AddAttribute adds an attribute to a Card
func (c *Card) AddAttribute(mainLabel, subLabel, url, iconURL string) {
	attr := Attribute{Label: mainLabel}
	attr.Value = AttributeValue{Label: subLabel, URL: url, Icon: iconURL}

	c.Attributes = append(c.Attributes, attr)
}

// List returns all the rooms authorized.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_all_rooms
func (r *RoomService) List() (*Rooms, *http.Response, error) {
	req, err := r.client.NewRequest("GET", "room", nil)
	if err != nil {
		return nil, nil, err
	}

	rooms := new(Rooms)
	resp, err := r.client.Do(req, rooms)
	if err != nil {
		return nil, resp, err
	}
	return rooms, resp, nil
}

// Get returns the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/get_room
func (r *RoomService) Get(id string) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("GET", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Notification sends a notification to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/send_room_notification
func (r *RoomService) Notification(id string, notifReq *NotificationRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/notification", id), notifReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// ShareFile sends a file to the room specified by the id.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/share_file_with_room
func (r *RoomService) ShareFile(id string, shareFileReq *ShareFileRequest) (*http.Response, error) {
	req, err := r.client.NewFileUploadRequest("POST", fmt.Sprintf("room/%s/share/file", id), shareFileReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Create creates a new room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/create_room
func (r *RoomService) Create(roomReq *CreateRoomRequest) (*Room, *http.Response, error) {
	req, err := r.client.NewRequest("POST", "room", roomReq)
	if err != nil {
		return nil, nil, err
	}

	room := new(Room)
	resp, err := r.client.Do(req, room)
	if err != nil {
		return nil, resp, err
	}
	return room, resp, nil
}

// Delete deletes an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/delete_room
func (r *RoomService) Delete(id string) (*http.Response, error) {
	req, err := r.client.NewRequest("DELETE", fmt.Sprintf("room/%s", id), nil)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Update updates an existing room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/update_room
func (r *RoomService) Update(id string, roomReq *UpdateRoomRequest) (*http.Response, error) {
	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s", id), roomReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// History fetches a room's chat history.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/view_room_history
func (r *RoomService) History(id string, roomReq *HistoryRequest) (*History, *http.Response, error) {
	u := fmt.Sprintf("room/%s/history", id)
	// Form query parameters
	if roomReq != nil {
		p := url.Values{}
		if roomReq.Date != "" {
			p.Add("date", roomReq.Date)
		}
		if roomReq.Timezone != "" {
			p.Add("timezone", roomReq.Timezone)
		}
		if roomReq.StartIndex != 0 {
			p.Add("start-index", strconv.FormatInt(int64(roomReq.StartIndex), 10))
		}
		if roomReq.MaxResults != 0 {
			p.Add("max-results", strconv.FormatInt(int64(roomReq.MaxResults), 10))
		}
		// There's no way to tell whether caller set a boolean or not. We have to always set
		// it.
		p.Add("reverse", strconv.FormatBool(roomReq.Reverse))
		u += "?" + p.Encode()
	}
	req, err := r.client.NewRequest("GET", u, nil)
	h := new(History)
	resp, err := r.client.Do(req, &h)
	if err != nil {
		return nil, resp, err
	}
	return h, resp, nil
}

// Set Room topic.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/set_topic
func (r *RoomService) SetTopic(id string, topic string) (*http.Response, error) {
	topicReq := &SetTopicRequest{Topic: topic}

	req, err := r.client.NewRequest("PUT", fmt.Sprintf("room/%s/topic", id), topicReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}

// Invite someone to the Room.
//
// HipChat API docs: https://www.hipchat.com/docs/apiv2/method/invite_user
func (r *RoomService) Invite(room string, user string, reason string) (*http.Response, error) {
	reasonReq := &InviteRequest{Reason: reason}

	req, err := r.client.NewRequest("POST", fmt.Sprintf("room/%s/invite/%s", room, user), reasonReq)
	if err != nil {
		return nil, err
	}

	return r.client.Do(req, nil)
}
<|endoftext|>


<commit_before>package gudp

import (
	"errors"
	"fmt"
	"net"
)

type handlers interface {
	getConnection(*net.UDPAddr) (*net.UDPConn, error)
	getAddress(string, int) (*net.UDPAddr, error)
}

// publicObject are boths sides of the message,
// the sender (Client) and the receiver (Server)
type publicObject struct {
	handlers
	Host       string
	Port       int
	BufferSize int
	err        error
}

// getConnection gets the connection from which the UDP
func getConnection(address *net.UDPAddr, action string) (conn *net.UDPConn, err error) {
	if action == "listen" {
		conn, err = net.ListenUDP("udp", address)
	} else if action == "dial" {
		conn, err = net.DialUDP("udp", nil, address)
	} else {
		err = errors.New("Invalid action. Available options are 'listen' and 'dial'")
	}

	return conn, err
}

// getAddress is a wrapper for the net.ResolveUDPAddr method
// It resolves the full server address given a host and a port
func getAddress(host string, port int) (udpAddr *net.UDPAddr, err error) {
	fullAddr := fmt.Sprintf("%s:%d", host, port)
	udpAddr, err = net.ResolveUDPAddr("udp", fullAddr)
	return udpAddr, err
}
<commit_msg>Remove unncessary publicObject struct<commit_after>package gudp

import (
	"errors"
	"fmt"
	"net"
)

type handlers interface {
	getConnection(*net.UDPAddr) (*net.UDPConn, error)
	getAddress(string, int) (*net.UDPAddr, error)
}

// getConnection creates the UDP connection for either side (Client or Server)
func getConnection(address *net.UDPAddr, action string) (conn *net.UDPConn, err error) {
	if action == "listen" {
		conn, err = net.ListenUDP("udp", address)
	} else if action == "dial" {
		conn, err = net.DialUDP("udp", nil, address)
	} else {
		err = errors.New("Invalid action. Available options are 'listen' and 'dial'")
	}

	return conn, err
}

// getAddress is a wrapper for the net.ResolveUDPAddr method
// It resolves the full server address given a host and a port
func getAddress(host string, port int) (udpAddr *net.UDPAddr, err error) {
	fullAddr := fmt.Sprintf("%s:%d", host, port)
	udpAddr, err = net.ResolveUDPAddr("udp", fullAddr)
	return udpAddr, err
}
<|endoftext|>


<commit_before>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

const channelName = "#poc_beta_test"

func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le probléme, en fait […]",
	}

	personalAnswers := []string{
		"C'est moi le plus fort, je suis SURPUISSANT !",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"J'ai un super nom pour le deuxiéme DC ! WATTOO WATTOO",
		"Attends, y a mon nom sur un t-shirt s'il te plait !",
	}

	openstackRegexp, _ := regexp.Compile("openstack|keystone|deploy|datacenter")
	technicalPoint := openstackRegexp.FindString(line.Text())
	if technicalPoint != ""  {
		conn.Privmsg(channelName, technicalAnswers[rand.Intn(len(technicalAnswers))])
	} else if rand.Intn(20) < 2 {
		conn.Privmsg(channelName, personalAnswers[rand.Intn(len(personalAnswers))])
	}
}

func main() {
	// Gentux (for concurency tests)
	//go connect(channelName, "GentuxBot", handleMessageGuilhem)
	// Guilhem
	connect(channelName, "GuilhemBot", handleMessageGuilhem)
}
<commit_msg>Add documentation string<commit_after>package main

import irc "github.com/fluffle/goirc/client"
import "math/rand"
import "regexp"

const channelName = "#poc_beta_test"

// Guilhem Bot core
func handleMessageGuilhem(conn *irc.Conn, line *irc.Line) {
	technicalAnswers := []string{
		"Mais ! C'est de la merde !",
		"On doit le réecrire en GO ça !",
		"Mais c'est pas du tout ça le probléme, en fait […]",
	}

	personalAnswers := []string{
		"C'est moi le plus fort, je suis SURPUISSANT !",
		"Je parle fort… J'ai du charisme, c'est tout !",
		"Yves, Yves, YVES ! Laisse nous parler",
		"J'ai un super nom pour le deuxiéme DC ! WATTOO WATTOO",
		"Attends, y a mon nom sur un t-shirt s'il te plait !",
	}

	openstackRegexp, _ := regexp.Compile("openstack|keystone|deploy|datacenter")
	technicalPoint := openstackRegexp.FindString(line.Text())
	if technicalPoint != "" {
		conn.Privmsg(channelName, technicalAnswers[rand.Intn(len(technicalAnswers))])
	} else if rand.Intn(20) < 2 {
		conn.Privmsg(channelName, personalAnswers[rand.Intn(len(personalAnswers))])
	}
}

func main() {
	// Gentux (for concurency tests)
	//go connect(channelName, "GentuxBot", handleMessageGuilhem)
	// Guilhem
	connect(channelName, "GuilhemBot", handleMessageGuilhem)
}
<|endoftext|>


<commit_before>package static_test

import (
	"."
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"testing"
)

func ExampleBuild() {
	handler := http.NewServeMux()
	paths := []string{}

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	paths = append(paths, "/world")

	options := static.DefaultOptions()
	static.Build(options, handler, paths, func(e static.Event) {
		fmt.Println(e)
	})

	// Output:
	// Action: build, Path: /world
}

func ExampleBuildSingle() {
	handler := http.NewServeMux()

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()
	err := static.BuildSingle(options, handler, "/world")
	fmt.Println("Built: /world, Error:", err)

	// Output:
	// Built: /world, Error: <nil>
}

func TestBuildSingle(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build_single")
	t.Logf("Created temp dir for OutputDir: %s", tempDir)
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	path := "/world"
	expectedOutputFilePath := filepath.Join(options.OutputDir, "world")
	expectedOutputFileContents := "Hello /world!"

	err := static.BuildSingle(options, handler, path)
	t.Logf("BuildSingle(..., %#v) => %v", path, err)
	if err != nil {
		t.Errorf("BuildSingle(..., %#v) => %v, want nil", path, err)
	}

	outputFileContents, err := ioutil.ReadFile(expectedOutputFilePath)
	if err != nil {
		t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expectedOutputFilePath, err)
	}

	t.Logf("Contents of %s => %s", expectedOutputFilePath, outputFileContents)
	if string(outputFileContents) != expectedOutputFileContents {
		t.Fatalf(`Contents of %s => %s, want %s`, expectedOutputFilePath, outputFileContents, expectedOutputFileContents)
	}
}

func TestBuild(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build")
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	paths := []string{"/go", "/world", "/universe"}
	expected := []struct {
		OutputFilePath     string
		OutputFileContents string
		Event              static.Event
	}{
		{filepath.Join(options.OutputDir, "go"), "Hello /go!", static.Event{"build", "/go", nil}},
		{filepath.Join(options.OutputDir, "world"), "Hello /world!", static.Event{"build", "/world", nil}},
		{filepath.Join(options.OutputDir, "universe"), "Hello /universe!", static.Event{"build", "/universe", nil}},
	}

	expectedNumberOfEvents := len(paths)
	events := make(chan static.Event, expectedNumberOfEvents)
	static.Build(options, handler, paths, func(e static.Event) {
		select {
		case events <- e:
		default:
			t.Errorf("Event %#v was seen, but we have already seen %d events and didn't expect anymore.", e, expectedNumberOfEvents)
		}
	})
	close(events)

	eventsSeen := make(map[static.Event]bool)
	for event := range events {
		t.Logf("Event %#v was seen.", event)
		if eventsSeen[event] {
			t.Errorf("Event %#v was seen more than once, but was only expected once.", event)
		}
		eventsSeen[event] = true
	}

	for _, expect := range expected {
		if !eventsSeen[expect.Event] {
			t.Errorf("Event %#v was not seen during the build.", expect.Event)
		}

		outputFileContents, err := ioutil.ReadFile(expect.OutputFilePath)
		if err != nil {
			t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expect.OutputFilePath, err)
		}

		t.Logf("Contents of %s => %s", expect.OutputFilePath, outputFileContents)
		if string(outputFileContents) != expect.OutputFileContents {
			t.Fatalf(`Contents of %s => %s, want %s`, expect.OutputFilePath, outputFileContents, expect.OutputFileContents)
		}
	}
}

func TestBuild_WithoutEventHandler(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", r.URL.Path)
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build_without_event_handler")
	t.Logf("Created temp dir for OutputDir: %s", tempDir)
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	paths := []string{"/go", "/world", "/universe"}
	expected := []struct {
		OutputFilePath     string
		OutputFileContents string
	}{
		{filepath.Join(options.OutputDir, "go"), "Hello /go!"},
		{filepath.Join(options.OutputDir, "world"), "Hello /world!"},
		{filepath.Join(options.OutputDir, "universe"), "Hello /universe!"},
	}

	static.Build(options, handler, paths, nil)

	for _, expect := range expected {
		outputFileContents, err := ioutil.ReadFile(expect.OutputFilePath)
		if err != nil {
			t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expect.OutputFilePath, err)
		}

		t.Logf("Contents of %s => %s", expect.OutputFilePath, outputFileContents)
		if string(outputFileContents) != expect.OutputFileContents {
			t.Fatalf(`Contents of %s => %s, want %s`, expect.OutputFilePath, outputFileContents, expect.OutputFileContents)
		}
	}
}
<commit_msg>Improve examples.<commit_after>package static_test

import (
	"."
	"fmt"
	"io/ioutil"
	"net/http"
	"path"
	"path/filepath"
	"testing"
)

func ExampleBuild() {
	handler := http.NewServeMux()
	paths := []string{}

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", path.Base(r.URL.Path))
	})

	paths = append(paths, "/world")

	options := static.DefaultOptions()
	static.Build(options, handler, paths, func(e static.Event) {
		fmt.Println(e)
	})

	// Output:
	// Action: build, Path: /world
}

func ExampleBuildSingle() {
	handler := http.NewServeMux()

	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", path.Base(r.URL.Path))
	})

	options := static.DefaultOptions()
	err := static.BuildSingle(options, handler, "/world")
	fmt.Println("Built: /world, Error:", err)

	// Output:
	// Built: /world, Error: <nil>
}

func TestBuildSingle(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", path.Base(r.URL.Path))
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build_single")
	t.Logf("Created temp dir for OutputDir: %s", tempDir)
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	path := "/world"
	expectedOutputFilePath := filepath.Join(options.OutputDir, "world")
	expectedOutputFileContents := "Hello world!"

	err := static.BuildSingle(options, handler, path)
	t.Logf("BuildSingle(..., %#v) => %v", path, err)
	if err != nil {
		t.Errorf("BuildSingle(..., %#v) => %v, want nil", path, err)
	}

	outputFileContents, err := ioutil.ReadFile(expectedOutputFilePath)
	if err != nil {
		t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expectedOutputFilePath, err)
	}

	t.Logf("Contents of %s => %s", expectedOutputFilePath, outputFileContents)
	if string(outputFileContents) != expectedOutputFileContents {
		t.Fatalf(`Contents of %s => %s, want %s`, expectedOutputFilePath, outputFileContents, expectedOutputFileContents)
	}
}

func TestBuild(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", path.Base(r.URL.Path))
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build")
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	paths := []string{"/go", "/world", "/universe"}
	expected := []struct {
		OutputFilePath     string
		OutputFileContents string
		Event              static.Event
	}{
		{filepath.Join(options.OutputDir, "go"), "Hello go!", static.Event{"build", "/go", nil}},
		{filepath.Join(options.OutputDir, "world"), "Hello world!", static.Event{"build", "/world", nil}},
		{filepath.Join(options.OutputDir, "universe"), "Hello universe!", static.Event{"build", "/universe", nil}},
	}

	expectedNumberOfEvents := len(paths)
	events := make(chan static.Event, expectedNumberOfEvents)
	static.Build(options, handler, paths, func(e static.Event) {
		select {
		case events <- e:
		default:
			t.Errorf("Event %#v was seen, but we have already seen %d events and didn't expect anymore.", e, expectedNumberOfEvents)
		}
	})
	close(events)

	eventsSeen := make(map[static.Event]bool)
	for event := range events {
		t.Logf("Event %#v was seen.", event)
		if eventsSeen[event] {
			t.Errorf("Event %#v was seen more than once, but was only expected once.", event)
		}
		eventsSeen[event] = true
	}

	for _, expect := range expected {
		if !eventsSeen[expect.Event] {
			t.Errorf("Event %#v was not seen during the build.", expect.Event)
		}

		outputFileContents, err := ioutil.ReadFile(expect.OutputFilePath)
		if err != nil {
			t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expect.OutputFilePath, err)
		}

		t.Logf("Contents of %s => %s", expect.OutputFilePath, outputFileContents)
		if string(outputFileContents) != expect.OutputFileContents {
			t.Fatalf(`Contents of %s => %s, want %s`, expect.OutputFilePath, outputFileContents, expect.OutputFileContents)
		}
	}
}

func TestBuild_WithoutEventHandler(t *testing.T) {
	handler := http.NewServeMux()
	handler.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello %s!", path.Base(r.URL.Path))
	})

	options := static.DefaultOptions()
	tempDir, _ := ioutil.TempDir("", "test_build_without_event_handler")
	t.Logf("Created temp dir for OutputDir: %s", tempDir)
	options.OutputDir = filepath.Join(tempDir, "build")
	t.Logf("OutputDir: %s", options.OutputDir)

	paths := []string{"/go", "/world", "/universe"}
	expected := []struct {
		OutputFilePath     string
		OutputFileContents string
	}{
		{filepath.Join(options.OutputDir, "go"), "Hello go!"},
		{filepath.Join(options.OutputDir, "world"), "Hello world!"},
		{filepath.Join(options.OutputDir, "universe"), "Hello universe!"},
	}

	static.Build(options, handler, paths, nil)

	for _, expect := range expected {
		outputFileContents, err := ioutil.ReadFile(expect.OutputFilePath)
		if err != nil {
			t.Fatalf("Expected %s to exist with the output but got error when opening: %v", expect.OutputFilePath, err)
		}

		t.Logf("Contents of %s => %s", expect.OutputFilePath, outputFileContents)
		if string(outputFileContents) != expect.OutputFileContents {
			t.Fatalf(`Contents of %s => %s, want %s`, expect.OutputFilePath, outputFileContents, expect.OutputFileContents)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"testing"
)

func TestBuildWillCreateBlogFolder(t *testing.T) {
	dir := createTmpFolder(t)

	BlogBuilder{dir + "/blog"}.Build()

	assertFilePathExist(t, dir+"/blog")
}

func TestBlogFolderWillBeDeletedBeforeBuild(t *testing.T) {
	dir := createTmpFolder(t)
	os.Mkdir(dir+"/blog", os.ModePerm)
	os.Create(dir + "/blog/delete_me")

	BlogBuilder{dir + "/blog"}.Build()

	if _, err := os.Stat(dir + "/blog/delete_me"); !os.IsNotExist(err) {
		t.Fatalf("Should delete exist blog folder before build")
	}
}

func TestBuildBlogWillGenerateIndexPage(t *testing.T) {
	dir := createTmpFolder(t)

	BlogBuilder{dir + "/blog"}.Build()

	assertFilePathExist(t, dir+"/blog/index.html")
}
<commit_msg>use NewBlogBuilder<commit_after>package main

import (
	"os"
	"testing"
)

func TestBuildWillCreateBlogFolder(t *testing.T) {
	dir := createTmpFolder(t)

	NewBlogBuilder(dir + "/blog").Build()

	assertFilePathExist(t, dir+"/blog")
}

func TestBlogFolderWillBeDeletedBeforeBuild(t *testing.T) {
	dir := createTmpFolder(t)
	os.Mkdir(dir+"/blog", os.ModePerm)
	os.Create(dir + "/blog/delete_me")

	NewBlogBuilder(dir + "/blog").Build()

	if _, err := os.Stat(dir + "/blog/delete_me"); !os.IsNotExist(err) {
		t.Fatalf("Should delete exist blog folder before build")
	}
}

func TestBuildBlogWillGenerateIndexPage(t *testing.T) {
	dir := createTmpFolder(t)

	NewBlogBuilder(dir + "/blog").Build()

	assertFilePathExist(t, dir+"/blog/index.html")
}
<|endoftext|>


<commit_before>package api

import (
	"encoding/hex"
	"fmt"
	"net/http"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/crypto"
	"github.com/rivine/rivine/encoding"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"

	"github.com/julienschmidt/httprouter"
)

type (
	// ExplorerBlock is a block with some extra information such as the id and
	// height. This information is provided for programs that may not be
	// complex enough to compute the ID on their own.
	ExplorerBlock struct {
		MinerPayoutIDs []types.CoinOutputID  `json:"minerpayoutids"`
		Transactions   []ExplorerTransaction `json:"transactions"`
		RawBlock       types.Block           `json:"rawblock"`
		HexBlock       string                `json:"hexblock"`

		modules.BlockFacts
	}

	// ExplorerTransaction is a transcation with some extra information such as
	// the parent block. This information is provided for programs that may not
	// be complex enough to compute the extra information on their own.
	ExplorerTransaction struct {
		ID             types.TransactionID `json:"id"`
		Height         types.BlockHeight   `json:"height"`
		Parent         types.BlockID       `json:"parent"`
		RawTransaction types.Transaction   `json:"rawtransaction"`
		HexTransaction string              `json:"hextransaction"`

		CoinInputOutputs             []ExplorerCoinOutput       `json:"coininputoutputs"` // the outputs being spent
		CoinOutputIDs                []types.CoinOutputID       `json:"coinoutputids"`
		CoinOutputUnlockHashes       []types.UnlockHash         `json:"coinoutputunlockhashes"`
		BlockStakeInputOutputs       []ExplorerBlockStakeOutput `json:"blockstakeinputoutputs"` // the outputs being spent
		BlockStakeOutputIDs          []types.BlockStakeOutputID `json:"blockstakeoutputids"`
		BlockStakeOutputUnlockHashes []types.UnlockHash         `json:"blockstakeunlockhashes"`
	}

	// ExplorerCoinOutput is the same a regular types.CoinOutput,
	// but with the addition of the pre-computed UnlockHash of its condition.
	ExplorerCoinOutput struct {
		types.CoinOutput
		UnlockHash types.UnlockHash `json:"unlockhash"`
	}

	// ExplorerBlockStakeOutput is the same a regular types.BlockStakeOutput,
	// but with the addition of the pre-computed UnlockHash of its condition.
	ExplorerBlockStakeOutput struct {
		types.BlockStakeOutput
		UnlockHash types.UnlockHash `json:"unlockhash"`
	}

	// ExplorerGET is the object returned as a response to a GET request to
	// /explorer.
	ExplorerGET struct {
		modules.BlockFacts
	}

	// ExplorerBlockGET is the object returned by a GET request to
	// /explorer/block.
	ExplorerBlockGET struct {
		Block ExplorerBlock `json:"block"`
	}

	// ExplorerHashGET is the object returned as a response to a GET request to
	// /explorer/hash. The HashType will indicate whether the hash corresponds
	// to a block id, a transaction id, a siacoin output id, a file contract
	// id, or a siafund output id. In the case of a block id, 'Block' will be
	// filled out and all the rest of the fields will be blank. In the case of
	// a transaction id, 'Transaction' will be filled out and all the rest of
	// the fields will be blank. For everything else, 'Transactions' and
	// 'Blocks' will/may be filled out and everything else will be blank.
	ExplorerHashGET struct {
		HashType     string                `json:"hashtype"`
		Block        ExplorerBlock         `json:"block"`
		Blocks       []ExplorerBlock       `json:"blocks"`
		Transaction  ExplorerTransaction   `json:"transaction"`
		Transactions []ExplorerTransaction `json:"transactions"`
		Unconfirmed  bool                  `json:"unconfirmed"`
	}
)

// buildExplorerTransaction takes a transaction and the height + id of the
// block it appears in an uses that to build an explorer transaction.
func (api *API) buildExplorerTransaction(height types.BlockHeight, parent types.BlockID, txn types.Transaction) (et ExplorerTransaction) {
	// Get the header information for the transaction.
	et.ID = txn.ID()
	et.Height = height
	et.Parent = parent
	et.RawTransaction = txn
	et.HexTransaction = hex.EncodeToString(encoding.Marshal(txn))

	// Add the siacoin outputs that correspond with each siacoin input.
	for _, sci := range txn.CoinInputs {
		sco, exists := api.explorer.CoinOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding coin output")
		}
		et.CoinInputOutputs = append(et.CoinInputOutputs, ExplorerCoinOutput{
			CoinOutput: sco,
			UnlockHash: sco.Condition.UnlockHash(),
		})
	}

	for i, co := range txn.CoinOutputs {
		et.CoinOutputIDs = append(et.CoinOutputIDs, txn.CoinOutputID(uint64(i)))
		et.CoinOutputUnlockHashes = append(et.CoinOutputUnlockHashes, co.Condition.UnlockHash())
	}

	// Add the siafund outputs that correspond to each siacoin input.
	for _, sci := range txn.BlockStakeInputs {
		sco, exists := api.explorer.BlockStakeOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding blockstake output")
		}
		et.BlockStakeInputOutputs = append(et.BlockStakeInputOutputs, ExplorerBlockStakeOutput{
			BlockStakeOutput: sco,
			UnlockHash:       sco.Condition.UnlockHash(),
		})
	}

	for i, bso := range txn.BlockStakeOutputs {
		et.BlockStakeOutputIDs = append(et.BlockStakeOutputIDs, txn.BlockStakeOutputID(uint64(i)))
		et.BlockStakeOutputUnlockHashes = append(et.BlockStakeOutputUnlockHashes, bso.Condition.UnlockHash())
	}

	return et
}

// buildExplorerBlock takes a block and its height and uses it to construct an
// explorer block.
func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block) ExplorerBlock {
	var mpoids []types.CoinOutputID
	for i := range block.MinerPayouts {
		mpoids = append(mpoids, block.MinerPayoutID(uint64(i)))
	}

	var etxns []ExplorerTransaction
	for _, txn := range block.Transactions {
		etxns = append(etxns, api.buildExplorerTransaction(height, block.ID(), txn))
	}

	facts, exists := api.explorer.BlockFacts(height)
	if build.DEBUG && !exists {
		panic("incorrect request to buildExplorerBlock - block does not exist")
	}

	return ExplorerBlock{
		MinerPayoutIDs: mpoids,
		Transactions:   etxns,
		RawBlock:       block,
		HexBlock:       hex.EncodeToString(encoding.Marshal(block)),

		BlockFacts: facts,
	}
}

// explorerHandler handles API calls to /explorer/blocks/:height.
func (api *API) explorerBlocksHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Parse the height that's being requested.
	var height types.BlockHeight
	_, err := fmt.Sscan(ps.ByName("height"), &height)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}

	// Fetch and return the explorer block.
	block, exists := api.cs.BlockAtHeight(height)
	if !exists {
		WriteError(w, Error{"no block found at input height in call to /explorer/block"}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, ExplorerBlockGET{
		Block: api.buildExplorerBlock(height, block),
	})
}

// buildTransactionSet returns the blocks and transactions that are associated
// with a set of transaction ids.
func (api *API) buildTransactionSet(txids []types.TransactionID) (txns []ExplorerTransaction, blocks []ExplorerBlock) {
	for _, txid := range txids {
		// Get the block containing the transaction - in the case of miner
		// payouts, the block might be the transaction.
		block, height, exists := api.explorer.Transaction(txid)
		if !exists && build.DEBUG {
			panic("explorer pointing to nonexistent txn")
		}

		// Check if the block is the transaction.
		if types.TransactionID(block.ID()) == txid {
			blocks = append(blocks, api.buildExplorerBlock(height, block))
		} else {
			// Find the transaction within the block with the correct id.
			for _, t := range block.Transactions {
				if t.ID() == txid {
					txns = append(txns, api.buildExplorerTransaction(height, block.ID(), t))
					break
				}
			}
		}
	}
	return txns, blocks
}

// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Scan the hash as a hash. If that fails, try scanning the hash as an
	// address.
	hash, err := scanHash(ps.ByName("hash"))
	if err != nil {
		addr, err := scanAddress(ps.ByName("hash"))
		if err != nil {
			WriteError(w, Error{err.Error()}, http.StatusBadRequest)
			return
		}

		// Try the hash as an unlock hash. Unlock hash is checked last because
		// unlock hashes do not have collision-free guarantees. Someone can create
		// an unlock hash that collides with another object id. They will not be
		// able to use the unlock hash, but they can disrupt the explorer. This is
		// handled by checking the unlock hash last. Anyone intentionally creating
		// a colliding unlock hash (such a collision can only happen if done
		// intentionally) will be unable to find their unlock hash in the
		// blockchain through the explorer hash lookup.
		txids := api.explorer.UnlockHash(addr)
		if len(txids) != 0 {
			txns, blocks := api.buildTransactionSet(txids)
			WriteJSON(w, ExplorerHashGET{
				HashType:     "unlockhash",
				Blocks:       blocks,
				Transactions: txns,
			})
			return
		}

		// Hash not found, return an error.
		WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
		return
	}

	// TODO: lookups on the zero hash are too expensive to allow. Need a
	// better way to handle this case.
	if hash == (crypto.Hash{}) {
		WriteError(w, Error{"can't lookup the empty unlock hash"}, http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := api.explorer.Block(types.BlockID(hash))
	if exists {
		WriteJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    api.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = api.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		WriteJSON(w, ExplorerHashGET{
			HashType:    HashTypeTransactionIDStr,
			Transaction: api.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := api.explorer.CoinOutputID(types.CoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeCoinOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = api.explorer.BlockStakeOutputID(types.BlockStakeOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeBlockStakeOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// if the transaction pool is available, try to use it
	if api.tpool != nil {
		// Try the hash as a transactionID in the transaction pool
		txn, err := api.tpool.Transaction(types.TransactionID(hash))
		if err == nil {
			WriteJSON(w, ExplorerHashGET{
				HashType:    HashTypeTransactionIDStr,
				Transaction: api.buildExplorerTransaction(0, types.BlockID{}, txn),
				Unconfirmed: true,
			})
			return
		}
		if err != modules.ErrTransactionNotFound {
			WriteError(w, Error{
				"error during call to /explorer/hash: failed to get txn from transaction pool: " + err.Error()},
				http.StatusInternalServerError)
			return
		}
	}

	// Hash not found, return an error.
	WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
}

// hash type string constants
const (
	HashTypeTransactionIDStr      = "transactionid"
	HashTypeCoinOutputIDStr       = "coinoutputid"
	HashTypeBlockStakeOutputIDStr = "blockstakeoutputid"
)

// explorerHandler handles API calls to /explorer
func (api *API) explorerHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	facts := api.explorer.LatestBlockFacts()
	WriteJSON(w, ExplorerGET{
		BlockFacts: facts,
	})
}

func (api *API) constantsHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
	WriteJSON(w, api.explorer.Constants())
}

func (api *API) historyStatsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	var history types.BlockHeight
	// GET request so the only place the vars can be is the queryparams
	q := req.URL.Query()
	_, err := fmt.Sscan(q.Get("history"), &history)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	stats, err := api.explorer.HistoryStats(history)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, stats)
}

func (api *API) rangeStatsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	var start, end types.BlockHeight
	// GET request so the only place the vars can be is the queryparams
	q := req.URL.Query()
	_, err := fmt.Sscan(q.Get("start"), &start)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	_, err = fmt.Sscan(q.Get("end"), &end)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	stats, err := api.explorer.RangeStats(start, end)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, stats)
}
<commit_msg>remove hex block/tx from explorer structs<commit_after>package api

import (
	"fmt"
	"net/http"

	"github.com/rivine/rivine/build"
	"github.com/rivine/rivine/crypto"
	"github.com/rivine/rivine/modules"
	"github.com/rivine/rivine/types"

	"github.com/julienschmidt/httprouter"
)

type (
	// ExplorerBlock is a block with some extra information such as the id and
	// height. This information is provided for programs that may not be
	// complex enough to compute the ID on their own.
	ExplorerBlock struct {
		MinerPayoutIDs []types.CoinOutputID  `json:"minerpayoutids"`
		Transactions   []ExplorerTransaction `json:"transactions"`
		RawBlock       types.Block           `json:"rawblock"`

		modules.BlockFacts
	}

	// ExplorerTransaction is a transcation with some extra information such as
	// the parent block. This information is provided for programs that may not
	// be complex enough to compute the extra information on their own.
	ExplorerTransaction struct {
		ID             types.TransactionID `json:"id"`
		Height         types.BlockHeight   `json:"height"`
		Parent         types.BlockID       `json:"parent"`
		RawTransaction types.Transaction   `json:"rawtransaction"`

		CoinInputOutputs             []ExplorerCoinOutput       `json:"coininputoutputs"` // the outputs being spent
		CoinOutputIDs                []types.CoinOutputID       `json:"coinoutputids"`
		CoinOutputUnlockHashes       []types.UnlockHash         `json:"coinoutputunlockhashes"`
		BlockStakeInputOutputs       []ExplorerBlockStakeOutput `json:"blockstakeinputoutputs"` // the outputs being spent
		BlockStakeOutputIDs          []types.BlockStakeOutputID `json:"blockstakeoutputids"`
		BlockStakeOutputUnlockHashes []types.UnlockHash         `json:"blockstakeunlockhashes"`
	}

	// ExplorerCoinOutput is the same a regular types.CoinOutput,
	// but with the addition of the pre-computed UnlockHash of its condition.
	ExplorerCoinOutput struct {
		types.CoinOutput
		UnlockHash types.UnlockHash `json:"unlockhash"`
	}

	// ExplorerBlockStakeOutput is the same a regular types.BlockStakeOutput,
	// but with the addition of the pre-computed UnlockHash of its condition.
	ExplorerBlockStakeOutput struct {
		types.BlockStakeOutput
		UnlockHash types.UnlockHash `json:"unlockhash"`
	}

	// ExplorerGET is the object returned as a response to a GET request to
	// /explorer.
	ExplorerGET struct {
		modules.BlockFacts
	}

	// ExplorerBlockGET is the object returned by a GET request to
	// /explorer/block.
	ExplorerBlockGET struct {
		Block ExplorerBlock `json:"block"`
	}

	// ExplorerHashGET is the object returned as a response to a GET request to
	// /explorer/hash. The HashType will indicate whether the hash corresponds
	// to a block id, a transaction id, a siacoin output id, a file contract
	// id, or a siafund output id. In the case of a block id, 'Block' will be
	// filled out and all the rest of the fields will be blank. In the case of
	// a transaction id, 'Transaction' will be filled out and all the rest of
	// the fields will be blank. For everything else, 'Transactions' and
	// 'Blocks' will/may be filled out and everything else will be blank.
	ExplorerHashGET struct {
		HashType     string                `json:"hashtype"`
		Block        ExplorerBlock         `json:"block"`
		Blocks       []ExplorerBlock       `json:"blocks"`
		Transaction  ExplorerTransaction   `json:"transaction"`
		Transactions []ExplorerTransaction `json:"transactions"`
		Unconfirmed  bool                  `json:"unconfirmed"`
	}
)

// buildExplorerTransaction takes a transaction and the height + id of the
// block it appears in an uses that to build an explorer transaction.
func (api *API) buildExplorerTransaction(height types.BlockHeight, parent types.BlockID, txn types.Transaction) (et ExplorerTransaction) {
	// Get the header information for the transaction.
	et.ID = txn.ID()
	et.Height = height
	et.Parent = parent
	et.RawTransaction = txn

	// Add the siacoin outputs that correspond with each siacoin input.
	for _, sci := range txn.CoinInputs {
		sco, exists := api.explorer.CoinOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding coin output")
		}
		et.CoinInputOutputs = append(et.CoinInputOutputs, ExplorerCoinOutput{
			CoinOutput: sco,
			UnlockHash: sco.Condition.UnlockHash(),
		})
	}

	for i, co := range txn.CoinOutputs {
		et.CoinOutputIDs = append(et.CoinOutputIDs, txn.CoinOutputID(uint64(i)))
		et.CoinOutputUnlockHashes = append(et.CoinOutputUnlockHashes, co.Condition.UnlockHash())
	}

	// Add the siafund outputs that correspond to each siacoin input.
	for _, sci := range txn.BlockStakeInputs {
		sco, exists := api.explorer.BlockStakeOutput(sci.ParentID)
		if build.DEBUG && !exists {
			panic("could not find corresponding blockstake output")
		}
		et.BlockStakeInputOutputs = append(et.BlockStakeInputOutputs, ExplorerBlockStakeOutput{
			BlockStakeOutput: sco,
			UnlockHash:       sco.Condition.UnlockHash(),
		})
	}

	for i, bso := range txn.BlockStakeOutputs {
		et.BlockStakeOutputIDs = append(et.BlockStakeOutputIDs, txn.BlockStakeOutputID(uint64(i)))
		et.BlockStakeOutputUnlockHashes = append(et.BlockStakeOutputUnlockHashes, bso.Condition.UnlockHash())
	}

	return et
}

// buildExplorerBlock takes a block and its height and uses it to construct an
// explorer block.
func (api *API) buildExplorerBlock(height types.BlockHeight, block types.Block) ExplorerBlock {
	var mpoids []types.CoinOutputID
	for i := range block.MinerPayouts {
		mpoids = append(mpoids, block.MinerPayoutID(uint64(i)))
	}

	var etxns []ExplorerTransaction
	for _, txn := range block.Transactions {
		etxns = append(etxns, api.buildExplorerTransaction(height, block.ID(), txn))
	}

	facts, exists := api.explorer.BlockFacts(height)
	if build.DEBUG && !exists {
		panic("incorrect request to buildExplorerBlock - block does not exist")
	}

	return ExplorerBlock{
		MinerPayoutIDs: mpoids,
		Transactions:   etxns,
		RawBlock:       block,

		BlockFacts: facts,
	}
}

// explorerHandler handles API calls to /explorer/blocks/:height.
func (api *API) explorerBlocksHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Parse the height that's being requested.
	var height types.BlockHeight
	_, err := fmt.Sscan(ps.ByName("height"), &height)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}

	// Fetch and return the explorer block.
	block, exists := api.cs.BlockAtHeight(height)
	if !exists {
		WriteError(w, Error{"no block found at input height in call to /explorer/block"}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, ExplorerBlockGET{
		Block: api.buildExplorerBlock(height, block),
	})
}

// buildTransactionSet returns the blocks and transactions that are associated
// with a set of transaction ids.
func (api *API) buildTransactionSet(txids []types.TransactionID) (txns []ExplorerTransaction, blocks []ExplorerBlock) {
	for _, txid := range txids {
		// Get the block containing the transaction - in the case of miner
		// payouts, the block might be the transaction.
		block, height, exists := api.explorer.Transaction(txid)
		if !exists && build.DEBUG {
			panic("explorer pointing to nonexistent txn")
		}

		// Check if the block is the transaction.
		if types.TransactionID(block.ID()) == txid {
			blocks = append(blocks, api.buildExplorerBlock(height, block))
		} else {
			// Find the transaction within the block with the correct id.
			for _, t := range block.Transactions {
				if t.ID() == txid {
					txns = append(txns, api.buildExplorerTransaction(height, block.ID(), t))
					break
				}
			}
		}
	}
	return txns, blocks
}

// explorerHashHandler handles GET requests to /explorer/hash/:hash.
func (api *API) explorerHashHandler(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
	// Scan the hash as a hash. If that fails, try scanning the hash as an
	// address.
	hash, err := scanHash(ps.ByName("hash"))
	if err != nil {
		addr, err := scanAddress(ps.ByName("hash"))
		if err != nil {
			WriteError(w, Error{err.Error()}, http.StatusBadRequest)
			return
		}

		// Try the hash as an unlock hash. Unlock hash is checked last because
		// unlock hashes do not have collision-free guarantees. Someone can create
		// an unlock hash that collides with another object id. They will not be
		// able to use the unlock hash, but they can disrupt the explorer. This is
		// handled by checking the unlock hash last. Anyone intentionally creating
		// a colliding unlock hash (such a collision can only happen if done
		// intentionally) will be unable to find their unlock hash in the
		// blockchain through the explorer hash lookup.
		txids := api.explorer.UnlockHash(addr)
		if len(txids) != 0 {
			txns, blocks := api.buildTransactionSet(txids)
			WriteJSON(w, ExplorerHashGET{
				HashType:     "unlockhash",
				Blocks:       blocks,
				Transactions: txns,
			})
			return
		}

		// Hash not found, return an error.
		WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
		return
	}

	// TODO: lookups on the zero hash are too expensive to allow. Need a
	// better way to handle this case.
	if hash == (crypto.Hash{}) {
		WriteError(w, Error{"can't lookup the empty unlock hash"}, http.StatusBadRequest)
		return
	}

	// Try the hash as a block id.
	block, height, exists := api.explorer.Block(types.BlockID(hash))
	if exists {
		WriteJSON(w, ExplorerHashGET{
			HashType: "blockid",
			Block:    api.buildExplorerBlock(height, block),
		})
		return
	}

	// Try the hash as a transaction id.
	block, height, exists = api.explorer.Transaction(types.TransactionID(hash))
	if exists {
		var txn types.Transaction
		for _, t := range block.Transactions {
			if t.ID() == types.TransactionID(hash) {
				txn = t
			}
		}
		WriteJSON(w, ExplorerHashGET{
			HashType:    HashTypeTransactionIDStr,
			Transaction: api.buildExplorerTransaction(height, block.ID(), txn),
		})
		return
	}

	// Try the hash as a siacoin output id.
	txids := api.explorer.CoinOutputID(types.CoinOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeCoinOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// Try the hash as a siafund output id.
	txids = api.explorer.BlockStakeOutputID(types.BlockStakeOutputID(hash))
	if len(txids) != 0 {
		txns, blocks := api.buildTransactionSet(txids)
		WriteJSON(w, ExplorerHashGET{
			HashType:     HashTypeBlockStakeOutputIDStr,
			Blocks:       blocks,
			Transactions: txns,
		})
		return
	}

	// if the transaction pool is available, try to use it
	if api.tpool != nil {
		// Try the hash as a transactionID in the transaction pool
		txn, err := api.tpool.Transaction(types.TransactionID(hash))
		if err == nil {
			WriteJSON(w, ExplorerHashGET{
				HashType:    HashTypeTransactionIDStr,
				Transaction: api.buildExplorerTransaction(0, types.BlockID{}, txn),
				Unconfirmed: true,
			})
			return
		}
		if err != modules.ErrTransactionNotFound {
			WriteError(w, Error{
				"error during call to /explorer/hash: failed to get txn from transaction pool: " + err.Error()},
				http.StatusInternalServerError)
			return
		}
	}

	// Hash not found, return an error.
	WriteError(w, Error{"unrecognized hash used as input to /explorer/hash"}, http.StatusBadRequest)
}

// hash type string constants
const (
	HashTypeTransactionIDStr      = "transactionid"
	HashTypeCoinOutputIDStr       = "coinoutputid"
	HashTypeBlockStakeOutputIDStr = "blockstakeoutputid"
)

// explorerHandler handles API calls to /explorer
func (api *API) explorerHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	facts := api.explorer.LatestBlockFacts()
	WriteJSON(w, ExplorerGET{
		BlockFacts: facts,
	})
}

func (api *API) constantsHandler(w http.ResponseWriter, _ *http.Request, _ httprouter.Params) {
	WriteJSON(w, api.explorer.Constants())
}

func (api *API) historyStatsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	var history types.BlockHeight
	// GET request so the only place the vars can be is the queryparams
	q := req.URL.Query()
	_, err := fmt.Sscan(q.Get("history"), &history)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	stats, err := api.explorer.HistoryStats(history)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, stats)
}

func (api *API) rangeStatsHandler(w http.ResponseWriter, req *http.Request, _ httprouter.Params) {
	var start, end types.BlockHeight
	// GET request so the only place the vars can be is the queryparams
	q := req.URL.Query()
	_, err := fmt.Sscan(q.Get("start"), &start)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	_, err = fmt.Sscan(q.Get("end"), &end)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	stats, err := api.explorer.RangeStats(start, end)
	if err != nil {
		WriteError(w, Error{err.Error()}, http.StatusBadRequest)
		return
	}
	WriteJSON(w, stats)
}
<|endoftext|>


<commit_before>package probing

import (
	"sync"
	"time"
)

var (
	// weight factor
	α = 0.125
)

type Status interface {
	Total() int64
	Loss() int64
	Health() bool
	Err() error
	// Estimated smoothed round trip time
	SRTT() time.Duration
	// Estimated clock difference
	ClockDiff() time.Duration
	StopNotify() <-chan struct{}
}

type status struct {
	mu        sync.Mutex
	srtt      time.Duration
	total     int64
	loss      int64
	health    bool
	err       error
	clockdiff time.Duration
	stopC     chan struct{}
}

// SRTT = (1-α) * SRTT + α * RTT
func (s *status) SRTT() time.Duration {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.srtt
}

func (s *status) Total() int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.total
}

func (s *status) Loss() int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.loss
}

func (s *status) Health() bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.health
}

func (s *status) Err() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.err
}

func (s *status) ClockDiff() time.Duration {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.clockdiff
}

func (s *status) StopNotify() <-chan struct{} {
	return s.stopC
}

func (s *status) record(rtt time.Duration, when time.Time) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.total += 1
	s.health = true
	s.srtt = time.Duration((1-α)*float64(s.srtt) + α*float64(rtt))
	s.clockdiff = time.Now().Sub(when) - s.srtt/2
	s.err = nil
}

func (s *status) recordFailure(err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.total++
	s.health = false
	s.loss += 1
	s.err = err
}

func (s *status) reset() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.srtt = 0
	s.total = 0
	s.health = false
	s.clockdiff = 0
	s.err = nil
}
<commit_msg>status: fix reset loss<commit_after>package probing

import (
	"sync"
	"time"
)

var (
	// weight factor
	α = 0.125
)

type Status interface {
	Total() int64
	Loss() int64
	Health() bool
	Err() error
	// Estimated smoothed round trip time
	SRTT() time.Duration
	// Estimated clock difference
	ClockDiff() time.Duration
	StopNotify() <-chan struct{}
}

type status struct {
	mu        sync.Mutex
	srtt      time.Duration
	total     int64
	loss      int64
	health    bool
	err       error
	clockdiff time.Duration
	stopC     chan struct{}
}

// SRTT = (1-α) * SRTT + α * RTT
func (s *status) SRTT() time.Duration {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.srtt
}

func (s *status) Total() int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.total
}

func (s *status) Loss() int64 {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.loss
}

func (s *status) Health() bool {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.health
}

func (s *status) Err() error {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.err
}

func (s *status) ClockDiff() time.Duration {
	s.mu.Lock()
	defer s.mu.Unlock()
	return s.clockdiff
}

func (s *status) StopNotify() <-chan struct{} {
	return s.stopC
}

func (s *status) record(rtt time.Duration, when time.Time) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.total += 1
	s.health = true
	s.srtt = time.Duration((1-α)*float64(s.srtt) + α*float64(rtt))
	s.clockdiff = time.Now().Sub(when) - s.srtt/2
	s.err = nil
}

func (s *status) recordFailure(err error) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.total++
	s.health = false
	s.loss += 1
	s.err = err
}

func (s *status) reset() {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.srtt = 0
	s.total = 0
	s.loss = 0
	s.health = false
	s.clockdiff = 0
	s.err = nil
}
<|endoftext|>


<commit_before>package task

import (
	"context"
	"os"
	"path/filepath"
	"time"

	"github.com/go-task/task/execext"
	"github.com/mattn/go-zglob"
)

func (t *Task) isUpToDate(ctx context.Context) (bool, error) {
	if len(t.Status) > 0 {
		return t.isUpToDateStatus(ctx)
	}
	return t.isUpToDateTimestamp(ctx)
}

func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) {
	for _, s := range t.Status {
		err := execext.RunCommand(&execext.RunCommandOptions{
			Context: ctx,
			Command: s,
			Dir:     t.Dir,
			Env:     t.getEnviron(),
		})
		if err != nil {
			return false, nil
		}
	}
	return true, nil
}

func (t *Task) isUpToDateTimestamp(ctx context.Context) (bool, error) {
	if len(t.Sources) == 0 || len(t.Generates) == 0 {
		return false, nil
	}

	sourcesMaxTime, err := getPatternsMaxTime(t.Dir, t.Sources)
	if err != nil || sourcesMaxTime.IsZero() {
		return false, nil
	}

	generatesMinTime, err := getPatternsMinTime(t.Dir, t.Generates)
	if err != nil || generatesMinTime.IsZero() {
		return false, nil
	}
	return !generatesMinTime.Before(sourcesMaxTime), nil
}

func minTime(a, b time.Time) time.Time {
	if !a.IsZero() && a.Before(b) {
		return a
	}
	return b
}
func maxTime(a, b time.Time) time.Time {
	if a.After(b) {
		return a
	}
	return b
}

func getPatternsMinTime(dir string, patterns []string) (m time.Time, err error) {
	for _, p := range patterns {
		if !filepath.IsAbs(p) {
			p = filepath.Join(dir, p)
		}
		mp, err := getPatternMinTime(p)
		if err != nil {
			return time.Time{}, err
		}
		m = minTime(m, mp)
	}
	return
}
func getPatternsMaxTime(dir string, patterns []string) (m time.Time, err error) {
	for _, p := range patterns {
		if !filepath.IsAbs(p) {
			p = filepath.Join(dir, p)
		}
		mp, err := getPatternMaxTime(p)
		if err != nil {
			return time.Time{}, err
		}
		m = maxTime(m, mp)
	}
	return
}

func getPatternMinTime(pattern string) (minTime time.Time, err error) {
	files, err := zglob.Glob(pattern)
	if err != nil {
		return time.Time{}, err
	}

	for _, f := range files {
		info, err := os.Stat(f)
		if err != nil {
			return time.Time{}, err
		}

		modTime := info.ModTime()
		if minTime.IsZero() || modTime.Before(minTime) {
			minTime = modTime
		}
	}
	return
}

func getPatternMaxTime(pattern string) (maxTime time.Time, err error) {
	files, err := zglob.Glob(pattern)
	if err != nil {
		return time.Time{}, err
	}

	for _, f := range files {
		info, err := os.Stat(f)
		if err != nil {
			return time.Time{}, err
		}

		modTime := info.ModTime()
		if modTime.After(maxTime) {
			maxTime = modTime
		}
	}
	return
}
<commit_msg>reuse of minTime and maxTime funcs<commit_after>package task

import (
	"context"
	"os"
	"path/filepath"
	"time"

	"github.com/go-task/task/execext"
	"github.com/mattn/go-zglob"
)

func (t *Task) isUpToDate(ctx context.Context) (bool, error) {
	if len(t.Status) > 0 {
		return t.isUpToDateStatus(ctx)
	}
	return t.isUpToDateTimestamp(ctx)
}

func (t *Task) isUpToDateStatus(ctx context.Context) (bool, error) {
	for _, s := range t.Status {
		err := execext.RunCommand(&execext.RunCommandOptions{
			Context: ctx,
			Command: s,
			Dir:     t.Dir,
			Env:     t.getEnviron(),
		})
		if err != nil {
			return false, nil
		}
	}
	return true, nil
}

func (t *Task) isUpToDateTimestamp(ctx context.Context) (bool, error) {
	if len(t.Sources) == 0 || len(t.Generates) == 0 {
		return false, nil
	}

	sourcesMaxTime, err := getPatternsMaxTime(t.Dir, t.Sources)
	if err != nil || sourcesMaxTime.IsZero() {
		return false, nil
	}

	generatesMinTime, err := getPatternsMinTime(t.Dir, t.Generates)
	if err != nil || generatesMinTime.IsZero() {
		return false, nil
	}
	return !generatesMinTime.Before(sourcesMaxTime), nil
}

func getPatternsMinTime(dir string, patterns []string) (m time.Time, err error) {
	for _, p := range patterns {
		if !filepath.IsAbs(p) {
			p = filepath.Join(dir, p)
		}
		mp, err := getPatternMinTime(p)
		if err != nil {
			return time.Time{}, err
		}
		m = minTime(m, mp)
	}
	return
}
func getPatternsMaxTime(dir string, patterns []string) (m time.Time, err error) {
	for _, p := range patterns {
		if !filepath.IsAbs(p) {
			p = filepath.Join(dir, p)
		}
		mp, err := getPatternMaxTime(p)
		if err != nil {
			return time.Time{}, err
		}
		m = maxTime(m, mp)
	}
	return
}

func getPatternMinTime(pattern string) (m time.Time, err error) {
	files, err := zglob.Glob(pattern)
	if err != nil {
		return time.Time{}, err
	}

	for _, f := range files {
		info, err := os.Stat(f)
		if err != nil {
			return time.Time{}, err
		}
		m = minTime(m, info.ModTime())
	}
	return
}

func getPatternMaxTime(pattern string) (m time.Time, err error) {
	files, err := zglob.Glob(pattern)
	if err != nil {
		return time.Time{}, err
	}

	for _, f := range files {
		info, err := os.Stat(f)
		if err != nil {
			return time.Time{}, err
		}
		m = maxTime(m, info.ModTime())
	}
	return
}

func minTime(a, b time.Time) time.Time {
	if !a.IsZero() && a.Before(b) {
		return a
	}
	return b
}

func maxTime(a, b time.Time) time.Time {
	if a.After(b) {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package scanner

import (
	"bufio"
	"bytes"
	"github.com/github/git-media/pointer"
	// "github.com/rubyist/tracerx"
	"io"
	"os/exec"
	"strconv"
)

var (
	blobSizeCutoff = 125
	stdoutBufSize  = 16384
)

func Scan(ref string) ([]*pointer.Pointer, error) {
	revs, _ := revListStream(ref, ref == "")
	smallShas, _ := catFileBatchCheck(revs)
	pointerc, _ := catFileBatch(smallShas)

	pointers := make([]*pointer.Pointer, 0)
	for p := range pointerc {
		pointers = append(pointers, p)
	}

	return pointers, nil
}

func revListStream(ref string, all bool) (chan string, error) {
	refArgs := []string{"rev-list", "--objects"}
	if all {
		refArgs = append(refArgs, "--all")
	} else {
		refArgs = append(refArgs, ref)
	}

	cmd, err := startCommand("git", refArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	revs := make(chan string)

	go func() {
		scanner := bufio.NewScanner(cmd.Stdout)
		for scanner.Scan() {
			revs <- scanner.Text()[0:40]
		}
		close(revs)
	}()

	return revs, nil
}

func catFileBatchCheck(revs chan string) (chan string, error) {
	cmd, err := startCommand("git", "cat-file", "--batch-check")
	if err != nil {
		return nil, err
	}

	smallRevs := make(chan string)

	go func() {
		scanner := bufio.NewScanner(cmd.Stdout)
		for scanner.Scan() {
			line := scanner.Text()
			if line[41:45] == "blob" {
				size, err := strconv.Atoi(line[46:len(line)])
				if err != nil {
					continue
				}
				if size < blobSizeCutoff {
					smallRevs <- line[0:40]
				}
			}
		}
		close(smallRevs)
	}()

	go func() {
		for r := range revs {
			cmd.Stdin.Write([]byte(r + "\n"))
		}
		cmd.Stdin.Close()
	}()

	return smallRevs, nil
}

func catFileBatch(revs chan string) (chan *pointer.Pointer, error) {
	cmd, err := startCommand("git", "cat-file", "--batch")
	if err != nil {
		return nil, err
	}

	pointers := make(chan *pointer.Pointer)

	go func() {
		for {
			l, err := cmd.Stdout.ReadBytes('\n')
			if err != nil { // Probably check for EOF
				break
			}

			// Line is formatted:
			// <sha1> <type> <size>
			fields := bytes.Fields(l)
			s, _ := strconv.Atoi(string(fields[2]))

			nbuf := make([]byte, s)
			_, err = io.ReadFull(cmd.Stdout, nbuf)
			if err != nil {
				break // Legit errors
			}

			p, err := pointer.Decode(bytes.NewBuffer(nbuf))
			if err == nil {
				pointers <- p
			}

			_, err = cmd.Stdout.ReadBytes('\n') // Extra \n inserted by cat-file
			if err != nil {                     // Probably check for EOF
				break
			}
		}
		close(pointers)
	}()

	// writes shas to cat-file stdin
	go func() {
		for r := range revs {
			cmd.Stdin.Write([]byte(r + "\n"))
		}
		cmd.Stdin.Close()
	}()

	return pointers, nil
}

type wrappedCmd struct {
	Stdin  io.WriteCloser
	Stdout *bufio.Reader
	*exec.Cmd
}

func startCommand(command string, args ...string) (*wrappedCmd, error) {
	cmd := exec.Command(command, args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	return &wrappedCmd{stdin, bufio.NewReaderSize(stdout, stdoutBufSize), cmd}, nil
}
<commit_msg>ラララララ ラー ウウウ フフフ<commit_after>package scanner

import (
	"bufio"
	"bytes"
	"github.com/github/git-media/pointer"
	// "github.com/rubyist/tracerx"
	"io"
	"os/exec"
	"strconv"
)

var (
	blobSizeCutoff = 125
	stdoutBufSize  = 16384
)

// Scan takes a ref and returns a slice of pointer.Pointer objects
// for all git media pointers it finds for that ref.
func Scan(ref string) ([]*pointer.Pointer, error) {
	revs, _ := revListShas(ref, ref == "")
	smallShas, _ := catFileBatchCheck(revs)
	pointerc, _ := catFileBatch(smallShas)

	pointers := make([]*pointer.Pointer, 0)
	for p := range pointerc {
		pointers = append(pointers, p)
	}

	return pointers, nil
}

// revListShas uses git rev-list to return the list of object sha1s
// for the given ref. If all is true, ref is ignored. It returns a
// channel from which sha1 strings can be read.
func revListShas(ref string, all bool) (chan string, error) {
	refArgs := []string{"rev-list", "--objects"}
	if all {
		refArgs = append(refArgs, "--all")
	} else {
		refArgs = append(refArgs, ref)
	}

	cmd, err := startCommand("git", refArgs...)
	if err != nil {
		return nil, err
	}

	cmd.Stdin.Close()

	revs := make(chan string)

	go func() {
		scanner := bufio.NewScanner(cmd.Stdout)
		for scanner.Scan() {
			revs <- scanner.Text()[0:40]
		}
		close(revs)
	}()

	return revs, nil
}

// catFileBatchCheck uses git cat-file --batch-check to get the type
// and size of a git object. Any object that isn't of type blob and
// under the blobSizeCutoff will be ignored. revs is a channel over
// which strings containing git sha1s will be sent. It returns a channel
// from which sha1 strings can be read.
func catFileBatchCheck(revs chan string) (chan string, error) {
	cmd, err := startCommand("git", "cat-file", "--batch-check")
	if err != nil {
		return nil, err
	}

	smallRevs := make(chan string)

	go func() {
		scanner := bufio.NewScanner(cmd.Stdout)
		for scanner.Scan() {
			line := scanner.Text()
			if line[41:45] == "blob" {
				size, err := strconv.Atoi(line[46:len(line)])
				if err != nil {
					continue
				}
				if size < blobSizeCutoff {
					smallRevs <- line[0:40]
				}
			}
		}
		close(smallRevs)
	}()

	go func() {
		for r := range revs {
			cmd.Stdin.Write([]byte(r + "\n"))
		}
		cmd.Stdin.Close()
	}()

	return smallRevs, nil
}

// catFileBatch uses git cat-file --batch to get the object contents
// of a git object, given its sha1. The contents will be decoded into
// a git media pointer. revs is a channel over which strings containing
// git sha1s will be sent. It returns a channel from which point.Pointers
// can be read.
func catFileBatch(revs chan string) (chan *pointer.Pointer, error) {
	cmd, err := startCommand("git", "cat-file", "--batch")
	if err != nil {
		return nil, err
	}

	pointers := make(chan *pointer.Pointer)

	go func() {
		for {
			l, err := cmd.Stdout.ReadBytes('\n')
			if err != nil { // Probably check for EOF
				break
			}

			// Line is formatted:
			// <sha1> <type> <size>
			fields := bytes.Fields(l)
			s, _ := strconv.Atoi(string(fields[2]))

			nbuf := make([]byte, s)
			_, err = io.ReadFull(cmd.Stdout, nbuf)
			if err != nil {
				break // Legit errors
			}

			p, err := pointer.Decode(bytes.NewBuffer(nbuf))
			if err == nil {
				pointers <- p
			}

			_, err = cmd.Stdout.ReadBytes('\n') // Extra \n inserted by cat-file
			if err != nil {                     // Probably check for EOF
				break
			}
		}
		close(pointers)
	}()

	// writes shas to cat-file stdin
	go func() {
		for r := range revs {
			cmd.Stdin.Write([]byte(r + "\n"))
		}
		cmd.Stdin.Close()
	}()

	return pointers, nil
}

type wrappedCmd struct {
	Stdin  io.WriteCloser
	Stdout *bufio.Reader
	*exec.Cmd
}

// startCommand starts up a command and creates a stdin pipe and a buffered
// stdout pipe, wrapped in a wrappedCmd. The stdout buffer wille be of stdoutBufSize
// bytes.
func startCommand(command string, args ...string) (*wrappedCmd, error) {
	cmd := exec.Command(command, args...)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		return nil, err
	}

	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}

	if err := cmd.Start(); err != nil {
		return nil, err
	}

	return &wrappedCmd{stdin, bufio.NewReaderSize(stdout, stdoutBufSize), cmd}, nil
}
<|endoftext|>


<commit_before>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	state           errorcode.ErrorCode
	reader          *bufio.Reader
	currentPosition *Position
	positionBuffer  []*Position

	currentLine   int
	currentColumn int

	asyncDef bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		state:          errorcode.E_OK,
		reader:         bufio.NewReader(r),
		positionBuffer: make([]*Position, 0),
		currentLine:    1,
		currentColumn:  0,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) NextToken() *token.Token {
	positions := NewPositions()

	pos := scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		// Check for async/await
		// literal := positions.String()
		// if literal == "async" || literal == "await" {
		// 	if scanner.asyncDef {
		// 		switch literal {
		// 		case "async":
		// 			return positions.AsToken(token.ASYNC)
		// 		case "await":
		// 			return positions.AsToken(token.AWAIT)
		// 		}
		// 	} else if literal == "async" {
		// 		nextToken := scanner.NextToken()
		// 		if nextToken.ID == token.NAME && nextToken.Literal == "def" {
		// 			scanner.asyncDef = true
		// 			return positions.AsToken(token.ASYNC)
		// 		}
		// 	}
		// }

		return positions.AsToken(token.NAME)
	case ch == '\n':
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			// Parse Number
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<commit_msg>add number parsing<commit_after>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	state           errorcode.ErrorCode
	reader          *bufio.Reader
	currentPosition *Position
	positionBuffer  []*Position

	currentLine   int
	currentColumn int

	asyncDef bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		state:          errorcode.E_OK,
		reader:         bufio.NewReader(r),
		positionBuffer: make([]*Position, 0),
		currentLine:    1,
		currentColumn:  0,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseNumber(positions *Positions, nextChar rune) *token.Token {
	pos := scanner.nextPosition()
	switch ch := pos.Char; {
	case nextChar == '0' && (ch == 'j' || ch == 'J'):
		// Imaginary
		positions.Append(pos)
	case nextChar == '0' && (ch == 'x' || ch == 'X'):
		// Hex
		positions.Append(pos)
		pos = scanner.nextPosition()
		if !IsXDigit(pos.Char) {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for IsXDigit(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'b' || ch == 'B'):
		// Binary
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char != '0' && pos.Char != '1' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char == '0' || pos.Char == '1' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'o' || ch == 'O'):
		// Octal
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char < '0' || pos.Char >= '8' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char >= '0' && pos.Char < '8' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	default:
		decimal := nextChar == '.'
		imaginary := false
		exponent := false
		for {
			if pos.Char == '.' && decimal {
				break
			} else if pos.Char == '.' && !decimal {
				decimal = true
			} else if (pos.Char == 'j' || pos.Char == 'J') && !imaginary {
				imaginary = true
			} else if (pos.Char == 'e' || pos.Char == 'E') && !exponent {
				exponent = true
				positions.Append(pos)
				pos2 := scanner.nextPosition()
				if pos2.Char == '-' || pos2.Char == '+' {
					pos3 := scanner.nextPosition()
					if !IsDigit(pos3.Char) {
						return positions.AsToken(token.ERRORTOKEN)
					}
					scanner.unreadPosition(pos3)
					positions.Append(pos2)
				} else if !IsDigit(pos2.Char) {
					return positions.AsToken(token.ERRORTOKEN)
				} else {
					scanner.unreadPosition(pos2)
				}
				pos = scanner.nextPosition()
				continue
			} else if !IsDigit(pos.Char) {
				break
			}
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	}

	return positions.AsToken(token.NUMBER)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) NextToken() *token.Token {
	positions := NewPositions()

	pos := scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		// Check for async/await
		// literal := positions.String()
		// if literal == "async" || literal == "await" {
		// 	if scanner.asyncDef {
		// 		switch literal {
		// 		case "async":
		// 			return positions.AsToken(token.ASYNC)
		// 		case "await":
		// 			return positions.AsToken(token.AWAIT)
		// 		}
		// 	} else if literal == "async" {
		// 		nextToken := scanner.NextToken()
		// 		if nextToken.ID == token.NAME && nextToken.Literal == "def" {
		// 			scanner.asyncDef = true
		// 			return positions.AsToken(token.ASYNC)
		// 		}
		// 	}
		// }

		return positions.AsToken(token.NAME)
	case ch == '\n':
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			positions.Append(pos2)
			return scanner.parseNumber(positions, pos2.Char)
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
		return scanner.parseNumber(positions, ch)
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<|endoftext|>