text: // Copyright (c) 2011-present, Facebook, Inc.  All rights reserved.
  //  This source code is licensed under both the GPLv2 (found in the
  //  COPYING file in the root directory) and Apache 2.0 License
  //  (found in the LICENSE.Apache file in the root directory).
  
  #pragma once
  #include <functional>
  #include <memory>
  #include <unordered_map>
  
  #include "options/configurable_helper.h"
  #include "rocksdb/convenience.h"
  #include "rocksdb/customizable.h"
  #include "rocksdb/status.h"
  #include "rocksdb/utilities/object_registry.h"
  
  namespace ROCKSDB_NAMESPACE {
  template <typename T>
  using SharedFactoryFunc =
      std::function<bool(const std::string&, std::shared_ptr<T>*)>;
  
  template <typename T>
  using UniqueFactoryFunc =
      std::function<bool(const std::string&, std::unique_ptr<T>*)>;
  
  template <typename T>
  using StaticFactoryFunc = std::function<bool(const std::string&, T**)>;
  
  // Creates a new shared Customizable object based on the input parameters.
  // This method parses the input value to determine the type of instance to
  // create. If there is an existing instance (in result) and it is the same type
  // as the object being created, the existing configuration is stored and used as
  // the default for the new object.
  //
  // The value parameter specified the instance class of the object to create.
  // If it is a simple string (e.g. BlockBasedTable), then the instance will be
  // created using the default settings.  If the value is a set of name-value
  // pairs, then the "id" value is used to determine the instance to create and
  // the remaining parameters are used to configure the object.  Id name-value
  // pairs are specified, there must be an "id=value" pairing or an error will
  // result.
  //
  // The config_options parameter controls the process and how errors are
  // returned. If ignore_unknown_options=true, unknown values are ignored during
  // the configuration If ignore_unsupported_options=true, unknown instance types
  // are ignored If invoke_prepare_options=true, the resulting instance wll be
  // initialized (via PrepareOptions
  //
  // @param config_options Controls how the instance is created and errors are
  // handled
  // @param value Either the simple name of the instance to create, or a set of
  // name-value pairs to
  //              create and initailzie the object
  // @param func  Optional function to call to attempt to create an instance
  // @param result The newly created instance.
  template <typename T>
  static Status LoadSharedObject(const ConfigOptions& config_options,
                                 const std::string& value,
                                 const SharedFactoryFunc<T>& func,
                                 std::shared_ptr<T>* result) {
    std::string id;
    std::unordered_map<std::string, std::string> opt_map;
    Status status =
        ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map);
    if (!status.ok()) {  // GetOptionsMap failed
      return status;
    }
    std::string curr_opts;
  #ifndef ROCKSDB_LITE
    if (result->get() != nullptr && result->get()->GetId() == id) {
      // Try to get the existing options, ignoring any errors
      ConfigOptions embedded = config_options;
      embedded.delimiter = ";";
      result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
    }
  #endif
    if (func == nullptr || !func(id, result)) {  // No factory, or it failed
      if (value.empty()) {
        // No Id and no options.  Clear the object
        result->reset();
        return Status::OK();
      } else if (id.empty()) {  // We have no Id but have options.  Not good
        return Status::NotSupported("Cannot reset object ", id);
      } else {
  #ifndef ROCKSDB_LITE
        status = config_options.registry->NewSharedObject(id, result);
  #else
        status = Status::NotSupported("Cannot load object in LITE mode ", id);
  #endif
        if (!status.ok()) {
          if (config_options.ignore_unsupported_options &&
              status.IsNotSupported()) {
            return Status::OK();
          } else {
            return status;
          }
        }
      }
    }
    return ConfigurableHelper::ConfigureNewObject(config_options, result->get(),
                                                  id, curr_opts, opt_map);
  }
  
  // Creates a new unique customizable instance object based on the input
  // parameters.
  // @see LoadSharedObject for more information on the inner workings of this
  // method.
  //
  // @param config_options Controls how the instance is created and errors are
  // handled
  // @param value Either the simple name of the instance to create, or a set of
  // name-value pairs to
  //              create and initailzie the object
  // @param func  Optional function to call to attempt to create an instance
  // @param result The newly created instance.
  template <typename T>
  static Status LoadUniqueObject(const ConfigOptions& config_options,
                                 const std::string& value,
                                 const UniqueFactoryFunc<T>& func,
                                 std::unique_ptr<T>* result) {
    std::string id;
    std::unordered_map<std::string, std::string> opt_map;
    Status status =
        ConfigurableHelper::GetOptionsMap(value, result->get(), &id, &opt_map);
    if (!status.ok()) {  // GetOptionsMap failed
      return status;
    }
    std::string curr_opts;
  #ifndef ROCKSDB_LITE
    if (result->get() != nullptr && result->get()->GetId() == id) {
      // Try to get the existing options, ignoring any errors
      ConfigOptions embedded = config_options;
      embedded.delimiter = ";";
      result->get()->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
    }
  #endif
    if (func == nullptr || !func(id, result)) {  // No factory, or it failed
      if (value.empty()) {
        // No Id and no options.  Clear the object
        result->reset();
        return Status::OK();
      } else if (id.empty()) {  // We have no Id but have options.  Not good
        return Status::NotSupported("Cannot reset object ", id);
      } else {
  #ifndef ROCKSDB_LITE
        status = config_options.registry->NewUniqueObject(id, result);
  #else
        status = Status::NotSupported("Cannot load object in LITE mode ", id);
  #endif  // ROCKSDB_LITE
        if (!status.ok()) {
          if (config_options.ignore_unsupported_options &&
              status.IsNotSupported()) {
            return Status::OK();
          } else {
            return status;
          }
        }
      }
    }
    return ConfigurableHelper::ConfigureNewObject(config_options, result->get(),
                                                  id, curr_opts, opt_map);
  }
  // Creates a new static (raw pointer) customizable instance object based on the
  // input parameters.
  // @see LoadSharedObject for more information on the inner workings of this
  // method.
  //
  // @param config_options Controls how the instance is created and errors are
  // handled
  // @param value Either the simple name of the instance to create, or a set of
  // name-value pairs to
  //              create and initailzie the object
  // @param func  Optional function to call to attempt to create an instance
  // @param result The newly created instance.
  template <typename T>
  static Status LoadStaticObject(const ConfigOptions& config_options,
                                 const std::string& value,
                                 const StaticFactoryFunc<T>& func, T** result) {
    std::string id;
    std::unordered_map<std::string, std::string> opt_map;
    Status status =
        ConfigurableHelper::GetOptionsMap(value, *result, &id, &opt_map);
    if (!status.ok()) {  // GetOptionsMap failed
      return status;
    }
    std::string curr_opts;
  #ifndef ROCKSDB_LITE
    if (*result != nullptr && (*result)->GetId() == id) {
      // Try to get the existing options, ignoring any errors
      ConfigOptions embedded = config_options;
      embedded.delimiter = ";";
      (*result)->GetOptionString(embedded, &curr_opts).PermitUncheckedError();
    }
  #endif
    if (func == nullptr || !func(id, result)) {  // No factory, or it failed
      if (value.empty()) {
        // No Id and no options.  Clear the object
        *result = nullptr;
        return Status::OK();
      } else if (id.empty()) {  // We have no Id but have options.  Not good
        return Status::NotSupported("Cannot reset object ", id);
      } else {
  #ifndef ROCKSDB_LITE
        status = config_options.registry->NewStaticObject(id, result);
  #else
        status = Status::NotSupported("Cannot load object in LITE mode ", id);
  #endif  // ROCKSDB_LITE
        if (!status.ok()) {
          if (config_options.ignore_unsupported_options &&
              status.IsNotSupported()) {
            return Status::OK();
          } else {
            return status;
          }
        }
      }
    }
    return ConfigurableHelper::ConfigureNewObject(config_options, *result, id,
                                                  curr_opts, opt_map);
  }
  }  // namespace ROCKSDB_NAMESPACE
  