Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ GOLANGCI_LINT_VERSION := v1.64.7
BAZELISK_VERSION := v1.27.0
# Set to use a different compiler. For example, `GO=go1.18rc1 make test`.
GO ?= go
DART ?= dart
# Directory containing the protoc-gen-dart plugin (from `dart pub global activate protoc_plugin`).
PROTOC_GEN_DART_BIN ?= $(HOME)/.pub-cache/bin
ARGS ?=


Expand Down Expand Up @@ -65,6 +68,26 @@ lint-protovalidate: | $(BIN)/buf ## Check invariants of validate.proto
conformance: ## Build conformance harness
$(GO) build -o $(BIN)/protovalidate-conformance ./tools/protovalidate-conformance

.PHONY: test-dart
test-dart: ## Analyze and unit-test the Dart runtime
cd dart && $(DART) pub get
cd dart && $(DART) analyze lib bin
cd dart && $(DART) test

.PHONY: conformance-dart
conformance-dart: conformance ## Run conformance tests against the Dart runtime
cd dart && $(DART) pub get
cd dart && $(DART) compile exe bin/protovalidate_conformance.dart -o $(abspath $(BIN))/protovalidate-conformance-dart
$(BIN)/protovalidate-conformance \
--expected_failures=dart/conformance/expected_failures.yaml \
$(BIN)/protovalidate-conformance-dart

.PHONY: generate-dart
generate-dart: | $(BIN)/buf ## Regenerate Dart code for the runtime and conformance suite
PATH="$(PROTOC_GEN_DART_BIN):$(abspath $(BIN)):$$PATH" \
$(BIN)/buf generate --template dart/buf.gen.yaml --include-imports --include-wkt
cd dart && $(DART) run tool/gen_conformance_registry.dart

.PHONY: generate
generate: ## Regenerate code and license headers
$(MAKE) generate-bazel
Expand Down
3 changes: 3 additions & 0 deletions dart/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Dart tool cache and build output.
.dart_tool/
build/
81 changes: 81 additions & 0 deletions dart/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# protovalidate-dart

A Dart runtime for [Protovalidate](https://protovalidate.com), validating
Protobuf messages against the standard validation rules declared with
`buf.validate` field options.

This runtime implements the **standard, CEL-free subset** of Protovalidate. It
does not evaluate custom [CEL](https://cel.dev) expressions; rules that are only
expressible through CEL are out of scope (see [Limitations](#limitations)).

## Usage

The validator reads rules and structure from a
`google.protobuf.FileDescriptorSet` and reads field values from your generated
messages by reflection. Build a descriptor set for your protos with
`buf build -o image.binpb`, then:

```dart
import 'dart:io';
import 'package:protovalidate/protovalidate.dart';
import 'package:your_app/gen/user.pb.dart';

void main() {
final validator =
Validator.fromBuffer(File('image.binpb').readAsBytesSync());

final user = User()..email = 'not-an-email';

// Returns the list of violations (empty when valid).
final violations = validator.validate(user);
for (final v in violations) {
print('${v.ruleId}: ${v.message}');
}

// Or throw on the first invalid message.
validator.check(user); // throws ValidationException
}
```

## Supported rules

| Target | Rules |
| --- | --- |
| field | `required`, `ignore` (`IGNORE_UNSPECIFIED` / `IGNORE_IF_ZERO_VALUE` / `IGNORE_ALWAYS`) |
| numeric | `const`, `lt`, `lte`, `gt`, `gte`, `in`, `not_in`, `finite` (float/double) |
| `bool` | `const` |
| `string` | `const`, `len`, `min_len`, `max_len`, `len_bytes`, `min_bytes`, `max_bytes`, `pattern`, `prefix`, `suffix`, `contains`, `not_contains`, `in`, `not_in`, `email`, `uri`, `uuid` |
| `bytes` | `const`, `len`, `min_len`, `max_len`, `prefix`, `suffix`, `contains`, `in`, `not_in`, `uuid` |
| `enum` | `const`, `defined_only`, `in`, `not_in` |
| repeated | `min_items`, `max_items`, `unique`, `items` |
| map | `min_pairs`, `max_pairs`, `keys`, `values` |

Nested messages are validated recursively. String lengths are counted in Unicode
code points (`len`/`min_len`/`max_len`) or UTF-8 bytes (`*_bytes`). `pattern` uses
RE2-style syntax via Dart `RegExp`.

## Limitations

Out of scope for this runtime (and recorded in
[`conformance/expected_failures.yaml`](conformance/expected_failures.yaml)):

- Custom, predefined, and message-level CEL rules (`cel`, `cel_expression`).
- Well-known-type rules (`google.protobuf.Any`, `Duration`, `Timestamp`,
`FieldMask`, and the wrapper types).
- Message-level `oneof` rules and `OneofRules`.
- String/bytes formats beyond `email`, `uri`, and `uuid` (e.g. `hostname`, `ip*`,
`address`, `uri_ref`, `tuuid`, `ulid`, `host_and_port`).
- Protobuf Editions edge cases (some of which `protoc-gen-dart` does not yet
generate, such as delimited-encoded message fields).

## Development

```shell
make test-dart # dart analyze + dart test
make conformance-dart # run the Protovalidate conformance suite
make generate-dart # regenerate Dart code from the .proto sources
```

`make generate-dart` requires `protoc-gen-dart` on `PATH`
(`dart pub global activate protoc_plugin`). Generated code lives in
`lib/src/gen/` and `bin/conformance_types.g.dart` and is checked in.
12 changes: 12 additions & 0 deletions dart/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
include: package:lints/recommended.yaml

analyzer:
exclude:
# Generated code carries its own ignore directives; keep analysis focused on
# the hand-written runtime.
- lib/src/gen/**

linter:
rules:
- prefer_final_locals
- avoid_print
Loading