< Summary

Information
Class: DotNetApiDiff.ApiExtraction.NameMapper
Assembly: DotNetApiDiff
File(s): /home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/ApiExtraction/NameMapper.cs
Line coverage
88%
Covered lines: 99
Uncovered lines: 13
Coverable lines: 112
Total lines: 218
Line coverage: 88.3%
Branch coverage
79%
Covered branches: 35
Total branches: 44
Branch coverage: 79.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)66.66%66100%
get_Configuration()100%210%
MapNamespace(...)90%101092.85%
MapTypeName(...)83.33%6687.5%
MapFullTypeName(...)83.33%121290.24%
ShouldAutoMapType(...)75%9876.47%
CombineNamespaceParts(...)50%22100%

File(s)

/home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/ApiExtraction/NameMapper.cs

#LineLine coverage
 1// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
 2
 3using DotNetApiDiff.Interfaces;
 4using DotNetApiDiff.Models.Configuration;
 5using Microsoft.Extensions.Logging;
 6
 7namespace DotNetApiDiff.ApiExtraction;
 8
 9/// <summary>
 10/// Implements namespace and type name mapping between assemblies
 11/// </summary>
 12public class NameMapper : INameMapper
 13{
 14    private readonly ILogger<NameMapper> _logger;
 15    private readonly MappingConfiguration _configuration;
 16    private readonly StringComparison _stringComparison;
 17
 18    /// <summary>
 19    /// Creates a new instance of the NameMapper
 20    /// </summary>
 21    /// <param name="configuration">Mapping configuration</param>
 22    /// <param name="logger">Logger for diagnostic information</param>
 2623    public NameMapper(MappingConfiguration configuration, ILogger<NameMapper> logger)
 2624    {
 2625        _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration));
 2626        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 2627        _stringComparison = configuration.IgnoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
 2628    }
 29
 30    /// <summary>
 31    /// Gets the mapping configuration
 32    /// </summary>
 033    public MappingConfiguration Configuration => _configuration;
 34
 35    /// <summary>
 36    /// Maps a source namespace to one or more target namespaces
 37    /// </summary>
 38    /// <param name="sourceNamespace">The source namespace to map</param>
 39    /// <returns>A collection of mapped target namespaces</returns>
 40    public IEnumerable<string> MapNamespace(string sourceNamespace)
 1441    {
 1442        if (string.IsNullOrEmpty(sourceNamespace))
 043        {
 044            return new[] { string.Empty };
 45        }
 46
 47        // Check for exact match first
 5548        foreach (var mapping in _configuration.NamespaceMappings)
 1049        {
 1050            if (string.Equals(mapping.Key, sourceNamespace, _stringComparison))
 751            {
 752                _logger.LogDebug(
 753                    "Mapped namespace {SourceNamespace} to {TargetNamespaces}",
 754                    sourceNamespace,
 755                    string.Join(", ", mapping.Value));
 756                return mapping.Value;
 57            }
 358        }
 59
 60        // Check for prefix matches (e.g., "Company.Product" -> "NewCompany.Product")
 2561        foreach (var mapping in _configuration.NamespaceMappings)
 362        {
 363            if (sourceNamespace.StartsWith(mapping.Key + ".", _stringComparison))
 264            {
 265                var suffix = sourceNamespace.Substring(mapping.Key.Length + 1);
 566                var results = mapping.Value.Select(target => CombineNamespaceParts(target, suffix)).ToList();
 67
 268                _logger.LogDebug(
 269                    "Mapped namespace {SourceNamespace} to {TargetNamespaces} using prefix mapping",
 270                    sourceNamespace,
 271                    string.Join(", ", results));
 272                return results;
 73            }
 174        }
 75
 76        // No mapping found, return the original
 577        return new[] { sourceNamespace };
 1478    }
 79
 80    /// <summary>
 81    /// Maps a source type name to a target type name
 82    /// </summary>
 83    /// <param name="sourceTypeName">The source type name to map</param>
 84    /// <returns>The mapped target type name, or the original if no mapping exists</returns>
 85    public string MapTypeName(string sourceTypeName)
 1186    {
 1187        if (string.IsNullOrEmpty(sourceTypeName))
 088        {
 089            return string.Empty;
 90        }
 91
 92        // Check for exact match
 3993        foreach (var mapping in _configuration.TypeMappings)
 594        {
 595            if (string.Equals(mapping.Key, sourceTypeName, _stringComparison))
 496            {
 497                _logger.LogDebug(
 498                    "Mapped type name {SourceTypeName} to {TargetTypeName}",
 499                    sourceTypeName,
 4100                    mapping.Value);
 4101                return mapping.Value;
 102            }
 1103        }
 104
 105        // No mapping found, return the original
 7106        return sourceTypeName;
 11107    }
 108
 109    /// <summary>
 110    /// Maps a fully qualified type name (namespace + type name) to one or more possible target type names
 111    /// </summary>
 112    /// <param name="sourceFullName">The fully qualified source type name</param>
 113    /// <returns>A collection of possible mapped target type names</returns>
 114    public IEnumerable<string> MapFullTypeName(string sourceFullName)
 8115    {
 8116        if (string.IsNullOrEmpty(sourceFullName))
 0117        {
 0118            return new[] { string.Empty };
 119        }
 120
 121        // Check for exact type mapping first
 27122        foreach (var mapping in _configuration.TypeMappings)
 2123        {
 2124            if (string.Equals(mapping.Key, sourceFullName, _stringComparison))
 1125            {
 1126                _logger.LogDebug(
 1127                    "Mapped full type name {SourceFullName} to {TargetFullName} using exact type mapping",
 1128                    sourceFullName,
 1129                    mapping.Value);
 1130                return new[] { mapping.Value };
 131            }
 1132        }
 133
 134        // If no exact match, try to split into namespace and type name
 7135        int lastDotIndex = sourceFullName.LastIndexOf('.');
 7136        if (lastDotIndex <= 0)
 0137        {
 138            // No namespace part or empty namespace
 0139            return new[] { MapTypeName(sourceFullName) };
 140        }
 141
 7142        string sourceNamespace = sourceFullName.Substring(0, lastDotIndex);
 7143        string sourceType = sourceFullName.Substring(lastDotIndex + 1);
 144
 145        // Map the namespace and type separately
 7146        var mappedNamespaces = MapNamespace(sourceNamespace);
 7147        string mappedType = MapTypeName(sourceType);
 148
 149        // Combine the mapped namespaces with the mapped type
 7150        var results = mappedNamespaces
 9151            .Select(ns => CombineNamespaceParts(ns, mappedType))
 7152            .ToList();
 153
 7154        if (results.Count > 1)
 2155        {
 2156            _logger.LogDebug(
 2157                "Mapped full type name {SourceFullName} to multiple targets: {TargetFullNames}",
 2158                sourceFullName,
 2159                string.Join(", ", results));
 2160        }
 5161        else if (results.Count == 1)
 5162        {
 5163            _logger.LogDebug(
 5164                "Mapped full type name {SourceFullName} to {TargetFullName}",
 5165                sourceFullName,
 5166                results[0]);
 5167        }
 168
 7169        return results;
 8170    }
 171
 172    /// <summary>
 173    /// Checks if a type name should be auto-mapped based on configuration
 174    /// </summary>
 175    /// <param name="typeName">The type name to check</param>
 176    /// <returns>True if the type should be auto-mapped, false otherwise</returns>
 177    public bool ShouldAutoMapType(string typeName)
 6178    {
 6179        if (!_configuration.AutoMapSameNameTypes)
 3180        {
 3181            return false;
 182        }
 183
 3184        if (string.IsNullOrEmpty(typeName))
 0185        {
 0186            return false;
 187        }
 188
 189        // Extract the simple type name (without namespace)
 3190        int lastDotIndex = typeName.LastIndexOf('.');
 3191        if (lastDotIndex <= 0)
 0192        {
 193            // No namespace part or empty namespace
 0194            return false;
 195        }
 196
 3197        string simpleTypeName = typeName.Substring(lastDotIndex + 1);
 198
 199        // Don't auto-map generic type definitions with backticks
 3200        if (simpleTypeName.Contains('`'))
 1201        {
 1202            return false;
 203        }
 204
 2205        return true;
 6206    }
 207
 208    /// <summary>
 209    /// Combines namespace parts with proper handling of empty namespaces
 210    /// </summary>
 211    /// <param name="namespacePart">First namespace part</param>
 212    /// <param name="suffix">Second namespace part</param>
 213    /// <returns>Combined namespace</returns>
 214    private string CombineNamespaceParts(string namespacePart, string suffix)
 12215    {
 12216        return string.IsNullOrEmpty(namespacePart) ? suffix : $"{namespacePart}.{suffix}";
 12217    }
 218}