syntax = "proto3";

package xds.type.matcher.v3;

import "xds/core/v3/extension.proto";
import "xds/type/matcher/v3/string.proto";

import "validate/validate.proto";

option java_package = "com.github.xds.type.matcher.v3";
option java_outer_classname = "MatcherProto";
option java_multiple_files = true;
option go_package = "github.com/cncf/xds/go/xds/type/matcher/v3";

// [#protodoc-title: Unified Matcher API]

// A matcher, which may traverse a matching tree in order to result in a match action.
// During matching, the tree will be traversed until a match is found, or if no match
// is found the action specified by the most specific on_no_match will be evaluated.
// As an on_no_match might result in another matching tree being evaluated, this process
// might repeat several times until the final OnMatch (or no match) is decided.
message Matcher {
  // What to do if a match is successful.
  message OnMatch {
    oneof on_match {
      option (validate.required) = true;

      // Nested matcher to evaluate.
      // If the nested matcher does not match and does not specify
      // on_no_match, then this matcher is considered not to have
      // matched, even if a predicate at this level or above returned
      // true.
      Matcher matcher = 1;

      // Protocol-specific action to take.
      core.v3.TypedExtensionConfig action = 2;
    }

    // If true and the Matcher matches, the action will be taken but the caller
    // will behave as if the Matcher did not match. A subsequent matcher or
    // on_no_match action will be used instead.
    // This field is not supported in all contexts in which the matcher API is
    // used. If this field is set in a context in which it's not supported,
    // the resource will be rejected.
    bool keep_matching = 3;
  }

  // A linear list of field matchers.
  // The field matchers are evaluated in order, and the first match
  // wins.
  message MatcherList {
    // Predicate to determine if a match is successful.
    message Predicate {
      // Predicate for a single input field.
      message SinglePredicate {
        // Protocol-specific specification of input field to match on.
        // [#extension-category: envoy.matching.common_inputs]
        core.v3.TypedExtensionConfig input = 1 [(validate.rules).message = {required: true}];

        oneof matcher {
          option (validate.required) = true;

          // Built-in string matcher.
          type.matcher.v3.StringMatcher value_match = 2;

          // Extension for custom matching logic.
          // [#extension-category: envoy.matching.input_matchers]
          core.v3.TypedExtensionConfig custom_match = 3;
        }
      }

      // A list of two or more matchers. Used to allow using a list within a oneof.
      message PredicateList {
        repeated Predicate predicate = 1 [(validate.rules).repeated = {min_items: 2}];
      }

      oneof match_type {
        option (validate.required) = true;

        // A single predicate to evaluate.
        SinglePredicate single_predicate = 1;

        // A list of predicates to be OR-ed together.
        PredicateList or_matcher = 2;

        // A list of predicates to be AND-ed together.
        PredicateList and_matcher = 3;

        // The invert of a predicate
        Predicate not_matcher = 4;
      }
    }

    // An individual matcher.
    message FieldMatcher {
      // Determines if the match succeeds.
      Predicate predicate = 1 [(validate.rules).message = {required: true}];

      // What to do if the match succeeds.
      OnMatch on_match = 2 [(validate.rules).message = {required: true}];
    }

    // A list of matchers. First match wins.
    repeated FieldMatcher matchers = 1 [(validate.rules).repeated = {min_items: 1}];
  }

  message MatcherTree {
    // A map of configured matchers. Used to allow using a map within a oneof.
    message MatchMap {
      map<string, OnMatch> map = 1 [(validate.rules).map = {min_pairs: 1}];
    }

    // Protocol-specific specification of input field to match on.
    core.v3.TypedExtensionConfig input = 1 [(validate.rules).message = {required: true}];

    // Exact or prefix match maps in which to look up the input value.
    // If the lookup succeeds, the match is considered successful, and
    // the corresponding OnMatch is used.
    oneof tree_type {
      option (validate.required) = true;

      MatchMap exact_match_map = 2;

      // Longest matching prefix wins.
      MatchMap prefix_match_map = 3;

      // Extension for custom matching logic.
      core.v3.TypedExtensionConfig custom_match = 4;
    }
  }

  oneof matcher_type {
    // A linear list of matchers to evaluate.
    MatcherList matcher_list = 1;

    // A match tree to evaluate.
    MatcherTree matcher_tree = 2;
  }

  // Optional OnMatch to use if no matcher above matched (e.g., if there are no matchers specified
  // above, or if none of the matches specified above succeeded).
  // If no matcher above matched and this field is not populated, the match will be considered unsuccessful.
  OnMatch on_no_match = 3;
}
