diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd673ba..16f4a9e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
Represents the **NuGet** versions.
+## v2.1.0
+- *Enhancement:* Added `CodeGenerator.LoadConfigAsync` to enable the loading of the configuration without having to execute the code generation. This is useful when needing to either further validate the configuration prior to execution, or be able to query the configuration without initiating the code generation.
+- *Fixed:* All dependencies updated to the latest version.
+
## v2.0.0
- *Enhancement:* **Breaking change** - underlying JSON serialization has been changed from `Newtonsoft.Json` to `System.Text.Json`, with new `Utility.JsonSerializer` encapsulating logic to enable. The following steps are required to migrate existing usage:
- Rename all attribute references from `JsonProperty` to `JsonPropertyName`.
diff --git a/README.md b/README.md
index 9ec8ff6..7f0b3f1 100644
--- a/README.md
+++ b/README.md
@@ -76,7 +76,7 @@ Attribute | Description
[`CodeGenPropertyAttribute`](./src/OnRamp/Config/CodeGenPropertyAttribute.cs) | Defines validation (`IsMandatory`, `IsUnique` and `Options`) and documentation for a property (non-collection).
[`CodeGenPropertyCollectionAttribute`](./src/OnRamp/Config/CodeGenPropertyCollectionAttribute.cs) | Defines validation (`IsMandatory`) and documentation for a collection property.
-The configuration must also use the [Newtonsoft Json.NET serializer attributes](https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm) as [Json.NET](https://www.newtonsoft.com/json/help) is used internally to perform all JSON deserialization.
+The configuration must also use the `System.Text.Json` [serializer attributes](https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/customize-properties) as [`System.Text.Json`](https://learn.microsoft.com/en-us/dotnet/standard/serialization/) is used internally to perform all JSON deserialization.
@@ -85,17 +85,16 @@ The configuration must also use the [Newtonsoft Json.NET serializer attributes](
An example is as follows:
``` csharp
-[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[CodeGenClass("Entity", Title = "'Entity' object.", Description = "The `Entity` object.", Markdown = "This is a _sample_ markdown.", ExampleMarkdown = "This is an `example` markdown.")]
[CodeGenCategory("Key", Title = "Provides the _Key_ configuration.")]
[CodeGenCategory("Collection", Title = "Provides related child (hierarchical) configuration.")]
public class EntityConfig : ConfigRootBase
{
- [JsonProperty("name")]
+ [JsonPropertyName("name")]
[CodeGenProperty("Key", Title = "The entity name.", IsMandatory = true)]
public string? Name { get; set; }
- [JsonProperty("properties")]
+ [JsonPropertyName("properties")]
[CodeGenPropertyCollection("Collection", Title = "The `Property` collection.", IsImportant = true)]
public List? Properties { get; set; }
@@ -105,22 +104,21 @@ public class EntityConfig : ConfigRootBase
}
}
-[JsonObject(MemberSerialization = MemberSerialization.OptIn)]
[CodeGenClass("Property", Title = "'Property' object.", Description = "The `Property` object.")]
[CodeGenCategory("Key", Title = "Provides the _Key_ configuration.")]
public class PropertyConfig : ConfigBase
{
public override string QualifiedKeyName => BuildQualifiedKeyName("Property", Name);
- [JsonProperty("name")]
+ [JsonPropertyName("name")]
[CodeGenProperty("Key", Title = "The property name.", IsMandatory = true, IsUnique = true)]
public string? Name { get; set; }
- [JsonProperty("type")]
+ [JsonPropertyName("type")]
[CodeGenProperty("Key", Title = "The property type.", Description = "This is a more detailed description for the property type.", IsImportant = true, Options = new string[] { "string", "int", "decimal" })]
public string? Type { get; set; }
- [JsonProperty("isNullable")]
+ [JsonPropertyName("isNullable")]
[CodeGenProperty("Key", Title = "Indicates whether the property is nullable.")]
public bool? IsNullable { get; set; }
diff --git a/src/OnRamp/CodeGenOutputArgs.cs b/src/OnRamp/CodeGenOutputArgs.cs
index c4e8fc5..4e9d69b 100644
--- a/src/OnRamp/CodeGenOutputArgs.cs
+++ b/src/OnRamp/CodeGenOutputArgs.cs
@@ -8,48 +8,36 @@ namespace OnRamp
///
/// The resulting arguments.
///
- public class CodeGenOutputArgs
+ /// The corresponding .
+ /// The optional generated directory name.
+ /// The generated file name.
+ /// The generated gen-once file name.
+ /// The generated content.
+ public class CodeGenOutputArgs(CodeGenScriptItem script, string? directoryName, string fileName, string? genOncePattern, string? content)
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The corresponding .
- /// The optional generated directory name.
- /// The generated file name.
- /// The generated gen-once file name.
- /// The generated content.
- public CodeGenOutputArgs(CodeGenScriptItem script, string? directoryName, string fileName, string? genOncePattern, string? content)
- {
- Script = script ?? throw new ArgumentNullException(nameof(script));
- DirectoryName = directoryName;
- FileName = string.IsNullOrEmpty(fileName) ? throw new ArgumentNullException(nameof(fileName)) : fileName;
- GenOncePattern = genOncePattern;
- Content = content;
- }
-
///
/// Gets the .
///
- public CodeGenScriptItem Script { get; }
+ public CodeGenScriptItem Script { get; } = script ?? throw new ArgumentNullException(nameof(script));
///
/// Gets the optional generated directory name.
///
- public string? DirectoryName { get; }
+ public string? DirectoryName { get; } = directoryName;
///
/// Gets the generated file name.
///
- public string FileName { get; }
+ public string FileName { get; } = string.IsNullOrEmpty(fileName) ? throw new ArgumentNullException(nameof(fileName)) : fileName;
///
/// Gets the generated content.
///
- public string? Content { get; }
+ public string? Content { get; } = content;
///
/// Gets the gen-once file name pattern (where specified).
///
- public string? GenOncePattern { get; }
+ public string? GenOncePattern { get; } = genOncePattern;
}
}
\ No newline at end of file
diff --git a/src/OnRamp/CodeGenerator.cs b/src/OnRamp/CodeGenerator.cs
index 5f7b9de..2f7fc61 100644
--- a/src/OnRamp/CodeGenerator.cs
+++ b/src/OnRamp/CodeGenerator.cs
@@ -42,7 +42,7 @@ public static async Task CreateAsync(ICodeGenera
///
private static async Task LoadScriptsAsync(ICodeGeneratorArgs args)
{
- var r = StreamLocator.GetScriptStreamReader(args.ScriptFileName ?? throw new CodeGenException("Script file name must be specified."), args.Assemblies.ToArray(), StreamLocator.YamlJsonExtensions);
+ var r = StreamLocator.GetScriptStreamReader(args.ScriptFileName ?? throw new CodeGenException("Script file name must be specified."), [.. args.Assemblies], StreamLocator.YamlJsonExtensions);
using var s = r.StreamReader ?? throw new CodeGenException($"Script '{args.ScriptFileName}' does not exist.");
args.ScriptFileName = r.FileName;
return await LoadScriptStreamAsync(args, null, args.ScriptFileName, s).ConfigureAwait(false);
@@ -84,7 +84,7 @@ private static async Task LoadScriptStreamAsync(ICodeGeneratorArg
{
foreach (var ifn in scripts.Inherits)
{
- using var s = StreamLocator.GetScriptStreamReader(ifn, args.Assemblies.ToArray(), StreamLocator.YamlJsonExtensions).StreamReader ?? throw new CodeGenException($"Script '{ifn}' does not exist.");
+ using var s = StreamLocator.GetScriptStreamReader(ifn, [.. args.Assemblies], StreamLocator.YamlJsonExtensions).StreamReader ?? throw new CodeGenException($"Script '{ifn}' does not exist.");
var inherit = await LoadScriptStreamAsync(args, rootScript, ifn, s).ConfigureAwait(false);
foreach (var iscript in inherit.Generators!)
{
@@ -131,22 +131,20 @@ protected CodeGenerator(ICodeGeneratorArgs args, CodeGenScript scripts)
public ICodeGeneratorArgs CodeGenArgs { get; }
///
- /// Execute the code-generation; loads the configuration file and executes each of the scripted templates.
+ /// Loads the from the specified .
///
- /// The filename (defaults to ) to load the content from the file system (primary) or (secondary, recursive until found).
- /// The resultant .
- /// Thrown when an error is encountered during the code-generation.
- /// Thrown where the code-generation would result in changes to an underlying artefact. This is managed by setting to true.
- public async Task GenerateAsync(string? configFileName = null)
+ /// The configuration file name.
+ /// The .
+ public async Task LoadConfigAsync(string? configFileName = null)
{
var fn = configFileName ?? CodeGenArgs.ConfigFileName ?? throw new CodeGenException("Config file must be specified.");
- var r = StreamLocator.GetStreamReader(fn, null, CodeGenArgs.Assemblies.ToArray());
+ var r = StreamLocator.GetStreamReader(fn, null, [.. CodeGenArgs.Assemblies]);
using var sr = r.StreamReader ?? throw new CodeGenException($"Config '{fn}' does not exist.");
- return await GenerateAsync(fn, sr, StreamLocator.GetContentType(r.FileName)).ConfigureAwait(false);
+ return await LoadConfigAsync(sr, StreamLocator.GetContentType(r.FileName), r.FileName).ConfigureAwait(false);
}
///
- /// Execute the code-generation; loads the configuration from the and executes each of the scripted templates.
+ /// Loads the from the specified .
///
/// The containing the configuration.
/// The corresponding .
@@ -154,12 +152,7 @@ public async Task GenerateAsync(string? configFileName = null
/// The resultant .
/// Thrown when an error is encountered during the code-generation.
/// Thrown where the code-generation would result in changes to an underlying artefact. This is managed by setting to true.
- public async Task GenerateAsync(TextReader configReader, StreamContentType contentType, string configFileName = "") => await GenerateAsync(configFileName, configReader, contentType).ConfigureAwait(false);
-
- ///
- /// Executes the code-generation.
- ///
- private async Task GenerateAsync(string configFileName, TextReader configReader, StreamContentType contentType)
+ public async Task LoadConfigAsync(TextReader configReader, StreamContentType contentType, string configFileName = "")
{
ConfigBase? config;
IRootConfig rootConfig;
@@ -199,11 +192,45 @@ private async Task GenerateAsync(string configFileName, TextR
{
await ce.AfterPrepareAsync(rootConfig).ConfigureAwait(false);
}
+
+ return config;
}
catch (CodeGenException cgex)
{
throw new CodeGenException($"Config '{configFileName}' is invalid: {cgex.Message}");
}
+ }
+
+ ///
+ /// Execute the code-generation; loads the configuration file and executes each of the scripted templates.
+ ///
+ /// The filename (defaults to ) to load the content from the file system (primary) or (secondary, recursive until found).
+ /// The resultant .
+ /// Thrown when an error is encountered during the code-generation.
+ /// Thrown where the code-generation would result in changes to an underlying artefact. This is managed by setting to true.
+ public async Task GenerateAsync(string? configFileName = null)
+ => await GenerateAsync(await LoadConfigAsync(configFileName).ConfigureAwait(false)).ConfigureAwait(false);
+
+ ///
+ /// Execute the code-generation; loads the configuration from the and executes each of the scripted templates.
+ ///
+ /// The containing the configuration.
+ /// The corresponding .
+ /// The optional configuration file name used specifically in error messages.
+ /// The resultant .
+ /// Thrown when an error is encountered during the code-generation.
+ /// Thrown where the code-generation would result in changes to an underlying artefact. This is managed by setting to true.
+ public async Task GenerateAsync(TextReader configReader, StreamContentType contentType, string configFileName = "")
+ => await GenerateAsync(await LoadConfigAsync(configReader, contentType, configFileName).ConfigureAwait(false)).ConfigureAwait(false);
+
+ ///
+ /// Executes the code-generation for the specific .
+ ///
+ /// The .
+ public Task GenerateAsync(ConfigBase config)
+ {
+ if (config is not IRootConfig rootConfig)
+ throw new ArgumentException("Configuration must implement IRootConfig.", nameof(config));
// Generate the scripted artefacts.
var overallStopwatch = Stopwatch.StartNew();
@@ -232,7 +259,7 @@ private async Task GenerateAsync(string configFileName, TextR
overallStopwatch.Stop();
overallStats.ElapsedMilliseconds = overallStopwatch.ElapsedMilliseconds;
- return overallStats;
+ return Task.FromResult(overallStats);
}
///
@@ -268,7 +295,7 @@ protected virtual void OnCodeGenerated(CodeGenOutputArgs outputArgs, CodeGenStat
else
{
// Perform a wildcard search and stop code-gen where any matches.
- if (di.GetFiles(outputArgs.GenOncePattern).Any())
+ if (di.GetFiles(outputArgs.GenOncePattern).Length != 0)
return;
}
}
@@ -314,7 +341,7 @@ protected virtual void OnCodeGenerated(CodeGenOutputArgs outputArgs, CodeGenStat
private static string[] ConvertContentIntoLines(string? content)
{
if (content is null)
- return Array.Empty();
+ return [];
string line;
var lines = new List();
@@ -324,7 +351,7 @@ private static string[] ConvertContentIntoLines(string? content)
lines.Add(line);
}
- return lines.ToArray();
+ return [.. lines];
}
///
diff --git a/src/OnRamp/CodeGeneratorArgsBase.cs b/src/OnRamp/CodeGeneratorArgsBase.cs
index 96ddf48..be4d7c2 100644
--- a/src/OnRamp/CodeGeneratorArgsBase.cs
+++ b/src/OnRamp/CodeGeneratorArgsBase.cs
@@ -23,10 +23,10 @@ public abstract class CodeGeneratorArgsBase : CodeGeneratorDbArgsBase, ICodeGene
public DirectoryInfo? OutputDirectory { get; set; }
///
- public List Assemblies { get; } = new List();
+ public List Assemblies { get; } = [];
///
- public Dictionary Parameters { get; } = new Dictionary();
+ public Dictionary Parameters { get; } = [];
///
public ILogger? Logger { get; set; }
diff --git a/src/OnRamp/Config/CodeGenCategoryAttribute.cs b/src/OnRamp/Config/CodeGenCategoryAttribute.cs
index ff0bcb1..89284ce 100644
--- a/src/OnRamp/Config/CodeGenCategoryAttribute.cs
+++ b/src/OnRamp/Config/CodeGenCategoryAttribute.cs
@@ -7,19 +7,14 @@ namespace OnRamp.Config
///
/// Represents the code-generation class category configuration.
///
+ /// The grouping category name.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
- public sealed class CodeGenCategoryAttribute : Attribute
+ public sealed class CodeGenCategoryAttribute(string category) : Attribute
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The grouping category name.
- public CodeGenCategoryAttribute(string category) => Category = category;
-
///
/// Gets or sets the category name.
///
- public string Category { get; }
+ public string Category { get; } = category;
///
/// Gets or sets the title.
diff --git a/src/OnRamp/Config/CodeGenClassAttribute.cs b/src/OnRamp/Config/CodeGenClassAttribute.cs
index 9c9dd4e..0a922aa 100644
--- a/src/OnRamp/Config/CodeGenClassAttribute.cs
+++ b/src/OnRamp/Config/CodeGenClassAttribute.cs
@@ -7,19 +7,14 @@ namespace OnRamp.Config
///
/// Represents the code-generation class configuration.
///
+ /// The class name.
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
- public sealed class CodeGenClassAttribute : Attribute
+ public sealed class CodeGenClassAttribute(string name) : Attribute
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The class name.
- public CodeGenClassAttribute(string name) => Name = name ?? throw new ArgumentNullException(nameof(name));
-
///
/// Gets the class name.
///
- public string Name { get; }
+ public string Name { get; } = name ?? throw new ArgumentNullException(nameof(name));
///
/// Gets or sets the title.
diff --git a/src/OnRamp/Config/CodeGenPropertyCollectionAttribute.cs b/src/OnRamp/Config/CodeGenPropertyCollectionAttribute.cs
index 818521e..a8e5493 100644
--- a/src/OnRamp/Config/CodeGenPropertyCollectionAttribute.cs
+++ b/src/OnRamp/Config/CodeGenPropertyCollectionAttribute.cs
@@ -8,19 +8,14 @@ namespace OnRamp.Config
/// Represents the code-generation property collection configuration.
///
/// The property should be either a List<string> or List<T> where T inherits from .
+ /// The grouping category.
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
- public sealed class CodeGenPropertyCollectionAttribute : Attribute
+ public sealed class CodeGenPropertyCollectionAttribute(string category) : Attribute
{
- ///
- /// Initializes a new instance of the class.
- ///
- /// The grouping category.
- public CodeGenPropertyCollectionAttribute(string category) => Category = category;
-
///
/// Gets or sets the category.
///
- public string Category { get; }
+ public string Category { get; } = category;
///
/// Gets or sets the title.
diff --git a/src/OnRamp/Config/ConfigBaseT.cs b/src/OnRamp/Config/ConfigBaseT.cs
index eb70e7d..fbfcd9f 100644
--- a/src/OnRamp/Config/ConfigBaseT.cs
+++ b/src/OnRamp/Config/ConfigBaseT.cs
@@ -97,7 +97,7 @@ private void CheckConfiguration()
protected async Task> PrepareCollectionAsync(List? coll) where T : ConfigBase
{
if (coll == null)
- return new List();
+ return [];
var dict = new Dictionary>();
foreach (var pi in typeof(T).GetProperties())
@@ -105,7 +105,7 @@ protected async Task> PrepareCollectionAsync(List? coll) where T :
foreach (var psa in pi.GetCustomAttributes(typeof(CodeGenPropertyAttribute), true).OfType())
{
if (psa.IsUnique)
- dict.Add(pi, new HashSet