From fc8fa7f2515d5e3a6fbce1b83ae6fa89d46aa945 Mon Sep 17 00:00:00 2001
From: Martin Valgur <martin.valgur@gmail.com>
Date: Mon, 28 Oct 2024 00:37:22 +0200
Subject: [PATCH] Add .enums dictionary to MessageDatabase

---
 python/bindings/message_database.cpp |  3 ++-
 python/bindings/message_decoder.cpp  |  4 ++--
 python/bindings/oem_enums.cpp        |  7 +++----
 python/bindings/py_database.hpp      |  7 ++++++-
 python/test/test_message_database.py | 31 ++++++++++++++++++++++++++++
 5 files changed, 44 insertions(+), 8 deletions(-)
 create mode 100644 python/test/test_message_database.py

diff --git a/python/bindings/message_database.cpp b/python/bindings/message_database.cpp
index 19f54e51a..674ba1b31 100644
--- a/python/bindings/message_database.cpp
+++ b/python/bindings/message_database.cpp
@@ -182,5 +182,6 @@ void init_common_message_database(nb::module_& m)
         .def("get_msg_def", nb::overload_cast<const std::string&>(&PyMessageDatabase::GetMsgDef, nb::const_), "msg_name"_a)
         .def("get_msg_def", nb::overload_cast<int32_t>(&PyMessageDatabase::GetMsgDef, nb::const_), "msg_id"_a)
         .def("get_enum_def", &PyMessageDatabase::GetEnumDefId, "enum_id"_a)
-        .def("get_enum_def", &PyMessageDatabase::GetEnumDefName, "enum_name"_a);
+        .def("get_enum_def", &PyMessageDatabase::GetEnumDefName, "enum_name"_a)
+        .def_prop_ro("enums", &PyMessageDatabase::GetEnumsByNameDict);
 }
diff --git a/python/bindings/message_decoder.cpp b/python/bindings/message_decoder.cpp
index 303bde403..863f171eb 100644
--- a/python/bindings/message_decoder.cpp
+++ b/python/bindings/message_decoder.cpp
@@ -20,8 +20,8 @@ nb::object convert_field(const FieldContainer& field, const PyMessageDatabase::C
     if (field.fieldDef->type == FIELD_TYPE::ENUM)
     {
         const std::string& enumId = static_cast<const EnumField*>(field.fieldDef.get())->enumId;
-        auto it = parent_db->GetEnumsByIdMap().find(enumId);
-        if (it == parent_db->GetEnumsByIdMap().end())
+        auto it = parent_db->GetEnumsByIdDict().find(enumId);
+        if (it == parent_db->GetEnumsByIdDict().end())
         {
             throw std::runtime_error("Enum definition for " + field.fieldDef->name + " field with ID '" + enumId +
                                      "' not found in the JSON database");
diff --git a/python/bindings/oem_enums.cpp b/python/bindings/oem_enums.cpp
index b502ee075..183f325f1 100644
--- a/python/bindings/oem_enums.cpp
+++ b/python/bindings/oem_enums.cpp
@@ -7,10 +7,9 @@ using namespace novatel::edie;
 
 void init_novatel_oem_enums(nb::module_& m)
 {
-    nb::module_ enums_mod = m.def_submodule("enums", "");
-    for (const auto& [_, enum_type] : MessageDbSingleton::get()->GetEnumsByIdMap())
+    nb::module_ enums_mod = m.def_submodule("enums", "Enumerations used by NovAtel OEM message fields.");
+    for (const auto& [name, enum_type] : MessageDbSingleton::get()->GetEnumsByNameDict()) //
     {
-        nb::object name = enum_type.attr("_name");
-        enums_mod.attr(name) = enum_type;
+        enums_mod.attr(name.c_str()) = enum_type;
     }
 }
diff --git a/python/bindings/py_database.hpp b/python/bindings/py_database.hpp
index b930b8b5d..44c6e57b9 100644
--- a/python/bindings/py_database.hpp
+++ b/python/bindings/py_database.hpp
@@ -20,7 +20,9 @@ class PyMessageDatabase final : public MessageDatabase
 
     explicit PyMessageDatabase(const MessageDatabase& message_db) : MessageDatabase(message_db) { UpdatePythonEnums(); }
 
-    [[nodiscard]] const std::unordered_map<std::string, nb::object>& GetEnumsByIdMap() const { return enums_by_id; }
+    [[nodiscard]] const std::unordered_map<std::string, nb::object>& GetEnumsByIdDict() const { return enums_by_id; }
+
+    [[nodiscard]] const std::unordered_map<std::string, nb::object>& GetEnumsByNameDict() const { return enums_by_name; }
 
   private:
     void GenerateMappings() override
@@ -33,6 +35,7 @@ class PyMessageDatabase final : public MessageDatabase
     {
         nb::object IntEnum = nb::module_::import_("enum").attr("IntEnum");
         enums_by_id.clear();
+        enums_by_name.clear();
         for (const auto& enum_def : EnumDefinitions())
         {
             nb::dict values;
@@ -42,10 +45,12 @@ class PyMessageDatabase final : public MessageDatabase
             enum_type.attr("_name") = enum_name;
             enum_type.attr("_id") = enum_def->_id;
             enums_by_id[enum_def->_id.c_str()] = enum_type;
+            enums_by_name[enum_name] = enum_type;
         }
     }
 
     std::unordered_map<std::string, nb::object> enums_by_id{};
+    std::unordered_map<std::string, nb::object> enums_by_name{};
 
   public:
     using Ptr = std::shared_ptr<PyMessageDatabase>;
diff --git a/python/test/test_message_database.py b/python/test/test_message_database.py
new file mode 100644
index 000000000..f1a6ede27
--- /dev/null
+++ b/python/test/test_message_database.py
@@ -0,0 +1,31 @@
+################################################################################
+#
+# COPYRIGHT NovAtel Inc, 2024. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+################################################################################=
+
+import novatel_edie as ne
+
+
+def test_message_db_enums(json_db):
+    assert json_db.enums["Datum"].WGS84 == 61
+    assert json_db.enums["Datum"].WGS84.name == "WGS84"
+    assert json_db.enums["Datum"].WGS84 == ne.enums.Datum.WGS84