<commit_before>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	campaigns_path       = "/campaigns"
	single_campaign_path = campaigns_path + "/%s"

	send_test_path = single_campaign_path + "/actions/test"
	send_path = single_campaign_path + "/actions/send"

	CAMPAIGN_TYPE_REGULAR = "regular"
	CAMPAIGN_TYPE_PLAINTEXT = "plaintext"
	CAMPAIGN_TYPE_ABSPLIT = "absplit" // deprecated by mailchimp
	CAMPAIGN_TYPE_RSS = "rss"
	CAMPAIGN_TYPE_VARIATE = "variate"

	CAMPAIGN_SEND_TYPE_HTML = "html"
	CAMPAIGN_SEND_TYPE_PLAINTEXT = "plaintext"
)

type CampaignQueryParams struct {
	ExtendedQueryParams

	Type string
	Status string
	BeforeSendTime string
	SinceSendTime string
	BeforeCreateTime string
	SinceCreateTime string
	ListId string
	FolderId string
	SortField string
	SortDir string
}

func (q CampaignQueryParams) Params() map[string]string {
	m := q.ExtendedQueryParams.Params()
	m["type"] = q.Type
	m["status"] = q.Status
	m["before_send_time"] = q.BeforeSendTime
	m["since_send_time"] = q.SinceSendTime
	m["before_create_time"] = q.BeforeCreateTime
	m["since_create_time"] = q.SinceCreateTime
	m["list_id"] = q.ListId
	m["folder_id"] = q.FolderId
	m["sort_field"] = q.SortField
	m["sort_dir"] = q.SortDir
	return m
}

type ListOfCampaigns struct {
	baseList
	Campaigns []CampaignResponse `json:"campaigns"`
}

type CampaignCreationRecipients struct {
	ListId string `json:"list_id"`
	// segment_opts not implemented
}

type CampaignCreationSettings struct {
	SubjectLine     string `json:"subject_line"`
	PreviewText     string `json:"preview_text"`
	Title           string `json:"title"`
	FromName        string `json:"from_name"`
	ReplyTo         string `json:"reply_to"`
	UseConversation bool `json:"use_conversation"`
	ToName          string `json:"to_name"`
	FolderId        string `json:"folder_id"`
	Authenticate    bool `json:"authenticate"`
	AutoFooter      bool `json:"auto_footer"`
	InlineCss       bool `json:"inline_css"`
	AutoTweet       bool `json:"auto_tweet"`
	FbComments      bool `json:"fb_comments"`
	TemplateId      uint `json:"template_id"`
}

type CampaignCreationRequest struct {
	Type       string           `json:"type"` // must be one of the CAMPAIGN_TYPE_* consts
	Recipients CampaignCreationRecipients `json:"recipients"`
	Settings   CampaignCreationSettings `json:"settings"`
	// variate_settings not implemented
	Tracking          CampaignTracking `json:"tracking"`
	// rss_opts not implemented
	// social_card not implemented
}

type CampaignResponseRecipients struct {
	ListId string `json:"list_id"`
	ListName string `json:"list_name"`
	SegmentText string `json:"segment_text"`
	RecipientCount int `json:"recipient_count"`
}

type CampaignResponseSettings struct {
	SubjectLine     string `json:"subject_line"`
	PreviewText     string `json:"preview_text"`
	Title           string `json:"title"`
	FromName        string `json:"from_name"`
	ReplyTo         string `json:"reply_to"`
	UseConversation bool `json:"use_conversation"`
	ToName          string `json:"to_name"`
	FolderId        string `json:"folder_id"`
	Authenticate    bool `json:"authenticate"`
	AutoFooter      bool `json:"auto_footer"`
	InlineCss       bool `json:"inline_css"`
	AutoTweet       bool `json:"auto_tweet"`
	FbComments      bool `json:"fb_comments"`
	Timewarp        bool `json:"timewarp"`
	TemplateId      uint `json:"template_id"`
	DragAndDrop     bool `json:"drag_and_drop"`
}

type CampaignTracking struct {
	Opens           bool `json:"opens"`
	HtmlClicks      bool `json:"html_clicks"`
	TextClicks      bool `json:"text_clicks"`
	GoalTracking    bool `json:"goal_tracking"`
	Ecomm360        bool `json:"ecomm360"`
	GoogleAnalytics string `json:"google_analytics"`
	Clicktale       string `json:"clicktale"`
}

type CampaignEcommerce struct {
	TotalOrders  int `json:"total_orders"`
	TotalSpent   int `json:"total_spent"`
	TotalRevenue int `json:"total_revenue"`
}

type CampaignReportSummary struct {
	Opens            int `json:"opens"`
	UniqueOpens      int `json:"unique_opens"`
	OpenRate         float32 `json:"open_rate"`
	Clicks           int `json:"clicks"`
	SubscriberClicks int `json:"subscriber_clicks"`
	ClickRate        float32 `json:"click_rate"`
	Ecommerce        CampaignEcommerce `json:"ecommerce"`
}

type CampaignDeliveryStatus struct {
	Enabled  		 bool `json:"enabled"`
}

type CampaignResponse struct {
	withLinks

	ID                string `json:"id"`
	WebID             uint   `json:"web_id"`
	Type              string `json:"type"`
	CreateTime        string `json:"create_time"`
	ArchiveUrl        string `json:"archive_url"`
	LongArchiveUrl    string `json:"long_archive_url"`
	Status            string `json:"status"`
	EmailsSent        uint   `json:"emails_sent"`
	SendTime          string `json:"send_time"`
	ContentType       string `json:"content_type"`
	NeedsBlockRefresh bool   `json:"needs_block_refresh"`
	Recipients        CampaignResponseRecipients `json:"recipients"`
	Settings          CampaignResponseSettings `json:"settings"`
	Tracking          CampaignTracking `json:"tracking"`
	ReportSummary     CampaignReportSummary `json:"report_summary"`
	DeliveryStatus    CampaignDeliveryStatus `json:"delivery_status"`

	api *API
}

func (campaign CampaignResponse) CanMakeRequest() error {
	if campaign.ID == "" {
		return errors.New("No ID provided on campaign")
	}

	return nil
}

func (api API) GetCampaigns(params *CampaignQueryParams) (*ListOfCampaigns, error) {
	response := new(ListOfCampaigns)

	err := api.Request("GET", campaigns_path, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, l := range response.Campaigns {
		l.api = &api
	}

	return response, nil
}

func (api API) GetCampaign(id string, params *BasicQueryParams) (*CampaignResponse, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)

	response := new(CampaignResponse)
	response.api = &api

	return response, api.Request("GET", endpoint, params, nil, response)
}

func (api API) CreateCampaign(body *CampaignCreationRequest) (*CampaignResponse, error) {
	response := new(CampaignResponse)
	response.api = &api
	return response, api.Request("POST", campaigns_path, nil, body, response)
}

func (api API) UpdateCampaign(id string, body *CampaignCreationRequest) (*CampaignResponse, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)

	response := new(CampaignResponse)
	response.api = &api

	return response, api.Request("PATCH", endpoint, nil, body, response)
}

func (api API) DeleteCampaign(id string) (bool, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)
	return api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Actions
// ------------------------------------------------------------------------------------------------

type TestEmailRequest struct {
	TestEmails []string `json:"test_emails"`
	SendType	string	`json:"send_type"`	// one of the CAMPAIGN_SEND_TYPE_* constants
}

type SendCampaignRequest struct {
	CampaignId	string	`json:"campaign_id"`
}

func (api API) SendTestEmail(id string, body *TestEmailRequest) (bool, error) {
	endpoint := fmt.Sprintf(send_test_path, id)
	err := api.Request("POST", endpoint, nil, body, nil)

	if err != nil {
		return false, err
	}
	return true, nil
}

func (api API) SendCampaign(id string, body *SendCampaignRequest) (bool, error) {
	endpoint := fmt.Sprintf(send_path, id)
	err := api.Request("POST", endpoint, nil, body, nil)

	if err != nil {
		return false, err
	}
	return true, nil
}
<commit_msg>Add support for adding conditions when creating campaigns<commit_after>package gochimp3

import (
	"errors"
	"fmt"
)

const (
	campaigns_path       = "/campaigns"
	single_campaign_path = campaigns_path + "/%s"

	send_test_path = single_campaign_path + "/actions/test"
	send_path = single_campaign_path + "/actions/send"

	CAMPAIGN_TYPE_REGULAR = "regular"
	CAMPAIGN_TYPE_PLAINTEXT = "plaintext"
	CAMPAIGN_TYPE_ABSPLIT = "absplit" // deprecated by mailchimp
	CAMPAIGN_TYPE_RSS = "rss"
	CAMPAIGN_TYPE_VARIATE = "variate"

	CAMPAIGN_SEND_TYPE_HTML = "html"
	CAMPAIGN_SEND_TYPE_PLAINTEXT = "plaintext"

	CONDITION_MATCH_ANY = "any"
	CONDITION_MATCH_ALL = "all"

	CONDITION_TYPE_INTERESTS = "Interests"

	CONDITION_OP_CONTAINS = "interestcontains"
)

type CampaignQueryParams struct {
	ExtendedQueryParams

	Type string
	Status string
	BeforeSendTime string
	SinceSendTime string
	BeforeCreateTime string
	SinceCreateTime string
	ListId string
	FolderId string
	SortField string
	SortDir string
}

func (q CampaignQueryParams) Params() map[string]string {
	m := q.ExtendedQueryParams.Params()
	m["type"] = q.Type
	m["status"] = q.Status
	m["before_send_time"] = q.BeforeSendTime
	m["since_send_time"] = q.SinceSendTime
	m["before_create_time"] = q.BeforeCreateTime
	m["since_create_time"] = q.SinceCreateTime
	m["list_id"] = q.ListId
	m["folder_id"] = q.FolderId
	m["sort_field"] = q.SortField
	m["sort_dir"] = q.SortDir
	return m
}

type ListOfCampaigns struct {
	baseList
	Campaigns []CampaignResponse `json:"campaigns"`
}

type CampaignCreationRecipients struct {
	ListId string `json:"list_id"`
	SegmentOptions CampaignCreationSegmentOptions `json:"segment_opts"`
}

type CampaignCreationSegmentOptions struct {
	SavedSegmentId int `json:"saved_segment_id"`
	Match      string               `json:"match"`		// one of CONDITION_MATCH_*

	// this accepts various payloads. See http://developer.mailchimp.com/documentation/mailchimp/reference/campaigns/#create-post_campaigns
	Conditions interface{} `json:"conditions"`
}

type InterestsCondition struct {
	ConditionType string `json:"condition_type"`
	Field string `json:"field"`
	Op    string `json:"op"`
	Value []string `json:"value"`
}

type CampaignCreationSettings struct {
	SubjectLine     string `json:"subject_line"`
	PreviewText     string `json:"preview_text"`
	Title           string `json:"title"`
	FromName        string `json:"from_name"`
	ReplyTo         string `json:"reply_to"`
	UseConversation bool `json:"use_conversation"`
	ToName          string `json:"to_name"`
	FolderId        string `json:"folder_id"`
	Authenticate    bool `json:"authenticate"`
	AutoFooter      bool `json:"auto_footer"`
	InlineCss       bool `json:"inline_css"`
	AutoTweet       bool `json:"auto_tweet"`
	FbComments      bool `json:"fb_comments"`
	TemplateId      uint `json:"template_id"`
}

type CampaignCreationRequest struct {
	Type       string           `json:"type"` // must be one of the CAMPAIGN_TYPE_* consts
	Recipients CampaignCreationRecipients `json:"recipients"`
	Settings   CampaignCreationSettings `json:"settings"`
	// variate_settings not implemented
	Tracking          CampaignTracking `json:"tracking"`
	// rss_opts not implemented
	// social_card not implemented
}

type CampaignResponseRecipients struct {
	ListId string `json:"list_id"`
	ListName string `json:"list_name"`
	SegmentText string `json:"segment_text"`
	RecipientCount int `json:"recipient_count"`
}

type CampaignResponseSettings struct {
	SubjectLine     string `json:"subject_line"`
	PreviewText     string `json:"preview_text"`
	Title           string `json:"title"`
	FromName        string `json:"from_name"`
	ReplyTo         string `json:"reply_to"`
	UseConversation bool `json:"use_conversation"`
	ToName          string `json:"to_name"`
	FolderId        string `json:"folder_id"`
	Authenticate    bool `json:"authenticate"`
	AutoFooter      bool `json:"auto_footer"`
	InlineCss       bool `json:"inline_css"`
	AutoTweet       bool `json:"auto_tweet"`
	FbComments      bool `json:"fb_comments"`
	Timewarp        bool `json:"timewarp"`
	TemplateId      uint `json:"template_id"`
	DragAndDrop     bool `json:"drag_and_drop"`
}

type CampaignTracking struct {
	Opens           bool `json:"opens"`
	HtmlClicks      bool `json:"html_clicks"`
	TextClicks      bool `json:"text_clicks"`
	GoalTracking    bool `json:"goal_tracking"`
	Ecomm360        bool `json:"ecomm360"`
	GoogleAnalytics string `json:"google_analytics"`
	Clicktale       string `json:"clicktale"`
}

type CampaignEcommerce struct {
	TotalOrders  int `json:"total_orders"`
	TotalSpent   int `json:"total_spent"`
	TotalRevenue int `json:"total_revenue"`
}

type CampaignReportSummary struct {
	Opens            int `json:"opens"`
	UniqueOpens      int `json:"unique_opens"`
	OpenRate         float32 `json:"open_rate"`
	Clicks           int `json:"clicks"`
	SubscriberClicks int `json:"subscriber_clicks"`
	ClickRate        float32 `json:"click_rate"`
	Ecommerce        CampaignEcommerce `json:"ecommerce"`
}

type CampaignDeliveryStatus struct {
	Enabled  		 bool `json:"enabled"`
}

type CampaignResponse struct {
	withLinks

	ID                string `json:"id"`
	WebID             uint   `json:"web_id"`
	Type              string `json:"type"`
	CreateTime        string `json:"create_time"`
	ArchiveUrl        string `json:"archive_url"`
	LongArchiveUrl    string `json:"long_archive_url"`
	Status            string `json:"status"`
	EmailsSent        uint   `json:"emails_sent"`
	SendTime          string `json:"send_time"`
	ContentType       string `json:"content_type"`
	NeedsBlockRefresh bool   `json:"needs_block_refresh"`
	Recipients        CampaignResponseRecipients `json:"recipients"`
	Settings          CampaignResponseSettings `json:"settings"`
	Tracking          CampaignTracking `json:"tracking"`
	ReportSummary     CampaignReportSummary `json:"report_summary"`
	DeliveryStatus    CampaignDeliveryStatus `json:"delivery_status"`

	api *API
}

func (campaign CampaignResponse) CanMakeRequest() error {
	if campaign.ID == "" {
		return errors.New("No ID provided on campaign")
	}

	return nil
}

func (api API) GetCampaigns(params *CampaignQueryParams) (*ListOfCampaigns, error) {
	response := new(ListOfCampaigns)

	err := api.Request("GET", campaigns_path, params, nil, response)
	if err != nil {
		return nil, err
	}

	for _, l := range response.Campaigns {
		l.api = &api
	}

	return response, nil
}

func (api API) GetCampaign(id string, params *BasicQueryParams) (*CampaignResponse, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)

	response := new(CampaignResponse)
	response.api = &api

	return response, api.Request("GET", endpoint, params, nil, response)
}

func (api API) CreateCampaign(body *CampaignCreationRequest) (*CampaignResponse, error) {
	response := new(CampaignResponse)
	response.api = &api
	return response, api.Request("POST", campaigns_path, nil, body, response)
}

func (api API) UpdateCampaign(id string, body *CampaignCreationRequest) (*CampaignResponse, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)

	response := new(CampaignResponse)
	response.api = &api

	return response, api.Request("PATCH", endpoint, nil, body, response)
}

func (api API) DeleteCampaign(id string) (bool, error) {
	endpoint := fmt.Sprintf(single_campaign_path, id)
	return api.RequestOk("DELETE", endpoint)
}

// ------------------------------------------------------------------------------------------------
// Actions
// ------------------------------------------------------------------------------------------------

type TestEmailRequest struct {
	TestEmails []string `json:"test_emails"`
	SendType	string	`json:"send_type"`	// one of the CAMPAIGN_SEND_TYPE_* constants
}

type SendCampaignRequest struct {
	CampaignId	string	`json:"campaign_id"`
}

func (api API) SendTestEmail(id string, body *TestEmailRequest) (bool, error) {
	endpoint := fmt.Sprintf(send_test_path, id)
	err := api.Request("POST", endpoint, nil, body, nil)

	if err != nil {
		return false, err
	}
	return true, nil
}

func (api API) SendCampaign(id string, body *SendCampaignRequest) (bool, error) {
	endpoint := fmt.Sprintf(send_path, id)
	err := api.Request("POST", endpoint, nil, body, nil)

	if err != nil {
		return false, err
	}
	return true, nil
}
<|endoftext|>


<commit_before>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/utils"
)

// CacheDir stores the charm cache directory path.
var CacheDir string

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// EventResponse is sent by the charm store in response to charm-event requests.
type EventResponse struct {
	Kind     string   `json:"kind"`
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
	Time     string   `json:"time,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// NotFoundError represents an error indicating that the requested data wasn't found.
type NotFoundError struct {
	msg string
}

func (e *NotFoundError) Error() string {
	return e.msg
}

// CharmStore is a Repository that provides access to the public juju charm store.
type CharmStore struct {
	BaseURL   string
	authToken string
}

var Store = &CharmStore{BaseURL: "https://store.juju.ubuntu.com"}

// Return a copy of the store with auth token set
func (s *CharmStore) WithAuthToken(token string) Repository {
	authCS := *s
	authCS.authToken = token
	return &authCS
}

// Perform an http get, adding custom auth header if necessary.
func (s *CharmStore) get(url string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	if s.authToken != "" {
		/* To comply with RFC 2617, we send the auth token in
		   the Authorization header with a custom auth scheme
		   and the token. */
		req.Header.Add("Authorization", "charmstore "+s.authToken)
	}
	return http.DefaultClient.Do(req)
}

// Info returns details for a charm in the charm store.
func (s *CharmStore) Info(curl *URL) (*InfoResponse, error) {
	key := curl.String()
	resp, err := s.get(s.BaseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return nil, err
	}
	info, found := infos[key]
	if !found {
		return nil, fmt.Errorf("charm: charm store returned response without charm %q", key)
	}
	if len(info.Errors) == 1 && info.Errors[0] == "entry not found" {
		return nil, &NotFoundError{fmt.Sprintf("charm not found: %s", curl)}
	}
	return info, nil
}

// Event returns details for a charm event in the charm store.
//
// If digest is empty, the latest event is returned.
func (s *CharmStore) Event(curl *URL, digest string) (*EventResponse, error) {
	key := curl.String()
	query := key
	if digest != "" {
		query += "@" + digest
	}
	resp, err := s.get(s.BaseURL + "/charm-event?charms=" + url.QueryEscape(query))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	events := make(map[string]*EventResponse)
	if err = json.Unmarshal(body, &events); err != nil {
		return nil, err
	}
	event, found := events[key]
	if !found {
		return nil, fmt.Errorf("charm: charm store returned response without charm %q", key)
	}
	if len(event.Errors) == 1 && event.Errors[0] == "entry not found" {
		if digest == "" {
			return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q", curl)}
		} else {
			return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q with digest %q", curl, digest)}
		}
	}
	return event, nil
}

// revision returns the revision and SHA256 digest of the charm referenced by curl.
func (s *CharmStore) revision(curl *URL) (revision int, digest string, err error) {
	info, err := s.Info(curl)
	if err != nil {
		return 0, "", err
	}
	for _, w := range info.Warnings {
		log.Warningf("charm: charm store reports for %q: %s", curl, w)
	}
	if info.Errors != nil {
		return 0, "", fmt.Errorf("charm info errors for %q: %s", curl, strings.Join(info.Errors, "; "))
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *CharmStore) Latest(curl *URL) (int, error) {
	rev, _, err := s.revision(curl.WithRevision(-1))
	return rev, err
}

// BranchLocation returns the location for the branch holding the charm at curl.
func (s *CharmStore) BranchLocation(curl *URL) string {
	if curl.User != "" {
		return fmt.Sprintf("lp:~%s/charms/%s/%s/trunk", curl.User, curl.Series, curl.Name)
	}
	return fmt.Sprintf("lp:charms/%s/%s", curl.Series, curl.Name)
}

var branchPrefixes = []string{
	"lp:",
	"bzr+ssh://bazaar.launchpad.net/+branch/",
	"bzr+ssh://bazaar.launchpad.net/",
	"http://launchpad.net/+branch/",
	"http://launchpad.net/",
	"https://launchpad.net/+branch/",
	"https://launchpad.net/",
	"http://code.launchpad.net/+branch/",
	"http://code.launchpad.net/",
	"https://code.launchpad.net/+branch/",
	"https://code.launchpad.net/",
}

// CharmURL returns the charm URL for the branch at location.
func (s *CharmStore) CharmURL(location string) (*URL, error) {
	var l string
	if len(location) > 0 && location[0] == '~' {
		l = location
	} else {
		for _, prefix := range branchPrefixes {
			if strings.HasPrefix(location, prefix) {
				l = location[len(prefix):]
				break
			}
		}
	}
	if l != "" {
		for len(l) > 0 && l[len(l)-1] == '/' {
			l = l[:len(l)-1]
		}
		u := strings.Split(l, "/")
		if len(u) == 3 && u[0] == "charms" {
			return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2]))
		}
		if len(u) == 4 && u[0] == "charms" && u[3] == "trunk" {
			return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2]))
		}
		if len(u) == 5 && u[1] == "charms" && u[4] == "trunk" && len(u[0]) > 0 && u[0][0] == '~' {
			return ParseURL(fmt.Sprintf("cs:%s/%s/%s", u[0], u[2], u[3]))
		}
	}
	return nil, fmt.Errorf("unknown branch location: %q", location)
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		return err
	}
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
// CacheDir must have been set, otherwise Get will panic.
func (s *CharmStore) Get(curl *URL) (Charm, error) {
	// The cache location must have been previously set.
	if CacheDir == "" {
		panic("charm cache directory path is empty")
	}
	if err := os.MkdirAll(CacheDir, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.revision(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(CacheDir, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := s.get(s.BaseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(CacheDir, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := utils.ReplaceFile(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing subdirectories
// named after an Ubuntu series, each of which contains charms targeted for
// that series. For example:
//
//   /path/to/repository/oneiric/mongodb/
//   /path/to/repository/precise/mongodb.charm
//   /path/to/repository/precise/wordpress/
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

func repoNotFound(path string) error {
	return &NotFoundError{fmt.Sprintf("no repository found at %q", path)}
}

func charmNotFound(curl *URL, repoPath string) error {
	return &NotFoundError{fmt.Sprintf("charm not found in %q: %s", repoPath, curl)}
}

func mightBeCharm(info os.FileInfo) bool {
	if info.IsDir() {
		return !strings.HasPrefix(info.Name(), ".")
	}
	return strings.HasSuffix(info.Name(), ".charm")
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	if curl.Schema != "local" {
		return nil, fmt.Errorf("local repository got URL with non-local schema: %q", curl)
	}
	info, err := os.Stat(r.Path)
	if err != nil {
		if os.IsNotExist(err) {
			err = repoNotFound(r.Path)
		}
		return nil, err
	}
	if !info.IsDir() {
		return nil, repoNotFound(r.Path)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, charmNotFound(curl, r.Path)
	}
	var latest Charm
	for _, info := range infos {
		chPath := filepath.Join(path, info.Name())
		if info.Mode()&os.ModeSymlink != 0 {
			var err error
			if info, err = os.Stat(chPath); err != nil {
				return nil, err
			}
		}
		if !mightBeCharm(info) {
			continue
		}
		if ch, err := Read(chPath); err != nil {
			log.Warningf("charm: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return nil, charmNotFound(curl, r.Path)
}
<commit_msg>Code style fixes.<commit_after>// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"launchpad.net/juju-core/log"
	"launchpad.net/juju-core/utils"
)

// CacheDir stores the charm cache directory path.
var CacheDir string

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// EventResponse is sent by the charm store in response to charm-event requests.
type EventResponse struct {
	Kind     string   `json:"kind"`
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
	Time     string   `json:"time,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// NotFoundError represents an error indicating that the requested data wasn't found.
type NotFoundError struct {
	msg string
}

func (e *NotFoundError) Error() string {
	return e.msg
}

// CharmStore is a Repository that provides access to the public juju charm store.
type CharmStore struct {
	BaseURL   string
	authToken string
}

var Store = &CharmStore{BaseURL: "https://store.juju.ubuntu.com"}

// Return a copy of the store with auth token set
func (s *CharmStore) WithAuthToken(token string) Repository {
	authCS := *s
	authCS.authToken = token
	return &authCS
}

// Perform an http get, adding custom auth header if necessary.
func (s *CharmStore) get(url string) (resp *http.Response, err error) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}
	if s.authToken != "" {
		// To comply with RFC 2617, we send the auth token in
		// the Authorization header with a custom auth scheme
		// and the token.
		req.Header.Add("Authorization", "charmstore "+s.authToken)
	}
	return http.DefaultClient.Do(req)
}

// Info returns details for a charm in the charm store.
func (s *CharmStore) Info(curl *URL) (*InfoResponse, error) {
	key := curl.String()
	resp, err := s.get(s.BaseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return nil, err
	}
	info, found := infos[key]
	if !found {
		return nil, fmt.Errorf("charm: charm store returned response without charm %q", key)
	}
	if len(info.Errors) == 1 && info.Errors[0] == "entry not found" {
		return nil, &NotFoundError{fmt.Sprintf("charm not found: %s", curl)}
	}
	return info, nil
}

// Event returns details for a charm event in the charm store.
//
// If digest is empty, the latest event is returned.
func (s *CharmStore) Event(curl *URL, digest string) (*EventResponse, error) {
	key := curl.String()
	query := key
	if digest != "" {
		query += "@" + digest
	}
	resp, err := s.get(s.BaseURL + "/charm-event?charms=" + url.QueryEscape(query))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}
	events := make(map[string]*EventResponse)
	if err = json.Unmarshal(body, &events); err != nil {
		return nil, err
	}
	event, found := events[key]
	if !found {
		return nil, fmt.Errorf("charm: charm store returned response without charm %q", key)
	}
	if len(event.Errors) == 1 && event.Errors[0] == "entry not found" {
		if digest == "" {
			return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q", curl)}
		} else {
			return nil, &NotFoundError{fmt.Sprintf("charm event not found for %q with digest %q", curl, digest)}
		}
	}
	return event, nil
}

// revision returns the revision and SHA256 digest of the charm referenced by curl.
func (s *CharmStore) revision(curl *URL) (revision int, digest string, err error) {
	info, err := s.Info(curl)
	if err != nil {
		return 0, "", err
	}
	for _, w := range info.Warnings {
		log.Warningf("charm: charm store reports for %q: %s", curl, w)
	}
	if info.Errors != nil {
		return 0, "", fmt.Errorf("charm info errors for %q: %s", curl, strings.Join(info.Errors, "; "))
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *CharmStore) Latest(curl *URL) (int, error) {
	rev, _, err := s.revision(curl.WithRevision(-1))
	return rev, err
}

// BranchLocation returns the location for the branch holding the charm at curl.
func (s *CharmStore) BranchLocation(curl *URL) string {
	if curl.User != "" {
		return fmt.Sprintf("lp:~%s/charms/%s/%s/trunk", curl.User, curl.Series, curl.Name)
	}
	return fmt.Sprintf("lp:charms/%s/%s", curl.Series, curl.Name)
}

var branchPrefixes = []string{
	"lp:",
	"bzr+ssh://bazaar.launchpad.net/+branch/",
	"bzr+ssh://bazaar.launchpad.net/",
	"http://launchpad.net/+branch/",
	"http://launchpad.net/",
	"https://launchpad.net/+branch/",
	"https://launchpad.net/",
	"http://code.launchpad.net/+branch/",
	"http://code.launchpad.net/",
	"https://code.launchpad.net/+branch/",
	"https://code.launchpad.net/",
}

// CharmURL returns the charm URL for the branch at location.
func (s *CharmStore) CharmURL(location string) (*URL, error) {
	var l string
	if len(location) > 0 && location[0] == '~' {
		l = location
	} else {
		for _, prefix := range branchPrefixes {
			if strings.HasPrefix(location, prefix) {
				l = location[len(prefix):]
				break
			}
		}
	}
	if l != "" {
		for len(l) > 0 && l[len(l)-1] == '/' {
			l = l[:len(l)-1]
		}
		u := strings.Split(l, "/")
		if len(u) == 3 && u[0] == "charms" {
			return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2]))
		}
		if len(u) == 4 && u[0] == "charms" && u[3] == "trunk" {
			return ParseURL(fmt.Sprintf("cs:%s/%s", u[1], u[2]))
		}
		if len(u) == 5 && u[1] == "charms" && u[4] == "trunk" && len(u[0]) > 0 && u[0][0] == '~' {
			return ParseURL(fmt.Sprintf("cs:%s/%s/%s", u[0], u[2], u[3]))
		}
	}
	return nil, fmt.Errorf("unknown branch location: %q", location)
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		return err
	}
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
// CacheDir must have been set, otherwise Get will panic.
func (s *CharmStore) Get(curl *URL) (Charm, error) {
	// The cache location must have been previously set.
	if CacheDir == "" {
		panic("charm cache directory path is empty")
	}
	if err := os.MkdirAll(CacheDir, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.revision(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(CacheDir, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := s.get(s.BaseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(CacheDir, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := utils.ReplaceFile(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing subdirectories
// named after an Ubuntu series, each of which contains charms targeted for
// that series. For example:
//
//   /path/to/repository/oneiric/mongodb/
//   /path/to/repository/precise/mongodb.charm
//   /path/to/repository/precise/wordpress/
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

func repoNotFound(path string) error {
	return &NotFoundError{fmt.Sprintf("no repository found at %q", path)}
}

func charmNotFound(curl *URL, repoPath string) error {
	return &NotFoundError{fmt.Sprintf("charm not found in %q: %s", repoPath, curl)}
}

func mightBeCharm(info os.FileInfo) bool {
	if info.IsDir() {
		return !strings.HasPrefix(info.Name(), ".")
	}
	return strings.HasSuffix(info.Name(), ".charm")
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	if curl.Schema != "local" {
		return nil, fmt.Errorf("local repository got URL with non-local schema: %q", curl)
	}
	info, err := os.Stat(r.Path)
	if err != nil {
		if os.IsNotExist(err) {
			err = repoNotFound(r.Path)
		}
		return nil, err
	}
	if !info.IsDir() {
		return nil, repoNotFound(r.Path)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, charmNotFound(curl, r.Path)
	}
	var latest Charm
	for _, info := range infos {
		chPath := filepath.Join(path, info.Name())
		if info.Mode()&os.ModeSymlink != 0 {
			var err error
			if info, err = os.Stat(chPath); err != nil {
				return nil, err
			}
		}
		if !mightBeCharm(info) {
			continue
		}
		if ch, err := Read(chPath); err != nil {
			log.Warningf("charm: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return nil, charmNotFound(curl, r.Path)
}
<|endoftext|>


<commit_before>package req

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"time"
)

// Resp represents a request with it's response
type Resp struct {
	r      *Req
	req    *http.Request
	resp   *http.Response
	client *http.Client
	*multipartHelper
	reqBody  []byte
	respBody []byte
	cost     time.Duration
	err      error // delayed error
}

// Cost returns time spent by the request
func (r *Resp) Cost() time.Duration {
	return r.cost
}

// Request returns *http.Request
func (r *Resp) Request() *http.Request {
	return r.req
}

// Response returns *http.Response
func (r *Resp) Response() *http.Response {
	return r.resp
}

// Bytes returns response body as []byte
func (r *Resp) Bytes() []byte {
	data, _ := r.ToBytes()
	return data
}

// ToBytes returns response body as []byte,
// return error if error happend when reading
// the response body
func (r *Resp) ToBytes() ([]byte, error) {
	if r.err != nil {
		return nil, r.err
	}
	if r.respBody != nil {
		return r.respBody, nil
	}
	respBody, err := ioutil.ReadAll(r.resp.Body)
	if err != nil {
		r.err = err
		return nil, err
	}
	r.respBody = respBody
	return r.respBody, nil
}

// String returns response body as string
func (r *Resp) String() string {
	data, _ := r.ToBytes()
	return string(data)
}

// ToString returns response body as string,
// return error if error happend when reading
// the response body
func (r *Resp) ToString() (string, error) {
	data, err := r.ToBytes()
	return string(data), err
}

// ToJSON convert json response body to struct or map
func (r *Resp) ToJSON(v interface{}) error {
	data, err := r.ToBytes()
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}

// ToXML convert xml response body to struct or map
func (r *Resp) ToXML(v interface{}) error {
	data, err := r.ToBytes()
	if err != nil {
		return err
	}
	return xml.Unmarshal(data, v)
}

// ToFile download the response body to file
func (r *Resp) ToFile(name string) error {
	file, err := os.Create(name)
	if err != nil {
		return err
	}
	defer file.Close()

	if r.respBody != nil {
		_, err = file.Write(r.respBody)
		return err
	}

	_, err = io.Copy(file, r.resp.Body)
	return err
}

var regNewline = regexp.MustCompile(`\n|\r`)

func (r *Resp) autoFormat(s fmt.State) {
	req := r.req
	fmt.Fprint(s, req.Method, " ", req.URL.String())
	if r.r.flag&Lcost != 0 {
		fmt.Fprint(s, " ", r.cost.String())
	}

	// test if it is should be outputed pretty
	var pretty bool
	var parts []string
	addPart := func(part string) {
		if part == "" {
			return
		}
		parts = append(parts, part)
		if !pretty && regNewline.MatchString(part) {
			pretty = true
		}
	}
	if r.r.flag&LreqBody != 0 { // request body
		addPart(string(r.reqBody))
	}
	if r.r.flag&LrespBody != 0 { // response body
		addPart(r.String())
	}

	for _, part := range parts {
		if pretty {
			fmt.Fprint(s, "\n")
		}
		fmt.Fprint(s, " ", part)
	}
}

func (r *Resp) miniFormat(s fmt.State) {
	req := r.req
	fmt.Fprint(s, req.Method, " ", req.URL.String())
	if r.r.flag&Lcost != 0 {
		fmt.Fprint(s, " ", r.cost.String())
	}
	if r.r.flag&LreqBody != 0 && len(r.reqBody) > 0 { // request body
		str := regNewline.ReplaceAllString(string(r.reqBody), " ")
		fmt.Fprint(s, " ", str)
	}
	if r.r.flag&LrespBody != 0 && r.String() != "" { // response body
		str := regNewline.ReplaceAllString(r.String(), " ")
		fmt.Fprint(s, " ", str)
	}
}

func (r *Resp) Format(s fmt.State, verb rune) {
	if r == nil || r.req == nil {
		return
	}
	if s.Flag('+') { // include header and format pretty.
		fmt.Fprint(s, r.dump())
	} else if s.Flag('-') { // keep all informations in one line.
		r.miniFormat(s)
	} else { // auto
		r.autoFormat(s)
	}

}
<commit_msg>add optional DownloadCallback when calling ToFile<commit_after>package req

import (
	"encoding/json"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"time"
)

// Resp represents a request with it's response
type Resp struct {
	r      *Req
	req    *http.Request
	resp   *http.Response
	client *http.Client
	*multipartHelper
	reqBody  []byte
	respBody []byte
	cost     time.Duration
	err      error // delayed error
}

// Cost returns time spent by the request
func (r *Resp) Cost() time.Duration {
	return r.cost
}

// Request returns *http.Request
func (r *Resp) Request() *http.Request {
	return r.req
}

// Response returns *http.Response
func (r *Resp) Response() *http.Response {
	return r.resp
}

// Bytes returns response body as []byte
func (r *Resp) Bytes() []byte {
	data, _ := r.ToBytes()
	return data
}

// ToBytes returns response body as []byte,
// return error if error happend when reading
// the response body
func (r *Resp) ToBytes() ([]byte, error) {
	if r.err != nil {
		return nil, r.err
	}
	if r.respBody != nil {
		return r.respBody, nil
	}
	respBody, err := ioutil.ReadAll(r.resp.Body)
	if err != nil {
		r.err = err
		return nil, err
	}
	r.respBody = respBody
	return r.respBody, nil
}

// String returns response body as string
func (r *Resp) String() string {
	data, _ := r.ToBytes()
	return string(data)
}

// ToString returns response body as string,
// return error if error happend when reading
// the response body
func (r *Resp) ToString() (string, error) {
	data, err := r.ToBytes()
	return string(data), err
}

// ToJSON convert json response body to struct or map
func (r *Resp) ToJSON(v interface{}) error {
	data, err := r.ToBytes()
	if err != nil {
		return err
	}
	return json.Unmarshal(data, v)
}

// ToXML convert xml response body to struct or map
func (r *Resp) ToXML(v interface{}) error {
	data, err := r.ToBytes()
	if err != nil {
		return err
	}
	return xml.Unmarshal(data, v)
}

// ToFile download the response body to file with optional download callback
func (r *Resp) ToFile(name string, callback ...DownloadCallback) error {
	file, err := os.Create(name)
	if err != nil {
		return err
	}
	defer file.Close()

	if r.respBody != nil {
		_, err = file.Write(r.respBody)
		return err
	}

	if len(callback) > 0 && r.resp.ContentLength > 0 {
		return r.download(file, r.resp.ContentLength, callback...)
	}

	_, err = io.Copy(file, r.resp.Body)
	return err
}

func (r *Resp) download(file *os.File, totalLength int64, callback ...DownloadCallback) error {
	p := make([]byte, 1024)
	b := r.resp.Body
	var currentLength int64
	for {
		l, err := b.Read(p)
		if l > 0 {
			_, _err := file.Write(p[:l])
			if _err != nil {
				return _err
			}
			currentLength += int64(l)
			for _, cb := range callback {
				cb.OnProgress(currentLength, totalLength)
			}
		}
		if err != nil {
			if err == io.EOF {
				for _, cb := range callback {
					cb.OnComplete(file)
				}
				return nil
			}
			return err
		}
	}
}

var regNewline = regexp.MustCompile(`\n|\r`)

func (r *Resp) autoFormat(s fmt.State) {
	req := r.req
	fmt.Fprint(s, req.Method, " ", req.URL.String())
	if r.r.flag&Lcost != 0 {
		fmt.Fprint(s, " ", r.cost.String())
	}

	// test if it is should be outputed pretty
	var pretty bool
	var parts []string
	addPart := func(part string) {
		if part == "" {
			return
		}
		parts = append(parts, part)
		if !pretty && regNewline.MatchString(part) {
			pretty = true
		}
	}
	if r.r.flag&LreqBody != 0 { // request body
		addPart(string(r.reqBody))
	}
	if r.r.flag&LrespBody != 0 { // response body
		addPart(r.String())
	}

	for _, part := range parts {
		if pretty {
			fmt.Fprint(s, "\n")
		}
		fmt.Fprint(s, " ", part)
	}
}

func (r *Resp) miniFormat(s fmt.State) {
	req := r.req
	fmt.Fprint(s, req.Method, " ", req.URL.String())
	if r.r.flag&Lcost != 0 {
		fmt.Fprint(s, " ", r.cost.String())
	}
	if r.r.flag&LreqBody != 0 && len(r.reqBody) > 0 { // request body
		str := regNewline.ReplaceAllString(string(r.reqBody), " ")
		fmt.Fprint(s, " ", str)
	}
	if r.r.flag&LrespBody != 0 && r.String() != "" { // response body
		str := regNewline.ReplaceAllString(r.String(), " ")
		fmt.Fprint(s, " ", str)
	}
}

func (r *Resp) Format(s fmt.State, verb rune) {
	if r == nil || r.req == nil {
		return
	}
	if s.Flag('+') { // include header and format pretty.
		fmt.Fprint(s, r.dump())
	} else if s.Flag('-') { // keep all informations in one line.
		r.miniFormat(s)
	} else { // auto
		r.autoFormat(s)
	}
}

// DownloadCallback is the callback when downloading
type DownloadCallback interface {
	OnComplete(file *os.File)
	OnProgress(currentLength int64, totalLength int64)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/sthorne/go-hunspell"
	"net/http"
	"regexp"
)

var h = hunspell.Hunspell("/usr/share/hunspell/en_GB.aff", "/usr/share/hunspell/en_GB.dic")

func main() {
	fs := http.FileServer(http.Dir("static"))
	http.HandleFunc("/check", checkHandler)
	http.HandleFunc("/suggest", suggestHandler)
	http.Handle("/", fs)

	http.ListenAndServe(":80", nil)
}

var wordRegexp = regexp.MustCompile(`\b[\w']+\b`)

func checkHandler(w http.ResponseWriter, r *http.Request) {
	text := r.FormValue("text")

	typos := []string{}
	for _, word := range wordRegexp.FindAllString(text, -1) {
		if !h.Spell(word) {
			typos = append(typos, word)
		}
	}

	result := map[string]interface{}{
		"typos": typos,
	}

	b, err := json.Marshal(result)
	if err != nil {
		fmt.Println("json error:", err)
		return
	}

	w.Write(b)
}

func suggestHandler(w http.ResponseWriter, r *http.Request) {
	word := r.FormValue("word")

	suggestions := h.Suggest(word)

	result := map[string]interface{}{
		"suggestions": suggestions,
	}

	b, err := json.Marshal(result)
	if err != nil {
		fmt.Println("json error:", err)
		return
	}

	w.Write(b)
}
<commit_msg>suggest: Return array for now<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/sthorne/go-hunspell"
	"net/http"
	"regexp"
)

var h = hunspell.Hunspell("/usr/share/hunspell/en_GB.aff", "/usr/share/hunspell/en_GB.dic")

func main() {
	fs := http.FileServer(http.Dir("static"))
	http.HandleFunc("/check", checkHandler)
	http.HandleFunc("/suggest", suggestHandler)
	http.Handle("/", fs)

	http.ListenAndServe(":80", nil)
}

var wordRegexp = regexp.MustCompile(`\b[\w']+\b`)

func checkHandler(w http.ResponseWriter, r *http.Request) {
	text := r.FormValue("text")

	typos := []string{}
	for _, word := range wordRegexp.FindAllString(text, -1) {
		if !h.Spell(word) {
			typos = append(typos, word)
		}
	}

	result := map[string]interface{}{
		"typos": typos,
	}

	b, err := json.Marshal(result)
	if err != nil {
		fmt.Println("json error:", err)
		return
	}

	w.Write(b)
}

func suggestHandler(w http.ResponseWriter, r *http.Request) {
	word := r.FormValue("word")

	result := h.Suggest(word)

	b, err := json.Marshal(result)
	if err != nil {
		fmt.Println("json error:", err)
		return
	}

	w.Write(b)
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"log"
	"net"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gholt/ring"
)

type msgMap struct {
	lock    sync.RWMutex
	mapping map[uint64]ring.MsgUnmarshaller
}

func newMsgMap() *msgMap {
	return &msgMap{mapping: make(map[uint64]ring.MsgUnmarshaller)}
}

func (mm *msgMap) set(t uint64, f ring.MsgUnmarshaller) ring.MsgUnmarshaller {
	mm.lock.Lock()
	p := mm.mapping[t]
	mm.mapping[t] = f
	mm.lock.Unlock()
	return p
}

func (mm *msgMap) get(t uint64) ring.MsgUnmarshaller {
	mm.lock.RLock()
	f := mm.mapping[t]
	mm.lock.RUnlock()
	return f
}

type node struct {
	id uint64
}

func (n *node) NodeID() uint64 {
	return n.id
}

func (n *node) Active() bool {
	return true
}

func (n *node) Capacity() uint32 {
	return 1
}

func (n *node) TierValues() []int {
	return nil
}

func (n *node) Address() string {
	return ""
}

type ringPipe struct {
	ring            ring.Ring
	conn            net.Conn
	lock            sync.RWMutex
	msgMap          *msgMap
	logError        *log.Logger
	logWarning      *log.Logger
	typeBytes       int
	lengthBytes     int
	writeChan       chan ring.Msg
	writingDoneChan chan struct{}
	sendDrops       uint32
}

func NewRingPipe(localNodeAddress string, c net.Conn) *ringPipe {
	b := ring.NewBuilder()
	b.SetReplicaCount(2)
	var localNodeID uint64
	n := b.AddNode(true, 1, nil, []string{"127.0.0.1:11111"}, "", nil)
	if localNodeAddress == "127.0.0.1:11111" {
		localNodeID = n.ID()
	}
	n = b.AddNode(true, 1, nil, []string{"127.0.0.1:22222"}, "", nil)
	if localNodeAddress == "127.0.0.1:22222" {
		localNodeID = n.ID()
	}
	// n = b.AddNode(true, 1, nil, []string{"127.0.0.1:33333"}, "", nil)
	// if localNodeAddress == "127.0.0.1:33333" {
	// 	localNodeID = n.ID()
	// }
	r := b.Ring()
	r.SetLocalNode(localNodeID)
	rp := &ringPipe{
		ring:            r,
		conn:            c,
		msgMap:          newMsgMap(),
		logError:        log.New(os.Stderr, "", log.LstdFlags),
		logWarning:      log.New(os.Stderr, "", log.LstdFlags),
		typeBytes:       8,
		lengthBytes:     8,
		writeChan:       make(chan ring.Msg, 8),
		writingDoneChan: make(chan struct{}, 1),
	}
	return rp
}

func (rp *ringPipe) Ring() ring.Ring {
	return rp.ring
}

func (rp *ringPipe) Start() {
	go rp.reading()
	go rp.writing()
}

func (rp *ringPipe) MaxMsgLength() uint64 {
	return 16 * 1024 * 1024
}

func (rp *ringPipe) SetMsgHandler(t uint64, h ring.MsgUnmarshaller) {
	rp.msgMap.set(t, h)
}

func (rp *ringPipe) MsgToNode(m ring.Msg, localNodeID uint64, timeout time.Duration) {
	select {
	case rp.writeChan <- m:
	case <-time.After(timeout):
		atomic.AddUint32(&rp.sendDrops, 1)
	}
	m.Free()
}

func (rp *ringPipe) MsgToOtherReplicas(m ring.Msg, partition uint32, timeout time.Duration) {
	select {
	case rp.writeChan <- m:
	case <-time.After(timeout):
		atomic.AddUint32(&rp.sendDrops, 1)
	}
	m.Free()
}

func (rp *ringPipe) reading() {
	time.Sleep(50 * time.Millisecond)
	b := make([]byte, rp.typeBytes+rp.lengthBytes)
	d := make([]byte, 65536)
	for {
		var n int
		var sn int
		var err error
		for n != len(b) {
			if err != nil {
				if n != 0 || err != io.EOF {
					rp.logError.Print("error reading msg", err)
				}
				return
			}
			sn, err = rp.conn.Read(b[n:])
			n += sn
		}
		if err != nil {
			rp.logError.Print("error reading msg start", err)
			return
		}
		var t uint64
		for i := 0; i < rp.typeBytes; i++ {
			t = (t << 8) | uint64(b[i])
		}
		var l uint64
		for i := 0; i < rp.lengthBytes; i++ {
			l = (l << 8) | uint64(b[rp.typeBytes+i])
		}
		f := rp.msgMap.get(t)
		if f != nil {
			_, err = f(rp.conn, l)
			if err != nil {
				rp.logError.Print("error reading msg content", err)
				return
			}
		} else {
			rp.logWarning.Printf("unknown msg type %d", t)
			for l > 0 {
				if err != nil {
					rp.logError.Print("err reading unknown msg content", err)
					return
				}
				if l >= uint64(len(d)) {
					sn, err = rp.conn.Read(d)
				} else {
					sn, err = rp.conn.Read(d[:l])
				}
				l -= uint64(sn)
			}
		}
	}
}

func (rp *ringPipe) writing() {
	b := make([]byte, rp.typeBytes+rp.lengthBytes)
	for {
		time.Sleep(50 * time.Millisecond)
		m := <-rp.writeChan
		if m == nil {
			break
		}
		t := m.MsgType()
		for i := rp.typeBytes - 1; i >= 0; i-- {
			b[i] = byte(t)
			t >>= 8
		}
		l := m.MsgLength()
		for i := rp.lengthBytes - 1; i >= 0; i-- {
			b[rp.typeBytes+i] = byte(l)
			l >>= 8
		}
		_, err := rp.conn.Write(b)
		if err != nil {
			rp.logError.Print("err writing msg", err)
			break
		}
		_, err = m.WriteContent(rp.conn)
		if err != nil {
			rp.logError.Print("err writing msg content", err)
			break
		}
	}
	rp.writingDoneChan <- struct{}{}
}
<commit_msg>Updated due to NewBuilder change<commit_after>package main

import (
	"io"
	"log"
	"net"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"github.com/gholt/ring"
)

type msgMap struct {
	lock    sync.RWMutex
	mapping map[uint64]ring.MsgUnmarshaller
}

func newMsgMap() *msgMap {
	return &msgMap{mapping: make(map[uint64]ring.MsgUnmarshaller)}
}

func (mm *msgMap) set(t uint64, f ring.MsgUnmarshaller) ring.MsgUnmarshaller {
	mm.lock.Lock()
	p := mm.mapping[t]
	mm.mapping[t] = f
	mm.lock.Unlock()
	return p
}

func (mm *msgMap) get(t uint64) ring.MsgUnmarshaller {
	mm.lock.RLock()
	f := mm.mapping[t]
	mm.lock.RUnlock()
	return f
}

type node struct {
	id uint64
}

func (n *node) NodeID() uint64 {
	return n.id
}

func (n *node) Active() bool {
	return true
}

func (n *node) Capacity() uint32 {
	return 1
}

func (n *node) TierValues() []int {
	return nil
}

func (n *node) Address() string {
	return ""
}

type ringPipe struct {
	ring            ring.Ring
	conn            net.Conn
	lock            sync.RWMutex
	msgMap          *msgMap
	logError        *log.Logger
	logWarning      *log.Logger
	typeBytes       int
	lengthBytes     int
	writeChan       chan ring.Msg
	writingDoneChan chan struct{}
	sendDrops       uint32
}

func NewRingPipe(localNodeAddress string, c net.Conn) *ringPipe {
	b := ring.NewBuilder(64)
	b.SetReplicaCount(2)
	var localNodeID uint64
	n := b.AddNode(true, 1, nil, []string{"127.0.0.1:11111"}, "", nil)
	if localNodeAddress == "127.0.0.1:11111" {
		localNodeID = n.ID()
	}
	n = b.AddNode(true, 1, nil, []string{"127.0.0.1:22222"}, "", nil)
	if localNodeAddress == "127.0.0.1:22222" {
		localNodeID = n.ID()
	}
	// n = b.AddNode(true, 1, nil, []string{"127.0.0.1:33333"}, "", nil)
	// if localNodeAddress == "127.0.0.1:33333" {
	// 	localNodeID = n.ID()
	// }
	r := b.Ring()
	r.SetLocalNode(localNodeID)
	rp := &ringPipe{
		ring:            r,
		conn:            c,
		msgMap:          newMsgMap(),
		logError:        log.New(os.Stderr, "", log.LstdFlags),
		logWarning:      log.New(os.Stderr, "", log.LstdFlags),
		typeBytes:       8,
		lengthBytes:     8,
		writeChan:       make(chan ring.Msg, 8),
		writingDoneChan: make(chan struct{}, 1),
	}
	return rp
}

func (rp *ringPipe) Ring() ring.Ring {
	return rp.ring
}

func (rp *ringPipe) Start() {
	go rp.reading()
	go rp.writing()
}

func (rp *ringPipe) MaxMsgLength() uint64 {
	return 16 * 1024 * 1024
}

func (rp *ringPipe) SetMsgHandler(t uint64, h ring.MsgUnmarshaller) {
	rp.msgMap.set(t, h)
}

func (rp *ringPipe) MsgToNode(m ring.Msg, localNodeID uint64, timeout time.Duration) {
	select {
	case rp.writeChan <- m:
	case <-time.After(timeout):
		atomic.AddUint32(&rp.sendDrops, 1)
	}
	m.Free()
}

func (rp *ringPipe) MsgToOtherReplicas(m ring.Msg, partition uint32, timeout time.Duration) {
	select {
	case rp.writeChan <- m:
	case <-time.After(timeout):
		atomic.AddUint32(&rp.sendDrops, 1)
	}
	m.Free()
}

func (rp *ringPipe) reading() {
	time.Sleep(50 * time.Millisecond)
	b := make([]byte, rp.typeBytes+rp.lengthBytes)
	d := make([]byte, 65536)
	for {
		var n int
		var sn int
		var err error
		for n != len(b) {
			if err != nil {
				if n != 0 || err != io.EOF {
					rp.logError.Print("error reading msg", err)
				}
				return
			}
			sn, err = rp.conn.Read(b[n:])
			n += sn
		}
		if err != nil {
			rp.logError.Print("error reading msg start", err)
			return
		}
		var t uint64
		for i := 0; i < rp.typeBytes; i++ {
			t = (t << 8) | uint64(b[i])
		}
		var l uint64
		for i := 0; i < rp.lengthBytes; i++ {
			l = (l << 8) | uint64(b[rp.typeBytes+i])
		}
		f := rp.msgMap.get(t)
		if f != nil {
			_, err = f(rp.conn, l)
			if err != nil {
				rp.logError.Print("error reading msg content", err)
				return
			}
		} else {
			rp.logWarning.Printf("unknown msg type %d", t)
			for l > 0 {
				if err != nil {
					rp.logError.Print("err reading unknown msg content", err)
					return
				}
				if l >= uint64(len(d)) {
					sn, err = rp.conn.Read(d)
				} else {
					sn, err = rp.conn.Read(d[:l])
				}
				l -= uint64(sn)
			}
		}
	}
}

func (rp *ringPipe) writing() {
	b := make([]byte, rp.typeBytes+rp.lengthBytes)
	for {
		time.Sleep(50 * time.Millisecond)
		m := <-rp.writeChan
		if m == nil {
			break
		}
		t := m.MsgType()
		for i := rp.typeBytes - 1; i >= 0; i-- {
			b[i] = byte(t)
			t >>= 8
		}
		l := m.MsgLength()
		for i := rp.lengthBytes - 1; i >= 0; i-- {
			b[rp.typeBytes+i] = byte(l)
			l >>= 8
		}
		_, err := rp.conn.Write(b)
		if err != nil {
			rp.logError.Print("err writing msg", err)
			break
		}
		_, err = m.WriteContent(rp.conn)
		if err != nil {
			rp.logError.Print("err writing msg content", err)
			break
		}
	}
	rp.writingDoneChan <- struct{}{}
}
<|endoftext|>


<commit_before>package handler

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

func Offers(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}
<commit_msg>add a small json example<commit_after>package handler

import (
	"encoding/json"
	"fmt"
	"html"
	"net/http"
)

type Response map[string]interface{}

func (r Response) String() (s string) {
	b, err := json.Marshal(r)
	if err != nil {
		s = ""
		return
	}
	s = string(b)
	return
}

func Offers(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, Response{"response": "Hello, %s!"}.String(), html.EscapeString(r.URL.Path))
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/codegangsta/cli"
	_ "github.com/go-sql-driver/mysql"
	"github.com/karamani/iostreams"
)

var (
	debugMode           bool
	queryArg            string
	execArg             string
	connectionStringArg string
	formatArg           string
	fakeMode            bool
)

func main() {
	app := cli.NewApp()
	app.Name = "massquery"
	app.Usage = "massquery"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "debug mode",
			EnvVar:      "MASSQUERY_DEBUG",
			Destination: &debugMode,
		},
		cli.StringFlag{
			Name:        "query",
			Usage:       "sql-query",
			Destination: &queryArg,
		},
		cli.StringFlag{
			Name:        "exec",
			Usage:       "exec-string (insert, update or delete)",
			Destination: &execArg,
		},
		cli.StringFlag{
			Name:        "cnn",
			Usage:       "db connection string",
			EnvVar:      "MASSQUERY_CNN",
			Destination: &connectionStringArg,
		},
		cli.StringFlag{
			Name:        "format",
			Usage:       "output format",
			Destination: &formatArg,
		},
	}

	app.Action = func(c *cli.Context) {

		if err := validateArgs(); err != nil {
			log.Fatalf("Arguments isn't valid: %s", err.Error())
		}

		query := queryArg
		isExec := len(query) == 0
		if isExec {
			query = execArg
		}

		if !iostreams.StdinReady() {
			processOneQuery(connectionStringArg, query, isExec, "")
			return
		}

		// this func's called for each stdin's row
		process := func(row []byte) error {

			debug(string(row))

			params := strings.Split(string(row), "\t")
			rowQuery := parameterizedString(query, "{%d}", params)
			rowCnn := parameterizedString(connectionStringArg, "{%d}", params)

			debug("connection:" + rowCnn)
			debug("query:" + rowQuery)

			processOneQuery(rowCnn, rowQuery, isExec, string(row))

			return nil
		}

		if err := iostreams.ProcessStdin(process); err != nil {
			log.Panicln(err.Error())
		}
	}

	app.Run(os.Args)
}

func validateArgs() error {

	var err error

	switch {
	case len(connectionStringArg) == 0:
		err = fmt.Errorf("'cnn' arg is required")
	case len(queryArg) == 0 && len(execArg) == 0:
		err = fmt.Errorf("it should be one of the arguments 'query' or 'exec'")
	case len(queryArg) > 0 && len(execArg) > 0:
		err = fmt.Errorf("it should be only one of the arguments 'query' or 'exec'")
	}

	return err
}

func processOneQuery(cnn, query string, isExec bool, input string) {
	res, err := runQuery(cnn, query, isExec)
	if err != nil {
		log.Println(err.Error())
		printRes(formatRes(formatArg, input, cnn, "error", nil))
		return
	}

	for _, resrow := range res {
		printRes(formatRes(formatArg, input, cnn, "success", resrow))
	}
}

func parameterizedString(s, tpl string, params []string) string {
	res := s
	for i, param := range params {
		t := fmt.Sprintf(tpl, i)
		res = strings.Replace(res, t, param, -1)
	}
	return res
}

func formatRes(format, input, cnn, status string, values []string) string {

	res := strings.Join(values, "\t")

	if len(format) > 0 {
		s := format

		// remove unnecessary quotes from command line
		s = strings.Replace(s, "\\t", "\t", -1)
		s = strings.Replace(s, "\\n", "\n", -1)

		rpl := map[string]string{
			"{input}":  input,
			"{res}":    res,
			"{cnn}":    cnn,
			"{status}": status,
		}
		for initial, new := range rpl {
			s = strings.Replace(s, initial, new, -1)
		}

		s = parameterizedString(s, "{res%d}", values)

		res = s
	}

	return res
}

func printRes(s string) {
	if len(s) > 0 {
		fmt.Println(s)
	}
}

func runQuery(connectionString, query string, isExec bool) (res [][]string, resErr error) {

	res, resErr = nil, nil

	db, resErr := sql.Open("mysql", connectionString)
	if resErr != nil {
		return
	}
	defer db.Close()

	resErr = db.Ping()
	if resErr != nil {
		return
	}

	if isExec {

		resErr = func() error {
			execRes, err := db.Exec(query)
			if err != nil {
				return err
			}
			affected, err := execRes.RowsAffected()
			if err != nil {
				return err
			}
			lastInsertID, err := execRes.LastInsertId()
			if err != nil {
				return err
			}
			res = append(res, []string{
				strconv.FormatInt(affected, 10),
				strconv.FormatInt(lastInsertID, 10),
			})
			return nil
		}()

	} else {

		resErr = func() error {

			rows, err := db.Query(query)
			if err != nil {
				return err
			}
			defer rows.Close()

			cols, err := rows.Columns()
			if err != nil {
				return err
			}

			container := newScanContainer(len(cols))
			for rows.Next() {
				if err := rows.Scan(container.Pointers...); err != nil {
					return err
				}
				res = append(res, container.AsStrings())
			}

			if err := rows.Err(); err != nil {
				res = nil
				return err
			}
			return nil
		}()
	}

	return
}

func debug(format string, args ...interface{}) {
	if debugMode {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}
<commit_msg>faleMode var is removed<commit_after>package main

import (
	"database/sql"
	"fmt"
	"log"
	"os"
	"strconv"
	"strings"

	"github.com/codegangsta/cli"
	_ "github.com/go-sql-driver/mysql"
	"github.com/karamani/iostreams"
)

var (
	debugMode           bool
	queryArg            string
	execArg             string
	connectionStringArg string
	formatArg           string
)

func main() {
	app := cli.NewApp()
	app.Name = "massquery"
	app.Usage = "massquery"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "debug",
			Usage:       "debug mode",
			EnvVar:      "MASSQUERY_DEBUG",
			Destination: &debugMode,
		},
		cli.StringFlag{
			Name:        "query",
			Usage:       "sql-query",
			Destination: &queryArg,
		},
		cli.StringFlag{
			Name:        "exec",
			Usage:       "exec-string (insert, update or delete)",
			Destination: &execArg,
		},
		cli.StringFlag{
			Name:        "cnn",
			Usage:       "db connection string",
			EnvVar:      "MASSQUERY_CNN",
			Destination: &connectionStringArg,
		},
		cli.StringFlag{
			Name:        "format",
			Usage:       "output format",
			Destination: &formatArg,
		},
	}

	app.Action = func(c *cli.Context) {

		if err := validateArgs(); err != nil {
			log.Fatalf("Arguments isn't valid: %s", err.Error())
		}

		query := queryArg
		isExec := len(query) == 0
		if isExec {
			query = execArg
		}

		if !iostreams.StdinReady() {
			processOneQuery(connectionStringArg, query, isExec, "")
			return
		}

		// this func's called for each stdin's row
		process := func(row []byte) error {

			debug(string(row))

			params := strings.Split(string(row), "\t")
			rowQuery := parameterizedString(query, "{%d}", params)
			rowCnn := parameterizedString(connectionStringArg, "{%d}", params)

			debug("connection:" + rowCnn)
			debug("query:" + rowQuery)

			processOneQuery(rowCnn, rowQuery, isExec, string(row))

			return nil
		}

		if err := iostreams.ProcessStdin(process); err != nil {
			log.Panicln(err.Error())
		}
	}

	app.Run(os.Args)
}

func validateArgs() error {

	var err error

	switch {
	case len(connectionStringArg) == 0:
		err = fmt.Errorf("'cnn' arg is required")
	case len(queryArg) == 0 && len(execArg) == 0:
		err = fmt.Errorf("it should be one of the arguments 'query' or 'exec'")
	case len(queryArg) > 0 && len(execArg) > 0:
		err = fmt.Errorf("it should be only one of the arguments 'query' or 'exec'")
	}

	return err
}

func processOneQuery(cnn, query string, isExec bool, input string) {
	res, err := runQuery(cnn, query, isExec)
	if err != nil {
		log.Println(err.Error())
		printRes(formatRes(formatArg, input, cnn, "error", nil))
		return
	}

	for _, resrow := range res {
		printRes(formatRes(formatArg, input, cnn, "success", resrow))
	}
}

func parameterizedString(s, tpl string, params []string) string {
	res := s
	for i, param := range params {
		t := fmt.Sprintf(tpl, i)
		res = strings.Replace(res, t, param, -1)
	}
	return res
}

func formatRes(format, input, cnn, status string, values []string) string {

	res := strings.Join(values, "\t")

	if len(format) > 0 {
		s := format

		// remove unnecessary quotes from command line
		s = strings.Replace(s, "\\t", "\t", -1)
		s = strings.Replace(s, "\\n", "\n", -1)

		rpl := map[string]string{
			"{input}":  input,
			"{res}":    res,
			"{cnn}":    cnn,
			"{status}": status,
		}
		for initial, new := range rpl {
			s = strings.Replace(s, initial, new, -1)
		}

		s = parameterizedString(s, "{res%d}", values)

		res = s
	}

	return res
}

func printRes(s string) {
	if len(s) > 0 {
		fmt.Println(s)
	}
}

func runQuery(connectionString, query string, isExec bool) (res [][]string, resErr error) {

	res, resErr = nil, nil

	db, resErr := sql.Open("mysql", connectionString)
	if resErr != nil {
		return
	}
	defer db.Close()

	resErr = db.Ping()
	if resErr != nil {
		return
	}

	if isExec {

		resErr = func() error {
			execRes, err := db.Exec(query)
			if err != nil {
				return err
			}
			affected, err := execRes.RowsAffected()
			if err != nil {
				return err
			}
			lastInsertID, err := execRes.LastInsertId()
			if err != nil {
				return err
			}
			res = append(res, []string{
				strconv.FormatInt(affected, 10),
				strconv.FormatInt(lastInsertID, 10),
			})
			return nil
		}()

	} else {

		resErr = func() error {

			rows, err := db.Query(query)
			if err != nil {
				return err
			}
			defer rows.Close()

			cols, err := rows.Columns()
			if err != nil {
				return err
			}

			container := newScanContainer(len(cols))
			for rows.Next() {
				if err := rows.Scan(container.Pointers...); err != nil {
					return err
				}
				res = append(res, container.AsStrings())
			}

			if err := rows.Err(); err != nil {
				res = nil
				return err
			}
			return nil
		}()
	}

	return
}

func debug(format string, args ...interface{}) {
	if debugMode {
		log.Printf("[DEBUG] "+format+"\n", args...)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 David Chen <chendahui007@gmail.com>.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package safe implements a password safety checker.
package safe

import (
	"bufio"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// Level identifies the strength of the password.
type Level uint8

const (
	Terrible Level = iota
	Simple
	Medium
	Strong
)

// qwertyuiop
//  asdfghjkl
//   zxcvbnm
var (
	asdf    = "qwertyuiopasdfghjklzxcvbnm"
	revAsdf = reverse(asdf)

	words = make(map[string]int) // most frequent used passwords
)

var (
	lower  = regexp.MustCompile(`[a-z]`)
	upper  = regexp.MustCompile(`[A-Z]`)
	number = regexp.MustCompile(`[0-9]`)
	marks  = regexp.MustCompile(`[^0-9a-zA-Z]`)
)

type Safety struct {
	ml    int   // minimal length
	mf    int   // minimal frequency
	mt    int   // minimal type (minimum character family)
	level Level // default level to validate password
}

// New returns a Safety object.
func New(ml, mf, mt int, level Level) *Safety {
	return &Safety{ml, mf, mt, level}
}

func (s *Safety) SetWords(fpath string) error {
	f, err := os.Open(fpath)
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(f)
	var tmp []string
	for scanner.Scan() {
		tmp = strings.Split(scanner.Text(), " ")
		words[tmp[0]], _ = strconv.Atoi(tmp[1])
	}
	return nil
}

// Check validate the password, and get the level.
func (s *Safety) Check(raw string) Level {
	l := len([]rune(raw))
	if l < s.ml {
		return Terrible
	}

	if s.isAsdf(raw) || s.isByStep(raw) {
		return Simple
	}

	if s.isCommonPassword(raw, s.mf) {
		return Simple
	}

	typ := 0

	if lower.MatchString(raw) {
		typ++
	}
	if upper.MatchString(raw) {
		typ++
	}
	if number.MatchString(raw) {
		typ++
	}
	if marks.MatchString(raw) {
		typ++
	}

	if l < 8 && typ == 2 {
		return Simple
	}

	if typ < s.mt {
		return Medium
	}

	return Strong
}

// If the password is in the order on keyboard.
func (s *Safety) isAsdf(raw string) bool {
	// s in asdf , or reverse in asdf
	rev := reverse(raw)
	if strings.Contains(asdf, raw) || strings.Contains(asdf, rev) {
		return true
	}

	return false
}

// If the password is alphabet step by step.
func (s *Safety) isByStep(raw string) bool {
	r := []rune(raw)
	delta := r[1] - r[0]

	for i, _ := range r {
		if i == 0 {
			continue
		}
		if r[i]-r[i-1] != delta {
			return false
		}
	}

	return true
}

// If the password is common used
// 10k top passwords: https://xato.net/passwords/more-top-worst-passwords/
func (s *Safety) isCommonPassword(raw string, freq int) bool {
	f, ok := words[raw]
	if !ok {
		return false
	}
	return f > freq
}

func reverse(raw string) string {
	r := []rune(raw)

	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
<commit_msg>optimize  code<commit_after>// Copyright 2015 David Chen <chendahui007@gmail.com>.  All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Package safe implements a password safety checker.
package safe

import (
	"bufio"
	"os"
	"regexp"
	"strconv"
	"strings"
)

// Level identifies the strength of the password.
type Level uint8

const (
	Terrible Level = iota
	Simple
	Medium
	Strong
)

// qwertyuiop
//  asdfghjkl
//   zxcvbnm
var (
	asdf    = "qwertyuiopasdfghjklzxcvbnm"
	revAsdf = reverse(asdf)

	words = make(map[string]int) // most frequent used passwords
)

var (
	lower  = regexp.MustCompile(`[a-z]`)
	upper  = regexp.MustCompile(`[A-Z]`)
	number = regexp.MustCompile(`[0-9]`)
	marks  = regexp.MustCompile(`[^0-9a-zA-Z]`)
)

type Safety struct {
	ml    int   // minimal length
	mf    int   // minimal frequency
	mt    int   // minimal type (minimum character family)
	level Level // default level to validate password
}

// New returns a Safety object.
func New(ml, mf, mt int, level Level) *Safety {
	return &Safety{ml, mf, mt, level}
}

func (s *Safety) SetWords(fpath string) error {
	f, err := os.Open(fpath)
	if err != nil {
		return err
	}

	scanner := bufio.NewScanner(f)
	var tmp []string
	for scanner.Scan() {
		tmp = strings.Split(scanner.Text(), " ")
		words[tmp[0]], _ = strconv.Atoi(tmp[1])
	}
	return nil
}

// Check validate the password, and get the level.
func (s *Safety) Check(raw string) Level {
	l := len([]rune(raw))
	if l < s.ml {
		return Terrible
	}

	if s.isAsdf(raw) || s.isByStep(raw) {
		return Simple
	}

	if s.isCommonPassword(raw, s.mf) {
		return Simple
	}

	typ := 0

	if lower.MatchString(raw) {
		typ++
	}
	if upper.MatchString(raw) {
		typ++
	}
	if number.MatchString(raw) {
		typ++
	}
	if marks.MatchString(raw) {
		typ++
	}

	if l < 8 && typ == 2 {
		return Simple
	}

	if typ < s.mt {
		return Medium
	}

	return Strong
}

// If the password is in the order on keyboard.
func (s *Safety) isAsdf(raw string) bool {
	// s in asdf , or reverse in asdf
	rev := reverse(raw)
	return strings.Contains(asdf, raw) || strings.Contains(asdf, rev)
}

// If the password is alphabet step by step.
func (s *Safety) isByStep(raw string) bool {
	r := []rune(raw)
	delta := r[1] - r[0]

	for i, _ := range r {
		if i == 0 {
			continue
		}
		if r[i]-r[i-1] != delta {
			return false
		}
	}

	return true
}

// If the password is common used
// 10k top passwords: https://xato.net/passwords/more-top-worst-passwords/
func (s *Safety) isCommonPassword(raw string, freq int) bool {
	f, ok := words[raw]
	if !ok {
		return false
	}
	return f > freq
}

func reverse(raw string) string {
	r := []rune(raw)

	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}
<|endoftext|>


<commit_before>package task

import (
	"fmt"
	"neon/build"
	"neon/util"
	"strings"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"path/filepath"
	"net/http"
	"io"
	"path"
)

var LOCAL_REPOSITORY = util.ExpandUserHome("~/.java/repository")
var DEFAULT_REPOSITORY = "http://repo1.maven.org/maven2"

func init() {
	build.TaskMap["classpath"] = build.TaskDescriptor{
		Constructor: Classpath,
		Help: `Build a Java classpath.

Arguments:

- classpath: the name of the property to set with classpath.
- classes: a list of class directories to add in classpath (optional).
- jars: a glob or list of globs of jar files to add to classpath (optional).
- dependencies: a list of dependency files to add to classpath (optional).
- scopes: the classpath scope (optional, if set will take dependencies without
  scope and listed scopes, if not set, will only take dependencies without
  scope).
- repositories: a list of repository URLs to get dependencies from (optional,
  defaults to 'http://repo1.maven.org/maven2').
- todir: to copy jar files to given directory (optional).

Examples:

	# build classpath with classes in build/classes directory
	- classpath: 'classpath'
	  classes:   'build/classes'
    # build classpath with jar files in lib directory
    - classpath: 'classpath'
      jars:      'lib/*.jar'
	# build classpath with a dependencies file
	- classpath:    'classpath'
	  dependencies: 'dependencies.yml'
	# copy classpath's jar files to 'build/lib' directory
	- classpath:    _
	  dependencies: 'dependencies.yml'
      todir:        'build/lib'

Notes:

Dependency files should list dependencies as follows:

	- group:    junit
      artifact: junit
      version:  4.12
	  scopes:   [test]

Scopes is optional. If not set, dependency will always be included. If set,
dependency will be included for classpath with these scopes.`,
	}
}

func Classpath(target *build.Target, args util.Object) (build.Task, error) {
	fields := []string{"classpath", "classes", "jars", "dependencies", "scopes", "repositories", "todir"}
	if err := CheckFields(args, fields, fields[:1]); err != nil {
		return nil, err
	}
	classpath, err := args.GetString("classpath")
	if err != nil {
		return nil, fmt.Errorf("argument classpath must be a string")
	}
	var classes []string
	if args.HasField("classes") {
		classes, err = args.GetListStringsOrString("classes")
		if err != nil {
			return nil, fmt.Errorf("argument classes of task classpath must be a string or list of strings")
		}
	}
	var jars []string
	if args.HasField("jars") {
		jars, err = args.GetListStringsOrString("jars")
		if err != nil {
			return nil, fmt.Errorf("argument jars of task classpath must be a string or list of strings")
		}
	}
	var dependencies []string
	if args.HasField("dependencies") {
		dependencies, err = args.GetListStringsOrString("dependencies")
		if err != nil {
			return nil, fmt.Errorf("argument dependencies of task classpath must be a string or list of strings")
		}
	}
	var scopes []string
	if args.HasField("scopes") {
		scopes, err = args.GetListStringsOrString("scopes")
		if err != nil {
			return nil, fmt.Errorf("argument scopes of task classpath must be a string or list of strings")
		}
	}
	var repositories []string
	if args.HasField("repositories") {
		repositories, err = args.GetListStringsOrString("repositories")
		if err != nil {
			return nil, fmt.Errorf("argument repositories of task classpath must be a string or list of strings")
		}
	}
	var todir string
	if args.HasField("todir") {
		todir, err = args.GetString("todir")
		if err != nil {
			return nil, fmt.Errorf("argument todir of task classpath must be a string")
		}
	}
	return func(context *build.Context) error {
		// evaluate arguments
		_classpath, _err := context.EvaluateString(classpath)
		if _err != nil {
			return fmt.Errorf("evaluating classpath argument: %v", _err)
		}
		var _classes []string
		for _, _class := range classes {
			_c, _err := context.EvaluateString(_class)
			if _err != nil {
				return fmt.Errorf("evaluating classes argument: %v", _err)
			}
			_classes = append(_classes, _c)
		}
		_jars, _err := context.FindFiles(".", jars, nil, false)
		if _err != nil {
			return fmt.Errorf("finding jar files: %v", _err)
		}
		var _dependencies []string
		for _, _dependency := range dependencies {
			_d, _err := context.EvaluateString(_dependency)
			if _err != nil {
				return fmt.Errorf("evaluating dependencies argument: %v", _err)
			}
			_dependencies = append(_dependencies, _d)
		}
		var _scopes []string
		for _, _scope := range scopes {
			_s, _err := context.EvaluateString(_scope)
			if _err != nil {
				return fmt.Errorf("evaluating scopes argument: %v", _err)
			}
			_scopes = append(_scopes, _s)
		}
		var _repositories []string
		for _, _repository := range repositories {
			_r, _err := context.EvaluateString(_repository)
			if _err != nil {
				return fmt.Errorf("evaluating repositories argument: %v", _err)
			}
			_repositories = append(_repositories, _r)
		}
		_todir, _err := context.EvaluateString(todir)
		if _err != nil {
			return fmt.Errorf("evaluating destination directory: %v", _err)
		}
		// get dependencies
		_deps, _err := getDependencies(_dependencies, _scopes, _repositories, context)
		if _err != nil {
			return fmt.Errorf("getting dependencies: %v", _err)
		}
		// evaluate classpath
		var _elements []string
		_elements = append(_elements, _classes...)
		_elements = append(_elements, _jars...)
		_elements = append(_elements, _deps...)
		_path := strings.Join(_elements, string(os.PathListSeparator))
		context.SetProperty(_classpath, _path)
		// copy jar files to destination directory
		if _todir != "" {
			var _jars []string
			for _, _element := range _elements {
				if strings.HasSuffix(_element, ".jar") {
					_jars = append(_jars, _element)
				}
			}
			_err = copyJarsToDir(_jars, _todir)
			if err != nil {
				return fmt.Errorf("copying jar files to destination directory: %v", _err)
			}
		}
		return nil
	}, nil
}

func getDependencies(dependencies, scopes, repositories []string, context *build.Context) ([]string, error) {
	if !util.DirExists(LOCAL_REPOSITORY) {
		os.MkdirAll(LOCAL_REPOSITORY, util.DIR_FILE_MODE)
	}
	var deps []string
	for _, dependency := range dependencies {
		dep, err := getDependency(dependency, scopes, repositories, context)
		if err != nil {
			return nil, err
		}
		deps = append(deps, dep...)
	}
	return deps, nil
}

func getDependency(file string, scopes, repositories []string, context *build.Context) ([]string, error) {
	var dependencies Dependencies
	source, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(source, &dependencies)
	if err != nil {
		return nil, err
	}
	var paths []string
	for _, dependency := range dependencies {
		if selected(scopes, dependency.Scopes) {
			path := dependency.Path(LOCAL_REPOSITORY)
			paths = append(paths, path)
			if !util.FileExists(path) {
				err = downloadDependency(dependency, repositories, context)
				if err != nil {
					return nil, err
				}
			}
		}
	}
	if err != nil {
		return nil, err
	}
	return paths, nil
}

func downloadDependency(dependency Dependency, repositories []string, context *build.Context) error {
	context.Message("Downloading dependency '%s'", dependency.String())
	path := dependency.Path(LOCAL_REPOSITORY)
	dir := filepath.Dir(path)
	if !util.DirExists(dir) {
		os.MkdirAll(dir, util.DIR_FILE_MODE)
	}
	if repositories == nil {
		repositories = []string{DEFAULT_REPOSITORY}
	}
	var err error
	for _, repository := range repositories {
		url := dependency.Path(repository)
		err = download(path, url)
		if err == nil {
			return nil
		}
	}
	return err
}

func download(path, url string) error {
	response, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("getting '%s': %v", url, err)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return fmt.Errorf("getting '%s': %s", url, response.Status)
	}
	output, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	defer output.Close()
	_, err = io.Copy(output, response.Body)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	return nil
}

type Dependency struct {
	Group    string
	Artifact string
	Version  string
	Scopes   []string
}

func (d *Dependency) Path(root string) string {
	return fmt.Sprintf("%s/%s/%s/%s/%s-%s.jar", root, strings.Replace(d.Group, ".", "/", -1), d.Artifact, d.Version, d.Artifact, d.Version)
}

func (d *Dependency) String() string {
	return fmt.Sprintf("%s/%s/%s", d.Group, d.Artifact, d.Version)
}

type Dependencies []Dependency

func selected(classpath, dependency []string) bool {
	if dependency == nil {
		return true
	}
	for _, scope1 := range classpath {
		for _, scope2 := range dependency {
			if scope1 == scope2 {
				return true
			}
		}
	}
	return false
}

func copyJarsToDir(jars []string, dir string) error {
	if !util.DirExists(dir) {
		os.MkdirAll(dir, util.DIR_FILE_MODE)
	}
	for _, jar := range jars {
		dest := path.Join(dir, path.Base(jar))
		err := util.CopyFile(jar, dest)
		if err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Fixed Maven repo URL<commit_after>package task

import (
	"fmt"
	"neon/build"
	"neon/util"
	"strings"
	"os"
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"path/filepath"
	"net/http"
	"io"
	"path"
)

var LOCAL_REPOSITORY = util.ExpandUserHome("~/.java/repository")
var DEFAULT_REPOSITORY = "http://central.maven.org/maven2"

func init() {
	build.TaskMap["classpath"] = build.TaskDescriptor{
		Constructor: Classpath,
		Help: `Build a Java classpath.

Arguments:

- classpath: the name of the property to set with classpath.
- classes: a list of class directories to add in classpath (optional).
- jars: a glob or list of globs of jar files to add to classpath (optional).
- dependencies: a list of dependency files to add to classpath (optional).
- scopes: the classpath scope (optional, if set will take dependencies without
  scope and listed scopes, if not set, will only take dependencies without
  scope).
- repositories: a list of repository URLs to get dependencies from (optional,
  defaults to 'http://repo1.maven.org/maven2').
- todir: to copy jar files to given directory (optional).

Examples:

	# build classpath with classes in build/classes directory
	- classpath: 'classpath'
	  classes:   'build/classes'
    # build classpath with jar files in lib directory
    - classpath: 'classpath'
      jars:      'lib/*.jar'
	# build classpath with a dependencies file
	- classpath:    'classpath'
	  dependencies: 'dependencies.yml'
	# copy classpath's jar files to 'build/lib' directory
	- classpath:    _
	  dependencies: 'dependencies.yml'
      todir:        'build/lib'

Notes:

Dependency files should list dependencies as follows:

	- group:    junit
      artifact: junit
      version:  4.12
	  scopes:   [test]

Scopes is optional. If not set, dependency will always be included. If set,
dependency will be included for classpath with these scopes.`,
	}
}

func Classpath(target *build.Target, args util.Object) (build.Task, error) {
	fields := []string{"classpath", "classes", "jars", "dependencies", "scopes", "repositories", "todir"}
	if err := CheckFields(args, fields, fields[:1]); err != nil {
		return nil, err
	}
	classpath, err := args.GetString("classpath")
	if err != nil {
		return nil, fmt.Errorf("argument classpath must be a string")
	}
	var classes []string
	if args.HasField("classes") {
		classes, err = args.GetListStringsOrString("classes")
		if err != nil {
			return nil, fmt.Errorf("argument classes of task classpath must be a string or list of strings")
		}
	}
	var jars []string
	if args.HasField("jars") {
		jars, err = args.GetListStringsOrString("jars")
		if err != nil {
			return nil, fmt.Errorf("argument jars of task classpath must be a string or list of strings")
		}
	}
	var dependencies []string
	if args.HasField("dependencies") {
		dependencies, err = args.GetListStringsOrString("dependencies")
		if err != nil {
			return nil, fmt.Errorf("argument dependencies of task classpath must be a string or list of strings")
		}
	}
	var scopes []string
	if args.HasField("scopes") {
		scopes, err = args.GetListStringsOrString("scopes")
		if err != nil {
			return nil, fmt.Errorf("argument scopes of task classpath must be a string or list of strings")
		}
	}
	var repositories []string
	if args.HasField("repositories") {
		repositories, err = args.GetListStringsOrString("repositories")
		if err != nil {
			return nil, fmt.Errorf("argument repositories of task classpath must be a string or list of strings")
		}
	}
	var todir string
	if args.HasField("todir") {
		todir, err = args.GetString("todir")
		if err != nil {
			return nil, fmt.Errorf("argument todir of task classpath must be a string")
		}
	}
	return func(context *build.Context) error {
		// evaluate arguments
		_classpath, _err := context.EvaluateString(classpath)
		if _err != nil {
			return fmt.Errorf("evaluating classpath argument: %v", _err)
		}
		var _classes []string
		for _, _class := range classes {
			_c, _err := context.EvaluateString(_class)
			if _err != nil {
				return fmt.Errorf("evaluating classes argument: %v", _err)
			}
			_classes = append(_classes, _c)
		}
		_jars, _err := context.FindFiles(".", jars, nil, false)
		if _err != nil {
			return fmt.Errorf("finding jar files: %v", _err)
		}
		var _dependencies []string
		for _, _dependency := range dependencies {
			_d, _err := context.EvaluateString(_dependency)
			if _err != nil {
				return fmt.Errorf("evaluating dependencies argument: %v", _err)
			}
			_dependencies = append(_dependencies, _d)
		}
		var _scopes []string
		for _, _scope := range scopes {
			_s, _err := context.EvaluateString(_scope)
			if _err != nil {
				return fmt.Errorf("evaluating scopes argument: %v", _err)
			}
			_scopes = append(_scopes, _s)
		}
		var _repositories []string
		for _, _repository := range repositories {
			_r, _err := context.EvaluateString(_repository)
			if _err != nil {
				return fmt.Errorf("evaluating repositories argument: %v", _err)
			}
			_repositories = append(_repositories, _r)
		}
		_todir, _err := context.EvaluateString(todir)
		if _err != nil {
			return fmt.Errorf("evaluating destination directory: %v", _err)
		}
		// get dependencies
		_deps, _err := getDependencies(_dependencies, _scopes, _repositories, context)
		if _err != nil {
			return fmt.Errorf("getting dependencies: %v", _err)
		}
		// evaluate classpath
		var _elements []string
		_elements = append(_elements, _classes...)
		_elements = append(_elements, _jars...)
		_elements = append(_elements, _deps...)
		_path := strings.Join(_elements, string(os.PathListSeparator))
		context.SetProperty(_classpath, _path)
		// copy jar files to destination directory
		if _todir != "" {
			var _jars []string
			for _, _element := range _elements {
				if strings.HasSuffix(_element, ".jar") {
					_jars = append(_jars, _element)
				}
			}
			_err = copyJarsToDir(_jars, _todir)
			if err != nil {
				return fmt.Errorf("copying jar files to destination directory: %v", _err)
			}
		}
		return nil
	}, nil
}

func getDependencies(dependencies, scopes, repositories []string, context *build.Context) ([]string, error) {
	if !util.DirExists(LOCAL_REPOSITORY) {
		os.MkdirAll(LOCAL_REPOSITORY, util.DIR_FILE_MODE)
	}
	var deps []string
	for _, dependency := range dependencies {
		dep, err := getDependency(dependency, scopes, repositories, context)
		if err != nil {
			return nil, err
		}
		deps = append(deps, dep...)
	}
	return deps, nil
}

func getDependency(file string, scopes, repositories []string, context *build.Context) ([]string, error) {
	var dependencies Dependencies
	source, err := ioutil.ReadFile(file)
	if err != nil {
		return nil, err
	}
	err = yaml.Unmarshal(source, &dependencies)
	if err != nil {
		return nil, err
	}
	var paths []string
	for _, dependency := range dependencies {
		if selected(scopes, dependency.Scopes) {
			path := dependency.Path(LOCAL_REPOSITORY)
			paths = append(paths, path)
			if !util.FileExists(path) {
				err = downloadDependency(dependency, repositories, context)
				if err != nil {
					return nil, err
				}
			}
		}
	}
	if err != nil {
		return nil, err
	}
	return paths, nil
}

func downloadDependency(dependency Dependency, repositories []string, context *build.Context) error {
	context.Message("Downloading dependency '%s'", dependency.String())
	path := dependency.Path(LOCAL_REPOSITORY)
	dir := filepath.Dir(path)
	if !util.DirExists(dir) {
		os.MkdirAll(dir, util.DIR_FILE_MODE)
	}
	if repositories == nil {
		repositories = []string{DEFAULT_REPOSITORY}
	}
	var err error
	for _, repository := range repositories {
		url := dependency.Path(repository)
		err = download(path, url)
		if err == nil {
			return nil
		}
	}
	return err
}

func download(path, url string) error {
	response, err := http.Get(url)
	if err != nil {
		return fmt.Errorf("getting '%s': %v", url, err)
	}
	defer response.Body.Close()
	if response.StatusCode != 200 {
		return fmt.Errorf("getting '%s': %s", url, response.Status)
	}
	output, err := os.Create(path)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	defer output.Close()
	_, err = io.Copy(output, response.Body)
	if err != nil {
		return fmt.Errorf("saving dependency '%s': %v", path, err)
	}
	return nil
}

type Dependency struct {
	Group    string
	Artifact string
	Version  string
	Scopes   []string
}

func (d *Dependency) Path(root string) string {
	return fmt.Sprintf("%s/%s/%s/%s/%s-%s.jar", root, strings.Replace(d.Group, ".", "/", -1), d.Artifact, d.Version, d.Artifact, d.Version)
}

func (d *Dependency) String() string {
	return fmt.Sprintf("%s/%s/%s", d.Group, d.Artifact, d.Version)
}

type Dependencies []Dependency

func selected(classpath, dependency []string) bool {
	if dependency == nil {
		return true
	}
	for _, scope1 := range classpath {
		for _, scope2 := range dependency {
			if scope1 == scope2 {
				return true
			}
		}
	}
	return false
}

func copyJarsToDir(jars []string, dir string) error {
	if !util.DirExists(dir) {
		os.MkdirAll(dir, util.DIR_FILE_MODE)
	}
	for _, jar := range jars {
		dest := path.Join(dir, path.Base(jar))
		err := util.CopyFile(jar, dest)
		if err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package migo

import (
	"database/sql"
	"fmt"

	"github.com/pkg/errors"
)

func Seed(path, dbpath, env string) error {
	dbconf, err := NewDb(dbpath, env)
	if err != nil {
		return errors.Wrap(err, "Parse db configue")
	}
	sqlconf := NewSql(*dbconf)
	db, err := sql.Open("mysql", sqlconf.DbConf.FormatDSN())
	if err != nil {
		return errors.Wrap(err, "Create db connection")
	}
	defer db.Close()

	y, err := ParseYAML(path)
	if err != nil {
		return errors.Wrap(err, "Parse YAML")
	}

	qs := []string{}
	qs = append(qs, "SET FOREIGN_KEY_CHECKS = 0")
	for table, data := range y {
		datalist, ok := data.([]interface{})
		if !ok {
			return errors.Wrapf(ErrInvalidTable, "%+v", data)
		}
		for _, d := range datalist {
			q, err := InsertQueryBuilder(table, d)
			if err != nil {
				return errors.Wrap(err, "Query build failed")
			}
			qs = append(qs, q)
		}
	}
	qs = append(qs, "SET FOREIGN_KEY_CHECKS = 1")

	for _, q := range qs {
		_, err := db.Exec(q)
		if err != nil {
			return errors.Wrapf(err, "Query execute error: %s", q)
		}
	}
	return nil
}

func InsertQueryBuilder(table string, data interface{}) (string, error) {
	q := fmt.Sprintf("INSERT INTO %s ", table)

	l, ok := data.(map[string]interface{})
	if !ok {
		return q, errors.Wrapf(ErrInvalidTable, "%+v", data)
	}
	var key, val string
	for k, v := range l {
		key += fmt.Sprintf("%s, ", k)
		switch v := v.(type) {
		default:
			val += fmt.Sprintf("'%v', ", v)
		case string:
			val += fmt.Sprintf("'%s', ", v)
		}
	}
	q += fmt.Sprintf("(%s) VALUES (%s)", key[0:len(key)-2], val[0:len(val)-2])
	return q, nil
}
<commit_msg>refactor seed<commit_after>package migo

import (
	"database/sql"
	"fmt"

	"github.com/pkg/errors"
)

const (
	offFKCheck = "SET FOREIGN_KEY_CHECKS = 0"
	onFKCheck  = "SET FOREIGN_KEY_CHECKS = 1"
)

func GetDSN(dbpath, env string) (string, error) {
	dbconf, err := NewDb(dbpath, env)
	if err != nil {
		return "", errors.Wrap(err, "Parse db configue")
	}
	sqlconf := NewSql(*dbconf)
	return sqlconf.DbConf.FormatDSN(), nil
}

func GetInsertQuery(table string, data interface{}) (string, error) {
	q := fmt.Sprintf("INSERT INTO %s ", table)

	l, ok := data.(map[string]interface{})
	if !ok {
		return q, errors.Wrapf(ErrInvalidTable, "%+v", data)
	}
	var key, val string
	for k, v := range l {
		key += fmt.Sprintf("%s, ", k)
		switch v := v.(type) {
		default:
			val += fmt.Sprintf("'%v', ", v)
		case string:
			val += fmt.Sprintf("'%s', ", v)
		}
	}
	q += fmt.Sprintf("(%s) VALUES (%s)", key[0:len(key)-2], val[0:len(val)-2])
	return q, nil
}

func GetInsertQueries(path string) ([]string, error) {
	y, err := ParseYAML(path)
	if err != nil {
		return nil, errors.Wrap(err, "Parse YAML")
	}

	qs := []string{offFKCheck}
	for table, data := range y {
		datalist, ok := data.([]interface{})
		if !ok {
			return nil, errors.Wrapf(ErrInvalidTable, "%+v", data)
		}
		for _, d := range datalist {
			q, err := GetInsertQuery(table, d)
			if err != nil {
				return nil, errors.Wrap(err, "Query build failed")
			}
			qs = append(qs, q)
		}
	}
	qs = append(qs, onFKCheck)
	return qs, nil
}

func GetDeleteQueries(path string) ([]string, error) {
	y, err := ParseYAML(path)
	if err != nil {
		return nil, errors.Wrap(err, "Parse YAML")
	}

	qs := []string{offFKCheck}
	for table, _ := range y {
		qs = append(qs, fmt.Sprintf("TRUNCATE TABLE %s", table))
	}
	qs = append(qs, onFKCheck)
	return qs, nil
}

func Exec(qs []string, dsn string) error {
	db, err := sql.Open("mysql", dsn)
	if err != nil {
		return errors.Wrap(err, "Create db connection")
	}
	defer db.Close()

	for _, q := range qs {
		_, err := db.Exec(q)
		if err != nil {
			return errors.Wrapf(err, "Query execute error: %s", q)
		}
	}
	return nil
}

func InsertRecords(path, dsn string) error {
	qs, err := GetInsertQueries(path)
	if err != nil {
		return err
	}
	if err := Exec(qs, dsn); err != nil {
		return errors.Wrap(err, "insert records")
	}
	return nil
}

func DropRecords(path, dsn string) error {
	qs, err := GetDeleteQueries(path)
	if err != nil {
		return err
	}
	if err := Exec(qs, dsn); err != nil {
		return errors.Wrap(err, "truncate records")
	}
	return nil
}

func Seed(path, dbpath, env string) error {
	dsn, err := GetDSN(dbpath, env)
	if err != nil {
		return err
	}

	if err := InsertRecords(path, dsn); err != nil {
		return err
	}

	return nil

}
<|endoftext|>