Skip to content

Commit cccc3a5

Browse files
committed
Type-safe(r) args
1 parent d941bd2 commit cccc3a5

File tree

4 files changed

+160
-46
lines changed

4 files changed

+160
-46
lines changed

lib/src/args_wrapper.dart

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import 'package:args/args.dart';
2+
import 'package:meta/meta.dart';
3+
4+
abstract class Arg<T> {
5+
String name;
6+
7+
final String help;
8+
final String abbr;
9+
final bool hide;
10+
11+
String valueHelp;
12+
13+
Arg._(
14+
this.name, {
15+
@required this.abbr,
16+
@required this.help,
17+
@required this.hide,
18+
});
19+
}
20+
21+
class Flag extends Arg<bool> {
22+
final bool negatable;
23+
final bool defaultsTo;
24+
25+
Flag(
26+
String name, {
27+
this.negatable,
28+
String abbr,
29+
String help,
30+
bool hide,
31+
this.defaultsTo,
32+
}) : super._(name, abbr: abbr, help: help, hide: hide);
33+
}
34+
35+
class MultiOption extends Arg<List<String>> {
36+
final bool splitCommas;
37+
List<String> allowed;
38+
Map<String, String> allowedHelp;
39+
final List<String> defaultsTo;
40+
MultiOption(String name,
41+
{this.splitCommas,
42+
bool hide = false,
43+
String help,
44+
String abbr,
45+
this.allowed,
46+
this.allowedHelp,
47+
this.defaultsTo})
48+
: super._(name, abbr: abbr, help: help, hide: hide);
49+
}
50+
51+
class Option extends Arg<String> {
52+
final List<String> allowed;
53+
final Map<String, String> allowedHelp;
54+
final String defaultsTo;
55+
Option(String name,
56+
{String abbr,
57+
String help,
58+
this.allowed,
59+
this.allowedHelp,
60+
this.defaultsTo,
61+
bool hide})
62+
: super._(name, abbr: abbr, help: help, hide: hide);
63+
}
64+
65+
extension ArgResultExt on ArgResults {
66+
T get<T>(Arg<T> arg) => this[arg.name] as T;
67+
bool argWasParsed(Arg arg) => wasParsed(arg.name);
68+
}
69+
70+
extension ArgParserExt on ArgParser {
71+
void add<T>(Arg<T> arg) {
72+
if (arg is Flag) {
73+
final flag = arg as Flag;
74+
addFlag(
75+
flag.name,
76+
abbr: flag.abbr,
77+
hide: flag.hide,
78+
defaultsTo: flag.defaultsTo,
79+
negatable: flag.negatable,
80+
);
81+
} else if (arg is MultiOption) {
82+
final multi = arg as MultiOption;
83+
addMultiOption(
84+
multi.name,
85+
abbr: multi.abbr,
86+
help: multi.help,
87+
allowed: multi.allowed,
88+
hide: multi.hide,
89+
defaultsTo: multi.defaultsTo,
90+
allowedHelp: multi.allowedHelp,
91+
valueHelp: multi.valueHelp,
92+
splitCommas: multi.splitCommas,
93+
);
94+
} else {
95+
final option = arg as Option;
96+
addOption(
97+
option.name,
98+
abbr: option.abbr,
99+
help: option.help,
100+
allowed: option.allowed,
101+
hide: option.hide,
102+
defaultsTo: option.defaultsTo,
103+
allowedHelp: option.allowedHelp,
104+
valueHelp: option.valueHelp,
105+
);
106+
}
107+
}
108+
}

lib/src/command/add.dart

Lines changed: 50 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import '../args_wrapper.dart';
56
import 'package:pub_semver/pub_semver.dart';
67
import 'package:yaml/yaml.dart';
78

@@ -36,44 +37,47 @@ class AddCommand extends PubCommand {
3637
@override
3738
String get docUrl => 'https://dart.dev/tools/pub/cmd/pub-add';
3839
@override
39-
bool get isOffline => argResults['offline'];
40-
41-
bool get isDev => argResults['dev'];
42-
bool get isDryRun => argResults['dry-run'];
43-
String get gitUrl => argResults['git-url'];
44-
String get gitPath => argResults['git-path'];
45-
String get gitRef => argResults['git-ref'];
46-
String get hostUrl => argResults['hosted-url'];
47-
String get path => argResults['path'];
48-
String get sdk => argResults['sdk'];
40+
bool get isOffline => argResults.get(isOfflineFlag);
41+
42+
final Flag isOfflineFlag = Flag('offline',
43+
help: 'Use cached packages instead of accessing the network.');
44+
final Flag isDev = Flag('dev',
45+
abbr: 'd',
46+
negatable: false,
47+
help: 'Adds package to the development dependencies instead.');
48+
final Flag isDryRun = Flag('dry-run',
49+
help: 'Use cached packages instead of accessing the network.',
50+
negatable: false);
51+
52+
final Flag precompile = Flag('precompile',
53+
help: 'Precompile executables in immediate dependencies.');
54+
55+
final Option gitUrl = Option('git-url', help: 'Git URL of the package');
56+
final Option gitRef =
57+
Option('git-ref', help: 'Git branch or commit to be retrieved');
58+
final Option gitPath =
59+
Option('git-path', help: 'Path of git package in repository');
60+
final Option hostUrl =
61+
Option('hosted-url', help: 'URL of package host server');
62+
final Option path = Option('path', help: 'Local path');
63+
final Option sdk = Option('sdk', help: 'SDK source for package');
4964

5065
bool get hasGitOptions => gitUrl != null || gitRef != null || gitPath != null;
5166
bool get hasHostOptions => hostUrl != null;
5267

5368
AddCommand() {
54-
argParser.addFlag('dev',
55-
abbr: 'd',
56-
negatable: false,
57-
help: 'Adds package to the development dependencies instead.');
58-
59-
argParser.addOption('git-url', help: 'Git URL of the package');
60-
argParser.addOption('git-ref',
61-
help: 'Git branch or commit to be retrieved');
62-
argParser.addOption('git-path', help: 'Path of git package in repository');
63-
argParser.addOption('hosted-url', help: 'URL of package host server');
64-
argParser.addOption('path', help: 'Local path');
65-
argParser.addOption('sdk', help: 'SDK source for package');
66-
67-
argParser.addFlag('offline',
68-
help: 'Use cached packages instead of accessing the network.');
69-
70-
argParser.addFlag('dry-run',
71-
abbr: 'n',
72-
negatable: false,
73-
help: "Report what dependencies would change but don't change any.");
74-
75-
argParser.addFlag('precompile',
76-
help: 'Precompile executables in immediate dependencies.');
69+
argParser.add(isDev);
70+
argParser.add(gitUrl);
71+
argParser.add(gitRef);
72+
argParser.add(gitPath);
73+
argParser.add(hostUrl);
74+
argParser.add(sdk);
75+
76+
argParser.add(isOfflineFlag);
77+
78+
argParser.add(isDryRun);
79+
80+
argParser.add(precompile);
7781
}
7882

7983
@override
@@ -126,7 +130,7 @@ class AddCommand extends PubCommand {
126130
'does not satisfy constraint "${package.constraint}".');
127131
}
128132

129-
if (isDryRun) {
133+
if (argResults.get(isDryRun)) {
130134
/// Even if it is a dry run, run `acquireDependencies` so that the user
131135
/// gets a report on the other packages that might change version due
132136
/// to this new dependency.
@@ -138,19 +142,19 @@ class AddCommand extends PubCommand {
138142
.acquireDependencies(
139143
SolveType.GET,
140144
dryRun: true,
141-
precompile: argResults['precompile'],
145+
precompile: argResults.get(precompile),
142146
);
143147
} else {
144148
/// Update the `pubspec.yaml` before calling [acquireDependencies] to
145149
/// ensure that the modification timestamp on `pubspec.lock` and
146150
/// `.dart_tool/package_config.json` is newer than `pubspec.yaml`,
147151
/// ensuring that [entrypoint.assertUptoDate] will pass.
148-
_updatePubspec(resultPackage, packageInformation, isDev);
152+
_updatePubspec(resultPackage, packageInformation, argResults.get(isDev));
149153

150154
/// Create a new [Entrypoint] since we have to reprocess the updated
151155
/// pubspec file.
152156
await Entrypoint.current(cache).acquireDependencies(SolveType.GET,
153-
precompile: argResults['precompile']);
157+
precompile: argResults.get(precompile));
154158
}
155159

156160
if (isOffline) {
@@ -172,7 +176,7 @@ class AddCommand extends PubCommand {
172176
final devDependencyNames =
173177
devDependencies.map((devDependency) => devDependency.name);
174178

175-
if (isDev) {
179+
if (argResults.get(isDev)) {
176180
/// TODO(walnut): Change the error message once pub upgrade --bump is
177181
/// released
178182
if (devDependencyNames.contains(package.name)) {
@@ -252,18 +256,19 @@ class AddCommand extends PubCommand {
252256
ArgumentError.checkNotNull(package, 'package');
253257

254258
final _conflictingFlagSets = [
255-
['git-url', 'git-ref', 'git-path'],
256-
['hosted-url'],
257-
['path'],
258-
['sdk'],
259+
[gitUrl, gitRef, gitPath],
260+
[hostUrl],
261+
[path],
262+
[sdk],
259263
];
260264

261-
for (final flag
262-
in _conflictingFlagSets.expand((s) => s).where(argResults.wasParsed)) {
265+
for (final flag in _conflictingFlagSets
266+
.expand((s) => s)
267+
.where(argResults.argWasParsed)) {
263268
final conflictingFlag = _conflictingFlagSets
264269
.where((s) => !s.contains(flag))
265270
.expand((s) => s)
266-
.firstWhere(argResults.wasParsed, orElse: () => null);
271+
.firstWhere(argResults.argWasParsed, orElse: () => null);
267272
if (conflictingFlag != null) {
268273
usageException(
269274
'Packages can only have one source, "pub add" flags "--$flag" and '

lib/src/validator.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:args/args.dart';
78
import 'package:meta/meta.dart';
89
import 'package:pub_semver/pub_semver.dart';
910

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ environment:
66
dependencies:
77
# Note: Pub's test infrastructure assumes that any dependencies used in tests
88
# will be hosted dependencies.
9-
analyzer: ^0.40.0
9+
analyzer: ^0.41.2
1010
args: ^1.4.1
1111
async: ^2.0.0
1212
cli_util: ^0.2.0

0 commit comments

Comments
 (0)