From 79cfb20a08f00149ca9f913261594f4dc6348a5c Mon Sep 17 00:00:00 2001
From: Ilia Ki <ki9ilia@gmail.com>
Date: Tue, 23 Apr 2024 14:40:13 +0700
Subject: [PATCH] Use serdeMembersExactly

---
 dub.sdl                  |  2 +-
 source/mir/ser/cbor.d    | 25 +++++++++++++++++++++++++
 source/mir/ser/package.d |  9 ++++++++-
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/dub.sdl b/dub.sdl
index 36b8b24..f5db035 100644
--- a/dub.sdl
+++ b/dub.sdl
@@ -3,7 +3,7 @@ description "Fast, Expressive, and Easy to use Ion, JSON, YAML, CSV, and Msgpack
 authors "Ilia Ki (Asdf & Mir Ion) " "Yannick Koechlin (Asdf)"
 copyright "Tamedia Digital, 2016, Symmetry Investments and Kaleidic Associates, 2020, Symmetry Investments and Ilia Ki 2021-"
 license "Apache-2.0"
-dependency "mir-algorithm" version=">=3.21.0"
+dependency "mir-algorithm" version=">=3.22.1"
 dependency "mir-cpuid" version="~>1.2.6"
 
 configuration "library" {
diff --git a/source/mir/ser/cbor.d b/source/mir/ser/cbor.d
index e54570d..894156d 100644
--- a/source/mir/ser/cbor.d
+++ b/source/mir/ser/cbor.d
@@ -616,6 +616,31 @@ version(mir_ion_test) unittest
     serializeCbor(book).should == [0xBF, 0x65, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x72, 0x41, 0x20, 0x48, 0x65, 0x72, 0x6F, 0x20, 0x6F, 0x66, 0x20, 0x4F, 0x75, 0x72, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x6E, 0x77, 0x6F, 0x75, 0x6C, 0x64, 0x52, 0x65, 0x63, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x64, 0xF5, 0x6B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x60, 0x70, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x4F, 0x66, 0x4E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x61, 0x73, 0x05, 0x65, 0x70, 0x72, 0x69, 0x63, 0x65, 0xFB, 0x40, 0x1F, 0xF5, 0xC2, 0x8F, 0x5C, 0x28, 0xF6, 0x66, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0xFA, 0x40, 0xDC, 0x28, 0xF6, 0x64, 0x74, 0x61, 0x67, 0x73, 0x83, 0x67, 0x72, 0x75, 0x73, 0x73, 0x69, 0x61, 0x6E, 0x65, 0x6E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x31, 0x39, 0x74, 0x68, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x75, 0x72, 0x79, 0xFF];
 }
 
+/// Test maps with serdeMembersExactly
+@safe pure
+version(mir_ion_test) unittest
+{
+    import mir.test;
+    import mir.serde: serdeMembersExactly;
+
+    @serdeMembersExactly(7)
+    struct Book
+    {
+        string title;
+        bool wouldRecommend;
+        string description;
+        uint numberOfNovellas;
+        double price;
+        float weight;
+        string[] tags;
+    }
+
+    Book book = Book("A Hero of Our Time", true, "", 5, 7.99, 6.88, ["russian", "novel", "19th century"]);
+
+    // This will probably break if you modify how any of the data types
+    // are serialized.
+    serializeCbor(book).should == [0xA7, 0x65, 0x74, 0x69, 0x74, 0x6C, 0x65, 0x72, 0x41, 0x20, 0x48, 0x65, 0x72, 0x6F, 0x20, 0x6F, 0x66, 0x20, 0x4F, 0x75, 0x72, 0x20, 0x54, 0x69, 0x6D, 0x65, 0x6E, 0x77, 0x6F, 0x75, 0x6C, 0x64, 0x52, 0x65, 0x63, 0x6F, 0x6D, 0x6D, 0x65, 0x6E, 0x64, 0xF5, 0x6B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x60, 0x70, 0x6E, 0x75, 0x6D, 0x62, 0x65, 0x72, 0x4F, 0x66, 0x4E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x61, 0x73, 0x05, 0x65, 0x70, 0x72, 0x69, 0x63, 0x65, 0xFB, 0x40, 0x1F, 0xF5, 0xC2, 0x8F, 0x5C, 0x28, 0xF6, 0x66, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0xFA, 0x40, 0xDC, 0x28, 0xF6, 0x64, 0x74, 0x61, 0x67, 0x73, 0x83, 0x67, 0x72, 0x75, 0x73, 0x73, 0x69, 0x61, 0x6E, 0x65, 0x6E, 0x6F, 0x76, 0x65, 0x6C, 0x6C, 0x31, 0x39, 0x74, 0x68, 0x20, 0x63, 0x65, 0x6E, 0x74, 0x75, 0x72, 0x79];
+}
 
 /// Test serializing annotated structs
 @safe pure
diff --git a/source/mir/ser/package.d b/source/mir/ser/package.d
index f726b46..d4a9238 100644
--- a/source/mir/ser/package.d
+++ b/source/mir/ser/package.d
@@ -466,7 +466,7 @@ private void serializeValueImpl(S, V)(scope ref S serializer, scope ref const V
     if (isAggregateType!V && (!isIterable!V || hasFields!V || hasUDA!(V, serdeProxy) && !hasUDA!(V, serdeLikeList)))
 {
     import mir.algebraic;
-    auto state = serializer.structBegin;
+    auto state = serializer.beginStruct(value);
 
     static if (hasUDA!(V, serdeDiscriminatedField))
     {{
@@ -1303,6 +1303,13 @@ auto beginStruct(S, V)(scope ref S serializer, scope ref V value)
         return serializer.structBegin(value.length);
     }
     else
+    static if (hasUDA!(V, serdeMembersExactly))
+    {
+        enum auto udas = getUDAs!(V, serdeMembersExactly);
+        static assert(udas.length == 1, V.stringof ~ " can have exactly one UDA serdeMembersExactly");
+        return serializer.structBegin(getUDAs!(V, serdeMembersExactly)[0].n);
+    }
+    else
     static if (__traits(compiles, serializer.structBegin))
     {
         return serializer.structBegin;