<commit_before>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"fmt"
	"html/template"
	"model"
	"net/url"
	"regexp"
	"strings"
	"time"

	. "db"

	"github.com/fatih/structs"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/set"
	"github.com/polaris1119/slices"
	"golang.org/x/net/context"
)

type CommentLogic struct{}

var DefaultComment = CommentLogic{}

// FindObjComments 获得某个对象的所有评论
// owner: 被评论对象属主
// TODO:分页暂不做
func (self CommentLogic) FindObjComments(ctx context.Context, objid, objtype int, owner, lastCommentUid int /*, page, pageNum int*/) (comments []map[string]interface{}, ownerUser, lastReplyUser *model.User) {
	objLog := GetLogger(ctx)

	commentList := make([]*model.Comment, 0)
	err := MasterDB.Where("objid=? AND objtype=?", objid, objtype).Find(&commentList)
	if err != nil {
		objLog.Errorln("CommentLogic FindObjComments Error:", err)
		return
	}

	uids := slices.StructsIntSlice(commentList, "Uid")

	// 避免某些情况下最后回复人没在回复列表中
	uids = append(uids, owner, lastCommentUid)

	// 获得用户信息
	userMap := DefaultUser.FindUserInfos(ctx, uids)
	ownerUser = userMap[owner]
	if lastCommentUid != 0 {
		lastReplyUser = userMap[lastCommentUid]
	}
	comments = make([]map[string]interface{}, 0, len(commentList))
	for _, comment := range commentList {
		tmpMap := structs.Map(comment)
		tmpMap["content"] = template.HTML(self.decodeCmtContent(ctx, comment))
		tmpMap["user"] = userMap[comment.Uid]
		comments = append(comments, tmpMap)
	}
	return
}

// FindObjectComments 获得某个对象的所有评论（新版）
// TODO:分页暂不做
func (self CommentLogic) FindObjectComments(ctx context.Context, objid, objtype int) (commentList []*model.Comment, err error) {
	objLog := GetLogger(ctx)

	commentList = make([]*model.Comment, 0)
	err = MasterDB.Where("objid=? AND objtype=?", objid, objtype).Asc("cid").Find(&commentList)
	if err != nil {
		objLog.Errorln("comment logic FindObjectComments Error:", err)
	}

	for _, comment := range commentList {
		self.decodeCmtContentForShow(ctx, comment)
	}

	return
}

// Total 评论总数(objtypes[0] 取某一类型的评论总数)
func (CommentLogic) Total(objtypes ...int) int64 {
	var (
		total int64
		err   error
	)
	if len(objtypes) > 0 {
		total, err = MasterDB.Where("objtype=?", objtypes[0]).Count(new(model.Comment))
	} else {

		total, err = MasterDB.Count(new(model.Comment))
	}
	if err != nil {
		logger.Errorln("CommentLogic Total error:", err)
	}
	return total
}

// FindRecent 获得最近的评论
// 如果 uid!=0，表示获取某人的评论；
// 如果 objtype!=-1，表示获取某类型的评论；
func (self CommentLogic) FindRecent(ctx context.Context, uid, objtype, limit int) []*model.Comment {
	dbSession := MasterDB.OrderBy("cid DESC").Limit(limit)

	if uid != 0 {
		dbSession.And("uid=?", uid)
	}
	if objtype != -1 {
		dbSession.And("objtype=?", objtype)
	}

	comments := make([]*model.Comment, 0)
	err := dbSession.Find(&comments)
	if err != nil {
		logger.Errorln("CommentLogic FindRecent error:", err)
		return nil
	}

	cmtMap := make(map[int][]*model.Comment, len(model.PathUrlMap))
	for _, comment := range comments {
		self.decodeCmtContent(ctx, comment)

		if _, ok := cmtMap[comment.Objtype]; !ok {
			cmtMap[comment.Objtype] = make([]*model.Comment, 0, 10)
		}

		cmtMap[comment.Objtype] = append(cmtMap[comment.Objtype], comment)
	}

	cmtObjs := []CommentObjecter{
		model.TypeTopic:    TopicComment{},
		model.TypeArticle:  ArticleComment{},
		model.TypeResource: ResourceComment{},
		model.TypeWiki:     nil,
		model.TypeProject:  ProjectComment{},
		model.TypeBook:     BookComment{},
	}
	for cmtType, cmts := range cmtMap {
		self.fillObjinfos(cmts, cmtObjs[cmtType])
	}

	return comments
}

// Publish 发表评论（或回复）。
// objid 注册的评论对象
// uid 评论人
func (self CommentLogic) Publish(ctx context.Context, uid, objid int, form url.Values) (*model.Comment, error) {
	objLog := GetLogger(ctx)

	objtype := goutils.MustInt(form.Get("objtype"))
	comment := &model.Comment{
		Objid:   objid,
		Objtype: objtype,
		Uid:     uid,
		Content: form.Get("content"),
	}

	// TODO:评论楼层怎么处理，避免冲突？最后的楼层信息保存在内存中？

	// 暂时只是从数据库中取出最后的评论楼层
	tmpCmt := &model.Comment{}
	_, err := MasterDB.Where("objid=? AND objtype=?", objid, objtype).OrderBy("ctime DESC").Get(tmpCmt)
	if err != nil {
		objLog.Errorln("post comment service error:", err)
		return nil, err
	} else {
		comment.Floor = tmpCmt.Floor + 1
	}
	// 入评论库
	_, err = MasterDB.Insert(comment)
	if err != nil {
		objLog.Errorln("post comment service error:", err)
		return nil, err
	}
	self.decodeCmtContentForShow(ctx, comment)

	// 回调，不关心处理结果（有些对象可能不需要回调）
	if commenter, ok := commenters[objtype]; ok {
		now := time.Now()

		objLog.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功，通知被评论者更新", objid, objtype, uid)
		go commenter.UpdateComment(comment.Cid, objid, uid, now)

		DefaultFeed.updateComment(objid, objtype, uid, now)
	}

	go commentObservable.NotifyObservers(uid, objtype, comment.Cid)

	go self.sendSystemMsg(ctx, uid, objid, objtype, comment.Cid, form)

	return comment, nil
}

func (CommentLogic) sendSystemMsg(ctx context.Context, uid, objid, objtype, cid int, form url.Values) {
	// 给被评论对象所有者发系统消息 TODO: ext 考虑结构化
	ext := map[string]interface{}{
		"objid":   objid,
		"objtype": objtype,
		"cid":     cid,
		"uid":     uid,
	}

	to := 0
	switch objtype {
	case model.TypeTopic:
		to = DefaultTopic.getOwner(objid)
	case model.TypeArticle:
		to = DefaultArticle.getOwner(objid)
	case model.TypeResource:
		to = DefaultResource.getOwner(objid)
	case model.TypeWiki:
		to = DefaultWiki.getOwner(objid)
	case model.TypeProject:
		to = DefaultProject.getOwner(ctx, objid)
	}

	DefaultMessage.SendSystemMsgTo(ctx, to, objtype, ext)

	// @某人 发系统消息
	DefaultMessage.SendSysMsgAtUids(ctx, form.Get("uid"), ext, to)
	DefaultMessage.SendSysMsgAtUsernames(ctx, form.Get("usernames"), ext, to)
}

// Modify 修改评论信息
func (CommentLogic) Modify(ctx context.Context, cid int, content string) (errMsg string, err error) {
	objLog := GetLogger(ctx)

	_, err = MasterDB.Table(new(model.Comment)).Id(cid).Update(map[string]interface{}{"content": content})
	if err != nil {
		objLog.Errorf("更新评论内容 【%d】 失败：%s", cid, err)
		errMsg = "对不起，服务器内部错误，请稍后再试！"
		return
	}

	return
}

// fillObjinfos 填充评论对应的主体信息
func (CommentLogic) fillObjinfos(comments []*model.Comment, cmtObj CommentObjecter) {
	if len(comments) == 0 {
		return
	}
	count := len(comments)
	commentMap := make(map[int][]*model.Comment, count)
	idSet := set.New(set.NonThreadSafe)
	for _, comment := range comments {
		if _, ok := commentMap[comment.Objid]; !ok {
			commentMap[comment.Objid] = make([]*model.Comment, 0, count)
		}
		commentMap[comment.Objid] = append(commentMap[comment.Objid], comment)
		idSet.Add(comment.Objid)
	}
	cmtObj.SetObjinfo(set.IntSlice(idSet), commentMap)
}

// 提供给其他service调用（包内）
func (CommentLogic) findByIds(cids []int) map[int]*model.Comment {
	if len(cids) == 0 {
		return nil
	}

	comments := make(map[int]*model.Comment)
	err := MasterDB.In("cid", cids).Find(&comments)
	if err != nil {
		return nil
	}

	return comments
}

func (CommentLogic) findById(cid int) *model.Comment {
	comment := &model.Comment{}
	_, err := MasterDB.Where("cid=?", cid).Get(comment)
	if err != nil {
		logger.Errorln("CommentLogic findById error:", err)
	}

	return comment
}

func (CommentLogic) decodeCmtContent(ctx context.Context, comment *model.Comment) string {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)
	// @别人
	content = parseAtUser(ctx, content)

	// 回复某一楼层
	reg := regexp.MustCompile(`#(\d+)楼`)
	url := fmt.Sprintf("%s%d#comment", model.PathUrlMap[comment.Objtype], comment.Objid)
	content = reg.ReplaceAllString(content, `<a href="`+url+`$1" title="$1">#$1<span>楼</span></a>`)

	comment.Content = content

	return content
}

// decodeCmtContentForShow 采用引用的方式显示对其他楼层的回复
func (CommentLogic) decodeCmtContentForShow(ctx context.Context, comment *model.Comment) {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)

	// 回复某一楼层
	reg := regexp.MustCompile(`#(\d+)楼 @([a-zA-Z0-9_]+)`)
	matches := reg.FindStringSubmatch(content)
	if len(matches) > 2 {
		comment.ReplyFloor = goutils.MustInt(matches[1])
		content = strings.TrimSpace(content[len(matches[0]):])
	}

	// @别人
	content = parseAtUser(ctx, content)

	comment.Content = content
}

// 填充 Comment 对象的 Objinfo 成员接口
// 评论属主应该实现该接口（以便填充 Objinfo 成员）
type CommentObjecter interface {
	// ids 是属主的主键 slice （comment 中的 objid）
	// commentMap 中的 key 是属主 id
	SetObjinfo(ids []int, commentMap map[int][]*model.Comment)
}
<commit_msg>判断内容是不是一样的，避免某种原因，重复发评论<commit_after>// Copyright 2016 The StudyGolang Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// http://studygolang.com
// Author:polaris	polaris@studygolang.com

package logic

import (
	"fmt"
	"html/template"
	"model"
	"net/url"
	"regexp"
	"strings"
	"time"

	. "db"

	"github.com/fatih/structs"
	"github.com/polaris1119/goutils"
	"github.com/polaris1119/logger"
	"github.com/polaris1119/set"
	"github.com/polaris1119/slices"
	"golang.org/x/net/context"
)

type CommentLogic struct{}

var DefaultComment = CommentLogic{}

// FindObjComments 获得某个对象的所有评论
// owner: 被评论对象属主
// TODO:分页暂不做
func (self CommentLogic) FindObjComments(ctx context.Context, objid, objtype int, owner, lastCommentUid int /*, page, pageNum int*/) (comments []map[string]interface{}, ownerUser, lastReplyUser *model.User) {
	objLog := GetLogger(ctx)

	commentList := make([]*model.Comment, 0)
	err := MasterDB.Where("objid=? AND objtype=?", objid, objtype).Find(&commentList)
	if err != nil {
		objLog.Errorln("CommentLogic FindObjComments Error:", err)
		return
	}

	uids := slices.StructsIntSlice(commentList, "Uid")

	// 避免某些情况下最后回复人没在回复列表中
	uids = append(uids, owner, lastCommentUid)

	// 获得用户信息
	userMap := DefaultUser.FindUserInfos(ctx, uids)
	ownerUser = userMap[owner]
	if lastCommentUid != 0 {
		lastReplyUser = userMap[lastCommentUid]
	}
	comments = make([]map[string]interface{}, 0, len(commentList))
	for _, comment := range commentList {
		tmpMap := structs.Map(comment)
		tmpMap["content"] = template.HTML(self.decodeCmtContent(ctx, comment))
		tmpMap["user"] = userMap[comment.Uid]
		comments = append(comments, tmpMap)
	}
	return
}

// FindObjectComments 获得某个对象的所有评论（新版）
// TODO:分页暂不做
func (self CommentLogic) FindObjectComments(ctx context.Context, objid, objtype int) (commentList []*model.Comment, err error) {
	objLog := GetLogger(ctx)

	commentList = make([]*model.Comment, 0)
	err = MasterDB.Where("objid=? AND objtype=?", objid, objtype).Asc("cid").Find(&commentList)
	if err != nil {
		objLog.Errorln("comment logic FindObjectComments Error:", err)
	}

	for _, comment := range commentList {
		self.decodeCmtContentForShow(ctx, comment)
	}

	return
}

// Total 评论总数(objtypes[0] 取某一类型的评论总数)
func (CommentLogic) Total(objtypes ...int) int64 {
	var (
		total int64
		err   error
	)
	if len(objtypes) > 0 {
		total, err = MasterDB.Where("objtype=?", objtypes[0]).Count(new(model.Comment))
	} else {

		total, err = MasterDB.Count(new(model.Comment))
	}
	if err != nil {
		logger.Errorln("CommentLogic Total error:", err)
	}
	return total
}

// FindRecent 获得最近的评论
// 如果 uid!=0，表示获取某人的评论；
// 如果 objtype!=-1，表示获取某类型的评论；
func (self CommentLogic) FindRecent(ctx context.Context, uid, objtype, limit int) []*model.Comment {
	dbSession := MasterDB.OrderBy("cid DESC").Limit(limit)

	if uid != 0 {
		dbSession.And("uid=?", uid)
	}
	if objtype != -1 {
		dbSession.And("objtype=?", objtype)
	}

	comments := make([]*model.Comment, 0)
	err := dbSession.Find(&comments)
	if err != nil {
		logger.Errorln("CommentLogic FindRecent error:", err)
		return nil
	}

	cmtMap := make(map[int][]*model.Comment, len(model.PathUrlMap))
	for _, comment := range comments {
		self.decodeCmtContent(ctx, comment)

		if _, ok := cmtMap[comment.Objtype]; !ok {
			cmtMap[comment.Objtype] = make([]*model.Comment, 0, 10)
		}

		cmtMap[comment.Objtype] = append(cmtMap[comment.Objtype], comment)
	}

	cmtObjs := []CommentObjecter{
		model.TypeTopic:    TopicComment{},
		model.TypeArticle:  ArticleComment{},
		model.TypeResource: ResourceComment{},
		model.TypeWiki:     nil,
		model.TypeProject:  ProjectComment{},
		model.TypeBook:     BookComment{},
	}
	for cmtType, cmts := range cmtMap {
		self.fillObjinfos(cmts, cmtObjs[cmtType])
	}

	return comments
}

// Publish 发表评论（或回复）。
// objid 注册的评论对象
// uid 评论人
func (self CommentLogic) Publish(ctx context.Context, uid, objid int, form url.Values) (*model.Comment, error) {
	objLog := GetLogger(ctx)

	objtype := goutils.MustInt(form.Get("objtype"))
	comment := &model.Comment{
		Objid:   objid,
		Objtype: objtype,
		Uid:     uid,
		Content: form.Get("content"),
	}

	// TODO:评论楼层怎么处理，避免冲突？最后的楼层信息保存在内存中？

	// 暂时只是从数据库中取出最后的评论楼层
	tmpCmt := &model.Comment{}
	_, err := MasterDB.Where("objid=? AND objtype=?", objid, objtype).OrderBy("ctime DESC").Get(tmpCmt)
	if err != nil {
		objLog.Errorln("post comment service error:", err)
		return nil, err
	} else {
		comment.Floor = tmpCmt.Floor + 1
	}

	if tmpCmt.Uid == comment.Uid && tmpCmt.Content == comment.Content {
		objLog.Infof("had post comment: %+v", *comment)
		return tmpCmt, nil
	}

	// 入评论库
	_, err = MasterDB.Insert(comment)
	if err != nil {
		objLog.Errorln("post comment service error:", err)
		return nil, err
	}
	self.decodeCmtContentForShow(ctx, comment)

	// 回调，不关心处理结果（有些对象可能不需要回调）
	if commenter, ok := commenters[objtype]; ok {
		now := time.Now()

		objLog.Debugf("评论[objid:%d] [objtype:%d] [uid:%d] 成功，通知被评论者更新", objid, objtype, uid)
		go commenter.UpdateComment(comment.Cid, objid, uid, now)

		DefaultFeed.updateComment(objid, objtype, uid, now)
	}

	go commentObservable.NotifyObservers(uid, objtype, comment.Cid)

	go self.sendSystemMsg(ctx, uid, objid, objtype, comment.Cid, form)

	return comment, nil
}

func (CommentLogic) sendSystemMsg(ctx context.Context, uid, objid, objtype, cid int, form url.Values) {
	// 给被评论对象所有者发系统消息 TODO: ext 考虑结构化
	ext := map[string]interface{}{
		"objid":   objid,
		"objtype": objtype,
		"cid":     cid,
		"uid":     uid,
	}

	to := 0
	switch objtype {
	case model.TypeTopic:
		to = DefaultTopic.getOwner(objid)
	case model.TypeArticle:
		to = DefaultArticle.getOwner(objid)
	case model.TypeResource:
		to = DefaultResource.getOwner(objid)
	case model.TypeWiki:
		to = DefaultWiki.getOwner(objid)
	case model.TypeProject:
		to = DefaultProject.getOwner(ctx, objid)
	}

	DefaultMessage.SendSystemMsgTo(ctx, to, objtype, ext)

	// @某人 发系统消息
	DefaultMessage.SendSysMsgAtUids(ctx, form.Get("uid"), ext, to)
	DefaultMessage.SendSysMsgAtUsernames(ctx, form.Get("usernames"), ext, to)
}

// Modify 修改评论信息
func (CommentLogic) Modify(ctx context.Context, cid int, content string) (errMsg string, err error) {
	objLog := GetLogger(ctx)

	_, err = MasterDB.Table(new(model.Comment)).Id(cid).Update(map[string]interface{}{"content": content})
	if err != nil {
		objLog.Errorf("更新评论内容 【%d】 失败：%s", cid, err)
		errMsg = "对不起，服务器内部错误，请稍后再试！"
		return
	}

	return
}

// fillObjinfos 填充评论对应的主体信息
func (CommentLogic) fillObjinfos(comments []*model.Comment, cmtObj CommentObjecter) {
	if len(comments) == 0 {
		return
	}
	count := len(comments)
	commentMap := make(map[int][]*model.Comment, count)
	idSet := set.New(set.NonThreadSafe)
	for _, comment := range comments {
		if _, ok := commentMap[comment.Objid]; !ok {
			commentMap[comment.Objid] = make([]*model.Comment, 0, count)
		}
		commentMap[comment.Objid] = append(commentMap[comment.Objid], comment)
		idSet.Add(comment.Objid)
	}
	cmtObj.SetObjinfo(set.IntSlice(idSet), commentMap)
}

// 提供给其他service调用（包内）
func (CommentLogic) findByIds(cids []int) map[int]*model.Comment {
	if len(cids) == 0 {
		return nil
	}

	comments := make(map[int]*model.Comment)
	err := MasterDB.In("cid", cids).Find(&comments)
	if err != nil {
		return nil
	}

	return comments
}

func (CommentLogic) findById(cid int) *model.Comment {
	comment := &model.Comment{}
	_, err := MasterDB.Where("cid=?", cid).Get(comment)
	if err != nil {
		logger.Errorln("CommentLogic findById error:", err)
	}

	return comment
}

func (CommentLogic) decodeCmtContent(ctx context.Context, comment *model.Comment) string {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)
	// @别人
	content = parseAtUser(ctx, content)

	// 回复某一楼层
	reg := regexp.MustCompile(`#(\d+)楼`)
	url := fmt.Sprintf("%s%d#comment", model.PathUrlMap[comment.Objtype], comment.Objid)
	content = reg.ReplaceAllString(content, `<a href="`+url+`$1" title="$1">#$1<span>楼</span></a>`)

	comment.Content = content

	return content
}

// decodeCmtContentForShow 采用引用的方式显示对其他楼层的回复
func (CommentLogic) decodeCmtContentForShow(ctx context.Context, comment *model.Comment) {
	// 安全过滤
	content := template.HTMLEscapeString(comment.Content)

	// 回复某一楼层
	reg := regexp.MustCompile(`#(\d+)楼 @([a-zA-Z0-9_]+)`)
	matches := reg.FindStringSubmatch(content)
	if len(matches) > 2 {
		comment.ReplyFloor = goutils.MustInt(matches[1])
		content = strings.TrimSpace(content[len(matches[0]):])
	}

	// @别人
	content = parseAtUser(ctx, content)

	comment.Content = content
}

// 填充 Comment 对象的 Objinfo 成员接口
// 评论属主应该实现该接口（以便填充 Objinfo 成员）
type CommentObjecter interface {
	// ids 是属主的主键 slice （comment 中的 objid）
	// commentMap 中的 key 是属主 id
	SetObjinfo(ids []int, commentMap map[int][]*model.Comment)
}
<|endoftext|>


<commit_before>//
// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

const (
	//baseProductLoc string = "/etc/products.d/baseproduct"
	baseProductLoc string = "baseproduct.xml"
)

type InstalledProduct struct {
	Identifier string `xml:"name"`
	Version string `xml:"version`
	Arch string `xml:"arch"`
}

// parses installed product data
func ParseInstalledProduct(reader io.Reader) (InstalledProduct, error) {
	xmlData, err := ioutil.ReadAll(reader)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't read base product file: %v", err.Error())
	}

	var p InstalledProduct
	xml.Unmarshal(xmlData, &p)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't parse base product file: %v", err.Error())
	}

	return p, nil
}

// read the product file from the standard location
func ReadInstalledProduct() (InstalledProduct, error) {
	if _, err := os.Stat(baseProductLoc); os.IsNotExist(err) {
		return InstalledProduct{}, fmt.Errorf("No base product detected")
	}

	xmlFile, err := os.Open(baseProductLoc)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't open base product file: %v", err.Error())
	}
	defer xmlFile.Close()

	return ParseInstalledProduct(xmlFile)
}
<commit_msg>fix product location<commit_after>//
// Copyright (c) 2015 SUSE LLC. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package main

import (
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

const (
	baseProductLoc string = "/etc/products.d/baseproduct"
)

type InstalledProduct struct {
	Identifier string `xml:"name"`
	Version string `xml:"version`
	Arch string `xml:"arch"`
}

// parses installed product data
func ParseInstalledProduct(reader io.Reader) (InstalledProduct, error) {
	xmlData, err := ioutil.ReadAll(reader)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't read base product file: %v", err.Error())
	}

	var p InstalledProduct
	xml.Unmarshal(xmlData, &p)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't parse base product file: %v", err.Error())
	}

	return p, nil
}

// read the product file from the standard location
func ReadInstalledProduct() (InstalledProduct, error) {
	if _, err := os.Stat(baseProductLoc); os.IsNotExist(err) {
		return InstalledProduct{}, fmt.Errorf("No base product detected")
	}

	xmlFile, err := os.Open(baseProductLoc)
	if err != nil {
		return InstalledProduct{}, fmt.Errorf("Can't open base product file: %v", err.Error())
	}
	defer xmlFile.Close()

	return ParseInstalledProduct(xmlFile)
}
<|endoftext|>


<commit_before>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	usr        user.User
	tempfiles  []string
	filename   = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	sha256Hash = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// Install installs Hugo
func Install() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy := filepath.Clean(usr.HomeDir + "/.caddy/")
	bin := filepath.Clean(caddy + "/bin")
	temp := filepath.Clean(caddy + "/temp")
	hugo := filepath.Clean(bin + "/hugo")

	switch runtime.GOOS {
	case "darwin":
		filename += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			filename += "32-bit-only"
		}

		filename += ".zip"
		hugo += ".exe"
	default:
		filename += ".tar.gz"
	}

	// Check if Hugo is already installed
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on " + caddy)

	err = os.MkdirAll(caddy, 0666)
	err = os.Mkdir(bin, 0666)
	err = os.Mkdir(temp, 0666)

	if !os.IsExist(err) {
		fmt.Println(err)
		os.Exit(-1)
	}

	tempfile := temp + "/" + filename

	// Create the file
	tempfiles = append(tempfiles, tempfile)
	out, err := os.Create(tempfile)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Get the data
	resp, err := http.Get(baseurl + filename)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[filename] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
	fmt.Print("Unziping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, bin)
	default:
		err = ungzip(tempfile, bin)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	tempfiles = append(tempfiles, bin+"/README.md", bin+"/LICENSE.md")
	clean()

	ftorename := bin + "/" + strings.Replace(filename, ".tar.gz", "", 1)

	if runtime.GOOS == "windows" {
		ftorename = bin + "/" + strings.Replace(filename, ".zip", ".exe", 1)
	}

	os.Rename(ftorename, hugo)
	fmt.Println("Hugo installed at " + hugo)
	return hugo
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")

	for _, file := range tempfiles {
		os.Remove(file)
	}

	fmt.Println("done.")
}
<commit_msg>update file perm<commit_after>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
	"strings"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	usr        user.User
	tempfiles  []string
	filename   = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	sha256Hash = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// Install installs Hugo
func Install() string {
	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy := filepath.Clean(usr.HomeDir + "/.caddy/")
	bin := filepath.Clean(caddy + "/bin")
	temp := filepath.Clean(caddy + "/temp")
	hugo := filepath.Clean(bin + "/hugo")

	switch runtime.GOOS {
	case "darwin":
		filename += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			filename += "32-bit-only"
		}

		filename += ".zip"
		hugo += ".exe"
	default:
		filename += ".tar.gz"
	}

	// Check if Hugo is already installed
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on " + caddy)

	err = os.MkdirAll(caddy, 0774)
	err = os.Mkdir(bin, 0774)
	err = os.Mkdir(temp, 0774)

	if !os.IsExist(err) {
		fmt.Println(err)
		os.Exit(-1)
	}

	tempfile := temp + "/" + filename

	// Create the file
	tempfiles = append(tempfiles, tempfile)
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Get the data
	resp, err := http.Get(baseurl + filename)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[filename] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
	fmt.Print("Unziping... ")

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, bin)
	default:
		err = ungzip(tempfile, bin)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	tempfiles = append(tempfiles, bin+"/README.md", bin+"/LICENSE.md")
	clean()

	ftorename := bin + "/" + strings.Replace(filename, ".tar.gz", "", 1)

	if runtime.GOOS == "windows" {
		ftorename = bin + "/" + strings.Replace(filename, ".zip", ".exe", 1)
	}

	os.Rename(ftorename, hugo)
	fmt.Println("Hugo installed at " + hugo)
	return hugo
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")

	for _, file := range tempfiles {
		os.Remove(file)
	}

	fmt.Println("done.")
}
<|endoftext|>


<commit_before>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/user"
	"path/filepath"
	"runtime"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	caddy, bin, temp, hugo, tempfile, zipname, exename string
	sha256Hash                                         = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// GetPath retrives the Hugo path for the user or install it if it's not found
func GetPath() string {
	initializeVariables()

	/*	// Check if Hugo is already on $PATH
		if hugo, err := exec.LookPath("hugo"); err == nil {
			return hugo
		} */

	// Check if Hugo is on $HOME/.caddy/bin
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on your computer.")

	// Create the neccessary folders
	os.MkdirAll(caddy, 0774)
	os.Mkdir(bin, 0774)
	os.Mkdir(temp, 0774)

	downloadHugo()
	checkSHA256()

	fmt.Print("Unziping... ")

	var err error

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, temp)
	default:
		err = ungzip(tempfile, temp)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	var exetorename string

	err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error {
		if f.Name() == exename {
			exetorename = path
		}

		return nil
	})

	err = os.Rename(exetorename, hugo)

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("Hugo installed at " + hugo)
	clean()
	return hugo
}

func initializeVariables() {
	exename = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	zipname = exename

	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy = filepath.Join(usr.HomeDir, ".caddy")
	bin = filepath.Join(caddy, "bin")
	temp = filepath.Join(caddy, "temp")
	hugo = filepath.Join(bin, "hugo")

	switch runtime.GOOS {
	case "darwin":
		zipname += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			zipname += "32-bit-only"
		}

		zipname += ".zip"
		exename += ".exe"
		hugo += ".exe"
	default:
		zipname += ".tar.gz"
	}
}

func downloadHugo() {
	tempfile = filepath.Join(temp, zipname)

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + zipname)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
}

func checkSHA256() {
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")
	os.RemoveAll(temp)
	fmt.Println("done.")
}
<commit_msg>uncomment if<commit_after>package insthugo

import (
	"archive/zip"
	"compress/gzip"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"io"
	"log"
	"net/http"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"runtime"
)

const (
	version = "0.15"
	baseurl = "https://github.com/spf13/hugo/releases/download/v" + version + "/"
)

var (
	caddy, bin, temp, hugo, tempfile, zipname, exename string
	sha256Hash                                         = map[string]string{
		"hugo_0.15_darwin_386.zip":              "f9b7353f9b64e7aece5f7981e5aa97dc4b31974ce76251edc070e77691bc03e2",
		"hugo_0.15_darwin_amd64.zip":            "aeecd6a12d86ab920f5b04e9486474bbe478dc246cdc2242799849b84c61c6f1",
		"hugo_0.15_dragonfly_amd64.zip":         "e380343789f2b2e0c366c8e1eeb251ccd90eea53dac191ff85d8177b130e53bc",
		"hugo_0.15_freebsd_386.zip":             "98f9210bfa3dcb48bd154879ea1cfe1b0ed8a3d891fdeacbdb4c3fc69b72aac4",
		"hugo_0.15_freebsd_amd64.zip":           "aa6a3028899e76e6920b9b5a64c29e14017ae34120efa67276e614e3a69cb100",
		"hugo_0.15_freebsd_arm.zip":             "de52e1b07caf778bdc3bdb07f39119cd5a1739c8822ebe311cd4f667c43588ac",
		"hugo_0.15_linux_386.tar.gz":            "af28c4cbb16db765535113f361a38b2249c634ce2d3798dcf5b795de6e4b7ecf",
		"hugo_0.15_linux_amd64.tar.gz":          "32a6335bd76f72867efdec9306a8a7eb7b9498a2e0478105efa96c1febadb09b",
		"hugo_0.15_linux_arm.tar.gz":            "886dd1a843c057a46c541011183dd558469250580e81450eedbd1a4d041e9234",
		"hugo_0.15_netbsd_386.zip":              "6245f5db16b33a09466f149d5b7b68a7899d6d624903de9e7e70c4b6ea869a72",
		"hugo_0.15_netbsd_amd64.zip":            "103ea8d81d2a3d707c05e3dd68c98fcf8146ddd36b49bf0e65d9874cee230c88",
		"hugo_0.15_netbsd_arm.zip":              "9c9b5cf4ea3b6169be1b5fc924251a247d9c140dd8a45aa5175031878585ff0a",
		"hugo_0.15_openbsd_386.zip":             "81dfdb3048a27a61b249650241fe4e8da1eda31a3a7311c615eb419f1cdd06b1",
		"hugo_0.15_openbsd_amd64.zip":           "e7447cde0dd7628b05b25b86938018774d8db8156ab1330b364e0e2c6501ad87",
		"hugo_0.15_windows_386_32-bit-only.zip": "0a72f9a1a929f36c0e52fb1c6272b4d37a2bd1a6bd19ce57a6e7b6803b434756",
		"hugo_0.15_windows_amd64.zip":           "9f03602e48ae2199e06431d7436fb3b9464538c0d44aac9a76eb98e1d4d5d727",
	}
)

// GetPath retrives the Hugo path for the user or install it if it's not found
func GetPath() string {
	initializeVariables()

	// Check if Hugo is already on $PATH
	if hugo, err := exec.LookPath("hugo"); err == nil {
		return hugo
	}

	// Check if Hugo is on $HOME/.caddy/bin
	if _, err := os.Stat(hugo); err == nil {
		return hugo
	}

	fmt.Println("Unable to find Hugo on your computer.")

	// Create the neccessary folders
	os.MkdirAll(caddy, 0774)
	os.Mkdir(bin, 0774)
	os.Mkdir(temp, 0774)

	downloadHugo()
	checkSHA256()

	fmt.Print("Unziping... ")

	var err error

	// Unzip or Ungzip the file
	switch runtime.GOOS {
	case "darwin", "windows":
		err = unzip(tempfile, temp)
	default:
		err = ungzip(tempfile, temp)
	}

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("done.")

	var exetorename string

	err = filepath.Walk(temp, func(path string, f os.FileInfo, err error) error {
		if f.Name() == exename {
			exetorename = path
		}

		return nil
	})

	err = os.Rename(exetorename, hugo)

	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("Hugo installed at " + hugo)
	clean()
	return hugo
}

func initializeVariables() {
	exename = "hugo_" + version + "_" + runtime.GOOS + "_" + runtime.GOARCH
	zipname = exename

	usr, err := user.Current()
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	caddy = filepath.Join(usr.HomeDir, ".caddy")
	bin = filepath.Join(caddy, "bin")
	temp = filepath.Join(caddy, "temp")
	hugo = filepath.Join(bin, "hugo")

	switch runtime.GOOS {
	case "darwin":
		zipname += ".zip"
	case "windows":
		// At least for v0.15 version
		if runtime.GOARCH == "386" {
			zipname += "32-bit-only"
		}

		zipname += ".zip"
		exename += ".exe"
		hugo += ".exe"
	default:
		zipname += ".tar.gz"
	}
}

func downloadHugo() {
	tempfile = filepath.Join(temp, zipname)

	fmt.Print("Downloading Hugo from GitHub releases... ")

	// Create the file
	out, err := os.Create(tempfile)
	out.Chmod(0774)
	if err != nil {
		clean()
		fmt.Println(err)
		os.Exit(-1)
	}
	defer out.Close()

	// Get the data
	resp, err := http.Get(baseurl + zipname)
	if err != nil {
		fmt.Println("An error ocurred while downloading. If this error persists, try downloading Hugo from \"https://github.com/spf13/hugo/releases/\" and put the executable in " + bin + " and rename it to 'hugo' or 'hugo.exe' if you're on Windows.")
		fmt.Println(err)
		os.Exit(-1)
	}
	defer resp.Body.Close()

	// Writer the body to file
	_, err = io.Copy(out, resp.Body)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	fmt.Println("downloaded.")
}

func checkSHA256() {
	fmt.Print("Checking SHA256...")

	hasher := sha256.New()
	f, err := os.Open(tempfile)
	if err != nil {
		log.Fatal(err)
	}
	defer f.Close()
	if _, err := io.Copy(hasher, f); err != nil {
		log.Fatal(err)
	}

	if hex.EncodeToString(hasher.Sum(nil)) != sha256Hash[zipname] {
		fmt.Println("can't verify SHA256.")
		os.Exit(-1)
	}

	fmt.Println("checked!")
}

func unzip(archive, target string) error {
	reader, err := zip.OpenReader(archive)
	if err != nil {
		return err
	}

	if err := os.MkdirAll(target, 0755); err != nil {
		return err
	}

	for _, file := range reader.File {
		path := filepath.Join(target, file.Name)
		if file.FileInfo().IsDir() {
			os.MkdirAll(path, file.Mode())
			continue
		}

		fileReader, err := file.Open()
		if err != nil {
			return err
		}
		defer fileReader.Close()

		targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
		if err != nil {
			return err
		}
		defer targetFile.Close()

		if _, err := io.Copy(targetFile, fileReader); err != nil {
			return err
		}
	}

	return nil
}

func ungzip(source, target string) error {
	reader, err := os.Open(source)
	if err != nil {
		return err
	}
	defer reader.Close()

	archive, err := gzip.NewReader(reader)
	if err != nil {
		return err
	}
	defer archive.Close()

	target = filepath.Join(target, archive.Name)
	writer, err := os.Create(target)
	if err != nil {
		return err
	}
	defer writer.Close()

	_, err = io.Copy(writer, archive)
	return err
}

func clean() {
	fmt.Print("Removing temporary files... ")
	os.RemoveAll(temp)
	fmt.Println("done.")
}
<|endoftext|>


<commit_before>// Copyright 2017, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal // import "go.opencensus.io/internal"

import "time"

// UserAgent is the user agent to be added to the outgoing
// requests from the exporters.
const UserAgent = "opencensus-go [0.9.0]"

// MonotonicEndTime returns the end time at present
// but offset from start, monotonically.
//
// The monotonic clock is used in subtractions hence
// the duration since start added back to start gives
// end as a monotonic time.
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
func MonotonicEndTime(start time.Time) time.Time {
	return start.Add(time.Now().Sub(start))
}
<commit_msg>Update UserAgent version (#753)<commit_after>// Copyright 2017, OpenCensus Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package internal // import "go.opencensus.io/internal"

import "time"

// UserAgent is the user agent to be added to the outgoing
// requests from the exporters.
const UserAgent = "opencensus-go [0.10.0]"

// MonotonicEndTime returns the end time at present
// but offset from start, monotonically.
//
// The monotonic clock is used in subtractions hence
// the duration since start added back to start gives
// end as a monotonic time.
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
func MonotonicEndTime(start time.Time) time.Time {
	return start.Add(time.Now().Sub(start))
}
<|endoftext|>


<commit_before>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	BaseResource    Resourcer
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.BaseResource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	var roles = []interface{}{}
	for _, role := range context.Roles {
		roles = append(roles, role)
	}
	return meta.Permission.HasPermission(mode, roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.BaseResource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.BaseResource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if relationship := f.Relationship; relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						if (relationship.Kind == "has_many" || relationship.Kind == "many_to_many") && f.Field.Len() == 0 {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						} else if (relationship.Kind == "has_one" || relationship.Kind == "belongs_to") && context.GetDB().NewScope(f.Field.Interface()).PrimaryKeyZero() {
							if f.Field.Kind() == reflect.Ptr && f.Field.IsNil() {
								f.Field.Set(reflect.New(f.Field.Type().Elem()))
							}

							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.BaseResource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						// set current field value to blank and replace it with new value
						field.Set(reflect.Zero(field.Type()))
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								scanner.Scan(utils.ToString(value))
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if context.GetDB().NewRecord(submodel.Interface()) && !context.GetDB().NewRecord(model.Addr().Interface()) {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<commit_msg>1. fix sql scan bug<commit_after>package resource

import (
	"bytes"
	"database/sql"
	"encoding/json"
	"fmt"
	"reflect"
	"strings"
	"time"

	"github.com/jinzhu/gorm"
	"github.com/qor/qor"
	"github.com/qor/qor/utils"
	"github.com/qor/roles"
	"github.com/qor/validations"
)

// Metaor interface
type Metaor interface {
	GetName() string
	GetFieldName() string
	GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	GetFormattedValuer() func(interface{}, *qor.Context) interface{}
	GetValuer() func(interface{}, *qor.Context) interface{}
	GetResource() Resourcer
	GetMetas() []Metaor
	HasPermission(roles.PermissionMode, *qor.Context) bool
}

// ConfigureMetaBeforeInitializeInterface if a struct's field's type implemented this interface, it will be called when initializing a meta
type ConfigureMetaBeforeInitializeInterface interface {
	ConfigureQorMetaBeforeInitialize(Metaor)
}

// ConfigureMetaInterface if a struct's field's type implemented this interface, it will be called after configed
type ConfigureMetaInterface interface {
	ConfigureQorMeta(Metaor)
}

// MetaConfigInterface meta configuration interface
type MetaConfigInterface interface {
	ConfigureMetaInterface
}

// MetaConfig base meta config struct
type MetaConfig struct {
}

// ConfigureQorMeta implement the MetaConfigInterface
func (MetaConfig) ConfigureQorMeta(Metaor) {
}

// Meta meta struct definition
type Meta struct {
	Name            string
	FieldName       string
	FieldStruct     *gorm.StructField
	Setter          func(resource interface{}, metaValue *MetaValue, context *qor.Context)
	Valuer          func(interface{}, *qor.Context) interface{}
	FormattedValuer func(interface{}, *qor.Context) interface{}
	Config          MetaConfigInterface
	BaseResource    Resourcer
	Resource        Resourcer
	Permission      *roles.Permission
}

// GetBaseResource get base resource from meta
func (meta Meta) GetBaseResource() Resourcer {
	return meta.BaseResource
}

// GetName get meta's name
func (meta Meta) GetName() string {
	return meta.Name
}

// GetFieldName get meta's field name
func (meta Meta) GetFieldName() string {
	return meta.FieldName
}

// SetFieldName set meta's field name
func (meta *Meta) SetFieldName(name string) {
	meta.FieldName = name
}

// GetSetter get setter from meta
func (meta Meta) GetSetter() func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
	return meta.Setter
}

// SetSetter set setter to meta
func (meta *Meta) SetSetter(fc func(resource interface{}, metaValue *MetaValue, context *qor.Context)) {
	meta.Setter = fc
}

// GetValuer get valuer from meta
func (meta Meta) GetValuer() func(interface{}, *qor.Context) interface{} {
	return meta.Valuer
}

// SetValuer set valuer for meta
func (meta *Meta) SetValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.Valuer = fc
}

// GetFormattedValuer get formatted valuer from meta
func (meta *Meta) GetFormattedValuer() func(interface{}, *qor.Context) interface{} {
	if meta.FormattedValuer != nil {
		return meta.FormattedValuer
	}
	return meta.Valuer
}

// SetFormattedValuer set formatted valuer for meta
func (meta *Meta) SetFormattedValuer(fc func(interface{}, *qor.Context) interface{}) {
	meta.FormattedValuer = fc
}

// HasPermission check has permission or not
func (meta Meta) HasPermission(mode roles.PermissionMode, context *qor.Context) bool {
	if meta.Permission == nil {
		return true
	}
	var roles = []interface{}{}
	for _, role := range context.Roles {
		roles = append(roles, role)
	}
	return meta.Permission.HasPermission(mode, roles...)
}

// SetPermission set permission for meta
func (meta *Meta) SetPermission(permission *roles.Permission) {
	meta.Permission = permission
}

// PreInitialize when will be run before initialize, used to fill some basic necessary information
func (meta *Meta) PreInitialize() error {
	if meta.Name == "" {
		utils.ExitWithMsg("Meta should have name: %v", reflect.TypeOf(meta))
	} else if meta.FieldName == "" {
		meta.FieldName = meta.Name
	}

	// parseNestedField used to handle case like Profile.Name
	var parseNestedField = func(value reflect.Value, name string) (reflect.Value, string) {
		fields := strings.Split(name, ".")
		value = reflect.Indirect(value)
		for _, field := range fields[:len(fields)-1] {
			value = value.FieldByName(field)
		}

		return value, fields[len(fields)-1]
	}

	var getField = func(fields []*gorm.StructField, name string) *gorm.StructField {
		for _, field := range fields {
			if field.Name == name || field.DBName == name {
				return field
			}
		}
		return nil
	}

	var nestedField = strings.Contains(meta.FieldName, ".")
	var scope = &gorm.Scope{Value: meta.BaseResource.GetResource().Value}
	if nestedField {
		subModel, name := parseNestedField(reflect.ValueOf(meta.BaseResource.GetResource().Value), meta.FieldName)
		meta.FieldStruct = getField(scope.New(subModel.Interface()).GetStructFields(), name)
	} else {
		meta.FieldStruct = getField(scope.GetStructFields(), meta.FieldName)
	}
	return nil
}

// Initialize initialize meta, will set valuer, setter if haven't configure it
func (meta *Meta) Initialize() error {
	var (
		nestedField = strings.Contains(meta.FieldName, ".")
		field       = meta.FieldStruct
		hasColumn   = meta.FieldStruct != nil
	)

	var fieldType reflect.Type
	if hasColumn {
		fieldType = field.Struct.Type
		for fieldType.Kind() == reflect.Ptr {
			fieldType = fieldType.Elem()
		}
	}

	// Set Meta Valuer
	if meta.Valuer == nil {
		if hasColumn {
			meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
				scope := context.GetDB().NewScope(value)
				fieldName := meta.FieldName
				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				if f, ok := scope.FieldByName(fieldName); ok {
					if relationship := f.Relationship; relationship != nil && f.Field.CanAddr() && !scope.PrimaryKeyZero() {
						if (relationship.Kind == "has_many" || relationship.Kind == "many_to_many") && f.Field.Len() == 0 {
							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						} else if (relationship.Kind == "has_one" || relationship.Kind == "belongs_to") && context.GetDB().NewScope(f.Field.Interface()).PrimaryKeyZero() {
							if f.Field.Kind() == reflect.Ptr && f.Field.IsNil() {
								f.Field.Set(reflect.New(f.Field.Type().Elem()))
							}

							context.GetDB().Model(value).Related(f.Field.Addr().Interface(), meta.FieldName)
						}
					}

					return f.Field.Interface()
				}

				return ""
			}
		} else {
			utils.ExitWithMsg("Meta %v is not supported for resource %v, no `Valuer` configured for it", meta.FieldName, reflect.TypeOf(meta.BaseResource.GetResource().Value))
		}
	}

	if meta.Setter == nil && hasColumn {
		if relationship := field.Relationship; relationship != nil {
			if relationship.Kind == "belongs_to" || relationship.Kind == "many_to_many" {
				meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
					scope := &gorm.Scope{Value: resource}
					reflectValue := reflect.Indirect(reflect.ValueOf(resource))
					field := reflectValue.FieldByName(meta.FieldName)

					if field.Kind() == reflect.Ptr {
						if field.IsNil() {
							field.Set(utils.NewValue(field.Type()).Elem())
						}

						for field.Kind() == reflect.Ptr {
							field = field.Elem()
						}
					}

					primaryKeys := utils.ToArray(metaValue.Value)
					// associations not changed for belongs to
					if relationship.Kind == "belongs_to" && len(relationship.ForeignFieldNames) == 1 {
						oldPrimaryKeys := utils.ToArray(reflectValue.FieldByName(relationship.ForeignFieldNames[0]).Interface())
						// if not changed
						if fmt.Sprint(primaryKeys) == fmt.Sprint(oldPrimaryKeys) {
							return
						}

						// if removed
						if len(primaryKeys) == 0 {
							field := reflectValue.FieldByName(relationship.ForeignFieldNames[0])
							field.Set(reflect.Zero(field.Type()))
						}
					}

					if len(primaryKeys) > 0 {
						// set current field value to blank and replace it with new value
						field.Set(reflect.Zero(field.Type()))
						context.GetDB().Where(primaryKeys).Find(field.Addr().Interface())
					}

					// Replace many 2 many relations
					if relationship.Kind == "many_to_many" {
						if !scope.PrimaryKeyZero() {
							context.GetDB().Model(resource).Association(meta.FieldName).Replace(field.Interface())
							field.Set(reflect.Zero(field.Type()))
						}
					}
				}
			}
		} else {
			meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
				if metaValue == nil {
					return
				}

				var (
					value     = metaValue.Value
					fieldName = meta.FieldName
				)

				defer func() {
					if r := recover(); r != nil {
						context.AddError(validations.NewError(resource, meta.Name, fmt.Sprintf("Can't set value %v", value)))
					}
				}()

				if nestedField {
					fields := strings.Split(fieldName, ".")
					fieldName = fields[len(fields)-1]
				}

				field := reflect.Indirect(reflect.ValueOf(resource)).FieldByName(fieldName)
				if field.Kind() == reflect.Ptr {
					if field.IsNil() && utils.ToString(value) != "" {
						field.Set(utils.NewValue(field.Type()).Elem())
					}

					if utils.ToString(value) == "" {
						field.Set(reflect.Zero(field.Type()))
						return
					}

					for field.Kind() == reflect.Ptr {
						field = field.Elem()
					}
				}

				if field.IsValid() && field.CanAddr() {
					switch field.Kind() {
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						field.SetInt(utils.ToInt(value))
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						field.SetUint(utils.ToUint(value))
					case reflect.Float32, reflect.Float64:
						field.SetFloat(utils.ToFloat(value))
					case reflect.Bool:
						// TODO: add test
						if utils.ToString(value) == "true" {
							field.SetBool(true)
						} else {
							field.SetBool(false)
						}
					default:
						if scanner, ok := field.Addr().Interface().(sql.Scanner); ok {
							if value == nil && len(metaValue.MetaValues.Values) > 0 {
								decodeMetaValuesToField(meta.Resource, field, metaValue, context)
								return
							}

							if scanner.Scan(value) != nil {
								if err := scanner.Scan(utils.ToString(value)); err != nil {
									context.AddError(err)
									return
								}
							}
						} else if reflect.TypeOf("").ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToString(value)).Convert(field.Type()))
						} else if reflect.TypeOf([]string{}).ConvertibleTo(field.Type()) {
							field.Set(reflect.ValueOf(utils.ToArray(value)).Convert(field.Type()))
						} else if rvalue := reflect.ValueOf(value); reflect.TypeOf(rvalue.Type()).ConvertibleTo(field.Type()) {
							field.Set(rvalue.Convert(field.Type()))
						} else if _, ok := field.Addr().Interface().(*time.Time); ok {
							if str := utils.ToString(value); str != "" {
								if newTime, err := utils.ParseTime(str, context); err == nil {
									field.Set(reflect.ValueOf(newTime))
								}
							} else {
								field.Set(reflect.Zero(field.Type()))
							}
						} else {
							var buf = bytes.NewBufferString("")
							json.NewEncoder(buf).Encode(value)
							if err := json.NewDecoder(strings.NewReader(buf.String())).Decode(field.Addr().Interface()); err != nil {
								utils.ExitWithMsg("Can't set value %v to %v [meta %v]", reflect.TypeOf(value), field.Type(), meta)
							}
						}
					}
				}
			}
		}
	}

	if nestedField {
		oldvalue := meta.Valuer
		meta.Valuer = func(value interface{}, context *qor.Context) interface{} {
			return oldvalue(getNestedModel(value, meta.FieldName, context), context)
		}
		oldSetter := meta.Setter
		meta.Setter = func(resource interface{}, metaValue *MetaValue, context *qor.Context) {
			oldSetter(getNestedModel(resource, meta.FieldName, context), metaValue, context)
		}
	}
	return nil
}

func getNestedModel(value interface{}, fieldName string, context *qor.Context) interface{} {
	model := reflect.Indirect(reflect.ValueOf(value))
	fields := strings.Split(fieldName, ".")
	for _, field := range fields[:len(fields)-1] {
		if model.CanAddr() {
			submodel := model.FieldByName(field)
			if context.GetDB().NewRecord(submodel.Interface()) && !context.GetDB().NewRecord(model.Addr().Interface()) {
				if submodel.CanAddr() {
					context.GetDB().Model(model.Addr().Interface()).Association(field).Find(submodel.Addr().Interface())
					model = submodel
				} else {
					break
				}
			} else {
				model = submodel
			}
		}
	}

	if model.CanAddr() {
		return model.Addr().Interface()
	}
	return nil
}
<|endoftext|>


<commit_before>package parser

import (
	"fmt"

	"github.com/nitrogen-lang/nitrogen/src/ast"
	"github.com/nitrogen-lang/nitrogen/src/lexer"
	"github.com/nitrogen-lang/nitrogen/src/token"
)

const (
	priLowest      int = iota
	priCompare         // and, or
	priEquals          // ==
	priLessGreater     // > or <
	priSum             // +, -
	priProduct         // *, /
	priPrefix          // -x or !x
	priCall            // myFunction(x)
	priIndex           // array[index]
	priAssign
)

var precedences = map[token.TokenType]int{
	token.LAnd:        priCompare,
	token.LOr:         priCompare,
	token.Equal:       priEquals,
	token.NotEqual:    priEquals,
	token.LessThan:    priLessGreater,
	token.GreaterThan: priLessGreater,
	token.Plus:        priSum,
	token.Dash:        priSum,
	token.Slash:       priProduct,
	token.Asterisk:    priProduct,
	token.Modulo:      priProduct,
	token.LParen:      priCall,
	token.LSquare:     priIndex,
	token.Assign:      priAssign,
}

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

type Parser struct {
	l      *lexer.Lexer
	errors []string

	lastToken token.Token
	curToken  token.Token
	peekToken token.Token

	insertedTokens []token.Token

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFns  map[token.TokenType]infixParseFn
}

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:              l,
		errors:         []string{},
		insertedTokens: make([]token.Token, 0, 5),
	}

	// Prefix parsing functions
	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.Identifier, p.parseIdentifier)
	p.registerPrefix(token.Integer, p.parseIntegerLiteral)
	p.registerPrefix(token.Float, p.parseFloatLiteral)
	p.registerPrefix(token.Nil, p.parseNullLiteral)
	p.registerPrefix(token.String, p.parseStringLiteral)
	p.registerPrefix(token.True, p.parseBoolean)
	p.registerPrefix(token.False, p.parseBoolean)
	p.registerPrefix(token.LSquare, p.parseArrayLiteral)
	p.registerPrefix(token.LBrace, p.parseHashLiteral)
	p.registerPrefix(token.Function, p.parseFunctionLiteral)
	p.registerPrefix(token.Bang, p.parsePrefixExpression)
	p.registerPrefix(token.Dash, p.parsePrefixExpression)
	p.registerPrefix(token.LParen, p.parseGroupedExpression)
	p.registerPrefix(token.If, p.parseIfExpression)

	// Infix parsing functions
	p.infixParseFns = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.Plus, p.parseInfixExpression)
	p.registerInfix(token.Dash, p.parseInfixExpression)
	p.registerInfix(token.Slash, p.parseInfixExpression)
	p.registerInfix(token.Asterisk, p.parseInfixExpression)
	p.registerInfix(token.Modulo, p.parseInfixExpression)
	p.registerInfix(token.Equal, p.parseInfixExpression)
	p.registerInfix(token.NotEqual, p.parseInfixExpression)
	p.registerInfix(token.LessThan, p.parseInfixExpression)
	p.registerInfix(token.GreaterThan, p.parseInfixExpression)
	p.registerInfix(token.LAnd, p.parseCompareExpression)
	p.registerInfix(token.LOr, p.parseCompareExpression)
	p.registerInfix(token.LParen, p.parseCallExpression)
	p.registerInfix(token.LSquare, p.parseIndexExpression)
	p.registerInfix(token.Assign, p.parseAssignmentStatement)

	// Read the first two tokens to populate curToken and peekToken
	p.nextToken()
	p.nextToken()

	return p
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) registerPrefix(tt token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tt] = fn
}

func (p *Parser) registerInfix(tt token.TokenType, fn infixParseFn) {
	p.infixParseFns[tt] = fn
}

func (p *Parser) addError(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	p.errors = append(p.errors, msg)
}

func (p *Parser) addErrorWithPos(format string, args ...interface{}) {
	if len(args) > 0 {
		args = append([]interface{}{p.curToken.Pos.Line, p.curToken.Pos.Col}, args)
	} else {
		args = []interface{}{p.curToken.Pos.Line, p.curToken.Pos.Col}
	}
	msg := fmt.Sprintf("<%d,%d> "+format, args...)
	p.errors = append(p.errors, msg)
}

func (p *Parser) peekError(t token.TokenType) {
	p.addError(
		"<%d,%d> Incorrect next token. Expected %q, got %q",
		p.peekToken.Pos.Line,
		p.peekToken.Pos.Col,
		t.String(),
		p.peekToken.Type.String(),
	)
}

func (p *Parser) noPrefixParseFnError(t token.TokenType) {
	p.addError("<%d,%d> Invalid prefix: %s", p.curToken.Pos.Line, p.curToken.Pos.Col, t)
}

func (p *Parser) nextToken() {
	p.advanceToken()
	for p.curTokenIs(token.Comment) {
		p.advanceToken()
	}

	if p.curTokenIs(token.Illegal) {
		p.addError("Got illegal token: %s", p.curToken.Literal)
		p.nextToken()
	}
}

func (p *Parser) advanceToken() {
	p.lastToken = p.curToken
	p.curToken = p.peekToken

	if len(p.insertedTokens) > 0 {
		p.peekToken = p.insertedTokens[len(p.insertedTokens)-1]
		p.insertedTokens = p.insertedTokens[:len(p.insertedTokens)-1]
	} else {
		p.peekToken = p.l.NextToken()
	}
}

func (p *Parser) insertToken(t token.Token) {
	p.insertedTokens = append(p.insertedTokens, p.peekToken)
	p.peekToken = t
}

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		if len(p.errors) > 0 {
			return program
		}
		p.nextToken()
	}

	return program
}

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}
	leftExp := prefix()

	for !p.peekTokenIs(token.Semicolon) && precedence < p.peekPrecedence() {
		infix := p.infixParseFns[p.peekToken.Type]
		if infix == nil {
			return leftExp
		}

		p.nextToken()
		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseExpressionStatement() ast.Statement {
	stmt := &ast.ExpressionStatement{Token: p.curToken}

	stmt.Expression = p.parseExpression(priLowest)

	if p.peekTokenIs(token.Semicolon) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) parseBlockStatements() *ast.BlockStatement {
	block := &ast.BlockStatement{
		Token:      p.curToken,
		Statements: []ast.Statement{},
	}
	p.nextToken()

	for !p.curTokenIs(token.RBrace) {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}

	return block
}

func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
	list := []ast.Expression{}

	if p.peekTokenIs(end) {
		p.nextToken()
		return list
	}

	p.nextToken()
	list = append(list, p.parseExpression(priLowest))

	for p.peekTokenIs(token.Comma) {
		p.nextToken()
		p.nextToken()
		list = append(list, p.parseExpression(priLowest))
	}

	if !p.expectPeek(end) {
		return nil
	}

	return list
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	expression := &ast.PrefixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}

	p.nextToken()
	expression.Right = p.parseExpression(priPrefix)
	return expression
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	expression := &ast.InfixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
		Left:     left,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)
	return expression
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()
	exp := p.parseExpression(priLowest)

	if !p.expectPeek(token.RParen) {
		return nil
	}
	return exp
}
<commit_msg>parser: Fixed bug where a comment was in peekToken<commit_after>package parser

import (
	"fmt"

	"github.com/nitrogen-lang/nitrogen/src/ast"
	"github.com/nitrogen-lang/nitrogen/src/lexer"
	"github.com/nitrogen-lang/nitrogen/src/token"
)

const (
	priLowest      int = iota
	priCompare         // and, or
	priEquals          // ==
	priLessGreater     // > or <
	priSum             // +, -
	priProduct         // *, /
	priPrefix          // -x or !x
	priCall            // myFunction(x)
	priIndex           // array[index]
	priAssign
)

var precedences = map[token.TokenType]int{
	token.LAnd:        priCompare,
	token.LOr:         priCompare,
	token.Equal:       priEquals,
	token.NotEqual:    priEquals,
	token.LessThan:    priLessGreater,
	token.GreaterThan: priLessGreater,
	token.Plus:        priSum,
	token.Dash:        priSum,
	token.Slash:       priProduct,
	token.Asterisk:    priProduct,
	token.Modulo:      priProduct,
	token.LParen:      priCall,
	token.LSquare:     priIndex,
	token.Assign:      priAssign,
}

type (
	prefixParseFn func() ast.Expression
	infixParseFn  func(ast.Expression) ast.Expression
)

type Parser struct {
	l      *lexer.Lexer
	errors []string

	lastToken token.Token
	curToken  token.Token
	peekToken token.Token

	insertedTokens []token.Token

	prefixParseFns map[token.TokenType]prefixParseFn
	infixParseFns  map[token.TokenType]infixParseFn
}

func New(l *lexer.Lexer) *Parser {
	p := &Parser{
		l:              l,
		errors:         []string{},
		insertedTokens: make([]token.Token, 0, 5),
	}

	// Prefix parsing functions
	p.prefixParseFns = make(map[token.TokenType]prefixParseFn)
	p.registerPrefix(token.Identifier, p.parseIdentifier)
	p.registerPrefix(token.Integer, p.parseIntegerLiteral)
	p.registerPrefix(token.Float, p.parseFloatLiteral)
	p.registerPrefix(token.Nil, p.parseNullLiteral)
	p.registerPrefix(token.String, p.parseStringLiteral)
	p.registerPrefix(token.True, p.parseBoolean)
	p.registerPrefix(token.False, p.parseBoolean)
	p.registerPrefix(token.LSquare, p.parseArrayLiteral)
	p.registerPrefix(token.LBrace, p.parseHashLiteral)
	p.registerPrefix(token.Function, p.parseFunctionLiteral)
	p.registerPrefix(token.Bang, p.parsePrefixExpression)
	p.registerPrefix(token.Dash, p.parsePrefixExpression)
	p.registerPrefix(token.LParen, p.parseGroupedExpression)
	p.registerPrefix(token.If, p.parseIfExpression)

	// Infix parsing functions
	p.infixParseFns = make(map[token.TokenType]infixParseFn)
	p.registerInfix(token.Plus, p.parseInfixExpression)
	p.registerInfix(token.Dash, p.parseInfixExpression)
	p.registerInfix(token.Slash, p.parseInfixExpression)
	p.registerInfix(token.Asterisk, p.parseInfixExpression)
	p.registerInfix(token.Modulo, p.parseInfixExpression)
	p.registerInfix(token.Equal, p.parseInfixExpression)
	p.registerInfix(token.NotEqual, p.parseInfixExpression)
	p.registerInfix(token.LessThan, p.parseInfixExpression)
	p.registerInfix(token.GreaterThan, p.parseInfixExpression)
	p.registerInfix(token.LAnd, p.parseCompareExpression)
	p.registerInfix(token.LOr, p.parseCompareExpression)
	p.registerInfix(token.LParen, p.parseCallExpression)
	p.registerInfix(token.LSquare, p.parseIndexExpression)
	p.registerInfix(token.Assign, p.parseAssignmentStatement)

	// Read the first two tokens to populate curToken and peekToken
	p.nextToken()
	p.nextToken()

	return p
}

func (p *Parser) Errors() []string {
	return p.errors
}

func (p *Parser) registerPrefix(tt token.TokenType, fn prefixParseFn) {
	p.prefixParseFns[tt] = fn
}

func (p *Parser) registerInfix(tt token.TokenType, fn infixParseFn) {
	p.infixParseFns[tt] = fn
}

func (p *Parser) addError(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	p.errors = append(p.errors, msg)
}

func (p *Parser) addErrorWithPos(format string, args ...interface{}) {
	if len(args) > 0 {
		args = append([]interface{}{p.curToken.Pos.Line, p.curToken.Pos.Col}, args)
	} else {
		args = []interface{}{p.curToken.Pos.Line, p.curToken.Pos.Col}
	}
	msg := fmt.Sprintf("<%d,%d> "+format, args...)
	p.errors = append(p.errors, msg)
}

func (p *Parser) peekError(t token.TokenType) {
	p.addError(
		"<%d,%d> Incorrect next token. Expected %q, got %q",
		p.peekToken.Pos.Line,
		p.peekToken.Pos.Col,
		t.String(),
		p.peekToken.Type.String(),
	)
}

func (p *Parser) noPrefixParseFnError(t token.TokenType) {
	p.addError("<%d,%d> Invalid prefix: %s", p.curToken.Pos.Line, p.curToken.Pos.Col, t)
}

func (p *Parser) nextToken() {
	p.advanceToken()
	for p.peekTokenIs(token.Comment) {
		p.advancePeekToken()
	}

	if p.curTokenIs(token.Illegal) {
		p.addError("Got illegal token: %s", p.curToken.Literal)
		p.nextToken()
	}
}

func (p *Parser) advanceToken() {
	p.lastToken = p.curToken
	p.curToken = p.peekToken
	p.advancePeekToken()
}

func (p *Parser) advancePeekToken() {
	if len(p.insertedTokens) > 0 {
		p.peekToken = p.insertedTokens[len(p.insertedTokens)-1]
		p.insertedTokens = p.insertedTokens[:len(p.insertedTokens)-1]
	} else {
		p.peekToken = p.l.NextToken()
	}
}

func (p *Parser) insertToken(t token.Token) {
	p.insertedTokens = append(p.insertedTokens, p.peekToken)
	p.peekToken = t
}

func (p *Parser) ParseProgram() *ast.Program {
	program := &ast.Program{}
	program.Statements = []ast.Statement{}

	for p.curToken.Type != token.EOF {
		stmt := p.parseStatement()
		if stmt != nil {
			program.Statements = append(program.Statements, stmt)
		}
		if len(p.errors) > 0 {
			return program
		}
		p.nextToken()
	}

	return program
}

func (p *Parser) parseExpression(precedence int) ast.Expression {
	prefix := p.prefixParseFns[p.curToken.Type]
	if prefix == nil {
		p.noPrefixParseFnError(p.curToken.Type)
		return nil
	}
	leftExp := prefix()

	for !p.peekTokenIs(token.Semicolon) && precedence < p.peekPrecedence() {
		infix := p.infixParseFns[p.peekToken.Type]
		if infix == nil {
			return leftExp
		}

		p.nextToken()
		leftExp = infix(leftExp)
	}

	return leftExp
}

func (p *Parser) parseExpressionStatement() ast.Statement {
	stmt := &ast.ExpressionStatement{Token: p.curToken}

	stmt.Expression = p.parseExpression(priLowest)

	if p.peekTokenIs(token.Semicolon) {
		p.nextToken()
	}

	return stmt
}

func (p *Parser) parseBlockStatements() *ast.BlockStatement {
	block := &ast.BlockStatement{
		Token:      p.curToken,
		Statements: []ast.Statement{},
	}
	p.nextToken()

	for !p.curTokenIs(token.RBrace) {
		stmt := p.parseStatement()
		if stmt != nil {
			block.Statements = append(block.Statements, stmt)
		}
		p.nextToken()
	}

	return block
}

func (p *Parser) parseExpressionList(end token.TokenType) []ast.Expression {
	list := []ast.Expression{}

	if p.peekTokenIs(end) {
		p.nextToken()
		return list
	}

	p.nextToken()
	list = append(list, p.parseExpression(priLowest))

	for p.peekTokenIs(token.Comma) {
		p.nextToken()
		p.nextToken()
		list = append(list, p.parseExpression(priLowest))
	}

	if !p.expectPeek(end) {
		return nil
	}

	return list
}

func (p *Parser) parsePrefixExpression() ast.Expression {
	expression := &ast.PrefixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
	}

	p.nextToken()
	expression.Right = p.parseExpression(priPrefix)
	return expression
}

func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression {
	expression := &ast.InfixExpression{
		Token:    p.curToken,
		Operator: p.curToken.Literal,
		Left:     left,
	}

	precedence := p.curPrecedence()
	p.nextToken()
	expression.Right = p.parseExpression(precedence)
	return expression
}

func (p *Parser) parseGroupedExpression() ast.Expression {
	p.nextToken()
	exp := p.parseExpression(priLowest)

	if !p.expectPeek(token.RParen) {
		return nil
	}
	return exp
}
<|endoftext|>


<commit_before>// irmameta parses and prints info about the specified metadata attribute.
package main

import (
	"encoding/base64"
	"fmt"

	"os"
	"time"

	"encoding/json"

	"github.com/mhe/gabi"
	"github.com/mhe/gabi/big"
	"github.com/privacybydesign/irmago"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Println("Usage: irmago path_to_irma_configuration metadata_attribute_in_decimal")
	}

	metaint, ok := new(big.Int).SetString(os.Args[2], 10)
	if !ok {
		fmt.Println("Could not parse argument as decimal integer:")
		os.Exit(1)
	}

	confpath := os.Args[1]
	conf, err := irma.NewConfiguration(confpath, "")
	if err != nil {
		fmt.Println("Failed to parse irma_configuration:", err)
		os.Exit(1)
	}
	err = conf.ParseFolder()
	if err != nil {
		fmt.Println("Failed to parse irma_configuration:", err)
		os.Exit(1)
	}

	meta := irma.MetadataFromInt(metaint, conf)
	typ := meta.CredentialType()
	var key *gabi.PublicKey

	if typ == nil {
		fmt.Println("Unknown credential type, hash:", base64.StdEncoding.EncodeToString(meta.CredentialTypeHash()))
	} else {
		fmt.Println("Identifier      :", typ.Identifier())
		key, err = meta.PublicKey()
		if err != nil {
			fmt.Println("Failed to parse public key", err)
		}
	}
	fmt.Println("Signed          :", meta.SigningDate().String())
	fmt.Println("Expires         :", meta.Expiry().String())
	fmt.Println("IsValid         :", meta.IsValid())
	fmt.Println("Version         :", meta.Version())
	fmt.Println("KeyCounter      :", meta.KeyCounter())
	if key != nil {
		fmt.Println("KeyExpires      :", time.Unix(key.ExpiryDate, 0))
		fmt.Println("KeyModulusBitlen:", key.N.BitLen())
	}

	fmt.Println()
	fmt.Println("CredentialType  :", prettyprint(typ))
}

func prettyprint(ob interface{}) string {
	b, err := json.MarshalIndent(ob, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	return string(b)
}
<commit_msg>Support base64 in irmameta cli tool<commit_after>// irmameta parses and prints info about the specified metadata attribute.
package main

import (
	"encoding/base64"
	"fmt"

	"os"
	"time"

	"encoding/json"

	"github.com/mhe/gabi"
	"github.com/mhe/gabi/big"
	"github.com/privacybydesign/irmago"
)

func main() {
	if len(os.Args) != 3 {
		fmt.Println("Usage: irmago path_to_irma_configuration metadata_attribute_in_decimal")
	}

	metaint := new(big.Int)
	_, ok := metaint.SetString(os.Args[2], 10)
	if !ok {
		bts, err := base64.StdEncoding.DecodeString(os.Args[2])
		if err != nil {
			fmt.Println("Could not parse argument as decimal or base64 integer: ", err.Error())
			os.Exit(1)
		}
		metaint.SetBytes(bts)
	}

	confpath := os.Args[1]
	conf, err := irma.NewConfiguration(confpath, "")
	if err != nil {
		fmt.Println("Failed to parse irma_configuration:", err)
		os.Exit(1)
	}
	err = conf.ParseFolder()
	if err != nil {
		fmt.Println("Failed to parse irma_configuration:", err)
		os.Exit(1)
	}

	meta := irma.MetadataFromInt(metaint, conf)
	typ := meta.CredentialType()
	var key *gabi.PublicKey

	if typ == nil {
		fmt.Println("Unknown credential type, hash:", base64.StdEncoding.EncodeToString(meta.CredentialTypeHash()))
	} else {
		fmt.Println("Identifier      :", typ.Identifier())
		key, err = meta.PublicKey()
		if err != nil {
			fmt.Println("Failed to parse public key", err)
		}
	}
	fmt.Println("Signed          :", meta.SigningDate().String())
	fmt.Println("Expires         :", meta.Expiry().String())
	fmt.Println("IsValid         :", meta.IsValid())
	fmt.Println("Version         :", meta.Version())
	fmt.Println("KeyCounter      :", meta.KeyCounter())
	if key != nil {
		fmt.Println("KeyExpires      :", time.Unix(key.ExpiryDate, 0))
		fmt.Println("KeyModulusBitlen:", key.N.BitLen())
	}

	fmt.Println()
	fmt.Println("CredentialType  :", prettyprint(typ))
}

func prettyprint(ob interface{}) string {
	b, err := json.MarshalIndent(ob, "", "  ")
	if err != nil {
		fmt.Println("error:", err)
	}
	return string(b)
}
<|endoftext|>


<commit_before>package aiff

import (
	"bytes"
	"os"
	"testing"
)

func TestEncoderRoundTrip(t *testing.T) {
	os.Mkdir("testOutput", 0777)
	testCases := []struct {
		in  string
		out string
	}{
		// 22050, 8bit, mono
		{"fixtures/kick8b.aiff", "testOutput/kick8b.aiff"},
		// 22050, 16bit, mono
		{"fixtures/kick.aif", "testOutput/kick.aif"},
		// 22050, 16bit, mono
		{"fixtures/kick32b.aiff", "testOutput/kick32b.aiff"},
		// 44100, 16bit, mono
		{"fixtures/subsynth.aif", "testOutput/subsynth.aif"},
		// 44100, 16bit, stereo
		{"fixtures/bloop.aif", "testOutput/bloop.aif"},
		// 48000, 16bit, stereo
		{"fixtures/zipper.aiff", "testOutput/zipper.aiff"},
		// 48000, 24bit, stereo
		{"fixtures/zipper24b.aiff", "testOutput/zipper24b.aiff"},
	}

	for i, tc := range testCases {
		t.Logf("%d - in: %s, out: %s", i, tc.in, tc.out)
		in, err := os.Open(tc.in)
		if err != nil {
			t.Fatalf("couldn't open %s %v", tc.in, err)
		}
		d := NewDecoder(in)
		frames, err := d.Frames()
		if err != nil {
			t.Fatal(err)
		}
		in.Close()

		out, err := os.Create(tc.out)
		if err != nil {
			t.Fatalf("couldn't create %s %v", tc.out, err)
		}
		defer out.Close()

		e := NewEncoder(out, int(d.SampleRate), int(d.BitDepth), int(d.NumChans))
		e.Frames = frames
		if err := e.Write(); err != nil {
			t.Fatal(err)
		}

		// TODO compare frames
		nf, err := os.Open(tc.out)
		if err != nil {
			t.Fatal(err)
		}

		d2 := NewDecoder(nf)
		nframes, err := d2.Frames()
		if err != nil {
			t.Fatal(err)
		}
		if d2.SampleRate != d.SampleRate {
			t.Fatalf("sample rate didn't support roundtripping exp: %d, got: %d", d.SampleRate, d2.SampleRate)
		}
		if d2.BitDepth != d.BitDepth {
			t.Fatalf("sample size didn't support roundtripping exp: %d, got: %d", d.BitDepth, d2.BitDepth)
		}
		if d2.NumChans != d.NumChans {
			t.Fatalf("the number of channels didn't support roundtripping exp: %d, got: %d", d.NumChans, d2.NumChans)
		}

		if len(frames) != len(nframes) {
			t.Fatalf("the number of frames didn't support roundtripping, exp: %d, got: %d", len(frames), len(nframes))
		}
		for i := range frames {
			for j := 0; j < e.NumChans; j++ {
				if frames[i][j] != nframes[i][j] {
					t.Fatalf("frames[%d][%d]: %d didn't match nframes[%d][%d]: %d", i, j, frames[i][j], i, j, nframes[i][j])
				}
			}
		}

		// binary comparison
		out.Seek(0, 0)
		nf.Seek(0, 0)
		buf1 := make([]byte, 1024)
		buf2 := make([]byte, 1024)

		var err1, err2 error
		var n int
		readBytes := 0
		for err1 == nil && err2 == nil {
			n, err1 = out.Read(buf1)
			_, err2 = nf.Read(buf2)
			readBytes += n
			if bytes.Compare(buf1, buf2) != 0 {
				t.Fatalf("round trip failed, data differed after %d bytes", readBytes)
			}
		}

		nf.Close()
		os.Remove(nf.Name())
	}
}
<commit_msg>aiff: failed test<commit_after>package aiff

import (
	"bytes"
	"encoding/hex"
	"os"
	"testing"
)

func TestEncoderRoundTrip(t *testing.T) {
	os.Mkdir("testOutput", 0777)
	testCases := []struct {
		in  string
		out string
	}{
		// 22050, 8bit, mono
		{"fixtures/kick8b.aiff", "testOutput/kick8b.aiff"},
		// 22050, 16bit, mono
		{"fixtures/kick.aif", "testOutput/kick.aif"},
		// 22050, 16bit, mono
		{"fixtures/kick32b.aiff", "testOutput/kick32b.aiff"},
		// 44100, 16bit, mono
		{"fixtures/subsynth.aif", "testOutput/subsynth.aif"},
		// 44100, 16bit, stereo
		{"fixtures/bloop.aif", "testOutput/bloop.aif"},
		// 48000, 16bit, stereo
		{"fixtures/zipper.aiff", "testOutput/zipper.aiff"},
		// 48000, 24bit, stereo
		{"fixtures/zipper24b.aiff", "testOutput/zipper24b.aiff"},
	}

	for i, tc := range testCases {
		t.Logf("%d - in: %s, out: %s", i, tc.in, tc.out)
		in, err := os.Open(tc.in)
		if err != nil {
			t.Fatalf("couldn't open %s %v", tc.in, err)
		}
		d := NewDecoder(in)
		frames, err := d.Frames()
		if err != nil {
			t.Fatal(err)
		}
		defer in.Close()

		out, err := os.Create(tc.out)
		if err != nil {
			t.Fatalf("couldn't create %s %v", tc.out, err)
		}

		e := NewEncoder(out, int(d.SampleRate), int(d.BitDepth), int(d.NumChans))
		e.Frames = frames
		if err := e.Write(); err != nil {
			t.Fatal(err)
		}

		nf, err := os.Open(tc.out)
		if err != nil {
			t.Fatal(err)
		}

		d2 := NewDecoder(nf)
		d2.Clip()
		// TODO(mattetti): using d2.Duration() messes the later Frames() call
		if d.Size != d2.Size {
			t.Fatalf("the encoded size didn't match the original, expected: %d, got %d", d.Size, d2.Size)
		}
		nframes, err := d2.Frames()
		if err != nil {
			t.Fatal(err)
		}
		if d2.SampleRate != d.SampleRate {
			t.Fatalf("sample rate didn't support roundtripping exp: %d, got: %d", d.SampleRate, d2.SampleRate)
		}
		if d2.BitDepth != d.BitDepth {
			t.Fatalf("sample size didn't support roundtripping exp: %d, got: %d", d.BitDepth, d2.BitDepth)
		}
		if d2.NumChans != d.NumChans {
			t.Fatalf("the number of channels didn't support roundtripping exp: %d, got: %d", d.NumChans, d2.NumChans)
		}

		if len(frames) != len(nframes) {
			t.Fatalf("the number of frames didn't support roundtripping, exp: %d, got: %d", len(frames), len(nframes))
		}
		for i := range frames {
			for j := 0; j < e.NumChans; j++ {
				if frames[i][j] != nframes[i][j] {
					t.Fatalf("frames[%d][%d]: %d didn't match nframes[%d][%d]: %d", i, j, frames[i][j], i, j, nframes[i][j])
				}
			}
		}

		// binary comparison
		in.Seek(0, 0)
		nf.Seek(0, 0)
		buf1 := make([]byte, 32)
		buf2 := make([]byte, 32)

		var err1, err2 error
		var n int
		readBytes := 0
		for err1 == nil && err2 == nil {
			n, err1 = in.Read(buf1)
			_, err2 = nf.Read(buf2)
			readBytes += n
			if bytes.Compare(buf1, buf2) != 0 {
				t.Fatalf("round trip failed, data differed after %d bytes\n%s\n%s", readBytes, hex.Dump(buf1), hex.Dump(buf2))
			}
		}

		nf.Close()
		os.Remove(nf.Name())
	}
}
<|endoftext|>