{"id":33936355,"url":"https://github.com/tqdv/tear","last_synced_at":"2026-04-06T07:01:48.642Z","repository":{"id":57669398,"uuid":"265250215","full_name":"tqdv/tear","owner":"tqdv","description":"Typed early returns and loop control + Syntax sugar for try!-like error handling","archived":false,"fork":false,"pushed_at":"2021-04-11T19:34:47.000Z","size":139,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-09T04:08:46.330Z","etag":null,"topics":["early-return","error-handling","macro","syntax-sugar"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/tear","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/tqdv.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2020-05-19T13:08:27.000Z","updated_at":"2021-04-11T19:34:49.000Z","dependencies_parsed_at":"2022-09-26T20:40:53.910Z","dependency_job_id":null,"html_url":"https://github.com/tqdv/tear","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/tqdv/tear","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tqdv%2Ftear","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tqdv%2Ftear/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tqdv%2Ftear/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tqdv%2Ftear/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tqdv","download_url":"https://codeload.github.com/tqdv/tear/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tqdv%2Ftear/sbom","scorecard":{"id":896265,"data":{"date":"2025-08-11","repo":{"name":"github.com/tqdv/tear","commit":"88f2c675a6a0ed8fe440c75208fbc17ed7d63d2c"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/29 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":"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":"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":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"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":"Dangerous-Workflow","score":-1,"reason":"no workflows found","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":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"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":"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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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":"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":"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE-APACHE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE-APACHE: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"}}]},"last_synced_at":"2025-08-24T13:49:29.325Z","repository_id":57669398,"created_at":"2025-08-24T13:49:29.326Z","updated_at":"2025-08-24T13:49:29.326Z"},"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":["early-return","error-handling","macro","syntax-sugar"],"created_at":"2025-12-12T14:13:22.032Z","updated_at":"2026-04-06T07:01:48.636Z","avatar_url":"https://github.com/tqdv.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tear\n\nTyped early returns and loop control + Syntax sugar for try!-like error handling\n\n*Works with Rust v1.34+ (released on 11 April 2019)*\n\n## Synopsis\n\nImport the macros into your module:\n```rust\nuse tear::prelude::*;\n```\n\nExplicit error-handling syntax with `terror!`:\n```rust\nlet handled = terror! { can_error() =\u003e print_error };\nlet variant = terror! { can_io_error() =\u003e CustomError::Io };\n\n// Equivalent using `?`:\nlet handled = can_error().map_err(print_error)?;\nlet variant = can_io_error.map_err(CustomError::Io)?;\n```\n\nEarly loop continue with `twist!`:\n```rust\nfor re in regexes_strings {\n    // Skip iteration if the regex fails to compile\n    let re = twist! { Regex::new(re) =\u003e |_| next!() }\n\n    // Use regex...\n```\n\nKeyword-like early returns with `tear_if!`:\n```rust\nfn divide_i32 (num: i32, denom: i32) -\u003e Option\u003cf32\u003e {\n    // Return early if dividing by 0\n    tear_if! { denom == 0, None };\n\n    // Compute quotient...\n```\n\nTyped early returns with `tear!`:\n```rust\n// Tells the calling function to return early on failure\nfn get_value_or_return() -\u003e ValRet\u003cString, i32\u003e { Ret(-1) }\n\nfn status_code() -\u003e i32 {\n    let v = tear! { get_value_or_return() };\n\n    // Process value...\n```\n\nSee [the documentation](https://docs.rs/tear) for more info.\n\n## Changelog\n\nSee [CHANGELOG](CHANGELOG.md).\n\n## Rationale\n\n**I wanted to make early returns more explicit.**\n\nNormally, you need to read until the end of the\n`if` body to know if it returns early or not. `tear_if` places that information at\nthe beginning of the block.\n\n**I wanted typed early returns because it is useful for passing exitcodes up the callchain.**\n\nHaving a typed early return allows you to have functions that can force their caller\nto return early. It's an action at a distance inspired by how\n [Slips](https://docs.raku.org/type/Slip) work in Raku.\n\n**I wanted annotated failure points instead of too many combinators.**\n\nThe `?` operator works is essentially a conditional early-return.\nTo convert the errors you get to the right type, you need to use combinators.\nI find it hard to discern that those combinators are meant for error handling.\n  \nSomething like this:\n```rust\nlet path = find_config_file().ok_or(Error::FindPathF)?\nlet mut file = get_file_buffer(\u0026path).map_err(Error::GetFileF)?;\n```\n\nThe `terror!` macro makes the error handling more explicit:\n```rust\nlet path = terror! { find_config_file() =\u003e Error::FindPathF };\nlet mut file = terror! { get_file_buffer(\u0026path) =\u003e Error::GetFileF };\n```\n\n**Loop control and early returns are similar**\n\nI already implemented typed early return, so why not implement typed loop controls well ?\nThey're the same kind of useful.\n\n## See also\n\n- [Error Handling in Rust §The real `try!` macro / `?` operator][error-handling try]\n- [guard](https://docs.rs/crate/guard), a crate implementing \"guard\" expressions,\n  the counterpart to `tear_if!`\n\n[error-handling try]: https://blog.burntsushi.net/rust-error-handling/#the-real-try-macro-operator\n\n## License\n\nLicensed under either of [Apache License, Version 2.0](LICENSE-APACHE)\nor [MIT license](LICENSE-MIT) at your option.\n\n\u003csmall\u003eUnless you explicitly state otherwise, any contribution intentionally submitted for inclusion\nin this crate by you, as defined in the Apache-2.0 license, shall be dual licensed as above,\nwithout any additional terms or conditions.\u003c/small\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftqdv%2Ftear","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftqdv%2Ftear","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftqdv%2Ftear/lists"}