8000 Decorate Diff.Compare generics with interface · github/libgit2sharp@8a673bb · GitHub
[go: up one dir, main page]

Skip to content
This repository was archived by the owner on Feb 3, 2023. It is now read-only.

Commit 8a673bb

Browse files
committed
Decorate Diff.Compare generics with interface
This allows for a typesafe Diff.Compare model. This commit introduces a new IDiffResult interface which marks classes which can be passed to Diff.Compare<T> so we get rid of the runtime exception. Fixes libgit2#1176.
1 parent b279955 commit 8a673bb

File tree

8 files changed

+58
-69
lines changed

8 files changed

+58
-69
lines changed

LibGit2Sharp.Tests/DiffTreeToTreeFixture.cs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,17 +1123,6 @@ public void RetrievingDiffChangesMustAlwaysBeCaseSensitive()
11231123
}
11241124
}
11251125

1126-
[Fact]
1127-
public void CallingCompareWithAnUnsupportedGenericParamThrows()
1128-
{
1129-
var path = SandboxStandardTestRepoGitDir();
1130-
using (var repo = new Repository(path))
1131-
{
1132-
Assert.Throws<LibGit2SharpException>(() => repo.Diff.Compare<string>(default(Tree), default(Tree)));
1133-
Assert.Throws<LibGit2SharpException>(() => repo.Diff.Compare<string>());
1134-
}
1135-
}
1136-
11371126
[Fact]
11381127
public void UsingPatienceAlgorithmCompareOptionProducesPatienceDiff()
11391128
{

LibGit2Sharp.Tests/MetaFixture.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class MetaFixture
1717
{
1818
private static readonly HashSet<Type> explicitOnlyInterfaces = new HashSet<Type>
1919
{
20-
typeof(IBelongToARepository),
20+
typeof(IBelongToARepository), typeof(IDiffResult),
2121
};
2222

2323
[Fact]
@@ -401,6 +401,20 @@ where method.IsDefined(typeof(ExtensionAttribute), false)
401401
select method;
402402
return query;
403403
}
404+
405+
[Fact]
406+
public void AllIDiffResultsAreInChangesBuilder()
407+
{
408+
var diff = typeof(Diff).GetField("ChangesBuilders", BindingFlags.NonPublic | BindingFlags.Static);
409+
var changesBuilders = (System.Collections.IDictionary)diff.GetValue(null);
410+
411+
IEnumerable<Type> diffResults = typeof(Diff).Assembly.GetExportedTypes()
412+
.Where(type => type.GetInterface("IDiffResult") != null);
413+
414+
var nonBuilderTypes = diffResults.Where(diffResult => !changesBuilders.Contains(diffResult));
415+
Assert.False(nonBuilderTypes.Any(), "Classes which implement IDiffResult but are not registered under ChangesBuilders in Diff:" + Environment.NewLine +
416+
string.Join(Environment.NewLine, nonBuilderTypes.Select(type => type.FullName)));
417+
}
404418
}
405419

406420
internal static class TypeExtensions

LibGit2Sharp/Diff.cs

Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,21 @@ private static IDictionary<DiffTargets, Func<Repository, TreeComparisonHandleRet
102102
{ typeof(PatchStats), diff => new PatchStats(diff) },
103103
};
104104

105+
106+
private static T BuildDiffResult<T>(DiffSafeHandle diff) where T : class, IDiffResult
107+
{
108+
Func<DiffSafeHandle, object> builder;
109+
110+
if (!ChangesBuilders.TryGetValue(typeof(T), out builder))
111+
{
112+
throw new LibGit2SharpException(CultureInfo.InvariantCulture,
113+
"User-defined types passed to Compare are not supported. Supported values are: {0}",
114+
string.Join(", ", ChangesBuilders.Keys.Select(x => x.Name)));
115+
}
116+
117+
return (T)builder(diff);
118+
}
119+
105120
/// <summary>
106121
/// Show changes between two <see cref="Blob"/>s.
107122
/// </summary>
@@ -134,7 +149,7 @@ public virtual ContentChanges Compare(Blob oldBlob, Blob newBlob, CompareOptions
134149
/// <param name="oldTree">The <see cref="Tree"/> you want to compare from.</param>
135150
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
136151
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
137-
public virtual T Compare<T>(Tree oldTree, Tree newTree) where T : class
152+
public virtual T Compare<T>(Tree oldTree, Tree newTree) where T : class, IDiffResult
138153
{
139154
return Compare<T>(oldTree, newTree, null, null, null);
140155
}
@@ -146,7 +161,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree) where T : class
146161
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
147162
/// <param name="paths">The list of paths (either files or directories) that should be compared.</param>
148163
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
149-
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths) where T : class
164+
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths) where T : class, IDiffResult
150165
{
151166
return Compare<T>(oldTree, newTree, paths, null, null);
152167
}
@@ -163,7 +178,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
163178
/// </param>
164179
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
165180
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths,
166-
ExplicitPathsOptions explicitPathsOptions) where T : class
181+
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
167182
{
168183
return Compare<T>(oldTree, newTree, paths, explicitPathsOptions, null);
169184
}
@@ -176,7 +191,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
176191
/// <param name="paths">The list of paths (either files or directories) that should be compared.</param>
177192
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
178193
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
179-
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, CompareOptions compareOptions) where T : class
194+
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, CompareOptions compareOptions) where T : class, IDiffResult
180195
{
181196
return Compare<T>(oldTree, newTree, paths, null, compareOptions);
182197
}
@@ -188,7 +203,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
188203
/// <param name="newTree">The <see cref="Tree"/> you want to compare to.</param>
189204
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
190205
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
191-
public virtual T Compare<T>(Tree oldTree, Tree newTree, CompareOptions compareOptions) where T : class
206+
public virtual T Compare<T>(Tree oldTree, Tree newTree, CompareOptions compareOptions) where T : class, IDiffResult
192207
{
193208
return Compare<T>(oldTree, newTree, null, null, compareOptions);
194209
}
@@ -206,19 +221,8 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, CompareOptions compareOp
206221
/// <param name="compareOptions">Additional options to define patch generation behavior.</param>
207222
/// <returns>A <see cref="TreeChanges"/> containing the changes between the <paramref name="oldTree"/> and the <paramref name="newTree"/>.</returns>
208223
public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> paths, ExplicitPathsOptions explicitPathsOptions,
209-
CompareOptions compareOptions) where T : class
224+
CompareOptions compareOptions) where T : class, IDiffResult
210225
{
211-
Func<DiffSafeHandle, object> builder;
212-
213-
if (!ChangesBuilders.TryGetValue(typeof(T), out builder))
214-
{
215-
throw new LibGit2SharpException(CultureInfo.InvariantCulture,
216-
"Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.",
217-
typeof(T),
218-
typeof(TreeChanges),
219-
typeof(Patch));
220-
}
221-
222226
var comparer = TreeToTree(repo);
223227
ObjectId oldTreeId = oldTree != null ? oldTree.Id : null;
224228
ObjectId newTreeId = newTree != null ? newTree.Id : null;
@@ -236,7 +240,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
236240

237241
using (DiffSafeHandle diff = BuildDiffList(oldTreeId, newTreeId, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
238242
{
239-
return (T)builder(diff);
243+
return BuildDiffResult<T>(diff);
240244
}
241245
}
242246

@@ -252,7 +256,7 @@ public virtual T Compare<T>(Tree oldTree, Tree newTree, IEnumerable<string> path
252256
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
253257
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
254258
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
255-
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets) where T : class
259+
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets) where T : class, IDiffResult
256260
{
257261
return Compare<T>(oldTree, diffTargets, null, null, null);
258262
}
@@ -270,7 +274,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets) where T : cla
270274
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
271275
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
272276
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
273-
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths) where T : class
277+
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths) where T : class, IDiffResult
274278
{
275279
return Compare<T>(oldTree, diffTargets, paths, null, null);
276280
}
@@ -293,7 +297,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
293297
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
294298
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
295299
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths,
296-
ExplicitPathsOptions explicitPathsOptions) where T : class
300+
ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
297301
{
298302
return Compare<T>(oldTree, diffTargets, paths, explicitPathsOptions, null);
299303
}
@@ -317,19 +321,8 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
317321
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
318322
/// <returns>A <typeparamref name="T"/> containing the changes between the <see cref="Tree"/> and the selected target.</returns>
319323
public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<string> paths,
320-
ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) where T : class
324+
ExplicitPathsOptions explicitPathsOptions, CompareOptions compareOptions) where T : class, IDiffResult
321325
{
322-
Func<DiffSafeHandle, object> builder;
323-
324-
if (!ChangesBuilders.TryGetValue(typeof(T), out builder))
325-
{
326-
throw new LibGit2SharpException(CultureInfo.InvariantCulture,
327-
"Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.",
328-
typeof(T),
329-
typeof(TreeChanges),
330-
typeof(Patch));
331-
}
332-
333326
var comparer = HandleRetrieverDispatcher[diffTargets](repo);
334327
ObjectId oldTreeId = oldTree != null ? oldTree.Id : null;
335328

@@ -349,7 +342,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
349342

350343
using (DiffSafeHandle diff = BuildDiffList(oldTreeId, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
351344
{
352-
return (T)builder(diff);
345+
return BuildDiffResult<T>(diff);
353346
}
354347
}
355348

@@ -363,7 +356,7 @@ public virtual T Compare<T>(Tree oldTree, DiffTargets diffTargets, IEnumerable<s
363356
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
364357
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
365358
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
366-
public virtual T Compare<T>() where T : class
359+
public virtual T Compare<T>() where T : class, IDiffResult
367360
{
368361
return Compare<T>(DiffModifiers.None);
369362
}
@@ -379,7 +372,7 @@ public virtual T Compare<T>() where T : class
379372
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
380373
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
381374
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
382-
public virtual T Compare<T>(IEnumerable<string> paths) where T : class
375+
public virtual T Compare<T>(IEnumerable<string> paths) where T : class, IDiffResult
383376
{
384377
return Compare<T>(DiffModifiers.None, paths);
385378
}
@@ -396,7 +389,7 @@ public virtual T Compare<T>(IEnumerable<string> paths) where T : class
396389
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
397390
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
398391
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
399-
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked) where T : class
392+
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked) where T : class, IDiffResult
400393
{
401394
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths);
402395
}
@@ -417,7 +410,7 @@ public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked) wh
417410
/// <typeparam name="T">Can be either a <see cref="TreeChanges"/> if you are only interested in the list of files modified, added, ..., or
418411
/// a <see cref="Patch"/> if you want the actual patch content for the whole diff and for individual files.</typeparam>
419412
/// <returns>A <typeparamref name="T"/> containing the changes between the working directory and the index.</returns>
420-
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked, ExplicitPathsOptions explicitPathsOptions) where T : class
413+
public virtual T Compare<T>(IEnumerable<string> paths, bool includeUntracked, ExplicitPathsOptions explicitPathsOptions) where T : class, IDiffResult
421414
{
422415
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions);
423416
}
@@ -443,7 +436,7 @@ public virtual T Compare<T>(
443436
IEnumerable<string> paths,
444437
bool includeUntracked,
445438
ExplicitPathsOptions explicitPathsOptions,
446-
CompareOptions compareOptions) where T : class
439+
CompareOptions compareOptions) where T : class, IDiffResult
447440
{
448441
return Compare<T>(includeUntracked ? DiffModifiers.IncludeUntracked : DiffModifiers.None, paths, explicitPathsOptions, compareOptions);
449442
}
@@ -452,19 +445,8 @@ internal virtual T Compare<T>(
452445
DiffModifiers diffOptions,
453446
IEnumerable<string> paths = null,
454447
ExplicitPathsOptions explicitPathsOptions = null,
455-
CompareOptions compareOptions = null) where T : class
448+
CompareOptions compareOptions = null) where T : class, IDiffResult
456449
{
457-
Func<DiffSafeHandle, object> builder;
458-
459-
if (!ChangesBuilders.TryGetValue(typeof(T), out builder))
460-
{
461-
throw new LibGit2SharpException(CultureInfo.InvariantCulture,
462-
"Unexpected type '{0}' passed to Compare. Supported values are either '{1}' or '{2}'.",
463-
typeof(T),
464-
typeof(TreeChanges),
465-
typeof(Patch));
466-
}
467-
468450
var comparer = WorkdirToIndex(repo);
469451

470452
if (explicitPathsOptions != null)
@@ -479,7 +461,7 @@ internal virtual T Compare<T>(
479461

480462
using (DiffSafeHandle diff = BuildDiffList(null, null, comparer, diffOptions, paths, explicitPathsOptions, compareOptions))
481463
{
482-
return (T)builder(diff);
464+
return BuildDiffResult<T>(diff);
483465
}
484466
}
485467

LibGit2Sharp/IDiffResult.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace LibGit2Sharp
2+
{
3+
public interface IDiffResult
4+
{ }
5+
}

LibGit2Sharp/LibGit2Sharp.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,7 @@
380380
<Compile Include="Core\GitCertificateSsh.cs" />
381381
<Compile Include="Core\GitCertificateSshType.cs" />
382382
<Compile Include="CertificateSsh.cs" />
383+
<Compile Include="IDiffResult.cs" />
383384
</ItemGroup>
384385
<ItemGroup>
385386
<CodeAnalysisDictionary Include="CustomDictionary.xml" />

LibGit2Sharp/Patch.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace LibGit2Sharp
1616
/// deleted, modified, ..., then consider using a simpler <see cref="TreeChanges"/>.</para>
1717
/// </summary>
1818
[DebuggerDisplay("{DebuggerDisplay,nq}")]
19-
public class Patch : IEnumerable<PatchEntryChanges>
19+
public class Patch : IEnumerable<PatchEntryChanges>, IDiffResult
2020
{
2121
private readonly StringBuilder fullPatchBuilder = new StringBuilder();
2222

@@ -41,7 +41,6 @@ internal Patch(DiffSafeHandle diff)
4141
AddFileChange(delta);
4242
Proxy.git_patch_print(patch, PrintCallBack);
4343
}
44-
4544
}
4645
}
4746

LibGit2Sharp/PatchStats.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ namespace LibGit2Sharp
1313
/// <para>The individual patches for each file can be accessed through the indexer of this class.</para>
1414
/// </summary>
1515
[DebuggerDisplay("{DebuggerDisplay,nq}")]
16-
public class PatchStats : IEnumerable<ContentChangeStats>
16+
public class PatchStats : IEnumerable<ContentChangeStats>, IDiffResult
1717
{
1818
private readonly IDictionary<FilePath, ContentChangeStats> changes = new Dictionary<FilePath, ContentChangeStats>();
1919
private readonly int totalLinesAdded;

LibGit2Sharp/TreeChanges.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ namespace LibGit2Sharp
1515
/// <para>To obtain the actual patch of the diff, use the <see cref="Patch"/> class when calling Compare.</para>.
1616
/// </summary>
1717
[DebuggerDisplay("{DebuggerDisplay,nq}")]
18-
public class TreeChanges : IEnumerable<TreeEntryChanges>
18+
public class TreeChanges : IEnumerable<TreeEntryChanges>, IDiffResult
1919
{
2020
private readonly List<TreeEntryChanges> changes = new List<TreeEntryChanges>();
2121
private readonly List<TreeEntryChanges> added = new List<TreeEntryChanges>();
@@ -91,7 +91,6 @@ IEnumerator IEnumerable.GetEnumerator()
9191

9292
#endregion
9393

94-
9594
/// <summary>
9695
/// List of <see cref="TreeEntryChanges"/> that have been been added.
9796
/// </summary>

0 commit comments

Comments
 (0)
0