diff --git a/src/runtime.rs b/src/runtime.rs index 15b17fc..4a1c14c 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -42,8 +42,10 @@ impl BitOr for HasIterator { /// whichever impl happens to be applicable. Calling that method repeatedly on /// the returned value should be idempotent. pub mod ext { + use super::RepInterp; use super::{HasIterator as HasIter, ThereIsNoIteratorInRepetition as DoesNotHaveIter}; use crate::ToTokens; + use std::collections::btree_set::{self, BTreeSet}; use std::slice; /// Extension trait providing the `quote_into_iter` method on iterators. @@ -73,62 +75,62 @@ pub mod ext { impl RepToTokensExt for T {} - /// Extension trait providing the `quote_into_iter` method for types - /// convertable into slices. - /// - /// NOTE: This is implemented manually, rather than by using a blanket impl - /// over `AsRef<[T]>` to reduce the chance of ambiguity conflicts with other - /// `quote_into_iter` methods from this module. - pub trait RepSliceExt { - type Item; + /// Extension trait providing the `quote_into_iter` method for types that + /// can be referenced as an iterator. + pub trait RepAsIteratorExt<'q> { + type Iter: Iterator; - fn as_slice(&self) -> &[Self::Item]; + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); + } - fn quote_into_iter(&self) -> (slice::Iter, HasIter) { - (self.as_slice().iter(), HasIter) + impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a T { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) } } - impl<'a, T: RepSliceExt + ?Sized> RepSliceExt for &'a T { - type Item = T::Item; + impl<'q, 'a, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &'a mut T { + type Iter = T::Iter; - fn as_slice(&self) -> &[Self::Item] { - ::as_slice(*self) + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + ::quote_into_iter(*self) } } - impl<'a, T: RepSliceExt + ?Sized> RepSliceExt for &'a mut T { - type Item = T::Item; + impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] { + type Iter = slice::Iter<'q, T>; - fn as_slice(&self) -> &[Self::Item] { - ::as_slice(*self) + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) } } - impl RepSliceExt for [T] { - type Item = T; + impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec { + type Iter = slice::Iter<'q, T>; - fn as_slice(&self) -> &[Self::Item] { - self + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) } } - impl RepSliceExt for Vec { - type Item = T; + impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet { + type Iter = btree_set::Iter<'q, T>; - fn as_slice(&self) -> &[Self::Item] { - &self[..] + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) } } macro_rules! array_rep_slice { ($($l:tt)*) => { $( - impl RepSliceExt for [T; $l] { - type Item = T; + impl<'q, T: 'q> RepAsIteratorExt<'q> for [T; $l] { + type Iter = slice::Iter<'q, T>; - fn as_slice(&self) -> &[Self::Item] { - &self[..] + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + (self.iter(), HasIter) } } )* @@ -139,6 +141,14 @@ pub mod ext { 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 ); + + impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp { + type Iter = T::Iter; + + fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { + self.0.quote_into_iter() + } + } } // Helper type used within interpolations to allow for repeated binding names. @@ -156,14 +166,6 @@ impl RepInterp { } } -impl ext::RepSliceExt for RepInterp { - type Item = T::Item; - - fn as_slice(&self) -> &[Self::Item] { - self.0.as_slice() - } -} - impl Iterator for RepInterp { type Item = T::Item; diff --git a/tests/test.rs b/tests/test.rs index 33951bb..6421172 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,6 +1,7 @@ #![cfg_attr(feature = "cargo-clippy", allow(blacklisted_name))] use std::borrow::Cow; +use std::collections::BTreeSet; use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, TokenStreamExt}; @@ -250,6 +251,20 @@ fn test_duplicate_name_repetition_no_copy() { assert_eq!(expected, tokens.to_string()); } +#[test] +fn test_btreeset_repetition() { + let mut set = BTreeSet::new(); + set.insert("a".to_owned()); + set.insert("b".to_owned()); + + let tokens = quote! { + #(#set: #set),* + }; + + let expected = r#""a" : "a" , "b" : "b""#; + assert_eq!(expected, tokens.to_string()); +} + #[test] fn test_variable_name_conflict() { // The implementation of `#(...),*` uses the variable `_i` but it should be