//
// Test module using proto2 syntax.
//
// This module is used in test cases, and also serves as an example of how to
// define a YARA module using a protocol buffer.
//

// There are two versions of protobuf, "proto2" and "proto3". If this is omitted
// the default is "proto2", but it's recommendable to make it explicit.
syntax = "proto2";

// This import is required for defining a YARA module, it contains definitions
// that are common to all modules.
import "yara.proto";

// This is not strictly required, but it prevents name collisions with other
// YARA modules.
package test_proto2;

// This option section is also required, it gives YARA information about
// the module being defined.
option (yara.module_options) = {
  // The module's name. This is the string used in `import` statements in YARA
  // rules (e.g. import "test_proto2"). This field is required.
  name : "test_proto2"

  // The protobuf message that defines the top-level structure of the module.
  // A .proto file can contain multiple message definitions, usually organized
  // in a hierarchical structure in which one message has fields that are other
  // messages. YARA needs to know which of those message definitions describes
  // the top-level structure for the module. In this case the root message is
  // "test_proto2.TestProto2" which indicates that the "test_proto2" module
  // will have the fields defined in the "TestProto2" message. Notice that
  // the "test_proto2." prefix comes after the package name.
  root_message: "test_proto2.TestProto2"

  // The name of the Rust module that contains the code for this module. A
  // module with this name must exists under src/modules. In this case the
  // module name is "test_proto2", we can create a module with that name in
  // two ways: by creating a file "test_proto2.rs" under src/modules, or by
  // creating a "test_proto2" directory under src/modules and putting a
  // "mod.rs" file inside of it.
  //
  // Notice however that this is optional, as YARA modules can consists only
  // in the data structure defined by this proto file, and don't need to have
  // any associated code.
  rust_module: "test_proto2"

  // The name of the feature that controls whether this module is compiled or
  // not. A feature with this name must be added to the Cargo.toml file.
  cargo_feature: "test_proto2-module"
};

/// Top-level structure for this module.
///
/// In a YARA rule, after importing the module with `import "test_proto2"`, you
/// can access the fields in this structure, as in the following examples:
///
///  test_proto2.int32_zero == 0
///  test_proto2.string_foo == "foo"
///
/// Notice that proto2 fields must be either optional or required. Optional
/// fields don't need to be explicitly set to some value, they can remain
/// empty. If a field is empty it will appear to YARA as undefined. In the
/// other hand, required fields must be explicitly set to some value before
/// the structure is passed to YARA.
///
/// In proto3 you don't need to specify if fields are optional or required,
//  they are always optional and you don't need to set their values explicitly.
//  However, fields for which you don't set a value explicitly are considered
//  to have the default value for the type. Numeric values default to 0, and
//  string values default to an empty string. These fields are never undefined
//  to YARA, they always have some value, either their default values or the
//  value explicitly set while filling the structure.
message TestProto2 {

  // Numeric values initialized to 0 by the module.

  required int32              int32_zero = 1;
  required int64              int64_zero = 2;

  required sint32             sint32_zero = 3;
  required sint64             sint64_zero = 4;

  required uint32             uint32_zero = 5;
  required uint64             uint64_zero = 6;

  required fixed32            fixed32_zero = 7;
  required fixed64            fixed64_zero = 8;

  required sfixed32           sfixed32_zero = 9;
  required sfixed64           sfixed64_zero = 10;

  required float              float_zero = 11;
  required double             double_zero = 12;

  // Numeric values initialized to 1 by the module.

  required int32              int32_one = 21;
  required int64              int64_one = 22;

  required sint32             sint32_one = 23;
  required sint64             sint64_one = 24;

  required uint32             uint32_one = 25;
  required uint64             uint64_one = 26;

  required fixed32            fixed32_one = 27;
  required fixed64            fixed64_one = 28;

  required sfixed32           sfixed32_one = 29;
  required sfixed64           sfixed64_one = 30;

  required float              float_one = 31;
  required double             double_one = 32;

  // Numeric values that remain undefined.

  optional int32              int32_undef = 41;
  optional int64              int64_undef = 42;

  optional sint32             sint32_undef = 43;
  optional sint64             sint64_undef = 44;

  optional uint32             uint32_undef = 45;
  optional uint64             uint64_undef = 46;

  optional fixed32            fixed32_undef = 47;
  optional fixed64            fixed64_undef = 48;

  optional sfixed32           sfixed32_undef = 49;
  optional sfixed64           sfixed64_undef = 50;

  optional float              float_undef = 51;
  optional double             double_undef = 52;

  // String values.

  required string             string_foo = 61;
  required string             string_bar = 62;
  optional string             string_undef = 63;

  required bytes              bytes_foo = 64;
  required bytes              bytes_bar = 65;
  optional bytes              bytes_undef = 66;

  enum Enumeration {
    ITEM_0 = 0;
    ITEM_1 = 1;
    ITEM_2 = 2 [(yara.enum_value).i64 = 0x7fffffffffff];
    ITEM_3 = 3 [(yara.enum_value).i64 = -1];
  }

  optional Enumeration        enumeration = 100;
  optional NestedProto2       nested = 102;

  repeated int64              array_int64 = 150;
  repeated float              array_float = 151;
  repeated bool               array_bool = 152;
  repeated string             array_string = 153;
  repeated NestedProto2       array_struct = 154;

  map<string, NestedProto2>   map_string_struct = 200;
  map<string, int64>          map_string_int64 = 201;
  map<string, string>         map_string_string = 202;
  map<string, bool>           map_string_bool = 203;
  map<string, float>          map_string_float = 204;

  map<int64, NestedProto2>    map_int64_struct = 300;
  map<int64, int64>           map_int64_int64 = 301;
  map<int64, string>          map_int64_string = 302;
  map<int64, bool>            map_int64_bool = 303;
  map<int64, float>           map_int64_float = 304;

  /// This field will be visible in YARA as `bool_yara` instead of `bool_proto`.
  optional bool bool_proto = 350 [(yara.field_options).name = "bool_yara"];

  /// This field won't be visible to YARA.
  optional bool ignored = 351 [(yara.field_options).ignore = true];

  /// This field will be visible in YARA as `items` instead of `Enumeration2`.
  enum Enumeration2 {
    option (yara.enum_options).name = "items";
    ITEM_4 = 0;
    ITEM_5 = 1;
  }

  optional uint64 file_size = 400;

  /// This field is accessible only if the features "foo" (or "FOO") and "bar"
  // are enabled while compiling the YARA rules.
  optional uint64 requires_foo_and_bar = 500 [
    (yara.field_options) = {
      acl: [
        {
          accept_if: ["foo", "FOO"],
          error_title: "foo is required",
          error_label: "this field was used without foo"
        },
        {
          accept_if: "bar",
          error_title: "bar is required",
          error_label: "this field was used without bar"
        },
        {
          reject_if: "baz",
          error_title: "baz is forbidden",
          error_label: "this field was used with baz"
        }
      ]
    }
  ];
}

enum TopLevelEnumeration {
  ITEM_0x1000 = 0x1000;
  ITEM_0x2000 = 0x2000;
}

enum InlineEnumeration {
  option (yara.enum_options).inline = true;
  INLINE_0x1000 = 0x1000;
  INLINE_0x2000 = 0x2000;
}

message NestedProto2 {
  optional int32              nested_int32_zero = 1;
  optional int64              nested_int64_zero = 2;
  optional int32              nested_int32_one = 3;
  optional int64              nested_int64_one = 4;
  optional bool               nested_bool = 5;
  optional string             nested_string = 6;
  repeated int64              nested_array_int64 = 7;

  enum NestedEnumeration {
    ITEM_0 = 0;
    ITEM_1 = 1;
  }
}