package client
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"context"
"reflect"
"regexp"
"strconv"
"strings"
"time"
"github.com/martinlindhe/unit"
"github.com/syurchen93/api-football-client/common"
"github.com/syurchen93/api-football-client/request"
"github.com/syurchen93/api-football-client/request/fixture"
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
"github.com/syurchen93/api-football-client/response/leagues"
"github.com/syurchen93/api-football-client/response/misc"
"github.com/syurchen93/api-football-client/parser"
"github.com/go-playground/validator/v10"
"github.com/mitchellh/mapstructure"
"golang.org/x/time/rate"
)
var baseURL = "https://v3.football.api-sports.io/"
var apiHost = "v3.football.api-sports.io"
var validate *validator.Validate
var timeFormatShort = "2006-01-02"
type RateLimiterSettings struct {
Disabled bool
RequestCount int
Seconds int
}
type Client struct {
apiKey string
baseURL string
apiHost string
httpClient *http.Client
rateLimiter *rate.Limiter
}
func NewClient(apiKey string, rls RateLimiterSettings) *Client {
var rateLimiter *rate.Limiter
validate = validator.New(validator.WithRequiredStructEnabled())
if !rls.Disabled {
if rls.RequestCount == 0 {
rls.RequestCount = 10
}
if rls.Seconds == 0 {
rls.Seconds = 60
}
rl := rate.Every(time.Duration(rls.Seconds) * time.Second)
rateLimiter = rate.NewLimiter(rl, rls.RequestCount)
}
return &Client{
apiKey: apiKey,
baseURL: baseURL,
apiHost: apiHost,
httpClient: &http.Client{},
rateLimiter: rateLimiter,
}
}
func (c *Client) SetBaseURL(baseURL string) {
c.baseURL = baseURL
}
func (c *Client) SetApiHost(apiHost string) {
c.apiHost = apiHost
}
func (c *Client) DoRequest(requestStruct request.RequestInterface) ([]response.ResponseInterface, error) {
err := validate.Struct(requestStruct)
if err != nil {
return nil, err
}
requestUrlWithParams, err := c.prepareUrlWithParams(requestStruct)
if err != nil {
return nil, err
}
c.applyRateLimit()
httpRequest, err := http.NewRequest(
"GET",
requestUrlWithParams,
nil,
)
if err != nil {
return nil, err
}
httpRequest.Header.Add("x-rapidapi-host", c.apiHost)
httpRequest.Header.Add("x-rapidapi-key", c.apiKey)
httpResponse, err := c.httpClient.Do(httpRequest)
if err != nil {
return nil, err
}
if httpResponse.StatusCode != 200 {
return nil, fmt.Errorf("request failed with status code: %d", httpResponse.StatusCode)
}
defer httpResponse.Body.Close()
responseBody, err := io.ReadAll(httpResponse.Body)
if err != nil {
return nil, err
}
return mapResponseToCorrectStruct(responseBody, requestStruct)
}
func (c *Client) applyRateLimit() {
if c.rateLimiter != nil {
ctx := context.Background()
_ = c.rateLimiter.Wait(ctx)
}
}
func (c Client) prepareUrlWithParams(requestStruct request.RequestInterface) (string, error) {
urlStruct, err := url.Parse(c.baseURL + requestStruct.GetEndpoint())
curQuery := urlStruct.Query()
if err != nil {
return "", err
}
var queryToAddTemp map[string]interface{}
err = Decode(requestStruct, &queryToAddTemp)
if err != nil {
return "", err
}
queryToAdd := stringifyMapContent(queryToAddTemp)
for key, value := range queryToAdd {
if value != "" {
curQuery.Add(key, value)
}
}
urlStruct.RawQuery = curQuery.Encode()
return urlStruct.String(), nil
}
func mapResponseToCorrectStruct(
responseBody []byte,
requestStruct request.RequestInterface,
) ([]response.ResponseInterface, error) {
responseStruct := response.Response{}
jsonErr := json.Unmarshal(responseBody, &responseStruct)
if jsonErr != nil {
return nil, jsonErr
}
switch responseStruct.Errors.(type) {
case []interface{}:
case map[string]interface{}:
if len(responseStruct.Errors.(map[string]interface{})) > 0 {
return nil, fmt.Errorf("API returned errors: %v", responseStruct.Errors)
}
}
var responseMap []interface{}
switch responseStruct.ResponseMap.(type) {
case []interface{}:
responseMap = responseStruct.ResponseMap.([]interface{})
case interface{}:
responseMap = append(responseMap, responseStruct.ResponseMap)
}
endResponses := make([]response.ResponseInterface, 0)
responseChan := make(chan response.ResponseInterface)
errorChan := make(chan error)
for _, responseMap := range responseMap {
go func(rm interface{}) {
emptyResponseStruct := requestStruct.GetResponseStruct()
err := Decode(rm, &emptyResponseStruct)
if err != nil {
errorChan <- err
} else {
responseChan <- emptyResponseStruct
}
}(responseMap)
}
for range responseMap {
select {
case response := <-responseChan:
endResponses = append(endResponses, response)
case err := <-errorChan:
return nil, err
}
}
return endResponses, nil
}
func Decode(input interface{}, result interface{}) error {
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Metadata: nil,
DecodeHook: mapstructure.ComposeDecodeHookFunc(
ToTimeHookFunc(),
),
Result: result,
WeaklyTypedInput: true,
})
if err != nil {
return err
}
if err := decoder.Decode(input); err != nil {
return err
}
return err
}
func ToTimeHookFunc() mapstructure.DecodeHookFunc {
return func(
f reflect.Type,
t reflect.Type,
data interface{}) (interface{}, error) {
if f.Kind() == reflect.String && t.Kind() == reflect.Float32 {
re := regexp.MustCompile("[^0-9.]+")
cleanedString := re.ReplaceAllString(data.(string), "")
return strconv.ParseFloat(cleanedString, 32)
}
if f.Kind() == reflect.String && t.Kind() == reflect.Int {
return parseInt(data)
}
if f.Kind() == reflect.String && t.String() == "unit.Mass" {
intValue, _ := parseInt(data)
return unit.Mass(intValue), nil
}
if f.Kind() == reflect.String && t.String() == "unit.Length" {
intValue, _ := parseInt(data)
return unit.Length(intValue), nil
}
if t == reflect.TypeOf(fixtures.Lineup{}) {
dataMap := data.(map[string]interface{})
formation := dataMap["formation"]
dataMap["formation"] = parser.ParseFormationStringIntoMap(formation.(string))
if dataMap["startXI"] != nil {
dataMap["startXI"] = preparePlayerMap(dataMap["startXI"])
}
if dataMap["substitutes"] != nil {
dataMap["substitutes"] = preparePlayerMap(dataMap["substitutes"])
}
}
if t == reflect.TypeOf(fixtures.TeamPlayerStats{}) {
dataMap := data.(map[string]interface{})
if dataMap["statistics"] != nil {
dataMap["statistics"] = dataMap["statistics"].([]interface{})[0]
}
}
if t == reflect.TypeOf(leagues.SeasonYear{}) {
return leagues.SeasonYear{Year: int(data.(float64))}, nil
}
if t == reflect.TypeOf(misc.Timezone{}) {
return misc.Timezone{Value: data.(string)}, nil
}
if t == reflect.TypeOf(fixtures.Round{}) {
return fixtures.Round{Name: data.(string)}, nil
}
if t == reflect.TypeOf(time.Time{}) {
switch f.Kind() {
case reflect.String:
if strings.Contains(data.(string), "T") {
return time.Parse(time.RFC3339, data.(string))
} else {
return time.Parse(timeFormatShort, data.(string))
}
case reflect.Float64:
return time.Unix(0, int64(data.(float64))*int64(time.Millisecond)), nil
case reflect.Int64:
return time.Unix(0, data.(int64)*int64(time.Millisecond)), nil
default:
return data, nil
}
}
if f.String() == "string" && t.String() == "int" && strings.Contains(data.(string), "%") {
return strconv.ParseInt(strings.TrimSuffix(data.(string), "%"), 10, 64)
}
if f.String() == "*time.Time" {
return map[string]time.Time{
"date": *data.(*time.Time),
}, nil
}
if t == reflect.TypeOf(fixtures.Status{}) {
elapsed := data.(map[string]interface{})["elapsed"]
if nil != elapsed {
elapsed = int(elapsed.(float64))
} else {
elapsed = 0
}
return fixtures.Status{
Long: data.(map[string]interface{})["long"].(string),
Value: common.FixtureStatus(data.(map[string]interface{})["short"].(string)),
Elapsed: elapsed.(int),
}, nil
}
return data, nil
}
}
func stringifyMapContent(mapData map[string]interface{}) map[string]string {
stringifiedMap := make(map[string]string)
for key, value := range mapData {
var stringValue string
switch value := value.(type) {
case bool:
stringValue = boolToString(value)
case string:
stringValue = value
case common.StatsType:
stringValue = string(value)
case common.EventType:
stringValue = string(value)
case fixture.LineupType:
stringValue = string(value)
case int:
stringValue = fmt.Sprintf("%d", value)
case time.Time:
stringValue = value.Format(timeFormatShort)
case []common.FixtureStatus:
statusStrings := make([]string, len(value))
for i, status := range value {
statusStrings[i] = fmt.Sprintf("%v", status)
}
stringValue = strings.Join(statusStrings, "-")
case []int:
statusStrings := make([]string, len(value))
for i, status := range value {
statusStrings[i] = fmt.Sprintf("%v", status)
}
stringValue = strings.Join(statusStrings, "-")
case map[string]interface{}:
dateValue, ok := value["date"]
if ok && !dateValue.(time.Time).IsZero() {
stringValue = dateValue.(time.Time).Format(timeFormatShort)
}
}
stringifiedMap[key] = stringValue
}
return stringifiedMap
}
func boolToString(b bool) string {
if b {
return "true"
}
return "false"
}
func preparePlayerMap(players interface{}) interface{} {
playerSlice := make([]interface{}, 0)
for _, player := range players.([]interface{}) {
playerMap := player.(map[string]interface{})
fixedPlayerMap := playerMap["player"].(map[string]interface{})
grid := fixedPlayerMap["grid"]
if grid != nil {
fixedPlayerMap["grid"] = prepareGrid(fixedPlayerMap["grid"].(string))
}
playerSlice = append(playerSlice, fixedPlayerMap)
}
return playerSlice
}
func prepareGrid(gridString string) map[string]interface{} {
gridMap := make(map[string]interface{})
if gridString == "" {
return gridMap
}
gridSlice := strings.Split(gridString, ":")
if len(gridSlice) != 2 {
return gridMap
}
gridMap["row"] = gridSlice[0]
gridMap["column"] = gridSlice[1]
return gridMap
}
func parseInt(data interface{}) (int, error) {
re := regexp.MustCompile("[^0-9]+")
cleanedString := re.ReplaceAllString(data.(string), "")
parsedInt, err := strconv.Atoi(cleanedString)
if err != nil {
return 0, err
}
return parsedInt, nil
}
package common
type FixtureStatus string
const (
TimeToBeDefined FixtureStatus = "TBD"
NotStarted FixtureStatus = "NS"
FirstHalf FixtureStatus = "1H"
Halftime FixtureStatus = "HT"
SecondHalf FixtureStatus = "2H"
ExtraTime FixtureStatus = "ET"
BreakTime FixtureStatus = "BT"
Penalty FixtureStatus = "P"
Suspended FixtureStatus = "SUSP"
Interrupted FixtureStatus = "INT"
Finished FixtureStatus = "FT"
FinishedAfterExtra FixtureStatus = "AET"
FinishedAfterPenalty FixtureStatus = "PEN"
Postponed FixtureStatus = "PST"
Cancelled FixtureStatus = "CANC"
Abandoned FixtureStatus = "ABD"
TechnicalLoss FixtureStatus = "AWD"
WalkOver FixtureStatus = "WO"
InProgress FixtureStatus = "LIVE"
)
func (fs FixtureStatus) IsLive() bool {
return fs == InProgress || fs == FirstHalf ||
fs == SecondHalf || fs == ExtraTime || fs == Penalty
}
func (fs FixtureStatus) IsFinished() bool {
return fs == Finished ||
fs == FinishedAfterExtra ||
fs == FinishedAfterPenalty ||
fs == TechnicalLoss || fs == WalkOver ||
fs == Cancelled
}
func (fs FixtureStatus) IsInFuture() bool {
return fs == NotStarted || fs == TimeToBeDefined
}
package common
type StatsType string
const (
ShotsOnGoal StatsType = "Shots on Goal"
ShotsOffGoal StatsType = "Shots off Goal"
ShotsInsideBox StatsType = "Shots insidebox"
ShotsOutsideBox StatsType = "Shots outsidebox"
TotalShots StatsType = "Total Shots"
BlockedShots StatsType = "Blocked Shots"
Fouls StatsType = "Fouls"
CornerKicks StatsType = "Corner Kicks"
Offsides StatsType = "Offsides"
BallPossession StatsType = "Ball Possession"
YellowCards StatsType = "Yellow Cards"
RedCards StatsType = "Red Cards"
GoalkeeperSaves StatsType = "Goalkeeper Saves"
TotalPasses StatsType = "Total passes"
PassesAccurate StatsType = "Passes accurate"
PassesPercentage StatsType = "Passes %"
)
func (st StatsType) IsPercentage() bool {
return st == BallPossession || st == PassesPercentage
}
package parser
import (
"github.com/syurchen93/api-football-client/response/fixtures"
"strconv"
"strings"
)
func ParseFormationStringIntoMap(formation string) map[string]interface{} {
formationMap := make(map[string]interface{})
formationParts := strings.Split(formation, "-")
formationInts := make([]int, len(formationParts))
for i, part := range formationParts {
formationInts[i], _ = strconv.Atoi(part)
}
formationMap[string(fixtures.LineupPositionDefender)] = formationInts[0]
last := len(formationParts) - 1
formationMap[string(fixtures.LineupPositionForward)] = formationInts[last]
var midfielderCount int
switch len(formationInts) {
case 3:
midfielderCount = formationInts[1]
case 4:
if formationInts[1] == 2 && formationInts[2] == 3 {
// edge case for 4-2-3-1
formationMap[string(fixtures.LineupPositionDefensiveMidfielder)] = 2
formationMap[string(fixtures.LineupPositionAttackingMidfielder)] = 3
} else if formationParts[1] >= formationParts[2] {
midfielderCount = formationInts[1]
formationMap[string(fixtures.LineupPositionAttackingMidfielder)] = formationInts[2]
} else {
midfielderCount = formationInts[2]
formationMap[string(fixtures.LineupPositionDefensiveMidfielder)] = formationInts[1]
}
case 5:
midfielderCount = formationInts[2]
formationMap[string(fixtures.LineupPositionDefensiveMidfielder)] = formationInts[1]
formationMap[string(fixtures.LineupPositionAttackingMidfielder)] = formationInts[3]
}
if midfielderCount > 0 {
formationMap[string(fixtures.LineupPositionMidfielder)] = midfielderCount
}
formationMap["original"] = formation
return formationMap
}
package fixture
import (
"github.com/syurchen93/api-football-client/common"
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
)
type Event struct {
FixtureID int `mapstructure:"fixture" validate:"required"`
TeamID int `mapstructure:"team,omitempty"`
PlayerID int `mapstructure:"player,omitempty"`
Type common.EventType `mapstructure:"type,omitempty"`
}
func (e Event) GetEndpoint() string {
return "fixtures/events"
}
func (e Event) GetResponseStruct() response.ResponseInterface {
return fixtures.Event{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/common"
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
"time"
)
type Fixture struct {
ID int `mapstructure:"id,omitempty"`
IDs []int `mapstructure:"ids,omitempty" validate:"omitempty,max=20"`
Live string `mapstructure:"live,omitempty" validate:"omitempty,oneof=all id-id"`
Date time.Time `mapstructure:"date,omitempty"`
League int `mapstructure:"league,omitempty"`
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
Team int `mapstructure:"team,omitempty"`
Last int `mapstructure:"last,omitempty" validate:"omitempty,max=99"`
Next int `mapstructure:"next,omitempty" validate:"omitempty,max=99"`
From time.Time `mapstructure:"from,omitempty"`
To time.Time `mapstructure:"to,omitempty"`
Round string `mapstructure:"round,omitempty"`
Statuses []common.FixtureStatus `mapstructure:"status,omitempty"`
Venue int `mapstructure:"venue,omitempty"`
Timezone string `mapstructure:"timezone,omitempty"`
}
func (f Fixture) GetEndpoint() string {
return "fixtures"
}
func (f Fixture) GetResponseStruct() response.ResponseInterface {
return fixtures.Fixture{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/common"
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
"time"
)
type HeadToHead struct {
H2H []int `mapstructure:"h2h" validate:"required,len=2"`
Date time.Time `mapstructure:"date,omitempty"`
League int `mapstructure:"league,omitempty"`
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
Last int `mapstructure:"last,omitempty"`
Next int `mapstructure:"next,omitempty"`
From time.Time `mapstructure:"from,omitempty"`
To time.Time `mapstructure:"to,omitempty"`
Statuses []common.FixtureStatus `mapstructure:"status,omitempty"`
Venue int `mapstructure:"venue,omitempty"`
Timezone string `mapstructure:"timezone,omitempty"`
}
func (h2h HeadToHead) GetEndpoint() string {
return "fixtures/headtohead"
}
func (h2h HeadToHead) GetResponseStruct() response.ResponseInterface {
return fixtures.HeadToHead{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
)
type Lineup struct {
FixtureID int `mapstructure:"fixture" validate:"required"`
TeamID int `mapstructure:"team,omitempty"`
PlayerID int `mapstructure:"player,omitempty"`
Type LineupType `mapstructure:"type,omitempty"`
}
type LineupType string
const (
StartingXI LineupType = "startxi"
Substitutes LineupType = "substitutes"
Formation LineupType = "formation"
Coach LineupType = "coach"
)
func (l Lineup) GetEndpoint() string {
return "fixtures/lineups"
}
func (l Lineup) GetResponseStruct() response.ResponseInterface {
return fixtures.Lineup{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
)
type Predictions struct {
FixtureID int `mapstructure:"fixture" validate:"required"`
}
func (r Predictions) GetEndpoint() string {
return "predictions"
}
func (r Predictions) GetResponseStruct() response.ResponseInterface {
return fixtures.Predictions{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
)
type Round struct {
League int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
Current bool `mapstructure:"current"`
}
func (r Round) GetEndpoint() string {
return "fixtures/rounds"
}
func (r Round) GetResponseStruct() response.ResponseInterface {
return fixtures.Round{}
}
package fixture
import (
"github.com/syurchen93/api-football-client/common"
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/fixtures"
)
type TeamStatistics struct {
FixtureID int `mapstructure:"fixture" validate:"required"`
// Team id is required even though documentation says it's optional
TeamID int `mapstructure:"team" validate:"required"`
Type common.StatsType `mapstructure:"type"`
}
type PlayerStatistics struct {
FixtureID int `mapstructure:"fixture" validate:"required"`
// Team id is required even though documentation says it's optional
TeamID int `mapstructure:"team,omitempty"`
}
func (ts TeamStatistics) GetEndpoint() string {
return "fixtures/statistics"
}
func (ts TeamStatistics) GetResponseStruct() response.ResponseInterface {
return fixtures.TeamStatistics{}
}
func (ps PlayerStatistics) GetEndpoint() string {
return "fixtures/players"
}
func (ps PlayerStatistics) GetResponseStruct() response.ResponseInterface {
return fixtures.PlayerStatistics{}
}
package league
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type Country struct {
Name string `mapstructure:"name,omitempty"`
Code string `validate:"omitempty,len=2" mapstructure:"code,omitempty"`
Search string `validate:"omitempty,len=3" mapstructure:"search,omitempty"`
}
func (c Country) GetEndpoint() string {
return "countries"
}
func (c Country) GetResponseStruct() response.ResponseInterface {
return leagues.Country{}
}
package league
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type League struct {
ID int `mapstructure:"id,omitempty"`
Name string `mapstructure:"name,omitempty"`
CountryName string `mapstructure:"country,omitempty"`
CountryCode string `mapstructure:"code,omitempty" validate:"omitempty,len=2"`
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
Team int `mapstructure:"team,omitempty"`
Type string `mapstructure:"type,omitempty" validate:"omitempty,oneof=league cup"`
Current bool `mapstructure:"current,omitempty" validate:"omitempty"`
Search string `mapstructure:"search,omitempty" validate:"omitempty,min=3"`
Last int `mapstructure:"last,omitempty" validate:"omitempty,max=99"`
}
func (l League) GetEndpoint() string {
return "leagues"
}
func (l League) GetResponseStruct() response.ResponseInterface {
return leagues.LeagueData{}
}
package league
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type Season struct {
}
func (s Season) GetEndpoint() string {
return "leagues/seasons"
}
func (s Season) GetResponseStruct() response.ResponseInterface {
return leagues.SeasonYear{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Coach struct {
ID int `mapstructure:"id,omitempty"`
TeamID int `mapstructure:"team,omitempty"`
Search string `mapstructure:"search,omitempty" validate:"omitempty,min=3"`
}
func (c Coach) GetEndpoint() string {
return "coachs"
}
func (c Coach) GetResponseStruct() response.ResponseInterface {
return misc.Coach{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
"time"
)
type Injuries struct {
LeagueID int `mapstructure:"league,omitempty" validate:"omitempty"`
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
FixtureID int `mapstructure:"fixture,omitempty" validate:"omitempty"`
TeamID int `mapstructure:"team,omitempty" validate:"omitempty"`
PlayerID int `mapstructure:"player,omitempty" validate:"omitempty"`
Date time.Time `mapstructure:"date,omitempty" validate:"omitempty"`
Timezone string `mapstructure:"timezone,omitempty" validate:"omitempty"`
}
func (i Injuries) GetEndpoint() string {
return "injuries"
}
func (i Injuries) GetResponseStruct() response.ResponseInterface {
return misc.InjuryInfo{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Sideilined struct {
PlayerID int `mapstructure:"player,omitempty" validate:"required_without=CoachID,excluded_with=CoachID"`
CoachID int `mapstructure:"coach,omitempty" validate:"required_without=PlayerID,excluded_with=PlayerID"`
}
func (s Sideilined) GetEndpoint() string {
return "sidelined"
}
func (s Sideilined) GetResponseStruct() response.ResponseInterface {
return misc.SidelinedEntry{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Timezone struct{}
func (t Timezone) GetEndpoint() string {
return "timezone"
}
func (t Timezone) GetResponseStruct() response.ResponseInterface {
return misc.Timezone{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Transfer struct {
PlayerID int `mapstructure:"player,omitempty"`
TeamID int `mapstructure:"team,omitempty"`
}
func (t Transfer) GetEndpoint() string {
return "transfers"
}
func (t Transfer) GetResponseStruct() response.ResponseInterface {
return misc.Transfer{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Trophies struct {
PlayerID int `mapstructure:"player,omitempty" validate:"required_without=CoachID,excluded_with=CoachID"`
CoachID int `mapstructure:"coach,omitempty" validate:"required_without=PlayerID,excluded_with=PlayerID"`
}
func (p Trophies) GetEndpoint() string {
return "trophies"
}
func (p Trophies) GetResponseStruct() response.ResponseInterface {
return misc.Trophy{}
}
package misc
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/misc"
)
type Venue struct {
ID int `mapstructure:"id,omitempty"`
Name string `mapstructure:"name,omitempty"`
City string `mapstructure:"city,omitempty"`
Country string `mapstructure:"country,omitempty"`
Search string `mapstructure:"search,omitempty" validate:"min=3,omitempty"`
}
func (v Venue) GetEndpoint() string {
return "venues"
}
func (v Venue) GetResponseStruct() response.ResponseInterface {
return misc.Venue{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type PlayerInfo struct {
ID int `mapstructure:"id,omitempty"`
TeamID int `mapstructure:"team,omitempty"`
LeagueID int `mapstructure:"league,omitempty"`
// Requires the fields Id, League or Team
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
// Requires the fields League or Team
Search string `mapstructure:"search,omitempty" validate:"omitempty,min=4"`
Page int `mapstructure:"page,omitempty"`
}
func (p PlayerInfo) GetEndpoint() string {
return "players"
}
func (p PlayerInfo) GetResponseStruct() response.ResponseInterface {
return players.PlayerInfo{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type PlayerSeason struct {
PlayerID int `mapstructure:"player,omitempty"`
}
func (s PlayerSeason) GetEndpoint() string {
return "players/seasons"
}
func (s PlayerSeason) GetResponseStruct() response.ResponseInterface {
return leagues.SeasonYear{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type Squad struct {
TeamID int `mapstructure:"team,omitempty"`
PlayerID int `mapstructure:"player,omitempty"`
}
func (s Squad) GetEndpoint() string {
return "players/squads"
}
func (s Squad) GetResponseStruct() response.ResponseInterface {
return players.Squad{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type TopAssists struct {
LeagueID int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
}
func (ta TopAssists) GetEndpoint() string {
return "players/topassists"
}
func (ta TopAssists) GetResponseStruct() response.ResponseInterface {
return players.PlayerInfo{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type TopRedCards struct {
LeagueID int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
}
func (trc TopRedCards) GetEndpoint() string {
return "players/topredcards"
}
func (trc TopRedCards) GetResponseStruct() response.ResponseInterface {
return players.PlayerInfo{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type TopScorer struct {
LeagueID int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
}
func (p TopScorer) GetEndpoint() string {
return "players/topscorers"
}
func (p TopScorer) GetResponseStruct() response.ResponseInterface {
return players.PlayerInfo{}
}
package player
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/players"
)
type TopYellowCards struct {
LeagueID int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
}
func (tyc TopYellowCards) GetEndpoint() string {
return "players/topyellowcards"
}
func (tyc TopYellowCards) GetResponseStruct() response.ResponseInterface {
return players.PlayerInfo{}
}
package standings
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/standings"
)
type Standings struct {
League int `mapstructure:"league,omitempty"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
Team int `mapstructure:"team,omitempty"`
}
func (s Standings) GetEndpoint() string {
return "standings"
}
func (s Standings) GetResponseStruct() response.ResponseInterface {
return standings.Standings{}
}
package team
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type Country struct {
}
func (c Country) GetEndpoint() string {
return "teams/countries"
}
func (c Country) GetResponseStruct() response.ResponseInterface {
return leagues.Country{}
}
package team
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/team"
)
type Team struct {
ID int `mapstructure:"id,omitempty" validate:"omitempty"`
Name string `mapstructure:"name,omitempty" validate:"omitempty"`
League int `mapstructure:"league,omitempty" validate:"omitempty"`
Season int `mapstructure:"season,omitempty" validate:"omitempty,gte=1000,lte=9999"`
Country string `mapstructure:"country,omitempty" validate:"omitempty"`
CountryCode string `mapstructure:"code,omitempty" validate:"omitempty,len=3"`
Venue int `mapstructure:"venue,omitempty" validate:"omitempty"`
Search string `mapstructure:"search,omitempty" validate:"omitempty,min=3"`
}
func (t Team) GetEndpoint() string {
return "teams"
}
func (t Team) GetResponseStruct() response.ResponseInterface {
return team.Information{}
}
package team
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/leagues"
)
type TeamSeason struct {
Team int `mapstructure:"team" validate:"required"`
}
func (s TeamSeason) GetEndpoint() string {
return "teams/seasons"
}
func (s TeamSeason) GetResponseStruct() response.ResponseInterface {
return leagues.SeasonYear{}
}
package team
import (
"github.com/syurchen93/api-football-client/response"
"github.com/syurchen93/api-football-client/response/team"
"time"
)
type Statistics struct {
League int `mapstructure:"league" validate:"required"`
Season int `mapstructure:"season" validate:"required,gte=1000,lte=9999"`
Team int `mapstructure:"team" validate:"required"`
LimitDate time.Time `mapstructure:"date,omitempty" validate:"omitempty"`
}
func (s Statistics) GetEndpoint() string {
return "teams/statistics"
}
func (s Statistics) GetResponseStruct() response.ResponseInterface {
return team.Statistics{}
}
package fixtures
import (
"github.com/syurchen93/api-football-client/common"
)
type Event struct {
Time Time `json:"time"`
Team Team `json:"team"`
Player Player `json:"player"`
Assist Player `json:"assist"`
Type common.EventType `json:"type"`
Detail common.EventTypeDetails `json:"detail"`
Comments string `json:"comments"`
}
type Time struct {
Elapsed int `json:"elapsed"`
Extra int `json:"extra"`
}
type Player struct {
ID int `json:"id"`
Name string `json:"name"`
Photo string `mapstructure:"omitempty" json:"photo,omitempty"`
}
func (e Event) IsPenaltyShootout() bool {
return e.Comments == common.CommentPenaltyShootout
}
func (e Event) IsFoul() bool {
return e.Comments == common.CommentFoul
}