Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion basho.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import (
"strconv"
)

// BashoID represents the unique identifier for a Basho (sumo tournament).
// BashoID represents the unique identifier for a basho.
type BashoID struct {
Year int
Month int // Month is 1-12.
}

// BashoDayID represents a specific day within a basho (1-15), or a playoff match starting from 16.
type BashoDayID struct {
BashoID
Day int
}

func (b BashoID) String() string {
return fmt.Sprintf("%04d%02d", b.Year, b.Month)
}
Expand Down Expand Up @@ -40,3 +46,32 @@ func (b *BashoID) UnmarshalJSON(data []byte) error {
b.Month = month
return nil
}

func (b BashoDayID) String() string {
return fmt.Sprintf("%s-%d", b.BashoID.String(), b.Day)
}

func (b BashoDayID) MarshalJSON() ([]byte, error) {
return []byte(`"` + b.String() + `"`), nil
}

func (b *BashoDayID) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return fmt.Errorf("error unmarshaling BashoDayID: %w", err)
}
if len(s) < 8 {
return fmt.Errorf("invalid BashoDayID format: %s", s)
}
var bashoID BashoID
if err := bashoID.UnmarshalJSON([]byte(`"` + s[0:6] + `"`)); err != nil {
return fmt.Errorf("error parsing BashoID from BashoDayID: %w", err)
}
day, err := strconv.Atoi(s[7:])
if err != nil {
return fmt.Errorf("error parsing day from BashoDayID: %w", err)
}
b.BashoID = bashoID
b.Day = day
return nil
}
144 changes: 144 additions & 0 deletions basho_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,147 @@ func TestUnmarshalBashoIDFromJSON(t *testing.T) {
g.Expect(w.Basho).To(Equal(sumoapi.BashoID{Year: 2023, Month: 11}))
})
}

func TestMarshalBashoDayIDToJSON(t *testing.T) {
for _, tt := range []struct {
name string
bashoDayID sumoapi.BashoDayID
expectedJSON string
}{
{
name: "double digit month, single digit day",
bashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2023, Month: 11},
Day: 1,
},
expectedJSON: `"202311-1"`,
},
{
name: "single digit month, double digit day",
bashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 1999, Month: 3},
Day: 15,
},
expectedJSON: `"199903-15"`,
},
{
name: "playoff day",
bashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2025, Month: 1},
Day: 16,
},
expectedJSON: `"202501-16"`,
},
} {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
b, err := json.Marshal(tt.bashoDayID)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(string(b)).To(Equal(tt.expectedJSON))
})
}

t.Run("marshal inside struct", func(t *testing.T) {
g := NewWithT(t)
type wrapper struct {
BashoDay sumoapi.BashoDayID `json:"bashoDay"`
}
w := wrapper{
BashoDay: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2023, Month: 11},
Day: 1,
},
}
b, err := json.Marshal(w)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(string(b)).To(Equal(`{"bashoDay":"202311-1"}`))
})
}

func TestUnmarshalBashoDayIDFromJSON(t *testing.T) {
for _, tt := range []struct {
name string
jsonData string
expectedBashoDayID sumoapi.BashoDayID
expectError bool
}{
{
name: "double digit month, single digit day",
jsonData: `"202311-1"`,
expectedBashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2023, Month: 11},
Day: 1,
},
expectError: false,
},
{
name: "single digit month, double digit day",
jsonData: `"199903-15"`,
expectedBashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 1999, Month: 3},
Day: 15,
},
expectError: false,
},
{
name: "playoff day",
jsonData: `"202501-16"`,
expectedBashoDayID: sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2025, Month: 1},
Day: 16,
},
expectError: false,
},
{
name: "invalid basho format",
jsonData: `"20A311-1"`,
expectedBashoDayID: sumoapi.BashoDayID{},
expectError: true,
},
{
name: "wrong length - too short",
jsonData: `"202311"`,
expectedBashoDayID: sumoapi.BashoDayID{},
expectError: true,
},
{
name: "missing day",
jsonData: `"202311-"`,
expectedBashoDayID: sumoapi.BashoDayID{},
expectError: true,
},
{
name: "invalid day - not a number",
jsonData: `"202311-X"`,
expectedBashoDayID: sumoapi.BashoDayID{},
expectError: true,
},
} {
t.Run(tt.name, func(t *testing.T) {
g := NewWithT(t)
var b sumoapi.BashoDayID
err := json.Unmarshal([]byte(tt.jsonData), &b)
if tt.expectError {
g.Expect(err).To(HaveOccurred())
} else {
g.Expect(err).ToNot(HaveOccurred())
g.Expect(b).To(Equal(tt.expectedBashoDayID))
}
})
}

t.Run("unmarshal inside struct", func(t *testing.T) {
g := NewWithT(t)
type wrapper struct {
BashoDay sumoapi.BashoDayID `json:"bashoDay"`
}
jsonData := `{"bashoDay":"202311-1"}`
var w wrapper
err := json.Unmarshal([]byte(jsonData), &w)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(w.BashoDay).To(Equal(sumoapi.BashoDayID{
BashoID: sumoapi.BashoID{Year: 2023, Month: 11},
Day: 1,
}))
})
}
1 change: 1 addition & 0 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ type Client interface {
GetRikishiStatsAPI
ListRikishiMatchesAPI
ListRikishiMatchesAgainstOpponentAPI
ListKimariteAPI
ListKimariteMatchesAPI
ListMeasurementChangesAPI
ListRankChangesAPI
Expand Down
8 changes: 8 additions & 0 deletions kimarite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package sumoapi

// Kimarite represents a sumo wrestling winning technique.
type Kimarite struct {
Name string `json:"kimarite" jsonschema:"The kimarite (winning technique) name."`
Count int `json:"count" jsonschema:"The number of times this kimarite (winning technique) has been used in a match."`
LastUsage BashoDayID `json:"lastUsage" jsonschema:"The basho (sumo tournament) day (1-15) when this kimarite (winning technique) was last used in the format YYYYMM-{day}. A day value of 16 or higher represents an individual playoff match."`
}
45 changes: 45 additions & 0 deletions list_kimarite.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package sumoapi

import (
"context"
"fmt"
"net/url"
)

// ListKimariteAPI defines the methods available for listing kimarite.
type ListKimariteAPI interface {
// ListKimarite calls the GET /api/kimarite endpoint.
ListKimarite(ctx context.Context, req ListKimariteRequest) (*ListKimariteResponse, error)
}

// ListKimariteRequest represents the request parameters for the ListKimarite method.
type ListKimariteRequest struct {
SortField string `json:"sortField" jsonschema:"The field by which to sort the results. Valid values are 'kimarite', 'count' and 'lastUsage'."`
SortOrder string `json:"sortOrder,omitempty" jsonschema:"The order in which to sort the results by the sort field. Valid values are 'asc' for ascending and 'desc' for descending. Default is 'asc'."`
Limit int `json:"limit,omitempty" jsonschema:"The maximum number of results to return."`
Skip int `json:"skip,omitempty" jsonschema:"The number of results to skip over for pagination."`
}

// ListKimariteResponse represents the response from the ListKimarite method.
type ListKimariteResponse struct {
Limit int `json:"limit" jsonschema:"The maximum number of results that were returned."`
Skip int `json:"skip" jsonschema:"The number of results that were skipped over."`
SortField string `json:"sortField" jsonschema:"The field by which the results are sorted. Values are 'kimarite', 'count' and 'lastUsage'."`
SortOrder string `json:"sortOrder" jsonschema:"The order in which the results are sorted by the sort field. Values are 'asc' for ascending and 'desc' for descending."`
Kimarite []Kimarite `json:"records" jsonschema:"The list of kimarite (winning technique) records matching the filters."`
}

func (c *client) ListKimarite(ctx context.Context, req ListKimariteRequest) (*ListKimariteResponse, error) {
query := make(url.Values)
query.Set("sortField", req.SortField)
if order := getSortOrder(req.SortOrder); order != "" {
query.Set("sortOrder", order)
}
if req.Limit > 0 {
query.Set("limit", fmt.Sprint(req.Limit))
}
if req.Skip > 0 {
query.Set("skip", fmt.Sprint(req.Skip))
}
return getObject[ListKimariteResponse](ctx, c, "/kimarite", query)
}
Loading