From d1a320098025d17f0a9b5e238a7589ef9762635e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:52:41 +0000 Subject: [PATCH 01/26] Reduce using "please" in docs --- .../io/spine/tools/validation/java/JavaValidationPlugin.kt | 6 +++--- .../tools/validation/java/setonce/SetOnceBytesField.kt | 3 +-- .../spine/tools/validation/java/setonce/SetOnceEnumField.kt | 3 +-- .../src/main/kotlin/io/spine/validation/MessageValidator.kt | 2 +- .../kotlin/io/spine/validation/test/CurrencyOptionITest.kt | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt index fdd9c17afb..4187a6ce1c 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/JavaValidationPlugin.kt @@ -71,9 +71,9 @@ private val customOptions: List by lazy { * Dynamically discovered instances of custom * [MessageValidator][io.spine.validation.MessageValidator]s. * - * Please note that the KSP module is responsible for the actual discovering - * of the message validators. The discovered validators are written to a text file - * in the KSP task output. This property loads the validators from that file. + * Note that the KSP module is responsible for the actual discovering of the message validators. + * The discovered validators are written to a text file in the KSP task output. + * This property loads the validators from that file. */ private val customValidators: Map by lazy { val workingDir = System.getProperty("user.dir") diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceBytesField.kt b/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceBytesField.kt index 668383f1b6..b79a8940af 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceBytesField.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceBytesField.kt @@ -38,8 +38,7 @@ import io.spine.tools.psi.java.method /** * Renders Java code to support `(set_once)` option for the given byte array [field]. * - * Please note, in the generated Java code, Protobuf uses [ByteString] to represent - * an array of bytes. + * Note that Protobuf uses [ByteString] to represent an array of bytes in the generated code. * * @param field The byte array field that declared the option. * @param typeSystem The type system to resolve types. diff --git a/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceEnumField.kt b/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceEnumField.kt index 43361debc2..456deccdc6 100644 --- a/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceEnumField.kt +++ b/java/src/main/kotlin/io/spine/tools/validation/java/setonce/SetOnceEnumField.kt @@ -39,8 +39,7 @@ import io.spine.tools.psi.java.method /** * Renders Java code to support `(set_once)` option for the given enum [field]. * - * Please note, in the generated Java code, Protobuf uses an ordinal number - * to represent the currently set enum constant. + * Note that code generated by Protobuf uses an ordinal number to represent the currently set enum constant. * * @param field The enum field that declared the option. * @param typeSystem The type system to resolve types. diff --git a/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt b/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt index df147b202a..2b534fe50d 100644 --- a/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt +++ b/jvm-runtime/src/main/kotlin/io/spine/validation/MessageValidator.kt @@ -82,7 +82,7 @@ import io.spine.annotation.SPI * } * ``` * - * Please note that standalone instances of [M] and fields of [M] type that occur in + * Note that standalone instances of [M] and fields of [M] type that occur in * other external messages **will not be validated**. * * Consider the following example: diff --git a/tests/consumer/src/test/kotlin/io/spine/validation/test/CurrencyOptionITest.kt b/tests/consumer/src/test/kotlin/io/spine/validation/test/CurrencyOptionITest.kt index 3d6d8250c7..6d9a5a57a2 100644 --- a/tests/consumer/src/test/kotlin/io/spine/validation/test/CurrencyOptionITest.kt +++ b/tests/consumer/src/test/kotlin/io/spine/validation/test/CurrencyOptionITest.kt @@ -35,7 +35,7 @@ import org.junit.jupiter.api.Test * by [CurrencyOption] in the `:java-tests:extensions` module. * * The `extensions` module declares the `(currency)` message option, which is used by - * money data types in this module. Please see `main/proto/test/money.proto` for details. + * money data types in this module. See `main/proto/test/money.proto` for details. * * This test verifies that custom validation code works as expected. */ From 11c6121e49b035b19032818d1094e913c56c2904 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:54:04 +0000 Subject: [PATCH 02/26] Improve text layout --- docs/01-getting-started/index.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md index 9ed40dde89..57b8967ef0 100644 --- a/docs/01-getting-started/index.md +++ b/docs/01-getting-started/index.md @@ -1,6 +1,7 @@ # Getting Started -This section helps you set up Spine Validation, define your first validated Protobuf model, and see validation in action in Java and Kotlin. +This section helps you set up Spine Validation, define your first validated Protobuf model, +and see validation in action in Java and Kotlin. If you are new to the library, read the short overview first: - Introduction → [Overview](../00-intro/index.md) @@ -22,7 +23,9 @@ If you are new to the library, read the short overview first: - Protobuf compiler (`protoc`) - Optional: Kotlin 2.2.20+ for the Kotlin Protobuf DSL -If your project already generates Java/Kotlin sources from `.proto` files, you’re 90% there. Spine Validation integrates into the build to generate and inject validation logic into the code produced by `protoc`. +If your project already generates Java/Kotlin sources from `.proto` files, you’re 90% there. +Spine Validation integrates into the build to generate and inject validation logic into +the code produced by `protoc`. --- From b16788ace57f5b2ded9f9de608ba59cacd7761ca Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:54:28 +0000 Subject: [PATCH 03/26] Bump version -> `2.0.0-SNAPSHOT.394` --- version.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.gradle.kts b/version.gradle.kts index dc55d8b2ab..bae585a6c9 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -29,4 +29,4 @@ * * For Spine-based dependencies please see [io.spine.dependency.local.Spine]. */ -val validationVersion by extra("2.0.0-SNAPSHOT.393") +val validationVersion by extra("2.0.0-SNAPSHOT.394") From 9c0a34b3fb2f5018b17b2462b3409f910bda81c2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:55:43 +0000 Subject: [PATCH 04/26] Bump Validation and CoreJvm Compiler --- .../main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt | 4 ++-- .../src/main/kotlin/io/spine/dependency/local/Validation.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 2737c39cf0..b48d90e9e2 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -46,12 +46,12 @@ object CoreJvmCompiler { /** * The version used to in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.050" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.051" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.050" + const val version = "2.0.0-SNAPSHOT.051" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 271ccee000..1cad8b7403 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -36,7 +36,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.392" + const val version = "2.0.0-SNAPSHOT.393" /** * The last version of Validation compatible with ProtoData. From 6379b9f3ed638d697f923629ad28d35ef9ca1cfc Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:56:55 +0000 Subject: [PATCH 05/26] Update dependency reports --- dependencies.md | 60 ++++++++++++++++++++++++------------------------- pom.xml | 10 ++++----- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/dependencies.md b/dependencies.md index 9faee3fde2..6ec36ba3f3 100644 --- a/dependencies.md +++ b/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-context:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1139,14 +1139,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-context-tests:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -1731,14 +1731,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:51 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-gradle-plugin:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -2807,14 +2807,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-java:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -3901,14 +3901,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-java-bundle:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. @@ -3971,14 +3971,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:51 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine:validation-jvm-runtime:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -4811,14 +4811,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-ksp:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-ksp:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.auto.service. **Name** : auto-service-annotations. **Version** : 1.1.1. @@ -5747,14 +5747,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-consumer:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -6345,14 +6345,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:51 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-consumer-dependency:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -6863,14 +6863,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-extensions:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -7554,14 +7554,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-runtime:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8183,14 +8183,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-validating:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -8855,14 +8855,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-validator:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.20.0. @@ -9613,14 +9613,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:52 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-validator-dependency:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -9890,14 +9890,14 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:51 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.393` +# Dependencies of `io.spine.tools:validation-vanilla:2.0.0-SNAPSHOT.394` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -10248,6 +10248,6 @@ This report was generated on **Tue Jan 20 16:21:00 WET 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Tue Jan 20 16:21:00 WET 2026** using +This report was generated on **Tue Jan 20 19:55:51 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8b9da9273d..5f619d86ff 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools validation -2.0.0-SNAPSHOT.393 +2.0.0-SNAPSHOT.394 2015 @@ -62,7 +62,7 @@ all modules and does not describe the project structure per-subproject. io.spine spine-validation-jvm-runtime - 2.0.0-SNAPSHOT.392 + 2.0.0-SNAPSHOT.393 compile @@ -281,12 +281,12 @@ all modules and does not describe the project structure per-subproject. io.spine.tools core-jvm-gradle-plugins - 2.0.0-SNAPSHOT.050 + 2.0.0-SNAPSHOT.051 io.spine.tools core-jvm-routing - 2.0.0-SNAPSHOT.050 + 2.0.0-SNAPSHOT.051 io.spine.tools @@ -301,7 +301,7 @@ all modules and does not describe the project structure per-subproject. io.spine.tools validation-java-bundle - 2.0.0-SNAPSHOT.392 + 2.0.0-SNAPSHOT.393 net.sourceforge.pmd From 1397636d83a1f31092a9bc26d39f92572815eb93 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Tue, 20 Jan 2026 19:58:25 +0000 Subject: [PATCH 06/26] Update config ref. --- config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config b/config index dcd2cee3af..17e0dbb819 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit dcd2cee3af82ce8e4de407801636637f7cdcef3c +Subproject commit 17e0dbb819839d9b65b711efb085b38bcbb5eae9 From 6e2fe2b32c3418adbd1adbed89e50a60d9d37211 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 21 Jan 2026 18:28:56 +0000 Subject: [PATCH 07/26] Remove "Migration Guide" section --- docs/ToC.md | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/ToC.md b/docs/ToC.md index 87515e2df0..4129c539cd 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -84,13 +84,8 @@ - [Cross-field Logic](07-recipes/cross-field-logic.md) - [API Validation](07-recipes/api-validation.md) -## 8. Migration Guide -- [Overview](08-migration/index.md) -- [Migrating from `spine.base` Validation](08-migration/from-spine-base.md) -- [Version Changes](08-migration/version-changes.md) - -## 9. Reference -- [Reference Overview](09-reference/index.md) -- [List of Validation Options](09-reference/options.md) -- [Java/Kotlin API Index](09-reference/api.md) -- [Glossary](09-reference/glossary.md) +## 8. Reference +- [Reference Overview](08-reference/index.md) +- [List of Validation Options](08-reference/options.md) +- [Java/Kotlin API Index](08-reference/api.md) +- [Glossary](08-reference/glossary.md) From 479c28c4b5a500f56344bb2712f46115b249e1d5 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 21 Jan 2026 18:41:28 +0000 Subject: [PATCH 08/26] Improve text layout --- docs/00-intro/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index 542f713dac..85b897e3fc 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -3,8 +3,7 @@ Spine Validation is a Protobuf-centric validation framework that generates type-safe validation code directly from your `.proto` definitions. It allows you to describe constraints on fields, messages, and collections using -declarative options and then automatically enforces these constraints at -runtime. +declarative options and then automatically enforces these constraints at runtime. The library is part of the Spine toolchain but can also be used independently in any Java/Kotlin backend that models data using Protocol Buffers. From 4230509369fb9cba5351443149967ddefe9cf73b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 21 Jan 2026 19:56:20 +0000 Subject: [PATCH 09/26] Remove unnecessary decorations --- docs/00-intro/index.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index 85b897e3fc..8e47de76d4 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -12,7 +12,7 @@ in any Java/Kotlin backend that models data using Protocol Buffers. ## Key Capabilities -### 🔹 Declarative constraints in `.proto` +### Declarative constraints in `.proto` Validation rules are expressed as Protobuf options such as: - `required` @@ -24,8 +24,8 @@ Validation rules are expressed as Protobuf options such as: This keeps validation close to the data model and ensures it evolves together with it. -### 🔹 Generated validators -The Spine compiler plugin processes your Protobuf model and generates: +### Generated validators +The Spine Compiler plugin processes your Protobuf model and generates: - validation code for messages and builders, - runtime checks, @@ -33,7 +33,7 @@ The Spine compiler plugin processes your Protobuf model and generates: No manual validators, reflection, or annotations are required. -### 🔹 Runtime validation API +### Runtime validation API Every generated message can be validated at runtime via: - `validate()`, @@ -43,7 +43,7 @@ Every generated message can be validated at runtime via: Errors are represented as structured diagnostics suitable for API responses, logs, or domain exception flows. -### 🔹 Rich domain-oriented constraints +### Rich domain-oriented constraints Beyond simple “required/min/max”, the library includes: - collection rules (`distinct`, `non_empty`), @@ -51,7 +51,7 @@ Beyond simple “required/min/max”, the library includes: - advanced string formats (using regex), - temporal constraints (`PAST`, `FUTURE`). -### 🔹 Extensible architecture +### Extensible architecture Teams can define custom validation options by: - declaring new `.proto` options, From 77bbd9c2c1f79de58284dde04b6f8646d39fc64b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 21 Jan 2026 20:35:43 +0000 Subject: [PATCH 10/26] Remove hallucinated section links --- docs/ToC.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/ToC.md b/docs/ToC.md index 4129c539cd..b6deb3d2c3 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -38,9 +38,6 @@ ### 3.3. String Constraints - [Overview](03-built-in-options/strings/index.md) - [Advanced Patterns](03-built-in-options/strings/pattern-advanced.md) -- [email](03-built-in-options/strings/email.md) -- [hostname](03-built-in-options/strings/hostname.md) -- [uri](03-built-in-options/strings/uri.md) ### 3.4. Numeric Constraints - [Overview](03-built-in-options/numbers/index.md) From 4bf36f081d08e48a84242964e4a040b7637055b8 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Wed, 21 Jan 2026 20:35:55 +0000 Subject: [PATCH 11/26] Remove redundant horizontal lines --- docs/00-intro/target-audience.md | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/docs/00-intro/target-audience.md b/docs/00-intro/target-audience.md index ad707be8da..8d08edd16c 100644 --- a/docs/00-intro/target-audience.md +++ b/docs/00-intro/target-audience.md @@ -6,8 +6,6 @@ constraints on that data. The library serves several overlapping groups: ---- - ## 1. Application Developers Developers building backend services in **Java** or **Kotlin** who need @@ -27,8 +25,6 @@ These developers benefit from: This group typically works directly with generated message builders and calls `validate()` at appropriate points in their workflow. ---- - ## 2. Teams Using the Spine Event Engine Spine Validation is the standard validation mechanism used inside the @@ -46,8 +42,6 @@ For Spine users, this library provides: - rich temporal and domain-focused constraints, - cross-field and message-level validations. ---- - ## 3. Framework and Platform Integrators Engineers who build frameworks, platforms, or infrastructure around Protobuf @@ -69,8 +63,7 @@ For these integrators, Spine Validation provides: ## 4. Library Authors and Tooling Developers -Those who extend `protoc`, build code-gen pipelines, or maintain shared data -models across a large organization can use Spine Validation to: +Those who maintain shared data models across a large organization can use Spine Validation to: - standardize validation behavior for all services, - define custom domain-specific validation options, From 09a9d477b3d25170b5eae566ccdfddbf66b0346a Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 17:03:59 +0000 Subject: [PATCH 12/26] Convert titles to sentence case --- docs/00-intro/index.md | 10 +--- docs/00-intro/philosophy.md | 17 +++--- docs/00-intro/target-audience.md | 16 ++--- docs/01-getting-started/index.md | 5 +- docs/ToC.md | 88 ++++++++++++++-------------- docs/developers-guide/key-modules.md | 2 +- 6 files changed, 62 insertions(+), 76 deletions(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index 8e47de76d4..ce1654c0cc 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -8,9 +8,8 @@ declarative options and then automatically enforces these constraints at runtime The library is part of the Spine toolchain but can also be used independently in any Java/Kotlin backend that models data using Protocol Buffers. ---- -## Key Capabilities +## Key capabilities ### Declarative constraints in `.proto` Validation rules are expressed as Protobuf options such as: @@ -60,9 +59,8 @@ Teams can define custom validation options by: This allows entire organizations to standardize domain validation rules. ---- -## When to Use Spine Validation +## When to use Spine Validation Use Spine Validation if: @@ -79,7 +77,6 @@ It is especially useful in: - systems with rich domain models, - multi-service environments where shared `.proto` models are common. ---- ## Relationship to the Spine Event Engine @@ -96,9 +93,8 @@ framework’s error reporting mechanisms. However, the library is fully standalone and can be used without the rest of the Spine stack. ---- -## What’s Next +## What's next - [Target Audience](target-audience.md) - [Philosophy](philosophy.md) diff --git a/docs/00-intro/philosophy.md b/docs/00-intro/philosophy.md index 31c5384de4..d4645284c5 100644 --- a/docs/00-intro/philosophy.md +++ b/docs/00-intro/philosophy.md @@ -5,9 +5,8 @@ model, generate, and enforce constraints on their data. This section outlines those principles and explains why the library is designed the way it is. ---- -## 1. Validation Belongs to the Model +## 1. Validation belongs to the model In many systems, validation rules drift between: @@ -32,9 +31,8 @@ By placing constraints in `.proto` definitions, validation becomes: This prevents the typical “broken windows” of duplicated or inconsistent validation logic. ---- -## 2. Generated Code Over Reflection +## 2. Generated code over reflection Reflection-based frameworks (Bean Validation, etc.) are convenient but come with: @@ -54,9 +52,8 @@ Benefits: This approach scales better for complex domain models or high-throughput services. ---- -## 3. Declarative, Not Imperative +## 3. Declarative, not imperative Validation is expressed declaratively through Protobuf options: @@ -72,7 +69,7 @@ Declarative rules: Developers describe intent, and the library handles implementation. -## 4. Predictability and Consistency +## 4. Predictability and consistency Validation should behave the same regardless of: @@ -91,7 +88,7 @@ Given the same inputs, you always get the same: Consistency is especially important in distributed systems and domain-driven design contexts. -## 5. Domain-Oriented Constraints +## 5. Domain-oriented constraints Instead of focusing only on primitive checks (min/max, required), the library embraces **domain semantics**, such as: @@ -102,7 +99,7 @@ embraces **domain semantics**, such as: * constraints on identity fields, * collection semantics. -## 6. Extensibility as a First-Class Feature +## 6. Extensibility as a first-class feature Spine Validation is extensible via: @@ -113,7 +110,7 @@ Spine Validation is extensible via: This makes the library a foundation for building consistent validation standards across teams and services. -## 7. No UI or Presentation Layer Concerns +## 7. No UI or presentation layer concerns Spine Validation intentionally does not attempt to validate UI forms, front-end models, or JSON schemas. diff --git a/docs/00-intro/target-audience.md b/docs/00-intro/target-audience.md index 8d08edd16c..fedfd52abe 100644 --- a/docs/00-intro/target-audience.md +++ b/docs/00-intro/target-audience.md @@ -1,4 +1,4 @@ -# Target Audience +# Target audience Spine Validation is designed for developers who model data and APIs using **Protocol Buffers** and need a structured, type-safe way to express and enforce @@ -6,7 +6,7 @@ constraints on that data. The library serves several overlapping groups: -## 1. Application Developers +## 1. Application developers Developers building backend services in **Java** or **Kotlin** who need validation for: @@ -25,7 +25,7 @@ These developers benefit from: This group typically works directly with generated message builders and calls `validate()` at appropriate points in their workflow. -## 2. Teams Using the Spine Event Engine +## 2. Teams using the Spine Event Engine Spine Validation is the standard validation mechanism used inside the **Spine Event Engine**. @@ -42,7 +42,7 @@ For Spine users, this library provides: - rich temporal and domain-focused constraints, - cross-field and message-level validations. -## 3. Framework and Platform Integrators +## 3. Framework and platform integrators Engineers who build frameworks, platforms, or infrastructure around Protobuf often need to enforce rules across: @@ -59,9 +59,7 @@ For these integrators, Spine Validation provides: - hooks for custom validators, - extension points via custom validation options. ---- - -## 4. Library Authors and Tooling Developers +## 4. Library authors and tooling developers Those who maintain shared data models across a large organization can use Spine Validation to: @@ -75,9 +73,8 @@ This group interacts with advanced parts of the library such as: - option policies, - custom code generation units. ---- -## 5. Not the Target Audience (Explicitly) +## 5. Not the target audience (explicitly) The library is **not** designed for: @@ -89,7 +86,6 @@ The library is **not** designed for: Spine Validation is intentionally focused on the **Protobuf → runtime code** pipeline and the domain layer of applications. ---- ## Summary diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md index 57b8967ef0..a1d5708927 100644 --- a/docs/01-getting-started/index.md +++ b/docs/01-getting-started/index.md @@ -1,4 +1,4 @@ -# Getting Started +# Getting started This section helps you set up Spine Validation, define your first validated Protobuf model, and see validation in action in Java and Kotlin. @@ -8,7 +8,6 @@ If you are new to the library, read the short overview first: - Who this is for → [Target Audience](../00-intro/target-audience.md) - Design principles → [Philosophy](../00-intro/philosophy.md) ---- ## What you’ll learn @@ -27,7 +26,6 @@ If your project already generates Java/Kotlin sources from `.proto` files, you Spine Validation integrates into the build to generate and inject validation logic into the code produced by `protoc`. ---- ## Quick path @@ -45,7 +43,6 @@ the code produced by `protoc`. 4) Use the generated API - Validate on builder `build()` or call `validate()` explicitly. See [Your First Validated Model](first-model.md). ---- ## What’s next diff --git a/docs/ToC.md b/docs/ToC.md index b6deb3d2c3..3645eb7b9b 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -1,26 +1,26 @@ -# Spine Validation — Table of Contents +# Spine Validation — Table of contents ## 0. Introduction - [Overview](00-intro/index.md) -- [Target Audience](00-intro/target-audience.md) +- [Target audience](00-intro/target-audience.md) - [Philosophy](00-intro/philosophy.md) -## 1. Getting Started -- [Getting Started](01-getting-started/index.md) +## 1. Getting started +- [Getting started](01-getting-started/index.md) - [Installation](01-getting-started/installation.md) -- [Your First Validated Model](01-getting-started/first-model.md) -- [Validation Workflow](01-getting-started/workflow.md) +- [Your first validated model](01-getting-started/first-model.md) +- [Validation workflow](01-getting-started/workflow.md) ## 2. Concepts -- [Concepts Overview](02-concepts/index.md) -- [Validation Options Overview](02-concepts/options-overview.md) -- [The Validation Engine](02-concepts/validation-engine.md) -- [Integration with Protobuf & Compiler](02-concepts/protobuf-integration.md) +- [Concepts overview](02-concepts/index.md) +- [Validation options overview](02-concepts/options-overview.md) +- [The validation engine](02-concepts/validation-engine.md) +- [Integration with Protobuf & Spine Compiler](02-concepts/protobuf-integration.md) -## 3. Built-in Validation Options -- [Built-in Options Overview](03-built-in-options/index.md) +## 3. Built-in validation options +- [Built-in options overview](03-built-in-options/index.md) -### 3.1. Field Constraints +### 3.1. Field constraints - [Overview](03-built-in-options/fields/index.md) - [required](03-built-in-options/fields/required.md) - [pattern](03-built-in-options/fields/pattern.md) @@ -29,60 +29,60 @@ - [length/size](03-built-in-options/fields/length-size.md) - [unique](03-built-in-options/fields/unique.md) -### 3.2. Collection Constraints +### 3.2. Collection constraints - [Overview](03-built-in-options/collections/index.md) - [non_empty](03-built-in-options/collections/non-empty.md) - [distinct](03-built-in-options/collections/distinct.md) -- [collection size](03-built-in-options/collections/collection-size.md) +- [Collection size](03-built-in-options/collections/collection-size.md) -### 3.3. String Constraints +### 3.3. String constraints - [Overview](03-built-in-options/strings/index.md) -- [Advanced Patterns](03-built-in-options/strings/pattern-advanced.md) +- [Advanced patterns](03-built-in-options/strings/pattern-advanced.md) -### 3.4. Numeric Constraints +### 3.4. Numeric constraints - [Overview](03-built-in-options/numbers/index.md) -- [Numeric Bounds](03-built-in-options/numbers/numeric-bounds.md) +- [Numeric bounds](03-built-in-options/numbers/numeric-bounds.md) -### 3.5. Temporal Constraints +### 3.5. Temporal constraints - [Overview](03-built-in-options/temporal/index.md) - [when](03-built-in-options/temporal/when.md) -- [Timestamp & Duration](03-built-in-options/temporal/timestamp-duration.md) +- [Timestamp & duration](03-built-in-options/temporal/timestamp-duration.md) -### 3.6. Message-level Constraints +### 3.6. Message-level constraints - [Overview](03-built-in-options/message/index.md) - [required_for](03-built-in-options/message/required-for.md) -- [Nested Validation](03-built-in-options/message/nested-validation.md) -- [Cross-field Validation](03-built-in-options/message/cross-field.md) +- [Nested validation](03-built-in-options/message/nested-validation.md) +- [Cross-field validation](03-built-in-options/message/cross-field.md) -## 4. Using Validation in Code +## 4. Using validation in code - [Overview](04-using-validation/index.md) -- [Validating Messages](04-using-validation/validating-messages.md) -- [Handling Errors](04-using-validation/handling-errors.md) -- [Kotlin Usage](04-using-validation/kotlin-usage.md) -- [Framework Integration](04-using-validation/framework-integration.md) +- [Validating messages](04-using-validation/validating-messages.md) +- [Handling errors](04-using-validation/handling-errors.md) +- [Kotlin usage](04-using-validation/kotlin-usage.md) +- [Framework integration](04-using-validation/framework-integration.md) -## 5. Configuration & Tooling +## 5. Configuration & tooling - [Overview](05-configuration/index.md) -- [Compiler Configuration](05-configuration/compiler.md) -- [Library Modules](05-configuration/modules.md) -- [Debugging Generated Code](05-configuration/debugging.md) +- [Compiler configuration](05-configuration/compiler.md) +- [Library modules](05-configuration/modules.md) +- [Debugging generated code](05-configuration/debugging.md) -## 6. Extending Validation +## 6. Extending validation - [Overview](06-extending/index.md) - [Architecture](06-extending/architecture.md) -- [Custom Validation Options](06-extending/custom-options.md) -- [Custom Runtime Validators](06-extending/custom-runtime-validators.md) +- [Custom validation options](06-extending/custom-options.md) +- [Custom runtime validators](06-extending/custom-runtime-validators.md) -## 7. Recipes (Cookbook) +## 7. Recipes (cookbook) - [Overview](07-recipes/index.md) - [Domain IDs](07-recipes/domain-ids.md) -- [Common Cases](07-recipes/common-cases.md) -- [Temporal Logic](07-recipes/temporal-logic.md) -- [Cross-field Logic](07-recipes/cross-field-logic.md) -- [API Validation](07-recipes/api-validation.md) +- [Common cases](07-recipes/common-cases.md) +- [Temporal logic](07-recipes/temporal-logic.md) +- [Cross-field logic](07-recipes/cross-field-logic.md) +- [API validation](07-recipes/api-validation.md) ## 8. Reference -- [Reference Overview](08-reference/index.md) -- [List of Validation Options](08-reference/options.md) -- [Java/Kotlin API Index](08-reference/api.md) +- [Reference overview](08-reference/index.md) +- [List of validation options](08-reference/options.md) +- [Java/Kotlin API index](08-reference/api.md) - [Glossary](08-reference/glossary.md) diff --git a/docs/developers-guide/key-modules.md b/docs/developers-guide/key-modules.md index 4de498a725..f0d6b9bfc1 100644 --- a/docs/developers-guide/key-modules.md +++ b/docs/developers-guide/key-modules.md @@ -1,5 +1,5 @@ -### Key Modules +### Key modules | Module | Description | |-----------|----------------------------------------------------------------------| From 90bc7dbc76646389badf9fe72beaf4108a32ed6e Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 17:16:31 +0000 Subject: [PATCH 13/26] Allow empty lines after headers --- docs/00-intro/index.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index ce1654c0cc..c46fe76049 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -12,6 +12,7 @@ in any Java/Kotlin backend that models data using Protocol Buffers. ## Key capabilities ### Declarative constraints in `.proto` + Validation rules are expressed as Protobuf options such as: - `required` @@ -23,8 +24,9 @@ Validation rules are expressed as Protobuf options such as: This keeps validation close to the data model and ensures it evolves together with it. -### Generated validators -The Spine Compiler plugin processes your Protobuf model and generates: +### Generated validation code + +The Validation Plugin for Spine Compiler processes your Protobuf model and generates: - validation code for messages and builders, - runtime checks, @@ -43,14 +45,16 @@ Errors are represented as structured diagnostics suitable for API responses, logs, or domain exception flows. ### Rich domain-oriented constraints + Beyond simple “required/min/max”, the library includes: -- collection rules (`distinct`, `non_empty`), +- collection rules (`distinct`, `required`), - nested and cross-field validation, - advanced string formats (using regex), - temporal constraints (`PAST`, `FUTURE`). ### Extensible architecture + Teams can define custom validation options by: - declaring new `.proto` options, @@ -73,7 +77,7 @@ Use Spine Validation if: It is especially useful in: - backend services (Java/Kotlin), -- event-driven and CQRS systems, +- message-driven systems, - systems with rich domain models, - multi-service environments where shared `.proto` models are common. From 78da3fb30edaf11e6cecbad038ce53eefc8dde0b Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 17:43:21 +0000 Subject: [PATCH 14/26] Fix the proto options example Also: * Update the reference to Jakarta Validation, addressing the recent name change. --- docs/00-intro/philosophy.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/00-intro/philosophy.md b/docs/00-intro/philosophy.md index d4645284c5..0efdab7c6d 100644 --- a/docs/00-intro/philosophy.md +++ b/docs/00-intro/philosophy.md @@ -34,7 +34,7 @@ validation logic. ## 2. Generated code over reflection -Reflection-based frameworks (Bean Validation, etc.) are convenient but come with: +Reflection-based frameworks (Jakarta Validation, etc.) are convenient but come with: - runtime penalties, - fragile metadata conventions (annotations, naming), @@ -48,17 +48,18 @@ Benefits: - fast, predictable runtime without reflection, - validation logic is type-safe, - errors in validation configuration are caught early, -- generated validators integrate naturally into the Protobuf builder model. +- generated validation code integrates naturally into the Protobuf builder model. This approach scales better for complex domain models or high-throughput services. - ## 3. Declarative, not imperative Validation is expressed declaratively through Protobuf options: ```proto -string email = 1 [(pattern).email = true, (required) = true]; +message Name { + string value = 1 [(required) = true, (pattern).regex = "^[A-Za-z ]+$"]; +} ``` Declarative rules: * are concise, @@ -90,7 +91,7 @@ design contexts. ## 5. Domain-oriented constraints -Instead of focusing only on primitive checks (min/max, required), the library +Instead of focusing only on primitive checks (`min`/`max`, `required`), the library embraces **domain semantics**, such as: * temporal rules (past, future), @@ -118,7 +119,7 @@ front-end models, or JSON schemas. Its focus is entirely on: ``` -Protobuf → generated Java/Kotlin → domain logic +Protobuf → generated Java/Kotlin/TypeScript → domain logic ``` Everything else (frontend validation, OpenAPI, view models) should build on top From 2d1f0aac8f9dde823824d49ecb9e10a95cff0ab7 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 17:55:11 +0000 Subject: [PATCH 15/26] Use braces around proto options --- docs/00-intro/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index c46fe76049..07aea6c51b 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -15,10 +15,10 @@ in any Java/Kotlin backend that models data using Protocol Buffers. Validation rules are expressed as Protobuf options such as: -- `required` -- `min` / `max` -- `pattern` -- `when.in = PAST | FUTURE` +- `(required)` +- `(min)` / `(max)` +- `(pattern)` +- `(when).in = PAST | FUTURE` - cross-field rules and message-level constraints This keeps validation close to the data model and ensures it evolves together @@ -48,7 +48,7 @@ logs, or domain exception flows. Beyond simple “required/min/max”, the library includes: -- collection rules (`distinct`, `required`), +- collection rules (`(distinct)`, `(required)`), - nested and cross-field validation, - advanced string formats (using regex), - temporal constraints (`PAST`, `FUTURE`). From 0300e790af9d37ec1f8de591238b6a880c6b7ad2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:15:23 +0000 Subject: [PATCH 16/26] Temporarily add source code of proto options --- docs/options.proto | 1566 +++++++++++++++++++++++++++++++++++++++ docs/time_options.proto | 106 +++ 2 files changed, 1672 insertions(+) create mode 100644 docs/options.proto create mode 100644 docs/time_options.proto diff --git a/docs/options.proto b/docs/options.proto new file mode 100644 index 0000000000..31f02bfa67 --- /dev/null +++ b/docs/options.proto @@ -0,0 +1,1566 @@ +/* + * Copyright 2024, 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +syntax = "proto3"; + +// API Note on Packaging +// --------------------- +// We do not define the package for this file to allow shorter options for user-defined types. +// This allows to write: +// +// option (internal) = true; +// +// instead of: +// +// option (spine.base.internal) = true; +// + +// Custom Type Prefix Option +// ------------------------- +// The custom `type_url_prefix` option allows to define specify custom type URL prefix for messages +// defined in a proto file. This option is declared in this file. Other proto files must import +// `options.proto` to be able to specify custom type URL prefix. +// +// It is recommended that the import statement is provided before the line with `type_url_prefix` +// option to make it obvious that custom option is defined in the imported file. +// +// For example: +// +// syntax = "proto3"; +// +// package my.package; +// +// import "spine/options.proto"; +// +// option (type_url_prefix) = "type.example.org"; +// + +option (type_url_prefix) = "type.spine.io"; +option java_multiple_files = true; +option java_outer_classname = "OptionsProto"; +option java_package = "io.spine.option"; + +import "google/protobuf/descriptor.proto"; + +// +// Reserved Range of Option Field Numbers +// -------------------------------------- +// Spine Options use the range of option field numbers from the internal range reserved for +// individual organizations. For details of custom Protobuf options and this range please see: +// +// https://developers.google.com/protocol-buffers/docs/proto#customoptions +// +// The whole range reserved for individual organizations is 50000-99999. +// The range used by Spine Options is 73812-75000. +// In order to prevent number collision with custom options used by a project based on Spine, +// numbers for custom options defined in this project should be in the range 50000-73811 or +// 75001-99999. +// + +extend google.protobuf.FieldOptions { + + // Field Validation Constraints + //----------------------------- + // For constraints defined via message-based types, please see documentation of corresponding + // message types. + // + + // The option to mark a field as required. + // + // When this option is set: + // + // 1. For message or enum fields, the field must be set to a non-default instance. + // 2. For `string` and `bytes` fields, the value must be set to a non-empty string or an array. + // 3. For repeated fields and maps, at least one element must be present. + // + // Other field types are not supported by the option. + // + // Unlike the `required` keyword in Protobuf 2, this option does not affect message + // serialization or deserialization. Even if a message content violates the requirement + // set by the option, it would still be a valid message for the Protobuf library. + // + // Example: Using `(required)` field validation constraint. + // + // message MyOuterMessage { + // MyMessage field = 1 [(required) = true]; + // } + // + bool required = 73812; + + // See `IfMissingOption` + IfMissingOption if_missing = 73813; + + // Reserved 73814 and 73815 for deleted options `decimal_max` and `decimal_min`. + + // A higher boundary to the range of values of a number. + MaxOption max = 73816; + + // A lower boundary to the range of values of a number. + MinOption min = 73817; + + // 73818 reserved for the (digits) option. + + // 73819 reserved for the (when) option. + + // See `PatternOption`. + PatternOption pattern = 73820; + + // Enables in-depth validation for fields that refer to a message. + // + // This option applies only to fields that reference a message: + // + // 1. Singular message fields. + // 2. Repeated fields of message types. + // 3. Map fields with message types as values. + // + // When set to `true`, the field is valid only if its value satisfies the validation + // constraints defined in the corresponding message type: + // + // 1. For singular message fields: the message must meet its constraints. + // + // Note: default instances are considered valid even if the message has required fields. + // In such cases, it is unclear whether the field is set with an invalid instance or + // simply unset. + // + // Example: + // + // ``` + // // Note that the default instance of `Address` is not valid because the `value` field + // // is mandatory. + // message Address { + // string value = 1 [(required) = true]; + // } + // + // // However, the default instance of `Address` in `Student.address` is valid, despite + // // having `(validate)` constraint that forces the message to meet its constraints. + // // Since the `address` field is optional for `Student`, `(validate)` would allow + // // default instances for this field treating them as "no value set". + // message Student { + // Address address = 1 [(validate) = true]; // implicit `(required) = false`. + // } + // + // // Make the validated field required to avoid this behavior. In this case, `(validate)` + // // continues to bypass default instances, but the `(required)` option will report them. + // message Student { + // Address address = 1 [(validate) = true, (required) = true]; + // } + // ``` + // + // 2. For repeated fields: every element in the repeated field must meet the constraints + // of its message type. + // + // Example: + // + // ``` + // // Note that the default instance of `PhoneNumber` is not valid because the `value` + // // field is mandatory. + // message PhoneNumber { + // string value = 1 [(required) = true, (pattern).regex = "^\+?[0-9\s\-()]{1,30}$"]; + // } + // + // // In contrast to singular fields, the default instances in `repeated` will also be + // // reported by the `(validate)` constraint, with those that do not match the pattern. + // message Student { + // repeated PhoneNumber number = 1 [(validate) = true]; + // } + // ``` + // + // 3. For map fields: each value in the map must meet the constraints of message type. + // Note: Protobuf does not allow messages to be used as map keys. + // + // Example: + // + // ``` + // // Note that the default instance of `PhoneNumber` is not valid because the `value` + // // field is mandatory. + // message PhoneNumber { + // string value = 1 [(required) = true, (pattern).regex = "^\+?[0-9\s\-()]{1,30}$"]; + // } + // + // // In contrast to singular fields, the default instances in `map` values will also be + // // reported by the `(validate)` constraint, with those that do not match the pattern. + // message Contacts { + // map map = 1 [(validate) = true]; + // } + // ``` + // + // If the field contains `google.protobuf.Any`, the option will first attempt to unpack + // the enclosed message, and only then validate it. However, unpacking is not always possible: + // + // 1. The default instance of `Any` is always valid because there is nothing to unpack + // and validate. + // 2. Instances with type URLs that are unknown to the application are also valid. + // + // Unpacking requires a corresponding Java class to deserialize the message, but if + // the application does not recognize the type URL, it has no way to determine which + // class to use. + // + // Such may happen when the packed message comes from a newer app version, an external + // system, or is simply not included in the application’s dependencies. + // + bool validate = 73821; + + // See `IfInvalidOption`. + IfInvalidOption if_invalid = 73822 [deprecated = true]; + + // See `GoesOption`. + GoesOption goes = 73823; + + // Indicates that a field can only be set once. + // + // This option allows the target field to accept assignments only if one of the following + // conditions is met: + // + // 1. The current field value is the default for its type. + // Refer to the official docs on default values: https://protobuf.dev/programming-guides/proto3/#default. + // 2. The current field value equals to the proposed new value. + // + // The option can be applied to the following singular field types: + // + // - any message or enum type; + // - any numeric type; + // - `bool`, `string` and `bytes`. + // + // Repeated fields, maps, and fields with explicit `optional` cardinality are not supported. + // Such declarations will lead to build-time errors. For more information on field cardinality, + // refer to the official docs: https://protobuf.dev/programming-guides/proto3/#field-labels. + // + // Assigning a value to a message field can be done in various ways in the generated code. + // It depends on the target language and specific implementation of `protoc`. This option + // doesn't enforce field immutability at the binary representation level. Also, it does not + // prevent the use of Protobuf utilities that can construct new messages without using field + // setters or properties. The primary purpose of this option is to support standard use cases, + // such as assigning values through setters or retrieving them during data merging. + // + // For example, let's take a look on how it works for the generated Java code. The following + // use cases are supported and validated by the option: + // + // 1. Assigning a field value using the field setter. + // 2. Assigning a field value using the field descriptor. + // 3. Merging data from another instance of the message class. + // 4. Merging data from a message's binary representation. + // 5. Merging specific fields (available for Message fields). + // + // Unsupported Java use cases include: + // + // 1. Constructing a message using the `DynamicMessage` class. + // 2. Merging data from messages provided by third-party Protobuf implementations. + // 3. Clearing a specific field or an entire message. + // + // For unsupported scenarios, the option performs no validation. It does not throw errors + // or print warnings. + // + // A typical use case involves a field, such as an ID, which remains constant throughout + // the lifecycle of an entity. + // + // Example: using `(set_once)` field validation constraint. + // + // message User { + // UserId id = 1 [(set_once) = true]; + // } + // + // Once set, the `id` field cannot be changed. + // + // Use `(if_set_again).error_msg` option to specify a custom error message that will be used for + // composing the error upon attempting to re-assign the field value. Refer to the documentation + // for the corresponding option for an example of its usage. + // + bool set_once = 73824; + + // The option to enforce uniqueness for collection fields. + // + // When the option is set to `true`, the behavior is as follows: + // + // 1. For `repeated` fields: all elements must be unique. + // 2. For `map` fields: while the map keys are inherently unique, all associated values + // must also be unique. + // + // Other field types are not supported. + // + // Uniqueness is determined by comparing the elements themselves, using their full equality. + // For example, in Java, it is defined by the `equals()` method. No special cases are applied, + // such as comparing only specific fields like IDs. + // + // Example: using `(distinct)` constraint for a `repeated` field. + // + // message Blizzard { + // + // // All snowflakes must be unique in this blizzard. + // // + // // Attempting to add a snowflake that is equal to an existing one would result + // // in a constraint violation error. + // // + // repeated Snowflake snowflakes = 1 [(distinct) = true]; + // } + // + // Example: using `(distinct)` constraint for a `map` field. + // + // message UniqueEmails { + // + // // The associated email values must be unique. + // // + // // Attempting to add a key/value pair where the `Email` value duplicates + // // an existing one would result in a constraint violation error. + // // + // map emails = 1 [(distinct) = true]; + // } + // + bool distinct = 73825; + + // Reserved 73826 for deleted `range` option, which had `string` type. + + // Defines the error message used if a `set_once` field is set again. + // + // Applies only to the fields marked as `set_once`. + // + IfSetAgainOption if_set_again = 73827; + + // Defines the error message used if a `distinct` field has duplicates. + // + // Applies only to the repeated fields marked as `distinct`. + // + IfHasDuplicatesOption if_has_duplicates = 73828; + + // The option to indicate that a numeric field is required to have a value which belongs + // to the specified bounded range. + // + // For unbounded ranges, please use `(min)` and `(max) options. + // + RangeOption range = 73829; + + // Reserved 73830 to 73849 for future validation options. + + // API Annotations + //----------------- + + // Indicates a field which is internal to Spine, not part of the public API, and should not be + // used by users of the framework. + // + // If you plan to implement an extension of the framework, which is going to be + // wired into the framework, you may use the internal parts. Please consult with the Spine + // team, as the internal APIs do not have the same stability API guarantee as public ones. + // + bool internal = 73850; + + // Reserved 73851 for the deleted SPI option. + + // Indicates a field that can change at any time, and has no guarantee of API stability and + // backward-compatibility. + // + // Usage guidelines: + // 1. This annotation is used only on public API. Internal interfaces should not use it. + // 2. This annotation can only be added to new API. Adding it to an existing API is considered + // API-breaking. + // 3. Removing this annotation from an API gives it stable status. + // + bool experimental = 73852; + + // Signifies that a public API is subject to incompatible changes, or even removal, in a future + // release. + // + // An API bearing this annotation is exempt from any compatibility guarantees made by its + // containing library. Note that the presence of this annotation implies nothing about the + // quality of the API in question, only the fact that it is not "API-frozen." + // It is generally safe for applications to depend on beta APIs, at the cost of + // some extra work during upgrades. + // + bool beta = 73853; + + // Marks an entity state field as column. + // + // The column fields are stored separately from the entity record and can be specified as + // filtering criteria during entity querying. + // + // The column field should be declared as follows: + // + // message UserProfile { + // ... + // int32 year_of_registration = 8 [(column) = true]; + // } + // + // The `year_of_registration` field value can then be used as query parameter when reading + // entities of `UserProfile` type from the server side. + // + // The value of a column field can be updated in two ways: + // + // 1. In the receptors of the entity, just like any other part of entity state. + // 2. Using the language-specific tools like `EntityWithColumns` interface in Java. + // + // All column fields are considered optional by the framework. + // + // Currently, only entities of projection and process manager type are + // eligible for having columns (see `EntityOption`). + // For all other message types the column declarations are ignored. + // + // The `repeated` and `map` fields cannot be columns. + // + bool column = 73854; + + // Reserved 73855 to 73890 for future options. + + // Reserved 73900 for removed `by` option. +} + +extend google.protobuf.OneofOptions { + + // Deprecated: use the `(choice)` option instead. + bool is_required = 73891 [deprecated = true]; + + // Controls whether a `oneof` group must always have one of its fields set. + ChoiceOption choice = 73892; + + // Reserved 73893 to 73899 for future options. +} + +extend google.protobuf.MessageOptions { + + // Validation Constraints + //------------------------ + + // The default validation error message. + // + // Please note, this option is intended for INTERNAL USE only. It applies to message types + // that extend `FieldOptions` and is not intended for external usage. + // + // If a validation option detects a constraint violation and no custom error message is defined + // for that specific option, it will fall back to the message specified by `(default_message)`. + // + // For example, here is how to declare the default message for `(goes)` option: + // + // ``` + // message GoesOption { + // // The default error message. + // option (default_message) = "The field `${goes.companion}` must also be set when `${field.path}` is set."; + // } + // ``` + // + // Note: The placeholders available within `(default_message)` depend solely on the particular + // validation option that uses it. Each option may define its own set of placeholders, or none. + // + string default_message = 73901 [(internal) = true]; + + // Deprecated: use the `(require)` option instead. + string required_field = 73902 [deprecated = true]; + + // See `EntityOption`. + EntityOption entity = 73903; + + // An external validation constraint for a field. + // + // WARNING: This option is deprecated and is scheduled for removal in Spine v2.0.0. + // + // Allows to re-define validation constraints for a message when its usage as a field of + // another type requires alternative constraints. This includes definition of constraints for + // a message which does not have them defined within the type. + // + // A target field of an external constraint should be specified using a fully-qualified + // field name (e.g. `mypackage.MessageName.field_name`). + // + // Example: defining external validation constraint. + // + // package io.spine.example; + // + // // Defines a change in a string value. + // // + // // Both of the fields of this message are not `required` to be able to describe + // // a change from empty value to non-empty value, or from a non-empty value to + // // an empty string. + // // + // message StringChange { + // + // // The value of the field that's changing. + // string previous_value = 1; + // + // // The new value of the field. + // string new_value = 2; + // } + // + // // A command to change a name of a task. + // // + // // The task has a non-empty name. A new name cannot be empty. + // // + // message RenameTask { + // + // // The ID of the task to rename. + // string task_id = 1; + // + // // Instruction for changing the name. + // // + // // The value of `change.previous_value` is the current name of the task. + // // It cannot be empty. + // // + // // The value of `change.new_value` is the new name of the task. + // // It cannot be empty either. + // // + // StringChange change = 1 [(validate) = true]; + // } + // + // // External validation constraint for both fields of the `StringChange` message + // // in the scope of the `RenameTask` command. + // // + // message RequireTaskNames { + // option (constraint_for) = "spine.example.RenameTask.change"; + // + // string previous_value = 1 [(required) = true]; + // string new_value = 2 [(required) = true]; + // } + // + // NOTE: A target field for an external validation constraint must be have the option `(validate)` + // set to `true`. See the definition of the `RenameTask.change` field in the example + // above. If there is no such option defined, or it is set to `false`, the external + // constraint will not be applied. + // + // External validation constraints can be applied to fields of several types. + // To do so, separate fully-qualified references to these fields with comma. + // + // Example: external validation constraints for multiple fields. + // + // // External validation constraint for requiring a new value in renaming commands. + // message RequireNewName { + // option (constraint_for) = "spine.example.RenameTask.change," + // "spine.example.RenameProject.change,"; + // "spine.example.UpdateComment.text_change; + // + // string new_value = 1 [(required) = true]; + // } + // + // NOTE: An external validation constraint for a field must be defined only once. + // Spine Model Compiler does not check such an "overwriting". + // See the issue: https://github.com/SpineEventEngine/base/issues/318. + // + string constraint_for = 73904 [deprecated = true]; + + // Reserved 73905 to 73910 for future validation options. + + // API Annotations + //----------------- + + // Indicates a type usage of which is restricted in one of the following ways. + // + // 1. This type is internal to the Spine Event Engine framework. It is not a part of + // the public API, and must not be used by framework users. + // + // 2. The type is internal to a bounded context, artifact of which exposes the type to + // the outside world (presumably for historical reasons). + // + // The type with such an option can be used only inside the bounded context which declares it. + // + // The type must not be used neither for inbound (i.e. being sent to the bounded context + // which declares this type) nor for outbound communication (i.e. being sent by this + // bounded context outside). + // + // An attempt to violate these usage restrictions will result in a runtime error. + // + bool internal_type = 73911; + + // Indicates a file which contains elements of Service Provider Interface (SPI). + bool SPI_type = 73912; + + // Indicates a public API that can change at any time, and has no guarantee of + // API stability and backward-compatibility. + bool experimental_type = 73913; + + // Signifies that a public API is subject to incompatible changes, or even removal, + // in a future release. + bool beta_type = 73914; + + // Specifies a characteristic inherent in the the given message type. + // + // Example: using `(is)` message option. + // + // message CreateProject { + // option (is).java_type = "ProjectCommand"; + // + // // Remainder omitted. + // } + // + // In the example above, `CreateProject` message is a `ProjectCommand`. + // + // To specify a characteristic for every message in a `.proto` file, + // please use `(every_is)` file option. + // + // If both `(is)` and `(every_is)` options are applicable for a type, both are applied. + // + // When targeting Java, specify the name of a Java interface to be implemented by this + // message via `(is).java_type`. + // + IsOption is = 73915; + + // Reserved 73916 to 73921 for future API annotation options. + + // Reserved 73922 for removed `enrichment_for` option. + + // Specifies the natural ordering strategy for this type. + // + // Code generators should generate language-specific comparisons based on the field paths. + // + // Runtime comparators may use the reflection API to compare field values. + // + CompareByOption compare_by = 73923; + + // The constraint to require at least one of the fields or combinations of fields. + RequireOption require = 73924; + + // Reserved 73925 to 73938 for future options. + + // Reserved 73939 and 73940 for the deleted options `events` and `rejections`. +} + +extend google.protobuf.FileOptions { + + // Specifies a type URL prefix for all types within a file. + // + // This type URL will be used when packing messages into `Any`. + // See `any.proto` for more details. + // + string type_url_prefix = 73941; + + // Indicates a file which contains types usage of which is restricted. + // + // For more information on such restrictions please see the documentation of + // the type option called `internal_type`. + // + // If a file contains a declaration of a `service`, this option will NOT be applied to it. + // A service is not a data type, and therefore, this option does not apply to it. + // Internal services are not supported. + // + bool internal_all = 73942; + + // Indicates a file which contains elements of Service Provider Interface (SPI). + // + // This option applies to messages, enums, and services. + // + bool SPI_all = 73943; + + // Indicates a file declaring public data type API which that can change at any time, + // has no guarantee of API stability and backward-compatibility. + // + // If a file contains a declaration of a `service`, this option will NOT be applied to it. + // A service is not a data type, and therefore, this option does not apply to it. + // Experimental services are not supported. + // + bool experimental_all = 73944; + + // Signifies that a public data type API is subject to incompatible changes, or even removal, + // in a future release. + // + // If a file contains a declaration of a `service`, this option will NOT be applied to it. + // A service is not a data type, and therefore, this option does not apply to it. + // Beta services are not supported. + // + bool beta_all = 73945; + + // Specifies a characteristic common for all the message types in the given file. + // + // Example: marking all the messages using the `(every_is)` file option. + // ``` + // option (every_is).java_type = "ProjectCommand"; + // + // message CreateProject { + // // ... + // + // message WithAssignee { + // // ... + // } + // } + // + // message DeleteProject { /*...*/ } + // ``` + // + // In the example above, `CreateProject`, `CreateProject.WithAssignee`, and `DeleteProject` + // messages are `ProjectCommand`-s. + // + // To specify a characteristic for a single message, please use `(is)` message option. + // If both `(is)` and `(every_is)` options are applicable for a type, both are applied. + // + // When targeting Java, specify the name of a Java interface to be implemented by these + // message types via `(every_is).java_type`. + // + EveryIsOption every_is = 73946; + + // Reserved 73947 to 73970 for future use. +} + +extend google.protobuf.ServiceOptions { + + // Indicates that the service is a part of Service Provider Interface (SPI). + bool SPI_service = 73971; + + // Reserved 73971 to 73980. +} + +// Reserved 73981 to 74000 for other future Spine Options numbers. + +// +// Validation Option Types +//--------------------------- + +// Defines the error message used if a `required` field is not set. +// +// Applies only to the fields marked as `required`. +// +message IfMissingOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}`" + " of the type `${field.type}` must have a non-default value."; + + // A user-defined validation error format message. + // + // Use `error_msg` instead. + // + string msg_format = 1 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.type}` – the fully qualified name of the field type. + // 3. `${parent.type}` – the fully qualified name of the validated message. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + // Example: using the `(if_missing)` option. + // + // message Student { + // Name name = 1 [(required) = true, + // (if_missing).error_msg = "The `${field.path}` field is mandatory for `${parent.type}`."]; + // } + // + string error_msg = 2; +} + +// Indicates that the numeric field must be greater than or equal to the specified value. +// +// The option supports all singular and repeated numeric fields. +// +message MinOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}`" + " must be ${min.operator} ${min.value}. The passed value: `${field.value}`."; + + // The string representation of the minimum field value. + // + // ## Integer and floating-point values + // + // A minimum value for an integer field must use an integer number. Specifying a decimal + // number is not allowed, even if it has no fractional part (e.g., `5.0` is invalid). + // + // A minimum value for a floating-point field must use a decimal separator (`.`), even if + // the value has no fractional part. An exponent part represented by `E` or `e`, followed + // by an optional sign and digits is allowed (e.g., `1.2E3`, `0.5e-2`). + // + // Example: defining minimum values for integer and floating-point fields. + // + // message Measurements { + // int32 temperature = 1 [(min).value = "0"]; + // uint32 mass = 2 [(min).value = "5"]; + // float degree = 3 [(min).value = "0.0"]; + // double angle = 4 [(min).value = "30.0"]; + // float pressure = 5 [(min).value = "950.0E-2"]; + // } + // + // ## Field Type Limitations + // + // A minimum value must not fall below the limits of the field type. + // + // Example: invalid values that fall below the field type limits. + // + // message OverflowMeasurements { + // float pressure = 1 [(min).value = "-5.5E38"]; // Falls below the `float` minimum. + // uint32 mass = 2 [(min).value = "-5"]; // Falls below the `uint32` minimum. + // } + // + // ## Field references + // + // Instead of numeric literals, you can reference another numeric field. + // At runtime, the field’s value will be used as the bound. Nested fields are supported. + // + // Example: defining minimum values using field references. + // + // message Measurements { + // + // int32 min_length = 1; + // int32 length = 2 [(min).value = "min_length"]; + // + // Limits limits = 3; + // int32 temperature = 4 [(min).value = "limits.min_temperature"]; + // float pressure = 5 [(min).value = "limits.min_pressure"]; + // } + // + // message Limits { + // int32 min_temperature = 1; + // float min_pressure = 2; + // } + // + // Note: Field type compatibility is not required in this case; the value is + // automatically converted. However, only numeric fields can be referenced. + // Repeated and map fields are not supported. + // + string value = 1; + + // Specifies if the field should be strictly greater than the specified minimum. + // + // The default value is false, i.e. the bound is inclusive. + // + bool exclusive = 2; + + // A user-defined validation error format message. + string msg_format = 3 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.value}` - the field value. + // 2. `${field.path}` – the field path. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${min.value}` – the specified minimum `value`. For referenced fields, the actual + // field value is also printed in round brackets along with the reference itself. + // 6. `${min.operator}` – if `exclusive` is set to `true`, this placeholder equals to ">". + // Otherwise, ">=". + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 4; +} + +// Indicates that the numeric field must be less than or equal to the specified value. +// +// The option supports all singular and repeated numeric fields. +// +message MaxOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}`" + " must be ${max.operator} ${max.value}. The passed value: `${field.value}`."; + + // The string representation of the maximum field value. + // + // ## Integer and floating-point values + // + // A maximum value for an integer field must use an integer number. Specifying a decimal + // number is not allowed, even if it has no fractional part (e.g., `5.0` is invalid). + // + // A maximum value for a floating-point field must use a decimal separator (`.`), even if + // the value has no fractional part. An exponent part represented by `E` or `e`, followed + // by an optional sign and digits is allowed (e.g., `1.2E3`, `0.5e-2`). + // + // Example: defining maximum values for integer and floating-point fields. + // + // message Measurements { + // int32 temperature = 1 [(max).value = "270"]; + // uint32 mass = 2 [(max).value = "1200"]; + // float degree = 3 [(max).value = "360.0"]; + // double angle = 4 [(max).value = "90.0"]; + // float pressure = 5 [(max).value = "1050.0E-2"]; + // } + // + // ## Field Type Limitations + // + // A maximum value must not exceed the limits of the field type. + // + // Example: invalid values that exceed the field type limits. + // + // message OverflowMeasurements { + // float pressure = 1 [(min).value = "5.5E38"]; // Exceeds the `float` maximum. + // int32 mass = 2 [(min).value = "2147483648"]; // Exceeds the `int32` maximum. + // } + // + // ## Field references + // + // Instead of numeric literals, you can reference another numeric field. + // At runtime, the field’s value will be used as the bound. Nested fields are supported. + // + // Example: defining maximum values using field references. + // + // message Measurements { + // + // int32 max_length = 1; + // int32 length = 2 [(max).value = "max_length"]; + // + // Limits limits = 3; + // int32 temperature = 4 [(max).value = "limits.max_temperature"]; + // float pressure = 5 [(max).value = "limits.max_pressure"]; + // } + // + // message Limits { + // int32 max_temperature = 1; + // float max_pressure = 2; + // } + // + // Note: Field type compatibility is not required in this case; the value is + // automatically converted. However, only numeric fields can be referenced. + // Repeated and map fields are not supported. + // + string value = 1; + + // Specifies if the field should be strictly less than the specified maximum + // + // The default value is false, i.e. the bound is inclusive. + // + bool exclusive = 2; + + // A user-defined validation error format message. + string msg_format = 3 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` - the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${max.value}` – the specified maximum `value`. For referenced fields, the actual + // field value is also printed in round brackets along with the reference itself. + // 6. `${max.operator}` – if `exclusive` is set to `true`, this placeholder equals to "<". + // Otherwise, "<=". + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 4; +} + +// A string field value must match the given regular expression. +// +// This option is applicable only to string fields, +// including those that are repeated. +// +// Example: using the `(pattern)` option. +// +// message CreateAccount { +// string id = 1 [(pattern).regex = "^[A-Za-z0-9+]+$", +// (pattern).error_msg = "ID must be alphanumerical in `${parent.type}`. Provided: `${field.value}`."]; +// } +// +message PatternOption { + + // The default error message. + option (default_message) = "The `${parent.type}.${field.path}` field" + " must match the regular expression `${regex.pattern}` (modifiers: `${regex.modifiers}`)." + " The passed value: `${field.value}`."; + + // The regular expression that the field value must match. + // + // Please use the Java regex dialect for the syntax baseline: + // https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/regex/Pattern.html + // + // Note: in Java, regex patterns are not wrapped in explicit delimiters like in Perl or PHP. + // Instead, the pattern is provided as a string literal. Therefore, `/` symbol does not need + // to be escaped. + // + // The provided string literal is passed directly to the regex engine. So, it must be exactly + // what you would supply to the `java.util.regex.Pattern.compile()` method. + // + string regex = 1; + + reserved 2; + reserved "flag"; + + // Modifiers for this pattern. + Modifier modifier = 4; + + // A user-defined validation error format message. + string msg_format = 3 [deprecated = true]; + + // A user-defined validation error format message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` - the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${regex.pattern}` – the specified regex pattern. + // 6. `${regex.modifiers}` – the specified modifiers, if any. For example, `[dot_all, unicode]`. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 5; + + // Regular expression modifiers. + // + // These modifiers are specifically selected to be supported in many implementation platforms. + // + message Modifier { + + // Enables the dot (`.`) symbol to match all the characters. + // + // By default, the dot does not match line break characters. + // + // May also be known in some platforms as "single line" mode and be encoded with + // the `s` flag. + // + bool dot_all = 1; + + // Allows to ignore the case of the matched symbols. + // + // For example, this modifier is specified, string `ABC` would be a complete match for + // the regex `[a-z]+`. + // + // On some platforms may be represented by the `i` flag. + // + bool case_insensitive = 2; + + // Enables the `^` (caret) and `$` (dollar) signs to match a start and an end of a line + // instead of a start and an end of the whole expression. + // + // On some platforms may be represented by the `m` flag. + // + bool multiline = 3; + + // Enables matching the whole UTF-8 sequences, + // + // On some platforms may be represented by the `u` flag. + // + bool unicode = 4; + + // Allows the matched strings to contain a full match to the pattern and some other + // characters as well. + // + // By default, a string only matches a pattern if it is a full match, i.e. there are no + // unaccounted for leading and/or trailing characters. + // + // This modifier is usually not represented programming languages, as the control over + // weather to match an entire string or only its part is provided to the user by other + // language means. For example, in Java, this would be the difference between methods + // `matches()` and `find()` of the `java.util.regex.Matcher` class. + // + bool partial_match = 5; + } +} + +// Specifies the message to show if a validated field happens to be invalid. +// +// It is applicable only to fields marked with `(validate)`. +// +message IfInvalidOption { + + // Do not specify error message for `(validate)`, it is no longer used by + // the validation library. + option deprecated = true; + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}` of the type" + " `${field.type}` is invalid. The field value: `${field.value}`."; + + // A user-defined validation error format message. + // + // Use `error_msg` instead. + // + string msg_format = 1 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` - the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the field declaring type. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + // Example: using the `(if_invalid)` option. + // + // message Transaction { + // TransactionDetails details = 1 [(validate) = true, + // (if_invalid).error_msg = "The `${field.path}` field is invalid."]; + // } + // + string error_msg = 2; +} + +// Specifies that another field must be present if the option's target field is present. +// +// Unlike the `required_field` that handles combination of required fields, this option is useful +// when it is needed to say that an optional field makes sense only when another optional field +// is present. +// +// This option can be applied to the same field types as `(required)`, including both the +// target field and its companion. Supported field types are: +// +// - Messages and enums. +// - Repeated fields and maps. +// - `string` and `bytes`. +// +// Example: requiring mutual presence of optional fields. +// +// message ScheduledItem { +// ... +// spine.time.LocalDate date = 4; +// spine.time.LocalTime time = 5 [(goes).with = "date"]; +// } +// +message GoesOption { + + // The default error message. + option (default_message) = "The field `${goes.companion}` must also be set when `${field.path}`" + " is set in `${parent.type}`."; + + // The name of the companion field whose presence is required for this field to be valid. + string with = 1; + + // A user-defined validation error format message. + string msg_format = 2 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` – the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${goes.companion}` – the name of the companion specified in `with`. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 3; +} + +// Defines options of a message representing a state of an entity. +message EntityOption { + + // A type of an entity for state of which the message is defined. + enum Kind { + option allow_alias = true; + + // Reserved for errors. + KIND_UNKNOWN = 0; + + // The message is an aggregate state. + AGGREGATE = 1; + + // The message is a state of a projection (same as "view"). + PROJECTION = 2; + + // The message is a state of a view (same as "projection"). + VIEW = 2; + + // The message is a state of a process manager. + PROCESS_MANAGER = 3; + + // The message is a state of an entity, which is not of the type + // defined by other members of this enumeration. + ENTITY = 4; + } + + // The type of the entity. + Kind kind = 1; + + // The level of visibility of the entity to queries. + enum Visibility { + + // Default visibility is different for different types of entities: + // - for projections, "FULL" is default; + // - for aggregates, process managers, and other entities, "NONE" is default. + // + DEFAULT = 0; + + // The entity is not visible to queries. + NONE = 1; + + // Client-side applications can subscribe to updates of entities of this type. + SUBSCRIBE = 2; + + // Client-side applications can query this type of entities. + QUERY = 3; + + // Client-side applications can subscribe and query this type of entity. + FULL = 4; + } + + // The visibility of the entity. + // + // If not defined, the value of this option is `DEFAULT`. + // + Visibility visibility = 2; +} + +// Defines a common type for message types declared in the same proto file. +// +// The nature of the type depends on the target programming language. +// For example, the `java_type` property defines a name of the Java interface common +// to all message classes generated for the proto file having this option. +// +// The option triggers creation of the common type if the `generate` property is set to true. +// Otherwise, it is expected that the user provides the reference to an existing type. +// +message EveryIsOption { + + // Enables the generation of the common type. + // + // The default value is `false`. + // + bool generate = 1; + + // The reference to a Java top-level interface. + // + // The interface cannot be nested into a class or another interface. + // If a nested interface is provided, the code generation should fail the build process. + // + // The value may be a fully-qualified or a simple name. + // + // When a simple name is set, it is assumed that the interface belongs to + // the package of the generated message classes. + // + // If the value of the `generate` field is set to `false` the referenced interface must exist. + // Otherwise, a compilation error will occur. + // + // If the value of the `generate` field is set to `true`, the framework will + // generate the interface using the given name and the package as described above. + // + // The generated interface will extend `com.google.protobuf.Message` and + // will have no declared methods. + // + string java_type = 2; +} + +// Defines additional type for a message type in which this option is declared. +// +// The nature of the type depends on the target programming language. +// For example, the `java_type` property defines a name of the Java interface which +// the generated message class will implement. +// +message IsOption { + + // The reference to a Java top-level interface. + // + // The interface cannot be nested into a class or another interface. + // If a nested interface is provided, the code generation should fail the build process. + // + // The value may be a fully-qualified or a simple name. + // + // When a simple name is set, it is assumed that the interface belongs to + // the package of the generated message classes. + // + // The referenced interface must exist. Otherwise, a compilation error will occur. + // + string java_type = 1; +} + +// Defines the way to compare two messages of the same type to one another. +// +// Comparisons can be used to sort values. +// +// See the `(compare_by)` option. +// +message CompareByOption { + + // Field paths used for comparisons. + // + // The allowed field types are: + // - any number type; + // - `bool` (`false` is less than `true`); + // - `string` (in the order of respective Unicode values); + // - enumerations (following the order of numbers associated with each constant); + // - local messages (generated within the current build) marked with `(compare_by)`; + // - external messages (from dependencies), which either marked with `(compare_by)` + // OR have a comparator provided in `io.spine.compare.ComparatorRegistry`. + // + // Other types are not permitted. Repeated or map fields are not permitted either. + // Such declarations will lead to build-time errors. + // + // To refer to nested fields, separate the field names with a dot (`.`). + // No fields in the path can be repeated or maps. + // + // When multiple field paths are specified, comparison is executed in the order of reference. + // For example, specifying `["seconds", "nanos"]` makes the comparison mechanism prioritize + // the `seconds` field and refer to `nanos` only when `seconds` are equal. + // + // NOTE: When comparing fields with a message type, a non-set message is always less than + // a set message. But if a message is set to a default value, the comparison falls back to + // the field-wise comparison, i.e. number values are treated as zeros, `bool` — as `false`, + // and so on. + // + repeated string field = 1; + + // If true, the default order is reversed. For example, numbers are ordered from the greater to + // the lower, enums — from the last number value to the 0th value, etc. + bool descending = 2; +} + +// Defines the error message used if a `set_once` field is set again. +// +// Applies only to the fields marked as `set_once`. +// +message IfSetAgainOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}` of the type" + " `${field.type}` already has the value `${field.value}` and cannot be reassigned" + " to `${field.proposed_value}`."; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.type}` – the fully qualified name of the field type. + // 3. `${field.value}` – the current field value. + // 4. `${field.proposed_value}` – the value, which was attempted to be set. + // 5. `${parent.type}` – the fully qualified name of the validated message. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + // Example: using the `(set_once)` option. + // + // message User { + // UserId id = 1 [(set_once) = true, + // (if_set_again).error_msg = "A student ID is used as a permanent identifier within academic system, and cannot be re-assigned."]; + // } + // + string error_msg = 1; +} + +// Defines the error message used if a `distinct` field has duplicates. +// +// Applies only to the `repeated` and `map` fields marked with the `(distinct)` option. +// +message IfHasDuplicatesOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}` of the type" + " `${field.type}` must not contain duplicates." + " The duplicates found: `${field.duplicates}`."; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.type}` – the fully qualified name of the field type. + // 3. `${field.value}` – the field value (the whole collection). + // 4. `${field.duplicates}` – the duplicates found (elements that occur more than once). + // 5. `${parent.type}` – the fully qualified name of the validated message. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + // Example: using the `(distinct)` option. + // + // message Blizzard { + // repeated Snowflake = 1 [(distinct) = true, + // (if_has_duplicates).error_msg = "Every snowflake must be unique! The duplicates found: `${field.duplicates}`."]; + // } + // + string error_msg = 1; +} + +// Indicate that the numeric field must belong to the specified bounded range. +// +// For unbounded ranges, please use `(min)` and `(max) options. +// +// The option supports all singular and repeated numeric fields. +// +message RangeOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}` must be within" + " the following range: `${range.value}`. The passed value: `${field.value}`."; + + // The string representation of the range. + // + // A range consists of two bounds: a lower bound and an upper bound. These bounds are + // separated by either the `..` or ` .. ` delimiter. Each bound can be either open + // or closed. The format of the bounds and the valid values depend on the type of field. + // + // ## Bound Types + // + // - Closed bounds include the endpoint and are indicated using square brackets (`[`, `]`). + // Example: `[0..10]` represents values from 0 to 10, inclusive. + // + // - Open bounds exclude the endpoint and are indicated using parentheses (`(`, `)`). + // Example: `(0..10)` represents values strictly between 0 and 10. + // + // The lower bound must be less than or equal to the upper bound. + // + // ## Integer Fields + // + // A range for an integer field must use integer numbers. Specifying a decimal number + // is not allowed, even if it has no fractional part (e.g., `5.0` is invalid). + // + // Example: defining ranges for integer fields. + // + // message Measurements { + // int32 length = 1 [(range).value = "[0..100)"]; + // uint32 mass = 2 [(range).value = "(0..100]"]; + // } + // + // ## Floating-Point Fields + // + // A range for a floating-point field must use a decimal separator (`.`), even if the value + // has no fractional part. An exponent part represented by `E` or `e`, followed by an optional + // sign and digits is allowed (e.g., `1.2E3`, `0.5e-2`). + // + // Example: defining ranges for floating-point fields. + // + // message Measurements { + // float degree = 1 [(range).value = "[0.0 .. 360.0)"]; + // double angle = 2 [(range).value = "(0.0 .. 180.0)"]; + // float pressure = 3 [(range).value = "[950.0E-2 .. 1050.0E-2]"]; + // } + // + // ## Field Type Limitations + // + // A range must not exceed the limits of the field type. + // + // Example: invalid ranges that exceed the field type limits. + // + // message OverflowMeasurements { + // float price = 1 [(range).value = "[0, 5.5E38]"]; // Exceeds the `float` maximum. + // uint32 size = 2 [(range).value = "[-5; 10]"]; // Falls below the `uint32` minimum. + // } + // + // ## Field references + // + // Instead of numeric literals, you can reference another numeric field. + // At runtime, the field’s value will be used as the bound. Nested fields are supported. + // + // Example: defining ranges using field references. + // + // message Measurements { + // + // int32 max_length = 1; + // int32 length = 2 [(range).value = "[1 .. max_length"]; + // + // Limits limits = 3; + // int32 temperature = 4 [(range).value = "[limits.low_temp .. limits.high_temp]"]; + // } + // + // message Limits { + // int32 low_temp = 1; + // int32 high_temp = 2; + // } + // + // Note: Field type compatibility is not required in this case; the value is + // automatically converted. However, only numeric fields can be referenced. + // Repeated and map fields are not supported. + // + string value = 1; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` - the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${range.value}` – the specified range. For referenced fields, the actual + // field value is also printed in round brackets along with the reference itself. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 2; +} + +// Controls whether a `oneof` group must always have one of its fields set. +// +// Note that unlike the `(required)` constraint, this option supports any field types +// within the group cases. +// +message ChoiceOption { + + // The default error message. + option (default_message) = "The `oneof` group `${parent.type}.${group.path}` must" + " have one of its fields set."; + + // Enables or disables the requirement for the `oneof` group to have a value. + bool required = 1; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${group.path}` – the group path. + // 2. `${parent.type}` – the fully qualified name of the validated message. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 2; +} + +// Declares the field groups, at least one of which must have all of its fields set. +// +// Unlike the `(required)` field constraint, which requires the presence of +// a specific field, this option allows to specify alternative field groups. +// +message RequireOption { + + // The default error message. + option (default_message) = "The message `${message.type}` must have at least one of" + " the following field groups set: `${require.fields}`."; + + // A set of field groups, at least one of which must have all of its fields set. + // + // A field group can include one or more fields joined by the ampersand (`&`) symbol. + // `oneof` group names are also valid and can be used along with field names. + // Groups are separated using the pipe (`|`) symbol. + // + // The field type determines when the field is considered set: + // + // 1. For message or enum fields, it must have a non-default instance. + // 2. For `string` and `bytes` fields, it must be non-empty. + // 3. For repeated fields and maps, it must contain at least one element. + // + // Fields of other types are not supported by this option. + // + // For `oneof`s, the restrictions above do not apply. Any `oneof` group can be used + // without considering the field types, and its value will be checked directly without + // relying on the default values of the fields within the `oneof`. + // + // Example: defining two field groups. + // + // message PersonName { + // option (require).fields = "given_name | honorific_prefix & family_name"; + // + // string honorific_prefix = 1; + // string given_name = 2; + // string middle_name = 3; + // string family_name = 4; + // string honorific_suffix = 5; + // } + // + // In this example, at least `given_name` or a group of `honorific_prefix` + // and `family_name` fields must be set. + // + string fields = 1; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${message.type}` – the fully qualified name of the validated message. + // 2. `${require.fields}` – the specified field groups. + // + // The placeholders will be replaced at runtime when the error is constructed. + // + string error_msg = 2; +} diff --git a/docs/time_options.proto b/docs/time_options.proto new file mode 100644 index 0000000000..bc73fe15f5 --- /dev/null +++ b/docs/time_options.proto @@ -0,0 +1,106 @@ +/* + * Copyright 2025, 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 + * + * Redistribution and use in source and/or binary forms, with or without + * modification, must retain the above copyright notice and the following + * disclaimer. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +syntax = "proto3"; + +// API Note on Packaging +// --------------------- +// We do not define the package for this file to allow shorter options for user-defined types. +// This allows to write: +// +// [(when).in = FUTURE]; +// +// instead of: +// +// [(spine.time.when).in = FUTURE]; +// + +import "spine/options.proto"; + +option (type_url_prefix) = "type.spine.io"; +option java_package = "io.spine.time.validation"; +option java_outer_classname = "TimeOptionsProto"; +option java_multiple_files = true; + +import "google/protobuf/descriptor.proto"; + +extend google.protobuf.FieldOptions { + + // See `TimeOption`. + TimeOption when = 73819; +} + +// Specifies that the field value is a point in time lying either in the future or in the past. +// +// Applicable to `google.protobuf.Timestamp` and types introduced in the `spine.time` package +// that describe time-related concepts. +// +// Repeated fields are supported. +// +// Example: Using the `(when)` option. +// +// message ScheduleMeeting { +// spine.time.ZonedDateTime start = 1 [(when).in = FUTURE]; +// } +// +message TimeOption { + + // The default error message. + option (default_message) = "The field `${parent.type}.${field.path}`" + " of the type `${field.type}` must be in the `${when.in}`." + " The encountered value: `${field.value}`."; + + // Defines a restriction for the timestamp. + Time in = 1; + + // Deprecated: please use `error_msg` instead. + string msg_format = 2 [deprecated = true]; + + // A user-defined error message. + // + // The specified message may include the following placeholders: + // + // 1. `${field.path}` – the field path. + // 2. `${field.value}` - the field value. + // 3. `${field.type}` – the fully qualified name of the field type. + // 4. `${parent.type}` – the fully qualified name of the validated message. + // 5. `${when.in}` – the specified timestamp restriction. It is either "past" or "future". + // + string error_msg = 3; +} + +// This enumeration defines restriction for date/time values. +enum Time { + + // The default value (if the time option is not set). + TIME_UNDEFINED = 0; + + // The value must be in the past. + PAST = 1; + + // The value must be in the future. + FUTURE = 2; +} From 94f69c0a79929eb39a69ceb2ecf4778dbd103ffc Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:15:55 +0000 Subject: [PATCH 17/26] Fix capitalization Also: * Update Kotlin requirement to 2.2.21+. --- docs/01-getting-started/index.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md index a1d5708927..87b8f8af8e 100644 --- a/docs/01-getting-started/index.md +++ b/docs/01-getting-started/index.md @@ -5,7 +5,7 @@ and see validation in action in Java and Kotlin. If you are new to the library, read the short overview first: - Introduction → [Overview](../00-intro/index.md) -- Who this is for → [Target Audience](../00-intro/target-audience.md) +- Who this is for → [Target audience](../00-intro/target-audience.md) - Design principles → [Philosophy](../00-intro/philosophy.md) @@ -20,7 +20,7 @@ If you are new to the library, read the short overview first: - Java 17+ - Gradle (Kotlin DSL or Groovy) - Protobuf compiler (`protoc`) -- Optional: Kotlin 2.2.20+ for the Kotlin Protobuf DSL +- Optional: Kotlin 2.2.21+ for the Kotlin Protobuf DSL If your project already generates Java/Kotlin sources from `.proto` files, you’re 90% there. Spine Validation integrates into the build to generate and inject validation logic into @@ -38,10 +38,11 @@ the code produced by `protoc`. - Add declarative options like `(required)`, `(min)`, `(pattern)`, `(when).in = PAST`. 3) Build your project -- The Spine compiler plugin scans your model and generates validation code. +- The Validation plugin to Spine Compiler scans your model and generates validation code. 4) Use the generated API -- Validate on builder `build()` or call `validate()` explicitly. See [Your First Validated Model](first-model.md). +- Validate on builder `build()` or call `validate()` explicitly. + See [Your first validated model](first-model.md). ## What’s next From 449a789b4af410971477fce12bd24ea7880dd3d6 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:16:08 +0000 Subject: [PATCH 18/26] Backtick code in ToC --- docs/ToC.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ToC.md b/docs/ToC.md index 3645eb7b9b..35a57c7e0f 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -45,8 +45,8 @@ ### 3.5. Temporal constraints - [Overview](03-built-in-options/temporal/index.md) -- [when](03-built-in-options/temporal/when.md) -- [Timestamp & duration](03-built-in-options/temporal/timestamp-duration.md) +- [`(when)`](03-built-in-options/temporal/when.md) +- [`Timestamp` & `Duration`](03-built-in-options/temporal/timestamp-duration.md) ### 3.6. Message-level constraints - [Overview](03-built-in-options/message/index.md) From 3671ae4c12d10bea092d6513a6adc2fb1eaff6a9 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:24:43 +0000 Subject: [PATCH 19/26] Put the "Collection constraints" section after "Temporal constraints" --- docs/ToC.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/ToC.md b/docs/ToC.md index 35a57c7e0f..ccd0010228 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -29,24 +29,24 @@ - [length/size](03-built-in-options/fields/length-size.md) - [unique](03-built-in-options/fields/unique.md) -### 3.2. Collection constraints -- [Overview](03-built-in-options/collections/index.md) -- [non_empty](03-built-in-options/collections/non-empty.md) -- [distinct](03-built-in-options/collections/distinct.md) -- [Collection size](03-built-in-options/collections/collection-size.md) - -### 3.3. String constraints +### 3.2. String constraints - [Overview](03-built-in-options/strings/index.md) - [Advanced patterns](03-built-in-options/strings/pattern-advanced.md) -### 3.4. Numeric constraints +### 3.3. Numeric constraints - [Overview](03-built-in-options/numbers/index.md) - [Numeric bounds](03-built-in-options/numbers/numeric-bounds.md) -### 3.5. Temporal constraints +### 3.4. Temporal constraints - [Overview](03-built-in-options/temporal/index.md) - [`(when)`](03-built-in-options/temporal/when.md) -- [`Timestamp` & `Duration`](03-built-in-options/temporal/timestamp-duration.md) +- [Validating `Timestamp` & `Duration`](03-built-in-options/temporal/timestamp-duration.md) + +### 3.5. Collection constraints +- [Overview](03-built-in-options/collections/index.md) +- [`(required)`](03-built-in-options/collections/required.md) +- [`(distinct)`](03-built-in-options/collections/distinct.md) +- [Collection size](03-built-in-options/collections/collection-size.md) ### 3.6. Message-level constraints - [Overview](03-built-in-options/message/index.md) From 16a2736db12f90c72a3a8ebc6baa5bc4352f3c50 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:35:38 +0000 Subject: [PATCH 20/26] Describe Validation components --- docs/00-intro/index.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/00-intro/index.md b/docs/00-intro/index.md index 07aea6c51b..571934de27 100644 --- a/docs/00-intro/index.md +++ b/docs/00-intro/index.md @@ -8,6 +8,21 @@ declarative options and then automatically enforces these constraints at runtime The library is part of the Spine toolchain but can also be used independently in any Java/Kotlin backend that models data using Protocol Buffers. +## Components + +Spine Validation consists of three main parts: + +1. **Validation plugin to Spine Compiler** – analyzes your Protobuf definitions and + generates validation code for messages and builders. + +2. **Gradle plugin** – integrates with your build, configuring Spine Compiler to run + the Validation code generation as part of the Protobuf compilation process. + +3. **Runtime library** – provides validation APIs and error reporting mechanisms + for JVM projects (Java and Kotlin). + +Together, these components enable declarative, type-safe validation that is enforced +both at compile time and at runtime. ## Key capabilities @@ -26,7 +41,7 @@ with it. ### Generated validation code -The Validation Plugin for Spine Compiler processes your Protobuf model and generates: +The validation plugin to Spine Compiler processes your Protobuf model and generates: - validation code for messages and builders, - runtime checks, From 7fb87acebdc35c27b7970d4151ab1ab39dc31131 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 18:36:37 +0000 Subject: [PATCH 21/26] Remove "Configuration & tooling" section --- docs/ToC.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/ToC.md b/docs/ToC.md index ccd0010228..868955e1e5 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -61,19 +61,13 @@ - [Kotlin usage](04-using-validation/kotlin-usage.md) - [Framework integration](04-using-validation/framework-integration.md) -## 5. Configuration & tooling -- [Overview](05-configuration/index.md) -- [Compiler configuration](05-configuration/compiler.md) -- [Library modules](05-configuration/modules.md) -- [Debugging generated code](05-configuration/debugging.md) - -## 6. Extending validation +## 5. Extending validation - [Overview](06-extending/index.md) - [Architecture](06-extending/architecture.md) - [Custom validation options](06-extending/custom-options.md) - [Custom runtime validators](06-extending/custom-runtime-validators.md) -## 7. Recipes (cookbook) +## 6. Recipes (cookbook) - [Overview](07-recipes/index.md) - [Domain IDs](07-recipes/domain-ids.md) - [Common cases](07-recipes/common-cases.md) @@ -81,7 +75,7 @@ - [Cross-field logic](07-recipes/cross-field-logic.md) - [API validation](07-recipes/api-validation.md) -## 8. Reference +## 7. Reference - [Reference overview](08-reference/index.md) - [List of validation options](08-reference/options.md) - [Java/Kotlin API index](08-reference/api.md) From 51c50362d9ba90218a23ea78544f70ce0674b057 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 19:45:52 +0000 Subject: [PATCH 22/26] Rename "Installation" to "Adding Validation to your build" --- docs/01-getting-started/index.md | 7 +++---- docs/ToC.md | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/01-getting-started/index.md b/docs/01-getting-started/index.md index 87b8f8af8e..1fc0196a61 100644 --- a/docs/01-getting-started/index.md +++ b/docs/01-getting-started/index.md @@ -29,9 +29,8 @@ the code produced by `protoc`. ## Quick path -1) Install the library and compiler integration -- Follow [Installation](installation.md) to add the necessary repositories and build configuration. -- If you use a custom Protobuf setup, see [Compiler Configuration](../05-configuration/compiler.md). +1) Add Spine Validation to your Gradle build +- Follow the [instructions](adding-to-build.md). 2) Define constraints in your `.proto` files - Import `spine/options.proto` (and `spine/time_options.proto` for temporal constraints). @@ -47,6 +46,6 @@ the code produced by `protoc`. ## What’s next -- [Installation](installation.md) +- [Adding Validation to your build](adding-to-build.md) - [Your First Validated Model](first-model.md) - [Validation Workflow](workflow.md) diff --git a/docs/ToC.md b/docs/ToC.md index 868955e1e5..c3640baf0d 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -7,7 +7,7 @@ ## 1. Getting started - [Getting started](01-getting-started/index.md) -- [Installation](01-getting-started/installation.md) +- [Adding Validation to your build](01-getting-started/installation.md) - [Your first validated model](01-getting-started/first-model.md) - [Validation workflow](01-getting-started/workflow.md) From d3d0b60b2a54a1b3e0d0c138606d491f10a80000 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 20:42:02 +0000 Subject: [PATCH 23/26] Describe adding Validation to a Gradle build --- docs/01-getting-started/adding-to-build.md | 58 ++++++++++++++++++++++ docs/ToC.md | 2 +- 2 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 docs/01-getting-started/adding-to-build.md diff --git a/docs/01-getting-started/adding-to-build.md b/docs/01-getting-started/adding-to-build.md new file mode 100644 index 0000000000..3f2ceca970 --- /dev/null +++ b/docs/01-getting-started/adding-to-build.md @@ -0,0 +1,58 @@ +# Adding Validation to a Gradle build + +Spine Validation can be added to a JVM project in two different ways. +Choose the setup that matches your project: + +- Standalone use in any Protobuf-based project. +- As part of a Spine-based project that already uses the CoreJvm toolchain. + +Both modes integrate the Validation compiler into the build and add the runtime library. + + +## Mode 1: Standalone via Validation Gradle Plugin + +Use this mode if you want to run Validation without the rest of Spine. + +1) Add the Validation plugin to the build. + +```kotlin +plugins { + id("io.spine.validation") version "" +} +``` + +2) Make sure your repositories include Spine artifacts (the same repositories you use + for other Spine tools and libraries). + +3) Optional: control Validation generation explicitly. + +```kotlin +spine { + validation { + enabled.set(true) // `true` by default + } +} +``` + +The plugin wires Validation into Spine Compiler, adds the Validation Java codegen bundle, +and brings in the JVM runtime dependency automatically. + + +## Mode 2: Spine-based project via CoreJvm Gradle Plugin + +If your project is base on the Spine CoreJvm library, apply the CoreJvm Gradle plugin instead of +adding Validation directly. CoreJvm brings in the Validation Gradle plugin for you. + +```kotlin +plugins { + id("io.spine.core-jvm") version "" +} +``` + +Validation is available right away, and you can configure it using the same `validation` +extension if needed. + + +## Next step + +Continue with [Your first validated model](first-model.md). diff --git a/docs/ToC.md b/docs/ToC.md index c3640baf0d..088f3a9935 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -7,7 +7,7 @@ ## 1. Getting started - [Getting started](01-getting-started/index.md) -- [Adding Validation to your build](01-getting-started/installation.md) +- [Adding Validation to a Gradle build](01-getting-started/adding-to-build.md) - [Your first validated model](01-getting-started/first-model.md) - [Validation workflow](01-getting-started/workflow.md) From 5383ae2c17e79507598ca0136cc2181e2784369c Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 21:21:31 +0000 Subject: [PATCH 24/26] Improve the example type name in the `ValidationError` docs --- .../src/main/proto/spine/validation/validation_error.proto | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jvm-runtime/src/main/proto/spine/validation/validation_error.proto b/jvm-runtime/src/main/proto/spine/validation/validation_error.proto index 093d38e0a5..c9562d093e 100644 --- a/jvm-runtime/src/main/proto/spine/validation/validation_error.proto +++ b/jvm-runtime/src/main/proto/spine/validation/validation_error.proto @@ -72,7 +72,7 @@ message ConstraintViolation { // Example: // // ``` - // message Student { + // message AddressBook { // Contacts contacts = 1 [(validate) = true]; // } // @@ -85,8 +85,8 @@ message ConstraintViolation { // } // ``` // - // When the `Student` message is validated and the `value` field in the nested `Email` - // message is invalid, this property will contain the `Student` type name, not `Email`. + // When the `AddressBook` message is validated and the `value` field in the nested `Email` + // message is invalid, this property will contain the `AddressBook` type name, not `Email`. // This is so because this type is an entry point to the nested validation. // string type_name = 7; From 0bb61960e4d3f4137cadf656e0845ee0d7249cd2 Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 21:21:57 +0000 Subject: [PATCH 25/26] Remove the leading zero in the Introduction section title --- docs/ToC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ToC.md b/docs/ToC.md index 088f3a9935..ff30ffdb1d 100644 --- a/docs/ToC.md +++ b/docs/ToC.md @@ -1,6 +1,6 @@ # Spine Validation — Table of contents -## 0. Introduction +## Introduction - [Overview](00-intro/index.md) - [Target audience](00-intro/target-audience.md) - [Philosophy](00-intro/philosophy.md) From f99685afcc8494a98eab77592a61014cad43349a Mon Sep 17 00:00:00 2001 From: alexander-yevsyukov Date: Thu, 22 Jan 2026 21:22:28 +0000 Subject: [PATCH 26/26] Add wording for the `first-model.md` --- docs/01-getting-started/first-model.md | 104 +++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 docs/01-getting-started/first-model.md diff --git a/docs/01-getting-started/first-model.md b/docs/01-getting-started/first-model.md new file mode 100644 index 0000000000..917490a27d --- /dev/null +++ b/docs/01-getting-started/first-model.md @@ -0,0 +1,104 @@ +# Your first validated model + +This guide shows how to define a Protobuf model with validation rules and how those +rules are enforced in the generated JVM code. + +The validation options come from two files: + +- `spine/options.proto` for general constraints like `(required)`, `(min)`, `(max)`, + `(pattern)`, `(distinct)`, and `(validate)`. +- `spine/time_options.proto` for time constraints such as `(when).in = PAST | FUTURE`. + + +## 1) Define a validated message + +Create a `.proto` file and import the validation options you need: + +```protobuf +syntax = "proto3"; + +package example; + +import "google/protobuf/timestamp.proto"; +import "spine/options.proto"; +import "spine/time_options.proto"; + +// Provides bank card information with validation rules. +// +// The digits of the card are simplified for the sake of the example. +// +message BankCard { + + // Must be present and match a simple card pattern. + string digits = 1 [ + (required) = true, + (pattern).regex = "^\\d{4}(?: \\d{4}){3}" + ]; + + // Must be present and contain at least 4 Latin letters or spaces. + string owner = 2 [ + (required) = true, + (pattern).regex = "^[A-Z](?:[A-Za-z ]{2,}[a-z])$" + ]; + + // Must be in the past. + google.protobuf.Timestamp issued_at = 3 [ + (when).in = PAST, + (required) = true, + (validate) = true + ]; + + // All tags must be unique. Tags are optional. + repeated string tags = 4 [ + (distinct) = true + ]; +} +``` + +Notes on the options used: + +- `(required)` ensures the field is set and not default or empty. +- `(pattern).regex` validates string contents with a regular expression. +- `(when).in` restricts time values to `PAST` or `FUTURE`. +- `(distinct)` enforces uniqueness in `repeated` and `map` fields. + + +## 2) Build the project + +Run your Gradle build as usual. The Validation Gradle plugin integrates with Spine Compiler +and injects validation checks into the generated Java code. + + +## 3) Use the generated validation + +Validation runs on `build()` and can be triggered manually with `validate()`. + +```java +var card = BankCard.newBuilder() + .setDigits("invalid") + .setOwner("Al") + .build(); // Throws `ValidationException`. +``` + +```kotlin +val card = bankCard { + digits = "invalid" + owner = "Al" +} // Throws `ValidationException`. +``` + +To validate without throwing, use `validate()` on a built message: + +```java +var card = BankCard.newBuilder() + .setDigits("invalid") + .buildPartial(); + +var error = card.validate(); +error.ifPresent(err -> System.out.println(err.getMessage())); +``` + + +## What’s next + +Continue with [Validation Workflow](workflow.md).