{"id":17837829,"url":"https://github.com/poi2/callback_fn","last_synced_at":"2025-08-02T00:34:45.085Z","repository":{"id":234058808,"uuid":"785739468","full_name":"poi2/callback_fn","owner":"poi2","description":"Callback function for Rust","archived":false,"fork":false,"pushed_at":"2024-05-02T15:11:23.000Z","size":31,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-17T11:05:55.990Z","etag":null,"topics":["rust","rust-macro"],"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/poi2.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-04-12T14:17:48.000Z","updated_at":"2024-05-08T07:52:18.000Z","dependencies_parsed_at":"2024-05-02T16:29:13.121Z","dependency_job_id":"759b33fd-0fe2-4831-99c9-dbe4df1b81e1","html_url":"https://github.com/poi2/callback_fn","commit_stats":null,"previous_names":["poi2/callback_rs"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poi2%2Fcallback_fn","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poi2%2Fcallback_fn/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poi2%2Fcallback_fn/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poi2%2Fcallback_fn/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poi2","download_url":"https://codeload.github.com/poi2/callback_fn/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244507854,"owners_count":20463689,"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":["rust","rust-macro"],"created_at":"2024-10-27T20:48:57.430Z","updated_at":"2025-03-19T21:30:48.975Z","avatar_url":"https://github.com/poi2.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Callback function for Rust\n\ncallback_fn is a library that adds functions before, after and around the target function.\n\n## Features\n\n- **Custom Functions**: Users can specify custom functions to be executed before, after, and around the target function.\n- **Seamless Integration**: Integrates seamlessly into existing codebases.\n- **Error Handling**: Handles errors that occur within the callback functions\n\n## Uses\n\n- **Callback Functions**: Add functions before and after the target function.\n    - Useful for tasks such as logging, authentication, and other cross-cutting concerns.\n- **Design-by-Contracts**: Add pre-conditions and post-conditions to the target function.\n    - Specific conditions can be applied, such as only when testing, using the features flag.\n\n## Installation\n\nAdd callback_fn to your `Cargo.toml`.\n\n```toml\n[dependencies]\ncallback_fn = \"0.1.0\"\n```\n\n## Examples\n\n### For callback\n\nAfter user created, user cache will be created.\n\n```rust\nuse callback_fn::after_callback;\n\n#[allow(dead_code)]\n#[derive(Clone, Debug)]\nstruct User {\n    name: String,\n}\n\nstruct UserRepository {}\nimpl UserRepository {\n    async fn save(\u0026self, _user: User) -\u003e Result\u003c(), String\u003e {\n        tokio::time::sleep(Duration::from_micros(1)).await;\n        Ok(())\n    }\n}\n\nstruct UserCache {}\nimpl UserCache {\n    async fn save(\u0026self, _user: User) -\u003e Result\u003c(), String\u003e {\n        tokio::time::sleep(Duration::from_micros(1)).await;\n        Ok(())\n    }\n}\n\nstruct UserUseCase {\n    user_repository: UserRepository,\n    user_cache: UserCache,\n}\n\nimpl UserUseCase {\n    #[after_callback(let _ = self.create_user_cache(ret.clone()?).await?)]\n    async fn create_user(\u0026self, name: String) -\u003e Result\u003cUser, String\u003e {\n        let user = User { name };\n        self.user_repository.save(user.clone()).await?;\n        Ok(user)\n    }\n\n    async fn create_user_cache(\u0026self, user: User) -\u003e Result\u003cUser, String\u003e {\n        self.user_cache.save(user.clone()).await?;\n        Ok(user)\n    }\n}\n```\n\n### For logging\n\nAdd logging around the target function.\n\n```rust\nuse callback_fn::around_callback;\n\n#[around_callback(my_logger())]\nfn hello(str: \u0026str) {\n    println!(\"Hello {}\", str);\n}\n\nfn my_logger() {\n    println!(\"{}\", chrono::Local::now());\n}\n\n// hello will print:\n//\n// 2024-04-01T00:00:000.000000+09:00\n// Hello world\n// 2024-04-01T00:00:000.000100+09:00\n#[test]\nfn test_hello() {\n    hello(\"world\");\n}\n```\n\n### For Authentication\n\nAdd authentication before UseCase function.\n\n```rust\nuse callback_fn::before_callback;\nuse strum_macros::Display;\n\n#[before_callback(has_permission(current_user, Permission::ReadArticle).map_err(UseCaseError::from)?)]\nfn get_article_by_id(current_user: \u0026User, id: usize) -\u003e Result\u003cArticle, UseCaseError\u003e {\n    Ok(Article {\n        id,\n        title: \"Dummy Title\".to_string(),\n        body: \"Dummy Body\".to_string(),\n    })\n}\n\n#[before_callback(has_permission(current_user, Permission::CreateArticle).map_err(UseCaseError::from)?)]\nfn create_article(current_user: \u0026User, title: String, body: String) -\u003e Result\u003cArticle, UseCaseError\u003e {\n    Ok(Article { id: 1, title, body })\n}\n\n#[derive(Debug)]\nstruct User {\n    permissions: Vec\u003cPermission\u003e,\n}\n\n#[derive(Debug, Display, PartialEq)]\npub enum Permission {\n    ReadArticle,\n    CreateArticle,\n}\n\nfn has_permission(user: \u0026User, permission: Permission) -\u003e Result\u003c(), PermissionError\u003e {\n    if user.permissions.contains(\u0026permission) {\n        Ok(())\n    } else {\n        Err(PermissionError::PermissionDenied(permission))\n    }\n}\n\n#[derive(Debug, PartialEq)]\nstruct Article {\n    id: usize,\n    title: String,\n    body: String,\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum PermissionError {\n    #[error(\"User don't have {0} permission.\")]\n    PermissionDenied(Permission),\n}\n\n#[derive(thiserror::Error, Debug)]\npub enum UseCaseError {\n    #[error(\"PermissionError: {0}\")]\n    PermissionError(#[from] PermissionError),\n}\n```\n\n### For Design-by-contract\n\nAfter adding to the cart or cleaning, ensure if the total_price is correct.\n\n```rust\nuse callback_fn::around_callback;\n\nstruct Cart {\n    total_price: usize,\n    items: Vec\u003cItem\u003e,\n}\nstruct Item {\n    price: usize,\n}\n\nimpl Cart {\n    fn new() -\u003e Self {\n        Self {\n            total_price: 0,\n            items: vec![],\n        }\n    }\n\n    // Ensure total_price is correct around add_item.\n    // Error handling is available in runtime when conditions are not ensure.\n    #[around_callback(self.ensure_total_price()?)]\n    fn add_item(\u0026mut self, item: Item) -\u003e Result\u003c(), String\u003e {\n        self.items.push(item);\n        self.update_total_price();\n        Ok(())\n    }\n\n    fn update_total_price(\u0026mut self) {\n        self.total_price = self.items.iter().map(|item| item.price).sum()\n    }\n\n    fn ensure_total_price(\u0026self) -\u003e Result\u003c(), String\u003e {\n        if self.total_price == self.items.iter().map(|item| item.price).sum() {\n            Ok(())\n        } else {\n            Err(\"Total price is not correct\".to_string())\n        }\n    }\n}\n```\n\n### Use only in specific features\n\nIf you want to use callback_fn only in specific features, you can use `cfg_attr`.\n\n```rust\nuse callback_fn::after_callback;\n\n#[cfg_attr(test, after_callback(bar()))]\nfn foo() {\n    println!(\"foo\");\n}\n\nfn bar() {\n    println!(\"bar\");\n}\n```\n\nIf you run `cargo run --features test`, foo function will be like below.\n\n```rust\nfn foo() {\n    #[allow(unused_mut)]\n    let mut ret = {\n        {\n            ::std::io::_print(format_args!(\"foo\\n\"));\n        };\n    };\n    bar();\n    ret\n}\nfn bar() {\n    {\n        ::std::io::_print(format_args!(\"bar\\n\"));\n    };\n}\n```\n\nIf you run `cargo run`, foo function will be like below.\n\n```rust\nfn foo() {\n    {\n        ::std::io::_print(format_args!(\"foo\\n\"));\n    };\n}\nfn bar() {\n    {\n        ::std::io::_print(format_args!(\"bar\\n\"));\n    };\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoi2%2Fcallback_fn","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoi2%2Fcallback_fn","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoi2%2Fcallback_fn/lists"}