Implement rust tidl generator 39/317839/2
authorChanggyu Choi <changyu.choi@samsung.com>
Wed, 4 Sep 2024 00:37:01 +0000 (09:37 +0900)
committerChanggyu Choi <changyu.choi@samsung.com>
Thu, 19 Sep 2024 07:25:49 +0000 (07:25 +0000)
Rust tidl generator has been supported.
It supports only version2 protocol.

Change-Id: Id6e170a90b029c45f33e5214def5409f46b6e7b0
Signed-off-by: Changgyu Choi <changyu.choi@samsung.com>
28 files changed:
idlc/ast/parser.cc
idlc/ast/parser.h
idlc/ast/structure.cc
idlc/ast/structure.h
idlc/ast/tidlc.yy
idlc/ast/type.cc
idlc/gen/generator.cc
idlc/gen/generator.h
idlc/gen/replace_all.cc
idlc/gen/replace_all.h
idlc/gen/version2/cpp_stub_body_generator.cc
idlc/gen/version2/rs_gen_base.cc [new file with mode: 0644]
idlc/gen/version2/rs_gen_base.h [new file with mode: 0644]
idlc/gen/version2/rs_gen_base_cb.h [new file with mode: 0644]
idlc/gen/version2/rs_group_gen.cc [new file with mode: 0644]
idlc/gen/version2/rs_group_gen.h [new file with mode: 0644]
idlc/gen/version2/rs_group_gen_cb.h [new file with mode: 0644]
idlc/gen/version2/rs_proxy_gen.cc [new file with mode: 0644]
idlc/gen/version2/rs_proxy_gen.h [new file with mode: 0644]
idlc/gen/version2/rs_proxy_gen_cb.h [new file with mode: 0644]
idlc/gen/version2/rs_stub_gen.cc [new file with mode: 0644]
idlc/gen/version2/rs_stub_gen.h [new file with mode: 0644]
idlc/gen/version2/rs_stub_gen_cb.h [new file with mode: 0644]
idlc/main.cc
idlc/options.cc
idlc/options.h
idlc/version2_default_generator.cc
idlc/version2_default_generator.hh

index a074863e67541938541102eef98981af4db71fe4..94526476187c545a2e72a82a38574c177fc9f596 100644 (file)
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cctype>
 #include <fstream>
 #include <string>
 
@@ -34,10 +35,11 @@ void yylex_destroy(void*);
 namespace tidl {
 
 Parser::Parser(bool beta_enable, bool cion_enable, bool mqtt_enable,
-    bool group_enable)
+    bool group_enable, bool rust_enable)
     : scanner_(nullptr), error_(false), beta_enable_(beta_enable),
     cion_enable_(cion_enable), mqtt_enable_(mqtt_enable),
-    group_enable_(group_enable), ver_(1) {
+    group_enable_(group_enable), rust_enable_(rust_enable),
+    ver_(1) {
   yylex_init(&scanner_);
 }
 
@@ -113,4 +115,12 @@ bool Parser::IsGroupEnabled() const {
   return group_enable_;
 }
 
+bool Parser::IsRustEnabled() const {
+  return rust_enable_;
+}
+
+bool Parser::IsUpperCamelCase(const std::string& name) {
+  return std::isupper(name[0]);
+}
+
 }  // namespace tidl
index 20fb6a114d3355f1843423e968425a40c5d71107..9d8c56802e3acc7ebabecc70eaec7423e0f7a7db 100644 (file)
@@ -29,7 +29,8 @@ namespace tidl {
 class Parser {
  public:
   Parser(bool beta_enable = false, bool cion_enable = false,
-      bool mqtt_enable = false, bool group_enable = false);
+      bool mqtt_enable = false, bool group_enable = false,
+      bool rust_enable = false);
   ~Parser();
 
   void* Scanner() const { return scanner_; }
@@ -38,8 +39,10 @@ class Parser {
   void SetDoc(Document* doc);
   std::shared_ptr<Document> GetDoc() const;
   void ReportError(const std::string& err, unsigned line);
+  bool IsUpperCamelCase(const std::string& name);
   bool IsBetaEnabled() const;
   bool IsCionEnabled() const;
+  bool IsRustEnabled() const;
   bool IsMqttEnabled() const;
   bool IsGroupEnabled() const;
   void SetVersion(int ver);
@@ -53,6 +56,7 @@ class Parser {
   bool cion_enable_;
   bool mqtt_enable_;
   bool group_enable_;
+  bool rust_enable_;
   int ver_;
   std::unique_ptr<Preprocessor> pr_;
 };
index 94aedbaa9f8092248d9ecd4d93fcb4b4d27cb5a9..496f3292940396512c95a21f213201814c626630 100644 (file)
@@ -58,6 +58,18 @@ bool ValidateParent(const Structure* st, const std::string& id) {
   return true;
 }
 
+std::string GetEnumBase(const std::string& id, const Structure* st) {
+  if (st->GetEnums().Exist(id)) {
+    return st->GetID();
+  }
+
+  if (st->GetBase() != nullptr) {
+      return GetEnumBase(id, st->GetBase().get());
+  } else {
+    return "";
+  }
+}
+
 }  // namespace
 
 Structure::Structure(std::string id, Elements* elms, Enums* enums,
@@ -80,6 +92,12 @@ const std::shared_ptr<Structure>& Structure::GetBase() const {
   return base_;
 }
 
+std::string Structure::GetEnumBaseID(const std::string& id) const {
+  if (GetEnums().Exist(id))
+    return "";
+  return GetEnumBase(id, this);
+}
+
 bool Structure::FindAndValidateElements() const {
   for (auto& elm : GetElements()) {
     if (!ValidateElement(this, elm->GetID()))
index 816ea021c984bcbd1e23a06a1f51ac164a357f54..388620b5ca3af50af7bbfdc804b1cbbc8e0fd177 100644 (file)
@@ -34,7 +34,7 @@ class Structure : public Block {
   const Elements& GetElements() const;
   void SetBase(std::shared_ptr<Structure> base);
   const std::shared_ptr<Structure>& GetBase() const;
-
+  std::string GetEnumBaseID(const std::string& id) const;
   bool FindAndValidateElements() const;
 
  private:
index 7a7d5dfe660af368f6e5a8e5e33675e18ea1f7d6..03da7dc3fb98ab2bd3b64d1b7e76ef23a395cc37 100644 (file)
@@ -115,6 +115,9 @@ tidl::Document* document = nullptr;
 
 start: blocks {
      ps->SetDoc($1);
+     if (ps->IsRustEnabled() && ps->GetVersion() < 2) {
+       ps->ReportError("syntax error. rust is supported from protocol version 2.", 1);
+     }
   }
 ;
 
@@ -174,6 +177,12 @@ protocol_block: T_PROTOCOL T_NUMBER {
 ;
 
 structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
+    if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+      ps->ReportError("variant `" + $2->ToString() +
+          "` should have an upper camel case name", @1.begin.line);
+      YYABORT;
+    }
+
     $$ = new tidl::Structure($2->ToString(), $4, new tidl::Enums(),
         $1->GetComments(), @1.begin.line);
     if (!$$->FindAndValidateElements()) {
@@ -186,6 +195,12 @@ structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
     delete $2;
   }
   | T_STRUCTURE T_ID T_BRACE_OPEN enums elements T_BRACE_CLOSE {
+    if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+      ps->ReportError("variant `" + $2->ToString() +
+          "` should have an upper camel case name", @1.begin.line);
+      YYABORT;
+    }
+
     $$ = new tidl::Structure($2->ToString(), $5, $4, $1->GetComments(),
         @1.begin.line);
     if (!$$->FindAndValidateElements()) {
@@ -198,6 +213,12 @@ structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
     delete $2;
   }
   | T_STRUCTURE T_ID T_BRACE_OPEN enums T_BRACE_CLOSE {
+    if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+      ps->ReportError("variant `" + $2->ToString() +
+          "` should have an upper camel case name", @1.begin.line);
+      YYABORT;
+    }
+
     $$ = new tidl::Structure($2->ToString(), new tidl::Elements(), $4, $1->GetComments(),
         @1.begin.line);
     if (!$$->FindAndValidateElements()) {
@@ -222,6 +243,12 @@ structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
         ps->ReportError("syntax error. " + $4->ToString() + " does not exist.", @1.begin.line);
         delete $6;
       } else {
+        if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+          ps->ReportError("variant `" + $2->ToString() +
+              "` should have an upper camel case name", @1.begin.line);
+          YYABORT;
+        }
+
         $$ = new tidl::Structure($2->ToString(), $6, new tidl::Enums(),
             $1->GetComments(), @1.begin.line);
         $$->SetBase(std::dynamic_pointer_cast<tidl::Structure>(block));
@@ -251,6 +278,11 @@ structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
         delete $6;
         delete $7;
       } else {
+        if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+          ps->ReportError("variant `" + $2->ToString() +
+              "` should have an upper camel case name", @1.begin.line);
+          YYABORT;
+        }
         $$ = new tidl::Structure($2->ToString(), $7, $6, $1->GetComments(),
           @1.begin.line);
         $$->SetBase(std::dynamic_pointer_cast<tidl::Structure>(block));
@@ -278,6 +310,12 @@ structure_block: T_STRUCTURE T_ID T_BRACE_OPEN elements T_BRACE_CLOSE {
         ps->ReportError("syntax error. " + $4->ToString() + " does not exist.", @1.begin.line);
         delete $6;
       } else {
+        if (ps->IsRustEnabled() && !ps->IsUpperCamelCase($2->ToString())) {
+          ps->ReportError("variant `" + $2->ToString() +
+              "` should have an upper camel case name", @1.begin.line);
+          YYABORT;
+        }
+
         $$ = new tidl::Structure($2->ToString(), new tidl::Elements(), $6, $1->GetComments(),
             @1.begin.line);
         $$->SetBase(std::dynamic_pointer_cast<tidl::Structure>(block));
@@ -872,7 +910,7 @@ container_type: container_type_name T_META_OPEN base_type T_META_CLOSE {
 
         if ($1->ToString() == "set") {
           if (!tidl::BaseType::IsKeyType($3)) {
-            ps->ReportError("syntax error. The key type should be 'char', 'int', 'short', 'long', 'string', 'bool', 'float' and 'double.", @1.begin.line);
+            ps->ReportError("syntax error. The key type should be 'char', 'int', 'short', 'long', 'string' and 'bool'.", @1.begin.line);
           }
         }
 
@@ -887,7 +925,7 @@ container_type: container_type_name T_META_OPEN base_type T_META_CLOSE {
         delete $1;
       } else {
         if (!tidl::BaseType::IsKeyType($3)) {
-          ps->ReportError("syntax error. The key type should be 'char', 'int', 'short', 'long', 'string', 'bool', 'float' and 'double.", @1.begin.line);
+          ps->ReportError("syntax error. The key type should be 'char', 'int', 'short', 'long', 'string' and 'bool'.", @1.begin.line);
         }
 
         $$ = new tidl::BaseType($1->ToString(), $1->GetComments());
index 802d51b15955a9ba5e13d71863f5074590d33ee6..cc335bfa8a7a06f9295404923f39f0bb528d2cfb 100644 (file)
@@ -189,8 +189,7 @@ bool BaseType::IsKeyType(const BaseType* type) {
       type->ToString() == "short" ||
       type->ToString() == "string" ||
       type->ToString() == "bool" ||
-      type->ToString() == "float" ||
-      type->ToString() == "double")
+      type->ToString() == "long")
     return true;
 
   return false;
index d965ba9989d23468a736b7ca985a093ea469eb4e..0e2132e862858116c8c227d172ea53f59a088760 100644 (file)
@@ -124,4 +124,34 @@ std::string Generator::GetInterfaceNameFromDelegate(const BaseType& type) {
   return {};
 }
 
+std::string Generator::PascalToSnake(const std::string& pascal_case) {
+  std::string snake_case;
+  for (int i = 0; i < pascal_case.length(); i++) {
+    if (isupper(pascal_case[i])) {
+      if (i != 0 && !isupper(pascal_case[i - 1]))
+        snake_case += "_";
+      snake_case += tolower(pascal_case[i]);
+    } else {
+      snake_case += pascal_case[i];
+    }
+  }
+  return snake_case;
+}
+
+std::string Generator::SnakeToPascal(const std::string& snake_case) {
+  std::string pascal_case;
+  bool capitalize_next = true;
+  for (char c : snake_case) {
+    if (c == '_') {
+      capitalize_next = true;
+    } else if (capitalize_next) {
+      pascal_case += toupper(c);
+      capitalize_next = false;
+    } else {
+      pascal_case += c;
+    }
+  }
+  return pascal_case;
+}
+
 }  // namespace tidl
index 9355de3da060b5d94713464b4bef4c417ac6ec76..a46440a4131c6606f820422d9ea41999b7a34b16 100644 (file)
@@ -127,6 +127,8 @@ class Generator {
 
   virtual void OnInitGen(std::ofstream& stream) = 0;
   virtual void OnFiniGen(std::ofstream& stream) = 0;
+  std::string PascalToSnake(const std::string& pascal_case);
+  std::string SnakeToPascal(const std::string& snake_case);
 
  protected:
   const Document& GetDocument() {
index b1f3a121893ff4098de184eb57548f67c6bb631d..58ef28fbfc2b8353c2b4a5fc0fa8268f133f870a 100644 (file)
@@ -51,6 +51,48 @@ ReplaceAll& ReplaceAll::Change(const std::string& from,
   return *this;
 }
 
+int ReplaceAll::GetTagStartPos(int pos) {
+  while (pos > 0 && str_[pos - 1] == ' ')
+    pos--;
+  return pos;
+}
+
+int ReplaceAll::GetTagEndPos(int pos) {
+  while (pos < str_.length() && (str_[pos] == ' ' || str_[pos] == '\n')) {
+    if (str_[pos] == '\n') {
+      pos++;
+      break;
+    }
+    pos++;
+  }
+  return pos;
+}
+
+ReplaceAll& ReplaceAll::RemoveAll(const std::string& tag, bool remove) {
+  const auto from = "<" + tag + "?>";
+  const auto to = "</" + tag + "?>";
+  std::size_t pos1 = str_.find(from, 0);
+  auto pos2 = str_.find(to, pos1);
+
+  while (pos1 != std::string::npos && pos2 != std::string::npos) {
+    auto pos1_s = GetTagStartPos(pos1);
+    auto pos1_e = GetTagEndPos(pos1 + from.length());
+    auto pos2_s = GetTagStartPos(pos2);
+    auto pos2_e = GetTagEndPos(pos2 + to.length());
+    if (!remove) {
+      str_.erase(pos2_s, pos2_e - pos2_s);
+      str_.erase(pos1_s, pos1_e - pos1_s);
+    } else {
+      str_.erase(pos1_s, pos2_e - pos1_s);
+    }
+
+    pos1 = str_.find(from, pos1);
+    pos2 = str_.find(to, pos1);
+  }
+
+  return *this;
+}
+
 ReplaceAll& ReplaceAll::Remove(const std::string& tag, bool remove) {
   const auto from = "<" + tag + "?>";
   const auto to = "</" + tag + "?>";
@@ -60,11 +102,15 @@ ReplaceAll& ReplaceAll::Remove(const std::string& tag, bool remove) {
   if (pos1 == std::string::npos || pos2 == std::string::npos)
     return *this;
 
+  auto pos1_s = GetTagStartPos(pos1);
+  auto pos1_e = GetTagEndPos(pos1 + from.length());
+  auto pos2_s = GetTagStartPos(pos2);
+  auto pos2_e = GetTagEndPos(pos2 + to.length());
   if (!remove) {
-    str_.erase(pos2, to.length());
-    str_.erase(pos1, from.length());
+    str_.erase(pos2_s, pos2_e - pos2_s);
+    str_.erase(pos1_s, pos1_e - pos1_s);
   } else {
-    str_.erase(pos1, pos2 - pos1 + to.length());
+    str_.erase(pos1_s, pos2_e - pos1_s);
   }
 
   return *this;
@@ -79,13 +125,11 @@ ReplaceAll& ReplaceAll::ChangeToUpper(const std::string& from,
   });
 }
 
-ReplaceAll& ReplaceAll::ChangeToLower(const std::string& from,
-    const std::string& to) {
+ReplaceAll& ReplaceAll::ChangeToLower(const std::string &from, const std::string &to) {
   return Change(from, [to]() {
     std::string t = to;
     std::transform(t.begin(), t.end(), t.begin(), ::tolower);
-    return t;
-  });
+    return t; });
 }
 
 ReplaceAll& ReplaceAll::Once(const std::string& from,
index ce0fc05f7af61f80d13d154878c8f3d6b99c2131..02df11ad12e113ca31cd41e022a5bfdbac8ec99d 100755 (executable)
@@ -38,6 +38,7 @@ class ReplaceAll {
   ReplaceAll& Change(const std::string& from, const std::string& to);
 
   ReplaceAll& Remove(const std::string& tag, bool remove);
+  ReplaceAll& RemoveAll(const std::string& tag, bool remove);
 
   ReplaceAll& Replace(const std::string& from, const std::string& to) {
     return Change("<" + from + ">", to);
@@ -49,27 +50,46 @@ class ReplaceAll {
     const auto to = "</" + tag + "*>";
     auto pos1 = str_.find(from, 0);
     auto pos2 = str_.find(to, pos1);
-    auto pos3 = str_.rfind('\n', pos1);
-    auto space = pos1 - pos3 - 1;
-    std::string space_str(space, ' ');
 
     if (pos1 == std::string::npos || pos2 == std::string::npos)
       return *this;
 
-    std::string str = str_.substr(pos1 + from.length(),
-        pos2 - pos1 - from.length());
+    auto pos1_s = GetTagStartPos(pos1);
+    auto pos1_e = GetTagEndPos(pos1 + from.length());
+    auto pos2_s = GetTagStartPos(pos2);
+    auto pos2_e = GetTagEndPos(pos2 + to.length());
+    std::string str = str_.substr(pos1_e, pos2_s - pos1_e);
 
     std::string ret;
     for (const auto& i : container) {
       ReplaceAll rep(str);
-      if (fn(&rep, i)) {
-        if (!ret.empty())
-          ret += "\n" + space_str;
+      ReplaceAll& rep_ref = rep;
+      if (fn(rep_ref, i)) {
         ret += rep;
       }
     }
 
-    str_.replace(pos1, pos2 - pos1 + to.length(), ret);
+    str_.replace(pos1_s, pos2_e - pos1_s, ret);
+    return *this;
+  }
+
+  template <class F>
+  ReplaceAll& ReplaceBlock(const std::string& tag, F fn) {
+    const auto from = "<" + tag + "!>";
+    const auto to = "</" + tag + "!>";
+    auto pos1 = str_.find(from, 0);
+    auto pos2 = str_.find(to, pos1);
+    if (pos1 == std::string::npos || pos2 == std::string::npos)
+      return *this;
+    auto pos1_s = GetTagStartPos(pos1);
+    auto pos1_e = GetTagEndPos(pos1 + from.length());
+    auto pos2_s = GetTagStartPos(pos2);
+    auto pos2_e = GetTagEndPos(pos2 + to.length());
+    std::string str = str_.substr(pos1_e, pos2_s - pos1_e);
+    ReplaceAll rep(str);
+    ReplaceAll& rep_ref = rep;
+    fn(rep_ref);
+    str_.replace(pos1_s, pos2_e - pos1_s, rep);
     return *this;
   }
 
@@ -86,8 +106,11 @@ class ReplaceAll {
     return Replace(from, str);
   }
 
+  ReplaceAll& ChangeToLower(const std::string &from, const std::string &to);
   ReplaceAll& ChangeToUpper(const std::string& from, const std::string& to);
-  ReplaceAll& ChangeToLower(const std::string& from, const std::string& to);
+  ReplaceAll& ReplaceToUpper(const std::string& from, const std::string& to) {
+      return ChangeToUpper("<" + from + ">", to);
+  }
   ReplaceAll& Once(const std::string& from, const std::string& to);
 
   template <class T>
@@ -112,6 +135,9 @@ class ReplaceAll {
   ReplaceAll& AddIndent(int indent, bool space = true);
   void Out(std::ofstream& stream);
 
+ private:
+  int GetTagStartPos(int pos);
+  int GetTagEndPos(int pos);
  private:
   std::string str_;
 };
index 3c087f84c3fa95dbb1e365bfc5024a84e528e6b8..7d30b798bd49a7531aabca1f12448c9972ed7291 100644 (file)
@@ -162,11 +162,11 @@ std::string CppStubBodyGenerator::GenInterface(const Interface& iface) {
           .Change("<IMPL_SERVICE_BASE_SET_PRIVILEGE_MAP>",
                   GenInterfaceImplServiceBaseSetPrivilegeMap(iface))
           .Repeat("IMPL_ADD_PRIVILEGE", iface.GetAttributes(),
-                  [&](ReplaceAll* ra,
+                  [&](ReplaceAll& ra,
                       const std::unique_ptr<tidl::Attribute>& attr) {
                     if (attr->GetKey() != "privilege") return false;
 
-                    ra->Replace("PRIVILEGE", attr->GetValue());
+                    ra.Replace("PRIVILEGE", attr->GetValue());
                     return true;
                   })
           .Remove("IMPL_SET_TRUSTED",
diff --git a/idlc/gen/version2/rs_gen_base.cc b/idlc/gen/version2/rs_gen_base.cc
new file mode 100644 (file)
index 0000000..ca359aa
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <ctime>
+#include <vector>
+
+#include "idlc/gen/version2/rs_gen_base.h"
+
+#if (defined(_WIN32) || defined(__WIN32__))
+#define mkdir(A, B) mkdir(A)
+#endif
+
+namespace tidl {
+namespace version2 {
+namespace {
+#include "idlc/gen/version2/rs_gen_base_cb.h"
+
+std::string GetBaseTypeName(const std::shared_ptr<tidl::Structure>& base) {
+  if (base->GetBase() != nullptr)
+    return GetBaseTypeName(base->GetBase()) + "::" + base->GetID();
+
+  return base->GetID();
+}
+
+void GetElementsFromStructure(Elements* elms,
+    const std::shared_ptr<Structure>& base) {
+  if (base->GetBase() != nullptr)
+    GetElementsFromStructure(elms, base->GetBase());
+
+  for (auto elm : base->GetElements())
+    elms->Add(elm);
+}
+
+Elements GetElements(const Structure& st) {
+  Elements elms;
+  if (st.GetBase() != nullptr)
+    GetElementsFromStructure(&elms, st.GetBase());
+
+  for (auto elm : st.GetElements())
+    elms.Add(elm);
+
+  return elms;
+}
+
+} //namespace
+
+RsGeneratorBase::RsGeneratorBase(std::shared_ptr<Document> doc)
+    : Generator(doc) {
+  type_map_ = {
+      {"char", "i8"}, {"int", "i32"}, {"short", "i16"},
+      {"long", "i64"}, {"string", "String"}, {"bool", "bool"},
+      {"list", "LinkedList"}, {"array", "Vec"}, {"float", "f32"},
+      {"double", "f64"}, {"bundle", "Bundle"}, {"void", "()"},
+      {"file", "String"}, {"map", "HashMap"}, {"set", "HashSet"}
+  };
+
+  parcel_type_map_ = {
+    {"char", "byte"},
+    {"int", "int32"},
+    {"short", "int16"},
+    {"long", "int64"},
+    {"string", "string"},
+    {"bool", "bool"},
+    {"float", "float"},
+    {"double", "double"},
+    {"bundle", "bundle"},
+    {"file", "string"},
+  };
+
+  type_init_map_ = {
+    {"char", "0"},
+    {"int", "0"},
+    {"short", "0"},
+    {"long", "0"},
+    {"bool", "false"},
+    {"float", "0.0f"},
+    {"double", "0.0"},
+  };
+
+  for (auto& block : GetDocument().GetBlocks()) {
+    if (block->GetType() != Block::TYPE_STRUCTURE)
+      continue;
+
+    auto& st = static_cast<const Structure&>(*block);
+    AddTypeName(st);
+  }
+}
+
+void RsGeneratorBase::AddTypeName(const Structure& st) {
+  std::string name = st.GetID();
+  std::string type_name;
+  if (st.GetBase() != nullptr)
+    type_name = GetBaseTypeName(st.GetBase()) + "::" + name;
+  else
+    type_name = name;
+
+  struct_types_[std::move(name)] = std::move(type_name);
+}
+
+void RsGeneratorBase::OnInitGen(std::ofstream& stream) {
+  MakeDir(FileName);
+  stream.open(FileName + "/impl_internal.rs");
+  ReplaceAll(CB_COMMON_MAIN)
+      .Repeat("WRITE_PARCELS", GetDocument().GetBlocks(),
+          [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+        return SetParcels(ra, i);
+      })
+      .Repeat("READ_PARCELS", GetDocument().GetBlocks(),
+          [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+        return SetParcels(ra, i);
+      })
+      .Remove("READ_EXCEPTION", GetChannelType() == ChannelType::TYPE_GROUP)
+      .Remove("WRITE_EXCEPTION", GetChannelType() == ChannelType::TYPE_GROUP)
+      .Remove("BUNDLE_HEADER_BLOCK", HasBundle() != true)
+      .Remove("BUNDLE_BLOCK", HasBundle() != true)
+      .Repeat("WRITE_ENUMS", GetDocument().GetBlocks(),
+          [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+        return SetEnumParcels(ra, i);
+      })
+      .Repeat("READ_ENUMS", GetDocument().GetBlocks(),
+          [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+        return SetEnumParcels(ra, i);
+      })
+      .ReplaceBlock("META_PARCELS", [&](ReplaceAll& ra) {
+        return SetMetaParcelBlock(ra);
+      })
+      .Out(stream);
+  stream.close();
+}
+
+void RsGeneratorBase::OnFiniGen(std::ofstream& stream) {
+}
+
+bool RsGeneratorBase::HasBundle() {
+  for (auto& i : GetDocument().GetBlocks()) {
+    if (i->GetType() == Block::TYPE_STRUCTURE) {
+      Structure& st = static_cast<Structure&>(*i);
+      auto elms = GetElements(st);
+      for (auto& e : elms) {
+        if (ConvertTypeToString(e->GetType(), i).find("Bundle") != std::string::npos) {
+          return true;
+        }
+      }
+    } else if (i->GetType() == Block::TYPE_INTERFACE) {
+      Interface& iface = static_cast<Interface&>(*i);
+      for (auto& d : iface.GetDeclarations()) {
+        if (GetParameters(d->GetParameters()).find("Bundle") != std::string::npos) {
+          return true;
+        }
+        if (ConvertTypeToString((*d).GetType()).find("Bundle") != std::string::npos) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
+bool RsGeneratorBase::SetEnum(ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+  ra.Repeat("PROPERTIES",  e->GetFields(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Field>& f) {
+      if (f->GetValue().empty()) {
+        ra.Replace("ID", f->GetID());
+      } else {
+        ra.Replace("ID", f->GetID() + " = " + f->GetValue());
+      }
+      return true;
+      })
+    .Replace("ENUM_NAME", e->GetID());
+  return true;
+}
+
+bool RsGeneratorBase::SetStructs(ReplaceAll& ra,
+    const std::shared_ptr<Block>& i) {
+  if (i->GetType() != Block::TYPE_STRUCTURE)
+    return false;
+
+  ra.Repeat("ENUM",  i->GetEnums(),
+      [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+    return SetEnum(ra, e);
+  });
+
+  Structure& st = static_cast<Structure&>(*i);
+  auto elms = GetElements(st);
+  ra.Replace("STRUCT_MOD_NAME", PascalToSnake(st.GetID()))
+    .Replace("STRUCT_NAME", st.GetID())
+    .Repeat("PROPERTIES", elms,
+        [&](ReplaceAll& ra, const std::shared_ptr<Element>& e) {
+      ra.Replace("ID", PascalToSnake(e->GetID()))
+        .Replace("TYPE", ConvertTypeToString(e->GetType(), i));
+      return true;
+    });
+  return true;
+}
+
+std::string RsGeneratorBase::GetStructTypeString(const Structure& st) {
+  std::string str;
+
+  if (st.GetBase() != nullptr) {
+    str += GetStructTypeString(*st.GetBase()) + "::";
+  }
+  str += st.GetID();
+
+  return str;
+}
+
+bool RsGeneratorBase::SetEnumParcels(ReplaceAll& ra,
+    const std::shared_ptr<Block>& i) {
+  ra.Repeat("ENUMS",  i->GetEnums(), [&](ReplaceAll& ra,
+      const std::unique_ptr<Enum>& e) {
+    ra.Repeat("MATCH_PROPERTIES", e->GetFields(),
+          [&](ReplaceAll& ra, const std::unique_ptr<Field>& f) {
+        ra.Replace("FIELD", f->GetID());
+        return true;
+      })
+      .Replace("ENUM_NAME", e->GetID());
+      return true;
+    })
+    .Replace("BLOCK_NAME", PascalToSnake(i->GetID()));
+
+  return true;
+}
+
+bool RsGeneratorBase::SetParcels(ReplaceAll& ra,
+    const std::shared_ptr<Block>& i) {
+  if (i->GetType() != Block::TYPE_STRUCTURE)
+    return false;
+  const Structure& st = static_cast<const Structure&>(*i);
+  auto elms = GetElements(st);
+  ra.Replace("MOD_NAME", PascalToSnake(st.GetID()))
+    .Replace("TYPE", st.GetID())
+    .Repeat("PARCEL_BODY", elms, [&](ReplaceAll& ra,
+        const std::shared_ptr<Element>& e) {
+      ra.Replace("ID", PascalToSnake(e->GetID()));
+      return true;
+    })
+    .Repeat("UNIT_BODY", elms, [&](ReplaceAll& ra,
+        const std::shared_ptr<Element>& e) {
+      ra.Replace("ID", PascalToSnake(e->GetID()))
+        .Replace("REAL_ID", e->GetID())
+        .Replace("INSIDE_TYPE", ConvertTypeToString(e->GetType(), i, false));
+      return true;
+    })
+    .Replace("UNIT_TYPE", GetStructTypeString(st));
+  return true;
+}
+
+void RsGeneratorBase::SetMetaParcelBlock(ReplaceAll& ra) {
+  meta_type_list_.clear();
+  for (auto& i : GetDocument().GetBlocks()) {
+    if (i->GetType() == Block::TYPE_STRUCTURE) {
+      const Structure& st = static_cast<const Structure&>(*i);
+      for (const auto& j : st.GetElements()) {
+        auto& t = j->GetType();
+        AddMetaTypeList(t);
+      }
+    } else if (i->GetType() == Block::TYPE_INTERFACE) {
+      const Interface& iface = static_cast<const Interface&>(*i);
+      for (const auto& j : iface.GetDeclarations()) {
+        auto& t = j->GetType();
+        AddMetaTypeList(t);
+        for (const auto& k : j->GetParameters()) {
+          auto& t1 = k->GetParameterType().GetBaseType();
+          AddMetaTypeList(t1);
+        }
+      }
+    }
+  }
+
+  std::string ret;
+  for (auto& p : meta_type_list_) {
+    const BaseType& type = *(p.second);
+    ReplaceAll target = ra;
+    std::string unit_str, value_str;
+    std::string inside_str, inside_value_str;
+
+    if (type.GetMetaType()) {
+      unit_str = type.GetMetaType()->ToString();
+      inside_str = ConvertTypeToString(*type.GetMetaType());
+    } else {
+      if (type.GetKeyType()) {
+        unit_str = type.GetKeyType()->ToString();
+        inside_str = ConvertTypeToString(*type.GetKeyType());
+      }
+
+      if (type.GetValueType()) {
+        value_str = type.GetValueType()->ToString();
+        inside_value_str = ConvertTypeToString(
+            *type.GetValueType(), nullptr, false);
+      }
+    }
+
+    target.Replace("TYPE", ConvertTypeToString(type, nullptr, false))
+          .Replace("TYPE_ID", GetFullNameFromType(type))
+          .RemoveAll("LIST", type.ToString() != "list")
+          .RemoveAll("ARRAY", type.ToString() != "array")
+          .RemoveAll("SET", type.ToString() != "set")
+          .RemoveAll("MAP", type.ToString() != "map")
+          .Replace("UNIT_TYPE", unit_str)
+          .Replace("UNIT_VALUE_TYPE", value_str)
+          .Replace("INSIDE_TYPE", inside_str)
+          .Replace("INSIDE_VALUE_TYPE", inside_value_str);
+    ret += target;
+  }
+
+  ra = ReplaceAll(ret);
+}
+
+void RsGeneratorBase::AddMetaTypeList(const BaseType& type) {
+  if (type.GetMetaType() != nullptr) {
+    meta_type_list_[ConvertTypeToString(type)] = &type;
+    AddMetaTypeList(*type.GetMetaType());
+  }
+
+  if (type.GetValueType() != nullptr) {
+    auto value_type = type.GetValueType();
+    meta_type_list_[ConvertTypeToString(type)] = &type;
+    if (value_type->GetMetaType() != nullptr)
+      AddMetaTypeList(*value_type);
+  }
+
+}
+
+std::string RsGeneratorBase::GetFullNameFromType(const BaseType& type) {
+  auto found = struct_types_.find(type.GetFullName(true));
+  if (found != struct_types_.end())
+    return found->second;
+
+  if (type.IsEnumType())
+    return "int";
+
+  if (IsDelegateType(type) ||
+      type.ToString().find("::CallbackBase") != std::string::npos)
+    return "delegate";
+
+  std::string name = type.GetFullName(true);
+  return GetEnumTypeString(name, true);
+}
+
+std::string RsGeneratorBase::GetEnumTypeString(const std::string& type,
+    bool use_underbar) {
+  std::string concatenated_char = use_underbar ? "_" : "::";
+  auto pos = type.find('.');
+  if (pos == std::string::npos) {
+    std::string cls_name = GetClassNameFromEnumType(type);
+    if (!cls_name.empty())
+      return PascalToSnake(cls_name) + concatenated_char + type;
+
+    return type;
+  }
+
+  std::string block_id = type.substr(0, pos);
+  std::string type_id = type.substr(pos + 1, type.size() - (pos + 1));
+  return block_id + concatenated_char + type_id;
+}
+
+std::string RsGeneratorBase::GetClassNameFromEnumType(const std::string& type) {
+  for (auto& block : GetDocument().GetBlocks()) {
+    for (const auto& e : block->GetEnums()) {
+      if (e->GetID() == type)
+        return block->GetID();
+    }
+  }
+
+  return {};
+}
+
+std::string RsGeneratorBase::ConvertTypeToString(const BaseType& type,
+    const std::shared_ptr<Block>& i, bool definition) {
+  if (type.IsEnumType()) {
+    std::string str = type.ToString();
+    size_t pos = str.find(".");
+    if (pos != std::string::npos) {
+      while (pos != std::string::npos) {
+        str.replace(pos, 1, "::");
+        str = PascalToSnake(str.substr(0, pos)) + str.substr(pos);
+        pos = str.find(".");
+      }
+      return str;
+    } else {
+      if (i != nullptr && i->GetType() == Block::TYPE_STRUCTURE) {
+        const Structure& st = static_cast<const Structure&>(*i);
+        std::string base_id = st.GetEnumBaseID(str);
+        if (!base_id.empty())
+          return base_id + "::" + str;
+        else if (!definition)
+          return PascalToSnake(st.GetID()) + "::" +str;
+        else
+          return str;
+      } else {
+        if (!definition) {
+          for (auto& block : GetDocument().GetBlocks()) {
+            if (block->GetType() == Block::TYPE_STRUCTURE)
+              continue;
+            for (const auto& e : block->GetEnums()) {
+              if (e->GetID() == type.ToString())
+                return PascalToSnake(block->GetID()) + "::" + type.ToString();
+            }
+          }
+        }
+      }
+    }
+  }
+
+  if (type.IsStructureType())
+    return PascalToSnake(type.ToString()) + "::" + type.ToString();
+
+  if (type.IsUserDefinedType())
+    return type.ToString();
+
+  if (type.GetMetaType() != nullptr)
+    return type_map_[type.ToString()] + "<" +
+        ConvertTypeToString(*(type.GetMetaType()), i, definition) + ">";
+
+  if (type.GetKeyType() != nullptr && type.GetValueType() != nullptr) {
+    return type_map_[type.ToString()] + "<" +
+        ConvertTypeToString(*(type.GetKeyType()), i, definition) + ", " +
+        ConvertTypeToString(*(type.GetValueType()), i, definition) + ">";
+  }
+  return type_map_[type.ToString()];
+}
+
+std::string RsGeneratorBase::GetParameters(const Parameters& ps, bool with_id) {
+  std::string ret;
+  for (const auto& i : ps) {
+    if (!ret.empty()) {
+      ret += ", ";
+    }
+
+    std::string mut;
+    auto dir = i->GetParameterType().GetDirection();
+    if (dir == ParameterType::Direction::OUT ||
+        dir == ParameterType::Direction::REF) {
+      mut = "&mut ";
+    }
+
+    std::string life_time;
+    if (IsDelegateType(i->GetParameterType().GetBaseType())) {
+      if (IsProxy())
+        life_time = "<'b>";
+    }
+    if (with_id)
+      ret += i->GetID() + ": " + mut +
+             ConvertTypeToString(i->GetParameterType().GetBaseType()) +
+             life_time;
+    else
+      ret += mut + ConvertTypeToString(i->GetParameterType().GetBaseType()) +
+             life_time;
+  }
+
+  return ret;
+}
+
+std::string RsGeneratorBase::GetParameterIDs(const Parameters& ps) {
+  std::string ret;
+  for (const auto& i : ps) {
+    if (!ret.empty()) {
+      ret += ", ";
+    }
+
+    ret += i->GetID();
+  }
+
+  return ret;
+}
+
+std::string RsGeneratorBase::GetParameterIDsWithDir(const Parameters& ps) {
+  std::string ret;
+  for (const auto& i : ps) {
+    if (!ret.empty()) {
+      ret += ", ";
+    }
+
+    if (i->GetParameterType().GetDirection() == ParameterType::Direction::OUT
+        || i->GetParameterType().GetDirection() == ParameterType::Direction::REF)
+      ret += "&mut ";
+    ret += i->GetID();
+  }
+
+  return ret;
+}
+
+void RsGeneratorBase::MakeDir(const std::string& path) {
+  if (access(path.c_str(), F_OK) == 0)
+    return;
+
+  mode_t mod = (S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH | 02000);
+  mkdir(path.c_str(), mod);
+}
+
+}  // namespace version2
+}  // namespace tidl
+
diff --git a/idlc/gen/version2/rs_gen_base.h b/idlc/gen/version2/rs_gen_base.h
new file mode 100644 (file)
index 0000000..da91b22
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_GEN_VERSION2_RS_GEN_BASE_H_
+#define IDLC_GEN_VERSION2_RS_GEN_BASE_H_
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "idlc/ast/type.h"
+#include "idlc/ast/structure.h"
+#include "idlc/gen/generator.h"
+
+namespace tidl {
+namespace version2 {
+
+class RsGeneratorBase : public Generator {
+ public:
+  explicit RsGeneratorBase(std::shared_ptr<Document> doc);
+  virtual ~RsGeneratorBase() = default;
+
+  void AddTypeName(const Structure& st);
+  void OnInitGen(std::ofstream& stream) override;
+  void OnFiniGen(std::ofstream& stream) override;
+
+  std::string ConvertTypeToString(const BaseType& type,
+      const std::shared_ptr<Block>& i = nullptr, bool definition = true);
+  std::string GetParameters(const Parameters& ps, bool with_id = true);
+  std::string GetParameterIDs(const Parameters& ps);
+  std::string GetParameterIDsWithDir(const Parameters& ps);
+  void MakeDir(const std::string& path);
+  std::string GetFullNameFromType(const BaseType& type);
+  bool SetEnum(ReplaceAll& ra, const std::unique_ptr<Enum>& e);
+  bool SetStructs(ReplaceAll& ra, const std::shared_ptr<Block>& i);
+  bool HasBundle();
+ protected:
+  const int TAB_SIZE = 4;
+
+ private:
+  bool SetParcels(ReplaceAll& ra, const std::shared_ptr<Block>& i);
+  bool SetEnumParcels(ReplaceAll& ra, const std::shared_ptr<Block>& i);
+  void SetMetaParcelBlock(ReplaceAll& ra);
+  void AddMetaTypeList(const BaseType& type);
+
+  std::string GetEnumTypeString(const std::string& type, bool use_underbar);
+  std::string GetClassNameFromEnumType(const std::string& type);
+  std::string GetStructTypeString(const Structure& st);
+
+  std::unordered_map<std::string, std::string> type_map_;
+  std::unordered_map<std::string, const BaseType*> meta_type_list_;
+  std::unordered_map<std::string, std::string> struct_types_;
+  std::unordered_map<std::string, std::string> parcel_type_map_;
+  std::unordered_map<std::string, std::string> type_init_map_;
+  std::unordered_map<std::string, BaseType> unit_types_;
+};
+
+}  // namespace version2
+}  // namespace tidl
+
+#endif  // IDLC_GEN_VERSION2_RS_GEN_BASE_H_
diff --git a/idlc/gen/version2/rs_gen_base_cb.h b/idlc/gen/version2/rs_gen_base_cb.h
new file mode 100644 (file)
index 0000000..d5327d1
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_GEN_VERSION2_RS_GEN_BASE_CB_H_
+#define IDLC_GEN_VERSION2_RS_GEN_BASE_CB_H_
+
+constexpr const char CB_COMMON_MAIN[] =
+R"__rs_cb(
+extern crate rust_rpc_port;
+<BUNDLE_HEADER_BLOCK?>
+extern crate tizen_bundle;
+
+use tizen_bundle::tizen_bundle::Bundle;
+</BUNDLE_HEADER_BLOCK?>
+use rust_rpc_port::parcel::Parcel;
+use std::collections::{LinkedList, HashMap, HashSet};
+use super::*;
+
+macro_rules! DLOG_DEBUG { () =>  {3}; }
+macro_rules! DLOG_INFO { () =>  {4}; }
+macro_rules! DLOG_WARN { () =>  {5}; }
+macro_rules! DLOG_ERROR { () =>  {6}; }
+
+macro_rules! DLOG {
+    ($prio:expr, $($arg:tt)*) => {{
+        use std::ffi::c_int;
+
+        #[link(name = "dlog")]
+        extern "C" {
+            pub fn __dlog_print(
+                log_id: c_int,
+                ...
+            ) -> c_int;
+        }
+
+        fn f() {}
+        fn type_name_of<T>(_: T) -> &'static str {
+            std::any::type_name::<T>()
+        }
+        let prio = $prio;
+        let full_func_name = type_name_of(f).strip_suffix("::f").unwrap();
+        let func_name = &full_func_name[full_func_name.rfind("::").unwrap()+2..];
+        let file_path = std::path::PathBuf::from(file!());
+        let file_name = file_path.file_name().unwrap().to_str().unwrap();
+        let total_log = format!("{}: {}({}): {}\0", file_name, func_name, line!(), format_args!($($arg)*).to_string());
+        unsafe { __dlog_print(0, prio, "RUST_RPC_PORT\0".as_ptr(), total_log.as_ptr()); }
+    }}
+}
+
+macro_rules! debug { ($($arg:tt)*) => {{ DLOG!(DLOG_DEBUG!(), $($arg)*); }} }
+
+macro_rules! info { ($($arg:tt)*) => {{ DLOG!(DLOG_INFO!(), $($arg)*); }} }
+
+macro_rules! warn { ($($arg:tt)*) => {{ DLOG!(DLOG_WARN!(), $($arg)*); }} }
+
+macro_rules! error { ($($arg:tt)*) => {{ DLOG!(DLOG_ERROR!(), $($arg)*); }} }
+
+#[derive(Debug)]
+pub struct Unit {
+    parcel: Parcel,
+    name: String,
+    ty: String,
+}
+
+#[derive(Debug)]
+pub struct UnitMap {
+    map: HashMap<String, Unit>,
+}
+
+pub trait Writeable {
+    fn write_parcel(&self, parcel: &mut Parcel);
+    fn write_unit(&self, unit: &mut Unit);
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap);
+}
+
+pub trait Readable {
+    fn read_parcel(&mut self, parcel: &Parcel);
+    fn read_unit(&mut self, unit: &Unit);
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap);
+}
+
+impl Unit {
+    pub fn empty() -> Self {
+        Unit {
+            parcel: Parcel::new_without_header(),
+            name: String::new(),
+            ty: String::new(),
+        }
+    }
+    pub fn new(name: String, ty: String) -> Self {
+        Unit {
+            parcel: Parcel::new_without_header(),
+            name,
+            ty,
+        }
+    }
+
+    pub fn set_name(&mut self, name: String) {
+        self.name = name;
+    }
+
+    pub fn set_type(&mut self, ty: String) {
+        self.ty = ty;
+    }
+
+    pub fn get_name(&self) -> &String {
+        &self.name
+    }
+
+    pub fn get_type(&self) -> &String {
+        &self.ty
+    }
+
+    pub fn get_parcel(&self) -> &Parcel {
+        &self.parcel
+    }
+
+    pub fn get_mut_parcel(&mut self) -> &mut Parcel {
+        &mut self.parcel
+    }
+
+    pub fn serialize(&self, parcel: &mut Parcel) {
+        parcel.write_str(self.name.as_str());
+        parcel.write_str(self.ty.as_str());
+
+        let raw = self.parcel.get_raw();
+        parcel.write_array_count(raw.len());
+        parcel.write(raw);
+    }
+
+    pub fn deserialize(&mut self, parcel: &Parcel) {
+        self.set_name(parcel.read_string());
+        self.set_type(parcel.read_string());
+        let size = parcel.read_array_count();
+        let mut raw = Vec::<u8>::with_capacity(size);
+        raw.resize(size, 0);
+        let mut raw_slice: &mut [u8] = raw.as_mut_slice();
+        parcel.read(raw_slice);
+        self.parcel.write(raw_slice);
+    }
+
+    pub fn read(&self, data: &mut dyn Readable) {
+        data.read_unit(self);
+    }
+
+    pub fn write(&mut self, data: &dyn Writeable) {
+        data.write_unit(self);
+    }
+}
+
+impl UnitMap {
+    pub fn new() -> Self {
+        Self {
+            map: HashMap::new(),
+        }
+    }
+
+    pub fn clear(&mut self) {
+        self.map.clear();
+    }
+
+    pub fn len(&self) -> usize {
+        self.map.len()
+    }
+
+    pub fn insert(&mut self, name: String, unit: Unit) {
+        self.map.insert(name, unit);
+    }
+
+    pub fn lookup(&self, name: &str) -> Option<&Unit> {
+        self.map.get(name)
+    }
+
+    pub fn serialize(&self, parcel: &mut Parcel) {
+        parcel.write_array_count(self.len() as usize);
+        for unit in self.map.values() {
+            unit.serialize(parcel);
+        }
+    }
+
+    pub fn deserialize(&mut self, parcel: &Parcel) {
+        let size = parcel.read_array_count();
+        for _ in 0..size {
+            let mut unit = Unit::empty();
+            unit.deserialize(parcel);
+            let name = unit.get_name();
+            self.insert(String::from(name), unit);
+        }
+    }
+
+    pub fn read(&self, name: &str, data: &mut dyn Readable) {
+        let unit = self.lookup(name);
+        if let Some(unit) = unit {
+            unit.read(data);
+        } else {
+            panic!("Unit {} not found", name);
+        }
+    }
+}
+
+impl Writeable for i8 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_i8(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("char"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for i16 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_i16(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("short"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for i32 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_i32(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("int"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for i64 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_i64(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("long"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for f32 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_f32(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("float"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for f64 {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_f64(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("double"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for bool {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_bool(*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("bool"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Writeable for String {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_str(&*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("string"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+<WRITE_ENUMS*>
+<ENUMS*>
+impl Writeable for <BLOCK_NAME>::<ENUM_NAME> {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+       parcel.write_i32((*self) as i32);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("int"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+</ENUMS*>
+</WRITE_ENUMS*>
+<WRITE_EXCEPTION?>
+impl Writeable for RemoteException {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_i32(self.cause);
+        parcel.write_str(self.message.as_str());
+    }
+
+    fn write_unit(&self, unit: &mut Unit) {
+        let mut unit_map = UnitMap::new();
+        self.cause.write_unitmap("cause", &mut unit_map);
+        self.message.write_unitmap("message", &mut unit_map);
+        unit_map.serialize(unit.get_mut_parcel());
+    }
+
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), "remote_exception".to_owned());
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+</WRITE_EXCEPTION?>
+
+impl Readable for i8 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_i8();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"char") {
+                    error!("type({}) is not char", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for i16 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_i16();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"short") {
+                    error!("type({}) is not short", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for i32 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_i32();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"int") {
+                    error!("type({}) is not int", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for i64 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_i64();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"long") {
+                    error!("type({}) is not long", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for f32 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_f32();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"float") {
+                    error!("type({}) is not float", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for f64 {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_f64();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"double") {
+                    error!("type({}) is not double", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for bool {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_bool();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"bool") {
+                    error!("type({}) is not bool", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+impl Readable for String {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_string();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"string") {
+                    error!("type({}) is not string", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+<BUNDLE_BLOCK?>
+impl Writeable for Bundle {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        parcel.write_bundle(&*self);
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        self.write_parcel(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("bundle"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Readable for Bundle {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_bundle() as Bundle;
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"bundle") {
+                    error!("type({}) is not bundle", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+</BUNDLE_BLOCK?>
+
+<READ_ENUMS*>
+<ENUMS*>
+impl From<i32> for <BLOCK_NAME>::<ENUM_NAME> {
+    fn from(value: i32) -> Self {
+        match value {
+            <MATCH_PROPERTIES*>
+            x if x == <BLOCK_NAME>::<ENUM_NAME>::<FIELD> as i32 => <BLOCK_NAME>::<ENUM_NAME>::<FIELD>,
+            </MATCH_PROPERTIES*>
+            _ => <BLOCK_NAME>::<ENUM_NAME>::DEFAULT
+        }
+    }
+}
+
+impl Readable for <BLOCK_NAME>::<ENUM_NAME> {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        *self = parcel.read_i32().into();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        self.read_parcel(unit.get_parcel());
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"int") {
+                    error!("type({}) is not int", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+</ENUMS*>
+</READ_ENUMS*>
+<READ_EXCEPTION?>
+impl Readable for RemoteException {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        self.cause = parcel.read_i32();
+        self.message = parcel.read_string();
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        let mut unit_map = UnitMap::new();
+        unit_map.deserialize(unit.get_parcel());
+        unit_map.read("cause", &mut self.cause);
+        unit_map.read("message", &mut self.message);
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"remote_exception") {
+                    error!("type({}) is not remote_exception", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+
+</READ_EXCEPTION?>
+<WRITE_PARCELS*>
+impl Writeable for <MOD_NAME>::<TYPE> {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+        <PARCEL_BODY*>
+        self.<ID>.write_parcel(parcel);
+        </PARCEL_BODY*>
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        let mut __unit_map = UnitMap::new();
+        <UNIT_BODY*>
+        self.<ID>.write_unitmap("<REAL_ID>", &mut __unit_map);
+        </UNIT_BODY*>
+
+        __unit_map.serialize(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("<UNIT_TYPE>"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+</WRITE_PARCELS*>
+
+<READ_PARCELS*>
+impl Readable for <MOD_NAME>::<TYPE> {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+        <PARCEL_BODY*>
+        self.<ID>.read_parcel(parcel);
+        </PARCEL_BODY*>
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        let mut __unit_map = UnitMap::new();
+        __unit_map.deserialize(unit.get_parcel());
+        <UNIT_BODY*>
+        let mut tmp_<ID>: <INSIDE_TYPE> = Default::default();
+        __unit_map.read("<REAL_ID>", &mut tmp_<ID>);
+        self.<ID> = tmp_<ID>;
+        </UNIT_BODY*>
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"<UNIT_TYPE>") {
+                    error!("type({}) is not <UNIT_TYPE>", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+</READ_PARCELS*>
+
+<META_PARCELS!>
+impl Writeable for <TYPE> {
+    fn write_parcel(&self, parcel: &mut Parcel) {
+    }
+    fn write_unit(&self, unit: &mut Unit) {
+        let mut __unit_map = UnitMap::new();
+        <LIST?>
+        let mut __unit = Unit::new("length".to_owned(), "int".to_owned());
+        __unit.write(&(self.len() as i32));
+        __unit_map.insert("length".to_owned(), __unit);
+        for (index, e) in self.iter().enumerate() {
+            let mut __unit = Unit::new(index.to_string(), "<UNIT_TYPE>".to_owned());
+            __unit.write(e);
+            __unit_map.insert(index.to_string(), __unit);
+        }
+        </LIST?>
+        <ARRAY?>
+        let mut __unit = Unit::new("size".to_owned(), "int".to_owned());
+        __unit.write(&(self.len() as i32));
+        __unit_map.insert("size".to_owned(), __unit);
+        for (index, e) in self.iter().enumerate() {
+            let mut __unit = Unit::new(index.to_string(), "<UNIT_TYPE>".to_owned());
+            __unit.write(e);
+            __unit_map.insert(index.to_string(), __unit);
+        }
+        </ARRAY?>
+        <SET?>
+        let mut __unit = Unit::new("size".to_owned(), "int".to_owned());
+        __unit.write(&(self.len() as i32));
+        __unit_map.insert("size".to_owned(), __unit);
+        for (index, e) in self.iter().enumerate() {
+            let key_str = format!("{}{}", String::from("key-"), index);
+            let mut __unit = Unit::new(key_str.clone(), "<UNIT_TYPE>".to_owned());
+            __unit.write(e);
+            __unit_map.insert(key_str, __unit);
+        }
+        </SET?>
+        <MAP?>
+        let mut __unit = Unit::new("size".to_owned(), "int".to_owned());
+        __unit.write(&(self.len() as i32));
+        __unit_map.insert("size".to_owned(), __unit);
+        for (index, (key, value)) in self.iter().enumerate() {
+            let key_str = format!("{}{}", String::from("key-"), index);
+            let mut __unit = Unit::new(key_str.clone(), "<UNIT_TYPE>".to_owned());
+            __unit.write(key);
+            __unit_map.insert(key_str, __unit);
+            let value_str = format!("{}{}", String::from("value-"), index);
+            let mut __unit = Unit::new(value_str.clone(), "<UNIT_VALUE_TYPE>".to_owned());
+            __unit.write(value);
+            __unit_map.insert(value_str, __unit);
+        }
+        </MAP?>
+        __unit_map.serialize(unit.get_mut_parcel());
+    }
+    fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+        let mut unit = Unit::new(String::from(name), String::from("<TYPE_ID>"));
+        unit.write(self);
+        unit_map.insert(String::from(name), unit);
+    }
+}
+
+impl Readable for <TYPE> {
+    fn read_parcel(&mut self, parcel: &Parcel) {
+    }
+    fn read_unit(&mut self, unit: &Unit) {
+        let mut __unit_map = UnitMap::new();
+        __unit_map.deserialize(unit.get_parcel());
+        let mut __size: i32 = Default::default();
+        <LIST?>
+        __unit_map.read("length", &mut __size);
+        for index in 0..__size {
+            let mut __tmp_value: <INSIDE_TYPE> = Default::default();
+            __unit_map.read(&index.to_string(), &mut __tmp_value);
+            self.push_back(__tmp_value);
+        }
+        </LIST?>
+        <ARRAY?>
+        __unit_map.read("size", &mut __size);
+        for index in 0..__size {
+            let mut __tmp_value: <INSIDE_TYPE> = Default::default();
+            __unit_map.read(&index.to_string(), &mut __tmp_value);
+            self.push(__tmp_value);
+        }
+        </ARRAY?>
+        <SET?>
+        __unit_map.read("size", &mut __size);
+        for index in 0..__size {
+            let key_str = format!("{}{}", String::from("key-"), index);
+            let mut __tmp_value: <INSIDE_TYPE> = Default::default();
+            __unit_map.read(&key_str, &mut __tmp_value);
+            self.insert(__tmp_value);
+        }
+        </SET?>
+        <MAP?>
+        __unit_map.read("size", &mut __size);
+        for index in 0..__size {
+            let key_str = format!("{}{}", String::from("key-"), index);
+            let value_str = format!("{}{}", String::from("value-"), index);
+            let mut __tmp_key: <INSIDE_TYPE> = Default::default();
+            let mut __tmp_value: <INSIDE_VALUE_TYPE> = Default::default();
+            __unit_map.read(&key_str, &mut __tmp_key);
+            __unit_map.read(&value_str, &mut __tmp_value);
+            self.insert(__tmp_key, __tmp_value);
+        }
+        </MAP?>
+    }
+    fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+        match unit_map.lookup(name.as_str()) {
+            Some(unit) => {
+                if unit.get_type().ne(&"<TYPE_ID>") {
+                    error!("type({}) is not <TYPE>", unit.get_type());
+                    return;
+                }
+
+                unit.read(self);
+            }
+            None => {
+                error!("Failed to find {}", name);
+                return;
+            }
+        }
+    }
+}
+</META_PARCELS!>
+
+)__rs_cb";
+
+#endif  // IDLC_GEN_VERSION2_RS_GEN_BASE_CB_H_
diff --git a/idlc/gen/version2/rs_group_gen.cc b/idlc/gen/version2/rs_group_gen.cc
new file mode 100644 (file)
index 0000000..e29d2c0
--- /dev/null
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idlc/gen/version2/rs_group_gen.h"
+
+#include <algorithm>
+
+namespace {
+#include "idlc/gen/version2/rs_group_gen_cb.h"
+}
+
+namespace tidl {
+namespace version2 {
+
+RsGroupGen::RsGroupGen(std::shared_ptr<Document> doc) : RsGeneratorBase(doc) {}
+
+void RsGroupGen::OnInitGen(std::ofstream& stream) {
+  RsGeneratorBase::OnInitGen(stream);
+  stream.open(FileName + "/mod.rs");
+  ReplaceAll(::CB_GROUP_MAIN)
+      .Replace("VERSION", std::string(FULLVER))
+      .Repeat("STRUCTS", GetDocument().GetBlocks(),
+            [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+          return SetStructs(ra, i);
+        })
+      .ReplaceBlock("MODULE_BLOCK", [&](ReplaceAll& ra) { SetModules(ra); })
+      .Out(stream);
+  stream.close();
+}
+
+void RsGroupGen::OnFiniGen(std::ofstream& stream) {
+  RsGeneratorBase::OnFiniGen(stream);
+}
+
+void RsGroupGen::SetModules(ReplaceAll& ra) {
+  std::string str;
+  int m_cnt = 2;
+  int d_cnt = 1;
+
+  ra.Repeat("MODULES", GetDocument().GetBlocks(),
+      [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+    if (i->GetType() != Block::TYPE_INTERFACE)
+      return false;
+    Interface& iface = static_cast<Interface&>(*i);
+    ra.Replace("MOD_NAME", PascalToSnake(iface.GetID()))
+        .Repeat("METHOD_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetMethodIDs(ra, d, m_cnt);
+        })
+        .Repeat("DELEGATE_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetDelegateIDs(ra, d, d_cnt);
+        })
+        .ReplaceBlock("DELEGATE_BLOCK", [&](ReplaceAll& ra) {
+          SetDelegateBlock(ra, iface);
+        })
+        .Repeat("ENUM", iface.GetEnums(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+          return SetEnum(ra, e);
+        })
+        .ReplaceBlock("INTERFACE_BLOCK", [&](ReplaceAll& ra) {
+          SetInterfaceBlock(ra, iface);
+        });
+    return true;
+  });
+}
+
+bool RsGroupGen::SetMethodIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+bool RsGroupGen::SetDelegateIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() != Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+
+void RsGroupGen::SetDelegateBlock(ReplaceAll& ra, const Interface& iface) {
+  auto& decls = iface.GetDeclarations();
+
+  ra.Repeat("DELEGATES", decls,
+      [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& i) {
+    if (i->GetMethodType() != Declaration::MethodType::DELEGATE)
+      return false;
+    ra.Replace("TYPES", GetParameters(i->GetParameters(), true))
+      .Repeat("REPLY_BODY", i->GetParameters(),
+          [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+        ra.Replace("ID", p->GetID());
+        return true;
+      })
+      .Replace("METHOD_NAME", SnakeToPascal(i->GetID()))
+      .Replace("METHOD_ORG_NAME", i->GetID());
+    return true;
+  });
+}
+
+void RsGroupGen::SetInterfaceBlock(ReplaceAll& ra, const Interface& iface) {
+  ra.Repeat("ENUM", iface.GetEnums(),
+      [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+    return SetEnum(ra, e);
+  });
+
+  ra.Replace("IFACE_NAME", SnakeToPascal(iface.GetID()))
+    .Repeat("METHODS_PROTO", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+      return SetMethod(ra, d);
+    })
+    .Repeat("METHODS", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+      return SetMethod(ra, d);
+    })
+    .Repeat("DISPATCHES", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+      if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+        return false;
+
+      ra.Replace("ID", d->GetID())
+        .Replace("SNAKE_ID", PascalToSnake(d->GetID()));
+      return true;
+    })
+    .Repeat("PRIVILEGES", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+          if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+            return false;
+
+          std::vector<std::string> privileges;
+          for (auto& attr : d->GetAttributes()) {
+            if (attr->GetKey() != "privilege")
+              continue;
+
+            privileges.push_back(attr->GetValue());
+          }
+          if (privileges.empty())
+            return false;
+
+          ra.Repeat("PRIVILEGE", privileges,
+              [&](ReplaceAll& ra, const std::string& priv) {
+            ra.Replace("ID", priv);
+            return true;
+          })
+          .Replace("METHOD", SnakeToPascal(d->GetID()));
+          return true;
+        });
+}
+
+bool RsGroupGen::SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  if (!d->GetComments().empty())
+    ra.Replace("COMMENTS", AddIndent(2 * TAB_SIZE, d->GetComments()));
+  else
+    ra.Replace("COMMENTS", {});
+
+  ra.Replace("FN_NAME", PascalToSnake(d->GetID()))
+      .Replace("PARAMS", GetParameters(d->GetParameters()))
+      //    .Replace("RET", GetRetExpr(*d))
+      .Remove("ASYNC", (*d).GetMethodType() == Declaration::MethodType::SYNC)
+      .Remove("SYNC", (*d).GetMethodType() == Declaration::MethodType::ASYNC)
+      .Replace("METHOD_ID", SnakeToPascal((*d).GetID()))
+      .Repeat("REPLY_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+                return SetSenderBody(ra, i);
+              })
+      .Repeat("SENDER_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+                return SetSenderBody(ra, i);
+              })
+      .Repeat("RECEIVER_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+                ra.Replace("ID", p->GetID())
+                  .Replace("TYPE", ConvertTypeToString(
+                      p->GetParameterType().GetBaseType()));
+                return true;
+              })
+      .Repeat("DELEGATER_RECEIVER_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (!p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+                ra.Replace("ID", p->GetID())
+                  .Replace("TYPE", ConvertTypeToString(
+                      p->GetParameterType().GetBaseType()));
+                return true;
+              })
+      .Repeat("RETURNS", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (p->GetParameterType().GetDirection() == ParameterType::Direction::IN ||
+                    p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+
+                ra.Replace("ID", p->GetID());
+                return true;
+              })
+      .Replace("IDS", GetParameterIDsWithDir((*d).GetParameters()))
+      .Replace("TYPE", [&]() { return ConvertTypeToString((*d).GetType()); });
+  return true;
+}
+
+void RsGroupGen::SetFile(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  std::string type = GetFullNameFromType(i->GetParameterType().GetBaseType());
+
+  ra.Remove("FILE", type != "file")
+    .Remove("FILESLIST", type != "list_file")
+    .Remove("FILESARRAY", type != "array_file");
+}
+
+bool RsGroupGen::SetSenderBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::OUT)
+    return false;
+
+  SetFile(ra, i);
+
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+bool RsGroupGen::SetReceiverBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::IN)
+        return false;
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+std::string RsGroupGen::GetRetExpr(const Declaration& decl) {
+  if (decl.GetType().ToString() == "void")
+    return "";
+  return "-> " + ConvertTypeToString(decl.GetType());
+}
+
+}  // namespace version2
+}  // namespace tidl
diff --git a/idlc/gen/version2/rs_group_gen.h b/idlc/gen/version2/rs_group_gen.h
new file mode 100644 (file)
index 0000000..169787f
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_RS_GEN_RS_GROUP_GEN_H_
+#define IDLC_RS_GEN_RS_GROUP_GEN_H_
+
+#include <memory>
+#include <string>
+
+#include "idlc/gen/version2/rs_gen_base.h"
+
+namespace tidl {
+namespace version2 {
+
+class RsGroupGen : public RsGeneratorBase {
+ public:
+  explicit RsGroupGen(std::shared_ptr<Document> doc);
+  virtual ~RsGroupGen() = default;
+
+  void OnInitGen(std::ofstream& stream) override;
+  void OnFiniGen(std::ofstream& stream) override;
+
+ private:
+  void SetModules(ReplaceAll& ra);
+  void SetDelegateBlock(ReplaceAll& ra, const Interface& iface);
+  void SetInterfaceBlock(ReplaceAll& ra, const Interface& iface);
+  bool SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d);
+  std::string GetRetExpr(const Declaration& decl);
+  bool SetSenderBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetReceiverBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetMethodIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  bool SetDelegateIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  void SetFile(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+};
+
+}  // namespace version2
+}  // namespace tidl
+
+#endif  // IDLC_RS_GEN_RS_GROUP_GEN_H_
diff --git a/idlc/gen/version2/rs_group_gen_cb.h b/idlc/gen/version2/rs_group_gen_cb.h
new file mode 100644 (file)
index 0000000..ef947bf
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_RS_GEN_RS_GROUP_GEN_CB_H_
+#define IDLC_RS_GEN_RS_GROUP_GEN_CB_H_
+
+constexpr const char CB_GROUP_MAIN[] =
+R"__rs_cb(
+extern crate rust_app_event;
+extern crate rust_rpc_port;
+use rust_app_event::{AppEvent, Bundle};
+use rust_rpc_port::parcel::Parcel;
+use std::collections::{LinkedList, HashMap, HashSet};
+use std::sync::{Arc, Mutex};
+
+use impl_internal::*;
+#[macro_use]
+mod impl_internal;
+
+const TIDL_VERSION: &str = "<VERSION>";
+
+#[derive(PartialEq, Debug)]
+pub enum ErrorId {
+    IoError = -5,
+    OutOfMemory = -12,
+    PermissionDenied = -13,
+    InvalidParameter = -22,
+}
+
+fn convert_err(id: i32) -> ErrorId {
+    match id {
+        -5 => ErrorId::IoError,
+        -12 => ErrorId::OutOfMemory,
+        -13 => ErrorId::PermissionDenied,
+        -22 => ErrorId::InvalidParameter,
+        _ => {
+            error!("Unknown error {:?}", ErrorId::IoError);
+            ErrorId::IoError
+        }
+    }
+}
+
+<STRUCTS*>
+pub mod <STRUCT_MOD_NAME> {
+    use super::*;
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+
+    </ENUM*>
+    #[derive(Default)]
+    pub struct <STRUCT_NAME> {
+        <PROPERTIES*>
+        pub <ID>: <TYPE>,
+        </PROPERTIES*>
+    }
+}
+</STRUCTS*>
+
+<MODULE_BLOCK!>
+<MODULES*>
+pub mod <MOD_NAME> {
+    use super::super::*;
+    use super::*;
+
+    #[derive(PartialEq)]
+    enum MethodId {
+        __Result = 0,
+        __Callback = 1,
+        <METHOD_ID*>
+        <ID> = <COUNT>,
+        </METHOD_ID*>
+    }
+
+    #[derive(PartialEq)]
+    enum DelegateId {
+        <DELEGATE_ID*>
+        <ID> = <COUNT>,
+        </DELEGATE_ID*>
+    }
+
+    <INTERFACE_BLOCK!>
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+    </ENUM*>
+
+    pub struct Connection {
+        event_name: String,
+        group: Option<AppEvent>,
+    }
+
+    impl Connection {
+
+        /// <summary>
+        /// Unsubscribes the event.
+        /// </summary>
+        pub fn unsubscribe(&mut self) {
+            self.group = None;
+        }
+    }
+
+    pub trait ServiceHandler {
+
+        /// <summary>
+        /// The method for making service instances
+        /// </summary>
+        /// <param name="connection">The connection object</param>
+        fn new(connection: Arc<Mutex<Connection>>) -> Box<dyn ServiceHandler + Send> where Self: Sized;
+        <METHODS_PROTO*>
+        fn on_<FN_NAME>(&mut self, <PARAMS>);
+        </METHODS_PROTO*>
+    }
+
+    pub struct <IFACE_NAME> {
+        sender_id: String,
+        is_system: bool,
+        connection: Arc<Mutex<Connection>>,
+        handler: Box<dyn ServiceHandler + Send>,
+        on_received: Arc<Mutex<Box<dyn FnMut(&str, &Bundle) + Send>>>,
+    }
+
+    impl <IFACE_NAME> {
+
+        /// <summary>
+        /// Constructor for this struct
+        /// </summary>
+        /// <param name="sender_id">The sender application ID</param>
+        /// <param name="is_system">The flag indicating whether the event is a system event.</param>
+        pub fn new<T: ServiceHandler>(sender_id: String, is_system: bool) -> Arc<Mutex<<IFACE_NAME>>> {
+            let mut event_name: String;
+            match is_system {
+                true => event_name = "tizen.system.event.tidl_iface_<IFACE_NAME>".to_string(),
+                false => event_name = ("event.".to_owned() + &sender_id + ".tidl_iface_<IFACE_NAME>").to_string(),
+            }
+
+            let connection = Arc::new(Mutex::new(Connection {
+                event_name,
+                group: None,
+            }));
+            let mut obj = Arc::new(Mutex::new(Self {
+                sender_id,
+                is_system,
+                connection: connection.clone(),
+                handler: T::new(connection),
+                on_received: Arc::new(Mutex::new(Box::new(|event_name: &str, event_data: &Bundle| {
+                }))),
+            }));
+
+            let mut copy_obj = obj.clone();
+            obj.lock().unwrap().on_received = Arc::new(Mutex::new(Box::new(move |event_name: &str, event_data: &Bundle| {
+                let mut __parcel = copy_obj.lock().unwrap().get_parcel_from_bundle(event_data);
+                let mut __unit_map = UnitMap::new();
+                __unit_map.deserialize(&__parcel);
+                let mut __cmd: i32 = -1;
+                __cmd.read_unitmap("[METHOD]".to_string(), &mut __unit_map);
+                match __cmd {
+                    <DISPATCHES*>
+                    x if x == MethodId::<ID> as i32 => {
+                        copy_obj.lock().unwrap().dispatch_<SNAKE_ID>(&__unit_map);
+                    }
+                    </DISPATCHES*>
+                    _ => {
+                        error!("Unknown command: {}", __cmd);
+                        return;
+                    }
+                }
+            })));
+
+            obj
+        }
+
+        fn get_bundle_from_parcel(&self, parcel: &Parcel) -> Bundle {
+            let mut raw = parcel.get_raw();
+            let mut b = Bundle::new();
+
+            b.add_bytes("TIDL_RAW", &raw.to_vec());
+            b
+        }
+
+        fn get_parcel_from_bundle(&self, bundle: &Bundle) -> Parcel {
+            let mut raw = bundle.get_bytes("TIDL_RAW").unwrap();
+
+            Parcel::from_raw(raw.as_slice())
+        }
+
+        /// <summary>
+        /// Subscribes the event
+        /// </summary>
+        pub fn subscribe(&mut self) -> Result<(), ErrorId> {
+            let mut connection = self.connection.lock().unwrap();
+            if connection.group.is_some() {
+                return Err(ErrorId::IoError);
+            }
+
+            connection.group = Some(AppEvent::new(connection.event_name.clone()));
+            if let Err(e) = connection.group.as_mut().unwrap().on_received(self.on_received.clone()) {
+                error!("failed to subscribe event: {:?}", e);
+                return Err(convert_err(e as i32));
+            }
+
+            Ok(())
+        }
+
+        /// <summary>
+        /// Unsubscribes the event
+        /// </summary>
+        pub fn unsubscribe(&mut self) {
+            self.connection.lock().unwrap().unsubscribe();
+        }
+
+        <METHODS*>
+        pub fn <FN_NAME>(&mut self, <PARAMS>) -> Result<(), ErrorId> {
+            let mut __unit_map = UnitMap::new();
+            (MethodId::<METHOD_ID> as i32).write_unitmap("[METHOD]", &mut __unit_map);
+            <SENDER_BODY*>
+            <ID>.write_unitmap("<ID>", &mut __unit_map);
+            </SENDER_BODY*>
+
+            let mut __parcel = Parcel::new();
+            __unit_map.serialize(&mut __parcel);
+            let mut __bundle = self.get_bundle_from_parcel(&__parcel);
+
+            let connection = self.connection.lock().unwrap();
+            match AppEvent::publish(connection.event_name.as_str(), &mut __bundle) {
+                Ok(_) => Ok(()),
+                Err(e) => {
+                    error!("fail to publish");
+                    Err(convert_err(e as i32))
+                },
+            }
+        }
+
+        fn dispatch_<FN_NAME>(&mut self, unit_map: &UnitMap) {
+            <RECEIVER_BODY*>
+            let mut <ID>: <TYPE> = Default::default();
+            unit_map.read("<ID>", &mut <ID>);
+            </RECEIVER_BODY*>
+            self.handler.on_<FN_NAME>(<IDS>);
+        }
+        </METHODS*>
+    }
+    </INTERFACE_BLOCK!>
+}
+</MODULES*>
+</MODULE_BLOCK!>
+)__rs_cb";
+
+#endif  // IDLC_RS_GEN_RS_GROUP_GEN_CB_H_
diff --git a/idlc/gen/version2/rs_proxy_gen.cc b/idlc/gen/version2/rs_proxy_gen.cc
new file mode 100644 (file)
index 0000000..291996f
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idlc/gen/version2/rs_proxy_gen.h"
+
+namespace {
+#include "idlc/gen/version2/rs_proxy_gen_cb.h"
+}
+
+namespace tidl {
+namespace version2 {
+
+RsProxyGen::RsProxyGen(std::shared_ptr<Document> doc)
+    : RsGeneratorBase(doc) {}
+
+void RsProxyGen::OnInitGen(std::ofstream& stream) {
+  RsGeneratorBase::OnInitGen(stream);
+  stream.open(FileName + "/mod.rs");
+  ReplaceAll(::CB_PROXY_MAIN)
+      .Replace("VERSION", std::string(FULLVER))
+      .Repeat("STRUCTS", GetDocument().GetBlocks(),
+            [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+          return SetStructs(ra, i);
+        })
+      .ReplaceBlock("MODULE_BLOCK", [&](ReplaceAll& ra) { SetModules(ra); })
+      .Remove("BUNDLE_HEADER_BLOCK", RsGeneratorBase::HasBundle() != true)
+      .Out(stream);
+  stream.close();
+}
+
+void RsProxyGen::OnFiniGen(std::ofstream& stream) {
+  RsGeneratorBase::OnFiniGen(stream);
+}
+
+void RsProxyGen::SetModules(ReplaceAll& ra) {
+  std::string str;
+  int m_cnt = 2;
+  int d_cnt = 1;
+
+  ra.Repeat("MODULES", GetDocument().GetBlocks(),
+      [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+    if (i->GetType() != Block::TYPE_INTERFACE)
+      return false;
+    Interface& iface = static_cast<Interface&>(*i);
+    ra.Replace("MOD_NAME", PascalToSnake(iface.GetID()))
+        .Repeat("METHOD_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetMethodIDs(ra, d, m_cnt);
+        })
+        .Repeat("DELEGATE_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetDelegateIDs(ra, d, d_cnt);
+        })
+        .ReplaceBlock("DELEGATE_BLOCK", [&](ReplaceAll& ra) {
+          SetDelegateBlock(ra, iface);
+        })
+        .Repeat("ENUM", iface.GetEnums(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+          return SetEnum(ra, e);
+        })
+        .ReplaceBlock("INTERFACE_BLOCK", [&](ReplaceAll& ra) {
+          SetInterfaceBlock(ra, iface);
+        });
+    return true;
+  });
+}
+
+bool RsProxyGen::SetMethodIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+bool RsProxyGen::SetDelegateIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() != Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+
+void RsProxyGen::SetDelegateBlock(ReplaceAll& ra, const Interface& iface) {
+  auto& decls = iface.GetDeclarations();
+
+  ra.Repeat("DELEGATES", decls,
+      [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& i) {
+    if (i->GetMethodType() != Declaration::MethodType::DELEGATE)
+      return false;
+    ra.Replace("TYPES", GetParameters(i->GetParameters(), false))
+      .Repeat("RECEIVER_BODY", i->GetParameters(),
+        [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p){
+        ra.Replace("ID", p->GetID())
+          .Replace("TYPE", ConvertTypeToString(
+              p->GetParameterType().GetBaseType()));
+        return true;
+      })
+      .Replace("IDS", GetParameterIDs(i->GetParameters()))
+      .Replace("METHOD_NAME", SnakeToPascal(i->GetID()));
+    return true;
+  });
+}
+
+void RsProxyGen::SetInterfaceBlock(ReplaceAll& ra, const Interface& iface) {
+  ra.Repeat("ENUM", iface.GetEnums(),
+      [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+    return SetEnum(ra, e);
+  });
+
+  ra.Replace("IFACE_NAME", iface.GetID())
+    .Repeat("METHODS", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+      return SetMethod(ra, d);
+    });
+}
+
+bool RsProxyGen::SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  if (!d->GetComments().empty())
+    ra.Replace("COMMENTS", AddIndent(2 * TAB_SIZE, d->GetComments()));
+  else
+    ra.Replace("COMMENTS", {});
+
+  ra.Replace("FN_NAME", PascalToSnake(d->GetID()))
+    .Replace("PARAMS", GetParameters(d->GetParameters()))
+//    .Replace("RET", GetRetExpr(*d))
+    .Replace("METHOD_ID", SnakeToPascal((*d).GetID()))
+    .Repeat("SENDER_BODY", (*d).GetParameters(),
+        [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+      return SetSenderBody(ra, i);
+    })
+    .Remove("ASYNC", (*d).GetMethodType() == Declaration::MethodType::SYNC)
+    .Remove("SYNC", (*d).GetMethodType() == Declaration::MethodType::ASYNC)
+    .Repeat("RECEIVER_BODY", (*d).GetParameters(),
+        [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+      return SetReceiverBody(ra, i);
+    })
+    .Repeat("REG_DELEGATERS", (*d).GetParameters(),
+        [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+      return SetRegDelegaters(ra, i);
+    })
+    .Remove("HAS_RETURN", (*d).GetType().ToString() == "void" )
+    .Replace("TYPE", [&]() {
+      return ConvertTypeToString((*d).GetType());
+    });
+  return true;
+}
+
+void RsProxyGen::SetFile(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  std::string type = GetFullNameFromType(i->GetParameterType().GetBaseType());
+
+  ra.Remove("FILE", type != "file")
+    .Remove("FILESLIST", type != "list_file")
+    .Remove("FILESARRAY", type != "array_file");
+}
+
+bool RsProxyGen::SetSenderBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::OUT)
+    return false;
+
+  SetFile(ra, i);
+
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+bool RsProxyGen::SetReceiverBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::IN)
+        return false;
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+bool RsProxyGen::SetRegDelegaters(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (!IsDelegateType(i->GetParameterType().GetBaseType()))
+        return false;
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+std::string RsProxyGen::GetRetExpr(const Declaration& decl) {
+  if (decl.GetType().ToString() == "void")
+    return "";
+  return "-> " + ConvertTypeToString(decl.GetType());
+}
+
+}  // namespace version2
+}  // namespace tidl
diff --git a/idlc/gen/version2/rs_proxy_gen.h b/idlc/gen/version2/rs_proxy_gen.h
new file mode 100644 (file)
index 0000000..9d45383
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_GEN_VERSION2_RS_PROXY_GEN_H_
+#define IDLC_GEN_VERSION2_RS_PROXY_GEN_H_
+
+#include <memory>
+#include <string>
+
+#include "idlc/gen/version2/rs_gen_base.h"
+
+namespace tidl {
+namespace version2 {
+
+class RsProxyGen : public RsGeneratorBase {
+ public:
+  explicit RsProxyGen(std::shared_ptr<Document> doc);
+  virtual ~RsProxyGen() = default;
+
+  void OnInitGen(std::ofstream& stream) override;
+  void OnFiniGen(std::ofstream& stream) override;
+
+ private:
+  void SetModules(ReplaceAll& ra);
+  void SetDelegateBlock(ReplaceAll& ra, const Interface& iface);
+  void SetInterfaceBlock(ReplaceAll& ra, const Interface& iface);
+  bool SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d);
+  std::string GetRetExpr(const Declaration& decl);
+  bool SetSenderBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetReceiverBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetRegDelegaters(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetMethodIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  bool SetDelegateIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  void SetFile(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+};
+
+}  // namespace version2
+}  // namespace tidl
+
+#endif  // IDLC_RS_GEN_RS_PROXY_GEN_H_
diff --git a/idlc/gen/version2/rs_proxy_gen_cb.h b/idlc/gen/version2/rs_proxy_gen_cb.h
new file mode 100644 (file)
index 0000000..3330ea3
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_GEN_VERSION2_RS_PROXY_GEN_CB_H_
+#define IDLC_GEN_VERSION2_RS_PROXY_GEN_CB_H_
+
+constexpr const char CB_PROXY_MAIN[] =
+R"__rs_cb(
+extern crate rust_rpc_port;
+<BUNDLE_HEADER_BLOCK?>
+extern crate tizen_bundle;
+
+use tizen_bundle::tizen_bundle::Bundle;
+</BUNDLE_HEADER_BLOCK?>
+use rust_rpc_port::parcel::Parcel;
+use rust_rpc_port::proxy::{Proxy, Receive};
+use rust_rpc_port::{Port, PortType};
+use std::sync::{Arc, Mutex};
+use std::collections::{LinkedList, HashMap, HashSet};
+use std::sync::atomic::{AtomicI32, Ordering};
+
+use impl_internal::*;
+#[macro_use]
+mod impl_internal;
+
+static SEQ_NUM: AtomicI32 = AtomicI32::new(0);
+const TIDL_VERSION: &str = "<VERSION>";
+
+
+#[derive(PartialEq, Debug)]
+pub enum ErrorId {
+    IoError = -5,
+    OutOfMemory = -12,
+    PermissionDenied = -13,
+    InvalidParameter = -22,
+}
+
+fn convert_err(id: i32) -> ErrorId {
+    match id {
+        -5 => ErrorId::IoError,
+        -12 => ErrorId::OutOfMemory,
+        -13 => ErrorId::PermissionDenied,
+        -22 => ErrorId::InvalidParameter,
+        _ => {
+            error!("Unknown error {:?}", ErrorId::IoError);
+            ErrorId::IoError
+        }
+    }
+}
+
+#[derive(Debug, Clone)]
+pub struct RemoteException {
+    pub cause: i32,
+    pub message: String,
+}
+
+impl RemoteException {
+    pub fn new() -> Self {
+        RemoteException {
+            cause: 0,
+            message: "".to_string(),
+        }
+    }
+
+    pub fn with_message(message: &str) -> Self {
+        RemoteException {
+            cause: 0,
+            message: message.to_string(),
+        }
+    }
+
+    pub fn with_cause_and_message(cause: i32, message: &str) -> Self {
+        RemoteException {
+            cause,
+            message: message.to_string(),
+        }
+    }
+
+    pub fn get_cause(&self) -> i32 {
+        self.cause
+    }
+
+    pub fn get_message(&self) -> &String {
+        &self.message
+    }
+}
+
+#[derive(Debug)]
+pub enum Exception {
+    LocalException(ErrorId),
+    RemoteException(RemoteException),
+}
+
+<STRUCTS*>
+pub mod <STRUCT_MOD_NAME> {
+    use super::*;
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+
+    </ENUM*>
+    #[derive(Default)]
+    pub struct <STRUCT_NAME> {
+        <PROPERTIES*>
+        pub <ID>: <TYPE>,
+        </PROPERTIES*>
+    }
+}
+</STRUCTS*>
+
+<MODULE_BLOCK!>
+<MODULES*>
+pub mod <MOD_NAME> {
+    use super::*;
+
+    #[derive(PartialEq)]
+    enum MethodId {
+        __Result = 0,
+        __Callback = 1,
+        <METHOD_ID*>
+        <ID> = <COUNT>,
+        </METHOD_ID*>
+    }
+
+    #[derive(PartialEq)]
+    enum DelegateId {
+        <DELEGATE_ID*>
+        <ID> = <COUNT>,
+        </DELEGATE_ID*>
+    }
+
+    <DELEGATE_BLOCK!>
+    trait Delegate {
+        fn invoke(&self, unit_map: &UnitMap, id: i32, seq_id: i32) -> bool;
+    }
+
+    struct DelegateBase {
+        id: i32,
+        seq_id: i32,
+        once: bool,
+    }
+
+    impl Default for DelegateBase {
+        fn default() -> Self {
+            Self {
+                id: 0,
+                seq_id: 0,
+                once: false,
+            }
+        }
+    }
+
+    impl Writeable for DelegateBase {
+        fn write_parcel(&self, parcel: &mut Parcel) {
+            self.id.write_parcel(parcel);
+            self.seq_id.write_parcel(parcel);
+            self.once.write_parcel(parcel);
+        }
+        fn write_unit(&self, unit: &mut Unit) {
+            let mut __unit_map = UnitMap::new();
+            self.id.write_unitmap("id", &mut __unit_map);
+            self.seq_id.write_unitmap("seq_id", &mut __unit_map);
+            self.once.write_unitmap("once", &mut __unit_map);
+            __unit_map.serialize(unit.get_mut_parcel());
+        }
+        fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+            let mut unit = Unit::new(String::from(name), String::from("delegate"));
+            unit.write(self);
+            unit_map.insert(String::from(name), unit);
+        }
+    }
+
+    impl Readable for DelegateBase {
+        fn read_parcel(&mut self, parcel: &Parcel) {
+            self.id.read_parcel(parcel);
+            self.seq_id.read_parcel(parcel);
+            self.once.read_parcel(parcel);
+        }
+        fn read_unit(&mut self, unit: &Unit) {
+            let mut __unit_map = UnitMap::new();
+            __unit_map.deserialize(unit.get_parcel());
+
+            let mut __tmp_id: i32 = Default::default();
+            __unit_map.read("id", &mut __tmp_id);
+            self.id = __tmp_id;
+            let mut __tmp_seq_id: i32 = Default::default();
+            __unit_map.read("seq_id", &mut __tmp_seq_id);
+            self.seq_id = __tmp_seq_id;
+            let mut __tmp_once: bool = Default::default();
+            __unit_map.read("once", &mut __tmp_once);
+            self.once = __tmp_once;
+        }
+        fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+            match unit_map.lookup(name.as_str()) {
+                Some(unit) => {
+                    if unit.get_type().ne(&"delegate") {
+                        error!("type({}) is not delegate", unit.get_type());
+                        return;
+                    }
+
+                    unit.read(self);
+                }
+                None => {
+                    error!("Failed to find {}", name);
+                    return;
+                }
+            }
+        }
+    }
+
+    <DELEGATES*>
+    pub struct <METHOD_NAME><'a> {
+        base: DelegateBase,
+        callback: Box<dyn Fn(<TYPES>) + Send + 'a>
+    }
+
+    impl<'b> <METHOD_NAME><'b> {
+        pub fn new(once: bool) -> <METHOD_NAME><'b> {
+            Self {
+                base: DelegateBase {
+                    id: DelegateId::<METHOD_NAME> as i32,
+                    seq_id: SEQ_NUM.fetch_add(1, Ordering::SeqCst),
+                    once,
+                },
+                callback: Box::new(|<IDS>|{})
+            }
+        }
+
+        fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+            self.base.write_unitmap(name, unit_map);
+        }
+
+        pub fn on_received<F>(&mut self, handler: F)
+        where
+            F: Fn(<TYPES>) + Send + 'b,
+        {
+            self.callback = Box::new(handler);
+        }
+    }
+
+    impl Delegate for <METHOD_NAME><'_> {
+        fn invoke(&self, unit_map: &UnitMap, id: i32, seq_id: i32) -> bool {
+            if id != self.base.id || seq_id != self.base.seq_id {
+              return false;
+            }
+
+            <RECEIVER_BODY*>
+            let mut <ID>: <TYPE> = Default::default();
+            <ID>.read_unitmap("<ID>".to_string(), &unit_map);
+            </RECEIVER_BODY*>
+            let cb = &self.callback;
+            cb(<IDS>);
+            true
+        }
+    }
+    </DELEGATES*>
+    </DELEGATE_BLOCK!>
+
+    <INTERFACE_BLOCK!>
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+    </ENUM*>
+
+    pub struct <IFACE_NAME><'a> {
+        proxy: Arc<Mutex<Proxy<'a>>>,
+        delegate_list: Vec<Box<dyn Delegate + Send + 'a>>,
+    }
+
+    impl Receive for <IFACE_NAME><'_> {
+        fn on_received(&mut self, receiver: &str, port_name: &str) {
+            info!("receiver {receiver} port_name {port_name}");
+            self.on_received(receiver, port_name);
+        }
+    }
+
+    impl<'b> <IFACE_NAME><'b> {
+
+        /// <summary>
+        /// Constructor for this struct
+        /// </summary>
+        pub fn new() -> Arc<Mutex<<IFACE_NAME><'b>>> {
+            info!("proxy new");
+            let p = Arc::new(Mutex::new(Proxy::try_new().unwrap()));
+            let ret = Arc::new(Mutex::new(Self {
+                proxy: p.clone(),
+                delegate_list: vec![],
+            }));
+
+            p.lock().unwrap()
+                .on_received(ret.clone() as Arc<Mutex<dyn Receive + Send + 'b>>);
+            ret
+        }
+
+        /// <summary>
+        /// Connects to the service app.
+        /// </summary>
+        /// <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+        /// <privilege>http://tizen.org/privilege/datasharing</privilege>
+        /// <param name="appid">The service app ID to connect</param>
+        /// <exception cref="InvalidParameter">
+        /// Returns when the appid to connect is invalid.
+        /// </exception>
+        /// <exception cref="IoError">
+        /// Returns when internal I/O error happen.
+        /// </exception>
+        /// <exception cref="PermissionDenied">
+        /// Returns when the permission is denied.
+        /// </exception>
+        /// <remark> If you want to use this method, you must add privileges.</remark>
+        pub fn try_connect(&self, appid: &str) -> Result<(), ErrorId> {
+            info!("appid {appid}");
+            let result = self.proxy.lock().unwrap().try_connect(appid, "<IFACE_NAME>");
+            match result {
+                Ok(_) => Ok(()),
+                Err(id) => Err(convert_err(id))
+            }
+        }
+
+        /// <summary>
+        /// Connects to the service app synchornously.
+        /// </summary>
+        /// <privilege>http://tizen.org/privilege/appmanager.launch</privilege>
+        /// <privilege>http://tizen.org/privilege/datasharing</privilege>
+        /// <param name="appid">The service app ID to connect</param>
+        /// <exception cref="InvalidParameter">
+        /// Returns when the appid to connect is invalid.
+        /// </exception>
+        /// <exception cref="IoError">
+        /// Returns when internal I/O error happen.
+        /// </exception>
+        /// <exception cref="PermissionDenied">
+        /// Returns when the permission is denied.
+        /// </exception>
+        /// <remark> If you want to use this method, you must add privileges.</remark>
+        pub fn try_connect_sync(&self, appid: &str) -> Result<(), ErrorId> {
+            info!("appid {appid}");
+            let result = self.proxy.lock().unwrap().try_connect_sync(appid, "<IFACE_NAME>");
+            match result {
+                Ok(_) => Ok(()),
+                Err(id) => Err(convert_err(id))
+            }
+        }
+
+        /// <summary>
+        /// This method will be invoked when the client app is connected to the service app.
+        /// </summary>
+        /// <param name="handler">The handler to be invoked when the client app is connected</param>
+        pub fn on_connected<F>(&mut self, handler: F)
+        where
+            F: Fn(&str, &str, Port) + 'b,
+        {
+            self.proxy.lock().unwrap().on_connected(handler);
+        }
+
+        /// <summary>
+        /// This method will be invoked after the client app was disconnected from the service app.
+        /// </summary>
+        /// <param name="handler">The handler to be invoked when the client app is disconnected</param>
+        pub fn on_disconnected<F>(&mut self, handler: F)
+        where
+            F: Fn(&str, &str) + 'b,
+        {
+            self.proxy.lock().unwrap().on_disconnected(handler);
+        }
+
+        /// <summary>
+        /// This method will be invoked when the service app rejects the client app.
+        /// </summary>
+        /// <param name="handler">The handler to be invoked when the service app rejects the client app</param>
+        pub fn on_rejected<F>(&mut self, handler: F)
+        where
+            F: Fn(&str, &str) + 'b,
+        {
+            self.proxy.lock().unwrap().on_rejected(handler);
+        }
+
+        fn on_received(&mut self, receiver: &str, port_name: &str) {
+            info!("receiver {receiver} port_name {port_name}");
+            let __port = self.proxy.lock().unwrap().get_port(&PortType::Callback);
+            let __parcel_received = Parcel::from(&__port);
+
+            let mut __unit_map = UnitMap::new();
+            __unit_map.deserialize(&__parcel_received);
+            let mut __cmd: i32 = -1;
+            let unit = __unit_map.lookup("[METHOD]");
+            match unit {
+                Some(u) => {
+                    u.read(&mut __cmd);
+                }
+                None => {
+                    error!("Invalid protocol");
+                    return;
+                }
+            }
+            let __cmd_exp = MethodId::__Callback as i32;
+            if __cmd != __cmd_exp {
+                error!("Invalid protocol");
+                return;
+            }
+
+            let mut __delegate_base: DelegateBase = DelegateBase::default();
+            __delegate_base.read_unitmap("delegate".to_string(), &mut __unit_map);
+            for (pos, delegate) in self.delegate_list.iter().enumerate() {
+                if delegate.invoke(&__unit_map, __delegate_base.id, __delegate_base.seq_id) {
+                    if __delegate_base.once {
+                        self.delegate_list.remove(pos);
+                    }
+                    break;
+                }
+            }
+        }
+
+        <METHODS*><COMMENTS>
+        pub fn <FN_NAME>(&mut self, <PARAMS>) -> Result<<TYPE>, Exception> {
+            let mut __p = Parcel::new();
+            __p.set_tag(String::from(TIDL_VERSION));
+            let seq_num = __p.get_seq_num();
+            info!("get seq_num {seq_num}");
+            let __port = self.proxy.lock().unwrap().get_port(&PortType::Main);
+
+            let mut __map = UnitMap::new();
+            (MethodId::<METHOD_ID> as i32).write_unitmap("[METHOD]", &mut __map);
+            <SENDER_BODY*>
+            <FILE?>
+            __port.set_private_sharing(&<ID>);
+            </FILE?>
+            <FILESLIST?>
+            let __vec_tmp: Vec<String> = <ID>.clone().into_iter().map(|s| s).collect();
+            __port.set_private_sharing_array(&__vec_tmp);
+            </FILESLIST?>
+            <FILESARRAY?>
+            __port.set_private_sharing_array(&<ID>);
+            </FILESARRAY?>
+            <ID>.write_unitmap("<ID>", &mut __map);
+            </SENDER_BODY*>
+            <REG_DELEGATERS*>
+            self.delegate_list.push(Box::new(<ID>));
+            </REG_DELEGATERS*>
+
+            __map.serialize(&mut __p);
+            let __r = __p.try_send(&__port);
+            if let Err(id) = __r {
+                error!("fail to send");
+                return Err(Exception::LocalException(convert_err(id)));
+            } <ASYNC?> else {
+                return Ok(());
+            }
+            </ASYNC?>
+
+            <SYNC?>
+            let mut __map_received = UnitMap::new();
+            loop {
+                let __parcel_received = Parcel::from(&__port);
+                if __parcel_received.get_seq_num() != seq_num {
+                    continue;
+                }
+
+                __map_received.deserialize(&__parcel_received);
+                let mut cmd: i32 = -1;
+                __map_received.read("[METHOD]", &mut cmd);
+                let cmd_exp = MethodId::__Result as i32;
+                if cmd == cmd_exp {
+                    break;
+                }
+            }
+
+            if __map_received.len() == 0 {
+                error!("received map size is zero");
+            }
+
+            if let Some(unit) = __map_received.lookup("[REMOTE_EXCEPTION]") {
+                let mut __remote_exception = RemoteException::new();
+                __remote_exception.read_unit(unit);
+                error!("seq_num {seq_num} exception {:?}", Exception::RemoteException(__remote_exception.clone()));
+                return Err(Exception::RemoteException(__remote_exception));
+            }
+
+            <RECEIVER_BODY*>
+            __map_received.read("<ID>", <ID>);
+            </RECEIVER_BODY*>
+            <HAS_RETURN?>
+            let mut __ret: <TYPE> = Default::default();
+            __map_received.read("[RESULT]", &mut __ret);
+            Ok(__ret)
+            </HAS_RETURN?>
+            </SYNC?>
+        }
+        </METHODS*>
+    }
+    </INTERFACE_BLOCK!>
+}
+</MODULES*>
+</MODULE_BLOCK!>
+)__rs_cb";
+
+#endif  // IDLC_GEN_VERSION2_RS_PROXY_GEN_CB_H_
diff --git a/idlc/gen/version2/rs_stub_gen.cc b/idlc/gen/version2/rs_stub_gen.cc
new file mode 100644 (file)
index 0000000..5e6d2c1
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "idlc/gen/version2/rs_stub_gen.h"
+
+#include <algorithm>
+
+namespace {
+#include "idlc/gen/version2/rs_stub_gen_cb.h"
+}
+
+namespace tidl {
+namespace version2 {
+
+RsStubGen::RsStubGen(std::shared_ptr<Document> doc,
+                     std::shared_ptr<Options> options)
+      : RsGeneratorBase(doc), options_(std::move(options)) {}
+
+void RsStubGen::OnInitGen(std::ofstream& stream) {
+  RsGeneratorBase::OnInitGen(stream);
+  stream.open(FileName + "/mod.rs");
+  ReplaceAll(::CB_STUB_MAIN)
+      .Replace("VERSION", std::string(FULLVER))
+      .Repeat("STRUCTS", GetDocument().GetBlocks(),
+            [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+          return SetStructs(ra, i);
+        })
+      .ReplaceBlock("MODULE_BLOCK", [&](ReplaceAll& ra) { SetModules(ra); })
+      .Remove("BUNDLE_HEADER_BLOCK", RsGeneratorBase::HasBundle() != true)
+      .Out(stream);
+  stream.close();
+}
+
+void RsStubGen::OnFiniGen(std::ofstream& stream) {
+  RsGeneratorBase::OnFiniGen(stream);
+}
+
+void RsStubGen::SetModules(ReplaceAll& ra) {
+  std::string str;
+  int m_cnt = 2;
+  int d_cnt = 1;
+
+  ra.Repeat("MODULES", GetDocument().GetBlocks(),
+      [&](ReplaceAll& ra, const std::shared_ptr<Block>& i) {
+    if (i->GetType() != Block::TYPE_INTERFACE)
+      return false;
+    Interface& iface = static_cast<Interface&>(*i);
+    ra.Replace("MOD_NAME", PascalToSnake(iface.GetID()))
+        .Repeat("METHOD_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetMethodIDs(ra, d, m_cnt);
+        })
+        .Repeat("DELEGATE_ID", iface.GetDeclarations(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+          return SetDelegateIDs(ra, d, d_cnt);
+        })
+        .ReplaceBlock("DELEGATE_BLOCK", [&](ReplaceAll& ra) {
+          SetDelegateBlock(ra, iface);
+        })
+        .Repeat("ENUM",  iface.GetEnums(),
+            [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+          return SetEnum(ra, e);
+        })
+        .ReplaceBlock("INTERFACE_BLOCK", [&](ReplaceAll& ra) {
+          SetInterfaceBlock(ra, iface);
+        })
+        .RemoveAll("EXTENSION_BLOCK", options_->UseExtension() != true);
+    return true;
+  });
+}
+
+bool RsStubGen::SetMethodIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+bool RsStubGen::SetDelegateIDs(ReplaceAll& ra,
+    const std::unique_ptr<Declaration>& d, int& cnt) {
+  if (d->GetMethodType() != Declaration::MethodType::DELEGATE)
+    return false;
+
+  ra.Replace("ID", SnakeToPascal(d->GetID()))
+    .Replace("COUNT", std::to_string(cnt++));
+  return true;
+}
+
+
+void RsStubGen::SetDelegateBlock(ReplaceAll& ra, const Interface& iface) {
+  auto& decls = iface.GetDeclarations();
+
+  ra.Repeat("DELEGATES", decls,
+      [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& i) {
+    if (i->GetMethodType() != Declaration::MethodType::DELEGATE)
+      return false;
+    ra.Replace("TYPES", GetParameters(i->GetParameters(), true))
+      .Repeat("REPLY_BODY", i->GetParameters(),
+          [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+        ra.Replace("ID", p->GetID());
+        return true;
+      })
+      .Replace("METHOD_NAME", SnakeToPascal(i->GetID()))
+      .Replace("METHOD_ORG_NAME", i->GetID());
+    return true;
+  });
+}
+
+void RsStubGen::SetInterfaceBlock(ReplaceAll& ra, const Interface& iface) {
+  ra.Repeat("ENUM", iface.GetEnums(),
+      [&](ReplaceAll& ra, const std::unique_ptr<Enum>& e) {
+    return SetEnum(ra, e);
+  });
+
+  ra.Replace("IFACE_NAME", iface.GetID())
+    .Repeat("METHODS_PROTO", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+      return SetMethod(ra, d);
+    })
+    .Repeat("METHODS", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d){
+      return SetMethod(ra, d);
+    })
+    .Repeat("DISPATCHES", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+      if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+        return false;
+
+      ra.Replace("ID", d->GetID())
+        .Replace("SNAKE_ID", PascalToSnake(d->GetID()));
+      return true;
+    })
+    .Repeat("PRIVILEGES", iface.GetDeclarations(),
+        [&](ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+          if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+            return false;
+
+          std::vector<std::string> privileges;
+          for (auto& attr : d->GetAttributes()) {
+            if (attr->GetKey() != "privilege")
+              continue;
+
+            privileges.push_back(attr->GetValue());
+          }
+          if (privileges.empty())
+            return false;
+
+          ra.Repeat("PRIVILEGE", privileges,
+              [&](ReplaceAll& ra, const std::string& priv) {
+            ra.Replace("ID", priv);
+            return true;
+          })
+          .Replace("METHOD", SnakeToPascal(d->GetID()));
+          return true;
+        })
+        .Repeat("INTERFACE_PRIVILEGES", iface.GetAttributes(),
+          [&](ReplaceAll& ra, const std::unique_ptr<tidl::Attribute>& attr) {
+            if (attr->GetKey() != "privilege")
+              return false;
+
+            ra.Replace("INTERFACE_PRIVILEGE", attr->GetValue());
+            return true;
+          }
+        )
+        .Remove("SET_TRUSTED", std::find_if(
+                iface.GetAttributes().begin(),
+                iface.GetAttributes().end(),
+                [](const auto& attr) {
+                  return attr->GetKey() == "trusted" && attr->GetValue() == "true";
+                }) == iface.GetAttributes().end());
+}
+
+bool RsStubGen::SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d) {
+  if (d->GetMethodType() == Declaration::MethodType::DELEGATE)
+    return false;
+
+  if (!d->GetComments().empty())
+    ra.Replace("COMMENTS", AddIndent(2 * TAB_SIZE, d->GetComments()));
+  else
+    ra.Replace("COMMENTS", {});
+
+  ra.Replace("FN_NAME", PascalToSnake(d->GetID()))
+      .Replace("PARAMS", GetParameters(d->GetParameters()))
+      //    .Replace("RET", GetRetExpr(*d))
+      .Remove("ASYNC", (*d).GetMethodType() == Declaration::MethodType::SYNC)
+      .Remove("SYNC", (*d).GetMethodType() == Declaration::MethodType::ASYNC)
+      .Replace("METHOD_ID", SnakeToPascal((*d).GetID()))
+      .Repeat("REPLY_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& i) {
+                return SetSenderBody(ra, i);
+              })
+      .Repeat("RECEIVER_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+                ra.Remove("IS_OUT_DIR", p->GetParameterType().GetDirection() == ParameterType::Direction::OUT)
+                  .Replace("ID", p->GetID())
+                  .Replace("TYPE", ConvertTypeToString(
+                      p->GetParameterType().GetBaseType()));
+                return true;
+              })
+      .Repeat("DELEGATER_RECEIVER_BODY", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (!p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+                ra.Replace("ID", p->GetID())
+                  .Replace("TYPE", ConvertTypeToString(
+                      p->GetParameterType().GetBaseType()));
+                return true;
+              })
+      .Repeat("RETURNS", (*d).GetParameters(),
+              [&](ReplaceAll& ra, const std::unique_ptr<tidl::Parameter>& p) {
+                if (p->GetParameterType().GetDirection() == ParameterType::Direction::IN ||
+                    p->GetParameterType().GetBaseType().IsDelegateType())
+                  return false;
+
+                ra.Replace("ID", p->GetID());
+                return true;
+              })
+      .Replace("IDS", GetParameterIDsWithDir((*d).GetParameters()))
+      .Replace("TYPE", [&]() { return ConvertTypeToString((*d).GetType()); });
+  return true;
+}
+
+void RsStubGen::SetFile(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  std::string type = GetFullNameFromType(i->GetParameterType().GetBaseType());
+
+  ra.Remove("FILE", type != "file")
+    .Remove("FILESLIST", type != "list_file")
+    .Remove("FILESARRAY", type != "array_file");
+}
+
+bool RsStubGen::SetSenderBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::OUT)
+    return false;
+
+  SetFile(ra, i);
+
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+bool RsStubGen::SetReceiverBody(ReplaceAll& ra,
+    const std::unique_ptr<tidl::Parameter>& i) {
+  if (i->GetParameterType().GetDirection() == ParameterType::Direction::IN)
+        return false;
+  ra.Replace("ID", i->GetID());
+  return true;
+}
+
+std::string RsStubGen::GetRetExpr(const Declaration& decl) {
+  if (decl.GetType().ToString() == "void")
+    return "";
+  return "-> " + ConvertTypeToString(decl.GetType());
+}
+
+}  // namespace version2
+}  // namespace tidl
diff --git a/idlc/gen/version2/rs_stub_gen.h b/idlc/gen/version2/rs_stub_gen.h
new file mode 100644 (file)
index 0000000..acc8bcb
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_RS_GEN_RS_STUB_GEN_H_
+#define IDLC_RS_GEN_RS_STUB_GEN_H_
+
+#include <memory>
+#include <string>
+
+#include "idlc/gen/version2/rs_gen_base.h"
+#include "idlc/options.h"
+
+namespace tidl {
+namespace version2 {
+
+class RsStubGen : public RsGeneratorBase {
+ public:
+  explicit RsStubGen(std::shared_ptr<Document> doc,
+                     std::shared_ptr<Options> Options);
+  virtual ~RsStubGen() = default;
+
+  void OnInitGen(std::ofstream& stream) override;
+  void OnFiniGen(std::ofstream& stream) override;
+
+ private:
+  void SetModules(ReplaceAll& ra);
+  void SetDelegateBlock(ReplaceAll& ra, const Interface& iface);
+  void SetInterfaceBlock(ReplaceAll& ra, const Interface& iface);
+  bool SetMethod(ReplaceAll& ra, const std::unique_ptr<Declaration>& d);
+  std::string GetRetExpr(const Declaration& decl);
+  bool SetSenderBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetReceiverBody(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+  bool SetMethodIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  bool SetDelegateIDs(ReplaceAll& ra,
+      const std::unique_ptr<Declaration>& d, int& cnt);
+  void SetFile(ReplaceAll& ra,
+      const std::unique_ptr<tidl::Parameter>& i);
+
+ private:
+  std::shared_ptr<Options> options_;
+};
+
+}  // namespace version2
+}  // namespace tidl
+
+#endif  // IDLC_RS_GEN_RS_STUB_GEN_H_
diff --git a/idlc/gen/version2/rs_stub_gen_cb.h b/idlc/gen/version2/rs_stub_gen_cb.h
new file mode 100644 (file)
index 0000000..0245a71
--- /dev/null
@@ -0,0 +1,693 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef IDLC_RS_GEN_RS_STUB_GEN_CB_H_
+#define IDLC_RS_GEN_RS_STUB_GEN_CB_H_
+
+constexpr const char CB_STUB_MAIN[] =
+R"__rs_cb(
+extern crate rust_rpc_port;
+<BUNDLE_HEADER_BLOCK?>
+extern crate tizen_bundle;
+
+use tizen_bundle::tizen_bundle::Bundle;
+</BUNDLE_HEADER_BLOCK?>
+use rust_rpc_port::parcel::Parcel;
+use rust_rpc_port::stub::Stub;
+use rust_rpc_port::{Port, PortType};
+use std::collections::{LinkedList, HashMap, HashSet};
+use std::sync::{Arc, Mutex};
+use std::ffi::{c_char, c_void, CStr, CString};
+use std::sync::atomic::{AtomicI32, Ordering};
+
+use impl_internal::*;
+#[macro_use]
+mod impl_internal;
+
+#[link(name = "capi-appfw-app-manager")]
+extern "C" {
+    fn app_info_create(app_id: *const c_char, app_info: *mut *mut c_void) -> i32;
+    fn app_info_destroy(app_info: *mut c_void);
+    fn app_info_get_package(app_info: *mut c_void, package: *mut *mut c_char) -> i32;
+}
+
+#[link(name = "capi-appfw-package-manager")]
+extern "C" {
+    fn package_info_create(package: *const c_char, package_info: *mut *mut c_void) -> i32;
+    fn package_info_foreach_privilege_info(
+        package_info: *mut c_void,
+        callback: extern "C" fn(*const c_char, *mut c_void,) -> bool,
+        user_data: *mut c_void,
+    );
+    fn package_info_destroy(package_info: *mut c_void);
+}
+
+static SEQ_NUM: AtomicI32 = AtomicI32::new(0);
+const TIDL_VERSION: &str = "<VERSION>";
+
+#[derive(PartialEq, Debug)]
+pub enum ErrorId {
+    IoError = -5,
+    OutOfMemory = -12,
+    PermissionDenied = -13,
+    InvalidParameter = -22,
+}
+
+fn convert_err(id: i32) -> ErrorId {
+    match id {
+        -5 => ErrorId::IoError,
+        -12 => ErrorId::OutOfMemory,
+        -13 => ErrorId::PermissionDenied,
+        -22 => ErrorId::InvalidParameter,
+        _ => {
+            error!("Unknown error {:?}", ErrorId::IoError);
+            ErrorId::IoError
+        }
+    }
+}
+
+#[derive(Debug)]
+pub struct RemoteException {
+    pub cause: i32,
+    pub message: String,
+}
+
+impl RemoteException {
+    pub fn new() -> Self {
+        RemoteException {
+            cause: 0,
+            message: "".to_string(),
+        }
+    }
+
+    pub fn with_message(message: &str) -> Self {
+        RemoteException {
+            cause: 0,
+            message: message.to_string(),
+        }
+    }
+
+    pub fn with_cause_and_message(cause: i32, message: &str) -> Self {
+        RemoteException {
+            cause,
+            message: message.to_string(),
+        }
+    }
+
+    pub fn get_cause(&self) -> i32 {
+        self.cause
+    }
+
+    pub fn get_message(&self) -> &String {
+        &self.message
+    }
+}
+
+#[derive(Debug)]
+pub enum Exception {
+    LocalException(ErrorId),
+    RemoteException(RemoteException),
+}
+
+<STRUCTS*>
+pub mod <STRUCT_MOD_NAME> {
+    use super::*;
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+
+    </ENUM*>
+    #[derive(Default)]
+    pub struct <STRUCT_NAME> {
+        <PROPERTIES*>
+        pub <ID>: <TYPE>,
+        </PROPERTIES*>
+    }
+}
+</STRUCTS*>
+
+<MODULE_BLOCK!>
+<MODULES*>
+pub mod <MOD_NAME> {
+    use super::super::*;
+    use super::*;
+
+    #[derive(PartialEq)]
+    enum MethodId {
+        __Result = 0,
+        __Callback = 1,
+        <METHOD_ID*>
+        <ID> = <COUNT>,
+        </METHOD_ID*>
+    }
+
+    #[derive(PartialEq)]
+    enum DelegateId {
+        <DELEGATE_ID*>
+        <ID> = <COUNT>,
+        </DELEGATE_ID*>
+    }
+
+    pub struct Peer {
+        sender: String,
+        instance: String,
+        port: Option<Port>,
+    }
+
+    impl Peer {
+
+        /// <summary>
+        /// Gets client app ID
+        /// </summary>
+        pub fn get_sender(&self) -> String {
+            self.sender.clone()
+        }
+
+        /// <summary>
+        /// Gets client instance ID
+        /// </summary>
+        pub fn get_instance(&self) -> String {
+            self.instance.clone()
+        }
+
+        <EXTENSION_BLOCK?>
+        pub fn get_pid(&self) -> Result<i32, ErrorId> {
+            match self.port.unwrap().get_peer_info() {
+                Ok((pid, uid)) => { Ok(pid) }
+                Err(id) => Err(convert_err(id))
+            }
+        }
+
+        pub fn get_uid(&self) -> Result<u32, ErrorId> {
+            match self.port.unwrap().get_peer_info() {
+                Ok((pid, uid)) => { Ok(uid) }
+                Err(id) => Err(convert_err(id))
+            }
+        }
+        </EXTENSION_BLOCK?>
+
+        fn set_port(&mut self, port: Port) {
+            self.port = Some(port);
+        }
+
+        /// <summary>
+        /// Disconnects from the client app
+        /// </summary>
+        /// <exception cref="IoError">
+        /// Returns when internal I/O error happen.
+        /// </exception>
+        pub fn disconnect(&mut self) -> Result<(), ErrorId> {
+            if self.port.is_none() {
+                return Ok(());
+            }
+
+            match self.port.unwrap().try_disconnect() {
+                Ok(_) => {
+                    self.port = None;
+                    Ok(())
+                }
+                Err(id) => Err(convert_err(id))
+            }
+        }
+    }
+
+    <DELEGATE_BLOCK!>
+    <DELEGATES*>
+    pub struct <METHOD_NAME> {
+        id: i32,
+        seq_id: i32,
+        once: bool,
+        callback_port: Arc<Port>,
+        valid: bool,
+    }
+
+    impl <METHOD_NAME> {
+        fn new(callback_port: Arc<Port>) -> Self {
+            Self {
+                id: DelegateId::<METHOD_NAME> as i32,
+                seq_id: SEQ_NUM.fetch_add(1, Ordering::SeqCst),
+                once: false,
+                callback_port,
+                valid: true,
+            }
+        }
+
+        pub fn invoke(&mut self, <TYPES>) -> Result<(), ErrorId> {
+            if self.once && !self.valid {
+                return Err(ErrorId::IoError);
+            }
+
+            let mut __unit_map = UnitMap::new();
+            (MethodId::__Callback as i32).write_unitmap("[METHOD]", &mut __unit_map);
+            self.write_unitmap("delegate", &mut __unit_map);
+            <REPLY_BODY*>
+            <ID>.write_unitmap("<ID>", &mut __unit_map);
+            </REPLY_BODY*>
+
+            let mut __parcel = Parcel::new();
+            __unit_map.serialize(&mut __parcel);
+            if let Err(err) = __parcel.try_send(&self.callback_port) {
+                error!("send failed: {:?}", err);
+                return Err(convert_err(err));
+            }
+
+            self.valid = false;
+            Ok(())
+        }
+    }
+
+    impl Writeable for <METHOD_NAME> {
+        fn write_parcel(&self, parcel: &mut Parcel) {
+            self.id.write_parcel(parcel);
+            self.seq_id.write_parcel(parcel);
+            self.once.write_parcel(parcel);
+        }
+        fn write_unit(&self, unit: &mut Unit) {
+            let mut __unit_map = UnitMap::new();
+            self.id.write_unitmap("id", &mut __unit_map);
+            self.seq_id.write_unitmap("seq_id", &mut __unit_map);
+            self.once.write_unitmap("once", &mut __unit_map);
+            __unit_map.serialize(unit.get_mut_parcel());
+        }
+        fn write_unitmap(&self, name: &str, unit_map: &mut UnitMap) {
+            let mut unit = Unit::new(String::from(name), String::from("delegate"));
+            unit.write(self);
+            unit_map.insert(String::from(name), unit);
+        }
+    }
+
+    impl Readable for <METHOD_NAME> {
+        fn read_parcel(&mut self, parcel: &Parcel) {
+            self.id.read_parcel(parcel);
+            self.seq_id.read_parcel(parcel);
+            self.once.read_parcel(parcel);
+        }
+        fn read_unit(&mut self, unit: &Unit) {
+            let mut __unit_map = UnitMap::new();
+            __unit_map.deserialize(unit.get_parcel());
+
+            let mut __tmp_id: i32 = Default::default();
+            __unit_map.read("id", &mut __tmp_id);
+            self.id = __tmp_id;
+            let mut __tmp_seq_id: i32 = Default::default();
+            __unit_map.read("seq_id", &mut __tmp_seq_id);
+            self.seq_id = __tmp_seq_id;
+            let mut __tmp_once: bool = Default::default();
+            __unit_map.read("once", &mut __tmp_once);
+            self.once = __tmp_once;
+        }
+        fn read_unitmap(&mut self, name: String, unit_map: &UnitMap) {
+            match unit_map.lookup(name.as_str()) {
+                Some(unit) => {
+                    if unit.get_type().ne(&"delegate") {
+                        error!("type({}) is not delegate", unit.get_type());
+                        return;
+                    }
+
+                    unit.read(self);
+                }
+                None => {
+                    error!("Failed to find {}", name);
+                    return;
+                }
+            }
+        }
+    }
+    </DELEGATES*>
+    </DELEGATE_BLOCK!>
+
+    <INTERFACE_BLOCK!>
+    <ENUM*>
+    #[derive(Copy, Clone)]
+    pub enum <ENUM_NAME> {
+        <PROPERTIES*>
+        <ID>,
+        </PROPERTIES*>
+        DEFAULT
+    }
+    impl Default for <ENUM_NAME> {
+        fn default() -> Self {
+            <ENUM_NAME>::DEFAULT
+        }
+    }
+    </ENUM*>
+
+    pub trait ServiceHandler {
+
+        /// <summary>
+        /// The method for making service instances
+        /// </summary>
+        /// <param name="peer">The peer object</param>
+        fn new(peer: Arc<Mutex<Peer>>) -> Box<dyn ServiceHandler + Send> where Self: Sized;
+
+        /// <summary>
+        /// This method will be called when the client is connected
+        /// </summary>
+        fn on_create(&mut self);
+
+        /// <summary>
+        /// This method will be called when the client is disconnected
+        /// </summary>
+        fn on_terminate(&mut self);
+        <METHODS_PROTO*>
+        fn <FN_NAME>(&mut self, <PARAMS>) -> Result<<TYPE>, RemoteException>;
+        </METHODS_PROTO*>
+    }
+
+    struct Service {
+        peer: Arc<Mutex<Peer>>,
+        privilege_map: HashMap<i32, LinkedList<String>>,
+        privileges: HashSet<String>,
+        handler: Box<dyn ServiceHandler + Send>,
+    }
+
+    impl Service {
+        fn new<'a>(sender: &str, instance: &str, factory: &dyn Fn(Arc<Mutex<Peer>>) -> Box<dyn ServiceHandler + Send>) -> Self {
+            let peer = Arc::new(Mutex::new(Peer {
+                sender: sender.to_string(),
+                instance: instance.to_string(),
+                port: None,
+            }));
+
+            let mut service = Service {
+                peer: peer.clone(),
+                privilege_map: HashMap::new(),
+                privileges: HashSet::new(),
+                handler: (factory)(peer),
+            };
+
+            service.load_privileges();
+            service.set_privilege_map();
+            service
+        }
+
+        fn get_sender(&self) -> String {
+            self.peer.lock().unwrap().get_sender()
+        }
+
+        fn get_instance(&self) -> String {
+            self.peer.lock().unwrap().get_instance()
+        }
+
+        fn set_port(&mut self, port: Port) {
+            self.peer.lock().unwrap().set_port(port);
+        }
+
+        fn disconnect(&mut self) -> Result<(), ErrorId> {
+            info!("try disconnect");
+            self.peer.lock().unwrap().disconnect()
+        }
+
+        fn on_create(&mut self) {
+            self.handler.on_create();
+        }
+
+        fn on_terminate(&mut self) {
+            self.handler.on_terminate();
+        }
+
+        fn dispatch(&mut self, port: &Port, callback_port: Arc<Port>, parcel: &mut Parcel) {
+            let seq_num = parcel.get_seq_num();
+            let mut unit_map = UnitMap::new();
+            unit_map.deserialize(parcel);
+
+            let mut cmd: i32 = -1;
+            unit_map.read("[METHOD]", &mut cmd);
+
+            match cmd {
+                <DISPATCHES*>
+                x if x == MethodId::<ID> as i32 => {
+                    self.dispatch_<SNAKE_ID>(port, callback_port, seq_num, &mut unit_map);
+                }
+                </DISPATCHES*>
+                _ => {
+                    error!("Unknown command: {}", cmd);
+                    return;
+                }
+            }
+        }
+
+        extern "C" fn privilege_info_cb(
+            privilege_name: *const c_char,
+            user_data: *mut c_void,
+        ) -> bool {
+            let mut service: &mut Service = unsafe { &mut *(user_data as *mut Service) };
+            let privilege = unsafe { CStr::from_ptr(privilege_name).to_str().unwrap() };
+            debug!("appid: {}, privilege: {}", service.get_sender(), &privilege);
+            service.privileges.insert(privilege.to_string());
+            return true;
+        }
+
+        fn load_privileges(&mut self) {
+            let mut app_info: *mut c_void = std::ptr::null_mut();
+            let sender = self.get_sender();
+            let ret = unsafe { app_info_create(CString::new(sender).unwrap().as_ptr() as *const c_char, &mut app_info) };
+            if ret != 0 {
+                error!("app_info_create failed. ret({})", ret);
+                return;
+            }
+
+            let mut package: *mut c_char = std::ptr::null_mut();
+            let ret = unsafe { app_info_get_package(app_info, &mut package) };
+            unsafe { app_info_destroy(app_info); }
+            if ret != 0 {
+                error!("Failed to get package. error({})", ret);
+                return;
+            }
+
+            let mut package_info: *mut c_void = std::ptr::null_mut();
+            let ret = unsafe { package_info_create(package, &mut package_info) };
+            unsafe { libc::free(package as *mut c_void); }
+            if ret != 0 {
+                error!("Failed to create package info. error({})", ret);
+                return;
+            }
+
+            let data = self as *mut _ as *mut c_void;
+            unsafe {
+                package_info_foreach_privilege_info(package_info, Self::privilege_info_cb, data);
+                package_info_destroy(package_info);
+            }
+        }
+
+        fn set_privilege_map(&mut self) {
+            <PRIVILEGES*>
+            let privileges: LinkedList<String> = LinkedList::from([
+                <PRIVILEGE*>
+                "<ID>".to_owned(),
+                </PRIVILEGE*>
+            ]);
+            self.privilege_map.insert(MethodId::<METHOD> as i32, privileges);
+            </PRIVILEGES*>
+        }
+
+        fn check_privileges(&self, method_id: i32) -> bool {
+            info!("method_id {method_id}");
+            match self.privilege_map.get(&method_id) {
+                Some(privileges) => {
+                    for privilege in privileges {
+                        if self.privileges.get(privilege).is_none() {
+                            error!("{} does not exist", privilege);
+                            return false;
+                        }
+                    }
+                    true
+                }
+                None => true
+            }
+        }
+
+        <METHODS*>
+        fn dispatch_<FN_NAME>(&mut self, port: &Port, callback_port: Arc<Port>, seq_num: i32, unit_map: &UnitMap) {
+            let mut __map = UnitMap::new();
+            <SYNC?>
+            match self.check_privileges(MethodId::<METHOD_ID> as i32) {
+                false => {
+                    error!("Permission denied");
+                    let remote_except = RemoteException::with_cause_and_message(ErrorId::PermissionDenied as i32, "Permission denied");
+                    remote_except.write_unitmap("[REMOTE_EXCEPTION]", &mut __map);
+                }
+                true => {
+                    <RECEIVER_BODY*>
+                    let mut <ID>: <TYPE> = Default::default();
+                    <IS_OUT_DIR?>
+                    unit_map.read("<ID>", &mut <ID>);
+                    </IS_OUT_DIR?>
+                    </RECEIVER_BODY*>
+                    <DELEGATER_RECEIVER_BODY*>
+                    let mut <ID> = <TYPE>::new(callback_port);
+                    unit_map.read("<ID>", &mut <ID>);
+                    </DELEGATER_RECEIVER_BODY*>
+                    match self.handler.<FN_NAME>(<IDS>) {
+                        Ok(ret) => {
+                            ret.write_unitmap("[RESULT]", &mut __map);
+                            <RETURNS*>
+                            <ID>.write_unitmap("<ID>", &mut __map);
+                            </RETURNS*>
+                        }
+                        Err(err) => {
+                            error!("Error: cause({}), message({})", err.cause, err.message);
+                            err.write_unitmap("[REMOTE_EXCEPTION]", &mut __map);
+                        }
+                    }
+                }
+            }
+
+            (MethodId::__Result as i32).write_unitmap("[METHOD]", &mut __map);
+            let mut __parcel = Parcel::new();
+            __parcel.set_seq_num(seq_num);
+            __map.serialize(&mut __parcel);
+            if let Err(ret) = __parcel.try_send(port) {
+                error!("Failed to send parcel {}", ret);
+            }
+            </SYNC?>
+            <ASYNC?>
+            if self.check_privileges(MethodId::<METHOD_ID> as i32) {
+                error!("Permission denied");
+                return;
+            }
+
+            <RECEIVER_BODY*>
+            let mut <ID>: <TYPE> = Default::default();
+            unit_map.read("<ID>", &mut <ID>);
+            </RECEIVER_BODY*>
+            <DELEGATER_RECEIVER_BODY*>
+            let mut <ID> = <TYPE>::new(callback_port);
+            unit_map.read("<ID>", &mut <ID>);
+            </DELEGATER_RECEIVER_BODY*>
+            if let Err(err) = &self.handler.<FN_NAME>(<IDS>) {
+                error!("Error: cause({}), message({}", err.cause, err.message);
+            }
+            </ASYNC?>
+        }
+        </METHODS*>
+    }
+
+    pub struct <IFACE_NAME><'a> {
+        stub: Arc<Mutex<Stub<'a>>>,
+        services: Vec<Box<Service>>,
+    }
+
+    impl<'b> <IFACE_NAME><'b> {
+
+        /// <summary>
+        /// Constructor for this struct
+        /// </summary>
+        pub fn new<T: ServiceHandler>() -> Arc<Mutex<<IFACE_NAME><'b>>> {
+            info!("stub new");
+            let s = Arc::new(Mutex::new(Stub::try_new("<IFACE_NAME>").unwrap()));
+            let ret = Arc::new(Mutex::new(Self {
+                stub: s.clone(),
+                services: vec![],
+            }));
+
+            let mut clone_ret = ret.clone();
+            s.lock().unwrap().on_connected(move |sender, instance| {
+                let mut service = Box::new(Service::new(sender, instance, &T::new));
+                let mut port = clone_ret.lock().unwrap().get_port(&PortType::Callback, instance);
+                service.set_port(port);
+                service.on_create();
+                clone_ret.lock().unwrap().services.push(service);
+            });
+
+            let mut clone_ret = ret.clone();
+            s.lock().unwrap().on_disconnected(move |sender, instance| {
+                let mut services = &mut clone_ret.lock().unwrap().services;
+                services.retain_mut(|svc| {
+                    if svc.get_instance() == instance {
+                        svc.on_terminate();
+                        return true;
+                    }
+
+                    false
+                });
+            });
+
+            let mut clone_ret = ret.clone();
+            s.lock().unwrap().on_received(move |sender, instance, port| -> i32 {
+                let callback_port = Arc::new(clone_ret.lock().unwrap().get_port(&PortType::Callback, instance));
+                let mut services = &mut clone_ret.lock().unwrap().services;
+                for mut svc in services {
+                    if svc.get_instance() == instance {
+                        let mut parcel = Parcel::from(port);
+                        svc.dispatch(port, callback_port, &mut parcel);
+                        return 0;
+                    }
+                }
+
+                error!("Failed to find <IFACE_NAME> context. instance({})", instance);
+                -1
+            });
+
+            <INTERFACE_PRIVILEGES*>
+            s.lock().unwrap().add_privilege("<INTERFACE_PRIVILEGE>");
+            </INTERFACE_PRIVILEGES*>
+            <SET_TRUSTED?>
+            s.lock().unwrap().set_trusted(true);
+            </SET_TRUSTED?>
+            ret
+        }
+
+        <EXTENSION_BLOCK?>
+        pub fn has_pending_request(&self) -> Result<bool, ErrorId> {
+            match self.stub.lock().unwrap().has_pending_request() {
+                Ok(has_request) => { Ok(has_request) }
+                Err(id) => Err(convert_err(id))
+            }
+        }
+        </EXTENSION_BLOCK?>
+
+        /// <summary>
+        /// Gets the port
+        /// </summary>
+        /// <param name="port_type">The port type</param>
+        /// <param name="instance">The instance id of the connection</param>
+        pub fn get_port(&self, port_type: &PortType, instance: &str) -> Port {
+            self.stub.lock().unwrap().get_port(port_type, instance)
+        }
+
+        /// <summary>
+        /// Listens to client apps
+        /// </summary>
+        /// <exception cref="IoError">
+        /// Returns when internal I/O error happen.
+        /// </exception>
+        pub fn listen(&mut self) -> Result<(), ErrorId> {
+            if let Err(error) = self.stub.lock().unwrap().try_listen() {
+                error!("listen error: {:?}", error);
+                return Err(convert_err(error));
+            }
+
+            Ok(())
+        }
+    }
+    </INTERFACE_BLOCK!>
+}
+</MODULES*>
+</MODULE_BLOCK!>
+)__rs_cb";
+
+#endif  // IDLC_RS_GEN_RS_STUB_GEN_CB_H_
index 63ddb8409c0298913882e15ca586ccd0683d3e37..705df61d896436a07e58a234a964c1512d036829 100644 (file)
@@ -56,7 +56,8 @@ int main(int argc, char** argv) {
 
   tidl::Parser ps(options->IsBetaEnabled(), options->IsCion(),
     options->IsMqtt(),
-    options->GetType() == tidl::Options::Type::TYPE_GROUP ? true : false);
+    options->GetType() == tidl::Options::Type::TYPE_GROUP ? true : false,
+    options->GetLanguage() == tidl::Options::LanguageType::LANGUAGE_TYPE_RUST);
   std::string path(options->GetInput());
 
   if (!ps.ParseFromFile(path))
index de4b5d9e7f55f285f1919c345c4154fd6707ba1f..8414b2588b3e7453eb4b3bbad58025f2cd1c7f24 100644 (file)
@@ -32,7 +32,7 @@ Help Options:
   -h, --help                  Show help options
 
 Additional Options:
-  -l, --language=LANGUAGE     Select generating language (C, C++, C#, Java(CION & MQTT only), Dart).
+  -l, --language=LANGUAGE     Select generating language (C, C++, C#, Java(CION & MQTT only), Dart, Rust).
   -i, --input=INPUT           A tidl interface file.
   -o, --output=OUTPUT         The generated interface file.
   -n, --namespace             Add the prefix in the funtion name as output file name (C language only).
@@ -75,6 +75,8 @@ Options::LanguageType ConvertStr2LanguageType(std::string str_type) {
     return Options::LANGUAGE_TYPE_C;
   else if (str_type == "dart")
     return Options::LANGUAGE_TYPE_DART;
+  else if (str_type == "rust")
+    return Options::LANGUAGE_TYPE_RUST;
 
   return Options::LANGUAGE_TYPE_UNKNOWN;
 }
index eb9329bb83c43f71a35d6dd7074c71253099b5bc..4acc0a93d8481330da7ae2f3a1e465aaa26488d1 100644 (file)
@@ -37,7 +37,8 @@ class Options {
     LANGUAGE_TYPE_CPP,
     LANGUAGE_TYPE_CSHARP,
     LANGUAGE_TYPE_JAVA,
-    LANGUAGE_TYPE_DART
+    LANGUAGE_TYPE_DART,
+    LANGUAGE_TYPE_RUST,
   };
 
   Options();
index 0db6d775ecdb744713ec85d969ca8f65dba80aee..dc82e57b893a9eca3950f5c8effca545d14e8529 100644 (file)
@@ -34,6 +34,9 @@
 #include "idlc/gen/version2/cs_group_generator.h"
 #include "idlc/gen/version2/cs_proxy_generator.h"
 #include "idlc/gen/version2/cs_stub_generator.h"
+#include "idlc/gen/version2/rs_group_gen.h"
+#include "idlc/gen/version2/rs_proxy_gen.h"
+#include "idlc/gen/version2/rs_stub_gen.h"
 #include "idlc/options.h"
 
 namespace tidl {
@@ -51,6 +54,9 @@ DefaultGenerator::DefaultGenerator() {
       {std::make_pair(Options::Type::TYPE_STUB, Options::LANGUAGE_TYPE_CSHARP),
        std::bind(&DefaultGenerator::GenCsharpStubCode, this,
                  std::placeholders::_1, std::placeholders::_2)},
+      {std::make_pair(Options::Type::TYPE_STUB, Options::LANGUAGE_TYPE_RUST),
+       std::bind(&DefaultGenerator::GenRustStubCode, this,
+                 std::placeholders::_1, std::placeholders::_2)},
 
       // Proxy
       {std::make_pair(Options::Type::TYPE_PROXY, Options::LANGUAGE_TYPE_C),
@@ -62,6 +68,9 @@ DefaultGenerator::DefaultGenerator() {
       {std::make_pair(Options::Type::TYPE_PROXY, Options::LANGUAGE_TYPE_CSHARP),
        std::bind(&DefaultGenerator::GenCsharpProxyCode, this,
                  std::placeholders::_1, std::placeholders::_2)},
+      {std::make_pair(Options::Type::TYPE_PROXY, Options::LANGUAGE_TYPE_RUST),
+       std::bind(&DefaultGenerator::GenRustProxyCode, this,
+                 std::placeholders::_1, std::placeholders::_2)},
 
       // Group
       {std::make_pair(Options::Type::TYPE_GROUP, Options::LANGUAGE_TYPE_C),
@@ -73,6 +82,9 @@ DefaultGenerator::DefaultGenerator() {
       {std::make_pair(Options::Type::TYPE_GROUP, Options::LANGUAGE_TYPE_CSHARP),
        std::bind(&DefaultGenerator::GenCsharpGroupCode, this,
                  std::placeholders::_1, std::placeholders::_2)},
+      {std::make_pair(Options::Type::TYPE_GROUP, Options::LANGUAGE_TYPE_RUST),
+       std::bind(&DefaultGenerator::GenRustGroupCode, this,
+                 std::placeholders::_1, std::placeholders::_2)},
   };
 }
 
@@ -118,6 +130,14 @@ void DefaultGenerator::GenCsharpStubCode(std::shared_ptr<Options> options,
   stub.Run(options->GetOutput() + ".cs");
 }
 
+void DefaultGenerator::GenRustStubCode(std::shared_ptr<Options> options,
+                                       const Parser& ps) {
+  tidl::version2::RsStubGen stub(ps.GetDoc(), options);
+  stub.EnableProxy(false);
+  stub.SetChannelType(static_cast<Generator::ChannelType>(options->GetType()));
+  stub.Run(options->GetOutput(), true);
+}
+
 void DefaultGenerator::GenCProxyCode(std::shared_ptr<Options> options,
                                         const Parser& ps) {
   CProxyHeaderGenerator proxy_header(ps.GetDoc());
@@ -150,6 +170,14 @@ void DefaultGenerator::GenCsharpProxyCode(std::shared_ptr<Options> options,
   proxy.Run(options->GetOutput() + ".cs");
 }
 
+void DefaultGenerator::GenRustProxyCode(std::shared_ptr<Options> options,
+                                        const Parser &ps) {
+  RsProxyGen proxy(ps.GetDoc());
+  proxy.EnableProxy(true);
+  proxy.SetChannelType(static_cast<Generator::ChannelType>(options->GetType()));
+  proxy.Run(options->GetOutput(), true);
+}
+
 void DefaultGenerator::GenCGroupCode(std::shared_ptr<Options> options,
                                      const Parser& ps) {
   CGroupHeaderGenerator group_header(ps.GetDoc());
@@ -178,5 +206,12 @@ void DefaultGenerator::GenCsharpGroupCode(std::shared_ptr<Options> options,
   group.Run(options->GetOutput() + ".cs");
 }
 
+void DefaultGenerator::GenRustGroupCode(std::shared_ptr<Options> options,
+                                        const Parser &ps) {
+  RsGroupGen group(ps.GetDoc());
+  group.SetChannelType(static_cast<Generator::ChannelType>(options->GetType()));
+  group.Run(options->GetOutput(), true);
+}
+
 }  // namespace version2
 }  // namespace tidl
index 44d5d63dafea9e89679002c7c0607ccdbeb2d0a9..b7b885cbc9eb265eb5a59c02f56eb0bf8100aa0f 100644 (file)
@@ -41,16 +41,19 @@ class DefaultGenerator : public CodeGenerator {
   void GenCStubCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCppStubCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCsharpStubCode(std::shared_ptr<Options> options, const Parser& ps);
+  void GenRustStubCode(std::shared_ptr<Options> options, const Parser& ps);
 
   /// Proxy
   void GenCProxyCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCppProxyCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCsharpProxyCode(std::shared_ptr<Options> options, const Parser& ps);
+  void GenRustProxyCode(std::shared_ptr<Options> options, const Parser& ps);
 
   /// Group
   void GenCGroupCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCppGroupCode(std::shared_ptr<Options> options, const Parser& ps);
   void GenCsharpGroupCode(std::shared_ptr<Options> options, const Parser& ps);
+  void GenRustGroupCode(std::shared_ptr<Options> options, const Parser& ps);
 
  private:
   std::unordered_map<GeneratorCond, GeneratorFunc, PairHash> funcs_;