From 81adbb0c4249b2b2c157e1091bf9455dfc87a4f5 Mon Sep 17 00:00:00 2001 From: Dmitry Muhomor Date: Wed, 23 Aug 2023 12:31:45 +0300 Subject: [PATCH] improve device config loading - support specifying device{,-list} config with config file name instead of config path - support loading mutliple device{,-list} configs at once. Previously, loading multiple device configs was supported only by passing a device-list config - parallelize config loading with promises --- src/commands/generate-all.ts | 2 +- src/commands/generate-prep.ts | 2 +- src/config/device.ts | 81 +++++++++++++++++++++++++++++------ 3 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/commands/generate-all.ts b/src/commands/generate-all.ts index 6f6c137..b42186a 100644 --- a/src/commands/generate-all.ts +++ b/src/commands/generate-all.ts @@ -193,7 +193,7 @@ export default class GenerateFull extends Command { args: { config: configPath }, } = this.parse(GenerateFull) - let devices = await loadDeviceConfigs(configPath) + let devices = await loadDeviceConfigs([configPath]) await forEachDevice( devices, diff --git a/src/commands/generate-prep.ts b/src/commands/generate-prep.ts index 2c5e38c..0809c91 100644 --- a/src/commands/generate-prep.ts +++ b/src/commands/generate-prep.ts @@ -86,7 +86,7 @@ export default class GeneratePrep extends Command { args: { config: configPath }, } = this.parse(GeneratePrep) - let devices = await loadDeviceConfigs(configPath) + let devices = await loadDeviceConfigs([configPath]) await forEachDevice( devices, diff --git a/src/config/device.ts b/src/config/device.ts index 6c9a9eb..20ceb67 100644 --- a/src/config/device.ts +++ b/src/config/device.ts @@ -2,11 +2,13 @@ // eslint-disable-next-line @typescript-eslint/triple-slash-reference /// +import { flags } from '@oclif/command' import assert from 'assert' import path from 'path' import { loadAndMergeConfig } from './config-loader' import { FilterMode, Filters, SerializedFilters } from './filters' +import { DEVICE_CONFIG_DIR } from './paths' export enum ConfigType { Device = 'device', @@ -113,30 +115,85 @@ const DEFAULT_CONFIG_BASE = { }, } +export const DEVICE_CONFIG_FLAGS = { + devices: flags.string({ + char: 'd', + description: `Device or DeviceList config paths or names`, + multiple: true, + default: ['all'], + }), +} + +// Each string should refer to a Device or DeviceList config. +// There's two supported string formats: config path and config name from config dir (without .yml suffix), +// i.e. path/to/device_name.yml and device_name +export async function loadDeviceConfigs(strings: string[]) { + const configFileSuffix = '.yml' + + let promises: Promise[] = [] + + for (let string of strings) { + let configPath: string + if (string.endsWith(configFileSuffix)) { + configPath = string + } else { + configPath = path.join(DEVICE_CONFIG_DIR, string + configFileSuffix) + } + promises.push(...(await loadDeviceConfigsFromPath(configPath))) + } + + // Map is used to make sure there's at most one config per device + let map = new Map() + + for await (let config of promises) { + let key = config.device.name + if (map.get(key) !== undefined) { + console.warn(`loadDeviceConfigs: more than one config was passed for ${key}, only the last one will be used`) + } + map.set(key, config) + } + + return Array.from(map.values()) +} + async function loadAndMergeDeviceConfig(configPath: string) { return await loadAndMergeConfig(configPath, DEFAULT_CONFIG_BASE) } -export async function loadDeviceConfigs(configPath: string) { +async function loadDeviceConfigFromPath(configPath: string): Promise { let merged = await loadAndMergeDeviceConfig(configPath) - let { type } = merged + let type = merged.type + delete merged.type + assert(type === ConfigType.Device) + + let res = merged as DeviceConfig + checkConfigName(res, configPath) + return res +} + +function checkConfigName(config: DeviceConfig, configPath: string) { + let configName = path.basename(configPath, '.yml') + let deviceName = config.device.name + assert(configName === deviceName, `config name doesn't match device name (${deviceName}): ${configPath}`) +} + +async function loadDeviceConfigsFromPath(configPath: string): Promise[]> { + let merged = await loadAndMergeDeviceConfig(configPath) + let type = merged.type delete merged.type - if (type == ConfigType.Device) { - let configName = path.basename(configPath, '.yml') - let deviceName = merged.device.name - assert(configName === deviceName, `config name doesn't match device name (${deviceName}): ${configPath}`) - return [merged as DeviceConfig] - } - if (type == ConfigType.DeviceList) { + if (type === ConfigType.Device) { + let res = merged as DeviceConfig + checkConfigName(res, configPath) + return [Promise.resolve(res)] + } else if (type === ConfigType.DeviceList) { // Load all the device configs let list = merged as DeviceListConfig - let devices: DeviceConfig[] = [] + let devices: Promise[] = [] for (let devicePath of list.devices) { devicePath = path.resolve(path.dirname(configPath), devicePath) - devices.push(await loadAndMergeDeviceConfig(devicePath)) + devices.push(loadDeviceConfigFromPath(devicePath)) } - return devices } throw new Error(`Unknown config type ${type}`)