derive-into-owned-0.2.0/.cargo_vcs_info.json0000644000000001360000000000100143720ustar { "git": { "sha1": "57d509eb8c3d4b4ad488517ca0594b91ed567625" }, "path_in_vcs": "" }derive-into-owned-0.2.0/.github/workflows/ci.yml000064400000000000000000000015350072674642500177310ustar 00000000000000name: CI # limit ci building to pushes to master not to get twice the notification email # from github. on: workflow_dispatch: push: branches: - 'master' pull_request: branches: '*' jobs: build: runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install rust toolchain uses: hecrj/setup-rust-action@v1.3.4 with: rust-version: stable components: clippy, rustfmt - name: Cache uses: Swatinem/rust-cache@v1 - name: Build run: cargo build --all-features --all-targets - name: Cargo fmt run: cargo fmt --all -- --check - name: Cargo clippy run: cargo clippy --all-targets --all-features -- -D warnings - name: Test run: timeout 15m cargo test --all --all-features derive-into-owned-0.2.0/.gitignore000064400000000000000000000000360072674642500152010ustar 00000000000000target/ **/*.rs.bk Cargo.lock derive-into-owned-0.2.0/CHANGELOG.md000064400000000000000000000007670072674642500150350ustar 00000000000000# Changelog ## Next None so far. ## 0.2.0 (2022-01-08) * documentation changes * migrate from travis to github actions [#5](https://github.com/koivunej/derive-into-owned/pull/5) * jump from rust 2015 to rust 2021 with syn and other dependencies upgraded in [#4](https://github.com/koivunej/derive-into-owned/pull/4) ## 0.1.0 (2017-05-30) * Initial `#[derive(IntoOwned)]` that seems to work with quick-protobuf generated code * More limited `#[derive(Borrowed)]` that seems to work some cases derive-into-owned-0.2.0/Cargo.toml0000644000000016740000000000100124000ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "derive-into-owned" version = "0.2.0" authors = ["Joonas Koivunen "] description = "Custom derives to help with types containing Cow fields" homepage = "https://github.com/koivunej/derive-into-owned" license = "MIT" repository = "https://github.com/koivunej/derive-into-owned" resolver = "2" [lib] proc-macro = true [dependencies.proc-macro2] version = "1" [dependencies.quote] version = "1" [dependencies.syn] version = "1" derive-into-owned-0.2.0/Cargo.toml.orig000064400000000000000000000006430072674642500161040ustar 00000000000000[package] name = "derive-into-owned" version = "0.2.0" authors = ["Joonas Koivunen "] description = "Custom derives to help with types containing Cow fields" license = "MIT" repository = "https://github.com/koivunej/derive-into-owned" homepage = "https://github.com/koivunej/derive-into-owned" edition = "2021" [lib] proc-macro = true [dependencies] syn = "1" quote = "1" proc-macro2 = "1" derive-into-owned-0.2.0/README.md000064400000000000000000000067100072674642500144750ustar 00000000000000# derive-into-owned [![Build Status](https://travis-ci.org/koivunej/derive-into-owned.svg?branch=master)](https://travis-ci.org/koivunej/derive-into-owned) [![crates.io](https://img.shields.io/crates/v/derive-into-owned.svg)](https://crates.io/crates/derive-into-owned) [![docs.rs](https://docs.rs/derive-into-owned/badge.svg)](https://docs.rs/derive-into-owned/) Rust procedural macros for deriving methods to help with working with types that contain [`Cow`](https://doc.rust-lang.org/std/borrow/enum.Cow.html) fields. Please note that this derive somewhat strangely works with duck-typing, at least for now. It was originally created to help me reduce the boilerplate with `Cow` types. `[derive(IntoOwned)]` generates a method similar to: ```rust use std::borrow::Cow; struct Foo<'a> { field: Cow<'a, str>, } impl<'a> Foo<'a> { /// This method would be derived using #[derive(IntoOwned)] pub fn into_owned(self) -> Foo<'static> { Foo { field: Cow::Owned(self.field.into_owned()), } } } ``` Originally based off of [deep-clone-derive](https://github.com/asajeffrey/deep-clone/blob/master/deep-clone-derive/lib.rs) example but supports: * [tuple structs](./tests/tuple_struct.rs) * normal [structs](./tests/struct.rs) * enums with tuple variants [tuple enums](./tests/simple_enum.rs) * `IntoOwned` alike fields (actually assumes all fields with types with lifetimes are `IntoOwned` alike) * [options of Cow or Cow-like types](./tests/opt_field.rs) `Option>` and `Option>` * [vectors of Cow or Cow-like types](./tests/vec.rs) But wait there is even more! `[derive(Borrowed)]` generates a currently perhaps a bit limited version of a method like: ```rust impl<'a> Foo<'a> { pub fn borrowed<'b>(&'b self) -> Foo<'b> { Foo { field: Cow::Borrowed(self.field.as_ref()), } } } ``` ## Types with lifetimes If your struct has a field with type `Bar<'a>` then `Bar` is assumed to have a method `fn into_owned(self) -> Bar<'static>`. Note, there's no trait implementation expected because I didn't find one at the time and didn't think to create my own, assumed the `Cow::into_owned` might be getting an extension in standard library which never happened and so on. ## Limitations Currently deriving will fail miserably for at least but not limited to: * `IntoOwned`: borrowed fields like `&'a str` * `Borrowed`: struct/enum has more than one lifetime * both: arrays not supported * both: into_owned/borrowed types inside tuples inside vectors Using with incompatible types results in not so understandable error messages. For example, given a struct: ```rust #[derive(IntoOwned)] struct Foo<'a> { field: &'a str, } ``` The compiler error will be: ``` error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements --> tests/does_not_compile.rs:4:10 | 4 | #[derive(IntoOwned)] | ^^^^^^^^^ | note: first, the lifetime cannot outlive the lifetime 'a as defined on the impl at 4:10... --> tests/does_not_compile.rs:4:10 | 4 | #[derive(IntoOwned)] | ^^^^^^^^^ note: ...so that reference does not outlive borrowed content --> tests/does_not_compile.rs:4:10 | 4 | #[derive(IntoOwned)] | ^^^^^^^^^ = note: but, the lifetime must be valid for the static lifetime... note: ...so that expression is assignable (expected Foo<'static>, found Foo<'_>) --> tests/does_not_compile.rs:4:10 | 4 | #[derive(IntoOwned)] | ^^^^^^^^^ error: aborting due to previous error(s) ``` derive-into-owned-0.2.0/src/field_kind.rs000064400000000000000000000060070072674642500164420ustar 00000000000000use quote::{format_ident, quote}; use crate::helpers::{collect_segments, is_cow, is_cow_alike, is_iter_field, is_opt_cow}; #[derive(Debug)] pub enum FieldKind { PlainCow, AssumedCow, /// Option fields with either PlainCow or AssumedCow OptField(usize, Box), IterableField(Box), JustMoved, } impl FieldKind { pub fn resolve(ty: &syn::Type) -> Self { if let syn::Type::Path(syn::TypePath { ref path, .. }) = ty { if is_cow(&collect_segments(path)) { FieldKind::PlainCow } else if is_cow_alike(&collect_segments(path)) { FieldKind::AssumedCow } else if let Some(kind) = is_opt_cow(collect_segments(path)) { kind } else if let Some(kind) = is_iter_field(collect_segments(path)) { kind } else { FieldKind::JustMoved } } else { FieldKind::JustMoved } } pub fn move_or_clone_field(&self, var: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { use self::FieldKind::*; match *self { PlainCow => quote! { ::std::borrow::Cow::Owned(#var.into_owned()) }, AssumedCow => quote! { #var.into_owned() }, OptField(levels, ref inner) => { let next = format_ident!("val"); let next = quote! { #next }; let mut tokens = inner.move_or_clone_field(&next); for _ in 0..(levels - 1) { tokens = quote! { #next.map(|#next| #tokens) }; } quote! { #var.map(|#next| #tokens) } } IterableField(ref inner) => { let next = format_ident!("x"); let next = quote! { #next }; let tokens = inner.move_or_clone_field(&next); quote! { #var.into_iter().map(|x| #tokens).collect() } } JustMoved => quote! { #var }, } } pub fn borrow_or_clone(&self, var: &proc_macro2::TokenStream) -> proc_macro2::TokenStream { use self::FieldKind::*; match *self { PlainCow => quote! { ::std::borrow::Cow::Borrowed(#var.as_ref()) }, AssumedCow => quote! { #var.borrowed() }, OptField(levels, ref inner) => { let next = format_ident!("val"); let next = quote! { #next }; let mut tokens = inner.borrow_or_clone(&next); for _ in 0..(levels - 1) { tokens = quote! { #next.as_ref().map(|#next| #tokens) }; } quote! { #var.as_ref().map(|#next| #tokens) } } IterableField(ref inner) => { let next = format_ident!("x"); let next = quote! { #next }; let tokens = inner.borrow_or_clone(&next); quote! { #var.iter().map(|x| #tokens).collect() } } JustMoved => quote! { #var.clone() }, } } } derive-into-owned-0.2.0/src/helpers.rs000064400000000000000000000123350072674642500160150ustar 00000000000000use crate::field_kind::FieldKind; pub fn has_lifetime_arguments(segments: &[syn::PathSegment]) -> bool { if let Some(&syn::PathArguments::AngleBracketed(ref generics)) = segments.last().map(|x| &x.arguments) { generics .args .iter() .any(|f| matches!(f, syn::GenericArgument::Lifetime(_))) } else { false } } pub fn number_of_type_arguments(segments: &[syn::PathSegment]) -> usize { if let Some(&syn::PathArguments::AngleBracketed(ref generics)) = segments.last().map(|x| &x.arguments) { generics .args .iter() .filter(|f| matches!(f, syn::GenericArgument::Type(_))) .count() } else { 0 } } pub fn has_binding_arguments(segments: &[syn::PathSegment]) -> bool { if let Some(&syn::PathArguments::AngleBracketed(ref generics)) = segments.last().map(|x| &x.arguments) { generics .args .iter() .any(|f| matches!(f, syn::GenericArgument::Binding(_))) } else { false } } fn type_hopefully_is(segments: &[syn::PathSegment], expected: &str) -> bool { let expected = expected .split("::") .map(|x| quote::format_ident!("{}", x)) .collect::>(); if segments.len() > expected.len() { return false; } let expected = expected.iter().collect::>(); let segments = segments.iter().map(|x| &x.ident).collect::>(); for len in 0..expected.len() { if segments[..] == expected[expected.len() - len - 1..] { return true; } } false } pub fn is_cow(segments: &[syn::PathSegment]) -> bool { type_hopefully_is(segments, "std::borrow::Cow") } pub fn is_cow_alike(segments: &[syn::PathSegment]) -> bool { if let Some(&syn::PathArguments::AngleBracketed(ref _data)) = segments.last().map(|x| &x.arguments) { has_lifetime_arguments(segments) } else { false } } pub fn collect_segments(path: &syn::Path) -> Vec { path.segments.iter().cloned().collect::>() } pub fn is_opt_cow(mut segments: Vec) -> Option { let mut levels = 0; loop { if type_hopefully_is(&segments, "std::option::Option") { if let syn::PathSegment { arguments: syn::PathArguments::AngleBracketed(ref data), .. } = *segments.last().expect("last segment") { if has_lifetime_arguments(&segments) || has_binding_arguments(&segments) { // Option<&'a ?> cannot be moved but let the compiler complain // don't know about data bindings break; } if number_of_type_arguments(&segments) != 1 { // Option probably means some other, movable option break; } match *data.args.first().expect("first arg") { syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { // segments: ref next_segments, ref path, .. })) => { levels += 1; segments = collect_segments(path); continue; } _ => break, } } } else if is_cow(&segments) { return Some(FieldKind::OptField(levels, Box::new(FieldKind::PlainCow))); } else if is_cow_alike(&segments) { return Some(FieldKind::OptField(levels, Box::new(FieldKind::AssumedCow))); } break; } None } pub fn is_iter_field(mut segments: Vec) -> Option { loop { // this should be easy to do for arrays as well.. if type_hopefully_is(&segments, "std::vec::Vec") { if let syn::PathSegment { arguments: syn::PathArguments::AngleBracketed(ref data), .. } = *segments.last().expect("last segment") { if has_lifetime_arguments(&segments) || has_binding_arguments(&segments) { break; } // if data.types.len() != 1 { if number_of_type_arguments(&segments) != 1 { // TODO: this could be something like Vec<(u32, Bar<'a>)>? break; } match *data.args.first().expect("first arg") { syn::GenericArgument::Type(syn::Type::Path(syn::TypePath { // segments: ref next_segments, ref path, .. })) => { segments = collect_segments(path); continue; } _ => break, } } } else if is_cow(&segments) { return Some(FieldKind::IterableField(Box::new(FieldKind::PlainCow))); } else if is_cow_alike(&segments) { return Some(FieldKind::IterableField(Box::new(FieldKind::AssumedCow))); } break; } None } derive-into-owned-0.2.0/src/lib.rs000064400000000000000000000306620072674642500151240ustar 00000000000000//! # derive_into_owned //! //! This crate supports deriving two different methods (not traits): //! //! * `IntoOwned` //! * `Borrowed` //! //! These were first created to help out with types generated by [`quick_protobuf`] which generates //! structs with [`Cow`] fields. It is entirely possible that this crate is not needed at all and //! that there exists better alternatives to what this crate aims to support. //! //! ## Definitions, naming //! //! "Cow-alike" is used to mean a type that: //! //! * has a lifetime specifier, like `Foo<'a>` //! * has an implementation for `fn into_owned(self) -> Foo<'static>` //! //! This is a bit of a bad name as [`Cow`] itself has a different kind of `into_owned` which //! returns the owned version of the generic type parameter. I am open to suggestions for both //! "Cow-alike" and `into_owned`. //! //! ## `IntoOwned` //! //! `#[derive(IntoOwned)]` implements a method `fn into_owned(self) -> Foo<'static>` for type //! `Foo<'a>` which contains [`Cow`] or "Cow-alike" fields. The method returns a version with //! `'static` lifetime which means the value owns all of it's data. This is useful if you are //! for example, working with [`tokio-rs`] which currently requires types to be `'static`. //! //! ## `Borrowed` //! //! `#[derive(Borrowed)]` implements a method `fn borrowed<'b>(&'b self) -> Foo<'b>` for type //! `Foo<'a>`. This is useful in case you need to transform the value into another type using //! std conversions like [`From`], but you don't want to clone the data in the process. Note that //! the all the fields that are not [`Cow`] or "Cow-alike" are just cloned, and new vectors are //! collected, so this yields savings only when you manage to save big chunks of memory. //! //! ## Limitations //! //! Currently only the types I needed are supported and this might be a rather limited set of //! functionality. If you find that this does not work in your case please file an issue at [project //! repository](https://github.com/koivunej/derive-into-owned/issues). //! //! [`quick_protobuf`]: https://github.com/tafia/quick-protobuf/ //! [`tokio-rs`]: https://tokio.rs //! [`Cow`]: https://doc.rust-lang.org/std/borrow/enum.Cow.html //! [`From`]: https://doc.rust-lang.org/std/convert/trait.From.html use quote::{format_ident, quote}; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; mod field_kind; mod helpers; use field_kind::FieldKind; #[proc_macro_derive(IntoOwned)] pub fn into_owned(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let expanded = impl_with_generator(&ast, IntoOwnedGen); TokenStream::from(expanded) } #[proc_macro_derive(Borrowed)] pub fn borrowed(input: TokenStream) -> TokenStream { let ast = parse_macro_input!(input as DeriveInput); let expanded = impl_with_generator(&ast, BorrowedGen); TokenStream::from(expanded) } fn impl_with_generator( ast: &syn::DeriveInput, gen: G, ) -> proc_macro2::TokenStream { // this is based heavily on https://github.com/asajeffrey/deep-clone/blob/master/deep-clone-derive/lib.rs let name = &ast.ident; let borrowed_params = gen.quote_borrowed_params(ast); let borrowed = if borrowed_params.is_empty() { quote! {} } else { quote! { < #(#borrowed_params),* > } }; let params = gen.quote_type_params(ast); let params = if params.is_empty() { quote! {} } else { quote! { < #(#params),* > } }; let owned_params = gen.quote_rhs_params(ast); let owned = if owned_params.is_empty() { quote! {} } else { quote! { < #(#owned_params),* > } }; let body = match ast.data { syn::Data::Struct(ref body) => { let inner = gen.visit_struct(body); quote! { #name #inner } } syn::Data::Enum(ref body) => { let cases = body.variants.iter().map(|variant| { let unqualified_ident = &variant.ident; let ident = quote! { #name::#unqualified_ident }; gen.visit_enum_data(ident, variant) }); quote! { match self { #(#cases),* } } } syn::Data::Union(_) => todo!("unions are not supported (5)"), }; gen.combine_impl(borrowed, name, params, owned, body) } /// Probably not the best abstraction trait BodyGenerator { fn quote_borrowed_params(&self, ast: &syn::DeriveInput) -> Vec { let borrowed_lifetime_params = ast.generics.lifetimes().map(|alpha| quote! { #alpha }); let borrowed_type_params = ast.generics.type_params().map(|ty| quote! { #ty }); borrowed_lifetime_params .chain(borrowed_type_params) .collect::>() } fn quote_type_params(&self, ast: &syn::DeriveInput) -> Vec { ast.generics .lifetimes() .map(|alpha| quote! { #alpha }) .chain(ast.generics.type_params().map(|ty| { let ident = &ty.ident; quote! { #ident } })) .collect::>() } fn quote_rhs_params(&self, ast: &syn::DeriveInput) -> Vec { let owned_lifetime_params = ast.generics.lifetimes().map(|_| quote! { 'static }); let owned_type_params = ast.generics.type_params().map(|ty| { let ident = &ty.ident; quote! { #ident } }); owned_lifetime_params .chain(owned_type_params) .collect::>() } fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream; fn visit_enum_data( &self, variant: proc_macro2::TokenStream, data: &syn::Variant, ) -> proc_macro2::TokenStream; fn combine_impl( &self, borrows: proc_macro2::TokenStream, name: &syn::Ident, rhs_params: proc_macro2::TokenStream, owned: proc_macro2::TokenStream, body: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream; } struct IntoOwnedGen; impl BodyGenerator for IntoOwnedGen { fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream { // Helper ternary to avoid Option enum Fields { Named, Tuple, Unit, } use Fields::*; let fields_kind = data .fields .iter() .next() .map(|field| if field.ident.is_some() { Named } else { Tuple }) .unwrap_or(Unit); match fields_kind { Named => { let fields = data.fields.iter().map(|field| { let ident = field.ident.as_ref().expect("unexpected unnamed field"); let field_ref = quote! { self.#ident }; let code = FieldKind::resolve(&field.ty).move_or_clone_field(&field_ref); quote! { #ident: #code } }); quote! { { #(#fields),* } } } Tuple => { let fields = data.fields.iter().enumerate().map(|(index, field)| { let index = syn::Index::from(index); let index = quote! { self.#index }; FieldKind::resolve(&field.ty).move_or_clone_field(&index) }); quote! { ( #(#fields),* ) } } Unit => { quote! {} } } } fn visit_enum_data( &self, ident: proc_macro2::TokenStream, variant: &syn::Variant, ) -> proc_macro2::TokenStream { if variant.fields.is_empty() { return quote!(#ident => #ident); } let fields_are_named = variant.fields.iter().any(|field| field.ident.is_some()); if fields_are_named { let named_fields = variant .fields .iter() .filter_map(|field| field.ident.as_ref()); let cloned = variant.fields.iter().map(|field| { let ident = field.ident.as_ref().unwrap(); let ident = quote!(#ident); let code = FieldKind::resolve(&field.ty).move_or_clone_field(&ident); quote! { #ident: #code } }); quote! { #ident { #(#named_fields),* } => #ident { #(#cloned),* } } } else { let unnamed_fields = &variant .fields .iter() .enumerate() .filter(|(_, field)| field.ident.is_none()) .map(|(index, _)| format_ident!("arg_{}", index)) .collect::>(); let cloned = unnamed_fields .iter() .zip(variant.fields.iter()) .map(|(ident, field)| { let ident = quote! { #ident }; FieldKind::resolve(&field.ty).move_or_clone_field(&ident) }) .collect::>(); quote! { #ident ( #(#unnamed_fields),* ) => #ident ( #(#cloned),* ) } } } fn combine_impl( &self, borrowed: proc_macro2::TokenStream, name: &syn::Ident, params: proc_macro2::TokenStream, owned: proc_macro2::TokenStream, body: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { quote! { impl #borrowed #name #params { /// Returns a version of `self` with all fields converted to owning versions. pub fn into_owned(self) -> #name #owned { #body } } } } } struct BorrowedGen; impl BodyGenerator for BorrowedGen { fn quote_rhs_params(&self, ast: &syn::DeriveInput) -> Vec { let owned_lifetime_params = ast.generics.lifetimes().map(|_| quote! { '__borrowedgen }); let owned_type_params = ast.generics.type_params().map(|ty| { let ident = &ty.ident; quote! { #ident } }); owned_lifetime_params .chain(owned_type_params) .collect::>() } fn visit_struct(&self, data: &syn::DataStruct) -> proc_macro2::TokenStream { let fields = data.fields.iter().map(|field| { let ident = field.ident.as_ref().expect("this fields has no ident (4)"); let field_ref = quote! { self.#ident }; let code = FieldKind::resolve(&field.ty).borrow_or_clone(&field_ref); quote! { #ident: #code } }); quote! { { #(#fields),* } } } fn visit_enum_data( &self, ident: proc_macro2::TokenStream, variant: &syn::Variant, ) -> proc_macro2::TokenStream { if variant.fields.is_empty() { return quote!(#ident => #ident); } let fields_are_named = variant.fields.iter().any(|field| field.ident.is_some()); if fields_are_named { let idents = variant .fields .iter() .map(|field| field.ident.as_ref().expect("this fields has no ident (5)")); let cloned = variant.fields.iter().map(|field| { let ident = field.ident.as_ref().expect("this fields has no ident (6)"); let ident = quote! { #ident }; let code = FieldKind::resolve(&field.ty).borrow_or_clone(&ident); quote! { #ident: #code } }); quote! { #ident { #(ref #idents),* } => #ident { #(#cloned),* } } } else { let idents = (0..variant.fields.len()) .map(|index| quote::format_ident!("x{}", index)) .collect::>(); let cloned = idents .iter() .zip(variant.fields.iter()) .map(|(ident, field)| { let ident = quote! { #ident }; FieldKind::resolve(&field.ty).borrow_or_clone(&ident) }) .collect::>(); quote! { #ident ( #(ref #idents),* ) => #ident ( #(#cloned),* ) } } } fn combine_impl( &self, borrowed: proc_macro2::TokenStream, name: &syn::Ident, params: proc_macro2::TokenStream, owned: proc_macro2::TokenStream, body: proc_macro2::TokenStream, ) -> proc_macro2::TokenStream { quote! { impl #borrowed #name #params { /// Returns a clone of `self` that shares all the "Cow-alike" data with `self`. pub fn borrowed<'__borrowedgen>(&'__borrowedgen self) -> #name #owned { #body } } } } } derive-into-owned-0.2.0/tests/borrowed.rs000064400000000000000000000013310072674642500165430ustar 00000000000000#[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned, Borrowed)] struct Foo<'a> { a: Cow<'a, str>, b: Option>, } #[derive(IntoOwned, Borrowed)] struct Bar<'a> { c: Cow<'a, [u8]>, s: SomeCloneType, } #[derive(Clone)] struct SomeCloneType { #[allow(dead_code)] foo: u32, } #[test] fn borrowed() { let owned = Foo { a: Cow::Borrowed("str"), b: None, } .into_owned(); let borrowed = owned.borrowed(); // owned cannot be moved while borrowed exists test(&owned, borrowed); } fn test<'b, 'a: 'b>(lives_longer: &Foo<'a>, lives_less: Foo<'b>) { drop(lives_less); #[allow(clippy::drop_ref)] drop(lives_longer); } derive-into-owned-0.2.0/tests/generic_tuple_struct.rs000064400000000000000000000011610072674642500211520ustar 00000000000000#[macro_use] extern crate derive_into_owned; use std::borrow::Cow; use std::borrow::ToOwned; #[derive(IntoOwned)] struct Foo<'a, T: 'a + ToOwned + ?Sized>(Cow<'a, T>); #[test] fn tuple_struct() { let non_static_string: String = "foobar".to_string(); let thing = Foo(Cow::Borrowed(&non_static_string)); accepts_only_static(thing.into_owned()); let non_static_vec: Vec = vec![0u8; 8]; let thing = Foo(Cow::Borrowed(&non_static_vec[..])); accepts_only_static(thing.into_owned()); } fn accepts_only_static(static_foo: Foo<'static, T>) { drop(static_foo); } derive-into-owned-0.2.0/tests/opt_field.rs000064400000000000000000000013540072674642500166720ustar 00000000000000#![allow(dead_code)] #[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned, Clone, PartialEq, Debug)] struct Foo<'a> { field: Option>, } #[derive(IntoOwned)] struct Wild<'a> { field: Option>>>>, } #[derive(IntoOwned)] struct Wilder<'a> { field: Option>, } #[test] fn opt_cow_field() { let s = "foobar".to_string(); let thing = Foo { field: Some(Cow::Borrowed(&s)), }; assert_eq!(thing.clone().into_owned(), thing); accepts_only_static(thing.into_owned()); let thing = Foo { field: None }; accepts_only_static(thing.into_owned()); } fn accepts_only_static(anything: T) { drop(anything) } derive-into-owned-0.2.0/tests/simple_enum.rs000064400000000000000000000010220072674642500172320ustar 00000000000000#[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned)] enum Foo<'a> { Str(Cow<'a, str>), Bytes(Cow<'a, [u8]>), } #[test] fn enum_with_only_cow_variants() { let s = "foobar".to_string(); let v = b"12345234".to_vec(); let thing = Foo::Str(Cow::Borrowed(&s)); accepts_only_static(thing.into_owned()); let thing = Foo::Bytes(Cow::Borrowed(&v[..])); accepts_only_static(thing.into_owned()); } fn accepts_only_static(anything: T) { drop(anything) } derive-into-owned-0.2.0/tests/struct.rs000064400000000000000000000012540072674642500162500ustar 00000000000000#![allow(dead_code)] #[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned)] struct Simplest<'a> { field: Cow<'a, str>, } #[derive(IntoOwned)] struct PaddedWithDifferent<'a, 'b> { a: bool, b: u32, c: Cow<'a, str>, d: Simplest<'b>, } #[derive(IntoOwned)] struct PaddedWithSame<'a> { a: bool, b: u32, c: Cow<'a, str>, d: Simplest<'a>, } #[test] fn simplest() { let non_static_string = "foobar".to_string(); let simplest = Simplest { field: Cow::Borrowed(&non_static_string), }; accepts_only_static(simplest.into_owned()); } fn accepts_only_static(s: Simplest<'static>) { drop(s) } derive-into-owned-0.2.0/tests/tuple_struct.rs000064400000000000000000000013000072674642500174510ustar 00000000000000#![allow(dead_code)] #[macro_use] extern crate derive_into_owned; use std::borrow; use std::borrow::Cow; #[derive(IntoOwned)] struct Foo<'a>(Cow<'a, str>); #[derive(IntoOwned)] struct FooExtraFields<'a>(u32, Cow<'a, str>, bool, Vec); #[derive(IntoOwned)] struct Bar<'a>(::std::borrow::Cow<'a, str>); #[derive(IntoOwned)] struct Car<'a>(std::borrow::Cow<'a, str>); #[derive(IntoOwned)] struct Dar<'a>(borrow::Cow<'a, str>); #[test] fn tuple_struct() { let non_static_string: String = "foobar".to_string(); let thing = Foo(Cow::Borrowed(&non_static_string)); accepts_only_static(thing.into_owned()); } fn accepts_only_static(static_foo: Foo<'static>) { drop(static_foo); } derive-into-owned-0.2.0/tests/unit_struct.rs000064400000000000000000000001070072674642500173030ustar 00000000000000#![allow(unused)] #[derive(derive_into_owned::IntoOwned)] struct Far; derive-into-owned-0.2.0/tests/variant_enum.rs000064400000000000000000000011660072674642500174160ustar 00000000000000#[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned)] enum Foo<'a> { Str { a: Cow<'a, str>, b: u32 }, Bytes { a: bool, b: Cow<'a, [u8]> }, } #[test] fn enum_with_only_cow_variants() { let s = "foobar".to_string(); let v = b"12345234".to_vec(); let thing = Foo::Str { a: Cow::Borrowed(&s), b: 32, }; accepts_only_static(thing.into_owned()); let thing = Foo::Bytes { a: false, b: Cow::Borrowed(&v[..]), }; accepts_only_static(thing.into_owned()); } fn accepts_only_static(anything: T) { drop(anything) } derive-into-owned-0.2.0/tests/vec.rs000064400000000000000000000010530072674642500154760ustar 00000000000000#![allow(dead_code)] #[macro_use] extern crate derive_into_owned; use std::borrow::Cow; #[derive(IntoOwned, Borrowed)] struct Thing<'a> { bees: Vec>, cees: Vec>, } #[derive(IntoOwned, Borrowed)] struct Bar<'a> { s: Cow<'a, str>, } #[test] fn vec() { let local = "asdf".to_string(); let thing = Thing { bees: vec![Bar { s: Cow::Borrowed(&local), }], cees: vec![], }; accept_static(thing.into_owned()); } fn accept_static(thing: Thing<'static>) { drop(thing); }