[ffigen] Added support for including/excluding/renaming unnamed_enums (#96)

* added support for including/excluding/renaming unnamed_enums

 added faq, update version, changelog, update test

* fix typo
diff --git a/pkgs/ffigen/CHANGELOG.md b/pkgs/ffigen/CHANGELOG.md
index 95e0eff..6d143e5 100644
--- a/pkgs/ffigen/CHANGELOG.md
+++ b/pkgs/ffigen/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 0.3.0
+- Added support for including/excluding/renaming _un-named enums_ using key `unnamed_enums`.
+
 # 0.2.4+1
 - Minor changes to dylib creation error log.
 
diff --git a/pkgs/ffigen/README.md b/pkgs/ffigen/README.md
index 7fa1a1b..4309bfc 100644
--- a/pkgs/ffigen/README.md
+++ b/pkgs/ffigen/README.md
@@ -112,7 +112,7 @@
     <td><pre lang="yaml">compiler-opts: '-I/usr/lib/llvm-9/include/'</pre></td>
   </tr>
   <tr>
-    <td>functions<br>structs<br>enums<br>macros</td>
+    <td>functions<br>structs<br>enums<br>unnamed-enums<br>macros</td>
     <td>Filters for declarations.<br><b>Default: all are included</b></td>
     <td><pre lang="yaml">
 functions:
@@ -181,13 +181,6 @@
     </td>
     <td><pre lang="yaml">dart-bool: true</pre></td>
   </tr>
-  <tr>
-    <td>unnamed-enums</td>
-    <td>Should generate constants for anonymous unnamed enums.<br>
-    <b>Default: true</b>
-    </td>
-    <td><pre lang="yaml">unnamed-enums: true</pre></td>
-  </tr>
    <tr>
     <td>preamble</td>
     <td>Raw header of the file, pasted as-it-is.</td>
@@ -367,3 +360,23 @@
 Although `dart:ffi` doesn't have a NativeType for `bool`, they can be implemented as `Uint8`.
 Ffigen generates dart `bool` for function parameters and return type by default.
 To disable this, and use `int` instead, set `dart-bool: false` in configurations.
+
+### How are unnamed enums handled?
+
+Unnamed enums are handled separately, under the key `unnamed-enums`, and are generated as top level constants.
+
+Here's an example that shows how to include/exclude/rename unnamed enums
+```yaml
+unnamed-enums:
+  include:
+    - 'CX_.*'
+  exclude:
+    - '.*Flag'
+  rename:
+    'CXType_(.*)': '$1'
+```
+
+### Why are some struct declarations generated even after excluded them in config?
+
+This happens when an excluded struct is a dependency to some included declaration.
+(A dependency means a struct is being passed/returned by a function or is member of another struct in some way)
diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart
index 55ff570..ad8db21 100644
--- a/pkgs/ffigen/lib/src/config_provider/config.dart
+++ b/pkgs/ffigen/lib/src/config_provider/config.dart
@@ -43,7 +43,11 @@
   Declaration get enumClassDecl => _enumClassDecl;
   Declaration _enumClassDecl;
 
-  /// Declaration config for Enums.
+  /// Declaration config for Unnamed enum constants.
+  Declaration get unnamedEnumConstants => _unnamedEnumConstants;
+  Declaration _unnamedEnumConstants;
+
+  /// Declaration config for Macro constants.
   Declaration get macroDecl => _macroDecl;
   Declaration _macroDecl;
 
@@ -66,10 +70,6 @@
   bool get arrayWorkaround => _arrayWorkaround;
   bool _arrayWorkaround;
 
-  /// If constants should be generated for unnamed enums.
-  bool get unnamedEnums => _unnamedEnums;
-  bool _unnamedEnums;
-
   /// If dart bool should be generated for C booleans.
   bool get dartBool => _dartBool;
   bool _dartBool;
@@ -193,6 +193,14 @@
           _enumClassDecl = result as Declaration;
         },
       ),
+      strings.unnamedEnums: Specification<Declaration>(
+        requirement: Requirement.no,
+        validator: declarationConfigValidator,
+        extractor: declarationConfigExtractor,
+        defaultValue: () => Declaration(),
+        extractedResult: (dynamic result) =>
+            _unnamedEnumConstants = result as Declaration,
+      ),
       strings.macros: Specification<Declaration>(
         requirement: Requirement.no,
         validator: declarationConfigValidator,
@@ -245,13 +253,6 @@
         defaultValue: () => false,
         extractedResult: (dynamic result) => _arrayWorkaround = result as bool,
       ),
-      strings.unnamedEnums: Specification<bool>(
-        requirement: Requirement.no,
-        validator: booleanValidator,
-        extractor: booleanExtractor,
-        defaultValue: () => true,
-        extractedResult: (dynamic result) => _unnamedEnums = result as bool,
-      ),
       strings.dartBool: Specification<bool>(
         requirement: Requirement.no,
         validator: booleanValidator,
diff --git a/pkgs/ffigen/lib/src/header_parser/includer.dart b/pkgs/ffigen/lib/src/header_parser/includer.dart
index 5c534a7..5c8775b 100644
--- a/pkgs/ffigen/lib/src/header_parser/includer.dart
+++ b/pkgs/ffigen/lib/src/header_parser/includer.dart
@@ -40,6 +40,17 @@
   }
 }
 
+bool shouldIncludeUnnamedEnumConstant(String usr, String name) {
+  if (bindingsIndex.isSeenUnnamedEnumConstant(usr) || name == '') {
+    return false;
+  } else if (config.unnamedEnumConstants == null ||
+      config.unnamedEnumConstants.shouldInclude(name)) {
+    return true;
+  } else {
+    return false;
+  }
+}
+
 bool shouldIncludeMacro(String usr, String name) {
   if (bindingsIndex.isSeenMacro(usr) || name == '') {
     return false;
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index 49e4b1a..06ce225 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -36,8 +36,7 @@
   final enumName = name ?? cursor.spelling();
   if (enumName == '') {
     // Save this unnamed enum if it is anonymous (therefore not in a typedef).
-    if (config.unnamedEnums &&
-        clang.clang_Cursor_isAnonymous_wrap(cursor) != 0) {
+    if (clang.clang_Cursor_isAnonymous_wrap(cursor) != 0) {
       _logger.fine('Saving anonymous enum.');
       saveUnNamedEnum(cursor);
     } else {
diff --git a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
index 729f791..4c1e244 100644
--- a/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
+++ b/pkgs/ffigen/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
@@ -6,6 +6,7 @@
 
 import 'package:ffigen/src/code_generator.dart';
 import 'package:ffigen/src/header_parser/data.dart';
+import 'package:ffigen/src/header_parser/includer.dart';
 import 'package:logging/logging.dart';
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
@@ -37,7 +38,9 @@
         .finest('  unnamedenumCursorVisitor: ${cursor.completeStringRepr()}');
     switch (clang.clang_getCursorKind_wrap(cursor)) {
       case clang_types.CXCursorKind.CXCursor_EnumConstantDecl:
-        _addUnNamedEnumConstant(cursor);
+        if (shouldIncludeUnnamedEnumConstant(cursor.usr(), cursor.spelling())) {
+          _addUnNamedEnumConstant(cursor);
+        }
         break;
       default:
         _logger.severe('Invalid enum constant.');
@@ -58,8 +61,7 @@
     Constant(
       usr: cursor.usr(),
       originalName: cursor.spelling(),
-      name: config.enumClassDecl.renameMemberUsingConfig(
-        '', // Un-named enum constants have an empty declaration name.
+      name: config.unnamedEnumConstants.renameUsingConfig(
         cursor.spelling(),
       ),
       rawType: 'int',
diff --git a/pkgs/ffigen/lib/src/header_parser/utils.dart b/pkgs/ffigen/lib/src/header_parser/utils.dart
index d578b00..171113d 100644
--- a/pkgs/ffigen/lib/src/header_parser/utils.dart
+++ b/pkgs/ffigen/lib/src/header_parser/utils.dart
@@ -340,6 +340,7 @@
   final Map<String, Struc> _structs = {};
   final Map<String, Func> _functions = {};
   final Map<String, EnumClass> _enumClass = {};
+  final Map<String, Constant> _unnamedEnumConstants = {};
   final Map<String, String> _macros = {};
   // Stores only named typedefC used in NativeFunc.
   final Map<String, Typedef> _functionTypedefs = {};
@@ -380,6 +381,18 @@
     return _enumClass[usr];
   }
 
+  bool isSeenUnnamedEnumConstant(String usr) {
+    return _unnamedEnumConstants.containsKey(usr);
+  }
+
+  void addUnnamedEnumConstantToSeen(String usr, Constant enumConstant) {
+    _unnamedEnumConstants[usr] = enumConstant;
+  }
+
+  Constant getSeenUnnamedEnumConstant(String usr) {
+    return _unnamedEnumConstants[usr];
+  }
+
   bool isSeenMacro(String usr) {
     return _macros.containsKey(usr);
   }
diff --git a/pkgs/ffigen/lib/src/strings.dart b/pkgs/ffigen/lib/src/strings.dart
index 0749dbe..2cd2697 100644
--- a/pkgs/ffigen/lib/src/strings.dart
+++ b/pkgs/ffigen/lib/src/strings.dart
@@ -40,6 +40,7 @@
 const functions = 'functions';
 const structs = 'structs';
 const enums = 'enums';
+const unnamedEnums = 'unnamed-enums';
 const macros = 'macros';
 
 // Sub-fields of Declarations.
@@ -82,7 +83,6 @@
 const useSupportedTypedefs = 'use-supported-typedefs';
 const warnWhenRemoving = 'warn-when-removing';
 const arrayWorkaround = 'array-workaround';
-const unnamedEnums = 'unnamed-enums';
 const dartBool = 'dart-bool';
 
 const comments = 'comments';
diff --git a/pkgs/ffigen/pubspec.yaml b/pkgs/ffigen/pubspec.yaml
index ae53603..7aa1133 100644
--- a/pkgs/ffigen/pubspec.yaml
+++ b/pkgs/ffigen/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 0.2.4+1
+version: 0.3.0
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C header files.
 
diff --git a/pkgs/ffigen/test/header_parser_tests/unnamed_enums.h b/pkgs/ffigen/test/header_parser_tests/unnamed_enums.h
index ecb4a85..8b64555 100644
--- a/pkgs/ffigen/test/header_parser_tests/unnamed_enums.h
+++ b/pkgs/ffigen/test/header_parser_tests/unnamed_enums.h
@@ -1,7 +1,7 @@
 // Only this should be parsed.
 enum{
     A=1,
-    B=2,
+    B=2, // This will be excluded by config.
     C=3
 };
 
diff --git a/pkgs/ffigen/test/header_parser_tests/unnamed_enums_test.dart b/pkgs/ffigen/test/header_parser_tests/unnamed_enums_test.dart
index 99456f3..fa75392 100644
--- a/pkgs/ffigen/test/header_parser_tests/unnamed_enums_test.dart
+++ b/pkgs/ffigen/test/header_parser_tests/unnamed_enums_test.dart
@@ -29,6 +29,9 @@
 ${strings.enums}:
   ${strings.exclude}:
     - Named
+${strings.unnamedEnums}:
+  ${strings.exclude}:
+    - B
         ''') as yaml.YamlMap),
       );
     });
@@ -39,7 +42,6 @@
 
     test('Parse unnamed enum Values', () {
       expect(actual.getBindingAsString('A'), expected.getBindingAsString('A'));
-      expect(actual.getBindingAsString('B'), expected.getBindingAsString('B'));
       expect(actual.getBindingAsString('C'), expected.getBindingAsString('C'));
     });
 
@@ -64,11 +66,6 @@
         rawValue: '1',
       ),
       Constant(
-        name: 'B',
-        rawType: 'int',
-        rawValue: '2',
-      ),
-      Constant(
         name: 'C',
         rawType: 'int',
         rawValue: '3',
diff --git a/pkgs/ffigen/test/rename_tests/rename_test.dart b/pkgs/ffigen/test/rename_tests/rename_test.dart
index c3425cc..962e545 100644
--- a/pkgs/ffigen/test/rename_tests/rename_test.dart
+++ b/pkgs/ffigen/test/rename_tests/rename_test.dart
@@ -63,9 +63,11 @@
     'MemberRenameEnum4':
       '_(.*)': '\$1'
       'fullMatch': 'fullMatchSuccess'
-    '':
-      '_(.*)': '\$1'
-      'unnamedFullMatch': 'unnamedFullMatchSuccess'
+
+unnamed-enums:
+  ${strings.rename}:
+    '_(.*)': '\$1'
+    'unnamedFullMatch': 'unnamedFullMatchSuccess'
 
 macros:
   ${strings.rename}: