Skip to content

Modified RFC6901 #38

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
modified rfc6901
  • Loading branch information
TotalTechGeek committed Dec 8, 2024
commit d5aade1e0148f59e14aa982186589de88be6c86b
11 changes: 5 additions & 6 deletions general.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,11 @@ describe('Various Test Cases', () => {
})

it('is able to handle simple path escaping', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { get: [{ var: 'selected' }, 'b\\.c'] }, { selected: { 'b.c': 2 } }, 2)
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { get: [{ var: 'selected' }, 'b~2c'] }, { selected: { 'b.c': 2 } }, 2)
})

it('is able to handle simple path escaping in a variable', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { get: [{ var: 'selected' }, { var: 'key' }] }, { selected: { 'b.c': 2 }, key: 'b\\.c' }, 2)
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { get: [{ var: 'selected' }, { var: 'key' }] }, { selected: { 'b.c': 2 }, key: 'b~2c' }, 2)
})

it('is able to avoid returning functions', async () => {
Expand All @@ -154,24 +154,23 @@ describe('Various Test Cases', () => {
})

it('is able to handle path escaping in a var call', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: 'hello\\.world' }, { 'hello.world': 2 }, 2)
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: 'hello~2world' }, { 'hello.world': 2 }, 2)
})

it('is able to access empty keys', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: '.' }, { '': 2 }, 2)
})

it('is able to access dot keys', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: '\\.' }, { '.': 2 }, 2)
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: '~2' }, { '.': 2 }, 2)
})

it('is able to access "/" keys from above', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { map: [[1], { '+': [{ var: '' }, { var: '../../..\\/' }] }] }, { '': { '': { '/': 3 } } }, [4])
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { map: [[1], { '+': [{ var: '' }, { var: '../../..~1' }] }] }, { '': { '': { '/': 3 } } }, [4])
})

it('is able to handle path escaping with multiple escapes', async () => {
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: '\\foo' }, { '\\foo': 2 }, 2)
for (const engine of [...normalEngines, ...permissiveEngines]) await testEngine(engine, { var: '\\\\foo' }, { '\\foo': 2 }, 2)
})

it('is able to access the index in the iterators', async () => {
Expand Down
2 changes: 1 addition & 1 deletion suites/scopes.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
"rule": {
"map": [
{ "var": "arr" },
{ "+": [{ "var": "../../\\.\\./" }, { "var": "../../..\\/" }]}
{ "+": [{ "var": "../../~2~2~1" }, { "var": "../../..~1" }]}
]
},
"data": { "arr": [1,2,3], "../": 10, "": { "": { "/": 7 }} },
Expand Down
48 changes: 33 additions & 15 deletions suites/vars.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,62 +20,80 @@
},
{
"description": "Fetches a value from a key that is purely a dot",
"rule": { "var": "\\." },
"rule": { "var": "~2" },
"data": { "." : 20 },
"result": 20
},
{
"description": "Fetches a value from a key with a dot in it",
"rule": { "var": "\\.key" },
"rule": { "var": "~2key" },
"data": { ".key" : 4 },
"result": 4
},
{
"description":"Fetches a value from a key with a dot in it (2)",
"rule": { "var": "hello\\.world" },
"rule": { "var": "hello~2world" },
"data": { "hello.world" : 5 },
"result": 5
},
{
"description": "Fetches a value from a key inside an empty key with a dot in it",
"rule": { "var": ".\\.key" },
"rule": { "var": "/~2key" },
"data": { "": { ".key" : 6 } },
"result": 6
},
{
"description": "Going a few levels deep",
"rule": { "var": "..\\.key." },
"rule": { "var": "//~2key." },
"data": { "": { "": { ".key": { "": 7 }} }},
"result": 7
},
{
"description": "Escape / as well, which is useful for the scope proposal",
"rule": { "var": "\\/" },
"rule": { "var": "~1" },
"data": { "/" : 8 },
"result": 8
},
{
"description": "Though / doesn't inherently need to be escaped",
"rule": { "var": "/" },
"data": { "/" : 9 },
"result": 9
},
{
"description": "Dot then empty key",
"rule": { "var": "\\.." },
"rule": { "var": "~2." },
"data": { "." : { "" : 10 } },
"result": 10
},
{
"description": "Empty key then dot",
"rule": { "var": ".\\." },
"rule": { "var": ".~2" },
"data": { "" : { "." : 11 } },
"result": 11
},
{
"description": "Can use backslack in name, too",
"rule": { "var": "\\\\.Hello" },
"rule": { "var": "\\.Hello" },
"data": { "\\" : { "Hello" : 12 } },
"result": 12
},
{
"description": "Can escape tilde",
"rule": { "var": "~0" },
"data": { "~" : 13 },
"result": 13
},
{
"description": "Fetches a value from an empty key, traditional",
"rule": { "var": "/" },
"data": { "" : 1 },
"result": 1
},
{
"description": "Fetches a value from a nested empty key, traditional",
"rule": { "var": "//" },
"data": { "" : { "": 2 } },
"result": 2
},
{
"description": "Fetches a value from a doubly nested empty key, traditional",
"rule": { "var": "///" },
"data": { "" : { "": { "": 3 } } },
"result": 3
}
]
25 changes: 10 additions & 15 deletions utilities/splitPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,37 +20,32 @@ export function splitPathMemoized (str) {
return parts
}

const chars = ['~', '/', '.']

/**
* Splits a path string into an array of parts.
*
* @example splitPath('a.b.c') // ['a', 'b', 'c']
* @example splitPath('a\\.b.c') // ['a.b', 'c']
* @example splitPath('a\\\\.b.c') // ['a\\', 'b', 'c']
* @example splitPath('a\\\\\\.b.c') // ['a\\.b', 'c']
* @example splitPath('hello') // ['hello']
* @example splitPath('hello\\') // ['hello\\']
* @example splitPath('hello\\\\') // ['hello\\']
*
* @param {string} str
* @param {string} separator
* @returns {string[]}
*/
export function splitPath (str, separator = '.', escape = '\\', up = '/') {
export function splitPath (str) {
const parts = []
let current = ''

for (let i = 0; i < str.length; i++) {
const char = str[i]
if (char === escape) {
if (str[i + 1] === separator || str[i + 1] === up) {
current += str[i + 1]
if (char === '~') {
if (str[i + 1] === '0' || str[i + 1] === '1' || str[i + 1] === '2') {
current += chars[+str[i + 1]]
i++
} else if (str[i + 1] === escape) {
current += escape
} else if (str[i + 1] === '~') {
current += '~'
i++
// The following else might be something tweaked in a spec.
} else current += escape
} else if (char === separator) {
} else throw new Error('Invalid escape sequence')
} else if (char === '.' || char === '/') {
parts.push(current)
current = ''
} else current += char
Expand Down
Loading