func compareHttpResponse(r1, r2 *http.Response) bool {
	if r1 == nil && r2 == nil {
		return true
	}
	if r1 != nil && r2 != nil {
		return r1.StatusCode == r2.StatusCode
	}
	return false
}
compareHttpResponse returns whether two http.Response objects are equal or not.
Currently, only StatusCode is checked. This function is used when implementing the
Is(error) bool interface for the custom error types in this package.
package github
import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"time"
	"github.com/google/go-querystring/query"
)
const (
	defaultBaseURL = "https://api.github.com/"
	uploadBaseURL  = "https://uploads.github.com/"
	userAgent      = "go-github"
	headerRateLimit     = "X-RateLimit-Limit"
	headerRateRemaining = "X-RateLimit-Remaining"
	headerRateReset     = "X-RateLimit-Reset"
	headerOTP           = "X-GitHub-OTP"
	headerTokenExpiration = "GitHub-Authentication-Token-Expiration"
	mediaTypeV3                = "application/vnd.github.v3+json"
	defaultMediaType           = "application/octet-stream"
	mediaTypeV3SHA             = "application/vnd.github.v3.sha"
	mediaTypeV3Diff            = "application/vnd.github.v3.diff"
	mediaTypeV3Patch           = "application/vnd.github.v3.patch"
	mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json"
	mediaTypeIssueImportAPI    = "application/vnd.github.golden-comet-preview+json"
	mediaTypeStarringPreview = "application/vnd.github.v3.star+json"
	mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json"
	mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json"
	mediaTypeExpandDeploymentStatusPreview = "application/vnd.github.flash-preview+json"
	mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview"
	mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json"
	mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json"
	mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json"
	mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json"
	mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json"
	mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json"
	mediaTypeRequiredApprovingReviewsPreview = "application/vnd.github.luke-cage-preview+json"
	mediaTypePreReceiveHooksPreview = "application/vnd.github.eye-scream-preview"
	mediaTypeSignaturePreview = "application/vnd.github.zzzax-preview+json"
	mediaTypeProjectCardDetailsPreview = "application/vnd.github.starfox-preview+json"
	mediaTypeInteractionRestrictionsPreview = "application/vnd.github.sombra-preview+json"
	mediaTypeEnablePagesAPIPreview = "application/vnd.github.switcheroo-preview+json"
	mediaTypeRequiredVulnerabilityAlertsPreview = "application/vnd.github.dorian-preview+json"
	mediaTypeRequiredAutomatedSecurityFixesPreview = "application/vnd.github.london-preview+json"
	mediaTypeUpdatePullRequestBranchPreview = "application/vnd.github.lydian-preview+json"
	mediaTypeListPullsOrBranchesForCommitPreview = "application/vnd.github.groot-preview+json"
	mediaTypeMemberAllowedRepoCreationTypePreview = "application/vnd.github.surtur-preview+json"
	mediaTypeRepositoryTemplatePreview = "application/vnd.github.baptiste-preview+json"
	mediaTypeMultiLineCommentsPreview = "application/vnd.github.comfort-fade-preview+json"
	mediaTypeOAuthAppPreview = "application/vnd.github.doctor-strange-preview+json"
	mediaTypeRepositoryVisibilityPreview = "application/vnd.github.nebula-preview+json"
	mediaTypeContentAttachmentsPreview = "application/vnd.github.corsair-preview+json"
)
var errNonNilContext = errors.New("context must be non-nil")
type Client struct {
	clientMu sync.Mutex    
	client   *http.Client  
	BaseURL *url.URL
	UploadURL *url.URL
	UserAgent string
	rateMu     sync.Mutex
	rateLimits [categories]Rate  
	common service  
	Actions        *ActionsService
	Activity       *ActivityService
	Admin          *AdminService
	Apps           *AppsService
	Authorizations *AuthorizationsService
	Billing        *BillingService
	Checks         *ChecksService
	CodeScanning   *CodeScanningService
	Enterprise     *EnterpriseService
	Gists          *GistsService
	Git            *GitService
	Gitignores     *GitignoresService
	Interactions   *InteractionsService
	IssueImport    *IssueImportService
	Issues         *IssuesService
	Licenses       *LicensesService
	Marketplace    *MarketplaceService
	Migrations     *MigrationService
	Organizations  *OrganizationsService
	Projects       *ProjectsService
	PullRequests   *PullRequestsService
	Reactions      *ReactionsService
	Repositories   *RepositoriesService
	SCIM           *SCIMService
	Search         *SearchService
	Teams          *TeamsService
	Users          *UsersService
}
type service struct {
	client *Client
}
func (c *Client) Client() *http.Client {
	c.clientMu.Lock()
	defer c.clientMu.Unlock()
	clientCopy := *c.client
	return &clientCopy
}
type ListOptions struct {
	Page int `url:"page,omitempty"`
	PerPage int `url:"per_page,omitempty"`
}
type ListCursorOptions struct {
	Page string `url:"page,omitempty"`
	PerPage int `url:"per_page,omitempty"`
	After string `url:"after,omitempty"`
	Before string `url:"before,omitempty"`
	Cursor string `url:"cursor,omitempty"`
}
type UploadOptions struct {
	Name      string `url:"name,omitempty"`
	Label     string `url:"label,omitempty"`
	MediaType string `url:"-"`
}
type RawType uint8
const (
	Diff RawType = 1 + iota
	Patch
)
type RawOptions struct {
	Type RawType
}
func addOptions(s string, opts interface{}) (string, error) {
	v := reflect.ValueOf(opts)
	if v.Kind() == reflect.Ptr && v.IsNil() {
		return s, nil
	}
	u, err := url.Parse(s)
	if err != nil {
		return s, err
	}
	qs, err := query.Values(opts)
	if err != nil {
		return s, err
	}
	u.RawQuery = qs.Encode()
	return u.String(), nil
}
func NewClient(httpClient *http.Client) *Client {
	if httpClient == nil {
		httpClient = &http.Client{}
	}
	baseURL, _ := url.Parse(defaultBaseURL)
	uploadURL, _ := url.Parse(uploadBaseURL)
	c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL}
	c.common.client = c
	c.Actions = (*ActionsService)(&c.common)
	c.Activity = (*ActivityService)(&c.common)
	c.Admin = (*AdminService)(&c.common)
	c.Apps = (*AppsService)(&c.common)
	c.Authorizations = (*AuthorizationsService)(&c.common)
	c.Billing = (*BillingService)(&c.common)
	c.Checks = (*ChecksService)(&c.common)
	c.CodeScanning = (*CodeScanningService)(&c.common)
	c.Enterprise = (*EnterpriseService)(&c.common)
	c.Gists = (*GistsService)(&c.common)
	c.Git = (*GitService)(&c.common)
	c.Gitignores = (*GitignoresService)(&c.common)
	c.Interactions = (*InteractionsService)(&c.common)
	c.IssueImport = (*IssueImportService)(&c.common)
	c.Issues = (*IssuesService)(&c.common)
	c.Licenses = (*LicensesService)(&c.common)
	c.Marketplace = &MarketplaceService{client: c}
	c.Migrations = (*MigrationService)(&c.common)
	c.Organizations = (*OrganizationsService)(&c.common)
	c.Projects = (*ProjectsService)(&c.common)
	c.PullRequests = (*PullRequestsService)(&c.common)
	c.Reactions = (*ReactionsService)(&c.common)
	c.Repositories = (*RepositoriesService)(&c.common)
	c.SCIM = (*SCIMService)(&c.common)
	c.Search = (*SearchService)(&c.common)
	c.Teams = (*TeamsService)(&c.common)
	c.Users = (*UsersService)(&c.common)
	return c
}
func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) {
	baseEndpoint, err := url.Parse(baseURL)
	if err != nil {
		return nil, err
	}
	if !strings.HasSuffix(baseEndpoint.Path, "/") {
		baseEndpoint.Path += "/"
	}
	if !strings.HasSuffix(baseEndpoint.Path, "/api/v3/") &&
		!strings.HasPrefix(baseEndpoint.Host, "api.") &&
		!strings.Contains(baseEndpoint.Host, ".api.") {
		baseEndpoint.Path += "api/v3/"
	}
	uploadEndpoint, err := url.Parse(uploadURL)
	if err != nil {
		return nil, err
	}
	if !strings.HasSuffix(uploadEndpoint.Path, "/") {
		uploadEndpoint.Path += "/"
	}
	if !strings.HasSuffix(uploadEndpoint.Path, "/api/uploads/") &&
		!strings.HasPrefix(uploadEndpoint.Host, "api.") &&
		!strings.Contains(uploadEndpoint.Host, ".api.") {
		uploadEndpoint.Path += "api/uploads/"
	}
	c := NewClient(httpClient)
	c.BaseURL = baseEndpoint
	c.UploadURL = uploadEndpoint
	return c, nil
}
func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) {
	if !strings.HasSuffix(c.BaseURL.Path, "/") {
		return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL)
	}
	u, err := c.BaseURL.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	var buf io.ReadWriter
	if body != nil {
		buf = &bytes.Buffer{}
		enc := json.NewEncoder(buf)
		enc.SetEscapeHTML(false)
		err := enc.Encode(body)
		if err != nil {
			return nil, err
		}
	}
	req, err := http.NewRequest(method, u.String(), buf)
	if err != nil {
		return nil, err
	}
	if body != nil {
		req.Header.Set("Content-Type", "application/json")
	}
	req.Header.Set("Accept", mediaTypeV3)
	if c.UserAgent != "" {
		req.Header.Set("User-Agent", c.UserAgent)
	}
	return req, nil
}
func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) {
	if !strings.HasSuffix(c.UploadURL.Path, "/") {
		return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL)
	}
	u, err := c.UploadURL.Parse(urlStr)
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest("POST", u.String(), reader)
	if err != nil {
		return nil, err
	}
	req.ContentLength = size
	if mediaType == "" {
		mediaType = defaultMediaType
	}
	req.Header.Set("Content-Type", mediaType)
	req.Header.Set("Accept", mediaTypeV3)
	req.Header.Set("User-Agent", c.UserAgent)
	return req, nil
}
type Response struct {
	*http.Response
	NextPage  int
	PrevPage  int
	FirstPage int
	LastPage  int
	NextPageToken string
	Cursor string
	Rate Rate
	TokenExpiration Timestamp
}
func newResponse(r *http.Response) *Response {
	response := &Response{Response: r}
	response.populatePageValues()
	response.Rate = parseRate(r)
	response.TokenExpiration = parseTokenExpiration(r)
	return response
}
func (r *Response) populatePageValues() {
	if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 {
		for _, link := range strings.Split(links[0], ",") {
			segments := strings.Split(strings.TrimSpace(link), ";")
			if len(segments) < 2 {
				continue
			}
			if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") {
				continue
			}
			url, err := url.Parse(segments[0][1 : len(segments[0])-1])
			if err != nil {
				continue
			}
			q := url.Query()
			if cursor := q.Get("cursor"); cursor != "" {
				for _, segment := range segments[1:] {
					switch strings.TrimSpace(segment) {
					case `rel="next"`:
						r.Cursor = cursor
					}
				}
				continue
			}
			page := q.Get("page")
			if page == "" {
				continue
			}
			for _, segment := range segments[1:] {
				switch strings.TrimSpace(segment) {
				case `rel="next"`:
					if r.NextPage, err = strconv.Atoi(page); err != nil {
						r.NextPageToken = page
					}
				case `rel="prev"`:
					r.PrevPage, _ = strconv.Atoi(page)
				case `rel="first"`:
					r.FirstPage, _ = strconv.Atoi(page)
				case `rel="last"`:
					r.LastPage, _ = strconv.Atoi(page)
				}
			}
		}
	}
}
func parseRate(r *http.Response) Rate {
	var rate Rate
	if limit := r.Header.Get(headerRateLimit); limit != "" {
		rate.Limit, _ = strconv.Atoi(limit)
	}
	if remaining := r.Header.Get(headerRateRemaining); remaining != "" {
		rate.Remaining, _ = strconv.Atoi(remaining)
	}
	if reset := r.Header.Get(headerRateReset); reset != "" {
		if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 {
			rate.Reset = Timestamp{time.Unix(v, 0)}
		}
	}
	return rate
}
func parseTokenExpiration(r *http.Response) Timestamp {
	var exp Timestamp
	if v := r.Header.Get(headerTokenExpiration); v != "" {
		if t, err := time.Parse("2006-01-02 03:04:05 MST", v); err == nil {
			exp = Timestamp{t.Local()}
		}
	}
	return exp
}
type requestContext uint8
const (
	bypassRateLimitCheck requestContext = iota
)
func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error) {
	if ctx == nil {
		return nil, errNonNilContext
	}
	req = withContext(ctx, req)
	rateLimitCategory := category(req.URL.Path)
	if bypass := ctx.Value(bypassRateLimitCheck); bypass == nil {
		if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil {
			return &Response{
				Response: err.Response,
				Rate:     err.Rate,
			}, err
		}
	}
	resp, err := c.client.Do(req)
	if err != nil {
		select {
		case <-ctx.Done():
			return nil, ctx.Err()
		default:
		}
		if e, ok := err.(*url.Error); ok {
			if url, err := url.Parse(e.URL); err == nil {
				e.URL = sanitizeURL(url).String()
				return nil, e
			}
		}
		return nil, err
	}
	response := newResponse(resp)
	c.rateMu.Lock()
	c.rateLimits[rateLimitCategory] = response.Rate
	c.rateMu.Unlock()
	err = CheckResponse(resp)
	if err != nil {
		defer resp.Body.Close()
		aerr, ok := err.(*AcceptedError)
		if ok {
			b, readErr := ioutil.ReadAll(resp.Body)
			if readErr != nil {
				return response, readErr
			}
			aerr.Raw = b
			err = aerr
		}
	}
	return response, err
}
func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) {
	resp, err := c.BareDo(ctx, req)
	if err != nil {
		return resp, err
	}
	defer resp.Body.Close()
	switch v := v.(type) {
	case nil:
	case io.Writer:
		_, err = io.Copy(v, resp.Body)
	default:
		decErr := json.NewDecoder(resp.Body).Decode(v)
		if decErr == io.EOF {
			decErr = nil  
		}
		if decErr != nil {
			err = decErr
		}
	}
	return resp, err
}
func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError {
	c.rateMu.Lock()
	rate := c.rateLimits[rateLimitCategory]
	c.rateMu.Unlock()
	if !rate.Reset.Time.IsZero() && rate.Remaining == 0 && time.Now().Before(rate.Reset.Time) {
		resp := &http.Response{
			Status:     http.StatusText(http.StatusForbidden),
			StatusCode: http.StatusForbidden,
			Request:    req,
			Header:     make(http.Header),
			Body:       ioutil.NopCloser(strings.NewReader("")),
		}
		return &RateLimitError{
			Rate:     rate,
			Response: resp,
			Message:  fmt.Sprintf("API rate limit of %v still exceeded until %v, not making remote request.", rate.Limit, rate.Reset.Time),
		}
	}
	return nil
}