{"id":33939336,"url":"https://github.com/austinjones/lifeline-rs","last_synced_at":"2026-04-06T07:01:41.757Z","repository":{"id":48825020,"uuid":"289763504","full_name":"austinjones/lifeline-rs","owner":"austinjones","description":"A dependency injection library for message-based applications","archived":false,"fork":false,"pushed_at":"2021-07-09T18:14:19.000Z","size":120,"stargazers_count":39,"open_issues_count":9,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-14T03:33:49.365Z","etag":null,"topics":["async","async-await","channels","dependency-injection","lifeline","rust","tokio"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/austinjones.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-08-23T20:43:39.000Z","updated_at":"2025-10-20T09:06:14.000Z","dependencies_parsed_at":"2022-09-23T22:24:24.840Z","dependency_job_id":null,"html_url":"https://github.com/austinjones/lifeline-rs","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/austinjones/lifeline-rs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austinjones%2Flifeline-rs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austinjones%2Flifeline-rs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austinjones%2Flifeline-rs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austinjones%2Flifeline-rs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/austinjones","download_url":"https://codeload.github.com/austinjones/lifeline-rs/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/austinjones%2Flifeline-rs/sbom","scorecard":{"id":216772,"data":{"date":"2025-08-11","repo":{"name":"github.com/austinjones/lifeline-rs","commit":"45c4740df7417965b5a5d667dc9ccde28df1fe2a"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.4,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/release.yml:1","Warn: no topLevel permission defined: .github/workflows/rust.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:12: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/release.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/release.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/rust.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:25: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:32: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/rust.yml:44: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:48: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:53: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:58: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:63: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/rust.yml:74: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:78: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:82: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:89: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:96: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:102: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:108: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:114: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:120: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/rust.yml:126: update your workflow using https://app.stepsecurity.io/secureworkflow/austinjones/lifeline-rs/rust.yml/master?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of  17 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 16 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-17T01:48:07.896Z","repository_id":48825020,"created_at":"2025-08-17T01:48:07.896Z","updated_at":"2025-08-17T01:48:07.896Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31463015,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-05T21:22:52.476Z","status":"online","status_checked_at":"2026-04-06T02:00:07.287Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["async","async-await","channels","dependency-injection","lifeline","rust","tokio"],"created_at":"2025-12-12T15:25:07.626Z","updated_at":"2026-04-06T07:01:41.751Z","avatar_url":"https://github.com/austinjones.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lifeline-rs\nLifeline is a dependency injection library for message-based applications.  Lifeline produces applications which are:\n - **Clean:** Bus implementations provide a high-level overview of the application, and services clearly define the messages they send and receive.\n - **Decoupled:**  Services and tasks have no dependency on their peers, as they only depend on the message types they send and receive.\n - **Stoppable:** Services and tasks are trivially cancellable.  For example, you can terminate all tasks associated with a connection when a client disconnects.\n - **Greppable:**  The impact/reach of a message can be easily understood by searching for the type in the source code.\n - **Testable:**  Lifeline applications communicate via messages, which makes unit testing easy.  Create the bus, spawn the service, send a message, and expect an output message.\n\nIn order to achieve these goals, lifeline provides patterns, traits, and implementations:\n - The **Bus**, which constructs \u0026 distributes channel Senders/Receivers, and Resources.\n - The **Carrier**, which translates messages between two Bus instances.  Carriers are critical when building large applications, and help minimize the complexity of the messages on each bus.\n - The **Service**, which takes channels from the bus, and spawns tasks which send and receive messages.\n - The **Task**, an async future which returns a lifeline when spawned.  When the lifeline is dropped, the future is immedately cancelled.\n - The **Resource**, a struct which can be stored in the bus, and taken (or cloned) when services spawn.\n\nFor a quick introduction, see the [hello.rs example.](https://github.com/austinjones/lifeline-rs/blob/master/examples/hello.rs)\nFor a full-scale application see [tab-rs.](https://github.com/austinjones/tab-rs)\n\n## Quickstart\nLifeline can be used with the [tokio](https://docs.rs/tokio/) and [async-std](https://docs.rs/async-std/) runtimes.  By default, lifeline uses `tokio`.\n```toml\nlifeline = \"0.6\"\n```\n\n[async-std](https://docs.rs/async-std/) can be enabled with the `async-std-executor` feature.  And the `mpsc` implementation can be enabled with the `async-std-channels` feature:\n```toml\nlifeline = { version = \"0.6\", default-features = false, features = [\"dyn-bus\", \"async-std-executor\", \"async-std-channels\"] }\n```\n\nLifeline also supports [postage channels](https://docs.rs/postage/), a library that provides a portable set of channel implementations (compatible with any executor).\nPostage also provides Stream and Sink combinators (similar to futures StreamExt), that are optimized for async channels.  \nPostage is intended to replace the LifelineSender/LifelineReceiver wrappers that were removed in lifeline v0.6.0.\n\n## Upgrading\nv0.6.0 contains several breaking changes:\n- The LifelineSender and LifelineReceiver wrappers were removed.  This was necessary due to the recent changes in the Stream ecosystem, and the upcoming stabilization of the Stream RFC.\nIf you need Stream/Sink combinators, take a look at [postage](https://crates.io/crates/postage), or [tokio-stream](https://crates.io/crates/tokio-stream).\n- The barrier channel was removed.  It can be replaced with [postage::barrier](https://docs.rs/postage/0.3.1/postage/barrier/index.html).\n- The subscription channel was removed.  If you need it back, you can find the code before the removal [here](https://github.com/austinjones/lifeline-rs/blob/b15ab2342abcfa9c553d403cb58d2403531bf89c/src/channel/subscription.rs).\n- The Sender and Receiver traits were removed from prelude.   This is so that importing the lifeline prelude does not conflict with Sink/Stream traits.  You can import them with:\n`use lifeline::{Sender, Receiver}`.\n\n## The Bus\nThe bus carries channels and resources.  When services spawn, they receive a reference to the bus.\n\nChannels can be taken from the bus.  If the channel endpoint is clonable, it will remain available for other services.  If the channel is not clonable, future calls will receive an `Err` value.  The Rx/Tx type parameters are type-safe, and will produce a compile error\nif you attempt to take a channel for an message type which the bus does not carry.\n\n### Bus Example\n```rust\nuse lifeline::lifeline_bus;\nuse lifeline::Message;\nuse lifeline::Bus;\nuse myapp::message::MainSend;\nuse myapp::message::MainRecv;\nuse tokio::sync::mpsc;\n\nlifeline_bus!(pub struct MainBus);\n\nimpl Message\u003cMainBus\u003e for MainSend {\n    type Channel = mpsc::Sender\u003cSelf\u003e;\n}\n\nimpl Message\u003cMainBus\u003e for MainRecv {\n    type Channel = mpsc::Sender\u003cSelf\u003e;\n}\n\nfn use_bus() -\u003e anyhow::Result\u003c()\u003e {\n    let bus = MainBus::default();\n\n    let tx_main = bus.tx::\u003cMainRecv\u003e()?;\n    tx_main.send(MainRecv {});\n\n    Ok(())\n}\n```\n\n## The Carrier\nCarriers provide a way to move messages between busses.  Carriers can translate, ignore, or collect information,\nproviding each bus with the messages that it needs.\n\nLarge applications have a tree of Busses.  This is good, it breaks your app into small chunks.\n```\n- MainBus\n  | ConnectionListenerBus\n  |  | ConnectionBus\n  | DomainSpecificBus\n  |  | ...\n```\n\nCarriers allow each bus to define messages that minimally represent the information it's services need to function, and prevent an explosion of messages which are copied to all busses.\n\nCarriers centralize the communication between busses, making large applications easier to reason about.\n\n### Carrier Example\nBusses deeper in the tree should implement `FromCarrier` for their parents - see the [carrier.rs example](https://github.com/austinjones/lifeline-rs/blob/master/examples/carrier.rs) for more details.\n\n```rust\nlet main_bus = MainBus::default();\nlet connection_listener_bus = ConnectionListenerBus::default();\nlet _carrier = connection_listener_bus.carry_from(\u0026main_bus)?;\n// you can also use the IntoCarrier trait, which has a blanket implementation\nlet _carrier = main_bus.carry_into(\u0026main_bus)?;\n```\n\n## The Service\nThe Service synchronously takes channels from the Bus, and spawns a tree of async tasks (which send \u0026 receive messages).  When spawned, the service returns one or more Lifeline values.  When a Lifeline is dropped, the associated task is immediately cancelled.\n\nIt's common for `Service::spawn` to return a Result.  Taking channel endpoints is a fallible operation.  This is because, depending on the channel type, the endpoint may not be clonable.  Lifeline clones endpoints when it can (`mpsc::Sender`, `broadcast::*`, and `watch::Receiver`).  Lifeline tries to make this happen as early as possible.\n\n```rust\nuse lifeline::{Service, Task};\npub struct ExampleService {\n    _greet: Lifeline,\n}\n\nimpl Service for ExampleService {\n    type Bus = ExampleBus;\n    type Lifeline = Lifeline;\n\n    fn spawn(bus: \u0026Self::Bus) -\u003e Self::Lifeline {\n        let mut rx = bus.rx::\u003cExampleRecv\u003e()?;\n        let mut tx = bus.tx::\u003cExampleSend\u003e()?;\n        let config = bus.resource::\u003cExampleConfig\u003e()?;\n\n        Self::task(\"greet\", async move {\n            // drive the channels!\n        })\n    }\n}\n```\n\n## The Task\nThe Task executes a `Future`, and returns a `Lifeline` value when spawned.  When the lifeline is dropped, the future is immediately cancelled.\n\n`Task` is a trait that is implemented for all types - you can import it and use `Self::task` in any type.  In lifeline, it's \nmost commonly used in Service implementations.\n\nTasks can be infallible:\n```rust\nSelf::task(\"greet\", async move {\n    // do something\n})\n```\n\nOr, if you have a fallible task, you can return `anyhow::Result\u003cT\u003e`.  Anyhow is required to solve type inference issues.\n\n```rust\nSelf::try_task(\"greet\", async move {\n    // do something\n    let something = do_something()?;\n    Ok(())\n})\n```\n\n# Testing\nOne of the goals of Lifeline is to provide interfaces that are very easy to test.  Lifeline runtimes are easy to construct in tests:\n\n```rust\n#[tokio::test]\nasync fn test() -\u003e anyhow::Result\u003c()\u003e {\n    // this is zero-cost.  channel construction is lazy.\n    let bus = MainBus::default();\n    let service = MainService::spawn(\u0026bus)?;\n\n    // the service took `bus.rx::\u003cMainRecv\u003e()`\n    //                + `bus.tx::\u003cMainSend\u003e()`\n    // let's communicate using channels we take.\n    let tx = bus.tx::\u003cMainRecv\u003e()?;\n    let rx = bus.rx::\u003cMainSend\u003e()?;\n\n    // drop the bus, so that any 'channel closed' errors will occur during our test.\n    // this would likely happen in practice during the long lifetime of the program\n    drop(bus);\n\n    tx.send(MainRecv::MyMessage)?;\n\n    // wait up to 200ms for the message to arrive\n    // if we remove the 200 at the end, the default is 50ms\n    lifeline::assert_completes!(async move {\n        let response = rx.recv().await;\n        assert_eq!(MainSend::MyResponse, response);\n    }, 200);\n\n    Ok(())\n}\n```\n\n# The Details\n### Logging\nTasks (via [log](https://docs.rs/log/0.4.11/log/)) provide debug logs when the are started, ended, or cancelled.\n\nIf the task returns a value, it is also printed to the debug log using `Debug`.\n```\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] START ExampleService/ok_task\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] END ExampleService/ok_task\n\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] START ExampleService/valued_task\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] END ExampleService/valued_task: MyStruct {}\n```\n\nIf the task is cancelled (because it's lifeline is dropped), that is also printed.\n```\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] START ExampleService/cancelled_task\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] CANCEL ExampleService/cancelled_task\n```\n\nIf the task is started using `Task::try_task`, the `Ok`/`Err` value will be printed with `Display`.\n\n```\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] START ExampleService/ok_task\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] OK ExampleService/ok_task\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] END ExampleService/ok_task\n\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] START ExampleService/err_task\n2020-08-23 16:45:10,422 ERROR [lifeline::service] ERR: ExampleService/err_task: my error\n2020-08-23 16:45:10,422 DEBUG [lifeline::spawn] END ExampleService/err_task\n```\n\n### A note about autocomplete\n`rust-analyzer` does not currently support auto-import for structs defined in macros.  Lifeline really needs the\nstruct defined in the macro, as it injects magic fields which store the channels at runtime.\n\nThere is a workaround: define a `prelude.rs` file in your crate root that exports `pub use` for all your bus implementations.  \n```\npub use lifeline::*;\npub use crate::bus::MainBus;\npub use crate::other::OtherBus;\n...\n```\nThen in all your modules:\n`use crate::prelude::*`\n\n## The Resource\nResources can be stored on the bus.  This is very useful for configuration (e.g `MainConfig`), or connections (e.g. a `TcpStream`).\n\nResources implement the `Storage` trait, which is easy with the `impl_storage_clone!` and `impl_storage_take!` macros.\n\n```rust\nuse lifeline::{lifeline_bus, impl_storage_clone};\nlifeline_bus!(MainBus);\npub struct MainConfig {\n    pub port: u16\n}\n\nimpl_storage_clone!(MainConfig);\n\nfn main() {\n    let bus = MainBus::default()\n    bus.store_resource::\u003cMainConfig\u003e(MainConfig { port: 12345 });\n    // from here\n}\n```\n\nLifeline does not provide `Resource` implementations for Channel endpoints - use `bus.rx()` and `bus.tx()`.\n\n## The Channel\nChannel senders must implement the `Channel` trait to be usable in an `impl Message` binding.\n\nIn most cases, the `Channel` endpoints just implement `Storage`, which determines whether to 'take or clone' the endpoint on a `bus.rx()` or `bus.tx()` call.\n\nHere is an example implem\n```rust\nuse lifeline::Channel;\nuse crate::{impl_channel_clone, impl_channel_take};\nuse tokio::sync::{broadcast, mpsc, oneshot, watch};\n\nimpl\u003cT: Send + 'static\u003e Channel for mpsc::Sender\u003cT\u003e {\n    type Tx = Self;\n    type Rx = mpsc::Receiver\u003cT\u003e;\n\n    fn channel(capacity: usize) -\u003e (Self::Tx, Self::Rx) {\n        mpsc::channel(capacity)\n    }\n\n    fn default_capacity() -\u003e usize {\n        16\n    }\n}\n\nimpl_channel_clone!(mpsc::Sender\u003cT\u003e);\nimpl_channel_take!(mpsc::Receiver\u003cT\u003e);\n```\n\nBroadcast senders should implement the trait with the `clone_rx` method overriden, to take from `Rx`, then subscribe to `Tx`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faustinjones%2Flifeline-rs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faustinjones%2Flifeline-rs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faustinjones%2Flifeline-rs/lists"}