<commit_before>package main

import "fmt"
import "io"
import "os"
import "os/exec"
import "strings"
import "unsafe"

import . "./lua"
import "./alias"
import "./interpreter"
import "./mbcs"

const nyagos_exec_cmd = "nyagos.exec.cmd"

type LuaFunction struct {
	L            *Lua
	registoryKey string
}

func (this LuaFunction) String() string {
	return "<<Lua-function>>"
}

func (this LuaFunction) Call(cmd *exec.Cmd) (interpreter.NextT, error) {
	this.L.GetField(Registory, this.registoryKey)
	this.L.NewTable()
	for i, arg1 := range cmd.Args {
		this.L.PushInteger(i)
		this.L.PushString(arg1)
		this.L.SetTable(-3)
	}
	this.L.PushLightUserData(unsafe.Pointer(cmd))
	this.L.SetField(Registory, nyagos_exec_cmd)
	err := this.L.Call(1, 0)
	return interpreter.CONTINUE, err
}

func cmdAlias(L *Lua) int {
	name := L.ToString(1)
	key := strings.ToLower(name)
	switch L.GetType(2) {
	case TSTRING:
		value := L.ToString(2)
		alias.Table[key] = alias.New(value)
	case TFUNCTION:
		regkey := "nyagos.alias." + key
		L.SetField(Registory, regkey)
		alias.Table[key] = LuaFunction{L, regkey}
	}
	return 0
}

func cmdSetEnv(L *Lua) int {
	name := L.ToString(1)
	value := L.ToString(2)
	os.Setenv(name, value)
	return 0
}

func cmdGetEnv(L *Lua) int {
	name := L.ToString(1)
	value := os.Getenv(name)
	if len(value) > 0 {
		L.PushString(value)
		return 1
	} else {
		return 0
	}
}

func cmdExec(L *Lua) int {
	statement := L.ToString(1)
	_, err := interpreter.Interpret(statement, nil)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	return 0
}

func cmdEval(L *Lua) int {
	statement := L.ToString(1)
	r, w, err := os.Pipe()
	if err != nil {
		return 0
	}
	go func(statement string, w *os.File) {
		interpreter.Interpret(statement, &interpreter.Stdio{Stdout: w})
		w.Close()
	}(statement, w)

	var result = []byte{}
	for {
		buffer := make([]byte, 256)
		size, err := r.Read(buffer)
		if err != nil || size <= 0 {
			break
		}
		result = append(result, buffer[0:size]...)
	}
	r.Close()
	if result != nil {
		L.PushAnsiString(result)
		return 1
	} else {
		return 0
	}
}

func cmdEcho(L *Lua) int {
	var out io.Writer
	L.GetField(Registory, nyagos_exec_cmd)
	if L.GetType(-1) == TLIGHTUSERDATA {
		cmd := (*exec.Cmd)(L.ToUserData(-1))
		L.Pop(1)
		if cmd != nil {
			out = cmd.Stdout
		} else {
			out = os.Stdout
		}
	} else {
		out = os.Stdout
	}

	n := L.GetTop()
	for i := 1; i <= n; i++ {
		if i > 1 {
			fmt.Fprint(out, "\t")
		}
		fmt.Fprint(out, L.ToString(i))
	}
	fmt.Fprint(out, "\n")
	return 0
}

func cmdGetwd(L *Lua) int {
	wd, err := os.Getwd()
	if err == nil {
		L.PushString(wd)
		return 1
	} else {
		return 0
	}
}

func cmdWhich(L *Lua) int {
	if L.GetType(-1) != TSTRING {
		return 0
	}
	name := L.ToString(-1)
	path, err := exec.LookPath(name)
	if err == nil {
		L.PushString(path)
		return 1
	} else {
		return 0
	}
}

func cmdAtoU(L *Lua) int {
	str, err := mbcs.AtoU(L.ToAnsiString(1))
	if err == nil {
		L.PushString(str)
		return 1
	} else {
		return 0
	}
}

func cmdUtoA(L *Lua) int {
	str, err := mbcs.UtoA(L.ToString(1))
	if err == nil {
		L.PushAnsiString(str)
		return 1
	} else {
		return 0
	}
}

func SetLuaFunctions(this *Lua) {
	stackPos := this.GetTop()
	defer this.SetTop(stackPos)
	this.NewTable()
	this.PushGoFunction(cmdAlias)
	this.SetField(-2, "alias")
	this.PushGoFunction(cmdSetEnv)
	this.SetField(-2, "setenv")
	this.PushGoFunction(cmdGetEnv)
	this.SetField(-2, "getenv")
	this.PushGoFunction(cmdExec)
	this.SetField(-2, "exec")
	this.PushGoFunction(cmdEcho)
	this.SetField(-2, "echo")
	this.PushGoFunction(cmdAtoU)
	this.SetField(-2, "atou")
	this.PushGoFunction(cmdUtoA)
	this.SetField(-2, "utoa")
	this.PushGoFunction(cmdGetwd)
	this.SetField(-2, "getwd")
	this.PushGoFunction(cmdWhich)
	this.SetField(-2, "which")
	this.PushGoFunction(cmdEval)
	this.SetField(-2, "eval")
	this.SetGlobal("nyagos")

	// replace io.getenv
	this.GetGlobal("os")
	this.PushGoFunction(cmdGetEnv)
	this.SetField(-2, "getenv")

	var orgArgHook func([]string) []string
	orgArgHook = interpreter.SetArgsHook(func(args []string) []string {
		pos := this.GetTop()
		defer this.SetTop(pos)
		this.GetGlobal("nyagos")
		this.GetField(-1, "argsfilter")
		if !this.IsFunction(-1) {
			return orgArgHook(args)
		}
		this.NewTable()
		for i := 0; i < len(args); i++ {
			this.PushInteger(i)
			this.PushString(args[i])
			this.SetTable(-3)
		}
		if err := this.Call(1, 1); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			return orgArgHook(args)
		}
		if this.GetType(-1) != TTABLE {
			return orgArgHook(args)
		}
		newargs := []string{}
		for i := 0; true; i++ {
			this.PushInteger(i)
			this.GetTable(-2)
			if this.GetType(-1) == TNIL {
				break
			}
			newargs = append(newargs, this.ToString(-1))
			this.Pop(1)
		}
		return orgArgHook(newargs)
	})
}
<commit_msg>Lua関数: nyagos.glob を作成<commit_after>package main

import "fmt"
import "io"
import "os"
import "os/exec"
import "strings"
import "unsafe"

import . "./lua"
import "./alias"
import "./dos"
import "./interpreter"
import "./mbcs"

const nyagos_exec_cmd = "nyagos.exec.cmd"

type LuaFunction struct {
	L            *Lua
	registoryKey string
}

func (this LuaFunction) String() string {
	return "<<Lua-function>>"
}

func (this LuaFunction) Call(cmd *exec.Cmd) (interpreter.NextT, error) {
	this.L.GetField(Registory, this.registoryKey)
	this.L.NewTable()
	for i, arg1 := range cmd.Args {
		this.L.PushInteger(i)
		this.L.PushString(arg1)
		this.L.SetTable(-3)
	}
	this.L.PushLightUserData(unsafe.Pointer(cmd))
	this.L.SetField(Registory, nyagos_exec_cmd)
	err := this.L.Call(1, 0)
	return interpreter.CONTINUE, err
}

func cmdAlias(L *Lua) int {
	name := L.ToString(1)
	key := strings.ToLower(name)
	switch L.GetType(2) {
	case TSTRING:
		value := L.ToString(2)
		alias.Table[key] = alias.New(value)
	case TFUNCTION:
		regkey := "nyagos.alias." + key
		L.SetField(Registory, regkey)
		alias.Table[key] = LuaFunction{L, regkey}
	}
	return 0
}

func cmdSetEnv(L *Lua) int {
	name := L.ToString(1)
	value := L.ToString(2)
	os.Setenv(name, value)
	return 0
}

func cmdGetEnv(L *Lua) int {
	name := L.ToString(1)
	value := os.Getenv(name)
	if len(value) > 0 {
		L.PushString(value)
		return 1
	} else {
		return 0
	}
}

func cmdExec(L *Lua) int {
	statement := L.ToString(1)
	_, err := interpreter.Interpret(statement, nil)

	if err != nil {
		fmt.Fprintln(os.Stderr, err)
	}
	return 0
}

func cmdEval(L *Lua) int {
	statement := L.ToString(1)
	r, w, err := os.Pipe()
	if err != nil {
		return 0
	}
	go func(statement string, w *os.File) {
		interpreter.Interpret(statement, &interpreter.Stdio{Stdout: w})
		w.Close()
	}(statement, w)

	var result = []byte{}
	for {
		buffer := make([]byte, 256)
		size, err := r.Read(buffer)
		if err != nil || size <= 0 {
			break
		}
		result = append(result, buffer[0:size]...)
	}
	r.Close()
	if result != nil {
		L.PushAnsiString(result)
		return 1
	} else {
		return 0
	}
}

func cmdEcho(L *Lua) int {
	var out io.Writer
	L.GetField(Registory, nyagos_exec_cmd)
	if L.GetType(-1) == TLIGHTUSERDATA {
		cmd := (*exec.Cmd)(L.ToUserData(-1))
		L.Pop(1)
		if cmd != nil {
			out = cmd.Stdout
		} else {
			out = os.Stdout
		}
	} else {
		out = os.Stdout
	}

	n := L.GetTop()
	for i := 1; i <= n; i++ {
		if i > 1 {
			fmt.Fprint(out, "\t")
		}
		fmt.Fprint(out, L.ToString(i))
	}
	fmt.Fprint(out, "\n")
	return 0
}

func cmdGetwd(L *Lua) int {
	wd, err := os.Getwd()
	if err == nil {
		L.PushString(wd)
		return 1
	} else {
		return 0
	}
}

func cmdWhich(L *Lua) int {
	if L.GetType(-1) != TSTRING {
		return 0
	}
	name := L.ToString(-1)
	path, err := exec.LookPath(name)
	if err == nil {
		L.PushString(path)
		return 1
	} else {
		return 0
	}
}

func cmdAtoU(L *Lua) int {
	str, err := mbcs.AtoU(L.ToAnsiString(1))
	if err == nil {
		L.PushString(str)
		return 1
	} else {
		return 0
	}
}

func cmdUtoA(L *Lua) int {
	str, err := mbcs.UtoA(L.ToString(1))
	if err == nil {
		L.PushAnsiString(str)
		return 1
	} else {
		return 0
	}
}

func cmdGlob(L *Lua) int {
	if !L.IsString(-1) {
		return 0
	}
	list, err := dos.Glob(L.ToString(-1))
	if err != nil {
		L.PushNil()
		L.PushString(err.Error())
		return 2
	} else {
		L.NewTable()
		for i := 0; i < len(list); i++ {
			L.PushInteger(i + 1)
			L.PushString(list[i])
			L.SetTable(-3)
		}
		return 1
	}
}

func SetLuaFunctions(this *Lua) {
	stackPos := this.GetTop()
	defer this.SetTop(stackPos)
	this.NewTable()
	this.PushGoFunction(cmdAlias)
	this.SetField(-2, "alias")
	this.PushGoFunction(cmdSetEnv)
	this.SetField(-2, "setenv")
	this.PushGoFunction(cmdGetEnv)
	this.SetField(-2, "getenv")
	this.PushGoFunction(cmdExec)
	this.SetField(-2, "exec")
	this.PushGoFunction(cmdEcho)
	this.SetField(-2, "echo")
	this.PushGoFunction(cmdAtoU)
	this.SetField(-2, "atou")
	this.PushGoFunction(cmdUtoA)
	this.SetField(-2, "utoa")
	this.PushGoFunction(cmdGetwd)
	this.SetField(-2, "getwd")
	this.PushGoFunction(cmdWhich)
	this.SetField(-2, "which")
	this.PushGoFunction(cmdEval)
	this.SetField(-2, "eval")
	this.PushGoFunction(cmdGlob)
	this.SetField(-2, "glob")
	this.SetGlobal("nyagos")

	// replace io.getenv
	this.GetGlobal("os")
	this.PushGoFunction(cmdGetEnv)
	this.SetField(-2, "getenv")

	var orgArgHook func([]string) []string
	orgArgHook = interpreter.SetArgsHook(func(args []string) []string {
		pos := this.GetTop()
		defer this.SetTop(pos)
		this.GetGlobal("nyagos")
		this.GetField(-1, "argsfilter")
		if !this.IsFunction(-1) {
			return orgArgHook(args)
		}
		this.NewTable()
		for i := 0; i < len(args); i++ {
			this.PushInteger(i)
			this.PushString(args[i])
			this.SetTable(-3)
		}
		if err := this.Call(1, 1); err != nil {
			fmt.Fprintf(os.Stderr, "%s\n", err)
			return orgArgHook(args)
		}
		if this.GetType(-1) != TTABLE {
			return orgArgHook(args)
		}
		newargs := []string{}
		for i := 0; true; i++ {
			this.PushInteger(i)
			this.GetTable(-2)
			if this.GetType(-1) == TNIL {
				break
			}
			newargs = append(newargs, this.ToString(-1))
			this.Pop(1)
		}
		return orgArgHook(newargs)
	})
}
<|endoftext|>


<commit_before>package main

import (
    "fmt";
    "http";
    "os";
    "flag";
    "io";
)

func main() {
    flag.Parse(); // Parse command line args

    if flag.NArg() < 1 {
        fmt.Println("Cowardly refusing to shorten a blank URL");
        os.Exit(-1);
    }

    url, error := shortenURL(flag.Arg(0));
    if error != nil {
        fmt.Println(error);
        os.Exit(-1);
    }

    fmt.Println(url);

}

// Developed using the "API" listed here: http://is.gd/api_info.php
func shortenURL (url string) (shortURL string, err os.Error) {
    u := "http://is.gd/api.php?longurl=" + http.URLEscape(url);

    response, _, err := http.Get(u);

    // Make sure we can connect
    if err != nil {
        return
    }

    // Make sure we get a 200 response code
    if response.StatusCode != 200 {
        return "", os.NewError("Could not shorted your URL. Perhaps it was malformed?");
    }

    b, err := io.ReadAll(response.Body);
    response.Body.Close();

    return string(b), nil;
}
<commit_msg>Cleaned up the implementation, removed superfluous code<commit_after>package main

import ("fmt"; "http"; "os"; "flag"; "io";)

func main() {
    flag.Parse(); // Parse command line args

    if flag.NArg() < 1 {
        fmt.Println("Cowardly refusing to shorten a blank URL");
        os.Exit(-1);
    }

    url, error := shortenURL(flag.Arg(0));
    if error != nil {
        fmt.Println(error);
        os.Exit(-1);
    }

    fmt.Println(url);

}

// Developed using the "API" listed here: http://is.gd/api_info.php
func shortenURL (url string) (shortURL string, err os.Error) {
    u := "http://is.gd/api.php?longurl=" + http.URLEscape(url);

    response, _, err := http.Get(u);

    // Make sure we can connect
    if err != nil {
        return
    }

    // Make sure we get a 200 response code
    if response.StatusCode != 200 {
        return "", os.NewError("Could not shorted your URL. Perhaps it was malformed?");
    }

    b, err := io.ReadAll(response.Body);
    response.Body.Close();

    return string(b), nil
}
<|endoftext|>


<commit_before>package onedrive

import (
	"fmt"
	"net/http"
	"time"
)

// ItemService manages the communication with Item related API endpoints
type ItemService struct {
	*OneDrive
}

// The Thumbnail resource type represents a thumbnail for an image, video,
// document, or any file or folder on OneDrive that has a graphical representation.
// See: http://onedrive.github.io/resources/thumbnail.htm
type Thumbnail struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
	// Relationships
	Content []byte `json:"content"`
}

// The ThumbnailSet type is a keyed collection of Thumbnail objects. It is used
// to represent a set of thumbnails associated with a single file on OneDrive.
// See: http://onedrive.github.io/resources/thumbnailSet.htm
type ThumbnailSet struct {
	ID     string     `json:"id"`
	Small  *Thumbnail `json:"small"`
	Medium *Thumbnail `json:"medium"`
	Large  *Thumbnail `json:"large"`
}

// Items represents a collection of Items
type Items struct {
	Collection []*Item `json:"value"`
}

// The ItemReference type groups data needed to reference a OneDrive item across
// the service into a single structure.
// See: http://onedrive.github.io/resources/itemReference.htm
type ItemReference struct {
	DriveID string `json:"driveId"`
	ID      string `json:"id"`
	Path    string `json:"path"`
}

// The Item resource type represents metadata for an item in OneDrive. All
// top-level filesystem objects in OneDrive are Item resources. If an item is
// a Folder or File facet, the Item resource will contain a value for either
// the folder or file property, respectively.
// See: http://onedrive.github.io/resources/item.htm
type Item struct {
	ID                   string         `json:"id"`
	Name                 string         `json:"name"`
	ETag                 string         `json:"eTag"`
	CTag                 string         `json:"cTag"`
	CreatedBy            *IdentitySet   `json:"createdBy"`
	LastModifiedBy       *IdentitySet   `json:"lastModifiedBy"`
	CreatedDateTime      time.Time      `json:"createdDateTime"`
	LastModifiedDateTime time.Time      `json:"lastModifiedDateTime"`
	Size                 int64          `json:"size"`
	ParentReference      *ItemReference `json:"parentReference"`
	WebURL               string         `json:"webUrl"`
	File                 *FileFacet     `json:"file"`
	Folder               *FolderFacet   `json:"folder"`
	Image                *ImageFacet    `json:"image"`
	Photo                *PhotoFacet    `json:"photo"`
	Audio                *AudioFacet    `json:"audio"`
	Video                *VideoFacet    `json:"video"`
	Location             *LocationFacet `json:"location"`
	Deleted              *DeletedFacet  `json:"deleted"`
	// Instance attributes
	ConflictBehaviour string `json:"@name.conflictBehavior"`
	DownloadURL       string `json:"@content.downloadUrl"`
	SourceURL         string `json:"@content.sourceUrl"`
	// Relationships
	Content    []byte        `json:"content"`
	Children   []*Item       `json:"children"`
	Thumbnails *ThumbnailSet `json:"thumbnails"`
}

// driveURIFromID returns a valid request URI based on the ID of the drive.
// Mostly exists to simplify special cases such as the default and root drives.
func itemURIFromID(itemID string) string {
	switch itemID {
	case "", "root":
		return "/drive/root"
	default:
		return fmt.Sprintf("/drive/items/%s", itemID)
	}
}

// Get returns an item with the specified ID.
func (is *ItemService) Get(itemID string) (*Item, *http.Response, error) {
	req, err := is.newRequest("GET", itemURIFromID(itemID), nil, nil)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}

// GetDefaultDriveRootFolder is a convenience function to return the root folder
// of the users default Drive
func (is *ItemService) GetDefaultDriveRootFolder() (*Item, *http.Response, error) {
	return is.Get("root")
}

// ListChildren returns a collection of all the Items under an Item
func (is *ItemService) ListChildren(itemID string) (*Items, *http.Response, error) {
	reqURI := fmt.Sprintf("/drive/items/%s/children", itemID)
	req, err := is.newRequest("GET", reqURI, nil, nil)
	if err != nil {
		return nil, nil, err
	}

	items := new(Items)
	resp, err := is.do(req, items)
	if err != nil {
		return nil, resp, err
	}

	return items, resp, nil
}

type newFolder struct {
	Name   string       `json:"name"`
	Folder *FolderFacet `json:"folder"`
}

// CreateFolder creates a new folder within the parent.
func (is *ItemService) CreateFolder(parentID, folderName string) (*Item, *http.Response, error) {
	folder := newFolder{
		Name:   folderName,
		Folder: new(FolderFacet),
	}

	path := fmt.Sprintf("/drive/items/%s/children/%s", parentID, folderName)
	req, err := is.newRequest("PUT", path, nil, folder)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}

type newWebUpload struct {
	SourceURL string     `json:"@content.sourceUrl"`
	Name      string     `json:"name"`
	File      *FileFacet `json:"file"`
}

// UploadFromURL allows your app to upload an item to OneDrive by providing a URL.
// OneDrive will download the file directly from a remote server so your app
// doesn't have to upload the file's bytes.
// See: http://onedrive.github.io/items/upload_url.htm
func (is *ItemService) UploadFromURL(parentID, name, webURL string) (*Item, *http.Response, error) {
	requestHeaders := map[string]string{
		"Prefer": "respond-async",
	}

	newFile := newWebUpload{
		webURL, name, new(FileFacet),
	}

	path := fmt.Sprintf("/drive/items/%s/children", parentID)
	req, err := is.newRequest("PUT", path, requestHeaders, newFile)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}
<commit_msg>Upload from URL requires POST method.<commit_after>package onedrive

import (
	"fmt"
	"net/http"
	"time"
)

// ItemService manages the communication with Item related API endpoints
type ItemService struct {
	*OneDrive
}

// The Thumbnail resource type represents a thumbnail for an image, video,
// document, or any file or folder on OneDrive that has a graphical representation.
// See: http://onedrive.github.io/resources/thumbnail.htm
type Thumbnail struct {
	Width  int    `json:"width"`
	Height int    `json:"height"`
	URL    string `json:"url"`
	// Relationships
	Content []byte `json:"content"`
}

// The ThumbnailSet type is a keyed collection of Thumbnail objects. It is used
// to represent a set of thumbnails associated with a single file on OneDrive.
// See: http://onedrive.github.io/resources/thumbnailSet.htm
type ThumbnailSet struct {
	ID     string     `json:"id"`
	Small  *Thumbnail `json:"small"`
	Medium *Thumbnail `json:"medium"`
	Large  *Thumbnail `json:"large"`
}

// Items represents a collection of Items
type Items struct {
	Collection []*Item `json:"value"`
}

// The ItemReference type groups data needed to reference a OneDrive item across
// the service into a single structure.
// See: http://onedrive.github.io/resources/itemReference.htm
type ItemReference struct {
	DriveID string `json:"driveId"`
	ID      string `json:"id"`
	Path    string `json:"path"`
}

// The Item resource type represents metadata for an item in OneDrive. All
// top-level filesystem objects in OneDrive are Item resources. If an item is
// a Folder or File facet, the Item resource will contain a value for either
// the folder or file property, respectively.
// See: http://onedrive.github.io/resources/item.htm
type Item struct {
	ID                   string         `json:"id"`
	Name                 string         `json:"name"`
	ETag                 string         `json:"eTag"`
	CTag                 string         `json:"cTag"`
	CreatedBy            *IdentitySet   `json:"createdBy"`
	LastModifiedBy       *IdentitySet   `json:"lastModifiedBy"`
	CreatedDateTime      time.Time      `json:"createdDateTime"`
	LastModifiedDateTime time.Time      `json:"lastModifiedDateTime"`
	Size                 int64          `json:"size"`
	ParentReference      *ItemReference `json:"parentReference"`
	WebURL               string         `json:"webUrl"`
	File                 *FileFacet     `json:"file"`
	Folder               *FolderFacet   `json:"folder"`
	Image                *ImageFacet    `json:"image"`
	Photo                *PhotoFacet    `json:"photo"`
	Audio                *AudioFacet    `json:"audio"`
	Video                *VideoFacet    `json:"video"`
	Location             *LocationFacet `json:"location"`
	Deleted              *DeletedFacet  `json:"deleted"`
	// Instance attributes
	ConflictBehaviour string `json:"@name.conflictBehavior"`
	DownloadURL       string `json:"@content.downloadUrl"`
	SourceURL         string `json:"@content.sourceUrl"`
	// Relationships
	Content    []byte        `json:"content"`
	Children   []*Item       `json:"children"`
	Thumbnails *ThumbnailSet `json:"thumbnails"`
}

// driveURIFromID returns a valid request URI based on the ID of the drive.
// Mostly exists to simplify special cases such as the default and root drives.
func itemURIFromID(itemID string) string {
	switch itemID {
	case "", "root":
		return "/drive/root"
	default:
		return fmt.Sprintf("/drive/items/%s", itemID)
	}
}

// Get returns an item with the specified ID.
func (is *ItemService) Get(itemID string) (*Item, *http.Response, error) {
	req, err := is.newRequest("GET", itemURIFromID(itemID), nil, nil)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}

// GetDefaultDriveRootFolder is a convenience function to return the root folder
// of the users default Drive
func (is *ItemService) GetDefaultDriveRootFolder() (*Item, *http.Response, error) {
	return is.Get("root")
}

// ListChildren returns a collection of all the Items under an Item
func (is *ItemService) ListChildren(itemID string) (*Items, *http.Response, error) {
	reqURI := fmt.Sprintf("/drive/items/%s/children", itemID)
	req, err := is.newRequest("GET", reqURI, nil, nil)
	if err != nil {
		return nil, nil, err
	}

	items := new(Items)
	resp, err := is.do(req, items)
	if err != nil {
		return nil, resp, err
	}

	return items, resp, nil
}

type newFolder struct {
	Name   string       `json:"name"`
	Folder *FolderFacet `json:"folder"`
}

// CreateFolder creates a new folder within the parent.
func (is *ItemService) CreateFolder(parentID, folderName string) (*Item, *http.Response, error) {
	folder := newFolder{
		Name:   folderName,
		Folder: new(FolderFacet),
	}

	path := fmt.Sprintf("/drive/items/%s/children/%s", parentID, folderName)
	req, err := is.newRequest("PUT", path, nil, folder)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}

type newWebUpload struct {
	SourceURL string     `json:"@content.sourceUrl"`
	Name      string     `json:"name"`
	File      *FileFacet `json:"file"`
}

// UploadFromURL allows your app to upload an item to OneDrive by providing a URL.
// OneDrive will download the file directly from a remote server so your app
// doesn't have to upload the file's bytes.
// See: http://onedrive.github.io/items/upload_url.htm
func (is *ItemService) UploadFromURL(parentID, name, webURL string) (*Item, *http.Response, error) {
	requestHeaders := map[string]string{
		"Prefer": "respond-async",
	}

	newFile := newWebUpload{
		webURL, name, new(FileFacet),
	}

	path := fmt.Sprintf("/drive/items/%s/children", parentID)
	req, err := is.newRequest("POST", path, requestHeaders, newFile)
	if err != nil {
		return nil, nil, err
	}

	item := new(Item)
	resp, err := is.do(req, item)
	if err != nil {
		return nil, resp, err
	}

	return item, resp, nil
}
<|endoftext|>


<commit_before>/********************************
*** Multiplexer for Go        ***
*** Bone is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/go-zoo         ***
*********************************/

package bone

import (
	"fmt"
	"net/http"
)

// Register the route in the router
func (m *Mux) Register(method string, path string, handler http.Handler) *Route {
	fmt.Printf("%s registed !\n", path)
	return m.register(method, path, handler)
}

// Get add a new route to the Mux with the Get method
func (m *Mux) GetFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("GET", path, handler)
}

// Post add a new route to the Mux with the Post method
func (m *Mux) PostFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("POST", path, handler)
}

// Put add a new route to the Mux with the Put method
func (m *Mux) PutFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("PUT", path, handler)
}

// Delete add a new route to the Mux with the Delete method
func (m *Mux) DeleteFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("DELETE", path, handler)
}

// Head add a new route to the Mux with the Head method
func (m *Mux) HeadFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("HEAD", path, handler)
}

// Patch add a new route to the Mux with the Patch method
func (m *Mux) PatchFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("PATCH", path, handler)
}

// Options add a new route to the Mux with the Options method
func (m *Mux) OptionsFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("OPTIONS", path, handler)
}

// NotFound the mux custom 404 handler
func (m *Mux) NotFoundFunc(handler http.HandlerFunc) {
	m.notFound = handler
}
<commit_msg>Doc minor fix<commit_after>/********************************
*** Multiplexer for Go        ***
*** Bone is under MIT license ***
*** Code by CodingFerret      ***
*** github.com/go-zoo         ***
*********************************/

package bone

import "net/http"

// Register the route in the router
func (m *Mux) Register(method string, path string, handler http.Handler) *Route {
	return m.register(method, path, handler)
}

// GetFunc add a new route to the Mux with the Get method
func (m *Mux) GetFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("GET", path, handler)
}

// PostFunc add a new route to the Mux with the Post method
func (m *Mux) PostFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("POST", path, handler)
}

// PutFunc add a new route to the Mux with the Put method
func (m *Mux) PutFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("PUT", path, handler)
}

// DeleteFunc add a new route to the Mux with the Delete method
func (m *Mux) DeleteFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("DELETE", path, handler)
}

// HeadFunc add a new route to the Mux with the Head method
func (m *Mux) HeadFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("HEAD", path, handler)
}

// PatchFunc add a new route to the Mux with the Patch method
func (m *Mux) PatchFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("PATCH", path, handler)
}

// OptionsFunc add a new route to the Mux with the Options method
func (m *Mux) OptionsFunc(path string, handler http.HandlerFunc) *Route {
	return m.register("OPTIONS", path, handler)
}

// NotFoundFunc the mux custom 404 handler
func (m *Mux) NotFoundFunc(handler http.HandlerFunc) {
	m.notFound = handler
}
<|endoftext|>


<commit_before>package main

import (
	"strconv"

	"github.com/shirou/gopsutil/net"
)

// `netinfo` represent the network trafic informations
type netinfo struct {
	up   string
	down string
}

// Get informations about the net trafic
func getNetinfo() netinfo {
	ioconters, err := net.IOCounters(false)
	if err != nil {
		panic(err) //TODO do not panic but manage the error
	}

	ret := netinfo{
		up:   strconv.FormatUint(ioconters[1].BytesSent, 10),
		down: strconv.FormatUint(ioconters[1].BytesRecv, 10),
	}
	return ret
}
<commit_msg>Update netinfo stats<commit_after>package main

import (
	"strconv"

	"github.com/shirou/gopsutil/net"
)

// Netinfo represent the network trafic informations
type Netinfo struct {
	up   string
	down string
}

// Get informations about the net trafic
func getNetinfo() Netinfo {
	ioconters, err := net.IOCounters(false)
	if err != nil {
		panic(err) //TODO do not panic but manage the error
	}

	ret := Netinfo{
		//up:   strconv.FormatUint(ioconters[0].BytesSent, 10),
		up: strconv.FormatFloat(float64(ioconters[0].BytesSent)/(1024*1024), 'f', 2, 64),
		//down: strconv.FormatUint(ioconters[0].BytesRecv, 10),
		down: strconv.FormatFloat(float64(ioconters[0].BytesRecv)/(1024*1024), 'f', 2, 64),
	}
	return ret
}
<|endoftext|>


<commit_before>package req

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

var regBlank = regexp.MustCompile(`\s+`)

// M represents the request params.
type M map[string]string

// Request provides much easier useage than http.Request
type Request struct {
	url       string
	urlEncode bool
	params    M
	req       *http.Request
	resp      *Response
	body      []byte
	client    http.Client
}

// Request return the raw *http.Request.
func (r *Request) Request() *http.Request {
	return r.req
}

// InsecureTLS insecure the https.
func (r *Request) InsecureTLS() *Request {
	r.client.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return r
}

// Param set single param to the request.
func (r *Request) Param(k, v string) *Request {
	r.params[k] = v
	return r
}

// Params set multiple params to the request.
func (r *Request) Params(params M) *Request {
	for k, v := range params {
		r.params[k] = v
	}
	return r
}

// Header set the request header.
func (r *Request) Header(k, v string) *Request {
	r.req.Header.Set(k, v)
	return r
}

// Headers set multiple headers.
func (r *Request) Headers(params M) *Request {
	for k, v := range params {
		r.req.Header.Set(k, v)
	}
	return r
}

// Body set the request body,support string and []byte.
func (r *Request) Body(body interface{}) *Request {
	switch v := body.(type) {
	case string:
		bf := bytes.NewBufferString(v)
		r.req.Body = ioutil.NopCloser(bf)
		r.req.ContentLength = int64(len(v))
		r.body = []byte(v)
	case []byte:
		bf := bytes.NewBuffer(v)
		r.req.Body = ioutil.NopCloser(bf)
		r.req.ContentLength = int64(len(v))
		r.body = v
	}
	return r
}

// GetBody return the request body.
func (r *Request) GetBody() []byte {
	return r.body
}

// Bytes execute the request and get the response body as []byte.
func (r *Request) ReceiveBytes() (data []byte, err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	data, err = resp.ReceiveBytes()
	return
}

// MustBytes execute the request and get the response body as []byte.panic if error happens.
func (r *Request) Bytes() (data []byte) {
	data, _ = r.ReceiveBytes()
	return
}

// String execute the request and get the response body as string.
func (r *Request) ReceiveString() (s string, err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	s, err = resp.ReceiveString()
	return
}

// MustString execute the request and get the response body as string.panic if error happens.
func (r *Request) String() (s string) {
	s, _ = r.ReceiveString()
	return
}

// String execute the request and get the response body unmarshal to json.
func (r *Request) ToJson(v interface{}) (err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	err = resp.ToJson(v)
	return
}

// String execute the request and get the response body unmarshal to xml.
func (r *Request) ToXml(v interface{}) (err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	err = resp.ToXml(v)
	return
}

// UrlEncode set weighter urlencode the params or not.default to true.
func (r *Request) UrlEncode(urlEncode bool) *Request {
	r.urlEncode = urlEncode
	return r
}

func (r *Request) getParamBody() string {
	if len(r.params) == 0 {
		return ""
	}
	var buf bytes.Buffer
	for k, v := range r.params {
		if r.urlEncode {
			k = url.QueryEscape(k)
			v = url.QueryEscape(v)
		}
		buf.WriteString(k)
		buf.WriteByte('=')
		buf.WriteString(v)
		buf.WriteByte('&')
	}
	p := buf.String()
	p = p[0 : len(p)-1]
	return p
}

func (r *Request) buildGetUrl() string {
	ret := r.url
	if p := r.getParamBody(); p != "" {
		if strings.Index(r.url, "?") != -1 {
			ret += "&" + p
		} else {
			ret += "?" + p
		}
	}
	return ret
}

func (r *Request) setParamBody() {
	if r.urlEncode {
		r.Header("Content-Type", "application/x-www-form-urlencoded")
	}
	r.Body(r.getParamBody())
}

// GetUrl return the url of the request.
func (r *Request) GetUrl() string {
	if r.req.Method != "GET" || r.resp != nil {
		return r.url
	}
	return r.buildGetUrl() //GET method and did not send request yet.
}

// Url set the request's url.
func (r *Request) Url(urlStr string) *Request {
	r.url = urlStr
	return r
}

// ReceiveResponse execute the request and get the response, return error if error happens.
func (r *Request) ReceiveResponse() (resp *Response, err error) {
	if r.resp != nil { // provent multiple call
		resp = r.resp
		return
	}
	err = r.Do()
	if err != nil {
		return
	}
	resp = r.resp
	return
}

// Do just execute the request. return error if error happens.
func (r *Request) Do() (err error) {
	// handle request params
	if len(r.params) > 0 {
		switch r.req.Method {
		case "GET":
			r.url = r.buildGetUrl()
		case "POST":
			if r.req.Body == nil {
				r.setParamBody()
			}
		}
	}
	// set url
	u, err := url.Parse(r.url)
	if err != nil {
		return
	}
	r.req.URL = u
	respRaw, err := r.client.Do(r.req)
	if err != nil {
		return
	}
	resp := NewResponse(respRaw)
	err = resp.Receive()
	if err != nil {
		return
	}
	r.resp = resp
	return
}

// Response execute the request and get the response.panic if error happens.
func (r *Request) Response() (resp *Response) {
	resp, _ = r.ReceiveResponse()
	return
}

// Get returns *Request with GET method.
func Get(url string) *Request {
	return newRequest(url, "GET")
}

// Post returns *Request with POST method.
func Post(url string) *Request {
	return newRequest(url, "POST")
}

// New return a Request with the underlying *http.Request.
func New(req *http.Request) *Request {
	return &Request{
		urlEncode: true,
		params:    M{},
		req:       req,
	}
}

func newRequest(url, method string) *Request {
	return &Request{
		url:       url,
		urlEncode: true,
		params:    M{},
		req: &http.Request{
			Method:     method,
			Header:     make(http.Header),
			Proto:      "HTTP/1.1",
			ProtoMajor: 1,
			ProtoMinor: 1,
		},
	}
}

// Format implements fmt.Formatter, format the request's infomation.
func (r *Request) Format(s fmt.State, verb rune) {
	if s.Flag('+') { // include header and format pretty.
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl(), " ", r.req.Proto)
		for name, values := range r.req.Header {
			for _, value := range values {
				fmt.Fprint(s, "\n", name, ":", value)
			}
		}
		if len(r.body) > 0 {
			fmt.Fprint(s, "\n\n", string(r.body))
		}
		if verb != 'r' {
			if resp := r.Response(); resp != nil {
				fmt.Fprint(s, "\n\n")
				resp.Format(s, verb)
			}
		}
	} else if s.Flag('-') { // keep all infomations in one line.
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl())
		if len(r.body) > 0 {
			str := regBlank.ReplaceAllString(string(r.body), "")
			fmt.Fprint(s, str)
		}
		if str := r.String(); str != "" {
			str = regBlank.ReplaceAllString(str, "")
			fmt.Fprint(s, " ", str)
		}
	} else { // auto
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl())
		if verb == 'r' {
			if len(r.body) > 0 {
				if bytes.IndexByte(r.body, '\n') != -1 && r.body[0] != '\n' {
					fmt.Fprint(s, "\n")
				}
				fmt.Fprint(s, string(r.body))
			}
		} else {
			respBody := r.Bytes()
			if (len(r.body) > 0 && bytes.IndexByte(r.body, '\n') != -1) || (len(respBody) > 0 && bytes.IndexByte(respBody, '\n') != -1) { // pretty format
				if len(r.body) > 0 {
					fmt.Fprint(s, "\n", string(r.body))
				}
				if len(respBody) > 0 {
					fmt.Fprint(s, "\n", string(respBody))
				}
			} else {
				if len(r.body) > 0 {
					fmt.Fprint(s, " ", string(r.body))
				}
				if len(respBody) > 0 {
					fmt.Fprint(s, " ", string(respBody))
				}
			}
		}
	}

}
<commit_msg>add Undo support. when Request is executed, it would not execute again by default,call r.Undo() making it could be executed agin,maybe you could change some params and try again the request.<commit_after>package req

import (
	"bytes"
	"crypto/tls"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

var regBlank = regexp.MustCompile(`\s+`)

// M represents the request params.
type M map[string]string

// Request provides much easier useage than http.Request
type Request struct {
	url       string
	urlEncode bool
	params    M
	req       *http.Request
	resp      *Response
	body      []byte
	client    http.Client
}

// Request return the raw *http.Request.
func (r *Request) Request() *http.Request {
	return r.req
}

// InsecureTLS insecure the https.
func (r *Request) InsecureTLS() *Request {
	r.client.Transport = &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return r
}

// Param set single param to the request.
func (r *Request) Param(k, v string) *Request {
	r.params[k] = v
	return r
}

// Params set multiple params to the request.
func (r *Request) Params(params M) *Request {
	for k, v := range params {
		r.params[k] = v
	}
	return r
}

// Header set the request header.
func (r *Request) Header(k, v string) *Request {
	r.req.Header.Set(k, v)
	return r
}

// Headers set multiple headers.
func (r *Request) Headers(params M) *Request {
	for k, v := range params {
		r.req.Header.Set(k, v)
	}
	return r
}

// Body set the request body,support string and []byte.
func (r *Request) Body(body interface{}) *Request {
	switch v := body.(type) {
	case string:
		bf := bytes.NewBufferString(v)
		r.req.Body = ioutil.NopCloser(bf)
		r.req.ContentLength = int64(len(v))
		r.body = []byte(v)
	case []byte:
		bf := bytes.NewBuffer(v)
		r.req.Body = ioutil.NopCloser(bf)
		r.req.ContentLength = int64(len(v))
		r.body = v
	}
	return r
}

// GetBody return the request body.
func (r *Request) GetBody() []byte {
	return r.body
}

// Bytes execute the request and get the response body as []byte.
func (r *Request) ReceiveBytes() (data []byte, err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	data, err = resp.ReceiveBytes()
	return
}

// MustBytes execute the request and get the response body as []byte.panic if error happens.
func (r *Request) Bytes() (data []byte) {
	data, _ = r.ReceiveBytes()
	return
}

// String execute the request and get the response body as string.
func (r *Request) ReceiveString() (s string, err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	s, err = resp.ReceiveString()
	return
}

// MustString execute the request and get the response body as string.panic if error happens.
func (r *Request) String() (s string) {
	s, _ = r.ReceiveString()
	return
}

// String execute the request and get the response body unmarshal to json.
func (r *Request) ToJson(v interface{}) (err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	err = resp.ToJson(v)
	return
}

// String execute the request and get the response body unmarshal to xml.
func (r *Request) ToXml(v interface{}) (err error) {
	resp, err := r.ReceiveResponse()
	if err != nil {
		return
	}
	err = resp.ToXml(v)
	return
}

// UrlEncode set weighter urlencode the params or not.default to true.
func (r *Request) UrlEncode(urlEncode bool) *Request {
	r.urlEncode = urlEncode
	return r
}

func (r *Request) getParamBody() string {
	if len(r.params) == 0 {
		return ""
	}
	var buf bytes.Buffer
	for k, v := range r.params {
		if r.urlEncode {
			k = url.QueryEscape(k)
			v = url.QueryEscape(v)
		}
		buf.WriteString(k)
		buf.WriteByte('=')
		buf.WriteString(v)
		buf.WriteByte('&')
	}
	p := buf.String()
	p = p[0 : len(p)-1]
	return p
}

func (r *Request) buildGetUrl() string {
	ret := r.url
	if p := r.getParamBody(); p != "" {
		if strings.Index(r.url, "?") != -1 {
			ret += "&" + p
		} else {
			ret += "?" + p
		}
	}
	return ret
}

func (r *Request) setParamBody() {
	if r.urlEncode {
		r.Header("Content-Type", "application/x-www-form-urlencoded")
	}
	r.Body(r.getParamBody())
}

// GetUrl return the url of the request.
func (r *Request) GetUrl() string {
	if r.req.Method == "GET" {
		return r.buildGetUrl() //GET method and did not send request yet.
	}
	return r.url
}

// Url set the request's url.
func (r *Request) Url(urlStr string) *Request {
	r.url = urlStr
	return r
}

// ReceiveResponse execute the request and get the response, return error if error happens.
func (r *Request) ReceiveResponse() (resp *Response, err error) {
	if r.resp != nil { // provent multiple call
		resp = r.resp
		return
	}
	err = r.Do()
	if err != nil {
		return
	}
	resp = r.resp
	return
}

// Undo let the request could be executed again.
func (r *Request) Undo() *Request {
	r.resp = nil
	return r
}

// Do just execute the request. return error if error happens.
func (r *Request) Do() (err error) {
	// handle request params
	destUrl := r.url
	if len(r.params) > 0 {
		switch r.req.Method {
		case "GET":
			destUrl = r.buildGetUrl()
		case "POST":
			r.setParamBody()
		}
	}
	// set url
	u, err := url.Parse(destUrl)
	if err != nil {
		return
	}
	r.req.URL = u
	respRaw, err := r.client.Do(r.req)
	if err != nil {
		return
	}
	resp := NewResponse(respRaw)
	err = resp.Receive()
	if err != nil {
		return
	}
	r.resp = resp
	return
}

// Response execute the request and get the response.panic if error happens.
func (r *Request) Response() (resp *Response) {
	resp, _ = r.ReceiveResponse()
	return
}

// Get returns *Request with GET method.
func Get(url string) *Request {
	return newRequest(url, "GET")
}

// Post returns *Request with POST method.
func Post(url string) *Request {
	return newRequest(url, "POST")
}

// New return a Request with the underlying *http.Request.
func New(req *http.Request) *Request {
	return &Request{
		urlEncode: true,
		params:    M{},
		req:       req,
	}
}

func newRequest(url, method string) *Request {
	return &Request{
		url:       url,
		urlEncode: true,
		params:    M{},
		req: &http.Request{
			Method:     method,
			Header:     make(http.Header),
			Proto:      "HTTP/1.1",
			ProtoMajor: 1,
			ProtoMinor: 1,
		},
	}
}

// Format implements fmt.Formatter, format the request's infomation.
func (r *Request) Format(s fmt.State, verb rune) {
	if s.Flag('+') { // include header and format pretty.
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl(), " ", r.req.Proto)
		for name, values := range r.req.Header {
			for _, value := range values {
				fmt.Fprint(s, "\n", name, ":", value)
			}
		}
		if len(r.body) > 0 {
			fmt.Fprint(s, "\n\n", string(r.body))
		}
		if verb != 'r' {
			if resp := r.Response(); resp != nil {
				fmt.Fprint(s, "\n\n")
				resp.Format(s, verb)
			}
		}
	} else if s.Flag('-') { // keep all infomations in one line.
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl())
		if len(r.body) > 0 {
			str := regBlank.ReplaceAllString(string(r.body), "")
			fmt.Fprint(s, str)
		}
		if str := r.String(); str != "" {
			str = regBlank.ReplaceAllString(str, "")
			fmt.Fprint(s, " ", str)
		}
	} else { // auto
		fmt.Fprint(s, r.req.Method, " ", r.GetUrl())
		if verb == 'r' {
			if len(r.body) > 0 {
				if bytes.IndexByte(r.body, '\n') != -1 && r.body[0] != '\n' {
					fmt.Fprint(s, "\n")
				}
				fmt.Fprint(s, string(r.body))
			}
		} else {
			respBody := r.Bytes()
			if (len(r.body) > 0 && bytes.IndexByte(r.body, '\n') != -1) || (len(respBody) > 0 && bytes.IndexByte(respBody, '\n') != -1) { // pretty format
				if len(r.body) > 0 {
					fmt.Fprint(s, "\n", string(r.body))
				}
				if len(respBody) > 0 {
					fmt.Fprint(s, "\n", string(respBody))
				}
			} else {
				if len(r.body) > 0 {
					fmt.Fprint(s, " ", string(r.body))
				}
				if len(respBody) > 0 {
					fmt.Fprint(s, " ", string(respBody))
				}
			}
		}
	}

}
<|endoftext|>


<commit_before>package goprocessctx

import (
	goprocess "github.com/jbenet/goprocess"
	context "golang.org/x/net/context"
)

// WithContext constructs and returns a Process that respects
// given context. It is the equivalent of:
//
//   func ProcessWithContext(ctx context.Context) goprocess.Process {
//     p := goprocess.WithParent(goprocess.Background())
//     CloseAfterContext(p, ctx)
//     return p
//   }
//
func WithContext(ctx context.Context) goprocess.Process {
	p := goprocess.WithParent(goprocess.Background())
	CloseAfterContext(p, ctx)
	return p
}

// WithContextAndTeardown is a helper function to set teardown at initiation
// of WithContext
func WithContextAndTeardown(ctx context.Context, tf goprocess.TeardownFunc) goprocess.Process {
	if ctx == nil {
		panic("nil Context")
	}
	p := goprocess.WithTeardown(tf)
	go func() {
		<-ctx.Done()
		p.Close()
	}()
	return p
}

// WaitForContext makes p WaitFor ctx. When Closing, p waits for
// ctx.Done(), before being Closed(). It is simply:
//
//   p.WaitFor(goprocess.WithContext(ctx))
//
func WaitForContext(ctx context.Context, p goprocess.Process) {
	p.WaitFor(WithContext(ctx))
}

// CloseAfterContext schedules the process to close after the given
// context is done. It is the equivalent of:
//
//   func CloseAfterContext(p goprocess.Process, ctx context.Context) {
//     go func() {
//       <-ctx.Done()
//       p.Close()
//     }()
//   }
//
func CloseAfterContext(p goprocess.Process, ctx context.Context) {
	if p == nil {
		panic("nil Process")
	}
	if ctx == nil {
		panic("nil Context")
	}

	go func() {
		<-ctx.Done()
		p.Close()
	}()
}

// WithProcessClosing returns a context.Context derived from ctx that
// is cancelled as p is Closing (after: <-p.Closing()). It is simply:
//
//   func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
//     ctx, cancel := context.WithCancel(ctx)
//     go func() {
//       <-p.Closing()
//       cancel()
//     }()
//     return ctx
//   }
//
func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-p.Closing()
		cancel()
	}()
	return ctx
}

// WithProcessClosed returns a context.Context that is cancelled
// after Process p is Closed. It is the equivalent of:
//
//   func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
//     ctx, cancel := context.WithCancel(ctx)
//     go func() {
//       <-p.Closed()
//       cancel()
//     }()
//     return ctx
//   }
//
func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-p.Closed()
		cancel()
	}()
	return ctx
}
<commit_msg>avoid wasting a goroutine on ctx.{Background, TODO}()<commit_after>package goprocessctx

import (
	goprocess "github.com/jbenet/goprocess"
	context "golang.org/x/net/context"
)

// WithContext constructs and returns a Process that respects
// given context. It is the equivalent of:
//
//   func ProcessWithContext(ctx context.Context) goprocess.Process {
//     p := goprocess.WithParent(goprocess.Background())
//     CloseAfterContext(p, ctx)
//     return p
//   }
//
func WithContext(ctx context.Context) goprocess.Process {
	p := goprocess.WithParent(goprocess.Background())
	CloseAfterContext(p, ctx)
	return p
}

// WithContextAndTeardown is a helper function to set teardown at initiation
// of WithContext
func WithContextAndTeardown(ctx context.Context, tf goprocess.TeardownFunc) goprocess.Process {
	if ctx == nil {
		panic("nil Context")
	}
	p := goprocess.WithTeardown(tf)
	CloseAfterContext(p, ctx)
	return p
}

// WaitForContext makes p WaitFor ctx. When Closing, p waits for
// ctx.Done(), before being Closed(). It is simply:
//
//   p.WaitFor(goprocess.WithContext(ctx))
//
func WaitForContext(ctx context.Context, p goprocess.Process) {
	p.WaitFor(WithContext(ctx))
}

// CloseAfterContext schedules the process to close after the given
// context is done. It is the equivalent of:
//
//   func CloseAfterContext(p goprocess.Process, ctx context.Context) {
//     go func() {
//       <-ctx.Done()
//       p.Close()
//     }()
//   }
//
func CloseAfterContext(p goprocess.Process, ctx context.Context) {
	if p == nil {
		panic("nil Process")
	}
	if ctx == nil {
		panic("nil Context")
	}

	// context.Background(). if ctx.Done() is nil, it will never be done.
	// we check for this to avoid wasting a goroutine forever.
	if ctx.Done() == nil {
		return
	}

	go func() {
		<-ctx.Done()
		p.Close()
	}()
}

// WithProcessClosing returns a context.Context derived from ctx that
// is cancelled as p is Closing (after: <-p.Closing()). It is simply:
//
//   func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
//     ctx, cancel := context.WithCancel(ctx)
//     go func() {
//       <-p.Closing()
//       cancel()
//     }()
//     return ctx
//   }
//
func WithProcessClosing(ctx context.Context, p goprocess.Process) context.Context {
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-p.Closing()
		cancel()
	}()
	return ctx
}

// WithProcessClosed returns a context.Context that is cancelled
// after Process p is Closed. It is the equivalent of:
//
//   func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
//     ctx, cancel := context.WithCancel(ctx)
//     go func() {
//       <-p.Closed()
//       cancel()
//     }()
//     return ctx
//   }
//
func WithProcessClosed(ctx context.Context, p goprocess.Process) context.Context {
	ctx, cancel := context.WithCancel(ctx)
	go func() {
		<-p.Closed()
		cancel()
	}()
	return ctx
}
<|endoftext|>


<commit_before>// Package set is a thread safe SET data structure implementation
// The thread safety encompasses all operations on one set.
// Operations on multiple sets are consistent in that the elements
// of each set used was valid at exactly one point in time between the
// start and the end of the operation.
package set

import (
	"fmt"
	"strings"
	"sync"
)

// Interface describing a Set. Sets are an unordered, unique list of values.
type Interface interface {
	Add(items ...interface{})
	Remove(items ...interface{})
	Pop() interface{}
	Has(items ...interface{}) bool
	Size() int
	Clear()
	IsEmpty() bool
	IsEqual(s Interface) bool
	IsSubset(s Interface) bool
	IsSuperset(s Interface) bool
	Each(func(interface{}) bool)
	String() string
	List() []interface{}
	Copy() Interface
	Union(s Interface) Interface
	Merge(s Interface)
	Separate(s Interface)
	Intersection(s Interface) Interface
	Difference(s Interface) Interface
	SymmetricDifference(s Interface) Interface
	StringSlice() []string
	IntSlice() []int
}

// SetNonTS defines a non-thread safe set data structure.
type SetNonTS struct {
	m map[interface{}]struct{} // struct{} doesn't take up space
}

// Set defines a thread safe set data structure.
type Set struct {
	SetNonTS
	l sync.RWMutex // we name it because we don't want to expose it
}

// helpful to not write everywhere struct{}{}
var keyExists = struct{}{}

// New creates and initialize a new Set. It's accept a variable number of
// arguments to populate the initial set. If nothing passed a Set with zero
// size is created.
func New(items ...interface{}) *Set {
	s := &Set{}
	s.m = make(map[interface{}]struct{})

	s.Add(items...)
	return s
}

// NewNonTS creates and initialize a new non-threadsafe Set.
// It accepts a variable number of arguments to populate the initial set.
// If nothing is passed a SetNonTS with zero size is created.
func NewNonTS(items ...interface{}) *SetNonTS {
	s := &SetNonTS{
		m: make(map[interface{}]struct{}),
	}

	s.Add(items...)
	return s
}

// Add includes the specified items (one or more) to the set. The underlying
// Set s is modified. If passed nothing it silently returns.
func (s *Set) Add(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	s.l.Lock()
	defer s.l.Unlock()

	for _, item := range items {
		s.m[item] = keyExists
	}
}

// Add includes the specified items (one or more) to the set. The underlying
// Set s is modified. If passed nothing it silently returns.
func (s *SetNonTS) Add(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	for _, item := range items {
		s.m[item] = keyExists
	}
}

// Remove deletes the specified items from the set.  The underlying Set s is
// modified. If passed nothing it silently returns.
func (s *Set) Remove(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	s.l.Lock()
	defer s.l.Unlock()

	for _, item := range items {
		delete(s.m, item)
	}
}

// Remove deletes the specified items from the set.  The underlying Set s is
// modified. If passed nothing it silently returns.
func (s *SetNonTS) Remove(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	for _, item := range items {
		delete(s.m, item)
	}
}

// Pop  deletes and return an item from the set. The underlying Set s is
// modified. If set is empty, nil is returned.
func (s *Set) Pop() interface{} {
	s.l.RLock()
	for item := range s.m {
		s.l.RUnlock()
		s.l.Lock()
		delete(s.m, item)
		s.l.Unlock()
		return item
	}
	s.l.RUnlock()
	return nil
}

// Pop  deletes and return an item from the set. The underlying Set s is
// modified. If set is empty, nil is returned.
func (s *SetNonTS) Pop() interface{} {
	for item := range s.m {
		delete(s.m, item)
		return item
	}
	return nil
}

// Has looks for the existence of items passed. It returns false if nothing is
// passed. For multiple items it returns true only if all of  the items exist.
func (s *Set) Has(items ...interface{}) bool {
	// assume checked for empty item, which not exist
	if len(items) == 0 {
		return false
	}

	s.l.RLock()
	defer s.l.RUnlock()

	has := true
	for _, item := range items {
		if _, has = s.m[item]; !has {
			break
		}
	}
	return has
}

// Has looks for the existence of items passed. It returns false if nothing is
// passed. For multiple items it returns true only if all of  the items exist.
func (s *SetNonTS) Has(items ...interface{}) bool {
	// assume checked for empty item, which not exist
	if len(items) == 0 {
		return false
	}

	has := true
	for _, item := range items {
		if _, has = s.m[item]; !has {
			break
		}
	}
	return has
}

// Size returns the number of items in a set.
func (s *Set) Size() int {
	s.l.RLock()
	defer s.l.RUnlock()

	l := len(s.m)
	return l
}

// Size returns the number of items in a set.
func (s *SetNonTS) Size() int {
	return len(s.m)
}

// Clear removes all items from the set.
func (s *Set) Clear() {
	s.l.Lock()
	defer s.l.Unlock()

	s.m = make(map[interface{}]struct{})
}

// Clear removes all items from the set.
func (s *SetNonTS) Clear() {
	s.m = make(map[interface{}]struct{})
}

// IsEmpty reports whether the Set is empty.
func (s *SetNonTS) IsEmpty() bool {
	return s.Size() == 0
}

// IsEqual test whether s and t are the same in size and have the same items.
func (s *Set) IsEqual(t Interface) bool {
	s.l.RLock()
	defer s.l.RUnlock()

	// Force locking only if given set is threadsafe.
	if conv, ok := t.(*Set); ok {
		conv.l.RLock()
		defer conv.l.RUnlock()
	}

	equal := true
	if equal = len(s.m) == t.Size(); equal {
		t.Each(func(item interface{}) (equal bool) {
			_, equal = s.m[item]
			return
		})
	}

	return equal
}

// IsEqual test whether s and t are the same in size and have the same items.
func (s *SetNonTS) IsEqual(t Interface) bool {
	// Force locking only if given set is threadsafe.
	if conv, ok := t.(*Set); ok {
		conv.l.RLock()
		defer conv.l.RUnlock()
	}

	equal := true
	if equal = len(s.m) == t.Size(); equal {
		t.Each(func(item interface{}) (equal bool) {
			_, equal = s.m[item]
			return
		})
	}

	return equal
}

// IsSubset tests whether t is a subset of s.
func (s *Set) IsSubset(t Interface) (subset bool) {
	s.l.RLock()
	defer s.l.RUnlock()

	subset = true

	t.Each(func(item interface{}) bool {
		_, subset = s.m[item]
		return subset
	})

	return
}

// IsSubset tests whether t is a subset of s.
func (s *SetNonTS) IsSubset(t Interface) (subset bool) {
	subset = true

	t.Each(func(item interface{}) bool {
		_, subset = s.m[item]
		return subset
	})

	return
}

// IsSuperset tests whether t is a superset of s.
func (s *SetNonTS) IsSuperset(t Interface) bool {
	return t.IsSubset(s)
}

// Each traverses the items in the Set, calling the provided function for each
// set member. Traversal will continue until all items in the Set have been
// visited, or if the closure returns false.
func (s *Set) Each(f func(item interface{}) bool) {
	s.l.RLock()
	defer s.l.RUnlock()

	for item := range s.m {
		if !f(item) {
			break
		}
	}
}

// Each traverses the items in the Set, calling the provided function for each
// set member. Traversal will continue until all items in the Set have been
// visited, or if the closure returns false.
func (s *SetNonTS) Each(f func(item interface{}) bool) {
	for item := range s.m {
		if !f(item) {
			break
		}
	}
}

// String returns a string representation of s
func (s *SetNonTS) String() string {
	t := make([]string, 0, len(s.List()))
	for _, item := range s.List() {
		t = append(t, fmt.Sprintf("%v", item))
	}

	return fmt.Sprintf("[%s]", strings.Join(t, ", "))
}

// List returns a slice of all items. There is also StringSlice() and
// IntSlice() methods for returning slices of type string or int.
func (s *Set) List() []interface{} {
	s.l.RLock()
	defer s.l.RUnlock()

	list := make([]interface{}, 0, len(s.m))

	for item := range s.m {
		list = append(list, item)
	}

	return list
}

// List returns a slice of all items. There is also StringSlice() and
// IntSlice() methods for returning slices of type string or int.
func (s *SetNonTS) List() []interface{} {
	list := make([]interface{}, 0, len(s.m))

	for item := range s.m {
		list = append(list, item)
	}

	return list
}

// Copy returns a new Set with a copy of s.
func (s *Set) Copy() Interface {
	return New(s.List()...)
}

// Copy returns a new Set with a copy of s.
func (s *SetNonTS) Copy() Interface {
	return NewNonTS(s.List()...)
}

// Union is the merger of two sets. It returns a new set with the element in s
// and t combined. It doesn't modify s. Use Merge() if  you want to change the
// underlying set s.
func (s *SetNonTS) Union(t Interface) Interface {
	u := s.Copy()
	u.Merge(t)
	return u
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *Set) Merge(t Interface) {
	s.l.Lock()
	defer s.l.Unlock()

	t.Each(func(item interface{}) bool {
		s.m[item] = keyExists
		return true
	})
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *SetNonTS) Merge(t Interface) {
	t.Each(func(item interface{}) bool {
		s.m[item] = keyExists
		return true
	})
}

// it's not the opposite of Merge.
// Separate removes the set items containing in t from set s. Please aware that
func (s *SetNonTS) Separate(t Interface) {
	s.Remove(t.List()...)
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *SetNonTS) Intersection(t Interface) Interface {
	u := s.Copy()
	u.Separate(u.Difference(t))
	return u
}

// Difference returns a new set which contains items which are in s but not in t.
func (s *SetNonTS) Difference(t Interface) Interface {
	u := s.Copy()
	u.Separate(t)
	return u
}

// SymmetricDifference returns a new set which s is the difference of items which are in
// one of either, but not in both.
func (s *SetNonTS) SymmetricDifference(t Interface) Interface {
	u := s.Difference(t)
	v := t.Difference(s)
	return u.Union(v)
}

// StringSlice is a helper function that returns a slice of strings of s. If
// the set contains mixed types of items only items of type string are returned.
func (s *SetNonTS) StringSlice() []string {
	slice := make([]string, 0)
	for _, item := range s.List() {
		v, ok := item.(string)
		if !ok {
			continue
		}

		slice = append(slice, v)
	}
	return slice
}

// IntSlice is a helper function that returns a slice of ints of s. If
// the set contains mixed types of items only items of type int are returned.
func (s *SetNonTS) IntSlice() []int {
	slice := make([]int, 0)
	for _, item := range s.List() {
		v, ok := item.(int)
		if !ok {
			continue
		}

		slice = append(slice, v)
	}
	return slice
}

// Union is the merger of multiple sets. It returns a new set with the
// element in combined in all sets that are passed. Unlike the Union() method
// you can use this function seperatly with multiple sets. If no items are
// passed an empty set is returned.
func Union(sets ...Interface) Interface {
	u := New()
	for _, set := range sets {
		set.Each(func(item interface{}) bool {
			u.m[item] = keyExists
			return true
		})
	}

	return u
}

// Difference returns a new set which contains items which are in in the first
// set but not in the others. Unlike the Difference() method you can use this
// function seperatly with multiple sets. If no items are passed an empty set
// is returned.
func Difference(sets ...Interface) Interface {
	if len(sets) == 0 {
		return New()
	}

	s := sets[0].Copy()
	for _, set := range sets[1:] {
		s.Separate(set) // seperate is thread safe
	}
	return s
}
<commit_msg>Introduce set, a common baseline for both non-ts and ts Set variants.<commit_after>// Package set is a thread safe SET data structure implementation
// The thread safety encompasses all operations on one set.
// Operations on multiple sets are consistent in that the elements
// of each set used was valid at exactly one point in time between the
// start and the end of the operation.
package set

import (
	"fmt"
	"strings"
	"sync"
)

// Interface describing a Set. Sets are an unordered, unique list of values.
type Interface interface {
	Add(items ...interface{})
	Remove(items ...interface{})
	Pop() interface{}
	Has(items ...interface{}) bool
	Size() int
	Clear()
	IsEmpty() bool
	IsEqual(s Interface) bool
	IsSubset(s Interface) bool
	IsSuperset(s Interface) bool
	Each(func(interface{}) bool)
	String() string
	List() []interface{}
	Copy() Interface
	Union(s Interface) Interface
	Merge(s Interface)
	Separate(s Interface)
	Intersection(s Interface) Interface
	Difference(s Interface) Interface
	SymmetricDifference(s Interface) Interface
	StringSlice() []string
	IntSlice() []int
}

// Provides a common set baseline for both threadsafe and non-ts Sets.
type set struct {
	m map[interface{}]struct{} // struct{} doesn't take up space
}

// SetNonTS defines a non-thread safe set data structure.
type SetNonTS struct {
	set
}

// Set defines a thread safe set data structure.
type Set struct {
	set
	l sync.RWMutex // we name it because we don't want to expose it
}

// helpful to not write everywhere struct{}{}
var keyExists = struct{}{}

// New creates and initialize a new Set. It's accept a variable number of
// arguments to populate the initial set. If nothing passed a Set with zero
// size is created.
func New(items ...interface{}) *Set {
	s := &Set{}
	s.m = make(map[interface{}]struct{})

	s.Add(items...)
	return s
}

// NewNonTS creates and initialize a new non-threadsafe Set.
// It accepts a variable number of arguments to populate the initial set.
// If nothing is passed a SetNonTS with zero size is created.
func NewNonTS(items ...interface{}) *SetNonTS {
	s := &SetNonTS{}
	s.m = make(map[interface{}]struct{})

	s.Add(items...)
	return s
}

// Add includes the specified items (one or more) to the set. The underlying
// Set s is modified. If passed nothing it silently returns.
func (s *Set) Add(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	s.l.Lock()
	defer s.l.Unlock()

	for _, item := range items {
		s.m[item] = keyExists
	}
}

// Add includes the specified items (one or more) to the set. The underlying
// Set s is modified. If passed nothing it silently returns.
func (s *set) Add(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	for _, item := range items {
		s.m[item] = keyExists
	}
}

// Remove deletes the specified items from the set.  The underlying Set s is
// modified. If passed nothing it silently returns.
func (s *Set) Remove(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	s.l.Lock()
	defer s.l.Unlock()

	for _, item := range items {
		delete(s.m, item)
	}
}

// Remove deletes the specified items from the set.  The underlying Set s is
// modified. If passed nothing it silently returns.
func (s *set) Remove(items ...interface{}) {
	if len(items) == 0 {
		return
	}

	for _, item := range items {
		delete(s.m, item)
	}
}

// Pop  deletes and return an item from the set. The underlying Set s is
// modified. If set is empty, nil is returned.
func (s *Set) Pop() interface{} {
	s.l.RLock()
	for item := range s.m {
		s.l.RUnlock()
		s.l.Lock()
		delete(s.m, item)
		s.l.Unlock()
		return item
	}
	s.l.RUnlock()
	return nil
}

// Pop  deletes and return an item from the set. The underlying Set s is
// modified. If set is empty, nil is returned.
func (s *set) Pop() interface{} {
	for item := range s.m {
		delete(s.m, item)
		return item
	}
	return nil
}

// Has looks for the existence of items passed. It returns false if nothing is
// passed. For multiple items it returns true only if all of  the items exist.
func (s *Set) Has(items ...interface{}) bool {
	// assume checked for empty item, which not exist
	if len(items) == 0 {
		return false
	}

	s.l.RLock()
	defer s.l.RUnlock()

	has := true
	for _, item := range items {
		if _, has = s.m[item]; !has {
			break
		}
	}
	return has
}

// Has looks for the existence of items passed. It returns false if nothing is
// passed. For multiple items it returns true only if all of  the items exist.
func (s *set) Has(items ...interface{}) bool {
	// assume checked for empty item, which not exist
	if len(items) == 0 {
		return false
	}

	has := true
	for _, item := range items {
		if _, has = s.m[item]; !has {
			break
		}
	}
	return has
}

// Size returns the number of items in a set.
func (s *Set) Size() int {
	s.l.RLock()
	defer s.l.RUnlock()

	l := len(s.m)
	return l
}

// Size returns the number of items in a set.
func (s *set) Size() int {
	return len(s.m)
}

// Clear removes all items from the set.
func (s *Set) Clear() {
	s.l.Lock()
	defer s.l.Unlock()

	s.m = make(map[interface{}]struct{})
}

// Clear removes all items from the set.
func (s *set) Clear() {
	s.m = make(map[interface{}]struct{})
}

// IsEmpty reports whether the Set is empty.
func (s *set) IsEmpty() bool {
	return s.Size() == 0
}

// IsEqual test whether s and t are the same in size and have the same items.
func (s *Set) IsEqual(t Interface) bool {
	s.l.RLock()
	defer s.l.RUnlock()

	// Force locking only if given set is threadsafe.
	if conv, ok := t.(*Set); ok {
		conv.l.RLock()
		defer conv.l.RUnlock()
	}

	equal := true
	if equal = len(s.m) == t.Size(); equal {
		t.Each(func(item interface{}) (equal bool) {
			_, equal = s.m[item]
			return
		})
	}

	return equal
}

// IsEqual test whether s and t are the same in size and have the same items.
func (s *set) IsEqual(t Interface) bool {
	// Force locking only if given set is threadsafe.
	if conv, ok := t.(*Set); ok {
		conv.l.RLock()
		defer conv.l.RUnlock()
	}

	equal := true
	if equal = len(s.m) == t.Size(); equal {
		t.Each(func(item interface{}) (equal bool) {
			_, equal = s.m[item]
			return
		})
	}

	return equal
}

// IsSubset tests whether t is a subset of s.
func (s *Set) IsSubset(t Interface) (subset bool) {
	s.l.RLock()
	defer s.l.RUnlock()

	subset = true

	t.Each(func(item interface{}) bool {
		_, subset = s.m[item]
		return subset
	})

	return
}

// IsSubset tests whether t is a subset of s.
func (s *set) IsSubset(t Interface) (subset bool) {
	subset = true

	t.Each(func(item interface{}) bool {
		_, subset = s.m[item]
		return subset
	})

	return
}

// IsSuperset tests whether t is a superset of s.
func (s *set) IsSuperset(t Interface) bool {
	return t.IsSubset(s)
}

// Each traverses the items in the Set, calling the provided function for each
// set member. Traversal will continue until all items in the Set have been
// visited, or if the closure returns false.
func (s *Set) Each(f func(item interface{}) bool) {
	s.l.RLock()
	defer s.l.RUnlock()

	for item := range s.m {
		if !f(item) {
			break
		}
	}
}

// Each traverses the items in the Set, calling the provided function for each
// set member. Traversal will continue until all items in the Set have been
// visited, or if the closure returns false.
func (s *set) Each(f func(item interface{}) bool) {
	for item := range s.m {
		if !f(item) {
			break
		}
	}
}

// String returns a string representation of s
func (s *set) String() string {
	t := make([]string, 0, len(s.List()))
	for _, item := range s.List() {
		t = append(t, fmt.Sprintf("%v", item))
	}

	return fmt.Sprintf("[%s]", strings.Join(t, ", "))
}

// List returns a slice of all items. There is also StringSlice() and
// IntSlice() methods for returning slices of type string or int.
func (s *Set) List() []interface{} {
	s.l.RLock()
	defer s.l.RUnlock()

	list := make([]interface{}, 0, len(s.m))

	for item := range s.m {
		list = append(list, item)
	}

	return list
}

// List returns a slice of all items. There is also StringSlice() and
// IntSlice() methods for returning slices of type string or int.
func (s *set) List() []interface{} {
	list := make([]interface{}, 0, len(s.m))

	for item := range s.m {
		list = append(list, item)
	}

	return list
}

// Copy returns a new Set with a copy of s.
func (s *Set) Copy() Interface {
	return New(s.List()...)
}

// Copy returns a new Set with a copy of s.
func (s *set) Copy() Interface {
	return NewNonTS(s.List()...)
}

// Union is the merger of two sets. It returns a new set with the element in s
// and t combined. It doesn't modify s. Use Merge() if  you want to change the
// underlying set s.
func (s *set) Union(t Interface) Interface {
	u := s.Copy()
	u.Merge(t)
	return u
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *Set) Merge(t Interface) {
	s.l.Lock()
	defer s.l.Unlock()

	t.Each(func(item interface{}) bool {
		s.m[item] = keyExists
		return true
	})
}

// Merge is like Union, however it modifies the current set it's applied on
// with the given t set.
func (s *set) Merge(t Interface) {
	t.Each(func(item interface{}) bool {
		s.m[item] = keyExists
		return true
	})
}

// it's not the opposite of Merge.
// Separate removes the set items containing in t from set s. Please aware that
func (s *set) Separate(t Interface) {
	s.Remove(t.List()...)
}

// Intersection returns a new set which contains items which is in both s and t.
func (s *set) Intersection(t Interface) Interface {
	u := s.Copy()
	u.Separate(u.Difference(t))
	return u
}

// Difference returns a new set which contains items which are in s but not in t.
func (s *set) Difference(t Interface) Interface {
	u := s.Copy()
	u.Separate(t)
	return u
}

// SymmetricDifference returns a new set which s is the difference of items which are in
// one of either, but not in both.
func (s *set) SymmetricDifference(t Interface) Interface {
	u := s.Difference(t)
	v := t.Difference(s)
	return u.Union(v)
}

// StringSlice is a helper function that returns a slice of strings of s. If
// the set contains mixed types of items only items of type string are returned.
func (s *set) StringSlice() []string {
	slice := make([]string, 0)
	for _, item := range s.List() {
		v, ok := item.(string)
		if !ok {
			continue
		}

		slice = append(slice, v)
	}
	return slice
}

// IntSlice is a helper function that returns a slice of ints of s. If
// the set contains mixed types of items only items of type int are returned.
func (s *set) IntSlice() []int {
	slice := make([]int, 0)
	for _, item := range s.List() {
		v, ok := item.(int)
		if !ok {
			continue
		}

		slice = append(slice, v)
	}
	return slice
}

// Union is the merger of multiple sets. It returns a new set with the
// element in combined in all sets that are passed. Unlike the Union() method
// you can use this function seperatly with multiple sets. If no items are
// passed an empty set is returned.
func Union(sets ...Interface) Interface {
	u := New()
	for _, set := range sets {
		set.Each(func(item interface{}) bool {
			u.m[item] = keyExists
			return true
		})
	}

	return u
}

// Difference returns a new set which contains items which are in in the first
// set but not in the others. Unlike the Difference() method you can use this
// function seperatly with multiple sets. If no items are passed an empty set
// is returned.
func Difference(sets ...Interface) Interface {
	if len(sets) == 0 {
		return New()
	}

	s := sets[0].Copy()
	for _, set := range sets[1:] {
		s.Separate(set) // seperate is thread safe
	}
	return s
}
<|endoftext|>


<commit_before>package chatroom

import (
	"chant/app/factory"
	"chant/app/models"
	"container/list"
	"time"
	// "github.com/revel/revel"

	"github.com/otiai10/rodeo"
)

// Event ...
type Event struct {
	Type      string // "join", "leave", or "message"
	User      *models.User
	Timestamp int    // Unix timestamp (secs)
	Text      string // What the user said (if Type == "message")
	RoomInfo  *Info
}

// Subscription ...
type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Info ...
type Info struct {
	Users    map[string]*models.User
	Updated  bool
	AllUsers *list.List
}

// Cancel Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

// NewEvent ...
func NewEvent(typ string, user *models.User, msg string) Event {
	return Event{
		typ,
		user,
		int(time.Now().Unix()),
		msg,
		info,
	}
}

// NewKeepAlive ...
func NewKeepAlive() Event {
	return Event{
		"keepalive",
		&models.User{},
		int(time.Now().Unix()),
		"",
		info,
	}
}

// Subscribe ...
func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

// Join ...
func Join(user *models.User) {
	publish <- NewEvent("join", user, "")
}

// Say ...
func Say(user *models.User, message string) {
	publish <- NewEvent("message", user, message)
}

// Leave ...
func Leave(user *models.User) {
	publish <- NewEvent("leave", user, "")
}

const archiveSize = 4
const soundArchiveSize = 21
const stampArchiveSize = 18

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 1000)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 1000)
	// Send events here to publish them.
	publish = make(chan Event, 1000)

	keepalive = time.Tick(50 * time.Second)

	info = &Info{
		make(map[string]*models.User),
		true,
		list.New(),
	}

	// SoundTrack ...
	SoundTrack = list.New()
	// StampArchive ...
	StampArchive = []models.Stamp{}

	vaquero    *rodeo.Vaquero
	persistent = false
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			// {{{ クソ
			event.RoomInfo.Updated = false
			if event.Type == "join" {
				info.AllUsers.PushBack(event.User)
				event.RoomInfo.Updated = true
			}
			if event.Type == "leave" {
				// delete(info.Users, event.User.ScreenName)
				leaveUser(event.User)
				event.RoomInfo.Updated = true
			}
			restoreRoomUsers()
			event.RoomInfo = info
			// }}}
			if event.Type == "message" {
				sound, soundError := factory.SoundFromText(event.Text, event.User)
				if soundError == nil {
					//fmt.Printf("このサウンドをアーカイブ:\t%+v\n", sound)
					if SoundTrack.Len() >= soundArchiveSize {
						SoundTrack.Remove(SoundTrack.Front())
					}
					SoundTrack.PushBack(sound)
				} else {
					// revel.ERROR.Println("たぶんここ？", soundError)
				}
				if stamp, err := factory.StampFromText(event.Text); err == nil {
					if stamp.IsUsedEvent {
						event.Type = "message"
						event.Text = stamp.Value
					}
					addStamp(stamp)
				}
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			if event.Type != "leave" && event.Type != "join" {
				archive.PushBack(event)
			}

			// Finally, subscribe
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				ch.Value.(chan Event) <- event
			}
		case <-keepalive:
			for subscriber := subscribers.Front(); subscriber != nil; subscriber = subscriber.Next() {
				subscriber.Value.(chan Event) <- NewKeepAlive()
			}

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

func leaveUser(user *models.User) {
	for u := info.AllUsers.Front(); u != nil; u = u.Next() {
		if u.Value.(*models.User).ScreenName == user.ScreenName {
			// delete only one
			_ = info.AllUsers.Remove(u)
			return
		}
	}
}
func restoreRoomUsers() {
	// TODO: DRY
	info.Users = make(map[string]*models.User)
	for u := info.AllUsers.Front(); u != nil; u = u.Next() {
		user := u.Value.(*models.User)
		info.Users[user.ScreenName] = user
	}
}
func init() {
	RestoreStamps()
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}

// RestoreStamps restores stamp archive from Database.
func RestoreStamps() {
	var err error
	if vaquero, err = rodeo.NewVaquero("localhost", "6379"); err != nil {
		return
	}
	persistent = true
	vaquero.Cast("chant.stamps", &StampArchive)
}

// SaveStamps saves stamps
func SaveStamps() {
	vaquero.Store("chant.stamps", StampArchive)
	return
}

// addStamp adds stamp to archive, sort them by LRU and delete overflow
func addStamp(stamp models.Stamp) {
	// filter first
	pool := []models.Stamp{}
	for _, s := range StampArchive {
		if s.Value != stamp.Value {
			pool = append(pool, s)
		}
	}
	// append new
	StampArchive = append(pool, stamp)
	// cut head
	if len(StampArchive) > stampArchiveSize {
		StampArchive = StampArchive[len(StampArchive)-stampArchiveSize:]
	}
	// FIXME: ここで毎回呼ぶのはクソ
	if persistent {
		SaveStamps()
	}
}

// GetStampArchive returns stamp archives sorted by LRU
func GetStampArchive() []models.Stamp {
	return StampArchive
}
<commit_msg>publish by goroutine<commit_after>package chatroom

import (
	"chant/app/factory"
	"chant/app/models"
	"container/list"
	"log"
	"time"
	// "github.com/revel/revel"

	"github.com/otiai10/rodeo"
)

// Event ...
type Event struct {
	Type      string // "join", "leave", or "message"
	User      *models.User
	Timestamp int    // Unix timestamp (secs)
	Text      string // What the user said (if Type == "message")
	RoomInfo  *Info
}

// Subscription ...
type Subscription struct {
	Archive []Event      // All the events from the archive.
	New     <-chan Event // New events coming in.
}

// Info ...
type Info struct {
	Users    map[string]*models.User
	Updated  bool
	AllUsers *list.List
}

// Cancel Owner of a subscription must cancel it when they stop listening to events.
func (s Subscription) Cancel() {
	unsubscribe <- s.New // Unsubscribe the channel.
	drain(s.New)         // Drain it, just in case there was a pending publish.
}

// NewEvent ...
func NewEvent(typ string, user *models.User, msg string) Event {
	return Event{
		typ,
		user,
		int(time.Now().Unix()),
		msg,
		info,
	}
}

// NewKeepAlive ...
func NewKeepAlive() Event {
	return Event{
		"keepalive",
		&models.User{},
		int(time.Now().Unix()),
		"",
		info,
	}
}

// Subscribe ...
func Subscribe() Subscription {
	resp := make(chan Subscription)
	subscribe <- resp
	return <-resp
}

// Join ...
func Join(user *models.User) {
	publish <- NewEvent("join", user, "")
}

// Say ...
func Say(user *models.User, message string) {
	publish <- NewEvent("message", user, message)
}

// Leave ...
func Leave(user *models.User) {
	publish <- NewEvent("leave", user, "")
}

const archiveSize = 4
const soundArchiveSize = 21
const stampArchiveSize = 25

var (
	// Send a channel here to get room events back.  It will send the entire
	// archive initially, and then new messages as they come in.
	subscribe = make(chan (chan<- Subscription), 1000)
	// Send a channel here to unsubscribe.
	unsubscribe = make(chan (<-chan Event), 1000)
	// Send events here to publish them.
	publish = make(chan Event, 1000)

	keepalive = time.Tick(50 * time.Second)

	info = &Info{
		make(map[string]*models.User),
		true,
		list.New(),
	}

	// SoundTrack ...
	SoundTrack = list.New()
	// StampArchive ...
	StampArchive = []models.Stamp{}

	vaquero    *rodeo.Vaquero
	persistent = false
)

// This function loops forever, handling the chat room pubsub
func chatroom() {
	archive := list.New()
	subscribers := list.New()

	for {
		select {
		case ch := <-subscribe:
			var events []Event
			for e := archive.Front(); e != nil; e = e.Next() {
				events = append(events, e.Value.(Event))
			}
			subscriber := make(chan Event, 10)
			subscribers.PushBack(subscriber)
			ch <- Subscription{events, subscriber}

		case event := <-publish:
			// {{{ クソ
			event.RoomInfo.Updated = false
			if event.Type == "join" {
				info.AllUsers.PushBack(event.User)
				event.RoomInfo.Updated = true
			}
			if event.Type == "leave" {
				// delete(info.Users, event.User.ScreenName)
				leaveUser(event.User)
				event.RoomInfo.Updated = true
			}
			restoreRoomUsers()
			event.RoomInfo = info
			// }}}
			if event.Type == "message" {
				sound, soundError := factory.SoundFromText(event.Text, event.User)
				if soundError == nil {
					//fmt.Printf("このサウンドをアーカイブ:\t%+v\n", sound)
					if SoundTrack.Len() >= soundArchiveSize {
						SoundTrack.Remove(SoundTrack.Front())
					}
					SoundTrack.PushBack(sound)
				} else {
					// revel.ERROR.Println("たぶんここ？", soundError)
				}
				if stamp, err := factory.StampFromText(event.Text); err == nil {
					if stamp.IsUsedEvent {
						event.Type = "message"
						event.Text = stamp.Value
					}
					addStamp(stamp)
				}
			}
			if archive.Len() >= archiveSize {
				archive.Remove(archive.Front())
			}
			if event.Type != "leave" && event.Type != "join" {
				archive.PushBack(event)
			}

			// Finally, subscribe
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				log.Println("[process]", "102", "時間くってる気がする")
				if sub, ok := ch.Value.(chan Event); ok {
					go func(sub chan Event, event Event) {
						sub <- event
						log.Println("[process]", "104", "goroutineにしてみた")
					}(sub, event)
				}
				log.Println("[process]", "103", "listの単位終わり")
			}
		case <-keepalive:
			for subscriber := subscribers.Front(); subscriber != nil; subscriber = subscriber.Next() {
				subscriber.Value.(chan Event) <- NewKeepAlive()
			}

		case unsub := <-unsubscribe:
			for ch := subscribers.Front(); ch != nil; ch = ch.Next() {
				if ch.Value.(chan Event) == unsub {
					subscribers.Remove(ch)
					break
				}
			}
		}
	}
}

func leaveUser(user *models.User) {
	for u := info.AllUsers.Front(); u != nil; u = u.Next() {
		if u.Value.(*models.User).ScreenName == user.ScreenName {
			// delete only one
			_ = info.AllUsers.Remove(u)
			return
		}
	}
}
func restoreRoomUsers() {
	// TODO: DRY
	info.Users = make(map[string]*models.User)
	for u := info.AllUsers.Front(); u != nil; u = u.Next() {
		user := u.Value.(*models.User)
		info.Users[user.ScreenName] = user
	}
}
func init() {
	RestoreStamps()
	go chatroom()
}

// Helpers

// Drains a given channel of any messages.
func drain(ch <-chan Event) {
	for {
		select {
		case _, ok := <-ch:
			if !ok {
				return
			}
		default:
			return
		}
	}
}

// RestoreStamps restores stamp archive from Database.
func RestoreStamps() {
	var err error
	if vaquero, err = rodeo.NewVaquero("localhost", "6379"); err != nil {
		return
	}
	persistent = true
	vaquero.Cast("chant.stamps", &StampArchive)
}

// SaveStamps saves stamps
func SaveStamps() {
	vaquero.Store("chant.stamps", StampArchive)
	return
}

// addStamp adds stamp to archive, sort them by LRU and delete overflow
func addStamp(stamp models.Stamp) {
	// filter first
	pool := []models.Stamp{}
	for _, s := range StampArchive {
		if s.Value != stamp.Value {
			pool = append(pool, s)
		}
	}
	// append new
	StampArchive = append(pool, stamp)
	// cut head
	if len(StampArchive) > stampArchiveSize {
		StampArchive = StampArchive[len(StampArchive)-stampArchiveSize:]
	}
	// FIXME: ここで毎回呼ぶのはクソ
	if persistent {
		SaveStamps()
	}
}

// GetStampArchive returns stamp archives sorted by LRU
func GetStampArchive() []models.Stamp {
	return StampArchive
}
<|endoftext|>


<commit_before>package chatroom

import (
	"container/list"
	"crypto/md5"
	"time"

	"chant/app/chatroom/bot"
	"chant/app/models"
	"chant/app/repository"

	"log"

	"fmt"
)

const (
	bufsize = 100
	// PrivilegeAPIToken ああつらい
	PrivilegeAPIToken = "tmp_X-API"
)

// Name - *Room のハッシュテーブル
var rooms = map[string]*Room{}

// Room ひとつの名前を持った部屋に対応
type Room struct {
	Name        string
	Token       string
	entrance    chan Subscription
	exit        chan Subscription
	publish     chan *models.Event
	terminate   chan interface{}
	subscribers *list.List
	members     *list.List
	Repo        *repository.Client
	Bot         *models.User
}

// Serve Roomごとに部屋を開く. foreverなgoroutineをつくる.
func (room *Room) Serve() {
	for {
		select {
		// Roomは、
		// エントランスからsubscription依頼が来たら
		// subscriptionをRoomのsubscribersに登録しつつ
		// 受信用のチャンネルを返してあげる必要がある.
		case sub := <-room.entrance:
			room.subscribers.PushBack(sub)
			// Roomは、
		// 出口にsubscriptionを投げられたら
		// subscriptionをRoomのsubscribersから抹消する必要がある.

		case sub := <-room.exit:
			for one := room.subscribers.Front(); one != nil; one = one.Next() {
				if the, ok := one.Value.(Subscription); ok && the == sub {
					room.subscribers.Remove(one)
				}
			}
			// unhandlableなsubscriptionが残ってると、そいつのせいでblockするので
			// exitに来たchanは必ずdrainにかけなければならない
			// XXX: これ、room.subscribers.Removeの前にinvokeしてもいいんじゃね？
			room.drain(sub)
		// Roomは、
		// publish用のチャンネルに新しいイベントを流し込まれたとき、
		// そのイベントを登録されている全Subscribersに配信する必要がある.
		case ev := <-room.publish:
			for one := room.subscribers.Front(); one != nil; one = one.Next() {
				if the, ok := one.Value.(Subscription); ok {
					// 誰かへのpublishが詰まっても、全体として詰まらないようにするため、
					// 各人へのpublishはgoroutineにして独立させる.
					// 逆にいえば、ここはsub.Newへの流し込みが、チャンネルの先でhandle仕切れてない
					// ので詰まり現象が発生しているのではないかと推測している.
					go func(sub Subscription, ev *models.Event) {
						start := time.Now()
						sub.New <- ev
						log.Printf("[publish]\t%v\t%v\tto:%s\n", time.Now().Sub(start), ev.Type, sub.ID)
					}(the, ev)
				}
			}
		// Roomは、
		// なんらかの不具合があったときに
		// foreverなルーチンを終了して死ぬ.
		case cause := <-room.terminate:
			log.Println(cause)
			return
		}
	}
}

func newRoom(id string) *Room {
	room := &Room{
		Name:        id,
		Token:       newtoken(id),
		entrance:    make(chan Subscription, bufsize),
		exit:        make(chan Subscription, bufsize),
		publish:     make(chan *models.Event, bufsize),
		subscribers: list.New(),
		members:     list.New(),
		Repo:        repository.NewRepoClient(id),
		Bot:         bot.DefaultBot(),
	}
	go room.Serve()
	return room
}

// Exists APIからのコールで無駄にRoom立てるんじゃねえよ
func Exists(id string) bool {
	_, ok := rooms[id]
	return ok
}

// GetRoom id(Name)からRoomをひいてくる.
// 指定されなければdefaultを採用する.
func GetRoom(id, token string) *Room {
	room := getRoom(id)
	// うーん、この
	if id == "default" {
		return room
	}
	if token != PrivilegeAPIToken && token != room.Token {
		return nil
	}
	return room
}

func getRoom(id string) *Room {
	room, ok := rooms[id]
	if !ok || room == nil {
		room = newRoom(id)
		rooms[id] = room
	}
	return room
}

// GetRoomByPassword ...
func GetRoomByPassword(id, password string) *Room {
	if id == "default" {
		return getRoom(id)
	}
	return nil
}

// Subscribe は呼ばれると、このroomのsubscribersに登録されたsubscriptionが提供される。
func (room *Room) Subscribe(user *models.User) Subscription {
	subscription := Subscription{
		ID:  user.ScreenName,
		New: make(chan *models.Event),
	}
	room.entrance <- subscription
	return subscription
}

// Unsubscribe は呼ばれると、このroomのsubscribersから抜ける（べき）。
func (room *Room) Unsubscribe(subscription Subscription) {
	room.exit <- subscription
	room.drain(subscription)
}

// 不要になったsubscriptionからの流し込みを受けるだけのメソッド。
func (room *Room) drain(subscription Subscription) {
	for {
		select {
		case _, ok := <-subscription.New:
			if !ok {
				return
			}
		default:
			return
		}
	}
}

// Subscription 新しいイベントを伝えるためのチャンネルラッパー
type Subscription struct {
	ID  string             // サブスクリプションに名前をつけましょ
	New chan *models.Event // 新しいイベントをこのsubscriberに伝えるチャンネル
}

// Say Roomへの発言の窓口となるメソッド.
// このイベンントをアーカイブするか否かはここで判断する.
// Controllerからしか呼んではいけない. (so far)
// TODO: アプリケーションサーバでエラーが起きたときに、Roomが自発的に呼ぶかも？
func (room *Room) Say(user *models.User, msg string) (*models.Event, error) {
	event, err := models.ConstructEvent(user, msg)
	if err != nil {
		fmt.Println("construct event error", err)
		// TODO: なんかする
		return event, err
	}
	room.ArchiveEvent(event)
	room.publish <- event

	// {{{
	go func() {
		if response := room.BotHandle(event); response != nil {
			room.ArchiveEvent(response)
			room.publish <- response
		}
	}()
	// }}}

	return event, nil
}

// Join ユーザがこのRoomにJoinしてきたときの処理をすべて行う.
// Subscribeでsubscriptionの登録はできてるのだから、joinイベントの発行しかしてない気がする
func (room *Room) Join(user *models.User) *models.Event {
	room.members.PushBack(user)
	event := new(models.Event)
	event.User = user
	event.Type = models.JOIN
	event.Value = room.getUniqueUsers()
	room.publish <- event
	return event
}

// Leave ユーザが接続を切ったりしたときに退出する処理をすべて行う.
func (room *Room) Leave(user *models.User) {
	room.removeOneUser(user)
	event := new(models.Event)
	event.User = user
	event.Type = models.LEAVE
	event.Value = room.getUniqueUsers()
	room.publish <- event
}

func (room *Room) getUniqueUsers() map[string]*models.User {
	res := map[string]*models.User{}
	for e := room.members.Front(); e != nil; e = e.Next() {
		user := e.Value.(*models.User)
		res[user.IDstr] = user
	}
	return res
}

func (room *Room) removeOneUser(user *models.User) {
	for e := room.members.Front(); e != nil; e = e.Next() {
		if the := e.Value.(*models.User); the.IDstr == user.IDstr {
			room.members.Remove(e)
			return
		}
	}
}

// ArchiveEvent ...
func (room *Room) ArchiveEvent(event *models.Event) {
	switch event.Type {
	case models.MESSAGE, models.AMESH:
		room.Repo.PushMessage(event)
	case models.STAMPRIZE, models.STAMPUSE:
		room.Repo.PushMessage(event)
		room.Repo.PushStamp(event)
	}
}

func newtoken(id string) string {
	a := md5.New().Sum([]byte(id + time.Now().String()))
	return fmt.Sprintf("%x", a)
}

//BotHandle ...
func (room *Room) BotHandle(event *models.Event) *models.Event {
	for _, h := range bot.Handlers {
		if h.Match(event) {
			return h.Handle(event, room.Bot)
		}
	}
	return nil
}
<commit_msg>Fix #193<commit_after>package chatroom

import (
	"container/list"
	"crypto/md5"
	"time"

	"chant/app/chatroom/bot"
	"chant/app/models"
	"chant/app/repository"

	"log"

	"fmt"
)

const (
	bufsize = 100
	// PrivilegeAPIToken ああつらい
	PrivilegeAPIToken = "tmp_X-API"
)

// Name - *Room のハッシュテーブル
var rooms = map[string]*Room{}

// Room ひとつの名前を持った部屋に対応
type Room struct {
	Name        string
	Token       string
	entrance    chan Subscription
	exit        chan Subscription
	publish     chan *models.Event
	terminate   chan interface{}
	subscribers *list.List
	members     *list.List
	Repo        *repository.Client
	Bot         *models.User
}

// Serve Roomごとに部屋を開く. foreverなgoroutineをつくる.
func (room *Room) Serve() {
	for {
		select {
		// Roomは、
		// エントランスからsubscription依頼が来たら
		// subscriptionをRoomのsubscribersに登録しつつ
		// 受信用のチャンネルを返してあげる必要がある.
		case sub := <-room.entrance:
			room.subscribers.PushBack(sub)
			// Roomは、
		// 出口にsubscriptionを投げられたら
		// subscriptionをRoomのsubscribersから抹消する必要がある.

		case sub := <-room.exit:
			for one := room.subscribers.Front(); one != nil; one = one.Next() {
				if the, ok := one.Value.(Subscription); ok && the == sub {
					room.subscribers.Remove(one)
				}
			}
			// unhandlableなsubscriptionが残ってると、そいつのせいでblockするので
			// exitに来たchanは必ずdrainにかけなければならない
			// XXX: これ、room.subscribers.Removeの前にinvokeしてもいいんじゃね？
			room.drain(sub)
		// Roomは、
		// publish用のチャンネルに新しいイベントを流し込まれたとき、
		// そのイベントを登録されている全Subscribersに配信する必要がある.
		case ev := <-room.publish:
			for one := room.subscribers.Front(); one != nil; one = one.Next() {
				if the, ok := one.Value.(Subscription); ok {
					// 誰かへのpublishが詰まっても、全体として詰まらないようにするため、
					// 各人へのpublishはgoroutineにして独立させる.
					// 逆にいえば、ここはsub.Newへの流し込みが、チャンネルの先でhandle仕切れてない
					// ので詰まり現象が発生しているのではないかと推測している.
					go func(sub Subscription, ev *models.Event) {
						// start := time.Now()
						sub.New <- ev
						// log.Printf("[publish]\t%v\t%v\tto:%s\n", time.Now().Sub(start), ev.Type, sub.ID)
					}(the, ev)
				}
			}
		// Roomは、
		// なんらかの不具合があったときに
		// foreverなルーチンを終了して死ぬ.
		case cause := <-room.terminate:
			log.Println(cause)
			return
		}
	}
}

func newRoom(id string) *Room {
	room := &Room{
		Name:        id,
		Token:       newtoken(id),
		entrance:    make(chan Subscription, bufsize),
		exit:        make(chan Subscription, bufsize),
		publish:     make(chan *models.Event, bufsize),
		subscribers: list.New(),
		members:     list.New(),
		Repo:        repository.NewRepoClient(id),
		Bot:         bot.DefaultBot(),
	}
	go room.Serve()
	return room
}

// Exists APIからのコールで無駄にRoom立てるんじゃねえよ
func Exists(id string) bool {
	_, ok := rooms[id]
	return ok
}

// GetRoom id(Name)からRoomをひいてくる.
// 指定されなければdefaultを採用する.
func GetRoom(id, token string) *Room {
	room := getRoom(id)
	// うーん、この
	if id == "default" {
		return room
	}
	if token != PrivilegeAPIToken && token != room.Token {
		return nil
	}
	return room
}

func getRoom(id string) *Room {
	room, ok := rooms[id]
	if !ok || room == nil {
		room = newRoom(id)
		rooms[id] = room
	}
	return room
}

// GetRoomByPassword ...
func GetRoomByPassword(id, password string) *Room {
	if id == "default" {
		return getRoom(id)
	}
	return nil
}

// Subscribe は呼ばれると、このroomのsubscribersに登録されたsubscriptionが提供される。
func (room *Room) Subscribe(user *models.User) Subscription {
	subscription := Subscription{
		ID:  user.ScreenName,
		New: make(chan *models.Event),
	}
	room.entrance <- subscription
	return subscription
}

// Unsubscribe は呼ばれると、このroomのsubscribersから抜ける（べき）。
func (room *Room) Unsubscribe(subscription Subscription) {
	room.exit <- subscription
	room.drain(subscription)
}

// 不要になったsubscriptionからの流し込みを受けるだけのメソッド。
func (room *Room) drain(subscription Subscription) {
	for {
		select {
		case _, ok := <-subscription.New:
			if !ok {
				return
			}
		default:
			return
		}
	}
}

// Subscription 新しいイベントを伝えるためのチャンネルラッパー
type Subscription struct {
	ID  string             // サブスクリプションに名前をつけましょ
	New chan *models.Event // 新しいイベントをこのsubscriberに伝えるチャンネル
}

// Say Roomへの発言の窓口となるメソッド.
// このイベンントをアーカイブするか否かはここで判断する.
// Controllerからしか呼んではいけない. (so far)
// TODO: アプリケーションサーバでエラーが起きたときに、Roomが自発的に呼ぶかも？
func (room *Room) Say(user *models.User, msg string) (*models.Event, error) {
	event, err := models.ConstructEvent(user, msg)
	if err != nil {
		fmt.Println("construct event error", err)
		// TODO: なんかする
		return event, err
	}
	room.ArchiveEvent(event)
	room.publish <- event

	// {{{
	go func() {
		if response := room.BotHandle(event); response != nil {
			room.ArchiveEvent(response)
			room.publish <- response
		}
	}()
	// }}}

	return event, nil
}

// Join ユーザがこのRoomにJoinしてきたときの処理をすべて行う.
// Subscribeでsubscriptionの登録はできてるのだから、joinイベントの発行しかしてない気がする
func (room *Room) Join(user *models.User) *models.Event {
	room.members.PushBack(user)
	event := new(models.Event)
	event.User = user
	event.Type = models.JOIN
	event.Value = room.getUniqueUsers()
	room.publish <- event
	return event
}

// Leave ユーザが接続を切ったりしたときに退出する処理をすべて行う.
func (room *Room) Leave(user *models.User) {
	room.removeOneUser(user)
	event := new(models.Event)
	event.User = user
	event.Type = models.LEAVE
	event.Value = room.getUniqueUsers()
	room.publish <- event
}

func (room *Room) getUniqueUsers() map[string]*models.User {
	res := map[string]*models.User{}
	for e := room.members.Front(); e != nil; e = e.Next() {
		user := e.Value.(*models.User)
		res[user.IDstr] = user
	}
	return res
}

func (room *Room) removeOneUser(user *models.User) {
	for e := room.members.Front(); e != nil; e = e.Next() {
		if the := e.Value.(*models.User); the.IDstr == user.IDstr {
			room.members.Remove(e)
			return
		}
	}
}

// ArchiveEvent ...
func (room *Room) ArchiveEvent(event *models.Event) {
	switch event.Type {
	case models.MESSAGE, models.AMESH:
		room.Repo.PushMessage(event)
	case models.STAMPRIZE, models.STAMPUSE:
		room.Repo.PushMessage(event)
		room.Repo.PushStamp(event)
	}
}

func newtoken(id string) string {
	a := md5.New().Sum([]byte(id + time.Now().String()))
	return fmt.Sprintf("%x", a)
}

//BotHandle ...
func (room *Room) BotHandle(event *models.Event) *models.Event {
	for _, h := range bot.Handlers {
		if h.Match(event) {
			return h.Handle(event, room.Bot)
		}
	}
	return nil
}
<|endoftext|>