diff --git a/IMPLEMENTATION.md b/IMPLEMENTATION.md index f82009e..66ede0e 100644 --- a/IMPLEMENTATION.md +++ b/IMPLEMENTATION.md @@ -47,7 +47,7 @@ Each type lives in `src/FrameProcessor/Domain/`. Tests in `tests/FrameProcessor. - JSON converter + `IParsable`. - **Tests:** `FrameNameTests` — accepts URL-safe, rejects whitespace/reserved chars with clear message. -### [ ] 1.3 `Orientation` +### [x] 1.3 `Orientation` - `enum { Landscape, Portrait }`. - JSON converter reading/writing kebab-case lowercase. diff --git a/src/FrameProcessor/Domain/Orientation.cs b/src/FrameProcessor/Domain/Orientation.cs new file mode 100644 index 0000000..4b991b6 --- /dev/null +++ b/src/FrameProcessor/Domain/Orientation.cs @@ -0,0 +1,34 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace FrameProcessor.Domain; + +[JsonConverter(typeof(OrientationJsonConverter))] +public enum Orientation +{ + Landscape, + Portrait, +} + +internal sealed class OrientationJsonConverter : JsonConverter +{ + public override Orientation Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var s = reader.GetString(); + return s switch + { + "landscape" => Orientation.Landscape, + "portrait" => Orientation.Portrait, + _ => throw new JsonException( + $"'{s}' is not a valid orientation. Expected 'landscape' or 'portrait'."), + }; + } + + public override void Write(Utf8JsonWriter writer, Orientation value, JsonSerializerOptions options) + => writer.WriteStringValue(value switch + { + Orientation.Landscape => "landscape", + Orientation.Portrait => "portrait", + _ => throw new JsonException($"Unknown orientation: {value}."), + }); +} diff --git a/tests/FrameProcessor.Tests/OrientationTests.cs b/tests/FrameProcessor.Tests/OrientationTests.cs new file mode 100644 index 0000000..d63fad2 --- /dev/null +++ b/tests/FrameProcessor.Tests/OrientationTests.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using FrameProcessor.Domain; + +namespace FrameProcessor.Tests; + +public class OrientationTests +{ + [Theory] + [InlineData("\"landscape\"", Orientation.Landscape)] + [InlineData("\"portrait\"", Orientation.Portrait)] + public void JsonDeserialize_KebabCaseLowercase(string json, Orientation expected) + { + var value = JsonSerializer.Deserialize(json); + Assert.Equal(expected, value); + } + + [Theory] + [InlineData(Orientation.Landscape, "\"landscape\"")] + [InlineData(Orientation.Portrait, "\"portrait\"")] + public void JsonSerialize_KebabCaseLowercase(Orientation value, string expected) + { + var json = JsonSerializer.Serialize(value); + Assert.Equal(expected, json); + } + + [Theory] + [InlineData("\"Landscape\"")] + [InlineData("\"PORTRAIT\"")] + [InlineData("\"diagonal\"")] + [InlineData("\"\"")] + public void JsonDeserialize_RejectsInvalid(string json) + { + Assert.Throws(() => JsonSerializer.Deserialize(json)); + } + + [Fact] + public void JsonRoundTrip_PreservesValue() + { + foreach (var value in new[] { Orientation.Landscape, Orientation.Portrait }) + { + var json = JsonSerializer.Serialize(value); + var roundTripped = JsonSerializer.Deserialize(json); + Assert.Equal(value, roundTripped); + } + } +}