<commit_before>﻿// <copyright file="FuncTaskExtensions.cs" company="<NAME>">
// Copyright (c) <NAME>. All rights reserved.
// </copyright>

namespace TaskSample.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    public static class FuncTaskExtensions
    {
        public static async Task<T> FirstAsync<T>(this IEnumerable<Func<CancellationToken, Task<T>>> funcs, Predicate<T> pred)
        {
            var tasks = new List<Task<T>>();
            Tuple<T> firstResult = null;
            using (CancellationTokenSource cts = new CancellationTokenSource())
            {
                foreach (Func<CancellationToken, Task<T>> func in funcs)
                {
                    if (cts.IsCancellationRequested)
                    {
                        break;
                    }

                    Func<CancellationToken, Task<T>> match = async t =>
                    {
                        T result = default(T);
                        try
                        {
                            result = await func(t);
                            if (pred(result))
                            {
                                firstResult = Tuple.Create(result);
                                cts.Cancel();
                            }
                        }
                        catch (Exception)
                        {
                        }

                        return result;
                    };

                    Task<T> task = match(cts.Token);
                    tasks.Add(task);
                }

                await Task.WhenAll(tasks);
                if (firstResult == null)
                {
                    throw new InvalidOperationException("No matching result.");
                }

                return firstResult.Item1;
            }
        }
    }
}<commit_msg>TaskSample - refactor FirstAsync (extract MatchFunc class)<commit_after>﻿// <copyright file="FuncTaskExtensions.cs" company="<NAME>">
// Copyright (c) <NAME>. All rights reserved.
// </copyright>

namespace TaskSample.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Threading;
    using System.Threading.Tasks;

    public static class FuncTaskExtensions
    {
        public static async Task<T> FirstAsync<T>(this IEnumerable<Func<CancellationToken, Task<T>>> funcs, Predicate<T> pred)
        {
            var tasks = new List<Task<T>>();
            using (MatchFunc<T> match = new MatchFunc<T>(pred))
            {
                foreach (Func<CancellationToken, Task<T>> func in funcs)
                {
                    if (match.Canceled)
                    {
                        break;
                    }

                    Task<T> task = match.RunAsync(func);
                    tasks.Add(task);
                }

                await Task.WhenAll(tasks);
                return match.Result;
            }
        }

        private sealed class MatchFunc<T> : IDisposable
        {
            private readonly CancellationTokenSource cts;
            private readonly Predicate<T> pred;

            private volatile Tuple<T> firstResult;

            public MatchFunc(Predicate<T> pred)
            {
                this.cts = new CancellationTokenSource();
                this.pred = pred;
            }

            public bool Canceled => this.cts.IsCancellationRequested;

            public T Result
            {
                get
                {
                    if (this.firstResult == null)
                    {
                        throw new InvalidOperationException("No matching result.");
                    }

                    return this.firstResult.Item1;
                }
            }

            public async Task<T> RunAsync(Func<CancellationToken, Task<T>> func)
            {
                T result = default(T);
                try
                {
                    result = await func(this.cts.Token);
                    if (this.pred(result))
                    {
                        this.firstResult = Tuple.Create(result);
                        this.cts.Cancel();
                    }
                }
                catch (Exception)
                {
                }

                return result;
            }

            public void Dispose()
            {
                this.cts.Dispose();
            }
        }
    }
}
{'fraction_non_alphanumeric': 0.08639501224671566, 'fraction_numerical': 0.00044533511467379205, 'mean_word_length': 1.0624426078971534, 'pattern_counts': {'":': 0, '<': 45, '<?xml version=': 0, '>': 47, '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': '3020946', 'n_tokens_mistral': 1083, 'n_tokens_neox': 1022, 'n_words': 228}