public static async Task BinderateAsync(BindingConfig config)
        {
            // Prime our Maven repositories
            await MavenFactory.Initialize(config);

            if (config.DownloadExternals)
            {
                var artifactDir = Path.Combine(config.BasePath, config.ExternalsDir);
                if (!Directory.Exists(artifactDir))
                {
                    Directory.CreateDirectory(artifactDir);
                }
            }

            await ProcessConfig(config);
        }
        static async Task ProcessConfig(BindingConfig config)
        {
            var mavenProjects = new Dictionary <string, Project>();
            var mavenGroups   = new List <MavenGroup>();

            foreach (var artifact in config.MavenArtifacts)
            {
                if (artifact.DependencyOnly)
                {
                    continue;
                }

                var maven = MavenFactory.GetMavenRepository(config, artifact);

                var mavenGroup = maven.Groups.FirstOrDefault(g => g.Id == artifact.GroupId);

                Project project = null;

                project = await maven.GetProjectAsync(artifact.GroupId, artifact.ArtifactId, artifact.Version);

                if (project != null)
                {
                    mavenProjects.Add($"{artifact.GroupId}/{artifact.ArtifactId}-{artifact.Version}", project);
                }
            }

            if (config.DownloadExternals)
            {
                await DownloadArtifacts(config, mavenProjects);
            }

            var slnProjModels = new Dictionary <string, BindingProjectModel>();
            var models        = BuildProjectModels(config, mavenProjects);

            var json = Newtonsoft.Json.JsonConvert.SerializeObject(models);

            if (config.Debug.DumpModels)
            {
                File.WriteAllText(Path.Combine(config.BasePath, "models.json"), json);
            }

            var engine = new RazorLightEngineBuilder()
                         .UseMemoryCachingProvider()
                         .Build();

            foreach (var model in models)
            {
                var template_set = config.GetTemplateSet(model.MavenArtifacts.FirstOrDefault()?.MavenArtifactConfig?.TemplateSet);

                foreach (var template in template_set.Templates)
                {
                    var inputTemplateFile = Path.Combine(config.BasePath, template.TemplateFile);
                    var templateSrc       = File.ReadAllText(inputTemplateFile);

                    AssignMetadata(model, template);

                    var outputFile = new FileInfo(template.GetOutputFile(config, model));
                    if (!outputFile.Directory.Exists)
                    {
                        outputFile.Directory.Create();
                    }

                    string result = await engine.CompileRenderAsync(inputTemplateFile, templateSrc, model);

                    File.WriteAllText(outputFile.FullName, result);

                    // We want to collect all the models for the .csproj's so we can add them to a .sln file after
                    if (!slnProjModels.ContainsKey(outputFile.FullName) && template.OutputFileRule.EndsWith(".csproj"))
                    {
                        slnProjModels.Add(outputFile.FullName, model);
                    }
                }
            }

            if (!string.IsNullOrEmpty(config.SolutionFile))
            {
                var slnPath = Path.Combine(config.BasePath ?? AppDomain.CurrentDomain.BaseDirectory, config.SolutionFile);
                var sln     = SolutionFileBuilder.Build(config, slnProjModels);
                File.WriteAllText(slnPath, sln);
            }
        }
        static async Task DownloadArtifacts(BindingConfig config, Dictionary <string, Project> mavenProjects)
        {
            var httpClient = new HttpClient();

            foreach (var mavenArtifact in config.MavenArtifacts)
            {
                // Skip downloading dependencies
                if (mavenArtifact.DependencyOnly)
                {
                    continue;
                }

                var maven   = MavenFactory.GetMavenRepository(config, mavenArtifact);
                var version = mavenArtifact.Version;

                if (!mavenProjects.TryGetValue($"{mavenArtifact.GroupId}/{mavenArtifact.ArtifactId}-{mavenArtifact.Version}", out var mavenProject))
                {
                    continue;
                }

                var artifactDir = Path.Combine(
                    config.BasePath,
                    config.ExternalsDir,
                    mavenArtifact.GroupId);

                var artifactExtractDir = Path.Combine(artifactDir, mavenArtifact.ArtifactId);

                Directory.CreateDirectory(artifactDir);
                Directory.CreateDirectory(artifactExtractDir);

                var mvnArt = maven.Groups.FirstOrDefault(g => g.Id == mavenArtifact.GroupId)?.Artifacts?.FirstOrDefault(a => a.Id == mavenArtifact.ArtifactId);

                // Download artifact
                var artifactFile = await DownloadPayload(mvnArt, mavenArtifact, mavenProject, artifactDir, config);

                // Determine MD5
                var md5File  = artifactFile + ".md5";
                var md5_func = new Func <Task <Stream> >(() => mvnArt.OpenLibraryFile(mavenArtifact.Version, mavenProject.Packaging + ".md5"));

                if (!(await TryDownloadFile(md5_func, md5File, ErrorLevel.Info)))
                {
                    // Then hash the downloaded artifact
                    using (var file = File.OpenRead(artifactFile))
                        File.WriteAllText(md5File, Util.HashMd5(file));
                }

                // Determine Sha256
                var sha256File = artifactFile + ".sha256";
                var sha_func   = new Func <Task <Stream> >(() => mvnArt.OpenLibraryFile(mavenArtifact.Version, mavenProject.Packaging + ".sha256"));

                // First try download, this almost certainly won't work
                // but in case Maven ever starts supporting sha256 it should start
                // they currently support .sha1 so there's no reason to believe the naming
                // convention should be any different, and one day .sha256 may exist
                if (!(await TryDownloadFile(sha_func, sha256File, ErrorLevel.Ignore)))
                {
                    // Create Sha256 hash if we couldn't download
                    using (var file = File.OpenRead(artifactFile))
                        File.WriteAllText(sha256File, Util.HashSha256(file));
                }

                var base_file_name = Path.Combine(artifactDir, config.DownloadExternalsWithFullName ? $"{mavenArtifact.GroupId}.{mavenArtifact.ArtifactId}" : $"{mavenArtifact.ArtifactId}");

                if (config.DownloadJavaSourceJars)
                {
                    var source_file = base_file_name + "-sources.jar";
                    var source_func = new Func <Task <Stream> >(() => maven.OpenArtifactSourcesFile(mavenArtifact.GroupId, mavenArtifact.ArtifactId, version));
                    await TryDownloadFile(source_func, source_file, ErrorLevel.Info);
                }

                if (config.DownloadPoms)
                {
                    var pom_file = base_file_name + ".pom";
                    var pom_func = new Func <Task <Stream> >(() => maven.OpenArtifactPomFile(mavenArtifact.GroupId, mavenArtifact.ArtifactId, version));
                    await TryDownloadFile(pom_func, pom_file, ErrorLevel.Info);
                }

                if (config.DownloadJavaDocJars)
                {
                    var javadoc_file = base_file_name + "-javadoc.jar";
                    var javadoc_func = new Func <Task <Stream> > (() => maven.OpenArtifactDocsFile(mavenArtifact.GroupId, mavenArtifact.ArtifactId, version));
                    await TryDownloadFile(javadoc_func, javadoc_file, ErrorLevel.Info);
                }

                if (config.DownloadMetadataFiles)
                {
                    var metadata_file = base_file_name + "-metadata.xml";
                    var metadata_func = new Func <Task <Stream> >(() => maven.OpenMavenMetadataFile(mavenArtifact.GroupId, mavenArtifact.ArtifactId));
                    await TryDownloadFile(metadata_func, metadata_file, ErrorLevel.Info);
                }

                if (Directory.Exists(artifactExtractDir))
                {
                    Directory.Delete(artifactExtractDir, true);
                }

                // Unzip artifact into externals
                if (mavenProject.Packaging.ToLowerInvariant() == "aar")
                {
                    ZipFile.ExtractToDirectory(artifactFile, artifactExtractDir);
                }
            }
        }