pax_global_header00006660000000000000000000000064146357303300014516gustar00rootroot0000000000000052 comment=6cdaf2bf5e2caf020fb70f5b22e643bebfe190d2 bounded-static-bounded-static-0.8.0/000077500000000000000000000000001463573033000173335ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/.github/000077500000000000000000000000001463573033000206735ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/.github/dependabot.yml000066400000000000000000000002141463573033000235200ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" open-pull-requests-limit: 10bounded-static-bounded-static-0.8.0/.github/workflows/000077500000000000000000000000001463573033000227305ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/.github/workflows/ci.yml000066400000000000000000000111751463573033000240530ustar00rootroot00000000000000on: push: branches: [ master ] pull_request: branches: [ master ] schedule: - cron: '00 18 * * *' name: Continuous integration jobs: check: runs-on: ubuntu-latest strategy: matrix: rust: [stable, beta, 1.64.0] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: check --no-default-features uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features - name: check --no-default-features --features alloc uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features alloc - name: check --no-default-features --features collections uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features collections - name: check --no-default-features --features std uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features std - name: check --no-default-features --features derive uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features derive - name: check --no-default-features --features smol_str uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features smol_str - name: check --no-default-features --features smallvec uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features smallvec - name: check --no-default-features --features smartstring uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features smartstring - name: check --no-default-features --features chrono uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features chrono - name: check --no-default-features --features chrono-clock uses: actions-rs/cargo@v1 with: command: check args: --workspace --no-default-features --features chrono-clock - name: check --all-features uses: actions-rs/cargo@v1 with: command: check args: --workspace --all-features build: runs-on: ubuntu-latest strategy: matrix: rust: [stable, beta, 1.64.0] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: build --workspace --all-features uses: actions-rs/cargo@v1 with: command: build args: --workspace --all-features test: runs-on: ubuntu-latest strategy: matrix: rust: [ stable, beta, 1.64.0 ] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true - name: test --workspace --all-features uses: actions-rs/cargo@v1 with: command: test args: --workspace --all-features fmt: runs-on: ubuntu-latest strategy: matrix: rust: [ stable, beta, 1.64.0 ] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true components: rustfmt - name: fmt --all -- --check uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: runs-on: ubuntu-latest strategy: matrix: rust: [ stable, beta, 1.64.0 ] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.rust }} override: true components: clippy - name: clippy --workspace --all-features --tests uses: actions-rs/cargo@v1 with: command: clippy args: --workspace --all-features --tests -- -Dwarnings cargo-deny: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: EmbarkStudios/cargo-deny-action@v1 with: log-level: warn command: check arguments: --all-featuresbounded-static-bounded-static-0.8.0/.gitignore000066400000000000000000000000431463573033000213200ustar00rootroot00000000000000/target .idea Cargo.lock *.DS_Storebounded-static-bounded-static-0.8.0/CHANGELOG.md000066400000000000000000000140261463573033000211470ustar00rootroot00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [bounded-static-0.8.0] & [bounded-static-derive-0.8.0] - 2024-06-23 ### Added - Added optional support for 3rd party `chrono` crate (by [@xkikeg](https://github.com/xkikeg)) This change ([#46](https://github.com/fujiapple852/bounded-static/pull/46)) adds support for types from the `chrono` crate via the `chrono` and `chrono-clock` feature flags. ### Changed - Increased MSRV to `1.64` - Updated `ahash`, `smol_str` and `smallvec` to the latest versions ## [bounded-static-0.7.0] & [bounded-static-derive-0.7.0] - 2023-10-25 ### Changed - Increased MSRV to `1.61` ## [bounded-static-0.6.0] & [bounded-static-derive-0.6.0] - 2023-10-20 ### Added - Added support for custom `RandomState` and `aHash` (by [@zhu-he](https://github.com/zhu-he)) This change ([#62](https://github.com/fujiapple852/bounded-static/pull/62)) adds support for using a custom `RandomState` with the stdlib `HashMap` and `HashSet` types. It also adds support for the 3rd party `AHashMap`, `AHashSet` and `RandomState` types from the `ahash` crate via the `ahash` feature flag. ## [bounded-static-0.5.0] & [bounded-static-derive-0.5.0] - 2023-04-29 ### Changed - Increased MSRV to `1.60` and updated all dependency versions ## [bounded-static-0.4.0] & [bounded-static-derive-0.4.0] - 2022-06-08 ### Added - Added support for non-zero integer types (by [@jakoschiko](https://github.com/jakoschiko)) ## [bounded-static-0.3.0] & [bounded-static-derive-0.3.0] - 2022-03-10 ### Added - Added support for tuples of up to 12 elements - Added optional support for 3rd party `smartstring::SmartString` - Added optional support for 3rd party `smallvec::SmallVec` - Added optional support for 3rd party `smol_str::SmolStr` - Added `Result` and `array` to the list of documented blanket implementation ### Changed - Refactored repo and crate directories to `bounded-static` and `bounded-static-derive` to match crate names ### Fixed - Fixed broken crate and documentation links ## [bounded-static-0.2.1] & [bounded-static-derive-0.2.1] - 2022-02-22 ### Fixed - Fixed broken links to crate documentation - Fixed broken link to LICENCE file ## [bounded-static-0.2.0] & [bounded-static-derive-0.2.0] - 2022-02-22 ### Added - Added support for complex generic bounds on struct and enum in the `ToStatic` derive macro > For example, the following `struct` is now supported: > > ```rust > #[derive(ToStatic)] > struct Baz<'a, T: Foo>(T, Cow<'a, str>) > where > T: Into + 'a + Bar; > ``` > > This produces (`ToBoundedStatic` shown, `IntoBoundedStatic` is also produced): > > ```rust > impl<'a, T: Foo + ::bounded_static::ToBoundedStatic> ::bounded_static::ToBoundedStatic for Baz<'a, T> > where > T: Into + 'a + Bar + ::bounded_static::ToBoundedStatic, > T::Static: Foo + Into + 'a + Bar, > { > type Static = Baz<'static, T::Static>; > fn to_static(&self) -> Self::Static { > Baz(self.0.to_static(), self.1.to_static()) > } > } > ``` - Added `ToBoundedStatic` and `IntoBoundedStatic` implementations for the `()` (unit) type - Added `ToBoundedStatic` and `IntoBoundedStatic` implementations for the `Result` type - Added doc comments for `ToBoundedStatic` and `IntoBoundedStatic` impls for all primitive types ### Fixed - Fixed broken links in documentation - Fixed additional Clippy lints and [lib.rs](https://lib.rs) crates validation errors ## [bounded-static-0.1.0] & [bounded-static-derive-0.1.0] - 2022-02-18 ### Added - Initial release of `bounded-static` and `bounded-static-derive` [bounded-static-0.8.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.7.0...bounded-static-0.8.0 [bounded-static-derive-0.8.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.7.0...bounded-static-derive-0.8.0 [bounded-static-0.7.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.6.0...bounded-static-0.7.0 [bounded-static-derive-0.7.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.6.0...bounded-static-derive-0.7.0 [bounded-static-0.6.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.5.0...bounded-static-0.6.0 [bounded-static-derive-0.6.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.5.0...bounded-static-derive-0.6.0 [bounded-static-0.5.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.4.0...bounded-static-0.5.0 [bounded-static-derive-0.5.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.4.0...bounded-static-derive-0.5.0 [bounded-static-0.4.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.3.0...bounded-static-0.4.0 [bounded-static-derive-0.4.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.3.0...bounded-static-derive-0.4.0 [bounded-static-0.3.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.1...bounded-static-0.3.0 [bounded-static-derive-0.3.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.1...bounded-static-derive-0.3.0 [bounded-static-0.2.1]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.0...bounded-static-0.2.1 [bounded-static-derive-0.2.1]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.0...bounded-static-derive-0.2.1 [bounded-static-0.2.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.1.0...bounded-static-0.2.0 [bounded-static-derive-0.2.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.1.0...bounded-static-derive-0.2.0 [bounded-static-0.1.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.0.0...bounded-static-0.1.0 [bounded-static-derive-0.1.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.0.0...bounded-static-derive-0.1.0 bounded-static-bounded-static-0.8.0/Cargo.toml000066400000000000000000000017361463573033000212720ustar00rootroot00000000000000[workspace] resolver = "2" members = ["bounded-static", "bounded-static-derive"] [workspace.package] version = "0.8.0" rust-version = "1.64.0" edition = "2021" authors = ["FujiApple "] repository = "https://github.com/fujiapple852/bounded-static" license = "Apache-2.0" keywords = ["cow", "static", "bounded", "owned", "derive"] categories = ["no-std", "rust-patterns", "data-structures", "memory-management"] [workspace.dependencies] bounded-static = { version = "0.8.0", path = "bounded-static" } bounded-static-derive = { version = "0.8.0", path = "bounded-static-derive" } syn = { version = "2.0.38", features = [ "full" ] } quote = "1.0.33" proc-macro2 = "1.0.69" smol_str = { version = "0.2.2", default-features = false } smallvec = { version = "1.13.2", default-features = false } smartstring = { version = "1.0.1", default-features = false } ahash = { version = "0.8.11", default-features = false } chrono = { version = "0.4.38", default-features = false }bounded-static-bounded-static-0.8.0/LICENSE000066400000000000000000000227731463573033000203530ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS bounded-static-bounded-static-0.8.0/README.md000066400000000000000000000100611463573033000206100ustar00rootroot00000000000000![ci](https://github.com/fujiapple852/bounded-static/actions/workflows/ci.yml/badge.svg) [![Documentation](https://docs.rs/bounded-static/badge.svg)](https://docs.rs/bounded-static/0.8.0) [![Crate](https://img.shields.io/crates/v/bounded-static.svg)](https://crates.io/crates/bounded-static/0.8.0) # Bounded Static This crate defines the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) and [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits, the [`ToStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/derive.ToStatic.html) macro and provides impls for common types. This crate has zero-dependencies, is `no_std` friendly and forbids `unsafe` code. As described in the [Common Rust Lifetime Misconceptions](https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#2-if-t-static-then-t-must-be-valid-for-the-entire-program): > `T: 'static` should be read as _"`T` is bounded by a `'static` lifetime"_ not _"`T` has a `'static` lifetime"_. The traits `ToBoundedStatic` and `IntoBoundedStatic` can be used to convert any suitable `T` and `&T` to an owned `T` such that `T: 'static`. Both traits define an associated type which is bounded by `'static` and provide a method to convert to that bounded type. The macros `ToStatic` can be used to automatically derive `ToBoundedStatic` and `IntoBoundedStatic` for any `struct` or `enum` that can be converted to a form that is bounded by `'static`. Refer to the crate [`documentation`](https://docs.rs/bounded-static/0.8.0/bounded_static) for details and examples. ## FAQ ### When is this useful? This is useful for data structures which directly or indirectly contain `Cow` types that must be supplied to a function which requires the `'static` bound (i.e. [`std::thread::spawn`](https://doc.rust-lang.org/std/thread/fn.spawn.html)): ```rust #[derive(Debug, PartialEq, ToStatic)] struct Foo<'a> { foo: Cow<'a, str>, bar: Vec> } #[derive(Debug, PartialEq, ToStatic)] enum Bar<'a> { First, Second(Cow<'a, str>), } fn main() { let value = String::from("data"); let foo = Foo { foo: Cow::from(&value), bar: vec![Bar::First, Bar::Second(Cow::from(&value))] }; let foo_static = foo.into_static(); std::thread::spawn(move || { assert_eq!(foo_static.foo, "data"); assert_eq!(foo_static.bar, vec![Bar::First, Bar::Second("data".into())]) }).join().unwrap(); } ``` ### How does this differ from the `ToOwned` trait? The [`ToOwned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html) trait defines an associated type `Owned` which is not bound by `'static` and therefore the follow will not compile: ```rust use std::borrow::Cow; fn main() { #[derive(Clone)] struct Foo<'a> { foo: Cow<'a, str>, } fn ensure_static(_: T) {} let s = String::from("data"); let foo = Foo { foo: Cow::from(&s) }; ensure_static(foo.to_owned()) } ``` Results in the following error: ``` error[E0597]: `s` does not live long enough --> src/lib.rs:12:36 | 12 | let foo = Foo { foo: Cow::from(&s) }; | ----------^^- | | | | | borrowed value does not live long enough | argument requires that `s` is borrowed for `'static` 13 | ensure_static(foo.to_owned()) 14 | } | - `s` dropped here while still borrowed ``` Replacing `Clone` with `ToStatic` and using `into_static()` (or `to_static()` as needed) allows the example to compile: ```rust use std::borrow::Cow; fn main() { #[derive(ToStatic)] struct Foo<'a> { foo: Cow<'a, str>, } fn ensure_static(_: T) {} let s = String::from("data"); let foo = Foo { foo: Cow::from(&s) }; ensure_static(foo.into_static()) } ``` ## License `bounded-static` is distributed under the terms of the Apache License (Version 2.0). See [LICENSE](LICENSE) for details. Copyright 2022bounded-static-bounded-static-0.8.0/bounded-static-derive/000077500000000000000000000000001463573033000235145ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static-derive/Cargo.toml000066400000000000000000000010071463573033000254420ustar00rootroot00000000000000[package] name = "bounded-static-derive" description = "Macro to derive ToBoundedStatic and IntoBoundedStatic traits" readme = "README.md" version.workspace = true rust-version.workspace = true edition.workspace = true authors.workspace = true license.workspace = true keywords.workspace = true categories.workspace = true [lib] proc-macro = true [dependencies] syn.workspace = true quote.workspace = true proc-macro2.workspace = true [dev-dependencies] bounded-static = { workspace = true, features = [ "derive" ] }bounded-static-bounded-static-0.8.0/bounded-static-derive/LICENCE000077700000000000000000000000001463573033000257142../LICENSEustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static-derive/README.md000066400000000000000000000020371463573033000247750ustar00rootroot00000000000000[![Documentation](https://docs.rs/bounded-static-derive/badge.svg)](https://docs.rs/bounded-static-derive/0.8.0) [![Crate](https://img.shields.io/crates/v/bounded-static-derive.svg)](https://crates.io/crates/bounded-static-derive/0.8.0) # Bounded Static Derive This crate provides the `ToStatic` macro which can be used to derive implementations of the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) and [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits for all `struct`and `enum` that can be converted to a form that is bounded by `'static`. The `ToStatic` macro should be used via the [`bounded-static`](https://docs.rs/bounded-static/0.8.0/bounded_static) crate rather than using this crate directly. ```yaml bounded-static = { version = "0.8.0", features = [ "derive" ] } ``` ## License `bounded-static-derive` is distributed under the terms of the Apache License (Version 2.0). See [LICENSE](LICENSE) for details. Copyright 2022bounded-static-bounded-static-0.8.0/bounded-static-derive/src/000077500000000000000000000000001463573033000243035ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static-derive/src/common.rs000066400000000000000000000204561463573033000261500ustar00rootroot00000000000000use proc_macro2::TokenStream; use quote::{format_ident, quote}; use syn::{ parse_quote, ConstParam, Field, GenericParam, Generics, Ident, Lifetime, PredicateType, Type, TypeParam, WhereClause, WherePredicate, }; /// The method and trait bound for both traits we will generate. #[derive(Copy, Clone)] pub(super) enum TargetTrait { ToBoundedStatic, IntoBoundedStatic, } impl TargetTrait { pub fn method(self) -> Ident { match self { Self::ToBoundedStatic => format_ident!("to_static"), Self::IntoBoundedStatic => format_ident!("into_static"), } } pub fn bound(self) -> Ident { match self { Self::ToBoundedStatic => format_ident!("ToBoundedStatic"), Self::IntoBoundedStatic => format_ident!("IntoBoundedStatic"), } } } /// Check for references which aren't `'static` and panic. /// /// # Examples /// /// The following `struct` cannot be made static _for all_ lifetimes `'a` (it is only valid for the `'static` lifetime) /// and so will fail this check: /// /// ```compile_fail /// #[derive(ToStatic)] /// struct Foo<'a> { /// bar: &'a str /// } /// ``` /// /// This `struct` will pass validation as the reference is `'static`: /// /// ```rust /// # use bounded_static::ToStatic; /// #[derive(ToStatic)] /// struct Foo { /// bar: &'static str /// } /// ``` /// /// This `struct` is will also pass validation as it can be converted to `'static` _for all_ lifetimes `'a`: /// /// ```rust /// # use bounded_static::ToStatic; /// #[derive(ToStatic)] /// struct Foo<'a> { /// bar: std::borrow::Cow<'a, str> /// } /// ``` /// /// Note that even without this check the compilation will fail if a non-static reference is used, however by /// performing this check we can issue a more explicit failure message to the developer. pub(super) fn check_field(field: &Field) { if let Type::Reference(ty) = &field.ty { if let Some(Lifetime { ident, .. }) = &ty.lifetime { #[allow(clippy::manual_assert)] if *ident != "static" { panic!( "non-static references cannot be made static: {:?}", quote!(#field).to_string() ) } } }; } /// The generic parameters of the `Static` associated type. /// /// i.e. `Static = Foo<'static, 'static, T::Static, R::Static>` pub(super) fn make_target_generics(generics: &Generics) -> Vec { generics .params .iter() .map(|param| match param { GenericParam::Type(TypeParam { ident, .. }) => quote!(#ident::Static), GenericParam::Lifetime(_) => quote!('static), GenericParam::Const(ConstParam { ident, .. }) => quote!(#ident), }) .collect() } /// Make a `Generics` with generic bounds for `TargetTrait`. /// /// # Examples /// /// Given the following struct: /// /// ```rust /// # use std::borrow::Cow; /// struct Baz<'a, T: Into + 'a> { /// t: T, /// r: Cow<'a, str>, /// } /// ``` /// /// We wish to produce (for example for `ToBoundedStatic`, similar for `IntoBoundedStatic`): /// /// ```rust /// # use std::borrow::Cow; /// # struct Baz<'a, T: Into + 'a> { /// # t: T, /// # r: Cow<'a, str>, /// # } /// impl<'a, T: Into + 'a + ::bounded_static::ToBoundedStatic> ::bounded_static::ToBoundedStatic for Baz<'a, T> /// where /// T::Static: Into + 'a { /// /// type Static = Baz<'static, T::Static>; /// /// fn to_static(&self) -> Self::Static { /// Baz { t: self.t.to_static(), r: self.r.to_static() } /// } /// } /// ``` /// /// In the above example we can see that `T` has the bound `Into + 'a` and therefore: /// /// - Generic parameter `T` has the additional bound `::bounded_static::ToBoundedStatic` /// - Associated type `T::Static` has the bound of `T`, i.e. `Into + 'a` /// pub(super) fn make_bounded_generics(generics: &Generics, target: TargetTrait) -> Generics { let params = make_bounded_generic_params(generics, target); let predicates = make_bounded_generic_predicates(generics, target); let static_predicates = make_static_generic_predicates(generics); let where_items: Vec<_> = predicates.into_iter().chain(static_predicates).collect(); Generics { params: parse_quote!(#(#params),*), where_clause: Some(parse_quote!(where #(#where_items),* )), ..*generics } } /// Make generic parameters bound by `TargetTrait`. /// /// i.e. given parameter `T: Into` create `T: Into + ::bounded_static::TargetTrait` fn make_bounded_generic_params(generics: &Generics, target: TargetTrait) -> Vec { generics .params .iter() .map(|param| match param { GenericParam::Type(ty) => GenericParam::Type(ty.clone_with_bound(&target.bound())), other => other.clone(), }) .collect() } /// Make generic predicates bound by `TargetTrait`. /// /// i.e. given predicate `T: Into` create `T: Into + ::bounded_static::TargetTrait` fn make_bounded_generic_predicates( generics: &Generics, target: TargetTrait, ) -> Vec { match generics.where_clause.as_ref() { Some(WhereClause { predicates, .. }) => predicates .iter() .map(|predicate| match predicate { WherePredicate::Type(ty) => { WherePredicate::Type(ty.clone_with_bound(&target.bound())) } other => other.clone(), }) .collect(), None => vec![], } } /// Make generic predicates for associated item `T::Static` bound as per `T`. /// /// i.e. given: /// /// ```rust /// # trait Foo {} /// struct Baz> where T: Foo { /// t: T, /// } /// ``` /// /// The generated trait impl associated type `Static` must reflect the original generic bounds as well as any /// additional bounds from a `where` clause. For the example above the associated type bound would be /// `T::Static: Into + Foo`. fn make_static_generic_predicates(generics: &Generics) -> Vec { generics .params .iter() .filter_map(|param| match param { GenericParam::Type(param_ty) => { let var = ¶m_ty.ident; let param_ty_bounds = ¶m_ty.bounds; match find_predicate(generics.where_clause.as_ref(), var) { None if param_ty_bounds.is_empty() => None, None => Some(parse_quote!(#var::Static: #param_ty_bounds)), Some(predicate_ty) => { let predicate_bounds = &predicate_ty.bounds; if param_ty_bounds.is_empty() { Some(parse_quote!(#var::Static: #predicate_bounds)) } else { Some(parse_quote!(#var::Static: #param_ty_bounds + #predicate_bounds)) } } } } _ => None, }) .collect() } /// Search the given `WhereClause` for a `WherePredicate` which matches the given `Ident`. fn find_predicate<'a>( where_clause: Option<&'a WhereClause>, var: &Ident, ) -> Option<&'a PredicateType> { where_clause .as_ref() .map(|WhereClause { predicates, .. }| predicates) .and_then(|predicate| { predicate.iter().find_map(|p| match p { WherePredicate::Type(ty) => match &ty.bounded_ty { Type::Path(path) => path.path.is_ident(var).then_some(ty), _ => None, }, _ => None, }) }) } /// Clone and add a bound to a type. trait CloneWithBound { fn clone_with_bound(&self, bound: &Ident) -> Self; } /// Clone and add a bound to a `PredicateType` (in a `where` clause). impl CloneWithBound for PredicateType { fn clone_with_bound(&self, bound: &Ident) -> Self { let mut bounded = self.clone(); bounded.bounds.push(parse_quote!(::bounded_static::#bound)); bounded } } /// Clone and add a bound to a `TypeParam`. impl CloneWithBound for TypeParam { fn clone_with_bound(&self, bound: &Ident) -> Self { let mut bounded = self.clone(); bounded.bounds.push(parse_quote!(::bounded_static::#bound)); bounded } } bounded-static-bounded-static-0.8.0/bounded-static-derive/src/data_enum.rs000066400000000000000000000126471463573033000266200ustar00rootroot00000000000000use crate::common; use crate::common::TargetTrait; use proc_macro2::{Ident, TokenStream}; use quote::{format_ident, quote}; use syn::{Fields, FieldsNamed, FieldsUnnamed, Generics, Variant}; /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for an `enum` deriving `ToStatic`. pub(super) fn generate_enum( name: &Ident, generics: &Generics, variants: &[&Variant], ) -> TokenStream { variants .iter() .for_each(|v| v.fields.iter().for_each(common::check_field)); let to = generate_enum_to(name, generics, variants); let into = generate_enum_into(name, generics, variants); quote!(#to #into) } /// Generate `ToBoundedStatic` for an enum. fn generate_enum_to(name: &Ident, generics: &Generics, variants: &[&Variant]) -> TokenStream { let arms = generate_match_arms(name, variants, TargetTrait::ToBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); let (impl_gens, to_ty_gens, to_where) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::ToBoundedStatic for #name #to_ty_gens #to_where { type Static = #name<#(#static_gens),*>; fn to_static(&self) -> Self::Static { match self { #(#arms),* } } } ) } /// Generate `IntoBoundedStatic` for an enum. fn generate_enum_into(name: &Ident, generics: &Generics, variants: &[&Variant]) -> TokenStream { let arms = generate_match_arms(name, variants, TargetTrait::IntoBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); let (impl_gens, into_ty_gens, into_where) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #into_ty_gens #into_where { type Static = #name<#(#static_gens),*>; fn into_static(self) -> Self::Static { match self { #(#arms),* } } } ) } /// Generate a collection of match arms for unit, named and unnamed variants. /// /// i.e.: /// /// *Unit*: `Foo::Bar => Foo::bar` /// /// *Named*: `Foo::Bar { a, b } => Foo::Bar { a: a.to_static(), b: b.to_static() }` /// /// *Unnamed*: `Foo::Bar(a, b) => Foo::Bar(a.to_static(), b.to_static())` fn generate_match_arms( name: &Ident, variants: &[&Variant], target: TargetTrait, ) -> Vec { variants .iter() .map(|variant| match &variant.fields { Fields::Unit => generate_variant_unit(name, &variant.ident), Fields::Named(fields_named) => { generate_variant_named(name, &variant.ident, fields_named, target) } Fields::Unnamed(fields_unnamed) => { generate_variant_unnamed(name, &variant.ident, fields_unnamed, target) } }) .collect() } /// Generate match arm for an unit variant. /// /// i.e. `Foo::Bar => Foo::bar` fn generate_variant_unit(name: &Ident, variant: &Ident) -> TokenStream { quote!(#name::#variant => #name::#variant) } /// Generate match arm for a named variant. /// /// i.e. `Foo::Bar { a, b } => Foo::Bar { a: a.to_static(), b: b.to_static() }` fn generate_variant_named( name: &Ident, variant: &Ident, fields_named: &FieldsNamed, target: TargetTrait, ) -> TokenStream { let fields = extract_named_fields(fields_named); let fields_to_method = generate_named_field_init_method(fields_named, target); quote!(#name::#variant{ #(#fields),* } => #name::#variant{ #(#fields_to_method),* }) } /// Generate match arm for an unnamed variant. /// /// i.e. `Foo::Bar(a, b) => Foo::Bar(a.to_static(), b.to_static())` fn generate_variant_unnamed( name: &Ident, variant: &Ident, fields_unnamed: &FieldsUnnamed, target: TargetTrait, ) -> TokenStream { let fields = extract_unnamed_fields(fields_unnamed); let fields_to_method = generate_unnamed_field_init_method(fields_unnamed, target); quote!(#name::#variant( #(#fields),* ) => #name::#variant( #(#fields_to_method),* )) } /// i.e. `foo: foo.to_static()` fn generate_named_field_init_method( fields_named: &FieldsNamed, target: TargetTrait, ) -> Vec { let method = target.method(); fields_named .named .iter() .map(|f| { let field_name = f.ident.as_ref().expect("FieldsNamed must have an ident"); quote!(#field_name: #field_name.#method()) }) .collect() } /// i.e. `foo.to_static()` fn generate_unnamed_field_init_method( fields_unnamed: &FieldsUnnamed, target: TargetTrait, ) -> Vec { let method = target.method(); fields_unnamed .unnamed .iter() .enumerate() .map(|(i, _)| { let field_name = format_ident!("field_{}", i); quote!(#field_name.#method()) }) .collect() } fn extract_named_fields(fields_named: &FieldsNamed) -> Vec<&Ident> { fields_named .named .iter() .map(|f| f.ident.as_ref().expect("FieldsNamed must have an ident")) .collect() } fn extract_unnamed_fields(fields_unnamed: &FieldsUnnamed) -> Vec { fields_unnamed .unnamed .iter() .enumerate() .map(|(i, _)| format_ident!("field_{}", i)) .collect() } bounded-static-bounded-static-0.8.0/bounded-static-derive/src/data_struct.rs000066400000000000000000000144131463573033000271710ustar00rootroot00000000000000use crate::common; use crate::common::TargetTrait; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{Field, FieldsNamed, FieldsUnnamed, Generics}; /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a `struct` with named fields deriving `ToStatic`. pub(super) fn generate_struct_named( name: &Ident, generics: &Generics, fields_named: &FieldsNamed, ) -> TokenStream { fields_named.named.iter().for_each(common::check_field); let to = generate_struct_named_to(name, generics, fields_named); let into = generate_struct_named_into(name, generics, fields_named); quote!(#to #into) } /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a `struct` with unnamed fields deriving `ToStatic`. pub(super) fn generate_struct_unnamed( name: &Ident, generics: &Generics, fields_unnamed: &FieldsUnnamed, ) -> TokenStream { fields_unnamed.unnamed.iter().for_each(common::check_field); let to = generate_struct_unnamed_to(name, generics, fields_unnamed); let into = generate_struct_unnamed_into(name, generics, fields_unnamed); quote!(#to #into) } /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a unit `struct` deriving `ToStatic`. pub(super) fn generate_struct_unit(name: &Ident) -> TokenStream { let to = generate_struct_unit_to(name); let into = generate_struct_unit_into(name); quote!(#to #into) } /// Generate `ToBoundedStatic` for a `struct` with with named fields. fn generate_struct_named_to( name: &Ident, generics: &Generics, fields_named: &FieldsNamed, ) -> TokenStream { let fields = make_named_fields_init_methods(fields_named, TargetTrait::ToBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause { type Static = #name<#(#static_gens),*>; fn to_static(&self) -> Self::Static { #name { #(#fields),* } } } ) } /// Generate `IntoBoundedStatic` for a `struct` with with named fields. fn generate_struct_named_into( name: &Ident, generics: &Generics, fields_named: &FieldsNamed, ) -> TokenStream { let fields = make_named_fields_init_methods(fields_named, TargetTrait::IntoBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause { type Static = #name<#(#static_gens),*>; fn into_static(self) -> Self::Static { #name { #(#fields),* } } } ) } /// Generate `ToBoundedStatic` for a `struct` with unnamed fields. fn generate_struct_unnamed_to( name: &Ident, generics: &Generics, fields_unnamed: &FieldsUnnamed, ) -> TokenStream { let fields = make_unnamed_fields(fields_unnamed, TargetTrait::ToBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause { type Static = #name<#(#static_gens),*>; fn to_static(&self) -> Self::Static { #name ( #(#fields),* ) } } ) } /// Generate `IntoBoundedStatic` for a `struct` with unnamed fields. fn generate_struct_unnamed_into( name: &Ident, generics: &Generics, fields_unnamed: &FieldsUnnamed, ) -> TokenStream { let fields = make_unnamed_fields(fields_unnamed, TargetTrait::IntoBoundedStatic); let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); let static_gens = common::make_target_generics(generics); quote!( impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause { type Static = #name<#(#static_gens),*>; fn into_static(self) -> Self::Static { #name ( #(#fields),* ) } } ) } /// Generate `ToBoundedStatic` for unit struct. fn generate_struct_unit_to(name: &Ident) -> TokenStream { quote!( impl ::bounded_static::ToBoundedStatic for #name { type Static = #name; fn to_static(&self) -> Self::Static { #name } } ) } /// Generate `IntoBoundedStatic` for unit struct. fn generate_struct_unit_into(name: &Ident) -> TokenStream { quote!( impl ::bounded_static::IntoBoundedStatic for #name { type Static = #name; fn into_static(self) -> Self::Static { #name } } ) } fn make_named_fields_init_methods( fields_named: &FieldsNamed, target: TargetTrait, ) -> Vec { fields_named .named .iter() .map(|field| make_named_field_init_method(field, target)) .collect() } /// i.e. `foo: self.foo.to_static()` fn make_named_field_init_method(field: &Field, target: TargetTrait) -> TokenStream { let field_name = field .ident .as_ref() .expect("FieldsNamed field must have an ident"); let method = target.method(); quote!(#field_name: self.#field_name.#method()) } fn make_unnamed_fields(fields_unnamed: &FieldsUnnamed, target: TargetTrait) -> Vec { let fields_to_static: Vec<_> = fields_unnamed .unnamed .iter() .enumerate() .map(|(i, _)| make_unnamed_field(i, target)) .collect(); fields_to_static } /// i.e. `self.0.to_static()` fn make_unnamed_field(i: usize, target: TargetTrait) -> TokenStream { let i = syn::Index::from(i); let method = target.method(); quote!(self.#i.#method()) } bounded-static-bounded-static-0.8.0/bounded-static-derive/src/lib.rs000066400000000000000000000044561463573033000254300ustar00rootroot00000000000000#![doc(html_root_url = "https://docs.rs/bounded-static-derive/0.8.0")] //! Provides the `ToStatic` derive macro. //! //! The [`ToStatic`] derive macro implements the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) //! and [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits for any `struct` //! and `enum` that can be converted to a form that is bounded by `'static`. //! //! The [`ToStatic`] macro should be used via the [`bounded-static`](https://docs.rs/bounded-static/0.8.0) crate //! rather than using this crate directly. #![warn(clippy::all, clippy::pedantic, clippy::nursery, rust_2018_idioms)] #![allow(clippy::redundant_pub_crate)] #![forbid(unsafe_code)] use proc_macro2::TokenStream; use syn::{Data, DataStruct, DeriveInput, Fields}; mod common; mod data_enum; mod data_struct; /// The `ToStatic` derive macro. /// /// Generate [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) and /// [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) impls for the data item deriving /// `ToStatic`. #[proc_macro_derive(ToStatic)] pub fn to_static(input: proc_macro::TokenStream) -> proc_macro::TokenStream { let input = syn::parse_macro_input!(input as syn::DeriveInput); proc_macro::TokenStream::from(generate_traits(&input)) } fn generate_traits(input: &DeriveInput) -> TokenStream { match &input.data { Data::Struct(DataStruct { fields: Fields::Named(fields_named), .. }) => data_struct::generate_struct_named(&input.ident, &input.generics, fields_named), Data::Struct(DataStruct { fields: Fields::Unnamed(fields_unnamed), .. }) => data_struct::generate_struct_unnamed(&input.ident, &input.generics, fields_unnamed), Data::Struct(DataStruct { fields: Fields::Unit, .. }) => data_struct::generate_struct_unit(&input.ident), Data::Enum(data_enum) => data_enum::generate_enum( &input.ident, &input.generics, data_enum.variants.iter().collect::>().as_slice(), ), Data::Union(_) => unimplemented!("union is not yet supported"), } } bounded-static-bounded-static-0.8.0/bounded-static-derive/tests/000077500000000000000000000000001463573033000246565ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static-derive/tests/integration_tests.rs000066400000000000000000000212101463573033000307650ustar00rootroot00000000000000use bounded_static::{IntoBoundedStatic, ToBoundedStatic, ToStatic}; use std::borrow::Cow; #[test] fn test_struct_named_fields_1() { #[derive(ToStatic)] struct Foo<'a> { value: Cow<'a, str>, } let value = String::from("value"); let data = Foo { value: Cow::from(&value), }; let owned = data.to_static(); ensure_static(owned); } #[test] fn test_struct_named_fields_2() { #[derive(ToStatic)] struct Foo<'a, 'b> { u8_value: u8, static_str: &'static str, owned_str: String, value: Cow<'a, str>, bar: Vec>, } #[derive(ToStatic)] struct Bar<'a> { u8_value: u8, static_str: &'static str, owned_str: String, value: Cow<'a, str>, } let value = String::from("value"); let bar = Bar { u8_value: 0, static_str: "", owned_str: String::from(""), value: Cow::from(&value), }; let data = Foo { u8_value: 0, static_str: "", owned_str: String::from(""), value: Cow::from(&value), bar: vec![bar], }; let owned = data.to_static(); ensure_static(owned); } #[test] fn test_no_generics_or_lifetimes() { #[derive(ToStatic)] struct Foo(u32); let data = Foo(0); ensure_static(data.to_static()) } #[test] fn test_struct_named_fields_no_generics() { #[derive(ToStatic)] struct Foo { foo: String, bar: &'static str, } let data = Foo { foo: String::from("value"), bar: "test", }; let owned = data.to_static(); ensure_static(owned); } #[test] fn test_struct_unnamed_fields() { #[derive(ToStatic)] struct Foo<'a>(String, Cow<'a, str>, u16, Bar<'a>); #[derive(ToStatic)] struct Bar<'a> { bar: Cow<'a, str>, } let value = String::from("value"); let data = Foo( String::from("test"), Cow::from(&value), 99, Bar { bar: Cow::from(&value), }, ); ensure_static(data.to_static()); } #[test] fn test_struct_unnamed_fields_no_generics() { #[derive(ToStatic)] struct Foo(String, &'static str); let data = Foo(String::from("value"), "test"); let owned = data.to_static(); ensure_static(owned); } #[test] fn test_unit_struct() { #[derive(ToStatic)] struct Foo; let data = Foo; ensure_static(data.to_static()); } #[test] fn test_struct_complex_lifetimes() { #[derive(ToStatic)] struct Foo<'a, 'b, R, T: 'b> where 'b: 'a, R: 'a, T: 'a, { baz: T, a: Cow<'a, str>, b: Cow<'b, str>, r: R, } let value = String::from("value"); let data = Foo { baz: 0isize, a: Cow::from(&value), b: Cow::from(&value), r: "test", }; let owned = data.to_static(); ensure_static(owned); } #[test] fn test_struct_named_fields_into() { #[derive(ToStatic)] struct Foo<'a> { value: Cow<'a, str>, } let value = String::from("value"); let data = Foo { value: Cow::from(&value), }; let owned = data.into_static(); ensure_static(owned); } #[test] fn test_struct_unnamed_fields_into() { #[derive(ToStatic)] struct Foo<'a>(String, Cow<'a, str>, u16, Bar<'a>); #[derive(ToStatic)] struct Bar<'a> { bar: Cow<'a, str>, } let value = String::from("value"); let data = Foo( String::from("test"), Cow::from(&value), 99, Bar { bar: Cow::from(&value), }, ); ensure_static(data.into_static()); } #[test] fn test_unit_struct_into() { #[derive(ToStatic)] struct Foo; let data = Foo; ensure_static(data.into_static()); } #[test] fn test_enum() { #[derive(ToStatic)] enum Foo<'a> { Unit, Named { name: String, age: i8 }, First(Cow<'a, str>, Cow<'a, str>), Second(Bar<'a>), Third(i128, bool, &'static str), } #[derive(ToStatic)] struct Bar<'a> { bar: Cow<'a, str>, } let value = String::from("value"); let unit = Foo::Unit; ensure_static(unit.to_static()); let named = Foo::Named { name: String::from("test"), age: 10, }; ensure_static(named.to_static()); let first = Foo::First(Cow::from(&value), Cow::from(&value)); ensure_static(first.to_static()); let second = Foo::Second(Bar { bar: Cow::from(&value), }); ensure_static(second.to_static()); let third = Foo::Third(100, true, "test"); ensure_static(third.to_static()); } #[test] fn test_enum_into() { #[derive(ToStatic)] enum Foo<'a> { Unit, Named { name: String, age: i8 }, First(Cow<'a, str>, Cow<'a, str>), Second(Bar<'a>), Third(i128, bool, &'static str), } #[derive(ToStatic)] struct Bar<'a> { bar: Cow<'a, str>, } let value = String::from("value"); let unit = Foo::Unit; ensure_static(unit.into_static()); let named = Foo::Named { name: String::from("test"), age: 10, }; ensure_static(named.into_static()); let first = Foo::First(Cow::from(&value), Cow::from(&value)); ensure_static(first.into_static()); let second = Foo::Second(Bar { bar: Cow::from(&value), }); ensure_static(second.into_static()); let third = Foo::Third(100, true, "test"); ensure_static(third.into_static()); } #[test] fn test_thread_spawn() { #[derive(Debug, Eq, PartialEq, ToStatic)] struct Foo<'a> { foo: Cow<'a, str>, bar: Vec>, } #[derive(Debug, Eq, PartialEq, ToStatic)] enum Bar<'a> { First, Second(Cow<'a, str>), } let value = String::from("data"); let data = Foo { foo: Cow::from(&value), bar: vec![Bar::First, Bar::Second(Cow::from(&value))], }; let data_static = data.into_static(); std::thread::spawn(move || { assert_eq!(data_static.foo, "data"); assert_eq!( data_static.bar, vec![Bar::First, Bar::Second("data".into())] ) }) .join() .unwrap(); } #[test] fn test_const_generics_struct() { #[derive(ToStatic)] struct Foo<'a, const N: usize, const M: usize> { value: Cow<'a, str>, left: [usize; N], right: [usize; M], } let value = String::from("value"); let data = Foo { value: Cow::from(&value), left: [0], right: [0, 1, 2], }; let owned = data.to_static(); ensure_static(owned); } #[test] fn test_const_generics_struct_into() { #[derive(ToStatic)] struct Foo<'a, const N: usize, const M: usize, const Q: bool> { value: Cow<'a, str>, left: [usize; N], right: [usize; M], } let value = String::from("value"); let data = Foo::<'_, 1, 3, true> { value: Cow::from(&value), left: [0], right: [0, 1, 2], }; let owned = data.into_static(); ensure_static(owned); } #[test] fn test_generic_bound_1() { #[derive(ToStatic)] struct Baz<'a, T: Into + 'a> { t: T, r: Cow<'a, str>, } let value = String::from("test"); let data = Baz { t: "", r: Cow::from(&value), }; ensure_static(data.to_static()); } #[test] fn test_generic_bound_2() { trait Foo {} trait Bar {} impl Foo for String {} impl Bar for String {} impl Foo for Cow<'_, T> {} impl Bar for Cow<'_, T> {} #[derive(ToStatic)] struct Baz { t: T, r: R, } let value = String::from("test"); let data = Baz { t: Cow::from(&value), r: String::from("test"), }; ensure_static(data.to_static()); } #[test] fn test_generic_bound_3() { #[derive(ToStatic)] struct Baz<'a, T: Into>(T, Cow<'a, str>); let value = String::from("test"); let data = Baz("", Cow::from(&value)); ensure_static(data.to_static()); } #[test] fn test_generic_bound_where_1() { #[derive(ToStatic)] struct Baz<'a, T: Foo>(T, Cow<'a, str>) where T: Into; trait Foo {} impl Foo for &str {} let value = String::from("test"); let data = Baz("", Cow::from(&value)); ensure_static(data.to_static()); } #[test] fn test_generic_bound_where_2() { #[derive(ToStatic)] struct Baz<'a, T: Foo>(T, Cow<'a, str>) where T: Into + 'a + Bar; trait Foo {} impl Foo for &str {} trait Bar {} impl Bar for &str {} let value = String::from("test"); let data = Baz("", Cow::from(&value)); ensure_static(data.into_static()); } fn ensure_static(s: S) { drop(s); } bounded-static-bounded-static-0.8.0/bounded-static/000077500000000000000000000000001463573033000222405ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static/Cargo.toml000066400000000000000000000025711463573033000241750ustar00rootroot00000000000000[package] name = "bounded-static" description = "Defines the ToBoundedStatic and IntoBoundedStatic traits" readme = "README.md" repository = "https://github.com/fujiapple852/bounded-static" version.workspace = true rust-version.workspace = true edition.workspace = true authors.workspace = true license.workspace = true keywords.workspace = true categories.workspace = true [features] default = [ "collections", "alloc", "std" ] # Enable impls of [To|Into]BoundedStatic for common types in the alloc crate. alloc = [] # Enable impls of [To|Into]BoundedStatic for collections in the alloc crate. collections = [ "alloc" ] # Enable impls of [To|Into]BoundedStatic for other types in std. std = [ "alloc", "ahash?/std", "chrono?/std" ] # Enable the ToStatic custom derive macro. derive = [ "bounded-static-derive" ] # Enable the clock feature for chrono. chrono-clock = [ "chrono", "chrono/clock" ] [dependencies] bounded-static-derive = { workspace = true, optional = true } smol_str = { workspace = true, optional = true, default-features = false } smallvec = { workspace = true, optional = true, default-features = false } smartstring = { workspace = true, optional = true, default-features = false } ahash = { workspace = true, optional = true, default-features = false } chrono = { workspace = true, optional = true, default-features = false } [package.metadata.docs.rs] all-features = true bounded-static-bounded-static-0.8.0/bounded-static/LICENCE000077700000000000000000000000001463573033000244402../LICENSEustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static/README.md000077700000000000000000000000001463573033000252042../README.mdustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static/src/000077500000000000000000000000001463573033000230275ustar00rootroot00000000000000bounded-static-bounded-static-0.8.0/bounded-static/src/lib.rs000066400000000000000000001503011463573033000241430ustar00rootroot00000000000000#![doc(html_root_url = "https://docs.rs/bounded-static/0.8.0")] //! Provides the [`ToBoundedStatic`] and [`IntoBoundedStatic`] traits and [`ToStatic`] derive macro. //! //! As described in the [Common Rust Lifetime Misconceptions](https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md#2-if-t-static-then-t-must-be-valid-for-the-entire-program): //! //! > `T: 'static` should be read as "`T` is bounded by a `'static` lifetime" not "`T` has a `'static` lifetime". //! //! The traits [`ToBoundedStatic`] and [`IntoBoundedStatic`] can be used to convert any suitable `T` and `&T` to an //! owned `T` such that `T: 'static`. Both traits define an associated type which is bounded by `'static` and provide //! a method to convert to that bounded type: //! //! ```rust //! pub trait ToBoundedStatic { //! type Static: 'static; //! //! fn to_static(&self) -> Self::Static; //! } //! //! pub trait IntoBoundedStatic { //! type Static: 'static; //! //! fn into_static(self) -> Self::Static; //! } //! ``` //! //! Implementations of [`ToBoundedStatic`] and [`IntoBoundedStatic`] are provided for the following `core` types: //! //! - [`primitive`](core::primitive) (no-op conversions) //! - [`array`](array) //! - [`tuple`](tuple) //! - [`Option`] //! - [`Result`] //! //! Additional implementations are available by enabling the following features: //! //! - `alloc` for common types from the `alloc` crate: //! - [Cow](https://doc.rust-lang.org/alloc/borrow/enum.Cow.html) //! - [String](https://doc.rust-lang.org/alloc/string/struct.String.html) //! - [Vec](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) //! - [Box](https://doc.rust-lang.org/alloc/boxed/struct.Box.html) //! //! - `collections` for all collection types in the `alloc` crate: //! - [BinaryHeap](https://doc.rust-lang.org/alloc/collections/binary_heap/struct.BinaryHeap.html) //! - [BTreeMap](https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html) //! - [BTreeSet](https://doc.rust-lang.org/alloc/collections/btree_set/struct.BTreeSet.html) //! - [LinkedList](https://doc.rust-lang.org/alloc/collections/linked_list/struct.LinkedList.html) //! - [VecDeque](https://doc.rust-lang.org/alloc/collections/vec_deque/struct.VecDeque.html) //! //! - `std` for additional types from `std`: //! - [HashMap](https://doc.rust-lang.org/std/collections/struct.HashMap.html) //! - [HashSet](https://doc.rust-lang.org/std/collections/struct.HashSet.html) //! - [RandomState](https://doc.rust-lang.org/std/collections/hash_map/struct.RandomState.html) //! //! Note that `collections`, `alloc` and `std` are enabled be default. //! //! Additional implementations for 3rd party types are available by enabling the following features: //! //! - `smol_str` for [`SmolStr`](https://docs.rs/smol_str/0.2.2/smol_str/struct.SmolStr.html) //! - `smallvec` for [`SmallVec`](https://docs.rs/smallvec/1.13.2/smallvec/struct.SmallVec.html) //! - `smartstring` for [`SmartString`](https://docs.rs/smartstring/1.0.1/smartstring/index.html) //! - `ahash` for: //! - [`RandomState`](https://docs.rs/ahash/0.8.6/ahash/random_state/struct.RandomState.html) //! - [`AHashMap`](https://docs.rs/ahash/0.8.6/ahash/struct.AHashMap.html) //! - [`AHashSet`](https://docs.rs/ahash/0.8.6/ahash/struct.AHashSet.html) //! - `chrono` for: //! - [`DateTime`](https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html) //! - [`FixedOffset`](https://docs.rs/chrono/0.4.38/chrono/struct.FixedOffset.html) //! - [`Months`](https://docs.rs/chrono/0.4.38/chrono/struct.Months.html) //! - [`TimeDelta`](https://docs.rs/chrono/0.4.38/chrono/struct.TimeDelta.html) //! - [`Utc`](https://docs.rs/chrono/0.4.38/chrono/struct.Utc.html) //! - [`Month`](https://docs.rs/chrono/0.4.38/chrono/enum.Month.html) //! - [`Weekday`](https://docs.rs/chrono/0.4.38/chrono/enum.Weekday.html) //! - [`Days`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.Days.html) //! - [`IsoWeek`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.IsoWeek.html) //! - [`NaiveDate`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDate.html) //! - [`NaiveDateTime`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html) //! - [`NaiveTime`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveTime.html) //! - `chrono-clock` for: //! - [`Local`](https://docs.rs/chrono/0.4.38/chrono/struct.Local.html) //! //! # Examples //! //! Given a structure which can be borrow or owned and a function which requires its argument is bounded by the //! `'static` lifetime: //! //! ```rust //! # use std::borrow::Cow; //! struct Foo<'a> { //! bar: Cow<'a, str>, //! baz: Vec>, //! } //! //! fn ensure_static(_: T) {} //! ``` //! //! We can implement [`ToBoundedStatic`] (and [`IntoBoundedStatic`]) for `Foo<'_>`: //! //! ```rust //! # use std::borrow::Cow; //! # use bounded_static::ToBoundedStatic; //! struct Foo<'a> { //! bar: Cow<'a, str>, //! baz: Vec>, //! } //! impl ToBoundedStatic for Foo<'_> { //! type Static = Foo<'static>; //! //! fn to_static(&self) -> Self::Static { //! Foo { bar: self.bar.to_static(), baz: self.baz.to_static() } //! } //! } //! ``` //! //! This allows it to be converted to an owned representation such that it is now bounded by `'static`: //! //! ```rust //! # use std::borrow::Cow; //! # use bounded_static::ToBoundedStatic; //! # struct Foo<'a> { //! # bar: Cow<'a, str>, //! # baz: Vec>, //! # } //! # impl ToBoundedStatic for Foo<'_> { //! # type Static = Foo<'static>; //! # //! # fn to_static(&self) -> Self::Static { //! # Foo { bar: self.bar.to_static(), baz: self.baz.to_static() } //! # } //! # } //! fn test() { //! # fn ensure_static(_: T) {} //! let s = String::from("data"); //! let foo = Foo { bar: Cow::from(&s), baz: vec![Cow::from(&s)] }; //! let to_static = foo.to_static(); //! ensure_static(to_static); //! } //! ``` //! //! # Derive //! //! These traits may be automatically derived for any `struct` or `enum` that can be converted to a form that is //! bounded by `'static` by using the [`ToStatic`] macro. It support all `struct` flavors (unit, named & unnamed), //! all `enum` variant flavors (unit, named & unnamed). It does not currently support `union`. //! //! To use the [`ToStatic`] macro you must enable the `derive` feature: //! //! ```yaml //! bounded-static = { version = "0.8.0", features = [ "derive" ] } //! ``` //! //! # Examples //! //! ```rust //! # use std::borrow::Cow; //! # use std::collections::HashMap; //! # use bounded_static::ToStatic; //! /// Named field struct //! #[derive(ToStatic)] //! struct Foo<'a> { //! aaa: Cow<'a, str>, //! bbb: &'static str, //! ccc: Baz<'a>, //! } //! //! /// Unnamed field struct //! #[derive(ToStatic)] //! struct Bar<'a, 'b>(u128, HashMap, Cow<'b, str>>); //! //! /// Unit struct //! #[derive(ToStatic)] //! struct Qux; //! //! #[derive(ToStatic)] //! enum Baz<'a> { //! First(String, usize, Vec>), //! Second { fst: u32, snd: &'static str }, //! Third, //! } //! ``` #![warn(clippy::all, clippy::pedantic, clippy::nursery, rust_2018_idioms)] #![allow(clippy::missing_const_for_fn)] #![forbid(unsafe_code)] #![no_std] #[cfg(feature = "std")] extern crate std; #[cfg(feature = "alloc")] extern crate alloc; use core::num::{ NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, }; #[cfg(feature = "alloc")] use alloc::{ borrow::{Cow, ToOwned}, boxed::Box, string::String, vec::Vec, }; #[cfg(feature = "collections")] use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; #[cfg(feature = "derive")] /// Re-export for the custom derive macro `ToStatic`. pub use bounded_static_derive::ToStatic; /// A trait for converting `&T` to an owned `T` such that `T: 'static`. /// /// See the module level documentation for details. pub trait ToBoundedStatic { /// The target type is bounded by the `'static` lifetime. type Static: 'static; /// Convert an `&T` to an owned `T` such that `T: 'static`. #[must_use = "converting is often expensive and is not expected to have side effects"] fn to_static(&self) -> Self::Static; } /// A trait for converting an owned `T` into an owned `T` such that `T: 'static`. /// /// See the module level documentation for details. pub trait IntoBoundedStatic { /// The target type is bounded by the `'static` lifetime. type Static: 'static; /// Convert an owned `T` into an owned `T` such that `T: 'static`. #[must_use = "converting is often expensive and is not expected to have side effects"] fn into_static(self) -> Self::Static; } /// No-op [`ToBoundedStatic`] impl for converting `&'static str` to `&'static str`. impl ToBoundedStatic for &'static str { type Static = &'static str; fn to_static(&self) -> Self::Static { self } } /// No-op [`IntoBoundedStatic`] impl for converting `&'static str` into `&'static str`. impl IntoBoundedStatic for &'static str { type Static = &'static str; fn into_static(self) -> Self::Static { self } } /// No-op [`ToBoundedStatic`] and [`IntoBoundedStatic`] impls for `Copy` types. macro_rules! make_copy_impl { ($id:ty) => { /// No-op [`ToBoundedStatic`] impl for this `Copy` type. impl ToBoundedStatic for $id { type Static = Self; fn to_static(&self) -> Self::Static { *self } } /// No-op [`IntoBoundedStatic`] impl for this `Copy` type. impl IntoBoundedStatic for $id { type Static = Self; fn into_static(self) -> Self::Static { self } } }; } make_copy_impl!(bool); make_copy_impl!(char); make_copy_impl!(f32); make_copy_impl!(f64); make_copy_impl!(usize); make_copy_impl!(u8); make_copy_impl!(u16); make_copy_impl!(u32); make_copy_impl!(u64); make_copy_impl!(u128); make_copy_impl!(isize); make_copy_impl!(i8); make_copy_impl!(i16); make_copy_impl!(i32); make_copy_impl!(i64); make_copy_impl!(i128); make_copy_impl!(NonZeroUsize); make_copy_impl!(NonZeroU8); make_copy_impl!(NonZeroU16); make_copy_impl!(NonZeroU32); make_copy_impl!(NonZeroU64); make_copy_impl!(NonZeroU128); make_copy_impl!(NonZeroIsize); make_copy_impl!(NonZeroI8); make_copy_impl!(NonZeroI16); make_copy_impl!(NonZeroI32); make_copy_impl!(NonZeroI64); make_copy_impl!(NonZeroI128); /// No-op [`ToBoundedStatic`] impl for unit type `()`. impl ToBoundedStatic for () { type Static = (); fn to_static(&self) -> Self::Static {} } /// No-op [`IntoBoundedStatic`] impl for unit type `()`. impl IntoBoundedStatic for () { type Static = (); fn into_static(self) -> Self::Static {} } /// Blanket [`ToBoundedStatic`] impl for converting `Option` to `Option: 'static`. impl ToBoundedStatic for Option where T: ToBoundedStatic, { type Static = Option; fn to_static(&self) -> Self::Static { self.as_ref().map(ToBoundedStatic::to_static) } } /// Blanket [`IntoBoundedStatic`] impl for converting `Option` into `Option: 'static`. impl IntoBoundedStatic for Option where T: IntoBoundedStatic, { type Static = Option; fn into_static(self) -> Self::Static { self.map(IntoBoundedStatic::into_static) } } /// Blanket [`ToBoundedStatic`] impl for converting `Result` to `Result: 'static`. impl ToBoundedStatic for Result where T: ToBoundedStatic, E: ToBoundedStatic, { type Static = Result; fn to_static(&self) -> Self::Static { match self { Ok(value) => Ok(value.to_static()), Err(err) => Err(err.to_static()), } } } /// Blanket [`IntoBoundedStatic`] impl for converting `Result` into `Result: 'static`. impl IntoBoundedStatic for Result where T: IntoBoundedStatic, E: IntoBoundedStatic, { type Static = Result; fn into_static(self) -> Self::Static { match self { Ok(value) => Ok(value.into_static()), Err(err) => Err(err.into_static()), } } } /// Blanket [`ToBoundedStatic`] impl for converting `[T; const N: usize]` to `[T; const N: usize]: 'static`. impl ToBoundedStatic for [T; N] where T: ToBoundedStatic + Copy, { type Static = [T::Static; N]; fn to_static(&self) -> Self::Static { // Note that we required that `T` is `Copy` here whereas the `IntoBoundedStatic` impl does does not. self.map(|item| item.to_static()) } } /// Blanket [`IntoBoundedStatic`] impl for converting `[T; const N: usize]` into `[T; const N: usize]: 'static`. impl IntoBoundedStatic for [T; N] where T: IntoBoundedStatic, { type Static = [T::Static; N]; fn into_static(self) -> Self::Static { self.map(IntoBoundedStatic::into_static) } } /// Blanket [`ToBoundedStatic`] impl for converting tuples `(T1, T2, ...)` to `(T1, T2, ..): 'static`. macro_rules! tuple_to_static { () => (); ($($name:ident,)+) => { tuple_to_static! ( @gen $($name,)+, concat!( "Blanket [`ToBoundedStatic`] impl for converting tuple `", stringify!(($($name,)+)), "` to `", stringify!(($($name,)+)), ": 'static `" ) ); }; (@gen $($name:ident,)+, $doc:expr) => { #[doc = $doc] impl<$($name: ToBoundedStatic),+> ToBoundedStatic for ($($name,)+) { type Static = ($($name::Static,)+); #[allow(non_snake_case)] fn to_static(&self) -> Self::Static { let ($(ref $name,)+) = *self; ($($name.to_static(),)+) } } tuple_to_static! {@peel $($name,)+ } }; (@peel $name:ident, $($other:ident,)*) => {tuple_to_static! { $($other,)* }}; } /// Blanket [`IntoBoundedStatic`] impl for converting tuples `(T1, T2, ...)` into `(T1, T2, ..): 'static`. macro_rules! tuple_into_static { () => (); ($($name:ident,)+) => { tuple_into_static! ( @gen $($name,)+, concat!( "Blanket [`IntoBoundedStatic`] impl for converting tuple `", stringify!(($($name,)+)), "` into `", stringify!(($($name,)+)), ": 'static `" ) ); }; (@gen $($name:ident,)+, $doc:expr) => { #[doc = $doc] impl<$($name: IntoBoundedStatic),+> IntoBoundedStatic for ($($name,)+) { type Static = ($($name::Static,)+); #[allow(non_snake_case)] fn into_static(self) -> Self::Static { let ($($name,)+) = self; ($($name.into_static(),)+) } } tuple_into_static! {@peel $($name,)+ } }; (@peel $name:ident, $($other:ident,)*) => {tuple_into_static! { $($other,)* }}; } tuple_to_static! { T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0, } tuple_into_static! { T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0, } #[cfg(feature = "alloc")] /// Blanket [`ToBoundedStatic`] impl for converting `Cow<'a, T: ?Sized>` to `Cow<'static, T: ?Sized>`. impl ToBoundedStatic for Cow<'_, T> where T: 'static + ToOwned + ?Sized, { type Static = Cow<'static, T>; fn to_static(&self) -> Self::Static { Cow::Owned(self.clone().into_owned()) } } #[cfg(feature = "alloc")] /// Blanket [`IntoBoundedStatic`] impl for converting `Cow<'a, T: ?Sized>` into `Cow<'static, T: ?Sized>`. impl IntoBoundedStatic for Cow<'_, T> where T: 'static + ToOwned + ?Sized, { type Static = Cow<'static, T>; fn into_static(self) -> Self::Static { Cow::Owned(self.into_owned()) } } #[cfg(feature = "alloc")] /// [`ToBoundedStatic`] impl for `String`. impl ToBoundedStatic for String { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } #[cfg(feature = "alloc")] /// No-op [`IntoBoundedStatic`] impl for `String`. impl IntoBoundedStatic for String { type Static = Self; fn into_static(self) -> Self::Static { self } } #[cfg(feature = "alloc")] /// Blanket [`ToBoundedStatic`] impl for converting `Vec` to `Vec: 'static`. impl ToBoundedStatic for Vec where T: ToBoundedStatic, { type Static = Vec; fn to_static(&self) -> Self::Static { self.iter().map(ToBoundedStatic::to_static).collect() } } #[cfg(feature = "alloc")] /// Blanket [`IntoBoundedStatic`] impl for converting `Vec` into `Vec: 'static`. impl IntoBoundedStatic for Vec where T: IntoBoundedStatic, { type Static = Vec; fn into_static(self) -> Self::Static { self.into_iter() .map(IntoBoundedStatic::into_static) .collect() } } #[cfg(feature = "collections")] /// Blanket [`ToBoundedStatic`] impl for converting `BinaryHeap` into `BinaryHeap: 'static`. impl ToBoundedStatic for BinaryHeap where T: ToBoundedStatic, T::Static: Ord, { type Static = BinaryHeap; fn to_static(&self) -> Self::Static { self.iter().map(ToBoundedStatic::to_static).collect() } } #[cfg(feature = "collections")] /// Blanket [`IntoBoundedStatic`] impl for converting `BinaryHeap` into `BinaryHeap: 'static`. impl IntoBoundedStatic for BinaryHeap where T: IntoBoundedStatic, T::Static: Ord, { type Static = BinaryHeap; fn into_static(self) -> Self::Static { self.into_iter() .map(IntoBoundedStatic::into_static) .collect() } } #[cfg(feature = "collections")] /// Blanket [`ToBoundedStatic`] impl for converting `BTreeMap` into `BTreeMap: 'static`. impl ToBoundedStatic for BTreeMap where K: ToBoundedStatic, K::Static: Ord, V: ToBoundedStatic, { type Static = BTreeMap; fn to_static(&self) -> Self::Static { self.iter() .map(|(k, v)| (k.to_static(), v.to_static())) .collect() } } #[cfg(feature = "collections")] /// Blanket [`IntoBoundedStatic`] impl for converting `BTreeMap` into `BTreeMap: 'static`. impl IntoBoundedStatic for BTreeMap where K: IntoBoundedStatic, K::Static: Ord, V: IntoBoundedStatic, { type Static = BTreeMap; fn into_static(self) -> Self::Static { self.into_iter() .map(|(k, v)| (k.into_static(), v.into_static())) .collect() } } #[cfg(feature = "collections")] /// Blanket [`ToBoundedStatic`] impl for converting `BTreeSet` into `BTreeSet: 'static`. impl ToBoundedStatic for BTreeSet where T: ToBoundedStatic, T::Static: Ord, { type Static = BTreeSet; fn to_static(&self) -> Self::Static { self.iter().map(ToBoundedStatic::to_static).collect() } } #[cfg(feature = "collections")] /// Blanket [`IntoBoundedStatic`] impl for converting `BTreeSet` into `BTreeSet: 'static`. impl IntoBoundedStatic for BTreeSet where T: IntoBoundedStatic, T::Static: Ord, { type Static = BTreeSet; fn into_static(self) -> Self::Static { self.into_iter() .map(IntoBoundedStatic::into_static) .collect() } } #[cfg(feature = "collections")] /// Blanket [`ToBoundedStatic`] impl for converting `LinkedList` into `LinkedList: 'static`. impl ToBoundedStatic for LinkedList where T: ToBoundedStatic, { type Static = LinkedList; fn to_static(&self) -> Self::Static { self.iter().map(ToBoundedStatic::to_static).collect() } } #[cfg(feature = "collections")] /// Blanket [`IntoBoundedStatic`] impl for converting `LinkedList` into `LinkedList: 'static`. impl IntoBoundedStatic for LinkedList where T: IntoBoundedStatic, { type Static = LinkedList; fn into_static(self) -> Self::Static { self.into_iter() .map(IntoBoundedStatic::into_static) .collect() } } #[cfg(feature = "collections")] /// Blanket [`ToBoundedStatic`] impl for converting `VecDeque` into `VecDeque: 'static`. impl ToBoundedStatic for VecDeque where T: ToBoundedStatic, { type Static = VecDeque; fn to_static(&self) -> Self::Static { self.iter().map(ToBoundedStatic::to_static).collect() } } #[cfg(feature = "collections")] /// Blanket [`IntoBoundedStatic`] impl for converting `VecDeque` into `VecDeque: 'static`. impl IntoBoundedStatic for VecDeque where T: IntoBoundedStatic, { type Static = VecDeque; fn into_static(self) -> Self::Static { self.into_iter() .map(IntoBoundedStatic::into_static) .collect() } } #[cfg(feature = "alloc")] /// Blanket [`ToBoundedStatic`] impl for converting `Box` to `Box: 'static`. impl ToBoundedStatic for Box where T: ToBoundedStatic, { type Static = Box; fn to_static(&self) -> Self::Static { Box::new(self.as_ref().to_static()) } } #[cfg(feature = "alloc")] /// Blanket [`IntoBoundedStatic`] impl for converting `Box` into `Box: 'static`. impl IntoBoundedStatic for Box where T: IntoBoundedStatic, { type Static = Box; fn into_static(self) -> Self::Static { Box::new((*self).into_static()) } } #[cfg(feature = "std")] /// Blanket [`ToBoundedStatic`] impl for converting `HashMap` to `HashMap: 'static`. impl ToBoundedStatic for std::collections::HashMap where K: ToBoundedStatic, K::Static: Eq + std::hash::Hash, V: ToBoundedStatic, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = std::collections::HashMap; fn to_static(&self) -> Self::Static { let mut map = std::collections::HashMap::with_capacity_and_hasher( self.len(), self.hasher().to_static(), ); map.extend(self.iter().map(|(k, v)| (k.to_static(), v.to_static()))); map } } #[cfg(feature = "std")] /// Blanket [`IntoBoundedStatic`] impl for for converting `HashMap` into `HashMap: 'static`. impl IntoBoundedStatic for std::collections::HashMap where K: IntoBoundedStatic, K::Static: Eq + std::hash::Hash, V: IntoBoundedStatic, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = std::collections::HashMap; fn into_static(self) -> Self::Static { let mut map = std::collections::HashMap::with_capacity_and_hasher( self.len(), self.hasher().to_static(), ); map.extend( self.into_iter() .map(|(k, v)| (k.into_static(), v.into_static())), ); map } } #[cfg(feature = "std")] /// Blanket [`ToBoundedStatic`] impl for converting `HashSet` into `HashSet: 'static`. impl ToBoundedStatic for std::collections::HashSet where T: ToBoundedStatic, T::Static: Eq + std::hash::Hash, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = std::collections::HashSet; fn to_static(&self) -> Self::Static { let mut set = std::collections::HashSet::with_capacity_and_hasher( self.len(), self.hasher().to_static(), ); set.extend(self.iter().map(ToBoundedStatic::to_static)); set } } #[cfg(feature = "std")] /// Blanket [`IntoBoundedStatic`] impl for converting `HashSet` into `HashSet: 'static`. impl IntoBoundedStatic for std::collections::HashSet where T: IntoBoundedStatic, T::Static: Eq + std::hash::Hash, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = std::collections::HashSet; fn into_static(self) -> Self::Static { let mut set = std::collections::HashSet::with_capacity_and_hasher( self.len(), self.hasher().to_static(), ); set.extend(self.into_iter().map(IntoBoundedStatic::into_static)); set } } #[cfg(feature = "std")] /// [`ToBoundedStatic`] impl for `std::collections::hash_map::RandomState`. impl ToBoundedStatic for std::collections::hash_map::RandomState { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } /// [`ToBoundedStatic`] impl for `smol_str::SmolStr`. #[cfg(feature = "smol_str")] impl ToBoundedStatic for smol_str::SmolStr { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } /// No-op [`IntoBoundedStatic`] impl for `smol_str::SmolStr`. #[cfg(feature = "smol_str")] impl IntoBoundedStatic for smol_str::SmolStr { type Static = Self; fn into_static(self) -> Self::Static { self } } /// [`ToBoundedStatic`] impl for `smallvec::SmallVec`. #[cfg(feature = "smallvec")] impl ToBoundedStatic for smallvec::SmallVec where A: smallvec::Array + 'static, A::Item: Clone, { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } /// No-op [`IntoBoundedStatic`] impl for `smallvec::SmallVec`. #[cfg(feature = "smallvec")] impl IntoBoundedStatic for smallvec::SmallVec where A: smallvec::Array + 'static, A::Item: Clone, { type Static = Self; fn into_static(self) -> Self::Static { self } } /// [`ToBoundedStatic`] impl for `smartstring::SmartString`. #[cfg(feature = "smartstring")] impl ToBoundedStatic for smartstring::SmartString where Mode: smartstring::SmartStringMode + 'static, { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } /// No-op [`IntoBoundedStatic`] impl for `smartstring::SmartString`. #[cfg(feature = "smartstring")] impl IntoBoundedStatic for smartstring::SmartString where Mode: smartstring::SmartStringMode + 'static, { type Static = Self; fn into_static(self) -> Self::Static { self } } #[cfg(feature = "ahash")] /// [`ToBoundedStatic`] impl for `ahash::RandomState`. impl ToBoundedStatic for ahash::RandomState { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } #[cfg(all(feature = "ahash", feature = "std"))] /// Blanket [`ToBoundedStatic`] impl for converting `ahash::AHashMap` to `ahash::AHashMap: 'static`. impl ToBoundedStatic for ahash::AHashMap where K: ToBoundedStatic, K::Static: Eq + std::hash::Hash, V: ToBoundedStatic, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = ahash::AHashMap; fn to_static(&self) -> Self::Static { let mut map = ahash::AHashMap::with_capacity_and_hasher(self.len(), self.hasher().to_static()); map.extend(self.iter().map(|(k, v)| (k.to_static(), v.to_static()))); map } } #[cfg(all(feature = "ahash", feature = "std"))] /// Blanket [`IntoBoundedStatic`] impl for converting `ahash::AHashMap` into `ahash::AHashMap: 'static`. impl IntoBoundedStatic for ahash::AHashMap where K: IntoBoundedStatic, K::Static: Eq + std::hash::Hash, V: IntoBoundedStatic, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = ahash::AHashMap; fn into_static(self) -> Self::Static { let mut map = ahash::AHashMap::with_capacity_and_hasher(self.len(), self.hasher().to_static()); map.extend( self.into_iter() .map(|(k, v)| (k.into_static(), v.into_static())), ); map } } #[cfg(all(feature = "ahash", feature = "std"))] /// Blanket [`ToBoundedStatic`] impl for converting `ahash::AHashSet` to `ahash::AHashSet: 'static`. impl ToBoundedStatic for ahash::AHashSet where T: ToBoundedStatic, T::Static: Eq + std::hash::Hash, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = ahash::AHashSet; fn to_static(&self) -> Self::Static { let mut set = ahash::AHashSet::with_capacity_and_hasher(self.len(), self.hasher().to_static()); set.extend(self.iter().map(ToBoundedStatic::to_static)); set } } #[cfg(all(feature = "ahash", feature = "std"))] /// Blanket [`IntoBoundedStatic`] impl for converting `ahash::AHashSet` into `ahash::AHashSet: 'static`. impl IntoBoundedStatic for ahash::AHashSet where T: IntoBoundedStatic, T::Static: Eq + std::hash::Hash, S: ToBoundedStatic, S::Static: std::hash::BuildHasher, { type Static = ahash::AHashSet; fn into_static(self) -> Self::Static { let mut set = ahash::AHashSet::with_capacity_and_hasher(self.len(), self.hasher().to_static()); set.extend(self.into_iter().map(IntoBoundedStatic::into_static)); set } } #[cfg(feature = "chrono")] /// Blanket [`ToBoundedStatic`] impl for converting `chrono::DateTime` into `chrono::DateTime: 'static`. impl ToBoundedStatic for chrono::DateTime where Tz: ToBoundedStatic + chrono::TimeZone, Tz::Static: chrono::TimeZone, { type Static = chrono::DateTime; fn to_static(&self) -> Self::Static { self.with_timezone(&self.timezone().to_static()) } } #[cfg(feature = "chrono")] /// Blanket [`IntoBoundedStatic`] impl for converting `chrono::DateTime` into `chrono::DateTime: 'static`. impl IntoBoundedStatic for chrono::DateTime where Tz: IntoBoundedStatic + chrono::TimeZone, Tz::Static: chrono::TimeZone, { type Static = chrono::DateTime; fn into_static(self) -> Self::Static { self.with_timezone(&self.timezone().into_static()) } } #[cfg(feature = "chrono")] make_copy_impl!(chrono::FixedOffset); #[cfg(feature = "chrono")] make_copy_impl!(chrono::Months); #[cfg(feature = "chrono")] make_copy_impl!(chrono::TimeDelta); #[cfg(feature = "chrono")] make_copy_impl!(chrono::Utc); #[cfg(feature = "chrono")] make_copy_impl!(chrono::Month); #[cfg(feature = "chrono")] make_copy_impl!(chrono::Weekday); #[cfg(feature = "chrono")] make_copy_impl!(chrono::naive::Days); #[cfg(feature = "chrono")] make_copy_impl!(chrono::naive::IsoWeek); #[cfg(feature = "chrono")] make_copy_impl!(chrono::naive::NaiveDate); #[cfg(feature = "chrono")] make_copy_impl!(chrono::naive::NaiveDateTime); #[cfg(feature = "chrono")] make_copy_impl!(chrono::naive::NaiveTime); #[cfg(feature = "chrono-clock")] make_copy_impl!(chrono::Local); // No implementation for chrono::NaiveWeek as it's not Copy nor Clone. #[cfg(test)] mod core_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_bool() { ensure_static(false.to_static()); } #[test] fn test_char() { ensure_static('a'.to_static()); } #[test] fn test_f32() { ensure_static(0.0f32.to_static()); } #[test] fn test_f64() { ensure_static(0.0f64.to_static()); } #[test] fn test_usize() { ensure_static(0usize.to_static()); } #[test] fn test_u8() { ensure_static(0u8.to_static()); } #[test] fn test_u16() { ensure_static(0u16.to_static()); } #[test] fn test_u32() { ensure_static(0u32.to_static()); } #[test] fn test_u64() { ensure_static(0u64.to_static()); } #[test] fn test_u128() { ensure_static(0u128.to_static()); } #[test] fn test_isize() { ensure_static(0isize.to_static()); } #[test] fn test_i8() { ensure_static(0i8.to_static()); } #[test] fn test_i16() { ensure_static(0i16.to_static()); } #[test] fn test_i32() { ensure_static(0i32.to_static()); } #[test] fn test_i64() { ensure_static(0i64.to_static()); } #[test] fn test_i128() { ensure_static(0i128.to_static()); } #[test] fn test_non_zero_usize() { ensure_static(NonZeroUsize::new(1).unwrap().to_static()); } #[test] fn test_non_zero_u8() { ensure_static(NonZeroU8::new(1).unwrap().to_static()); } #[test] fn test_non_zero_u16() { ensure_static(NonZeroU16::new(1).unwrap().to_static()); } #[test] fn test_non_zero_u32() { ensure_static(NonZeroU32::new(1).unwrap().to_static()); } #[test] fn test_non_zero_u64() { ensure_static(NonZeroU64::new(1).unwrap().to_static()); } #[test] fn test_non_zero_u128() { ensure_static(NonZeroU128::new(1).unwrap().to_static()); } #[test] fn test_non_zero_isize() { ensure_static(NonZeroIsize::new(1).unwrap().to_static()); } #[test] fn test_non_zero_i8() { ensure_static(NonZeroI8::new(1).unwrap().to_static()); } #[test] fn test_non_zero_i16() { ensure_static(NonZeroI16::new(1).unwrap().to_static()); } #[test] fn test_non_zero_i32() { ensure_static(NonZeroI32::new(1).unwrap().to_static()); } #[test] fn test_non_zero_i64() { ensure_static(NonZeroI64::new(1).unwrap().to_static()); } #[test] fn test_non_zero_i128() { ensure_static(NonZeroI128::new(1).unwrap().to_static()); } #[test] fn test_unit() { #[allow(clippy::unit_arg)] ensure_static(().to_static()); } #[test] fn test_str() { let s = ""; let to_static = s.to_static(); ensure_static(to_static); } #[test] fn test_option_none() { let value: Option = None; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_option_some() { let value: Option = Some(32); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_result() { #[derive(Clone)] struct MyError; #[allow(clippy::unnecessary_wraps)] fn foo_ok() -> Result<(), MyError> { Ok(()) } #[allow(clippy::unnecessary_wraps)] fn foo_err() -> Result<(), MyError> { Err(MyError) } impl ToBoundedStatic for MyError { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } let ok_result = foo_ok(); ensure_static(ok_result.to_static()); assert!(ok_result.is_ok()); let err_result = foo_err(); ensure_static(err_result.to_static()); assert!(err_result.is_err()); } #[test] fn test_array() { let arr = ["test"]; ensure_static(arr.to_static()); } #[test] fn test_tuple2() { let tuple = ("test", 32); ensure_static(tuple.to_static()); } #[test] fn test_tuple11() { let tuple = ( (), '1', "2", 3_i32, 4_usize, 5_isize, 6.0_f64, ["7"], Some(8), 9, (10,), false, ); ensure_static(tuple.to_static()); } } #[cfg(feature = "alloc")] #[cfg(test)] mod alloc_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_string() { let s = String::new(); let to_static = s.to_static(); ensure_static(to_static); } #[test] fn test_cow_borrowed_str() { let s = String::new(); let to_static = Cow::from(&s).to_static(); ensure_static(to_static); } #[test] fn test_cow_owned_string() { let s = String::new(); let to_static = Cow::from(s).to_static(); ensure_static(to_static); } #[test] fn test_cow_to_static() { let s = String::new(); let s_cow: Cow<'_, str> = Cow::Borrowed(&s); let s1_cow_owned: Cow<'_, str> = s_cow.to_static(); let s2_cow_owned: Cow<'_, str> = Cow::Owned(s_cow.into_owned()); assert_eq!(s1_cow_owned, s2_cow_owned); } #[test] fn test_cow_into_static() { let s = String::new(); let s_cow: Cow<'_, str> = Cow::Borrowed(&s); let s1_cow_owned: Cow<'_, str> = s_cow.clone().into_static(); let s2_cow_owned: Cow<'_, str> = Cow::Owned(s_cow.into_owned()); assert_eq!(s1_cow_owned, s2_cow_owned); } #[test] fn test_option_none() { let value: Option> = None; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_option_some() { let s = String::new(); let value = Some(Cow::from(&s)); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_array() { let arr = ["test"]; ensure_static(arr.to_static()); } #[test] fn test_array_into() { let s = String::new(); let arr = [Cow::from(&s)]; ensure_static(arr.into_static()); } #[test] fn test_vec1() { let s = String::new(); let value = alloc::vec![Cow::from(&s)]; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_vec2() { let s = String::new(); let value = alloc::vec![Cow::from(&s), Cow::from(s.as_str())]; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_box() { let s = String::new(); let value = Box::new(s); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_box_cow() { let s = String::new(); let value = Box::new(Cow::from(&s)); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_box_vec_cow() { let s = String::new(); let value = Box::new(alloc::vec![Cow::from(&s)]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_vec_box_cow() { let s = String::new(); let value = alloc::vec![Box::new(Cow::from(&s))]; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_cow_box() { let s = String::new(); let boxed = Box::new(s); let value = Cow::Borrowed(&boxed); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_cow_struct() { #[derive(Copy, Clone)] struct Foo {} impl ToBoundedStatic for Foo { type Static = Self; fn to_static(&self) -> Self::Static { *self } } let foo = Foo {}; let value = Cow::Borrowed(&foo); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_cow_struct_of_cow() { #[derive(Clone)] struct Foo<'a> { foo: Cow<'a, str>, } impl ToBoundedStatic for Foo<'_> { type Static = Foo<'static>; fn to_static(&self) -> Self::Static { Foo { foo: self.foo.to_static(), } } } let s = String::new(); let foo = Foo { foo: Cow::from(&s) }; let value = Cow::Borrowed(&foo); // TODO need to `into_owned()` here let to_static = value.into_owned().to_static(); ensure_static(to_static); } #[test] fn test_cow_cow() { let s = String::new(); let value1: Cow<'_, str> = Cow::Borrowed(&s); let value2: Cow<'_, Cow<'_, str>> = Cow::Borrowed(&value1); // TODO need to `into_owned()` here let to_static = value2.into_owned().to_static(); ensure_static(to_static); } #[test] fn test_struct_cow_borrowed_str() { struct Foo<'a> { foo: Cow<'a, str>, } impl ToBoundedStatic for Foo<'_> { type Static = Foo<'static>; fn to_static(&self) -> Self::Static { Foo { foo: self.foo.to_static(), } } } let s = String::new(); let foo = Foo { foo: Cow::from(&s) }; let to_static = foo.to_static(); ensure_static(to_static); } #[test] fn test_struct_cow_owned_string() { struct Foo<'a> { foo: Cow<'a, str>, } impl ToBoundedStatic for Foo<'_> { type Static = Foo<'static>; fn to_static(&self) -> Self::Static { Foo { foo: self.foo.to_static(), } } } let s = String::new(); let foo = Foo { foo: Cow::from(s) }; let to_static = foo.to_static(); ensure_static(to_static); } #[test] fn test_struct_multi() { #[derive(Clone)] struct Foo<'a> { bar: Cow<'a, str>, baz: Vec>, } impl ToBoundedStatic for Foo<'_> { type Static = Foo<'static>; fn to_static(&self) -> Self::Static { Foo { bar: self.bar.to_static(), baz: self.baz.to_static(), } } } let s = String::new(); let foo = Foo { bar: Cow::from(&s), baz: alloc::vec![Cow::from(&s)], }; let to_static = foo.to_static(); ensure_static(to_static); } #[test] fn test_struct_mixed() { struct Foo<'a> { prim: u64, borrowed_str: &'static str, owned_str: String, cow_str: Cow<'a, str>, } impl ToBoundedStatic for Foo<'_> { type Static = Foo<'static>; fn to_static(&self) -> Self::Static { Foo { prim: self.prim.to_static(), borrowed_str: self.borrowed_str.to_static(), owned_str: self.owned_str.to_static(), cow_str: self.cow_str.to_static(), } } } let s = String::new(); let foo = Foo { prim: 0, borrowed_str: "", owned_str: s.clone(), cow_str: Cow::from(&s), }; let to_static = foo.to_static(); ensure_static(to_static); } } #[cfg(feature = "collections")] #[cfg(test)] mod collections_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_binary_heap() { let s = String::new(); let value = BinaryHeap::from([Cow::from(&s)]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_btree_map() { let k = String::from("key"); let v = String::from("value"); let value = BTreeMap::from([(Cow::from(&k), Cow::from(&v))]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_btree_set() { let s = String::new(); let value = BTreeSet::from([Cow::from(&s)]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_linked_list() { let s = String::new(); let value = LinkedList::from([Cow::from(&s)]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_vec_deque() { let s = String::new(); let value = VecDeque::from([Cow::from(&s)]); let to_static = value.to_static(); ensure_static(to_static); } } #[cfg(feature = "std")] #[cfg(test)] mod std_tests { use core::any::Any; use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_hashmap1() { let k = String::from("key"); let v = String::from("value"); let value = std::collections::HashMap::from([(Cow::from(&k), Cow::from(&v))]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_hashmap2() { let k = "key"; let v = String::from("value"); let value = std::collections::HashMap::from([(k, Cow::from(&v))]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_hashmap3() { let k = String::from("key"); let v = 0i16; let value = std::collections::HashMap::from([(Cow::from(&k), v)]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_hashset() { let value = String::from("data"); let value = std::collections::HashSet::from([(Cow::from(&value))]); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_custom_random_state() { #[derive(Clone, Default)] struct RandomState; impl std::hash::BuildHasher for RandomState { type Hasher = std::collections::hash_map::DefaultHasher; fn build_hasher(&self) -> Self::Hasher { std::collections::hash_map::DefaultHasher::default() } } impl ToBoundedStatic for RandomState { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } let k = "key"; let v = 0i16; let value = std::collections::HashMap::<_, _, RandomState>::from_iter([(k, v)]); let to_static = value.to_static(); assert_eq!(value.type_id(), to_static.type_id()); ensure_static(to_static); let value = std::collections::HashSet::<_, RandomState>::from_iter([k]); let to_static = value.to_static(); assert_eq!(value.type_id(), to_static.type_id()); ensure_static(to_static); } } #[cfg(feature = "smol_str")] #[cfg(test)] mod smol_str_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_smol_str() { ensure_static(smol_str::SmolStr::new("smol").to_static()); ensure_static(smol_str::SmolStr::new("smol").into_static()); } } #[cfg(feature = "smallvec")] #[cfg(test)] mod smallvec_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_smallvec1() { let vec: smallvec::SmallVec<[usize; 0]> = smallvec::SmallVec::new(); ensure_static(vec.to_static()); ensure_static(vec.into_static()); } #[test] fn test_smallvec2() { let buf = [1, 2, 3, 4, 5]; let small_vec: smallvec::SmallVec<_> = smallvec::SmallVec::from_buf(buf); ensure_static(small_vec.to_static()); ensure_static(small_vec.into_static()); } } #[cfg(feature = "smartstring")] #[cfg(test)] mod smartstring_tests { use super::*; use smartstring::alias::String; fn ensure_static(t: T) { drop(t); } #[test] fn test_smartstring() { let string = String::from("test"); ensure_static(string.to_static()); ensure_static(string.into_static()); } } #[cfg(feature = "ahash")] #[cfg(test)] mod ahash_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_ahash_random_state() { ensure_static(ahash::RandomState::new().to_static()); } #[cfg(feature = "std")] #[test] fn test_ahash_ahashmap() { let k = String::from("key"); let v = String::from("value"); let value = ahash::AHashMap::from([(Cow::from(&k), Cow::from(&v))]); let to_static = value.to_static(); ensure_static(to_static); } #[cfg(feature = "std")] #[test] fn test_ahash_ahashset() { let value = String::from("data"); let value = ahash::AHashSet::from([(Cow::from(&value))]); let to_static = value.to_static(); ensure_static(to_static); } } #[cfg(feature = "chrono")] #[cfg(test)] mod chrono_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_chrono_datetime() { let value = chrono::Utc::now(); let to_static = value.to_static(); assert_eq!(value, to_static); ensure_static(to_static); } #[test] fn test_chrono_datetime_with_custom_tz() { use chrono::{ DateTime, FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime, Offset, TimeZone, }; #[derive(Debug, Clone)] struct MyOffset; impl Offset for MyOffset { fn fix(&self) -> FixedOffset { FixedOffset::east_opt(1).unwrap() } } #[derive(Clone)] struct MyTz; impl TimeZone for MyTz { type Offset = MyOffset; fn from_offset(_offset: &Self::Offset) -> Self { Self } fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime { MappedLocalTime::None } fn offset_from_local_datetime( &self, _local: &NaiveDateTime, ) -> MappedLocalTime { MappedLocalTime::None } fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Self::Offset { MyOffset } fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Self::Offset { MyOffset } } impl ToBoundedStatic for MyTz { type Static = Self; fn to_static(&self) -> Self::Static { self.clone() } } let value = DateTime::from_timestamp(0, 0).unwrap().with_timezone(&MyTz); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_fixed_offset() { let value = chrono::FixedOffset::east_opt(1).unwrap(); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_months() { let value = chrono::Months::new(1); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_time_delta() { let value = chrono::TimeDelta::days(10); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_utc() { let value = chrono::Utc; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_month() { let value = chrono::Month::January; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_weekday() { let value = chrono::Weekday::Mon; let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_naive_days() { let value = chrono::naive::Days::new(1); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_naive_iso_week() { use chrono::Datelike; let value = chrono::naive::NaiveDate::from_ymd_opt(2024, 6, 1) .unwrap() .iso_week(); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_naive_date() { let value = chrono::naive::NaiveDate::from_ymd_opt(2024, 6, 1).unwrap(); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_naive_date_time() { let value = chrono::naive::NaiveDateTime::new( chrono::NaiveDate::from_ymd_opt(2024, 6, 1).unwrap(), chrono::NaiveTime::from_hms_opt(22, 33, 44).unwrap(), ); let to_static = value.to_static(); ensure_static(to_static); } #[test] fn test_chrono_naive_time() { let value = chrono::naive::NaiveTime::from_hms_opt(22, 33, 44).unwrap(); let to_static = value.to_static(); ensure_static(to_static); } } #[cfg(feature = "chrono-clock")] #[cfg(test)] mod chrono_clock_tests { use super::*; fn ensure_static(t: T) { drop(t); } #[test] fn test_chrono_local() { let value = chrono::Local::now(); let to_static = value.to_static(); ensure_static(to_static); } } bounded-static-bounded-static-0.8.0/deny.toml000066400000000000000000000005641463573033000211740ustar00rootroot00000000000000[licenses] unlicensed = "deny" allow = [ "Apache-2.0", "MPL-2.0", "Unicode-DFS-2016" ] copyleft = "deny" allow-osi-fsf-free = "neither" default = "deny" confidence-threshold = 0.8 exceptions = [] [advisories] db-path = "~/.cargo/advisory-db" db-urls = ["https://github.com/rustsec/advisory-db"] vulnerability = "deny" unmaintained = "deny" yanked = "warn" notice = "warn"