Skip to content

[WIP] feat: implement schema inheritance and composition with schemars integration#21

Merged
Artifizer merged 16 commits intoGlobalTypeSystem:mainfrom
Artifizer:main
Dec 23, 2025
Merged

[WIP] feat: implement schema inheritance and composition with schemars integration#21
Artifizer merged 16 commits intoGlobalTypeSystem:mainfrom
Artifizer:main

Conversation

@Artifizer
Copy link
Copy Markdown
Contributor

- Add schematools and schemars dependencies for schema generation
- Refactor gts_schema_for! macro to use schemars for property extraction
- Implement proper allOf inheritance with $ref to direct parent types
- Add generic field type safety with additionalProperties: false
- Add GtsSchema trait methods for nesting path collection and schema wrapping
- Update tests to work with new schemars-based implementation
- Add comprehensive inheritance tests and examples
- Update README documentation for schema composition features
- Add gts-macros-cli tool for testing schema generation
- Fix schema resolution and inlining in GtsStore
- Add support for nested generic type composition (BaseEventV1<AuditPayloadV1<PlaceOrderDataV1>>)

…gration

- Add schematools and schemars dependencies for schema generation
- Refactor gts_schema_for! macro to use schemars for property extraction
- Implement proper allOf inheritance with  to direct parent types
- Add generic field type safety with additionalProperties: false
- Add GtsSchema trait methods for nesting path collection and schema wrapping
- Update tests to work with new schemars-based implementation
- Add comprehensive inheritance tests and examples
- Update README documentation for schema composition features
- Add gts-macros-cli tool for testing schema generation
- Fix schema resolution and inlining in GtsStore
- Add support for nested generic type composition (BaseEventV1<AuditPayloadV1<PlaceOrderDataV1>>)

This enables runtime schema composition for complex type hierarchies while maintaining
type safety through JSON Schema validation.
…gration

- Add schematools and schemars dependencies for schema generation
- Refactor gts_schema_for! macro to use schemars for property extraction
- Implement proper allOf inheritance with $ref to direct parent types
- Add generic field type safety with additionalProperties: false
- Add GtsSchema trait methods for nesting path collection and schema wrapping
- Update tests to work with new schemars-based implementation
- Add comprehensive inheritance tests and examples
- Update README documentation for schema composition features
- Add gts-macros-cli tool for testing schema generation
- Fix schema resolution and inlining in GtsStore
- Add support for nested generic type composition (BaseEventV1<AuditPayloadV1<PlaceOrderDataV1>>)

Signed-off-by: Artifizer <artifizer@gmail.com>
Signed-off-by: Artifizer <artifizer@gmail.com>

#[struct_to_gts_schema(
dir_path = "schemas",
schema_id = "gts.x.core.events.type.v1~x.core.audit.event.v1~x.marketplace.orders.purchase.v1~",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me looks better to include the schema as Rust struct, and specify a gts apart.
So the final gts is composable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea:

#[struct_to_gts_schema(
    dir_path = "schemas",
    base = true,
    gts_id = "gts.x.core.events.type.v1~",
    description = "Base event type definition",
    properties = "event_type,id,tenant_id,sequence_id,payload"
)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct BaseEventV1<P> {
    #[serde(rename = "type")]
    pub event_type: String,
    pub id: uuid::Uuid,
    pub tenant_id: uuid::Uuid,
    pub sequence_id: u64,
    pub payload: P,
}

#[struct_to_gts_schema(
    dir_path = "schemas",
    base = BaseEventV1,
    gts_id = "x.core.audit.event.v1~",
    description = "Audit event with user context",
    properties = "user_agent,user_id,ip_address,data"
)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct AuditPayloadV1<D> {
    pub user_agent: String,
    pub user_id: uuid::Uuid,
    pub ip_address: String,
    pub data: D,
}

#[struct_to_gts_schema(
    dir_path = "schemas",
    base = AuditPayloadV1,
    gts = "x.marketplace.orders.purchase.v1~",
    description = "Order placement audit event",
    properties = "order_id,product_id"
)]
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
pub struct PlaceOrderDataV1<E> {
    pub order_id: uuid::Uuid,
    pub product_id: uuid::Uuid,
    pub last: E,
}

Note the change in the macro

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about the same at some moment, actually
Let me try to implement it

- Add clap-based CLI to gts-macros-cli with dump functionality
- Refactor CLI code to minimize duplication with helper functions
- Fix schema generation for generic fields (remove additionalProperties from placeholders)
- Ensure base types have additionalProperties: false at root level
- Update event_type to use proper GTS ID format
- Add comprehensive schema and instance dumping functionality
- Improve error handling by replacing unwrap() with proper error propagation
- Fix clippy warnings and formatting issues
- Update integration tests to match new schema generation behavior

This enhances the developer experience with better CLI tooling and improves
schema generation accuracy for nested generic types.

Signed-off-by: Artifizer <artifizer@gmail.com>
Signed-off-by: Artifizer <artifizer@gmail.com>
Signed-off-by: Artifizer <artifizer@gmail.com>
…ruct_to_gts_schema

Signed-off-by: Artifizer <artifizer@gmail.com>
…nheritance

BREAKING CHANGE: The `base` attribute is now required for `struct_to_gts_schema`.

Add explicit 'base' attribute to define struct inheritance relationships:
- `base = true` marks a struct as a base type (single-segment schema_id)
- `base = ParentStruct` for parent structs

Tests:
- Add compile_fail tests for base validation errors
- Add integration tests for BASE_SCHEMA_ID constant
- Update all existing tests with required base attribute

Signed-off-by: Artifizer <artifizer@gmail.com>
Comment thread Cargo.toml Outdated
Comment thread gts/src/schema.rs Outdated
///
/// Panics if `result` is not a JSON object when attempting to remove `allOf`.
#[must_use]
pub fn compose_schema(parent_schema: &Value, generic_field: &str, nested_schema: &Value) -> Value {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not used anywhere. Also it's doing allOf merge wrong. Let's remove it.

Comment thread Cargo.toml
jsonschema = "0.18"

# JSON Schema generation
schemars = { version = "0.8", features = ["uuid1"] }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I remember only schemars = "1.1" supports json schema Draft 2020-12

Add automatic GtsSchema trait bounds to generic type parameters in
struct_to_gts_schema macro to ensure type safety across GTS inheritance
chains.

Changes:
- Automatically add GtsSchema bound to all generic type parameters
- Prevent usage of non-GTS structs as generic arguments
- Ensure only () or properly annotated structs can be used as generics
- Update README with documentation of new constraints and error examples

This prevents accidental use of arbitrary structs that haven't been
properly annotated with struct_to_gts_schema, ensuring compile-time
validation of the entire GTS inheritance chain.

Examples:
Good: BaseEventV1<()> and BaseEventV1<AuditPayloadV1<()>> now work
Fail: BaseEventV1<SomeRandomStruct> now fails at compile time

Signed-off-by: Artifizer <artifizer@gmail.com>
…ems with different compilation fail tests stderr
@Artifizer Artifizer merged commit cca1d37 into GlobalTypeSystem:main Dec 23, 2025
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants