Browse Source

Code formatting

tags/v8.0.0
Cédric Belin 4 months ago
parent
commit
b960180b7a
25 changed files with 546 additions and 537 deletions
  1. +8
    -3
      .editorconfig
  2. +5
    -5
      .vscode/settings.json
  3. +1
    -1
      analysis_options.yaml
  4. +32
    -32
      bin/where.dart
  5. +3
    -3
      doc/index.md
  6. +8
    -8
      doc/installation.md
  7. +31
    -31
      doc/usage/api.md
  8. +4
    -4
      doc/usage/cli.md
  9. +9
    -5
      etc/mkdocs.yaml
  10. +9
    -9
      example/main.dart
  11. +4
    -4
      lib/src/cli.dart
  12. +21
    -21
      lib/src/cli.g.dart
  13. +15
    -15
      lib/src/cli/options.dart
  14. +6
    -6
      lib/src/cli/usage.dart
  15. +5
    -5
      lib/src/io.dart
  16. +105
    -105
      lib/src/io/file_stat.dart
  17. +104
    -104
      lib/src/io/finder.dart
  18. +14
    -14
      lib/src/io/where.dart
  19. +1
    -1
      lib/src/version.dart
  20. +1
    -1
      lib/where.dart
  21. +1
    -1
      pubspec.yaml
  22. +40
    -40
      test/file_stat_test.dart
  23. +54
    -54
      test/finder_test.dart
  24. +62
    -62
      test/where_test.dart
  25. +3
    -3
      tool/clean.ps1

+ 8
- 3
.editorconfig View File

@@ -3,11 +3,16 @@ root = true

[*]
charset = utf-8
indent_size = 2
indent_style = space
indent_style = tab
insert_final_newline = true
max_line_length = 200
quote_type = double
tab_width = 2
trim_trailing_whitespace = true

[*.md]
indent_size = 4
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2
indent_style = space

+ 5
- 5
.vscode/settings.json View File

@@ -1,7 +1,7 @@
{
"editor.insertSpaces": true,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
"editor.insertSpaces": false,
"editor.tabSize": 2,
"files.encoding": "utf8",
"files.insertFinalNewline": true,
"files.trimFinalNewlines": true
}

+ 1
- 1
analysis_options.yaml View File

@@ -90,6 +90,7 @@ linter:
- prefer_const_literals_to_create_immutables
- prefer_constructors_over_static_methods
- prefer_contains
- prefer_double_quotes
- prefer_equal_for_default_values
- prefer_expression_function_bodies
- prefer_final_fields
@@ -112,7 +113,6 @@ linter:
- prefer_mixin
- prefer_null_aware_operators
- prefer_relative_imports
- prefer_single_quotes
- prefer_spread_collections
- prefer_typing_uninitialized_variables
- prefer_void_to_null


+ 32
- 32
bin/where.dart View File

@@ -1,44 +1,44 @@
#!/usr/bin/env dart

// ignore_for_file: avoid_print
import 'dart:io';
import 'package:where/where.dart';
import 'package:where/src/cli.dart';
import 'package:where/src/version.dart';
import "dart:io";
import "package:where/where.dart";
import "package:where/src/cli.dart";
import "package:where/src/version.dart";

/// Application entry point.
Future<void> main(List<String> args) async {
// Parse the command line arguments.
Options options;
// Parse the command line arguments.
Options options;

try {
options = parseOptions(args);
if (options.help) return print(usage);
if (options.version) return print(packageVersion);
if (options.rest.isEmpty) throw const FormatException('A command must be provided.');
}
try {
options = parseOptions(args);
if (options.help) return print(usage);
if (options.version) return print(packageVersion);
if (options.rest.isEmpty) throw const FormatException("A command must be provided.");
}

on FormatException {
print(usage);
exitCode = 64;
return;
}
on FormatException {
print(usage);
exitCode = 64;
return;
}

// Run the program.
try {
var executables = await where(options.rest.first, all: options.all);
if (!options.silent) {
if (executables is! List<String>) executables = <String>[executables];
executables.forEach(print);
}
}
// Run the program.
try {
var executables = await where(options.rest.first, all: options.all);
if (!options.silent) {
if (executables is! List<String>) executables = <String>[executables];
executables.forEach(print);
}
}

on FinderException {
exitCode = 1;
}
on FinderException {
exitCode = 1;
}

on Exception catch (err) {
print(err);
exitCode = 2;
}
on Exception catch (err) {
print(err);
exitCode = 2;
}
}

+ 3
- 3
doc/index.md View File

@@ -6,14 +6,14 @@ Find the instances of an executable in the system path, in [Dart](https://dart.d
## Quick start
Append the following line to your project's `pubspec.yaml` file:

```yaml
``` yaml
dependencies:
where: *
where: *
```

Install the latest version of **Where.dart** with [Pub](https://dart.dev/tools/pub):

```shell
``` shell
pub get
```



+ 8
- 8
doc/installation.md View File

@@ -6,7 +6,7 @@ and [Pub](https://dart.dev/tools/pub), the Dart package manager, up and running.

You can verify if you're already good to go with the following commands:

```shell
``` shell
dart --version
# Dart VM version: 2.8.1 (stable) (Thu Apr 30 09:25:21 2020 +0200) on "windows_x64"

@@ -15,29 +15,29 @@ pub --version
```

!!! info
If you plan to play with the package sources, you will also need
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).
If you plan to play with the package sources, you will also need
[PowerShell](https://docs.microsoft.com/en-us/powershell) and [Material for MkDocs](https://squidfunk.github.io/mkdocs-material).

## Installing with Pub package manager

### 1. Depend on it
Add this to your project's `pubspec.yaml` file:

```yaml
``` yaml
dependencies:
where: *
where: *
```

### 2. Install it
Install this package and its dependencies from a command prompt:

```shell
``` shell
pub get
```

### 3. Import it
Now in your [Dart](https://dart.dev) code, you can use:

```dart
import 'package:where/where.dart';
``` dart
import "package:where/where.dart";
```

+ 31
- 31
doc/usage/api.md View File

@@ -6,19 +6,19 @@ source: lib/src/io/where.dart
# Application programming interface
This package provides a single function, `where()`, allowing to locate a command in the system path:

```dart
import 'package:where/where.dart';
``` dart
import "package:where/where.dart";

Future<void> main() async {
try {
// `path` is the absolute path to the executable.
final path = await where('foobar');
print('The command "foobar" is located at: $path');
}
on FinderException catch (err) {
print('The command "${err.command}" is not found');
}
try {
// `path` is the absolute path to the executable.
final path = await where("foobar");
print("The command 'foobar' is located at: $path");
}
on FinderException catch (err) {
print("The command '${err.command}' is not found");
}
}
```

@@ -32,38 +32,38 @@ A value indicating whether to return all executables found, instead of just the

If you pass `true` as parameter value, the function will return a `Future<List<String>>` providing all paths found, instead of a `Future<String>`:

```dart
import 'package:where/where.dart';
``` dart
import "package:where/where.dart";

Future<void> main() async {
final paths = await where('foobar', all: true);
print('The command "foobar" was found at these locations:');
for (final path in paths) print(path);
final paths = await where("foobar", all: true);
print("The command 'foobar' was found at these locations:");
for (final path in paths) print(path);
}
```

### String | List&lt;String&gt; **extensions**
The executable file extensions, provided as a string or a list of file extensions. Defaults to the list of extensions provided by the `PATHEXT` environment variable.

```dart
where('foobar', extensions: '.FOO;.EXE;.CMD');
where('foobar', extensions: ['.foo', '.exe', '.cmd']);
``` dart
where("foobar", extensions: ".FOO;.EXE;.CMD");
where("foobar", extensions: [".foo", ".exe", ".cmd"]);
```

!!! tip
The `extensions` option is only meaningful on the Windows platform,
where the executability of a file is determined from its extension.
The `extensions` option is only meaningful on the Windows platform,
where the executability of a file is determined from its extension.

### dynamic **onError**(String command)
By default, when the specified command cannot be located, a `FinderException` is thrown. You can disable this exception by providing your own error handler:

```dart
import 'package:where/where.dart';
``` dart
import "package:where/where.dart";

Future<void> main() async {
final path = await where('foobar', onError: (command) => '');
if (path.isEmpty) print('The command "foobar" was not found');
else print('The command "foobar" is located at: $path');
final path = await where("foobar", onError: (command) => "");
if (path.isEmpty) print("The command 'foobar' was not found");
else print("The command 'foobar' is located at: $path");
}
```

@@ -72,15 +72,15 @@ When an `onError` handler is provided, it is called with the command as argument
### String | List&lt;String&gt; **path**
The system path, provided as a string or a list of directories. Defaults to the list of paths provided by the `PATH` environment variable.

```dart
where('foobar', path: '/usr/local/bin:/usr/bin');
where('foobar', path: ['/usr/local/bin', '/usr/bin']);
``` dart
where("foobar", path: "/usr/local/bin:/usr/bin");
where("foobar", path: ["/usr/local/bin", "/usr/bin"]);
```

### String **pathSeparator**
The character used to separate paths in the system path. Defaults to the platform path separator (i.e. `";"` on Windows, `":"` on other platforms).

```dart
where('foobar', pathSeparator: '#');
``` dart
where("foobar", pathSeparator: "#");
// For example: "/usr/local/bin#/usr/bin"
```

+ 4
- 4
doc/usage/cli.md View File

@@ -6,16 +6,16 @@ source: lib/src/cli/options.dart
# Command line interface
From a command prompt, install the `where` executable:

```shell
``` shell
pub global activate where
```

!!! tip
Consider adding the [`pub global`](https://dart.dev/tools/pub/cmd/pub-global) executables directory to your system path.
Consider adding the [`pub global`](https://dart.dev/tools/pub/cmd/pub-global) executables directory to your system path.

Then use it to find the instances of an executable command:

```shell
``` shell
$ where --help

Find the instances of an executable in the system path.
@@ -31,7 +31,7 @@ Options:

For example:

```shell
``` shell
where --all dart
# /usr/bin/dart
```

+ 9
- 5
etc/mkdocs.yaml View File

@@ -10,19 +10,23 @@ site_dir: ../www

repo_name: git.belin.io
repo_url: https://git.belin.io/cedx/where.dart
edit_uri: ''
edit_uri: ""

copyright: Copyright &copy; 2017 - 2020 Cédric Belin
extra:
social:
- icon: fontawesome/solid/globe
link: 'https://belin.io'
link: "https://belin.io"
name: Belin.io
- icon: fontawesome/brands/github
link: 'https://github.com/cedx'
link: "https://github.com/cedx"
name: GitHub
- icon: fontawesome/brands/twitter
link: 'https://twitter.com/cedxbelin'
link: "https://twitter.com/cedxbelin"
name: Twitter
- icon: fontawesome/brands/linkedin
link: 'https://linkedin.com/in/cedxbelin'
link: "https://linkedin.com/in/cedxbelin"
name: LinkedIn

markdown_extensions:
- admonition


+ 9
- 9
example/main.dart View File

@@ -1,15 +1,15 @@
// ignore_for_file: avoid_print
import 'package:where/where.dart';
import "package:where/where.dart";

/// Finds the instances of an executable.
Future<void> main() async {
try {
// `path` is the absolute path to the executable.
final path = await where('foobar');
print('The command "foobar" is located at: $path');
}
try {
// `path` is the absolute path to the executable.
final path = await where("foobar");
print("The command 'foobar' is located at: $path");
}

on FinderException catch (err) {
print('The command "${err.command}" is not found');
}
on FinderException catch (err) {
print("The command '${err.command}' is not found");
}
}

+ 4
- 4
lib/src/cli.dart View File

@@ -1,11 +1,11 @@
/// Provides the command line interface.
library where.cli;

import 'package:build_cli_annotations/build_cli_annotations.dart';
import "package:build_cli_annotations/build_cli_annotations.dart";

part 'cli.g.dart';
part 'cli/options.dart';
part 'cli/usage.dart';
part "cli.g.dart";
part "cli/options.dart";
part "cli/usage.dart";

/// The command line argument parser.
ArgParser get argParser => _$parserForOptions;

+ 21
- 21
lib/src/cli.g.dart View File

@@ -7,31 +7,31 @@ part of where.cli;
// **************************************************************************

Options _$parseOptionsResult(ArgResults result) => Options()
..all = result['all'] as bool
..help = result['help'] as bool
..rest = result.rest
..silent = result['silent'] as bool
..version = result['version'] as bool;
..all = result["all"] as bool
..help = result["help"] as bool
..rest = result.rest
..silent = result["silent"] as bool
..version = result["version"] as bool;

ArgParser _$populateOptionsParser(ArgParser parser) => parser
..addFlag('all',
abbr: 'a',
help:
'List all instances of executables found (instead of just the first one).',
negatable: false)
..addFlag('help',
abbr: 'h', help: 'Output usage information.', negatable: false)
..addFlag('silent',
abbr: 's',
help:
'Silence the output, just return the exit code (0 if any executable is found, otherwise 1).',
negatable: false)
..addFlag('version',
abbr: 'v', help: 'Output the version number.', negatable: false);
..addFlag("all",
abbr: "a",
help:
"List all instances of executables found (instead of just the first one).",
negatable: false)
..addFlag("help",
abbr: "h", help: "Output usage information.", negatable: false)
..addFlag("silent",
abbr: "s",
help:
"Silence the output, just return the exit code (0 if any executable is found, otherwise 1).",
negatable: false)
..addFlag("version",
abbr: "v", help: "Output the version number.", negatable: false);

final _$parserForOptions = _$populateOptionsParser(ArgParser());

Options parseOptions(List<String> args) {
final result = _$parserForOptions.parse(args);
return _$parseOptionsResult(result);
final result = _$parserForOptions.parse(args);
return _$parseOptionsResult(result);
}

+ 15
- 15
lib/src/cli/options.dart View File

@@ -1,25 +1,25 @@
part of '../cli.dart';
part of "../cli.dart";

/// The parsed command line arguments.
@CliOptions()
class Options {

/// Value indicating whether to list all instances of executables found, instead of just the first one.
@CliOption(abbr: 'a', help: 'List all instances of executables found (instead of just the first one).', negatable: false)
bool all = false;
/// Value indicating whether to list all instances of executables found, instead of just the first one.
@CliOption(abbr: "a", help: "List all instances of executables found (instead of just the first one).", negatable: false)
bool all = false;

/// Value indicating whether to output usage information.
@CliOption(abbr: 'h', help: 'Output usage information.', negatable: false)
bool help = false;
/// Value indicating whether to output usage information.
@CliOption(abbr: "h", help: "Output usage information.", negatable: false)
bool help = false;

/// The remaining command-line arguments that were not parsed as options or flags.
List<String> rest = <String>[];
/// The remaining command-line arguments that were not parsed as options or flags.
List<String> rest = <String>[];

/// Value indicating whether to silence the output, and just return the exit code.
@CliOption(abbr: 's', help: 'Silence the output, just return the exit code (0 if any executable is found, otherwise 1).', negatable: false)
bool silent = false;
/// Value indicating whether to silence the output, and just return the exit code.
@CliOption(abbr: "s", help: "Silence the output, just return the exit code (0 if any executable is found, otherwise 1).", negatable: false)
bool silent = false;

/// Value indicating whether to output the version number.
@CliOption(abbr: 'v', help: 'Output the version number.', negatable: false)
bool version = false;
/// Value indicating whether to output the version number.
@CliOption(abbr: "v", help: "Output the version number.", negatable: false)
bool version = false;
}

+ 6
- 6
lib/src/cli/usage.dart View File

@@ -1,9 +1,9 @@
part of '../cli.dart';
part of "../cli.dart";

/// The usage information.
final String usage = (StringBuffer()
..writeln('Find the instances of an executable in the system path.')..writeln()
..writeln('Usage: where [options] <command>')..writeln()
..writeln('Options:')
..write(argParser.usage))
.toString();
..writeln("Find the instances of an executable in the system path.")..writeln()
..writeln("Usage: where [options] <command>")..writeln()
..writeln("Options:")
..write(argParser.usage))
.toString();

+ 5
- 5
lib/src/io.dart View File

@@ -1,9 +1,9 @@
/// Provides the I/O support.
library where.io;

import 'dart:io' as io;
import 'package:path/path.dart' as p;
import "dart:io" as io;
import "package:path/path.dart" as p;

part 'io/file_stat.dart';
part 'io/finder.dart';
part 'io/where.dart';
part "io/file_stat.dart";
part "io/finder.dart";
part "io/where.dart";

+ 105
- 105
lib/src/io/file_stat.dart View File

@@ -1,111 +1,111 @@
part of '../io.dart';
part of "../io.dart";

/// Represents the result of calling the POSIX [`stat`](http://manpages.ubuntu.com/manpages/xenial/man2/stat.2.html) function on a file system entity.
/// It is an immutable object, representing the snapshotted values returned by the `stat()` call.
class FileStat implements io.FileStat {

/// Creates new file stats from the specified native [stats].
FileStat._fromStats(io.FileStat stats, {this.gid = -1, this.uid = -1}):
accessed = stats.accessed,
changed = stats.changed,
mode = stats.mode,
modified = stats.modified,
size = stats.size,
type = stats.type;
/// Calls the operating system's `stat()` function on the specified [path].
///
/// Completes with a [FileStat] object containing the data returned by `stat()`.
/// If the call fails, completes the future with a [FileStat] object with [type] set to `FileSystemEntityType.notFound` and the other fields invalid.
static Future<FileStat> stat(String path) async {
assert(path.isNotEmpty);
final stats = await io.FileStat.stat(path);
if (Finder.isWindows) return FileStat._fromStats(stats);
final args = io.Platform.isMacOS ? ['-f', '%u:%g', '-L'] : ['--dereference', '--format=%u:%g'];
final result = await io.Process.run('stat', args..add(path));
if (result.exitCode != 0) return FileStat._fromStats(stats);
final parts = result.stdout.trim().split(':');
return parts.length != 2 ? FileStat._fromStats(stats) : FileStat._fromStats(
stats,
gid: int.tryParse(parts.last, radix: 10) ?? -1,
uid: int.tryParse(parts.first, radix: 10) ?? -1
);
}
/// Synchronously calls the operating system's `stat()` function on the specified [path].
///
/// Returns a [FileStat] object containing the data returned by `stat()`.
/// If the call fails, returns a [FileStat] object with [type] set to `FileSystemEntityType.notFound` and the other fields invalid.
static FileStat statSync(String path) { // ignore: prefer_constructors_over_static_methods
assert(path.isNotEmpty);
final stats = io.FileStat.statSync(path);
if (Finder.isWindows) return FileStat._fromStats(stats);
final args = io.Platform.isMacOS ? ['-f', '%u:%g', '-L'] : ['--dereference', '--format=%u:%g'];
final result = io.Process.runSync('stat', [...args, path]);
if (result.exitCode != 0) return FileStat._fromStats(stats);
final parts = result.stdout.trim().split(':');
return parts.length != 2 ? FileStat._fromStats(stats) : FileStat._fromStats(
stats,
gid: int.tryParse(parts.last, radix: 10) ?? -1,
uid: int.tryParse(parts.first, radix: 10) ?? -1
);
}
/// The time of the last access to the data of the file system entity.
/// On Windows platforms, this may have 1 day granularity, and be out of date by an hour.
@override
final DateTime accessed;
/// The time of the last change to the data or metadata of the file system object.
/// On Windows platforms, this is instead the file creation time.
@override
final DateTime changed;
/// The numeric identity of the file's group, or `-1` if this information is not available.
final int gid;
/// The time of the last change to the data of the file system object.
@override
final DateTime modified;
/// The mode of the file system entity. Permissions are encoded in the lower 16 bits of this number.
@override
final int mode;
/// The total size, in bytes, of the file system entity.
@override
final int size;
/// The type of the file system entity.
/// If the call to `stat()` fails, the type of the returned entity is `FileSystemEntityType.notFound`.
@override
final io.FileSystemEntityType type;
/// The numeric identity of the file's owner, or `-1` if this information is not available.
final int uid;
/// Returns the [mode] value as a human-readable string, in the format `"rwxrwxrwx"`,
/// reflecting the user, group, and world permissions to read, write, and execute the file system entity,
/// with `"-"` replacing the letter for missing permissions.
///
/// Extra permission bits may be represented by prepending `"(suid)"`, `"(guid)"`, and/or `"(sticky)"` to the mode string.
@override
String modeString() {
const codes = ['---', '--x', '-w-', '-wx', 'r--', 'r-x', 'rw-', 'rwx'];
final permissions = mode & 0xFFF;
return [
if ((permissions & 0x800) != 0) '(suid) ',
if ((permissions & 0x400) != 0) '(guid) ',
if ((permissions & 0x200) != 0) '(sticky) ',
codes[(permissions >> 6) & 0x7],
codes[(permissions >> 3) & 0x7],
codes[permissions & 0x7]
].join();
}
/// Creates new file stats from the specified native [stats].
FileStat._fromStats(io.FileStat stats, {this.gid = -1, this.uid = -1}):
accessed = stats.accessed,
changed = stats.changed,
mode = stats.mode,
modified = stats.modified,
size = stats.size,
type = stats.type;
/// Calls the operating system's `stat()` function on the specified [path].
///
/// Completes with a [FileStat] object containing the data returned by `stat()`.
/// If the call fails, completes the future with a [FileStat] object with [type] set to `FileSystemEntityType.notFound` and the other fields invalid.
static Future<FileStat> stat(String path) async {
assert(path.isNotEmpty);
final stats = await io.FileStat.stat(path);
if (Finder.isWindows) return FileStat._fromStats(stats);
final args = io.Platform.isMacOS ? ["-f", "%u:%g", "-L"] : ["--dereference", "--format=%u:%g"];
final result = await io.Process.run("stat", args..add(path));
if (result.exitCode != 0) return FileStat._fromStats(stats);
final parts = result.stdout.trim().split(":");
return parts.length != 2 ? FileStat._fromStats(stats) : FileStat._fromStats(
stats,
gid: int.tryParse(parts.last, radix: 10) ?? -1,
uid: int.tryParse(parts.first, radix: 10) ?? -1
);
}
/// Synchronously calls the operating system's `stat()` function on the specified [path].
///
/// Returns a [FileStat] object containing the data returned by `stat()`.
/// If the call fails, returns a [FileStat] object with [type] set to `FileSystemEntityType.notFound` and the other fields invalid.
static FileStat statSync(String path) { // ignore: prefer_constructors_over_static_methods
assert(path.isNotEmpty);
final stats = io.FileStat.statSync(path);
if (Finder.isWindows) return FileStat._fromStats(stats);
final args = io.Platform.isMacOS ? ["-f", "%u:%g", "-L"] : ["--dereference", "--format=%u:%g"];
final result = io.Process.runSync("stat", [...args, path]);
if (result.exitCode != 0) return FileStat._fromStats(stats);
final parts = result.stdout.trim().split(":");
return parts.length != 2 ? FileStat._fromStats(stats) : FileStat._fromStats(
stats,
gid: int.tryParse(parts.last, radix: 10) ?? -1,
uid: int.tryParse(parts.first, radix: 10) ?? -1
);
}
/// The time of the last access to the data of the file system entity.
/// On Windows platforms, this may have 1 day granularity, and be out of date by an hour.
@override
final DateTime accessed;
/// The time of the last change to the data or metadata of the file system object.
/// On Windows platforms, this is instead the file creation time.
@override
final DateTime changed;
/// The numeric identity of the file's group, or `-1` if this information is not available.
final int gid;
/// The time of the last change to the data of the file system object.
@override
final DateTime modified;
/// The mode of the file system entity. Permissions are encoded in the lower 16 bits of this number.
@override
final int mode;
/// The total size, in bytes, of the file system entity.
@override
final int size;
/// The type of the file system entity.
/// If the call to `stat()` fails, the type of the returned entity is `FileSystemEntityType.notFound`.
@override
final io.FileSystemEntityType type;
/// The numeric identity of the file's owner, or `-1` if this information is not available.
final int uid;
/// Returns the [mode] value as a human-readable string, in the format `"rwxrwxrwx"`,
/// reflecting the user, group, and world permissions to read, write, and execute the file system entity,
/// with `"-"` replacing the letter for missing permissions.
///
/// Extra permission bits may be represented by prepending `"(suid)"`, `"(guid)"`, and/or `"(sticky)"` to the mode string.
@override
String modeString() {
const codes = ["---", "--x", "-w-", "-wx", "r--", "r-x", "rw-", "rwx"];
final permissions = mode & 0xFFF;
return [
if ((permissions & 0x800) != 0) "(suid) ",
if ((permissions & 0x400) != 0) "(guid) ",
if ((permissions & 0x200) != 0) "(sticky) ",
codes[(permissions >> 6) & 0x7],
codes[(permissions >> 3) & 0x7],
codes[permissions & 0x7]
].join();
}
}

+ 104
- 104
lib/src/io/finder.dart View File

@@ -1,117 +1,117 @@
part of '../io.dart';
part of "../io.dart";

/// Finds the instances of an executable in the system path.
class Finder {

/// Creates a new finder from the following parameters:
/// - [extensions]: A string, or a list of strings, specifying the executable file extensions. Defaults to the `PATHEXT` environment variable.
/// - [path]: A string, or a list of strings, specifying the system path. Defaults to the `PATH` environment variable.
/// - [pathSeparator]: The character used to separate paths in the system path. Defaults to the platform path separator.
Finder({extensions = '', path = '', this.pathSeparator = ''}):
assert(extensions is String || extensions is List<String>),
assert(path is String || path is List<String>)
{
if (pathSeparator.isEmpty) pathSeparator = isWindows ? ';' : ':';
if (path is! List) path = path.toString().split(pathSeparator)..retainWhere((item) => item.isNotEmpty);
if (path.isEmpty) {
final pathEnv = io.Platform.environment['PATH'] ?? '';
if (pathEnv.isNotEmpty) path = pathEnv.split(pathSeparator);
}
if (extensions is! List) extensions = extensions.toString().split(pathSeparator)..retainWhere((item) => item.isNotEmpty);
if (extensions.isEmpty && isWindows) {
final pathExt = io.Platform.environment['PATHEXT'] ?? '';
extensions = pathExt.isNotEmpty ? pathExt.split(pathSeparator) : ['.exe', '.cmd', '.bat', '.com'];
}
this.extensions.addAll(List<String>.from(extensions.map((extension) => extension.toLowerCase())));
this.path.addAll(List<String>.from(path.map((directory) => directory.replaceAll(RegExp(r'^"+|"+$'), ''))));
}
/// The list of executable file extensions.
final List<String> extensions = <String>[];
/// Value indicating whether the current platform is Windows.
static bool get isWindows {
if (io.Platform.isWindows) return true;
return io.Platform.environment['OSTYPE'] == 'cygwin' || io.Platform.environment['OSTYPE'] == 'msys';
}
/// The list of system paths.
final List<String> path = <String>[];
/// The character used to separate paths in the system path.
String pathSeparator;
/// Finds the instances of the specified [command] in the system path.
Stream<io.File> find(String command) async* {
for (final directory in path) yield* _findExecutables(directory, command);
}
/// Gets a value indicating whether the specified [file] is executable.
Future<bool> isExecutable(String file) async {
assert(file.isNotEmpty);
final type = io.FileSystemEntity.typeSync(file);
if (type != io.FileSystemEntityType.file && type != io.FileSystemEntityType.link) return false;
return isWindows ? _checkFileExtension(file) : _checkFilePermissions(await FileStat.stat(file));
}
/// Checks that the specified [file] is executable according to the executable file extensions.
bool _checkFileExtension(String file) =>
extensions.contains(p.extension(file).toLowerCase()) || extensions.contains(file.toLowerCase());
/// Checks that the file referenced by the specified [fileStats] is executable according to its permissions.
Future<bool> _checkFilePermissions(FileStat fileStats) async {
// Others.
final perms = fileStats.mode;
if (perms & int.parse('001', radix: 8) != 0) return true;
// Group.
final execByGroup = int.parse('010', radix: 8);
if (perms & execByGroup != 0) return fileStats.gid == await _getProcessId('g');
// Owner.
final execByOwner = int.parse('100', radix: 8);
final userId = await _getProcessId('u');
if (perms & execByOwner != 0) return fileStats.uid == userId;
// Root.
return (perms & (execByOwner | execByGroup) != 0) && userId == 0;
}
/// Finds the instances of a [command] in the specified [directory].
Stream<io.File> _findExecutables(String directory, String command) async* {
assert(directory.isNotEmpty);
assert(command.isNotEmpty);
for (final extension in ['', ...extensions]) {
final resolvedPath = p.canonicalize('${p.join(directory, command)}${extension.toLowerCase()}');
if (await isExecutable(resolvedPath)) yield io.File(resolvedPath);
}
}
/// Gets a numeric [identity] of the process.
Future<int> _getProcessId(String identity) async {
assert(identity.isNotEmpty);
if (isWindows) return -1;
final result = await io.Process.run('id', ['-$identity']);
return result.exitCode != 0 ? -1 : int.tryParse(result.stdout.trim(), radix: 10) ?? -1;
}
/// Creates a new finder from the following parameters:
/// - [extensions]: A string, or a list of strings, specifying the executable file extensions. Defaults to the `PATHEXT` environment variable.
/// - [path]: A string, or a list of strings, specifying the system path. Defaults to the `PATH` environment variable.
/// - [pathSeparator]: The character used to separate paths in the system path. Defaults to the platform path separator.
Finder({extensions = "", path = "", this.pathSeparator = ""}):
assert(extensions is String || extensions is List<String>),
assert(path is String || path is List<String>)
{
if (pathSeparator.isEmpty) pathSeparator = isWindows ? ";" : ":";
if (path is! List) path = path.toString().split(pathSeparator)..retainWhere((item) => item.isNotEmpty);
if (path.isEmpty) {
final pathEnv = io.Platform.environment["PATH"] ?? "";
if (pathEnv.isNotEmpty) path = pathEnv.split(pathSeparator);
}
if (extensions is! List) extensions = extensions.toString().split(pathSeparator)..retainWhere((item) => item.isNotEmpty);
if (extensions.isEmpty && isWindows) {
final pathExt = io.Platform.environment["PATHEXT"] ?? "";
extensions = pathExt.isNotEmpty ? pathExt.split(pathSeparator) : [".exe", ".cmd", ".bat", ".com"];
}
this.extensions.addAll(List<String>.from(extensions.map((extension) => extension.toLowerCase())));
this.path.addAll(List<String>.from(path.map((directory) => directory.replaceAll(RegExp(r'^"+|"+$'), ""))));
}
/// The list of executable file extensions.
final List<String> extensions = <String>[];
/// Value indicating whether the current platform is Windows.
static bool get isWindows {
if (io.Platform.isWindows) return true;
return io.Platform.environment["OSTYPE"] == "cygwin" || io.Platform.environment["OSTYPE"] == "msys";
}
/// The list of system paths.
final List<String> path = <String>[];
/// The character used to separate paths in the system path.
String pathSeparator;
/// Finds the instances of the specified [command] in the system path.
Stream<io.File> find(String command) async* {
for (final directory in path) yield* _findExecutables(directory, command);
}
/// Gets a value indicating whether the specified [file] is executable.
Future<bool> isExecutable(String file) async {
assert(file.isNotEmpty);
final type = io.FileSystemEntity.typeSync(file);
if (type != io.FileSystemEntityType.file && type != io.FileSystemEntityType.link) return false;
return isWindows ? _checkFileExtension(file) : _checkFilePermissions(await FileStat.stat(file));
}
/// Checks that the specified [file] is executable according to the executable file extensions.
bool _checkFileExtension(String file) =>
extensions.contains(p.extension(file).toLowerCase()) || extensions.contains(file.toLowerCase());
/// Checks that the file referenced by the specified [fileStats] is executable according to its permissions.
Future<bool> _checkFilePermissions(FileStat fileStats) async {
// Others.
final perms = fileStats.mode;
if (perms & int.parse("001", radix: 8) != 0) return true;
// Group.
final execByGroup = int.parse("010", radix: 8);
if (perms & execByGroup != 0) return fileStats.gid == await _getProcessId("g");
// Owner.
final execByOwner = int.parse("100", radix: 8);
final userId = await _getProcessId("u");
if (perms & execByOwner != 0) return fileStats.uid == userId;
// Root.
return (perms & (execByOwner | execByGroup) != 0) && userId == 0;
}
/// Finds the instances of a [command] in the specified [directory].
Stream<io.File> _findExecutables(String directory, String command) async* {
assert(directory.isNotEmpty);
assert(command.isNotEmpty);
for (final extension in ["", ...extensions]) {
final resolvedPath = p.canonicalize("${p.join(directory, command)}${extension.toLowerCase()}");
if (await isExecutable(resolvedPath)) yield io.File(resolvedPath);
}
}
/// Gets a numeric [identity] of the process.
Future<int> _getProcessId(String identity) async {
assert(identity.isNotEmpty);
if (isWindows) return -1;
final result = await io.Process.run("id", ["-$identity"]);
return result.exitCode != 0 ? -1 : int.tryParse(result.stdout.trim(), radix: 10) ?? -1;
}
}

/// An exception caused by a [Finder] in a command lookup.
class FinderException implements io.IOException {

/// Creates a new finder exception.
FinderException(this.command, this.finder, [this.message = '']): assert(command.isNotEmpty);
/// Creates a new finder exception.
FinderException(this.command, this.finder, [this.message = ""]): assert(command.isNotEmpty);

/// The looked up command.
final String command;
/// The looked up command.
final String command;

/// The finder used to lookup the command.
final Finder finder;
/// The finder used to lookup the command.
final Finder finder;

/// A message describing the error.
final String message;
/// A message describing the error.
final String message;
}

+ 14
- 14
lib/src/io/where.dart View File

@@ -1,4 +1,4 @@
part of '../io.dart';
part of "../io.dart";

/// Finds the first instance of a [command] in the system path.
/// An [all] value indicates whether to return a [List] of all executables found, instead of just the first one.
@@ -10,21 +10,21 @@ part of '../io.dart';
/// - [path]: A string, or a list of strings, specifying the system path. Defaults to the `PATH` environment variable.
/// - [extensions]: A string, or a list of strings, specifying the executable file extensions. Defaults to the `PATHEXT` environment variable.
/// - [pathSeparator]: The character used to separate paths in the system path. Defaults to the platform path separator.
Future where(String command, {bool all = false, extensions = '', Function(String command) onError, path = '', String pathSeparator = ''}) async {
assert(command.isNotEmpty);
Future where(String command, {bool all = false, extensions = "", Function(String command) onError, path = "", String pathSeparator = ""}) async {
assert(command.isNotEmpty);

final finder = Finder(extensions: extensions, path: path, pathSeparator: pathSeparator);
final list = <String>[];
final finder = Finder(extensions: extensions, path: path, pathSeparator: pathSeparator);
final list = <String>[];

await for (final executable in finder.find(command)) {
if (!all) return executable.path;
list.add(executable.path);
}
await for (final executable in finder.find(command)) {
if (!all) return executable.path;
list.add(executable.path);
}

if (list.isEmpty) {
if (onError != null) return onError(command);
throw FinderException(command, finder, 'Command "$command" not found');
}
if (list.isEmpty) {
if (onError != null) return onError(command);
throw FinderException(command, finder, "Command '$command' not found");
}

return Set<String>.from(list).toList();
return Set<String>.from(list).toList();
}

+ 1
- 1
lib/src/version.dart View File

@@ -1,2 +1,2 @@
// Generated code. Do not modify.
const packageVersion = '7.0.0';
const packageVersion = "7.0.0";

+ 1
- 1
lib/where.dart View File

@@ -1,4 +1,4 @@
/// Find the instances of an executable in the system path.
library where;

export 'src/io.dart';
export "src/io.dart";

+ 1
- 1
pubspec.yaml View File

@@ -7,7 +7,7 @@ issue_tracker: https://git.belin.io/cedx/where.dart/issues
repository: https://git.belin.io/cedx/where.dart

environment:
sdk: '>=2.8.0 <3.0.0'
sdk: ">=2.8.0 <3.0.0"

executables:
where: where


+ 40
- 40
test/file_stat_test.dart View File

@@ -1,49 +1,49 @@
import 'dart:io' hide FileStat;
import 'package:test/test.dart';
import 'package:where/where.dart';
import "dart:io" hide FileStat;
import "package:test/test.dart";
import "package:where/where.dart";

/// Tests the features of the [FileStat] class.
void main() => group('FileStat', () {
group('.modeString()', () {
test('should return the file mode as a human-readable string', () async {
final mode = (await FileStat.stat('test/file_stat_test.dart')).modeString();
expect(mode, matches(RegExp(r'([r\-][w\-][x\-]){3}$')));
});
});
void main() => group("FileStat", () {
group(".modeString()", () {
test("should return the file mode as a human-readable string", () async {
final mode = (await FileStat.stat("test/file_stat_test.dart")).modeString();
expect(mode, matches(RegExp(r"([r\-][w\-][x\-]){3}$")));
});
});

group('.stat()', () {
test('should return a `FileSystemEntityType.notFound` if the file does not exist', () async {
final fileStats = await FileStat.stat('foo/bar/baz.dart');
expect(fileStats.modeString(), '---------');
expect(fileStats.type, FileSystemEntityType.notFound);
});
group(".stat()", () {
test("should return a `FileSystemEntityType.notFound` if the file does not exist", () async {
final fileStats = await FileStat.stat("foo/bar/baz.dart");
expect(fileStats.modeString(), "---------");
expect(fileStats.type, FileSystemEntityType.notFound);
});

test('should return a numeric identity greater than or equal to 0 for the file owner', () async {
final fileStats = await FileStat.stat('test/file_stat_test.dart');
expect(fileStats.uid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
test("should return a numeric identity greater than or equal to 0 for the file owner", () async {
final fileStats = await FileStat.stat("test/file_stat_test.dart");
expect(fileStats.uid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});

test('should return a numeric identity greater than or equal to 0 for the file group', () async {
final fileStats = await FileStat.stat('test/file_stat_test.dart');
expect(fileStats.gid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
});
test("should return a numeric identity greater than or equal to 0 for the file group", () async {
final fileStats = await FileStat.stat("test/file_stat_test.dart");
expect(fileStats.gid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
});

group('.statSync()', () {
test('should return a `FileSystemEntityType.notFound` if the file does not exist', () {
final fileStats = FileStat.statSync('foo/bar/baz.dart');
expect(fileStats.modeString(), '---------');
expect(fileStats.type, FileSystemEntityType.notFound);
});
group(".statSync()", () {
test("should return a `FileSystemEntityType.notFound` if the file does not exist", () {
final fileStats = FileStat.statSync("foo/bar/baz.dart");
expect(fileStats.modeString(), "---------");
expect(fileStats.type, FileSystemEntityType.notFound);
});

test('should return a numeric identity greater than or equal to 0 for the file owner', () {
final fileStats = FileStat.statSync('test/file_stat_test.dart');
expect(fileStats.uid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
test("should return a numeric identity greater than or equal to 0 for the file owner", () {
final fileStats = FileStat.statSync("test/file_stat_test.dart");
expect(fileStats.uid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});

test('should return a numeric identity greater than or equal to 0 for the file group', () {
final fileStats = FileStat.statSync('test/file_stat_test.dart');
expect(fileStats.gid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
});
test("should return a numeric identity greater than or equal to 0 for the file group", () {
final fileStats = FileStat.statSync("test/file_stat_test.dart");
expect(fileStats.gid, Finder.isWindows ? -1 : greaterThanOrEqualTo(0));
});
});
});

+ 54
- 54
test/finder_test.dart View File

@@ -1,68 +1,68 @@
import 'dart:io';
import 'package:test/test.dart';
import 'package:where/where.dart';
import "dart:io";
import "package:test/test.dart";
import "package:where/where.dart";

/// Tests the features of the [Finder] class.
void main() => group('Finder', () {
final delimiter = Finder.isWindows ? ';' : ':';
void main() => group("Finder", () {
final delimiter = Finder.isWindows ? ";" : ":";

group('constructor', () {
test('should set the `path` property to the value of the `PATH` environment variable by default', () {
final pathEnv = Platform.environment['PATH'] ?? '';
final path = pathEnv.isEmpty ? <String>[] : pathEnv.split(delimiter);
expect(Finder().path, orderedEquals(path));
});
group("constructor", () {
test("should set the `path` property to the value of the `PATH` environment variable by default", () {
final pathEnv = Platform.environment["PATH"] ?? "";
final path = pathEnv.isEmpty ? <String>[] : pathEnv.split(delimiter);
expect(Finder().path, orderedEquals(path));
});

test('should split the input path using the path separator', () {
final path = ['/usr/local/bin', '/usr/bin'];
expect(Finder(path: path.join(delimiter)).path, orderedEquals(path));
});
test("should split the input path using the path separator", () {
final path = ["/usr/local/bin", "/usr/bin"];
expect(Finder(path: path.join(delimiter)).path, orderedEquals(path));
});

test('should set the `extensions` property to the value of the `PATHEXT` environment variable by default', () {
final pathExt = Platform.environment['PATHEXT'] ?? '';
final extensions = pathExt.isEmpty ? <String>[] : pathExt.split(delimiter).map((item) => item.toLowerCase());
expect(Finder().extensions, orderedEquals(extensions));
});
test("should set the `extensions` property to the value of the `PATHEXT` environment variable by default", () {
final pathExt = Platform.environment["PATHEXT"] ?? "";
final extensions = pathExt.isEmpty ? <String>[] : pathExt.split(delimiter).map((item) => item.toLowerCase());
expect(Finder().extensions, orderedEquals(extensions));
});

test('should split the extension list using the path separator', () {
final extensions = ['.EXE', '.CMD', '.BAT'];
expect(Finder(extensions: extensions.join(delimiter)).extensions, orderedEquals(['.exe', '.cmd', '.bat']));
});
test("should split the extension list using the path separator", () {
final extensions = [".EXE", ".CMD", ".BAT"];
expect(Finder(extensions: extensions.join(delimiter)).extensions, orderedEquals([".exe", ".cmd", ".bat"]));
});

test('should set the `pathSeparator` property to the value of the platform path separator by default', () {
expect(Finder().pathSeparator, delimiter);
});
test("should set the `pathSeparator` property to the value of the platform path separator by default", () {
expect(Finder().pathSeparator, delimiter);
});

test('should properly set the path separator', () {
expect(Finder(pathSeparator: '#').pathSeparator, '#');
});
});
test("should properly set the path separator", () {
expect(Finder(pathSeparator: "#").pathSeparator, "#");
});
});

group('.find()', () {
test('should return the path of the `executable.cmd` file on Windows', () async {
final executables = await Finder(path: 'test/fixtures').find('executable').toList();
expect(executables.length, Finder.isWindows ? 1 : 0);
if (Finder.isWindows) expect(executables.first.path, endsWith(r'\test\fixtures\executable.cmd'));
});
group(".find()", () {
test("should return the path of the `executable.cmd` file on Windows", () async {
final executables = await Finder(path: "test/fixtures").find("executable").toList();
expect(executables.length, Finder.isWindows ? 1 : 0);
if (Finder.isWindows) expect(executables.first.path, endsWith(r"\test\fixtures\executable.cmd"));
});

test('should return the path of the `executable.sh` file on POSIX', () async {
final executables = await Finder(path: 'test/fixtures').find('executable.sh').toList();
expect(executables.length, Finder.isWindows ? 0 : 1);
if (!Finder.isWindows) expect(executables.first.path, endsWith('/test/fixtures/executable.sh'));
});
});
test("should return the path of the `executable.sh` file on POSIX", () async {
final executables = await Finder(path: "test/fixtures").find("executable.sh").toList();
expect(executables.length, Finder.isWindows ? 0 : 1);
if (!Finder.isWindows) expect(executables.first.path, endsWith("/test/fixtures/executable.sh"));
});
});

group('.isExecutable()', () {
test('should return `false` for a non-executable file', () async {
expect(await Finder().isExecutable('test/where_test.dart'), isFalse);
});
group(".isExecutable()", () {
test("should return `false` for a non-executable file", () async {
expect(await Finder().isExecutable("test/where_test.dart"), isFalse);
});

test('should return `false` for a POSIX executable, when test is run on Windows', () async {
expect(await Finder().isExecutable('test/fixtures/executable.sh'), isNot(Finder.isWindows));
});
test("should return `false` for a POSIX executable, when test is run on Windows", () async {
expect(await Finder().isExecutable("test/fixtures/executable.sh"), isNot(Finder.isWindows));
});

test('should return `false` for a Windows executable, when test is run on POSIX', () async {
expect(await Finder().isExecutable('test/fixtures/executable.cmd'), Finder.isWindows);
});
});
test("should return `false` for a Windows executable, when test is run on POSIX", () async {
expect(await Finder().isExecutable("test/fixtures/executable.cmd"), Finder.isWindows);
});
});
});

+ 62
- 62
test/where_test.dart View File

@@ -1,74 +1,74 @@
import 'package:test/test.dart';
import 'package:where/where.dart';
import "package:test/test.dart";
import "package:where/where.dart";

/// Tests the features of the [where] function.
void main() => group('where()', () {
test('should return the path of the `executable.cmd` file on Windows', () async {
try {
final executable = await where('executable', all: false, path: 'test/fixtures');
if (!Finder.isWindows) fail('Exception not thrown');
else expect(executable, allOf(const TypeMatcher<String>(), endsWith(r'\test\fixtures\executable.cmd')));
}
void main() => group("where()", () {
test("should return the path of the `executable.cmd` file on Windows", () async {
try {
final executable = await where("executable", all: false, path: "test/fixtures");
if (!Finder.isWindows) fail("Exception not thrown");
else expect(executable, allOf(const TypeMatcher<String>(), endsWith(r"\test\fixtures\executable.cmd")));
}

on Exception catch (err) {
if (Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});
on Exception catch (err) {
if (Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});

test('should return all the paths of the `executable.cmd` file on Windows', () async {
try {
final executables = await where('executable', all: true, path: 'test/fixtures');
if (!Finder.isWindows) fail('Exception not thrown');
else {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, allOf(const TypeMatcher<String>(), endsWith(r'\test\fixtures\executable.cmd')));
}
}
test("should return all the paths of the `executable.cmd` file on Windows", () async {
try {
final executables = await where("executable", all: true, path: "test/fixtures");
if (!Finder.isWindows) fail("Exception not thrown");
else {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, allOf(const TypeMatcher<String>(), endsWith(r"\test\fixtures\executable.cmd")));
}
}

on Exception catch (err) {
if (Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});
on Exception catch (err) {
if (Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});

test('should return the path of the `executable.sh` file on POSIX', () async {
try {
final executable = await where('executable.sh', all: false, path: 'test/fixtures');
if (Finder.isWindows) fail('Exception not thrown');
else expect(executable, allOf(const TypeMatcher<String>(), endsWith('/test/fixtures/executable.sh')));
}
test("should return the path of the `executable.sh` file on POSIX", () async {
try {
final executable = await where("executable.sh", all: false, path: "test/fixtures");
if (Finder.isWindows) fail("Exception not thrown");
else expect(executable, allOf(const TypeMatcher<String>(), endsWith("/test/fixtures/executable.sh")));
}

on Exception catch (err) {
if (!Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});
on Exception catch (err) {
if (!Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});

test('should return all the paths of the `executable.sh` file on POSIX', () async {
try {
final executables = await where('executable.sh', all: true, path: 'test/fixtures');
if (Finder.isWindows) fail('Exception not thrown');
else {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, allOf(const TypeMatcher<String>(), endsWith('/test/fixtures/executable.sh')));
}
}
test("should return all the paths of the `executable.sh` file on POSIX", () async {
try {
final executables = await where("executable.sh", all: true, path: "test/fixtures");
if (Finder.isWindows) fail("Exception not thrown");
else {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, allOf(const TypeMatcher<String>(), endsWith("/test/fixtures/executable.sh")));
}
}

on Exception catch (err) {
if (!Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});
on Exception catch (err) {
if (!Finder.isWindows) fail(err.toString());
else expect(err, const TypeMatcher<FinderException>());
}
});

test('should return the value of the `onError` handler', () async {
final executable = await where('executable', all: false, onError: (_) => 'foo', path: 'test/fixtures');
if (!Finder.isWindows) expect(executable, 'foo');
test("should return the value of the `onError` handler", () async {
final executable = await where("executable", all: false, onError: (_) => "foo", path: "test/fixtures");
if (!Finder.isWindows) expect(executable, "foo");

final executables = await where('executable.sh', all: true, onError: (_) => ['foo'], path: 'test/fixtures');
if (Finder.isWindows) {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, 'foo');
}
});
final executables = await where("executable.sh", all: true, onError: (_) => ["foo"], path: "test/fixtures");
if (Finder.isWindows) {
expect(executables, allOf(isList, hasLength(1)));
expect(executables.first, "foo");
}
});
});

+ 3
- 3
tool/clean.ps1 View File

@@ -2,10 +2,10 @@
Set-StrictMode -Version Latest
Set-Location (Split-Path $PSScriptRoot)

foreach ($item in '.dart_tool/build', 'build', 'doc/api', 'www') {
if (Test-Path $item) { Remove-Item $item -Recurse }
foreach ($item in ".dart_tool/build", "build", "doc/api", "www") {
if (Test-Path $item) { Remove-Item $item -Recurse }
}

foreach ($item in Get-ChildItem var -Exclude .gitkeep) {
Remove-Item $item -Recurse
Remove-Item $item -Recurse
}

Loading…
Cancel
Save