├── bounded-static ├── LICENCE ├── README.md ├── Cargo.toml └── src │ └── lib.rs ├── bounded-static-derive ├── LICENCE ├── Cargo.toml ├── README.md ├── src │ ├── lib.rs │ ├── data_enum.rs │ ├── data_struct.rs │ └── common.rs └── tests │ └── integration_tests.rs ├── .gitignore ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── deny.toml ├── Cargo.toml ├── README.md ├── CHANGELOG.md └── LICENSE /bounded-static/LICENCE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /bounded-static/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /bounded-static-derive/LICENCE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | Cargo.lock 4 | *.DS_Store -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 10 -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | version = 2 3 | allow = [ "Apache-2.0", "MPL-2.0", "Unicode-3.0" ] 4 | confidence-threshold = 0.8 5 | exceptions = [] 6 | 7 | [advisories] 8 | version = 2 9 | db-path = "~/.cargo/advisory-db" 10 | db-urls = ["https://github.com/rustsec/advisory-db"] 11 | -------------------------------------------------------------------------------- /bounded-static-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bounded-static-derive" 3 | description = "Macro to derive ToBoundedStatic and IntoBoundedStatic traits" 4 | readme = "README.md" 5 | version.workspace = true 6 | rust-version.workspace = true 7 | edition.workspace = true 8 | authors.workspace = true 9 | license.workspace = true 10 | keywords.workspace = true 11 | categories.workspace = true 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn.workspace = true 18 | quote.workspace = true 19 | proc-macro2.workspace = true 20 | 21 | [dev-dependencies] 22 | bounded-static = { workspace = true, features = [ "derive" ] } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["bounded-static", "bounded-static-derive"] 4 | 5 | [workspace.package] 6 | version = "0.8.0" 7 | rust-version = "1.68.0" 8 | edition = "2021" 9 | authors = ["FujiApple "] 10 | repository = "https://github.com/fujiapple852/bounded-static" 11 | license = "Apache-2.0" 12 | keywords = ["cow", "static", "bounded", "owned", "derive"] 13 | categories = ["no-std", "rust-patterns", "data-structures", "memory-management"] 14 | 15 | [workspace.dependencies] 16 | bounded-static = { version = "0.8.0", path = "bounded-static" } 17 | bounded-static-derive = { version = "0.8.0", path = "bounded-static-derive" } 18 | syn = { version = "2.0.38", features = [ "full" ] } 19 | quote = "1.0.33" 20 | proc-macro2 = "1.0.69" 21 | test-case = "3.3.1" 22 | smol_str = { version = "0.2.2", default-features = false } 23 | smallvec = { version = "1.13.2", default-features = false } 24 | smartstring = { version = "1.0.1", default-features = false } 25 | ahash = { version = "0.8.11", default-features = false } 26 | chrono = { version = "0.4.38", default-features = false } -------------------------------------------------------------------------------- /bounded-static-derive/README.md: -------------------------------------------------------------------------------- 1 | [![Documentation](https://docs.rs/bounded-static-derive/badge.svg)](https://docs.rs/bounded-static-derive/0.8.0) 2 | [![Crate](https://img.shields.io/crates/v/bounded-static-derive.svg)](https://crates.io/crates/bounded-static-derive/0.8.0) 3 | 4 | # Bounded Static Derive 5 | 6 | This crate provides the `ToStatic` macro which can be used to derive implementations of 7 | the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) and 8 | [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits for all `struct`and `enum` 9 | that can be converted to a form that is bounded by `'static`. 10 | 11 | The `ToStatic` macro should be used via the [`bounded-static`](https://docs.rs/bounded-static/0.8.0/bounded_static) crate rather 12 | than using this crate directly. 13 | 14 | ```yaml 15 | bounded-static = { version = "0.8.0", features = [ "derive" ] } 16 | ``` 17 | 18 | ## License 19 | 20 | `bounded-static-derive` is distributed under the terms of the Apache License (Version 2.0). 21 | 22 | See [LICENSE](LICENSE) for details. 23 | 24 | Copyright 2022 -------------------------------------------------------------------------------- /bounded-static/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bounded-static" 3 | description = "Defines the ToBoundedStatic and IntoBoundedStatic traits" 4 | readme = "README.md" 5 | repository = "https://github.com/fujiapple852/bounded-static" 6 | version.workspace = true 7 | rust-version.workspace = true 8 | edition.workspace = true 9 | authors.workspace = true 10 | license.workspace = true 11 | keywords.workspace = true 12 | categories.workspace = true 13 | 14 | [features] 15 | default = [ "collections", "alloc", "std" ] 16 | 17 | # Enable impls of [To|Into]BoundedStatic for common types in the alloc crate. 18 | alloc = [] 19 | 20 | # Enable impls of [To|Into]BoundedStatic for collections in the alloc crate. 21 | collections = [ "alloc" ] 22 | 23 | # Enable impls of [To|Into]BoundedStatic for other types in std. 24 | std = [ "alloc", "ahash?/std", "chrono?/std" ] 25 | 26 | # Enable the ToStatic custom derive macro. 27 | derive = [ "bounded-static-derive" ] 28 | 29 | # Enable the clock feature for chrono. 30 | chrono-clock = [ "chrono", "chrono/clock" ] 31 | 32 | [dependencies] 33 | bounded-static-derive = { workspace = true, optional = true } 34 | smol_str = { workspace = true, optional = true, default-features = false } 35 | smallvec = { workspace = true, optional = true, default-features = false } 36 | smartstring = { workspace = true, optional = true, default-features = false } 37 | ahash = { workspace = true, optional = true, default-features = false } 38 | chrono = { workspace = true, optional = true, default-features = false } 39 | 40 | [dev-dependencies] 41 | test-case.workspace = true 42 | 43 | [package.metadata.docs.rs] 44 | all-features = true 45 | -------------------------------------------------------------------------------- /bounded-static-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/bounded-static-derive/0.8.0")] 2 | //! Provides the `ToStatic` derive macro. 3 | //! 4 | //! The [`ToStatic`] derive macro implements the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) 5 | //! and [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits for any `struct` 6 | //! and `enum` that can be converted to a form that is bounded by `'static`. 7 | //! 8 | //! The [`ToStatic`] macro should be used via the [`bounded-static`](https://docs.rs/bounded-static/0.8.0) crate 9 | //! rather than using this crate directly. 10 | #![warn(clippy::all, clippy::pedantic, clippy::nursery, rust_2018_idioms)] 11 | #![allow(clippy::redundant_pub_crate, clippy::needless_for_each)] 12 | #![forbid(unsafe_code)] 13 | 14 | use proc_macro2::TokenStream; 15 | use syn::{Data, DataStruct, DeriveInput, Fields}; 16 | 17 | mod common; 18 | mod data_enum; 19 | mod data_struct; 20 | 21 | /// The `ToStatic` derive macro. 22 | /// 23 | /// Generate [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) and 24 | /// [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) impls for the data item deriving 25 | /// `ToStatic`. 26 | #[proc_macro_derive(ToStatic)] 27 | pub fn to_static(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 28 | let input = syn::parse_macro_input!(input as syn::DeriveInput); 29 | proc_macro::TokenStream::from(generate_traits(&input)) 30 | } 31 | 32 | fn generate_traits(input: &DeriveInput) -> TokenStream { 33 | match &input.data { 34 | Data::Struct(DataStruct { 35 | fields: Fields::Named(fields_named), 36 | .. 37 | }) => data_struct::generate_struct_named(&input.ident, &input.generics, fields_named), 38 | Data::Struct(DataStruct { 39 | fields: Fields::Unnamed(fields_unnamed), 40 | .. 41 | }) => data_struct::generate_struct_unnamed(&input.ident, &input.generics, fields_unnamed), 42 | Data::Struct(DataStruct { 43 | fields: Fields::Unit, 44 | .. 45 | }) => data_struct::generate_struct_unit(&input.ident), 46 | Data::Enum(data_enum) => data_enum::generate_enum( 47 | &input.ident, 48 | &input.generics, 49 | data_enum.variants.iter().collect::>().as_slice(), 50 | ), 51 | Data::Union(_) => unimplemented!("union is not yet supported"), 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![ci](https://github.com/fujiapple852/bounded-static/actions/workflows/ci.yml/badge.svg) 2 | [![Documentation](https://docs.rs/bounded-static/badge.svg)](https://docs.rs/bounded-static/0.8.0) 3 | [![Crate](https://img.shields.io/crates/v/bounded-static.svg)](https://crates.io/crates/bounded-static/0.8.0) 4 | 5 | # Bounded Static 6 | This crate defines the [`ToBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.ToBoundedStatic.html) 7 | and [`IntoBoundedStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/trait.IntoBoundedStatic.html) traits, 8 | the [`ToStatic`](https://docs.rs/bounded-static/0.8.0/bounded_static/derive.ToStatic.html) macro and provides impls 9 | for common types. This crate has zero-dependencies, is `no_std` friendly and 10 | forbids `unsafe` code. 11 | 12 | As described in 13 | 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): 14 | 15 | > `T: 'static` should be read as _"`T` is bounded by a `'static` lifetime"_ not _"`T` has a `'static` lifetime"_. 16 | 17 | The traits `ToBoundedStatic` and `IntoBoundedStatic` can be used to convert any suitable `T` and `&T` to an 18 | owned `T` such that `T: 'static`. Both traits define an associated type which is bounded by `'static` and provide a 19 | method to convert to that bounded type. 20 | 21 | The macros `ToStatic` can be used to automatically derive `ToBoundedStatic` and `IntoBoundedStatic` for any `struct` 22 | or `enum` that can be converted to a form that is bounded by `'static`. 23 | 24 | Refer to the crate [`documentation`](https://docs.rs/bounded-static/0.8.0/bounded_static) for details and examples. 25 | 26 | ## FAQ 27 | 28 | ### When is this useful? 29 | 30 | This is useful for data structures which directly or indirectly contain `Cow` types that must be supplied to 31 | a function which requires the `'static` bound (i.e. [`std::thread::spawn`](https://doc.rust-lang.org/std/thread/fn.spawn.html)): 32 | 33 | ```rust 34 | #[derive(Debug, PartialEq, ToStatic)] 35 | struct Foo<'a> { 36 | foo: Cow<'a, str>, 37 | bar: Vec> 38 | } 39 | #[derive(Debug, PartialEq, ToStatic)] 40 | enum Bar<'a> { 41 | First, 42 | Second(Cow<'a, str>), 43 | } 44 | 45 | fn main() { 46 | let value = String::from("data"); 47 | let foo = Foo { 48 | foo: Cow::from(&value), 49 | bar: vec![Bar::First, Bar::Second(Cow::from(&value))] 50 | }; 51 | let foo_static = foo.into_static(); 52 | std::thread::spawn(move || { 53 | assert_eq!(foo_static.foo, "data"); 54 | assert_eq!(foo_static.bar, vec![Bar::First, Bar::Second("data".into())]) 55 | }).join().unwrap(); 56 | } 57 | ``` 58 | 59 | ### How does this differ from the `ToOwned` trait? 60 | 61 | The [`ToOwned`](https://doc.rust-lang.org/std/borrow/trait.ToOwned.html) trait defines an associated type `Owned` which 62 | is not bound by `'static` and therefore the follow will not compile: 63 | 64 | ```rust 65 | use std::borrow::Cow; 66 | 67 | fn main() { 68 | #[derive(Clone)] 69 | struct Foo<'a> { 70 | foo: Cow<'a, str>, 71 | } 72 | 73 | fn ensure_static(_: T) {} 74 | 75 | let s = String::from("data"); 76 | let foo = Foo { foo: Cow::from(&s) }; 77 | ensure_static(foo.to_owned()) 78 | } 79 | ``` 80 | 81 | Results in the following error: 82 | 83 | ``` 84 | error[E0597]: `s` does not live long enough 85 | --> src/lib.rs:12:36 86 | | 87 | 12 | let foo = Foo { foo: Cow::from(&s) }; 88 | | ----------^^- 89 | | | | 90 | | | borrowed value does not live long enough 91 | | argument requires that `s` is borrowed for `'static` 92 | 13 | ensure_static(foo.to_owned()) 93 | 14 | } 94 | | - `s` dropped here while still borrowed 95 | ``` 96 | 97 | Replacing `Clone` with `ToStatic` and using `into_static()` (or `to_static()` as needed) allows the example to compile: 98 | 99 | ```rust 100 | use std::borrow::Cow; 101 | 102 | fn main() { 103 | 104 | #[derive(ToStatic)] 105 | struct Foo<'a> { 106 | foo: Cow<'a, str>, 107 | } 108 | 109 | fn ensure_static(_: T) {} 110 | 111 | let s = String::from("data"); 112 | let foo = Foo { foo: Cow::from(&s) }; 113 | ensure_static(foo.into_static()) 114 | } 115 | ``` 116 | 117 | ## License 118 | 119 | `bounded-static` is distributed under the terms of the Apache License (Version 2.0). 120 | 121 | See [LICENSE](LICENSE) for details. 122 | 123 | Copyright 2022 -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: [ master ] 4 | pull_request: 5 | branches: [ master ] 6 | schedule: 7 | - cron: '00 18 * * *' 8 | 9 | name: Continuous integration 10 | 11 | jobs: 12 | 13 | check: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | rust: [stable, beta, 1.68.0] 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: ${{ matrix.rust }} 24 | override: true 25 | 26 | - name: check --no-default-features 27 | uses: actions-rs/cargo@v1 28 | with: 29 | command: check 30 | args: --workspace --no-default-features 31 | 32 | - name: check --no-default-features --features alloc 33 | uses: actions-rs/cargo@v1 34 | with: 35 | command: check 36 | args: --workspace --no-default-features --features alloc 37 | 38 | - name: check --no-default-features --features collections 39 | uses: actions-rs/cargo@v1 40 | with: 41 | command: check 42 | args: --workspace --no-default-features --features collections 43 | 44 | - name: check --no-default-features --features std 45 | uses: actions-rs/cargo@v1 46 | with: 47 | command: check 48 | args: --workspace --no-default-features --features std 49 | 50 | - name: check --no-default-features --features derive 51 | uses: actions-rs/cargo@v1 52 | with: 53 | command: check 54 | args: --workspace --no-default-features --features derive 55 | 56 | - name: check --no-default-features --features smol_str 57 | uses: actions-rs/cargo@v1 58 | with: 59 | command: check 60 | args: --workspace --no-default-features --features smol_str 61 | 62 | - name: check --no-default-features --features smallvec 63 | uses: actions-rs/cargo@v1 64 | with: 65 | command: check 66 | args: --workspace --no-default-features --features smallvec 67 | 68 | - name: check --no-default-features --features smartstring 69 | uses: actions-rs/cargo@v1 70 | with: 71 | command: check 72 | args: --workspace --no-default-features --features smartstring 73 | 74 | - name: check --no-default-features --features ahash 75 | uses: actions-rs/cargo@v1 76 | with: 77 | command: check 78 | args: --workspace --no-default-features --features ahash 79 | 80 | - name: check --no-default-features --features chrono 81 | uses: actions-rs/cargo@v1 82 | with: 83 | command: check 84 | args: --workspace --no-default-features --features chrono 85 | 86 | - name: check --no-default-features --features chrono-clock 87 | uses: actions-rs/cargo@v1 88 | with: 89 | command: check 90 | args: --workspace --no-default-features --features chrono-clock 91 | 92 | - name: check --all-features 93 | uses: actions-rs/cargo@v1 94 | with: 95 | command: check 96 | args: --workspace --all-features 97 | 98 | build: 99 | runs-on: ubuntu-latest 100 | strategy: 101 | matrix: 102 | rust: [stable, beta, 1.68.0] 103 | steps: 104 | - uses: actions/checkout@v2 105 | - uses: actions-rs/toolchain@v1 106 | with: 107 | profile: minimal 108 | toolchain: ${{ matrix.rust }} 109 | override: true 110 | - name: build --workspace --all-features 111 | uses: actions-rs/cargo@v1 112 | with: 113 | command: build 114 | args: --workspace --all-features 115 | 116 | test: 117 | runs-on: ubuntu-latest 118 | strategy: 119 | matrix: 120 | rust: [ stable, beta, 1.68.0 ] 121 | steps: 122 | - uses: actions/checkout@v2 123 | - uses: actions-rs/toolchain@v1 124 | with: 125 | profile: minimal 126 | toolchain: ${{ matrix.rust }} 127 | override: true 128 | - name: test --workspace --all-features 129 | uses: actions-rs/cargo@v1 130 | with: 131 | command: test 132 | args: --workspace --all-features 133 | 134 | fmt: 135 | runs-on: ubuntu-latest 136 | strategy: 137 | matrix: 138 | rust: [ stable, beta, 1.68.0 ] 139 | steps: 140 | - uses: actions/checkout@v2 141 | - uses: actions-rs/toolchain@v1 142 | with: 143 | profile: minimal 144 | toolchain: ${{ matrix.rust }} 145 | override: true 146 | components: rustfmt 147 | - name: fmt --all -- --check 148 | uses: actions-rs/cargo@v1 149 | with: 150 | command: fmt 151 | args: --all -- --check 152 | 153 | clippy: 154 | runs-on: ubuntu-latest 155 | strategy: 156 | matrix: 157 | rust: [ stable, beta, 1.68.0 ] 158 | steps: 159 | - uses: actions/checkout@v2 160 | - uses: actions-rs/toolchain@v1 161 | with: 162 | profile: minimal 163 | toolchain: ${{ matrix.rust }} 164 | override: true 165 | components: clippy 166 | - name: clippy --workspace --all-features --tests 167 | uses: actions-rs/cargo@v1 168 | with: 169 | command: clippy 170 | args: --workspace --all-features --tests -- -Dwarnings 171 | 172 | cargo-deny: 173 | runs-on: ubuntu-latest 174 | steps: 175 | - uses: actions/checkout@v2 176 | - uses: EmbarkStudios/cargo-deny-action@v1 177 | with: 178 | log-level: warn 179 | command: check 180 | arguments: --all-features -------------------------------------------------------------------------------- /bounded-static-derive/src/data_enum.rs: -------------------------------------------------------------------------------- 1 | use crate::common; 2 | use crate::common::TargetTrait; 3 | use proc_macro2::{Ident, TokenStream}; 4 | use quote::{format_ident, quote}; 5 | use syn::{Fields, FieldsNamed, FieldsUnnamed, Generics, Variant}; 6 | 7 | /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for an `enum` deriving `ToStatic`. 8 | pub(super) fn generate_enum( 9 | name: &Ident, 10 | generics: &Generics, 11 | variants: &[&Variant], 12 | ) -> TokenStream { 13 | variants 14 | .iter() 15 | .for_each(|v| v.fields.iter().for_each(common::check_field)); 16 | let to = generate_enum_to(name, generics, variants); 17 | let into = generate_enum_into(name, generics, variants); 18 | quote!(#to #into) 19 | } 20 | 21 | /// Generate `ToBoundedStatic` for an enum. 22 | fn generate_enum_to(name: &Ident, generics: &Generics, variants: &[&Variant]) -> TokenStream { 23 | let arms = generate_match_arms(name, variants, TargetTrait::ToBoundedStatic); 24 | let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); 25 | let (impl_gens, to_ty_gens, to_where) = gens.split_for_impl(); 26 | let static_gens = common::make_target_generics(generics, TargetTrait::ToBoundedStatic); 27 | quote!( 28 | impl #impl_gens ::bounded_static::ToBoundedStatic for #name #to_ty_gens #to_where { 29 | type Static = #name<#(#static_gens),*>; 30 | fn to_static(&self) -> Self::Static { 31 | match self { 32 | #(#arms),* 33 | } 34 | } 35 | } 36 | ) 37 | } 38 | 39 | /// Generate `IntoBoundedStatic` for an enum. 40 | fn generate_enum_into(name: &Ident, generics: &Generics, variants: &[&Variant]) -> TokenStream { 41 | let arms = generate_match_arms(name, variants, TargetTrait::IntoBoundedStatic); 42 | let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); 43 | let (impl_gens, into_ty_gens, into_where) = gens.split_for_impl(); 44 | let static_gens = common::make_target_generics(generics, TargetTrait::IntoBoundedStatic); 45 | quote!( 46 | impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #into_ty_gens #into_where { 47 | type Static = #name<#(#static_gens),*>; 48 | fn into_static(self) -> Self::Static { 49 | match self { 50 | #(#arms),* 51 | } 52 | } 53 | } 54 | ) 55 | } 56 | 57 | /// Generate a collection of match arms for unit, named and unnamed variants. 58 | /// 59 | /// i.e.: 60 | /// 61 | /// *Unit*: `Foo::Bar => Foo::bar` 62 | /// 63 | /// *Named*: `Foo::Bar { a, b } => Foo::Bar { a: a.to_static(), b: b.to_static() }` 64 | /// 65 | /// *Unnamed*: `Foo::Bar(a, b) => Foo::Bar(a.to_static(), b.to_static())` 66 | fn generate_match_arms( 67 | name: &Ident, 68 | variants: &[&Variant], 69 | target: TargetTrait, 70 | ) -> Vec { 71 | variants 72 | .iter() 73 | .map(|variant| match &variant.fields { 74 | Fields::Unit => generate_variant_unit(name, &variant.ident), 75 | Fields::Named(fields_named) => { 76 | generate_variant_named(name, &variant.ident, fields_named, target) 77 | } 78 | Fields::Unnamed(fields_unnamed) => { 79 | generate_variant_unnamed(name, &variant.ident, fields_unnamed, target) 80 | } 81 | }) 82 | .collect() 83 | } 84 | 85 | /// Generate match arm for an unit variant. 86 | /// 87 | /// i.e. `Foo::Bar => Foo::bar` 88 | fn generate_variant_unit(name: &Ident, variant: &Ident) -> TokenStream { 89 | quote!(#name::#variant => #name::#variant) 90 | } 91 | 92 | /// Generate match arm for a named variant. 93 | /// 94 | /// i.e. `Foo::Bar { a, b } => Foo::Bar { a: a.to_static(), b: b.to_static() }` 95 | fn generate_variant_named( 96 | name: &Ident, 97 | variant: &Ident, 98 | fields_named: &FieldsNamed, 99 | target: TargetTrait, 100 | ) -> TokenStream { 101 | let fields = extract_named_fields(fields_named); 102 | let fields_to_method = generate_named_field_init_method(fields_named, target); 103 | quote!(#name::#variant{ #(#fields),* } => #name::#variant{ #(#fields_to_method),* }) 104 | } 105 | 106 | /// Generate match arm for an unnamed variant. 107 | /// 108 | /// i.e. `Foo::Bar(a, b) => Foo::Bar(a.to_static(), b.to_static())` 109 | fn generate_variant_unnamed( 110 | name: &Ident, 111 | variant: &Ident, 112 | fields_unnamed: &FieldsUnnamed, 113 | target: TargetTrait, 114 | ) -> TokenStream { 115 | let fields = extract_unnamed_fields(fields_unnamed); 116 | let fields_to_method = generate_unnamed_field_init_method(fields_unnamed, target); 117 | quote!(#name::#variant( #(#fields),* ) => #name::#variant( #(#fields_to_method),* )) 118 | } 119 | 120 | /// i.e. `foo: foo.to_static()` 121 | fn generate_named_field_init_method( 122 | fields_named: &FieldsNamed, 123 | target: TargetTrait, 124 | ) -> Vec { 125 | let method = target.method(); 126 | fields_named 127 | .named 128 | .iter() 129 | .map(|f| { 130 | let field_name = f.ident.as_ref().expect("FieldsNamed must have an ident"); 131 | quote!(#field_name: #field_name.#method()) 132 | }) 133 | .collect() 134 | } 135 | 136 | /// i.e. `foo.to_static()` 137 | fn generate_unnamed_field_init_method( 138 | fields_unnamed: &FieldsUnnamed, 139 | target: TargetTrait, 140 | ) -> Vec { 141 | let method = target.method(); 142 | fields_unnamed 143 | .unnamed 144 | .iter() 145 | .enumerate() 146 | .map(|(i, _)| { 147 | let field_name = format_ident!("field_{}", i); 148 | quote!(#field_name.#method()) 149 | }) 150 | .collect() 151 | } 152 | 153 | fn extract_named_fields(fields_named: &FieldsNamed) -> Vec<&Ident> { 154 | fields_named 155 | .named 156 | .iter() 157 | .map(|f| f.ident.as_ref().expect("FieldsNamed must have an ident")) 158 | .collect() 159 | } 160 | 161 | fn extract_unnamed_fields(fields_unnamed: &FieldsUnnamed) -> Vec { 162 | fields_unnamed 163 | .unnamed 164 | .iter() 165 | .enumerate() 166 | .map(|(i, _)| format_ident!("field_{}", i)) 167 | .collect() 168 | } 169 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres 6 | to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [bounded-static-0.8.0] & [bounded-static-derive-0.8.0] - 2024-06-23 9 | 10 | ### Added 11 | 12 | - Added optional support for 3rd party `chrono` crate (by [@xkikeg](https://github.com/xkikeg)) 13 | 14 | This change ([#46](https://github.com/fujiapple852/bounded-static/pull/46)) adds support for types from 15 | the `chrono` crate via the `chrono` and `chrono-clock` feature flags. 16 | 17 | ### Changed 18 | 19 | - Increased MSRV to `1.64` 20 | - Updated `ahash`, `smol_str` and `smallvec` to the latest versions 21 | 22 | ## [bounded-static-0.7.0] & [bounded-static-derive-0.7.0] - 2023-10-25 23 | 24 | ### Changed 25 | 26 | - Increased MSRV to `1.61` 27 | 28 | ## [bounded-static-0.6.0] & [bounded-static-derive-0.6.0] - 2023-10-20 29 | 30 | ### Added 31 | 32 | - Added support for custom `RandomState` and `aHash` (by [@zhu-he](https://github.com/zhu-he)) 33 | 34 | This change ([#62](https://github.com/fujiapple852/bounded-static/pull/62)) adds support for using a 35 | custom `RandomState` with the stdlib `HashMap` and `HashSet` types. 36 | 37 | It also adds support for the 3rd party `AHashMap`, `AHashSet` and `RandomState` types from the `ahash` crate via 38 | the `ahash` feature flag. 39 | 40 | ## [bounded-static-0.5.0] & [bounded-static-derive-0.5.0] - 2023-04-29 41 | 42 | ### Changed 43 | 44 | - Increased MSRV to `1.60` and updated all dependency versions 45 | 46 | ## [bounded-static-0.4.0] & [bounded-static-derive-0.4.0] - 2022-06-08 47 | 48 | ### Added 49 | 50 | - Added support for non-zero integer types (by [@jakoschiko](https://github.com/jakoschiko)) 51 | 52 | ## [bounded-static-0.3.0] & [bounded-static-derive-0.3.0] - 2022-03-10 53 | 54 | ### Added 55 | 56 | - Added support for tuples of up to 12 elements 57 | - Added optional support for 3rd party `smartstring::SmartString` 58 | - Added optional support for 3rd party `smallvec::SmallVec` 59 | - Added optional support for 3rd party `smol_str::SmolStr` 60 | - Added `Result` and `array` to the list of documented blanket implementation 61 | 62 | ### Changed 63 | 64 | - Refactored repo and crate directories to `bounded-static` and `bounded-static-derive` to match crate names 65 | 66 | ### Fixed 67 | 68 | - Fixed broken crate and documentation links 69 | 70 | ## [bounded-static-0.2.1] & [bounded-static-derive-0.2.1] - 2022-02-22 71 | 72 | ### Fixed 73 | 74 | - Fixed broken links to crate documentation 75 | - Fixed broken link to LICENCE file 76 | 77 | ## [bounded-static-0.2.0] & [bounded-static-derive-0.2.0] - 2022-02-22 78 | 79 | ### Added 80 | 81 | - Added support for complex generic bounds on struct and enum in the `ToStatic` derive macro 82 | 83 | > For example, the following `struct` is now supported: 84 | > 85 | > ```rust 86 | > #[derive(ToStatic)] 87 | > struct Baz<'a, T: Foo>(T, Cow<'a, str>) 88 | > where 89 | > T: Into + 'a + Bar; 90 | > ``` 91 | > 92 | > This produces (`ToBoundedStatic` shown, `IntoBoundedStatic` is also produced): 93 | > 94 | > ```rust 95 | > impl<'a, T: Foo + ::bounded_static::ToBoundedStatic> ::bounded_static::ToBoundedStatic for Baz<'a, T> 96 | > where 97 | > T: Into + 'a + Bar + ::bounded_static::ToBoundedStatic, 98 | > T::Static: Foo + Into + 'a + Bar, 99 | > { 100 | > type Static = Baz<'static, T::Static>; 101 | > fn to_static(&self) -> Self::Static { 102 | > Baz(self.0.to_static(), self.1.to_static()) 103 | > } 104 | > } 105 | > ``` 106 | 107 | - Added `ToBoundedStatic` and `IntoBoundedStatic` implementations for the `()` (unit) type 108 | - Added `ToBoundedStatic` and `IntoBoundedStatic` implementations for the `Result` type 109 | - Added doc comments for `ToBoundedStatic` and `IntoBoundedStatic` impls for all primitive types 110 | 111 | ### Fixed 112 | 113 | - Fixed broken links in documentation 114 | - Fixed additional Clippy lints and [lib.rs](https://lib.rs) crates validation errors 115 | 116 | ## [bounded-static-0.1.0] & [bounded-static-derive-0.1.0] - 2022-02-18 117 | 118 | ### Added 119 | 120 | - Initial release of `bounded-static` and `bounded-static-derive` 121 | 122 | [bounded-static-0.8.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.7.0...bounded-static-0.8.0 123 | 124 | [bounded-static-derive-0.8.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.7.0...bounded-static-derive-0.8.0 125 | 126 | [bounded-static-0.7.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.6.0...bounded-static-0.7.0 127 | 128 | [bounded-static-derive-0.7.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.6.0...bounded-static-derive-0.7.0 129 | 130 | [bounded-static-0.6.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.5.0...bounded-static-0.6.0 131 | 132 | [bounded-static-derive-0.6.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.5.0...bounded-static-derive-0.6.0 133 | 134 | [bounded-static-0.5.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.4.0...bounded-static-0.5.0 135 | 136 | [bounded-static-derive-0.5.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.4.0...bounded-static-derive-0.5.0 137 | 138 | [bounded-static-0.4.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.3.0...bounded-static-0.4.0 139 | 140 | [bounded-static-derive-0.4.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.3.0...bounded-static-derive-0.4.0 141 | 142 | [bounded-static-0.3.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.1...bounded-static-0.3.0 143 | 144 | [bounded-static-derive-0.3.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.1...bounded-static-derive-0.3.0 145 | 146 | [bounded-static-0.2.1]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.0...bounded-static-0.2.1 147 | 148 | [bounded-static-derive-0.2.1]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.2.0...bounded-static-derive-0.2.1 149 | 150 | [bounded-static-0.2.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.1.0...bounded-static-0.2.0 151 | 152 | [bounded-static-derive-0.2.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.1.0...bounded-static-derive-0.2.0 153 | 154 | [bounded-static-0.1.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.0.0...bounded-static-0.1.0 155 | 156 | [bounded-static-derive-0.1.0]: https://github.com/fujiapple852/bounded-static/compare/bounded-static-0.0.0...bounded-static-derive-0.1.0 157 | -------------------------------------------------------------------------------- /bounded-static-derive/src/data_struct.rs: -------------------------------------------------------------------------------- 1 | use crate::common; 2 | use crate::common::TargetTrait; 3 | use proc_macro2::{Ident, TokenStream}; 4 | use quote::quote; 5 | use syn::{Field, FieldsNamed, FieldsUnnamed, Generics}; 6 | 7 | /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a `struct` with named fields deriving `ToStatic`. 8 | pub(super) fn generate_struct_named( 9 | name: &Ident, 10 | generics: &Generics, 11 | fields_named: &FieldsNamed, 12 | ) -> TokenStream { 13 | fields_named.named.iter().for_each(common::check_field); 14 | let to = generate_struct_named_to(name, generics, fields_named); 15 | let into = generate_struct_named_into(name, generics, fields_named); 16 | quote!(#to #into) 17 | } 18 | 19 | /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a `struct` with unnamed fields deriving `ToStatic`. 20 | pub(super) fn generate_struct_unnamed( 21 | name: &Ident, 22 | generics: &Generics, 23 | fields_unnamed: &FieldsUnnamed, 24 | ) -> TokenStream { 25 | fields_unnamed.unnamed.iter().for_each(common::check_field); 26 | let to = generate_struct_unnamed_to(name, generics, fields_unnamed); 27 | let into = generate_struct_unnamed_into(name, generics, fields_unnamed); 28 | quote!(#to #into) 29 | } 30 | 31 | /// Generate `ToBoundedStatic` and `IntoBoundedStatic` impls for a unit `struct` deriving `ToStatic`. 32 | pub(super) fn generate_struct_unit(name: &Ident) -> TokenStream { 33 | let to = generate_struct_unit_to(name); 34 | let into = generate_struct_unit_into(name); 35 | quote!(#to #into) 36 | } 37 | 38 | /// Generate `ToBoundedStatic` for a `struct` with with named fields. 39 | fn generate_struct_named_to( 40 | name: &Ident, 41 | generics: &Generics, 42 | fields_named: &FieldsNamed, 43 | ) -> TokenStream { 44 | let fields = make_named_fields_init_methods(fields_named, TargetTrait::ToBoundedStatic); 45 | let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); 46 | let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); 47 | let static_gens = common::make_target_generics(generics, TargetTrait::ToBoundedStatic); 48 | quote!( 49 | impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause { 50 | type Static = #name<#(#static_gens),*>; 51 | fn to_static(&self) -> Self::Static { 52 | #name { 53 | #(#fields),* 54 | } 55 | } 56 | } 57 | ) 58 | } 59 | 60 | /// Generate `IntoBoundedStatic` for a `struct` with with named fields. 61 | fn generate_struct_named_into( 62 | name: &Ident, 63 | generics: &Generics, 64 | fields_named: &FieldsNamed, 65 | ) -> TokenStream { 66 | let fields = make_named_fields_init_methods(fields_named, TargetTrait::IntoBoundedStatic); 67 | let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); 68 | let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); 69 | let static_gens = common::make_target_generics(generics, TargetTrait::IntoBoundedStatic); 70 | quote!( 71 | impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause { 72 | type Static = #name<#(#static_gens),*>; 73 | fn into_static(self) -> Self::Static { 74 | #name { 75 | #(#fields),* 76 | } 77 | } 78 | } 79 | ) 80 | } 81 | 82 | /// Generate `ToBoundedStatic` for a `struct` with unnamed fields. 83 | fn generate_struct_unnamed_to( 84 | name: &Ident, 85 | generics: &Generics, 86 | fields_unnamed: &FieldsUnnamed, 87 | ) -> TokenStream { 88 | let fields = make_unnamed_fields(fields_unnamed, TargetTrait::ToBoundedStatic); 89 | let gens = common::make_bounded_generics(generics, TargetTrait::ToBoundedStatic); 90 | let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); 91 | let static_gens = common::make_target_generics(generics, TargetTrait::ToBoundedStatic); 92 | quote!( 93 | impl #impl_gens ::bounded_static::ToBoundedStatic for #name #ty_gens #where_clause { 94 | type Static = #name<#(#static_gens),*>; 95 | fn to_static(&self) -> Self::Static { 96 | #name ( 97 | #(#fields),* 98 | ) 99 | } 100 | } 101 | ) 102 | } 103 | 104 | /// Generate `IntoBoundedStatic` for a `struct` with unnamed fields. 105 | fn generate_struct_unnamed_into( 106 | name: &Ident, 107 | generics: &Generics, 108 | fields_unnamed: &FieldsUnnamed, 109 | ) -> TokenStream { 110 | let fields = make_unnamed_fields(fields_unnamed, TargetTrait::IntoBoundedStatic); 111 | let gens = common::make_bounded_generics(generics, TargetTrait::IntoBoundedStatic); 112 | let (impl_gens, ty_gens, where_clause) = gens.split_for_impl(); 113 | let static_gens = common::make_target_generics(generics, TargetTrait::IntoBoundedStatic); 114 | quote!( 115 | impl #impl_gens ::bounded_static::IntoBoundedStatic for #name #ty_gens #where_clause { 116 | type Static = #name<#(#static_gens),*>; 117 | fn into_static(self) -> Self::Static { 118 | #name ( 119 | #(#fields),* 120 | ) 121 | } 122 | } 123 | ) 124 | } 125 | 126 | /// Generate `ToBoundedStatic` for unit struct. 127 | fn generate_struct_unit_to(name: &Ident) -> TokenStream { 128 | quote!( 129 | impl ::bounded_static::ToBoundedStatic for #name { 130 | type Static = #name; 131 | fn to_static(&self) -> Self::Static { 132 | #name 133 | } 134 | } 135 | ) 136 | } 137 | 138 | /// Generate `IntoBoundedStatic` for unit struct. 139 | fn generate_struct_unit_into(name: &Ident) -> TokenStream { 140 | quote!( 141 | impl ::bounded_static::IntoBoundedStatic for #name { 142 | type Static = #name; 143 | fn into_static(self) -> Self::Static { 144 | #name 145 | } 146 | } 147 | ) 148 | } 149 | 150 | fn make_named_fields_init_methods( 151 | fields_named: &FieldsNamed, 152 | target: TargetTrait, 153 | ) -> Vec { 154 | fields_named 155 | .named 156 | .iter() 157 | .map(|field| make_named_field_init_method(field, target)) 158 | .collect() 159 | } 160 | 161 | /// i.e. `foo: self.foo.to_static()` 162 | fn make_named_field_init_method(field: &Field, target: TargetTrait) -> TokenStream { 163 | let field_name = field 164 | .ident 165 | .as_ref() 166 | .expect("FieldsNamed field must have an ident"); 167 | let method = target.method(); 168 | quote!(#field_name: self.#field_name.#method()) 169 | } 170 | 171 | fn make_unnamed_fields(fields_unnamed: &FieldsUnnamed, target: TargetTrait) -> Vec { 172 | let fields_to_static: Vec<_> = fields_unnamed 173 | .unnamed 174 | .iter() 175 | .enumerate() 176 | .map(|(i, _)| make_unnamed_field(i, target)) 177 | .collect(); 178 | fields_to_static 179 | } 180 | 181 | /// i.e. `self.0.to_static()` 182 | fn make_unnamed_field(i: usize, target: TargetTrait) -> TokenStream { 183 | let i = syn::Index::from(i); 184 | let method = target.method(); 185 | quote!(self.#i.#method()) 186 | } 187 | -------------------------------------------------------------------------------- /bounded-static-derive/src/common.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::{format_ident, quote}; 3 | use syn::{ 4 | parse_quote, ConstParam, Field, GenericParam, Generics, Ident, Lifetime, PredicateType, Type, 5 | TypeParam, WhereClause, WherePredicate, 6 | }; 7 | 8 | /// The method and trait bound for both traits we will generate. 9 | #[derive(Copy, Clone)] 10 | pub(super) enum TargetTrait { 11 | ToBoundedStatic, 12 | IntoBoundedStatic, 13 | } 14 | 15 | impl TargetTrait { 16 | pub fn method(self) -> Ident { 17 | match self { 18 | Self::ToBoundedStatic => format_ident!("to_static"), 19 | Self::IntoBoundedStatic => format_ident!("into_static"), 20 | } 21 | } 22 | 23 | pub fn bound(self) -> Ident { 24 | match self { 25 | Self::ToBoundedStatic => format_ident!("ToBoundedStatic"), 26 | Self::IntoBoundedStatic => format_ident!("IntoBoundedStatic"), 27 | } 28 | } 29 | } 30 | 31 | /// Check for references which aren't `'static` and panic. 32 | /// 33 | /// # Examples 34 | /// 35 | /// The following `struct` cannot be made static _for all_ lifetimes `'a` (it is only valid for the `'static` lifetime) 36 | /// and so will fail this check: 37 | /// 38 | /// ```compile_fail 39 | /// #[derive(ToStatic)] 40 | /// struct Foo<'a> { 41 | /// bar: &'a str 42 | /// } 43 | /// ``` 44 | /// 45 | /// This `struct` will pass validation as the reference is `'static`: 46 | /// 47 | /// ```rust 48 | /// # use bounded_static::ToStatic; 49 | /// #[derive(ToStatic)] 50 | /// struct Foo { 51 | /// bar: &'static str 52 | /// } 53 | /// ``` 54 | /// 55 | /// This `struct` is will also pass validation as it can be converted to `'static` _for all_ lifetimes `'a`: 56 | /// 57 | /// ```rust 58 | /// # use bounded_static::ToStatic; 59 | /// #[derive(ToStatic)] 60 | /// struct Foo<'a> { 61 | /// bar: std::borrow::Cow<'a, str> 62 | /// } 63 | /// ``` 64 | /// 65 | /// Note that even without this check the compilation will fail if a non-static reference is used, however by 66 | /// performing this check we can issue a more explicit failure message to the developer. 67 | pub(super) fn check_field(field: &Field) { 68 | if let Type::Reference(ty) = &field.ty { 69 | if let Some(Lifetime { ident, .. }) = &ty.lifetime { 70 | #[allow(clippy::manual_assert)] 71 | if *ident != "static" { 72 | panic!( 73 | "non-static references cannot be made static: {:?}", 74 | quote!(#field).to_string() 75 | ) 76 | } 77 | } 78 | } 79 | } 80 | 81 | /// The generic parameters of the `Static` associated type for `TargetTrait`. 82 | /// 83 | /// i.e. `Static = Foo<'static, ::Static>` 84 | pub(super) fn make_target_generics(generics: &Generics, target: TargetTrait) -> Vec { 85 | generics 86 | .params 87 | .iter() 88 | .map(|param| match param { 89 | GenericParam::Type(TypeParam { ident, .. }) => { 90 | let target_bound = target.bound(); 91 | quote!(<#ident as #target_bound>::Static) 92 | } 93 | GenericParam::Lifetime(_) => quote!('static), 94 | GenericParam::Const(ConstParam { ident, .. }) => quote!(#ident), 95 | }) 96 | .collect() 97 | } 98 | 99 | /// Make a `Generics` with generic bounds for `TargetTrait`. 100 | /// 101 | /// # Examples 102 | /// 103 | /// Given the following struct: 104 | /// 105 | /// ```rust 106 | /// # use std::borrow::Cow; 107 | /// struct Baz<'a, T: Into + 'a> { 108 | /// t: T, 109 | /// r: Cow<'a, str>, 110 | /// } 111 | /// ``` 112 | /// 113 | /// We wish to produce (for example for `ToBoundedStatic`, similar for `IntoBoundedStatic`): 114 | /// 115 | /// ```rust 116 | /// # use std::borrow::Cow; 117 | /// # struct Baz<'a, T: Into + 'a> { 118 | /// # t: T, 119 | /// # r: Cow<'a, str>, 120 | /// # } 121 | /// impl<'a, T: Into + 'a + ::bounded_static::ToBoundedStatic> ::bounded_static::ToBoundedStatic for Baz<'a, T> 122 | /// where 123 | /// ::Static: Into + 'a { 124 | /// 125 | /// type Static = Baz<'static, ::Static>; 126 | /// 127 | /// fn to_static(&self) -> Self::Static { 128 | /// Baz { t: self.t.to_static(), r: self.r.to_static() } 129 | /// } 130 | /// } 131 | /// ``` 132 | /// 133 | /// In the above example we can see that `T` has the bound `Into + 'a` and therefore: 134 | /// 135 | /// - Generic parameter `T` has the additional bound `::bounded_static::ToBoundedStatic` 136 | /// - Associated type `T::Static` has the bound of `T`, i.e. `Into + 'a` 137 | pub(super) fn make_bounded_generics(generics: &Generics, target: TargetTrait) -> Generics { 138 | let params = make_bounded_generic_params(generics, target); 139 | let predicates = make_bounded_generic_predicates(generics, target); 140 | let static_predicates = make_static_generic_predicates(generics, target); 141 | let where_items: Vec<_> = predicates.into_iter().chain(static_predicates).collect(); 142 | Generics { 143 | params: parse_quote!(#(#params),*), 144 | where_clause: Some(parse_quote!(where #(#where_items),* )), 145 | ..*generics 146 | } 147 | } 148 | 149 | /// Make generic parameters bound by `TargetTrait`. 150 | /// 151 | /// i.e. given parameter `T: Into` create `T: Into + ::bounded_static::TargetTrait` 152 | fn make_bounded_generic_params(generics: &Generics, target: TargetTrait) -> Vec { 153 | generics 154 | .params 155 | .iter() 156 | .map(|param| match param { 157 | GenericParam::Type(ty) => GenericParam::Type(ty.clone_with_bound(&target.bound())), 158 | other => other.clone(), 159 | }) 160 | .collect() 161 | } 162 | 163 | /// Make generic predicates bound by `TargetTrait`. 164 | /// 165 | /// i.e. given predicate `T: Into` create `T: Into + ::bounded_static::TargetTrait` 166 | fn make_bounded_generic_predicates( 167 | generics: &Generics, 168 | target: TargetTrait, 169 | ) -> Vec { 170 | match generics.where_clause.as_ref() { 171 | Some(WhereClause { predicates, .. }) => predicates 172 | .iter() 173 | .map(|predicate| match predicate { 174 | WherePredicate::Type(ty) => { 175 | WherePredicate::Type(ty.clone_with_bound(&target.bound())) 176 | } 177 | other => other.clone(), 178 | }) 179 | .collect(), 180 | None => vec![], 181 | } 182 | } 183 | 184 | /// Make generic predicates for associated item `T::Static` bound as per `T`. 185 | /// 186 | /// i.e. given: 187 | /// 188 | /// ```rust 189 | /// # trait Foo {} 190 | /// struct Baz> where T: Foo { 191 | /// t: T, 192 | /// } 193 | /// ``` 194 | /// 195 | /// The generated trait impl associated type `Static` must reflect the original generic bounds as well as any 196 | /// additional bounds from a `where` clause. For the example above the associated type bound would be 197 | /// `::Static: Into + Foo`. 198 | fn make_static_generic_predicates(generics: &Generics, target: TargetTrait) -> Vec { 199 | generics 200 | .params 201 | .iter() 202 | .filter_map(|param| match param { 203 | GenericParam::Type(param_ty) => { 204 | let var = ¶m_ty.ident; 205 | let param_ty_bounds = ¶m_ty.bounds; 206 | let target_bound = target.bound(); 207 | match find_predicate(generics.where_clause.as_ref(), var) { 208 | None if param_ty_bounds.is_empty() => None, 209 | None => Some(parse_quote!(<#var as #target_bound>::Static: #param_ty_bounds)), 210 | Some(predicate_ty) => { 211 | let predicate_bounds = &predicate_ty.bounds; 212 | if param_ty_bounds.is_empty() { 213 | Some(parse_quote!(<#var as #target_bound>::Static: #predicate_bounds)) 214 | } else { 215 | Some(parse_quote!(<#var as #target_bound>::Static: #param_ty_bounds + #predicate_bounds)) 216 | } 217 | } 218 | } 219 | } 220 | _ => None, 221 | }) 222 | .collect() 223 | } 224 | 225 | /// Search the given `WhereClause` for a `WherePredicate` which matches the given `Ident`. 226 | fn find_predicate<'a>( 227 | where_clause: Option<&'a WhereClause>, 228 | var: &Ident, 229 | ) -> Option<&'a PredicateType> { 230 | where_clause 231 | .as_ref() 232 | .map(|WhereClause { predicates, .. }| predicates) 233 | .and_then(|predicate| { 234 | predicate.iter().find_map(|p| match p { 235 | WherePredicate::Type(ty) => match &ty.bounded_ty { 236 | Type::Path(path) => path.path.is_ident(var).then_some(ty), 237 | _ => None, 238 | }, 239 | _ => None, 240 | }) 241 | }) 242 | } 243 | 244 | /// Clone and add a bound to a type. 245 | trait CloneWithBound { 246 | fn clone_with_bound(&self, bound: &Ident) -> Self; 247 | } 248 | 249 | /// Clone and add a bound to a `PredicateType` (in a `where` clause). 250 | impl CloneWithBound for PredicateType { 251 | fn clone_with_bound(&self, bound: &Ident) -> Self { 252 | let mut bounded = self.clone(); 253 | bounded.bounds.push(parse_quote!(::bounded_static::#bound)); 254 | bounded 255 | } 256 | } 257 | 258 | /// Clone and add a bound to a `TypeParam`. 259 | impl CloneWithBound for TypeParam { 260 | fn clone_with_bound(&self, bound: &Ident) -> Self { 261 | let mut bounded = self.clone(); 262 | bounded.bounds.push(parse_quote!(::bounded_static::#bound)); 263 | bounded 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | -------------------------------------------------------------------------------- /bounded-static-derive/tests/integration_tests.rs: -------------------------------------------------------------------------------- 1 | use bounded_static::{IntoBoundedStatic, ToBoundedStatic, ToStatic}; 2 | use std::borrow::Cow; 3 | 4 | #[test] 5 | fn test_struct_named_fields_1() { 6 | #[derive(ToStatic)] 7 | struct Foo<'a> { 8 | value: Cow<'a, str>, 9 | } 10 | let value = String::from("value"); 11 | let data = Foo { 12 | value: Cow::from(&value), 13 | }; 14 | let owned = data.to_static(); 15 | ensure_static(owned); 16 | } 17 | 18 | #[test] 19 | fn test_struct_named_fields_2() { 20 | #[derive(ToStatic)] 21 | struct Foo<'a, 'b> { 22 | u8_value: u8, 23 | static_str: &'static str, 24 | owned_str: String, 25 | value: Cow<'a, str>, 26 | bar: Vec>, 27 | } 28 | #[derive(ToStatic)] 29 | struct Bar<'a> { 30 | u8_value: u8, 31 | static_str: &'static str, 32 | owned_str: String, 33 | value: Cow<'a, str>, 34 | } 35 | let value = String::from("value"); 36 | let bar = Bar { 37 | u8_value: 0, 38 | static_str: "", 39 | owned_str: String::from(""), 40 | value: Cow::from(&value), 41 | }; 42 | let data = Foo { 43 | u8_value: 0, 44 | static_str: "", 45 | owned_str: String::from(""), 46 | value: Cow::from(&value), 47 | bar: vec![bar], 48 | }; 49 | let owned = data.to_static(); 50 | ensure_static(owned); 51 | } 52 | 53 | #[test] 54 | fn test_no_generics_or_lifetimes() { 55 | #[derive(ToStatic)] 56 | struct Foo(u32); 57 | let data = Foo(0); 58 | ensure_static(data.to_static()) 59 | } 60 | 61 | #[test] 62 | fn test_struct_named_fields_no_generics() { 63 | #[derive(ToStatic)] 64 | struct Foo { 65 | foo: String, 66 | bar: &'static str, 67 | } 68 | let data = Foo { 69 | foo: String::from("value"), 70 | bar: "test", 71 | }; 72 | let owned = data.to_static(); 73 | ensure_static(owned); 74 | } 75 | 76 | #[test] 77 | fn test_struct_unnamed_fields() { 78 | #[derive(ToStatic)] 79 | struct Foo<'a>(String, Cow<'a, str>, u16, Bar<'a>); 80 | #[derive(ToStatic)] 81 | struct Bar<'a> { 82 | bar: Cow<'a, str>, 83 | } 84 | let value = String::from("value"); 85 | let data = Foo( 86 | String::from("test"), 87 | Cow::from(&value), 88 | 99, 89 | Bar { 90 | bar: Cow::from(&value), 91 | }, 92 | ); 93 | ensure_static(data.to_static()); 94 | } 95 | 96 | #[test] 97 | fn test_struct_unnamed_fields_no_generics() { 98 | #[derive(ToStatic)] 99 | struct Foo(String, &'static str); 100 | let data = Foo(String::from("value"), "test"); 101 | let owned = data.to_static(); 102 | ensure_static(owned); 103 | } 104 | 105 | #[test] 106 | fn test_unit_struct() { 107 | #[derive(ToStatic)] 108 | struct Foo; 109 | let data = Foo; 110 | ensure_static(data.to_static()); 111 | } 112 | 113 | #[test] 114 | fn test_struct_complex_lifetimes() { 115 | #[derive(ToStatic)] 116 | struct Foo<'a, 'b, R, T: 'b> 117 | where 118 | 'b: 'a, 119 | R: 'a, 120 | T: 'a, 121 | { 122 | baz: T, 123 | a: Cow<'a, str>, 124 | b: Cow<'b, str>, 125 | r: R, 126 | } 127 | 128 | let value = String::from("value"); 129 | let data = Foo { 130 | baz: 0isize, 131 | a: Cow::from(&value), 132 | b: Cow::from(&value), 133 | r: "test", 134 | }; 135 | let owned = data.to_static(); 136 | ensure_static(owned); 137 | } 138 | 139 | #[test] 140 | fn test_struct_named_fields_into() { 141 | #[derive(ToStatic)] 142 | struct Foo<'a> { 143 | value: Cow<'a, str>, 144 | } 145 | let value = String::from("value"); 146 | let data = Foo { 147 | value: Cow::from(&value), 148 | }; 149 | let owned = data.into_static(); 150 | ensure_static(owned); 151 | } 152 | 153 | #[test] 154 | fn test_struct_unnamed_fields_into() { 155 | #[derive(ToStatic)] 156 | struct Foo<'a>(String, Cow<'a, str>, u16, Bar<'a>); 157 | #[derive(ToStatic)] 158 | struct Bar<'a> { 159 | bar: Cow<'a, str>, 160 | } 161 | let value = String::from("value"); 162 | let data = Foo( 163 | String::from("test"), 164 | Cow::from(&value), 165 | 99, 166 | Bar { 167 | bar: Cow::from(&value), 168 | }, 169 | ); 170 | ensure_static(data.into_static()); 171 | } 172 | 173 | #[test] 174 | fn test_unit_struct_into() { 175 | #[derive(ToStatic)] 176 | struct Foo; 177 | let data = Foo; 178 | ensure_static(data.into_static()); 179 | } 180 | 181 | #[test] 182 | fn test_enum() { 183 | #[derive(ToStatic)] 184 | enum Foo<'a> { 185 | Unit, 186 | Named { name: String, age: i8 }, 187 | First(Cow<'a, str>, Cow<'a, str>), 188 | Second(Bar<'a>), 189 | Third(i128, bool, &'static str), 190 | } 191 | #[derive(ToStatic)] 192 | struct Bar<'a> { 193 | bar: Cow<'a, str>, 194 | } 195 | let value = String::from("value"); 196 | let unit = Foo::Unit; 197 | ensure_static(unit.to_static()); 198 | let named = Foo::Named { 199 | name: String::from("test"), 200 | age: 10, 201 | }; 202 | ensure_static(named.to_static()); 203 | let first = Foo::First(Cow::from(&value), Cow::from(&value)); 204 | ensure_static(first.to_static()); 205 | let second = Foo::Second(Bar { 206 | bar: Cow::from(&value), 207 | }); 208 | ensure_static(second.to_static()); 209 | let third = Foo::Third(100, true, "test"); 210 | ensure_static(third.to_static()); 211 | } 212 | 213 | #[test] 214 | fn test_enum_into() { 215 | #[derive(ToStatic)] 216 | enum Foo<'a> { 217 | Unit, 218 | Named { name: String, age: i8 }, 219 | First(Cow<'a, str>, Cow<'a, str>), 220 | Second(Bar<'a>), 221 | Third(i128, bool, &'static str), 222 | } 223 | #[derive(ToStatic)] 224 | struct Bar<'a> { 225 | bar: Cow<'a, str>, 226 | } 227 | let value = String::from("value"); 228 | let unit = Foo::Unit; 229 | ensure_static(unit.into_static()); 230 | let named = Foo::Named { 231 | name: String::from("test"), 232 | age: 10, 233 | }; 234 | ensure_static(named.into_static()); 235 | let first = Foo::First(Cow::from(&value), Cow::from(&value)); 236 | ensure_static(first.into_static()); 237 | let second = Foo::Second(Bar { 238 | bar: Cow::from(&value), 239 | }); 240 | ensure_static(second.into_static()); 241 | let third = Foo::Third(100, true, "test"); 242 | ensure_static(third.into_static()); 243 | } 244 | 245 | #[test] 246 | fn test_thread_spawn() { 247 | #[derive(Debug, Eq, PartialEq, ToStatic)] 248 | struct Foo<'a> { 249 | foo: Cow<'a, str>, 250 | bar: Vec>, 251 | } 252 | #[derive(Debug, Eq, PartialEq, ToStatic)] 253 | enum Bar<'a> { 254 | First, 255 | Second(Cow<'a, str>), 256 | } 257 | let value = String::from("data"); 258 | let data = Foo { 259 | foo: Cow::from(&value), 260 | bar: vec![Bar::First, Bar::Second(Cow::from(&value))], 261 | }; 262 | let data_static = data.into_static(); 263 | std::thread::spawn(move || { 264 | assert_eq!(data_static.foo, "data"); 265 | assert_eq!( 266 | data_static.bar, 267 | vec![Bar::First, Bar::Second("data".into())] 268 | ) 269 | }) 270 | .join() 271 | .unwrap(); 272 | } 273 | 274 | #[test] 275 | fn test_const_generics_struct() { 276 | #[derive(ToStatic)] 277 | struct Foo<'a, const N: usize, const M: usize> { 278 | value: Cow<'a, str>, 279 | left: [usize; N], 280 | right: [usize; M], 281 | } 282 | let value = String::from("value"); 283 | let data = Foo { 284 | value: Cow::from(&value), 285 | left: [0], 286 | right: [0, 1, 2], 287 | }; 288 | let owned = data.to_static(); 289 | ensure_static(owned); 290 | } 291 | 292 | #[test] 293 | fn test_const_generics_struct_into() { 294 | #[derive(ToStatic)] 295 | struct Foo<'a, const N: usize, const M: usize, const Q: bool> { 296 | value: Cow<'a, str>, 297 | left: [usize; N], 298 | right: [usize; M], 299 | } 300 | let value = String::from("value"); 301 | let data = Foo::<'_, 1, 3, true> { 302 | value: Cow::from(&value), 303 | left: [0], 304 | right: [0, 1, 2], 305 | }; 306 | let owned = data.into_static(); 307 | ensure_static(owned); 308 | } 309 | 310 | #[test] 311 | fn test_generic_bound_1() { 312 | #[derive(ToStatic)] 313 | struct Baz<'a, T: Into + 'a> { 314 | t: T, 315 | r: Cow<'a, str>, 316 | } 317 | let value = String::from("test"); 318 | let data = Baz { 319 | t: "", 320 | r: Cow::from(&value), 321 | }; 322 | ensure_static(data.to_static()); 323 | } 324 | 325 | #[test] 326 | fn test_generic_bound_2() { 327 | trait Foo {} 328 | trait Bar {} 329 | 330 | impl Foo for String {} 331 | impl Bar for String {} 332 | impl Foo for Cow<'_, T> {} 333 | impl Bar for Cow<'_, T> {} 334 | 335 | #[derive(ToStatic)] 336 | struct Baz { 337 | t: T, 338 | r: R, 339 | } 340 | let value = String::from("test"); 341 | let data = Baz { 342 | t: Cow::from(&value), 343 | r: String::from("test"), 344 | }; 345 | ensure_static(data.to_static()); 346 | } 347 | 348 | #[test] 349 | fn test_generic_bound_3() { 350 | #[derive(ToStatic)] 351 | struct Baz<'a, T: Into>(T, Cow<'a, str>); 352 | let value = String::from("test"); 353 | let data = Baz("", Cow::from(&value)); 354 | ensure_static(data.to_static()); 355 | } 356 | 357 | #[test] 358 | fn test_generic_bound_where_1() { 359 | #[derive(ToStatic)] 360 | struct Baz<'a, T: Foo>(T, Cow<'a, str>) 361 | where 362 | T: Into; 363 | trait Foo {} 364 | impl Foo for &str {} 365 | let value = String::from("test"); 366 | let data = Baz("", Cow::from(&value)); 367 | ensure_static(data.to_static()); 368 | } 369 | 370 | #[test] 371 | fn test_generic_bound_where_2() { 372 | #[derive(ToStatic)] 373 | struct Baz<'a, T: Foo>(T, Cow<'a, str>) 374 | where 375 | T: Into + 'a + Bar; 376 | trait Foo {} 377 | impl Foo for &str {} 378 | trait Bar {} 379 | impl Bar for &str {} 380 | let value = String::from("test"); 381 | let data = Baz("", Cow::from(&value)); 382 | ensure_static(data.into_static()); 383 | } 384 | 385 | #[test] 386 | fn test_generic_fully_qualified_named_struct() { 387 | #[derive(ToStatic)] 388 | struct Foo { 389 | value: T, 390 | } 391 | let value = "test"; 392 | let owned = Foo { value }.to_static(); 393 | ensure_static(owned); 394 | let owned = Foo { value }.into_static(); 395 | ensure_static(owned); 396 | } 397 | 398 | #[test] 399 | fn test_generic_fully_qualified_unnamed_struct() { 400 | #[derive(ToStatic)] 401 | struct Foo(T); 402 | let value = "test"; 403 | let owned = Foo(value).to_static(); 404 | ensure_static(owned); 405 | let owned = Foo(value).into_static(); 406 | ensure_static(owned); 407 | } 408 | 409 | #[test] 410 | fn test_generic_fully_qualified_enum() { 411 | #[derive(ToStatic)] 412 | enum Foo { 413 | First(T), 414 | } 415 | let value = "test"; 416 | let owned = Foo::First(value).to_static(); 417 | ensure_static(owned); 418 | let owned = Foo::First(value).into_static(); 419 | ensure_static(owned); 420 | } 421 | 422 | fn ensure_static(s: S) { 423 | drop(s); 424 | } 425 | -------------------------------------------------------------------------------- /bounded-static/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc(html_root_url = "https://docs.rs/bounded-static/0.8.0")] 2 | //! Provides the [`ToBoundedStatic`] and [`IntoBoundedStatic`] traits and [`ToStatic`] derive macro. 3 | //! 4 | //! 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): 5 | //! 6 | //! > `T: 'static` should be read as "`T` is bounded by a `'static` lifetime" not "`T` has a `'static` lifetime". 7 | //! 8 | //! The traits [`ToBoundedStatic`] and [`IntoBoundedStatic`] can be used to convert any suitable `T` and `&T` to an 9 | //! owned `T` such that `T: 'static`. Both traits define an associated type which is bounded by `'static` and provide 10 | //! a method to convert to that bounded type: 11 | //! 12 | //! ```rust 13 | //! pub trait ToBoundedStatic { 14 | //! type Static: 'static; 15 | //! 16 | //! fn to_static(&self) -> Self::Static; 17 | //! } 18 | //! 19 | //! pub trait IntoBoundedStatic { 20 | //! type Static: 'static; 21 | //! 22 | //! fn into_static(self) -> Self::Static; 23 | //! } 24 | //! ``` 25 | //! 26 | //! Implementations of [`ToBoundedStatic`] and [`IntoBoundedStatic`] are provided for the following `core` types: 27 | //! 28 | //! - [`primitive`](core::primitive) (no-op conversions) 29 | //! - [`array`](array) 30 | //! - [`tuple`](tuple) 31 | //! - [`Option`] 32 | //! - [`Result`] 33 | //! 34 | //! Additional implementations are available by enabling the following features: 35 | //! 36 | //! - `alloc` for common types from the `alloc` crate: 37 | //! - [Cow](https://doc.rust-lang.org/alloc/borrow/enum.Cow.html) 38 | //! - [String](https://doc.rust-lang.org/alloc/string/struct.String.html) 39 | //! - [Vec](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) 40 | //! - [Box](https://doc.rust-lang.org/alloc/boxed/struct.Box.html) 41 | //! 42 | //! - `collections` for all collection types in the `alloc` crate: 43 | //! - [BinaryHeap](https://doc.rust-lang.org/alloc/collections/binary_heap/struct.BinaryHeap.html) 44 | //! - [BTreeMap](https://doc.rust-lang.org/alloc/collections/btree_map/struct.BTreeMap.html) 45 | //! - [BTreeSet](https://doc.rust-lang.org/alloc/collections/btree_set/struct.BTreeSet.html) 46 | //! - [LinkedList](https://doc.rust-lang.org/alloc/collections/linked_list/struct.LinkedList.html) 47 | //! - [VecDeque](https://doc.rust-lang.org/alloc/collections/vec_deque/struct.VecDeque.html) 48 | //! 49 | //! - `std` for additional types from `std`: 50 | //! - [HashMap](https://doc.rust-lang.org/std/collections/struct.HashMap.html) 51 | //! - [HashSet](https://doc.rust-lang.org/std/collections/struct.HashSet.html) 52 | //! - [RandomState](https://doc.rust-lang.org/std/collections/hash_map/struct.RandomState.html) 53 | //! 54 | //! Note that `collections`, `alloc` and `std` are enabled be default. 55 | //! 56 | //! Additional implementations for 3rd party types are available by enabling the following features: 57 | //! 58 | //! - `smol_str` for [`SmolStr`](https://docs.rs/smol_str/0.2.2/smol_str/struct.SmolStr.html) 59 | //! - `smallvec` for [`SmallVec`](https://docs.rs/smallvec/1.13.2/smallvec/struct.SmallVec.html) 60 | //! - `smartstring` for [`SmartString`](https://docs.rs/smartstring/1.0.1/smartstring/index.html) 61 | //! - `ahash` for: 62 | //! - [`RandomState`](https://docs.rs/ahash/0.8.6/ahash/random_state/struct.RandomState.html) 63 | //! - [`AHashMap`](https://docs.rs/ahash/0.8.6/ahash/struct.AHashMap.html) 64 | //! - [`AHashSet`](https://docs.rs/ahash/0.8.6/ahash/struct.AHashSet.html) 65 | //! - `chrono` for: 66 | //! - [`DateTime`](https://docs.rs/chrono/0.4.38/chrono/struct.DateTime.html) 67 | //! - [`FixedOffset`](https://docs.rs/chrono/0.4.38/chrono/struct.FixedOffset.html) 68 | //! - [`Months`](https://docs.rs/chrono/0.4.38/chrono/struct.Months.html) 69 | //! - [`TimeDelta`](https://docs.rs/chrono/0.4.38/chrono/struct.TimeDelta.html) 70 | //! - [`Utc`](https://docs.rs/chrono/0.4.38/chrono/struct.Utc.html) 71 | //! - [`Month`](https://docs.rs/chrono/0.4.38/chrono/enum.Month.html) 72 | //! - [`Weekday`](https://docs.rs/chrono/0.4.38/chrono/enum.Weekday.html) 73 | //! - [`Days`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.Days.html) 74 | //! - [`IsoWeek`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.IsoWeek.html) 75 | //! - [`NaiveDate`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDate.html) 76 | //! - [`NaiveDateTime`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveDateTime.html) 77 | //! - [`NaiveTime`](https://docs.rs/chrono/0.4.38/chrono/naive/struct.NaiveTime.html) 78 | //! - `chrono-clock` for: 79 | //! - [`Local`](https://docs.rs/chrono/0.4.38/chrono/struct.Local.html) 80 | //! 81 | //! # Examples 82 | //! 83 | //! Given a structure which can be borrow or owned and a function which requires its argument is bounded by the 84 | //! `'static` lifetime: 85 | //! 86 | //! ```rust 87 | //! # use std::borrow::Cow; 88 | //! struct Foo<'a> { 89 | //! bar: Cow<'a, str>, 90 | //! baz: Vec>, 91 | //! } 92 | //! 93 | //! fn ensure_static(_: T) {} 94 | //! ``` 95 | //! 96 | //! We can implement [`ToBoundedStatic`] (and [`IntoBoundedStatic`]) for `Foo<'_>`: 97 | //! 98 | //! ```rust 99 | //! # use std::borrow::Cow; 100 | //! # use bounded_static::ToBoundedStatic; 101 | //! struct Foo<'a> { 102 | //! bar: Cow<'a, str>, 103 | //! baz: Vec>, 104 | //! } 105 | //! impl ToBoundedStatic for Foo<'_> { 106 | //! type Static = Foo<'static>; 107 | //! 108 | //! fn to_static(&self) -> Self::Static { 109 | //! Foo { bar: self.bar.to_static(), baz: self.baz.to_static() } 110 | //! } 111 | //! } 112 | //! ``` 113 | //! 114 | //! This allows it to be converted to an owned representation such that it is now bounded by `'static`: 115 | //! 116 | //! ```rust 117 | //! # use std::borrow::Cow; 118 | //! # use bounded_static::ToBoundedStatic; 119 | //! # struct Foo<'a> { 120 | //! # bar: Cow<'a, str>, 121 | //! # baz: Vec>, 122 | //! # } 123 | //! # impl ToBoundedStatic for Foo<'_> { 124 | //! # type Static = Foo<'static>; 125 | //! # 126 | //! # fn to_static(&self) -> Self::Static { 127 | //! # Foo { bar: self.bar.to_static(), baz: self.baz.to_static() } 128 | //! # } 129 | //! # } 130 | //! fn test() { 131 | //! # fn ensure_static(_: T) {} 132 | //! let s = String::from("data"); 133 | //! let foo = Foo { bar: Cow::from(&s), baz: vec![Cow::from(&s)] }; 134 | //! let to_static = foo.to_static(); 135 | //! ensure_static(to_static); 136 | //! } 137 | //! ``` 138 | //! 139 | //! # Derive 140 | //! 141 | //! These traits may be automatically derived for any `struct` or `enum` that can be converted to a form that is 142 | //! bounded by `'static` by using the [`ToStatic`] macro. It support all `struct` flavors (unit, named & unnamed), 143 | //! all `enum` variant flavors (unit, named & unnamed). It does not currently support `union`. 144 | //! 145 | //! To use the [`ToStatic`] macro you must enable the `derive` feature: 146 | //! 147 | //! ```yaml 148 | //! bounded-static = { version = "0.8.0", features = [ "derive" ] } 149 | //! ``` 150 | //! 151 | //! # Examples 152 | //! 153 | //! ```rust 154 | //! # use std::borrow::Cow; 155 | //! # use std::collections::HashMap; 156 | //! # use bounded_static::ToStatic; 157 | //! /// Named field struct 158 | //! #[derive(ToStatic)] 159 | //! struct Foo<'a> { 160 | //! aaa: Cow<'a, str>, 161 | //! bbb: &'static str, 162 | //! ccc: Baz<'a>, 163 | //! } 164 | //! 165 | //! /// Unnamed field struct 166 | //! #[derive(ToStatic)] 167 | //! struct Bar<'a, 'b>(u128, HashMap, Cow<'b, str>>); 168 | //! 169 | //! /// Unit struct 170 | //! #[derive(ToStatic)] 171 | //! struct Qux; 172 | //! 173 | //! #[derive(ToStatic)] 174 | //! enum Baz<'a> { 175 | //! First(String, usize, Vec>), 176 | //! Second { fst: u32, snd: &'static str }, 177 | //! Third, 178 | //! } 179 | //! ``` 180 | #![warn(clippy::all, clippy::pedantic, clippy::nursery, rust_2018_idioms)] 181 | #![allow(clippy::missing_const_for_fn)] 182 | #![forbid(unsafe_code)] 183 | #![no_std] 184 | 185 | #[cfg(feature = "std")] 186 | extern crate std; 187 | 188 | #[cfg(feature = "alloc")] 189 | extern crate alloc; 190 | 191 | use core::num::{ 192 | NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, NonZeroU128, 193 | NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, 194 | }; 195 | 196 | #[cfg(feature = "alloc")] 197 | use alloc::{ 198 | borrow::{Cow, ToOwned}, 199 | boxed::Box, 200 | string::String, 201 | vec::Vec, 202 | }; 203 | 204 | #[cfg(feature = "collections")] 205 | use alloc::collections::{BTreeMap, BTreeSet, BinaryHeap, LinkedList, VecDeque}; 206 | 207 | #[cfg(feature = "derive")] 208 | /// Re-export for the custom derive macro `ToStatic`. 209 | pub use bounded_static_derive::ToStatic; 210 | 211 | /// A trait for converting `&T` to an owned `T` such that `T: 'static`. 212 | /// 213 | /// See the module level documentation for details. 214 | pub trait ToBoundedStatic { 215 | /// The target type is bounded by the `'static` lifetime. 216 | type Static: 'static; 217 | 218 | /// Convert an `&T` to an owned `T` such that `T: 'static`. 219 | #[must_use = "converting is often expensive and is not expected to have side effects"] 220 | fn to_static(&self) -> Self::Static; 221 | } 222 | 223 | /// A trait for converting an owned `T` into an owned `T` such that `T: 'static`. 224 | /// 225 | /// See the module level documentation for details. 226 | pub trait IntoBoundedStatic { 227 | /// The target type is bounded by the `'static` lifetime. 228 | type Static: 'static; 229 | 230 | /// Convert an owned `T` into an owned `T` such that `T: 'static`. 231 | #[must_use = "converting is often expensive and is not expected to have side effects"] 232 | fn into_static(self) -> Self::Static; 233 | } 234 | 235 | /// No-op [`ToBoundedStatic`] impl for converting `&'static str` to `&'static str`. 236 | impl ToBoundedStatic for &'static str { 237 | type Static = &'static str; 238 | 239 | fn to_static(&self) -> Self::Static { 240 | self 241 | } 242 | } 243 | 244 | /// No-op [`IntoBoundedStatic`] impl for converting `&'static str` into `&'static str`. 245 | impl IntoBoundedStatic for &'static str { 246 | type Static = &'static str; 247 | 248 | fn into_static(self) -> Self::Static { 249 | self 250 | } 251 | } 252 | 253 | /// No-op [`ToBoundedStatic`] and [`IntoBoundedStatic`] impls for `Copy` types. 254 | macro_rules! make_copy_impl { 255 | ($id:ty) => { 256 | /// No-op [`ToBoundedStatic`] impl for this `Copy` type. 257 | impl ToBoundedStatic for $id { 258 | type Static = Self; 259 | 260 | fn to_static(&self) -> Self::Static { 261 | *self 262 | } 263 | } 264 | /// No-op [`IntoBoundedStatic`] impl for this `Copy` type. 265 | impl IntoBoundedStatic for $id { 266 | type Static = Self; 267 | 268 | fn into_static(self) -> Self::Static { 269 | self 270 | } 271 | } 272 | }; 273 | } 274 | 275 | make_copy_impl!(bool); 276 | make_copy_impl!(char); 277 | make_copy_impl!(f32); 278 | make_copy_impl!(f64); 279 | make_copy_impl!(usize); 280 | make_copy_impl!(u8); 281 | make_copy_impl!(u16); 282 | make_copy_impl!(u32); 283 | make_copy_impl!(u64); 284 | make_copy_impl!(u128); 285 | make_copy_impl!(isize); 286 | make_copy_impl!(i8); 287 | make_copy_impl!(i16); 288 | make_copy_impl!(i32); 289 | make_copy_impl!(i64); 290 | make_copy_impl!(i128); 291 | make_copy_impl!(NonZeroUsize); 292 | make_copy_impl!(NonZeroU8); 293 | make_copy_impl!(NonZeroU16); 294 | make_copy_impl!(NonZeroU32); 295 | make_copy_impl!(NonZeroU64); 296 | make_copy_impl!(NonZeroU128); 297 | make_copy_impl!(NonZeroIsize); 298 | make_copy_impl!(NonZeroI8); 299 | make_copy_impl!(NonZeroI16); 300 | make_copy_impl!(NonZeroI32); 301 | make_copy_impl!(NonZeroI64); 302 | make_copy_impl!(NonZeroI128); 303 | 304 | /// No-op [`ToBoundedStatic`] impl for unit type `()`. 305 | impl ToBoundedStatic for () { 306 | type Static = (); 307 | 308 | fn to_static(&self) -> Self::Static {} 309 | } 310 | 311 | /// No-op [`IntoBoundedStatic`] impl for unit type `()`. 312 | impl IntoBoundedStatic for () { 313 | type Static = (); 314 | 315 | fn into_static(self) -> Self::Static {} 316 | } 317 | 318 | /// Blanket [`ToBoundedStatic`] impl for converting `Option` to `Option: 'static`. 319 | impl ToBoundedStatic for Option 320 | where 321 | T: ToBoundedStatic, 322 | { 323 | type Static = Option; 324 | 325 | fn to_static(&self) -> Self::Static { 326 | self.as_ref().map(ToBoundedStatic::to_static) 327 | } 328 | } 329 | 330 | /// Blanket [`IntoBoundedStatic`] impl for converting `Option` into `Option: 'static`. 331 | impl IntoBoundedStatic for Option 332 | where 333 | T: IntoBoundedStatic, 334 | { 335 | type Static = Option; 336 | 337 | fn into_static(self) -> Self::Static { 338 | self.map(IntoBoundedStatic::into_static) 339 | } 340 | } 341 | 342 | /// Blanket [`ToBoundedStatic`] impl for converting `Result` to `Result: 'static`. 343 | impl ToBoundedStatic for Result 344 | where 345 | T: ToBoundedStatic, 346 | E: ToBoundedStatic, 347 | { 348 | type Static = Result; 349 | 350 | fn to_static(&self) -> Self::Static { 351 | match self { 352 | Ok(value) => Ok(value.to_static()), 353 | Err(err) => Err(err.to_static()), 354 | } 355 | } 356 | } 357 | 358 | /// Blanket [`IntoBoundedStatic`] impl for converting `Result` into `Result: 'static`. 359 | impl IntoBoundedStatic for Result 360 | where 361 | T: IntoBoundedStatic, 362 | E: IntoBoundedStatic, 363 | { 364 | type Static = Result; 365 | 366 | fn into_static(self) -> Self::Static { 367 | match self { 368 | Ok(value) => Ok(value.into_static()), 369 | Err(err) => Err(err.into_static()), 370 | } 371 | } 372 | } 373 | 374 | /// Blanket [`ToBoundedStatic`] impl for converting `[T; const N: usize]` to `[T; const N: usize]: 'static`. 375 | impl ToBoundedStatic for [T; N] 376 | where 377 | T: ToBoundedStatic, 378 | { 379 | type Static = [T::Static; N]; 380 | 381 | fn to_static(&self) -> Self::Static { 382 | core::array::from_fn(|i| self[i].to_static()) 383 | } 384 | } 385 | 386 | /// Blanket [`IntoBoundedStatic`] impl for converting `[T; const N: usize]` into `[T; const N: usize]: 'static`. 387 | impl IntoBoundedStatic for [T; N] 388 | where 389 | T: IntoBoundedStatic, 390 | { 391 | type Static = [T::Static; N]; 392 | 393 | fn into_static(self) -> Self::Static { 394 | self.map(IntoBoundedStatic::into_static) 395 | } 396 | } 397 | 398 | /// Blanket [`ToBoundedStatic`] impl for converting tuples `(T1, T2, ...)` to `(T1, T2, ..): 'static`. 399 | macro_rules! tuple_to_static { 400 | () => (); 401 | ($($name:ident,)+) => { 402 | tuple_to_static! ( 403 | @gen $($name,)+, 404 | concat!( 405 | "Blanket [`ToBoundedStatic`] impl for converting tuple `", 406 | stringify!(($($name,)+)), "` to `", stringify!(($($name,)+)), ": 'static `" 407 | ) 408 | ); 409 | }; 410 | (@gen $($name:ident,)+, $doc:expr) => { 411 | #[doc = $doc] 412 | impl<$($name: ToBoundedStatic),+> ToBoundedStatic for ($($name,)+) { 413 | type Static = ($($name::Static,)+); 414 | #[allow(non_snake_case)] 415 | fn to_static(&self) -> Self::Static { 416 | let ($(ref $name,)+) = *self; 417 | ($($name.to_static(),)+) 418 | } 419 | } 420 | tuple_to_static! {@peel $($name,)+ } 421 | }; 422 | (@peel $name:ident, $($other:ident,)*) => {tuple_to_static! { $($other,)* }}; 423 | } 424 | 425 | /// Blanket [`IntoBoundedStatic`] impl for converting tuples `(T1, T2, ...)` into `(T1, T2, ..): 'static`. 426 | macro_rules! tuple_into_static { 427 | () => (); 428 | ($($name:ident,)+) => { 429 | tuple_into_static! ( 430 | @gen $($name,)+, 431 | concat!( 432 | "Blanket [`IntoBoundedStatic`] impl for converting tuple `", 433 | stringify!(($($name,)+)), "` into `", stringify!(($($name,)+)), ": 'static `" 434 | ) 435 | ); 436 | }; 437 | (@gen $($name:ident,)+, $doc:expr) => { 438 | #[doc = $doc] 439 | impl<$($name: IntoBoundedStatic),+> IntoBoundedStatic for ($($name,)+) { 440 | type Static = ($($name::Static,)+); 441 | #[allow(non_snake_case)] 442 | fn into_static(self) -> Self::Static { 443 | let ($($name,)+) = self; 444 | ($($name.into_static(),)+) 445 | } 446 | } 447 | tuple_into_static! {@peel $($name,)+ } 448 | }; 449 | (@peel $name:ident, $($other:ident,)*) => {tuple_into_static! { $($other,)* }}; 450 | } 451 | 452 | tuple_to_static! { T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0, } 453 | tuple_into_static! { T11, T10, T9, T8, T7, T6, T5, T4, T3, T2, T1, T0, } 454 | 455 | #[cfg(feature = "alloc")] 456 | /// Blanket [`ToBoundedStatic`] impl for converting `Cow<'a, T: ?Sized>` to `Cow<'static, T: ?Sized>`. 457 | impl ToBoundedStatic for Cow<'_, T> 458 | where 459 | T: 'static + ToOwned + ?Sized, 460 | { 461 | type Static = Cow<'static, T>; 462 | 463 | fn to_static(&self) -> Self::Static { 464 | Cow::Owned(self.clone().into_owned()) 465 | } 466 | } 467 | 468 | #[cfg(feature = "alloc")] 469 | /// Blanket [`IntoBoundedStatic`] impl for converting `Cow<'a, T: ?Sized>` into `Cow<'static, T: ?Sized>`. 470 | impl IntoBoundedStatic for Cow<'_, T> 471 | where 472 | T: 'static + ToOwned + ?Sized, 473 | { 474 | type Static = Cow<'static, T>; 475 | 476 | fn into_static(self) -> Self::Static { 477 | Cow::Owned(self.into_owned()) 478 | } 479 | } 480 | 481 | #[cfg(feature = "alloc")] 482 | /// [`ToBoundedStatic`] impl for `String`. 483 | impl ToBoundedStatic for String { 484 | type Static = Self; 485 | 486 | fn to_static(&self) -> Self::Static { 487 | self.clone() 488 | } 489 | } 490 | 491 | #[cfg(feature = "alloc")] 492 | /// No-op [`IntoBoundedStatic`] impl for `String`. 493 | impl IntoBoundedStatic for String { 494 | type Static = Self; 495 | 496 | fn into_static(self) -> Self::Static { 497 | self 498 | } 499 | } 500 | 501 | #[cfg(feature = "alloc")] 502 | /// Blanket [`ToBoundedStatic`] impl for converting `Vec` to `Vec: 'static`. 503 | impl ToBoundedStatic for Vec 504 | where 505 | T: ToBoundedStatic, 506 | { 507 | type Static = Vec; 508 | 509 | fn to_static(&self) -> Self::Static { 510 | self.iter().map(ToBoundedStatic::to_static).collect() 511 | } 512 | } 513 | 514 | #[cfg(feature = "alloc")] 515 | /// Blanket [`IntoBoundedStatic`] impl for converting `Vec` into `Vec: 'static`. 516 | impl IntoBoundedStatic for Vec 517 | where 518 | T: IntoBoundedStatic, 519 | { 520 | type Static = Vec; 521 | 522 | fn into_static(self) -> Self::Static { 523 | self.into_iter() 524 | .map(IntoBoundedStatic::into_static) 525 | .collect() 526 | } 527 | } 528 | 529 | #[cfg(feature = "collections")] 530 | /// Blanket [`ToBoundedStatic`] impl for converting `BinaryHeap` into `BinaryHeap: 'static`. 531 | impl ToBoundedStatic for BinaryHeap 532 | where 533 | T: ToBoundedStatic, 534 | T::Static: Ord, 535 | { 536 | type Static = BinaryHeap; 537 | 538 | fn to_static(&self) -> Self::Static { 539 | self.iter().map(ToBoundedStatic::to_static).collect() 540 | } 541 | } 542 | 543 | #[cfg(feature = "collections")] 544 | /// Blanket [`IntoBoundedStatic`] impl for converting `BinaryHeap` into `BinaryHeap: 'static`. 545 | impl IntoBoundedStatic for BinaryHeap 546 | where 547 | T: IntoBoundedStatic, 548 | T::Static: Ord, 549 | { 550 | type Static = BinaryHeap; 551 | 552 | fn into_static(self) -> Self::Static { 553 | self.into_iter() 554 | .map(IntoBoundedStatic::into_static) 555 | .collect() 556 | } 557 | } 558 | 559 | #[cfg(feature = "collections")] 560 | /// Blanket [`ToBoundedStatic`] impl for converting `BTreeMap` into `BTreeMap: 'static`. 561 | impl ToBoundedStatic for BTreeMap 562 | where 563 | K: ToBoundedStatic, 564 | K::Static: Ord, 565 | V: ToBoundedStatic, 566 | { 567 | type Static = BTreeMap; 568 | 569 | fn to_static(&self) -> Self::Static { 570 | self.iter() 571 | .map(|(k, v)| (k.to_static(), v.to_static())) 572 | .collect() 573 | } 574 | } 575 | 576 | #[cfg(feature = "collections")] 577 | /// Blanket [`IntoBoundedStatic`] impl for converting `BTreeMap` into `BTreeMap: 'static`. 578 | impl IntoBoundedStatic for BTreeMap 579 | where 580 | K: IntoBoundedStatic, 581 | K::Static: Ord, 582 | V: IntoBoundedStatic, 583 | { 584 | type Static = BTreeMap; 585 | 586 | fn into_static(self) -> Self::Static { 587 | self.into_iter() 588 | .map(|(k, v)| (k.into_static(), v.into_static())) 589 | .collect() 590 | } 591 | } 592 | 593 | #[cfg(feature = "collections")] 594 | /// Blanket [`ToBoundedStatic`] impl for converting `BTreeSet` into `BTreeSet: 'static`. 595 | impl ToBoundedStatic for BTreeSet 596 | where 597 | T: ToBoundedStatic, 598 | T::Static: Ord, 599 | { 600 | type Static = BTreeSet; 601 | 602 | fn to_static(&self) -> Self::Static { 603 | self.iter().map(ToBoundedStatic::to_static).collect() 604 | } 605 | } 606 | 607 | #[cfg(feature = "collections")] 608 | /// Blanket [`IntoBoundedStatic`] impl for converting `BTreeSet` into `BTreeSet: 'static`. 609 | impl IntoBoundedStatic for BTreeSet 610 | where 611 | T: IntoBoundedStatic, 612 | T::Static: Ord, 613 | { 614 | type Static = BTreeSet; 615 | 616 | fn into_static(self) -> Self::Static { 617 | self.into_iter() 618 | .map(IntoBoundedStatic::into_static) 619 | .collect() 620 | } 621 | } 622 | 623 | #[cfg(feature = "collections")] 624 | /// Blanket [`ToBoundedStatic`] impl for converting `LinkedList` into `LinkedList: 'static`. 625 | impl ToBoundedStatic for LinkedList 626 | where 627 | T: ToBoundedStatic, 628 | { 629 | type Static = LinkedList; 630 | 631 | fn to_static(&self) -> Self::Static { 632 | self.iter().map(ToBoundedStatic::to_static).collect() 633 | } 634 | } 635 | 636 | #[cfg(feature = "collections")] 637 | /// Blanket [`IntoBoundedStatic`] impl for converting `LinkedList` into `LinkedList: 'static`. 638 | impl IntoBoundedStatic for LinkedList 639 | where 640 | T: IntoBoundedStatic, 641 | { 642 | type Static = LinkedList; 643 | 644 | fn into_static(self) -> Self::Static { 645 | self.into_iter() 646 | .map(IntoBoundedStatic::into_static) 647 | .collect() 648 | } 649 | } 650 | 651 | #[cfg(feature = "collections")] 652 | /// Blanket [`ToBoundedStatic`] impl for converting `VecDeque` into `VecDeque: 'static`. 653 | impl ToBoundedStatic for VecDeque 654 | where 655 | T: ToBoundedStatic, 656 | { 657 | type Static = VecDeque; 658 | 659 | fn to_static(&self) -> Self::Static { 660 | self.iter().map(ToBoundedStatic::to_static).collect() 661 | } 662 | } 663 | 664 | #[cfg(feature = "collections")] 665 | /// Blanket [`IntoBoundedStatic`] impl for converting `VecDeque` into `VecDeque: 'static`. 666 | impl IntoBoundedStatic for VecDeque 667 | where 668 | T: IntoBoundedStatic, 669 | { 670 | type Static = VecDeque; 671 | 672 | fn into_static(self) -> Self::Static { 673 | self.into_iter() 674 | .map(IntoBoundedStatic::into_static) 675 | .collect() 676 | } 677 | } 678 | 679 | #[cfg(feature = "alloc")] 680 | /// Blanket [`ToBoundedStatic`] impl for converting `Box` to `Box: 'static`. 681 | impl ToBoundedStatic for Box 682 | where 683 | T: ToBoundedStatic, 684 | { 685 | type Static = Box; 686 | 687 | fn to_static(&self) -> Self::Static { 688 | Box::new(self.as_ref().to_static()) 689 | } 690 | } 691 | 692 | #[cfg(feature = "alloc")] 693 | /// Blanket [`IntoBoundedStatic`] impl for converting `Box` into `Box: 'static`. 694 | impl IntoBoundedStatic for Box 695 | where 696 | T: IntoBoundedStatic, 697 | { 698 | type Static = Box; 699 | 700 | fn into_static(self) -> Self::Static { 701 | Box::new((*self).into_static()) 702 | } 703 | } 704 | 705 | #[cfg(feature = "std")] 706 | /// Blanket [`ToBoundedStatic`] impl for converting `HashMap` to `HashMap: 'static`. 707 | impl ToBoundedStatic for std::collections::HashMap 708 | where 709 | K: ToBoundedStatic, 710 | K::Static: Eq + std::hash::Hash, 711 | V: ToBoundedStatic, 712 | S: ToBoundedStatic, 713 | S::Static: std::hash::BuildHasher, 714 | { 715 | type Static = std::collections::HashMap; 716 | 717 | fn to_static(&self) -> Self::Static { 718 | let mut map = std::collections::HashMap::with_capacity_and_hasher( 719 | self.len(), 720 | self.hasher().to_static(), 721 | ); 722 | map.extend(self.iter().map(|(k, v)| (k.to_static(), v.to_static()))); 723 | map 724 | } 725 | } 726 | 727 | #[cfg(feature = "std")] 728 | /// Blanket [`IntoBoundedStatic`] impl for for converting `HashMap` into `HashMap: 'static`. 729 | impl IntoBoundedStatic for std::collections::HashMap 730 | where 731 | K: IntoBoundedStatic, 732 | K::Static: Eq + std::hash::Hash, 733 | V: IntoBoundedStatic, 734 | S: ToBoundedStatic, 735 | S::Static: std::hash::BuildHasher, 736 | { 737 | type Static = std::collections::HashMap; 738 | 739 | fn into_static(self) -> Self::Static { 740 | let mut map = std::collections::HashMap::with_capacity_and_hasher( 741 | self.len(), 742 | self.hasher().to_static(), 743 | ); 744 | map.extend( 745 | self.into_iter() 746 | .map(|(k, v)| (k.into_static(), v.into_static())), 747 | ); 748 | map 749 | } 750 | } 751 | 752 | #[cfg(feature = "std")] 753 | /// Blanket [`ToBoundedStatic`] impl for converting `HashSet` into `HashSet: 'static`. 754 | impl ToBoundedStatic for std::collections::HashSet 755 | where 756 | T: ToBoundedStatic, 757 | T::Static: Eq + std::hash::Hash, 758 | S: ToBoundedStatic, 759 | S::Static: std::hash::BuildHasher, 760 | { 761 | type Static = std::collections::HashSet; 762 | 763 | fn to_static(&self) -> Self::Static { 764 | let mut set = std::collections::HashSet::with_capacity_and_hasher( 765 | self.len(), 766 | self.hasher().to_static(), 767 | ); 768 | set.extend(self.iter().map(ToBoundedStatic::to_static)); 769 | set 770 | } 771 | } 772 | 773 | #[cfg(feature = "std")] 774 | /// Blanket [`IntoBoundedStatic`] impl for converting `HashSet` into `HashSet: 'static`. 775 | impl IntoBoundedStatic for std::collections::HashSet 776 | where 777 | T: IntoBoundedStatic, 778 | T::Static: Eq + std::hash::Hash, 779 | S: ToBoundedStatic, 780 | S::Static: std::hash::BuildHasher, 781 | { 782 | type Static = std::collections::HashSet; 783 | 784 | fn into_static(self) -> Self::Static { 785 | let mut set = std::collections::HashSet::with_capacity_and_hasher( 786 | self.len(), 787 | self.hasher().to_static(), 788 | ); 789 | set.extend(self.into_iter().map(IntoBoundedStatic::into_static)); 790 | set 791 | } 792 | } 793 | 794 | #[cfg(feature = "std")] 795 | /// [`ToBoundedStatic`] impl for `std::collections::hash_map::RandomState`. 796 | impl ToBoundedStatic for std::collections::hash_map::RandomState { 797 | type Static = Self; 798 | 799 | fn to_static(&self) -> Self::Static { 800 | self.clone() 801 | } 802 | } 803 | 804 | /// [`ToBoundedStatic`] impl for `smol_str::SmolStr`. 805 | #[cfg(feature = "smol_str")] 806 | impl ToBoundedStatic for smol_str::SmolStr { 807 | type Static = Self; 808 | 809 | fn to_static(&self) -> Self::Static { 810 | self.clone() 811 | } 812 | } 813 | 814 | /// No-op [`IntoBoundedStatic`] impl for `smol_str::SmolStr`. 815 | #[cfg(feature = "smol_str")] 816 | impl IntoBoundedStatic for smol_str::SmolStr { 817 | type Static = Self; 818 | 819 | fn into_static(self) -> Self::Static { 820 | self 821 | } 822 | } 823 | 824 | /// [`ToBoundedStatic`] impl for `smallvec::SmallVec`. 825 | #[cfg(feature = "smallvec")] 826 | impl ToBoundedStatic for smallvec::SmallVec 827 | where 828 | A: smallvec::Array + ToBoundedStatic, 829 | T: ToBoundedStatic, 830 | ::Static: smallvec::Array, 831 | { 832 | type Static = smallvec::SmallVec; 833 | 834 | fn to_static(&self) -> Self::Static { 835 | self.iter().map(ToBoundedStatic::to_static).collect() 836 | } 837 | } 838 | 839 | /// [`IntoBoundedStatic`] impl for `smallvec::SmallVec`. 840 | #[cfg(feature = "smallvec")] 841 | impl IntoBoundedStatic for smallvec::SmallVec 842 | where 843 | A: smallvec::Array + IntoBoundedStatic, 844 | T: IntoBoundedStatic, 845 | ::Static: smallvec::Array, 846 | { 847 | type Static = smallvec::SmallVec; 848 | 849 | fn into_static(self) -> Self::Static { 850 | self.into_iter() 851 | .map(IntoBoundedStatic::into_static) 852 | .collect() 853 | } 854 | } 855 | 856 | /// [`ToBoundedStatic`] impl for `smartstring::SmartString`. 857 | #[cfg(feature = "smartstring")] 858 | impl ToBoundedStatic for smartstring::SmartString 859 | where 860 | Mode: smartstring::SmartStringMode + 'static, 861 | { 862 | type Static = Self; 863 | 864 | fn to_static(&self) -> Self::Static { 865 | self.clone() 866 | } 867 | } 868 | 869 | /// No-op [`IntoBoundedStatic`] impl for `smartstring::SmartString`. 870 | #[cfg(feature = "smartstring")] 871 | impl IntoBoundedStatic for smartstring::SmartString 872 | where 873 | Mode: smartstring::SmartStringMode + 'static, 874 | { 875 | type Static = Self; 876 | 877 | fn into_static(self) -> Self::Static { 878 | self 879 | } 880 | } 881 | 882 | #[cfg(feature = "ahash")] 883 | /// [`ToBoundedStatic`] impl for `ahash::RandomState`. 884 | impl ToBoundedStatic for ahash::RandomState { 885 | type Static = Self; 886 | 887 | fn to_static(&self) -> Self::Static { 888 | self.clone() 889 | } 890 | } 891 | 892 | #[cfg(all(feature = "ahash", feature = "std"))] 893 | /// Blanket [`ToBoundedStatic`] impl for converting `ahash::AHashMap` to `ahash::AHashMap: 'static`. 894 | impl ToBoundedStatic for ahash::AHashMap 895 | where 896 | K: ToBoundedStatic, 897 | K::Static: Eq + std::hash::Hash, 898 | V: ToBoundedStatic, 899 | S: ToBoundedStatic, 900 | S::Static: std::hash::BuildHasher, 901 | { 902 | type Static = ahash::AHashMap; 903 | 904 | fn to_static(&self) -> Self::Static { 905 | let mut map = 906 | ahash::AHashMap::with_capacity_and_hasher(self.len(), self.hasher().to_static()); 907 | map.extend(self.iter().map(|(k, v)| (k.to_static(), v.to_static()))); 908 | map 909 | } 910 | } 911 | 912 | #[cfg(all(feature = "ahash", feature = "std"))] 913 | /// Blanket [`IntoBoundedStatic`] impl for converting `ahash::AHashMap` into `ahash::AHashMap: 'static`. 914 | impl IntoBoundedStatic for ahash::AHashMap 915 | where 916 | K: IntoBoundedStatic, 917 | K::Static: Eq + std::hash::Hash, 918 | V: IntoBoundedStatic, 919 | S: ToBoundedStatic, 920 | S::Static: std::hash::BuildHasher, 921 | { 922 | type Static = ahash::AHashMap; 923 | 924 | fn into_static(self) -> Self::Static { 925 | let mut map = 926 | ahash::AHashMap::with_capacity_and_hasher(self.len(), self.hasher().to_static()); 927 | map.extend( 928 | self.into_iter() 929 | .map(|(k, v)| (k.into_static(), v.into_static())), 930 | ); 931 | map 932 | } 933 | } 934 | 935 | #[cfg(all(feature = "ahash", feature = "std"))] 936 | /// Blanket [`ToBoundedStatic`] impl for converting `ahash::AHashSet` to `ahash::AHashSet: 'static`. 937 | impl ToBoundedStatic for ahash::AHashSet 938 | where 939 | T: ToBoundedStatic, 940 | T::Static: Eq + std::hash::Hash, 941 | S: ToBoundedStatic, 942 | S::Static: std::hash::BuildHasher, 943 | { 944 | type Static = ahash::AHashSet; 945 | 946 | fn to_static(&self) -> Self::Static { 947 | let mut set = 948 | ahash::AHashSet::with_capacity_and_hasher(self.len(), self.hasher().to_static()); 949 | set.extend(self.iter().map(ToBoundedStatic::to_static)); 950 | set 951 | } 952 | } 953 | 954 | #[cfg(all(feature = "ahash", feature = "std"))] 955 | /// Blanket [`IntoBoundedStatic`] impl for converting `ahash::AHashSet` into `ahash::AHashSet: 'static`. 956 | impl IntoBoundedStatic for ahash::AHashSet 957 | where 958 | T: IntoBoundedStatic, 959 | T::Static: Eq + std::hash::Hash, 960 | S: ToBoundedStatic, 961 | S::Static: std::hash::BuildHasher, 962 | { 963 | type Static = ahash::AHashSet; 964 | 965 | fn into_static(self) -> Self::Static { 966 | let mut set = 967 | ahash::AHashSet::with_capacity_and_hasher(self.len(), self.hasher().to_static()); 968 | set.extend(self.into_iter().map(IntoBoundedStatic::into_static)); 969 | set 970 | } 971 | } 972 | 973 | #[cfg(feature = "chrono")] 974 | /// Blanket [`ToBoundedStatic`] impl for converting `chrono::DateTime` into `chrono::DateTime: 'static`. 975 | impl ToBoundedStatic for chrono::DateTime 976 | where 977 | Tz: ToBoundedStatic + chrono::TimeZone, 978 | Tz::Static: chrono::TimeZone, 979 | { 980 | type Static = chrono::DateTime; 981 | 982 | fn to_static(&self) -> Self::Static { 983 | self.with_timezone(&self.timezone().to_static()) 984 | } 985 | } 986 | 987 | #[cfg(feature = "chrono")] 988 | /// Blanket [`IntoBoundedStatic`] impl for converting `chrono::DateTime` into `chrono::DateTime: 'static`. 989 | impl IntoBoundedStatic for chrono::DateTime 990 | where 991 | Tz: IntoBoundedStatic + chrono::TimeZone, 992 | Tz::Static: chrono::TimeZone, 993 | { 994 | type Static = chrono::DateTime; 995 | 996 | fn into_static(self) -> Self::Static { 997 | self.with_timezone(&self.timezone().into_static()) 998 | } 999 | } 1000 | 1001 | #[cfg(feature = "chrono")] 1002 | make_copy_impl!(chrono::FixedOffset); 1003 | #[cfg(feature = "chrono")] 1004 | make_copy_impl!(chrono::Months); 1005 | #[cfg(feature = "chrono")] 1006 | make_copy_impl!(chrono::TimeDelta); 1007 | #[cfg(feature = "chrono")] 1008 | make_copy_impl!(chrono::Utc); 1009 | #[cfg(feature = "chrono")] 1010 | make_copy_impl!(chrono::Month); 1011 | #[cfg(feature = "chrono")] 1012 | make_copy_impl!(chrono::Weekday); 1013 | #[cfg(feature = "chrono")] 1014 | make_copy_impl!(chrono::naive::Days); 1015 | #[cfg(feature = "chrono")] 1016 | make_copy_impl!(chrono::naive::IsoWeek); 1017 | #[cfg(feature = "chrono")] 1018 | make_copy_impl!(chrono::naive::NaiveDate); 1019 | #[cfg(feature = "chrono")] 1020 | make_copy_impl!(chrono::naive::NaiveDateTime); 1021 | #[cfg(feature = "chrono")] 1022 | make_copy_impl!(chrono::naive::NaiveTime); 1023 | #[cfg(feature = "chrono-clock")] 1024 | make_copy_impl!(chrono::Local); 1025 | // No implementation for chrono::NaiveWeek as it's not Copy nor Clone. 1026 | 1027 | #[cfg(test)] 1028 | mod core_tests { 1029 | use super::*; 1030 | use test_case::test_case; 1031 | 1032 | fn ensure_static(t: T) { 1033 | drop(t); 1034 | } 1035 | 1036 | #[test_case(false; "bool")] 1037 | #[test_case('a'; "char")] 1038 | #[test_case(0.0f32; "f32")] 1039 | #[test_case(0.0f64; "f64")] 1040 | #[test_case(0usize; "usize")] 1041 | #[test_case(0u8; "u8")] 1042 | #[test_case(0u16; "u16")] 1043 | #[test_case(0u32; "u32")] 1044 | #[test_case(0u64; "u64")] 1045 | #[test_case(0u128; "u128")] 1046 | #[test_case(0isize; "isize")] 1047 | #[test_case(0i8; "i8")] 1048 | #[test_case(0i16; "i16")] 1049 | #[test_case(0i32; "i32")] 1050 | #[test_case(0i64; "i64")] 1051 | #[test_case(0i128; "i128")] 1052 | #[allow(clippy::needless_pass_by_value)] 1053 | fn test_primitive(t: T) { 1054 | ensure_static(t.to_static()); 1055 | } 1056 | 1057 | #[test_case(NonZeroUsize::new(1); "usize")] 1058 | #[test_case(NonZeroU8::new(1); "u8")] 1059 | #[test_case(NonZeroU16::new(1); "u16")] 1060 | #[test_case(NonZeroU32::new(1); "u32")] 1061 | #[test_case(NonZeroU64::new(1); "u64")] 1062 | #[test_case(NonZeroU128::new(1); "u128")] 1063 | #[test_case(NonZeroIsize::new(1); "isize")] 1064 | #[test_case(NonZeroI8::new(1); "i8")] 1065 | #[test_case(NonZeroI16::new(1); "i16")] 1066 | #[test_case(NonZeroI32::new(1); "i32")] 1067 | #[test_case(NonZeroI64::new(1); "i64")] 1068 | #[test_case(NonZeroI128::new(1); "i128")] 1069 | #[allow(clippy::needless_pass_by_value)] 1070 | fn test_non_zero(t: T) { 1071 | ensure_static(t.to_static()); 1072 | } 1073 | 1074 | #[test] 1075 | fn test_unit() { 1076 | #[allow(clippy::unit_arg)] 1077 | ensure_static(().to_static()); 1078 | } 1079 | 1080 | #[test] 1081 | fn test_str() { 1082 | let s = ""; 1083 | let to_static = s.to_static(); 1084 | ensure_static(to_static); 1085 | } 1086 | 1087 | #[test] 1088 | fn test_option_none() { 1089 | let value: Option = None; 1090 | let to_static = value.to_static(); 1091 | ensure_static(to_static); 1092 | } 1093 | 1094 | #[test] 1095 | fn test_option_some() { 1096 | let value: Option = Some(32); 1097 | let to_static = value.to_static(); 1098 | ensure_static(to_static); 1099 | } 1100 | 1101 | #[test] 1102 | fn test_result() { 1103 | #[derive(Clone)] 1104 | struct MyError; 1105 | #[allow(clippy::unnecessary_wraps)] 1106 | fn foo_ok() -> Result<(), MyError> { 1107 | Ok(()) 1108 | } 1109 | #[allow(clippy::unnecessary_wraps)] 1110 | fn foo_err() -> Result<(), MyError> { 1111 | Err(MyError) 1112 | } 1113 | impl ToBoundedStatic for MyError { 1114 | type Static = Self; 1115 | 1116 | fn to_static(&self) -> Self::Static { 1117 | self.clone() 1118 | } 1119 | } 1120 | let ok_result = foo_ok(); 1121 | ensure_static(ok_result.to_static()); 1122 | assert!(ok_result.is_ok()); 1123 | let err_result = foo_err(); 1124 | ensure_static(err_result.to_static()); 1125 | assert!(err_result.is_err()); 1126 | } 1127 | 1128 | #[test] 1129 | fn test_array() { 1130 | let arr = ["test"]; 1131 | ensure_static(arr.to_static()); 1132 | } 1133 | 1134 | #[test] 1135 | fn test_tuple2() { 1136 | let tuple = ("test", 32); 1137 | ensure_static(tuple.to_static()); 1138 | } 1139 | 1140 | #[test] 1141 | fn test_tuple11() { 1142 | let tuple = ( 1143 | (), 1144 | '1', 1145 | "2", 1146 | 3_i32, 1147 | 4_usize, 1148 | 5_isize, 1149 | 6.0_f64, 1150 | ["7"], 1151 | Some(8), 1152 | 9, 1153 | (10,), 1154 | false, 1155 | ); 1156 | ensure_static(tuple.to_static()); 1157 | } 1158 | } 1159 | 1160 | #[cfg(feature = "alloc")] 1161 | #[cfg(test)] 1162 | mod alloc_tests { 1163 | use super::*; 1164 | 1165 | fn ensure_static(t: T) { 1166 | drop(t); 1167 | } 1168 | 1169 | #[test] 1170 | fn test_string() { 1171 | let s = String::new(); 1172 | let to_static = s.to_static(); 1173 | ensure_static(to_static); 1174 | } 1175 | 1176 | #[test] 1177 | fn test_cow_borrowed_str() { 1178 | let s = String::new(); 1179 | let to_static = Cow::from(&s).to_static(); 1180 | ensure_static(to_static); 1181 | } 1182 | 1183 | #[test] 1184 | fn test_cow_owned_string() { 1185 | let s = String::new(); 1186 | let to_static = Cow::from(s).to_static(); 1187 | ensure_static(to_static); 1188 | } 1189 | 1190 | #[test] 1191 | fn test_cow_to_static() { 1192 | let s = String::new(); 1193 | let s_cow: Cow<'_, str> = Cow::Borrowed(&s); 1194 | let s1_cow_owned: Cow<'_, str> = s_cow.to_static(); 1195 | let s2_cow_owned: Cow<'_, str> = Cow::Owned(s_cow.into_owned()); 1196 | assert_eq!(s1_cow_owned, s2_cow_owned); 1197 | } 1198 | 1199 | #[test] 1200 | fn test_cow_into_static() { 1201 | let s = String::new(); 1202 | let s_cow: Cow<'_, str> = Cow::Borrowed(&s); 1203 | let s1_cow_owned: Cow<'_, str> = s_cow.clone().into_static(); 1204 | let s2_cow_owned: Cow<'_, str> = Cow::Owned(s_cow.into_owned()); 1205 | assert_eq!(s1_cow_owned, s2_cow_owned); 1206 | } 1207 | 1208 | #[test] 1209 | fn test_option_none() { 1210 | let value: Option> = None; 1211 | let to_static = value.to_static(); 1212 | ensure_static(to_static); 1213 | } 1214 | 1215 | #[test] 1216 | fn test_option_some() { 1217 | let s = String::new(); 1218 | let value = Some(Cow::from(&s)); 1219 | let to_static = value.to_static(); 1220 | ensure_static(to_static); 1221 | } 1222 | 1223 | #[test] 1224 | fn test_array() { 1225 | let arr = ["test"]; 1226 | ensure_static(arr.to_static()); 1227 | } 1228 | 1229 | #[test] 1230 | fn test_array_into() { 1231 | let s = String::new(); 1232 | let arr = [Cow::from(&s)]; 1233 | ensure_static(arr.into_static()); 1234 | } 1235 | 1236 | #[test] 1237 | fn test_vec1() { 1238 | let s = String::new(); 1239 | let value = alloc::vec![Cow::from(&s)]; 1240 | let to_static = value.to_static(); 1241 | ensure_static(to_static); 1242 | } 1243 | 1244 | #[test] 1245 | fn test_vec2() { 1246 | let s = String::new(); 1247 | let value = alloc::vec![Cow::from(&s), Cow::from(s.as_str())]; 1248 | let to_static = value.to_static(); 1249 | ensure_static(to_static); 1250 | } 1251 | 1252 | #[test] 1253 | fn test_box() { 1254 | let s = String::new(); 1255 | let value = Box::new(s); 1256 | let to_static = value.to_static(); 1257 | ensure_static(to_static); 1258 | } 1259 | 1260 | #[test] 1261 | fn test_box_cow() { 1262 | let s = String::new(); 1263 | let value = Box::new(Cow::from(&s)); 1264 | let to_static = value.to_static(); 1265 | ensure_static(to_static); 1266 | } 1267 | 1268 | #[test] 1269 | fn test_box_vec_cow() { 1270 | let s = String::new(); 1271 | let value = Box::new(alloc::vec![Cow::from(&s)]); 1272 | let to_static = value.to_static(); 1273 | ensure_static(to_static); 1274 | } 1275 | 1276 | #[test] 1277 | fn test_vec_box_cow() { 1278 | let s = String::new(); 1279 | let value = alloc::vec![Box::new(Cow::from(&s))]; 1280 | let to_static = value.to_static(); 1281 | ensure_static(to_static); 1282 | } 1283 | 1284 | #[test] 1285 | fn test_cow_box() { 1286 | let s = String::new(); 1287 | let boxed = Box::new(s); 1288 | let value = Cow::Borrowed(&boxed); 1289 | let to_static = value.to_static(); 1290 | ensure_static(to_static); 1291 | } 1292 | 1293 | #[test] 1294 | fn test_cow_struct() { 1295 | #[derive(Copy, Clone)] 1296 | struct Foo {} 1297 | impl ToBoundedStatic for Foo { 1298 | type Static = Self; 1299 | 1300 | fn to_static(&self) -> Self::Static { 1301 | *self 1302 | } 1303 | } 1304 | let foo = Foo {}; 1305 | let value = Cow::Borrowed(&foo); 1306 | let to_static = value.to_static(); 1307 | ensure_static(to_static); 1308 | } 1309 | 1310 | #[test] 1311 | fn test_cow_struct_of_cow() { 1312 | #[derive(Clone)] 1313 | struct Foo<'a> { 1314 | foo: Cow<'a, str>, 1315 | } 1316 | impl ToBoundedStatic for Foo<'_> { 1317 | type Static = Foo<'static>; 1318 | 1319 | fn to_static(&self) -> Self::Static { 1320 | Foo { 1321 | foo: self.foo.to_static(), 1322 | } 1323 | } 1324 | } 1325 | let s = String::new(); 1326 | let foo = Foo { foo: Cow::from(&s) }; 1327 | let value = Cow::Borrowed(&foo); 1328 | // TODO need to `into_owned()` here 1329 | let to_static = value.into_owned().to_static(); 1330 | ensure_static(to_static); 1331 | } 1332 | 1333 | #[test] 1334 | fn test_cow_cow() { 1335 | let s = String::new(); 1336 | let value1: Cow<'_, str> = Cow::Borrowed(&s); 1337 | let value2: Cow<'_, Cow<'_, str>> = Cow::Borrowed(&value1); 1338 | // TODO need to `into_owned()` here 1339 | let to_static = value2.into_owned().to_static(); 1340 | ensure_static(to_static); 1341 | } 1342 | 1343 | #[test] 1344 | fn test_struct_cow_borrowed_str() { 1345 | struct Foo<'a> { 1346 | foo: Cow<'a, str>, 1347 | } 1348 | impl ToBoundedStatic for Foo<'_> { 1349 | type Static = Foo<'static>; 1350 | 1351 | fn to_static(&self) -> Self::Static { 1352 | Foo { 1353 | foo: self.foo.to_static(), 1354 | } 1355 | } 1356 | } 1357 | let s = String::new(); 1358 | let foo = Foo { foo: Cow::from(&s) }; 1359 | let to_static = foo.to_static(); 1360 | ensure_static(to_static); 1361 | } 1362 | 1363 | #[test] 1364 | fn test_struct_cow_owned_string() { 1365 | struct Foo<'a> { 1366 | foo: Cow<'a, str>, 1367 | } 1368 | impl ToBoundedStatic for Foo<'_> { 1369 | type Static = Foo<'static>; 1370 | 1371 | fn to_static(&self) -> Self::Static { 1372 | Foo { 1373 | foo: self.foo.to_static(), 1374 | } 1375 | } 1376 | } 1377 | let s = String::new(); 1378 | let foo = Foo { foo: Cow::from(s) }; 1379 | let to_static = foo.to_static(); 1380 | ensure_static(to_static); 1381 | } 1382 | 1383 | #[test] 1384 | fn test_struct_multi() { 1385 | #[derive(Clone)] 1386 | struct Foo<'a> { 1387 | bar: Cow<'a, str>, 1388 | baz: Vec>, 1389 | } 1390 | impl ToBoundedStatic for Foo<'_> { 1391 | type Static = Foo<'static>; 1392 | 1393 | fn to_static(&self) -> Self::Static { 1394 | Foo { 1395 | bar: self.bar.to_static(), 1396 | baz: self.baz.to_static(), 1397 | } 1398 | } 1399 | } 1400 | let s = String::new(); 1401 | let foo = Foo { 1402 | bar: Cow::from(&s), 1403 | baz: alloc::vec![Cow::from(&s)], 1404 | }; 1405 | let to_static = foo.to_static(); 1406 | ensure_static(to_static); 1407 | } 1408 | 1409 | #[test] 1410 | fn test_struct_mixed() { 1411 | struct Foo<'a> { 1412 | prim: u64, 1413 | borrowed_str: &'static str, 1414 | owned_str: String, 1415 | cow_str: Cow<'a, str>, 1416 | } 1417 | impl ToBoundedStatic for Foo<'_> { 1418 | type Static = Foo<'static>; 1419 | 1420 | fn to_static(&self) -> Self::Static { 1421 | Foo { 1422 | prim: self.prim.to_static(), 1423 | borrowed_str: self.borrowed_str.to_static(), 1424 | owned_str: self.owned_str.to_static(), 1425 | cow_str: self.cow_str.to_static(), 1426 | } 1427 | } 1428 | } 1429 | let s = String::new(); 1430 | let foo = Foo { 1431 | prim: 0, 1432 | borrowed_str: "", 1433 | owned_str: s.clone(), 1434 | cow_str: Cow::from(&s), 1435 | }; 1436 | let to_static = foo.to_static(); 1437 | ensure_static(to_static); 1438 | } 1439 | } 1440 | 1441 | #[cfg(feature = "collections")] 1442 | #[cfg(test)] 1443 | mod collections_tests { 1444 | use super::*; 1445 | 1446 | fn ensure_static(t: T) { 1447 | drop(t); 1448 | } 1449 | 1450 | #[test] 1451 | fn test_binary_heap() { 1452 | let s = String::new(); 1453 | let value = BinaryHeap::from([Cow::from(&s)]); 1454 | let to_static = value.to_static(); 1455 | ensure_static(to_static); 1456 | } 1457 | 1458 | #[test] 1459 | fn test_btree_map() { 1460 | let k = String::from("key"); 1461 | let v = String::from("value"); 1462 | let value = BTreeMap::from([(Cow::from(&k), Cow::from(&v))]); 1463 | let to_static = value.to_static(); 1464 | ensure_static(to_static); 1465 | } 1466 | 1467 | #[test] 1468 | fn test_btree_set() { 1469 | let s = String::new(); 1470 | let value = BTreeSet::from([Cow::from(&s)]); 1471 | let to_static = value.to_static(); 1472 | ensure_static(to_static); 1473 | } 1474 | 1475 | #[test] 1476 | fn test_linked_list() { 1477 | let s = String::new(); 1478 | let value = LinkedList::from([Cow::from(&s)]); 1479 | let to_static = value.to_static(); 1480 | ensure_static(to_static); 1481 | } 1482 | 1483 | #[test] 1484 | fn test_vec_deque() { 1485 | let s = String::new(); 1486 | let value = VecDeque::from([Cow::from(&s)]); 1487 | let to_static = value.to_static(); 1488 | ensure_static(to_static); 1489 | } 1490 | } 1491 | 1492 | #[cfg(feature = "std")] 1493 | #[cfg(test)] 1494 | mod std_tests { 1495 | use core::any::Any; 1496 | 1497 | use super::*; 1498 | 1499 | fn ensure_static(t: T) { 1500 | drop(t); 1501 | } 1502 | 1503 | #[test] 1504 | fn test_hashmap1() { 1505 | let k = String::from("key"); 1506 | let v = String::from("value"); 1507 | let value = std::collections::HashMap::from([(Cow::from(&k), Cow::from(&v))]); 1508 | let to_static = value.to_static(); 1509 | ensure_static(to_static); 1510 | } 1511 | 1512 | #[test] 1513 | fn test_hashmap2() { 1514 | let k = "key"; 1515 | let v = String::from("value"); 1516 | let value = std::collections::HashMap::from([(k, Cow::from(&v))]); 1517 | let to_static = value.to_static(); 1518 | ensure_static(to_static); 1519 | } 1520 | 1521 | #[test] 1522 | fn test_hashmap3() { 1523 | let k = String::from("key"); 1524 | let v = 0i16; 1525 | let value = std::collections::HashMap::from([(Cow::from(&k), v)]); 1526 | let to_static = value.to_static(); 1527 | ensure_static(to_static); 1528 | } 1529 | 1530 | #[test] 1531 | fn test_hashset() { 1532 | let value = String::from("data"); 1533 | let value = std::collections::HashSet::from([(Cow::from(&value))]); 1534 | let to_static = value.to_static(); 1535 | ensure_static(to_static); 1536 | } 1537 | 1538 | #[test] 1539 | fn test_custom_random_state() { 1540 | #[derive(Clone, Default)] 1541 | struct RandomState; 1542 | 1543 | impl std::hash::BuildHasher for RandomState { 1544 | type Hasher = std::collections::hash_map::DefaultHasher; 1545 | 1546 | fn build_hasher(&self) -> Self::Hasher { 1547 | std::collections::hash_map::DefaultHasher::default() 1548 | } 1549 | } 1550 | 1551 | impl ToBoundedStatic for RandomState { 1552 | type Static = Self; 1553 | 1554 | fn to_static(&self) -> Self::Static { 1555 | self.clone() 1556 | } 1557 | } 1558 | 1559 | let k = "key"; 1560 | let v = 0i16; 1561 | let value = std::collections::HashMap::<_, _, RandomState>::from_iter([(k, v)]); 1562 | let to_static = value.to_static(); 1563 | assert_eq!(value.type_id(), to_static.type_id()); 1564 | ensure_static(to_static); 1565 | let value = std::collections::HashSet::<_, RandomState>::from_iter([k]); 1566 | let to_static = value.to_static(); 1567 | assert_eq!(value.type_id(), to_static.type_id()); 1568 | ensure_static(to_static); 1569 | } 1570 | } 1571 | 1572 | #[cfg(feature = "smol_str")] 1573 | #[cfg(test)] 1574 | mod smol_str_tests { 1575 | use super::*; 1576 | 1577 | fn ensure_static(t: T) { 1578 | drop(t); 1579 | } 1580 | 1581 | #[test] 1582 | fn test_smol_str() { 1583 | ensure_static(smol_str::SmolStr::new("smol").to_static()); 1584 | ensure_static(smol_str::SmolStr::new("smol").into_static()); 1585 | } 1586 | } 1587 | 1588 | #[cfg(feature = "smallvec")] 1589 | #[cfg(test)] 1590 | mod smallvec_tests { 1591 | use super::*; 1592 | 1593 | fn ensure_static(t: T) { 1594 | drop(t); 1595 | } 1596 | 1597 | #[test] 1598 | fn test_smallvec1() { 1599 | let vec: smallvec::SmallVec<[usize; 0]> = smallvec::SmallVec::new(); 1600 | ensure_static(vec.to_static()); 1601 | ensure_static(vec.into_static()); 1602 | } 1603 | 1604 | #[test] 1605 | fn test_smallvec2() { 1606 | let buf = [1, 2, 3, 4, 5]; 1607 | let small_vec: smallvec::SmallVec<_> = smallvec::SmallVec::from_buf(buf); 1608 | ensure_static(small_vec.to_static()); 1609 | ensure_static(small_vec.into_static()); 1610 | } 1611 | 1612 | #[test] 1613 | fn test_smallvec3() { 1614 | let x = String::from("foo"); 1615 | let y = String::from("bar"); 1616 | let buf = [Cow::Borrowed(x.as_str()), Cow::Borrowed(y.as_str())]; 1617 | let small_vec: smallvec::SmallVec<_> = smallvec::SmallVec::from_buf(buf); 1618 | ensure_static(small_vec.to_static()); 1619 | ensure_static(small_vec.into_static()); 1620 | } 1621 | } 1622 | 1623 | #[cfg(feature = "smartstring")] 1624 | #[cfg(test)] 1625 | mod smartstring_tests { 1626 | use super::*; 1627 | use smartstring::alias::String; 1628 | 1629 | fn ensure_static(t: T) { 1630 | drop(t); 1631 | } 1632 | 1633 | #[test] 1634 | fn test_smartstring() { 1635 | let string = String::from("test"); 1636 | ensure_static(string.to_static()); 1637 | ensure_static(string.into_static()); 1638 | } 1639 | } 1640 | 1641 | #[cfg(feature = "ahash")] 1642 | #[cfg(test)] 1643 | mod ahash_tests { 1644 | use super::*; 1645 | 1646 | fn ensure_static(t: T) { 1647 | drop(t); 1648 | } 1649 | 1650 | #[test] 1651 | fn test_ahash_random_state() { 1652 | ensure_static(ahash::RandomState::new().to_static()); 1653 | } 1654 | 1655 | #[cfg(feature = "std")] 1656 | #[test] 1657 | fn test_ahash_ahashmap() { 1658 | let k = String::from("key"); 1659 | let v = String::from("value"); 1660 | let value = ahash::AHashMap::from([(Cow::from(&k), Cow::from(&v))]); 1661 | let to_static = value.to_static(); 1662 | ensure_static(to_static); 1663 | } 1664 | 1665 | #[cfg(feature = "std")] 1666 | #[test] 1667 | fn test_ahash_ahashset() { 1668 | let value = String::from("data"); 1669 | let value = ahash::AHashSet::from([(Cow::from(&value))]); 1670 | let to_static = value.to_static(); 1671 | ensure_static(to_static); 1672 | } 1673 | } 1674 | 1675 | #[cfg(feature = "chrono")] 1676 | #[cfg(test)] 1677 | mod chrono_tests { 1678 | use super::*; 1679 | 1680 | fn ensure_static(t: T) { 1681 | drop(t); 1682 | } 1683 | 1684 | #[test] 1685 | fn test_chrono_datetime() { 1686 | let value = chrono::Utc::now(); 1687 | let to_static = value.to_static(); 1688 | assert_eq!(value, to_static); 1689 | ensure_static(to_static); 1690 | } 1691 | 1692 | #[test] 1693 | fn test_chrono_datetime_with_custom_tz() { 1694 | use chrono::{ 1695 | DateTime, FixedOffset, MappedLocalTime, NaiveDate, NaiveDateTime, Offset, TimeZone, 1696 | }; 1697 | #[derive(Debug, Clone)] 1698 | struct MyOffset; 1699 | impl Offset for MyOffset { 1700 | fn fix(&self) -> FixedOffset { 1701 | FixedOffset::east_opt(1).unwrap() 1702 | } 1703 | } 1704 | #[derive(Clone)] 1705 | struct MyTz; 1706 | impl TimeZone for MyTz { 1707 | type Offset = MyOffset; 1708 | 1709 | fn from_offset(_offset: &Self::Offset) -> Self { 1710 | Self 1711 | } 1712 | 1713 | fn offset_from_local_date(&self, _local: &NaiveDate) -> MappedLocalTime { 1714 | MappedLocalTime::None 1715 | } 1716 | 1717 | fn offset_from_local_datetime( 1718 | &self, 1719 | _local: &NaiveDateTime, 1720 | ) -> MappedLocalTime { 1721 | MappedLocalTime::None 1722 | } 1723 | 1724 | fn offset_from_utc_date(&self, _utc: &NaiveDate) -> Self::Offset { 1725 | MyOffset 1726 | } 1727 | 1728 | fn offset_from_utc_datetime(&self, _utc: &NaiveDateTime) -> Self::Offset { 1729 | MyOffset 1730 | } 1731 | } 1732 | 1733 | impl ToBoundedStatic for MyTz { 1734 | type Static = Self; 1735 | 1736 | fn to_static(&self) -> Self::Static { 1737 | self.clone() 1738 | } 1739 | } 1740 | 1741 | let value = DateTime::from_timestamp(0, 0).unwrap().with_timezone(&MyTz); 1742 | let to_static = value.to_static(); 1743 | ensure_static(to_static); 1744 | } 1745 | 1746 | #[test] 1747 | fn test_chrono_fixed_offset() { 1748 | let value = chrono::FixedOffset::east_opt(1).unwrap(); 1749 | let to_static = value.to_static(); 1750 | ensure_static(to_static); 1751 | } 1752 | 1753 | #[test] 1754 | fn test_chrono_months() { 1755 | let value = chrono::Months::new(1); 1756 | let to_static = value.to_static(); 1757 | ensure_static(to_static); 1758 | } 1759 | 1760 | #[test] 1761 | fn test_chrono_time_delta() { 1762 | let value = chrono::TimeDelta::days(10); 1763 | let to_static = value.to_static(); 1764 | ensure_static(to_static); 1765 | } 1766 | 1767 | #[test] 1768 | fn test_chrono_utc() { 1769 | let value = chrono::Utc; 1770 | let to_static = value.to_static(); 1771 | ensure_static(to_static); 1772 | } 1773 | 1774 | #[test] 1775 | fn test_chrono_month() { 1776 | let value = chrono::Month::January; 1777 | let to_static = value.to_static(); 1778 | ensure_static(to_static); 1779 | } 1780 | 1781 | #[test] 1782 | fn test_chrono_weekday() { 1783 | let value = chrono::Weekday::Mon; 1784 | let to_static = value.to_static(); 1785 | ensure_static(to_static); 1786 | } 1787 | 1788 | #[test] 1789 | fn test_chrono_naive_days() { 1790 | let value = chrono::naive::Days::new(1); 1791 | let to_static = value.to_static(); 1792 | ensure_static(to_static); 1793 | } 1794 | 1795 | #[test] 1796 | fn test_chrono_naive_iso_week() { 1797 | use chrono::Datelike; 1798 | let value = chrono::naive::NaiveDate::from_ymd_opt(2024, 6, 1) 1799 | .unwrap() 1800 | .iso_week(); 1801 | let to_static = value.to_static(); 1802 | ensure_static(to_static); 1803 | } 1804 | 1805 | #[test] 1806 | fn test_chrono_naive_date() { 1807 | let value = chrono::naive::NaiveDate::from_ymd_opt(2024, 6, 1).unwrap(); 1808 | let to_static = value.to_static(); 1809 | ensure_static(to_static); 1810 | } 1811 | 1812 | #[test] 1813 | fn test_chrono_naive_date_time() { 1814 | let value = chrono::naive::NaiveDateTime::new( 1815 | chrono::NaiveDate::from_ymd_opt(2024, 6, 1).unwrap(), 1816 | chrono::NaiveTime::from_hms_opt(22, 33, 44).unwrap(), 1817 | ); 1818 | let to_static = value.to_static(); 1819 | ensure_static(to_static); 1820 | } 1821 | 1822 | #[test] 1823 | fn test_chrono_naive_time() { 1824 | let value = chrono::naive::NaiveTime::from_hms_opt(22, 33, 44).unwrap(); 1825 | let to_static = value.to_static(); 1826 | ensure_static(to_static); 1827 | } 1828 | } 1829 | 1830 | #[cfg(feature = "chrono-clock")] 1831 | #[cfg(test)] 1832 | mod chrono_clock_tests { 1833 | use super::*; 1834 | 1835 | fn ensure_static(t: T) { 1836 | drop(t); 1837 | } 1838 | 1839 | #[test] 1840 | fn test_chrono_local() { 1841 | let value = chrono::Local::now(); 1842 | let to_static = value.to_static(); 1843 | ensure_static(to_static); 1844 | } 1845 | } 1846 | --------------------------------------------------------------------------------