crate::ix!();
pub fn merge_generics(wire: &Generics, op: &Generics) -> Generics {
info!("merge_generics: START");
info!(" wire generics = {}", wire.into_token_stream());
info!(" op generics = {}", op.into_token_stream());
let mut result = wire.clone();
info!(" cloned wire generics into result => {}", result.clone().into_token_stream());
for param in &op.params {
info!(" appending op param => {}", param.clone().into_token_stream());
result.params.push(param.clone());
}
if let Some(op_wc) = &op.where_clause {
info!(" op has where-clause => {}", op_wc.into_token_stream());
if let Some(wire_wc) = &mut result.where_clause {
info!(" wire also has where-clause => {}", wire_wc.into_token_stream());
wire_wc.predicates.extend(op_wc.predicates.clone());
info!(" merged wire+op where-clause => {}", wire_wc.into_token_stream());
} else {
info!(" wire has no where-clause, using op's where-clause in result");
result.where_clause = Some(op_wc.clone());
}
} else {
info!(" op has no where-clause, leaving wire's alone");
}
info!("merge_generics: FINISH => {}", result.clone().into_token_stream());
result
}
#[cfg(test)]
mod test_merge_generics {
use super::*;
use syn::{parse_str, DeriveInput};
fn parse_generics_str(input: &str) -> Result<Generics, syn::Error> {
info!("parse_generics_str: START => '{}'", input);
let code = format!("struct Dummy{};", input);
info!(" constructing dummy struct => {}", code);
match parse_str::<DeriveInput>(&code) {
Ok(ast) => {
let g = &ast.generics;
info!(" parse succeeded => {}", g.into_token_stream());
Ok(g.clone())
}
Err(err) => {
info!(" parse failed => {}", err);
Err(err)
}
}
}
#[test]
fn test_merge_empty_generics() {
info!("test_merge_empty_generics: START");
let wire = parse_generics_str("").unwrap();
let op = parse_generics_str("").unwrap();
let merged = merge_generics(&wire, &op);
info!("test_merge_empty_generics: merged => {}", merged.clone().into_token_stream());
assert!(merged.params.is_empty(), "Expected no generic parameters.");
assert!(merged.where_clause.is_none(), "Expected no where-clause.");
}
#[test]
fn test_merge_type_params() {
info!("test_merge_type_params: START");
let wire = parse_generics_str("<A, B>").unwrap();
let op = parse_generics_str("<C>").unwrap();
let merged = merge_generics(&wire, &op);
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
info!(" merged params = {:?}", merged_params);
assert_eq!(merged_params, vec!["A", "B", "C"]);
assert!(merged.where_clause.is_none(), "Expected no where-clause");
}
#[test]
fn test_merge_lifetime_params() {
info!("test_merge_lifetime_params: START");
let wire = parse_generics_str("<'a, T>").unwrap();
let op = parse_generics_str("<'b>").unwrap();
let merged = merge_generics(&wire, &op);
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
info!(" merged params = {:?}", merged_params);
assert_eq!(merged_params, vec!["'a", "T", "'b"]);
}
#[test]
fn test_merge_const_params() {
info!("test_merge_const_params: START");
let wire = parse_generics_str("<T, const N: usize>").unwrap();
let op = parse_generics_str("<const M: u8>").unwrap();
let merged = merge_generics(&wire, &op);
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
info!(" merged params = {:?}", merged_params);
assert_eq!(merged_params, vec!["T", "const N : usize", "const M : u8"]);
}
#[test]
fn test_merge_single_where_clause() {
info!("test_merge_single_where_clause: START");
let wire = parse_generics_str("<T> where T: Clone").unwrap();
let op = parse_generics_str("<U>").unwrap();
let merged = merge_generics(&wire, &op);
info!(" merged => {}", merged.clone().into_token_stream());
assert_eq!(merged.params.len(), 2, "Expected T, U");
match &merged.where_clause {
Some(clause) => {
let clause_str = clause.into_token_stream().to_string();
assert!(
clause_str.contains("T : Clone"),
"Expected 'T : Clone' in merged where."
);
}
None => panic!("Expected where-clause with T: Clone"),
}
}
#[test]
fn test_merge_both_where_clauses() {
info!("test_merge_both_where_clauses: START");
let wire = parse_generics_str("<T> where T: Clone").unwrap();
let op = parse_generics_str("<U> where U: Copy").unwrap();
let merged = merge_generics(&wire, &op);
info!(" merged => {}", merged.clone().into_token_stream());
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
assert_eq!(merged_params, vec!["T", "U"]);
match &merged.where_clause {
Some(clause) => {
let clause_str = clause.into_token_stream().to_string();
assert!(clause_str.contains("T : Clone"));
assert!(clause_str.contains("U : Copy"));
}
None => panic!("Expected merged where-clause with T:Clone, U:Copy"),
}
}
#[test]
fn test_merge_repeated_params() {
info!("test_merge_repeated_params: START");
let wire = parse_generics_str("<T, U>").unwrap();
let op = parse_generics_str("<T, U>").unwrap();
let merged = merge_generics(&wire, &op);
info!(" merged => {}", merged.clone().into_token_stream());
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
assert_eq!(merged_params, vec!["T", "U", "T", "U"]);
}
#[test]
fn test_merge_mixed_params_and_where() {
info!("test_merge_mixed_params_and_where: START");
let wire = parse_generics_str("<'a, T, const M: u8> where T: PartialEq").unwrap();
let op = parse_generics_str("<'b, U, const N: usize> where 'b: 'static, U: Clone").unwrap();
let merged = merge_generics(&wire, &op);
info!(" merged => {}", merged.clone().into_token_stream());
let merged_params: Vec<String> = merged
.params
.iter()
.map(|gp| gp.into_token_stream().to_string())
.collect();
assert_eq!(
merged_params,
vec!["'a", "T", "const M : u8", "'b", "U", "const N : usize"],
"Expected all wire + op parameters in correct order."
);
match &merged.where_clause {
Some(clause) => {
let clause_str = clause.into_token_stream().to_string();
info!(" merged where-clause => {}", clause_str);
assert!(clause_str.contains("T : PartialEq"));
assert!(clause_str.contains("'b : 'static"));
assert!(clause_str.contains("U : Clone"));
}
None => panic!("Expected a merged where-clause, but found none."),
}
}
}