{"id":13629681,"url":"https://github.com/paiden/SourceMapper","last_synced_at":"2025-04-17T09:35:39.057Z","repository":{"id":144114011,"uuid":"406720663","full_name":"paiden/SourceMapper","owner":"paiden","description":null,"archived":false,"fork":false,"pushed_at":"2021-09-16T11:21:09.000Z","size":53,"stargazers_count":5,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-05-23T07:52:22.388Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paiden.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-09-15T10:43:43.000Z","updated_at":"2023-09-03T19:33:42.000Z","dependencies_parsed_at":"2024-01-06T02:09:35.215Z","dependency_job_id":null,"html_url":"https://github.com/paiden/SourceMapper","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paiden%2FSourceMapper","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paiden%2FSourceMapper/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paiden%2FSourceMapper/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paiden%2FSourceMapper/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paiden","download_url":"https://codeload.github.com/paiden/SourceMapper/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223486881,"owners_count":17153241,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T22:01:16.459Z","updated_at":"2024-11-08T20:31:40.013Z","avatar_url":"https://github.com/paiden.png","language":"C#","funding_links":[],"categories":["Do not want to test 113 ( old ISourceGenerator )"],"sub_categories":["1. [ThisAssembly](https://ignatandrei.github.io/RSCG_Examples/v2/docs/ThisAssembly) , in the [EnhancementProject](https://ignatandrei.github.io/RSCG_Examples/v2/docs/rscg-examples#enhancementproject) category"],"readme":"# Introduction \r\nSource Mapper is a [Source Generator](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview)\r\nbased version of functionality similar to [AutoMapper](https://automapper.org/)\r\n\r\nAt the moment is a experimental project used to learn how to create source generators and implement \r\nadvanced code based configuration systems for these.\r\n\r\n# Getting Started\r\n\r\n1. Make sure you use VS2022 with Latest C# Language version\r\n2. Install the [Source Mapper Nuget Package](https://www.nuget.org/packages/SourceMapper)\r\n3. Configure a SourceMapper Context to auto generate Cloning/Mapping extension methods (see Configuration section below)\r\n\r\n# Configuration\r\n\r\nSourceMapper different to many other source generators does not use a Attribute based linking of user and generated code.\r\nSourceMapper uses a fluent style code configuration system. \r\n\r\nThe fluent code is not real code but instead is only lightweight compiler checked configuration code that will be parsed\r\nby the source generator. You should not split it into helper methods etc. as this will definitely break parser logic.\r\n\r\nThe examples below should outline, what valid configurations look like.\r\n\r\nReasons to use this system: \r\n\r\n- Better extensibility than attributes\r\n- No modification of mapped types needed\r\n- All configuration in one place\r\n\r\n## Make a object deep cloneable \r\n\r\nCloning will create a new object instance of the same type. Properties that have a type that is \r\nalso cloneable will also be cloned, otherwise reference assignment will be done.\r\n\r\n```csharp\r\n    // Input objects\r\n    public class DataTransferObject\r\n    {\r\n        public string Property { get; set; }\r\n\r\n        public InnerDataTranferObject Inner { get; set; } = new InnerDataTranferObject();\r\n    }\r\n\r\n    public class InnerDataTranferObject\r\n    {\r\n        public int Property { get; set; }\r\n    }\r\n\r\n    // Configuration\r\n    internal class CloneSampleContext : SourceMapperContext\r\n    {\r\n        protected override void Configure(ContextConfig config)\r\n        {\r\n            config\r\n                .Make\u003cDataTransferObject\u003e(it =\u003e it.Cloneable())\r\n                .Make\u003cInnerDataTranferObject\u003e(it =\u003e it.Cloneable());\r\n        }\r\n    }\r\n\r\n    // Usage\r\n    public class CloneUsage\r\n    {\r\n        public static void Use()\r\n        {\r\n            var cloneable = new DataTransferObject();\r\n            var cloned = cloneable.Clone();\r\n        }\r\n    }\r\n```\r\n\r\nThe generated source code\r\n\r\n```csharp\r\nnamespace Samples\r\n{\r\n    public static class InnerDataTranferObjectCloneSampleContextSourceMapperExtensions\r\n    {\r\n        public static InnerDataTranferObject Clone(this InnerDataTranferObject source)\r\n        {\r\n            var obj = new InnerDataTranferObject();\r\n\r\n            obj.Property = source.Property;\r\n\r\n            return obj;\r\n        }\r\n    }\r\n}\r\n\r\nnamespace Samples\r\n{\r\n    public static class DataTransferObjectCloneSampleContextSourceMapperExtensions\r\n    {\r\n        public static DataTransferObject Clone(this DataTransferObject source)\r\n        {\r\n            var obj = new DataTransferObject();\r\n\r\n            obj.Property = source.Property;\r\n            obj.Inner = source.Inner.Clone();\r\n\r\n            return obj;\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n## Make a object mapable \r\n\r\nMapping will map a object of some type to another type. If public get/set property names match the property will be \r\nassigned from the source property to the target property. If the source property type is cloneable, a clone will be \r\nassigned otherwise direct reference assignment will be done.\r\n\r\n```csharp\r\n    // Input Objects\r\n    public class A { public int Property { get; set; } }\r\n    public class B {  public int Property { get; set; } }\r\n\r\n    // Configuration\r\n    internal class MappingSampleContext : SourceMapperContext\r\n    {\r\n        protected override void Configure(ContextConfig config)\r\n        {\r\n            config.Make\u003cA\u003e(it =\u003e it.MapTo\u003cB\u003e())\r\n                .Make\u003cB\u003e(it =\u003e it.MapTo\u003cA\u003e());\r\n        }\r\n    }\r\n\r\n    // Usage\r\n    public static class MapToUsage\r\n    {\r\n        public static void Use()\r\n        {\r\n            var mapMe = new A();\r\n            var b = mapMe.MapTo\u003cB\u003e();\r\n        }\r\n    }\r\n```\r\n\r\nGenerated Code\r\n\r\n```csharp\r\nnamespace SourceMapper.Samples\r\n{\r\n    public static class BMappingSampleContextSourceMapperExtensions\r\n    {\r\n        public static A MapTo\u003cT\u003e(this B source) where T : A\r\n        {\r\n            var obj = new A();\r\n\r\n            obj.Property = source.Property;\r\n\r\n            return obj;\r\n        }\r\n    }\r\n}\r\n\r\nnamespace SourceMapper.Samples\r\n{\r\n    public static class AMappingSampleContextSourceMapperExtensions\r\n    {\r\n        public static B MapTo\u003cT\u003e(this A source) where T : B\r\n        {\r\n            var obj = new B();\r\n\r\n            obj.Property = source.Property;\r\n\r\n            return obj;\r\n        }\r\n    }\r\n}\r\n\r\n```\r\n\r\n# Custom object instantiation\r\n\r\nSometimes objects needs custom initialization DI etc. You can set a custom init method for objects via the Activator config method.\r\n\r\n```csharp\r\n    // Input objects\r\n    public record RecA(int X)\r\n    {\r\n        public string Prop { get; set; }\r\n    }\r\n\r\n    public record RecB(int Y)\r\n    {\r\n        public static RecB MapActivator(RecA src)\r\n        {\r\n            return new RecB(src.X + 1);\r\n        }\r\n\r\n        public string Prop { get; set; }\r\n    }\r\n\r\n    // Configuration\r\n    internal class ActivatorSampleContext : SourceMapperContext\r\n    {\r\n        protected override void Configure(ContextConfig config)\r\n        {\r\n            config\r\n                .Make\u003cRecA\u003e(it =\u003e it\r\n                    .Cloneable(cloning =\u003e cloning\r\n                        .Activator(src =\u003e new RecA(123)))\r\n                    .MapTo\u003cRecB\u003e(mapping =\u003e mapping\r\n                        .Activator(RecB.MapActivator)));\r\n        }\r\n    }\r\n```\r\n\r\nGenerated code: \r\n\r\n```csharp \r\n    public static class RecAActivatorSampleContextSourceMapperExtensions\r\n    {\r\n        public static RecA Clone(this RecA source)\r\n        {\r\n            Func\u003cRecA, RecA\u003e instanceCreator = src =\u003e new RecA(123);\r\n            var obj = instanceCreator(source);\r\n\r\n            obj.Prop = source.Prop;\r\n\r\n            return obj;\r\n        }\r\n        public static RecB MapTo\u003cT\u003e(this RecA source) where T : RecB\r\n        {\r\n            var obj = RecB.MapActivator(source);\r\n\r\n            obj.Prop = source.Prop;\r\n\r\n            return obj;\r\n        }\r\n    }\r\n```\r\n\r\n# Post Processing \r\n\r\n```csharp\r\n    // Input Objects\r\n    public class PostProcessSource\r\n    {\r\n        public string A { get; set; }\r\n        public string B { get; set; }\r\n    }\r\n\r\n    public class PostProcessTarget { public string AB { get; set; } }\r\n\r\n    // Configuration\r\n    internal class PostProcessSampleContext : SourceMapperContext\r\n    {\r\n        protected override void Configure(ContextConfig config)\r\n        {\r\n            config\r\n                .Make\u003cPostProcessSource\u003e(it =\u003e it\r\n                    .MapTo\u003cPostProcessTarget\u003e(mapping =\u003e mapping\r\n                        .Ignore(src =\u003e src.A)\r\n                        .Ignore(src =\u003e src.B)\r\n                        .PostProcess((ref PostProcessTarget tgt, PostProcessSource src) =\u003e tgt.AB = src.A + src.B)));\r\n        }\r\n    }\r\n```\r\n\r\nGenerated code\r\n\r\n```csharp\r\n    public static class PostProcessSourcePostProcessSampleContextSourceMapperExtensions\r\n    {\r\n        public static PostProcessTarget MapTo\u003cT\u003e(this PostProcessSource source) where T : PostProcessTarget\r\n        {\r\n            var obj = new PostProcessTarget();\r\n\r\n            var postProcess = (ref PostProcessTarget tgt, PostProcessSource src) =\u003e tgt.AB = src.A + src.B;\r\n            postProcess(ref obj, source);\r\n\r\n            return obj;\r\n        }\r\n    }\r\n```\r\n\r\n# Build and Test\r\nCheckout repository, open with VS2022+ and compile the project.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaiden%2FSourceMapper","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaiden%2FSourceMapper","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaiden%2FSourceMapper/lists"}