<commit_before>﻿using LibGit2Sharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Threading;
using System.Threading.Tasks;

namespace PoshGit2
{
    public sealed class ExpiringCache : IDisposable, IRepositoryCache
    {
        private readonly MemoryCache _cache = MemoryCache.Default;
        private readonly Func<string, ICurrentWorkingDirectory, IRepositoryStatus> _factory;
        private readonly ILogger _log;

        public ExpiringCache(ILogger log, Func<string, ICurrentWorkingDirectory, IRepositoryStatus> factory)
        {
            _log = log;
            _factory = factory;
        }

        public Task<IEnumerable<IRepositoryStatus>> GetAllReposAsync(CancellationToken cancellationToken)
        {
            var all = _cache
                .Select(o => new ReadonlyCopyRepositoryStatus(o.Value as IRepositoryStatus))
                .ToList();

            return Task.FromResult<IEnumerable<IRepositoryStatus>>(all);
        }

        public Task<IRepositoryStatus> FindRepoAsync(ICurrentWorkingDirectory cwd, CancellationToken cancellationToken)
        {
            var @null = Task.FromResult<IRepositoryStatus>(null);

            if (!cwd.IsValid)
            {
                return @null;
            }

            var path = cwd.CWD;
            var repo = FindGitRepo(path);

            if (repo == null)
            {
                return @null;
            }

            lock (_cache)
            {
                if (_cache.Contains(repo))
                {
                    var item = _cache.GetCacheItem(repo);
                    var value = item.Value as IRepositoryStatus;

                    if (value == null)
                    {
                        _log.Warning("Found an entry that is not IRepositoryStatus: {CacheValue}", item.Value?.GetType());
                        _cache.Remove(repo);
                    }
                    else
                    {
                        _log.Verbose("Found repo: {Path}", repo);
                        return Task.FromResult(new ReadonlyCopyRepositoryStatus(value, cwd) as IRepositoryStatus);
                    }
                }

                try
                {
                    _log.Information("Creating repo: {Path}", repo);

                    var status = _factory(repo, cwd);
                    var policy = new CacheItemPolicy
                    {
                        RemovedCallback = arg =>
                        {
                            _log.Information("Removing repo from cache: {Repo}", repo);
                            (arg.CacheItem.Value as IDisposable)?.Dispose();
                        },
                        SlidingExpiration = TimeSpan.FromMinutes(10)
                    };

                    _cache.Add(new CacheItem(repo, status), policy);

                    return Task.FromResult(new ReadonlyCopyRepositoryStatus(status, cwd) as IRepositoryStatus);
                }
                catch (RepositoryNotFoundException)
                {
                    return @null;
                }
                catch (Exception e)
                {
                    _log.Warning(e, "Unknown exception in ExpiringCache");
                    return @null;
                }
            }
        }

        private static string FindGitRepo(string path)
        {
            if (path == null)
            {
                return null;
            }

            if (Directory.Exists(Path.Combine(path, ".git")))
            {
                return path;
            }
            else
            {
                var up = Path.GetDirectoryName(path);

                return string.Equals(up, path, StringComparison.OrdinalIgnoreCase) ? null : FindGitRepo(up);
            }
        }

        public Task<bool> RemoveRepoAsync(string path, CancellationToken cancellationToken)
        {
            return Task.FromResult(Remove(path));
        }

        private bool Remove(string path)
        {
            var mainPath = FindGitRepo(path);

            if (mainPath == null)
            {
                return false;
            }<commit_msg>Add a try/catch for invalid repo in expiring cache<commit_after>﻿using LibGit2Sharp;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Caching;
using System.Threading;
using System.Threading.Tasks;

namespace PoshGit2
{
    public sealed class ExpiringCache : IDisposable, IRepositoryCache
    {
        private readonly MemoryCache _cache = MemoryCache.Default;
        private readonly Func<string, ICurrentWorkingDirectory, IRepositoryStatus> _factory;
        private readonly ILogger _log;

        public ExpiringCache(ILogger log, Func<string, ICurrentWorkingDirectory, IRepositoryStatus> factory)
        {
            _log = log;
            _factory = factory;
        }

        public Task<IEnumerable<IRepositoryStatus>> GetAllReposAsync(CancellationToken cancellationToken)
        {
            var all = _cache
                .Select(o => new ReadonlyCopyRepositoryStatus(o.Value as IRepositoryStatus))
                .ToList();

            return Task.FromResult<IEnumerable<IRepositoryStatus>>(all);
        }

        public Task<IRepositoryStatus> FindRepoAsync(ICurrentWorkingDirectory cwd, CancellationToken cancellationToken)
        {
            var @null = Task.FromResult<IRepositoryStatus>(null);

            if (!cwd.IsValid)
            {
                return @null;
            }

            var path = cwd.CWD;
            var repo = FindGitRepo(path);

            if (repo == null)
            {
                return @null;
            }

            lock (_cache)
            {
                if (_cache.Contains(repo))
                {
                    var item = _cache.GetCacheItem(repo);
                    var value = item.Value as IRepositoryStatus;

                    if (value == null)
                    {
                        _log.Warning("Found an entry that is not IRepositoryStatus: {CacheValue}", item.Value?.GetType());
                        _cache.Remove(repo);
                    }
                    else
                    {
                        _log.Verbose("Found repo: {Path}", repo);
                        return Task.FromResult(new ReadonlyCopyRepositoryStatus(value, cwd) as IRepositoryStatus);
                    }
                }

                try
                {
                    _log.Information("Creating repo: {Path}", repo);

                    var status = _factory(repo, cwd);
                    var policy = new CacheItemPolicy
                    {
                        RemovedCallback = arg =>
                        {
                            _log.Information("Removing repo from cache: {Repo}", repo);
                            (arg.CacheItem.Value as IDisposable)?.Dispose();
                        },
                        SlidingExpiration = TimeSpan.FromMinutes(10)
                    };

                    _cache.Add(new CacheItem(repo, status), policy);

                    return Task.FromResult(new ReadonlyCopyRepositoryStatus(status, cwd) as IRepositoryStatus);
                }
                catch (RepositoryNotFoundException)
                {
                    return @null;
                }
                catch (Exception e)
                {
                    _log.Warning(e, "Unknown exception in ExpiringCache");
                    return @null;
                }
            }
        }

        private string FindGitRepo(string path)
        {
            if (path == null)
            {
                return null;
            }

            if (Directory.Exists(Path.Combine(path, ".git")))
            {
                return path;
            }
            else
            {
                try
                {
                    var up = Path.GetDirectoryName(path);

                    return string.Equals(up, path, StringComparison.OrdinalIgnoreCase) ? null : FindGitRepo(up);
                }
                catch (Exception e)
                {
                    _log.Error("Invalid repo path: {Path}, {Exception}", path, e);

                    return null;
                }
            }
        }

        public Task<bool> RemoveRepoAsync(string path, CancellationToken cancellationToken)
        {
            return Task.FromResult(Remove(path));
        }

        private bool Remove(string path)
        {
            var mainPath = FindGitRepo(path);

            if (mainPath == null)
            {
                return false;
            }
{'fraction_non_alphanumeric': 0.07875100447709792, 'fraction_numerical': 0.0009183790609574102, 'mean_word_length': 1.285414480587618, 'pattern_counts': {'":': 0, '<': 21, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7535108', 'n_tokens_mistral': 2136, 'n_tokens_neox': 1997, 'n_words': 460}