Skip to content

bufbuild/protovalidate-cc

Repository files navigation

The Buf logo protovalidate-cc

CI Conformance BSR

protovalidate-cc is the C++ language implementation of protovalidate designed to validate Protobuf messages at runtime based on user-defined validation constraints. Powered by Google's Common Expression Language (CEL), it provides a flexible and efficient foundation for defining and evaluating custom validation rules. The primary goal of protovalidate is to help developers ensure data consistency and integrity across the network without requiring generated code.

The protovalidate project

Head over to the core protovalidate repository for:

Other protovalidate runtime implementations include

And others coming soon:

  • TypeScript: protovalidate-ts

Installation

Building from source

To install protovalidate-cc, clone the repository and build the project:

git clone https://github.com/bufbuild/protovalidate-cc.git
cd protovalidate-cc
make build

Remember to always check for the latest version of protovalidate-cc on the project's GitHub releases page to ensure you're using the most up-to-date version.

Bazel external repository

To use protovalidate-cc as an external Bazel repository, add the following to the WORKSPACE file:

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")

http_archive(
    name = "com_github_bufbuild_protovalidate_cc",
    sha256 = ...,
    strip_prefix = "protovalidate-cc-{verion}",
    urls = [
        "https://github.com/bufbuild/protovalidate-cc/archive/v{verion}.tar.gz",
    ],
)

load("@com_github_bufbuild_protovalidate_cc//bazel:deps.bzl", "protovalidate_cc_dependencies")

protovalidate_cc_dependencies()

Then add a dependency to a cc_library or cc_binary target:

cc_library(
    ...
    deps = [
        "@com_github_bufbuild_protovalidate_cc//buf/validate:validator",
        ...
    ]
)

Usage

Implementing validation constraints

Validation constraints are defined directly within .proto files. Documentation for adding constraints can be found in the protovalidate project README and its comprehensive docs.

syntax = "proto3";

package my.package;

import "google/protobuf/timestamp.proto";
import "buf/validate/validate.proto";

message Transaction {
  uint64 id = 1 [(buf.validate.field).uint64.gt = 999];
  google.protobuf.Timestamp purchase_date = 2;
  google.protobuf.Timestamp delivery_date = 3;

  string price = 4 [(buf.validate.field).cel = {
    id: "transaction.price",
    message: "price must be positive and include a valid currency symbol ($ or £)",
    expression: "(this.startsWith('$') || this.startsWith('£')) && double(this.substring(1)) > 0"
  }];

  option (buf.validate.message).cel = {
    id: "transaction.delivery_date",
    message: "delivery date must be after purchase date",
    expression: "this.delivery_date > this.purchase_date"
  };
}

Example

In your C++ code, include the header file and use the Validate function to validate your messages.

#include <iostream>

#include "buf/validate/validator.h"
#include "google/protobuf/arena.h"

#include "path/to/generated/protos/transaction.pb.h"

int main() {
  my::package::Transaction transaction;
  transaction.set_id(1234);
  transaction.set_price("$5.67");

  google::protobuf::Timestamp* purchase_date = transaction.mutable_purchase_date();
  google::protobuf::Timestamp* delivery_date = transaction.mutable_delivery_date();
  // set time for purchase_date and delivery_date

  std::unique_ptr<buf::validate::ValidatorFactory> factory =
      buf::validate::ValidatorFactory::New().value();
  google::protobuf::Arena arena;
  buf::validate::Validator validator = factory->NewValidator(&arena);
  buf::validate::Violations results = validator.Validate(transaction).value();
  if (results.violations_size() > 0) {
    std::cout << "validation failed" << std::endl;
  } else {
    std::cout << "validation succeeded" << std::endl;
  }
  return 0;
}

Ecosystem

Legal

Offered under the Apache 2 license.