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,384 @@
|
|
|
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 fakePlayerService struct {
|
|
14
|
+
searchResult cricinfo.NormalizedResult
|
|
15
|
+
profileResult cricinfo.NormalizedResult
|
|
16
|
+
newsResult cricinfo.NormalizedResult
|
|
17
|
+
statsResult cricinfo.NormalizedResult
|
|
18
|
+
careerResult cricinfo.NormalizedResult
|
|
19
|
+
matchResult cricinfo.NormalizedResult
|
|
20
|
+
inningsResult cricinfo.NormalizedResult
|
|
21
|
+
dismissals cricinfo.NormalizedResult
|
|
22
|
+
deliveries cricinfo.NormalizedResult
|
|
23
|
+
bowlingResult cricinfo.NormalizedResult
|
|
24
|
+
battingResult cricinfo.NormalizedResult
|
|
25
|
+
|
|
26
|
+
searchQueries []string
|
|
27
|
+
profileQueries []string
|
|
28
|
+
newsQueries []string
|
|
29
|
+
statsQueries []string
|
|
30
|
+
careerQueries []string
|
|
31
|
+
matchQueries []string
|
|
32
|
+
matchRefs []string
|
|
33
|
+
inningsQueries []string
|
|
34
|
+
inningsRefs []string
|
|
35
|
+
dismissQueries []string
|
|
36
|
+
dismissRefs []string
|
|
37
|
+
deliveryQueries []string
|
|
38
|
+
deliveryRefs []string
|
|
39
|
+
bowlingQueries []string
|
|
40
|
+
bowlingRefs []string
|
|
41
|
+
battingQueries []string
|
|
42
|
+
battingRefs []string
|
|
43
|
+
searchOpts []cricinfo.PlayerLookupOptions
|
|
44
|
+
profileOpts []cricinfo.PlayerLookupOptions
|
|
45
|
+
newsOpts []cricinfo.PlayerLookupOptions
|
|
46
|
+
statsOpts []cricinfo.PlayerLookupOptions
|
|
47
|
+
careerOpts []cricinfo.PlayerLookupOptions
|
|
48
|
+
matchOpts []cricinfo.PlayerLookupOptions
|
|
49
|
+
inningsOpts []cricinfo.PlayerLookupOptions
|
|
50
|
+
dismissOpts []cricinfo.PlayerLookupOptions
|
|
51
|
+
deliveryOpts []cricinfo.PlayerLookupOptions
|
|
52
|
+
bowlingOpts []cricinfo.PlayerLookupOptions
|
|
53
|
+
battingOpts []cricinfo.PlayerLookupOptions
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func (f *fakePlayerService) Close() error { return nil }
|
|
57
|
+
|
|
58
|
+
func (f *fakePlayerService) Search(_ context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
59
|
+
f.searchQueries = append(f.searchQueries, query)
|
|
60
|
+
f.searchOpts = append(f.searchOpts, opts)
|
|
61
|
+
return f.searchResult, nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func (f *fakePlayerService) Profile(_ context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
65
|
+
f.profileQueries = append(f.profileQueries, query)
|
|
66
|
+
f.profileOpts = append(f.profileOpts, opts)
|
|
67
|
+
return f.profileResult, nil
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
func (f *fakePlayerService) News(_ context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
71
|
+
f.newsQueries = append(f.newsQueries, query)
|
|
72
|
+
f.newsOpts = append(f.newsOpts, opts)
|
|
73
|
+
return f.newsResult, nil
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
func (f *fakePlayerService) Stats(_ context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
77
|
+
f.statsQueries = append(f.statsQueries, query)
|
|
78
|
+
f.statsOpts = append(f.statsOpts, opts)
|
|
79
|
+
return f.statsResult, nil
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func (f *fakePlayerService) Career(_ context.Context, query string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
83
|
+
f.careerQueries = append(f.careerQueries, query)
|
|
84
|
+
f.careerOpts = append(f.careerOpts, opts)
|
|
85
|
+
return f.careerResult, nil
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func (f *fakePlayerService) MatchStats(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
89
|
+
f.matchQueries = append(f.matchQueries, playerQuery)
|
|
90
|
+
f.matchRefs = append(f.matchRefs, matchQuery)
|
|
91
|
+
f.matchOpts = append(f.matchOpts, opts)
|
|
92
|
+
return f.matchResult, nil
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
func (f *fakePlayerService) Innings(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
96
|
+
f.inningsQueries = append(f.inningsQueries, playerQuery)
|
|
97
|
+
f.inningsRefs = append(f.inningsRefs, matchQuery)
|
|
98
|
+
f.inningsOpts = append(f.inningsOpts, opts)
|
|
99
|
+
return f.inningsResult, nil
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
func (f *fakePlayerService) Dismissals(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
103
|
+
f.dismissQueries = append(f.dismissQueries, playerQuery)
|
|
104
|
+
f.dismissRefs = append(f.dismissRefs, matchQuery)
|
|
105
|
+
f.dismissOpts = append(f.dismissOpts, opts)
|
|
106
|
+
return f.dismissals, nil
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
func (f *fakePlayerService) Deliveries(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
110
|
+
f.deliveryQueries = append(f.deliveryQueries, playerQuery)
|
|
111
|
+
f.deliveryRefs = append(f.deliveryRefs, matchQuery)
|
|
112
|
+
f.deliveryOpts = append(f.deliveryOpts, opts)
|
|
113
|
+
return f.deliveries, nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
func (f *fakePlayerService) Bowling(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
117
|
+
f.bowlingQueries = append(f.bowlingQueries, playerQuery)
|
|
118
|
+
f.bowlingRefs = append(f.bowlingRefs, matchQuery)
|
|
119
|
+
f.bowlingOpts = append(f.bowlingOpts, opts)
|
|
120
|
+
return f.bowlingResult, nil
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
func (f *fakePlayerService) Batting(_ context.Context, playerQuery, matchQuery string, opts cricinfo.PlayerLookupOptions) (cricinfo.NormalizedResult, error) {
|
|
124
|
+
f.battingQueries = append(f.battingQueries, playerQuery)
|
|
125
|
+
f.battingRefs = append(f.battingRefs, matchQuery)
|
|
126
|
+
f.battingOpts = append(f.battingOpts, opts)
|
|
127
|
+
return f.battingResult, nil
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
func TestPlayersCommandsResolveNamesAndRenderStableJSON(t *testing.T) {
|
|
131
|
+
service := &fakePlayerService{
|
|
132
|
+
searchResult: cricinfo.NewListResult(cricinfo.EntityPlayer, []any{
|
|
133
|
+
cricinfo.Player{ID: "253802", DisplayName: "Virat Kohli"},
|
|
134
|
+
}),
|
|
135
|
+
profileResult: cricinfo.NewDataResult(cricinfo.EntityPlayer, cricinfo.Player{
|
|
136
|
+
ID: "253802",
|
|
137
|
+
DisplayName: "Virat Kohli",
|
|
138
|
+
FullName: "Virat Kohli",
|
|
139
|
+
Position: "Top Order Batter",
|
|
140
|
+
NewsRef: "http://core.espnuk.org/v2/sports/cricket/athletes/253802/news",
|
|
141
|
+
Team: &cricinfo.PlayerAffiliation{ID: "6", Ref: "http://core.espnuk.org/v2/sports/cricket/teams/6"},
|
|
142
|
+
MajorTeams: []cricinfo.PlayerAffiliation{
|
|
143
|
+
{ID: "6", Ref: "http://core.espnuk.org/v2/sports/cricket/teams/6"},
|
|
144
|
+
},
|
|
145
|
+
}),
|
|
146
|
+
newsResult: cricinfo.NewListResult(cricinfo.EntityNewsArticle, []any{
|
|
147
|
+
cricinfo.NewsArticle{ID: "1530499", Headline: "Players with a hat-trick of POTM awards feat. Kohli, Kallis, Sehwag and more", Published: "2026-04-04T00:00Z"},
|
|
148
|
+
}),
|
|
149
|
+
statsResult: cricinfo.NewDataResult(cricinfo.EntityPlayerStats, cricinfo.PlayerStatistics{
|
|
150
|
+
PlayerID: "253802",
|
|
151
|
+
Name: "Total",
|
|
152
|
+
Categories: []cricinfo.StatCategory{
|
|
153
|
+
{
|
|
154
|
+
Name: "general",
|
|
155
|
+
DisplayName: "General",
|
|
156
|
+
Stats: []cricinfo.StatValue{
|
|
157
|
+
{Name: "matches", DisplayName: "Matches", DisplayValue: "302"},
|
|
158
|
+
},
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
}),
|
|
162
|
+
careerResult: cricinfo.NewDataResult(cricinfo.EntityPlayerStats, cricinfo.PlayerStatistics{
|
|
163
|
+
PlayerID: "253802",
|
|
164
|
+
Name: "Total",
|
|
165
|
+
}),
|
|
166
|
+
matchResult: cricinfo.NewDataResult(cricinfo.EntityPlayerMatch, cricinfo.PlayerMatch{
|
|
167
|
+
PlayerID: "253802",
|
|
168
|
+
PlayerName: "Virat Kohli",
|
|
169
|
+
MatchID: "1529474",
|
|
170
|
+
Summary: cricinfo.PlayerMatchSummary{
|
|
171
|
+
BallsFaced: 60,
|
|
172
|
+
StrikeRate: 55,
|
|
173
|
+
DismissalName: "caught",
|
|
174
|
+
DismissalCard: "c",
|
|
175
|
+
},
|
|
176
|
+
}),
|
|
177
|
+
inningsResult: cricinfo.NewListResult(cricinfo.EntityPlayerInnings, []any{
|
|
178
|
+
cricinfo.PlayerInnings{PlayerID: "253802", InningsNumber: 1, Period: 1},
|
|
179
|
+
}),
|
|
180
|
+
dismissals: cricinfo.NewListResult(cricinfo.EntityPlayerDismissal, []any{
|
|
181
|
+
cricinfo.PlayerDismissal{PlayerID: "253802", DetailRef: "http://core.espnuk.org/v2/sports/cricket/leagues/19138/events/1529474/competitions/1529474/details/52545007", DismissalCard: "c"},
|
|
182
|
+
}),
|
|
183
|
+
deliveries: cricinfo.NewListResult(cricinfo.EntityPlayerDelivery, []any{
|
|
184
|
+
cricinfo.DeliveryEvent{ID: "110", BatsmanPlayerID: "253802", BowlerPlayerID: "976585", Involvement: []string{"batting"}},
|
|
185
|
+
}),
|
|
186
|
+
bowlingResult: cricinfo.NewDataResult(cricinfo.EntityPlayerMatch, cricinfo.PlayerMatch{
|
|
187
|
+
PlayerID: "253802",
|
|
188
|
+
MatchID: "1529474",
|
|
189
|
+
Summary: cricinfo.PlayerMatchSummary{Dots: 20, EconomyRate: 3.5},
|
|
190
|
+
}),
|
|
191
|
+
battingResult: cricinfo.NewDataResult(cricinfo.EntityPlayerMatch, cricinfo.PlayerMatch{
|
|
192
|
+
PlayerID: "253802",
|
|
193
|
+
MatchID: "1529474",
|
|
194
|
+
Summary: cricinfo.PlayerMatchSummary{BallsFaced: 60, StrikeRate: 55},
|
|
195
|
+
}),
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
originalFactory := newPlayerService
|
|
199
|
+
newPlayerService = func() (playerCommandService, error) { return service, nil }
|
|
200
|
+
defer func() {
|
|
201
|
+
newPlayerService = originalFactory
|
|
202
|
+
}()
|
|
203
|
+
|
|
204
|
+
var searchOut bytes.Buffer
|
|
205
|
+
var searchErr bytes.Buffer
|
|
206
|
+
if err := Run([]string{"players", "search", "Virat", "Kohli", "--format", "json", "--limit", "5"}, &searchOut, &searchErr); err != nil {
|
|
207
|
+
t.Fatalf("Run players search error: %v", err)
|
|
208
|
+
}
|
|
209
|
+
searchPayload := decodeCLIJSONMap(t, searchOut.Bytes())
|
|
210
|
+
if searchPayload["kind"] != string(cricinfo.EntityPlayer) {
|
|
211
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityPlayer, searchPayload["kind"])
|
|
212
|
+
}
|
|
213
|
+
if len(service.searchQueries) != 1 || service.searchQueries[0] != "Virat Kohli" {
|
|
214
|
+
t.Fatalf("expected joined search query, got %+v", service.searchQueries)
|
|
215
|
+
}
|
|
216
|
+
if service.searchOpts[0].Limit != 5 {
|
|
217
|
+
t.Fatalf("expected search limit 5, got %+v", service.searchOpts)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
var profileOut bytes.Buffer
|
|
221
|
+
var profileErr bytes.Buffer
|
|
222
|
+
if err := Run([]string{"players", "profile", "Virat", "Kohli", "--format", "json"}, &profileOut, &profileErr); err != nil {
|
|
223
|
+
t.Fatalf("Run players profile error: %v", err)
|
|
224
|
+
}
|
|
225
|
+
profilePayload := decodeCLIJSONMap(t, profileOut.Bytes())
|
|
226
|
+
if profilePayload["kind"] != string(cricinfo.EntityPlayer) {
|
|
227
|
+
t.Fatalf("expected player profile kind, got %#v", profilePayload["kind"])
|
|
228
|
+
}
|
|
229
|
+
profileData, ok := profilePayload["data"].(map[string]any)
|
|
230
|
+
if !ok {
|
|
231
|
+
t.Fatalf("expected player profile object")
|
|
232
|
+
}
|
|
233
|
+
if profileData["displayName"] != "Virat Kohli" {
|
|
234
|
+
t.Fatalf("expected displayName in player profile, got %#v", profileData["displayName"])
|
|
235
|
+
}
|
|
236
|
+
if _, ok := profileData["majorTeams"]; !ok {
|
|
237
|
+
t.Fatalf("expected majorTeams in player profile output")
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
var newsOut bytes.Buffer
|
|
241
|
+
var newsErr bytes.Buffer
|
|
242
|
+
if err := Run([]string{"players", "news", "Virat", "Kohli", "--format", "text"}, &newsOut, &newsErr); err != nil {
|
|
243
|
+
t.Fatalf("Run players news error: %v", err)
|
|
244
|
+
}
|
|
245
|
+
if !strings.Contains(newsOut.String(), "POTM awards") {
|
|
246
|
+
t.Fatalf("expected news headline in text output, got %q", newsOut.String())
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
var statsOut bytes.Buffer
|
|
250
|
+
var statsErr bytes.Buffer
|
|
251
|
+
if err := Run([]string{"players", "stats", "Virat", "Kohli", "--format", "json"}, &statsOut, &statsErr); err != nil {
|
|
252
|
+
t.Fatalf("Run players stats error: %v", err)
|
|
253
|
+
}
|
|
254
|
+
statsPayload := decodeCLIJSONMap(t, statsOut.Bytes())
|
|
255
|
+
if statsPayload["kind"] != string(cricinfo.EntityPlayerStats) {
|
|
256
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityPlayerStats, statsPayload["kind"])
|
|
257
|
+
}
|
|
258
|
+
statsData, ok := statsPayload["data"].(map[string]any)
|
|
259
|
+
if !ok {
|
|
260
|
+
t.Fatalf("expected player statistics object")
|
|
261
|
+
}
|
|
262
|
+
categories, ok := statsData["categories"].([]any)
|
|
263
|
+
if !ok || len(categories) == 0 {
|
|
264
|
+
t.Fatalf("expected grouped categories in player statistics output")
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
var careerOut bytes.Buffer
|
|
268
|
+
var careerErr bytes.Buffer
|
|
269
|
+
if err := Run([]string{"players", "career", "Virat", "Kohli", "--format", "json"}, &careerOut, &careerErr); err != nil {
|
|
270
|
+
t.Fatalf("Run players career error: %v", err)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
var matchOut bytes.Buffer
|
|
274
|
+
var matchErr bytes.Buffer
|
|
275
|
+
if err := Run([]string{"players", "match-stats", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &matchOut, &matchErr); err != nil {
|
|
276
|
+
t.Fatalf("Run players match-stats error: %v", err)
|
|
277
|
+
}
|
|
278
|
+
matchPayload := decodeCLIJSONMap(t, matchOut.Bytes())
|
|
279
|
+
if matchPayload["kind"] != string(cricinfo.EntityPlayerMatch) {
|
|
280
|
+
t.Fatalf("expected kind %q, got %#v", cricinfo.EntityPlayerMatch, matchPayload["kind"])
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
var inningsOut bytes.Buffer
|
|
284
|
+
var inningsErr bytes.Buffer
|
|
285
|
+
if err := Run([]string{"players", "innings", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &inningsOut, &inningsErr); err != nil {
|
|
286
|
+
t.Fatalf("Run players innings error: %v", err)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
var dismissOut bytes.Buffer
|
|
290
|
+
var dismissErr bytes.Buffer
|
|
291
|
+
if err := Run([]string{"players", "dismissals", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &dismissOut, &dismissErr); err != nil {
|
|
292
|
+
t.Fatalf("Run players dismissals error: %v", err)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
var deliveriesOut bytes.Buffer
|
|
296
|
+
var deliveriesErr bytes.Buffer
|
|
297
|
+
if err := Run([]string{"players", "deliveries", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &deliveriesOut, &deliveriesErr); err != nil {
|
|
298
|
+
t.Fatalf("Run players deliveries error: %v", err)
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
var bowlingOut bytes.Buffer
|
|
302
|
+
var bowlingErr bytes.Buffer
|
|
303
|
+
if err := Run([]string{"players", "bowling", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &bowlingOut, &bowlingErr); err != nil {
|
|
304
|
+
t.Fatalf("Run players bowling error: %v", err)
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
var battingOut bytes.Buffer
|
|
308
|
+
var battingErr bytes.Buffer
|
|
309
|
+
if err := Run([]string{"players", "batting", "Virat", "Kohli", "--match", "1529474", "--format", "json"}, &battingOut, &battingErr); err != nil {
|
|
310
|
+
t.Fatalf("Run players batting error: %v", err)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if service.profileQueries[0] != "Virat Kohli" || service.newsQueries[0] != "Virat Kohli" || service.statsQueries[0] != "Virat Kohli" || service.careerQueries[0] != "Virat Kohli" {
|
|
314
|
+
t.Fatalf("expected all player commands to preserve joined alias query")
|
|
315
|
+
}
|
|
316
|
+
if len(service.matchRefs) != 1 || service.matchRefs[0] != "1529474" {
|
|
317
|
+
t.Fatalf("expected match context to be passed through for player match commands")
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
func TestPlayersHelpListsPhase10Commands(t *testing.T) {
|
|
322
|
+
t.Parallel()
|
|
323
|
+
|
|
324
|
+
var out bytes.Buffer
|
|
325
|
+
var errBuf bytes.Buffer
|
|
326
|
+
if err := Run([]string{"players", "--help"}, &out, &errBuf); err != nil {
|
|
327
|
+
t.Fatalf("Run players --help error: %v", err)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
helpText := out.String()
|
|
331
|
+
for _, snippet := range []string{
|
|
332
|
+
"players search", "players profile", "players news", "players stats", "players career",
|
|
333
|
+
"players match-stats", "players innings", "players dismissals", "players deliveries", "players bowling", "players batting",
|
|
334
|
+
} {
|
|
335
|
+
if !strings.Contains(helpText, snippet) {
|
|
336
|
+
t.Fatalf("expected help text to include %q, got %q", snippet, helpText)
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
func TestPlayersMatchContextCommandsRequireMatchFlag(t *testing.T) {
|
|
342
|
+
t.Parallel()
|
|
343
|
+
|
|
344
|
+
for _, args := range [][]string{
|
|
345
|
+
{"players", "match-stats", "Virat Kohli"},
|
|
346
|
+
{"players", "innings", "Virat Kohli"},
|
|
347
|
+
{"players", "dismissals", "Virat Kohli"},
|
|
348
|
+
{"players", "deliveries", "Virat Kohli"},
|
|
349
|
+
{"players", "bowling", "Virat Kohli"},
|
|
350
|
+
{"players", "batting", "Virat Kohli"},
|
|
351
|
+
} {
|
|
352
|
+
var out bytes.Buffer
|
|
353
|
+
var errBuf bytes.Buffer
|
|
354
|
+
err := Run(args, &out, &errBuf)
|
|
355
|
+
if err == nil || !strings.Contains(err.Error(), "--match is required") {
|
|
356
|
+
t.Fatalf("expected --match required error for %v, got %v", args, err)
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
func TestPlayersProfileJSONIsStable(t *testing.T) {
|
|
362
|
+
t.Parallel()
|
|
363
|
+
|
|
364
|
+
player := cricinfo.Player{
|
|
365
|
+
ID: "1361257",
|
|
366
|
+
DisplayName: "Fazal Haq Shaheen",
|
|
367
|
+
Styles: []cricinfo.PlayerStyle{
|
|
368
|
+
{Type: "batting", Description: "Left-hand bat", ShortDescription: "Lhb"},
|
|
369
|
+
},
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
var out bytes.Buffer
|
|
373
|
+
if err := cricinfo.Render(&out, cricinfo.NewDataResult(cricinfo.EntityPlayer, player), cricinfo.RenderOptions{Format: "json"}); err != nil {
|
|
374
|
+
t.Fatalf("Render player profile json error: %v", err)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
var payload map[string]any
|
|
378
|
+
if err := json.Unmarshal(out.Bytes(), &payload); err != nil {
|
|
379
|
+
t.Fatalf("decode rendered player profile json: %v", err)
|
|
380
|
+
}
|
|
381
|
+
if payload["kind"] != string(cricinfo.EntityPlayer) {
|
|
382
|
+
t.Fatalf("expected player kind, got %#v", payload["kind"])
|
|
383
|
+
}
|
|
384
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
package cli
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"fmt"
|
|
5
|
+
"io"
|
|
6
|
+
"strings"
|
|
7
|
+
|
|
8
|
+
"github.com/amxv/cricinfo-cli/internal/buildinfo"
|
|
9
|
+
"github.com/spf13/cobra"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
const commandName = "cricinfo"
|
|
13
|
+
|
|
14
|
+
type globalOptions struct {
|
|
15
|
+
format string
|
|
16
|
+
verbose bool
|
|
17
|
+
allFields bool
|
|
18
|
+
version bool
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
func Run(args []string, stdout, stderr io.Writer) error {
|
|
22
|
+
root := newRootCommand(stdout, stderr)
|
|
23
|
+
root.SetArgs(args)
|
|
24
|
+
|
|
25
|
+
if err := root.Execute(); err != nil {
|
|
26
|
+
return normalizeCommandError(err)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return nil
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
func newRootCommand(stdout, stderr io.Writer) *cobra.Command {
|
|
33
|
+
opts := &globalOptions{}
|
|
34
|
+
|
|
35
|
+
root := &cobra.Command{
|
|
36
|
+
Use: commandName,
|
|
37
|
+
Short: "Explore Cricinfo cricket data from the command line.",
|
|
38
|
+
Long: rootLongDescription(),
|
|
39
|
+
SilenceUsage: true,
|
|
40
|
+
SilenceErrors: true,
|
|
41
|
+
DisableAutoGenTag: true,
|
|
42
|
+
DisableSuggestions: true,
|
|
43
|
+
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
|
44
|
+
return validateFormat(opts.format)
|
|
45
|
+
},
|
|
46
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
47
|
+
if opts.version {
|
|
48
|
+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "%s %s\n", commandName, buildinfo.CurrentVersion())
|
|
49
|
+
return nil
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return cmd.Help()
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
root.SetOut(stdout)
|
|
57
|
+
root.SetErr(stderr)
|
|
58
|
+
|
|
59
|
+
root.Flags().BoolVar(&opts.version, "version", false, "Print version information")
|
|
60
|
+
root.PersistentFlags().StringVar(&opts.format, "format", "text", "Output format: text, json, or jsonl")
|
|
61
|
+
root.PersistentFlags().BoolVar(&opts.verbose, "verbose", false, "Show verbose output when available")
|
|
62
|
+
root.PersistentFlags().BoolVar(&opts.allFields, "all-fields", false, "Include long-tail fields in output")
|
|
63
|
+
|
|
64
|
+
root.AddCommand(newMatchesCommand(opts))
|
|
65
|
+
root.AddCommand(newPlayersCommand(opts))
|
|
66
|
+
root.AddCommand(newTeamsCommand(opts))
|
|
67
|
+
root.AddCommand(newLeaguesCommand(opts))
|
|
68
|
+
root.AddCommand(newSeasonsCommand(opts))
|
|
69
|
+
root.AddCommand(newStandingsCommand(opts))
|
|
70
|
+
root.AddCommand(newCompetitionsCommand(opts))
|
|
71
|
+
root.AddCommand(newSearchCommand(opts))
|
|
72
|
+
root.AddCommand(newAnalysisCommand(opts))
|
|
73
|
+
|
|
74
|
+
return root
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
func newPlaceholderGroupCommand(name, description string, nextSteps []string) *cobra.Command {
|
|
78
|
+
cmd := &cobra.Command{
|
|
79
|
+
Use: name,
|
|
80
|
+
Short: description,
|
|
81
|
+
Long: fmt.Sprintf("%s\n\n%s",
|
|
82
|
+
description,
|
|
83
|
+
formatNextSteps(nextSteps),
|
|
84
|
+
),
|
|
85
|
+
Args: cobra.NoArgs,
|
|
86
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
87
|
+
return cmd.Help()
|
|
88
|
+
},
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return cmd
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
func validateFormat(value string) error {
|
|
95
|
+
switch strings.ToLower(strings.TrimSpace(value)) {
|
|
96
|
+
case "text", "json", "jsonl":
|
|
97
|
+
return nil
|
|
98
|
+
default:
|
|
99
|
+
return fmt.Errorf("invalid value %q for --format (expected: text, json, jsonl)", value)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
func normalizeCommandError(err error) error {
|
|
104
|
+
message := strings.TrimSpace(err.Error())
|
|
105
|
+
if message == "" {
|
|
106
|
+
return err
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
firstLine := strings.SplitN(message, "\n", 2)[0]
|
|
110
|
+
|
|
111
|
+
if strings.HasPrefix(firstLine, "unknown command ") || strings.HasPrefix(firstLine, "unknown flag:") {
|
|
112
|
+
return fmt.Errorf("%s (run `%s --help`)", firstLine, commandName)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return fmt.Errorf("%s", firstLine)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
func rootLongDescription() string {
|
|
119
|
+
return strings.Join([]string{
|
|
120
|
+
"Domain-driven Cricinfo CLI for real-time match, player, team, league, season, and analysis workflows.",
|
|
121
|
+
"",
|
|
122
|
+
"Quick drill-down:",
|
|
123
|
+
" cricinfo matches list",
|
|
124
|
+
" cricinfo players profile 1361257",
|
|
125
|
+
" cricinfo teams roster 789643 --match 1529474",
|
|
126
|
+
" cricinfo leagues seasons 19138",
|
|
127
|
+
" cricinfo analysis bowling --metric economy --scope match:1529474",
|
|
128
|
+
"",
|
|
129
|
+
"JSON output for agents:",
|
|
130
|
+
" cricinfo matches show 1529474 --format json",
|
|
131
|
+
" cricinfo analysis dismissals --league 19138 --seasons 2025 --format json",
|
|
132
|
+
}, "\n")
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
func formatNextSteps(commands []string) string {
|
|
136
|
+
lines := []string{"Next steps:"}
|
|
137
|
+
for _, command := range commands {
|
|
138
|
+
lines = append(lines, " "+command)
|
|
139
|
+
}
|
|
140
|
+
return strings.Join(lines, "\n")
|
|
141
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
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 searchRuntimeOptions struct {
|
|
13
|
+
limit int
|
|
14
|
+
leagueID string
|
|
15
|
+
matchID string
|
|
16
|
+
leagueRef string
|
|
17
|
+
seasonRef string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
var newSearchResolver = func() (*cricinfo.Resolver, error) {
|
|
21
|
+
return cricinfo.NewResolver(cricinfo.ResolverConfig{})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func newSearchCommand(global *globalOptions) *cobra.Command {
|
|
25
|
+
opts := &searchRuntimeOptions{}
|
|
26
|
+
|
|
27
|
+
cmd := &cobra.Command{
|
|
28
|
+
Use: "search",
|
|
29
|
+
Short: "Cross-entity discovery for matches, players, teams, and leagues.",
|
|
30
|
+
Long: strings.Join([]string{
|
|
31
|
+
"Search domain entities using numeric IDs, refs, or cached aliases.",
|
|
32
|
+
"The resolver is seeded incrementally from current events, league traversal, and roster data.",
|
|
33
|
+
"",
|
|
34
|
+
"Next steps:",
|
|
35
|
+
" cricinfo search players <query>",
|
|
36
|
+
" cricinfo search teams <query>",
|
|
37
|
+
" cricinfo search leagues <query>",
|
|
38
|
+
" cricinfo search matches <query>",
|
|
39
|
+
}, "\n"),
|
|
40
|
+
Args: cobra.NoArgs,
|
|
41
|
+
RunE: func(cmd *cobra.Command, _ []string) error {
|
|
42
|
+
return cmd.Help()
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
cmd.PersistentFlags().IntVar(&opts.limit, "limit", 10, "Maximum number of results to return")
|
|
47
|
+
cmd.PersistentFlags().StringVar(&opts.leagueID, "league", "", "Preferred league ID for context-aware resolution")
|
|
48
|
+
cmd.PersistentFlags().StringVar(&opts.matchID, "match", "", "Preferred match/competition ID for context-aware resolution")
|
|
49
|
+
cmd.PersistentFlags().StringVar(&opts.leagueRef, "league-ref", "", "Known league ref to seed search context")
|
|
50
|
+
cmd.PersistentFlags().StringVar(&opts.seasonRef, "season-ref", "", "Known season ref to seed league traversal context")
|
|
51
|
+
|
|
52
|
+
cmd.AddCommand(newSearchEntityCommand("players", cricinfo.EntityPlayer, global, opts))
|
|
53
|
+
cmd.AddCommand(newSearchEntityCommand("teams", cricinfo.EntityTeam, global, opts))
|
|
54
|
+
cmd.AddCommand(newSearchEntityCommand("leagues", cricinfo.EntityLeague, global, opts))
|
|
55
|
+
cmd.AddCommand(newSearchEntityCommand("matches", cricinfo.EntityMatch, global, opts))
|
|
56
|
+
|
|
57
|
+
return cmd
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func newSearchEntityCommand(name string, kind cricinfo.EntityKind, global *globalOptions, searchOpts *searchRuntimeOptions) *cobra.Command {
|
|
61
|
+
usage := fmt.Sprintf("%s [query]", name)
|
|
62
|
+
command := &cobra.Command{
|
|
63
|
+
Use: usage,
|
|
64
|
+
Short: fmt.Sprintf("Search %s by id, ref, or alias", name),
|
|
65
|
+
Long: strings.Join([]string{
|
|
66
|
+
fmt.Sprintf("Search %s by numeric ID, known ref, or cached alias.", name),
|
|
67
|
+
"",
|
|
68
|
+
"Examples:",
|
|
69
|
+
fmt.Sprintf(" cricinfo search %s 1361257", name),
|
|
70
|
+
fmt.Sprintf(" cricinfo search %s \"fazal haq\"", name),
|
|
71
|
+
}, "\n"),
|
|
72
|
+
Args: cobra.ArbitraryArgs,
|
|
73
|
+
RunE: func(cmd *cobra.Command, args []string) error {
|
|
74
|
+
resolver, err := newSearchResolver()
|
|
75
|
+
if err != nil {
|
|
76
|
+
return err
|
|
77
|
+
}
|
|
78
|
+
defer func() {
|
|
79
|
+
_ = resolver.Close()
|
|
80
|
+
}()
|
|
81
|
+
|
|
82
|
+
query := strings.TrimSpace(strings.Join(args, " "))
|
|
83
|
+
searchResult, err := resolver.Search(context.Background(), kind, query, cricinfo.ResolveOptions{
|
|
84
|
+
Limit: searchOpts.limit,
|
|
85
|
+
LeagueID: searchOpts.leagueID,
|
|
86
|
+
MatchID: searchOpts.matchID,
|
|
87
|
+
LeagueRef: searchOpts.leagueRef,
|
|
88
|
+
SeasonRef: searchOpts.seasonRef,
|
|
89
|
+
})
|
|
90
|
+
if err != nil {
|
|
91
|
+
return err
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
items := make([]any, 0, len(searchResult.Entities))
|
|
95
|
+
for _, entity := range searchResult.Entities {
|
|
96
|
+
items = append(items, entity.ToRenderable())
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
result := cricinfo.NewListResult(kind, items)
|
|
100
|
+
if len(searchResult.Warnings) > 0 {
|
|
101
|
+
if result.Status == cricinfo.ResultStatusEmpty {
|
|
102
|
+
result.Status = cricinfo.ResultStatusPartial
|
|
103
|
+
result.Message = "partial data returned"
|
|
104
|
+
} else {
|
|
105
|
+
result = cricinfo.NewPartialListResult(kind, items, searchResult.Warnings...)
|
|
106
|
+
}
|
|
107
|
+
result.Warnings = searchResult.Warnings
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return cricinfo.Render(cmd.OutOrStdout(), result, cricinfo.RenderOptions{
|
|
111
|
+
Format: global.format,
|
|
112
|
+
Verbose: global.verbose,
|
|
113
|
+
AllFields: global.allFields,
|
|
114
|
+
})
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return command
|
|
119
|
+
}
|