Skip to content

Commit

Permalink
Return the full JsonPath if the object was found
Browse files Browse the repository at this point in the history
Introduces new function to resolve a new random path according to an
already resolved path with similar elements.

Reference:
#143 (comment)
  • Loading branch information
Lucki authored and ifl0w committed Feb 11, 2024
1 parent 975b97c commit ff3f8b9
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 51 deletions.
21 changes: 6 additions & 15 deletions src/adapter/genericJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,11 @@ class GenericJsonAdapter extends BaseAdapter {
const authorUrlJSONPath = this._settings.getString('author-url-path');

for (let i = 0; i < 5 && wallpaperResult.length < count; i++) {
const returnObject = JSONPath.getTarget(response_body, imageJSONPath);
if (!returnObject || (typeof returnObject.Object !== 'string' && typeof returnObject.Object !== 'number') || returnObject.Object === '')
const [returnObject, resolvedPath] = JSONPath.getTarget(response_body, imageJSONPath);
if (!returnObject || (typeof returnObject !== 'string' && typeof returnObject !== 'number') || returnObject === '')
throw new Error('Unexpected json member found');

const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject.Object);
const imageDownloadUrl = this._settings.getString('image-prefix') + String(returnObject);
const imageBlocked = this._isImageBlocked(Utils.fileName(imageDownloadUrl));

// Don't retry without @random present in JSONPath
Expand All @@ -60,32 +60,23 @@ class GenericJsonAdapter extends BaseAdapter {
if (imageBlocked)
continue;

// '@random' would yield different results so lets make sure the values stay
// the same as long as the path is identical
const samePath = imageJSONPath.substring(0, Utils.findFirstDifference(imageJSONPath, postJSONPath));

// count occurrences of '@random' to slice the array later
// https://stackoverflow.com/a/4009768
const occurrences = (samePath.match(/@random/g) || []).length;
const slicedRandomNumbers = returnObject?.RandomNumbers?.slice(0, occurrences);

// A bit cumbersome to handle "unknown" in the following parts:
// https://github.com/microsoft/TypeScript/issues/27706

let postUrl: string;
const postUrlObject = JSONPath.getTarget(response_body, postJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object;
const postUrlObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(postJSONPath, resolvedPath))[0];
if (typeof postUrlObject === 'string' || typeof postUrlObject === 'number')
postUrl = this._settings.getString('post-prefix') + String(postUrlObject);
else
postUrl = '';

let authorName: string | null = null;
const authorNameObject = JSONPath.getTarget(response_body, authorNameJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object;
const authorNameObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(authorNameJSONPath, resolvedPath))[0];
if (typeof authorNameObject === 'string' && authorNameObject !== '')
authorName = authorNameObject;

let authorUrl: string;
const authorUrlObject = JSONPath.getTarget(response_body, authorUrlJSONPath, slicedRandomNumbers ? [...slicedRandomNumbers] : undefined, false)?.Object;
const authorUrlObject = JSONPath.getTarget(response_body, JSONPath.replaceRandomInPath(authorUrlJSONPath, resolvedPath))[0];
if (typeof authorUrlObject === 'string' || typeof authorUrlObject === 'number')
authorUrl = this._settings.getString('author-url-prefix') + String(authorUrlObject);
else
Expand Down
80 changes: 44 additions & 36 deletions src/jsonPath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,13 @@ import * as Utils from './utils.js';
*
* @param {unknown} inputObject A JSON object
* @param {string} inputString JSONPath to follow, see wiki for syntax
* @param {number[]} randomNumbers Array of pre-generated numbers
* @param {boolean} newRandomness Whether to ignore a given randomNumbers array
*/
function getTarget(inputObject: unknown, inputString: string, randomNumbers?: number[], newRandomness?: boolean): { Object: unknown, RandomNumbers?: number[] } | null {
function getTarget(inputObject: unknown, inputString: string): [object: unknown, chosenPath: string] {
if (!inputObject)
return null;
return [null, ''];

if (inputString.length === 0) {
return {
Object: inputObject,
RandomNumbers: randomNumbers,
};
}

if (!randomNumbers) {
randomNumbers = [];
newRandomness = true;
}
if (inputString.length === 0)
return [inputObject, inputString];

let startDot = inputString.indexOf('.');
if (startDot === -1)
Expand All @@ -40,40 +29,31 @@ function getTarget(inputObject: unknown, inputString: string, randomNumbers?: nu
// Expect Object here
const targetObject = _getObjectMember(inputObject, keyString);
if (!targetObject)
return null;
return [null, ''];

return getTarget(targetObject, inputStringTail, randomNumbers, newRandomness);
const [object, path] = getTarget(targetObject, inputStringTail);
return [object, inputString.slice(0, inputString.length - inputStringTail.length) + path];
} else {
const indexString = keyString.slice(startParentheses + 1, keyString.length - 1);
keyString = keyString.slice(0, startParentheses);

// Expect an Array at this point
const targetObject = _getObjectMember(inputObject, keyString);
if (!targetObject || !Array.isArray(targetObject))
return null;
return [null, ''];

switch (indexString) {
case '@random': {
let randomNumber: number = -1;
let randomElement: unknown = null;

if (!newRandomness && randomNumbers.length > 0) {
// Take and remove first element
randomNumber = randomNumbers.shift() ?? -1;
randomElement = targetObject[randomNumber];
} else {
[randomElement, randomNumber] = _randomElement(targetObject);

if (newRandomness)
randomNumbers.push(randomNumber);
}

return getTarget(randomElement, inputStringTail, randomNumbers, newRandomness);
const [chosenElement, chosenNumber] = _randomElement(targetObject);
const [object, path] = getTarget(chosenElement, inputStringTail);
return [object, inputString.slice(0, inputString.length - inputStringTail.length).replace('@random', String(chosenNumber)) + path];
}
// add special keywords here
default:
default: {
// expecting integer
return getTarget(targetObject[parseInt(indexString)], inputStringTail, randomNumbers, newRandomness);
const [object, path] = getTarget(targetObject[parseInt(indexString)], inputStringTail);
return [object, inputString.slice(0, inputString.length - inputStringTail.length) + path];
}
}
}
}
Expand Down Expand Up @@ -106,4 +86,32 @@ function _randomElement<T>(array: Array<T>): [T, number] {
return [array[randomNumber], randomNumber];
}

export {getTarget};
/**
* Replace '@random' according to an already resolved path.
*
* '@random' would yield different results so this makes sure the values stay
* the same as long as the path is identical.
*
* @param {string} randomPath Path containing '@random' to resolve
* @param {string} resolvedPath Path with resolved '@random'
*/
function replaceRandomInPath(randomPath: string, resolvedPath: string): string {
if (!randomPath.includes('@random'))
return randomPath;

let newPath = randomPath;
while (newPath.includes('@random')) {
const startRandom = newPath.indexOf('@random');

// abort if path is not equal up to this point
if (newPath.substring(0, startRandom) !== resolvedPath.substring(0, startRandom))
break;

const endParenthesis = resolvedPath.indexOf(']', startRandom);
newPath = newPath.replace('@random', resolvedPath.substring(startRandom, endParenthesis));
}

return newPath;
}

export {getTarget, replaceRandomInPath};

0 comments on commit ff3f8b9

Please sign in to comment.