blob: c9b77de1d3911e99b678338128da4efe674faa19 [file] [log] [blame] [edit]
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'dart:io';
import 'package:args/args.dart';
import 'package:cli_config/cli_config.dart' as cli_config;
import 'package:yaml/yaml.dart';
import 'config_exception.dart';
/// YAML Reader which enables to override specific values from command line.
class YamlReader {
final cli_config.Config _config;
final Uri? _configRoot;
YamlReader.of(this._config, this._configRoot);
/// Parses the provided command line arguments and returns a [YamlReader].
///
/// This is a utility function which does all things a program would do when
/// parsing command line arguments, including exiting from the program when
/// arguments are invalid.
static YamlReader parseArgs(List<String> args,
{bool allowYamlConfig = true}) {
final parser = ArgParser();
parser.addFlag('help', abbr: 'h', help: 'Show this help.');
// Sometimes it's required to change a config value for a single invocation,
// then this option can be used. Conventionally in -D switch is used in
// C to set preprocessor variable & in java to override a config property.
parser.addMultiOption('override',
abbr: 'D',
help: 'Override or assign a config property from command line.');
if (allowYamlConfig) {
parser.addOption('config', abbr: 'c', help: 'Path to YAML config.');
}
final results = parser.parse(args);
if (results['help'] as bool) {
stderr.writeln(parser.usage);
exit(1);
}
final configFilePath = results['config'] as String?;
String? configFileContents;
Uri? configFileUri;
if (configFilePath != null) {
try {
configFileContents = File(configFilePath).readAsStringSync();
configFileUri = File(configFilePath).uri;
} on Exception catch (e) {
stderr.writeln('Cannot read $configFilePath: $e.');
}
}
final regex = RegExp('([a-z-_.]+)=(.+)');
final properties = <String, String>{};
for (var prop in results['override'] as List<String>) {
final match = regex.matchAsPrefix(prop);
if (match != null && match.group(0) == prop) {
final propertyName = match.group(1);
final propertyValue = match.group(2);
properties[propertyName!] = propertyValue!;
} else {
throw ConfigException('override does not match expected pattern');
}
}
final config = cli_config.Config.fromConfigFileContents(
commandLineDefines: results['override'] as List<String>,
workingDirectory: Directory.current.uri,
environment: Platform.environment,
fileContents: configFileContents,
fileSourceUri: configFileUri,
);
return YamlReader.of(
config,
configFileUri?.resolve('.'),
);
}
bool? getBool(String property) => _config.optionalBool(property);
String? getString(String property) => _config.optionalString(property);
/// Same as [getString] but path is resolved relative to YAML config if it's
/// from YAML config.
Uri? getPath(String property) => _config.optionalPath(property);
List<String>? getStringList(String property) => _config.optionalStringList(
property,
splitCliPattern: ';',
combineAllConfigs: false,
);
List<Uri>? getPathList(String property) => _config.optionalPathList(
property,
combineAllConfigs: false,
splitCliPattern: ';',
);
String? getOneOf(String property, Set<String> values) =>
_config.optionalString(property, validValues: values);
Map<String, String>? getStringMap(String property) {
final value = _config.valueOf<YamlMap?>(property);
return value?.cast<String, String>();
}
bool hasValue(String property) => _config.valueOf<dynamic>(property) != null;
/// Returns URI of the directory containing YAML config.
Uri? getConfigRoot() => _configRoot;
}