@@ -41,6 +41,20 @@ public class MappingCollection
4141 /// </summary>
4242 private readonly MappingCollectionInstance < IExistingTargetMapping , IExistingTargetUserMapping > _existingTargetMappings = new ( ) ;
4343
44+ /// <summary>
45+ /// Generic user-implemented new instance mapping templates.
46+ /// These are matched against concrete type pairs during <see cref="FindNewInstanceMapping"/>.
47+ /// </summary>
48+ private readonly List < GenericUserImplementedNewInstanceMethodMapping > _genericNewInstanceTemplates = [ ] ;
49+
50+ /// <summary>
51+ /// Generic user-implemented existing target mapping templates.
52+ /// These are matched against concrete type pairs during <see cref="FindExistingInstanceMapping"/>.
53+ /// </summary>
54+ private readonly List < GenericUserImplementedExistingTargetMethodMapping > _genericExistingTargetTemplates = [ ] ;
55+
56+ private GenericTypeChecker ? _genericTypeChecker ;
57+
4458 /// <inheritdoc cref="_methodMappings"/>
4559 public IReadOnlyCollection < MethodMapping > MethodMappings => _methodMappings ;
4660
@@ -62,15 +76,18 @@ public class MappingCollection
6276 . Concat ( _newInstanceMappings . UsedDuplicatedNonDefaultNonReferencedUserMappings )
6377 . Concat ( _existingTargetMappings . UsedDuplicatedNonDefaultNonReferencedUserMappings ) ;
6478
79+ public void SetGenericTypeChecker ( GenericTypeChecker genericTypeChecker ) => _genericTypeChecker = genericTypeChecker ;
80+
6581 public INewInstanceMapping ? FindNewInstanceMapping ( TypeMappingKey mappingKey , ParameterScope ? scope = null ) =>
66- _newInstanceMappings . Find ( mappingKey , scope ) ;
82+ _newInstanceMappings . Find ( mappingKey , scope ) ?? TryMatchGenericNewInstanceTemplate ( mappingKey ) ;
6783
6884 public INewInstanceUserMapping ? FindNewInstanceUserMapping ( IMethodSymbol method ) => _newInstanceMappings . FindUserMapping ( method ) ;
6985
7086 public INewInstanceMapping ? FindNamedNewInstanceMapping ( string name , out bool ambiguousName ) =>
7187 _newInstanceMappings . FindNamed ( name , out ambiguousName ) ;
7288
73- public IExistingTargetMapping ? FindExistingInstanceMapping ( TypeMappingKey mappingKey ) => _existingTargetMappings . Find ( mappingKey ) ;
89+ public IExistingTargetMapping ? FindExistingInstanceMapping ( TypeMappingKey mappingKey ) =>
90+ _existingTargetMappings . Find ( mappingKey ) ?? TryMatchGenericExistingTargetTemplate ( mappingKey ) ;
7491
7592 public IExistingTargetMapping ? FindExistingInstanceNamedMapping ( string name , out bool ambiguousName ) =>
7693 _existingTargetMappings . FindNamed ( name , out ambiguousName ) ;
@@ -88,6 +105,17 @@ public MappingCollectionAddResult AddUserMapping(IUserMapping userMapping, strin
88105 _methodMappings . Add ( methodMapping ) ;
89106 }
90107
108+ // Generic user-implemented mapping templates are stored separately
109+ // and matched lazily during Find operations.
110+ if ( userMapping is GenericUserImplementedNewInstanceMethodMapping genericNewInstance )
111+ {
112+ _genericNewInstanceTemplates . Add ( genericNewInstance ) ;
113+ }
114+ else if ( userMapping is GenericUserImplementedExistingTargetMethodMapping genericExistingTarget )
115+ {
116+ _genericExistingTargetTemplates . Add ( genericExistingTarget ) ;
117+ }
118+
91119 return userMapping switch
92120 {
93121 INewInstanceUserMapping newInstanceMapping => _newInstanceMappings . AddUserMapping (
@@ -158,6 +186,42 @@ public void AddNamedExistingInstanceUserMapping(string name, IExistingTargetUser
158186 _existingTargetMappings . AddNamedUserMapping ( name , mapping ) ;
159187 }
160188
189+ private INewInstanceMapping ? TryMatchGenericNewInstanceTemplate ( TypeMappingKey mappingKey )
190+ {
191+ if ( _genericTypeChecker == null || _genericNewInstanceTemplates . Count == 0 )
192+ return null ;
193+
194+ foreach ( var template in _genericNewInstanceTemplates )
195+ {
196+ if ( template . TryCreateConcreteMapping ( _genericTypeChecker , mappingKey . Source , mappingKey . Target ) is not { } mapping )
197+ continue ;
198+
199+ // Cache the concrete mapping so subsequent lookups find it directly.
200+ _newInstanceMappings . TryAddAsDefault ( mapping , mappingKey . Configuration ) ;
201+ return mapping ;
202+ }
203+
204+ return null ;
205+ }
206+
207+ private IExistingTargetMapping ? TryMatchGenericExistingTargetTemplate ( TypeMappingKey mappingKey )
208+ {
209+ if ( _genericTypeChecker == null || _genericExistingTargetTemplates . Count == 0 )
210+ return null ;
211+
212+ foreach ( var template in _genericExistingTargetTemplates )
213+ {
214+ if ( template . TryCreateConcreteMapping ( _genericTypeChecker , mappingKey . Source , mappingKey . Target ) is not { } mapping )
215+ continue ;
216+
217+ // Cache the concrete mapping so subsequent lookups find it directly.
218+ _existingTargetMappings . TryAddAsDefault ( mapping , mappingKey . Configuration ) ;
219+ return mapping ;
220+ }
221+
222+ return null ;
223+ }
224+
161225 private class MappingCollectionInstance < T , TUserMapping >
162226 where T : ITypeMapping
163227 where TUserMapping : T , IUserMapping
0 commit comments