Skip to content

Commit

Permalink
chore: add test coverage back
Browse files Browse the repository at this point in the history
  • Loading branch information
bmazzarol committed Dec 31, 2024
1 parent e61676e commit 9931d78
Show file tree
Hide file tree
Showing 16 changed files with 656 additions and 425 deletions.
24 changes: 22 additions & 2 deletions Tuxedo.SourceGenerator/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public static string RemoveGenerics(this string value)
return value == null ? null : SecurityElement.Escape(value);
}

public static string StripOutNameOf(this string value)
public static string StripExpressionParts(this string value)
{
if (
value.StartsWith("nameof(", StringComparison.Ordinal)
Expand All @@ -30,6 +30,26 @@ public static string StripOutNameOf(this string value)
{
return value.Substring(7, value.Length - 8);
}
return value;
return value.Replace("\"", string.Empty);
}

public static string? PrependIfNotNull(this string? value, string prepend)
{
return value == null ? value : $"{prepend}{value}";
}

public static string? RenderIfNotNull(this string? value, Func<string, string> render)
{
return value == null ? null : render(value);
}

public static string? LowercaseFirst(this string? value)
{
return value == null ? null : char.ToLowerInvariant(value[0]) + value.Substring(1);
}

public static string? UppercaseFirst(this string? value)
{
return value == null ? null : char.ToUpperInvariant(value[0]) + value.Substring(1);
}
}
Original file line number Diff line number Diff line change
@@ -1,139 +1,51 @@
using System.Diagnostics.CodeAnalysis;
#pragma warning disable MA0051

using Tuxedo.SourceGenerator.Extensions;

namespace Tuxedo.SourceGenerator;

public sealed partial class RefinementSourceGenerator
{
private sealed class MultiRefinedTypeModel : SingleRefinedTypeModel
private static string RenderMultiRefinedType(ViewModel model)
{
public string? AlternativeType { get; set; }
}

[SuppressMessage(
"Design",
"MA0051:Method is too long",
Justification = "template needs to be in one method"
)]
private static string RenderMultiRefinedType(MultiRefinedTypeModel model)
{
var rawImplicitConversion = $$"""
/// <summary>
/// Implicit conversion from the refined to the raw value
/// </summary>
/// <param name="this">refined value</param>
/// <returns>underlying raw value</returns>
public static implicit operator {{model.RawType}}({{model.RefinedType}}{{model.Generics}} @this)
{
return @this.RawValue;
}
""";

return $$"""
// <auto-generated/>
#nullable enable
{{RenderNamespaceParts(model)}}
using System.Diagnostics.CodeAnalysis;
namespace {{model.Namespace}};
/// <summary>
/// A refined {{model.RawType.EscapeXml()}} based on the {{model.Predicate.EscapeXml()}} refinement predicate which produces an alternative value {{model.AlternativeType.EscapeXml()}}
/// </summary>
{{model.AccessModifier}} readonly partial struct {{model.RefinedType}}{{model.Generics}}
{{model.GenericConstraints}}
{{RenderTypeNameParts(model)}}
{
private readonly {{model.RawType}}? _rawValue;
private readonly {{model.AlternativeType}}? _alternativeValue;
/// <summary>
/// The underlying refined value
/// </summary>
public {{model.RawType}} RawValue => _rawValue ?? throw new InvalidOperationException("Do not use the default value, please use the Parse and TryParse methods to construct a {{model.RefinedType}}");
/// <summary>
/// The alternative value produced by refining the raw value
/// </summary>
public {{model.AlternativeType}} RefinedValue => _alternativeValue ?? throw new InvalidOperationException("Do not use the default value, please use the Parse and TryParse methods to construct a {{model.RefinedType}}");
private {{model.RefinedType}}({{model.RawType}} rawValue, {{model.AlternativeType}} refinedValue)
{
_rawValue = rawValue;
_alternativeValue = refinedValue;
}
{{(
!string.Equals(model.RawType, model.AlternativeType, StringComparison.Ordinal)
? rawImplicitConversion
: string.Empty
)}}
/// <summary>
/// Implicit conversion from the refined to the refined value
/// </summary>
/// <param name="this">refined value</param>
/// <returns>underlying refined value</returns>
public static implicit operator {{model.AlternativeType}}({{model.RefinedType}}{{model.Generics}} @this)
{
return @this.RefinedValue;
}
/// <summary>
/// Explicit conversion from the raw to the refined value
/// </summary>
/// <param name="value">raw value</param>
/// <returns>refined value</returns>
/// <exception cref="InvalidOperationException">if the refinement fails</exception>
public static explicit operator {{model.RefinedType}}{{model.Generics}}({{model.RawType}} value)
{
return Parse(value);
}
/// <summary>
/// Try and refine the raw value
/// </summary>
/// <param name="value">raw value</param>
/// <param name="refined">refined value</param>
/// <param name="failureMessage">error message</param>
/// <returns>true if refined, false otherwise</returns>
public static bool TryParse(
{{model.RawType}} value,
out {{model.RefinedType}}{{model.Generics}} refined,
[NotNullWhen(false)] out string? failureMessage
)
{
if ({{model.Predicate}}{{model.Generics}}(value, out var alternative))
{
refined = new {{model.RefinedType}}{{model.Generics}}(value, alternative);
failureMessage = null;
return true;
}
{{RenderTypePropertyParts(
"Value",
model.RawType,
model,
addImplicitOperator: !string.Equals(model.RawType, model.AlternativeType, StringComparison.Ordinal))}}
refined = default!;
failureMessage = ${{model.FailureMessage}};
return false;
}
/// <summary>
/// Refines the value or throws
/// </summary>
/// <param name="value">raw value</param>
/// <returns>refined value</returns>
/// <exception cref="InvalidOperationException">if the refinement fails</exception>
public static {{model.RefinedType}}{{model.Generics}} Parse({{model.RawType}} value)
{{RenderTypePropertyParts(
"AltValue",
model.AlternativeType,
model,
addImplicitOperator: true)}}
private {{model.RefinedType}}({{model.RawType}} value, {{model.AlternativeType}} altValue)
{
return TryParse(value, out var result, out var failureMessage) ? result : throw new InvalidOperationException(failureMessage);
_value = value;
_altValue = altValue;
}
{{RenderParseMethod(model)}}
{{RenderTryParseMethod(model)}}
{{RenderEqualityMembers(model)}}
/// <summary>
/// Standard deconstruction to the underlying values
/// </summary>
/// <param name="rawValue">raw value</param>
/// <param name="refined">refined value</param>
public void Deconstruct(out {{model.RawType}} rawValue, out {{model.AlternativeType}} refined)
/// <param name="value">raw {{model.RawType.EscapeXml()}}</param>
/// <param name="altValue">alternative {{model.AlternativeType.EscapeXml()}}</param>
public void Deconstruct(out {{model.RawType}} value, out {{model.AlternativeType}} altValue)
{
rawValue = RawValue;
refined = RefinedValue;
value = Value;
altValue = AltValue;
}
}
""";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public RefinementAttributeParts(MethodDeclarationSyntax methodDeclaration)
nameToArgs.TryGetValue(nameof(IsInternal), out var value)
&& string.Equals(value, "true", StringComparison.Ordinal);
Name = nameToArgs.TryGetValue(nameof(Name), out var nameToName)
? nameToName.StripOutNameOf()
? nameToName.StripExpressionParts()
: null;
}
}
Expand Down
Loading

0 comments on commit 9931d78

Please sign in to comment.