diff --git a/docs/examples.md b/docs/examples.md index bae940a..22d1640 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -17,6 +17,13 @@ - array insertion could be affected via `:before` and `:after` +- Escaping special token is possible with `~` sequences: + - `~` is escaped `~0` + - `/` is escaped `~1` + - `?` is escaped `~2` + - `=` is escaped `~3` + - `:` is escaped `~7` + See pointer test examples in [patch/pointer_test.go](../patch/pointer_test.go). ## Operations diff --git a/patch/pointer.go b/patch/pointer.go index 4119449..bbb8ac9 100644 --- a/patch/pointer.go +++ b/patch/pointer.go @@ -7,8 +7,8 @@ import ( ) var ( - rfc6901Decoder = strings.NewReplacer("~0", "~", "~1", "/", "~7", ":") - rfc6901Encoder = strings.NewReplacer("~", "~0", "/", "~1", ":", "~7") + rfc6901Decoder = strings.NewReplacer("~0", "~", "~1", "/", "~2", "?", "~3", "=", "~7", ":") + rfc6901Encoder = strings.NewReplacer("~", "~0", "/", "~1", "?", "~2", "=", "~3", ":", "~7") ) // More or less based on https://tools.ietf.org/html/rfc6901 @@ -65,8 +65,6 @@ func NewPointerFromString(str string) (Pointer, error) { } } - tok = rfc6901Decoder.Replace(tok) - // parse as after last index if isLast && tok == "-" { if len(modifiers) > 0 { @@ -85,14 +83,15 @@ func NewPointerFromString(str string) (Pointer, error) { if strings.HasSuffix(tok, "?") { optional = true + tok = strings.TrimSuffix(tok, "?") } // parse name=val kv := strings.SplitN(tok, "=", 2) if len(kv) == 2 { token := MatchingIndexToken{ - Key: kv[0], - Value: strings.TrimSuffix(kv[1], "?"), + Key: rfc6901Decoder.Replace(kv[0]), + Value: rfc6901Decoder.Replace(kv[1]), Optional: optional, Modifiers: modifiers, } @@ -107,7 +106,7 @@ func NewPointerFromString(str string) (Pointer, error) { // it's a map key token := KeyToken{ - Key: strings.TrimSuffix(tok, "?"), + Key: rfc6901Decoder.Replace(tok), Optional: optional, } diff --git a/patch/pointer_test.go b/patch/pointer_test.go index 36c4aa2..c37beea 100644 --- a/patch/pointer_test.go +++ b/patch/pointer_test.go @@ -71,7 +71,7 @@ var testCases = []PointerTestCase{ {"/=?", []Token{RootToken{}, MatchingIndexToken{Key: "", Value: "", Optional: true}}}, {"/name=", []Token{RootToken{}, MatchingIndexToken{Key: "name", Value: ""}}}, {"/=val", []Token{RootToken{}, MatchingIndexToken{Key: "", Value: "val"}}}, - {"/==", []Token{RootToken{}, MatchingIndexToken{Key: "", Value: "="}}}, + {"/=~3", []Token{RootToken{}, MatchingIndexToken{Key: "", Value: "="}}}, {"/name=val:before", []Token{ RootToken{}, @@ -94,11 +94,14 @@ var testCases = []PointerTestCase{ KeyToken{Key: "key", Optional: true}, }}, - // Escaping (todo support ~2 for '?'; ~3 for '=') + // Escaping {"/m~0n", []Token{RootToken{}, KeyToken{Key: "m~n"}}}, {"/a~01b", []Token{RootToken{}, KeyToken{Key: "a~1b"}}}, {"/a~1b", []Token{RootToken{}, KeyToken{Key: "a/b"}}}, + {"/plip~2plop", []Token{RootToken{}, KeyToken{Key: "plip?plop"}}}, + {"/plop~36713", []Token{RootToken{}, KeyToken{Key: "plop=6713"}}}, {"/name~0n=val~0n", []Token{RootToken{}, MatchingIndexToken{Key: "name~n", Value: "val~n"}}}, + {"/name~1in~2question=key~3val", []Token{RootToken{}, MatchingIndexToken{Key: "name/in?question", Value: "key=val"}}}, {"/m~7n", []Token{RootToken{}, KeyToken{Key: "m:n"}}}, // Special chars