<commit_before>/*
 *  Licensed to Wikifeat under one or more contributor license agreements.
 *  See the LICENSE.txt file distributed with this work for additional information
 *  regarding copyright ownership.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  * Neither the name of Wikifeat nor the names of its contributors may be used
 *  to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

package user_service

import (
	"github.com/rhinoman/wikifeat/Godeps/_workspace/src/github.com/emicklei/go-restful"
	. "github.com/rhinoman/wikifeat/common/entities"
	. "github.com/rhinoman/wikifeat/common/services"
	"github.com/rhinoman/wikifeat/common/util"
	"io"
	"net/http"
	"strings"
)

type AvatarController struct{}

var avatarUri = "/{user-id}/avatar"

type avatarLinks struct {
	HatLinks
	SaveImage          *HatLink `json:"saveImage,omitempty"`
	GetLargeAvatar     *HatLink `json:"getLargeAvatar,omitempty"`
	GetThumbnailAvatar *HatLink `json:"getThumbnailAvatar",omitempty"`
}

type AvatarResponse struct {
	Links        avatarLinks `json:"_links"`
	AvatarRecord UserAvatar  `json:"avatar_record"`
}

//Define routes

func (ac AvatarController) AddRoutes(ws *restful.WebService) {
	ws.Route(ws.POST(avatarUri).To(ac.create).
		Doc("Create a new User Avatar Record").
		Filter(AuthUser).
		Operation("create").
		Reads(UserAvatar{}).
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(AvatarResponse{}))

	ws.Route(ws.GET(avatarUri).To(ac.read).
		Doc("Reads a User Avatar Record").
		Filter(AuthUser).
		Operation("read").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(AvatarResponse{}))

	ws.Route(ws.PUT(avatarUri).To(ac.update).
		Doc("Updated a User Avatar Record").
		Filter(AuthUser).
		Operation("update").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Param(ws.HeaderParameter("If-Match", "Avatar Revision").DataType("string")).
		Reads(UserAvatar{}).
		Writes(AvatarResponse{}))

	ws.Route(ws.DELETE(avatarUri).To(ac.del).
		Doc("Delete a User Avatar Record").
		Filter(AuthUser).
		Operation("delete").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(BooleanResponse{}))

	ws.Route(ws.POST(avatarUri + "/image").
		Doc("Saves a User Avatar Image").
		Filter(AuthUser).
		Consumes("multipart/form-data").To(ac.saveImage).
		Operation("saveImage").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Param(ws.HeaderParameter("If-Match", "File Revision").DataType("string")).
		Param(ws.FormParameter("file-data", "The Image File").DataType("string")).
		Writes(BooleanResponse{}))

	ws.Route(ws.GET(avatarUri + "/image").To(ac.getImage).
		Doc("Fetches a User Avatar Image").
		Operation("getAvatar").Produces("image/jpeg").
		Param(ws.PathParameter("user-id", "User id").DataType("string")))

	ws.Route(ws.GET(avatarUri + "/thumbnail").To(ac.getThumb).
		Doc("Fetches a Thumbnail Avatar Image").
		Operation("getThumbnail").Produces("image/jpeg").
		Param(ws.PathParameter("user-id", "User id").DataType("string")))

}

func (ac AvatarController) genAvatarUri(userId string) string {
	theUri := ApiPrefix() + "/users" + avatarUri
	return strings.Replace(theUri, "{user-id}", userId, 1)
}

//Create a new User Avatar Record
func (ac AvatarController) create(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	ua := new(UserAvatar)
	err := request.ReadEntity(ua)
	if err != nil {
		WriteServerError(err, response)
		return
	}
	rev, err := new(UserAvatarManager).Save(userId, "", ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	response.WriteHeader(http.StatusCreated)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Read a User Avatar Record
func (ac AvatarController) read(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	ua := new(UserAvatar)
	rev, err := new(UserAvatarManager).Read(userId, ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Updates a User Avatar Record
func (ac AvatarController) update(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	rev := request.HeaderParameter("If-Match")
	if userId == "" || rev == "" {
		WriteBadRequestError(response)
		return
	}
	ua := new(UserAvatar)
	err := request.ReadEntity(ua)
	if err != nil {
		WriteServerError(err, response)
		return
	}
	rev, err = new(UserAvatarManager).Save(userId, rev, ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Deletes a User Avatar Record
func (ac AvatarController) del(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	rev, err := new(UserAvatarManager).Delete(userId, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	SetAuth(response, curUser.Auth)
	response.WriteEntity(BooleanResponse{Success: true})
	response.AddHeader("ETag", rev)
}

//Saves an Avatar Image
func (ac AvatarController) saveImage(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	//Get the file data
	imageFile, header, err := request.Request.FormFile("file-data")
	if err != nil {
		WriteError(err, response)
	}
	userId := request.PathParameter("user-id")
	rev := request.HeaderParameter("If-Match")
	if userId == "" || rev == "" {
		WriteBadRequestError(response)
		return
	}
	attType := header.Header.Get("Content-Type")
	rev, err = new(UserAvatarManager).SaveImage(userId, rev, attType,
		imageFile, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	SetAuth(response, curUser.Auth)
	response.WriteEntity(BooleanResponse{Success: true})
	response.AddHeader("ETag", rev)
}

type imageReader func(string) (io.ReadCloser, error)

//Get an Avatar Image
func (ac AvatarController) getAvatar(request *restful.Request,
	response *restful.Response, uam *UserAvatarManager, ir imageReader) {
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	image, err := ir(userId)
	if err != nil {
		WriteError(err, response)
		return
	}
	defer image.Close()
	response.AddHeader("Content-Type", "image/jpeg")
	if _, err := io.Copy(response.ResponseWriter, image); err != nil {
		WriteError(err, response)
	}
}

//Get an Avatar Image (large size)
func (ac AvatarController) getImage(request *restful.Request,
	response *restful.Response) {
	uam := new(UserAvatarManager)
	ac.getAvatar(request, response, uam, uam.GetLargeAvatar)
}

//Get an Avatar Image (thumb size)
func (ac AvatarController) getThumb(request *restful.Request,
	response *restful.Response) {
	uam := new(UserAvatarManager)
	ac.getAvatar(request, response, uam, uam.GetThumbnailAvatar)
}

//Generates a UserAvatar Record Response
func (ac AvatarController) genRecordResponse(curUser *CurrentUserInfo,
	userId string, ua *UserAvatar) AvatarResponse {
	return AvatarResponse{
		Links: ac.genAvatarRecordLinks(curUser.User, userId,
			ac.genAvatarUri(userId)),
		AvatarRecord: *ua,
	}
}

//Generates the Links for a UserAvatar Record Response
func (ac AvatarController) genAvatarRecordLinks(user *User,
	userId string, uri string) avatarLinks {
	links := avatarLinks{}
	userRoles := user.Roles
	admin := util.HasRole(userRoles, AdminRole(MainDbName())) ||
		util.HasRole(userRoles, MasterRole())
	write := user.UserName == userId
	links.Self = &HatLink{Href: uri, Method: "GET"}
	links.GetLargeAvatar = &HatLink{Href: uri + "/image", Method: "GET"}
	links.GetThumbnailAvatar = &HatLink{Href: uri + "/thumbnail", Method: "GET"}
	if admin || write {
		links.Update = &HatLink{Href: uri, Method: "PUT"}
		links.Delete = &HatLink{Href: uri, Method: "DELETE"}
		links.SaveImage = &HatLink{Href: uri + "/image", Method: "PUT"}
	}
	return links
}
<commit_msg>fix struct tag<commit_after>/*
 *  Licensed to Wikifeat under one or more contributor license agreements.
 *  See the LICENSE.txt file distributed with this work for additional information
 *  regarding copyright ownership.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *  this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *  notice, this list of conditions and the following disclaimer in the
 *  documentation and/or other materials provided with the distribution.
 *  * Neither the name of Wikifeat nor the names of its contributors may be used
 *  to endorse or promote products derived from this software without
 *  specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 *  POSSIBILITY OF SUCH DAMAGE.
 */

package user_service

import (
	"github.com/rhinoman/wikifeat/Godeps/_workspace/src/github.com/emicklei/go-restful"
	. "github.com/rhinoman/wikifeat/common/entities"
	. "github.com/rhinoman/wikifeat/common/services"
	"github.com/rhinoman/wikifeat/common/util"
	"io"
	"net/http"
	"strings"
)

type AvatarController struct{}

var avatarUri = "/{user-id}/avatar"

type avatarLinks struct {
	HatLinks
	SaveImage          *HatLink `json:"saveImage,omitempty"`
	GetLargeAvatar     *HatLink `json:"getLargeAvatar,omitempty"`
	GetThumbnailAvatar *HatLink `json:"getThumbnailAvatar,omitempty"`
}

type AvatarResponse struct {
	Links        avatarLinks `json:"_links"`
	AvatarRecord UserAvatar  `json:"avatar_record"`
}

//Define routes

func (ac AvatarController) AddRoutes(ws *restful.WebService) {
	ws.Route(ws.POST(avatarUri).To(ac.create).
		Doc("Create a new User Avatar Record").
		Filter(AuthUser).
		Operation("create").
		Reads(UserAvatar{}).
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(AvatarResponse{}))

	ws.Route(ws.GET(avatarUri).To(ac.read).
		Doc("Reads a User Avatar Record").
		Filter(AuthUser).
		Operation("read").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(AvatarResponse{}))

	ws.Route(ws.PUT(avatarUri).To(ac.update).
		Doc("Updated a User Avatar Record").
		Filter(AuthUser).
		Operation("update").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Param(ws.HeaderParameter("If-Match", "Avatar Revision").DataType("string")).
		Reads(UserAvatar{}).
		Writes(AvatarResponse{}))

	ws.Route(ws.DELETE(avatarUri).To(ac.del).
		Doc("Delete a User Avatar Record").
		Filter(AuthUser).
		Operation("delete").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Writes(BooleanResponse{}))

	ws.Route(ws.POST(avatarUri + "/image").
		Doc("Saves a User Avatar Image").
		Filter(AuthUser).
		Consumes("multipart/form-data").To(ac.saveImage).
		Operation("saveImage").
		Param(ws.PathParameter("user-id", "User id").DataType("string")).
		Param(ws.HeaderParameter("If-Match", "File Revision").DataType("string")).
		Param(ws.FormParameter("file-data", "The Image File").DataType("string")).
		Writes(BooleanResponse{}))

	ws.Route(ws.GET(avatarUri + "/image").To(ac.getImage).
		Doc("Fetches a User Avatar Image").
		Operation("getAvatar").Produces("image/jpeg").
		Param(ws.PathParameter("user-id", "User id").DataType("string")))

	ws.Route(ws.GET(avatarUri + "/thumbnail").To(ac.getThumb).
		Doc("Fetches a Thumbnail Avatar Image").
		Operation("getThumbnail").Produces("image/jpeg").
		Param(ws.PathParameter("user-id", "User id").DataType("string")))

}

func (ac AvatarController) genAvatarUri(userId string) string {
	theUri := ApiPrefix() + "/users" + avatarUri
	return strings.Replace(theUri, "{user-id}", userId, 1)
}

//Create a new User Avatar Record
func (ac AvatarController) create(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	ua := new(UserAvatar)
	err := request.ReadEntity(ua)
	if err != nil {
		WriteServerError(err, response)
		return
	}
	rev, err := new(UserAvatarManager).Save(userId, "", ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	response.WriteHeader(http.StatusCreated)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Read a User Avatar Record
func (ac AvatarController) read(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	ua := new(UserAvatar)
	rev, err := new(UserAvatarManager).Read(userId, ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Updates a User Avatar Record
func (ac AvatarController) update(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	rev := request.HeaderParameter("If-Match")
	if userId == "" || rev == "" {
		WriteBadRequestError(response)
		return
	}
	ua := new(UserAvatar)
	err := request.ReadEntity(ua)
	if err != nil {
		WriteServerError(err, response)
		return
	}
	rev, err = new(UserAvatarManager).Save(userId, rev, ua, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	response.AddHeader("ETag", rev)
	ar := ac.genRecordResponse(curUser, userId, ua)
	SetAuth(response, curUser.Auth)
	response.WriteEntity(ar)
}

//Deletes a User Avatar Record
func (ac AvatarController) del(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	rev, err := new(UserAvatarManager).Delete(userId, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	SetAuth(response, curUser.Auth)
	response.WriteEntity(BooleanResponse{Success: true})
	response.AddHeader("ETag", rev)
}

//Saves an Avatar Image
func (ac AvatarController) saveImage(request *restful.Request,
	response *restful.Response) {
	curUser := GetCurrentUser(request, response)
	if curUser == nil {
		Unauthenticated(request, response)
		return
	}
	//Get the file data
	imageFile, header, err := request.Request.FormFile("file-data")
	if err != nil {
		WriteError(err, response)
	}
	userId := request.PathParameter("user-id")
	rev := request.HeaderParameter("If-Match")
	if userId == "" || rev == "" {
		WriteBadRequestError(response)
		return
	}
	attType := header.Header.Get("Content-Type")
	rev, err = new(UserAvatarManager).SaveImage(userId, rev, attType,
		imageFile, curUser)
	if err != nil {
		WriteError(err, response)
		return
	}
	SetAuth(response, curUser.Auth)
	response.WriteEntity(BooleanResponse{Success: true})
	response.AddHeader("ETag", rev)
}

type imageReader func(string) (io.ReadCloser, error)

//Get an Avatar Image
func (ac AvatarController) getAvatar(request *restful.Request,
	response *restful.Response, uam *UserAvatarManager, ir imageReader) {
	userId := request.PathParameter("user-id")
	if userId == "" {
		WriteBadRequestError(response)
		return
	}
	image, err := ir(userId)
	if err != nil {
		WriteError(err, response)
		return
	}
	defer image.Close()
	response.AddHeader("Content-Type", "image/jpeg")
	if _, err := io.Copy(response.ResponseWriter, image); err != nil {
		WriteError(err, response)
	}
}

//Get an Avatar Image (large size)
func (ac AvatarController) getImage(request *restful.Request,
	response *restful.Response) {
	uam := new(UserAvatarManager)
	ac.getAvatar(request, response, uam, uam.GetLargeAvatar)
}

//Get an Avatar Image (thumb size)
func (ac AvatarController) getThumb(request *restful.Request,
	response *restful.Response) {
	uam := new(UserAvatarManager)
	ac.getAvatar(request, response, uam, uam.GetThumbnailAvatar)
}

//Generates a UserAvatar Record Response
func (ac AvatarController) genRecordResponse(curUser *CurrentUserInfo,
	userId string, ua *UserAvatar) AvatarResponse {
	return AvatarResponse{
		Links: ac.genAvatarRecordLinks(curUser.User, userId,
			ac.genAvatarUri(userId)),
		AvatarRecord: *ua,
	}
}

//Generates the Links for a UserAvatar Record Response
func (ac AvatarController) genAvatarRecordLinks(user *User,
	userId string, uri string) avatarLinks {
	links := avatarLinks{}
	userRoles := user.Roles
	admin := util.HasRole(userRoles, AdminRole(MainDbName())) ||
		util.HasRole(userRoles, MasterRole())
	write := user.UserName == userId
	links.Self = &HatLink{Href: uri, Method: "GET"}
	links.GetLargeAvatar = &HatLink{Href: uri + "/image", Method: "GET"}
	links.GetThumbnailAvatar = &HatLink{Href: uri + "/thumbnail", Method: "GET"}
	if admin || write {
		links.Update = &HatLink{Href: uri, Method: "PUT"}
		links.Delete = &HatLink{Href: uri, Method: "DELETE"}
		links.SaveImage = &HatLink{Href: uri + "/image", Method: "PUT"}
	}
	return links
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/montanaflynn/stats"
	"time"
	"encoding/binary"
	"strconv"
	"crypto/rand"
	"runtime"
)

func main() {
	initGRs := runtime.NumGoroutine()
        maxGRs := 16
	for {
		time.Sleep(100)
		for runtime.NumGoroutine() - initGRs < maxGRs {
			go measure(1000)
		}
	}
}

func measure(n int) {
	mc := memcache.New("169.254.10.0:11211")
	timeToSet := make([]float64, n)
	timeToGet := make([]float64, n)
	for i := 0; i < n; i++ {
		timeToSet[i], timeToGet[i] = measureSetAndGetTime(mc)
		if i > 0 && timeToSet[i] < 0 {
			timeToSet[i] = timeToSet[i - 1]
		}
		if i > 0 && timeToGet[i] < 0 {
			timeToGet[i] = timeToGet[i - 1]
		}
	}
	fmt.Printf("Set min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToSet), stats.Max(timeToSet), stats.Median(timeToSet),
		stats.Percentile(timeToSet, 95), stats.Percentile(timeToSet, 99))
	fmt.Printf("Get min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		stats.Min(timeToGet), stats.Max(timeToGet), stats.Median(timeToGet),
		stats.Percentile(timeToGet, 95), stats.Percentile(timeToGet, 99))
}

func measureSetAndGetTime(mc *memcache.Client) (float64, float64) {

	// create key and value
	k := rand12Chars(1) // 12 bytes
	v := []byte(rand12Chars(85)) // 1020 bytes

	// test setting
	start := time.Now().Nanosecond()
	mc.Set(&memcache.Item{Key: k, Value: v})
	timeToSet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// test getting
	start = time.Now().Nanosecond()
	it, err := mc.Get(k)
	if err != nil {
		fmt.Printf("%v\n", err)
	 	return -1, -1
	}
	timeToGet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// validate the value
	if !bytes.Equal(it.Value, v) {
		fmt.Printf("Wrong value: %v\n", it.Value)
	 	return -1, -1
	}
	return timeToSet, timeToGet
}

func rand12Chars(l int) string {
	var buf bytes.Buffer
	for i := 0; i < l; i++ {
		var n uint64
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		buf.WriteString(strconv.FormatUint(n, 36))
	}
	return buf.String()
}
<commit_msg>Fixed mctest.go<commit_after>package main

import (
	"bytes"
	"fmt"
	"github.com/bradfitz/gomemcache/memcache"
	"github.com/montanaflynn/stats"
	"time"
	"encoding/binary"
	"strconv"
	"crypto/rand"
	"runtime"
)

func main() {
	mc := memcache.New("169.254.10.0:11211")
 	mc.Timeout = 20 * 1000 * time.Millisecond // 20 sec
	initGRs := runtime.NumGoroutine()
        maxGRs := 8
	for {
		time.Sleep(1000)
		for runtime.NumGoroutine() - initGRs < maxGRs {
			go measure(1000, mc)
		}
	}
}

func measure(n int, mc *memcache.Client) {
	timeToSet := make([]float64, n)
	timeToGet := make([]float64, n)
	for i := 0; i < n; i++ {
		timeToSet[i], timeToGet[i] = measureSetAndGetTime(mc)
		if i > 0 && timeToSet[i] < 0 {
			timeToSet[i] = timeToSet[i - 1]
		}
		if i > 0 && timeToGet[i] < 0 {
			timeToGet[i] = timeToGet[i - 1]
		}
	}
	min, _ := stats.Min(timeToSet)
	max, _ := stats.Max(timeToSet)
	med, _ := stats.Median(timeToSet)
	p95, _ := stats.Percentile(timeToSet, 95)
	p99, _ := stats.Percentile(timeToSet, 99)
	fmt.Printf("Set min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		min, max, med, p95, p99)

	min, _ = stats.Min(timeToGet)
	max, _ = stats.Max(timeToGet)
	med, _ = stats.Median(timeToGet)
	p95, _ = stats.Percentile(timeToGet, 95)
	p99, _ = stats.Percentile(timeToGet, 99)
	fmt.Printf("Get min: %.2f, max: %.2f, median: %.2f, 95%%: %.2f, 99%%: %.2f \n",
		min, max, med, p95, p99)
}

func measureSetAndGetTime(mc *memcache.Client) (float64, float64) {

	// create key and value
	k := rand12Chars(1) // 12 bytes
	v := []byte(rand12Chars(85)) // 1020 bytes

	// test setting
	start := time.Now().Nanosecond()
	mc.Set(&memcache.Item{Key: k, Value: v})
	timeToSet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// test getting
	start = time.Now().Nanosecond()
	it, err := mc.Get(k)
	if err != nil {
		fmt.Printf("%v\n", err)
	 	return -1, -1
	}
	timeToGet := float64((time.Now().Nanosecond() - start)) / 1000.0

	// validate the value
	if !bytes.Equal(it.Value, v) {
		fmt.Printf("Wrong value: %v\n", it.Value)
	 	return -1, -1
	}
	return timeToSet, timeToGet
}

func rand12Chars(l int) string {
	var buf bytes.Buffer
	for i := 0; i < l; i++ {
		var n uint64
		binary.Read(rand.Reader, binary.LittleEndian, &n)
		buf.WriteString(strconv.FormatUint(n, 36))
	}
	return buf.String()
}
<|endoftext|>


<commit_before>// Package md2web contains the MD2Web trim.Application.
package md2web

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/jwowillo/pack"
	"github.com/jwowillo/trim/application"
	"github.com/jwowillo/trim/controller"
	"github.com/jwowillo/trim/request"
	"github.com/jwowillo/trim/response"
	"github.com/jwowillo/trim/url"
	"github.com/russross/blackfriday"
)

// MD2Web is a trim.Applications which turns directories of markdown files and
// folders into a website.
type MD2Web struct {
	*application.Web
}

// New creates a MD2Web excluding the provided files which has the given host.
func New(h string, excs []string) *MD2Web {
	app := &MD2Web{Web: application.NewWeb()}
	app.RemoveAPI()
	app.ClearControllers()
	set := pack.NewHashSet(pack.StringHasher)
	for _, exc := range excs {
		set.Add(exc)
	}
	static := app.URLFor(
		url.Pattern{
			app.Static().Subdomain(),
			app.Static().BasePath(),
		}, h,
	).String()
	if err := app.AddController(newClientController(static, set)); err != nil {
		panic(err)
	}
	return app
}

// NewDebug creates an MD2Web that doesn't cache which has the given host.
func NewDebug(h string, excs []string) *MD2Web {
	cf := application.ClientDefault
	cf.CacheDuration = 0
	app := &MD2Web{
		Web: application.NewWebWithConfig(
			cf,
			application.APIDefault,
			application.StaticDefault,
		),
	}
	app.RemoveAPI()
	app.ClearControllers()
	set := pack.NewHashSet(pack.StringHasher)
	for _, exc := range excs {
		set.Add(exc)
	}
	static := app.URLFor(
		url.Pattern{
			app.Static().Subdomain(),
			app.Static().BasePath(),
		}, h,
	).String()
	if err := app.AddController(newClientController(static, set)); err != nil {
		panic(err)
	}
	return app
}

// clientController which renders markdown page's based on request paths.
type clientController struct {
	controller.Bare
	static   string
	excludes pack.Set
}

// newClientController creates a controller with the given template file and
// base folder.
func newClientController(
	static string,
	excs pack.Set,
) *clientController {
	excs.Add("static")
	excs.Add(".git")
	excs.Add(".gitignore")
	return &clientController{static: static, excludes: excs}
}

// Path of the clientController.
//
// Always a variable path which captures the entire path into the key
// 'fullName'.
func (c *clientController) Path() string {
	return "/:name*"
}

// Handle trim.Request by rendering the markdown page at the file name stored in
// the path.
func (c *clientController) Handle(req *request.Request) response.Response {
	fn := req.URL().Path()
	path := buildPath(fn)
	hl, err := headerLinks(path, c.excludes)
	nl, err := navLinks(path, c.excludes)
	bs, err := content(path)
	args := pack.AnyMap{
		"title":       filepath.Base(fn),
		"static":      c.static,
		"headerLinks": hl,
		"navLinks":    nl,
		"content": strings.Replace(
			string(bs),
			"{{ static }}",
			c.static,
			-1,
		),
	}
	if err != nil {
		args["headerLinks"] = map[string]string{"/": "/"}
		args["navLinks"] = nil
		args["content"] = fmt.Sprintf("%s couldn't be served.", fn)
		return response.NewTemplateFromString(
			Template,
			args,
			http.StatusInternalServerError,
		)
	}
	return response.NewTemplateFromString(Template, args, http.StatusOK)
}

// headerLinks are links to files along the provided path except what is in the
// provided set map mapped to their link text.
func headerLinks(path string, excs pack.Set) ([]linkPair, error) {
	ls := []linkPair{linkPair{Real: "/", Fake: "/"}}
	working := ""
	for _, part := range strings.Split(filepath.Dir(path), "/") {
		if part == "." {
			continue
		}
		working += part
		if excs.Contains(working) {
			return nil, fmt.Errorf("%s excluded", working)
		}
		if part == "main.md" {
			break
		}
		if strings.HasSuffix(part, ".md") {
			part = part[:len(part)-len(".md")]
		} else {
			part += "/"
		}
		ls = append(ls, linkPair{Real: "/" + working + "/", Fake: part})
	}
	return ls, nil
}

// navLinks are links to adjacent markdown files and folders to the provided
// path except what is in the excluded provided set mapped to their link text.
//
// Returns an error if the directory of the given path can't be read.
func navLinks(path string, excs pack.Set) ([]linkPair, error) {
	fs, err := ioutil.ReadDir(filepath.Dir(path))
	if err != nil {
		return nil, err
	}
	var ls []linkPair
	for _, f := range fs {
		fn := f.Name()
		if excs.Contains(fn) || excs.Contains(filepath.Base(fn)) {
			continue
		}
		key := f.Name()
		switch mode := f.Mode(); {
		case mode.IsDir():
			key = key + "/"
		case mode.IsRegular():
			if !strings.HasSuffix(fn, ".md") {
				continue
			}
			if fn == "main.md" {
				continue
			}
		}
		if strings.HasSuffix(key, ".md") {
			key = key[:len(key)-len(".md")]
			fn = fn[:len(fn)-len(".md")]
		}
		ls = append(ls, linkPair{Real: key, Fake: fn})
	}
	return ls, nil
}

// content of file at path.
//
// Returns an error if the file isn't a markdown file.
func content(path string) ([]byte, error) {
	if filepath.Ext(path) != ".md" {
		return nil, fmt.Errorf("%s isn't a markdown file", path)
	}
	bs, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return blackfriday.MarkdownCommon(bs), nil
}

// buildPath to markdown file represented by given name.
func buildPath(name string) string {
	path := "." + name
	if path == "" || path[len(path)-1] == '/' {
		path += "main"
	}
	path += ".md"
	return path
}

// Template file shown as page.
const Template = `
<!DOCTYPE html>

    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="icon" href="http://{{ static }}/favicon.png">
    <meta name="viewport" content="width=device-width, initial-scale=1">

      	{% for p in headerLinks %}
      	  {{ p.Fake }}
      	{% endfor %}

        {% for p in navLinks %}
          {{ p.Fake }}
        {% endfor %}

        {{ content | safe }}

`

// linkPair is a pair of a real and a fake link.
type linkPair struct {
	Real, Fake string
}
<commit_msg>Remove URLFor Dependency<commit_after>// Package md2web contains the MD2Web trim.Application.
package md2web

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/jwowillo/pack"
	"github.com/jwowillo/trim/application"
	"github.com/jwowillo/trim/controller"
	"github.com/jwowillo/trim/request"
	"github.com/jwowillo/trim/response"
	"github.com/jwowillo/trim/url"
	"github.com/russross/blackfriday"
)

// MD2Web is a trim.Applications which turns directories of markdown files and
// folders into a website.
type MD2Web struct {
	*application.Web
}

// New creates a MD2Web excluding the provided files which has the given host.
func New(h string, excs []string) *MD2Web {
	app := &MD2Web{Web: application.NewWeb()}
	app.RemoveAPI()
	app.ClearControllers()
	set := pack.NewHashSet(pack.StringHasher)
	for _, exc := range excs {
		set.Add(exc)
	}
	s := url.NewBuilder(h).
		SetSubdomain(app.Static().Subdomain()).
		SetPath(app.Static().BasePath()).
		Build().String()
	if err := app.AddController(newClientController(s, set)); err != nil {
		panic(err)
	}
	return app
}

// NewDebug creates an MD2Web that doesn't cache which has the given host.
func NewDebug(h string, excs []string) *MD2Web {
	cf := application.ClientDefault
	cf.CacheDuration = 0
	app := &MD2Web{
		Web: application.NewWebWithConfig(
			cf,
			application.APIDefault,
			application.StaticDefault,
		),
	}
	app.RemoveAPI()
	app.ClearControllers()
	set := pack.NewHashSet(pack.StringHasher)
	for _, exc := range excs {
		set.Add(exc)
	}
	static := app.URLFor(
		url.Pattern{
			app.Static().Subdomain(),
			app.Static().BasePath(),
		}, h,
	).String()
	if err := app.AddController(newClientController(static, set)); err != nil {
		panic(err)
	}
	return app
}

// clientController which renders markdown page's based on request paths.
type clientController struct {
	controller.Bare
	static   string
	excludes pack.Set
}

// newClientController creates a controller with the given template file and
// base folder.
func newClientController(
	static string,
	excs pack.Set,
) *clientController {
	excs.Add("static")
	excs.Add(".git")
	excs.Add(".gitignore")
	return &clientController{static: static, excludes: excs}
}

// Path of the clientController.
//
// Always a variable path which captures the entire path into the key
// 'fullName'.
func (c *clientController) Path() string {
	return "/:name*"
}

// Handle trim.Request by rendering the markdown page at the file name stored in
// the path.
func (c *clientController) Handle(req *request.Request) response.Response {
	fn := req.URL().Path()
	path := buildPath(fn)
	hl, err := headerLinks(path, c.excludes)
	nl, err := navLinks(path, c.excludes)
	bs, err := content(path)
	args := pack.AnyMap{
		"title":       filepath.Base(fn),
		"static":      c.static,
		"headerLinks": hl,
		"navLinks":    nl,
		"content": strings.Replace(
			string(bs),
			"{{ static }}",
			c.static,
			-1,
		),
	}
	if err != nil {
		args["headerLinks"] = map[string]string{"/": "/"}
		args["navLinks"] = nil
		args["content"] = fmt.Sprintf("%s couldn't be served.", fn)
		return response.NewTemplateFromString(
			Template,
			args,
			http.StatusInternalServerError,
		)
	}
	return response.NewTemplateFromString(Template, args, http.StatusOK)
}

// headerLinks are links to files along the provided path except what is in the
// provided set map mapped to their link text.
func headerLinks(path string, excs pack.Set) ([]linkPair, error) {
	ls := []linkPair{linkPair{Real: "/", Fake: "/"}}
	working := ""
	for _, part := range strings.Split(filepath.Dir(path), "/") {
		if part == "." {
			continue
		}
		working += part
		if excs.Contains(working) {
			return nil, fmt.Errorf("%s excluded", working)
		}
		if part == "main.md" {
			break
		}
		if strings.HasSuffix(part, ".md") {
			part = part[:len(part)-len(".md")]
		} else {
			part += "/"
		}
		ls = append(ls, linkPair{Real: "/" + working + "/", Fake: part})
	}
	return ls, nil
}

// navLinks are links to adjacent markdown files and folders to the provided
// path except what is in the excluded provided set mapped to their link text.
//
// Returns an error if the directory of the given path can't be read.
func navLinks(path string, excs pack.Set) ([]linkPair, error) {
	fs, err := ioutil.ReadDir(filepath.Dir(path))
	if err != nil {
		return nil, err
	}
	var ls []linkPair
	for _, f := range fs {
		fn := f.Name()
		if excs.Contains(fn) || excs.Contains(filepath.Base(fn)) {
			continue
		}
		key := f.Name()
		switch mode := f.Mode(); {
		case mode.IsDir():
			key = key + "/"
		case mode.IsRegular():
			if !strings.HasSuffix(fn, ".md") {
				continue
			}
			if fn == "main.md" {
				continue
			}
		}
		if strings.HasSuffix(key, ".md") {
			key = key[:len(key)-len(".md")]
			fn = fn[:len(fn)-len(".md")]
		}
		ls = append(ls, linkPair{Real: key, Fake: fn})
	}
	return ls, nil
}

// content of file at path.
//
// Returns an error if the file isn't a markdown file.
func content(path string) ([]byte, error) {
	if filepath.Ext(path) != ".md" {
		return nil, fmt.Errorf("%s isn't a markdown file", path)
	}
	bs, err := ioutil.ReadFile(path)
	if err != nil {
		return nil, err
	}
	return blackfriday.MarkdownCommon(bs), nil
}

// buildPath to markdown file represented by given name.
func buildPath(name string) string {
	path := "." + name
	if path == "" || path[len(path)-1] == '/' {
		path += "main"
	}
	path += ".md"
	return path
}

// Template file shown as page.
const Template = `
<!DOCTYPE html>

    <meta charset="utf-8">
    <title>{{ title }}</title>
    <link rel="icon" href="http://{{ static }}/favicon.png">
    <meta name="viewport" content="width=device-width, initial-scale=1">

      	{% for p in headerLinks %}
      	  {{ p.Fake }}
      	{% endfor %}

        {% for p in navLinks %}
          {{ p.Fake }}
        {% endfor %}

        {{ content | safe }}

`

// linkPair is a pair of a real and a fake link.
type linkPair struct {
	Real, Fake string
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"fmt"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/auth"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/git"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	CREATE  base.TplName = "repo/create"
	MIGRATE base.TplName = "repo/migrate"
)

func checkContextUser(ctx *middleware.Context, uid int64) *models.User {
	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return nil
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	// Not equal means current user is an organization.
	if uid == ctx.User.Id || uid == 0 {
		return ctx.User
	}

	org, err := models.GetUserByID(uid)
	if models.IsErrUserNotExist(err) {
		return ctx.User
	}

	if err != nil {
		ctx.Handle(500, "checkContextUser", fmt.Errorf("GetUserById(%d): %v", uid, err))
		return nil
	}

	// Check ownership of organization.
	if !org.IsOrganization() || !org.IsOwnedBy(ctx.User.Id) {
		ctx.Error(403)
		return nil
	}
	return org
}

func Create(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("new_repo")

	// Give default value for template to render.
	ctx.Data["Gitignores"] = models.Gitignores
	ctx.Data["Licenses"] = models.Licenses
	ctx.Data["Readmes"] = models.Readmes
	ctx.Data["readme"] = "Default"

	ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	ctx.HTML(200, CREATE)
}

func handleCreateError(ctx *middleware.Context, err error, name string, tpl base.TplName, form interface{}) {
	switch {
	case models.IsErrRepoAlreadyExist(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
	case models.IsErrNameReserved(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
	case models.IsErrNamePatternNotAllowed(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
	default:
		ctx.Handle(500, name, err)
	}
}

func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
	ctx.Data["Title"] = ctx.Tr("new_repo")

	ctx.Data["Gitignores"] = models.Gitignores
	ctx.Data["Licenses"] = models.Licenses
	ctx.Data["Readmes"] = models.Readmes

	ctxUser := checkContextUser(ctx, form.Uid)
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	if ctx.HasError() {
		ctx.HTML(200, CREATE)
		return
	}

	repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{
		Name:        form.RepoName,
		Description: form.Description,
		Gitignores:  form.Gitignores,
		License:     form.License,
		Readme:      form.Readme,
		IsPrivate:   form.Private,
		AutoInit:    form.AutoInit,
	})
	if err == nil {
		log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	handleCreateError(ctx, err, "CreatePost", CREATE, &form)
}

func Migrate(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("new_migrate")

	ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	ctx.HTML(200, MIGRATE)
}

func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
	ctx.Data["Title"] = ctx.Tr("new_migrate")

	ctxUser := checkContextUser(ctx, form.Uid)
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	if ctx.HasError() {
		ctx.HTML(200, MIGRATE)
		return
	}

	// Remote address can be HTTP/HTTPS/Git URL or local path.
	// Note: remember to change api/v1/repo.go: MigrateRepo
	// FIXME: merge these two functions with better error handling
	remoteAddr := form.CloneAddr
	if strings.HasPrefix(form.CloneAddr, "http://") ||
		strings.HasPrefix(form.CloneAddr, "https://") ||
		strings.HasPrefix(form.CloneAddr, "git://") {
		u, err := url.Parse(form.CloneAddr)
		if err != nil {
			ctx.Data["Err_CloneAddr"] = true
			ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
			return
		}
		if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
			u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
		}
		remoteAddr = u.String()
	} else if !com.IsDir(remoteAddr) {
		ctx.Data["Err_CloneAddr"] = true
		ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), MIGRATE, &form)
		return
	}

	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
	if err == nil {
		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	if strings.Contains(err.Error(), "Authentication failed") ||
		strings.Contains(err.Error(), " not found") {
		ctx.Data["Err_Auth"] = true
		ctx.RenderWithErr(ctx.Tr("form.auth_failed", strings.Replace(err.Error(), ":"+form.AuthPassword+"@", ":<password>@", 1)), MIGRATE, &form)
		return
	}

	handleCreateError(ctx, err, "MigratePost", MIGRATE, &form)
}

func Action(ctx *middleware.Context) {
	var err error
	switch ctx.Params(":action") {
	case "watch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.ID, true)
	case "unwatch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.ID, false)
	case "star":
		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.ID, true)
	case "unstar":
		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.ID, false)
	case "desc":
		if !ctx.Repo.IsOwner() {
			ctx.Error(404)
			return
		}

		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		err = models.UpdateRepository(ctx.Repo.Repository, false)
	}

	if err != nil {
		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
		ctx.JSON(200, map[string]interface{}{
			"ok":  false,
			"err": err.Error(),
		})
		return
	}

	redirectTo := ctx.Query("redirect_to")
	if len(redirectTo) == 0 {
		redirectTo = ctx.Repo.RepoLink
	}
	ctx.Redirect(redirectTo)

	return
	ctx.JSON(200, map[string]interface{}{
		"ok": true,
	})
}

func Download(ctx *middleware.Context) {
	var (
		uri         = ctx.Params("*")
		refName     string
		ext         string
		archivePath string
		archiveType git.ArchiveType
	)

	switch {
	case strings.HasSuffix(uri, ".zip"):
		ext = ".zip"
		archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
		archiveType = git.ZIP
	case strings.HasSuffix(uri, ".tar.gz"):
		ext = ".tar.gz"
		archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
		archiveType = git.TARGZ
	default:
		ctx.Error(404)
		return
	}
	refName = strings.TrimSuffix(uri, ext)

	if !com.IsDir(archivePath) {
		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
			ctx.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
			return
		}
	}

	// Get corresponding commit.
	var (
		commit *git.Commit
		err    error
	)
	gitRepo := ctx.Repo.GitRepo
	if gitRepo.IsBranchExist(refName) {
		commit, err = gitRepo.GetCommitOfBranch(refName)
		if err != nil {
			ctx.Handle(500, "Download", err)
			return
		}
	} else if gitRepo.IsTagExist(refName) {
		commit, err = gitRepo.GetCommitOfTag(refName)
		if err != nil {
			ctx.Handle(500, "Download", err)
			return
		}
	} else if len(refName) == 40 {
		commit, err = gitRepo.GetCommit(refName)
		if err != nil {
			ctx.Handle(404, "Download", nil)
			return
		}
	} else {
		ctx.Error(404)
		return
	}

	archivePath = path.Join(archivePath, base.ShortSha(commit.Id.String())+ext)
	if !com.IsFile(archivePath) {
		if err := commit.CreateArchive(archivePath, archiveType); err != nil {
			ctx.Handle(500, "Download -> CreateArchive "+archivePath, err)
			return
		}
	}

	ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+base.ShortSha(commit.Id.String())+ext)
}
<commit_msg>#965 Remember visibility preference<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"fmt"
	"net/url"
	"os"
	"path"
	"strings"

	"github.com/Unknwon/com"

	"github.com/gogits/gogs/models"
	"github.com/gogits/gogs/modules/auth"
	"github.com/gogits/gogs/modules/base"
	"github.com/gogits/gogs/modules/git"
	"github.com/gogits/gogs/modules/log"
	"github.com/gogits/gogs/modules/middleware"
	"github.com/gogits/gogs/modules/setting"
)

const (
	CREATE  base.TplName = "repo/create"
	MIGRATE base.TplName = "repo/migrate"
)

func checkContextUser(ctx *middleware.Context, uid int64) *models.User {
	if err := ctx.User.GetOrganizations(); err != nil {
		ctx.Handle(500, "GetOrganizations", err)
		return nil
	}
	ctx.Data["Orgs"] = ctx.User.Orgs

	// Not equal means current user is an organization.
	if uid == ctx.User.Id || uid == 0 {
		return ctx.User
	}

	org, err := models.GetUserByID(uid)
	if models.IsErrUserNotExist(err) {
		return ctx.User
	}

	if err != nil {
		ctx.Handle(500, "checkContextUser", fmt.Errorf("GetUserById(%d): %v", uid, err))
		return nil
	}

	// Check ownership of organization.
	if !org.IsOrganization() || !org.IsOwnedBy(ctx.User.Id) {
		ctx.Error(403)
		return nil
	}
	return org
}

func Create(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("new_repo")

	// Give default value for template to render.
	ctx.Data["Gitignores"] = models.Gitignores
	ctx.Data["Licenses"] = models.Licenses
	ctx.Data["Readmes"] = models.Readmes
	ctx.Data["readme"] = "Default"
	ctx.Data["private"] = ctx.User.LastRepoVisibility

	ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	ctx.HTML(200, CREATE)
}

func handleCreateError(ctx *middleware.Context, err error, name string, tpl base.TplName, form interface{}) {
	switch {
	case models.IsErrRepoAlreadyExist(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
	case models.IsErrNameReserved(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
	case models.IsErrNamePatternNotAllowed(err):
		ctx.Data["Err_RepoName"] = true
		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
	default:
		ctx.Handle(500, name, err)
	}
}

func CreatePost(ctx *middleware.Context, form auth.CreateRepoForm) {
	ctx.Data["Title"] = ctx.Tr("new_repo")

	ctx.Data["Gitignores"] = models.Gitignores
	ctx.Data["Licenses"] = models.Licenses
	ctx.Data["Readmes"] = models.Readmes

	ctxUser := checkContextUser(ctx, form.Uid)
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	if ctx.HasError() {
		ctx.HTML(200, CREATE)
		return
	}

	repo, err := models.CreateRepository(ctxUser, models.CreateRepoOptions{
		Name:        form.RepoName,
		Description: form.Description,
		Gitignores:  form.Gitignores,
		License:     form.License,
		Readme:      form.Readme,
		IsPrivate:   form.Private,
		AutoInit:    form.AutoInit,
	})
	if err == nil {
		// Remember visibility preference.
		ctx.User.LastRepoVisibility = repo.IsPrivate
		models.UpdateUser(ctx.User)

		log.Trace("Repository created: %s/%s", ctxUser.Name, repo.Name)
		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + repo.Name)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	handleCreateError(ctx, err, "CreatePost", CREATE, &form)
}

func Migrate(ctx *middleware.Context) {
	ctx.Data["Title"] = ctx.Tr("new_migrate")
	ctx.Data["private"] = ctx.User.LastRepoVisibility

	ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	ctx.HTML(200, MIGRATE)
}

func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
	ctx.Data["Title"] = ctx.Tr("new_migrate")

	ctxUser := checkContextUser(ctx, form.Uid)
	if ctx.Written() {
		return
	}
	ctx.Data["ContextUser"] = ctxUser

	if ctx.HasError() {
		ctx.HTML(200, MIGRATE)
		return
	}

	// Remote address can be HTTP/HTTPS/Git URL or local path.
	// Note: remember to change api/v1/repo.go: MigrateRepo
	// FIXME: merge these two functions with better error handling
	remoteAddr := form.CloneAddr
	if strings.HasPrefix(form.CloneAddr, "http://") ||
		strings.HasPrefix(form.CloneAddr, "https://") ||
		strings.HasPrefix(form.CloneAddr, "git://") {
		u, err := url.Parse(form.CloneAddr)
		if err != nil {
			ctx.Data["Err_CloneAddr"] = true
			ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
			return
		}
		if len(form.AuthUsername) > 0 || len(form.AuthPassword) > 0 {
			u.User = url.UserPassword(form.AuthUsername, form.AuthPassword)
		}
		remoteAddr = u.String()
	} else if !com.IsDir(remoteAddr) {
		ctx.Data["Err_CloneAddr"] = true
		ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), MIGRATE, &form)
		return
	}

	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
	if err == nil {
		// Remember visibility preference.
		ctx.User.LastRepoVisibility = repo.IsPrivate
		models.UpdateUser(ctx.User)

		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID, ctxUser.Name); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	if strings.Contains(err.Error(), "Authentication failed") ||
		strings.Contains(err.Error(), " not found") {
		ctx.Data["Err_Auth"] = true
		ctx.RenderWithErr(ctx.Tr("form.auth_failed", strings.Replace(err.Error(), ":"+form.AuthPassword+"@", ":<password>@", 1)), MIGRATE, &form)
		return
	}

	handleCreateError(ctx, err, "MigratePost", MIGRATE, &form)
}

func Action(ctx *middleware.Context) {
	var err error
	switch ctx.Params(":action") {
	case "watch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.ID, true)
	case "unwatch":
		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.ID, false)
	case "star":
		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.ID, true)
	case "unstar":
		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.ID, false)
	case "desc":
		if !ctx.Repo.IsOwner() {
			ctx.Error(404)
			return
		}

		ctx.Repo.Repository.Description = ctx.Query("desc")
		ctx.Repo.Repository.Website = ctx.Query("site")
		err = models.UpdateRepository(ctx.Repo.Repository, false)
	}

	if err != nil {
		log.Error(4, "Action(%s): %v", ctx.Params(":action"), err)
		ctx.JSON(200, map[string]interface{}{
			"ok":  false,
			"err": err.Error(),
		})
		return
	}

	redirectTo := ctx.Query("redirect_to")
	if len(redirectTo) == 0 {
		redirectTo = ctx.Repo.RepoLink
	}
	ctx.Redirect(redirectTo)

	return
	ctx.JSON(200, map[string]interface{}{
		"ok": true,
	})
}

func Download(ctx *middleware.Context) {
	var (
		uri         = ctx.Params("*")
		refName     string
		ext         string
		archivePath string
		archiveType git.ArchiveType
	)

	switch {
	case strings.HasSuffix(uri, ".zip"):
		ext = ".zip"
		archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
		archiveType = git.ZIP
	case strings.HasSuffix(uri, ".tar.gz"):
		ext = ".tar.gz"
		archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
		archiveType = git.TARGZ
	default:
		ctx.Error(404)
		return
	}
	refName = strings.TrimSuffix(uri, ext)

	if !com.IsDir(archivePath) {
		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
			ctx.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
			return
		}
	}

	// Get corresponding commit.
	var (
		commit *git.Commit
		err    error
	)
	gitRepo := ctx.Repo.GitRepo
	if gitRepo.IsBranchExist(refName) {
		commit, err = gitRepo.GetCommitOfBranch(refName)
		if err != nil {
			ctx.Handle(500, "Download", err)
			return
		}
	} else if gitRepo.IsTagExist(refName) {
		commit, err = gitRepo.GetCommitOfTag(refName)
		if err != nil {
			ctx.Handle(500, "Download", err)
			return
		}
	} else if len(refName) == 40 {
		commit, err = gitRepo.GetCommit(refName)
		if err != nil {
			ctx.Handle(404, "Download", nil)
			return
		}
	} else {
		ctx.Error(404)
		return
	}

	archivePath = path.Join(archivePath, base.ShortSha(commit.Id.String())+ext)
	if !com.IsFile(archivePath) {
		if err := commit.CreateArchive(archivePath, archiveType); err != nil {
			ctx.Handle(500, "Download -> CreateArchive "+archivePath, err)
			return
		}
	}

	ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+base.ShortSha(commit.Id.String())+ext)
}
<|endoftext|>


<commit_before>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bm "github.com/microcosm-cc/bluemonday"
	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Priority() int
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	ei, ej := e[i], e[j]
	if ei.Priority() != ej.Priority() {
		return ei.Priority() > ej.Priority()
	}
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type GCTime time.Time

const GCTimeLayout = "2006-01-02 15:04"

func (t *GCTime) UnmarshalJSON(b []byte) error {
	tmp, err := time.Parse(GCTimeLayout, strings.Trim(string(b), "\""))
	*t = GCTime(tmp)
	return err
}

func (t GCTime) String() string {
	return time.Time(t).Format(GCTimeLayout)
}
func (t GCTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.String() + `"`), nil
}

type Meta struct {
	Author   string
	Date     GCTime
	Title    string
	Priority int
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Priority() int {
	return e.meta.Priority
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	if err != nil {
		err = &os.PathError{
			Op:   "Parsing json in",
			Path: path,
			Err:  err,
		}
	}
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath+string(os.PathSeparator), path+string(os.PathSeparator)) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.MarkdownBasic(b)
		ret.html = bm.UGCPolicy().SanitizeBytes(ret.html)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c != nil && c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<commit_msg>enable table extension for markdown<commit_after>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bm "github.com/microcosm-cc/bluemonday"
	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Priority() int
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	ei, ej := e[i], e[j]
	if ei.Priority() != ej.Priority() {
		return ei.Priority() > ej.Priority()
	}
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type GCTime time.Time

const GCTimeLayout = "2006-01-02 15:04"

func (t *GCTime) UnmarshalJSON(b []byte) error {
	tmp, err := time.Parse(GCTimeLayout, strings.Trim(string(b), "\""))
	*t = GCTime(tmp)
	return err
}

func (t GCTime) String() string {
	return time.Time(t).Format(GCTimeLayout)
}
func (t GCTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.String() + `"`), nil
}

type Meta struct {
	Author   string
	Date     GCTime
	Title    string
	Priority int
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Priority() int {
	return e.meta.Priority
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	if err != nil {
		err = &os.PathError{
			Op:   "Parsing json in",
			Path: path,
			Err:  err,
		}
	}
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath+string(os.PathSeparator), path+string(os.PathSeparator)) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.Markdown(b, bf.HtmlRenderer(bf.HTML_USE_XHTML, "", ""), bf.EXTENSION_TABLES)
		ret.html = bm.UGCPolicy().SanitizeBytes(ret.html)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c != nil && c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// The chaosduck binary is an e2e testing tool for leader election, which loads
// the leader election configuration within the system namespace and
// periodically kills one of the leader pods for each HA component.
package main

import (
	"context"
	"errors"
	"flag"
	"log"
	"strings"
	"time"

	"knative.dev/pkg/injection"

	"golang.org/x/sync/errgroup"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	kubeclient "knative.dev/pkg/client/injection/kube/client"
	"knative.dev/pkg/kflag"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/system"
)

// components is a mapping from component name to the collection of leader pod names.
type components map[string]sets.String

var (
	disabledComponents kflag.StringSet
	tributePeriod      time.Duration = 20 * time.Second
	tributeFactor                    = 2.0
)

func init() {
	// Note that we don't explicitly call flag.Parse() because ParseAndGetConfigOrDie below does this already.
	flag.Var(&disabledComponents, "disable", "A repeatable flag to disable chaos for certain components.")
	flag.DurationVar(&tributePeriod, "period", tributePeriod, "How frequently to terminate a leader pod per component (this is the base duration used with the jitter factor from -factor).")
	flag.Float64Var(&tributeFactor, "factor", tributeFactor, "The jitter factor to apply to the period.")
}

func countingRFind(wr rune, wc int) func(rune) bool {
	cnt := 0
	return func(r rune) bool {
		if r == wr {
			cnt++
		}
		return cnt == wc
	}
}

// This is a copy of test/ha/ha.go that avoids a dependency that pulls in a
// redefinition of the kubeconfig flag.
func extractDeployment(pod string) string {
	if x := strings.LastIndexFunc(pod, countingRFind('-', 2)); x != -1 {
		return pod[:x]
	}
	return ""
}

// buildComponents crawls the list of leases and builds a mapping from component names
// to the set pod names that hold one or more leases.
func buildComponents(ctx context.Context, kc kubernetes.Interface) (components, error) {
	leases, err := kc.CoordinationV1().Leases(system.Namespace()).List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	cs := components{}
	for _, lease := range leases.Items {
		if lease.Spec.HolderIdentity == nil {
			log.Printf("Found lease %q held by nobody!", lease.Name)
			continue
		}
		pod := strings.SplitN(*lease.Spec.HolderIdentity, "_", 2)[0]
		deploymentName := extractDeployment(pod)
		if deploymentName == "" {
			continue
		}

		set, ok := cs[deploymentName]
		if !ok {
			set = make(sets.String, 1)
			cs[deploymentName] = set
		}
		set.Insert(pod)
	}
	return cs, nil
}

// quack will kill one of the components leader pods.
func quack(ctx context.Context, kc kubernetes.Interface, component string, leaders sets.String) error {
	tribute, ok := leaders.PopAny()
	if !ok {
		return errors.New("this should not be possible, since components are only created when they have components")
	}
	log.Printf("Quacking at %q leader %q", component, tribute)

	return kc.CoreV1().Pods(system.Namespace()).Delete(ctx, tribute, metav1.DeleteOptions{})
}

func main() {
	ctx, _ := injection.EnableInjectionOrDie(signals.NewContext(), nil)

	kc := kubeclient.Get(ctx)

	// Until we are shutdown, build up an index of components and kill
	// of a leader at the specified frequency.
	wait.JitterUntilWithContext(ctx, func(ctx context.Context) {
		components, err := buildComponents(ctx, kc)
		if err != nil {
			log.Print("Error building components: ", err)
		}
		log.Printf("Got components: %#v", components)

		eg, ctx := errgroup.WithContext(ctx)
		for name, leaders := range components {
			if disabledComponents.Value.Has(name) {
				continue
			}
			name, leaders := name, leaders
			eg.Go(func() error {
				return quack(ctx, kc, name, leaders)
			})
		}
		if err := eg.Wait(); err != nil {
			log.Print("Ended iteration with err: ", err)
		}
	}, tributePeriod, tributeFactor, true /* sliding: do not include the runtime of the above in the interval */)
}
<commit_msg>Allow disable via regex in chaosduck (#2117)<commit_after>/*
Copyright 2020 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// The chaosduck binary is an e2e testing tool for leader election, which loads
// the leader election configuration within the system namespace and
// periodically kills one of the leader pods for each HA component.
package main

import (
	"context"
	"errors"
	"flag"
	"log"
	"regexp"
	"strings"
	"time"

	"knative.dev/pkg/injection"

	"golang.org/x/sync/errgroup"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/sets"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	kubeclient "knative.dev/pkg/client/injection/kube/client"
	"knative.dev/pkg/kflag"
	"knative.dev/pkg/signals"
	"knative.dev/pkg/system"
)

// components is a mapping from component name to the collection of leader pod names.
type components map[string]sets.String

var (
	disabledComponents      kflag.StringSet
	disabledComponentsRegex kflag.StringSet
	tributePeriod           time.Duration = 20 * time.Second
	tributeFactor                         = 2.0
)

func init() {
	// Note that we don't explicitly call flag.Parse() because ParseAndGetConfigOrDie below does this already.
	flag.Var(&disabledComponents, "disable", "A repeatable flag to disable chaos for certain components.")
	flag.Var(&disabledComponentsRegex, "disableRegex", "A repeatable flag to disable chaos for components matching one of the passed regexes.")
	flag.DurationVar(&tributePeriod, "period", tributePeriod, "How frequently to terminate a leader pod per component (this is the base duration used with the jitter factor from -factor).")
	flag.Float64Var(&tributeFactor, "factor", tributeFactor, "The jitter factor to apply to the period.")
}

func countingRFind(wr rune, wc int) func(rune) bool {
	cnt := 0
	return func(r rune) bool {
		if r == wr {
			cnt++
		}
		return cnt == wc
	}
}

// This is a copy of test/ha/ha.go that avoids a dependency that pulls in a
// redefinition of the kubeconfig flag.
func extractDeployment(pod string) string {
	if x := strings.LastIndexFunc(pod, countingRFind('-', 2)); x != -1 {
		return pod[:x]
	}
	return ""
}

// buildComponents crawls the list of leases and builds a mapping from component names
// to the set pod names that hold one or more leases.
func buildComponents(ctx context.Context, kc kubernetes.Interface) (components, error) {
	leases, err := kc.CoordinationV1().Leases(system.Namespace()).List(ctx, metav1.ListOptions{})
	if err != nil {
		return nil, err
	}

	cs := components{}
	for _, lease := range leases.Items {
		if lease.Spec.HolderIdentity == nil {
			log.Printf("Found lease %q held by nobody!", lease.Name)
			continue
		}
		pod := strings.SplitN(*lease.Spec.HolderIdentity, "_", 2)[0]
		deploymentName := extractDeployment(pod)
		if deploymentName == "" {
			continue
		}

		set, ok := cs[deploymentName]
		if !ok {
			set = make(sets.String, 1)
			cs[deploymentName] = set
		}
		set.Insert(pod)
	}
	return cs, nil
}

// quack will kill one of the components leader pods.
func quack(ctx context.Context, kc kubernetes.Interface, component string, leaders sets.String) error {
	tribute, ok := leaders.PopAny()
	if !ok {
		return errors.New("this should not be possible, since components are only created when they have components")
	}
	log.Printf("Quacking at %q leader %q", component, tribute)

	return kc.CoreV1().Pods(system.Namespace()).Delete(ctx, tribute, metav1.DeleteOptions{})
}

// matchesAny returns true if any of the given regexes matches the given string.
func matchesAny(regexes []*regexp.Regexp, str string) bool {
	for _, re := range regexes {
		if re.MatchString(str) {
			return true
		}
	}
	return false
}

func main() {
	ctx, _ := injection.EnableInjectionOrDie(signals.NewContext(), nil)
	kc := kubeclient.Get(ctx)

	regexes := make([]*regexp.Regexp, 0, len(disabledComponentsRegex.Value))
	for re := range disabledComponentsRegex.Value {
		regexes = append(regexes, regexp.MustCompile(re))
	}

	// Until we are shutdown, build up an index of components and kill
	// of a leader at the specified frequency.
	wait.JitterUntilWithContext(ctx, func(ctx context.Context) {
		components, err := buildComponents(ctx, kc)
		if err != nil {
			log.Print("Error building components: ", err)
		}
		log.Printf("Got components: %#v", components)

		eg, ctx := errgroup.WithContext(ctx)
		for name, leaders := range components {
			if disabledComponents.Value.Has(name) || matchesAny(regexes, name) {
				continue
			}

			name, leaders := name, leaders
			eg.Go(func() error {
				return quack(ctx, kc, name, leaders)
			})
		}
		if err := eg.Wait(); err != nil {
			log.Print("Ended iteration with err: ", err)
		}
	}, tributePeriod, tributeFactor, true /* sliding: do not include the runtime of the above in the interval */)
}
<|endoftext|>


<commit_before>package runner

import (
	"os/user"
	"path/filepath"

	"github.com/scjalliance/resourceful/lease"
)

// Properties queries the execution context to determine the
// consumer, instance and environment for a resourceful run.
//
// The returned instance will be a random string.
func Properties(c Config, host string, u *user.User) lease.Properties {
	program := c.Program
	path := c.Program

	if abs, err := filepath.Abs(program); err == nil {
		path = abs
		program = filepath.Base(path)
	}

	return lease.Properties{
		"program.name":  program,
		"program.path":  path,
		"host.name":     host,
		"user.id":       u.Uid,
		"user.username": u.Username,
		"user.name":     u.Name,
	}
}
<commit_msg>runner: Executable path lookup now uses exec.LookPath<commit_after>package runner

import (
	"os/exec"
	"os/user"
	"path/filepath"

	"github.com/scjalliance/resourceful/lease"
)

// Properties queries the execution context to determine the
// consumer, instance and environment for a resourceful run.
//
// The returned instance will be a random string.
func Properties(c Config, host string, u *user.User) lease.Properties {
	program := c.Program
	path := c.Program

	if abs, err := exec.LookPath(program); err == nil {
		path = abs
		program = filepath.Base(path)
	}

	return lease.Properties{
		"program.name":  program,
		"program.path":  path,
		"host.name":     host,
		"user.id":       u.Uid,
		"user.username": u.Username,
		"user.name":     u.Name,
	}
}
<|endoftext|>


<commit_before>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package tygo

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"path"
	"strings"

	"io/ioutil"
)

var (
	PY_WRITER  io.Writer
	PY_TYPES   map[string]bool
	PY_OBJECTS map[string]*Object
)

func Python(dir string, name string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	PY_WRITER = &buffer
	PY_OBJECTS = ObjectMap(types)
	PY_TYPES = make(map[string]bool)
	var codes []string
	for _, t := range types {
		codes = append(codes, t.Python())
	}
	PROP_PRE = nil
	PY_TYPES = nil
	PY_WRITER = nil
	PY_OBJECTS = nil

	buffer.Write([]byte(fmt.Sprintf(`#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!

import typy

%s
`, strings.Join(codes, ""))))

	ioutil.WriteFile(path.Join(dir, name+".py"), buffer.Bytes(), 0666)
}

func (t *Enum) Python() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
	%s = %d, "%s"`, name, t.Values[name], name))
	}
	return fmt.Sprintf(`
class %s(typy.Enum):%s
`, t.Name, strings.Join(enums, ""))
}

func fieldsPython(name string, parent string, fs []*Field) string {
	if ok, exist := PY_TYPES[name]; exist && ok {
		return ""
	}

	PY_TYPES[name] = true
	var fields []string
	var sequences []string
	for _, f := range fs {
		sequences = append(sequences, fmt.Sprintf("'%s'", f.Name))
		fields = append(fields, fmt.Sprintf(`
	%s = typy.pb.%s`, f.Name, f.Python()))
	}

	var sequence string
	if sequences == nil {
		return ""
	} else if len(sequences) > 1 {
		sequence = fmt.Sprintf(`
	____propertySequence__ = %s`, strings.Join(sequences, ", "))
	}

	return fmt.Sprintf(`
class %s(%s):%s%s
`, name, parent, sequence, strings.Join(fields, ""))
}

func (t *Object) Python() string {
	var objects []string
	parent := "typy.Object"
	if t.HasParent() {
		parent = t.Parent.Name
	}
	objects = append(objects, fieldsPython(t.Name, parent, t.VisibleFields()))

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%s", t.Name, field.Name),
				"typy.Object", []*Field{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			var params []*Field
			for i, p := range method.Params {
				params = append(params, &Field{Type: p, Name: fmt.Sprintf("a%d", i)})
			}
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%sParam", t.Name, method.Name),
				"typy.Object", params))
		}
		if len(method.Results) > 0 {
			var results []*Field
			for i, r := range method.Results {
				results = append(results, &Field{Type: r, Name: fmt.Sprintf("a%d", i)})
			}
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%sResult", t.Name, method.Name),
				"typy.Object", results))
		}
	}

	return strings.Join(objects, "")
}

func (t UnknownType) Python() string {
	return ""
}

func (t SimpleType) Python() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		return "Integer"
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "Float"
	case SimpleType_BYTES:
		return "Bytes"
	case SimpleType_STRING:
		return "String"
	case SimpleType_BOOL:
		return "Boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Python: %d", t)
		return "Unknown"
	}
}

func (t *EnumType) Python() string {
	return fmt.Sprintf("Enum(%s)", t.Name)
}

func (t *InstanceType) Python() string {
	if object, ok := TS_OBJECTS[t.Name]; ok {
		PY_WRITER.Write([]byte(object.Python()))
		return fmt.Sprintf("Instance(%s)", t.Name)
	} else {
		return fmt.Sprintf("Python(%s)", t.Name)
	}
}

func (t *FixedPointType) Python() string {
	return fmt.Sprintf("FixedPoint(%d, %d)", t.Precision, t.Floor)
}

func (t *ListType) Python() string {
	return fmt.Sprintf("List(%s)", t.E.Python())
}

func (t *DictType) Python() string {
	return fmt.Sprintf("Dict(%s, %s)", t.K.Python(), t.V.Python())
}

func (t *VariantType) Python() string {
	var variants []string
	for _, v := range t.Ts {
		if inst, ok := v.(*InstanceType); ok {
			if object, ok := TS_OBJECTS[inst.Name]; ok {
				PY_WRITER.Write([]byte(object.Python()))
				variants = append(variants, inst.Name)
				continue
			}
		}
		variants = append(variants, v.Python())
	}
	return fmt.Sprintf("Instance(%s)", strings.Join(variants, ", "))
}

func Typyd(dir string, name string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	PY_OBJECTS = ObjectMap(types)
	var codes []string
	// for _, t := range types {
	// 	codes = append(codes, t.Typyd())
	// }
	PROP_PRE = nil
	PY_OBJECTS = nil

	buffer.Write([]byte(fmt.Sprintf(`#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!

import typy

%s
`, strings.Join(codes, ""))))

	ioutil.WriteFile(path.Join(dir, name+".py"), buffer.Bytes(), 0666)
}

func (t *Enum) Typyd() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
		%s = %d`, name, t.Values[name]))
	}
	return fmt.Sprintf(`

	const enum %s {%s
	}`, t.Name, strings.Join(enums, ","))
}

func (t *Object) Typyd() string {
	var parent string
	if t.HasParent() {
		parent = fmt.Sprintf(" extends %s", t.Parent.Typyd())
	}
	var members []string
	for _, field := range t.VisibleFields() {
		members = append(members, fmt.Sprintf(`
		%s: %s;`, field.Name, field.Typyd()))
	}

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			members = append(members, field.Python())
			// members = append(members, typeListPython(field.Name, "", []Type{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			// members = append(members, typeListPython(method.Name, "Param", method.Params))
		}
		if len(method.Results) > 0 {
			// members = append(members, typeListPython(method.Name, "Result", method.Results))
		}
	}

	return fmt.Sprintf(`

	class %s%s {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
%s
	}

	namespace %s {
		function Deserialize(data: Uint8Array): %s;
	}`, t.Name, parent, strings.Join(members, ""), t.Name, t.Name)
}

func (t UnknownType) Typyd() string {
	return ""
}

func (t SimpleType) Typyd() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		fallthrough
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "number"
	case SimpleType_BYTES:
		return "Uint8Array"
	case SimpleType_STRING:
		return "string"
	case SimpleType_BOOL:
		return "boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Typyd: %d", t)
		return "unknown"
	}
}

func (t *EnumType) Typyd() string {
	return t.Name
}

func (t *InstanceType) Typyd() string {
	if _, ok := TS_OBJECTS[t.Name]; ok {
		return t.Name
	} else {
		return "Type"
	}
}

func (t *FixedPointType) Typyd() string {
	return "number"
}

func (t *ListType) Typyd() string {
	return fmt.Sprintf("%s[]", t.E.Typyd())
}

func (t *DictType) Typyd() string {
	return fmt.Sprintf("{[index: %s]: %s}", t.K.Typyd(), t.V.Typyd())
}

func (t *VariantType) Typyd() string {
	return "any"
}
<commit_msg>inject python<commit_after>// Copyright 2017 ibelie, Chen Jie, Joungtao. All rights reserved.
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package tygo

import (
	"bytes"
	"fmt"
	"io"
	"log"
	"path"
	"strings"

	"io/ioutil"
)

var (
	PY_WRITER  io.Writer
	PY_TYPES   map[string]bool
	PY_OBJECTS map[string]*Object
)

func Python(dir string, name string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	PY_WRITER = &buffer
	PY_OBJECTS = ObjectMap(types)
	PY_TYPES = make(map[string]bool)
	var codes []string
	for _, t := range types {
		codes = append(codes, t.Python())
	}
	PROP_PRE = nil
	PY_TYPES = nil
	PY_WRITER = nil
	PY_OBJECTS = nil

	buffer.Write([]byte(fmt.Sprintf(`#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!

import typy

%s
`, strings.Join(codes, ""))))

	ioutil.WriteFile(path.Join(dir, name+".py"), buffer.Bytes(), 0666)
}

func (t *Enum) Python() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
	%s = %d, "%s"`, name, t.Values[name], name))
	}
	return fmt.Sprintf(`
class %s(typy.Enum):%s
`, t.Name, strings.Join(enums, ""))
}

func fieldsPython(name string, parent string, fs []*Field) string {
	if ok, exist := PY_TYPES[name]; exist && ok {
		return ""
	}

	PY_TYPES[name] = true
	var fields []string
	var sequences []string
	for _, f := range fs {
		sequences = append(sequences, fmt.Sprintf("'%s'", f.Name))
		fields = append(fields, fmt.Sprintf(`
	%s = %s`, f.Name, strings.Replace(f.Python(), "typy.", "typy.pb.", 1)))
	}

	var sequence string
	if sequences == nil {
		return ""
	} else if len(sequences) > 1 {
		sequence = fmt.Sprintf(`
	____propertySequence__ = %s`, strings.Join(sequences, ", "))
	}

	return fmt.Sprintf(`
class %s(%s):%s%s
`, name, parent, sequence, strings.Join(fields, ""))
}

func (t *Object) Python() string {
	var objects []string
	parent := "typy.Object"
	if t.HasParent() {
		parent = t.Parent.Name
	}
	objects = append(objects, fieldsPython(t.Name, parent, t.VisibleFields()))

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%s", t.Name, field.Name),
				"typy.Object", []*Field{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			var params []*Field
			for i, p := range method.Params {
				params = append(params, &Field{Type: p, Name: fmt.Sprintf("a%d", i)})
			}
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%sParam", t.Name, method.Name),
				"typy.Object", params))
		}
		if len(method.Results) > 0 {
			var results []*Field
			for i, r := range method.Results {
				results = append(results, &Field{Type: r, Name: fmt.Sprintf("a%d", i)})
			}
			objects = append(objects, fieldsPython(fmt.Sprintf("%s_%sResult", t.Name, method.Name),
				"typy.Object", results))
		}
	}

	return strings.Join(objects, "")
}

func (t UnknownType) Python() string {
	return ""
}

func (t SimpleType) Python() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		return "typy.Integer"
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "typy.Float"
	case SimpleType_BYTES:
		return "typy.Bytes"
	case SimpleType_STRING:
		return "typy.String"
	case SimpleType_BOOL:
		return "typy.Boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Python: %d", t)
		return "Unknown"
	}
}

func (t *EnumType) Python() string {
	PY_WRITER.Write([]byte(t.Enum.Python()))
	return fmt.Sprintf("typy.Enum(%s)", t.Name)
}

func (t *InstanceType) Python() string {
	if object, ok := TS_OBJECTS[t.Name]; ok {
		PY_WRITER.Write([]byte(object.Python()))
		return fmt.Sprintf("typy.Instance(%s)", t.Name)
	} else {
		return fmt.Sprintf("typy.Python(%s)", t.Name)
	}
}

func (t *FixedPointType) Python() string {
	return fmt.Sprintf("typy.FixedPoint(%d, %d)", t.Precision, t.Floor)
}

func (t *ListType) Python() string {
	return fmt.Sprintf("typy.List(%s)", t.E.Python())
}

func (t *DictType) Python() string {
	return fmt.Sprintf("typy.Dict(%s, %s)", t.K.Python(), t.V.Python())
}

func (t *VariantType) Python() string {
	var variants []string
	for _, v := range t.Ts {
		if inst, ok := v.(*InstanceType); ok {
			if object, ok := TS_OBJECTS[inst.Name]; ok {
				PY_WRITER.Write([]byte(object.Python()))
				variants = append(variants, inst.Name)
				continue
			}
		}
		variants = append(variants, v.Python())
	}
	return fmt.Sprintf("typy.Instance(%s)", strings.Join(variants, ", "))
}

func Typyd(dir string, name string, types []Type, propPre []Type) {
	var buffer bytes.Buffer

	PROP_PRE = propPre
	PY_OBJECTS = ObjectMap(types)
	var codes []string
	// for _, t := range types {
	// 	codes = append(codes, t.Typyd())
	// }
	PROP_PRE = nil
	PY_OBJECTS = nil

	buffer.Write([]byte(fmt.Sprintf(`#-*- coding: utf-8 -*-
# Generated by ibelie-rpc.  DO NOT EDIT!

import typy

%s
`, strings.Join(codes, ""))))

	ioutil.WriteFile(path.Join(dir, name+".py"), buffer.Bytes(), 0666)
}

func (t *Enum) Typyd() string {
	var enums []string
	for _, name := range t.Sorted() {
		enums = append(enums, fmt.Sprintf(`
		%s = %d`, name, t.Values[name]))
	}
	return fmt.Sprintf(`

	const enum %s {%s
	}`, t.Name, strings.Join(enums, ","))
}

func (t *Object) Typyd() string {
	var parent string
	if t.HasParent() {
		parent = fmt.Sprintf(" extends %s", t.Parent.Typyd())
	}
	var members []string
	for _, field := range t.VisibleFields() {
		members = append(members, fmt.Sprintf(`
		%s: %s;`, field.Name, field.Typyd()))
	}

	if PROP_PRE != nil {
		for _, field := range t.VisibleFields() {
			members = append(members, field.Python())
			// members = append(members, typeListPython(field.Name, "", []Type{field}))
		}
	}

	for _, method := range t.Methods {
		if len(method.Params) > 0 {
			// members = append(members, typeListPython(method.Name, "Param", method.Params))
		}
		if len(method.Results) > 0 {
			// members = append(members, typeListPython(method.Name, "Result", method.Results))
		}
	}

	return fmt.Sprintf(`

	class %s%s {
		__class__: string;
		ByteSize(): number;
		Serialize(): Uint8Array;
		Deserialize(data: Uint8Array): void;
%s
	}

	namespace %s {
		function Deserialize(data: Uint8Array): %s;
	}`, t.Name, parent, strings.Join(members, ""), t.Name, t.Name)
}

func (t UnknownType) Typyd() string {
	return ""
}

func (t SimpleType) Typyd() string {
	switch t {
	case SimpleType_INT32:
		fallthrough
	case SimpleType_INT64:
		fallthrough
	case SimpleType_UINT32:
		fallthrough
	case SimpleType_UINT64:
		fallthrough
	case SimpleType_FLOAT32:
		fallthrough
	case SimpleType_FLOAT64:
		return "number"
	case SimpleType_BYTES:
		return "Uint8Array"
	case SimpleType_STRING:
		return "string"
	case SimpleType_BOOL:
		return "boolean"
	default:
		log.Fatalf("[Tygo][SimpleType] Unexpect enum value for Typyd: %d", t)
		return "unknown"
	}
}

func (t *EnumType) Typyd() string {
	return t.Name
}

func (t *InstanceType) Typyd() string {
	if _, ok := TS_OBJECTS[t.Name]; ok {
		return t.Name
	} else {
		return "Type"
	}
}

func (t *FixedPointType) Typyd() string {
	return "number"
}

func (t *ListType) Typyd() string {
	return fmt.Sprintf("%s[]", t.E.Typyd())
}

func (t *DictType) Typyd() string {
	return fmt.Sprintf("{[index: %s]: %s}", t.K.Typyd(), t.V.Typyd())
}

func (t *VariantType) Typyd() string {
	return "any"
}
<|endoftext|>