// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#ifndef GOOGLE_PROTOBUF_FEATURE_RESOLVER_H__
#define GOOGLE_PROTOBUF_FEATURE_RESOLVER_H__

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.h"
#include "absl/types/span.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/descriptor.pb.h"
#include "google/protobuf/dynamic_message.h"

// Must be included last.
#include "google/protobuf/port_def.inc"

namespace google {
namespace protobuf {

// These helpers implement the unique behaviors of edition features.  For more
// details, see go/protobuf-editions-features.
class PROTOBUF_EXPORT FeatureResolver {
 public:
  FeatureResolver(FeatureResolver&&) = default;
  FeatureResolver& operator=(FeatureResolver&&) = delete;

  // Compiles a set of FeatureSet extensions into a mapping of edition to unique
  // defaults.  This is the most complicated part of feature resolution, and by
  // abstracting this out into an intermediate message, we can make feature
  // resolution significantly more portable.
  static absl::StatusOr<FeatureSetDefaults> CompileDefaults(
      const Descriptor* feature_set,
      absl::Span<const FieldDescriptor* const> extensions,
      Edition minimum_edition, Edition maximum_edition);

  // Creates a new FeatureResolver at a specific edition.  This calculates the
  // default feature set for that edition, using the output of CompileDefaults.
  static absl::StatusOr<FeatureResolver> Create(
      Edition edition, const FeatureSetDefaults& defaults);

  // Creates a new feature set using inheritance and default behavior. This is
  // designed to be called recursively, and the parent feature set is expected
  // to be a fully merged one.  The returned FeatureSet will be fully resolved
  // for any extensions that were used to construct the defaults.
  absl::StatusOr<FeatureSet> MergeFeatures(
      const FeatureSet& merged_parent, const FeatureSet& unmerged_child) const;

  // Validates an unresolved FeatureSet object to make sure they obey the
  // lifetime requirements.  This needs to run *within* the pool being built, so
  // that the descriptors of any feature extensions are known and can be
  // validated.  `pool_descriptor` should point to the FeatureSet descriptor
  // inside the pool, or nullptr if one doesn't exist,
  //
  // This will return error messages for any explicitly set features used before
  // their introduction or after their removal.  Warnings will be included for
  // any explicitly set features that have been deprecated.
  struct PROTOBUF_FUTURE_ADD_EARLY_WARN_UNUSED ValidationResults {
    std::vector<std::string> errors;
    std::vector<std::string> warnings;
  };
  PROTOBUF_FUTURE_ADD_EARLY_NODISCARD static ValidationResults
  ValidateFeatureLifetimes(Edition edition, const Message& option,
                           const Descriptor* pool_descriptor);

  // Validates feature support on features and options
  // to enforce feature support to be written correctly.
  //
  // This will return error status and error message for incorrectly written
  // feature support.
  static absl::Status ValidateFeatureSupport(
      const FieldOptions::FeatureSupport& support, absl::string_view full_name);

  // Calls ValidateFeatureSupport on fields during proto parsing.
  // This will handle feature validation on fields of different types,
  // especially of type ENUM.
  //
  // This will return error status and error message of ValidateFeatureSupport.
  static absl::Status ValidateFieldFeatureSupport(const FieldDescriptor& field);

 private:
  explicit FeatureResolver(FeatureSet defaults)
      : defaults_(std::move(defaults)) {}

  FeatureSet defaults_;
};

namespace internal {
// Gets the default feature set for a given edition.
absl::StatusOr<FeatureSet> PROTOBUF_EXPORT GetEditionFeatureSetDefaults(
    Edition edition, const FeatureSetDefaults& defaults);
}  // namespace internal
}  // namespace protobuf
}  // namespace google

#include "google/protobuf/port_undef.inc"

#endif  // GOOGLE_PROTOBUF_FEATURE_RESOLVER_H__
