cricinfo-cli-go 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +63 -0
- package/CONTRIBUTORS.md +75 -0
- package/LICENSE +21 -0
- package/Makefile +131 -0
- package/README.md +130 -0
- package/bin/cricinfo.js +44 -0
- package/cmd/cricinfo/main.go +15 -0
- package/go.mod +10 -0
- package/go.sum +10 -0
- package/internal/app/app.go +11 -0
- package/internal/app/app_test.go +122 -0
- package/internal/buildinfo/buildinfo.go +16 -0
- package/internal/cli/analysis.go +262 -0
- package/internal/cli/analysis_test.go +175 -0
- package/internal/cli/competitions.go +154 -0
- package/internal/cli/competitions_test.go +165 -0
- package/internal/cli/leagues.go +297 -0
- package/internal/cli/leagues_test.go +194 -0
- package/internal/cli/matches.go +403 -0
- package/internal/cli/matches_test.go +413 -0
- package/internal/cli/players.go +263 -0
- package/internal/cli/players_test.go +384 -0
- package/internal/cli/root.go +141 -0
- package/internal/cli/search.go +119 -0
- package/internal/cli/teams.go +214 -0
- package/internal/cli/teams_test.go +192 -0
- package/internal/cricinfo/analysis.go +1401 -0
- package/internal/cricinfo/analysis_phase15_test.go +267 -0
- package/internal/cricinfo/client.go +471 -0
- package/internal/cricinfo/client_test.go +280 -0
- package/internal/cricinfo/cmd/fixture-refresh/main.go +145 -0
- package/internal/cricinfo/competitions.go +405 -0
- package/internal/cricinfo/competitions_phase13_test.go +234 -0
- package/internal/cricinfo/coverage_ledger.go +122 -0
- package/internal/cricinfo/coverage_ledger_test.go +253 -0
- package/internal/cricinfo/decode.go +115 -0
- package/internal/cricinfo/decode_test.go +100 -0
- package/internal/cricinfo/entity_index.go +618 -0
- package/internal/cricinfo/entity_index_test.go +175 -0
- package/internal/cricinfo/fixture_matrix.go +243 -0
- package/internal/cricinfo/fixture_matrix_test.go +49 -0
- package/internal/cricinfo/fixtures_test.go +264 -0
- package/internal/cricinfo/historical_hydration.go +1641 -0
- package/internal/cricinfo/historical_phase14_test.go +542 -0
- package/internal/cricinfo/leagues.go +1210 -0
- package/internal/cricinfo/leagues_phase12_test.go +324 -0
- package/internal/cricinfo/live_leagues_test.go +169 -0
- package/internal/cricinfo/live_matches_test.go +203 -0
- package/internal/cricinfo/live_matrix_test.go +118 -0
- package/internal/cricinfo/live_players_test.go +122 -0
- package/internal/cricinfo/live_search_test.go +86 -0
- package/internal/cricinfo/live_smoke_test.go +213 -0
- package/internal/cricinfo/live_teams_test.go +104 -0
- package/internal/cricinfo/matches.go +1508 -0
- package/internal/cricinfo/matches_phase7_test.go +207 -0
- package/internal/cricinfo/matches_phase9_test.go +253 -0
- package/internal/cricinfo/normalize_entities.go +1727 -0
- package/internal/cricinfo/normalize_leagues.go +346 -0
- package/internal/cricinfo/players.go +1332 -0
- package/internal/cricinfo/players_phase10_test.go +174 -0
- package/internal/cricinfo/players_phase11_test.go +373 -0
- package/internal/cricinfo/render_contract.go +1088 -0
- package/internal/cricinfo/render_phase4_test.go +633 -0
- package/internal/cricinfo/renderer.go +1689 -0
- package/internal/cricinfo/resolver.go +813 -0
- package/internal/cricinfo/resolver_test.go +244 -0
- package/internal/cricinfo/teams.go +603 -0
- package/internal/cricinfo/teams_phase8_test.go +231 -0
- package/internal/cricinfo/testdata/fixtures/README.md +43 -0
- package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/broadcasts.json +11 -0
- package/internal/cricinfo/testdata/fixtures/aux-competition-metadata/officials.json +150 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-110.json +157 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-52545007.json +145 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/detail-52559021.json +143 -0
- package/internal/cricinfo/testdata/fixtures/details-plays/plays.json +15 -0
- package/internal/cricinfo/testdata/fixtures/endpoint-matrix.tsv +19 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow-1.json +12 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/fow.json +42 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/innings-1-2.json +38 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnership-1.json +31 -0
- package/internal/cricinfo/testdata/fixtures/innings-fow-partnerships/partnerships.json +42 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-offdays.json +20 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar-ondays.json +21 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/calendar.json +14 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-2025.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-group-1.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-groups.json +11 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-type-1.json +13 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/season-types.json +11 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/seasons.json +30 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-item-1.json +72 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings-root.json +3 -0
- package/internal/cricinfo/testdata/fixtures/leagues-seasons-standings/standings.json +15 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/competition.json +460 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/event-1529474.json +86 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/matchcards-1527966.json +368 -0
- package/internal/cricinfo/testdata/fixtures/matches-competitions/situation-1529474.json +10 -0
- package/internal/cricinfo/testdata/fixtures/players/athlete-1361257-statistics.json +126 -0
- package/internal/cricinfo/testdata/fixtures/players/athlete-1361257.json +113 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-1-statistics-0.json +208 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores-1-2-statistics-0.json +252 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-linescores.json +74 -0
- package/internal/cricinfo/testdata/fixtures/players/roster-1361257-statistics-0.json +1008 -0
- package/internal/cricinfo/testdata/fixtures/root-discovery/events.json +72 -0
- package/internal/cricinfo/testdata/fixtures/root-discovery/root.json +28 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/competitor-789643.json +40 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/leaders-789643.json +353 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/records-789643.json +91 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772-object.json +231 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-1147772.json +235 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/roster-789643.json +322 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/scores-789643.json +19 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/statistics-789643.json +629 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643-athletes.json +7 -0
- package/internal/cricinfo/testdata/fixtures/team-competitor/team-789643.json +67 -0
- package/internal/cricinfo/testdata/golden/match-empty.golden +1 -0
- package/internal/cricinfo/testdata/golden/match-list.golden +2 -0
- package/internal/cricinfo/testdata/golden/match-partial.golden +3 -0
- package/internal/cricinfo/types.go +54 -0
- package/package.json +51 -0
- package/scripts/postinstall.js +153 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
package cli
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"bytes"
|
|
5
|
+
"context"
|
|
6
|
+
"encoding/json"
|
|
7
|
+
"strings"
|
|
8
|
+
"testing"
|
|
9
|
+
|
|
10
|
+
"github.com/amxv/cricinfo-cli/internal/cricinfo"
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
type fakeMatchService struct {
|
|
14
|
+
listResult cricinfo.NormalizedResult
|
|
15
|
+
liveResult cricinfo.NormalizedResult
|
|
16
|
+
showResult cricinfo.NormalizedResult
|
|
17
|
+
statusResult cricinfo.NormalizedResult
|
|
18
|
+
scorecardResult cricinfo.NormalizedResult
|
|
19
|
+
detailsResult cricinfo.NormalizedResult
|
|
20
|
+
playsResult cricinfo.NormalizedResult
|
|
21
|
+
situationResult cricinfo.NormalizedResult
|
|
22
|
+
inningsResult cricinfo.NormalizedResult
|
|
23
|
+
partnerships cricinfo.NormalizedResult
|
|
24
|
+
fowResult cricinfo.NormalizedResult
|
|
25
|
+
deliveries cricinfo.NormalizedResult
|
|
26
|
+
|
|
27
|
+
inningsQueries []string
|
|
28
|
+
inningsOpts []cricinfo.MatchInningsOptions
|
|
29
|
+
partnershipsQueries []string
|
|
30
|
+
partnershipsOpts []cricinfo.MatchInningsOptions
|
|
31
|
+
fowQueries []string
|
|
32
|
+
fowOpts []cricinfo.MatchInningsOptions
|
|
33
|
+
deliveriesQueries []string
|
|
34
|
+
deliveriesOpts []cricinfo.MatchInningsOptions
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func (f *fakeMatchService) Close() error { return nil }
|
|
38
|
+
|
|
39
|
+
func (f *fakeMatchService) List(context.Context, cricinfo.MatchListOptions) (cricinfo.NormalizedResult, error) {
|
|
40
|
+
return f.listResult, nil
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
func (f *fakeMatchService) Live(context.Context, cricinfo.MatchListOptions) (cricinfo.NormalizedResult, error) {
|
|
44
|
+
return f.liveResult, nil
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
func (f *fakeMatchService) Show(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
48
|
+
return f.showResult, nil
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
func (f *fakeMatchService) Status(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
52
|
+
return f.statusResult, nil
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
func (f *fakeMatchService) Scorecard(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
56
|
+
return f.scorecardResult, nil
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
func (f *fakeMatchService) Details(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
60
|
+
return f.detailsResult, nil
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
func (f *fakeMatchService) Plays(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
64
|
+
return f.playsResult, nil
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
func (f *fakeMatchService) Situation(context.Context, string, cricinfo.MatchLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
68
|
+
return f.situationResult, nil
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func (f *fakeMatchService) Innings(_ context.Context, query string, opts cricinfo.MatchInningsOptions) (cricinfo.NormalizedResult, error) {
|
|
72
|
+
f.inningsQueries = append(f.inningsQueries, query)
|
|
73
|
+
f.inningsOpts = append(f.inningsOpts, opts)
|
|
74
|
+
return f.inningsResult, nil
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func (f *fakeMatchService) Partnerships(_ context.Context, query string, opts cricinfo.MatchInningsOptions) (cricinfo.NormalizedResult, error) {
|
|
78
|
+
f.partnershipsQueries = append(f.partnershipsQueries, query)
|
|
79
|
+
f.partnershipsOpts = append(f.partnershipsOpts, opts)
|
|
80
|
+
return f.partnerships, nil
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func (f *fakeMatchService) FallOfWicket(_ context.Context, query string, opts cricinfo.MatchInningsOptions) (cricinfo.NormalizedResult, error) {
|
|
84
|
+
f.fowQueries = append(f.fowQueries, query)
|
|
85
|
+
f.fowOpts = append(f.fowOpts, opts)
|
|
86
|
+
return f.fowResult, nil
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
func (f *fakeMatchService) Deliveries(_ context.Context, query string, opts cricinfo.MatchInningsOptions) (cricinfo.NormalizedResult, error) {
|
|
90
|
+
f.deliveriesQueries = append(f.deliveriesQueries, query)
|
|
91
|
+
f.deliveriesOpts = append(f.deliveriesOpts, opts)
|
|
92
|
+
return f.deliveries, nil
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func TestMatchesCommandsRenderTextAndJSON(t *testing.T) {
|
|
96
|
+
match := cricinfo.Match{
|
|
97
|
+
ID: "1529474",
|
|
98
|
+
LeagueID: "19138",
|
|
99
|
+
EventID: "1529474",
|
|
100
|
+
CompetitionID: "1529474",
|
|
101
|
+
Description: "3rd Match",
|
|
102
|
+
MatchState: "Boost lead by 20 runs",
|
|
103
|
+
Date: "2026-04-09T05:30Z",
|
|
104
|
+
VenueSummary: "Aino Maina, Kandahar, Afghanistan",
|
|
105
|
+
ScoreSummary: "BOOST 319 & 69/2 (19 ov) | SGH 368",
|
|
106
|
+
Teams: []cricinfo.Team{
|
|
107
|
+
{ID: "789643", Name: "Boost Region", ShortName: "BOOST", ScoreSummary: "319 & 69/2 (19 ov)"},
|
|
108
|
+
{ID: "789647", Name: "Speen Ghar Region", ShortName: "SGH", ScoreSummary: "368"},
|
|
109
|
+
},
|
|
110
|
+
StatusRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/status",
|
|
111
|
+
DetailsRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/details",
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
scorecard := cricinfo.MatchScorecard{
|
|
115
|
+
MatchID: "1529474",
|
|
116
|
+
BattingCards: []cricinfo.BattingCard{{InningsNumber: 3, TeamName: "Boost", Players: []cricinfo.BattingCardEntry{{PlayerName: "Numan Shah", Runs: "52"}}}},
|
|
117
|
+
BowlingCards: []cricinfo.BowlingCard{{InningsNumber: 3, TeamName: "Speen-Ghar", Players: []cricinfo.BowlingCardEntry{{PlayerName: "Hayatullah Noori", Wickets: "1"}}}},
|
|
118
|
+
PartnershipCards: []cricinfo.PartnershipCard{
|
|
119
|
+
{InningsNumber: 3, TeamName: "Boost", Players: []cricinfo.PartnershipCardEntry{{PartnershipWicketName: "1st", PartnershipRuns: "50"}}},
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
x := 12.5
|
|
124
|
+
y := 27.25
|
|
125
|
+
delivery := cricinfo.DeliveryEvent{
|
|
126
|
+
ID: "110",
|
|
127
|
+
ShortText: "Amanullah to Fazal Haq Shaheen, 1 run",
|
|
128
|
+
BatsmanRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/athletes/1361257",
|
|
129
|
+
BowlerRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/athletes/976585",
|
|
130
|
+
ScoreValue: 1,
|
|
131
|
+
Dismissal: map[string]any{"dismissal": false, "type": ""},
|
|
132
|
+
PlayType: map[string]any{"id": "1", "description": "run"},
|
|
133
|
+
BBBTimestamp: 0,
|
|
134
|
+
XCoordinate: &x,
|
|
135
|
+
YCoordinate: &y,
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
situation := cricinfo.MatchSituation{
|
|
139
|
+
Ref: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/situation",
|
|
140
|
+
OddsRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/situation/odds",
|
|
141
|
+
Data: map[string]any{"session": "Day 2"},
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
service := &fakeMatchService{
|
|
145
|
+
listResult: cricinfo.NewListResult(cricinfo.EntityMatch, []any{match}),
|
|
146
|
+
liveResult: cricinfo.NewListResult(cricinfo.EntityMatch, []any{match}),
|
|
147
|
+
showResult: cricinfo.NewDataResult(cricinfo.EntityMatch, match),
|
|
148
|
+
statusResult: cricinfo.NewDataResult(cricinfo.EntityMatch, match),
|
|
149
|
+
scorecardResult: cricinfo.NewDataResult(cricinfo.EntityMatchScorecard, scorecard),
|
|
150
|
+
detailsResult: cricinfo.NewListResult(cricinfo.EntityDeliveryEvent, []any{delivery}),
|
|
151
|
+
playsResult: cricinfo.NewListResult(cricinfo.EntityDeliveryEvent, []any{delivery}),
|
|
152
|
+
situationResult: cricinfo.NewDataResult(cricinfo.EntityMatchSituation, situation),
|
|
153
|
+
inningsResult: cricinfo.NewListResult(cricinfo.EntityInnings, []any{
|
|
154
|
+
cricinfo.Innings{
|
|
155
|
+
TeamName: "BOOST",
|
|
156
|
+
InningsNumber: 1,
|
|
157
|
+
Period: 3,
|
|
158
|
+
Score: "69/2 (19 ov)",
|
|
159
|
+
OverTimeline: []cricinfo.InningsOver{
|
|
160
|
+
{Number: 12, Runs: 10, WicketCount: 1},
|
|
161
|
+
},
|
|
162
|
+
WicketTimeline: []cricinfo.InningsWicket{
|
|
163
|
+
{Number: 1, FOW: "60/1", Over: "11.2", DetailRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/details/52545007"},
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
}),
|
|
167
|
+
partnerships: cricinfo.NewListResult(cricinfo.EntityPartnership, []any{
|
|
168
|
+
cricinfo.Partnership{WicketName: "1st", Runs: 60, Overs: 11.2, InningsID: "1", Period: "1"},
|
|
169
|
+
}),
|
|
170
|
+
fowResult: cricinfo.NewListResult(cricinfo.EntityFallOfWicket, []any{
|
|
171
|
+
cricinfo.FallOfWicket{WicketNumber: 1, Runs: 60, WicketOver: 11.2, InningsID: "1", Period: "1"},
|
|
172
|
+
}),
|
|
173
|
+
deliveries: cricinfo.NewDataResult(cricinfo.EntityInnings, cricinfo.Innings{
|
|
174
|
+
TeamName: "BOOST",
|
|
175
|
+
InningsNumber: 1,
|
|
176
|
+
Period: 1,
|
|
177
|
+
OverTimeline: []cricinfo.InningsOver{
|
|
178
|
+
{Number: 12, Runs: 10, WicketCount: 1},
|
|
179
|
+
},
|
|
180
|
+
WicketTimeline: []cricinfo.InningsWicket{
|
|
181
|
+
{Number: 1, FOW: "60/1", Over: "11.2", DetailRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/details/52545007"},
|
|
182
|
+
},
|
|
183
|
+
}),
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
originalFactory := newMatchService
|
|
187
|
+
newMatchService = func() (matchCommandService, error) { return service, nil }
|
|
188
|
+
defer func() {
|
|
189
|
+
newMatchService = originalFactory
|
|
190
|
+
}()
|
|
191
|
+
|
|
192
|
+
var textOut bytes.Buffer
|
|
193
|
+
var textErr bytes.Buffer
|
|
194
|
+
if err := Run([]string{"matches", "list", "--format", "text"}, &textOut, &textErr); err != nil {
|
|
195
|
+
t.Fatalf("Run matches list --format text error: %v", err)
|
|
196
|
+
}
|
|
197
|
+
text := textOut.String()
|
|
198
|
+
if !strings.Contains(text, "Matches (1)") {
|
|
199
|
+
t.Fatalf("expected text list header, got %q", text)
|
|
200
|
+
}
|
|
201
|
+
if !strings.Contains(text, "BOOST") || !strings.Contains(text, "SGH") {
|
|
202
|
+
t.Fatalf("expected team names in text output, got %q", text)
|
|
203
|
+
}
|
|
204
|
+
if !strings.Contains(text, "Boost lead by 20 runs") {
|
|
205
|
+
t.Fatalf("expected match state in text output, got %q", text)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
var jsonOut bytes.Buffer
|
|
209
|
+
var jsonErr bytes.Buffer
|
|
210
|
+
if err := Run([]string{"matches", "show", "1529474", "--format", "json"}, &jsonOut, &jsonErr); err != nil {
|
|
211
|
+
t.Fatalf("Run matches show --format json error: %v", err)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
var payload map[string]any
|
|
215
|
+
if err := json.Unmarshal(jsonOut.Bytes(), &payload); err != nil {
|
|
216
|
+
t.Fatalf("decode JSON output: %v", err)
|
|
217
|
+
}
|
|
218
|
+
if payload["kind"] != string(cricinfo.EntityMatch) {
|
|
219
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityMatch, payload["kind"])
|
|
220
|
+
}
|
|
221
|
+
data, ok := payload["data"].(map[string]any)
|
|
222
|
+
if !ok {
|
|
223
|
+
t.Fatalf("expected object data in json output")
|
|
224
|
+
}
|
|
225
|
+
if data["id"] != "1529474" {
|
|
226
|
+
t.Fatalf("expected id 1529474 in json output, got %#v", data["id"])
|
|
227
|
+
}
|
|
228
|
+
if data["matchState"] != "Boost lead by 20 runs" {
|
|
229
|
+
t.Fatalf("expected matchState in json output, got %#v", data["matchState"])
|
|
230
|
+
}
|
|
231
|
+
if data["scoreSummary"] == nil {
|
|
232
|
+
t.Fatalf("expected scoreSummary in json output")
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
var scorecardOut bytes.Buffer
|
|
236
|
+
var scorecardErr bytes.Buffer
|
|
237
|
+
if err := Run([]string{"matches", "scorecard", "1529474", "--format", "text"}, &scorecardOut, &scorecardErr); err != nil {
|
|
238
|
+
t.Fatalf("Run matches scorecard --format text error: %v", err)
|
|
239
|
+
}
|
|
240
|
+
scorecardText := scorecardOut.String()
|
|
241
|
+
if !strings.Contains(scorecardText, "Batting") || !strings.Contains(scorecardText, "Bowling") || !strings.Contains(scorecardText, "Partnerships") {
|
|
242
|
+
t.Fatalf("expected scorecard sections in text output, got %q", scorecardText)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
var detailsOut bytes.Buffer
|
|
246
|
+
var detailsErr bytes.Buffer
|
|
247
|
+
if err := Run([]string{"matches", "details", "1529474", "--format", "json"}, &detailsOut, &detailsErr); err != nil {
|
|
248
|
+
t.Fatalf("Run matches details --format json error: %v", err)
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
var detailsPayload map[string]any
|
|
252
|
+
if err := json.Unmarshal(detailsOut.Bytes(), &detailsPayload); err != nil {
|
|
253
|
+
t.Fatalf("decode details JSON output: %v", err)
|
|
254
|
+
}
|
|
255
|
+
items, ok := detailsPayload["items"].([]any)
|
|
256
|
+
if !ok || len(items) == 0 {
|
|
257
|
+
t.Fatalf("expected items in details json output")
|
|
258
|
+
}
|
|
259
|
+
first, ok := items[0].(map[string]any)
|
|
260
|
+
if !ok {
|
|
261
|
+
t.Fatalf("expected first details item object")
|
|
262
|
+
}
|
|
263
|
+
if _, ok := first["dismissal"]; !ok {
|
|
264
|
+
t.Fatalf("expected dismissal field in details json output")
|
|
265
|
+
}
|
|
266
|
+
if _, ok := first["playType"]; !ok {
|
|
267
|
+
t.Fatalf("expected playType field in details json output")
|
|
268
|
+
}
|
|
269
|
+
if _, ok := first["bbbTimestamp"]; !ok {
|
|
270
|
+
t.Fatalf("expected bbbTimestamp field in details json output")
|
|
271
|
+
}
|
|
272
|
+
if _, ok := first["xCoordinate"]; !ok {
|
|
273
|
+
t.Fatalf("expected xCoordinate field in details json output")
|
|
274
|
+
}
|
|
275
|
+
if _, ok := first["yCoordinate"]; !ok {
|
|
276
|
+
t.Fatalf("expected yCoordinate field in details json output")
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
var playsOut bytes.Buffer
|
|
280
|
+
var playsErr bytes.Buffer
|
|
281
|
+
if err := Run([]string{"matches", "plays", "1529474", "--format", "text"}, &playsOut, &playsErr); err != nil {
|
|
282
|
+
t.Fatalf("Run matches plays --format text error: %v", err)
|
|
283
|
+
}
|
|
284
|
+
if !strings.Contains(playsOut.String(), "Amanullah to Fazal Haq Shaheen, 1 run") {
|
|
285
|
+
t.Fatalf("expected plays text output to include normalized short text, got %q", playsOut.String())
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
var situationOut bytes.Buffer
|
|
289
|
+
var situationErr bytes.Buffer
|
|
290
|
+
if err := Run([]string{"matches", "situation", "1529474", "--format", "json"}, &situationOut, &situationErr); err != nil {
|
|
291
|
+
t.Fatalf("Run matches situation --format json error: %v", err)
|
|
292
|
+
}
|
|
293
|
+
var situationPayload map[string]any
|
|
294
|
+
if err := json.Unmarshal(situationOut.Bytes(), &situationPayload); err != nil {
|
|
295
|
+
t.Fatalf("decode situation JSON output: %v", err)
|
|
296
|
+
}
|
|
297
|
+
if situationPayload["kind"] != string(cricinfo.EntityMatchSituation) {
|
|
298
|
+
t.Fatalf("expected kind %q in situation output, got %#v", cricinfo.EntityMatchSituation, situationPayload["kind"])
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
var inningsOut bytes.Buffer
|
|
302
|
+
var inningsErr bytes.Buffer
|
|
303
|
+
if err := Run([]string{"matches", "innings", "1529474", "--team", "BOOST", "--format", "text"}, &inningsOut, &inningsErr); err != nil {
|
|
304
|
+
t.Fatalf("Run matches innings --format text error: %v", err)
|
|
305
|
+
}
|
|
306
|
+
inningsText := inningsOut.String()
|
|
307
|
+
if !strings.Contains(strings.ToLower(inningsText), "innings 1/3") {
|
|
308
|
+
t.Fatalf("expected innings heading in text output, got %q", inningsText)
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
var partnershipsOut bytes.Buffer
|
|
312
|
+
var partnershipsErr bytes.Buffer
|
|
313
|
+
if err := Run([]string{"matches", "partnerships", "1529474", "--team", "BOOST", "--innings", "1", "--period", "1", "--format", "json"}, &partnershipsOut, &partnershipsErr); err != nil {
|
|
314
|
+
t.Fatalf("Run matches partnerships --format json error: %v", err)
|
|
315
|
+
}
|
|
316
|
+
var partnershipsPayload map[string]any
|
|
317
|
+
if err := json.Unmarshal(partnershipsOut.Bytes(), &partnershipsPayload); err != nil {
|
|
318
|
+
t.Fatalf("decode partnerships JSON output: %v", err)
|
|
319
|
+
}
|
|
320
|
+
if partnershipsPayload["kind"] != string(cricinfo.EntityPartnership) {
|
|
321
|
+
t.Fatalf("expected kind %q in partnerships output, got %#v", cricinfo.EntityPartnership, partnershipsPayload["kind"])
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
var fowOut bytes.Buffer
|
|
325
|
+
var fowErr bytes.Buffer
|
|
326
|
+
if err := Run([]string{"matches", "fow", "1529474", "--team", "BOOST", "--innings", "1", "--period", "1", "--format", "text"}, &fowOut, &fowErr); err != nil {
|
|
327
|
+
t.Fatalf("Run matches fow --format text error: %v", err)
|
|
328
|
+
}
|
|
329
|
+
if !strings.Contains(fowOut.String(), "Wicket 1") && !strings.Contains(fowOut.String(), "wicket 1") {
|
|
330
|
+
t.Fatalf("expected wicket summary in fow output, got %q", fowOut.String())
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
var deliveriesOut bytes.Buffer
|
|
334
|
+
var deliveriesErr bytes.Buffer
|
|
335
|
+
if err := Run([]string{"matches", "deliveries", "1529474", "--team", "BOOST", "--innings", "1", "--period", "1", "--format", "text"}, &deliveriesOut, &deliveriesErr); err != nil {
|
|
336
|
+
t.Fatalf("Run matches deliveries --format text error: %v", err)
|
|
337
|
+
}
|
|
338
|
+
deliveriesText := deliveriesOut.String()
|
|
339
|
+
if !strings.Contains(deliveriesText, "Over Timeline") || !strings.Contains(deliveriesText, "Wicket Timeline") {
|
|
340
|
+
t.Fatalf("expected deliveries timeline sections in text output, got %q", deliveriesText)
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
func TestMatchesHelpIncludesDrillDownHints(t *testing.T) {
|
|
345
|
+
var out bytes.Buffer
|
|
346
|
+
var errBuf bytes.Buffer
|
|
347
|
+
|
|
348
|
+
if err := Run([]string{"matches", "status", "--help"}, &out, &errBuf); err != nil {
|
|
349
|
+
t.Fatalf("Run matches status --help error: %v", err)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
help := out.String()
|
|
353
|
+
if !strings.Contains(help, "matches scorecard") {
|
|
354
|
+
t.Fatalf("expected scorecard drill-down hint in help output, got %q", help)
|
|
355
|
+
}
|
|
356
|
+
if !strings.Contains(help, "matches innings") {
|
|
357
|
+
t.Fatalf("expected innings drill-down hint in help output, got %q", help)
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
func TestMatchesInningsDepthSelectorsAndRequiredFlags(t *testing.T) {
|
|
362
|
+
service := &fakeMatchService{
|
|
363
|
+
inningsResult: cricinfo.NewListResult(cricinfo.EntityInnings, []any{
|
|
364
|
+
cricinfo.Innings{InningsNumber: 1, Period: 2, TeamName: "BOOST"},
|
|
365
|
+
}),
|
|
366
|
+
partnerships: cricinfo.NewListResult(cricinfo.EntityPartnership, []any{
|
|
367
|
+
cricinfo.Partnership{InningsID: "1", Period: "2", Runs: 32, Overs: 5.5},
|
|
368
|
+
}),
|
|
369
|
+
fowResult: cricinfo.NewListResult(cricinfo.EntityFallOfWicket, []any{
|
|
370
|
+
cricinfo.FallOfWicket{InningsID: "1", Period: "2", WicketNumber: 1},
|
|
371
|
+
}),
|
|
372
|
+
deliveries: cricinfo.NewDataResult(cricinfo.EntityInnings, cricinfo.Innings{InningsNumber: 1, Period: 2}),
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
originalFactory := newMatchService
|
|
376
|
+
newMatchService = func() (matchCommandService, error) { return service, nil }
|
|
377
|
+
defer func() {
|
|
378
|
+
newMatchService = originalFactory
|
|
379
|
+
}()
|
|
380
|
+
|
|
381
|
+
var out bytes.Buffer
|
|
382
|
+
var errBuf bytes.Buffer
|
|
383
|
+
if err := Run([]string{"matches", "partnerships", "1529474", "--team", "Boost Region", "--innings", "1", "--period", "2", "--format", "json"}, &out, &errBuf); err != nil {
|
|
384
|
+
t.Fatalf("Run matches partnerships with selectors error: %v", err)
|
|
385
|
+
}
|
|
386
|
+
if len(service.partnershipsOpts) != 1 {
|
|
387
|
+
t.Fatalf("expected partnerships opts capture, got %d", len(service.partnershipsOpts))
|
|
388
|
+
}
|
|
389
|
+
if service.partnershipsOpts[0].TeamQuery != "Boost Region" || service.partnershipsOpts[0].Innings != 1 || service.partnershipsOpts[0].Period != 2 {
|
|
390
|
+
t.Fatalf("unexpected partnerships options: %+v", service.partnershipsOpts[0])
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
var missingPeriodOut bytes.Buffer
|
|
394
|
+
var missingPeriodErr bytes.Buffer
|
|
395
|
+
err := Run([]string{"matches", "partnerships", "1529474", "--team", "Boost Region", "--innings", "1"}, &missingPeriodOut, &missingPeriodErr)
|
|
396
|
+
if err == nil || !strings.Contains(err.Error(), "--period is required") {
|
|
397
|
+
t.Fatalf("expected --period required error, got %v", err)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
var missingTeamOut bytes.Buffer
|
|
401
|
+
var missingTeamErr bytes.Buffer
|
|
402
|
+
err = Run([]string{"matches", "fow", "1529474", "--innings", "1", "--period", "2"}, &missingTeamOut, &missingTeamErr)
|
|
403
|
+
if err == nil || !strings.Contains(err.Error(), "--team is required") {
|
|
404
|
+
t.Fatalf("expected --team required error, got %v", err)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
var missingInningsOut bytes.Buffer
|
|
408
|
+
var missingInningsErr bytes.Buffer
|
|
409
|
+
err = Run([]string{"matches", "deliveries", "1529474", "--team", "Boost Region", "--period", "2"}, &missingInningsOut, &missingInningsErr)
|
|
410
|
+
if err == nil || !strings.Contains(err.Error(), "--innings is required") {
|
|
411
|
+
t.Fatalf("expected --innings required error, got %v", err)
|
|
412
|
+
}
|
|
413
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
package cli
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"fmt"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/amxv/cricinfo-cli/internal/cricinfo"
|
|
9
|
+
"github.com/spf13/cobra"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
type playerCommandService interface {
|
|
13
|
+
Close() error
|
|
14
|
+
Search(ctx context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
15
|
+
Profile(ctx context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
16
|
+
News(ctx context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
17
|
+
Stats(ctx context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
18
|
+
Career(ctx context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
19
|
+
MatchStats(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
20
|
+
Innings(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
21
|
+
Dismissals(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
22
|
+
Deliveries(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
23
|
+
Bowling(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
24
|
+
Batting(ctx context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type playerRuntimeOptions struct {
|
|
28
|
+
leagueID string
|
|
29
|
+
limit int
|
|
30
|
+
match string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
var newPlayerService = func() (playerCommandService, error) {
|
|
34
|
+
return cricinfo.NewPlayerService(cricinfo.PlayerServiceConfig{})
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
func newPlayersCommand(global *globalOptions) *cobra.Command {
|
|
38
|
+
opts := &playerRuntimeOptions{}
|
|
39
|
+
|
|
40
|
+
cmd := &cobra.Command{
|
|
41
|
+
Use: "players",
|
|
42
|
+
Short: "Player discovery with profile, news, and grouped career statistics.",
|
|
43
|
+
Long: strings.Join([]string{
|
|
44
|
+
"Resolve players by ID/ref/alias and inspect normalized profile, related news, and grouped statistics.",
|
|
45
|
+
"",
|
|
46
|
+
"Next steps:",
|
|
47
|
+
" cricinfo players search <query>",
|
|
48
|
+
" cricinfo players profile <player>",
|
|
49
|
+
" cricinfo players news <player>",
|
|
50
|
+
" cricinfo players stats <player>",
|
|
51
|
+
" cricinfo players career <player>",
|
|
52
|
+
" cricinfo players match-stats <player> --match <match>",
|
|
53
|
+
" cricinfo players innings <player> --match <match>",
|
|
54
|
+
" cricinfo players dismissals <player> --match <match>",
|
|
55
|
+
" cricinfo players deliveries <player> --match <match>",
|
|
56
|
+
" cricinfo players bowling <player> --match <match>",
|
|
57
|
+
" cricinfo players batting <player> --match <match>",
|
|
58
|
+
}, "\n"),
|
|
59
|
+
Args: cobra.NoArgs,
|
|
60
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
61
|
+
return cmd.Help()
|
|
62
|
+
},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
cmd.PersistentFlags().StringVar(&opts.leagueID, "league", "", "Preferred league ID for resolver context")
|
|
66
|
+
cmd.PersistentFlags().IntVar(&opts.limit, "limit", 10, "Maximum number of results to return for search and news")
|
|
67
|
+
|
|
68
|
+
searchCmd := &cobra.Command{
|
|
69
|
+
Use: "search <query>",
|
|
70
|
+
Short: "Search players by ID, ref, or alias",
|
|
71
|
+
Args: cobra.MinimumNArgs(1),
|
|
72
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
73
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
74
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
75
|
+
return service.Search(ctx, query, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID, Limit: opts.limit})
|
|
76
|
+
})
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
profileCmd := &cobra.Command{
|
|
81
|
+
Use: "profile <player>",
|
|
82
|
+
Short: "Show one normalized player profile",
|
|
83
|
+
Args: cobra.MinimumNArgs(1),
|
|
84
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
85
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
86
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
87
|
+
return service.Profile(ctx, query, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
88
|
+
})
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
newsCmd := &cobra.Command{
|
|
93
|
+
Use: "news <player>",
|
|
94
|
+
Short: "Show normalized related news articles for one player",
|
|
95
|
+
Args: cobra.MinimumNArgs(1),
|
|
96
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
97
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
98
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
99
|
+
return service.News(ctx, query, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID, Limit: opts.limit})
|
|
100
|
+
})
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
statsCmd := &cobra.Command{
|
|
105
|
+
Use: "stats <player>",
|
|
106
|
+
Short: "Show grouped global player statistics",
|
|
107
|
+
Args: cobra.MinimumNArgs(1),
|
|
108
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
109
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
110
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
111
|
+
return service.Stats(ctx, query, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
112
|
+
})
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
careerCmd := &cobra.Command{
|
|
117
|
+
Use: "career <player>",
|
|
118
|
+
Short: "Show grouped career statistics for one player",
|
|
119
|
+
Args: cobra.MinimumNArgs(1),
|
|
120
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
121
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
122
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
123
|
+
return service.Career(ctx, query, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
124
|
+
})
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
matchStatsCmd := &cobra.Command{
|
|
129
|
+
Use: "match-stats <player>",
|
|
130
|
+
Short: "Show player-in-match batting/bowling/fielding statistics",
|
|
131
|
+
Args: cobra.MinimumNArgs(1),
|
|
132
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
133
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
134
|
+
return fmt.Errorf("--match is required")
|
|
135
|
+
}
|
|
136
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
137
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
138
|
+
return service.MatchStats(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
139
|
+
})
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
matchStatsCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
143
|
+
|
|
144
|
+
inningsCmd := &cobra.Command{
|
|
145
|
+
Use: "innings <player>",
|
|
146
|
+
Short: "Show player innings splits from roster-player linescores",
|
|
147
|
+
Args: cobra.MinimumNArgs(1),
|
|
148
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
149
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
150
|
+
return fmt.Errorf("--match is required")
|
|
151
|
+
}
|
|
152
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
153
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
154
|
+
return service.Innings(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
155
|
+
})
|
|
156
|
+
},
|
|
157
|
+
}
|
|
158
|
+
inningsCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
159
|
+
|
|
160
|
+
dismissalsCmd := &cobra.Command{
|
|
161
|
+
Use: "dismissals <player>",
|
|
162
|
+
Short: "Show dismissal and wicket views for a player in one match",
|
|
163
|
+
Args: cobra.MinimumNArgs(1),
|
|
164
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
165
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
166
|
+
return fmt.Errorf("--match is required")
|
|
167
|
+
}
|
|
168
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
169
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
170
|
+
return service.Dismissals(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
171
|
+
})
|
|
172
|
+
},
|
|
173
|
+
}
|
|
174
|
+
dismissalsCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
175
|
+
|
|
176
|
+
deliveriesCmd := &cobra.Command{
|
|
177
|
+
Use: "deliveries <player>",
|
|
178
|
+
Short: "Show player delivery events (including coordinate-aware shots/balls) for one match",
|
|
179
|
+
Args: cobra.MinimumNArgs(1),
|
|
180
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
181
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
182
|
+
return fmt.Errorf("--match is required")
|
|
183
|
+
}
|
|
184
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
185
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
186
|
+
return service.Deliveries(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
187
|
+
})
|
|
188
|
+
},
|
|
189
|
+
}
|
|
190
|
+
deliveriesCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
191
|
+
|
|
192
|
+
bowlingCmd := &cobra.Command{
|
|
193
|
+
Use: "bowling <player>",
|
|
194
|
+
Short: "Show player-in-match bowling split categories",
|
|
195
|
+
Args: cobra.MinimumNArgs(1),
|
|
196
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
197
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
198
|
+
return fmt.Errorf("--match is required")
|
|
199
|
+
}
|
|
200
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
201
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
202
|
+
return service.Bowling(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
203
|
+
})
|
|
204
|
+
},
|
|
205
|
+
}
|
|
206
|
+
bowlingCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
207
|
+
|
|
208
|
+
battingCmd := &cobra.Command{
|
|
209
|
+
Use: "batting <player>",
|
|
210
|
+
Short: "Show player-in-match batting split categories",
|
|
211
|
+
Args: cobra.MinimumNArgs(1),
|
|
212
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
213
|
+
if strings.TrimSpace(opts.match) == "" {
|
|
214
|
+
return fmt.Errorf("--match is required")
|
|
215
|
+
}
|
|
216
|
+
playerQuery := strings.TrimSpace(strings.Join(args, " "))
|
|
217
|
+
return runPlayerCommand(cmd, global, func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error) {
|
|
218
|
+
return service.Batting(ctx, playerQuery, opts.match, cricinfo.PlayerLookupOptions{LeagueID: opts.leagueID})
|
|
219
|
+
})
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
battingCmd.Flags().StringVar(&opts.match, "match", "", "Required: match ID/ref/alias")
|
|
223
|
+
|
|
224
|
+
cmd.AddCommand(
|
|
225
|
+
searchCmd,
|
|
226
|
+
profileCmd,
|
|
227
|
+
newsCmd,
|
|
228
|
+
statsCmd,
|
|
229
|
+
careerCmd,
|
|
230
|
+
matchStatsCmd,
|
|
231
|
+
inningsCmd,
|
|
232
|
+
dismissalsCmd,
|
|
233
|
+
deliveriesCmd,
|
|
234
|
+
bowlingCmd,
|
|
235
|
+
battingCmd,
|
|
236
|
+
)
|
|
237
|
+
return cmd
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
func runPlayerCommand(
|
|
241
|
+
cmd *cobra.Command,
|
|
242
|
+
global *globalOptions,
|
|
243
|
+
fn func(ctx context.Context, service playerCommandService) (cricinfo.NormalizedResult, error),
|
|
244
|
+
) error {
|
|
245
|
+
service, err := newPlayerService()
|
|
246
|
+
if err != nil {
|
|
247
|
+
return err
|
|
248
|
+
}
|
|
249
|
+
defer func() {
|
|
250
|
+
_ = service.Close()
|
|
251
|
+
}()
|
|
252
|
+
|
|
253
|
+
result, err := fn(cmd.Context(), service)
|
|
254
|
+
if err != nil {
|
|
255
|
+
return err
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return cricinfo.Render(cmd.OutOrStdout(), result, cricinfo.RenderOptions{
|
|
259
|
+
Format: global.format,
|
|
260
|
+
Verbose: global.verbose,
|
|
261
|
+
AllFields: global.allFields,
|
|
262
|
+
})
|
|
263
|
+
}
|