Revision control

++ // Copyright (C) 2022 The Qt Company Ltd.
++ // Copyright (C) 2019 Alexey Edelev <semlanik@gmail.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
++
++ #include "options.h"
++ #include "utils.h"
++
++ #include <regex>
++ #include <string_view>
++
++ using namespace ::qtprotoccommon;
++ static const char QmlPluginOption[] = "QML";
++ static const char CommentsGenerationOption[] = "COPY_COMMENTS";
++ static const char FolderGenerationOption[] = "GENERATE_PACKAGE_SUBFOLDERS";
++ static const char FieldEnumGenerationOption[] = "FIELD_ENUM";
++ static const char ExtraNamespaceGenerationOption[] = "EXTRA_NAMESPACE";
++ static const char ExportMacroGenerationOption[] = "EXPORT_MACRO";
++ static const char HeaderGuardOption[] = "HEADER_GUARD";
++ static const char MutableGetterConflicts[] = "ALLOW_MUTABLE_GETTER_CONFLICTS";
++ static const char GenerateNonFinalMessagesOption[] = "GENERATE_NON_FINAL_MESSAGES";
++ static const char GenerateNonFinalPropertiesOption[] = "GENERATE_NON_FINAL_PROPERTIES";
++
++ static const char ExportSuffix[] = "_exports.qpb.h";
++
++ static constexpr std::string_view HeaderGuardPragma = "pragma";
++ static constexpr std::string_view HeaderGuardProtoFilename = "filename";
++
++ Options::Options()
++ : m_generateComments(false), m_isFolder(false), m_generateFieldEnum(true),
++ m_generateMacroExportFile(false), m_qml(false), m_mutableGetterConflicts(false)
++ {
++ }
++
++ Options::~Options() = default;
++
++ Options &Options::mutableInstance()
++ {
++ static Options _instance;
++ return _instance;
++ }
++
++ const Options &Options::instance()
++ {
++ return mutableInstance();
++ }
++
++ std::string extractCompositeOptionValue(const std::string &option)
++ {
++ std::vector<std::string> compositeOption = utils::split(option, "=");
++ if (compositeOption.size() != 2)
++ return {};
++ const std::string &optionValue = compositeOption.back();
++ if (utils::startsWith(optionValue, '\"') && utils::endsWith(optionValue, '\"')) {
++ return optionValue.substr(1, optionValue.size() - 2);
++ }
++ return optionValue;
++ }
++
++ void Options::setFromString(const std::string &options, GeneratorType /*unused*/,
++ std::string *error)
++ {
++ Options &instance = mutableInstance();
++ for (const auto &option : utils::split(options, ";")) {
++ QT_PROTOBUF_DEBUG("option: " << option);
++ if (option == CommentsGenerationOption) {
++ QT_PROTOBUF_DEBUG("set m_generateComments: true");
++ instance.m_generateComments = true;
++ } else if (option == FolderGenerationOption) {
++ QT_PROTOBUF_DEBUG("set m_isFolder: true");
++ instance.m_isFolder = true;
++ } else if (option == FieldEnumGenerationOption) {
++ // TODO: Opt in this option by default since it's required for
++ // Oneof fields.
++ // QT_PROTOBUF_DEBUG("set m_generateFieldEnum: true");
++ // instance.m_generateFieldEnum = true;
++ } else if (option.find(ExtraNamespaceGenerationOption) == 0) {
++ instance.m_extraNamespace = extractCompositeOptionValue(option);
++ QT_PROTOBUF_DEBUG("set m_extraNamespace: " << instance.m_extraNamespace);
++ } else if (option.find(ExportMacroGenerationOption) == 0) {
++ auto export_macro_values = utils::split(extractCompositeOptionValue(option), ":");
++ if (!export_macro_values.empty()) {
++ static const std::regex valid_c_identifier("[a-zA-Z_][0-9a-zA-Z_]*");
++ if (!std::regex_match(export_macro_values[0], valid_c_identifier)) {
++ *error = "EXPORT_MACRO '" + export_macro_values[0]
++ + "' is not a valid C identifier.";
++ return;
++ }
++ instance.m_exportMacro = export_macro_values[0];
++ QT_PROTOBUF_DEBUG("set m_exportMacro: " << instance.m_exportMacro);
++ if (export_macro_values.size() > 1) {
++ instance.m_exportMacroFilename = export_macro_values[1];
++ QT_PROTOBUF_DEBUG("set m_exportMacroFilename: "
++ << instance.m_exportMacroFilename);
++ if (export_macro_values.size() > 2) {
++ instance.m_generateMacroExportFile = export_macro_values[2] == "true";
++ QT_PROTOBUF_DEBUG("set m_generateMacroExportFile: "
++ << instance.m_generateMacroExportFile);
++ }
++ }
++ if (instance.m_exportMacroFilename.empty()) {
++ std::string exportMacroLower = instance.m_exportMacro;
++ utils::asciiToLower(exportMacroLower);
++ instance.m_exportMacroFilename = exportMacroLower + ExportSuffix;
++ QT_PROTOBUF_DEBUG("set m_exportMacroFilename: "
++ << instance.m_exportMacroFilename);
++ }
++ }
++ } else if (option == QmlPluginOption) {
++ instance.m_qml = true;
++ QT_PROTOBUF_DEBUG("set m_qml: true");
++ } else if (option.find(HeaderGuardOption) == 0) {
++ const auto headerGuardValue = extractCompositeOptionValue(option);
++ if (headerGuardValue == HeaderGuardPragma) {
++ instance.m_headerGuard = Options::HeaderGuardType::Pragma;
++ } else if (headerGuardValue != HeaderGuardProtoFilename) {
++ QT_PROTOBUF_DEBUG("Unknown HEADER_GUARD option value");
++ }
++ } else if (option == MutableGetterConflicts) {
++ instance.m_mutableGetterConflicts = true;
++ } else if (option == GenerateNonFinalMessagesOption) {
++ instance.m_messageGenerationType = Options::FinalPolicy::NonFinal;
++ } else if (option == GenerateNonFinalPropertiesOption) {
++ instance.m_propertyGenerationType = Options::FinalPolicy::NonFinal;
++ }
++ }
++ }