diff --git a/src/MapTo/Extensions/GeneratorExecutionContextExtensions.cs b/src/MapTo/Extensions/GeneratorExecutionContextExtensions.cs
index 7403ccc..e03c45f 100644
--- a/src/MapTo/Extensions/GeneratorExecutionContextExtensions.cs
+++ b/src/MapTo/Extensions/GeneratorExecutionContextExtensions.cs
@@ -35,7 +35,7 @@ namespace MapTo.Extensions
internal static string GetBuildPropertyName(string propertyName) => $"build_property.{PropertyNameSuffix}{propertyName}";
- internal static void AddSource(this GeneratorExecutionContext context, Source source) =>
- context.AddSource(source.HintName, source.Code);
+ internal static void AddSource(this GeneratorExecutionContext context, SourceCode sourceCode) =>
+ context.AddSource(sourceCode.HintName, sourceCode.Text);
}
}
\ No newline at end of file
diff --git a/src/MapTo/MapToGenerator.cs b/src/MapTo/MapToGenerator.cs
index e5d77d2..e0a18ed 100644
--- a/src/MapTo/MapToGenerator.cs
+++ b/src/MapTo/MapToGenerator.cs
@@ -25,6 +25,7 @@ namespace MapTo
context.AddSource(MapFromAttributeSource.Generate(options));
context.AddSource(IgnorePropertyAttributeSource.Generate(options));
context.AddSource(TypeConverterSource.Generate(options));
+ context.AddSource(MapPropertyAttributeSource.Generate(options));
if (context.SyntaxReceiver is MapToSyntaxReceiver receiver && receiver.CandidateClasses.Any())
{
diff --git a/src/MapTo/Sources/IgnorePropertyAttributeSource.cs b/src/MapTo/Sources/IgnorePropertyAttributeSource.cs
index c82ddc2..bb0a0bf 100644
--- a/src/MapTo/Sources/IgnorePropertyAttributeSource.cs
+++ b/src/MapTo/Sources/IgnorePropertyAttributeSource.cs
@@ -7,7 +7,7 @@ namespace MapTo.Sources
{
internal const string AttributeName = "IgnoreProperty";
- internal static Source Generate(SourceGenerationOptions options)
+ internal static SourceCode Generate(SourceGenerationOptions options)
{
var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
diff --git a/src/MapTo/Sources/MapClassSource.cs b/src/MapTo/Sources/MapClassSource.cs
index 268d607..2952117 100644
--- a/src/MapTo/Sources/MapClassSource.cs
+++ b/src/MapTo/Sources/MapClassSource.cs
@@ -6,7 +6,7 @@ namespace MapTo.Sources
{
internal static class MapClassSource
{
- internal static Source Generate(MapModel model)
+ internal static SourceCode Generate(MapModel model)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
diff --git a/src/MapTo/Sources/MapFromAttributeSource.cs b/src/MapTo/Sources/MapFromAttributeSource.cs
index a938e5d..8b404fd 100644
--- a/src/MapTo/Sources/MapFromAttributeSource.cs
+++ b/src/MapTo/Sources/MapFromAttributeSource.cs
@@ -7,7 +7,7 @@ namespace MapTo.Sources
{
internal const string AttributeName = "MapFrom";
- internal static Source Generate(SourceGenerationOptions options)
+ internal static SourceCode Generate(SourceGenerationOptions options)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
diff --git a/src/MapTo/Sources/MapPropertyAttributeSource.cs b/src/MapTo/Sources/MapPropertyAttributeSource.cs
new file mode 100644
index 0000000..7e389ef
--- /dev/null
+++ b/src/MapTo/Sources/MapPropertyAttributeSource.cs
@@ -0,0 +1,64 @@
+using MapTo.Models;
+using static MapTo.Sources.Constants;
+
+namespace MapTo.Sources
+{
+ internal static class MapPropertyAttributeSource
+ {
+ internal const string AttributeName = "MapProperty";
+
+ internal static SourceCode Generate(SourceGenerationOptions options)
+ {
+ using var builder = new SourceBuilder()
+ .WriteLine(GeneratedFilesHeader)
+ .WriteLine("using System;")
+ .WriteLine()
+ .WriteLine($"namespace {RootNamespace}")
+ .WriteOpeningBracket();
+
+ if (options.GenerateXmlDocument)
+ {
+ builder
+ .WriteLine("/// ")
+ .WriteLine("/// Maps a property to property of another object.")
+ .WriteLine("/// ");
+ }
+
+ builder
+ .WriteLine("[AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]")
+ .WriteLine($"public sealed class {AttributeName}Attribute : Attribute")
+ .WriteOpeningBracket();
+
+ if (options.GenerateXmlDocument)
+ {
+ builder
+ .WriteLine("/// ")
+ .WriteLine("/// Initializes a new instance of .")
+ .WriteLine("/// ")
+ .WriteLine("/// The to convert the value of the annotated property.");
+ }
+
+ builder
+ .WriteLine($"public {AttributeName}Attribute(Type converter = null)")
+ .WriteOpeningBracket()
+ .WriteLine("Converter = converter;")
+ .WriteClosingBracket()
+ .WriteLine();
+
+ if (options.GenerateXmlDocument)
+ {
+ builder
+ .WriteLine("/// ")
+ .WriteLine("/// Gets the to convert the value of the annotated property.")
+ .WriteLine("/// ");
+ }
+
+ builder
+ .WriteLine("public Type Converter { get; }")
+ .WriteClosingBracket()
+ .WriteClosingBracket();
+
+ return new(builder.ToString(), $"{AttributeName}Attribute.g.cs");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/MapTo/Sources/Source.cs b/src/MapTo/Sources/Source.cs
deleted file mode 100644
index db066a0..0000000
--- a/src/MapTo/Sources/Source.cs
+++ /dev/null
@@ -1,4 +0,0 @@
-namespace MapTo.Sources
-{
- internal record Source(string Code, string HintName);
-}
\ No newline at end of file
diff --git a/src/MapTo/Sources/SourceCode.cs b/src/MapTo/Sources/SourceCode.cs
new file mode 100644
index 0000000..c9f8f4b
--- /dev/null
+++ b/src/MapTo/Sources/SourceCode.cs
@@ -0,0 +1,4 @@
+namespace MapTo.Sources
+{
+ internal record SourceCode(string Text, string HintName);
+}
\ No newline at end of file
diff --git a/src/MapTo/Sources/TypeConverterSource.cs b/src/MapTo/Sources/TypeConverterSource.cs
index 40b1b6c..ee751de 100644
--- a/src/MapTo/Sources/TypeConverterSource.cs
+++ b/src/MapTo/Sources/TypeConverterSource.cs
@@ -7,7 +7,7 @@ namespace MapTo.Sources
{
internal const string InterfaceName = "ITypeConverter";
- internal static Source Generate(SourceGenerationOptions options)
+ internal static SourceCode Generate(SourceGenerationOptions options)
{
using var builder = new SourceBuilder()
.WriteLine(GeneratedFilesHeader)
diff --git a/test/MapTo.Tests/Tests.cs b/test/MapTo.Tests/Tests.cs
index ab4f464..2b9cce9 100644
--- a/test/MapTo.Tests/Tests.cs
+++ b/test/MapTo.Tests/Tests.cs
@@ -512,5 +512,37 @@ namespace MapTo
diagnostics.ShouldBeSuccessful();
compilation.SyntaxTrees.ShouldContainSource(TypeConverterSource.InterfaceName, expectedInterface);
}
+
+ [Fact]
+ public void VerifyMapPropertyAttribute()
+ {
+ // Arrange
+ const string source = "";
+ var expectedInterface = $@"
+{Constants.GeneratedFilesHeader}
+using System;
+
+namespace MapTo
+{{
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
+ public sealed class MapPropertyAttribute : Attribute
+ {{
+ public MapPropertyAttribute(Type converter = null)
+ {{
+ Converter = converter;
+ }}
+
+ public Type Converter {{ get; }}
+ }}
+}}
+".Trim();
+
+ // Act
+ var (compilation, diagnostics) = CSharpGenerator.GetOutputCompilation(source, analyzerConfigOptions: DefaultAnalyzerOptions);
+
+ // Assert
+ diagnostics.ShouldBeSuccessful();
+ compilation.SyntaxTrees.ShouldContainSource(MapPropertyAttributeSource.AttributeName, expectedInterface);
+ }
}
}
\ No newline at end of file
diff --git a/test/TestConsoleApp/ViewModels/User.cs b/test/TestConsoleApp/ViewModels/User.cs
index 475dd76..27c4c80 100644
--- a/test/TestConsoleApp/ViewModels/User.cs
+++ b/test/TestConsoleApp/ViewModels/User.cs
@@ -7,6 +7,10 @@ namespace TestConsoleApp.ViewModels
[MapFrom(typeof(Data.Models.User))]
public partial class User
{
+ public int Id { get; }
+
public string FirstName { get; }
+
+ public string LastName { get; }
}
}
\ No newline at end of file
diff --git a/test/TestConsoleApp/ViewModels/UserViewModel.cs b/test/TestConsoleApp/ViewModels/UserViewModel.cs
index 404c759..68d36ac 100644
--- a/test/TestConsoleApp/ViewModels/UserViewModel.cs
+++ b/test/TestConsoleApp/ViewModels/UserViewModel.cs
@@ -7,7 +7,16 @@ namespace TestConsoleApp.ViewModels
{
public string FirstName { get; }
- // [IgnoreProerty]
+ [IgnoreProperty]
public string LastName { get; }
+
+ [MapProperty(converter: typeof(LastNameConverter))]
+ public string Key { get; }
+
+ private class LastNameConverter : ITypeConverter
+ {
+ ///
+ public string Convert(int source) => $"{source} :: With Type Converter";
+ }
}
}
\ No newline at end of file