Skip to content

Commit

Permalink
[cli_config] First version (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
dcharkes authored Mar 31, 2023
1 parent 1e37e1c commit 5994406
Show file tree
Hide file tree
Showing 17 changed files with 1,595 additions and 10 deletions.
2 changes: 1 addition & 1 deletion pkgs/cli_config/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
include: package:lints/recommended.yaml
include: package:dart_flutter_team_lints/analysis_options.yaml

analyzer:
language:
Expand Down
12 changes: 12 additions & 0 deletions pkgs/cli_config/example/bin/cli_config_example.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) 2023, 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 'package:cli_config/cli_config.dart';

Future<void> main(List<String> args) async {
final config = await Config.fromArgs(args: args);
final myPath =
config.optionalPath('my_path', resolveUri: true, mustExist: false);
print(myPath?.path);
}
13 changes: 13 additions & 0 deletions pkgs/cli_config/example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: cli_config_example
description: An example for cli_config.

repository: https://github.com/dart-lang/tools/tree/main/pkgs/cli_config/example

publish_to: none

environment:
sdk: ">=2.19.3 <4.0.0"

dependencies:
cli_config:
path: ../
2 changes: 1 addition & 1 deletion pkgs/cli_config/lib/cli_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
/// and environment variables.
library cli_config;

export 'src/cli_config.dart';
export 'src/config.dart';
5 changes: 0 additions & 5 deletions pkgs/cli_config/lib/src/cli_config.dart

This file was deleted.

53 changes: 53 additions & 0 deletions pkgs/cli_config/lib/src/cli_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright (c) 2023, 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 'package:args/args.dart';

class CliParser {
final ArgParser parser = () {
final parser = ArgParser();
parser.addFlag(
'help',
abbr: 'h',
help: 'Show this help.',
);
parser.addMultiOption(
'define',
abbr: 'D',
help: '''Define or override a config property from command line.
The same option can be passed multiple times.
Keys should only contain lower-case alphanumeric characters, underscores,
and '.'s''',
);
parser.addOption(
'config',
abbr: 'c',
help: '''Path to JSON or YAML config file.
Keys should only contain lower-case alphanumeric characters, and underscores.
Hierarchies should be maps.''',
);
return parser;
}();

ArgResults parse(List<String> args) => parser.parse(args);
}

class DefinesParser {
static final _defineRegex = RegExp('([a-z_.]+)=(.+)');

Map<String, List<String>> parse(List<String> args) {
final defines = <String, List<String>>{};
for (final arg in args) {
final match = _defineRegex.matchAsPrefix(arg);
if (match == null || match.group(0) != arg) {
throw FormatException("Define '$arg' does not match expected pattern "
"'${_defineRegex.pattern}'.");
}
final key = match.group(1)!;
final value = match.group(2)!;
defines[key] = (defines[key] ?? [])..add(value);
}
return defines;
}
}
95 changes: 95 additions & 0 deletions pkgs/cli_config/lib/src/cli_source.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) 2023, 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 'config.dart';
import 'source.dart';

class CliSource extends Source {
/// Configuration options passed in via CLI arguments.
///
/// Options can be passed multiple times, so the values here are a list.
///
/// Stored as a flat non-hierarchical structure, keys contain `.`.
final Map<String, List<String>> _cli;

/// If provided, used to resolve paths within [_cli].
///
/// Typically the current working directory at application start.
@override
final Uri? baseUri;

CliSource(this._cli, this.baseUri);

@override
String? optionalString(String key) {
final value = _cli[key];
if (value == null) {
return null;
}
if (value.length > 1) {
throw FormatException(
"More than one value was passed for '$key' in the CLI defines."
' Values passed: $value');
}
return value.single;
}

@override
List<String>? stringList(
String key, {
String? splitPattern,
}) {
final cliValue = _cli[key];
if (cliValue == null) {
return null;
}
if (splitPattern != null) {
return [for (final value in cliValue) ...value.split(splitPattern)];
}
return cliValue;
}

@override
bool? optionalBool(String key) {
final stringValue = optionalString(key);
if (stringValue != null) {
Source.throwIfUnexpectedValue(key, stringValue, Config.boolStrings.keys);
return Config.boolStrings[stringValue]!;
}
return null;
}

@override
int? optionalInt(String key) {
final stringValue = optionalString(key);
if (stringValue != null) {
try {
return int.parse(stringValue);
} on FormatException catch (e) {
throw FormatException(
"Unexpected value '$stringValue' for key '$key'. Expected an int."
' ${e.message}');
}
}
return null;
}

@override
double? optionalDouble(String key) {
final stringValue = optionalString(key);
if (stringValue != null) {
try {
return double.parse(stringValue);
} on FormatException catch (e) {
throw FormatException(
"Unexpected value '$stringValue' for key '$key'. Expected a double."
' ${e.message}');
}
}
return null;
}

@override
String toString() => 'CliSource($_cli)';
}
Loading

0 comments on commit 5994406

Please sign in to comment.