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

package gitlab

import (
	"bytes"
	"fmt"
	"io"
	"net/url"
	"time"
)

// JobsService handles communication with the ci builds related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type JobsService struct {
	client *Client
}

// Job represents a ci build.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type Job struct {
	Commit        *Commit    `json:"commit"`
	CreatedAt     *time.Time `json:"created_at"`
	Coverage      float64    `json:"coverage"`
	ArtifactsFile struct {
		Filename string `json:"filename"`
		Size     int    `json:"size"`
	} `json:"artifacts_file"`
	FinishedAt *time.Time `json:"finished_at"`
	ID         int        `json:"id"`
	Name       string     `json:"name"`
	Pipeline   struct {
		ID     int    `json:"id"`
		Ref    string `json:"ref"`
		Sha    string `json:"sha"`
		Status string `json:"status"`
	} `json:"pipeline"`
	Ref    string `json:"ref"`
	Runner struct {
		ID          int    `json:"id"`
		Description string `json:"description"`
		Active      bool   `json:"active"`
		IsShared    bool   `json:"is_shared"`
		Name        string `json:"name"`
	} `json:"runner"`
	Stage     string     `json:"stage"`
	StartedAt *time.Time `json:"started_at"`
	Status    string     `json:"status"`
	Tag       bool       `json:"tag"`
	User      *User      `json:"user"`
	WebURL    string     `json:"web_url"`
}

// ListJobsOptions are options for two list apis
type ListJobsOptions struct {
	ListOptions
	Scope []BuildStateValue `url:"scope,omitempty" json:"scope,omitempty"`
}

// ListProjectJobs gets a list of jobs in a project.
//
// The scope of jobs to show, one or array of: created, pending, running,
// failed, success, canceled, skipped; showing all jobs if none provided
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, err
}

// ListPipelineJobs gets a list of jobs for specific pipeline in a
// project. If the pipeline ID is not found, it will respond with 404.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", url.QueryEscape(project), pipelineID)

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []*Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, err
}

// GetJob gets a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// GetJobArtifacts get jobs artifacts of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactsBuf, resp, err
}

// DownloadArtifactsFile download the artifacts file from the given
// reference name and job provided the job finished successfully.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, job string, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download?job=%s", url.QueryEscape(project), refName, job)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactsBuf, resp, err
}

// DownloadSingleArtifactsFile download a file from the artifacts from the
// given reference name and job provided the job finished successfully.
// Only a single file is going to be extracted from the archive and streamed
// to a client.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf(
		"projects/%s/jobs/%d/artifacts/%s",
		url.QueryEscape(project),
		jobID,
		artifactPath,
	)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactBuf, resp, err
}

// GetTraceFile gets a trace of a specific job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/trace", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	traceBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, traceBuf)
	if err != nil {
		return nil, resp, err
	}

	return traceBuf, resp, err
}

// CancelJob cancels a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/cancel", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// RetryJob retries a single job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/retry", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// EraseJob erases a single job of a project, removes a job
// artifacts and a job trace.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/erase", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// KeepArtifacts prevents artifacts from being deleted when
// expiration is set.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// PlayJob triggers a manual action to start a job.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/play", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}
<commit_msg>[Jobs] Send job query parameter to opt{} instead of manually appending it to url path in download artifacts file<commit_after>//
// Copyright 2017, Arkbriar
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package gitlab

import (
	"bytes"
	"fmt"
	"io"
	"net/url"
	"time"
)

// JobsService handles communication with the ci builds related methods
// of the GitLab API.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type JobsService struct {
	client *Client
}

// Job represents a ci build.
//
// GitLab API docs: https://docs.gitlab.com/ce/api/jobs.html
type Job struct {
	Commit        *Commit    `json:"commit"`
	CreatedAt     *time.Time `json:"created_at"`
	Coverage      float64    `json:"coverage"`
	ArtifactsFile struct {
		Filename string `json:"filename"`
		Size     int    `json:"size"`
	} `json:"artifacts_file"`
	FinishedAt *time.Time `json:"finished_at"`
	ID         int        `json:"id"`
	Name       string     `json:"name"`
	Pipeline   struct {
		ID     int    `json:"id"`
		Ref    string `json:"ref"`
		Sha    string `json:"sha"`
		Status string `json:"status"`
	} `json:"pipeline"`
	Ref    string `json:"ref"`
	Runner struct {
		ID          int    `json:"id"`
		Description string `json:"description"`
		Active      bool   `json:"active"`
		IsShared    bool   `json:"is_shared"`
		Name        string `json:"name"`
	} `json:"runner"`
	Stage     string     `json:"stage"`
	StartedAt *time.Time `json:"started_at"`
	Status    string     `json:"status"`
	Tag       bool       `json:"tag"`
	User      *User      `json:"user"`
	WebURL    string     `json:"web_url"`
}

// ListJobsOptions are options for two list apis
type ListJobsOptions struct {
	ListOptions
	Scope []BuildStateValue `url:"scope,omitempty" json:"scope,omitempty"`
}

// ListProjectJobs gets a list of jobs in a project.
//
// The scope of jobs to show, one or array of: created, pending, running,
// failed, success, canceled, skipped; showing all jobs if none provided
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-project-jobs
func (s *JobsService) ListProjectJobs(pid interface{}, opts *ListJobsOptions, options ...OptionFunc) ([]Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs", url.QueryEscape(project))

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, err
}

// ListPipelineJobs gets a list of jobs for specific pipeline in a
// project. If the pipeline ID is not found, it will respond with 404.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#list-pipeline-jobs
func (s *JobsService) ListPipelineJobs(pid interface{}, pipelineID int, opts *ListJobsOptions, options ...OptionFunc) ([]*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/pipelines/%d/jobs", url.QueryEscape(project), pipelineID)

	req, err := s.client.NewRequest("GET", u, opts, options)
	if err != nil {
		return nil, nil, err
	}

	var jobs []*Job
	resp, err := s.client.Do(req, &jobs)
	if err != nil {
		return nil, resp, err
	}

	return jobs, resp, err
}

// GetJob gets a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-single-job
func (s *JobsService) GetJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// GetJobArtifacts get jobs artifacts of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-job-artifacts
func (s *JobsService) GetJobArtifacts(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactsBuf, resp, err
}

// DownloadArtifactsFile download the artifacts file from the given
// reference name and job provided the job finished successfully.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#download-the-artifacts-file
func (s *JobsService) DownloadArtifactsFile(pid interface{}, refName string, job string, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}

    var q struct {
        Job string `url:"job,omitempty" json:"job,omitempty"`
    }
    q.Job = job

	u := fmt.Sprintf("projects/%s/jobs/artifacts/%s/download", url.QueryEscape(project), refName)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactsBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactsBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactsBuf, resp, err
}

// DownloadSingleArtifactsFile download a file from the artifacts from the
// given reference name and job provided the job finished successfully.
// Only a single file is going to be extracted from the archive and streamed
// to a client.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#download-a-single-artifact-file
func (s *JobsService) DownloadSingleArtifactsFile(pid interface{}, jobID int, artifactPath string, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}

	u := fmt.Sprintf(
		"projects/%s/jobs/%d/artifacts/%s",
		url.QueryEscape(project),
		jobID,
		artifactPath,
	)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	artifactBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, artifactBuf)
	if err != nil {
		return nil, resp, err
	}

	return artifactBuf, resp, err
}

// GetTraceFile gets a trace of a specific job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#get-a-trace-file
func (s *JobsService) GetTraceFile(pid interface{}, jobID int, options ...OptionFunc) (io.Reader, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/trace", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("GET", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	traceBuf := new(bytes.Buffer)
	resp, err := s.client.Do(req, traceBuf)
	if err != nil {
		return nil, resp, err
	}

	return traceBuf, resp, err
}

// CancelJob cancels a single job of a project.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#cancel-a-job
func (s *JobsService) CancelJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/cancel", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// RetryJob retries a single job of a project
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#retry-a-job
func (s *JobsService) RetryJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/retry", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// EraseJob erases a single job of a project, removes a job
// artifacts and a job trace.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#erase-a-job
func (s *JobsService) EraseJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/erase", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// KeepArtifacts prevents artifacts from being deleted when
// expiration is set.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#keep-artifacts
func (s *JobsService) KeepArtifacts(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/artifacts/keep", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}

// PlayJob triggers a manual action to start a job.
//
// GitLab API docs:
// https://docs.gitlab.com/ce/api/jobs.html#play-a-job
func (s *JobsService) PlayJob(pid interface{}, jobID int, options ...OptionFunc) (*Job, *Response, error) {
	project, err := parseID(pid)
	if err != nil {
		return nil, nil, err
	}
	u := fmt.Sprintf("projects/%s/jobs/%d/play", url.QueryEscape(project), jobID)

	req, err := s.client.NewRequest("POST", u, nil, options)
	if err != nil {
		return nil, nil, err
	}

	job := new(Job)
	resp, err := s.client.Do(req, job)
	if err != nil {
		return nil, resp, err
	}

	return job, resp, err
}
<|endoftext|>


<commit_before>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/issue9/logs"
)

// RenderJSON 用于将v转换成json数据并写入到w中。code为服务端返回的代码。
// 默认情况下，会在返回的文件头信息中添加Content-Type=application/json;charset=utf-8
// 的信息，若想手动指定该内容，可通过在headers中传递同名变量来改变。
//
// 确保参数v不为nil，否则将触发panic。
// 若v的值是string,[]byte,[]rune则直接转换成字符串写入w。为nil时，
// 不输出任何内容，若需要输出一个空对象，请使用"{}"字符串；
//
// headers用于指定额外的Header信息，若传递nil，则表示没有。
func RenderJSON(w http.ResponseWriter, code int, v interface{}, headers map[string]string) {
	if w == nil {
		panic("web.RenderJSON:参数w不能为空")
	}

	if v == nil {
		renderJSONHeader(w, code, headers)
		return
	}

	var data []byte
	switch val := v.(type) {
	case string:
		data = []byte(val)
	case []byte:
		data = val
	case []rune:
		data = []byte(string(val))
	default:
		var err error
		data, err = json.Marshal(val)
		if err != nil {
			logs.Error("web.RenderJSON:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	renderJSONHeader(w, code, headers)

	// 输出数据
	if _, err := w.Write(data); err != nil {
		logs.Error("web.RenderJSON:", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

// 将headers当作一个头信息输出，若未指定Content-Type，则默认添加application/json;charset=utf-8作为其值。
func renderJSONHeader(w http.ResponseWriter, code int, headers map[string]string) {
	if headers == nil {
		w.Header().Set("Content-Type", "application/json;charset=utf-8")
		w.WriteHeader(code)
		return
	}

	if _, found := headers["Content-Type"]; !found {
		headers["Content-Type"] = "application/json;charset=utf-8"
	}

	for k, v := range headers {
		w.Header().Set(k, v)
	}
	w.WriteHeader(code)
}

// ReadJSON 用于将r中的body当作一个json格式的数据读取到v中，若出错，则返回相应的http状态码表示其错误类型。
// 200 表示一切正常。
func ReadJSON(w http.ResponseWriter, r *http.Request, v interface{}) (status int) {
	if r.Method != "GET" {
		ct := r.Header.Get("Content-Type")
		if strings.Index(ct, "application/json") < 0 && strings.Index(ct, "*/*") < 0 {
			logs.Error("web.ReadJSON:", "request.Content-Type值不正确：", ct)
			return http.StatusUnsupportedMediaType
		}
	}

	accept := r.Header.Get("Accept")
	if strings.Index(accept, "application/json") < 0 && strings.Index(accept, "*/*") < 0 {
		logs.Error("web.ReadJSON:", "request.Accept值不正确：", accept)
		return http.StatusUnsupportedMediaType
	}

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logs.Error("web.ReadJSON:", err)
		return http.StatusInternalServerError
	}

	err = json.Unmarshal(data, v)
	if err != nil {
		logs.Error("web.ReadJSON:", err)
		return 422 // http包中并未定义422错误
	}

	return http.StatusOK
}
<commit_msg>去掉readjson中不必要的w参数<commit_after>// Copyright 2015 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package web

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/issue9/logs"
)

// RenderJSON 用于将v转换成json数据并写入到w中。code为服务端返回的代码。
// 默认情况下，会在返回的文件头信息中添加Content-Type=application/json;charset=utf-8
// 的信息，若想手动指定该内容，可通过在headers中传递同名变量来改变。
//
// 确保参数v不为nil，否则将触发panic。
// 若v的值是string,[]byte,[]rune则直接转换成字符串写入w。为nil时，
// 不输出任何内容，若需要输出一个空对象，请使用"{}"字符串；
//
// headers用于指定额外的Header信息，若传递nil，则表示没有。
func RenderJSON(w http.ResponseWriter, code int, v interface{}, headers map[string]string) {
	if w == nil {
		panic("web.RenderJSON:参数w不能为空")
	}

	if v == nil {
		renderJSONHeader(w, code, headers)
		return
	}

	var data []byte
	switch val := v.(type) {
	case string:
		data = []byte(val)
	case []byte:
		data = val
	case []rune:
		data = []byte(string(val))
	default:
		var err error
		data, err = json.Marshal(val)
		if err != nil {
			logs.Error("web.RenderJSON:", err)
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	renderJSONHeader(w, code, headers)

	// 输出数据
	if _, err := w.Write(data); err != nil {
		logs.Error("web.RenderJSON:", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
}

// 将headers当作一个头信息输出，若未指定Content-Type，则默认添加application/json;charset=utf-8作为其值。
func renderJSONHeader(w http.ResponseWriter, code int, headers map[string]string) {
	if headers == nil {
		w.Header().Set("Content-Type", "application/json;charset=utf-8")
		w.WriteHeader(code)
		return
	}

	if _, found := headers["Content-Type"]; !found {
		headers["Content-Type"] = "application/json;charset=utf-8"
	}

	for k, v := range headers {
		w.Header().Set(k, v)
	}
	w.WriteHeader(code)
}

// ReadJSON 用于将r中的body当作一个json格式的数据读取到v中，若出错，则返回相应的http状态码表示其错误类型。
// 200 表示一切正常。
func ReadJSON(r *http.Request, v interface{}) (status int) {
	if r.Method != "GET" {
		ct := r.Header.Get("Content-Type")
		if strings.Index(ct, "application/json") < 0 && strings.Index(ct, "*/*") < 0 {
			logs.Error("web.ReadJSON:", "request.Content-Type值不正确：", ct)
			return http.StatusUnsupportedMediaType
		}
	}

	accept := r.Header.Get("Accept")
	if strings.Index(accept, "application/json") < 0 && strings.Index(accept, "*/*") < 0 {
		logs.Error("web.ReadJSON:", "request.Accept值不正确：", accept)
		return http.StatusUnsupportedMediaType
	}

	data, err := ioutil.ReadAll(r.Body)
	if err != nil {
		logs.Error("web.ReadJSON:", err)
		return http.StatusInternalServerError
	}

	err = json.Unmarshal(data, v)
	if err != nil {
		logs.Error("web.ReadJSON:", err)
		return 422 // http包中并未定义422错误
	}

	return http.StatusOK
}
<|endoftext|>


<commit_before>package recommender

import (
	"bytes"
	"encoding/json"
	"log"
	"os"

	"github.com/boltdb/bolt"
)

const ratingsDbName string = "ratings.db"
const userBucketName string = "user"
const userLikesBucketName string = "userLikes"
const itemBucketName string = "item"
const itemLikesBucketName string = "itemLikes"

var traceLog *log.Logger

type Rater struct {
	db *bolt.DB
}

func init() {
	traceLog = log.New(os.Stdout, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile)
}

// NewRater returns a new Rater
func NewRater() *Rater {
	// create key/value store for ratings data
	db, err := bolt.Open(ratingsDbName, 0600, nil)
	if err != nil {
		log.Panic(err)
	}
	// create buckets for users and items
	err = db.Update(func(tx *bolt.Tx) error {
		_, err = tx.CreateBucketIfNotExists([]byte(userBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(itemBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(itemLikesBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(userLikesBucketName))
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		log.Panic(err)
	}
	return &Rater{db}
}

// Close closes the rater's store connection. Deferring a call to this method
// is recommended on creation of a Rater.
func (r *Rater) Close() {
	err := r.db.Close()
	if err != nil {
		log.Panic(err)
	}
}

// AddLike records a user liking an item. If the user already likes the item,
// nothing happens. Only if the recording fails will this return an error.
func (r *Rater) AddLike(user *User, item *Item) error {
	traceLog.Printf("AddLike (%s, %s)\n", user.Name, item.Name)

	// If user doesn't exist, add to user bucket
	if err := r.addUser(user); err != nil {
		return err
	}

	// If item doesn't exist, add to item bucket
	if err := r.addItem(item); err != nil {
		return err
	}

	// TODO Make private method
	// Add item to user's likes
	if err := r.db.Update(func(tx *bolt.Tx) error {
		itemIds := [][]byte{}
		userLikesBucket := tx.Bucket([]byte(userLikesBucketName))
		// Find user's liked items
		if data := userLikesBucket.Get(user.Id); data != nil {
			// Get user's liked item IDs
			if err := json.Unmarshal(data, &itemIds); err != nil {
				return err
			}
			// Look for given item ID
			for _, id := range itemIds {
				if bytes.Equal(id, item.Id) {
					// If user already likes item, return early
					traceLog.Printf("Like by (%s, %s) already exists.\n", item.Name, user.Name)
					return nil
				}
			}
		}
		// Add item to user's liked items
		itemIds = append(itemIds, item.Id)
		data, err := json.Marshal(itemIds)
		if err != nil {
			return err
		}
		if err := userLikesBucket.Put(user.Id, data); err != nil {
			return err
		}
		traceLog.Printf("Like by (%s, %s) added.\n", item.Name, user.Name)
		return nil
	}); err != nil {
		return err
	}

	// TODO Return early if the user already likes the item?

	// TODO Make private method
	// Add user to item's LikedBys
	if err := r.db.Update(func(tx *bolt.Tx) error {
		userIds := [][]byte{}
		itemLikesBucket := tx.Bucket([]byte(itemLikesBucketName))
		// Find users who like item
		if data := itemLikesBucket.Get(item.Id); data != nil {
			// Get item's liked-by user IDs
			if err := json.Unmarshal(data, &userIds); err != nil {
				return err
			}
			// Look for user ID
			for _, id := range userIds {
				if bytes.Equal(id, user.Id) {
					// If item already liked by user, return early
					traceLog.Printf("Like (%s, %s) already exists.\n", user.Name, item.Name)
					return nil
				}
			}
		}
		// Add user to item's liked-by
		userIds = append(userIds, user.Id)
		data, err := json.Marshal(userIds)
		if err != nil {
			return err
		}
		if err := itemLikesBucket.Put(item.Id, data); err != nil {
			return err
		}
		traceLog.Printf("Like (%s, %s) added.\n", user.Name, item.Name)
		return nil
	}); err != nil {
		return err
	}

	// In series, do the following (first should `go call` the next)
	// TODO go routine: update similarity indices
	// TODO go routine: update suggestions

	return nil
}

func (r *Rater) addUser(user *User) error {
	err := r.db.Update(func(tx *bolt.Tx) error {
		userBucket := tx.Bucket([]byte(userBucketName))
		// Return early if user already exists
		if data := userBucket.Get(user.Id); data != nil {
			return nil
		}
		// Write JSON encoding of user to DB
		data, err := json.Marshal(user)
		if err != nil {
			return err
		}
		if err := userBucket.Put(user.Id, data); err != nil {
			return err
		}
		traceLog.Printf("User %s added.\n", user.Name)
		return nil
	})

	if err != nil {
		return err
	}
	return nil
}

func (r *Rater) addItem(item *Item) error {
	err := r.db.Update(func(tx *bolt.Tx) error {
		itemBucket := tx.Bucket([]byte(itemBucketName))
		// Return early if item already exists
		if data := itemBucket.Get(item.Id); data != nil {
			return nil
		}
		// Write JSON encoding of item to DB
		data, err := json.Marshal(item)
		if err != nil {
			return err
		}
		if err := itemBucket.Put(item.Id, data); err != nil {
			return err
		}
		traceLog.Printf("Item %s added.\n", item.Name)
		return nil
	})

	if err != nil {
		return err
	}
	return nil
}

// RemoveLike removes a user's record of liking an item. If the user already
// does not like the item (which is different than disliking it), then
// nothing happens. Only if the removal fails will this return an error.
func (r *Rater) RemoveLike(user *User, item *Item) error {
	return nil
}

func (r *Rater) GetItemsByUser(user *User) ([]Item, error) {
	// itemIds used to unmarshal values of (key, value) pairs, which
	// are (user ID, [item ID, ...])
	var itemIds [][]byte
	var items []Item
	// Get item IDs
	if err := r.db.View(func(tx *bolt.Tx) error {
		userBucket := tx.Bucket([]byte(userBucketName))
		if data := userBucket.Get(user.Id); data != nil {
			if err := json.Unmarshal(data, &itemIds); err != nil {
				return err
			}
		}
		return nil

	}); err != nil {
		return nil, err
	}
	// Get Items by itemIds
	if err := r.db.View(func(tx *bolt.Tx) error {
		itemBucket := tx.Bucket([]byte(itemBucketName))
		for _, id := range itemIds {
			if data := itemBucket.Get(id); data != nil {
				var item Item
				if err := json.Unmarshal(data, &item); err != nil {
					return err
				}
				items = append(items, item)
			}
		}
		return nil

	}); err != nil {
		return nil, err
	}
	return items, nil
}

func (r *Rater) GetUsersByItem(item *Item) ([]User, error) {
	return make([]User, 0), nil
}
<commit_msg>Factored AddLike into smaller functions. Wrote GetLikedItems and GetUsersWhoLike functionality.<commit_after>package recommender

import (
	"bytes"
	"encoding/json"
	"log"
	"os"

	"github.com/boltdb/bolt"
)

const ratingsDbName string = "ratings.db"
const userBucketName string = "user"
const userLikesBucketName string = "userLikes"
const itemBucketName string = "item"
const itemLikesBucketName string = "itemLikes"

var traceLog *log.Logger

type Rater struct {
	db *bolt.DB
}

func init() {
	traceLog = log.New(os.Stdout, "TRACE: ", log.Ldate|log.Ltime|log.Lshortfile)
}

// NewRater returns a new Rater
func NewRater() (*Rater, error) {
	// create key/value store for ratings data
	db, err := bolt.Open(ratingsDbName, 0600, nil)
	if err != nil {
		return nil, err
	}
	// create buckets for users and items
	err = db.Update(func(tx *bolt.Tx) error {
		_, err = tx.CreateBucketIfNotExists([]byte(userBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(itemBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(itemLikesBucketName))
		if err != nil {
			return err
		}
		_, err = tx.CreateBucketIfNotExists([]byte(userLikesBucketName))
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	return &Rater{db}, nil
}

// Close closes the rater's store connection. Deferring a call to this method
// is recommended on creation of a Rater.
func (r *Rater) Close() {
	err := r.db.Close()
	if err != nil {
		log.Panic(err)
	}
}

func (r *Rater) GetLikedItems(user *User) ([]Item, error) {
	var items []Item
	var itemIds [][]byte

	if err := r.db.View(func(tx *bolt.Tx) error {
		// Get item IDs
		userLikesBucket := tx.Bucket([]byte(userLikesBucketName))
		data := userLikesBucket.Get(user.Id)
		if data == nil {
			return nil
		}
		if err := json.Unmarshal(data, &itemIds); err != nil {
			return err
		}
		// Get items by ID
		for _, id := range itemIds {
			item, err := r.getItem(id)
			if err != nil {
				traceLog.Printf("WARNING: Cannot find item ID=%v\n", id)
				continue
			}
			items = append(items, *item)
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return items, nil
}

func (r *Rater) getItem(id []byte) (*Item, error) {
	var item Item

	if err := r.db.View(func(tx *bolt.Tx) error {
		itemBucket := tx.Bucket([]byte(itemBucketName))
		if data := itemBucket.Get(id); data != nil {
			if err := json.Unmarshal(data, &item); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return &item, nil
}

func (r *Rater) GetUsersWhoLike(item *Item) ([]User, error) {
	var users []User
	var userIds [][]byte

	if err := r.db.View(func(tx *bolt.Tx) error {
		itemLikeBucket := tx.Bucket([]byte(itemLikesBucketName))
		// Get user IDs
		data := itemLikeBucket.Get(item.Id)
		if data == nil {
			return nil
		}
		if err := json.Unmarshal(data, &userIds); err != nil {
			return err
		}
		// Get users by ID
		for _, id := range userIds {
			user, err := r.getUser(id)
			if err != nil {
				traceLog.Printf("WARNING: Cannot find user ID=%v\n", id)
				continue
			}
			users = append(users, *user)
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return users, nil
}

func (r *Rater) getUser(id []byte) (*User, error) {
	var user User

	if err := r.db.View(func(tx *bolt.Tx) error {
		userBucket := tx.Bucket([]byte(userBucketName))
		if data := userBucket.Get(id); data != nil {
			if err := json.Unmarshal(data, &user); err != nil {
				return err
			}
		}
		return nil
	}); err != nil {
		return nil, err
	}

	return &user, nil
}

// AddLike records a user liking an item. If the user already likes the item,
// nothing happens. Only if the recording fails will this return an error.
func (r *Rater) AddLike(user *User, item *Item) error {
	traceLog.Printf("AddLike (%s, %s)\n", user.Name, item.Name)

	// Add user if record does not already exist
	if err := r.addUser(user); err != nil {
		return err
	}

	// Add item if records does not already exist
	if err := r.addItem(item); err != nil {
		return err
	}

	// Add like (bi-directional) if records do not already exist.
	if err := r.addLike(user, item); err != nil {
		return err
	}

	// In series:
	// TODO go update similarity indices
	// TODO go update suggestions

	return nil
}

func (r *Rater) addUser(user *User) error {
	err := r.db.Update(func(tx *bolt.Tx) error {
		userBucket := tx.Bucket([]byte(userBucketName))
		// Return early if user already exists
		if data := userBucket.Get(user.Id); data != nil {
			return nil
		}
		// Write JSON encoding of user to DB
		data, err := json.Marshal(user)
		if err != nil {
			return err
		}
		if err := userBucket.Put(user.Id, data); err != nil {
			return err
		}
		traceLog.Printf("User %s added.\n", user.Name)
		return nil
	})

	if err != nil {
		return err
	}
	return nil
}

func (r *Rater) addItem(item *Item) error {
	err := r.db.Update(func(tx *bolt.Tx) error {
		itemBucket := tx.Bucket([]byte(itemBucketName))
		// Return early if item already exists
		if data := itemBucket.Get(item.Id); data != nil {
			return nil
		}
		// Write JSON encoding of item to DB
		data, err := json.Marshal(item)
		if err != nil {
			return err
		}
		if err := itemBucket.Put(item.Id, data); err != nil {
			return err
		}
		traceLog.Printf("Item %s added.\n", item.Name)
		return nil
	})

	if err != nil {
		return err
	}
	return nil
}

func (r *Rater) addLike(user *User, item *Item) error {
	// Add item to user's likes
	if err := r.db.Update(func(tx *bolt.Tx) error {
		itemIds := [][]byte{}
		userLikesBucket := tx.Bucket([]byte(userLikesBucketName))
		// Find user's liked items
		if data := userLikesBucket.Get(user.Id); data != nil {
			// Get user's liked item IDs
			if err := json.Unmarshal(data, &itemIds); err != nil {
				return err
			}
			// Look for given item ID
			for _, id := range itemIds {
				if bytes.Equal(id, item.Id) {
					// If user already likes item, return early
					traceLog.Printf("Like by (%s, %s) already exists.\n", item.Name, user.Name)
					return nil
				}
			}
		}
		// Add item to user's liked items
		itemIds = append(itemIds, item.Id)
		data, err := json.Marshal(itemIds)
		if err != nil {
			return err
		}
		if err := userLikesBucket.Put(user.Id, data); err != nil {
			return err
		}

		userIds := [][]byte{}
		itemLikesBucket := tx.Bucket([]byte(itemLikesBucketName))
		// Find users who like item
		if data := itemLikesBucket.Get(item.Id); data != nil {
			// Get item's liked-by user IDs
			if err := json.Unmarshal(data, &userIds); err != nil {
				return err
			}
			// Look for user ID
			for _, id := range userIds {
				if bytes.Equal(id, user.Id) {
					// If item already liked by user, return early
					traceLog.Printf("Like (%s, %s) already exists.\n", user.Name, item.Name)
					return nil
				}
			}
		}
		// Add user to item's liked-by
		userIds = append(userIds, user.Id)
		data, err = json.Marshal(userIds)
		if err != nil {
			return err
		}
		if err := itemLikesBucket.Put(item.Id, data); err != nil {
			return err
		}

		traceLog.Printf("Like (%s, %s) added.\n", user.Name, item.Name)
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// RemoveLike removes a user's record of liking an item. If the user already
// does not like the item (which is different than disliking it), then
// nothing happens. Only if the removal fails will this return an error.
func (r *Rater) RemoveLike(user *User, item *Item) error {
	// TODO
	return nil
}

func (r *Rater) GetItemsByUser(user *User) ([]Item, error) {
	// itemIds used to unmarshal values of (key, value) pairs, which
	// are (user ID, [item ID, ...])
	var itemIds [][]byte
	var items []Item
	// Get item IDs
	if err := r.db.View(func(tx *bolt.Tx) error {
		userBucket := tx.Bucket([]byte(userBucketName))
		if data := userBucket.Get(user.Id); data != nil {
			if err := json.Unmarshal(data, &itemIds); err != nil {
				return err
			}
		}
		return nil

	}); err != nil {
		return nil, err
	}
	// Get Items by itemIds
	if err := r.db.View(func(tx *bolt.Tx) error {
		itemBucket := tx.Bucket([]byte(itemBucketName))
		for _, id := range itemIds {
			if data := itemBucket.Get(id); data != nil {
				var item Item
				if err := json.Unmarshal(data, &item); err != nil {
					return err
				}
				items = append(items, item)
			}
		}
		return nil

	}); err != nil {
		return nil, err
	}
	return items, nil
}

func (r *Rater) GetUsersByItem(item *Item) ([]User, error) {
	return make([]User, 0), nil
}
<|endoftext|>


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

package cmdrpkgget

import (
	"context"
	"fmt"
	"strings"

	"github.com/GoogleContainerTools/kpt/internal/errors"
	"github.com/GoogleContainerTools/kpt/internal/util/porch"
	api "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	"k8s.io/client-go/rest"
	"k8s.io/kubectl/pkg/cmd/get"
)

const (
	command = "cmdrpkgget"
	longMsg = `
kpt alpha rpkg get [PACKAGE ...] [flags]

Args:

PACKAGE:
  Name of the package revision to get.

Flags:

--name
  Name of the packages to get. Any package whose name contains this value will be included in the results.

--revision
  Revision of the package to get. Any package whose revision matches this value will be included in the results.

`
)

func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner {
	r := &runner{
		ctx:        ctx,
		cfg:        rcg,
		printFlags: get.NewGetPrintFlags(),
	}
	c := &cobra.Command{
		Use:        "get",
		Aliases:    []string{"list"},
		SuggestFor: []string{},
		Short:      "Gets or lists packages in registered repositories.",
		Long:       longMsg,
		Example:    "kpt alpha rpkg get repository:package:v1 --namespace=default",
		PreRunE:    r.preRunE,
		RunE:       r.runE,
		Hidden:     porch.HidePorchCommands,
	}
	r.Command = c

	// Create flags
	c.Flags().StringVar(&r.name, "name", "", "Name of the packages to get. Any package whose name contains this value will be included in the results.")
	c.Flags().StringVar(&r.revision, "revision", "", "Revision of the packages to get. Any package whose revision matches this value will be included in the results.")
	r.printFlags.AddFlags(c)
	return r
}

func NewCommand(ctx context.Context, rcg *genericclioptions.ConfigFlags) *cobra.Command {
	return newRunner(ctx, rcg).Command
}

type runner struct {
	ctx     context.Context
	cfg     *genericclioptions.ConfigFlags
	Command *cobra.Command

	// Flags
	name       string
	revision   string
	printFlags *get.PrintFlags

	requestTable bool
}

func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
	outputOption := cmd.Flags().Lookup("output").Value.String()
	if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
		r.requestTable = false
	} else {
		r.requestTable = true
	}
	return nil
}

func (r *runner) runE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".runE"

	var objs []runtime.Object
	b := resource.NewBuilder(r.cfg).
		Unstructured().
		NamespaceParam(*r.cfg.Namespace).DefaultNamespace()

	if len(args) > 0 {
		b = b.ResourceNames("packagerevisions", args...)
	} else {
		b = b.SelectAllParam(true).
			ResourceTypes("packagerevisions")
	}

	b = b.ContinueOnError().
		Latest().
		Flatten()

	if r.requestTable {
		b = b.TransformRequests(func(req *rest.Request) {
			req.SetHeader("Accept", strings.Join([]string{
				"application/json;as=Table;g=meta.k8s.io;v=v1",
				"application/json",
			}, ","))
		})
	}

	res := b.Do()
	if err := res.Err(); err != nil {
		return errors.E(op, err)
	}

	infos, err := res.Infos()
	if err != nil {
		return errors.E(op, err)
	}

	for _, i := range infos {
		switch obj := i.Object.(type) {
		case *unstructured.Unstructured:
			o, err := r.tranformAndMatch(obj)
			if err != nil {
				return errors.E(op, err)
			}
			if o != nil {
				objs = append(objs, o)
			}
		default:
			return errors.E(op, fmt.Sprintf("Unrecognized response %T", obj))
		}
	}

	printer, err := r.printFlags.ToPrinter()
	if err != nil {
		return errors.E(op, err)
	}

	if r.requestTable {
		printer = &get.TablePrinter{Delegate: printer}
	}

	w := printers.GetNewTabWriter(cmd.OutOrStdout())

	for _, obj := range objs {
		if err := printer.PrintObj(obj, w); err != nil {
			return errors.E(op, err)
		}
	}

	if err := w.Flush(); err != nil {
		return errors.E(op, err)
	}

	return nil
}

var tableGVK = metav1.SchemeGroupVersion.WithKind("Table")
var packageRevisionGVK = api.SchemeGroupVersion.WithKind("PackageRevision")

func (r *runner) tranformAndMatch(obj *unstructured.Unstructured) (runtime.Object, error) {
	switch gvk := obj.GroupVersionKind(); gvk {
	case tableGVK:
		return r.matchTable(obj)
	case packageRevisionGVK:
		return r.matchPackageRevision(obj)
	default:
		fmt.Fprintf(r.Command.OutOrStderr(), "Unrecognized response type %s", gvk)
		return obj, nil // unrecognized, attempt to print
	}
}

func (r *runner) matchPackageRevision(o *unstructured.Unstructured) (runtime.Object, error) {
	packageName, _, err := unstructured.NestedString(o.Object, "spec", "packageName")
	if err != nil {
		return nil, err
	}
	revision, _, err := unstructured.NestedString(o.Object, "spec", "revision")
	if err != nil {
		return nil, err
	}
	if !strings.Contains(packageName, r.name) {
		return nil, nil
	}
	if r.revision != "" && r.revision != revision {
		return nil, nil
	}
	return o, nil
}

func findColumn(cols []metav1.TableColumnDefinition, name string) int {
	for i := range cols {
		if cols[i].Name == name {
			return i
		}
	}
	return -1
}

func getStringCell(cells []interface{}, col int) (string, bool) {
	if col < 0 {
		return "", false
	}
	s, ok := cells[col].(string)
	return s, ok
}

func (r *runner) matchTable(u *unstructured.Unstructured) (runtime.Object, error) {
	table := &metav1.Table{}
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, table); err != nil {
		return nil, err
	}

	filtered := make([]metav1.TableRow, 0, len(table.Rows))
	nameCol := findColumn(table.ColumnDefinitions, "Package")
	revisionCol := findColumn(table.ColumnDefinitions, "Revision")

	for i := range table.Rows {
		row := &table.Rows[i]
		if row.Object.Object != nil {
			fmt.Fprintf(r.Command.OutOrStderr(), "Found non-nil Object in table: %s",
				row.Object.Object.GetObjectKind().GroupVersionKind())
		} else {
			if name, ok := getStringCell(row.Cells, nameCol); ok {
				if !strings.Contains(name, r.name) {
					continue
				}
			}
			if revision, ok := getStringCell(row.Cells, revisionCol); ok {
				if r.revision != "" && r.revision != revision {
					continue
				}
			}
		}

		// Row matches
		filtered = append(filtered, *row)
	}
	table.Rows = filtered
	return table, nil
}
<commit_msg>update kpt alpha rpkg get help (#3091)<commit_after>// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmdrpkgget

import (
	"context"
	"fmt"
	"strings"

	"github.com/GoogleContainerTools/kpt/internal/errors"
	"github.com/GoogleContainerTools/kpt/internal/util/porch"
	api "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
	"github.com/spf13/cobra"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/cli-runtime/pkg/genericclioptions"
	"k8s.io/cli-runtime/pkg/printers"
	"k8s.io/cli-runtime/pkg/resource"
	"k8s.io/client-go/rest"
	"k8s.io/kubectl/pkg/cmd/get"
)

const (
	command = "cmdrpkgget"
	longMsg = `
kpt alpha rpkg get [PACKAGE ...] [flags]

Args:

PACKAGE:
  Name of the package revision to get.

Flags:

--name
  Name of the packages to get. Any package whose name contains this value will be included in the results.

--revision
  Revision of the package to get. Any package whose revision matches this value will be included in the results.

`
)

func newRunner(ctx context.Context, rcg *genericclioptions.ConfigFlags) *runner {
	r := &runner{
		ctx:        ctx,
		cfg:        rcg,
		printFlags: get.NewGetPrintFlags(),
	}
	c := &cobra.Command{
		Use:        "get",
		Aliases:    []string{"list"},
		SuggestFor: []string{},
		Short:      "Gets or lists packages in registered repositories.",
		Long:       longMsg,
		Example:    "kpt alpha rpkg get package-name --namespace=default",
		PreRunE:    r.preRunE,
		RunE:       r.runE,
		Hidden:     porch.HidePorchCommands,
	}
	r.Command = c

	// Create flags
	c.Flags().StringVar(&r.name, "name", "", "Name of the packages to get. Any package whose name contains this value will be included in the results.")
	c.Flags().StringVar(&r.revision, "revision", "", "Revision of the packages to get. Any package whose revision matches this value will be included in the results.")
	r.printFlags.AddFlags(c)
	return r
}

func NewCommand(ctx context.Context, rcg *genericclioptions.ConfigFlags) *cobra.Command {
	return newRunner(ctx, rcg).Command
}

type runner struct {
	ctx     context.Context
	cfg     *genericclioptions.ConfigFlags
	Command *cobra.Command

	// Flags
	name       string
	revision   string
	printFlags *get.PrintFlags

	requestTable bool
}

func (r *runner) preRunE(cmd *cobra.Command, args []string) error {
	outputOption := cmd.Flags().Lookup("output").Value.String()
	if strings.Contains(outputOption, "custom-columns") || outputOption == "yaml" || strings.Contains(outputOption, "json") {
		r.requestTable = false
	} else {
		r.requestTable = true
	}
	return nil
}

func (r *runner) runE(cmd *cobra.Command, args []string) error {
	const op errors.Op = command + ".runE"

	var objs []runtime.Object
	b := resource.NewBuilder(r.cfg).
		Unstructured().
		NamespaceParam(*r.cfg.Namespace).DefaultNamespace()

	if len(args) > 0 {
		b = b.ResourceNames("packagerevisions", args...)
	} else {
		b = b.SelectAllParam(true).
			ResourceTypes("packagerevisions")
	}

	b = b.ContinueOnError().
		Latest().
		Flatten()

	if r.requestTable {
		b = b.TransformRequests(func(req *rest.Request) {
			req.SetHeader("Accept", strings.Join([]string{
				"application/json;as=Table;g=meta.k8s.io;v=v1",
				"application/json",
			}, ","))
		})
	}

	res := b.Do()
	if err := res.Err(); err != nil {
		return errors.E(op, err)
	}

	infos, err := res.Infos()
	if err != nil {
		return errors.E(op, err)
	}

	for _, i := range infos {
		switch obj := i.Object.(type) {
		case *unstructured.Unstructured:
			o, err := r.tranformAndMatch(obj)
			if err != nil {
				return errors.E(op, err)
			}
			if o != nil {
				objs = append(objs, o)
			}
		default:
			return errors.E(op, fmt.Sprintf("Unrecognized response %T", obj))
		}
	}

	printer, err := r.printFlags.ToPrinter()
	if err != nil {
		return errors.E(op, err)
	}

	if r.requestTable {
		printer = &get.TablePrinter{Delegate: printer}
	}

	w := printers.GetNewTabWriter(cmd.OutOrStdout())

	for _, obj := range objs {
		if err := printer.PrintObj(obj, w); err != nil {
			return errors.E(op, err)
		}
	}

	if err := w.Flush(); err != nil {
		return errors.E(op, err)
	}

	return nil
}

var tableGVK = metav1.SchemeGroupVersion.WithKind("Table")
var packageRevisionGVK = api.SchemeGroupVersion.WithKind("PackageRevision")

func (r *runner) tranformAndMatch(obj *unstructured.Unstructured) (runtime.Object, error) {
	switch gvk := obj.GroupVersionKind(); gvk {
	case tableGVK:
		return r.matchTable(obj)
	case packageRevisionGVK:
		return r.matchPackageRevision(obj)
	default:
		fmt.Fprintf(r.Command.OutOrStderr(), "Unrecognized response type %s", gvk)
		return obj, nil // unrecognized, attempt to print
	}
}

func (r *runner) matchPackageRevision(o *unstructured.Unstructured) (runtime.Object, error) {
	packageName, _, err := unstructured.NestedString(o.Object, "spec", "packageName")
	if err != nil {
		return nil, err
	}
	revision, _, err := unstructured.NestedString(o.Object, "spec", "revision")
	if err != nil {
		return nil, err
	}
	if !strings.Contains(packageName, r.name) {
		return nil, nil
	}
	if r.revision != "" && r.revision != revision {
		return nil, nil
	}
	return o, nil
}

func findColumn(cols []metav1.TableColumnDefinition, name string) int {
	for i := range cols {
		if cols[i].Name == name {
			return i
		}
	}
	return -1
}

func getStringCell(cells []interface{}, col int) (string, bool) {
	if col < 0 {
		return "", false
	}
	s, ok := cells[col].(string)
	return s, ok
}

func (r *runner) matchTable(u *unstructured.Unstructured) (runtime.Object, error) {
	table := &metav1.Table{}
	if err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, table); err != nil {
		return nil, err
	}

	filtered := make([]metav1.TableRow, 0, len(table.Rows))
	nameCol := findColumn(table.ColumnDefinitions, "Package")
	revisionCol := findColumn(table.ColumnDefinitions, "Revision")

	for i := range table.Rows {
		row := &table.Rows[i]
		if row.Object.Object != nil {
			fmt.Fprintf(r.Command.OutOrStderr(), "Found non-nil Object in table: %s",
				row.Object.Object.GetObjectKind().GroupVersionKind())
		} else {
			if name, ok := getStringCell(row.Cells, nameCol); ok {
				if !strings.Contains(name, r.name) {
					continue
				}
			}
			if revision, ok := getStringCell(row.Cells, revisionCol); ok {
				if r.revision != "" && r.revision != revision {
					continue
				}
			}
		}

		// Row matches
		filtered = append(filtered, *row)
	}
	table.Rows = filtered
	return table, nil
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"log"
	"os"
	"os/exec"
	"reflect"
	"text/template"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util"
)

const (
	nginxConf = `
events {
  worker_connections 1024;
}
http {
  # http://nginx.org/en/docs/http/ngx_http_core_module.html
  types_hash_max_size 2048;
  server_names_hash_max_size 512;
  server_names_hash_bucket_size 64;

  server {
    listen 80;
    return 301 https://$host$request_uri;
  }

{{range $ing := .Items}}
{{range $rule := $ing.Spec.Rules}}
  server {
    listen 443;
    ssl on;
    ssl_certificate /keys/wildcard-ssl.crt;
    ssl_certificate_key /keys/wildcard-ssl.key;
    server_name {{$rule.Host}};
{{ range $path := $rule.HTTP.Paths }}
    location {{$path.Path}} {
      proxy_set_header Host $host;
      proxy_pass http://{{$path.Backend.ServiceName}}.{{$ing.Namespace}}.svc.cluster.local;
    }{{end}}
  }{{end}}{{end}}
}`
)

func shellOut(cmd string) {
	out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	if err != nil {
		log.Fatalf("Failed to execute %v: %v, err: %v", cmd, string(out), err)
	}
}

func main() {
	var ingClient client.IngressInterface
	if kubeClient, err := client.NewInCluster(); err != nil {
		log.Fatalf("Failed to create client: %v.", err)
	} else {
		ingClient = kubeClient.Extensions().Ingress(api.NamespaceAll)
	}
	tmpl, _ := template.New("nginx").Parse(nginxConf)
	rateLimiter := util.NewTokenBucketRateLimiter(0.1, 1)
	known := &extensions.IngressList{}

	// Controller loop
	shellOut("nginx")
	for {
		rateLimiter.Accept()
		ingresses, err := ingClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving ingresses: %v", err)
			continue
		}
		if reflect.DeepEqual(ingresses.Items, known.Items) {
			continue
		}
		known = ingresses
		if w, err := os.Create("/etc/nginx/nginx.conf"); err != nil {
			log.Fatalf("Failed to open %v: %v", nginxConf, err)
		} else if err := tmpl.Execute(w, ingresses); err != nil {
			log.Fatalf("Failed to write template %v", err)
		}
		shellOut("nginx -s reload")
	}
}
<commit_msg>add some more information to proxied connections<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

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

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

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

package main

import (
	"log"
	"os"
	"os/exec"
	"reflect"
	"text/template"

	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/apis/extensions"
	client "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/fields"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util"
)

const (
	nginxConf = `
events {
  worker_connections 1024;
}
http {
  # http://nginx.org/en/docs/http/ngx_http_core_module.html
  types_hash_max_size 2048;
  server_names_hash_max_size 512;
  server_names_hash_bucket_size 64;

  server {
    listen 80;
    return 301 https://$host$request_uri;
  }

{{range $ing := .Items}}
{{range $rule := $ing.Spec.Rules}}
  server {
    listen 443;
    ssl on;
    ssl_certificate /keys/wildcard-ssl.crt;
    ssl_certificate_key /keys/wildcard-ssl.key;
    server_name {{$rule.Host}};
{{ range $path := $rule.HTTP.Paths }}
    location {{$path.Path}} {
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_redirect http:// https://;
      proxy_pass http://{{$path.Backend.ServiceName}}.{{$ing.Namespace}}.svc.cluster.local;
    }{{end}}
  }{{end}}{{end}}
}`
)

func shellOut(cmd string) {
	out, err := exec.Command("sh", "-c", cmd).CombinedOutput()
	if err != nil {
		log.Fatalf("Failed to execute %v: %v, err: %v", cmd, string(out), err)
	}
}

func main() {
	var ingClient client.IngressInterface
	if kubeClient, err := client.NewInCluster(); err != nil {
		log.Fatalf("Failed to create client: %v.", err)
	} else {
		ingClient = kubeClient.Extensions().Ingress(api.NamespaceAll)
	}
	tmpl, _ := template.New("nginx").Parse(nginxConf)
	rateLimiter := util.NewTokenBucketRateLimiter(0.1, 1)
	known := &extensions.IngressList{}

	// Controller loop
	shellOut("nginx")
	for {
		rateLimiter.Accept()
		ingresses, err := ingClient.List(labels.Everything(), fields.Everything())
		if err != nil {
			log.Printf("Error retrieving ingresses: %v", err)
			continue
		}
		if reflect.DeepEqual(ingresses.Items, known.Items) {
			continue
		}
		known = ingresses
		if w, err := os.Create("/etc/nginx/nginx.conf"); err != nil {
			log.Fatalf("Failed to open %v: %v", nginxConf, err)
		} else if err := tmpl.Execute(w, ingresses); err != nil {
			log.Fatalf("Failed to write template %v", err)
		}
		shellOut("nginx -s reload")
	}
}
<|endoftext|>


<commit_before>// +build windows

package ansicolor

import (
	"bytes"
	"io"
	"strings"
	"syscall"
	"unsafe"
)

type csiState int

const (
	outsideCsiCode csiState = iota
	firstCsiCode
	secondeCsiCode
)

type ansiColorWriter struct {
	w        io.Writer
	state    csiState
	paramBuf bytes.Buffer
	textBuf  bytes.Buffer
}

const (
	firstCsiChar   byte = '\x1b'
	secondeCsiChar byte = '['
	separatorChar  byte = ';'
	sgrCode        byte = 'm'
)

const (
	foregroundBlue      = uint16(0x0001)
	foregroundGreen     = uint16(0x0002)
	foregroundRed       = uint16(0x0004)
	foregroundIntensity = uint16(0x0008)
	backgroundBlue      = uint16(0x0010)
	backgroundGreen     = uint16(0x0020)
	backgroundRed       = uint16(0x0040)
	backgroundIntensity = uint16(0x0080)

	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
)

const (
	ansiReset        = "0"
	ansiIntensityOn  = "1"
	ansiIntensityOff = "22"

	ansiForegroundBlack   = "30"
	ansiForegroundRed     = "31"
	ansiForegroundGreen   = "32"
	ansiForegroundYellow  = "33"
	ansiForegroundBlue    = "34"
	ansiForegroundMagenta = "35"
	ansiForegroundCyan    = "36"
	ansiForegroundWhite   = "37"
	ansiForegroundDefault = "39"

	ansiBackgroundBlack   = "40"
	ansiBackgroundRed     = "41"
	ansiBackgroundGreen   = "42"
	ansiBackgroundYellow  = "43"
	ansiBackgroundBlue    = "44"
	ansiBackgroundMagenta = "45"
	ansiBackgroundCyan    = "46"
	ansiBackgroundWhite   = "47"
	ansiBackgroundDefault = "49"
)

type drawType int

const (
	foreground drawType = iota
	background
)

type winColor struct {
	code     uint16
	drawType drawType
}

var colorMap = map[string]winColor{
	ansiForegroundBlack:   {0, foreground},
	ansiForegroundRed:     {foregroundRed, foreground},
	ansiForegroundGreen:   {foregroundGreen, foreground},
	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
	ansiForegroundBlue:    {foregroundBlue, foreground},
	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiBackgroundBlack:   {0, background},
	ansiBackgroundRed:     {backgroundRed, background},
	ansiBackgroundGreen:   {backgroundGreen, background},
	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
	ansiBackgroundBlue:    {backgroundBlue, background},
	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
	ansiBackgroundDefault: {0, background},
}

var (
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
)

type coord struct {
	X, Y int16
}

type smallRect struct {
	Left, Top, Right, Bottom int16
}

type consoleScreenBufferInfo struct {
	DwSize              coord
	DwCursorPosition    coord
	WAttributes         uint16
	SrWindow            smallRect
	DwMaximumWindowSize coord
}

func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
	var csbi consoleScreenBufferInfo
	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
		hConsoleOutput,
		uintptr(unsafe.Pointer(&csbi)))
	if ret == 0 {
		return nil
	}
	return &csbi
}

func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
	ret, _, _ := procSetConsoleTextAttribute.Call(
		hConsoleOutput,
		uintptr(wAttributes))
	return ret != 0
}

func changeColor(param []byte) {
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo == nil {
		return
	}

	wAttributes := screenInfo.WAttributes
	winForegroundColor := wAttributes & (foregroundRed | foregroundGreen | foregroundBlue)
	winBackgroundColor := wAttributes & (backgroundRed | backgroundGreen | backgroundBlue)
	isWinIntensity := (wAttributes & foregroundIntensity) != 0
	csiParam := strings.Split(string(param), string(separatorChar))
	for _, p := range csiParam {
		c, ok := colorMap[p]
		switch {
		case !ok:
			switch p {
			case ansiReset:
				winForegroundColor = foregroundRed | foregroundGreen | foregroundBlue
				winBackgroundColor = 0
				isWinIntensity = false
			case ansiIntensityOn:
				isWinIntensity = true
			case ansiIntensityOff:
				isWinIntensity = false
			default:
				// unknown code
			}
		case c.drawType == foreground:
			winForegroundColor = c.code
		case c.drawType == background:
			winBackgroundColor = c.code
		}
	}
	if isWinIntensity {
		winForegroundColor |= foregroundIntensity
	}
	setConsoleTextAttribute(uintptr(syscall.Stdout), winForegroundColor|winBackgroundColor)
}

func parseEscapeSequence(command byte, param []byte) {
	switch command {
	case sgrCode:
		changeColor(param)
	}
}

func isParameterChar(b byte) bool {
	return ('0' <= b && b <= '9') || b == separatorChar
}

func (cw *ansiColorWriter) pushBuffer(ch byte) {
	cw.textBuf.WriteByte(ch)
}

func (cw *ansiColorWriter) flushBuffer() (int, error) {
	text := cw.textBuf.Bytes()
	cw.textBuf.Reset()
	return cw.w.Write(text)
}

func (cw *ansiColorWriter) Write(p []byte) (int, error) {
	r := 0
	for _, ch := range p {
		switch cw.state {
		case outsideCsiCode:
			if ch == firstCsiChar {
				cw.state = firstCsiCode
			} else {
				cw.pushBuffer(ch)
			}
		case firstCsiCode:
			switch ch {
			case firstCsiChar:
				cw.pushBuffer(ch)
			case secondeCsiChar:
				cw.state = secondeCsiCode
			default:
				cw.pushBuffer(firstCsiChar)
				cw.pushBuffer(ch)
				cw.state = outsideCsiCode
			}
		case secondeCsiCode:
			if isParameterChar(ch) {
				cw.paramBuf.WriteByte(ch)
			} else {
				nw, err := cw.flushBuffer()
				r += nw
				if err != nil {
					return r, err
				}
				param := cw.paramBuf.Bytes()
				cw.paramBuf.Reset()
				parseEscapeSequence(ch, param)
				cw.state = outsideCsiCode
			}
		default:
			cw.pushBuffer(ch)
			cw.state = outsideCsiCode
		}
	}

	nw, err := cw.flushBuffer()
	return r + nw, err
}
<commit_msg>Refactor<commit_after>// +build windows

package ansicolor

import (
	"bytes"
	"io"
	"strings"
	"syscall"
	"unsafe"
)

type csiState int

const (
	outsideCsiCode csiState = iota
	firstCsiCode
	secondeCsiCode
)

type ansiColorWriter struct {
	w        io.Writer
	state    csiState
	paramBuf bytes.Buffer
	textBuf  bytes.Buffer
}

const (
	firstCsiChar   byte = '\x1b'
	secondeCsiChar byte = '['
	separatorChar  byte = ';'
	sgrCode        byte = 'm'
)

const (
	foregroundBlue      = uint16(0x0001)
	foregroundGreen     = uint16(0x0002)
	foregroundRed       = uint16(0x0004)
	foregroundIntensity = uint16(0x0008)
	backgroundBlue      = uint16(0x0010)
	backgroundGreen     = uint16(0x0020)
	backgroundRed       = uint16(0x0040)
	backgroundIntensity = uint16(0x0080)

	foregroundMask = foregroundBlue | foregroundGreen | foregroundRed | foregroundIntensity
	backgroundMask = backgroundBlue | backgroundGreen | backgroundRed | backgroundIntensity
)

const (
	ansiReset        = "0"
	ansiIntensityOn  = "1"
	ansiIntensityOff = "22"

	ansiForegroundBlack   = "30"
	ansiForegroundRed     = "31"
	ansiForegroundGreen   = "32"
	ansiForegroundYellow  = "33"
	ansiForegroundBlue    = "34"
	ansiForegroundMagenta = "35"
	ansiForegroundCyan    = "36"
	ansiForegroundWhite   = "37"
	ansiForegroundDefault = "39"

	ansiBackgroundBlack   = "40"
	ansiBackgroundRed     = "41"
	ansiBackgroundGreen   = "42"
	ansiBackgroundYellow  = "43"
	ansiBackgroundBlue    = "44"
	ansiBackgroundMagenta = "45"
	ansiBackgroundCyan    = "46"
	ansiBackgroundWhite   = "47"
	ansiBackgroundDefault = "49"
)

type drawType int

const (
	foreground drawType = iota
	background
)

type winColor struct {
	code     uint16
	drawType drawType
}

var colorMap = map[string]winColor{
	ansiForegroundBlack:   {0, foreground},
	ansiForegroundRed:     {foregroundRed, foreground},
	ansiForegroundGreen:   {foregroundGreen, foreground},
	ansiForegroundYellow:  {foregroundRed | foregroundGreen, foreground},
	ansiForegroundBlue:    {foregroundBlue, foreground},
	ansiForegroundMagenta: {foregroundRed | foregroundBlue, foreground},
	ansiForegroundCyan:    {foregroundGreen | foregroundBlue, foreground},
	ansiForegroundWhite:   {foregroundRed | foregroundGreen | foregroundBlue, foreground},
	ansiForegroundDefault: {foregroundRed | foregroundGreen | foregroundBlue, foreground},

	ansiBackgroundBlack:   {0, background},
	ansiBackgroundRed:     {backgroundRed, background},
	ansiBackgroundGreen:   {backgroundGreen, background},
	ansiBackgroundYellow:  {backgroundRed | backgroundGreen, background},
	ansiBackgroundBlue:    {backgroundBlue, background},
	ansiBackgroundMagenta: {backgroundRed | backgroundBlue, background},
	ansiBackgroundCyan:    {backgroundGreen | backgroundBlue, background},
	ansiBackgroundWhite:   {backgroundRed | backgroundGreen | backgroundBlue, background},
	ansiBackgroundDefault: {0, background},
}

var (
	kernel32                       = syscall.NewLazyDLL("kernel32.dll")
	procSetConsoleTextAttribute    = kernel32.NewProc("SetConsoleTextAttribute")
	procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
)

type coord struct {
	X, Y int16
}

type smallRect struct {
	Left, Top, Right, Bottom int16
}

type consoleScreenBufferInfo struct {
	DwSize              coord
	DwCursorPosition    coord
	WAttributes         uint16
	SrWindow            smallRect
	DwMaximumWindowSize coord
}

func getConsoleScreenBufferInfo(hConsoleOutput uintptr) *consoleScreenBufferInfo {
	var csbi consoleScreenBufferInfo
	ret, _, _ := procGetConsoleScreenBufferInfo.Call(
		hConsoleOutput,
		uintptr(unsafe.Pointer(&csbi)))
	if ret == 0 {
		return nil
	}
	return &csbi
}

func setConsoleTextAttribute(hConsoleOutput uintptr, wAttributes uint16) bool {
	ret, _, _ := procSetConsoleTextAttribute.Call(
		hConsoleOutput,
		uintptr(wAttributes))
	return ret != 0
}

func changeColor(param []byte) {
	screenInfo := getConsoleScreenBufferInfo(uintptr(syscall.Stdout))
	if screenInfo == nil {
		return
	}

	wAttributes := screenInfo.WAttributes
	winForegroundColor := wAttributes & (foregroundRed | foregroundGreen | foregroundBlue)
	winBackgroundColor := wAttributes & (backgroundRed | backgroundGreen | backgroundBlue)
	winIntensity := wAttributes & foregroundIntensity
	csiParam := strings.Split(string(param), string(separatorChar))
	for _, p := range csiParam {
		c, ok := colorMap[p]
		switch {
		case !ok:
			switch p {
			case ansiReset:
				winForegroundColor = foregroundRed | foregroundGreen | foregroundBlue
				winBackgroundColor = 0
				winIntensity = 0
			case ansiIntensityOn:
				winIntensity = foregroundIntensity
			case ansiIntensityOff:
				winIntensity = 0
			default:
				// unknown code
			}
		case c.drawType == foreground:
			winForegroundColor = c.code
		case c.drawType == background:
			winBackgroundColor = c.code
		}
	}
	setConsoleTextAttribute(uintptr(syscall.Stdout), winForegroundColor|winBackgroundColor|winIntensity)
}

func parseEscapeSequence(command byte, param []byte) {
	switch command {
	case sgrCode:
		changeColor(param)
	}
}

func isParameterChar(b byte) bool {
	return ('0' <= b && b <= '9') || b == separatorChar
}

func (cw *ansiColorWriter) pushBuffer(ch byte) {
	cw.textBuf.WriteByte(ch)
}

func (cw *ansiColorWriter) flushBuffer() (int, error) {
	text := cw.textBuf.Bytes()
	cw.textBuf.Reset()
	return cw.w.Write(text)
}

func (cw *ansiColorWriter) Write(p []byte) (int, error) {
	r := 0
	for _, ch := range p {
		switch cw.state {
		case outsideCsiCode:
			if ch == firstCsiChar {
				cw.state = firstCsiCode
			} else {
				cw.pushBuffer(ch)
			}
		case firstCsiCode:
			switch ch {
			case firstCsiChar:
				cw.pushBuffer(ch)
			case secondeCsiChar:
				cw.state = secondeCsiCode
			default:
				cw.pushBuffer(firstCsiChar)
				cw.pushBuffer(ch)
				cw.state = outsideCsiCode
			}
		case secondeCsiCode:
			if isParameterChar(ch) {
				cw.paramBuf.WriteByte(ch)
			} else {
				nw, err := cw.flushBuffer()
				r += nw
				if err != nil {
					return r, err
				}
				param := cw.paramBuf.Bytes()
				cw.paramBuf.Reset()
				parseEscapeSequence(ch, param)
				cw.state = outsideCsiCode
			}
		default:
			cw.pushBuffer(ch)
			cw.state = outsideCsiCode
		}
	}

	nw, err := cw.flushBuffer()
	return r + nw, err
}
<|endoftext|>


<commit_before>// Package apherror provides a consistent way to handler JSONAPI
// related http errors.
package apherror

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/gocraft/dbr"

	"github.com/manyminds/api2go"
	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/errhttp"
)

var (
	//HTTPError represents generic http errors
	HTTPError = errors.NewClass("HTTP Error", errors.NoCaptureStack())
	//ErrReqContext represents error in extracting context value from http request
	ErrReqContext = newErrorClass("Unable to retrieve context", http.StatusInternalServerError)
	//ErrOuthExchange represents error in exchanging code for token with oauth server
	ErrOauthExchange = newErrorClass("Unable to exchange token for code", http.StatusInternalServerError)
	//ErrUserRetrieval represents error in retrieving user information from oauth provider
	ErrUserRetrieval = newErrorClass("Unable to retrieve user information", http.StatusInternalServerError)
	//ErrDatabaseQuery represents database query related errors
	ErrDatabaseQuery = newErrorClass("Database query error", http.StatusInternalServerError)
	//ErrNotFound represents the absence of an HTTP resource
	ErrNotFound = newErrorClass("Resource not found", http.StatusNotFound)
	//ErrJSONEncoding represents any json encoding error
	ErrJSONEncoding = newErrorClass("Json encoding error", http.StatusInternalServerError)
	//ErrStructMarshal represents any error with marshalling structure
	ErrStructMarshal = newErrorClass("Structure marshalling error", http.StatusInternalServerError)
	//ErrIncludeParam represents any error with invalid include query parameter
	ErrIncludeParam = newErrorClassWithParam("Invalid include query parameter", "include", http.StatusBadRequest)
	//ErrSparseFieldSets represents any error with invalid sparse fieldsets query parameter
	ErrSparseFieldSets = newErrorClassWithParam("Invalid sparse fieldsets query parameter", "fieldsets", http.StatusBadRequest)
	//ErrFilterParam represents any error with invalid filter query paramter
	ErrFilterParam = newErrorClassWithParam("Invalid filter query parameter", "filter", http.StatusBadRequest)
	//ErrNotAcceptable represents any error with wrong or inappropriate http Accept header
	ErrNotAcceptable = newErrorClass("Accept header is not acceptable", http.StatusNotAcceptable)
	//ErrUnsupportedMedia represents any error with unsupported media type in http header
	ErrUnsupportedMedia = newErrorClass("Media type is not supported", http.StatusUnsupportedMediaType)
	//ErrQueryParam represents any error with http query parameters
	ErrQueryParam = newErrorClass("Invalid query parameter", http.StatusBadRequest)
	titleErrKey   = errors.GenSym()
	pointerErrKey = errors.GenSym()
	paramErrKey   = errors.GenSym()
)

func newErrorClassWithParam(msg, param string, code int) *errors.ErrorClass {
	err := newErrorClass(msg, code)
	err.MustAddData(paramErrKey, param)
	return err
}

func newErrorClassWithPointer(msg, pointer string, code int) *errors.ErrorClass {
	err := newErrorClass(msg, code)
	err.MustAddData(pointerErrKey, pointer)
	return err
}

func newErrorClass(msg string, code int) *errors.ErrorClass {
	err := HTTPError.NewClass(
		http.StatusText(code),
		errhttp.SetStatusCode(code),
	)
	err.MustAddData(titleErrKey, msg)
	return err
}

//JSONAPIError generate JSONAPI formatted http error from an error object
func JSONAPIError(w http.ResponseWriter, err error) {
	status := errhttp.GetStatusCode(err, http.StatusInternalServerError)
	title, _ := errors.GetData(err, titleErrKey).(string)
	jsnErr := api2go.Error{
		Status: strconv.Itoa(status),
		Title:  title,
		Detail: errhttp.GetErrorBody(err),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	errSource := new(api2go.ErrorSource)
	pointer, ok := errors.GetData(err, pointerErrKey).(string)
	if ok {
		errSource.Pointer = pointer
	}
	param, ok := errors.GetData(err, paramErrKey).(string)
	if ok {
		errSource.Parameter = param
	}
	jsnErr.Source = errSource
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(api2go.HTTPError{Errors: []api2go.Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

// DatabaseError is for generating JSONAPI formatted error for database related
// errors
func DatabaseError(w http.ResponseWriter, err error) {
	if err == dbr.ErrNotFound {
		JSONAPIError(w, ErrNotFound.New(err.Error()))
		return
	}
	// possible database query error
	JSONAPIError(w, ErrDatabaseQuery.New(err.Error()))
}
<commit_msg>Removed jsonapi and dbr dependencies<commit_after>// Package apherror provides a consistent way to handler JSONAPI
// related http errors.
package apherror

import (
	"encoding/json"
	"net/http"
	"strconv"

	"github.com/spacemonkeygo/errors"
	"github.com/spacemonkeygo/errors/errhttp"
)

var (
	//HTTPError represents generic http errors
	HTTPError = errors.NewClass("HTTP Error", errors.NoCaptureStack())
	//ErrReqContext represents error in extracting context value from http request
	ErrReqContext = newErrorClass("Unable to retrieve context", http.StatusInternalServerError)
	//ErrOuthExchange represents error in exchanging code for token with oauth server
	ErrOauthExchange = newErrorClass("Unable to exchange token for code", http.StatusInternalServerError)
	//ErrUserRetrieval represents error in retrieving user information from oauth provider
	ErrUserRetrieval = newErrorClass("Unable to retrieve user information", http.StatusInternalServerError)
	//ErrDatabaseQuery represents database query related errors
	ErrDatabaseQuery = newErrorClass("Database query error", http.StatusInternalServerError)
	//ErrNotFound represents the absence of an HTTP resource
	ErrNotFound = newErrorClass("Resource not found", http.StatusNotFound)
	//ErrJSONEncoding represents any json encoding error
	ErrJSONEncoding = newErrorClass("Json encoding error", http.StatusInternalServerError)
	//ErrStructMarshal represents any error with marshalling structure
	ErrStructMarshal = newErrorClass("Structure marshalling error", http.StatusInternalServerError)
	//ErrIncludeParam represents any error with invalid include query parameter
	ErrIncludeParam = newErrorClassWithParam("Invalid include query parameter", "include", http.StatusBadRequest)
	//ErrSparseFieldSets represents any error with invalid sparse fieldsets query parameter
	ErrSparseFieldSets = newErrorClassWithParam("Invalid sparse fieldsets query parameter", "fieldsets", http.StatusBadRequest)
	//ErrFilterParam represents any error with invalid filter query paramter
	ErrFilterParam = newErrorClassWithParam("Invalid filter query parameter", "filter", http.StatusBadRequest)
	//ErrNotAcceptable represents any error with wrong or inappropriate http Accept header
	ErrNotAcceptable = newErrorClass("Accept header is not acceptable", http.StatusNotAcceptable)
	//ErrUnsupportedMedia represents any error with unsupported media type in http header
	ErrUnsupportedMedia = newErrorClass("Media type is not supported", http.StatusUnsupportedMediaType)
	//ErrQueryParam represents any error with http query parameters
	ErrQueryParam = newErrorClass("Invalid query parameter", http.StatusBadRequest)
	titleErrKey   = errors.GenSym()
	pointerErrKey = errors.GenSym()
	paramErrKey   = errors.GenSym()
)

// HTTPError is used for errors
type HTTPError struct {
	err    error
	msg    string
	status int
	Errors []Error `json:"errors,omitempty"`
}

// Error can be used for all kind of application errors
// e.g. you would use it to define form errors or any
// other semantical application problems
// for more information see http://jsonapi.org/format/#errors
type Error struct {
	ID     string       `json:"id,omitempty"`
	Links  *ErrorLinks  `json:"links,omitempty"`
	Status string       `json:"status,omitempty"`
	Code   string       `json:"code,omitempty"`
	Title  string       `json:"title,omitempty"`
	Detail string       `json:"detail,omitempty"`
	Source *ErrorSource `json:"source,omitempty"`
	Meta   interface{}  `json:"meta,omitempty"`
}

// ErrorLinks is used to provide an About URL that leads to
// further details about the particular occurrence of the problem.
//
// for more information see http://jsonapi.org/format/#error-objects
type ErrorLinks struct {
	About string `json:"about,omitempty"`
}

// ErrorSource is used to provide references to the source of an error.
//
// The Pointer is a JSON Pointer to the associated entity in the request
// document.
// The Paramter is a string indicating which query parameter caused the error.
//
// for more information see http://jsonapi.org/format/#error-objects
type ErrorSource struct {
	Pointer   string `json:"pointer,omitempty"`
	Parameter string `json:"parameter,omitempty"`
}

func newErrorClassWithParam(msg, param string, code int) *errors.ErrorClass {
	err := newErrorClass(msg, code)
	err.MustAddData(paramErrKey, param)
	return err
}

func newErrorClassWithPointer(msg, pointer string, code int) *errors.ErrorClass {
	err := newErrorClass(msg, code)
	err.MustAddData(pointerErrKey, pointer)
	return err
}

func newErrorClass(msg string, code int) *errors.ErrorClass {
	err := HTTPError.NewClass(
		http.StatusText(code),
		errhttp.SetStatusCode(code),
	)
	err.MustAddData(titleErrKey, msg)
	return err
}

//JSONAPIError generate JSONAPI formatted http error from an error object
func JSONAPIError(w http.ResponseWriter, err error) {
	status := errhttp.GetStatusCode(err, http.StatusInternalServerError)
	title, _ := errors.GetData(err, titleErrKey).(string)
	jsnErr := Error{
		Status: strconv.Itoa(status),
		Title:  title,
		Detail: errhttp.GetErrorBody(err),
		Meta: map[string]interface{}{
			"creator": "api error helper",
		},
	}
	errSource := new(ErrorSource)
	pointer, ok := errors.GetData(err, pointerErrKey).(string)
	if ok {
		errSource.Pointer = pointer
	}
	param, ok := errors.GetData(err, paramErrKey).(string)
	if ok {
		errSource.Parameter = param
	}
	jsnErr.Source = errSource
	w.Header().Set("Content-Type", "application/vnd.api+json")
	w.WriteHeader(status)
	encErr := json.NewEncoder(w).Encode(HTTPError{Errors: []Error{jsnErr}})
	if encErr != nil {
		http.Error(w, encErr.Error(), http.StatusInternalServerError)
	}
}

// DatabaseError is for generating JSONAPI formatted error for database related
// errors
//func DatabaseError(w http.ResponseWriter, err error) {
//if err == dbr.ErrNotFound {
//JSONAPIError(w, ErrNotFound.New(err.Error()))
//return
//}
//// possible database query error
//JSONAPIError(w, ErrDatabaseQuery.New(err.Error()))
//}
<|endoftext|>