{"id":13753734,"url":"https://github.com/cksac/compose-rt","last_synced_at":"2025-05-09T21:35:41.593Z","repository":{"id":57609683,"uuid":"468621489","full_name":"cksac/compose-rt","owner":"cksac","description":"A positional memoization runtime","archived":false,"fork":false,"pushed_at":"2024-12-24T16:25:07.000Z","size":257,"stargazers_count":37,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-04-28T05:20:49.169Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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/cksac.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-03-11T05:32:02.000Z","updated_at":"2024-12-26T12:16:17.000Z","dependencies_parsed_at":"2022-08-27T11:31:55.505Z","dependency_job_id":null,"html_url":"https://github.com/cksac/compose-rt","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/cksac%2Fcompose-rt","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cksac%2Fcompose-rt/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cksac%2Fcompose-rt/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cksac%2Fcompose-rt/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cksac","download_url":"https://codeload.github.com/cksac/compose-rt/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253329006,"owners_count":21891562,"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-03T09:01:28.379Z","updated_at":"2025-05-09T21:35:41.565Z","avatar_url":"https://github.com/cksac.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"# compose-rt\r\n![Rust](https://github.com/cksac/compose-rt/workflows/Rust/badge.svg)\r\n[![Docs Status](https://docs.rs/compose-rt/badge.svg)](https://docs.rs/compose-rt)\r\n[![Latest Version](https://img.shields.io/crates/v/compose-rt.svg)](https://crates.io/crates/compose-rt)\r\n\r\nA positional memoization runtime similar to Jetpack Compose Runtime.\r\n\r\n\u003eWhat this means is that Compose is, at its core, a general-purpose tool for managing a tree of nodes of any type. Well a “tree of nodes” describes just about anything, and as a result Compose can target just about anything. – [jakewharton](https://jakewharton.com/a-jetpack-compose-by-any-other-name/)\r\n\r\n## Example\r\n```rust\r\nuse std::env;\r\n\r\nuse compose_rt::{ComposeNode, Composer, Root};\r\n\r\n#[derive(Debug)]\r\npub struct Data(String);\r\n\r\nimpl From\u003cString\u003e for Data {\r\n    fn from(s: String) -\u003e Self {\r\n        Self(s)\r\n    }\r\n}\r\n\r\nimpl From\u003c\u0026str\u003e for Data {\r\n    fn from(s: \u0026str) -\u003e Self {\r\n        Self(s.to_string())\r\n    }\r\n}\r\n\r\nimpl ComposeNode for Data {\r\n    type Context = ();\r\n}\r\n\r\ntype Scope\u003cS\u003e = compose_rt::Scope\u003cS, Data\u003e;\r\n\r\npub struct Div;\r\npub struct Button;\r\npub struct Text;\r\n\r\npub trait Html {\r\n    fn div\u003cC\u003e(\u0026self, content: C)\r\n    where\r\n        C: Fn(Scope\u003cDiv\u003e) + Clone + 'static;\r\n\r\n    fn button\u003cT\u003e(\u0026self, text: T)\r\n    where\r\n        T: Into\u003cString\u003e + Clone + 'static;\r\n\r\n    fn text\u003cT\u003e(\u0026self, text: T)\r\n    where\r\n        T: Into\u003cString\u003e + Clone + 'static;\r\n}\r\n\r\nimpl\u003cS\u003e Html for Scope\u003cS\u003e\r\nwhere\r\n    S: 'static,\r\n{\r\n    #[track_caller]\r\n    fn div\u003cC\u003e(\u0026self, content: C)\r\n    where\r\n        C: Fn(Scope\u003cDiv\u003e) + Clone + 'static,\r\n    {\r\n        let child_scope = self.child::\u003cDiv\u003e();\r\n        self.create_node(\r\n            child_scope,\r\n            content,\r\n            || {},\r\n            |_, _| \"div\".into(),\r\n            |_, _, _| {},\r\n        );\r\n    }\r\n\r\n    #[track_caller]\r\n    fn button\u003cT\u003e(\u0026self, text: T)\r\n    where\r\n        T: Into\u003cString\u003e + Clone + 'static,\r\n    {\r\n        let child_scope = self.child::\u003cButton\u003e();\r\n        self.create_node(\r\n            child_scope,\r\n            |_| {},\r\n            move || text.clone().into(),\r\n            |text, _| format!(\"button({})\", text).into(),\r\n            |n, text, _| {\r\n                n.0 = text;\r\n            },\r\n        );\r\n    }\r\n\r\n    #[track_caller]\r\n    fn text\u003cT\u003e(\u0026self, text: T)\r\n    where\r\n        T: Into\u003cString\u003e + Clone + 'static,\r\n    {\r\n        let child_scope = self.child::\u003cText\u003e();\r\n        self.create_node(\r\n            child_scope,\r\n            |_| {},\r\n            move || text.clone().into(),\r\n            |text, _| format!(\"text({})\", text).into(),\r\n            |n, text, _| {\r\n                n.0 = text;\r\n            },\r\n        );\r\n    }\r\n}\r\n\r\nfn app(s: Scope\u003cRoot\u003e, n: usize) {\r\n    s.div(move |s| {\r\n        let count = s.use_state(|| 0);\r\n        s.text(\"start\");\r\n        s.div(move |s| {\r\n            let c = count.get();\r\n            if c == 0 {\r\n                s.button(\"Load items\");\r\n                count.set(n);\r\n            } else {\r\n                for i in 0..c {\r\n                    s.key(i, move |s| {\r\n                        s.button(format!(\"Item {}\", i));\r\n                    });\r\n                }\r\n                count.set(0);\r\n            }\r\n        });\r\n        s.text(\"end\");\r\n    });\r\n}\r\n\r\nfn main() {\r\n    let count = env::args()\r\n        .nth(1)\r\n        .unwrap_or(\"2\".to_string())\r\n        .parse()\r\n        .unwrap();\r\n    let iter = env::args()\r\n        .nth(2)\r\n        .unwrap_or(\"1\".to_string())\r\n        .parse()\r\n        .unwrap();\r\n    let print = env::args()\r\n        .nth(3)\r\n        .unwrap_or(\"true\".to_string())\r\n        .parse()\r\n        .unwrap();\r\n    println!(\"count: {}, iter: {}, print: {}\", count, iter, print);\r\n    let start = std::time::Instant::now();\r\n    let mut recomposer = Composer::compose(move |s| app(s, count), ());\r\n    if print {\r\n        recomposer.print_tree();\r\n    }\r\n    for _ in 0..iter {\r\n        recomposer.recompose();\r\n    }\r\n    if print {\r\n        recomposer.print_tree();\r\n    }\r\n    println!(\"Time: {:?}\", start.elapsed());\r\n}\r\n```\r\n\r\n## LICENSE\r\nThis project is licensed under either of\r\n\r\n- Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or\r\n  http://www.apache.org/licenses/LICENSE-2.0)\r\n- MIT license ([LICENSE-MIT](LICENSE-MIT) or\r\n  http://opensource.org/licenses/MIT)\r\n\r\nat your option.\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcksac%2Fcompose-rt","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcksac%2Fcompose-rt","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcksac%2Fcompose-rt/lists"}