Skip to content

Commit

Permalink
fix: Allow deriving Borrowed for unit and tuple structs
Browse files Browse the repository at this point in the history
This just copies the logic for handling tuple and unit structs from
IntoOwnedGen to BorrowedGen.
  • Loading branch information
neocturne committed May 6, 2024
1 parent 38208b2 commit 5f65417
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 17 deletions.
45 changes: 38 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,13 +292,44 @@ impl BodyGenerator for BorrowedGen {
}

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),* } }
// Helper ternary to avoid Option<bool>
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).borrow_or_clone(&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).borrow_or_clone(&index)
});
quote! { ( #(#fields),* ) }
}
Unit => {
quote! {}
}
}
}

fn visit_enum_data(
Expand Down
28 changes: 18 additions & 10 deletions tests/tuple_struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,41 @@
#[macro_use]
extern crate derive_into_owned;

use std::borrow;
use std::borrow::Cow;
use std::borrow::{self, Cow};

#[derive(IntoOwned)]
#[derive(IntoOwned, Borrowed)]
struct Foo<'a>(Cow<'a, str>);

#[derive(IntoOwned)]
#[derive(IntoOwned, Borrowed)]
struct FooExtraFields<'a>(u32, Cow<'a, str>, bool, Vec<bool>);

#[derive(IntoOwned)]
#[derive(IntoOwned, Borrowed)]
struct Bar<'a>(::std::borrow::Cow<'a, str>);

#[derive(IntoOwned)]
#[derive(IntoOwned, Borrowed)]
struct Car<'a>(std::borrow::Cow<'a, str>);

#[derive(IntoOwned)]
#[derive(IntoOwned, Borrowed)]
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));
let owned = thing.into_owned();

accepts_only_static(thing.into_owned());
accepts_only_static(&owned);

let borrowed = owned.borrowed();
// owned cannot be moved while borrowed exists
test_borrowed(&owned, borrowed);
}

fn accepts_only_static(static_foo: Foo<'static>) {
drop(static_foo);
fn accepts_only_static(_static_foo: &Foo<'static>) {}

fn test_borrowed<'b, 'a: 'b>(lives_longer: &Foo<'a>, lives_less: Foo<'b>) {
drop(lives_less);
#[allow(dropping_references)]
drop(lives_longer);
}

0 comments on commit 5f65417

Please sign in to comment.