{"id":24488519,"url":"https://github.com/codx-dev/jsonrpc-reactor","last_synced_at":"2025-03-15T00:15:11.056Z","repository":{"id":58346850,"uuid":"531339917","full_name":"codx-dev/jsonrpc-reactor","owner":"codx-dev","description":"JSON-RPC 2.0 Rust implementation with tokio backend.","archived":false,"fork":false,"pushed_at":"2022-09-01T13:42:07.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-02-28T05:45:30.453Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/codx-dev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-09-01T02:52:02.000Z","updated_at":"2024-07-22T03:14:17.000Z","dependencies_parsed_at":"2022-09-15T07:22:51.500Z","dependency_job_id":null,"html_url":"https://github.com/codx-dev/jsonrpc-reactor","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codx-dev%2Fjsonrpc-reactor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codx-dev%2Fjsonrpc-reactor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codx-dev%2Fjsonrpc-reactor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/codx-dev%2Fjsonrpc-reactor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/codx-dev","download_url":"https://codeload.github.com/codx-dev/jsonrpc-reactor/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243663576,"owners_count":20327306,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-21T16:19:56.099Z","updated_at":"2025-03-15T00:15:11.016Z","avatar_url":"https://github.com/codx-dev.png","language":"Rust","readme":"# JSON-RPC\n\nJSON-RPC 2.0 Rust implementation with tokio backend.\n\n# Example\n\n```rust\nuse std::time::Duration;\n\nuse jsonrpc_reactor::*;\n\n// a request handler is expected to take a request and generate a\n// response.\n//\n// optimally, it will be infallible, returning valid RPC errors in\n// case it fails. this behavior is a good practice, but it is not\n// enforced by the library, leaving the option on how to implement\n// that to the user\n//\n// it can be a complicated structure or a simple function, as in this\n// example\nasync fn requests_handler(request: Request) -\u003e Response {\n    let Request { id, method, params } = request;\n\n    match method.as_str() {\n        \"math/inc\" =\u003e {\n            let number = params\n                .as_object()\n                .expect(\"name should be object\")[\"number\"]\n                .as_i64()\n                .expect(\"the provided argument isn't a valid number\");\n\n            let number = number\n                .wrapping_add(1)\n                .into();\n\n            Response {\n                id: id,\n                result: Ok(number),\n            }\n        }\n\n        _ =\u003e Response {\n            id: id,\n            result: Err(RpcError {\n                code: -1,\n                message: \"invalid method\".into(),\n                data: method.into(),\n            }),\n        },\n    }\n}\n\n// a notification handler is expected to take a notification without a\n// reply.\n//\n// it is the same as the request handle. using channels will give the\n// user great flexibility so he can chose how to concretely implement\n// the handler\nasync fn notifications_handler(notification: Notification) {\n    let Notification { method, params } = notification;\n\n    match method.as_str() {\n        \"misc/greet\" =\u003e {\n            let name = \u0026params\n                .as_object()\n                .expect(\"name should be object\")[\"name\"];\n\n            println!(\"Hello, {}!\", name)\n        }\n\n        _ =\u003e println!(\"invalid method\"),\n    }\n}\n\n#[tokio::main]\nasync fn main() {\n    // this is the buffer capacity so the internal maps and channels\n    // will tweak around that\n    let capacity = 100;\n\n    // setup outbound requests and notifications channels\n    let (rtx, mut requests) = mpsc::channel(capacity);\n    let (ntx, mut notifications) = mpsc::channel(capacity);\n\n    // spawn the reactor thread, returning its controller and the\n    // channel that will submit requests responses to the reactor\n    let (mut reactor, service) = Reactor::spawn(capacity, rtx, ntx);\n\n    // requests handler thread. consume the service channel so the\n    // handler can submit responses for the requests\n    tokio::spawn(async move {\n        while let Some(r) = requests.recv().await {\n            let response = requests_handler(r).await;\n\n            service.send(response).await.ok();\n        }\n    });\n\n    // notifications handler thread\n    tokio::spawn(async move {\n        while let Some(n) = notifications.recv().await {\n            notifications_handler(n).await;\n        }\n    });\n\n    // the notifications timeout is used by the tokio channels in\n    // case the handler can't take more notifications\n    let method = \"misc/greet\";\n    let timeout = Some(Duration::from_secs(2));\n    let params: Params = json!({\n        \"name\": \"Victor\"\n    })\n    .try_into()\n    .expect(\"failed to create params\");\n\n    reactor.notify(method, params, timeout).await;\n\n    // the timeout is used both by the tokio channels and reactor; the\n    // latter discarding pending responses in case the handler takes\n    // too much time to reply. the oneshot channel will receive a\n    // valid JSONRPC response with a `timeout` error in case the\n    // request is dropped\n    let method = \"math/inc\";\n    let timeout = Some(Duration::from_secs(2));\n    let params: Params = json!({\n        \"number\": 15\n    })\n    .try_into()\n    .expect(\"failed to create params\");\n\n    // fetch the oneshot channel for this specific request\n    let mut awaiting = reactor\n        .request(method, params, timeout)\n        .await\n        .expect(\"failed to fetch oneshot receiver\");\n\n    // this will be an implementation detail of the application and\n    // will define how often we probe tokio oneshot channels for the reply\n    let mut interval = time::interval(Duration::from_millis(100));\n    loop {\n        tokio::select! {\n            _ = interval.tick() =\u003e (),\n\n            reply = \u0026mut awaiting =\u003e {\n                let reply = reply\n                    .expect(\"failed to read oneshot channel\")\n                    .expect(\"failed to fetch response from handler\");\n\n                println!(\"response: {}\", reply);\n\n                break;\n            }\n        }\n    }\n}\n```\n\nIt will produce the following output\n\n```\nHello, \"Victor\"!\nresponse: 16\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodx-dev%2Fjsonrpc-reactor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcodx-dev%2Fjsonrpc-reactor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcodx-dev%2Fjsonrpc-reactor/lists"}