code: func (b *Builder) Prune(ctx context.Context, opts types.BuildCachePruneOptions) (int64, []string, error) {
  	ch := make(chan *controlapi.UsageRecord)
  
  	eg, ctx := errgroup.WithContext(ctx)
  
  	validFilters := make(map[string]bool, 1+len(cacheFields))
  	validFilters["unused-for"] = true
  	validFilters["until"] = true
  	validFilters["label"] = true  // TODO(tiborvass): handle label
  	validFilters["label!"] = true // TODO(tiborvass): handle label!
  	for k, v := range cacheFields {
  		validFilters[k] = v
  	}
  	if err := opts.Filters.Validate(validFilters); err != nil {
  		return 0, nil, err
  	}
  
  	pi, err := toBuildkitPruneInfo(opts)
  	if err != nil {
  		return 0, nil, err
  	}
  
  	eg.Go(func() error {
  		defer close(ch)
  		return b.controller.Prune(&controlapi.PruneRequest{
  			All:          pi.All,
  			KeepDuration: int64(pi.KeepDuration),
  			KeepBytes:    pi.KeepBytes,
  			Filter:       pi.Filter,
  		}, &pruneProxy{
  			streamProxy: streamProxy{ctx: ctx},
  			ch:          ch,
  		})
  	})
  
  	var size int64
  	var cacheIDs []string
  	eg.Go(func() error {
  		for r := range ch {
  			size += r.Size_
  			cacheIDs = append(cacheIDs, r.ID)
  		}
  		return nil
  	})
  
  	if err := eg.Wait(); err != nil {
  		return 0, nil, err
  	}
  
  	return size, cacheIDs, nil
  }
docs: // Prune clears all reclaimable build cache