diff --git a/lib/schemas/jsonrpc/client.v b/lib/schemas/jsonrpc/client.v index 5d6bbb02..73241f38 100644 --- a/lib/schemas/jsonrpc/client.v +++ b/lib/schemas/jsonrpc/client.v @@ -37,6 +37,7 @@ pub fn new_client(client Client) &Client { // These parameters control timeout and retry behavior. @[params] pub struct SendParams { +pub: // Maximum time in seconds to wait for a response (default: 60) timeout int = 60 diff --git a/lib/schemas/jsonrpc/handler.v b/lib/schemas/jsonrpc/handler.v index 854e2b3e..1c4b9509 100644 --- a/lib/schemas/jsonrpc/handler.v +++ b/lib/schemas/jsonrpc/handler.v @@ -9,7 +9,7 @@ import net.websocket // Handler is a JSON-RPC request handler that maps method names to their corresponding procedure handlers. // It can be used with a WebSocket server to handle incoming JSON-RPC requests. pub struct Handler { -pub: +pub mut: // A map where keys are method names and values are the corresponding procedure handler functions procedures map[string]ProcedureHandler } @@ -20,7 +20,7 @@ pub: // 2. Execute the procedure with the extracted parameters // 3. Return the result as a JSON-encoded string // If an error occurs during any of these steps, it should be returned. -type ProcedureHandler = fn (payload string) !string +pub type ProcedureHandler = fn (payload string) !string // new_handler creates a new JSON-RPC handler with the specified procedure handlers. // @@ -33,6 +33,15 @@ pub fn new_handler(handler Handler) !&Handler { return &Handler{...handler} } +// register_procedure registers a new procedure handler for the specified method. +// +// Parameters: +// - method: The name of the method to register +// - procedure: The procedure handler function to register +pub fn (mut handler Handler) register_procedure(method string, procedure ProcedureHandler) { + handler.procedures[method] = procedure +} + // handler is a callback function compatible with the WebSocket server's message handler interface. // It processes an incoming WebSocket message as a JSON-RPC request and returns the response. // @@ -58,14 +67,14 @@ pub fn (handler Handler) handler(client &websocket.Client, message string) strin 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}') + // 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') + // log.error('No procedure handler for method ${method} found') return method_not_found } - + // Execute the procedure handler with the request payload response := procedure_func(message) or { panic(err) } return response diff --git a/lib/schemas/jsonrpc/model_error.v b/lib/schemas/jsonrpc/model_error.v index e47ab1f8..f51a5e83 100644 --- a/lib/schemas/jsonrpc/model_error.v +++ b/lib/schemas/jsonrpc/model_error.v @@ -73,7 +73,7 @@ pub mut: // // Returns: // - A Response object containing the error -pub fn new_error(id string, error RPCError) Response { +pub fn new_error(id int, error RPCError) Response { return Response{ jsonrpc: jsonrpc_version error_: error diff --git a/lib/schemas/jsonrpc/model_request.v b/lib/schemas/jsonrpc/model_request.v index a87a9404..919fc98b 100644 --- a/lib/schemas/jsonrpc/model_request.v +++ b/lib/schemas/jsonrpc/model_request.v @@ -1,5 +1,6 @@ module jsonrpc +import json import x.json2 import rand @@ -20,7 +21,7 @@ pub mut: // An identifier established by the client that must be included in the response // This is used to correlate requests with their corresponding responses - id string @[required] + id int @[required] } // new_request creates a new JSON-RPC request with the specified method and parameters. @@ -37,7 +38,7 @@ pub fn new_request(method string, params string) Request { jsonrpc: jsonrpc.jsonrpc_version method: method params: params - id: rand.uuid_v4() // Automatically generate a unique ID using UUID v4 + id: rand.i32() // Automatically generate a unique ID using UUID v4 } } @@ -68,7 +69,7 @@ pub fn (req Request) encode() string { pub fn (req Request) validate() ! { if req.jsonrpc == '' { return error('request jsonrpc version not specified') - } else if req.id == '' { + } else if req.id == -1 { return error('request id is empty') } else if req.method == '' { return error('request method is empty') @@ -90,7 +91,7 @@ pub mut: params T // An identifier established by the client - id string @[required] + id int @[required] } // new_request_generic creates a new generic JSON-RPC request with strongly-typed parameters. @@ -107,7 +108,7 @@ pub fn new_request_generic[T](method string, params T) RequestGeneric[T] { jsonrpc: jsonrpc.jsonrpc_version method: method params: params - id: rand.uuid_v4() + id: rand.i32() } } @@ -119,11 +120,11 @@ pub fn new_request_generic[T](method string, params T) RequestGeneric[T] { // // Returns: // - The ID as a string, or an error if the ID field is missing -pub fn decode_request_id(data string) !string { +pub fn decode_request_id(data string) !int { data_any := json2.raw_decode(data)! data_map := data_any.as_map() id_any := data_map['id'] or { return error('ID field not found') } - return id_any.str() + return id_any.int() } // decode_request_method extracts just the method field from a JSON-RPC request string. @@ -149,7 +150,7 @@ pub fn decode_request_method(data string) !string { // Returns: // - A RequestGeneric object with parameters of type T, or an error if parsing fails pub fn decode_request_generic[T](data string) !RequestGeneric[T] { - return json2.decode[RequestGeneric[T]](data)! + return json.decode(RequestGeneric[T], data)! } // encode serializes the RequestGeneric object into a JSON string. diff --git a/lib/schemas/jsonrpc/model_request_test.v b/lib/schemas/jsonrpc/model_request_test.v index 1fdb3c25..37591d2e 100644 --- a/lib/schemas/jsonrpc/model_request_test.v +++ b/lib/schemas/jsonrpc/model_request_test.v @@ -5,7 +5,7 @@ fn test_new_request() { assert request.jsonrpc == jsonrpc.jsonrpc_version assert request.method == 'test_method' assert request.params == 'test_params' - assert request.id != '' // Ensure the ID is generated + assert request.id != -1 // Ensure the ID is generated } fn test_decode_request() { @@ -17,7 +17,7 @@ fn test_decode_request() { assert request.jsonrpc == '2.0' assert request.method == 'test_method' assert request.params == 'test_params' - assert request.id == '123' + assert request.id == 123 } fn test_request_encode() { @@ -32,7 +32,7 @@ fn test_new_request_generic() { assert request.jsonrpc == jsonrpc.jsonrpc_version assert request.method == 'test_method' assert request.params == params - assert request.id != '' // Ensure the ID is generated + assert request.id != -1 // Ensure the ID is generated } fn test_decode_request_id() { @@ -41,7 +41,7 @@ fn test_decode_request_id() { assert false, 'Failed to decode request ID: $err' return } - assert id == '123' + assert id == 123 } fn test_decode_request_method() { @@ -54,7 +54,7 @@ fn test_decode_request_method() { } fn test_decode_request_generic() { - data := '{"jsonrpc":"2.0","method":"test_method","params":{"key":"value"},"id":"123"}' + 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' return @@ -62,7 +62,7 @@ fn test_decode_request_generic() { assert request.jsonrpc == '2.0' assert request.method == 'test_method' assert request.params == {'key': 'value'} - assert request.id == '123' + assert request.id == 123 } fn test_request_generic_encode() { diff --git a/lib/schemas/jsonrpc/model_response.v b/lib/schemas/jsonrpc/model_response.v index 0a9d85c6..43003f18 100644 --- a/lib/schemas/jsonrpc/model_response.v +++ b/lib/schemas/jsonrpc/model_response.v @@ -21,7 +21,7 @@ pub: error_ ?RPCError @[json: 'error'] // Must match the id of the request that generated this response - id string @[required] + id int @[required] } // new_response creates a successful JSON-RPC response with the given result. @@ -32,7 +32,7 @@ pub: // // Returns: // - A Response object containing the result -pub fn new_response(id string, result string) Response { +pub fn new_response(id int, result string) Response { return Response{ jsonrpc: jsonrpc.jsonrpc_version result: result @@ -48,7 +48,7 @@ pub fn new_response(id string, result string) Response { // // Returns: // - A Response object containing the error -pub fn new_error_response(id string, error RPCError) Response { +pub fn new_error_response(id int, error RPCError) Response { return Response{ jsonrpc: jsonrpc.jsonrpc_version error_: error @@ -79,7 +79,7 @@ pub fn decode_response(data string) !Response { 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.str() + id: id_any.int() jsonrpc: jsonrpc_version error_: json2.decode[RPCError](err.str())! } @@ -87,7 +87,7 @@ pub fn decode_response(data string) !Response { // Handle successful responses return Response { - id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.str() + id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int() jsonrpc: jsonrpc_version result: raw_map['result']!.str() } @@ -167,7 +167,7 @@ pub mut: error_ ?RPCError @[json: 'error'] // Must match the id of the request that generated this response - id string @[required] + id int @[required] } // new_response_generic creates a successful generic JSON-RPC response with a strongly-typed result. @@ -178,7 +178,7 @@ pub mut: // // Returns: // - A ResponseGeneric object with result of type D -pub fn new_response_generic[D](id string, result D) ResponseGeneric[D] { +pub fn new_response_generic[D](id int, result D) ResponseGeneric[D] { return ResponseGeneric[D]{ jsonrpc: jsonrpc.jsonrpc_version result: result @@ -196,7 +196,6 @@ pub fn new_response_generic[D](id string, 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 - println('respodata ${data}') raw := json2.raw_decode(data)! raw_map := raw.as_map() @@ -211,7 +210,7 @@ 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')}.str() + id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int() jsonrpc: jsonrpc_version error_: json2.decode[RPCError](err.str())! } @@ -220,7 +219,7 @@ pub fn decode_response_generic[D](data string) !ResponseGeneric[D] { // 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')}.str() + id: raw_map['id'] or {return error('Invalid JSONRPC response, no ID Field found')}.int() jsonrpc: jsonrpc_version result: resp.result } diff --git a/lib/schemas/jsonrpc/model_response_test.v b/lib/schemas/jsonrpc/model_response_test.v index db8f82e9..3f994c0b 100644 --- a/lib/schemas/jsonrpc/model_response_test.v +++ b/lib/schemas/jsonrpc/model_response_test.v @@ -1,9 +1,9 @@ module jsonrpc fn test_new_response() { - response := new_response('123', 'test_result') + response := new_response(123, 'test_result') assert response.jsonrpc == jsonrpc.jsonrpc_version - assert response.id == '123' + assert response.id == 123 assert response.is_result() assert !response.is_error() // Ensure no error is set @@ -20,13 +20,13 @@ fn test_new_error_response() { message: 'Test error' data: 'Error details' } - response := new_error_response('123', error) + response := new_error_response(123, error) assert response.jsonrpc == jsonrpc.jsonrpc_version response.validate()! assert response.is_error() assert !response.is_result() // Ensure no result is set - assert response.id == '123' + assert response.id == 123 response_error := response.error()? assert response_error == error @@ -39,7 +39,7 @@ fn test_decode_response() { return } assert response.jsonrpc == '2.0' - assert response.id == '123' + assert response.id == 123 assert response.is_result() assert !response.is_error() // Ensure no error is set @@ -51,13 +51,13 @@ fn test_decode_response() { } fn test_response_encode() { - response := new_response('123', 'test_result') + response := new_response(123, 'test_result') json := response.encode() assert json.contains('"jsonrpc"') && json.contains('"result"') && json.contains('"id"') } fn test_response_validate() { - response := new_response('123', 'test_result') + response := new_response(123, 'test_result') response.validate() or { assert false, 'Validation failed for valid response: $err' } error := RPCError{ @@ -69,7 +69,7 @@ fn test_response_validate() { jsonrpc: '2.0' result: 'test_result' error_: error - id: '123' + id: 123 } invalid_response.validate() or { assert err.msg().contains('Response contains both error and result.') @@ -82,7 +82,7 @@ fn test_response_error() { message: 'Test error' data: 'Error details' } - response := new_error_response('123', error) + response := new_error_response(123, error) err := response.error() or { assert false, 'Failed to get error: $err' return @@ -93,7 +93,7 @@ fn test_response_error() { } fn test_response_result() { - response := new_response('123', 'test_result') + response := new_response(123, 'test_result') result := response.result() or { assert false, 'Failed to get result: $err' return @@ -102,9 +102,9 @@ fn test_response_result() { } fn test_new_response_generic() { - response := new_response_generic('123', {'key': 'value'}) + response := new_response_generic(123, {'key': 'value'}) assert response.jsonrpc == jsonrpc.jsonrpc_version - assert response.id == '123' + assert response.id == 123 assert response.is_result() assert !response.is_error() // Ensure no error is set @@ -122,7 +122,7 @@ fn test_decode_response_generic() { return } assert response.jsonrpc == '2.0' - assert response.id == '123' + assert response.id == 123 assert response.is_result() assert !response.is_error() // Ensure no error is set @@ -134,13 +134,13 @@ fn test_decode_response_generic() { } 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 := new_response_generic(123, {'key': 'value'}) response.validate() or { assert false, 'Validation failed for valid response: $err' } error := RPCError{ @@ -152,7 +152,7 @@ fn test_response_generic_validate() { jsonrpc: '2.0' result: {'key': 'value'} error_: error - id: '123' + id: 123 } invalid_response.validate() or { assert err.msg().contains('Response contains both error and result.') @@ -168,7 +168,7 @@ fn test_response_generic_error() { response := ResponseGeneric[map[string]string]{ jsonrpc: '2.0' error_: error - id: '123' + id: 123 } err := response.error() or { assert false, 'Failed to get error: $err' @@ -180,7 +180,7 @@ 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' return