Skip to content

Commit f9a0063

Browse files
authored
Merge pull request #89 from muonsoft/cursor/-bc-f3bb1229-b862-4389-b59b-67a226f7ded1-f668
Add ISIN validation constraint
2 parents dc1e6f0 + be4351a commit f9a0063

15 files changed

Lines changed: 293 additions & 5 deletions

File tree

.cursor/skills/validation-add-constraint/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ Add when useful for **standalone boolean checks** (e.g. in conditions, or for `O
230230
- [ ] `it/*.go`: constraint (StringFuncConstraint or custom struct), godoc, empty/nil handling, BuildViolation
231231
- [ ] `test/constraints_*_cases_test.go`: test cases + merge into `validateTestCases`
232232
- [ ] `it/example_test.go`: ExampleXxx_valid, ExampleXxx_invalid with `// Output:`
233+
- [ ] `CHANGELOG.md`: under `[Unreleased]``Added` (or other fitting section), user-facing bullet for the new API
233234
- [ ] Optional: `validate`: function, tests, examples, godoc
234235
- [ ] Optional: `is`: function, tests, examples, godoc
235236
- [ ] `go test ./...` and `golangci-lint run`

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,25 @@ func (c NumericConstraint) ValidateString(ctx context.Context, validator *valida
182182
4. Update documentation if adding public APIs
183183
5. Add examples for new features
184184
6. Ensure all tests pass in CI
185+
7. Update **CHANGELOG.md** when the change is user-visible (see [Changelog](#changelog) below)
186+
187+
## Changelog
188+
189+
The project uses **[Keep a Changelog](https://keepachangelog.com/)** in **`CHANGELOG.md`**.
190+
191+
### Rules for agents and contributors
192+
193+
1. **Always edit `CHANGELOG.md`** in the same branch/PR when your work would matter to library users: new or changed public API, new constraints, message or translation changes, behavior changes, deprecations, removals, security fixes, or notable bug fixes.
194+
2. **Use the `[Unreleased]` section** at the top. Maintainers move entries under a version heading and date when they cut a release.
195+
3. **Pick the right subsection**: `Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`. Use `Breaking` only for incompatible changes (or describe breaking impact under `Changed` if you prefer a single list).
196+
4. **Write for consumers**: short, imperative bullets; mention package or symbol names (`it.IsXxx`, `validate.Xxx`, `validation.ErrXxx`) when helpful. Linking to PRs/issues is optional.
197+
5. **Do not rewrite published versions**: never change the bullet list under a released version tag except to fix obvious typos or incorrect facts.
198+
6. **Skip the changelog** only for internal-only changes (refactors, tests, CI, comments) with no user-visible effect.
199+
7. **Release links**: when adding a new version section, update the comparison links at the bottom of the file (`[Unreleased]: ...compare/vX.Y.Z...HEAD` and `[X.Y.Z]: ...releases/tag/vX.Y.Z`).
185200

186201
## Resources
187202

203+
- **CHANGELOG.md** - Release history for users and upgraders
188204
- **README.md** - User-facing documentation
189205
- **CONTRIBUTING.md** - Contribution guidelines
190206
- **CODE_OF_CONDUCT.md** - Community standards

RELEASE_NOTES.md renamed to CHANGELOG.md

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,17 @@
1-
# Release Notes
1+
# Changelog
22

3-
## v0.19.0
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added
11+
12+
- ISIN (International Securities Identification Number) validation: `it.IsISIN()`, `validate.ISIN`, `is.ISIN`, with `validation.ErrInvalidISIN` / `message.InvalidISIN` and English and Russian translations (behavior aligned with Symfony `Isin`).
13+
14+
## [0.19.0] - 2026-02-09
415

516
### Added
617

@@ -15,10 +26,13 @@
1526
- **Documentation**: README restructured with installation, custom constraints, and property paths; expanded custom constraints guide with interface details and examples.
1627
- **Skill docs**: SKILLS.md removed; new reference and skill documentation for adding validation constraints.
1728

29+
### Breaking
30+
31+
- Tests using `CheckNoViolations` or validator setup need to be updated to the new signatures and helpers.
32+
1833
### Fixed
1934

2035
- Correct handling of single violations returned from validatable objects in `validateIt`.
2136

22-
### Breaking Changes
23-
24-
- Tests using `CheckNoViolations` or validator setup need to be updated to the new signatures and helpers.
37+
[Unreleased]: https://github.com/muonsoft/validation/compare/v0.19.0...HEAD
38+
[0.19.0]: https://github.com/muonsoft/validation/releases/tag/v0.19.0

errors.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var (
1515
ErrInvalidEAN8 = NewError("invalid EAN-8", message.InvalidEAN8)
1616
ErrInvalidEmail = NewError("invalid email", message.InvalidEmail)
1717
ErrInvalidHostname = NewError("invalid hostname", message.InvalidHostname)
18+
ErrInvalidISIN = NewError("invalid ISIN", message.InvalidISIN)
1819
ErrInvalidIP = NewError("invalid IP address", message.InvalidIP)
1920
ErrInvalidJSON = NewError("invalid JSON", message.InvalidJSON)
2021
ErrInvalidTime = NewError("invalid time", message.InvalidTime)

is/example_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,18 @@ func ExampleULID() {
253253
// false
254254
}
255255

256+
func ExampleISIN() {
257+
fmt.Println(is.ISIN("US0378331005"))
258+
fmt.Println(is.ISIN("US037833100"))
259+
fmt.Println(is.ISIN("123456789101"))
260+
fmt.Println(is.ISIN("XS2012239364"))
261+
// Output:
262+
// true
263+
// false
264+
// false
265+
// false
266+
}
267+
256268
func ExampleUUID() {
257269
fmt.Println(is.UUID("83eab6fd-230b-44fe-b52f-463387bd8788")) // v4
258270
fmt.Println(is.UUID("83eab6fd-230b-44fe-b52f-463387bd8788", validate.AllowUUIDVersions(4))) // v4

is/identifiers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ func ULID(value string) bool {
88
return validate.ULID(value) == nil
99
}
1010

11+
// ISIN validates whether the value is a valid International Securities Identification Number (ISIN).
12+
// See [github.com/muonsoft/validation/validate.ISIN] for validation rules and possible errors.
13+
//
14+
// See https://en.wikipedia.org/wiki/International_Securities_Identification_Number.
15+
func ISIN(value string) bool {
16+
return validate.ISIN(value) == nil
17+
}
18+
1119
// UUID validates whether a string value is a valid UUID (also known as GUID).
1220
//
1321
// By default, it uses strict mode and checks the UUID as specified in RFC 4122.

it/example_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,20 @@ func ExampleIsUPCE() {
4141
// violation: "This value is not a valid UPC-E."
4242
}
4343

44+
func ExampleIsISIN_valid() {
45+
err := validator.Validate(context.Background(), validation.String("US0378331005", it.IsISIN()))
46+
fmt.Println(err)
47+
// Output:
48+
// <nil>
49+
}
50+
51+
func ExampleIsISIN_invalid() {
52+
err := validator.Validate(context.Background(), validation.String("XS2012239364", it.IsISIN()))
53+
fmt.Println(err)
54+
// Output:
55+
// violation: "This value is not a valid International Securities Identification Number (ISIN)."
56+
}
57+
4458
func ExampleIsNotBlank() {
4559
fmt.Println(validator.Validate(context.Background(), validation.String("", it.IsNotBlank())))
4660
fmt.Println(validator.Validate(context.Background(), validation.Countable(len([]string{}), it.IsNotBlank())))

it/identifiers.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@ func IsULID() validation.StringFuncConstraint {
1616
WithMessage(validation.ErrInvalidULID.Message())
1717
}
1818

19+
// IsISIN validates whether the value is a valid International Securities Identification Number (ISIN).
20+
// See https://en.wikipedia.org/wiki/International_Securities_Identification_Number.
21+
func IsISIN() validation.StringFuncConstraint {
22+
return validation.OfStringBy(is.ISIN).
23+
WithError(validation.ErrInvalidISIN).
24+
WithMessage(validation.ErrInvalidISIN.Message())
25+
}
26+
1927
// UUIDConstraint validates whether a string value is a valid UUID (also known as GUID).
2028
//
2129
// By default, it uses strict mode and checks the UUID as specified in RFC 4122.

message/messages.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const (
3333
InvalidEAN8 = "This value is not a valid EAN-8."
3434
InvalidEmail = "This value is not a valid email address."
3535
InvalidHostname = "This value is not a valid hostname."
36+
InvalidISIN = "This value is not a valid International Securities Identification Number (ISIN)."
3637
InvalidIP = "This is not a valid IP address."
3738
InvalidJSON = "This value should be valid JSON."
3839
InvalidTime = "This value is not a valid time."

message/translations/english/messages.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ var Messages = map[language.Tag]map[string]catalog.Message{
5454
message.InvalidEAN8: catalog.String(message.InvalidEAN8),
5555
message.InvalidEmail: catalog.String(message.InvalidEmail),
5656
message.InvalidHostname: catalog.String(message.InvalidHostname),
57+
message.InvalidISIN: catalog.String(message.InvalidISIN),
5758
message.InvalidIP: catalog.String(message.InvalidIP),
5859
message.InvalidJSON: catalog.String(message.InvalidJSON),
5960
message.InvalidTime: catalog.String(message.InvalidTime),

0 commit comments

Comments
 (0)