cricinfo-cli-go 0.1.1 → 0.1.4

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.
@@ -137,7 +137,7 @@ func TestFieldPathFamilyCoverageLedgerKnownFamiliesMapped(t *testing.T) {
137
137
  func researchedTemplatesFromTSV(t *testing.T) []string {
138
138
  t.Helper()
139
139
 
140
- path := filepath.Join(repoRoot(t), "gg", "agent-outputs", "cricinfo-working-templates.tsv")
140
+ path := filepath.Join("testdata", "coverage", "cricinfo-working-templates.tsv")
141
141
  file, err := os.Open(path)
142
142
  if err != nil {
143
143
  t.Fatalf("open working templates TSV %q: %v", path, err)
@@ -177,7 +177,7 @@ func researchedTemplatesFromTSV(t *testing.T) []string {
177
177
  func researchedFieldPathFamilies(t *testing.T) map[string]struct{} {
178
178
  t.Helper()
179
179
 
180
- path := filepath.Join(repoRoot(t), "gg", "agent-outputs", "cricinfo-field-path-catalog.txt")
180
+ path := filepath.Join("testdata", "coverage", "cricinfo-field-path-catalog.txt")
181
181
  file, err := os.Open(path)
182
182
  if err != nil {
183
183
  t.Fatalf("open field-path catalog %q: %v", path, err)
@@ -231,23 +231,3 @@ func isNumericToken(value string) bool {
231
231
  }
232
232
  return true
233
233
  }
234
-
235
- func repoRoot(t *testing.T) string {
236
- t.Helper()
237
-
238
- dir, err := os.Getwd()
239
- if err != nil {
240
- t.Fatalf("getwd: %v", err)
241
- }
242
-
243
- for {
244
- if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil {
245
- return dir
246
- }
247
- parent := filepath.Dir(dir)
248
- if parent == dir {
249
- t.Fatalf("unable to locate repository root from %q", dir)
250
- }
251
- dir = parent
252
- }
253
- }
@@ -472,12 +472,39 @@ func mergeAliasSlices(slices ...[]string) []string {
472
472
  }
473
473
  seen[normalized] = struct{}{}
474
474
  out = append(out, value)
475
+ if acronym := aliasAcronym(normalized); acronym != "" {
476
+ if _, ok := seen[acronym]; !ok {
477
+ seen[acronym] = struct{}{}
478
+ out = append(out, acronym)
479
+ }
480
+ }
475
481
  }
476
482
  }
477
483
  sort.Strings(out)
478
484
  return out
479
485
  }
480
486
 
487
+ func aliasAcronym(normalized string) string {
488
+ tokens := strings.Fields(strings.TrimSpace(normalized))
489
+ if len(tokens) < 2 {
490
+ return ""
491
+ }
492
+
493
+ var builder strings.Builder
494
+ for _, token := range tokens {
495
+ if token == "" {
496
+ continue
497
+ }
498
+ builder.WriteByte(token[0])
499
+ }
500
+
501
+ acronym := strings.TrimSpace(builder.String())
502
+ if len(acronym) < 2 {
503
+ return ""
504
+ }
505
+ return acronym
506
+ }
507
+
481
508
  func aliasSet(aliases []string) map[string]struct{} {
482
509
  set := map[string]struct{}{}
483
510
  for _, alias := range aliases {
@@ -6,6 +6,7 @@ import (
6
6
  "sort"
7
7
  "strconv"
8
8
  "strings"
9
+ "sync"
9
10
  "time"
10
11
  )
11
12
 
@@ -133,6 +134,8 @@ type HistoricalScopeSession struct {
133
134
  partnershipWarnByMatch map[string][]string
134
135
 
135
136
  metrics HydrationMetrics
137
+
138
+ resolveMu sync.Mutex
136
139
  }
137
140
 
138
141
  func newHistoricalScopeSession(client *Client, resolver *Resolver, opts HistoricalScopeOptions) *HistoricalScopeSession {
@@ -292,52 +295,78 @@ func (s *HistoricalScopeSession) HydratePlayerMatchSummaries(ctx context.Context
292
295
  continue
293
296
  }
294
297
 
295
- for _, entry := range entries {
296
- playerID := strings.TrimSpace(entry.PlayerID)
297
- if playerID == "" {
298
- continue
299
- }
300
- if strings.TrimSpace(entry.DisplayName) == "" && s.resolver != nil {
301
- _ = s.resolver.seedPlayerByID(ctx, playerID, match.LeagueID, match.ID)
302
- }
303
- entry = s.enrichRosterEntryFromIndex(entry)
298
+ type playerHydrationResult struct {
299
+ item *PlayerMatch
300
+ warning string
301
+ }
302
+ results := make([]playerHydrationResult, len(entries))
303
+ sem := make(chan struct{}, detailSubresourceFetchConcurrency)
304
+ var wg sync.WaitGroup
305
+ for i, entry := range entries {
306
+ wg.Add(1)
307
+ go func(index int, entry TeamRosterEntry, team Team) {
308
+ defer wg.Done()
309
+ sem <- struct{}{}
310
+ defer func() { <-sem }()
304
311
 
305
- statsRef := rosterPlayerStatisticsRef(match, team, entry)
306
- if statsRef == "" {
307
- warnings = append(warnings, fmt.Sprintf("player %s has no match statistics route", playerID))
308
- continue
309
- }
312
+ playerID := strings.TrimSpace(entry.PlayerID)
313
+ if playerID == "" {
314
+ return
315
+ }
316
+ if strings.TrimSpace(entry.DisplayName) == "" && s.resolver != nil {
317
+ _ = s.resolver.seedPlayerByID(ctx, playerID, match.LeagueID, match.ID)
318
+ }
319
+ entry = s.enrichRosterEntryFromIndex(entry)
310
320
 
311
- statsDoc, err := s.resolve(ctx, statsRef)
312
- if err != nil {
313
- warnings = append(warnings, fmt.Sprintf("player statistics %s: %v", statsRef, err))
314
- continue
315
- }
321
+ statsRef := rosterPlayerStatisticsRef(match, team, entry)
322
+ if statsRef == "" {
323
+ results[index] = playerHydrationResult{warning: fmt.Sprintf("player %s has no match statistics route", playerID)}
324
+ return
325
+ }
316
326
 
317
- categories, err := NormalizeStatCategories(statsDoc.Body)
318
- if err != nil {
319
- warnings = append(warnings, fmt.Sprintf("player statistics %s: %v", statsDoc.CanonicalRef, err))
327
+ statsDoc, err := s.resolve(ctx, statsRef)
328
+ if err != nil {
329
+ results[index] = playerHydrationResult{warning: fmt.Sprintf("player statistics %s: %v", statsRef, err)}
330
+ return
331
+ }
332
+
333
+ categories, err := NormalizeStatCategories(statsDoc.Body)
334
+ if err != nil {
335
+ results[index] = playerHydrationResult{warning: fmt.Sprintf("player statistics %s: %v", statsDoc.CanonicalRef, err)}
336
+ return
337
+ }
338
+
339
+ batting, bowling, fielding := splitPlayerStatCategories(categories)
340
+ playerMatch := PlayerMatch{
341
+ PlayerID: playerID,
342
+ PlayerRef: entry.PlayerRef,
343
+ PlayerName: nonEmpty(entry.DisplayName, "Unknown Player"),
344
+ MatchID: match.ID,
345
+ CompetitionID: nonEmpty(match.CompetitionID, match.ID),
346
+ EventID: match.EventID,
347
+ LeagueID: match.LeagueID,
348
+ TeamID: team.ID,
349
+ TeamName: nonEmpty(team.ShortName, team.Name, "Unknown Team"),
350
+ StatisticsRef: statsDoc.CanonicalRef,
351
+ LinescoresRef: rosterPlayerLinescoresRef(match, team, entry),
352
+ Batting: batting,
353
+ Bowling: bowling,
354
+ Fielding: fielding,
355
+ Summary: summarizePlayerMatchCategories(categories),
356
+ }
357
+ results[index] = playerHydrationResult{item: &playerMatch}
358
+ }(i, entry, team)
359
+ }
360
+ wg.Wait()
361
+
362
+ for _, result := range results {
363
+ if strings.TrimSpace(result.warning) != "" {
364
+ warnings = append(warnings, result.warning)
320
365
  continue
321
366
  }
322
-
323
- batting, bowling, fielding := splitPlayerStatCategories(categories)
324
- items = append(items, PlayerMatch{
325
- PlayerID: playerID,
326
- PlayerRef: entry.PlayerRef,
327
- PlayerName: nonEmpty(entry.DisplayName, "Unknown Player"),
328
- MatchID: match.ID,
329
- CompetitionID: nonEmpty(match.CompetitionID, match.ID),
330
- EventID: match.EventID,
331
- LeagueID: match.LeagueID,
332
- TeamID: team.ID,
333
- TeamName: nonEmpty(team.ShortName, team.Name, "Unknown Team"),
334
- StatisticsRef: statsDoc.CanonicalRef,
335
- LinescoresRef: rosterPlayerLinescoresRef(match, team, entry),
336
- Batting: batting,
337
- Bowling: bowling,
338
- Fielding: fielding,
339
- Summary: summarizePlayerMatchCategories(categories),
340
- })
367
+ if result.item != nil {
368
+ items = append(items, *result.item)
369
+ }
341
370
  }
342
371
  }
343
372
 
@@ -1397,23 +1426,34 @@ func (s *HistoricalScopeSession) resolve(ctx context.Context, ref string) (*Reso
1397
1426
  return nil, fmt.Errorf("ref is empty")
1398
1427
  }
1399
1428
 
1429
+ s.resolveMu.Lock()
1400
1430
  if cached, ok := s.resolvedDocs[ref]; ok {
1401
1431
  s.metrics.ResolveCacheHits++
1432
+ s.resolveMu.Unlock()
1402
1433
  return cached, nil
1403
1434
  }
1435
+ s.resolveMu.Unlock()
1404
1436
 
1405
1437
  resolved, err := s.client.ResolveRefChain(ctx, ref)
1406
1438
  if err != nil {
1407
1439
  return nil, err
1408
1440
  }
1409
- s.metrics.ResolveCacheMisses++
1410
1441
 
1411
1442
  copied := *resolved
1412
1443
  pointer := &copied
1413
1444
  keys := compactWarnings([]string{ref, copied.RequestedRef, copied.CanonicalRef})
1445
+
1446
+ s.resolveMu.Lock()
1447
+ if cached, ok := s.resolvedDocs[ref]; ok {
1448
+ s.metrics.ResolveCacheHits++
1449
+ s.resolveMu.Unlock()
1450
+ return cached, nil
1451
+ }
1452
+ s.metrics.ResolveCacheMisses++
1414
1453
  for _, key := range keys {
1415
1454
  s.resolvedDocs[key] = pointer
1416
1455
  }
1456
+ s.resolveMu.Unlock()
1417
1457
  return pointer, nil
1418
1458
  }
1419
1459