Skip to content

Commit

Permalink
Set up golangci-lint
Browse files Browse the repository at this point in the history
  • Loading branch information
joanlopez committed Dec 8, 2023
1 parent ddf454f commit d0cd6ab
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 53 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/lint.yml
@@ -0,0 +1,25 @@
name: Lint
on:
push:
branches:
- main
pull_request:

permissions:
contents: read

jobs:
golangci:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v3
with:
version: 'v1.55.2'
116 changes: 116 additions & 0 deletions .golangci.yml
@@ -0,0 +1,116 @@
linters:
disable-all: true
enable:
- asciicheck
- bidichk
# - bodyclose -> streams?
- containedctx
- contextcheck
- cyclop
- decorder
# - depguard -> reports testify/require and go-querystring/query
# - dogsled -> why not?
- dupl
- dupword
- durationcheck
- errcheck
- errchkjson
- errname
- errorlint
# - execinquery -> no sql
# - exhaustive -> why?
# - exhaustruct -> why?
- exportloopref
- forbidigo
- forcetypeassert
- funlen
- gci
# - ginkgolinter -> not used
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
# - goerr113 -> annoying, revisit in the future
- gofmt
- gofumpt
- goheader
- goimports
- gomnd
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- gosmopolitan
- govet
- grouper
- importas
- ineffassign
- interfacebloat
# - ireturn -> why not? sometimes (generic)
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- musttag
- nakedret
- nestif
- nilerr
- nilnil
# - nlreturn -> annoying
- noctx
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- prealloc
- predeclared
# - promlinter -> no prom
# - protogetter -> no protos
- reassign
- revive
# - rowserrcheck -> no sql/rows
- sloglint
# - sqlclosecheck -> no sql
- staticcheck
# - stylecheck -> annoying (var-naming)
- tagalign
- tagliatelle
- tenv
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unconvert
- unparam
- unused
- usestdlibvars
# - varnamelen -> annoying
- wastedassign
- whitespace
# - wrapcheck -> why? always?
# - wsl -> annoying
# - zerologlint -> no zerolog

linters-settings:
revive:
rules:
- name: var-naming
disabled: true

gci:
sections:
- standard
- default
- prefix(github.com/joanlopez/go-lichess)

# Make the section order the same as the order of `sections`.
custom-order: true
12 changes: 10 additions & 2 deletions lichess/games_export.go
Expand Up @@ -63,7 +63,11 @@ func (s *GamesService) ExportById(ctx context.Context, id string, opts *ExportOp

// ExportCurrent exports the current [Game] being played by the given username.
// Find more details at https://lichess.org/api#tag/Games/operation/apiUserCurrentGame.
func (s *GamesService) ExportCurrent(ctx context.Context, username string, opts *ExportOptions) (*Game, *Response, error) {
func (s *GamesService) ExportCurrent(
ctx context.Context,
username string,
opts *ExportOptions,
) (*Game, *Response, error) {
u := fmt.Sprintf("api/user/%v/current-game", username)
u, err := addOptions(u, opts)
if err != nil {
Expand All @@ -86,7 +90,11 @@ func (s *GamesService) ExportCurrent(ctx context.Context, username string, opts

// ExportByUsername exports a list of [Game] played by the given username.
// Find more details at https://lichess.org/api#tag/Games/operation/apiGamesUser.
func (s *GamesService) ExportByUsername(ctx context.Context, username string, opts *ExportByUsernameOptions) ([]*Game, *Response, error) {
func (s *GamesService) ExportByUsername(
ctx context.Context,
username string,
opts *ExportByUsernameOptions,
) ([]*Game, *Response, error) {
u := fmt.Sprintf("api/games/user/%v", username)
u, err := addOptions(u, opts)
if err != nil {
Expand Down
52 changes: 26 additions & 26 deletions lichess/games_stream.go
Expand Up @@ -93,41 +93,41 @@ func (s *GamesService) StreamGameMoves(ctx context.Context, id string) (chan Gam

ch := make(chan GameStreamEvent)

finalize := func() {
go s.streamGameMoves(ctx, ch, resp)

return ch, resp, nil
}

func (s *GamesService) streamGameMoves(ctx context.Context, ch chan GameStreamEvent, resp *Response) {
defer func() {
// Explicit ignore error.
// We might want to revisit this later.
_ = resp.Body.Close()
close(ch)
}
}()

go func() {
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
select {
case <-ctx.Done():
finalize()
return
default:
}

select {
case <-ctx.Done():
finalize()
return
case ch <- s.parseGameStreamEvent(scanner.Text()):
}
scanner := bufio.NewScanner(resp.Body)

for scanner.Scan() {
select {
case <-ctx.Done():
return
default:
}

if scanner.Err() != nil {
select {
case <-ctx.Done():
case ch <- GameStreamEventError{error: scanner.Err()}:
}
select {
case <-ctx.Done():
return
case ch <- s.parseGameStreamEvent(scanner.Text()):
}
finalize()
}()
}

return ch, resp, nil
if scanner.Err() != nil {
select {
case <-ctx.Done():
case ch <- GameStreamEventError{error: scanner.Err()}:
}
}
}

func (s *GamesService) parseGameStreamEvent(event string) GameStreamEvent {
Expand Down
61 changes: 37 additions & 24 deletions lichess/lichess.go
Expand Up @@ -151,9 +151,9 @@ func (c *Client) BareDo(req *http.Request) (*Response, error) {
// If rate limit is exceeded and reset time is in the future, Do returns
// *RateLimitError immediately without making a network API call.
func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
resp, err := c.BareDo(req)
res, err := c.BareDo(req)
if err != nil {
return resp, err
return res, err
}

// We only close the response body, when the
Expand All @@ -164,43 +164,56 @@ func (c *Client) Do(req *http.Request, v interface{}) (*Response, error) {
if v != nil {
// Explicit ignore error.
// We might want to revisit this later.
_ = resp.Body.Close()
_ = res.Body.Close()
}
}()

err = c.decodeResponse(req, res, v)

return res, err
}

func (c *Client) decodeResponse(req *http.Request, res *Response, v interface{}) error {
var err error

switch v := v.(type) {
case nil:
case io.Writer:
_, err = io.Copy(v, resp.Body)
_, err = io.Copy(v, res.Body)
default:
switch typeOfResponse(req.Method, req.URL.Path) {
case jsonResponseType:
decErr := json.NewDecoder(resp.Body).Decode(v)
if decErr == io.EOF {
decErr = nil // ignore EOF errors caused by empty response body
}
if decErr != nil {
decErr := json.NewDecoder(res.Body).Decode(v)
// Ignore EOF errors caused by empty response body
if decErr != nil && !errors.Is(decErr, io.EOF) {
err = decErr
}
case ndJsonResponseType:
if reflect.ValueOf(v).Elem().Kind() != reflect.Slice {
err = errors.New("v is not a pointer to a slice")
}
err = c.decodeNdJson(res, v)
}
}

itemType := reflect.ValueOf(v).Elem().Type().Elem()
return err
}

scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
item := reflect.New(itemType).Interface()
if err = json.Unmarshal(scanner.Bytes(), item); err != nil {
break
}
func (c *Client) decodeNdJson(res *Response, v interface{}) error {
if reflect.ValueOf(v).Elem().Kind() != reflect.Slice {
return errors.New("v is not a pointer to a slice")
}

reflect.ValueOf(v).Elem().Set(reflect.Append(reflect.ValueOf(v).Elem(), reflect.ValueOf(item).Elem()))
}
itemType := reflect.ValueOf(v).Elem().Type().Elem()

scanner := bufio.NewScanner(res.Body)
for scanner.Scan() {
item := reflect.New(itemType).Interface()
if err := json.Unmarshal(scanner.Bytes(), item); err != nil {
return err
}

reflect.ValueOf(v).Elem().Set(reflect.Append(reflect.ValueOf(v).Elem(), reflect.ValueOf(item).Elem()))
}
return resp, err

return nil
}

type service struct {
Expand Down Expand Up @@ -252,7 +265,7 @@ const (
)

// typeOfResponse returns the response type of the endpoint, determined by HTTP method and Request.URL.Path.
func typeOfResponse(method, path string) responseType {
func typeOfResponse(_, path string) responseType {
switch {
default:
return jsonResponseType
Expand All @@ -262,7 +275,7 @@ func typeOfResponse(method, path string) responseType {
}
}

// roundTripperFunc creates a RoundTripper (transport)
// roundTripperFunc creates a RoundTripper (transport).
type roundTripperFunc func(*http.Request) (*http.Response, error)

func (fn roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
Expand Down
5 changes: 4 additions & 1 deletion lichess/puzzles_activity.go
Expand Up @@ -16,7 +16,10 @@ type GetPuzzleActivityOptions struct {
Before *int `url:"before,omitempty"` // >= 1356998400070
}

func (s *PuzzlesService) GetPuzzleActivity(ctx context.Context, opts *GetPuzzleActivityOptions) ([]*PuzzleRound, *Response, error) {
func (s *PuzzlesService) GetPuzzleActivity(
ctx context.Context,
opts *GetPuzzleActivityOptions,
) ([]*PuzzleRound, *Response, error) {
u := "api/puzzle/activity"
u, err := addOptions(u, opts)
if err != nil {
Expand Down

0 comments on commit d0cd6ab

Please sign in to comment.