package dyn import ( "fmt" "strconv" "strings" ) // MustPathFromString is like NewPathFromString but panics on error. func MustPathFromString(input string) Path { p, err := NewPathFromString(input) if err != nil { panic(err) } return p } // NewPathFromString parses a path from a string. // // The string must be a sequence of keys and indices separated by dots. // Indices must be enclosed in square brackets. // The string may include a leading dot. // // Examples: // - foo.bar // - foo[1].bar // - foo.bar[1] // - foo.bar[1][2] // - . func NewPathFromString(input string) (Path, error) { var path Path p := input // Trim leading dot. if p != "" && p[0] == '.' { p = p[1:] } for p != "" { // Every component may have a leading dot. if p != "" && p[0] == '.' { p = p[1:] } if p == "" { return nil, fmt.Errorf("invalid path: %s", input) } if p[0] == '[' { // Find next ] i := strings.Index(p, "]") if i < 0 { return nil, fmt.Errorf("invalid path: %s", input) } // Parse index j, err := strconv.Atoi(p[1:i]) if err != nil { return nil, fmt.Errorf("invalid path: %s", input) } // Append index path = append(path, Index(j)) p = p[i+1:] // The next character must be a . or [ if p != "" && strings.IndexAny(p, ".[") != 0 { return nil, fmt.Errorf("invalid path: %s", input) } } else { // Find next . or [ i := strings.IndexAny(p, ".[") if i < 0 { i = len(p) } if i == 0 { return nil, fmt.Errorf("invalid path: %s", input) } // Append key path = append(path, Key(p[:i])) p = p[i:] } } return path, nil }