/// <summary>
        /// Extracts raw file indexes from each layer in an image, working from the top down.
        /// </summary>
        /// <param name="image"></param>
        /// <param name="maxDepth">Maximum layers down to search. If 0, searches all layers</param>
        /// <returns></returns>
        async IAsyncEnumerable <LayerIndex> GetRawIndexesAsync(string repository, Image image, int maxDepth = 0)
        {
            var layers = image.Layers.Reverse();
            var depth  = 1;

            foreach (var layer in layers)
            {
                List <string> files;
                using (var layerStream = await RecurseClient.GetLayerArchiveAsync(repository, layer.Digest))
                {
                    try
                    {
                        files = extractor.ExtractFiles(layerStream).ToList();
                    }
                    catch (SharpZipBaseException ex)
                    {
                        logger.LogError(ex, "Encountered corrupt layer archive, halting index.");
                        yield break;
                    }
                }

                yield return(new LayerIndex
                {
                    Depth = depth++,
                    Digest = layer.Digest,
                    Files = files
                });

                if (maxDepth > 0 && depth > maxDepth)
                {
                    break;
                }
            }
        }
        private async Task <ImageSet> ParseV2ThinManifest(string repository, string digest, string manifest)
        {
            var thinManifest = JsonConvert.DeserializeObject <ManifestV2>(manifest);
            var config       = await RecurseClient.GetImageConfigAsync(repository, thinManifest.Config.Digest);

            if (config == null)
            {
                throw new NotFoundException("The requested manifest does not exist in the registry.");
            }
            var image = new Image
            {
                History  = config.History.Select(h => Model.History.From(h)),
                Layers   = thinManifest.Layers.Select(l => l.ToLayer()),
                Digest   = digest,
                Platform = new Platform
                {
                    Architecture = config.Architecture,
                    OS           = config.OS,
                    OSVerion     = config.OSVersion
                }
            };

            return(new ImageSet
            {
                Date = image.History.Max(h => h.Created),
                Images = new[] { image },
                Platforms = new[] { image.Platform },
                SetDigest = image.Digest
            });
        }
        private async Task <ImageSet> ParseV2FatManifest(string repository, string digest, string manifest)
        {
            var images      = new List <Image>();
            var fatManifest = JsonConvert.DeserializeObject <FatManifest>(manifest);

            foreach (var subManifest in fatManifest.Manifests)
            {
                images.Add((await RecurseClient.GetImageSetAsync(repository, subManifest.Digest)).Images.First());
            }

            return(new ImageSet
            {
                Date = images.SelectMany(i => i.History.Select(h => h.Created)).Max(),
                Images = images,
                Platforms = images.Select(i => i.Platform),
                SetDigest = digest,
            });
        }