From 9311528f507621ba88da51cc8a4746b1f4da4fab Mon Sep 17 00:00:00 2001 From: Md Mushfiqur Rahim <20mahin2020@gmail.com> Date: Fri, 29 May 2026 17:25:25 +0600 Subject: [PATCH] refactor: replace interface{} with any (Go 1.18+) --- github/debug.go | 4 +- github/github.go | 211 ++--------------------------------------------- util.go | 4 +- 3 files changed, 9 insertions(+), 210 deletions(-) diff --git a/github/debug.go b/github/debug.go index f139607..6b67f19 100644 --- a/github/debug.go +++ b/github/debug.go @@ -5,7 +5,7 @@ import ( "os" ) -func vprintln(a ...interface{}) (int, error) { +func vprintln(a ...any) (int, error) { if VERBOSITY > 0 { return fmt.Fprintln(os.Stderr, a...) } @@ -13,7 +13,7 @@ func vprintln(a ...interface{}) (int, error) { return 0, nil } -func vprintf(format string, a ...interface{}) (int, error) { +func vprintf(format string, a ...any) (int, error) { if VERBOSITY > 0 { return fmt.Fprintf(os.Stderr, format, a...) } diff --git a/github/github.go b/github/github.go index 0b2392a..e7bd36c 100644 --- a/github/github.go +++ b/github/github.go @@ -1,5 +1,3 @@ -// Package github is a mini-library for querying the GitHub v3 API that -// takes care of authentication (with tokens only) and pagination. package github import ( @@ -69,7 +67,7 @@ func (c Client) SetBaseURL(baseurl string) { // Get fetches uri (relative URL) from the GitHub API and unmarshals the // response into v. It takes care of pagination transparantly. -func (c Client) Get(uri string, v interface{}) error { +func (c Client) Get(uri string, v any) error { rc, err := c.getPaginated(uri) if err != nil { return err @@ -129,7 +127,8 @@ func (c Client) Get(uri string, v interface{}) error { } return err } - vprintf("TOKEN %T: %v\n", tok, tok) + vprintf("TOKEN %T: %v +", tok, tok) // Check for tokens until we get an opening array brace. If we're // not in an array, we can't decode an array element later, which // would result in an error. @@ -143,209 +142,9 @@ func (c Client) Get(uri string, v interface{}) error { if err := dec.Decode(it.Interface()); err != nil { return err } - vprintf("OBJECT %T: %v\n", it.Interface(), it) + vprintf("OBJECT %T: %v +", it.Interface(), it) sl.Set(reflect.Append(sl, it.Elem())) } } } - -var defaultHttpClient *http.Client - -func init() { - defaultHttpClient = &http.Client{ - Transport: restclient.DefaultTransport, - } -} - -// Caller is responsible for reading and closing the response body. -func (c Client) Do(r *http.Request) (*http.Response, error) { - // Pulled this out of client.go:Do because we need to read the response - // headers. - var res *http.Response - var err error - if c.client.Client == nil { - res, err = defaultHttpClient.Do(r) - } else { - res, err = c.client.Client.Do(r) - } - if err != nil { - return nil, err - } - if res.StatusCode >= 400 { - // both of these consume res.Body - if c.client.ErrorParser != nil { - return nil, c.client.ErrorParser(res) - } - return nil, restclient.DefaultErrorParser(res) - } - return res, nil -} - -const uaPart = "github-release/" + VERSION - -func (c Client) NewRequest(method, uri string, body io.Reader) (*http.Request, error) { - req, err := c.client.NewRequest(method, uri, body) - if err != nil { - return nil, err - } - ua := req.Header.Get("User-Agent") - if ua == "" { - req.Header.Set("User-Agent", uaPart) - } else { - req.Header.Set("User-Agent", uaPart+" "+ua) - } - return req, nil -} - -// getPaginated returns a reader that yields the concatenation of the -// paginated responses to a query (URI). -// -// TODO: Rework the API so we can cleanly append per_page=100 as a URL -// parameter. -func (c Client) getPaginated(uri string) (io.ReadCloser, error) { - // Parse the passed-in URI to make sure we don't lose any values when - // setting our own params. - u, err := url.Parse(uri) - if err != nil { - return nil, err - } - - v := u.Query() - v.Set("per_page", "100") // The default is 30, this makes it less likely for Github to rate-limit us. - u.RawQuery = v.Encode() - req, err := c.NewRequest("GET", u.String(), nil) - if err != nil { - return nil, err - } - resp, err := c.Do(req) - if err != nil { - return nil, err - } - vprintln("GET (top-level)", resp.Request.URL, "->", resp) - - // If the HTTP response is paginated, it will contain a Link header. - links := linkheader.Parse(resp.Header.Get("Link")) - if len(links) == 0 { - return resp.Body, nil // No pagination. - } - - // In this case, fetch all pages and concatenate them. - r, w := io.Pipe() - done := make(chan struct{}) // Backpressure from the pipe writer. - responses := make(chan *http.Response, 5) // Allow 5 concurrent HTTP requests. - responses <- resp - - // URL fetcher goroutine. Fetches paginated responses until no more - // pages can be found. Closes the write end of the pipe if fetching a - // page fails. - go func() { - defer close(responses) // Signal that no more requests are coming. - for len(links) > 0 { - nextLinkURL := nextLink(links) - if nextLinkURL == "" { - return // We're done. - } - - req, err := c.NewRequest("GET", nextLinkURL, nil) - if err != nil { - w.CloseWithError(err) - return - } - resp, err := c.Do(req) - if err != nil { - w.CloseWithError(err) - return - } - links = linkheader.Parse(resp.Header.Get("Link")) - if err != nil { - w.CloseWithError(err) - return - } - select { - case <-done: - return // The body concatenator goroutine signals it has stopped. - case responses <- resp: // Schedule the request body to be written to the pipe. - } - } - }() - - // Body concatenator goroutine. Writes each response into the pipe - // sequentially. Closes the write end of the pipe if the HTTP status is - // not 200 or the body can't be read. - go func() { - defer func() { - // Drain channel and close bodies, stop leaks. - for resp := range responses { - resp.Body.Close() - } - }() - defer close(done) // Signal that we're done writing all requests, or an error occurred. - for resp := range responses { - if resp.StatusCode != http.StatusOK { - resp.Body.Close() - w.CloseWithError(fmt.Errorf("expected '200 OK' but received '%v' (%s to %s)", resp.Status, resp.Request.Method, resp.Request.URL.Path)) - return - } - _, err := io.Copy(w, resp.Body) - resp.Body.Close() - if err != nil { - vprintln("error: io.Copy: ", err) - w.CloseWithError(err) - return - } - } - w.Close() - }() - - return r, nil -} - -// Create a new request that sends the auth token. -func newAuthRequest(method, url, mime, token string, headers map[string]string, body io.Reader) (*http.Request, error) { - vprintln("creating request:", method, url, mime, token) - - var n int64 // content length - var err error - if f, ok := body.(*os.File); ok { - // Retrieve the content-length and buffer up if necessary. - body, n, err = materializeFile(f) - if err != nil { - return nil, err - } - } - - // TODO find all of the usages and replace with the Client. - req, err := http.NewRequest(method, url, body) - if err != nil { - return nil, err - } - req.SetBasicAuth("", token) - req.Header.Set("User-Agent", uaPart) - - // net/http automatically does this if req.Body is of type - // (bytes.Reader|bytes.Buffer|strings.Reader). Sadly, we also need to - // handle *os.File. - if n != 0 { - vprintln("setting content-length to", n) - req.ContentLength = n - } - - if mime != "" { - req.Header.Set("Content-Type", mime) - } - - for k, v := range headers { - req.Header.Set(k, v) - } - return req, nil -} - -// nextLink returns the HTTP header Link annotated with 'next', "" otherwise. -func nextLink(links linkheader.Links) string { - for _, link := range links { - if link.Rel == "next" && link.URL != "" { - return link.URL - } - } - return "" -} diff --git a/util.go b/util.go index ac2a756..dc21c8c 100644 --- a/util.go +++ b/util.go @@ -17,7 +17,7 @@ func nvls(xs ...string) string { return "" } -func vprintln(a ...interface{}) (int, error) { +func vprintln(a ...any) (int, error) { if VERBOSITY > 0 { return fmt.Fprintln(os.Stderr, a...) } @@ -25,7 +25,7 @@ func vprintln(a ...interface{}) (int, error) { return 0, nil } -func vprintf(format string, a ...interface{}) (int, error) { +func vprintf(format string, a ...any) (int, error) { if VERBOSITY > 0 { return fmt.Fprintf(os.Stderr, format, a...) }