...
This commit is contained in:
@@ -30,7 +30,9 @@ mut:
|
||||
// Returns:
|
||||
// - A pointer to a new Client instance
|
||||
pub fn new_client(client Client) &Client {
|
||||
return &Client{...client}
|
||||
return &Client{
|
||||
...client
|
||||
}
|
||||
}
|
||||
|
||||
// SendParams defines configuration options for sending JSON-RPC requests.
|
||||
@@ -40,9 +42,9 @@ pub struct SendParams {
|
||||
pub:
|
||||
// Maximum time in seconds to wait for a response (default: 60)
|
||||
timeout int = 60
|
||||
|
||||
|
||||
// Number of times to retry the request if it fails
|
||||
retry int
|
||||
retry int
|
||||
}
|
||||
|
||||
// send sends a JSON-RPC request with parameters of type T and expects a response with result of type D.
|
||||
@@ -61,16 +63,14 @@ pub:
|
||||
pub fn (mut c Client) send[T, D](request RequestGeneric[T], params SendParams) !D {
|
||||
// Send the encoded request through the transport layer
|
||||
response_json := c.transport.send(request.encode(), params)!
|
||||
|
||||
|
||||
// Decode the response JSON into a strongly-typed response object
|
||||
response := decode_response_generic[D](response_json) or {
|
||||
return error('Unable to decode response.\n- Response: ${response_json}\n- Error: ${err}')
|
||||
}
|
||||
|
||||
// Validate the response according to the JSON-RPC specification
|
||||
response.validate() or {
|
||||
return error('Received invalid response: ${err}')
|
||||
}
|
||||
response.validate() or { return error('Received invalid response: ${err}') }
|
||||
|
||||
// Ensure the response ID matches the request ID to prevent response/request mismatch
|
||||
if response.id != request.id {
|
||||
@@ -79,4 +79,4 @@ pub fn (mut c Client) send[T, D](request RequestGeneric[T], params SendParams) !
|
||||
|
||||
// Return the result or propagate any error from the response
|
||||
return response.result()!
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,14 +31,14 @@ fn (t TestRPCTransportClient) send(request_json string, params SendParams) !stri
|
||||
new_response(request.id, request.params)
|
||||
} else if request.method == 'test_error' {
|
||||
error := RPCError{
|
||||
code: 1
|
||||
code: 1
|
||||
message: 'intentional jsonrpc error response'
|
||||
}
|
||||
new_error_response(request.id, error)
|
||||
} else {
|
||||
new_error_response(request.id, method_not_found)
|
||||
}
|
||||
|
||||
|
||||
return response.encode()
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ fn test_send_json_rpc() {
|
||||
assert err.code() == 1
|
||||
assert err.msg() == 'intentional jsonrpc error response'
|
||||
}
|
||||
|
||||
|
||||
// Test case 3: Method not found error
|
||||
request2 := new_request_generic[string]('nonexistent_method', '')
|
||||
if response2 := client.send[string, string](request2) {
|
||||
|
||||
@@ -30,7 +30,9 @@ pub type ProcedureHandler = fn (payload string) !string
|
||||
// Returns:
|
||||
// - A pointer to a new Handler instance or an error if creation fails
|
||||
pub fn new_handler(handler Handler) !&Handler {
|
||||
return &Handler{...handler}
|
||||
return &Handler{
|
||||
...handler
|
||||
}
|
||||
}
|
||||
|
||||
// register_procedure registers a new procedure handler for the specified method.
|
||||
@@ -68,7 +70,7 @@ pub fn (handler Handler) handle(message string) !string {
|
||||
// Extract the method name from the request
|
||||
method := decode_request_method(message)!
|
||||
// log.info('Handling remote procedure call to method: ${method}')
|
||||
|
||||
|
||||
// Look up the procedure handler for the requested method
|
||||
procedure_func := handler.procedures[method] or {
|
||||
// log.error('No procedure handler for method ${method} found')
|
||||
|
||||
@@ -56,17 +56,17 @@ fn method_error(text string) !string {
|
||||
fn method_echo_handler(data string) !string {
|
||||
// Decode the request with string parameters
|
||||
request := decode_request_generic[string](data)!
|
||||
|
||||
|
||||
// Call the echo method and handle any errors
|
||||
result := method_echo(request.params) or {
|
||||
// If an error occurs, create an error response
|
||||
response := new_error_response(request.id,
|
||||
code: err.code()
|
||||
code: err.code()
|
||||
message: err.msg()
|
||||
)
|
||||
return response.encode()
|
||||
}
|
||||
|
||||
|
||||
// Create a success response with the result
|
||||
response := new_response_generic(request.id, result)
|
||||
return response.encode()
|
||||
@@ -83,17 +83,17 @@ fn method_echo_handler(data string) !string {
|
||||
fn method_echo_struct_handler(data string) !string {
|
||||
// Decode the request with TestStruct parameters
|
||||
request := decode_request_generic[TestStruct](data)!
|
||||
|
||||
|
||||
// Call the echo struct method and handle any errors
|
||||
result := method_echo_struct(request.params) or {
|
||||
// If an error occurs, create an error response
|
||||
response := new_error_response(request.id,
|
||||
code: err.code()
|
||||
code: err.code()
|
||||
message: err.msg()
|
||||
)
|
||||
return response.encode()
|
||||
}
|
||||
|
||||
|
||||
// Create a success response with the struct result
|
||||
response := new_response_generic[TestStruct](request.id, result)
|
||||
return response.encode()
|
||||
@@ -110,17 +110,17 @@ fn method_echo_struct_handler(data string) !string {
|
||||
fn method_error_handler(data string) !string {
|
||||
// Decode the request with string parameters
|
||||
request := decode_request_generic[string](data)!
|
||||
|
||||
|
||||
// Call the error method, which always returns an error
|
||||
result := method_error(request.params) or {
|
||||
// Create an error response with the error details
|
||||
response := new_error_response(request.id,
|
||||
code: err.code()
|
||||
code: err.code()
|
||||
message: err.msg()
|
||||
)
|
||||
return response.encode()
|
||||
}
|
||||
|
||||
|
||||
// This code should never be reached since method_error always returns an error
|
||||
response := new_response_generic(request.id, result)
|
||||
return response.encode()
|
||||
@@ -141,9 +141,9 @@ fn test_handle() {
|
||||
// Create a new handler with three test procedures
|
||||
handler := new_handler(Handler{
|
||||
procedures: {
|
||||
'method_echo': method_echo_handler
|
||||
'method_echo': method_echo_handler
|
||||
'method_echo_struct': method_echo_struct_handler
|
||||
'method_error': method_error_handler
|
||||
'method_error': method_error_handler
|
||||
}
|
||||
})!
|
||||
|
||||
|
||||
@@ -7,45 +7,45 @@ module jsonrpc
|
||||
// This error is returned when the server is unable to parse the request.
|
||||
// Error code: -32700
|
||||
pub const parse_error = RPCError{
|
||||
code: -32700
|
||||
code: -32700
|
||||
message: 'Parse error'
|
||||
data: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
|
||||
data: 'Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.'
|
||||
}
|
||||
|
||||
// invalid_request indicates that the sent JSON is not a valid Request object.
|
||||
// This error is returned when the request object doesn't conform to the JSON-RPC 2.0 specification.
|
||||
// Error code: -32600
|
||||
pub const invalid_request = RPCError{
|
||||
code: -32600
|
||||
code: -32600
|
||||
message: 'Invalid Request'
|
||||
data: 'The JSON sent is not a valid Request object.'
|
||||
data: 'The JSON sent is not a valid Request object.'
|
||||
}
|
||||
|
||||
// method_not_found indicates that the requested method doesn't exist or is not available.
|
||||
// This error is returned when the method specified in the request is not supported.
|
||||
// Error code: -32601
|
||||
pub const method_not_found = RPCError{
|
||||
code: -32601
|
||||
code: -32601
|
||||
message: 'Method not found'
|
||||
data: 'The method does not exist / is not available.'
|
||||
data: 'The method does not exist / is not available.'
|
||||
}
|
||||
|
||||
// invalid_params indicates that the method parameters are invalid.
|
||||
// This error is returned when the parameters provided to the method are incorrect or incompatible.
|
||||
// Error code: -32602
|
||||
pub const invalid_params = RPCError{
|
||||
code: -32602
|
||||
code: -32602
|
||||
message: 'Invalid params'
|
||||
data: 'Invalid method parameter(s).'
|
||||
data: 'Invalid method parameter(s).'
|
||||
}
|
||||
|
||||
// internal_error indicates an internal JSON-RPC error.
|
||||
// This is a generic server-side error when no more specific error is applicable.
|
||||
// Error code: -32603
|
||||
pub const internal_error = RPCError{
|
||||
code: -32603
|
||||
code: -32603
|
||||
message: 'Internal Error'
|
||||
data: 'Internal JSON-RPC error.'
|
||||
data: 'Internal JSON-RPC error.'
|
||||
}
|
||||
|
||||
// RPCError represents a JSON-RPC 2.0 error object as defined in the specification.
|
||||
@@ -55,13 +55,13 @@ pub struct RPCError {
|
||||
pub mut:
|
||||
// Numeric error code. Predefined codes are in the range -32768 to -32000.
|
||||
// Custom error codes should be outside this range.
|
||||
code int
|
||||
|
||||
code int
|
||||
|
||||
// Short description of the error
|
||||
message string
|
||||
|
||||
|
||||
// Additional information about the error (optional)
|
||||
data string
|
||||
data string
|
||||
}
|
||||
|
||||
// new_error creates a new error response for a given request ID.
|
||||
@@ -76,8 +76,8 @@ pub mut:
|
||||
pub fn new_error(id int, error RPCError) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: error
|
||||
id: id
|
||||
error_: error
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,4 +106,4 @@ pub fn (err RPCError) code() int {
|
||||
// - true if the error is empty, false otherwise
|
||||
pub fn (err RPCError) is_empty() bool {
|
||||
return err.code == 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,25 +4,25 @@ module jsonrpc
|
||||
// It contains all the required fields according to the JSON-RPC 2.0 specification.
|
||||
// See: https://www.jsonrpc.org/specification#notification
|
||||
pub struct Notification {
|
||||
pub mut:
|
||||
pub mut:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string = "2.0" @[required]
|
||||
|
||||
jsonrpc string = '2.0' @[required]
|
||||
|
||||
// The name of the method to be invoked on the server
|
||||
method string @[required]
|
||||
method string @[required]
|
||||
}
|
||||
|
||||
// Notification represents a JSON-RPC 2.0 notification object.
|
||||
// It contains all the required fields according to the JSON-RPC 2.0 specification.
|
||||
// See: https://www.jsonrpc.org/specification#notification
|
||||
pub struct NotificationGeneric[T] {
|
||||
pub mut:
|
||||
pub mut:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string = "2.0" @[required]
|
||||
|
||||
jsonrpc string = '2.0' @[required]
|
||||
|
||||
// The name of the method to be invoked on the server
|
||||
method string @[required]
|
||||
params ?T
|
||||
method string @[required]
|
||||
params ?T
|
||||
}
|
||||
|
||||
// new_notification creates a new JSON-RPC notification with the specified method and parameters.
|
||||
@@ -36,15 +36,15 @@ pub struct NotificationGeneric[T] {
|
||||
// - A fully initialized Notification object
|
||||
pub fn new_notification[T](method string, params T) NotificationGeneric[T] {
|
||||
return NotificationGeneric[T]{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
jsonrpc: jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_blank_notification(method string) Notification {
|
||||
return Notification{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
jsonrpc: jsonrpc_version
|
||||
method: method
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,18 +10,18 @@ import rand
|
||||
pub struct Request {
|
||||
pub mut:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string @[required]
|
||||
|
||||
jsonrpc string @[required]
|
||||
|
||||
// The name of the method to be invoked on the server
|
||||
method string @[required]
|
||||
|
||||
method string @[required]
|
||||
|
||||
// The parameters to the method, encoded as a JSON string
|
||||
// This can be omitted if the method doesn't require parameters
|
||||
params string
|
||||
|
||||
params string
|
||||
|
||||
// An identifier established by the client that must be included in the response
|
||||
// This is used to correlate requests with their corresponding responses
|
||||
id int @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_request creates a new JSON-RPC request with the specified method and parameters.
|
||||
@@ -35,10 +35,10 @@ pub mut:
|
||||
// - A fully initialized Request object
|
||||
pub fn new_request(method string, params string) Request {
|
||||
return Request{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.i32() // Automatically generate a unique ID using UUID v4
|
||||
jsonrpc: jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.i32() // Automatically generate a unique ID using UUID v4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,15 +83,15 @@ pub struct RequestGeneric[T] {
|
||||
pub mut:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string @[required]
|
||||
|
||||
|
||||
// The name of the method to be invoked on the server
|
||||
method string @[required]
|
||||
|
||||
method string @[required]
|
||||
|
||||
// The parameters to the method, with a specific type T
|
||||
params T
|
||||
|
||||
params T
|
||||
|
||||
// An identifier established by the client
|
||||
id int @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_request_generic creates a new generic JSON-RPC request with strongly-typed parameters.
|
||||
@@ -105,10 +105,10 @@ pub mut:
|
||||
// - A fully initialized RequestGeneric object with parameters of type T
|
||||
pub fn new_request_generic[T](method string, params T) RequestGeneric[T] {
|
||||
return RequestGeneric[T]{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.i32()
|
||||
jsonrpc: jsonrpc_version
|
||||
method: method
|
||||
params: params
|
||||
id: rand.i32()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,4 +159,4 @@ pub fn decode_request_generic[T](data string) !RequestGeneric[T] {
|
||||
// - A JSON string representation of the RequestGeneric object
|
||||
pub fn (req RequestGeneric[T]) encode[T]() string {
|
||||
return json2.encode(req)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ module jsonrpc
|
||||
|
||||
fn test_new_request() {
|
||||
request := new_request('test_method', 'test_params')
|
||||
assert request.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert request.jsonrpc == jsonrpc_version
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == 'test_params'
|
||||
assert request.id != -1 // Ensure the ID is generated
|
||||
@@ -11,7 +11,7 @@ fn test_new_request() {
|
||||
fn test_decode_request() {
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":"test_params","id":"123"}'
|
||||
request := decode_request(data) or {
|
||||
assert false, 'Failed to decode request: $err'
|
||||
assert false, 'Failed to decode request: ${err}'
|
||||
return
|
||||
}
|
||||
assert request.jsonrpc == '2.0'
|
||||
@@ -23,13 +23,16 @@ fn test_decode_request() {
|
||||
fn test_request_encode() {
|
||||
request := new_request('test_method', 'test_params')
|
||||
json := request.encode()
|
||||
assert json.contains('"jsonrpc"') && json.contains('"method"') && json.contains('"params"') && json.contains('"id"')
|
||||
assert json.contains('"jsonrpc"') && json.contains('"method"') && json.contains('"params"')
|
||||
&& json.contains('"id"')
|
||||
}
|
||||
|
||||
fn test_new_request_generic() {
|
||||
params := {'key': 'value'}
|
||||
params := {
|
||||
'key': 'value'
|
||||
}
|
||||
request := new_request_generic('test_method', params)
|
||||
assert request.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert request.jsonrpc == jsonrpc_version
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == params
|
||||
assert request.id != -1 // Ensure the ID is generated
|
||||
@@ -38,7 +41,7 @@ fn test_new_request_generic() {
|
||||
fn test_decode_request_id() {
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":"test_params","id":"123"}'
|
||||
id := decode_request_id(data) or {
|
||||
assert false, 'Failed to decode request ID: $err'
|
||||
assert false, 'Failed to decode request ID: ${err}'
|
||||
return
|
||||
}
|
||||
assert id == 123
|
||||
@@ -47,7 +50,7 @@ fn test_decode_request_id() {
|
||||
fn test_decode_request_method() {
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":"test_params","id":"123"}'
|
||||
method := decode_request_method(data) or {
|
||||
assert false, 'Failed to decode request method: $err'
|
||||
assert false, 'Failed to decode request method: ${err}'
|
||||
return
|
||||
}
|
||||
assert method == 'test_method'
|
||||
@@ -56,18 +59,23 @@ fn test_decode_request_method() {
|
||||
fn test_decode_request_generic() {
|
||||
data := '{"jsonrpc":"2.0","method":"test_method","params":{"key":"value"},"id":123}'
|
||||
request := decode_request_generic[map[string]string](data) or {
|
||||
assert false, 'Failed to decode generic request: $err'
|
||||
assert false, 'Failed to decode generic request: ${err}'
|
||||
return
|
||||
}
|
||||
assert request.jsonrpc == '2.0'
|
||||
assert request.method == 'test_method'
|
||||
assert request.params == {'key': 'value'}
|
||||
assert request.params == {
|
||||
'key': 'value'
|
||||
}
|
||||
assert request.id == 123
|
||||
}
|
||||
|
||||
fn test_request_generic_encode() {
|
||||
params := {'key': 'value'}
|
||||
params := {
|
||||
'key': 'value'
|
||||
}
|
||||
request := new_request_generic('test_method', params)
|
||||
json := request.encode()
|
||||
assert json.contains('"jsonrpc"') && json.contains('"method"') && json.contains('"params"') && json.contains('"id"')
|
||||
}
|
||||
assert json.contains('"jsonrpc"') && json.contains('"method"') && json.contains('"params"')
|
||||
&& json.contains('"id"')
|
||||
}
|
||||
|
||||
@@ -13,15 +13,15 @@ pub struct Response {
|
||||
pub:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string @[required]
|
||||
|
||||
|
||||
// The result of the method invocation (only present if the call was successful)
|
||||
result ?string
|
||||
|
||||
result ?string
|
||||
|
||||
// Error object if the request failed (only present if the call failed)
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
// Must match the id of the request that generated this response
|
||||
id int @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_response creates a successful JSON-RPC response with the given result.
|
||||
@@ -34,9 +34,9 @@ pub:
|
||||
// - A Response object containing the result
|
||||
pub fn new_response(id int, result string) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
result: result
|
||||
id: id
|
||||
jsonrpc: jsonrpc_version
|
||||
result: result
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +50,9 @@ pub fn new_response(id int, result string) Response {
|
||||
// - A Response object containing the error
|
||||
pub fn new_error_response(id int, error RPCError) Response {
|
||||
return Response{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
error_: error
|
||||
id: id
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: error
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,19 +77,19 @@ pub fn decode_response(data string) !Response {
|
||||
|
||||
// Handle error responses
|
||||
if err := raw_map['error'] {
|
||||
id_any := raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}
|
||||
return Response {
|
||||
id: id_any.int()
|
||||
id_any := raw_map['id'] or { return error('Invalid JSONRPC response, no ID Field found') }
|
||||
return Response{
|
||||
id: id_any.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
}
|
||||
}
|
||||
|
||||
// Handle successful responses
|
||||
return Response {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
return Response{
|
||||
id: raw_map['id'] or { return error('Invalid JSONRPC response, no ID Field found') }.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
result: raw_map['result']!.str()
|
||||
result: raw_map['result']!.str()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,8 +148,9 @@ pub fn (resp Response) error() ?RPCError {
|
||||
pub fn (resp Response) result() !string {
|
||||
if err := resp.error() {
|
||||
return err
|
||||
} // Ensure no error is present
|
||||
return resp.result or {''}
|
||||
}
|
||||
// Ensure no error is present
|
||||
return resp.result or { '' }
|
||||
}
|
||||
|
||||
// ResponseGeneric is a type-safe version of the Response struct that allows
|
||||
@@ -159,15 +160,15 @@ pub struct ResponseGeneric[D] {
|
||||
pub mut:
|
||||
// The JSON-RPC protocol version, must be exactly "2.0"
|
||||
jsonrpc string @[required]
|
||||
|
||||
|
||||
// The result of the method invocation with a specific type D
|
||||
result ?D
|
||||
|
||||
result ?D
|
||||
|
||||
// Error object if the request failed
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
error_ ?RPCError @[json: 'error']
|
||||
|
||||
// Must match the id of the request that generated this response
|
||||
id int @[required]
|
||||
id int @[required]
|
||||
}
|
||||
|
||||
// new_response_generic creates a successful generic JSON-RPC response with a strongly-typed result.
|
||||
@@ -180,9 +181,9 @@ pub mut:
|
||||
// - A ResponseGeneric object with result of type D
|
||||
pub fn new_response_generic[D](id int, result D) ResponseGeneric[D] {
|
||||
return ResponseGeneric[D]{
|
||||
jsonrpc: jsonrpc.jsonrpc_version
|
||||
result: result
|
||||
id: id
|
||||
jsonrpc: jsonrpc_version
|
||||
result: result
|
||||
id: id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ pub fn new_response_generic[D](id int, result D) ResponseGeneric[D] {
|
||||
// - A ResponseGeneric object with result of type D, or an error if parsing fails
|
||||
pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||
// Debug output - consider removing in production
|
||||
|
||||
|
||||
raw := json2.raw_decode(data)!
|
||||
raw_map := raw.as_map()
|
||||
|
||||
@@ -209,19 +210,21 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] {
|
||||
|
||||
// Handle error responses
|
||||
if err := raw_map['error'] {
|
||||
return ResponseGeneric[D] {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
return ResponseGeneric[D]{
|
||||
id: raw_map['id'] or {
|
||||
return error('Invalid JSONRPC response, no ID Field found')
|
||||
}.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
error_: json2.decode[RPCError](err.str())!
|
||||
}
|
||||
}
|
||||
|
||||
// Handle successful responses
|
||||
resp := json.decode(ResponseGeneric[D], data)!
|
||||
return ResponseGeneric[D] {
|
||||
id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int()
|
||||
return ResponseGeneric[D]{
|
||||
id: raw_map['id'] or { return error('Invalid JSONRPC response, no ID Field found') }.int()
|
||||
jsonrpc: jsonrpc_version
|
||||
result: resp.result
|
||||
result: resp.result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +279,7 @@ pub fn (resp ResponseGeneric[D]) error() ?RPCError {
|
||||
pub fn (resp ResponseGeneric[D]) result() !D {
|
||||
if err := resp.error() {
|
||||
return err
|
||||
} // Ensure no error is present
|
||||
return resp.result or {D{}}
|
||||
}
|
||||
}
|
||||
// Ensure no error is present
|
||||
return resp.result or { D{} }
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ module jsonrpc
|
||||
|
||||
fn test_new_response() {
|
||||
response := new_response(123, 'test_result')
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert response.jsonrpc == jsonrpc_version
|
||||
assert response.id == 123
|
||||
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
result := response.result() or {
|
||||
@@ -16,18 +16,18 @@ fn test_new_response() {
|
||||
|
||||
fn test_new_error_response() {
|
||||
error := RPCError{
|
||||
code: 123
|
||||
code: 123
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
data: 'Error details'
|
||||
}
|
||||
response := new_error_response(123, error)
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
assert response.jsonrpc == jsonrpc_version
|
||||
|
||||
response.validate()!
|
||||
assert response.is_error()
|
||||
assert !response.is_result() // Ensure no result is set
|
||||
assert response.id == 123
|
||||
|
||||
|
||||
response_error := response.error()?
|
||||
assert response_error == error
|
||||
}
|
||||
@@ -35,7 +35,7 @@ fn test_new_error_response() {
|
||||
fn test_decode_response() {
|
||||
data := '{"jsonrpc":"2.0","result":"test_result","id":"123"}'
|
||||
response := decode_response(data) or {
|
||||
assert false, 'Failed to decode response: $err'
|
||||
assert false, 'Failed to decode response: ${err}'
|
||||
return
|
||||
}
|
||||
assert response.jsonrpc == '2.0'
|
||||
@@ -58,18 +58,18 @@ fn test_response_encode() {
|
||||
|
||||
fn test_response_validate() {
|
||||
response := new_response(123, 'test_result')
|
||||
response.validate() or { assert false, 'Validation failed for valid response: $err' }
|
||||
response.validate() or { assert false, 'Validation failed for valid response: ${err}' }
|
||||
|
||||
error := RPCError{
|
||||
code: 123
|
||||
code: 123
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
data: 'Error details'
|
||||
}
|
||||
invalid_response := Response{
|
||||
jsonrpc: '2.0'
|
||||
result: 'test_result'
|
||||
error_: error
|
||||
id: 123
|
||||
result: 'test_result'
|
||||
error_: error
|
||||
id: 123
|
||||
}
|
||||
invalid_response.validate() or {
|
||||
assert err.msg().contains('Response contains both error and result.')
|
||||
@@ -78,13 +78,13 @@ fn test_response_validate() {
|
||||
|
||||
fn test_response_error() {
|
||||
error := RPCError{
|
||||
code: 123
|
||||
code: 123
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
data: 'Error details'
|
||||
}
|
||||
response := new_error_response(123, error)
|
||||
err := response.error() or {
|
||||
assert false, 'Failed to get error: $err'
|
||||
assert false, 'Failed to get error: ${err}'
|
||||
return
|
||||
}
|
||||
assert err.code == 123
|
||||
@@ -95,64 +95,76 @@ fn test_response_error() {
|
||||
fn test_response_result() {
|
||||
response := new_response(123, 'test_result')
|
||||
result := response.result() or {
|
||||
assert false, 'Failed to get result: $err'
|
||||
assert false, 'Failed to get result: ${err}'
|
||||
return
|
||||
}
|
||||
assert result == 'test_result'
|
||||
}
|
||||
|
||||
fn test_new_response_generic() {
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
assert response.jsonrpc == jsonrpc.jsonrpc_version
|
||||
response := new_response_generic(123, {
|
||||
'key': 'value'
|
||||
})
|
||||
assert response.jsonrpc == jsonrpc_version
|
||||
assert response.id == 123
|
||||
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
result := response.result() or {
|
||||
assert false, 'Response should have result'
|
||||
return
|
||||
}
|
||||
assert result == {'key': 'value'}
|
||||
assert result == {
|
||||
'key': 'value'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_decode_response_generic() {
|
||||
data := '{"jsonrpc":"2.0","result":{"key":"value"},"id":"123"}'
|
||||
response := decode_response_generic[map[string]string](data) or {
|
||||
assert false, 'Failed to decode generic response: $err'
|
||||
assert false, 'Failed to decode generic response: ${err}'
|
||||
return
|
||||
}
|
||||
assert response.jsonrpc == '2.0'
|
||||
assert response.id == 123
|
||||
|
||||
|
||||
assert response.is_result()
|
||||
assert !response.is_error() // Ensure no error is set
|
||||
result := response.result() or {
|
||||
assert false, 'Response should have result'
|
||||
return
|
||||
}
|
||||
assert result == {'key': 'value'}
|
||||
assert result == {
|
||||
'key': 'value'
|
||||
}
|
||||
}
|
||||
|
||||
fn test_response_generic_encode() {
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
response := new_response_generic(123, {
|
||||
'key': 'value'
|
||||
})
|
||||
json := response.encode()
|
||||
assert json.contains('"jsonrpc"') && json.contains('"result"') && json.contains('"id"')
|
||||
}
|
||||
|
||||
fn test_response_generic_validate() {
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
response.validate() or { assert false, 'Validation failed for valid response: $err' }
|
||||
response := new_response_generic(123, {
|
||||
'key': 'value'
|
||||
})
|
||||
response.validate() or { assert false, 'Validation failed for valid response: ${err}' }
|
||||
|
||||
error := RPCError{
|
||||
code: 123
|
||||
code: 123
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
data: 'Error details'
|
||||
}
|
||||
invalid_response := ResponseGeneric{
|
||||
jsonrpc: '2.0'
|
||||
result: {'key': 'value'}
|
||||
error_: error
|
||||
id: 123
|
||||
result: {
|
||||
'key': 'value'
|
||||
}
|
||||
error_: error
|
||||
id: 123
|
||||
}
|
||||
invalid_response.validate() or {
|
||||
assert err.msg().contains('Response contains both error and result.')
|
||||
@@ -161,17 +173,17 @@ fn test_response_generic_validate() {
|
||||
|
||||
fn test_response_generic_error() {
|
||||
error := RPCError{
|
||||
code: 123
|
||||
code: 123
|
||||
message: 'Test error'
|
||||
data: 'Error details'
|
||||
data: 'Error details'
|
||||
}
|
||||
response := ResponseGeneric[map[string]string]{
|
||||
jsonrpc: '2.0'
|
||||
error_: error
|
||||
id: 123
|
||||
error_: error
|
||||
id: 123
|
||||
}
|
||||
err := response.error() or {
|
||||
assert false, 'Failed to get error: $err'
|
||||
assert false, 'Failed to get error: ${err}'
|
||||
return
|
||||
}
|
||||
assert err.code == 123
|
||||
@@ -180,10 +192,14 @@ fn test_response_generic_error() {
|
||||
}
|
||||
|
||||
fn test_response_generic_result() {
|
||||
response := new_response_generic(123, {'key': 'value'})
|
||||
response := new_response_generic(123, {
|
||||
'key': 'value'
|
||||
})
|
||||
result := response.result() or {
|
||||
assert false, 'Failed to get result: $err'
|
||||
assert false, 'Failed to get result: ${err}'
|
||||
return
|
||||
}
|
||||
assert result == {'key': 'value'}
|
||||
}
|
||||
assert result == {
|
||||
'key': 'value'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +15,8 @@ fn testfunction0_handler(data string) !string {
|
||||
result := testfunction0(request.params)
|
||||
response := jsonrpc.JsonRpcResponse[string]{
|
||||
jsonrpc: '2.0.0'
|
||||
id: request.id
|
||||
result: result
|
||||
id: request.id
|
||||
result: result
|
||||
}
|
||||
return response.to_json()
|
||||
}
|
||||
@@ -26,8 +26,8 @@ fn testfunction1_handler(data string) !string {
|
||||
result := testfunction1(request.params)
|
||||
response := jsonrpc.JsonRpcResponse[[]string]{
|
||||
jsonrpc: '2.0.0'
|
||||
id: request.id
|
||||
result: result
|
||||
id: request.id
|
||||
result: result
|
||||
}
|
||||
return response.to_json()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user