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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use crate::{
	error::Error,
	token_iter::{is_ident, SubGroupIter},
	Result, SubstitutionGroup, TokenIter,
};
use heck::ToSnakeCase;
use proc_macro::{Ident, Span, TokenStream, TokenTree};

/// Finds a substitution identifier whose substitutions only contain one
/// identifier and nothing else for all duplicates.
pub(crate) fn find_simple<'a>(
	substitutions: impl Iterator<Item = &'a SubstitutionGroup> + Clone,
	mod_span: Span,
) -> Result<String>
{
	let mut substitutions = substitutions.peekable();
	if substitutions.peek().is_none()
	{
		// No duplications are made, so either the module doesn't need disambiguation
		// (as even with global substitutions only 1 duplicate will be made)
		// or the invocation will fails somewhere else (from the lack of substitution
		// groups)
		return Ok("".into());
	}
	'outer: for ident in substitutions.peek().unwrap().identifiers_ordered()
	{
		for group in substitutions.clone()
		{
			let substitution = group.substitution_of(ident).unwrap();
			if substitution.substitutes_identifier().is_none()
			{
				continue 'outer;
			}
		}
		return Ok(ident.clone());
	}
	Err(Error::new(
		"Was unable to find a suitable substitution identifier to postfix on the module's \
		 name.\nHint: If a substitution identifier's substitutions all consist of a single \
		 identifier and nothing, they will automatically be postfixed on the module name to make \
		 them unique.",
	)
	.span(mod_span))
}

/// If the next token is the 'mod' keyword, substitutes the following module
/// name with its disambiguation, returning 'mod' plus the disambiguation.
pub(crate) fn try_substitute_mod<'a, T: SubGroupIter<'a>>(
	// If Some(), then tries to disambiguate, otherwise doesn't.
	//
	// First is the module name to disambiguate, then the substitution identifier to use
	// for disambiguation.
	mod_and_postfix_sub: &Option<(Ident, String)>,
	substitutions: &SubstitutionGroup,
	// The item being substituted. Will consume 'mod' and the following name if successful
	item_iter: &mut TokenIter<'a, T>,
) -> TokenStream
{
	let mut result = TokenStream::new();
	if let Some((mod_name, mod_sub_ident)) = mod_and_postfix_sub
	{
		item_iter
			.extract_simple(|t| is_ident(t, Some("mod")), |t| t, None)
			.map_or((), |mod_keyword| {
				result.extend(Some(mod_keyword).into_iter());

				// Consume mod name (since we will replace it)
				let mod_name_t = item_iter.next_fallible().unwrap().unwrap();

				let postfix = substitutions
					.substitution_of(&mod_sub_ident)
					.unwrap()
					.substitutes_identifier()
					.unwrap()
					.to_string()
					.to_snake_case();
				let replacement_name = mod_name.to_string() + "_" + &postfix;
				let replacement = Ident::new(&replacement_name, TokenTree::from(mod_name_t).span());
				result.extend(Some(TokenTree::Ident(replacement)).into_iter());
			});
	}
	result
}