func()

in mapstr/mapstr.go [282:354]


func (m M) Traverse(path string, mode TraversalMode, visitor TraversalVisitor) (err error) {
	segments := strings.Split(path, ".")
	var match func(string, string) bool

	switch mode {
	case CaseInsensitiveMode:
		match = strings.EqualFold
	case CaseSensitiveMode:
		match = func(a, b string) bool { return a == b }
	}

	// the initial value must be `true` for the first iteration to work
	found := true
	// start with the root
	current := m
	// allocate only once
	var (
		mapType bool
		next    interface{}
	)

	for i, segment := range segments {
		if !found {
			return fmt.Errorf("could not fetch value for key: %s, Error: %w ", path, ErrKeyNotFound)
		}
		found = false

		// we have to go through the list of all key on each level to detect case-insensitive collisions
		for k := range current {
			if !match(segment, k) {
				continue
			}

			// if already found on this level, it's a collision
			if found {
				return fmt.Errorf("multiple keys match %q on the same level of the path %q: %w", k, path, ErrKeyCollision)
			}

			// mark for collision detection
			found = true

			// we need to save this in case the visitor makes changes in keys
			next = current[k]
			err = visitor(current, k)
			if err != nil {
				return fmt.Errorf("error visiting key %q of the path %q: %w", k, path, err)
			}

			// if it's the last segment, we don't need to go deeper, skipping...
			if i == len(segments)-1 {
				continue
			}

			// try to go one level deeper
			current, mapType = tryToMapStr(next)
			if !mapType {
				return fmt.Errorf("cannot continue path %q, next value %q is not a map: %w", path, k, ErrNotMapType)
			}

			// if it's a case-sensitive key match, we don't have to care about collision detection
			// and we can simply stop iterating here.
			if mode == CaseSensitiveMode {
				break
			}
		}
	}

	if !found {
		return fmt.Errorf("could not fetch value for key: %s, Error: %w", path, ErrKeyNotFound)
	}

	return nil
}