Skip to content

Commit c640323

Browse files
authored
Allow adding generics to impl and trait name (#90)
* Add with_impl_generics * Add tests * fix clippy * fix format
1 parent c4baf10 commit c640323

File tree

4 files changed

+121
-13
lines changed

4 files changed

+121
-13
lines changed

src/generate/generator.rs

+59
Original file line numberDiff line numberDiff line change
@@ -309,4 +309,63 @@ mod test {
309309
.collect::<String>()
310310
);
311311
}
312+
313+
#[test]
314+
fn impl_for_with_trait_generics() {
315+
let mut generator = Generator::new(
316+
Ident::new("StructOrEnum", Span::call_site()),
317+
Generics::try_take(&mut token_stream("<'a>")).unwrap(),
318+
None,
319+
);
320+
let _ = generator.impl_for("Foo").with_trait_generics(["&'a str"]);
321+
let output = generator.finish().unwrap();
322+
assert_eq!(
323+
output
324+
.into_iter()
325+
.map(|v| v.to_string())
326+
.collect::<String>(),
327+
token_stream("impl<'a> Foo<&'a str> for StructOrEnum<'a> { }")
328+
.map(|v| v.to_string())
329+
.collect::<String>(),
330+
);
331+
}
332+
333+
#[test]
334+
fn impl_for_with_impl_generics() {
335+
//with simple generics
336+
let mut generator = Generator::new(
337+
Ident::new("StructOrEnum", Span::call_site()),
338+
Generics::try_take(&mut token_stream("<T1, T2>")).unwrap(),
339+
None,
340+
);
341+
let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]);
342+
343+
let output = generator.finish().unwrap();
344+
assert_eq!(
345+
output
346+
.into_iter()
347+
.map(|v| v.to_string())
348+
.collect::<String>(),
349+
token_stream("impl<T1, T2, Bar> Foo for StructOrEnum<T1, T2> { }")
350+
.map(|v| v.to_string())
351+
.collect::<String>()
352+
);
353+
// with lifetimes
354+
let mut generator = Generator::new(
355+
Ident::new("StructOrEnum", Span::call_site()),
356+
Generics::try_take(&mut token_stream("<'alpha, 'beta>")).unwrap(),
357+
None,
358+
);
359+
let _ = generator.impl_for("Foo").with_impl_generics(["Bar"]);
360+
let output = generator.finish().unwrap();
361+
assert_eq!(
362+
output
363+
.into_iter()
364+
.map(|v| v.to_string())
365+
.collect::<String>(),
366+
token_stream("impl<'alpha, 'beta, Bar> Foo for StructOrEnum<'alpha, 'beta> { }")
367+
.map(|v| v.to_string())
368+
.collect::<String>()
369+
);
370+
}
312371
}

src/generate/impl_for.rs

+37-8
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ pub struct ImplFor<'a, P: Parent> {
1313
type_name: StringOrIdent,
1414
trait_name: Option<StringOrIdent>,
1515
lifetimes: Option<Vec<String>>,
16-
generics: Option<Vec<String>>,
16+
trait_generics: Option<Vec<String>>,
17+
impl_generics: Vec<String>,
1718
consts: Vec<StreamBuilder>,
1819
custom_generic_constraints: Option<GenericConstraints>,
1920
impl_types: Vec<StreamBuilder>,
@@ -33,7 +34,8 @@ impl<'a, P: Parent> ImplFor<'a, P> {
3334
trait_name,
3435
type_name,
3536
lifetimes: None,
36-
generics: None,
37+
trait_generics: None,
38+
impl_generics: vec![],
3739
consts: Vec::new(),
3840
custom_generic_constraints: None,
3941
impl_types: Vec::new(),
@@ -100,7 +102,30 @@ impl<'a, P: Parent> ImplFor<'a, P> {
100102
ITER: IntoIterator,
101103
ITER::Item: Into<String>,
102104
{
103-
self.generics = Some(generics.into_iter().map(Into::into).collect());
105+
self.trait_generics = Some(generics.into_iter().map(Into::into).collect());
106+
self
107+
}
108+
109+
/// Add generic parameters to the impl block.
110+
///```
111+
/// # use virtue::prelude::Generator;
112+
/// # let mut generator = Generator::with_name("Bar");
113+
/// generator.impl_for("Foo")
114+
/// .with_impl_generics(["Baz"]);
115+
/// # generator.assert_eq("impl < Baz > Foo for Bar { }");
116+
/// # Ok::<_, virtue::Error>(())
117+
/// ```
118+
///
119+
/// Generates:
120+
/// ```ignore
121+
/// impl<Baz> Foo for Bar { }
122+
/// ```
123+
pub fn with_impl_generics<ITER>(mut self, generics: ITER) -> Self
124+
where
125+
ITER: IntoIterator,
126+
ITER::Item: Into<String>,
127+
{
128+
self.impl_generics = generics.into_iter().map(Into::into).collect();
104129
self
105130
}
106131

@@ -282,20 +307,24 @@ impl<P: Parent> Drop for ImplFor<'_, P> {
282307
impl<P: Parent> ImplFor<'_, P> {
283308
fn generate_impl_definition(&mut self, builder: &mut StreamBuilder) {
284309
builder.ident_str("impl");
310+
311+
let impl_generics = self.impl_generics.as_slice();
285312
if let Some(lifetimes) = &self.lifetimes {
286313
if let Some(generics) = self.generator.generics() {
287-
builder.append(generics.impl_generics_with_additional_lifetimes(lifetimes));
314+
builder.append(generics.impl_generics_with_additional(lifetimes, impl_generics));
288315
} else {
289-
append_lifetimes_and_generics(builder, lifetimes, &[]);
316+
append_lifetimes_and_generics(builder, lifetimes, impl_generics);
290317
}
291318
} else if let Some(generics) = self.generator.generics() {
292-
builder.append(generics.impl_generics());
319+
builder.append(generics.impl_generics_with_additional(&[], impl_generics));
320+
} else if !impl_generics.is_empty() {
321+
append_lifetimes_and_generics(builder, &[], impl_generics)
293322
}
294323
if let Some(t) = &self.trait_name {
295324
builder.push_parsed(t.to_string()).unwrap();
296325

297326
let lifetimes = self.lifetimes.as_deref().unwrap_or_default();
298-
let generics = self.generics.as_deref().unwrap_or_default();
327+
let generics = self.trait_generics.as_deref().unwrap_or_default();
299328
append_lifetimes_and_generics(builder, lifetimes, generics);
300329
builder.ident_str("for");
301330
}
@@ -333,7 +362,7 @@ fn append_lifetimes_and_generics(
333362
if idx > 0 || !lifetimes.is_empty() {
334363
builder.punct(',');
335364
}
336-
builder.ident_str(gen);
365+
builder.push_parsed(gen).unwrap();
337366
}
338367

339368
builder.punct('>');

src/parse/body.rs

+1
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ pub struct EnumVariant {
364364
/// - `Baz = 5`
365365
/// - `Baz(i32) = 5`
366366
/// - `Baz { a: i32} = 5`
367+
///
367368
/// In either case this value will be `Some(Literal::i32(5))`
368369
pub value: Option<Literal>,
369370
/// The attributes of this variant

src/parse/generics.rs

+24-5
Original file line numberDiff line numberDiff line change
@@ -114,20 +114,39 @@ impl Generics {
114114
result
115115
}
116116

117-
pub(crate) fn impl_generics_with_additional_lifetimes(
117+
pub(crate) fn impl_generics_with_additional(
118118
&self,
119-
lifetime: &[String],
119+
lifetimes: &[String],
120+
types: &[String],
120121
) -> StreamBuilder {
121122
let mut result = StreamBuilder::new();
122-
for (idx, lt) in lifetime.iter().enumerate() {
123-
result.punct(if idx == 0 { '<' } else { ',' });
123+
result.punct('<');
124+
let mut is_first = true;
125+
for lt in lifetimes.iter() {
126+
if !is_first {
127+
result.punct(',');
128+
} else {
129+
is_first = false;
130+
}
124131
result.lifetime_str(lt);
125132
}
126133

127134
for generic in self.iter() {
128-
result.punct(',');
135+
if !is_first {
136+
result.punct(',');
137+
} else {
138+
is_first = false;
139+
}
129140
generic.append_to_result_with_constraints(&mut result);
130141
}
142+
for ty in types {
143+
if !is_first {
144+
result.punct(',');
145+
} else {
146+
is_first = false;
147+
}
148+
result.ident_str(ty);
149+
}
131150

132151
result.punct('>');
133152

0 commit comments

Comments
 (0)