Skip to content

Commit 47284f7

Browse files
authored
Merge pull request #19 from beheshty/feature/immutable-models
Feature/immutable models
2 parents f203e37 + 7e9fef6 commit 47284f7

7 files changed

Lines changed: 27 additions & 16 deletions

File tree

src/SetSharp/CodeGeneration/PocoGenerator.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ internal static string Generate(List<SettingClassInfo> classes, string @namespac
1212
var sb = new StringBuilder();
1313

1414
sb.AppendLine("// <auto-generated/>");
15+
sb.AppendLine("#nullable enable");
16+
sb.AppendLine();
1517
sb.AppendLine("using System.Collections.Generic;");
18+
sb.AppendLine("using System.Collections.Immutable;");
1619
sb.AppendLine();
1720
sb.AppendLine($"namespace {@namespace}");
1821
sb.AppendLine("{");
@@ -22,7 +25,7 @@ internal static string Generate(List<SettingClassInfo> classes, string @namespac
2225
sb.AppendLine();
2326
AddClassSummary(sb, SettingClassInfo);
2427
sb.AppendLine($" [System.CodeDom.Compiler.GeneratedCode(\"SetSharp\", \"{assemblyVersion}\")]");
25-
sb.AppendLine($" public partial class {SettingClassInfo.ClassName}");
28+
sb.AppendLine($" public partial record {SettingClassInfo.ClassName}");
2629
sb.AppendLine(" {");
2730

2831
AddSectionNameConstant(sb, SettingClassInfo);
@@ -63,11 +66,12 @@ private static void AddSectionNameConstant(StringBuilder sb, SettingClassInfo Se
6366

6467
private static void AddProperty(StringBuilder sb, SettingPropertyInfo property)
6568
{
69+
sb.AppendLine($" /// <summary>Maps to the '{property.OriginalJsonKey}' configuration key.</summary>");
6670
if (property.PropertyName != property.OriginalJsonKey)
6771
{
6872
sb.AppendLine($" [Microsoft.Extensions.Configuration.ConfigurationKeyName(\"{property.OriginalJsonKey}\")]");
6973
}
70-
sb.AppendLine($" public {property.PropertyType} {property.PropertyName} {{ get; set; }}");
74+
sb.AppendLine($" public {property.PropertyType} {property.PropertyName} {{ get; init; }}");
7175
sb.AppendLine();
7276
}
7377
}

src/SetSharp/Diagnostics/DiagnosticDescriptors.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ internal static class DiagnosticDescriptors
88
"SSG001", "App settings parsing failed", "{0}", "Error", DiagnosticSeverity.Error, true);
99

1010
internal static readonly DiagnosticDescriptor MissingOptionsDependencyError = new(
11-
"SSG002", "Missing Dependencies", "To generate IOptions extensions, the project must reference 'Microsoft.Extensions.Options.ConfigurationExtensions'. Please install the corresponding NuGet package.", "Usage", DiagnosticSeverity.Error, true);
11+
"SSG002", "Missing Dependencies", "To generate IOptions extensions, the project must reference 'Microsoft.Extensions.Options.ConfigurationExtensions'. " +
12+
"Please install the corresponding NuGet package.", "Usage", DiagnosticSeverity.Error, true);
1213

1314
internal static readonly DiagnosticDescriptor MissingBaseDependencyError = new(
14-
"SSG003", "Missing Dependency", "The project must reference 'Microsoft.Extensions.Configuration.Abstractions' for basic functionality. Please install the corresponding NuGet package.", "Usage", DiagnosticSeverity.Error, true);
15+
"SSG003", "Missing Dependency", "The project must reference 'Microsoft.Extensions.Configuration.Abstractions' and 'System.Collections.Immutable' " +
16+
"for basic functionality. Please install the corresponding NuGet package.", "Usage", DiagnosticSeverity.Error, true);
1517
}
1618
}

src/SetSharp/ModelBuilder/ConfigurationModelBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ string HandleNestedObject(Dictionary<string, object> obj)
5757

5858
private string InferListType(string parentSectionPath, string key, List<object> list)
5959
{
60-
if (list.Count == 0) return "List<object>";
60+
if (list.Count == 0) return "ImmutableList<object>";
6161

6262
string HandleListItemObject(Dictionary<string, object> obj)
6363
{
@@ -79,7 +79,7 @@ string HandleListItemObject(Dictionary<string, object> obj)
7979
_ => "object"
8080
};
8181

82-
return $"List<{listTypeName}>";
82+
return $"ImmutableList<{listTypeName}>";
8383
}
8484

8585
private string CreateNestedClass(string sectionPath, string classNameKey, Dictionary<string, object> obj, bool isFromCollection = false)

src/SetSharp/SetSharp.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
1111

1212
<PackageId>SetSharp</PackageId>
13-
<Version>1.3.2</Version>
13+
<Version>2.0.0</Version>
1414
<PackageIcon>icon.png</PackageIcon>
1515
<Authors>Amirhossein Beheshti</Authors>
1616
<Description>Generates strongly typed settings classes from appsettings.json using Source Generators.</Description>

src/SetSharp/SetSharpSourceGenerator.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using SetSharp.ModelBuilder;
66
using SetSharp.Models;
77
using System.Text;
8-
using Microsoft.CodeAnalysis.Diagnostics;
98
using SetSharp.Diagnostics;
109

1110
namespace SetSharp
@@ -119,6 +118,11 @@ private static SetSharpSettings ReadSetSharpSettings(Dictionary<string, object>
119118
return Diagnostic.Create(DiagnosticDescriptors.MissingBaseDependencyError, Location.None);
120119
}
121120

121+
if (compilation.GetTypeByMetadataName("System.Collections.Immutable.ImmutableList") == null)
122+
{
123+
return Diagnostic.Create(DiagnosticDescriptors.MissingBaseDependencyError, Location.None);
124+
}
125+
122126
if (!checkForOptionsPattern)
123127
{
124128
return null;

tests/SetSharp.Tests/CodeGeneration/PocoGeneratorTests.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ public void Generate_WithMultipleClasses_ShouldGenerateAllClasses()
3737
var result = PocoGenerator.Generate(classes);
3838

3939
// Assert
40-
Assert.Contains("public partial class FirstSettings", result);
41-
Assert.Contains("public partial class SecondSettings", result);
40+
Assert.Contains("public partial record FirstSettings", result);
41+
Assert.Contains("public partial record SecondSettings", result);
4242
}
4343

4444
[Fact]
@@ -62,7 +62,7 @@ public void Generate_WithDifferentPropertyAndJsonKey_ShouldAddConfigurationKeyNa
6262

6363
// Assert
6464
Assert.Contains("[Microsoft.Extensions.Configuration.ConfigurationKeyName(\"Logging:LogLevel:Default\")]", result);
65-
Assert.Contains("public string LogLevel { get; set; }", result);
65+
Assert.Contains("public string LogLevel { get; init; }", result);
6666
}
6767

6868
[Fact]
@@ -92,7 +92,10 @@ public void Generate_WithEmptyClassList_ShouldGenerateEmptyNamespace()
9292
// Arrange
9393
var classes = new List<SettingClassInfo>();
9494
var expected = @"// <auto-generated/>
95+
#nullable enable
96+
9597
using System.Collections.Generic;
98+
using System.Collections.Immutable;
9699
97100
namespace SetSharp.Configuration
98101
{

tests/SetSharp.Tests/ModelBuilder/ConfigurationModelBuilderTests.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public void BuildFrom_WithListOfPrimitives_ShouldInferListType()
106106
var rootModel = Assert.Single(result);
107107
var listProperty = Assert.Single(rootModel.Properties);
108108
Assert.Equal("AllowedHosts", listProperty.PropertyName);
109-
Assert.Equal("List<string>", listProperty.PropertyType);
109+
Assert.Equal("ImmutableList<string>", listProperty.PropertyType);
110110
}
111111

112112
[Fact]
@@ -126,7 +126,7 @@ public void BuildFrom_WithEmptyList_ShouldDefaultToListOfObject()
126126
var rootModel = Assert.Single(result);
127127
var listProperty = Assert.Single(rootModel.Properties);
128128
Assert.Equal("EmptyList", listProperty.PropertyName);
129-
Assert.Equal("List<object>", listProperty.PropertyType);
129+
Assert.Equal("ImmutableList<object>", listProperty.PropertyType);
130130
}
131131

132132
[Fact]
@@ -154,7 +154,7 @@ public void BuildFrom_WithListOfObjects_ShouldCreateSeparateClassForListItems()
154154
var rootModel = result.First(c => c.ClassName == "RootOptions");
155155
var listProperty = Assert.Single(rootModel.Properties);
156156
Assert.Equal("Endpoints", listProperty.PropertyName);
157-
Assert.Equal("List<EndpointsItemOptions>", listProperty.PropertyType);
157+
Assert.Equal("ImmutableList<EndpointsItemOptions>", listProperty.PropertyType);
158158

159159
// Test Nested List Item Class
160160
var endpointItemModel = result.First(c => c.ClassName == "EndpointsItemOptions");
@@ -167,8 +167,6 @@ public void BuildFrom_WithListOfObjects_ShouldCreateSeparateClassForListItems()
167167
[Fact]
168168
public void NormalizeName_WithInvalidChars_ShouldSanitizeName()
169169
{
170-
// This test calls a private method. To test it directly, you could make it internal
171-
// or use a PrivateObject accessor. Here, we test it indirectly via its effect in BuildFrom.
172170
// Arrange
173171
var builder = new ConfigurationModelBuilder();
174172
var root = new Dictionary<string, object>

0 commit comments

Comments
 (0)