// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package pods

import (
	
	
	
	
	
	
	
	
	
)

// Pod encapsulates a set of files emitted during the executions of a
// coverage-instrumented binary. Each pod contains a single meta-data
// file, and then 0 or more counter data files that refer to that
// meta-data file. Pods are intended to simplify processing of
// coverage output files in the case where we have several coverage
// output directories containing output files derived from more
// than one instrumented executable. In the case where the files that
// make up a pod are spread out across multiple directories, each
// element of the "Origins" field below will be populated with the
// index of the originating directory for the corresponding counter
// data file (within the slice of input dirs handed to CollectPods).
// The ProcessIDs field will be populated with the process ID of each
// data file in the CounterDataFiles slice.
type Pod struct {
	MetaFile         string
	CounterDataFiles []string
	Origins          []int
	ProcessIDs       []int
}

// CollectPods visits the files contained within the directories in
// the list 'dirs', collects any coverage-related files, partitions
// them into pods, and returns a list of the pods to the caller, along
// with an error if something went wrong during directory/file
// reading.
//
// CollectPods skips over any file that is not related to coverage
// (e.g. avoids looking at things that are not meta-data files or
// counter-data files). CollectPods also skips over 'orphaned' counter
// data files (e.g. counter data files for which we can't find the
// corresponding meta-data file). If "warn" is true, CollectPods will
// issue warnings to stderr when it encounters non-fatal problems (for
// orphans or a directory with no meta-data files).
func ( []string,  bool) ([]Pod, error) {
	 := []string{}
	 := []int{}
	for ,  := range  {
		,  := os.ReadDir()
		if  != nil {
			return nil, 
		}
		for ,  := range  {
			if .IsDir() {
				continue
			}
			 = append(, filepath.Join(, .Name()))
			 = append(, )
		}
	}
	return collectPodsImpl(, , ), nil
}

// CollectPodsFromFiles functions the same as "CollectPods" but
// operates on an explicit list of files instead of a directory.
func ( []string,  bool) []Pod {
	return collectPodsImpl(, nil, )
}

type fileWithAnnotations struct {
	file   string
	origin int
	pid    int
}

type protoPod struct {
	mf       string
	elements []fileWithAnnotations
}

// collectPodsImpl examines the specified list of files and picks out
// subsets that correspond to coverage pods. The first stage in this
// process is collecting a set { M1, M2, ... MN } where each M_k is a
// distinct coverage meta-data file. We then create a single pod for
// each meta-data file M_k, then find all of the counter data files
// that refer to that meta-data file (recall that the counter data
// file name incorporates the meta-data hash), and add the counter
// data file to the appropriate pod.
//
// This process is complicated by the fact that we need to keep track
// of directory indices for counter data files. Here is an example to
// motivate:
//
//	directory 1:
//
// M1   covmeta.9bbf1777f47b3fcacb05c38b035512d6
// C1   covcounters.9bbf1777f47b3fcacb05c38b035512d6.1677673.1662138360208416486
// C2   covcounters.9bbf1777f47b3fcacb05c38b035512d6.1677637.1662138359974441782
//
//	directory 2:
//
// M2   covmeta.9bbf1777f47b3fcacb05c38b035512d6
// C3   covcounters.9bbf1777f47b3fcacb05c38b035512d6.1677445.1662138360208416480
// C4   covcounters.9bbf1777f47b3fcacb05c38b035512d6.1677677.1662138359974441781
// M3   covmeta.a723844208cea2ae80c63482c78b2245
// C5   covcounters.a723844208cea2ae80c63482c78b2245.3677445.1662138360208416480
// C6   covcounters.a723844208cea2ae80c63482c78b2245.1877677.1662138359974441781
//
// In these two directories we have three meta-data files, but only
// two are distinct, meaning that we'll wind up with two pods. The
// first pod (with meta-file M1) will have four counter data files
// (C1, C2, C3, C4) and the second pod will have two counter data files
// (C5, C6).
func collectPodsImpl( []string,  []int,  bool) []Pod {
	 := regexp.MustCompile(fmt.Sprintf(`^%s\.(\S+)$`, coverage.MetaFilePref))
	 := make(map[string]protoPod)
	for ,  := range  {
		 := filepath.Base()
		if  := .FindStringSubmatch();  != nil {
			 := [1]
			// We need to allow for the possibility of duplicate
			// meta-data files. If we hit this case, use the
			// first encountered as the canonical version.
			if ,  := []; ! {
				[] = protoPod{mf: }
			}
			// FIXME: should probably check file length and hash here for
			// the duplicate.
		}
	}
	 := regexp.MustCompile(fmt.Sprintf(coverage.CounterFileRegexp, coverage.CounterFilePref))
	for ,  := range  {
		 := filepath.Base()
		if  := .FindStringSubmatch();  != nil {
			 := [1] // meta hash
			,  := strconv.Atoi([2])
			if  != nil {
				continue
			}
			if ,  := [];  {
				 := -1
				if  != nil {
					 = []
				}
				 := fileWithAnnotations{file: , origin: , pid: }
				.elements = append(.elements, )
				[] = 
			} else {
				if  {
					warning("skipping orphaned counter file: %s", )
				}
			}
		}
	}
	if len() == 0 {
		if  {
			warning("no coverage data files found")
		}
		return nil
	}
	 := make([]Pod, 0, len())
	for ,  := range  {
		slices.SortFunc(.elements, func(,  fileWithAnnotations) int {
			if  := cmp.Compare(.origin, .origin);  != 0 {
				return 
			}
			return strings.Compare(.file, .file)
		})
		 := Pod{
			MetaFile:         .mf,
			CounterDataFiles: make([]string, 0, len(.elements)),
			Origins:          make([]int, 0, len(.elements)),
			ProcessIDs:       make([]int, 0, len(.elements)),
		}
		for ,  := range .elements {
			.CounterDataFiles = append(.CounterDataFiles, .file)
			.Origins = append(.Origins, .origin)
			.ProcessIDs = append(.ProcessIDs, .pid)
		}
		 = append(, )
	}
	slices.SortFunc(, func(,  Pod) int {
		return strings.Compare(.MetaFile, .MetaFile)
	})
	return 
}

func warning( string,  ...interface{}) {
	fmt.Fprintf(os.Stderr, "warning: ")
	fmt.Fprintf(os.Stderr, , ...)
	fmt.Fprintf(os.Stderr, "\n")
}