diff --git a/src/OrasProject.Oras/Content/MemoryGraph.cs b/src/OrasProject.Oras/Content/MemoryGraph.cs index 026852c..ee1f5c2 100644 --- a/src/OrasProject.Oras/Content/MemoryGraph.cs +++ b/src/OrasProject.Oras/Content/MemoryGraph.cs @@ -34,7 +34,7 @@ public Task> GetPredecessorsAsync(Descriptor node, Cance var key = node.BasicDescriptor; if (_predecessors.TryGetValue(key, out var predecessors)) { - return Task.FromResult> (predecessors.Values); + return Task.FromResult>(predecessors.Values); } return Task.FromResult>(Array.Empty()); } diff --git a/src/OrasProject.Oras/Copy.cs b/src/OrasProject.Oras/Copy.cs deleted file mode 100644 index fd82615..0000000 --- a/src/OrasProject.Oras/Copy.cs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright The ORAS Authors. -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -using OrasProject.Oras.Oci; -using System; -using System.Threading; -using System.Threading.Tasks; -using static OrasProject.Oras.Content.Extensions; - -namespace OrasProject.Oras -{ - public class Copy - { - - /// - /// Copy copies a rooted directed acyclic graph (DAG) with the tagged root node - /// in the source Target to the destination Target. - /// The destination reference will be the same as the source reference if the - /// destination reference is left blank. - /// Returns the descriptor of the root node on successful copy. - /// - /// - /// - /// - /// - /// - /// - /// - public static async Task CopyAsync(ITarget src, string srcRef, ITarget dst, string dstRef, CancellationToken cancellationToken) - { - if (src is null) - { - throw new Exception("null source target"); - } - if (dst is null) - { - throw new Exception("null destination target"); - } - if (dstRef == string.Empty) - { - dstRef = srcRef; - } - var root = await src.ResolveAsync(srcRef, cancellationToken); - await CopyGraphAsync(src, dst, root, cancellationToken); - await dst.TagAsync(root, dstRef, cancellationToken); - return root; - } - - public static async Task CopyGraphAsync(ITarget src, ITarget dst, Descriptor node, CancellationToken cancellationToken) - { - // check if node exists in target - if (!await dst.ExistsAsync(node, cancellationToken)) - { - // retrieve successors - var successors = await src.GetSuccessorsAsync(node, cancellationToken); - // obtain data stream - var dataStream = await src.FetchAsync(node, cancellationToken); - // check if the node has successors - if (successors != null) - { - foreach (var childNode in successors) - { - await CopyGraphAsync(src, dst, childNode, cancellationToken); - } - } - await dst.PushAsync(node, dataStream, cancellationToken); - } - } - } -} diff --git a/src/OrasProject.Oras/Extensions.cs b/src/OrasProject.Oras/Extensions.cs new file mode 100644 index 0000000..505f7ff --- /dev/null +++ b/src/OrasProject.Oras/Extensions.cs @@ -0,0 +1,73 @@ +// Copyright The ORAS Authors. +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +using OrasProject.Oras.Oci; +using System; +using System.Threading; +using System.Threading.Tasks; +using static OrasProject.Oras.Content.Extensions; + +namespace OrasProject.Oras; + +public static class Extensions +{ + + /// + /// Copy copies a rooted directed acyclic graph (DAG) with the tagged root node + /// in the source Target to the destination Target. + /// The destination reference will be the same as the source reference if the + /// destination reference is left blank. + /// Returns the descriptor of the root node on successful copy. + /// + /// + /// + /// + /// + /// + /// + /// + public static async Task CopyAsync(this ITarget src, string srcRef, ITarget dst, string dstRef, CancellationToken cancellationToken = default) + { + if (string.IsNullOrEmpty(dstRef)) + { + dstRef = srcRef; + } + var root = await src.ResolveAsync(srcRef, cancellationToken).ConfigureAwait(false); + await src.CopyGraphAsync(dst, root, cancellationToken).ConfigureAwait(false); + await dst.TagAsync(root, dstRef, cancellationToken).ConfigureAwait(false); + return root; + } + + public static async Task CopyGraphAsync(this ITarget src, ITarget dst, Descriptor node, CancellationToken cancellationToken) + { + // check if node exists in target + if (await dst.ExistsAsync(node, cancellationToken).ConfigureAwait(false)) + { + return; + } + + // retrieve successors + var successors = await src.GetSuccessorsAsync(node, cancellationToken).ConfigureAwait(false); + // obtain data stream + var dataStream = await src.FetchAsync(node, cancellationToken).ConfigureAwait(false); + // check if the node has successors + if (successors != null) + { + foreach (var childNode in successors) + { + await src.CopyGraphAsync(dst, childNode, cancellationToken).ConfigureAwait(false); + } + } + await dst.PushAsync(node, dataStream, cancellationToken).ConfigureAwait(false); + } +} diff --git a/src/OrasProject.Oras/ITarget.cs b/src/OrasProject.Oras/ITarget.cs index daefec3..d6fa4f3 100644 --- a/src/OrasProject.Oras/ITarget.cs +++ b/src/OrasProject.Oras/ITarget.cs @@ -9,8 +9,8 @@ // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and -// limitations under the License. - +// limitations under the License. + using OrasProject.Oras.Content; namespace OrasProject.Oras; diff --git a/tests/OrasProject.Oras.Tests/CopyTest.cs b/tests/OrasProject.Oras.Tests/CopyTest.cs index b8a7837..3fa66ce 100644 --- a/tests/OrasProject.Oras.Tests/CopyTest.cs +++ b/tests/OrasProject.Oras.Tests/CopyTest.cs @@ -70,7 +70,7 @@ public async Task CanCopyBetweenMemoryTargetsWithTaggedNode() var reference = "foobar"; await sourceTarget.TagAsync(root, reference, cancellationToken); var destinationTarget = new MemoryTarget(); - var gotDesc = await Copy.CopyAsync(sourceTarget, reference, destinationTarget, "", cancellationToken); + var gotDesc = await sourceTarget.CopyAsync(reference, destinationTarget, "", cancellationToken); Assert.Equal(gotDesc, root); Assert.Equal(await destinationTarget.ResolveAsync(reference, cancellationToken), root); @@ -130,7 +130,7 @@ public async Task CanCopyBetweenMemoryTargets() } var root = descs[3]; var destinationTarget = new MemoryTarget(); - await Copy.CopyGraphAsync(sourceTarget, destinationTarget, root, cancellationToken); + await sourceTarget.CopyGraphAsync(destinationTarget, root, cancellationToken); for (var i = 0; i < descs.Count; i++) { Assert.True(await destinationTarget.ExistsAsync(descs[i], cancellationToken)); diff --git a/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs b/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs index fc92779..9a5f882 100644 --- a/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs +++ b/tests/OrasProject.Oras.Tests/RemoteTest/RepositoryTest.cs @@ -2103,7 +2103,7 @@ public async Task CopyFromRepositoryToMemory() var dst = new MemoryTarget(); var tagName = "latest"; - var desc = await Copy.CopyAsync(src, tagName, dst, tagName, CancellationToken.None); + var desc = await src.CopyAsync(tagName, dst, tagName, CancellationToken.None); } [Fact]