{"id":13808167,"url":"https://github.com/igumnoff/gabriel2","last_synced_at":"2025-05-14T02:31:22.311Z","repository":{"id":228495883,"uuid":"774148477","full_name":"igumnoff/gabriel2","owner":"igumnoff","description":"Gabriel2: Indeed, an actor library based on Tokio, written in Rust","archived":false,"fork":false,"pushed_at":"2024-07-12T06:02:59.000Z","size":1713,"stargazers_count":24,"open_issues_count":6,"forks_count":6,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-10-30T12:23:20.306Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://docs.rs/gabriel2","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/igumnoff.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-19T02:54:22.000Z","updated_at":"2024-09-19T06:53:11.000Z","dependencies_parsed_at":"2024-04-28T12:29:24.569Z","dependency_job_id":"85c925c4-fb85-4ee1-b7c2-8e8f1c7d0bc5","html_url":"https://github.com/igumnoff/gabriel2","commit_stats":null,"previous_names":["igumnoff/gabriel"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igumnoff%2Fgabriel2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igumnoff%2Fgabriel2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igumnoff%2Fgabriel2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/igumnoff%2Fgabriel2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/igumnoff","download_url":"https://codeload.github.com/igumnoff/gabriel2/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225273222,"owners_count":17448071,"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":"2024-08-04T01:01:36.522Z","updated_at":"2024-11-19T00:30:35.923Z","avatar_url":"https://github.com/igumnoff.png","language":"Rust","funding_links":[],"categories":["Libraries"],"sub_categories":["Asynchronous"],"readme":"# Gabriel2\n\n![Gabriel2](https://github.com/igumnoff/gabriel2/raw/HEAD/logo.png)\n\nGabriel2: Indeed, an actor library based on Tokio, written in Rust\n\n## Features\n\n- [x] Async for sending messages\n- [x] Async for messages processing in actor\n- [x] Support messaging like send and forget \n- [x] Support messaging like send and wait response\n- [x] Mutable state of actor\n- [x] Self reference in actor from context\n- [x] Actor lifecycle (pre_start, pre_stop)\n- [x] Sink to actor\n- [x] Stream from actor\n- [x] Remote Actor\n- [x] Event Bus\n- [x] Load Balancer\n\n\n## Usage\n\nCargo.toml\n\n```toml\n[dependencies]\ngabriel2 = { version = \"1.5.0\", features = [\"remote\", \"sink-stream\", \"broadcast\", \"balancer\"] }\n```\n\necho.rs\n\n```rust\nuse std::sync::Arc;\nuse gabriel2::*;\n\nuse bincode::{Decode, Encode};\nuse derive_more::{Display, Error};\n\n\n#[derive(Debug)]\npub struct EchoActor;\n\n#[derive(Debug)]\npub enum EchoMessage {\n    Ping,\n}\n\n#[derive(Debug)]\npub enum EchoResponse {\n    Pong {counter: u32},\n}\n\n#[derive(Debug,Clone)]\npub struct EchoState {\n    pub counter: u32,\n}\n\n#[derive(Debug, Display, Error)]\npub enum EchoError {\n    #[display(fmt = \"Unknown error\")]\n    Unknown,\n}\n\nimpl From\u003cstd::io::Error\u003e for EchoError {\n    fn from(_err: std::io::Error) -\u003e Self {\n        EchoError::Unknown\n    }\n}\n\nimpl Handler for EchoActor {\n    type Actor = EchoActor;\n    type Message = EchoMessage;\n    type State = EchoState;\n    type Response = EchoResponse;\n    type Error = EchoError;\n\n    async fn receive(\u0026self, ctx: Arc\u003cContext\u003cSelf::Actor, Self::Message, Self::State, Self::Response, Self::Error\u003e\u003e) -\u003e Result\u003cEchoResponse, EchoError\u003e {\n        match ctx.mgs {\n            EchoMessage::Ping =\u003e {\n                println!(\"Received Ping\");\n                let mut state_lock = ctx.state.lock().await;\n                state_lock.counter += 1;\n                if state_lock.counter \u003e 10 {\n                    Err(EchoError::Unknown)\n                } else {\n                    Ok(EchoResponse::Pong{counter: state_lock.counter})\n                }\n            }\n        }\n    }\n}\n```\n\nmain.rs\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let state = EchoState {\n        counter: 0,\n    };\n\n    let echo_ref = ActorRef::new(\"echo\", EchoActor {}, state, 100000).await?;\n\n    println!(\"Sent Ping\");\n    echo_ref.send(EchoMessage::Ping).await?;\n\n    println!(\"Sent Ping and ask response\");\n    let pong = echo_ref.ask(EchoMessage::Ping).await?;\n    println!(\"Got {:?}\", pong);\n\n    _ = echo_ref.stop().await;\n    Ok(())\n}\n```\n\nExample output:\n\n```text \nSent Ping\nSent Ping and ask response\nReceived Ping\nReceived Ping\nGot Pong { counter: 2 }\n```\n\nExample sources: https://github.com/igumnoff/gabriel2/tree/main/test\n\n\n## Sink\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let state = EchoState {\n        counter: 0,\n    };\n\n    let echo_ref = ActorRef::new(\"echo\", crate::echo::EchoActor {}, state, 100000).await?;\n    let echo_sink = ActorSink::sink(echo_ref.clone());\n    let message_stream = futures::stream::iter(vec![EchoMessage::Ping, EchoMessage::Ping, EchoMessage::Ping]).map(Ok);\n    _ = message_stream.forward(echo_sink).await;\n    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;\n     Ok(())\n}\n```\n\nExample output:\n```\nReceived Ping\nReceived Ping\nReceived Ping\n```\n\n### Stream\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let state = EchoState {\n        counter: 0,\n    };\n\n    let echo_ref = ActorRef::new(\"echo\", crate::echo::EchoActor {}, state, 100000).await?;\n    let (echo_sink, echo_stream) = ActorSink::sink_stream(echo_ref.clone());\n    let message_stream = futures::stream::iter(vec![EchoMessage::Ping, EchoMessage::Ping, EchoMessage::Ping]).map(Ok);\n    _ = message_stream.forward(echo_sink).await;\n    echo_stream.for_each(|message| async move {\n        println!(\"Got {:?}\", message.unwrap());\n    }).await;\n    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;\n\n    Ok(())\n}\n```\nExample output:\n\n```\nReceived Ping\nReceived Ping\nReceived Ping\nGot Pong { counter: 1 }\nGot Pong { counter: 2 }\nGot Pong { counter: 3 }\n```\n\n## Remote \n\nPreparations for remote:\n\nAdd Encode, Decode from \"bincode\" to derive(..) for EchoActor, EchoMessage, EchoResponse, EchoState and EchoError\n\nRemote version:\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let state = EchoState {\n        counter: 0,\n    };\n\n    let echo_ref = ActorRef::new(\"echo\", crate::echo::EchoActor {}, state, 100000).await?;\n    let echo_server = ActorServer::new(\"echo_server\", \"127.0.0.1\", 9001, echo_ref).await?;\n    let echo_client: Arc\u003cActorClient\u003cEchoActor, EchoMessage, EchoState, EchoResponse, EchoError \u003e\u003e = ActorClient::new(\"echo_client\", \"127.0.0.1\", 9001).await?;\n\n    println!(\"Sent Ping\");\n    echo_client.send(EchoMessage::Ping).await?;\n\n    println!(\"Sent Ping and ask response\");\n    let pong = echo_client.ask(EchoMessage::Ping).await?;\n    println!(\"Got {:?}\", pong);\n\n    _ = echo_client.stop().await;\n    _ = echo_server.stop().await;\n    Ok(())\n\n}\n```\n\n### Event Bus\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let state = EchoState {\n        counter: 0,\n    };\n\n    #[derive(Debug, Copy, Clone)]\n    enum EventElement {\n        Fire,\n        Water\n    }\n\n    let echo_ref = ActorRef::new(\"echo\", crate::echo::EchoActor {}, state, 100000).await?;\n\n    let event_bus: Arc\u003cEventBus\u003cEventElement\u003e\u003e = Arc::new(EventBus::new());\n\n    let subscriber_id = event_bus.subscribe(move |event: EventElement| {\n        async move {\n            match event {\n                EventElement::Fire =\u003e {\n                    let _ = echo_ref.send(EchoMessage::Ping).await;\n                    ()\n                },\n                _ =\u003e ()\n            }\n        }}).await;\n\n    event_bus.publish(EventElement::Fire).await;\n\n    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;\n\n    event_bus.unsubscribe(subscriber_id).await;\n\n    Ok(())\n}\n``` \n\n### Load Balancer\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e Result\u003c(), EchoError\u003e {\n    let echo_load_balancer: Arc\u003cLoadBalancer\u003cEchoActor, EchoMessage, EchoState, EchoResponse, EchoError\u003e\u003e =\n        LoadBalancer::new(\"echo_load_balancer\", 10, |id: usize| {\n            Box::pin(async move {\n                let user: Arc\u003c\n                    ActorRef\u003cEchoActor, EchoMessage, EchoState, EchoResponse, EchoError\u003e,\n                \u003e = ActorRef::new(\n                    format!(\"echo-{}\", id),\n                    EchoActor {},\n                    EchoState { counter: 0 },\n                    10000,\n                )\n                    .await?;\n                Ok(user)\n            })\n        })\n            .await\n            .unwrap();\n\n    for _ in 0..30 {\n        echo_load_balancer.send(EchoMessage::Ping).await?;\n    }\n\n    Ok(())\n}\n``` \n\n\n## Contributing\nI would love to see contributions from the community. If you experience bugs, feel free to open an issue. If you would like to implement a new feature or bug fix, please follow the steps:\n1. Read \"[Contributor License Agreement (CLA)](https://github.com/igumnoff/gabriel2/blob/main/CLA)\"\n2. Contact with me via telegram @ievkz or discord @igumnovnsk\n3. Confirm e-mail invitation in repository\n4. Do \"git clone\" (You don't need to fork!)\n5. Create branch with your assigned issue\n6. Create pull request to main branch\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figumnoff%2Fgabriel2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Figumnoff%2Fgabriel2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Figumnoff%2Fgabriel2/lists"}