Skip to content

Commit

Permalink
feat(mason_logger): generic support for chooseOne and chooseAny (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfenrain committed Oct 13, 2022
1 parent 0184b2b commit 8adaa25
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 18 deletions.
4 changes: 2 additions & 2 deletions packages/mason_cli/test/commands/make_test.dart
Expand Up @@ -621,7 +621,7 @@ bricks:
)..createSync(recursive: true);
Directory.current = testDir.path;
when(
() => logger.chooseOne(
() => logger.chooseOne<String>(
any(),
choices: any(named: 'choices'),
defaultValue: any(named: 'defaultValue'),
Expand Down Expand Up @@ -680,7 +680,7 @@ bricks:
)..createSync(recursive: true);
Directory.current = testDir.path;
when(
() => logger.chooseAny(
() => logger.chooseAny<String>(
any(),
choices: any(named: 'choices'),
defaultValues: any(named: 'defaultValues'),
Expand Down
22 changes: 22 additions & 0 deletions packages/mason_logger/example/main.dart
@@ -1,5 +1,12 @@
import 'package:mason_logger/mason_logger.dart';

enum Shape {
square,
circle,
triangle,
diamond,
}

Future<void> main() async {
final logger = Logger(level: Level.verbose)
..info('info')
Expand Down Expand Up @@ -52,4 +59,19 @@ Future<void> main() async {
uri: Uri.parse('https://github.com/felangel/mason'),
);
logger.info('To learn more, visit the $repoLink.');

final shape = logger.chooseOne<Shape>(
'What is your favorite shape?',
choices: Shape.values,
display: (shape) => 'Is it a ${shape.name}?',
);
logger.info('You chose $shape!');

final shapes = logger.chooseAny<Shape>(
'Or did you want to choose multiples?',
choices: Shape.values,
defaultValues: [shape],
display: (shape) => 'Is it a ${shape.name}?',
);
logger.info('You chose the following shapes: $shapes!');
}
37 changes: 21 additions & 16 deletions packages/mason_logger/lib/src/mason_logger.dart
Expand Up @@ -144,12 +144,15 @@ class Logger {
///
/// An optional [defaultValue] can be specified.
/// The [defaultValue] must be one of the provided [choices].
String chooseOne(
T chooseOne<T extends Object?>(
String? message, {
required List<String> choices,
String? defaultValue,
required List<T> choices,
T? defaultValue,
String Function(T choice)? display,
}) {
final hasDefault = defaultValue != null && defaultValue.isNotEmpty;
final _display = display ?? (value) => '$value';
final hasDefault =
defaultValue != null && _display(defaultValue).isNotEmpty;
var index = hasDefault ? choices.indexOf(defaultValue) : 0;

void writeChoices() {
Expand All @@ -166,11 +169,11 @@ class Logger {
if (isCurrent) {
_stdout
..write(green.wrap('❯'))
..write(' $checkBox ${lightCyan.wrap(choice)}');
..write(' $checkBox ${lightCyan.wrap(_display(choice))}');
} else {
_stdout
..write(' ')
..write(' $checkBox $choice');
..write(' $checkBox ${_display(choice)}');
}
if (choices.last != choice) {
_stdout.write('\n');
Expand All @@ -185,8 +188,8 @@ class Logger {
writeChoices();

final event = <int>[];
var result = '';
while (result.isEmpty) {
T? result;
while (result == null) {
final byte = _stdin.readByteSync();
if (event.length == 3) event.clear();
event.add(byte);
Expand All @@ -209,7 +212,7 @@ class Logger {
// show cursor
..write('\x1b[?25h')
..write('$message ')
..writeln(styleDim.wrap(lightCyan.wrap(choices[index])));
..writeln(styleDim.wrap(lightCyan.wrap(_display(choices[index]))));

result = choices[index];
break;
Expand All @@ -220,19 +223,21 @@ class Logger {
writeChoices();
}

return result;
return result!;
}

/// Prompts user with [message] to choose zero or more values
/// from the provided [choices].
///
/// An optional list of [defaultValues] can be specified.
/// The [defaultValues] must be one of the provided [choices].
List<String> chooseAny(
List<T> chooseAny<T extends Object?>(
String? message, {
required List<String> choices,
List<String>? defaultValues,
required List<T> choices,
List<T>? defaultValues,
String Function(T choice)? display,
}) {
final _display = display ?? (value) => '$value';
final hasDefaults = defaultValues != null && defaultValues.isNotEmpty;
final selections = hasDefaults
? defaultValues.map((value) => choices.indexOf(value)).toSet()
Expand All @@ -255,11 +260,11 @@ class Logger {
if (isCurrent) {
_stdout
..write(green.wrap('❯'))
..write(' $checkBox ${lightCyan.wrap(choice)}');
..write(' $checkBox ${lightCyan.wrap(_display(choice))}');
} else {
_stdout
..write(' ')
..write(' $checkBox $choice');
..write(' $checkBox ${_display(choice)}');
}
if (choices.last != choice) {
_stdout.write('\n');
Expand All @@ -274,7 +279,7 @@ class Logger {
writeChoices();

final event = <int>[];
List<String>? results;
List<T>? results;
while (results == null) {
final byte = _stdin.readByteSync();
if (event.length == 3) event.clear();
Expand Down
67 changes: 67 additions & 0 deletions packages/mason_logger/test/src/mason_logger_test.dart
Expand Up @@ -904,6 +904,38 @@ void main() {
stdin: () => stdin,
);
});

test('converts list to a preferred display', () {
IOOverrides.runZoned(
() {
const message = 'test message';
when(() => stdin.readByteSync()).thenReturn(10);
final actual = Logger().chooseAny<Map<String, String>>(
message,
choices: [
{'key': 'a'},
{'key': 'b'},
{'key': 'c'},
],
display: (data) => 'Key: ${data['key']}',
);
expect(actual, isEmpty);
verifyInOrder([
() => stdout.write('\x1b7'),
() => stdout.write('\x1b[?25l'),
() => stdout.writeln(message),
() => stdout.write(green.wrap('❯')),
() => stdout.write(' ◯ ${lightCyan.wrap('Key: a')}'),
() => stdout.write(' '),
() => stdout.write(' ◯ Key: b'),
() => stdout.write(' '),
() => stdout.write(' ◯ Key: c'),
]);
},
stdout: () => stdout,
stdin: () => stdin,
);
});
});

group('.chooseOne', () {
Expand Down Expand Up @@ -1252,6 +1284,41 @@ void main() {
stdin: () => stdin,
);
});

test('converts list to a preferred display', () {
IOOverrides.runZoned(
() {
const message = 'test message';
const expected = {'key': 'a'};
when(() => stdin.readByteSync()).thenReturn(10);
final actual = Logger().chooseOne<Map<String, String>>(
message,
choices: [
{'key': 'a'},
{'key': 'b'},
{'key': 'c'},
],
display: (data) => 'Key: ${data['key']}',
);
expect(actual, equals(expected));
verifyInOrder([
() => stdout.write('\x1b7'),
() => stdout.write('\x1b[?25l'),
() => stdout.writeln(message),
() => stdout.write(green.wrap('❯')),
() => stdout.write(
' ${lightCyan.wrap('◉')} ${lightCyan.wrap('Key: a')}',
),
() => stdout.write(' '),
() => stdout.write(' ◯ Key: b'),
() => stdout.write(' '),
() => stdout.write(' ◯ Key: c'),
]);
},
stdout: () => stdout,
stdin: () => stdin,
);
});
});
});
}

0 comments on commit 8adaa25

Please sign in to comment.