Update public API (#70)

- Updated `Config`'s class members to use public getter, private variable pattern.
- Replaced ConfigError with FormatException.
- Refactored all shared global variables to data.dart.
- Send test coverage to coveralls in travis.
diff --git a/.travis.yml b/.travis.yml
index fe0efef..cd06229 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,14 +12,20 @@
   directories:
     - $HOME/.pub-cache
 before_install:
-  # install libclang-dev
+  # Install libclang-dev.
   - sudo add-apt-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main"
   - wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|sudo apt-key add -
   - sudo apt-get update
   - sudo apt-get install clang-10 libclang-10-dev
-  # use clang from installed llvm
+  # Use clang from installed llvm.
   - export PATH="/usr/lib/llvm-10/bin:$PATH"
 
 before_script:
   - 'pub run ffigen:setup'
   - cd test/native_test && dart build_test_dylib.dart && cd ../..
+
+matrix:
+  include:
+  - dart: dev
+    script: ./tool/travis.sh
+    name: Collect and report coverage.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 21853fb..b773ee0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,4 @@
-# 0.2.0-dev
+# 0.2.0
 - Updated header config. Header `entry-points` and `include-directives` are now specified under `headers` key. Glob syntax is allowed.
 - Updated declaration `include`/`exclude` config. These are now specified as a list.
 - Added Regexp based declaration renaming using `rename` subkey.
diff --git a/README.md b/README.md
index 477a0e9..f97ab70 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,6 @@
+[![pub package](https://img.shields.io/pub/v/ffigen.svg)](https://pub.dev/packages/ffigen)
 [![Build Status](https://travis-ci.org/dart-lang/ffigen.svg?branch=master)](https://travis-ci.org/dart-lang/ffigen)
+[![Coverage Status](https://coveralls.io/repos/github/dart-lang/ffigen/badge.svg?branch=master)](https://coveralls.io/github/dart-lang/ffigen?branch=master)
 
 Experimental binding generator for [FFI](https://dart.dev/guides/libraries/c-interop)
 bindings.
diff --git a/bin/ffigen.dart b/bin/ffigen.dart
index 9328b28..d0b2ee1 100644
--- a/bin/ffigen.dart
+++ b/bin/ffigen.dart
@@ -34,7 +34,7 @@
   Config config;
   try {
     config = getConfig(argResult);
-  } on ConfigError {
+  } on FormatException {
     print('Please fix configuration errors and re-run the tool.');
     exit(1);
   }
@@ -42,10 +42,6 @@
   // Parse the bindings according to config object provided.
   final library = parse(config);
 
-  if (config.sort) {
-    library.sort();
-  }
-
   // Generate file for the parsed bindings.
   final gen = File(config.output);
   library.generateFile(gen);
diff --git a/lib/ffigen.dart b/lib/ffigen.dart
index 2b0a304..3425eec 100644
--- a/lib/ffigen.dart
+++ b/lib/ffigen.dart
@@ -8,5 +8,5 @@
 library ffigen;
 
 export 'src/code_generator.dart' show Library;
-export 'src/config_provider.dart' show Config, ConfigError;
+export 'src/config_provider.dart' show Config;
 export 'src/header_parser.dart' show parse;
diff --git a/lib/src/config_provider/config.dart b/lib/src/config_provider/config.dart
index 583287d..0a913ad 100644
--- a/lib/src/config_provider/config.dart
+++ b/lib/src/config_provider/config.dart
@@ -21,53 +21,66 @@
 /// Handles validation, extraction of confiurations from yaml file.
 class Config {
   /// output file name.
-  String output;
-
+  String get output => _output;
+  String _output;
   // Holds headers and filters for header.
-  Headers headers;
+  Headers get headers => _headers;
+  Headers _headers;
 
   /// CommandLine Arguments to pass to clang_compiler.
-  List<String> compilerOpts;
+  List<String> get compilerOpts => _compilerOpts;
+  List<String> _compilerOpts;
 
   /// Declaration config for Functions.
-  Declaration functionDecl;
+  Declaration get functionDecl => _functionDecl;
+  Declaration _functionDecl;
 
   /// Declaration config for Structs.
-  Declaration structDecl;
+  Declaration get structDecl => _structDecl;
+  Declaration _structDecl;
 
   /// Declaration config for Enums.
-  Declaration enumClassDecl;
+  Declaration get enumClassDecl => _enumClassDecl;
+  Declaration _enumClassDecl;
 
   /// Declaration config for Enums.
-  Declaration macroDecl;
+  Declaration get macroDecl => _macroDecl;
+  Declaration _macroDecl;
 
   /// If generated bindings should be sorted alphabetically.
-  bool sort;
+  bool get sort => _sort;
+  bool _sort;
 
   /// If typedef of supported types(int8_t) should be directly used.
-  bool useSupportedTypedefs;
+  bool get useSupportedTypedefs => _useSupportedTypedefs;
+  bool _useSupportedTypedefs;
 
   /// Extracted Doc comment type.
-  CommentType commentType;
+  CommentType get commentType => _commentType;
+  CommentType _commentType;
 
   /// If tool should generate array workarounds.
   ///
   /// If false(default), structs with inline array members will have all its
   /// members removed.
-  bool arrayWorkaround;
+  bool get arrayWorkaround => _arrayWorkaround;
+  bool _arrayWorkaround;
 
   /// If constants should be generated for unnamed enums.
-  bool unnamedEnums;
+  bool get unnamedEnums => _unnamedEnums;
+  bool _unnamedEnums;
 
   /// Name of the wrapper class.
-  String wrapperName;
+  String get wrapperName => _wrapperName;
+  String _wrapperName;
 
   /// Doc comment for the wrapper class.
-  String wrapperDocComment;
+  String get wrapperDocComment => _wrapperDocComment;
+  String _wrapperDocComment;
 
   /// Header of the generated bindings.
-  String preamble;
-
+  String get preamble => _preamble;
+  String _preamble;
   Config._();
 
   /// Create config from Yaml map.
@@ -79,7 +92,7 @@
 
     final result = configspecs._checkConfigs(map, specs);
     if (!result) {
-      throw ConfigError();
+      throw FormatException('Invalid configurations provided.');
     }
 
     configspecs._extract(map, specs);
@@ -133,20 +146,21 @@
         requirement: Requirement.yes,
         validator: outputValidator,
         extractor: outputExtractor,
-        extractedResult: (dynamic result) => output = result as String,
+        extractedResult: (dynamic result) => _output = result as String,
       ),
       strings.headers: Specification<Headers>(
         requirement: Requirement.yes,
         validator: headersValidator,
         extractor: headersExtractor,
-        extractedResult: (dynamic result) => headers = result as Headers,
+        extractedResult: (dynamic result) => _headers = result as Headers,
       ),
       strings.compilerOpts: Specification<List<String>>(
         requirement: Requirement.no,
         validator: compilerOptsValidator,
         extractor: compilerOptsExtractor,
+        defaultValue: () => [],
         extractedResult: (dynamic result) =>
-            compilerOpts = result as List<String>,
+            _compilerOpts = result as List<String>,
       ),
       strings.functions: Specification<Declaration>(
         requirement: Requirement.no,
@@ -154,7 +168,7 @@
         extractor: declarationConfigExtractor,
         defaultValue: () => Declaration(),
         extractedResult: (dynamic result) {
-          functionDecl = result as Declaration;
+          _functionDecl = result as Declaration;
         },
       ),
       strings.structs: Specification<Declaration>(
@@ -163,7 +177,7 @@
         extractor: declarationConfigExtractor,
         defaultValue: () => Declaration(),
         extractedResult: (dynamic result) {
-          structDecl = result as Declaration;
+          _structDecl = result as Declaration;
         },
       ),
       strings.enums: Specification<Declaration>(
@@ -172,7 +186,7 @@
         extractor: declarationConfigExtractor,
         defaultValue: () => Declaration(),
         extractedResult: (dynamic result) {
-          enumClassDecl = result as Declaration;
+          _enumClassDecl = result as Declaration;
         },
       ),
       strings.macros: Specification<Declaration>(
@@ -181,7 +195,7 @@
         extractor: declarationConfigExtractor,
         defaultValue: () => Declaration(),
         extractedResult: (dynamic result) {
-          macroDecl = result as Declaration;
+          _macroDecl = result as Declaration;
         },
       ),
       strings.sizemap: Specification<Map<int, SupportedNativeType>>(
@@ -202,7 +216,7 @@
         validator: booleanValidator,
         extractor: booleanExtractor,
         defaultValue: () => false,
-        extractedResult: (dynamic result) => sort = result as bool,
+        extractedResult: (dynamic result) => _sort = result as bool,
       ),
       strings.useSupportedTypedefs: Specification<bool>(
         requirement: Requirement.no,
@@ -210,7 +224,7 @@
         extractor: booleanExtractor,
         defaultValue: () => true,
         extractedResult: (dynamic result) =>
-            useSupportedTypedefs = result as bool,
+            _useSupportedTypedefs = result as bool,
       ),
       strings.comments: Specification<CommentType>(
         requirement: Requirement.no,
@@ -218,28 +232,28 @@
         extractor: commentExtractor,
         defaultValue: () => CommentType.def(),
         extractedResult: (dynamic result) =>
-            commentType = result as CommentType,
+            _commentType = result as CommentType,
       ),
       strings.arrayWorkaround: Specification<bool>(
         requirement: Requirement.no,
         validator: booleanValidator,
         extractor: booleanExtractor,
         defaultValue: () => false,
-        extractedResult: (dynamic result) => arrayWorkaround = result as bool,
+        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,
+        extractedResult: (dynamic result) => _unnamedEnums = result as bool,
       ),
       strings.name: Specification<String>(
         requirement: Requirement.prefer,
         validator: dartClassNameValidator,
         extractor: stringExtractor,
         defaultValue: () => 'NativeLibrary',
-        extractedResult: (dynamic result) => wrapperName = result as String,
+        extractedResult: (dynamic result) => _wrapperName = result as String,
       ),
       strings.description: Specification<String>(
         requirement: Requirement.prefer,
@@ -247,28 +261,14 @@
         extractor: stringExtractor,
         defaultValue: () => null,
         extractedResult: (dynamic result) =>
-            wrapperDocComment = result as String,
+            _wrapperDocComment = result as String,
       ),
       strings.preamble: Specification<String>(
         requirement: Requirement.no,
         validator: nonEmptyStringValidator,
         extractor: stringExtractor,
-        extractedResult: (dynamic result) => preamble = result as String,
+        extractedResult: (dynamic result) => _preamble = result as String,
       ),
     };
   }
 }
-
-class ConfigError implements Exception {
-  final String message;
-  ConfigError([this.message]);
-
-  @override
-  String toString() {
-    if (message == null) {
-      return 'ConfigError: Invalid configurations provided.';
-    } else {
-      return 'ConfigError: $message';
-    }
-  }
-}
diff --git a/lib/src/config_provider/config_types.dart b/lib/src/config_provider/config_types.dart
index b699239..fa59745 100644
--- a/lib/src/config_provider/config_types.dart
+++ b/lib/src/config_provider/config_types.dart
@@ -54,12 +54,14 @@
   /// Path to headers.
   ///
   /// This contains all the headers, after extraction from Globs.
-  List<String> entryPoints = [];
+  final List<String> entryPoints;
 
   /// Include filter for headers.
-  HeaderIncludeFilter includeFilter = GlobHeaderFilter();
+  final HeaderIncludeFilter includeFilter;
 
-  Headers({this.entryPoints, this.includeFilter});
+  Headers({List<String> entryPoints, HeaderIncludeFilter includeFilter})
+      : entryPoints = entryPoints ?? [],
+        includeFilter = includeFilter ?? GlobHeaderFilter();
 }
 
 abstract class HeaderIncludeFilter {
diff --git a/lib/src/header_parser/data.dart b/lib/src/header_parser/data.dart
index 87551cb..6c21338 100644
--- a/lib/src/header_parser/data.dart
+++ b/lib/src/header_parser/data.dart
@@ -5,7 +5,9 @@
 import 'dart:ffi';
 import 'dart:isolate';
 
-import 'package:ffigen/src/config_provider.dart';
+import 'package:ffigen/src/code_generator.dart' show Constant;
+import 'package:ffigen/src/config_provider.dart' show Config;
+import 'package:meta/meta.dart';
 import 'clang_bindings/clang_bindings.dart' show Clang;
 
 import 'utils.dart';
@@ -13,16 +15,40 @@
 /// Holds all Global shared variables.
 
 /// Holds configurations.
-Config config;
+Config get config => _config;
+Config _config;
 
 /// Holds clang functions.
-Clang clang;
+Clang get clang => _clang;
+Clang _clang;
+
+// Tracks seen status for bindings
+BindingsIndex get bindingsIndex => _bindingsIndex;
+BindingsIndex _bindingsIndex;
 
 /// Used for naming typedefs.
-IncrementalNamer incrementalNamer;
+IncrementalNamer get incrementalNamer => _incrementalNamer;
+IncrementalNamer _incrementalNamer;
 
 /// Holds the unique id refering to this isolate.
 ///
 /// Used by visitChildren_wrap to call the correct dart function from C.
 // int get uid => Isolate.current.controlPort.;
 final uid = Isolate.current.controlPort.nativePort;
+
+/// Saved macros, Key: prefixedName, Value originalName.
+Map<String, String> get savedMacros => _savedMacros;
+Map<String, String> _savedMacros;
+
+/// Saved unnamed EnumConstants.
+List<Constant> get unnamedEnumConstants => _unnamedEnumConstants;
+List<Constant> _unnamedEnumConstants;
+
+void initializeGlobals({@required Config config, @required Clang clang}) {
+  _config = config;
+  _clang = clang;
+  _incrementalNamer = IncrementalNamer();
+  _savedMacros = {};
+  _unnamedEnumConstants = [];
+  _bindingsIndex = BindingsIndex();
+}
diff --git a/lib/src/header_parser/includer.dart b/lib/src/header_parser/includer.dart
index c17b13e..a4766e1 100644
--- a/lib/src/header_parser/includer.dart
+++ b/lib/src/header_parser/includer.dart
@@ -2,20 +2,13 @@
 // 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 'package:ffigen/src/code_generator.dart';
 import 'data.dart';
 
 /// Utility functions to check whether a binding should be parsed or not
 /// based on filters.
 
-// Stores binding names already scene. Mp key is same as their original name.
-Map<String, Struc> _structs = {};
-Map<String, Func> _functions = {};
-Map<String, EnumClass> _enumClass = {};
-Map<String, String> _macros = {};
-
 bool shouldIncludeStruct(String name) {
-  if (_structs.containsKey(name) || name == '') {
+  if (bindingsIndex.isSeenStruct(name) || name == '') {
     return false;
   } else if (config.structDecl == null ||
       config.structDecl.shouldInclude(name)) {
@@ -26,7 +19,7 @@
 }
 
 bool shouldIncludeFunc(String name) {
-  if (_functions.containsKey(name) || name == '') {
+  if (bindingsIndex.isSeenFunc(name) || name == '') {
     return false;
   } else if (config.functionDecl == null ||
       config.functionDecl.shouldInclude(name)) {
@@ -37,7 +30,7 @@
 }
 
 bool shouldIncludeEnumClass(String name) {
-  if (_enumClass.containsKey(name) || name == '') {
+  if (bindingsIndex.isSeenEnumClass(name) || name == '') {
     return false;
   } else if (config.enumClassDecl == null ||
       config.enumClassDecl.shouldInclude(name)) {
@@ -48,7 +41,7 @@
 }
 
 bool shouldIncludeMacro(String name) {
-  if (_macros.containsKey(name) || name == '') {
+  if (bindingsIndex.isSeenMacro(name) || name == '') {
     return false;
   } else if (config.macroDecl == null || config.macroDecl.shouldInclude(name)) {
     return true;
@@ -76,51 +69,3 @@
 
   return _headerCache[sourceFile];
 }
-
-bool isSeenStruc(String originalName) {
-  return _structs.containsKey(originalName);
-}
-
-void addStrucToSeen(String originalName, Struc struc) {
-  _structs[originalName] = struc;
-}
-
-Struc getSeenStruc(String originalName) {
-  return _structs[originalName];
-}
-
-bool isSeenFunc(String originalName) {
-  return _functions.containsKey(originalName);
-}
-
-void addFuncToSeen(String originalName, Func func) {
-  _functions[originalName] = func;
-}
-
-Func getSeenFunc(String originalName) {
-  return _functions[originalName];
-}
-
-bool isSeenEnumClass(String originalName) {
-  return _enumClass.containsKey(originalName);
-}
-
-void addEnumClassToSeen(String originalName, EnumClass enumClass) {
-  _enumClass[originalName] = enumClass;
-}
-
-EnumClass getSeenEnumClass(String originalName) {
-  return _enumClass[originalName];
-}
-
-bool isSeenMacro(String originalName) {
-  return _macros.containsKey(originalName);
-}
-
-void addMacroToSeen(String originalName, String macro) {
-  _macros[originalName] = macro;
-}
-
-String getSeenMacro(String originalName) {
-  return _macros[originalName];
-}
diff --git a/lib/src/header_parser/parser.dart b/lib/src/header_parser/parser.dart
index 87e14ef..1771574 100644
--- a/lib/src/header_parser/parser.dart
+++ b/lib/src/header_parser/parser.dart
@@ -10,7 +10,6 @@
 import 'package:ffigen/src/header_parser/sub_parsers/macro_parser.dart';
 import 'package:ffigen/src/config_provider/config_types.dart';
 import 'package:ffigen/src/find_resource.dart';
-import 'package:ffigen/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart';
 import 'package:ffigen/src/header_parser/translation_unit_parser.dart';
 import 'package:ffigen/src/strings.dart' as strings;
 import 'package:logging/logging.dart';
@@ -21,8 +20,8 @@
 import 'utils.dart';
 
 /// Main entrypoint for header_parser.
-Library parse(Config conf, {bool sort = false}) {
-  initParser(conf);
+Library parse(Config c) {
+  initParser(c);
 
   final bindings = parseToBindings();
 
@@ -33,7 +32,7 @@
     header: config.preamble,
   );
 
-  if (sort) {
+  if (config.sort) {
     library.sort();
   }
   return library;
@@ -45,13 +44,9 @@
 
 var _logger = Logger('ffigen.header_parser.parser');
 
-/// Initialises parser, clears any previous values.
+/// Initializes parser, clears any previous values.
 void initParser(Config c) {
-  // Set global configurations.
-  config = c;
-  incrementalNamer = IncrementalNamer();
-
-  // Find full path of dynamic library and initialise bindings.
+  // Find full path of dynamic library and initialize bindings.
   if (findDotDartTool() == null) {
     throw Exception('Unable to find .dart_tool.');
   } else {
@@ -60,7 +55,12 @@
       strings.ffigenFolderName,
       strings.dylibFileName,
     );
-    clang = clang_types.Clang(DynamicLibrary.open(fullDylibPath));
+
+    // Initialize global variables.
+    initializeGlobals(
+      config: c,
+      clang: clang_types.Clang(DynamicLibrary.open(fullDylibPath)),
+    );
   }
 }
 
@@ -74,7 +74,6 @@
   /// Add compiler opt for comment parsing for clang based on config.
   if (config.commentType.length != CommentLength.none &&
       config.commentType.style == CommentStyle.any) {
-    config.compilerOpts ??= [];
     config.compilerOpts.add(strings.fparseAllComments);
   }
 
@@ -122,7 +121,7 @@
   }
 
   // Add all saved unnamed enums.
-  bindings.addAll(getSavedUnNamedEnums());
+  bindings.addAll(unnamedEnumConstants);
 
   // Parse all saved macros.
   bindings.addAll(parseSavedMacros());
diff --git a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
index 0fbb9e8..3460e28 100644
--- a/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/enumdecl_parser.dart
@@ -42,14 +42,15 @@
     } else {
       _logger.fine('Unnamed enum inside a typedef.');
     }
-  } else if (shouldIncludeEnumClass(enumName) && !isSeenEnumClass(enumName)) {
+  } else if (shouldIncludeEnumClass(enumName) &&
+      !bindingsIndex.isSeenEnumClass(enumName)) {
     _logger.fine('++++ Adding Enum: ${cursor.completeStringRepr()}');
     _stack.top.enumClass = EnumClass(
       dartDoc: getCursorDocComment(cursor),
       originalName: enumName,
       name: config.enumClassDecl.renameUsingConfig(enumName),
     );
-    addEnumClassToSeen(enumName, _stack.top.enumClass);
+    bindingsIndex.addEnumClassToSeen(enumName, _stack.top.enumClass);
     _addEnumConstant(cursor);
   }
 
diff --git a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
index d958c57..9137c73 100644
--- a/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/functiondecl_parser.dart
@@ -32,7 +32,7 @@
   _stack.top.unimplementedParameterType = false;
 
   final funcName = cursor.spelling();
-  if (shouldIncludeFunc(funcName) && !isSeenFunc(funcName)) {
+  if (shouldIncludeFunc(funcName) && !bindingsIndex.isSeenFunc(funcName)) {
     _logger.fine('++++ Adding Function: ${cursor.completeStringRepr()}');
 
     final rt = _getFunctionReturnType(cursor);
@@ -70,7 +70,7 @@
       returnType: rt,
       parameters: parameters,
     );
-    addFuncToSeen(funcName, _stack.top.func);
+    bindingsIndex.addFuncToSeen(funcName, _stack.top.func);
   }
 
   return _stack.pop().func;
diff --git a/lib/src/header_parser/sub_parsers/macro_parser.dart b/lib/src/header_parser/sub_parsers/macro_parser.dart
index 27a7b30..dd35b78 100644
--- a/lib/src/header_parser/sub_parsers/macro_parser.dart
+++ b/lib/src/header_parser/sub_parsers/macro_parser.dart
@@ -18,21 +18,18 @@
 
 var _logger = Logger('ffigen.header_parser.macro_parser');
 
-/// Saved macros, Key: prefixedName, Value originalName.
-final _savedMacros = <String, String>{};
-
 /// Adds a macro definition to be parsed later.
 void saveMacroDefinition(Pointer<clang_types.CXCursor> cursor) {
   final originalMacroName = cursor.spelling();
   if (shouldIncludeMacro(originalMacroName) &&
-      !isSeenMacro(originalMacroName) &&
+      !bindingsIndex.isSeenMacro(originalMacroName) &&
       clang.clang_Cursor_isMacroBuiltin_wrap(cursor) == 0 &&
       clang.clang_Cursor_isMacroFunctionLike_wrap(cursor) == 0) {
     // Parse macro only if it's not builtin or function-like.
     _logger.fine(
         "++++ Saved Macro '$originalMacroName' for later : ${cursor.completeStringRepr()}");
     final prefixedName = config.macroDecl.renameUsingConfig(originalMacroName);
-    addMacroToSeen(originalMacroName, prefixedName);
+    bindingsIndex.addMacroToSeen(originalMacroName, prefixedName);
     _saveMacro(prefixedName, originalMacroName);
   }
 }
@@ -41,7 +38,7 @@
 ///
 /// Macros are parsed later in [parseSavedMacros()].
 void _saveMacro(String name, String originalName) {
-  _savedMacros[name] = originalName;
+  savedMacros[name] = originalName;
 }
 
 List<Constant> _bindings;
@@ -52,7 +49,7 @@
 List<Constant> parseSavedMacros() {
   _bindings = [];
 
-  if (_savedMacros.keys.isEmpty) {
+  if (savedMacros.keys.isEmpty) {
     return _bindings;
   }
 
@@ -117,7 +114,7 @@
       switch (k) {
         case clang_types.CXEvalResultKind.CXEval_Int:
           constant = Constant(
-            originalName: _savedMacros[macroName],
+            originalName: savedMacros[macroName],
             name: macroName,
             rawType: 'int',
             rawValue: clang.clang_EvalResult_getAsLongLong(e).toString(),
@@ -125,7 +122,7 @@
           break;
         case clang_types.CXEvalResultKind.CXEval_Float:
           constant = Constant(
-            originalName: _savedMacros[macroName],
+            originalName: savedMacros[macroName],
             name: macroName,
             rawType: 'double',
             rawValue: clang.clang_EvalResult_getAsDouble(e).toString(),
@@ -138,7 +135,7 @@
           // Escape ' character, because our strings are enclosed with '.
           value = value.replaceAll("'", r"\'");
           constant = Constant(
-            originalName: _savedMacros[macroName],
+            originalName: savedMacros[macroName],
             name: macroName,
             rawType: 'String',
             rawValue: "'${value}'",
@@ -177,7 +174,7 @@
 /// Generated macro variable names.
 ///
 /// Used to determine if macro should be included in bindings or not.
-Set<String> _macroVarNames = {};
+Set<String> _macroVarNames;
 
 /// Creates a temporary file for parsing macros in current directory.
 File createFileForMacros() {
@@ -204,10 +201,10 @@
   }
 
   _macroVarNames = {};
-  for (final prefixedMacroName in _savedMacros.keys) {
+  for (final prefixedMacroName in savedMacros.keys) {
     // Write macro.
     final macroVarName = MacroVariableString.encode(prefixedMacroName);
-    sb.writeln('auto ${macroVarName} = ${_savedMacros[prefixedMacroName]};');
+    sb.writeln('auto ${macroVarName} = ${savedMacros[prefixedMacroName]};');
     // Add to _macroVarNames.
     _macroVarNames.add(macroVarName);
   }
diff --git a/lib/src/header_parser/sub_parsers/structdecl_parser.dart b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
index 39c56de..3b62123 100644
--- a/lib/src/header_parser/sub_parsers/structdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/structdecl_parser.dart
@@ -43,7 +43,7 @@
   if (structName.isEmpty) {
     _logger.finest('unnamed structure or typedef structure declaration');
   } else if ((ignoreFilter || shouldIncludeStruct(structName)) &&
-      (!isSeenStruc(structName))) {
+      (!bindingsIndex.isSeenStruct(structName))) {
     _logger.fine(
         '++++ Adding Structure: structName: ${structName}, ${cursor.completeStringRepr()}');
     _stack.top.struc = Struc(
@@ -53,7 +53,7 @@
     );
     // Adding to seen here to stop recursion if a struct has itself as a
     // member, members are updated later.
-    addStrucToSeen(structName, _stack.top.struc);
+    bindingsIndex.addStructToSeen(structName, _stack.top.struc);
     _setStructMembers(cursor);
   }
 
diff --git a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
index fedeb7e..b5039c4 100644
--- a/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
+++ b/lib/src/header_parser/sub_parsers/unnamed_enumdecl_parser.dart
@@ -14,10 +14,6 @@
 
 var _logger = Logger('ffigen.header_parser.unnamed_enumdecl_parser');
 
-List<Constant> _constants = [];
-
-List<Constant> getSavedUnNamedEnums() => _constants;
-
 /// Saves unnamed enums.
 void saveUnNamedEnum(Pointer<clang_types.CXCursor> cursor) {
   final resultCode = clang.clang_visitChildren_wrap(
@@ -58,7 +54,7 @@
 
 /// Adds the parameter to func in [functiondecl_parser.dart].
 void _addUnNamedEnumConstant(Pointer<clang_types.CXCursor> cursor) {
-  _constants.add(
+  unnamedEnumConstants.add(
     Constant(
       originalName: cursor.spelling(),
       name: config.enumClassDecl.renameMemberUsingConfig(
diff --git a/lib/src/header_parser/type_extractor/extractor.dart b/lib/src/header_parser/type_extractor/extractor.dart
index 1336d7e..c76c8ea 100644
--- a/lib/src/header_parser/type_extractor/extractor.dart
+++ b/lib/src/header_parser/type_extractor/extractor.dart
@@ -10,7 +10,6 @@
 
 import '../clang_bindings/clang_bindings.dart' as clang_types;
 import '../data.dart';
-import '../includer.dart';
 import '../sub_parsers/structdecl_parser.dart';
 import '../translation_unit_parser.dart';
 import '../type_extractor/cxtypekindmap.dart';
@@ -104,8 +103,8 @@
 
       // Also add a struct binding, if its unseen.
       // TODO(23): Check if we should auto add struct.
-      if (isSeenStruc(structName)) {
-        type = Type.struct(getSeenStruc(structName));
+      if (bindingsIndex.isSeenStruct(structName)) {
+        type = Type.struct(bindingsIndex.getSeenStruct(structName));
       } else {
         final struc = parseStructDeclaration(cursor,
             name: fixedStructName, ignoreFilter: true);
@@ -113,7 +112,7 @@
         // Add to bindings.
         addToBindings(struc);
         // Add to seen.
-        addStrucToSeen(structName, struc);
+        bindingsIndex.addStructToSeen(structName, struc);
       }
 
       cxtype.dispose();
diff --git a/lib/src/header_parser/utils.dart b/lib/src/header_parser/utils.dart
index e8190bf..6d8e159 100644
--- a/lib/src/header_parser/utils.dart
+++ b/lib/src/header_parser/utils.dart
@@ -322,3 +322,60 @@
     return '${base}_$i';
   }
 }
+
+/// Tracks if a binding is 'seen' or not.
+class BindingsIndex {
+  // Stores binding names already seen. Map key is same as their original name.
+  final Map<String, Struc> _structs = {};
+  final Map<String, Func> _functions = {};
+  final Map<String, EnumClass> _enumClass = {};
+  final Map<String, String> _macros = {};
+
+  bool isSeenStruct(String originalName) {
+    return _structs.containsKey(originalName);
+  }
+
+  void addStructToSeen(String originalName, Struc struc) {
+    _structs[originalName] = struc;
+  }
+
+  Struc getSeenStruct(String originalName) {
+    return _structs[originalName];
+  }
+
+  bool isSeenFunc(String originalName) {
+    return _functions.containsKey(originalName);
+  }
+
+  void addFuncToSeen(String originalName, Func func) {
+    _functions[originalName] = func;
+  }
+
+  Func getSeenFunc(String originalName) {
+    return _functions[originalName];
+  }
+
+  bool isSeenEnumClass(String originalName) {
+    return _enumClass.containsKey(originalName);
+  }
+
+  void addEnumClassToSeen(String originalName, EnumClass enumClass) {
+    _enumClass[originalName] = enumClass;
+  }
+
+  EnumClass getSeenEnumClass(String originalName) {
+    return _enumClass[originalName];
+  }
+
+  bool isSeenMacro(String originalName) {
+    return _macros.containsKey(originalName);
+  }
+
+  void addMacroToSeen(String originalName, String macro) {
+    _macros[originalName] = macro;
+  }
+
+  String getSeenMacro(String originalName) {
+    return _macros[originalName];
+  }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 3151c7d..5659a9d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -3,7 +3,7 @@
 # BSD-style license that can be found in the LICENSE file.
 
 name: ffigen
-version: 0.2.0-dev
+version: 0.2.0
 homepage: https://github.com/dart-lang/ffigen
 description: Experimental generator for FFI bindings, using LibClang to parse C/C++ header files.
 
diff --git a/test/test_coverage.dart b/test/test_coverage.dart
new file mode 100644
index 0000000..812105f
--- /dev/null
+++ b/test/test_coverage.dart
@@ -0,0 +1,46 @@
+// Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+// 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 'code_generator_test.dart' as code_generator_test;
+import 'collision_tests/decl_decl_collision_test.dart'
+    as collision_tests_decl_decl_collision_test;
+import 'collision_tests/reserved_keyword_collision_test.dart'
+    as collision_tests_reserved_keyword_collision_test;
+import 'example_tests/cjson_example_test.dart'
+    as example_tests_cjson_example_test;
+import 'example_tests/libclang_example_test.dart'
+    as example_tests_libclang_example_test;
+import 'example_tests/simple_example_test.dart'
+    as example_tests_simple_example_test;
+import 'header_parser_tests/function_n_struct_test.dart'
+    as header_parser_tests_function_n_struct_test;
+import 'header_parser_tests/functions_test.dart'
+    as header_parser_tests_functions_test;
+import 'header_parser_tests/macros_test.dart'
+    as header_parser_tests_macros_test;
+import 'header_parser_tests/nested_parsing_test.dart'
+    as header_parser_tests_nested_parsing_test;
+import 'header_parser_tests/unnamed_enums_test.dart'
+    as header_parser_tests_unnamed_enums_test;
+import 'large_integration_tests/large_test.dart'
+    as large_integration_tests_large_test;
+import 'native_test/native_test.dart' as native_test_native_test;
+import 'rename_tests/rename_test.dart' as rename_tests_rename_test;
+
+void main() {
+  large_integration_tests_large_test.main();
+  example_tests_cjson_example_test.main();
+  example_tests_simple_example_test.main();
+  example_tests_libclang_example_test.main();
+  collision_tests_decl_decl_collision_test.main();
+  collision_tests_reserved_keyword_collision_test.main();
+  header_parser_tests_functions_test.main();
+  header_parser_tests_macros_test.main();
+  header_parser_tests_function_n_struct_test.main();
+  header_parser_tests_nested_parsing_test.main();
+  header_parser_tests_unnamed_enums_test.main();
+  native_test_native_test.main();
+  rename_tests_rename_test.main();
+  code_generator_test.main();
+}
diff --git a/tool/travis.sh b/tool/travis.sh
new file mode 100755
index 0000000..987955b
--- /dev/null
+++ b/tool/travis.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+# Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file
+# 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.
+
+# Fast fail the script on failures.
+set -e
+
+# Gather coverage and upload to Coveralls.
+pub global activate remove_from_coverage
+pub global activate dart_coveralls
+# Generate coverage report.
+pub global run dart_coveralls calc test/test_coverage.dart > lcov.info
+# Remove extra files from coverage report.
+pub global run remove_from_coverage -f lcov.info -r ".pub-cache"
+# Upload to coveralls.
+gem install coveralls-lcov
+coveralls-lcov lcov.info