Skip to content
Merged
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
9 changes: 0 additions & 9 deletions .agents/skills/writer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,6 @@ description: >

- Follow `.agents/documentation-guidelines.md` and `.agents/documentation-tasks.md`.
- Use fenced code blocks for commands and examples; format file/dir names as code.
- When referencing a documentation page or section in body prose, use typographic
double quotation marks only if the visible reference text is the actual page or
section title, such as the “Getting started” page or the “Troubleshooting”
section. The title normally starts with a capital letter. Do not add these
quotes around generic or descriptive links such as “this page”, “the next
section”, “declaring constraints”, or `4.3`, even if they point to a page or
section. Do not add these quotes in “What’s next” sections or navigation
elements. Keep file paths, identifiers, frontmatter values, navigation labels,
and Markdown link labels in their expected syntax.
- In Markdown files, prefer footnote-style reference links for external `https://`
targets instead of inline links. Write readable body text like
`[label][short-id]`, then place the URL definition near the end of the file,
Expand Down
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,3 @@ pubspec.lock
# Python cache
__pycache__/
*.pyc
/.claude/worktrees/
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
## Agent Guidelines
Please read and follow all guidelines in the project's agent documentation:

- Start with the table of contents: `.agents/_TOC.md`.
- Start with the table of contents: `./agents/_TOC.md`.
Comment thread
alexander-yevsyukov marked this conversation as resolved.
- Follow all linked documents from the TOC.
- Apply all coding standards, formatting rules, and project conventions found in these documents.

Expand Down
88 changes: 78 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ Spine Validation solves this by modifying the code generated by the Protobuf com
At build time, Spine Validation injects assertions directly into the generated Java classes,
enabling automatic enforcement of constraints without explicit API calls in application code.

## Table of Contents

- [Prerequisites](#prerequisites)
- [Validation in Action](#validation-in-action)
- [Architecture](#architecture)
- [Extending the Library](#extending-the-library)
Comment on lines +29 to +30
Comment on lines +29 to +30

## Prerequisites

This library is built with Java 17.
Expand Down Expand Up @@ -71,12 +78,12 @@ optionalError.ifPresent(err -> {
});
```

## Validation Options
### Validation Options

Validation options are defined by the following files:

1. [`options.proto`][options-proto]
2. [`time_options.proto`][time-options-proto]
1. [options.proto](https://github.com/SpineEventEngine/base-libraries/blob/master/base/src/main/proto/spine/options.proto).
2. [time_options.proto](https://github.com/SpineEventEngine/time/blob/master/time/src/main/proto/spine/time_options.proto).

Users must import these .proto files to use the options they define.

Expand All @@ -85,20 +92,81 @@ import "spine/options.proto"; // Brings all options, except for time-related one
import "spine/time_options.proto"; // Brings time-related options.
```

## Adding custom validation
# Adding custom validation

Users can extend the library by providing custom Protobuf options and code generation logic.

See the [Custom validation][custom-validation] section
of the User Guide for details.
Follow these steps to create a custom option:
Comment on lines +95 to +99
Comment on lines +95 to +99

1. Declare a Protobuf [extension](https://protobuf.dev/programming-guides/proto3/#customoptions)
in your `.proto` file.
2. Register it via `io.spine.option.OptionsProvider`.
3. Implement the following entities:
- Policy (`MyOptionPolicy`) – discovers and validates the option.
- View (`MyOptionView`) – accumulates valid option applications.
- Generator (`MyOptionGenerator`) – generates Java code for the option.
4. Register them via `io.spine.tools.validation.java.ValidationOption`.

Below is a workflow diagram for a typical option:

![Typical custom option](.github/readme/typical_custom_option.jpg)

Take a look at the `:java-tests:extensions` module that contains a full example of
implementation of the custom `(currency)` option.

Note that a custom option can provide several policies and views, but only one generator.
This allows building more complex models, using more entities and events.

Let's take a closer look at each entity.

### Policy

Usually, this is an entry point to the option handling.

The policy subscribes to one of `*OptionDiscovered` events:

- `FileOptionDiscovered`.
- `MessageOptionDiscovered`.
- `FieldOptionDiscovered`.
- `OneofOptionDiscovered`.

It filters incoming events, taking only those who contain the option of the interest. The policy
may validate the option application, query `TypeSystem`, extract and transform data arrived with
the option, if any. Once ready, it emits an event signaling that the discovered option is valid
and ready for the code generation.

The policy may report a compilation warning or an error, failing the whole compilation if it
finds an illegal application of the option.

For example:

1. An unsupported field type.
2. Illegal option content (invalid regex, parameter, signature).

The policy may just ignore the discovered option and emit `NoReaction`. A typical example
of this is a boolean option, such as `(required)`, which does nothing when it is set to `false`.

The desired behavior depends on the option itself.

### View

Views accumulate events from policies, serving as data providers for the validation model
used by code generators. Views are typically simple and only accumulate data; for more complex
logic, use policies.

Usually, one view represents a single application of an option.

### Generator

The generator is an entity that provides an actual implementation of the option behavior.
The generator produces Java code for every application of that option within the message type.

It has access to the `Querying` interface and can query views to find those belonging
to the processed message type.

[codecov]: https://codecov.io/gh/SpineEventEngine/validation
[codecov-badge]: https://codecov.io/gh/SpineEventEngine/validation/branch/master/graph/badge.svg
[license-badge]: https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat
[license]: http://www.apache.org/licenses/LICENSE-2.0
[gh-actions]: https://github.com/SpineEventEngine/validation/actions
[ubuntu-build-badge]: https://github.com/SpineEventEngine/validation/actions/workflows/build-on-ubuntu.yml/badge.svg

[options-proto]: https://github.com/SpineEventEngine/base-libraries/blob/master/base/src/main/proto/spine/options.proto
[time-options-proto]: https://github.com/SpineEventEngine/time/blob/master/time/src/main/proto/spine/time_options.proto
[custom-validation]: docs/content/docs/validation/user/05-custom-validation/_index.md
12 changes: 10 additions & 2 deletions buildSrc/src/main/kotlin/fat-jar.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
/*
* Copyright 2026, TeamDev. All rights reserved.
* Copyright 2023, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
Expand Down Expand Up @@ -55,6 +55,14 @@ tasks.publish {

tasks.shadowJar {
exclude(
/*
* Excluding this type to avoid it being located in the fat JAR.
*
* Locating this type in its own `io:spine:protodata` artifact is crucial
* for obtaining proper version values from the manifest file.
* This file is only present in the `io:spine:protodata` artifact.
*/
"io/spine/protodata/gradle/plugin/Plugin.class",
"META-INF/gradle-plugins/io.spine.tools.compiler.properties",

/*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ object Validation {
/**
* The version of the Validation library artifacts.
*/
const val version = "2.0.0-SNAPSHOT.415"
const val version = "2.0.0-SNAPSHOT.414"

/**
* The last version of Validation compatible with ProtoData.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ package io.spine.tools.validation
* We have the same items in this enum as in `io.spine.validation.RuntimeErrorPlaceholder`
* in the runtime library, which is exactly as this one. Please keep them in sync.
* This duplication is done intentionally to prevent clash between the runtime library,
* which is added to the classpath of the Compiler, and the runtime library, which is part
* which is added to the classpath of the Compiler and the runtime library, which is part
* of the Compiler itself because it is a part of Spine. As we complete our migration
Comment on lines 38 to 42
* of validation to codegen, the runtime library will either be significantly simplified,
* or even its content may be moved to `base`. Then, the duplicate enum should be removed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,4 @@ public abstract class ValidationPlugin(
IfSetAgainReaction(),
RequireReaction()
)
) // Plugin
)
Loading
Loading