| // Copyright (C) 2021 The Android Open Source Project |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use size file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| import { |
| Args, |
| ArgsTree, |
| ArgsTreeArray, |
| ArgsTreeMap, |
| isArgTreeArray, |
| isArgTreeMap, |
| } from '../common/arg_types'; |
| |
| // Converts a flats sequence of key-value pairs into a JSON-like nested |
| // structure. Dots in keys are used to create a nested dictionary, indices in |
| // brackets used to create nested array. For example, consider the following |
| // sequence of key-value pairs: |
| // |
| // simple_key = simple_value |
| // thing.key = value |
| // thing.point[0].x = 10 |
| // thing.point[0].y = 20 |
| // thing.point[1].x = 0 |
| // thing.point[1].y = -10 |
| // |
| // It's going to be converted to a following object: |
| // |
| // { |
| // "simple_key": "simple_value", |
| // "thing": { |
| // "key": "value", |
| // "point": [ |
| // { "x": "10", "y": "20" }, |
| // { "x": "0", "y": "-10" } |
| // ] |
| // } |
| // } |
| export function parseArgs(args: Args): ArgsTree|undefined { |
| const result: ArgsTreeMap = {}; |
| for (const [key, value] of args) { |
| if (typeof value === 'string') { |
| fillObject(result, key.split('.'), value); |
| } |
| } |
| return result; |
| } |
| |
| function getOrCreateMap( |
| object: ArgsTreeMap|ArgsTreeArray, key: string|number): ArgsTreeMap { |
| let value: ArgsTree; |
| if (isArgTreeMap(object) && typeof key === 'string') { |
| value = object[key]; |
| } else if (isArgTreeArray(object) && typeof key === 'number') { |
| value = object[key]; |
| } else { |
| throw new Error('incompatible parameters to getOrCreateSubmap'); |
| } |
| |
| if (value !== undefined) { |
| if (isArgTreeMap(value)) { |
| return value; |
| } else { |
| // There is a value, but it's not a map - something wrong with the key set |
| throw new Error('inconsistent keys'); |
| } |
| } |
| |
| value = {}; |
| if (isArgTreeMap(object) && typeof key === 'string') { |
| object[key] = value; |
| } else if (isArgTreeArray(object) && typeof key === 'number') { |
| object[key] = value; |
| } |
| |
| return value; |
| } |
| |
| function getOrCreateArray(object: ArgsTreeMap, key: string): ArgsTree[] { |
| let value = object[key]; |
| if (value !== undefined) { |
| if (isArgTreeArray(value)) { |
| return value; |
| } else { |
| // There is a value, but it's not an array - something wrong with the key |
| // set |
| throw new Error('inconsistent keys'); |
| } |
| } |
| |
| value = []; |
| object[key] = value; |
| return value; |
| } |
| |
| function fillObject(object: ArgsTreeMap, path: string[], value: string) { |
| let current = object; |
| for (let i = 0; i < path.length - 1; i++) { |
| const [part, index] = parsePathSegment(path[i]); |
| if (index === undefined) { |
| current = getOrCreateMap(current, part); |
| } else { |
| const array = getOrCreateArray(current, part); |
| current = getOrCreateMap(array, index); |
| } |
| } |
| |
| const [part, index] = parsePathSegment(path[path.length - 1]); |
| if (index === undefined) { |
| current[part] = value; |
| } else { |
| const array = getOrCreateArray(current, part); |
| array[index] = value; |
| } |
| } |
| |
| // Segment is either a simple key (e.g. "foo") or a key with an index (e.g. |
| // "bar[42]"). This function returns a pair of key and index (if present). |
| function parsePathSegment(segment: string): [string, number?] { |
| if (!segment.endsWith(']')) { |
| return [segment, undefined]; |
| } |
| |
| const indexStart = segment.indexOf('['); |
| const indexString = segment.substring(indexStart + 1, segment.length - 1); |
| return [segment.substring(0, indexStart), Math.floor(Number(indexString))]; |
| } |