// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2023 // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // #pragma once #include "td/tl/tl_writer.h" #include <cassert> #include <cstdint> #include <sstream> #include <string> #include <vector> namespace td { namespace tl { class TlWriterDotNet final : public TL_writer { public: bool is_header_; std::string prefix_; TlWriterDotNet(const std::string &name, bool is_header, const std::string &prefix = "") : TL_writer(name), is_header_(is_header), prefix_(prefix) { } int get_max_arity(void) const final { return 0; } bool is_built_in_simple_type(const std::string &name) const final { return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" || name == "String" || name == "Bytes"; } bool is_built_in_complex_type(const std::string &name) const final { return name == "Vector"; } bool is_type_bare(const tl_type *t) const final { return t->simple_constructors <= 1 || (is_built_in_simple_type(t->name) && t->name != "Bool") || is_built_in_complex_type(t->name); } std::vector<std::string> get_parsers(void) const final { return {"FromUnmanaged"}; } int get_parser_type(const tl_combinator *t, const std::string &name) const final { return 0; } Mode get_parser_mode(int type) const final { return All; // Server; } std::vector<std::string> get_storers(void) const final { return {"ToUnmanaged", "ToString"}; } std::vector<std::string> get_additional_functions(void) const final { return {"ToUnmanaged", "FromUnmanaged"}; } int get_storer_type(const tl_combinator *t, const std::string &name) const final { return name == "ToString"; } Mode get_storer_mode(int type) const final { return type <= 1 ? All : Server; } std::string gen_base_tl_class_name(void) const final { return "BaseObject"; } std::string gen_base_type_class_name(int arity) const final { assert(arity == 0); return "Object"; } std::string gen_base_function_class_name(void) const final { return "Function"; } static std::string to_camelCase(const std::string &name) { return to_cCamelCase(name, false); } static std::string to_CamelCase(const std::string &name) { return to_cCamelCase(name, true); } static std::string to_cCamelCase(const std::string &name, bool flag) { bool next_to_upper = flag; std::string result; for (std::size_t i = 0; i < name.size(); i++) { if (!is_alnum(name[i])) { next_to_upper = true; continue; } if (next_to_upper) { result += to_upper(name[i]); next_to_upper = false; } else { result += name[i]; } } return result; } std::string gen_native_field_name(std::string name) const { for (std::size_t i = 0; i < name.size(); i++) { if (!is_alnum(name[i])) { name[i] = '_'; } } assert(name.size() > 0); assert(name[name.size() - 1] != '_'); return name + "_"; } std::string gen_native_class_name(std::string name) const { if (name == "Object") { assert(0); } if (name == "#") { return "int32_t"; } for (std::size_t i = 0; i < name.size(); i++) { if (!is_alnum(name[i])) { name[i] = '_'; } } return name; } std::string gen_class_name(std::string name) const final { if (name == "Object" || name == "#") { assert(0); } return to_CamelCase(name); } std::string gen_field_name(std::string name) const final { assert(name.size() > 0); assert(is_alnum(name.back())); return to_CamelCase(name); } std::string gen_type_name(const tl_tree_type *tree_type) const final { const tl_type *t = tree_type->type; const std::string &name = t->name; if (name == "#") { assert(0); } if (name == "Bool") { return "bool"; } if (name == "Int32") { return "int32"; } if (name == "Int53" || name == "Int64") { return "int64"; } if (name == "Double") { return "float64"; } if (name == "String") { return "String^"; } if (name == "Bytes") { return "Array<BYTE>^"; } if (name == "Vector") { assert(t->arity == 1); assert(tree_type->children.size() == 1); assert(tree_type->children[0]->get_type() == NODE_TYPE_TYPE); const tl_tree_type *child = static_cast<const tl_tree_type *>(tree_type->children[0]); return "Array<" + gen_type_name(child) + ">^"; } assert(!is_built_in_simple_type(name) && !is_built_in_complex_type(name)); for (std::size_t i = 0; i < tree_type->children.size(); i++) { assert(tree_type->children[i]->get_type() == NODE_TYPE_NAT_CONST); } return gen_main_class_name(t) + "^"; } std::string gen_output_begin(void) const final { return prefix_ + "#include \"td/tl/tl_dotnet_object.h\"\n\n" "namespace Telegram {\n" "namespace Td {\n" "namespace Api {\n"; } std::string gen_output_end() const final { return "}\n" "}\n" "}\n"; } std::string gen_forward_class_declaration(const std::string &class_name, bool is_proxy) const final { if (!is_header_) { return ""; } std::stringstream ss; ss << (is_proxy ? "interface" : "ref") << " class " << class_name << ";\n"; return ss.str(); } std::string gen_class_begin(const std::string &class_name, const std::string &base_class_name, bool is_proxy, const tl::tl_tree *result) const final { if (!is_header_) { return ""; } std::stringstream ss; ss << "\npublic " << (is_proxy ? "interface" : "ref") << " class " << class_name << (is_proxy ? "" : " sealed") << (class_name == gen_base_tl_class_name() ? "" : " : " + base_class_name) << " {\n" << " public:\n"; return ss.str(); } std::string gen_class_end(void) const final { return ""; } std::string gen_field_definition(const std::string &class_name, const std::string &type_name, const std::string &field_name) const final { if (!is_header_) { return ""; } auto fixed_field_name = field_name; if (field_name == class_name) { fixed_field_name += "Value"; } if (type_name.substr(0, field_name.size()) == field_name) { auto fixed_type_name = "::Telegram::Td::Api::" + type_name; std::stringstream ss; ss << "private:\n"; ss << " " << fixed_type_name << " " << fixed_field_name << "PrivateField;\n"; ss << "public:\n"; ss << " property " << fixed_type_name << " " << fixed_field_name << " {\n"; ss << " " << fixed_type_name << " get() {\n"; ss << " return " << fixed_field_name << "PrivateField;\n"; ss << " }\n"; ss << " void set(" << fixed_type_name << " newValue) {\n"; ss << " " << fixed_field_name << "PrivateField = newValue;\n"; ss << " }\n"; ss << " }\n"; return ss.str(); } return " property " + type_name + " " + fixed_field_name + ";\n"; } std::string gen_store_function_begin(const std::string &storer_name, const std::string &class_name, int arity, std::vector<var_description> &vars, int storer_type) const final { if (storer_type < 0) { return ""; } std::stringstream ss; ss << "\n"; if (storer_type) { ss << (is_header_ ? " virtual " : "") << "String^ " << (is_header_ ? "" : gen_class_name(class_name) + "::") << "ToString()" << (is_header_ ? " override;" : " {\n return ::Telegram::Td::Api::ToString(this);\n}") << "\n"; } else { ss << (is_header_ ? " virtual " : "") << "NativeObject^ " << (is_header_ ? "" : gen_class_name(class_name) + "::") << "ToUnmanaged()"; if (is_header_) { ss << ";\n"; } else { ss << " {\n return REF_NEW NativeObject(::Telegram::Td::Api::ToUnmanaged(this).release());\n}\n"; } } return ss.str(); } std::string gen_store_function_end(const std::vector<var_description> &vars, int storer_type) const final { return ""; } std::string gen_constructor_begin(int field_count, const std::string &class_name, bool is_default) const final { std::stringstream ss; ss << "\n"; ss << (is_header_ ? " " : gen_class_name(class_name) + "::") << gen_class_name(class_name) << "("; return ss.str(); } std::string gen_constructor_parameter(int field_num, const std::string &class_name, const arg &a, bool is_default) const final { if (is_default) { return ""; } std::stringstream ss; ss << (field_num == 0 ? "" : ", "); auto field_type = gen_field_type(a); auto pos = 0; while (field_type.substr(pos, 6) == "Array<") { pos += 6; } if (field_type.substr(pos, 4) != "BYTE" && field_type.substr(pos, 6) != "String" && to_upper(field_type[pos]) == field_type[pos]) { field_type = field_type.substr(0, pos) + "::Telegram::Td::Api::" + field_type.substr(pos); } ss << field_type << " " << to_camelCase(a.name); return ss.str(); } std::string gen_constructor_field_init(int field_num, const std::string &class_name, const arg &a, bool is_default) const final { if (is_default || is_header_) { return ""; } std::stringstream ss; if (field_num == 0) { ss << ") {\n"; } auto field_name = gen_field_name(a.name); if (field_name == class_name) { field_name += "Value"; } ss << " " << field_name << " = " << to_camelCase(a.name) << ";\n"; return ss.str(); } std::string gen_constructor_end(const tl_combinator *t, int field_count, bool is_default) const final { if (is_header_) { return ");\n"; } std::stringstream ss; if (field_count == 0) { ss << ") {\n"; } ss << "}\n"; return ss.str(); } std::string gen_additional_function(const std::string &function_name, const tl_combinator *t, bool is_function) const final { std::stringstream ss; if (is_header_ && function_name == "ToUnmanaged") { ss << "};\n"; } ss << "\n"; if (function_name == "ToUnmanaged") { gen_to_unmanaged(ss, t); } else { gen_from_unmanaged(ss, t); } return ss.str(); } void gen_to_unmanaged(std::stringstream &ss, const tl_combinator *t) const { auto native_class_name = gen_native_class_name(t->name); auto class_name = gen_class_name(t->name); ss << "td::td_api::object_ptr<td::td_api::" << native_class_name << "> ToUnmanaged(" << class_name << "^ from)"; if (is_header_) { ss << ";\n"; return; } ss << " {\n" << " if (!from) {\n" << " return nullptr;\n" << " }\n" << " return td::td_api::make_object<td::td_api::" << native_class_name << ">("; bool is_first = true; for (auto &it : t->args) { if (is_first) { is_first = false; } else { ss << ", "; } auto field_name = gen_field_name(it.name); if (field_name == class_name) { field_name += "Value"; } ss << "ToUnmanaged(from->" << field_name << ")"; } ss << ");\n}\n"; } void gen_from_unmanaged(std::stringstream &ss, const tl_combinator *t) const { auto native_class_name = gen_native_class_name(t->name); auto class_name = gen_class_name(t->name); ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << " &from)"; if (is_header_) { ss << ";\n"; return; } ss << " {\n" << " return REF_NEW " << class_name << "("; bool is_first = true; for (auto &it : t->args) { if (is_first) { is_first = false; } else { ss << ", "; } bool need_bytes = gen_field_type(it) == "Array<BYTE>^" || gen_field_type(it) == "Array<Array<BYTE>^>^"; ss << (need_bytes ? "Bytes" : "") << "FromUnmanaged(from." << gen_native_field_name(it.name) << ")"; } ss << ");\n}\n"; } std::string gen_array_type_name(const tl_tree_array *arr, const std::string &field_name) const final { assert(0); return std::string(); } std::string gen_var_type_name(void) const final { assert(0); return std::string(); } std::string gen_int_const(const tl_tree *tree_c, const std::vector<var_description> &vars) const final { assert(0); return std::string(); } std::string gen_var_name(const var_description &desc) const final { assert(0); return ""; } std::string gen_parameter_name(int index) const final { assert(0); return ""; } std::string gen_class_alias(const std::string &class_name, const std::string &alias_name) const final { return ""; } std::string gen_vars(const tl_combinator *t, const tl_tree_type *result_type, std::vector<var_description> &vars) const final { assert(vars.empty()); return ""; } std::string gen_function_vars(const tl_combinator *t, std::vector<var_description> &vars) const final { assert(vars.empty()); return ""; } std::string gen_uni(const tl_tree_type *result_type, std::vector<var_description> &vars, bool check_negative) const final { assert(result_type->children.empty()); return ""; } std::string gen_constructor_id_store(std::int32_t id, int storer_type) const final { return ""; } std::string gen_field_fetch(int field_num, const arg &a, std::vector<var_description> &vars, bool flat, int parser_type) const final { return ""; // std::stringstream ss; // ss << gen_field_name(a.name) << " = from_unmanaged(from->" << // gen_native_field_name(a.name) << ");\n"; return ss.str(); } std::string gen_field_store(const arg &a, std::vector<var_description> &vars, bool flat, int storer_type) const final { return ""; // std::stringstream ss; // ss << "to_unmanaged(" << gen_field_name(a.name) << ")"; // return ss.str(); } std::string gen_type_fetch(const std::string &field_name, const tl_tree_type *tree_type, const std::vector<var_description> &vars, int parser_type) const final { assert(vars.empty()); return ""; } std::string gen_type_store(const std::string &field_name, const tl_tree_type *tree_type, const std::vector<var_description> &vars, int storer_type) const final { return ""; } std::string gen_var_type_fetch(const arg &a) const final { assert(0); return ""; } std::string gen_get_id(const std::string &class_name, std::int32_t id, bool is_proxy) const final { return ""; } std::string gen_function_result_type(const tl_tree *result) const final { return ""; } std::string gen_fetch_function_begin(const std::string &parser_name, const std::string &class_name, const std::string &parent_class_name, int arity, int field_count, std::vector<var_description> &vars, int parser_type) const final { return ""; } std::string gen_fetch_function_end(bool has_parent, int field_count, const std::vector<var_description> &vars, int parser_type) const final { return ""; } std::string gen_fetch_function_result_begin(const std::string &parser_name, const std::string &class_name, const tl_tree *result) const final { return ""; } std::string gen_fetch_function_result_end(void) const final { return ""; } std::string gen_fetch_function_result_any_begin(const std::string &parser_name, const std::string &class_name, bool is_proxy) const final { return ""; } std::string gen_fetch_function_result_any_end(bool is_proxy) const final { return ""; } std::string gen_fetch_switch_begin(void) const final { return ""; } std::string gen_fetch_switch_case(const tl_combinator *t, int arity) const final { return ""; } std::string gen_fetch_switch_end(void) const final { return ""; } std::string gen_additional_proxy_function_begin(const std::string &function_name, const tl_type *type, const std::string &name, int arity, bool is_function) const final { std::stringstream ss; if (is_header_ && function_name == "ToUnmanaged") { ss << "};\n"; } if (type == nullptr) { return ss.str(); } auto native_class_name = gen_native_class_name(type->name); auto class_name = gen_class_name(type->name); if (function_name == "ToUnmanaged") { ss << "td::td_api::object_ptr<td::td_api::" << native_class_name << "> ToUnmanaged(" << class_name << "^ from)"; if (is_header_) { ss << ";\n"; return ss.str(); } ss << " {\n" << " if (!from) {\n" << " return nullptr;\n" << " }\n" << " return td::td_api::move_object_as<td::td_api::" << native_class_name << ">(from->ToUnmanaged()->get_object_ptr());\n}\n"; } else { ss << class_name << "^ FromUnmanaged(td::td_api::" << native_class_name << " &from)"; if (is_header_) { ss << ";\n"; return ss.str(); } ss << " {\n"; ss << " return DoFromUnmanaged<" << class_name << "^>(from);\n"; ss << "}\n"; } return ss.str(); } std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type, const std::string &class_name, int arity) const final { return ""; } std::string gen_additional_proxy_function_case(const std::string &function_name, const tl_type *type, const tl_combinator *t, int arity, bool is_function) const final { return ""; } std::string gen_additional_proxy_function_end(const std::string &function_name, const tl_type *type, bool is_function) const final { return ""; } }; } // namespace tl } // namespace td