Especially when paired with the diagnostic attribute, this should make it much easier to understand why certain types don't fit together when using fluent_embed.
JWZT34UC7OTMMUZKGYFF6NDGIFNOA6TYXAZ6K66ELM3ZW7ZM7I5AC fn first_second_strings(#[values("a regular string", "", r#"""#)] first: String,#[values("a regular string", "", r#"""#)] second: String,) {
#[case::zero(0_u64, "0")]#[case::two(2_u64, "2")]#[case::max(u64::MAX, "18,446,744,073,709,551,615")]fn number(#[case] quantity: u64, #[case] expected: String) {compare_message(Message::OpenTabs { quantity },format!("{expected} tabs open."),langid!("en-US"),)
#[case::double_quote(r#"""#)]fn one_string(#[case] first: String) {let expected_message = format!(r#"Here is a string: "{first}"."#);let data = Message::OneString { first };let mut buffer = Vec::new();data.message_for_locale(&mut buffer, &langid!("en-US")).unwrap();assert_eq!(String::from_utf8(buffer), Ok(expected_message));
#[case::single_hyphen(r#"""#)]#[case::english("Ferris")]#[case::accent("Férris")]#[case::unicode("蟹")]fn string(#[case] name: String) {compare_message(Message::Person { name: name.clone() },format!("How many tabs does {name} have open?"),langid!("en-US"),)
#[apply(first_second_strings)]fn two_strings(first: String, second: String) {let expected_message = format!(r#"Here is a string: "{first}". And another: "{second}"."#);let data = Message::TwoStrings { first, second };let mut buffer = Vec::new();data.message_for_locale(&mut buffer, &langid!("en-US")).unwrap();assert_eq!(String::from_utf8(buffer), Ok(expected_message));}#[apply(first_second_strings)]#[case::zero(0, "0")]#[case::one(1, "1")]#[case::two(2, "2")]#[case::max(u64::MAX, "18,446,744,073,709,551,615")]fn many_interpolations(first: String, second: String, #[case] third: u64, #[case] plural: &str) {let expected_message = format!(r#"Here is a string: "{first}". And another: "{second}". I once counted {plural} strings in total!"#
#[rstest]#[case::empty(0_u64, "0", "")]#[case::simple(2_u64, "2", "Ferris")]#[case::complex(u64::MAX, "18,446,744,073,709,551,615", "蟹")]fn numbers_and_strings(#[case] quantity: u64, #[case] expected: String, #[case] name: String) {compare_message(Message::TabStatus {name: name.clone(),quantity,},format!("{name} has {expected} tabs open!"),langid!("en-US"),
#[rstest]#[case::zero(0, "0")]#[case::one(1, "1")]#[case::two(2, "2")]#[case::max(u64::MAX, "18,446,744,073,709,551,615")]fn one_number(#[case] number: u64, #[case] plural: &str) {let expected_message = format!("Here is a number: {plural}.");let data = Message::OneNumber { number };let mut buffer = Vec::new();data.message_for_locale(&mut buffer, &langid!("en-US")).unwrap();assert_eq!(String::from_utf8(buffer), Ok(expected_message));}
// Get the generics for the derived itemlet (impl_generics, type_generics, where_clause) = derive_input.generics.split_for_impl();// Combine all of the derived item's generic parameters along with `std::io::Write` for `Localize`
// Get the types of each named fieldlet named_fields: Vec<&syn::Type> = match &derive_input.data {syn::Data::Struct(struct_data) => match &struct_data.fields {syn::Fields::Named(named_fields) => {named_fields.named.iter().map(|field| &field.ty).collect()}_ => todo!(),},syn::Data::Enum(enum_data) => enum_data.variants.iter().flat_map(|variant| match &variant.fields {syn::Fields::Named(named_fields) => {named_fields.named.iter().map(|field| &field.ty)}_ => todo!(),}).collect(),syn::Data::Union(_union_data) => todo!(),};// Add a bound on `Localize` for each field's typelet mut generics = derive_input.generics.clone();let additional_bounds = named_fields.into_iter().map(|field| -> syn::WherePredicate {// Attribute this bound to the original source codelet span = field.span();parse_quote_spanned!(span=> #field: ::fluent_embed::Localize<W>)});generics.make_where_clause().predicates.extend(additional_bounds);// Define a parameter of `std::io::Write` for `Localize`
let localize_impl_generics =derive_input.generics.params.clone().into_iter().chain(std::iter::once(syn::GenericParam::Type(parse_quote!(W: std::io::Write),)));
generics.params.push(syn::GenericParam::Type(parse_quote!(W: std::io::Write)));let (impl_generics, _type_generics, where_clause) = generics.split_for_impl();