// // Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2022 // // 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) // #include "tl_json_converter.h" #include "td/tl/tl_simple.h" #include "td/utils/buffer.h" #include "td/utils/common.h" #include "td/utils/filesystem.h" #include "td/utils/Slice.h" #include "td/utils/SliceBuilder.h" #include "td/utils/StringBuilder.h" #include namespace td { using Mode = tl::TL_writer::Mode; template void gen_to_json_constructor(StringBuilder &sb, const T *constructor, bool is_header) { sb << "void to_json(JsonValueScope &jv, " << "const td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &object)"; if (is_header) { sb << ";\n\n"; return; } sb << " {\n"; sb << " auto jo = jv.enter_object();\n"; sb << " jo(\"@type\", \"" << tl::simple::gen_cpp_name(constructor->name) << "\");\n"; for (auto &arg : constructor->args) { auto field_name = tl::simple::gen_cpp_field_name(arg.name); bool is_custom = arg.type->type == tl::simple::Type::Custom; auto object = PSTRING() << "object." << field_name; if (is_custom) { sb << " if (" << object << ") {\n "; } if (arg.type->type == tl::simple::Type::Bytes) { object = PSTRING() << "base64_encode(" << object << ")"; } else if (arg.type->type == tl::simple::Type::Bool) { object = PSTRING() << "JsonBool{" << object << "}"; } else if (arg.type->type == tl::simple::Type::Int64) { object = PSTRING() << "JsonInt64{" << object << "}"; } else if (arg.type->type == tl::simple::Type::Vector && arg.type->vector_value_type->type == tl::simple::Type::Int64) { object = PSTRING() << "JsonVectorInt64{" << object << "}"; } if (is_custom) { sb << " jo(\"" << arg.name << "\", ToJson(*" << object << "));\n"; } else if (arg.type->type == tl::simple::Type::Int64 || arg.type->type == tl::simple::Type::Vector) { sb << " jo(\"" << arg.name << "\", ToJson(" << object << "));\n"; } else { sb << " jo(\"" << arg.name << "\", " << object << ");\n"; } if (is_custom) { sb << " }\n"; } } sb << "}\n\n"; } void gen_to_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header, Mode mode) { for (auto *custom_type : schema.custom_types) { if (!((custom_type->is_query_ && mode != Mode::Server) || (custom_type->is_result_ && mode != Mode::Client))) { continue; } if (custom_type->constructors.size() > 1) { auto type_name = tl::simple::gen_cpp_name(custom_type->name); sb << "void to_json(JsonValueScope &jv, const td_api::" << type_name << " &object)"; if (is_header) { sb << ";\n\n"; } else { sb << " {\n" << " td_api::downcast_call(const_cast(object), [&jv](const auto &object) { " "to_json(jv, object); });\n" << "}\n\n"; } } for (auto *constructor : custom_type->constructors) { gen_to_json_constructor(sb, constructor, is_header); } } if (mode == Mode::Server) { return; } for (auto *function : schema.functions) { gen_to_json_constructor(sb, function, is_header); } } template void gen_from_json_constructor(StringBuilder &sb, const T *constructor, bool is_header) { sb << "Status from_json(td_api::" << tl::simple::gen_cpp_name(constructor->name) << " &to, JsonObject &from)"; if (is_header) { sb << ";\n\n"; } else { sb << " {\n"; for (auto &arg : constructor->args) { sb << " TRY_STATUS(from_json" << (arg.type->type == tl::simple::Type::Bytes ? "_bytes" : "") << "(to." << tl::simple::gen_cpp_field_name(arg.name) << ", get_json_object_field_force(from, \"" << tl::simple::gen_cpp_name(arg.name) << "\")));\n"; } sb << " return Status::OK();\n"; sb << "}\n\n"; } } void gen_from_json(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header, Mode mode) { for (auto *custom_type : schema.custom_types) { if (!((custom_type->is_query_ && mode != Mode::Client) || (custom_type->is_result_ && mode != Mode::Server))) { continue; } for (auto *constructor : custom_type->constructors) { gen_from_json_constructor(sb, constructor, is_header); } } if (mode == Mode::Client) { return; } for (auto *function : schema.functions) { gen_from_json_constructor(sb, function, is_header); } } using Vec = std::vector>; void gen_tl_constructor_from_string(StringBuilder &sb, Slice name, const Vec &vec, bool is_header) { sb << "Result tl_constructor_from_string(td_api::" << name << " *object, const std::string &str)"; if (is_header) { sb << ";\n\n"; return; } sb << " {\n"; sb << " static const std::unordered_map m = {\n"; bool is_first = true; for (auto &p : vec) { if (is_first) { is_first = false; } else { sb << ",\n"; } sb << " {\"" << p.second << "\", " << p.first << "}"; } sb << "\n };\n"; sb << " auto it = m.find(str);\n"; sb << " if (it == m.end()) {\n" << " return Status::Error(PSLICE() << \"Unknown class \\\"\" << str << \"\\\"\");\n" << " }\n" << " return it->second;\n"; sb << "}\n\n"; } void gen_tl_constructor_from_string(StringBuilder &sb, const tl::simple::Schema &schema, bool is_header, Mode mode) { Vec vec_for_nullary; for (auto *custom_type : schema.custom_types) { if (!((custom_type->is_query_ && mode != Mode::Client) || (custom_type->is_result_ && mode != Mode::Server))) { continue; } Vec vec; for (auto *constructor : custom_type->constructors) { vec.emplace_back(constructor->id, constructor->name); vec_for_nullary.push_back(vec.back()); } if (vec.size() > 1) { gen_tl_constructor_from_string(sb, tl::simple::gen_cpp_name(custom_type->name), vec, is_header); } } gen_tl_constructor_from_string(sb, "Object", vec_for_nullary, is_header); if (mode == Mode::Client) { return; } Vec vec_for_function; for (auto *function : schema.functions) { vec_for_function.emplace_back(function->id, function->name); } gen_tl_constructor_from_string(sb, "Function", vec_for_function, is_header); } void gen_json_converter_file(const tl::simple::Schema &schema, const std::string &file_name_base, bool is_header, Mode mode) { auto file_name = is_header ? file_name_base + ".h" : file_name_base + ".cpp"; file_name = "auto/" + file_name; auto old_file_content = [&] { auto r_content = read_file(file_name); if (r_content.is_error()) { return BufferSlice(); } return r_content.move_as_ok(); }(); std::string buf(2000000, ' '); StringBuilder sb(buf); if (is_header) { sb << "#pragma once\n\n"; sb << "#include \"td/telegram/td_api.h\"\n\n"; sb << "#include \"td/utils/JsonBuilder.h\"\n"; sb << "#include \"td/utils/Status.h\"\n\n"; } else { sb << "#include \"" << file_name_base << ".h\"\n\n"; sb << "#include \"td/telegram/td_api.h\"\n"; sb << "#include \"td/telegram/td_api.hpp\"\n\n"; sb << "#include \"td/tl/tl_json.h\"\n\n"; sb << "#include \"td/utils/base64.h\"\n"; sb << "#include \"td/utils/common.h\"\n"; sb << "#include \"td/utils/Slice.h\"\n\n"; sb << "#include \n"; sb << "#include \n\n"; } sb << "namespace td {\n"; sb << "namespace td_api {\n"; if (is_header) { sb << "\nvoid to_json(JsonValueScope &jv, const tl_object_ptr &value);\n"; sb << "\nStatus from_json(tl_object_ptr &to, td::JsonValue from);\n"; sb << "\nvoid to_json(JsonValueScope &jv, const Object &object);\n"; sb << "\nvoid to_json(JsonValueScope &jv, const Function &object);\n\n"; } else { sb << R"ABCD( void to_json(JsonValueScope &jv, const tl_object_ptr &value) { td::to_json(jv, std::move(value)); } Status from_json(tl_object_ptr &to, td::JsonValue from) { return td::from_json(to, std::move(from)); } template auto lazy_to_json(JsonValueScope &jv, const T &t) -> decltype(td_api::to_json(jv, t)) { return td_api::to_json(jv, t); } template void lazy_to_json(std::reference_wrapper, const T &t) { UNREACHABLE(); } void to_json(JsonValueScope &jv, const Object &object) { downcast_call(const_cast(object), [&jv](const auto &object) { lazy_to_json(jv, object); }); } void to_json(JsonValueScope &jv, const Function &object) { downcast_call(const_cast(object), [&jv](const auto &object) { lazy_to_json(jv, object); }); } )ABCD"; } gen_tl_constructor_from_string(sb, schema, is_header, mode); gen_from_json(sb, schema, is_header, mode); gen_to_json(sb, schema, is_header, mode); sb << "} // namespace td_api\n"; sb << "} // namespace td\n"; CHECK(!sb.is_error()); buf.resize(sb.as_cslice().size()); #if TD_WINDOWS string new_file_content; for (auto c : buf) { if (c == '\n') { new_file_content += '\r'; } new_file_content += c; } #else auto new_file_content = std::move(buf); #endif if (new_file_content != old_file_content.as_slice()) { write_file(file_name, new_file_content).ensure(); } } void gen_json_converter(const tl::tl_config &config, const std::string &file_name, Mode mode) { tl::simple::Schema schema(config); gen_json_converter_file(schema, file_name, true, mode); gen_json_converter_file(schema, file_name, false, mode); } } // namespace td