func validateSymlinks()

in load.go [643:718]


func validateSymlinks(symlinks map[string]SymlinkTarget) error {
	var (
		errs     []error
		numPairs int
	)

	for oldpath, cfg := range symlinks {
		var err error
		if oldpath == "" {
			err = fmt.Errorf("symlink source is empty")
			errs = append(errs, err)
		}

		if (cfg.Path != "" && len(cfg.Paths) > 0) || (cfg.Path == "" && len(cfg.Paths) == 0) {
			err = fmt.Errorf("'path' and 'paths' fields are mutually exclusive, and at least one is required: "+
				"symlink to %s", oldpath)

			errs = append(errs, err)
		}

		if err != nil {
			continue
		}

		if cfg.Path != "" { // this means .Paths is empty
			numPairs++
			continue
		}

		for _, newpath := range cfg.Paths { // this means .Path is empty
			numPairs++
			if newpath == "" {
				errs = append(errs, fmt.Errorf("symlink newpath should not be empty"))
				continue
			}
		}
	}

	// The remainder of this function checks for duplicate `newpath`s in the
	// symlink pairs. This is not allowed: neither the ordering of the
	// `oldpath` map keys, nor that of the `.Paths` values can be trusted. We
	// also sort both to avoid cache misses, so we would end up with
	// inconsistent behavior -- regardless of whether the inputs are the same.
	if numPairs < 2 {
		return goerrors.Join(errs...)
	}

	var (
		oldpath string
		cfg     SymlinkTarget
	)

	seen := make(map[string]string, numPairs)
	checkDuplicateNewpath := func(newpath string) {
		if newpath == "" {
			return
		}

		if seenPath, found := seen[newpath]; found {
			errs = append(errs, fmt.Errorf("symlink 'newpaths' must be unique: %q points to both %q and %q",
				newpath, oldpath, seenPath))
		}

		seen[newpath] = oldpath
	}

	for oldpath, cfg = range symlinks {
		checkDuplicateNewpath(cfg.Path)

		for _, newpath := range cfg.Paths {
			checkDuplicateNewpath(newpath)
		}
	}

	return goerrors.Join(errs...)
}