Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mason_logger): generic support for chooseOne and chooseAny #539

Merged
merged 11 commits into from Oct 13, 2022
6 changes: 3 additions & 3 deletions packages/mason_cli/test/commands/make_test.dart
Expand Up @@ -621,10 +621,10 @@ bricks:
)..createSync(recursive: true);
Directory.current = testDir.path;
when(
() => logger.chooseOne(
() => logger.chooseOne<String>(
any(),
choices: any(named: 'choices'),
defaultValue: any(named: 'defaultValue'),
defaultValue: any<String>(named: 'defaultValue'),
),
).thenReturn('blue');
final result = await commandRunner.run(['make', 'favorite_color']);
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
26 changes: 26 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,23 @@ Future<void> main() async {
uri: Uri.parse('https://github.com/felangel/mason'),
);
logger.info('To learn more, visit the $repoLink.');

final shape = logger.chooseOne(
'What is your favorite shape?',
choices: Shape.values,
display: (Shape shape) {
return 'Is it a ${shape.toString().split('.').last}?';
wolfenrain marked this conversation as resolved.
Show resolved Hide resolved
},
);
logger.info('You chose $shape!');

final shapes = logger.chooseAny(
'Or did you want to choose multiples?',
choices: Shape.values,
defaultValues: [shape],
display: (Shape shape) {
return 'Is it a ${shape.toString().split('.').last}?';
wolfenrain marked this conversation as resolved.
Show resolved Hide resolved
},
);
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>(
String? message, {
required List<String> choices,
String? defaultValue,
required List<T> choices,
T? defaultValue,
String Function(T)? 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>(
String? message, {
required List<String> choices,
List<String>? defaultValues,
required List<T> choices,
List<T>? defaultValues,
String Function(T)? 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,
);
});
});
});
}