// Copyright (c) 2015-present, Parse, LLC. All rights reserved. This source code is licensed under the BSD-style license found in the LICENSE file in the root directory of this source tree. An additional grant of patent rights can be found in the PATENTS file in the same directory. using System; using System.Collections.Generic; using System.Linq; using System.Runtime.ExceptionServices; using System.Threading.Tasks; namespace LeanCloud.Common { /// /// Provides helper methods that allow us to use terser code elsewhere. /// public static class LCTaskExtensions { /// /// Ensures a task (even null) is awaitable. /// /// /// /// public static Task Safe(this Task task) { return task ?? Task.FromResult(default); } /// /// Ensures a task (even null) is awaitable. /// /// /// public static Task Safe(this Task task) { return task ?? Task.FromResult(null); } public delegate void PartialAccessor(ref T arg); public static TValue GetOrDefault(this IDictionary self, TKey key, TValue defaultValue) { if (self.TryGetValue(key, out TValue value)) { return value; } return defaultValue; } public static bool CollectionsEqual(this IEnumerable a, IEnumerable b) { return Equals(a, b) || (a != null && b != null && a.SequenceEqual(b)); } public static Task OnSuccess(this Task task, Func continuation) { return task.ContinueWith(t => { if (t.IsFaulted) { var ex = t.Exception.Flatten(); if (ex.InnerExceptions.Count == 1) { ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); } else { ExceptionDispatchInfo.Capture(ex).Throw(); } // Unreachable return Task.FromResult(default(TResult)); } else if (t.IsCanceled) { var tcs = new TaskCompletionSource(); tcs.SetCanceled(); return tcs.Task; } else { return Task.FromResult(continuation(t)); } }).Unwrap(); } public static Task OnSuccess(this Task task, Func, TResult> continuation) { return ((Task)task).OnSuccess(t => continuation((Task)t)); } public static Task OnSuccess(this Task task, Action> continuation) { return task.OnSuccess((Func, object>)(t => { continuation(t); return null; })); } public static Task OnSuccess(this Task task, Action continuation) { return task.OnSuccess((Func)(t => { continuation(t); return null; })); } // TaskScheduler public static Task OnSuccess(this Task task, Func continuation, TaskScheduler scheduler) { return task.ContinueWith(t => { if (t.IsFaulted) { var ex = t.Exception.Flatten(); if (ex.InnerExceptions.Count == 1) { ExceptionDispatchInfo.Capture(ex.InnerExceptions[0]).Throw(); } else { ExceptionDispatchInfo.Capture(ex).Throw(); } // Unreachable return Task.FromResult(default(TResult)); } else if (t.IsCanceled) { var tcs = new TaskCompletionSource(); tcs.SetCanceled(); return tcs.Task; } else { return Task.FromResult(continuation(t)); } }, scheduler).Unwrap(); } public static Task OnSuccess(this Task task, Func, TResult> continuation, TaskScheduler scheduler) { return ((Task)task).OnSuccess(t => continuation((Task)t), scheduler); } public static Task OnSuccess(this Task task, Action> continuation, TaskScheduler scheduler) { return task.OnSuccess((Func, object>)(t => { continuation(t); return null; }), scheduler); } public static Task OnSuccess(this Task task, Action continuation, TaskScheduler scheduler) { return task.OnSuccess((Func)(t => { continuation(t); return null; }), scheduler); } public static Task WhileAsync(Func> predicate, Func body) { Func iterate = null; iterate = () => { return predicate().OnSuccess(t => { if (!t.Result) { return Task.FromResult(0); } return body().OnSuccess(_ => iterate()).Unwrap(); }).Unwrap(); }; return iterate(); } } }