This commit is contained in:
2025-05-25 06:44:51 +04:00
parent a7fb704627
commit 3ef1698c2c
2 changed files with 248 additions and 35 deletions

View File

@@ -30,7 +30,7 @@ This module provides a robust implementation of the JSON-RPC 2.0 protocol in VLa
Create a new JSON-RPC client using a custom transport layer.
```v
import jsonrpc
import freeflowuniverse.herolib.schemas.jsonrpc
// Implement the IRPCTransportClient interface for your transport (e.g., WebSocket)
struct WebSocketTransport {
@@ -48,11 +48,26 @@ mut client := jsonrpc.new_client(jsonrpc.Client{
Send a strongly-typed JSON-RPC request and handle the response.
```v
import jsonrpc
import freeflowuniverse.herolib.schemas.jsonrpc
// Define a request method and parameters
params := YourParams{...}
request := jsonrpc.new_request_generic('methodName', params)
// Define your parameter and result types
struct UserParams {
id int
include_details bool
}
struct UserResult {
name string
email string
role string
}
// Create a strongly-typed request with generic parameters
params := UserParams{
id: 123
include_details: true
}
request := jsonrpc.new_request_generic('getUser', params)
// Configure send parameters
send_params := jsonrpc.SendParams{
@@ -60,13 +75,15 @@ send_params := jsonrpc.SendParams{
retry: 3
}
// Send the request and process the response
response := client.send[YourParams, YourResult](request, send_params) or {
// Send the request and receive a strongly-typed response
// The generic types [UserParams, UserResult] ensure type safety for both request and response
user := client.send[UserParams, UserResult](request, send_params) or {
eprintln('Error sending request: $err')
return
}
println('Response result: $response')
// Access the strongly-typed result fields directly
println('User name: ${user.name}, email: ${user.email}, role: ${user.role}')
```
### 3. **Handling Errors**
@@ -74,7 +91,7 @@ println('Response result: $response')
Use the predefined JSON-RPC errors or create custom ones.
```v
import jsonrpc
import freeflowuniverse.herolib.schemas.jsonrpc
// Predefined error
err := jsonrpc.method_not_found
@@ -93,17 +110,89 @@ println(response)
---
### 4. **Working with Generic Responses**
The JSON-RPC module provides strong typing for responses using generics, allowing you to define the exact structure of your expected results.
```v
import freeflowuniverse.herolib.schemas.jsonrpc
// Define your result type
struct ServerStats {
cpu_usage f64
memory_usage f64
uptime int
active_connections int
}
// Create a request (with or without parameters)
request := jsonrpc.new_request('getServerStats', '{}')
// Decode a response directly to your type
response_json := '{"jsonrpc":"2.0","result":{"cpu_usage":45.2,"memory_usage":62.7,"uptime":86400,"active_connections":128},"id":1}'
response := jsonrpc.decode_response_generic[ServerStats](response_json) or {
eprintln('Failed to decode response: $err')
return
}
// Access the strongly-typed result
if !response.is_error() {
stats := response.result() or {
eprintln('Error getting result: $err')
return
}
println('Server stats:')
println('- CPU: ${stats.cpu_usage}%')
println('- Memory: ${stats.memory_usage}%')
println('- Uptime: ${stats.uptime} seconds')
println('- Connections: ${stats.active_connections}')
}
```
### 5. **Creating Generic Responses**
When implementing a JSON-RPC server, you can create strongly-typed responses:
```v
import freeflowuniverse.herolib.schemas.jsonrpc
// Define a result type
struct SearchResult {
total_count int
items []string
page int
page_size int
}
// Create a response with a strongly-typed result
result := SearchResult{
total_count: 157
items: ['item1', 'item2', 'item3']
page: 1
page_size: 3
}
// Create a generic response with the strongly-typed result
response := jsonrpc.new_response_generic(1, result)
// Encode the response to send it
json := response.encode()
println(json)
// Output: {"jsonrpc":"2.0","id":1,"result":{"total_count":157,"items":["item1","item2","item3"],"page":1,"page_size":3}}
```
## Modules and Key Components
### 1. **`model_request.v`**
Handles JSON-RPC requests:
- Structs: `Request`, `RequestGeneric`
- Methods: `new_request`, `new_request_generic`, `decode_request`, etc.
- Structs: `Request`, `RequestGeneric[T]`
- Methods: `new_request`, `new_request_generic[T]`, `decode_request`, `decode_request_generic[T]`, etc.
### 2. **`model_response.v`**
Handles JSON-RPC responses:
- Structs: `Response`, `ResponseGeneric`
- Methods: `new_response`, `new_response_generic`, `decode_response`, `validate`, etc.
- Structs: `Response`, `ResponseGeneric[D]`
- Methods: `new_response`, `new_response_generic[D]`, `decode_response`, `decode_response_generic[D]`, `validate`, etc.
### 3. **`model_error.v`**
Manages JSON-RPC errors:
@@ -115,7 +204,7 @@ Manages JSON-RPC errors:
Implements the JSON-RPC client:
- Structs: `Client`, `SendParams`, `ClientConfig`
- Interface: `IRPCTransportClient`
- Method: `send`
- Method: `send[T, D]` - Generic method for sending requests with parameters of type T and receiving responses with results of type D
---

View File

@@ -1,37 +1,161 @@
# JSON Schema
A V library for the JSON Schema model, and a few handy functions.
A V library for working with JSON Schema - providing model definitions, bidirectional code generation, and utility functions.
## Overview
This module provides comprehensive tools for working with [JSON Schema](https://json-schema.org/), which is "a declarative language that allows you to annotate and validate JSON documents." The module offers:
1. **JSON Schema Model**: Complete V struct definitions that map to the JSON Schema specification
2. **V Code ↔ JSON Schema Conversion**: Bidirectional conversion between V code and JSON Schema
3. **Code Generation**: Generate V structs from JSON Schema and vice versa
## Module Structure
- `model.v`: Core JSON Schema model definitions
- `decode.v`: Functions to decode JSON Schema strings into Schema structures
- `consts_numeric.v`: Numeric constants for JSON Schema
- `codegen/`: Code generation functionality
- `generate.v`: Generate JSON Schema from V code models
- `codegen.v`: Generate V code from JSON Schema
- `templates/`: Templates for code generation
## JSON Schema Model
Defined [here](https://json-schema.org/), "JSON Schema is a declarative language that allows you to annotate and validate JSON documents." The model in this module provides a struct that can easily be encoded into a JSON Schema.
The module provides a comprehensive V struct representation of JSON Schema (based on draft-07), including:
## Generating a Schema
The generate.v file provides functions that can generate JSONSchema from [codemodels](../codemodel/). This allows for easy generation of JSON Schema from structs, and is useful for generating schemas from parsed code in v.
Example:
```go
struct_ := code.Struct {
name: "Mystruct"
fields: [
code.StructField {
name: "myfield"
typ: "string"
}
]
```v
pub struct Schema {
pub mut:
schema string // The $schema keyword identifies which version of JSON Schema
id string // The $id keyword defines a URI for the schema
title string // Human-readable title
description string // Human-readable description
typ string // Data type (string, number, object, array, boolean, null)
properties map[string]SchemaRef // Object properties when type is "object"
additional_properties ?SchemaRef // Controls additional properties
required []string // List of required property names
items ?Items // Schema for array items when type is "array"
// ... and many more validation properties
}
schema := struct_to_schema(struct_)
```
### Generating Schemas for Anonymous Structs
The properties of a JSON Schema is a list of key value pairs, where keys represent the subschema's name and the value is the schema (or the reference to the schema which is defined elsewhere) of the property. This is analogous to the fields of a struct, which is represented by a field name and a type.
It's good practice to define object type schemas separately and reference them in properties, especially if the same schema is used in multiple places. However, object type schemas can also be defined in property definitions. This may make sense if the schema is exclusively used as a property of a schema, similar to using an anonymous struct for the type definition of a field of a struct.
As such, schema's generated from structs that declare anonymous structs as field types, include a schema definition in the property field.
## Code Generation
### V Code to JSON Schema
The module can generate JSON Schema from V code models, making it easy to create schemas from your existing V structs:
```v
// Example: Generate JSON Schema from a V struct
import freeflowuniverse.herolib.core.code
import freeflowuniverse.herolib.schemas.jsonschema.codegen
// Create a struct model
struct_ := code.Struct{
name: 'Person'
description: 'A person record'
fields: [
code.StructField{
name: 'name'
typ: 'string'
description: 'Full name'
},
code.StructField{
name: 'age'
typ: 'int'
description: 'Age in years'
}
]
}
// Generate JSON Schema from the struct
schema := codegen.struct_to_schema(struct_)
// The resulting schema will represent:
// {
// "title": "Person",
// "description": "A person record",
// "type": "object",
// "properties": {
// "name": {
// "type": "string",
// "description": "Full name"
// },
// "age": {
// "type": "integer",
// "description": "Age in years"
// }
// }
// }
```
### JSON Schema to V Code
The module can also generate V code from JSON Schema:
```v
import freeflowuniverse.herolib.schemas.jsonschema
import freeflowuniverse.herolib.schemas.jsonschema.codegen
// Create or load a JSON Schema
schema := jsonschema.Schema{
title: 'Person'
description: 'A person record'
typ: 'object'
properties: {
'name': jsonschema.Schema{
typ: 'string'
description: 'Full name'
}
'age': jsonschema.Schema{
typ: 'integer'
description: 'Age in years'
}
}
}
// Generate V structs from the schema
v_code := codegen.schema_to_v(schema)
// The resulting V code will be:
// module schema.title.
//
// // A person record
// struct Person {
// name string // Full name
// age int // Age in years
// }
```
### Advanced Features
#### Handling References
The module supports JSON Schema references (`$ref`), allowing for modular schema definitions:
```v
// Example of a schema with references
schema := jsonschema.Schema{
// ...
properties: {
'address': jsonschema.Reference{
ref: '#/components/schemas/Address'
}
}
}
```
#### Anonymous Structs
When generating schemas from V structs with anonymous struct fields, the module creates inline schema definitions in the property field, similar to how anonymous structs work in V.
## Notes
As [this issue](https://github.com/vlang/v/issues/15081) is still not resolved, a json schema cannot be decoded into the json schema structure defined in this module. As such, to decode json schema string into a structure the `pub fn decode(data str) !Schema` function defined in `decode.v` must be used.
Due to [this issue](https://github.com/vlang/v/issues/15081), a JSON Schema cannot be directly decoded into the JSON Schema structure defined in this module. To decode a JSON Schema string into a structure, use the `pub fn decode(data str) !Schema` function defined in `decode.v`.