<commit_before>package itchio

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/go-errors/errors"
)

// A Client allows consuming the itch.io API
type Client struct {
	Key           string
	HTTPClient    *http.Client
	BaseURL       string
	RetryPatterns []time.Duration
	UserAgent     string
}

func defaultRetryPatterns() []time.Duration {
	return []time.Duration{
		1 * time.Second,
		2 * time.Second,
		4 * time.Second,
		8 * time.Second,
		16 * time.Second,
	}
}

// ClientWithKey creates a new itch.io API client with a given API key
func ClientWithKey(key string) *Client {
	c := &Client{
		Key:           key,
		HTTPClient:    http.DefaultClient,
		RetryPatterns: defaultRetryPatterns(),
		UserAgent:     "go-itchio",
	}
	c.SetServer("https://itch.io")
	return c
}

// SetServer allows changing the server to which we're making API
// requests (which defaults to the reference itch.io server)
func (c *Client) SetServer(itchioServer string) *Client {
	c.BaseURL = fmt.Sprintf("%s/api/1", itchioServer)
	return c
}

// WharfStatus requests the status of the wharf infrastructure
func (c *Client) WharfStatus() (r WharfStatusResponse, err error) {
	path := c.MakePath("wharf/status")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// GetMe returns information about to which the current credentials belong
func (c *Client) GetMe() (GetMeResponse, error) {
	path := c.MakePath("me")

	var r GetMeResponse

	resp, err := c.Get(path)
	if err != nil {
		return r, errors.Wrap(err, 0)
	}

	err = ParseAPIResponse(&r, resp)
	if err != nil {
		return r, errors.Wrap(err, 0)
	}

	return r, nil
}

// ListMyGames lists the games one develops (ie. can edit)
func (c *Client) ListMyGames() (r ListMyGamesResponse, err error) {
	path := c.MakePath("my-games")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// GameUploadsResponse is what the server replies with when asked for a game's uploads
type GameUploadsResponse struct {
	Response

	Uploads []*Upload `json:"uploads"`
}

// GameUploads lists the uploads for a game that we have access to with our API key
func (c *Client) GameUploads(gameID int64) (r GameUploadsResponse, err error) {
	path := c.MakePath("game/%d/uploads", gameID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// UploadDownloadResponse is what the API replies to when we ask to download an upload
type UploadDownloadResponse struct {
	Response

	URL string
}

// UploadDownload attempts to download an upload without a download key
func (c *Client) UploadDownload(uploadID int64) (r UploadDownloadResponse, err error) {
	return c.UploadDownloadWithKey("", uploadID)
}

// UploadDownloadWithKey attempts to download an upload with a download key
func (c *Client) UploadDownloadWithKey(downloadKey string, uploadID int64) (r UploadDownloadResponse, err error) {
	return c.UploadDownloadWithKeyAndValues(downloadKey, uploadID, nil)
}

// UploadDownloadWithKeyAndValues attempts to download an upload with a download key and additional parameters
func (c *Client) UploadDownloadWithKeyAndValues(downloadKey string, uploadID int64, values url.Values) (r UploadDownloadResponse, err error) {
	if values == nil {
		values = url.Values{}
	}

	if downloadKey != "" {
		values.Add("download_key_id", downloadKey)
	}

	path := c.MakePath("upload/%d/download", uploadID)
	if len(values) > 0 {
		path = fmt.Sprintf("%s?%s", path, values.Encode())
	}

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return r, err
}

// NewBuildResponse is what the API replies with when we create a new build
type NewBuildResponse struct {
	Response

	Build struct {
		ID          int64 `json:"id"`
		UploadID    int64 `json:"upload_id"`
		ParentBuild struct {
			ID int64 `json:"id"`
		} `json:"parent_build"`
	}<commit_msg>Fix NewBuildResponse json tags<commit_after>package itchio

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"

	"github.com/go-errors/errors"
)

// A Client allows consuming the itch.io API
type Client struct {
	Key           string
	HTTPClient    *http.Client
	BaseURL       string
	RetryPatterns []time.Duration
	UserAgent     string
}

func defaultRetryPatterns() []time.Duration {
	return []time.Duration{
		1 * time.Second,
		2 * time.Second,
		4 * time.Second,
		8 * time.Second,
		16 * time.Second,
	}
}

// ClientWithKey creates a new itch.io API client with a given API key
func ClientWithKey(key string) *Client {
	c := &Client{
		Key:           key,
		HTTPClient:    http.DefaultClient,
		RetryPatterns: defaultRetryPatterns(),
		UserAgent:     "go-itchio",
	}
	c.SetServer("https://itch.io")
	return c
}

// SetServer allows changing the server to which we're making API
// requests (which defaults to the reference itch.io server)
func (c *Client) SetServer(itchioServer string) *Client {
	c.BaseURL = fmt.Sprintf("%s/api/1", itchioServer)
	return c
}

// WharfStatus requests the status of the wharf infrastructure
func (c *Client) WharfStatus() (r WharfStatusResponse, err error) {
	path := c.MakePath("wharf/status")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// GetMe returns information about to which the current credentials belong
func (c *Client) GetMe() (GetMeResponse, error) {
	path := c.MakePath("me")

	var r GetMeResponse

	resp, err := c.Get(path)
	if err != nil {
		return r, errors.Wrap(err, 0)
	}

	err = ParseAPIResponse(&r, resp)
	if err != nil {
		return r, errors.Wrap(err, 0)
	}

	return r, nil
}

// ListMyGames lists the games one develops (ie. can edit)
func (c *Client) ListMyGames() (r ListMyGamesResponse, err error) {
	path := c.MakePath("my-games")

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// GameUploadsResponse is what the server replies with when asked for a game's uploads
type GameUploadsResponse struct {
	Response

	Uploads []*Upload `json:"uploads"`
}

// GameUploads lists the uploads for a game that we have access to with our API key
func (c *Client) GameUploads(gameID int64) (r GameUploadsResponse, err error) {
	path := c.MakePath("game/%d/uploads", gameID)

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return
}

// UploadDownloadResponse is what the API replies to when we ask to download an upload
type UploadDownloadResponse struct {
	Response

	URL string
}

// UploadDownload attempts to download an upload without a download key
func (c *Client) UploadDownload(uploadID int64) (r UploadDownloadResponse, err error) {
	return c.UploadDownloadWithKey("", uploadID)
}

// UploadDownloadWithKey attempts to download an upload with a download key
func (c *Client) UploadDownloadWithKey(downloadKey string, uploadID int64) (r UploadDownloadResponse, err error) {
	return c.UploadDownloadWithKeyAndValues(downloadKey, uploadID, nil)
}

// UploadDownloadWithKeyAndValues attempts to download an upload with a download key and additional parameters
func (c *Client) UploadDownloadWithKeyAndValues(downloadKey string, uploadID int64, values url.Values) (r UploadDownloadResponse, err error) {
	if values == nil {
		values = url.Values{}
	}

	if downloadKey != "" {
		values.Add("download_key_id", downloadKey)
	}

	path := c.MakePath("upload/%d/download", uploadID)
	if len(values) > 0 {
		path = fmt.Sprintf("%s?%s", path, values.Encode())
	}

	resp, err := c.Get(path)
	if err != nil {
		return
	}

	err = ParseAPIResponse(&r, resp)
	return r, err
}

// NewBuildResponse is what the API replies with when we create a new build
type NewBuildResponse struct {
	Response

	Build struct {
		ID          int64 `json:"id"`
		UploadID    int64 `json:"uploadId"`
		ParentBuild struct {
			ID int64 `json:"id"`
		} `json:"parentBuild"`
	}
{'fraction_non_alphanumeric': 0.11275066212637155, 'fraction_numerical': 0.006053726825576996, 'mean_word_length': 3.9193548387096775, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 54, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '438515', 'n_tokens_mistral': 2722, 'n_tokens_neox': 2592, 'n_words': 845}