[jnigen] Use `package:cli_config` (https://github.com/dart-lang/jnigen/issues/197)

diff --git a/pkgs/jnigen/bin/jnigen.dart b/pkgs/jnigen/bin/jnigen.dart
index a5065de..518ba11 100644
--- a/pkgs/jnigen/bin/jnigen.dart
+++ b/pkgs/jnigen/bin/jnigen.dart
@@ -12,6 +12,9 @@
   } on ConfigException catch (e) {
     log.fatal(e);
     return;
+  } on FormatException catch (e) {
+    log.fatal(e);
+    return;
   }
   await generateJniBindings(config);
 }
diff --git a/pkgs/jnigen/lib/src/config/yaml_reader.dart b/pkgs/jnigen/lib/src/config/yaml_reader.dart
index 4347ecb..ecae2cb 100644
--- a/pkgs/jnigen/lib/src/config/yaml_reader.dart
+++ b/pkgs/jnigen/lib/src/config/yaml_reader.dart
@@ -6,15 +6,17 @@
 
 import 'package:args/args.dart';
 import 'package:yaml/yaml.dart';
+import 'package:cli_config/cli_config.dart' as cli_config;
 
 import 'config_exception.dart';
 
 /// YAML Reader which enables to override specific values from command line.
 class YamlReader {
-  YamlReader.of(this.cli, this.yaml, this.yamlFile);
-  Map<String, String> cli;
-  Map<dynamic, dynamic> yaml;
-  File? yamlFile;
+  final cli_config.Config _config;
+
+  final Uri? _configRoot;
+
+  YamlReader.of(this._config, this._configRoot);
 
   /// Parses the provided command line arguments and returns a [YamlReader].
   ///
@@ -42,19 +44,15 @@
       stderr.writeln(parser.usage);
       exit(1);
     }
-    final configFile = results['config'] as String?;
-    Map<dynamic, dynamic> yamlMap = {};
-    if (configFile != null) {
+    final configFilePath = results['config'] as String?;
+    String? configFileContents;
+    Uri? configFileUri;
+    if (configFilePath != null) {
       try {
-        final yamlInput = loadYaml(File(configFile).readAsStringSync(),
-            sourceUrl: Uri.file(configFile));
-        if (yamlInput is Map) {
-          yamlMap = yamlInput;
-        } else {
-          throw ConfigException('YAML config must be set of key value pairs');
-        }
+        configFileContents = File(configFilePath).readAsStringSync();
+        configFileUri = File(configFilePath).uri;
       } on Exception catch (e) {
-        stderr.writeln('cannot read $configFile: $e');
+        stderr.writeln('Cannot read $configFilePath: $e.');
       }
     }
     final regex = RegExp('([a-z-_.]+)=(.+)');
@@ -69,97 +67,53 @@
         throw ConfigException('override does not match expected pattern');
       }
     }
+    final config = cli_config.Config.fromConfigFileContents(
+      commandLineDefines: results['override'],
+      workingDirectory: Directory.current.uri,
+      environment: Platform.environment,
+      fileContents: configFileContents,
+      fileSourceUri: configFileUri,
+    );
     return YamlReader.of(
-        properties, yamlMap, configFile != null ? File(configFile) : null);
+      config,
+      configFileUri?.resolve('.'),
+    );
   }
 
-  bool? getBool(String property) {
-    if (cli.containsKey(property)) {
-      final v = cli[property]!;
-      if (v == 'true') {
-        return true;
-      }
-      if (v == 'false') {
-        return false;
-      }
-      throw ConfigException('expected boolean value for $property, got $v');
-    }
-    return getYamlValue<bool>(property);
-  }
+  bool? getBool(String property) => _config.optionalBool(property);
 
-  String? getString(String property) {
-    final configValue = cli[property] ?? getYamlValue<String>(property);
-    return configValue;
-  }
+  String? getString(String property) => _config.optionalString(property);
 
   /// Same as [getString] but path is resolved relative to YAML config if it's
   /// from YAML config.
-  String? getPath(String property) {
-    final cliOverride = cli[property];
-    if (cliOverride != null) return cliOverride;
-    final path = getYamlValue<String>(property);
-    if (path == null) return null;
-    // In (very unlikely) case YAML config didn't come from a file,
-    // do not try to resolve anything.
-    if (yamlFile == null) return path;
-    final yamlDir = yamlFile!.parent;
-    return yamlDir.uri.resolve(path).toFilePath();
-  }
+  String? getPath(String property) =>
+      _config.optionalPath(property)?.toFilePath();
 
-  List<String>? getStringList(String property) {
-    final configValue = cli[property]?.split(';') ??
-        getYamlValue<YamlList>(property)?.cast<String>();
-    return configValue;
-  }
+  List<String>? getStringList(String property) => _config.optionalStringList(
+        property,
+        splitCliPattern: ';',
+        combineAllConfigs: false,
+      );
 
   List<String>? getPathList(String property) {
-    final cliOverride = cli[property]?.split(';');
-    if (cliOverride != null) return cliOverride;
-    final paths = getYamlValue<YamlList>(property)?.cast<String>();
-    if (paths == null) return null;
-    // In (very unlikely) case YAML config didn't come from a file.
-    if (yamlFile == null) return paths;
-    final yamlDir = yamlFile!.parent;
-    return paths.map((path) => yamlDir.uri.resolve(path).toFilePath()).toList();
+    final configResult = _config.optionalPathList(
+      property,
+      combineAllConfigs: false,
+      splitCliPattern: ';',
+    );
+    return configResult?.map((e) => e.path).toList();
   }
 
-  String? getOneOf(String property, Set<String> values) {
-    final value = cli[property] ?? getYamlValue<String>(property);
-    if (value == null || values.contains(value)) {
-      return value;
-    }
-    throw ConfigException('expected one of $values for $property');
-  }
+  String? getOneOf(String property, Set<String> values) =>
+      _config.optionalString(property, validValues: values);
 
   Map<String, String>? getStringMap(String property) {
-    final value = getYamlValue<YamlMap>(property);
+    final value = _config.valueOf<YamlMap?>(property);
     return value?.cast<String, String>();
   }
 
-  bool hasValue(String property) => getYamlValue<dynamic>(property) != null;
-
-  T? getYamlValue<T>(String property) {
-    final path = property.split('.');
-    dynamic cursor = yaml;
-    String current = '';
-    for (var i in path) {
-      if (cursor is YamlMap || cursor is Map) {
-        cursor = cursor[i];
-      } else {
-        throw ConfigException('expected $current to be a YAML map');
-      }
-      current = [if (current != '') current, i].join('.');
-      if (cursor == null) {
-        return null;
-      }
-    }
-    if (cursor is! T) {
-      throw ConfigException(
-          'expected $T for $property, got ${cursor.runtimeType}');
-    }
-    return cursor;
-  }
+  bool hasValue(String property) => _config.valueOf<dynamic>(property) != null;
 
   /// Returns URI of the directory containing YAML config.
-  Uri? getConfigRoot() => yamlFile?.parent.uri;
+  Uri? getConfigRoot() => _configRoot;
 }
diff --git a/pkgs/jnigen/pubspec.yaml b/pkgs/jnigen/pubspec.yaml
index b944f6b..2a3c731 100644
--- a/pkgs/jnigen/pubspec.yaml
+++ b/pkgs/jnigen/pubspec.yaml
@@ -17,6 +17,7 @@
   args: ^2.3.0
   yaml: ^3.1.0
   logging: ^1.0.2
+  cli_config: ^0.1.0
 
 dev_dependencies:
   lints: ^2.0.0
@@ -25,4 +26,3 @@
   test: ^1.17.5
   build_runner: ^2.2.0
   json_serializable: ^6.6.0
-
diff --git a/pkgs/jnigen/test/config_test.dart b/pkgs/jnigen/test/config_test.dart
index 883bae8..fd935c3 100644
--- a/pkgs/jnigen/test/config_test.dart
+++ b/pkgs/jnigen/test/config_test.dart
@@ -2,6 +2,8 @@
 // for details. All rights reserved. Use of this source code is governed by a
 // BSD-style license that can be found in the LICENSE file.
 
+import 'dart:io';
+
 import 'package:jnigen/src/config/config.dart';
 import 'package:test/test.dart';
 import 'package:path/path.dart' hide equals;
@@ -10,12 +12,12 @@
 import 'jackson_core_test/generate.dart';
 
 const packageTests = 'test';
-final jacksonCoreTests = join(packageTests, 'jackson_core_test');
-final thirdParty = join(jacksonCoreTests, 'third_party');
-final lib = join(thirdParty, 'lib');
-final src = join(thirdParty, 'src');
-final testLib = join(thirdParty, 'test_', 'lib');
-final testSrc = join(thirdParty, 'test_', 'src');
+final jacksonCoreTests = absolute(packageTests, 'jackson_core_test');
+final thirdParty = absolute(jacksonCoreTests, 'third_party');
+final lib = absolute(thirdParty, 'lib');
+final src = absolute(thirdParty, 'src');
+final testLib = absolute(thirdParty, 'test_', 'lib');
+final testSrc = absolute(thirdParty, 'test_', 'src');
 
 /// Compares 2 [Config] objects using [expect] to give useful errors when
 /// two fields are not equal.
@@ -74,7 +76,7 @@
 Config parseYamlConfig({List<String> overrides = const []}) =>
     Config.parseArgs(['--config', jnigenYaml, ...overrides]);
 
-void testForErrorChecking(
+void testForErrorChecking<T extends Exception>(
     {required String name,
     required List<String> overrides,
     dynamic Function(Config)? function}) {
@@ -86,7 +88,7 @@
           function(config);
         }
       },
-      throwsA(isA<ConfigException>()),
+      throwsA(isA<T>()),
     );
   });
 }
@@ -95,8 +97,8 @@
   final config = Config.parseArgs([
     '--config',
     jnigenYaml,
-    '-Doutput.c.path=$testSrc/',
-    '-Doutput.dart.path=$testLib/',
+    '-Doutput.c.path=$testSrc${Platform.pathSeparator}',
+    '-Doutput.dart.path=$testLib${Platform.pathSeparator}',
   ]);
 
   test('compare configuration values', () {
@@ -104,19 +106,19 @@
   });
 
   group('Test for config error checking', () {
-    testForErrorChecking(
+    testForErrorChecking<ConfigException>(
       name: 'Invalid bindings type',
       overrides: ['-Doutput.bindings_type=c_base'],
     );
-    testForErrorChecking(
+    testForErrorChecking<ConfigException>(
       name: 'Invalid output structure',
       overrides: ['-Doutput.dart.structure=singl_file'],
     );
-    testForErrorChecking(
+    testForErrorChecking<ConfigException>(
       name: 'Dart path not ending with /',
       overrides: ['-Doutput.dart.path=lib'],
     );
-    testForErrorChecking(
+    testForErrorChecking<FormatException>(
       name: 'Invalid log level',
       overrides: ['-Dlog_level=inf'],
     );