#include <gio/gio.h>
#include <glib.h>
#include <fstream>
#include <iostream>
#include <ostream>
#include <string>
#include <string_view>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/string_util.h"
// --output=remoting/host/linux/dbus_interfaces/com_example_Interface.h
std::string HeaderGuard(const base::FilePath& header) {
std::string result = base::ToUpperASCII(header.MaybeAsASCII());
base::ReplaceChars(result, "/.", "_", &result);
return result + "_";
}
std::string Namespace(std::string_view interface) {
std::string result;
base::ReplaceChars(interface, ".", "_", &result);
return result;
}
void WriteParameters(std::ostream& output, GDBusArgInfo** args) {
if (args == nullptr || *args == nullptr) {
output << " \"()\"" << std::endl;
return;
}
output << " \"(\"" << std::endl;
for (GDBusArgInfo** arg = args; *arg != nullptr; UNSAFE_TODO(++arg)) {
output << " \"" << (**arg).signature << "\" // " << (**arg).name
<< std::endl;
}
output << " \")\"" << std::endl;
}
int main(int argc, char* argv[]) {
base::CommandLine::Init(argc, argv);
base::FilePath input_path =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("input");
base::FilePath output_path =
base::CommandLine::ForCurrentProcess()->GetSwitchValuePath("output");
if (input_path.empty() || output_path.empty()) {
std::cerr << "Usage: gen_dbus_interface --input=com.example.Interface.xml "
"--output=remoting/host/linux/dbus_interfaces/"
"com_example_Interface.h"
<< std::endl;
return 1;
}
std::string header_guard = HeaderGuard(output_path);
if (header_guard.empty()) {
std::cerr << "Output name is not ASCII" << std::endl;
return 1;
}
std::string xml;
if (!base::ReadFileToString(input_path, &xml)) {
std::cerr << "Failed to read input file.";
}
GError* error = nullptr;
GDBusNodeInfo* node = g_dbus_node_info_new_for_xml(xml.c_str(), &error);
if (error) {
std::cerr << "Error parsing xml: " << error->message << std::endl;
g_error_free(error);
return 1;
}
std::ofstream output(output_path.value());
if (!output) {
std::cerr << "Failed to open output file for writing." << std::endl;
g_dbus_node_info_unref(node);
return 1;
}
output << "// This file was generated from " << input_path.BaseName().value()
<< std::endl
<< std::endl;
output << "#ifndef " << header_guard << std::endl;
output << "#define " << header_guard << std::endl << std::endl;
output << "#include \"remoting/host/linux/gvariant_type.h\"" << std::endl
<< std::endl;
for (GDBusInterfaceInfo** interface = node->interfaces;
interface != nullptr && *interface != nullptr;
UNSAFE_TODO(++interface)) {
std::string namespace_name = Namespace((**interface).name);
output << "namespace remoting::" << namespace_name << " {" << std::endl
<< std::endl;
for (GDBusMethodInfo** method = (**interface).methods;
method != nullptr && *method != nullptr; UNSAFE_TODO(++method)) {
output << "// method" << std::endl;
output << "struct " << (**method).name << " {" << std::endl;
output << " static constexpr char kInterfaceName[] = \""
<< (**interface).name << "\";" << std::endl;
output << " static constexpr char kMethodName[] = \"" << (**method).name
<< "\";" << std::endl;
output << " static constexpr gvariant::Type kInType{" << std::endl;
WriteParameters(output, (**method).in_args);
output << " };" << std::endl;
output << " static constexpr gvariant::Type kOutType{" << std::endl;
WriteParameters(output, (**method).out_args);
output << " };" << std::endl;
output << "};" << std::endl << std::endl;
}
for (GDBusPropertyInfo** property = (**interface).properties;
property != nullptr && *property != nullptr; UNSAFE_TODO(++property)) {
output << "// property" << std::endl;
output << "struct " << (**property).name << " {" << std::endl;
output << " static constexpr char kInterfaceName[] = \""
<< (**interface).name << "\";" << std::endl;
output << " static constexpr char kPropertyName[] = \""
<< (**property).name << "\";" << std::endl;
output << " static constexpr gvariant::Type kType{\""
<< (**property).signature << "\"};" << std::endl;
output << " static constexpr bool kReadable = "
<< ((**property).flags & G_DBUS_PROPERTY_INFO_FLAGS_READABLE
? "true;"
: "false;")
<< std::endl;
output << " static constexpr bool kWritable = "
<< ((**property).flags & G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE
? "true;"
: "false;")
<< std::endl;
output << "};" << std::endl << std::endl;
}
for (GDBusSignalInfo** signal = (**interface).signals;
signal != nullptr && *signal != nullptr; UNSAFE_TODO(++signal)) {
output << "// signal" << std::endl;
output << "struct " << (**signal).name << " {" << std::endl;
output << " static constexpr char kInterfaceName[] = \""
<< (**interface).name << "\";" << std::endl;
output << " static constexpr char kSignalName[] = \"" << (**signal).name
<< "\";" << std::endl;
output << " static constexpr gvariant::Type kType{" << std::endl;
WriteParameters(output, (**signal).args);
output << " };" << std::endl;
output << "};" << std::endl << std::endl;
}
output << "} // namespace remoting::" << namespace_name << std::endl
<< std::endl;
}
output << "#endif // " << header_guard << std::endl;
g_dbus_node_info_unref(node);
}