From 0a36d70fb0ecd02ce9a8e6085e7c355f3de29f08 Mon Sep 17 00:00:00 2001 From: "D. Moonfire" Date: Sun, 15 Jan 2023 13:57:43 -0600 Subject: [PATCH] feat: implemented a JSON project as a variant of YAML --- MfGames.Nitride.sln | 30 ++++++++ src/MfGames.Nitride.Json/IsJson.cs | 9 +++ src/MfGames.Nitride.Json/IsJsonExtensions.cs | 16 ++++ .../MfGames.Nitride.Json.csproj | 33 ++++++++ .../NitrideJsonBuilderExtensions.cs | 12 +++ .../NitrideJsonEntityExtensions.cs | 77 +++++++++++++++++++ src/MfGames.Nitride.Json/NitrideJsonModule.cs | 13 ++++ src/MfGames.Nitride.Yaml/HasYamlModel.cs | 2 +- .../NitrideYamlEntityExtensions.cs | 52 +++++++++++-- .../MfGames.Nitride.IO.Tests.csproj | 18 ++--- .../MfGames.Nitride.Json.Tests.csproj | 29 +++++++ .../TextContentJsonTests.cs | 51 ++++++++++++ .../MfGames.Nitride.Temporal.Tests.csproj | 16 ++-- .../MfGames.Nitride.Yaml.Tests.csproj | 14 ++-- ...lHeaderTest.cs => ParseYamlHeaderTests.cs} | 4 +- .../TextContentYamlTests.cs | 51 ++++++++++++ 16 files changed, 395 insertions(+), 32 deletions(-) create mode 100644 src/MfGames.Nitride.Json/IsJson.cs create mode 100644 src/MfGames.Nitride.Json/IsJsonExtensions.cs create mode 100644 src/MfGames.Nitride.Json/MfGames.Nitride.Json.csproj create mode 100644 src/MfGames.Nitride.Json/NitrideJsonBuilderExtensions.cs create mode 100644 src/MfGames.Nitride.Json/NitrideJsonEntityExtensions.cs create mode 100644 src/MfGames.Nitride.Json/NitrideJsonModule.cs create mode 100644 tests/MfGames.Nitride.Json.Tests/MfGames.Nitride.Json.Tests.csproj create mode 100644 tests/MfGames.Nitride.Json.Tests/TextContentJsonTests.cs rename tests/MfGames.Nitride.Yaml.Tests/{ParseYamlHeaderTest.cs => ParseYamlHeaderTests.cs} (96%) create mode 100644 tests/MfGames.Nitride.Yaml.Tests/TextContentYamlTests.cs diff --git a/MfGames.Nitride.sln b/MfGames.Nitride.sln index 82ad9dd..1e0b9d5 100644 --- a/MfGames.Nitride.sln +++ b/MfGames.Nitride.sln @@ -47,6 +47,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Nitride.Temporal.Te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Nitride.Markdown.Tests", "tests\MfGames.Nitride.Markdown.Tests\MfGames.Nitride.Markdown.Tests.csproj", "{2AAE2B69-A93D-4045-B7E6-A32ED08D0D65}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Nitride.Json", "src\MfGames.Nitride.Json\MfGames.Nitride.Json.csproj", "{9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MfGames.Nitride.Json.Tests", "tests\MfGames.Nitride.Json.Tests\MfGames.Nitride.Json.Tests.csproj", "{7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -288,6 +292,30 @@ Global {2AAE2B69-A93D-4045-B7E6-A32ED08D0D65}.Release|x64.Build.0 = Release|Any CPU {2AAE2B69-A93D-4045-B7E6-A32ED08D0D65}.Release|x86.ActiveCfg = Release|Any CPU {2AAE2B69-A93D-4045-B7E6-A32ED08D0D65}.Release|x86.Build.0 = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|x64.ActiveCfg = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|x64.Build.0 = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|x86.ActiveCfg = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Debug|x86.Build.0 = Debug|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|Any CPU.Build.0 = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|x64.ActiveCfg = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|x64.Build.0 = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|x86.ActiveCfg = Release|Any CPU + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113}.Release|x86.Build.0 = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|x64.ActiveCfg = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|x64.Build.0 = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|x86.ActiveCfg = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Debug|x86.Build.0 = Debug|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|Any CPU.Build.0 = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|x64.ActiveCfg = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|x64.Build.0 = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|x86.ActiveCfg = Release|Any CPU + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {D480943C-764D-4A8A-B546-642ED10586BB} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} @@ -309,5 +337,7 @@ Global {C49E07D0-CD32-4332-90FA-07494195CAC4} = {251D9C68-34EB-439D-B167-688BCC47DA17} {0B74A4DE-4F92-44EE-8273-E5A15EAB4266} = {251D9C68-34EB-439D-B167-688BCC47DA17} {2AAE2B69-A93D-4045-B7E6-A32ED08D0D65} = {251D9C68-34EB-439D-B167-688BCC47DA17} + {9A0D2BEE-859A-4E74-8CA7-5E0FB7C2B113} = {570184FD-ECE4-4EC8-86E1-C1265E17D647} + {7CCC3A82-D5FE-4D54-9751-5E7985DE1F26} = {251D9C68-34EB-439D-B167-688BCC47DA17} EndGlobalSection EndGlobal diff --git a/src/MfGames.Nitride.Json/IsJson.cs b/src/MfGames.Nitride.Json/IsJson.cs new file mode 100644 index 0000000..ec68980 --- /dev/null +++ b/src/MfGames.Nitride.Json/IsJson.cs @@ -0,0 +1,9 @@ +namespace MfGames.Nitride.Json; + +/// +/// A marker class that indicates that the entity is JSON. +/// +public record IsJson +{ + public static IsJson Instance { get; } = new(); +} diff --git a/src/MfGames.Nitride.Json/IsJsonExtensions.cs b/src/MfGames.Nitride.Json/IsJsonExtensions.cs new file mode 100644 index 0000000..37e3291 --- /dev/null +++ b/src/MfGames.Nitride.Json/IsJsonExtensions.cs @@ -0,0 +1,16 @@ +using MfGames.Gallium; + +namespace MfGames.Nitride.Json; + +public static class IsJsonExtensions +{ + public static Entity RemoveIsJson(this Entity entity) + { + return entity.Remove(); + } + + public static Entity SetIsJson(this Entity entity) + { + return entity.Set(IsJson.Instance); + } +} diff --git a/src/MfGames.Nitride.Json/MfGames.Nitride.Json.csproj b/src/MfGames.Nitride.Json/MfGames.Nitride.Json.csproj new file mode 100644 index 0000000..b32b6c1 --- /dev/null +++ b/src/MfGames.Nitride.Json/MfGames.Nitride.Json.csproj @@ -0,0 +1,33 @@ + + + + net6.0 + enable + + + + An extension to Nitride static site generator to parse JSON content. + + + + + + + + + + + + + + True + + + + + Analyzer + False + + + + diff --git a/src/MfGames.Nitride.Json/NitrideJsonBuilderExtensions.cs b/src/MfGames.Nitride.Json/NitrideJsonBuilderExtensions.cs new file mode 100644 index 0000000..14a4b1d --- /dev/null +++ b/src/MfGames.Nitride.Json/NitrideJsonBuilderExtensions.cs @@ -0,0 +1,12 @@ +using Autofac; + +namespace MfGames.Nitride.Json; + +public static class NitrideJsonBuilderExtensions +{ + public static NitrideBuilder UseJson(this NitrideBuilder builder) + { + return builder.ConfigureContainer( + x => x.RegisterModule()); + } +} diff --git a/src/MfGames.Nitride.Json/NitrideJsonEntityExtensions.cs b/src/MfGames.Nitride.Json/NitrideJsonEntityExtensions.cs new file mode 100644 index 0000000..36afd5d --- /dev/null +++ b/src/MfGames.Nitride.Json/NitrideJsonEntityExtensions.cs @@ -0,0 +1,77 @@ +using System; + +using MfGames.Gallium; +using MfGames.Nitride.Contents; + +using Newtonsoft.Json; + +namespace MfGames.Nitride.Json; + +public static class NitrideJsonEntityExtensions +{ + /// + /// Parses the entity text as a JSON file and returns the results. + /// + public static TType? GetTextContentJson( + this Entity entity, + Action configure) + { + JsonSerializerSettings settings = new(); + + configure.Invoke(settings); + + return entity.GetTextContentJson(settings); + } + + /// + /// Parses the entity text as a JSON file and returns the results. + /// + public static TType? GetTextContentJson( + this Entity entity, + JsonSerializerSettings? settings = null) + { + string? text = entity.GetTextContentString(); + + return text != null + ? JsonConvert.DeserializeObject(text, settings) + : default; + } + + /// + /// Sets the text content to the serialized value. If this is null, then + /// the text content is removed. This uses the default serializer which + /// may be configured. + /// + /// The same entity for chaining methods. + public static Entity SetTextContentJson( + this Entity entity, + TType? value, + Action configure) + { + JsonSerializerSettings settings = new(); + + configure.Invoke(settings); + + return SetTextContentJson(entity, value, settings); + } + + /// + /// Sets the text content to the serialized value using the serializer + /// provided. If the value is null, then the text content is removed. + /// + /// The same entity for chaining methods. + public static Entity SetTextContentJson( + this Entity entity, + TType? value, + JsonSerializerSettings? settings = null) + { + if (value == null) + { + return entity.Remove(); + } + + string json = JsonConvert.SerializeObject(value, settings); + + return entity.SetTextContent(json); + } +} diff --git a/src/MfGames.Nitride.Json/NitrideJsonModule.cs b/src/MfGames.Nitride.Json/NitrideJsonModule.cs new file mode 100644 index 0000000..8d4b4b2 --- /dev/null +++ b/src/MfGames.Nitride.Json/NitrideJsonModule.cs @@ -0,0 +1,13 @@ +using Autofac; + +namespace MfGames.Nitride.Json; + +public class NitrideJsonModule : Module +{ + /// + protected override void Load(ContainerBuilder builder) + { + builder.RegisterOperators(this); + builder.RegisterValidators(this); + } +} diff --git a/src/MfGames.Nitride.Yaml/HasYamlModel.cs b/src/MfGames.Nitride.Yaml/HasYamlModel.cs index 741824a..da59beb 100644 --- a/src/MfGames.Nitride.Yaml/HasYamlModel.cs +++ b/src/MfGames.Nitride.Yaml/HasYamlModel.cs @@ -1,7 +1,7 @@ namespace MfGames.Nitride.Yaml; /// -/// A marker class that indicates that the has a YAML model. +/// A marker class that indicates that entity has a YAML model. /// public record HasYamlModel { diff --git a/src/MfGames.Nitride.Yaml/NitrideYamlEntityExtensions.cs b/src/MfGames.Nitride.Yaml/NitrideYamlEntityExtensions.cs index cf8a6f1..c231308 100644 --- a/src/MfGames.Nitride.Yaml/NitrideYamlEntityExtensions.cs +++ b/src/MfGames.Nitride.Yaml/NitrideYamlEntityExtensions.cs @@ -10,9 +10,10 @@ namespace MfGames.Nitride.Yaml; public static class NitrideYamlEntityExtensions { /// - /// Parses the entity text as a YAML file and returns the results. + /// Parses the entity text as a YAML file and returns the results. This + /// uses the default serializer, which may be configured. /// - public static TType? GetYaml( + public static TType? GetTextContentYaml( this Entity entity, Action? configure = null) { @@ -22,13 +23,14 @@ public static class NitrideYamlEntityExtensions IDeserializer deserializer = builder.Build(); - return entity.GetYaml(deserializer); + return entity.GetTextContentYaml(deserializer); } /// - /// Parses the entity text as a YAML file and returns the results. + /// Parses the entity text as a YAML file using the given serializer and + /// returns the results. /// - public static TType? GetYaml( + public static TType? GetTextContentYaml( this Entity entity, IDeserializer deserializer) { @@ -38,4 +40,44 @@ public static class NitrideYamlEntityExtensions ? deserializer.Deserialize(text) : default; } + + /// + /// Sets the text content to the serialized value. If this is null, then + /// the text content is removed. This uses the default serializer which + /// may be configured. + /// + /// The same entity for chaining methods. + public static Entity SetTextContentYaml( + this Entity entity, + TType? value, + Action? configure = null) + { + SerializerBuilder builder = new(); + + configure?.Invoke(builder); + + ISerializer serializer = builder.Build(); + + return SetTextContentYaml(entity, value, serializer); + } + + /// + /// Sets the text content to the serialized value using the serializer + /// provided. If the value is null, then the text content is removed. + /// + /// The same entity for chaining methods. + public static Entity SetTextContentYaml( + this Entity entity, + TType? value, + ISerializer serializer) + { + if (value == null) + { + return entity.Remove(); + } + + string yaml = serializer.Serialize(value); + + return entity.SetTextContent(yaml); + } } diff --git a/tests/MfGames.Nitride.IO.Tests/MfGames.Nitride.IO.Tests.csproj b/tests/MfGames.Nitride.IO.Tests/MfGames.Nitride.IO.Tests.csproj index 8c1fde0..f1f4f20 100644 --- a/tests/MfGames.Nitride.IO.Tests/MfGames.Nitride.IO.Tests.csproj +++ b/tests/MfGames.Nitride.IO.Tests/MfGames.Nitride.IO.Tests.csproj @@ -6,22 +6,22 @@ - - + + - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/MfGames.Nitride.Json.Tests/MfGames.Nitride.Json.Tests.csproj b/tests/MfGames.Nitride.Json.Tests/MfGames.Nitride.Json.Tests.csproj new file mode 100644 index 0000000..e250840 --- /dev/null +++ b/tests/MfGames.Nitride.Json.Tests/MfGames.Nitride.Json.Tests.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + enable + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + diff --git a/tests/MfGames.Nitride.Json.Tests/TextContentJsonTests.cs b/tests/MfGames.Nitride.Json.Tests/TextContentJsonTests.cs new file mode 100644 index 0000000..8a4d5e2 --- /dev/null +++ b/tests/MfGames.Nitride.Json.Tests/TextContentJsonTests.cs @@ -0,0 +1,51 @@ +using MfGames.Gallium; +using MfGames.Nitride.Tests; + +using Xunit; +using Xunit.Abstractions; + +namespace MfGames.Nitride.Json.Tests; + +public class TextContentJsonTests : NitrideTestBase +{ + public TextContentJsonTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void NoTextContent() + { + Entity entity = new(); + TestContent? output = entity.GetTextContentJson(); + + Assert.Null(output); + } + + [Fact] + public void SetAndGetContent() + { + Entity entity = new Entity() + .SetTextContentJson(new TestContent { Value = "t1" }); + TestContent? output = entity.GetTextContentJson(); + + Assert.NotNull(output); + Assert.Equal("t1", output.Value); + } + + [Fact] + public void SetNullRemovesContent() + { + Entity entity = new Entity() + .SetTextContentJson(new TestContent { Value = "t1" }) + .SetTextContentJson(null); + TestContent? output = entity.GetTextContentJson(); + + Assert.Null(output); + } + + private class TestContent + { + public string? Value { get; set; } + } +} diff --git a/tests/MfGames.Nitride.Temporal.Tests/MfGames.Nitride.Temporal.Tests.csproj b/tests/MfGames.Nitride.Temporal.Tests/MfGames.Nitride.Temporal.Tests.csproj index bd40748..1e9f90c 100644 --- a/tests/MfGames.Nitride.Temporal.Tests/MfGames.Nitride.Temporal.Tests.csproj +++ b/tests/MfGames.Nitride.Temporal.Tests/MfGames.Nitride.Temporal.Tests.csproj @@ -6,17 +6,17 @@ - - + + - - - - - - + + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/MfGames.Nitride.Yaml.Tests/MfGames.Nitride.Yaml.Tests.csproj b/tests/MfGames.Nitride.Yaml.Tests/MfGames.Nitride.Yaml.Tests.csproj index 0147bb0..ec613c8 100644 --- a/tests/MfGames.Nitride.Yaml.Tests/MfGames.Nitride.Yaml.Tests.csproj +++ b/tests/MfGames.Nitride.Yaml.Tests/MfGames.Nitride.Yaml.Tests.csproj @@ -6,10 +6,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -21,9 +21,9 @@ - - - + + + diff --git a/tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTest.cs b/tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTests.cs similarity index 96% rename from tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTest.cs rename to tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTests.cs index ce304bf..1ac6c8e 100644 --- a/tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTest.cs +++ b/tests/MfGames.Nitride.Yaml.Tests/ParseYamlHeaderTests.cs @@ -10,9 +10,9 @@ using Xunit.Abstractions; namespace MfGames.Nitride.Yaml.Tests; -public class ParseYamlHeaderTest : NitrideTestBase +public class ParseYamlHeaderTests : NitrideTestBase { - public ParseYamlHeaderTest(ITestOutputHelper output) + public ParseYamlHeaderTests(ITestOutputHelper output) : base(output) { } diff --git a/tests/MfGames.Nitride.Yaml.Tests/TextContentYamlTests.cs b/tests/MfGames.Nitride.Yaml.Tests/TextContentYamlTests.cs new file mode 100644 index 0000000..67f2036 --- /dev/null +++ b/tests/MfGames.Nitride.Yaml.Tests/TextContentYamlTests.cs @@ -0,0 +1,51 @@ +using MfGames.Gallium; +using MfGames.Nitride.Tests; + +using Xunit; +using Xunit.Abstractions; + +namespace MfGames.Nitride.Yaml.Tests; + +public class TextContentYamlTests : NitrideTestBase +{ + public TextContentYamlTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void NoTextContent() + { + Entity entity = new(); + TestContent? output = entity.GetTextContentYaml(); + + Assert.Null(output); + } + + [Fact] + public void SetAndGetContent() + { + Entity entity = new Entity() + .SetTextContentYaml(new TestContent { Value = "t1" }); + TestContent? output = entity.GetTextContentYaml(); + + Assert.NotNull(output); + Assert.Equal("t1", output.Value); + } + + [Fact] + public void SetNullRemovesContent() + { + Entity entity = new Entity() + .SetTextContentYaml(new TestContent { Value = "t1" }) + .SetTextContentYaml(null); + TestContent? output = entity.GetTextContentYaml(); + + Assert.Null(output); + } + + private class TestContent + { + public string? Value { get; set; } + } +}