1package authoring23import (4 "context"5 "fmt"6 "sort"7)89type buildEvidence struct {10 Captures map[string]Capture11 Sets map[string]CaptureSet12 Cached map[string]bool13}1415type captureGroup struct {16 Requests []PlannedRequest17}1819func acquirePlanEvidence(ctx context.Context, plan Plan, cache *Cache, offline bool) (buildEvidence, error) {20 evidence := buildEvidence{21 Captures: make(map[string]Capture, len(plan.Requests)),22 Sets: make(map[string]CaptureSet, len(plan.Requests)),23 Cached: make(map[string]bool, len(plan.Requests)),24 }25 registry := defaultAdapterRegistry()26 usedRequests := 027 var usedBytes int6428 for _, group := range planCaptureGroups(plan) {29 remainingRequests := plan.Budgets.Requests - usedRequests30 remainingBytes := plan.Budgets.TotalBytes - usedBytes31 if remainingRequests <= 0 || remainingBytes <= 0 {32 return buildEvidence{}, fmt.Errorf("captured evidence exhausts build budgets before source %s", group.Requests[0].Source)33 }34 requests := make([]Request, len(group.Requests))35 for index := range group.Requests {36 requests[index] = group.Requests[index].Request37 }38 set, cached, err := cache.AcquireSet(ctx, requests, registry, CaptureSetOptions{39 Offline: offline, MaxRequests: remainingRequests, MaxBytes: remainingBytes,40 })41 if err != nil {42 return buildEvidence{}, err43 }44 for _, root := range set.Roots {45 usedRequests += len(root.Pages)46 }47 usedBytes += set.TotalBytes48 if usedRequests > plan.Budgets.Requests || usedBytes > plan.Budgets.TotalBytes {49 return buildEvidence{}, fmt.Errorf("captured evidence exceeds build budgets")50 }51 for _, planned := range group.Requests {52 root, ok := captureRootForRequest(set, planned.Request)53 if !ok {54 return buildEvidence{}, fmt.Errorf("capture set omits request %s", planned.ID)55 }56 evidence.Captures[planned.ID] = root.Pages[0]57 evidence.Sets[planned.ID] = set58 evidence.Cached[planned.ID] = cached59 }60 }61 return evidence, nil62}6364func planCaptureGroups(plan Plan) []captureGroup {65 bySource := make(map[string][]PlannedRequest)66 for _, request := range plan.Requests {67 key := string(request.Kind) + "\x00" + request.Source68 bySource[key] = append(bySource[key], request)69 }70 groups := make([]captureGroup, 0, len(bySource))71 for _, requests := range bySource {72 sort.Slice(requests, func(i, j int) bool {73 return requestCacheKey(requests[i].Request) < requestCacheKey(requests[j].Request)74 })75 groups = append(groups, captureGroup{Requests: requests})76 }77 sort.Slice(groups, func(i, j int) bool { return groups[i].Requests[0].ID < groups[j].Requests[0].ID })78 return groups79}8081func captureRootForRequest(set CaptureSet, request Request) (CaptureRoot, bool) {82 want := requestCacheKey(request)83 index := sort.Search(len(set.Roots), func(index int) bool { return set.Roots[index].Acquisition >= want })84 if index == len(set.Roots) || set.Roots[index].Acquisition != want {85 return CaptureRoot{}, false86 }87 return set.Roots[index], true88}