gts-id
Validation and parsing primitives for GTS (Global Type System) identifiers.
This crate is the single source of truth for GTS identifier parsing in gts-rust: it is shared by the gts runtime library and the gts-macros proc-macro crate. It has no runtime dependencies beyond thiserror (and optionally uuid).
Identifier shape
A GTS identifier is a ~-chained sequence of segments under the gts. prefix:
gts.<vendor>.<package>.<namespace>.<type>.v<MAJOR>[.<MINOR>]
- A type identifier ends with a
~marker:gts.x.core.events.topic.v1~ - An instance identifier does not:
gts.x.core.events.topic.v1~acme.shop.orders.order.v1.0 - A combined anonymous instance ends with a UUID tail:
gts.x.core.events.topic.v1~7a1d2f34-5678-49ab-9012-abcdef123456
Configurable identifier prefix
By default, all GTS identifiers start with gts.. This prefix can be overridden
at compile time via the GTS_ID_PREFIX environment variable:
# Use a custom prefix for your organization
GTS_ID_PREFIX=acme.
The override is validated at compile time — an invalid value fails the build
with a clear diagnostic. A valid prefix is a single lowercase token
([a-z][a-z0-9_]*) terminated by a single .:
| Value | Accepted? | Reason |
|---|---|---|
acme. |
✅ | Single lowercase token + trailing dot |
gts. |
✅ | The default |
acme |
❌ | Missing trailing . |
Acme. |
❌ | Uppercase rejected |
my.org. |
❌ | Multi-segment (contains an extra .) |
acme-prod. |
❌ | Hyphen is not allowed |
_. |
❌ | Must start with a letter, not underscore |
| (empty) | ❌ | Empty prefix |
Because the prefix is baked into the binary at compile time, a single binary
cannot work with multiple prefixes. The build.rs emits
cargo:rerun-if-env-changed=GTS_ID_PREFIX so Cargo rebuilds when the variable
changes.
Relevant constants
| Constant | Description |
|---|---|
GTS_ID_PREFIX |
The configured prefix (default "gts."). |
DEFAULT_GTS_ID_PREFIX |
The default prefix ("gts."), regardless of override. |
GTS_ID_PREFIX_ENV |
The env var name ("GTS_ID_PREFIX"). |
GTS_ID_MAX_LENGTH |
Maximum identifier length (1024 chars). |
Types
| Type | Purpose |
|---|---|
GtsId |
A validated, concrete identifier. |
GtsIdPattern |
A match pattern — an identifier that may end in a single trailing *, or be fully concrete. |
GtsIdSegment |
One concrete segment of a GtsId (Concrete or UuidTail). |
GtsIdPatternSegment |
One segment of a pattern (Segment or Wildcard). |
GtsUuidTail |
An anonymous-instance UUID tail (guaranteed well-formed). |
GtsIdError |
The error returned by all parsing entry points. |
Every value is produced only by the validating constructors, so a parsed value is always well-formed and its invariants cannot be forged.
Examples
The snippets below use ? for brevity; assume they run inside a function that returns Result<_, gts_id::GtsIdError>.
Validate and parse a concrete identifier
use GtsId;
// Fallible constructor.
let id = try_new?;
assert_eq!;
assert!; // ends with '~'
// `FromStr` is also available.
let id: GtsId = "gts.x.core.events.topic.v1~".parse?;
// Cheap validity check that doesn't keep the parsed value.
assert!;
assert!; // uppercase rejected
Inspect segments
use GtsId;
let id = try_new?;
let seg = &id.segments;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert!;
Type vs. instance, and the parent type of a chain
use GtsId;
let instance = try_new?;
assert!;
// The parent type id (every segment but the last).
assert_eq!;
Wildcard patterns and matching
A GtsIdPattern may contain a single trailing * (e.g. gts.x.core.* or gts.x.core.events.topic.v1~*), or be fully concrete. Concrete identifiers are validated with GtsId, which rejects wildcards.
use ;
let pattern = try_new?;
let id = try_new?;
assert!;
let other = try_new?;
assert!;
// A concrete `GtsId` never accepts a wildcard:
assert!;
Pattern coverage
covers answers whether one pattern is broader than another — every identifier matched by other is also matched by self:
use GtsIdPattern;
let broad = try_new?;
let narrow = try_new?;
assert!;
assert!;
Anonymous instances (UUID tail)
use ;
let id = try_new?;
// The last segment is a UUID tail; match on it to read the UUID.
if let Some = id.segments.last
Error handling
use GtsId;
let err = try_new.unwrap_err;
// `GtsIdError` implements `Display` with a human-readable cause.
println!;
Feature flags
uuid— enablesGtsId::to_uuid(), a deterministic UUID v5 derivation (the same identifier always maps to the same UUID). Off by default so parsing-only consumers don't pull in theuuidcrate.
[]
= { = "0.11", = ["uuid"] }
// with the `uuid` feature enabled:
use GtsId;
let id = try_new?;
let uuid = id.to_uuid;
assert_eq!; // deterministic
License
Apache-2.0