text: #pragma once
  
  #include <string>
  #include <tuple>
  #include <vector>
  
  #include "envoy/extensions/filters/network/thrift_proxy/filters/header_to_metadata/v3/header_to_metadata.pb.h"
  
  #include "source/common/common/logger.h"
  #include "source/common/common/matchers.h"
  #include "source/extensions/filters/network/thrift_proxy/filters/pass_through_filter.h"
  
  #include "absl/strings/string_view.h"
  
  namespace Envoy {
  namespace Extensions {
  namespace ThriftFilters {
  namespace HeaderToMetadataFilter {
  
  using namespace Envoy::Extensions::NetworkFilters;
  using ProtoRule = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::
      v3::HeaderToMetadata::Rule;
  using KeyValuePair = envoy::extensions::filters::network::thrift_proxy::filters::
      header_to_metadata::v3::HeaderToMetadata::KeyValuePair;
  using ValueType = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::
      v3::HeaderToMetadata::ValueType;
  using ValueEncode = envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::
      v3::HeaderToMetadata::ValueEncode;
  
  // Get value from a header.
  class HeaderValueSelector {
  public:
    explicit HeaderValueSelector(Http::LowerCaseString header, bool remove)
        : header_(std::move(header)), remove_(std::move(remove)) {}
    absl::optional<std::string> extract(Http::HeaderMap& map) const;
    std::string toString() const { return fmt::format("header '{}'", header_.get()); }
  
  private:
    const Http::LowerCaseString header_;
    const bool remove_;
  };
  
  class Rule {
  public:
    Rule(const ProtoRule& rule);
    const ProtoRule& rule() const { return rule_; }
    const Regex::CompiledMatcherPtr& regexRewrite() const { return regex_rewrite_; }
    const std::string& regexSubstitution() const { return regex_rewrite_substitution_; }
    std::shared_ptr<const HeaderValueSelector> selector_;
  
  private:
    const ProtoRule rule_;
    Regex::CompiledMatcherPtr regex_rewrite_{};
    std::string regex_rewrite_substitution_{};
  };
  
  using HeaderToMetadataRules = std::vector<Rule>;
  
  const uint32_t MAX_HEADER_VALUE_LEN = 8 * 1024;
  
  class Config {
  public:
    Config(const envoy::extensions::filters::network::thrift_proxy::filters::header_to_metadata::v3::
               HeaderToMetadata& config);
    const HeaderToMetadataRules& requestRules() const { return request_rules_; }
  
  private:
    HeaderToMetadataRules request_rules_;
  };
  
  using ConfigSharedPtr = std::shared_ptr<Config>;
  
  class HeaderToMetadataFilter : public ThriftProxy::ThriftFilters::PassThroughDecoderFilter,
                                 protected Logger::Loggable<Envoy::Logger::Id::main> {
  public:
    HeaderToMetadataFilter(const ConfigSharedPtr config);
  
    ThriftProxy::FilterStatus
        transportBegin(Extensions::NetworkFilters::ThriftProxy::MessageMetadataSharedPtr) override;
  
  private:
    using ProtobufRepeatedRule = Protobuf::RepeatedPtrField<ProtoRule>;
    using StructMap = std::map<std::string, ProtobufWkt::Struct>;
  
    /**
     *  writeHeaderToMetadata encapsulates (1) searching for the header and (2) writing it to the
     *  request metadata.
     *  @param headers the map of key-value headers to look through. These are request headers.
     *  @param rules the header-to-metadata mapping set in configuration.
     */
    void writeHeaderToMetadata(Http::HeaderMap& headers, const HeaderToMetadataRules& rules,
                               ThriftProxy::ThriftFilters::DecoderFilterCallbacks& callbacks) const;
    bool addMetadata(StructMap&, const std::string&, const std::string&, std::string, ValueType,
                     ValueEncode) const;
    void applyKeyValue(std::string&&, const Rule&, const KeyValuePair&, StructMap&) const;
    const std::string& decideNamespace(const std::string& nspace) const;
  
    const ConfigSharedPtr config_;
  };
  
  } // namespace HeaderToMetadataFilter
  } // namespace ThriftFilters
  } // namespace Extensions
  } // namespace Envoy
  