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
This commit is contained in:
Dmitry Muhomor 2023-08-23 12:31:45 +03:00 committed by Daniel Micay
parent 4503081dbf
commit 81adbb0c42
3 changed files with 71 additions and 14 deletions

View file

@ -193,7 +193,7 @@ export default class GenerateFull extends Command {
args: { config: configPath }, args: { config: configPath },
} = this.parse(GenerateFull) } = this.parse(GenerateFull)
let devices = await loadDeviceConfigs(configPath) let devices = await loadDeviceConfigs([configPath])
await forEachDevice( await forEachDevice(
devices, devices,

View file

@ -86,7 +86,7 @@ export default class GeneratePrep extends Command {
args: { config: configPath }, args: { config: configPath },
} = this.parse(GeneratePrep) } = this.parse(GeneratePrep)
let devices = await loadDeviceConfigs(configPath) let devices = await loadDeviceConfigs([configPath])
await forEachDevice( await forEachDevice(
devices, devices,

View file

@ -2,11 +2,13 @@
// eslint-disable-next-line @typescript-eslint/triple-slash-reference // eslint-disable-next-line @typescript-eslint/triple-slash-reference
///<reference path="../util/jstypes.d.ts" /> ///<reference path="../util/jstypes.d.ts" />
import { flags } from '@oclif/command'
import assert from 'assert' import assert from 'assert'
import path from 'path' import path from 'path'
import { loadAndMergeConfig } from './config-loader' import { loadAndMergeConfig } from './config-loader'
import { FilterMode, Filters, SerializedFilters } from './filters' import { FilterMode, Filters, SerializedFilters } from './filters'
import { DEVICE_CONFIG_DIR } from './paths'
export enum ConfigType { export enum ConfigType {
Device = 'device', 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<DeviceConfig>[] = []
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<string, DeviceConfig>()
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) { async function loadAndMergeDeviceConfig(configPath: string) {
return await loadAndMergeConfig(configPath, DEFAULT_CONFIG_BASE) return await loadAndMergeConfig(configPath, DEFAULT_CONFIG_BASE)
} }
export async function loadDeviceConfigs(configPath: string) { async function loadDeviceConfigFromPath(configPath: string): Promise<DeviceConfig> {
let merged = await loadAndMergeDeviceConfig(configPath) 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<Promise<DeviceConfig>[]> {
let merged = await loadAndMergeDeviceConfig(configPath)
let type = merged.type
delete merged.type delete merged.type
if (type == ConfigType.Device) { if (type === ConfigType.Device) {
let configName = path.basename(configPath, '.yml') let res = merged as DeviceConfig
let deviceName = merged.device.name checkConfigName(res, configPath)
assert(configName === deviceName, `config name doesn't match device name (${deviceName}): ${configPath}`) return [Promise.resolve(res)]
return [merged as DeviceConfig] } else if (type === ConfigType.DeviceList) {
}
if (type == ConfigType.DeviceList) {
// Load all the device configs // Load all the device configs
let list = merged as DeviceListConfig let list = merged as DeviceListConfig
let devices: DeviceConfig[] = [] let devices: Promise<DeviceConfig>[] = []
for (let devicePath of list.devices) { for (let devicePath of list.devices) {
devicePath = path.resolve(path.dirname(configPath), devicePath) devicePath = path.resolve(path.dirname(configPath), devicePath)
devices.push(await loadAndMergeDeviceConfig(devicePath)) devices.push(loadDeviceConfigFromPath(devicePath))
} }
return devices return devices
} }
throw new Error(`Unknown config type ${type}`) throw new Error(`Unknown config type ${type}`)