/// <summary> /// Copy files one one location to another. This method has the start and destination locations "pinned". /// </summary> /// <param name="source"></param> /// <param name="destination"></param> /// <param name="files"></param> /// <param name="statusUpdate"></param> /// <param name="failNow"></param> /// <param name="timeout">Minutes of no progress before canceling it</param> /// <returns></returns> public static async Task <Uri[]> CopyFilesAsync(IPlace source, IPlace destination, Uri[] files, Action <string> statusUpdate = null, Func <bool> failNow = null, int timeout = 60) { // Simple checks var goodFiles = files .ThrowIfNull(() => new ArgumentNullException($"files can't be a null argument")) .Throw(u => u.Scheme != "gridds", u => new UnknownUriSchemeException($"Can only deal with gridds:// uris - found: {u.OriginalString}")); if (destination == null) { throw new ArgumentNullException("Can't have a null destination!"); } if (!(await _places).Contains(destination)) { throw new ArgumentException($"Place {destination.Name} is not on our master list of places!"); } if (source == null) { throw new ArgumentNullException("Can't have a null source!"); } if (!(await _places).Contains(source)) { throw new ArgumentException($"Place {source.Name} is not on our master list of places!"); } var all_check = await Task.WhenAll(files.Select(async fl => await source.HasFileAsync(fl, statusUpdate, failNow))); if (!all_check.All(f => f)) { throw new ArgumentException($"Place {source.Name} does not have all the requested files"); } // Find a route to make them "local", and sort them by the route name (e.g. we can batch the file accesses). var routeSources = new IPlace[] { source }; var routedFilesList = await Task.WhenAll(goodFiles .Select(async u => new RoutedFileInfo { r = await FindRouteFromSources((await destination.HasFileAsync(u, statusUpdate, failNow)) ? routeSources.Concat(new IPlace[] { destination }).ToArray() : routeSources, u, p => p == destination, p => p == destination), f = u })); var routedFiles = routedFilesList .GroupBy(info => info.r.Name) .ToArray(); // To do the files, we need to first fine a routing from wherever they are to the final location. var resultUris = await GetListOfRoutedFiles(statusUpdate, failNow, timeout, routedFiles); return(resultUris.ToArray()); }