Skip to content

Commit 4c20fa7

Browse files
committed
feat: implement validators
1 parent 2375cbf commit 4c20fa7

File tree

13 files changed

+281
-40
lines changed

13 files changed

+281
-40
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,16 @@
66
- Add `TaskTheme` property in `task` component
77
- Add `SwapTheme` property in `swap` component
88
- Remove useless `message` property in task component
9+
- Implement validators
10+
- `notEmpty` : Check if the value is not empty
11+
- `empty` : Check if the value is empty
12+
- `email` : Check if the value is an email
13+
- `between` : Check if the value is between two values
14+
- `lowerThan` : Check if the value is lower than a value
15+
- `greaterThan` : Check if the value is greater than a value
16+
- `minLength` : Check if the value length is greater than a value
17+
- `maxLength` : Check if the value length is lower than a value
18+
- `equals` : Check if the value is equals to a value
919

1020
# 2.2.4
1121
- Make `task` component as windows compatible

README.md

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,24 @@ Then run `pub get` to install the dependencies.
2222
A simple example of using Commander to create an ask component :
2323

2424
- ✅ Secure
25-
- Validator with error message as callback
25+
- Integrated or custom validators
2626
- ✅ Default value
2727

2828
```dart
2929
Future<void> main() async {
3030
final commander = Commander(level: Level.verbose);
3131
32+
final value = await commander.ask('What is your email ?',
33+
validate: (validator) => validator
34+
..notEmpty(message: 'Name cannot be empty :)')
35+
..email(message: 'Please enter a valid email'));
36+
37+
// Custom validator
3238
final value = await commander.ask('What is your name ?',
33-
defaultValue: 'John Doe',
34-
validate: (value) {
35-
return switch (value) {
36-
String(:final isEmpty) when isEmpty => 'Name cannot be empty',
37-
_ => null,
38-
};
39-
});
39+
validate: (validator) => validator
40+
..validate((value) => value == 'Bob'
41+
? 'Bob is not allowed'
42+
: null));
4043
4144
print(value);
4245
}
@@ -202,3 +205,41 @@ Future<void> main() async {
202205
Future<void> wait() =>
203206
Future.delayed(Duration(seconds: Random().nextInt(3) + 1));
204207
```
208+
209+
## Theming
210+
211+
Commander provides a theming system to customize the appearance of the components.
212+
It is possible to define a global theme for all components or a specific theme for each component.
213+
214+
```dart
215+
Future<void> main() async {
216+
final commander = Commander(
217+
level: Level.verbose,
218+
componentTheme: ComponentTheme(
219+
askTheme: DefaultAskTheme.copyWith(askPrefix: '🤖')
220+
));
221+
222+
final value = await commander.ask('What is your email ?',
223+
validate: (validator) => validator
224+
..notEmpty(message: 'Name cannot be empty :)')
225+
..email(message: 'Please enter a valid email'));
226+
227+
print(value);
228+
}
229+
```
230+
231+
Each component that interacts with the user has a `theme` property that allows the appearance to be customised.
232+
233+
```dart
234+
Future<void> main() async {
235+
final commander = Commander(level: Level.verbose);
236+
237+
final value = await commander.ask('What is your email ?',
238+
theme: DefaultAskTheme.copyWith(askPrefix: '🤖'),
239+
validate: (validator) => validator
240+
..notEmpty(message: 'Name cannot be empty :)')
241+
..email(message: 'Please enter a valid email'));
242+
243+
print(value);
244+
}
245+
```

example/ask.dart

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,11 @@
11
import 'package:commander_ui/commander_ui.dart';
2-
import 'package:commander_ui/src/application/themes/default_ask_theme.dart';
32

43
Future<void> main() async {
5-
final theme = DefaultAskTheme.copyWith(askPrefix: '🤖');
6-
74
final commander = Commander(level: Level.verbose);
85

96
final value = await commander.ask('What is your name ?',
10-
// defaultValue: 'John Doe',
11-
validate: (value) {
12-
return switch (value) {
13-
String(:final isEmpty) when isEmpty => 'Name cannot be empty',
14-
_ => null,
15-
};
16-
});
7+
validate: (validator) =>
8+
validator..notEmpty(message: 'Name cannot be empty :)'));
179

1810
print(value);
1911
}

lib/commander_ui.dart

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,26 @@
1-
export 'package:commander_ui/src/commander.dart';
2-
export 'package:commander_ui/src/level.dart';
3-
export 'package:commander_ui/src/application/utils/terminal_tools.dart';
4-
export 'package:commander_ui/src/domains/models/commander_theme.dart';
5-
export 'package:commander_ui/src/domains/models/component.dart';
6-
7-
export 'package:commander_ui/src/application/components/select.dart';
81
export 'package:commander_ui/src/application/components/ask.dart';
92
export 'package:commander_ui/src/application/components/checkbox.dart';
103
export 'package:commander_ui/src/application/components/screen.dart';
4+
export 'package:commander_ui/src/application/components/select.dart';
115
export 'package:commander_ui/src/application/components/swap.dart';
126
export 'package:commander_ui/src/application/components/table.dart';
137
export 'package:commander_ui/src/application/components/task.dart';
14-
8+
export 'package:commander_ui/src/application/themes/default_ask_theme.dart';
9+
export 'package:commander_ui/src/application/themes/default_checkbox_theme.dart';
10+
export 'package:commander_ui/src/application/themes/default_select_theme.dart';
11+
export 'package:commander_ui/src/application/themes/default_swap_theme.dart';
12+
export 'package:commander_ui/src/application/themes/default_task_theme.dart';
13+
export 'package:commander_ui/src/application/utils/terminal_tools.dart';
14+
export 'package:commander_ui/src/application/validators/chain_validator.dart';
15+
export 'package:commander_ui/src/commander.dart';
16+
export 'package:commander_ui/src/domains/models/chain_validator.dart';
17+
export 'package:commander_ui/src/domains/models/commander_theme.dart';
18+
export 'package:commander_ui/src/domains/models/component.dart';
19+
export 'package:commander_ui/src/domains/models/component_theme.dart';
20+
export 'package:commander_ui/src/domains/themes/ask_theme.dart';
21+
export 'package:commander_ui/src/domains/themes/checkbox_theme.dart';
22+
export 'package:commander_ui/src/domains/themes/select_theme.dart';
23+
export 'package:commander_ui/src/domains/themes/swap_theme.dart';
24+
export 'package:commander_ui/src/domains/themes/task_theme.dart';
25+
export 'package:commander_ui/src/level.dart';
1526
export 'package:mansion/mansion.dart';

lib/src/application/components/ask.dart

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import 'dart:io';
44
import 'package:commander_ui/src/application/terminals/terminal.dart';
55
import 'package:commander_ui/src/application/themes/default_ask_theme.dart';
66
import 'package:commander_ui/src/application/utils/terminal_tools.dart';
7+
import 'package:commander_ui/src/application/validators/chain_validator.dart';
8+
import 'package:commander_ui/src/domains/models/chain_validator.dart';
79
import 'package:commander_ui/src/domains/models/component.dart';
810
import 'package:commander_ui/src/domains/themes/ask_theme.dart';
911
import 'package:mansion/mansion.dart';
@@ -15,10 +17,10 @@ final class Ask<T> with TerminalTools implements Component<Future<T>> {
1517
final Terminal _terminal;
1618
final AskTheme _theme;
1719

18-
late final String _message;
19-
late final String? _defaultValue;
20-
late final bool _hidden;
21-
late final String? Function(String value)? _validate;
20+
final String _message;
21+
final String? _defaultValue;
22+
final bool _hidden;
23+
final Function(TextualChainValidator)? _validate;
2224

2325
/// Creates a new instance of [Ask].
2426
bool get _hasDefault => _defaultValue != null && '$_defaultValue'.isNotEmpty;
@@ -37,7 +39,7 @@ final class Ask<T> with TerminalTools implements Component<Future<T>> {
3739
{required String message,
3840
String? defaultValue,
3941
bool hidden = false,
40-
String? Function(String value)? validate,
42+
Function(TextualChainValidator)? validate,
4143
AskTheme? theme})
4244
: _message = message,
4345
_defaultValue = defaultValue,
@@ -62,7 +64,10 @@ final class Ask<T> with TerminalTools implements Component<Future<T>> {
6264
input == null || input.isEmpty ? _resolvedDefaultValue : input;
6365

6466
if (_validate != null) {
65-
final result = _validate!(response);
67+
final validator = ValidatorChain();
68+
_validate!(validator);
69+
70+
final result = validator.execute(response);
6671
if (result case String error) {
6772
_onError(error);
6873

lib/src/application/terminals/unix_terminal.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import 'dart:ffi';
22
import 'dart:io';
33

44
import 'package:commander_ui/src/application/ffi/control_mode.dart';
5+
import 'package:commander_ui/src/application/ffi/input_mode.dart';
56
import 'package:commander_ui/src/application/ffi/local_mode.dart';
67
import 'package:commander_ui/src/application/ffi/output_mode.dart';
7-
import 'package:commander_ui/src/application/ffi/input_mode.dart';
8-
import 'package:ffi/ffi.dart';
98
import 'package:commander_ui/src/application/terminals/terminal.dart';
9+
import 'package:ffi/ffi.dart';
1010

1111
class UnixTerminal implements Terminal {
1212
late final DynamicLibrary _lib;

lib/src/application/themes/default_checkbox_theme.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:commander_ui/commander_ui.dart';
2-
import 'package:commander_ui/src/domains/themes/checkbox_theme.dart';
32

43
final class DefaultCheckBoxTheme implements CheckboxTheme {
54
@override

lib/src/application/themes/default_select_theme.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:commander_ui/commander_ui.dart';
2-
import 'package:commander_ui/src/domains/themes/select_theme.dart';
32

43
final class DefaultSelectTheme implements SelectTheme {
54
@override

lib/src/application/themes/default_swap_theme.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:commander_ui/commander_ui.dart';
2-
import 'package:commander_ui/src/domains/themes/swap_theme.dart';
32

43
final class DefaultSwapTheme implements SwapTheme {
54
@override

lib/src/application/themes/default_task_theme.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import 'package:commander_ui/commander_ui.dart';
2-
import 'package:commander_ui/src/domains/themes/task_theme.dart';
32

43
final class DefaultTaskTheme implements TaskTheme {
54
@override

0 commit comments

Comments
 (0)