scale_typegen/typegen/settings/
substitutes.rs1use proc_macro2::Span;
2use scale_info::form::PortableForm;
3use std::{borrow::Borrow, collections::HashMap};
4use syn::{parse_quote, spanned::Spanned as _, PathSegment};
5
6use crate::{
7 typegen::{
8 error::{TypeSubstitutionError, TypeSubstitutionErrorKind},
9 type_path::{TypePath, TypePathType},
10 },
11 TypeGeneratorSettings,
12};
13
14use TypeSubstitutionErrorKind::*;
15
16fn error(span: Span, kind: TypeSubstitutionErrorKind) -> TypeSubstitutionError {
17 TypeSubstitutionError { span, kind }
18}
19
20#[derive(Debug, Clone)]
23pub struct TypeSubstitutes {
24 substitutes: HashMap<PathSegments, Substitute>,
25}
26
27pub type PathSegments = Vec<String>;
32
33#[derive(Debug, Clone)]
35pub struct Substitute {
36 path: syn::Path,
37 param_mapping: TypeParamMapping,
38}
39
40impl Substitute {
41 pub fn path(&self) -> &syn::Path {
43 &self.path
44 }
45}
46
47#[derive(Debug, Clone)]
48enum TypeParamMapping {
49 PassThrough,
51 Specified(Vec<(syn::Ident, usize)>),
53}
54
55impl Default for TypeSubstitutes {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61impl TypeSubstitutes {
62 pub fn new() -> Self {
64 Self {
65 substitutes: HashMap::new(),
66 }
67 }
68
69 pub fn insert(
71 &mut self,
72 source: syn::Path,
73 target: AbsolutePath,
74 ) -> Result<(), TypeSubstitutionError> {
75 let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
76 self.substitutes.insert(key, val);
77 Ok(())
78 }
79
80 pub fn insert_if_not_exists(
83 &mut self,
84 source: syn::Path,
85 target: AbsolutePath,
86 ) -> Result<(), TypeSubstitutionError> {
87 let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
88 self.substitutes.entry(key).or_insert(val);
89 Ok(())
90 }
91
92 pub fn extend(
94 &mut self,
95 elems: impl IntoIterator<Item = (syn::Path, AbsolutePath)>,
96 ) -> Result<(), TypeSubstitutionError> {
97 for (source, target) in elems.into_iter() {
98 let (key, val) = TypeSubstitutes::parse_path_substitution(source, target.0)?;
99 self.substitutes.insert(key, val);
100 }
101 Ok(())
102 }
103
104 fn parse_path_substitution(
107 src_path: syn::Path,
108 target_path: syn::Path,
109 ) -> Result<(PathSegments, Substitute), TypeSubstitutionError> {
110 let param_mapping = Self::parse_path_param_mapping(&src_path, &target_path)?;
111 let src_path = path_segments(&src_path);
112 Ok((
113 src_path,
114 Substitute {
115 path: target_path,
118 param_mapping,
119 },
120 ))
121 }
122
123 fn parse_path_param_mapping(
126 src_path: &syn::Path,
127 target_path: &syn::Path,
128 ) -> Result<TypeParamMapping, TypeSubstitutionError> {
129 let Some(syn::PathSegment {
130 arguments: src_path_args,
131 ..
132 }) = src_path.segments.last()
133 else {
134 return Err(error(src_path.span(), EmptySubstitutePath));
135 };
136 let Some(syn::PathSegment {
137 arguments: target_path_args,
138 ..
139 }) = target_path.segments.last()
140 else {
141 return Err(error(target_path.span(), EmptySubstitutePath));
142 };
143
144 let source_args = match src_path_args {
146 syn::PathArguments::None => {
147 Vec::new()
149 }
150 syn::PathArguments::AngleBracketed(args) => {
151 args.args
153 .iter()
154 .map(|arg| match get_valid_from_substitution_type(arg) {
155 Some(ident) => Ok(ident),
156 None => Err(error(arg.span(), InvalidFromType)),
157 })
158 .collect::<Result<Vec<_>, _>>()?
159 }
160 syn::PathArguments::Parenthesized(args) => {
161 return Err(error(args.span(), ExpectedAngleBracketGenerics));
163 }
164 };
165
166 let target_args = match target_path_args {
168 syn::PathArguments::None => {
169 Vec::new()
171 }
172 syn::PathArguments::AngleBracketed(args) => {
173 args.args
175 .iter()
176 .map(|arg| match get_valid_to_substitution_type(arg) {
177 Some(arg) => Ok(arg),
178 None => Err(error(arg.span(), InvalidToType)),
179 })
180 .collect::<Result<Vec<_>, _>>()?
181 }
182 syn::PathArguments::Parenthesized(args) => {
183 return Err(error(args.span(), ExpectedAngleBracketGenerics));
185 }
186 };
187
188 if source_args.is_empty() && target_args.is_empty() {
191 return Ok(TypeParamMapping::PassThrough);
192 }
193
194 let mapping = source_args
196 .into_iter()
197 .enumerate()
198 .map(|(idx, ident)| (ident.clone(), idx))
199 .collect();
200
201 Ok(TypeParamMapping::Specified(mapping))
202 }
203
204 pub fn contains(&self, path: &PathSegments) -> bool {
206 !path.is_empty() && self.substitutes.contains_key(path)
207 }
208
209 pub fn for_path_with_params(
212 &self,
213 path: &PathSegments,
214 params: &[TypePath],
215 settings: &TypeGeneratorSettings,
216 ) -> Option<TypePathType> {
217 fn replace_params(
220 mut substitute_path: syn::Path,
221 params: &[TypePath],
222 mapping: &TypeParamMapping,
223 settings: &TypeGeneratorSettings,
224 ) -> TypePathType {
225 match mapping {
226 TypeParamMapping::Specified(mapping) => {
228 let replacement_map: Vec<(&syn::Ident, &TypePath)> = mapping
230 .iter()
231 .filter_map(|(ident, idx)| params.get(*idx).map(|param| (ident, param)))
232 .collect();
233
234 if !replacement_map.is_empty() {
237 replace_path_params_recursively(
238 &mut substitute_path,
239 &replacement_map,
240 settings,
241 );
242 }
243
244 TypePathType::Path {
245 path: substitute_path,
246 params: Vec::new(),
247 }
248 }
249 TypeParamMapping::PassThrough => TypePathType::Path {
251 path: substitute_path,
252 params: params.to_vec(),
253 },
254 }
255 }
256
257 self.substitutes
258 .get(path)
259 .map(|sub| replace_params(sub.path.clone(), params, &sub.param_mapping, settings))
260 }
261
262 pub fn iter(&self) -> impl Iterator<Item = (&PathSegments, &Substitute)> {
264 self.substitutes.iter()
265 }
266}
267
268fn replace_path_params_recursively<I: Borrow<syn::Ident>, P: Borrow<TypePath>>(
281 path: &mut syn::Path,
282 params: &Vec<(I, P)>,
283 settings: &TypeGeneratorSettings,
284) {
285 for segment in &mut path.segments {
286 let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments else {
287 continue;
288 };
289 for arg in &mut args.args {
290 let syn::GenericArgument::Type(ty) = arg else {
291 continue;
292 };
293 let syn::Type::Path(path) = ty else {
294 continue;
295 };
296 if let Some(ident) = get_ident_from_type_path(path) {
297 if let Some((_, replacement)) = params.iter().find(|(i, _)| ident == i.borrow()) {
298 *ty = replacement.borrow().to_syn_type(&settings.alloc_crate_path);
299 continue;
300 }
301 }
302 replace_path_params_recursively(&mut path.path, params, settings);
303 }
304 }
305}
306
307fn get_valid_to_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::TypePath> {
310 let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
311 return None;
313 };
314 Some(type_path)
315}
316
317fn get_valid_from_substitution_type(arg: &syn::GenericArgument) -> Option<&syn::Ident> {
320 let syn::GenericArgument::Type(syn::Type::Path(type_path)) = arg else {
321 return None;
323 };
324 get_ident_from_type_path(type_path)
325}
326
327fn get_ident_from_type_path(type_path: &syn::TypePath) -> Option<&syn::Ident> {
329 if type_path.qself.is_some() {
330 return None;
332 }
333 if type_path.path.leading_colon.is_some() {
334 return None;
336 }
337 if type_path.path.segments.len() > 1 {
338 return None;
340 }
341 let Some(segment) = type_path.path.segments.last() else {
342 return None;
344 };
345 if !segment.arguments.is_empty() {
346 return None;
348 }
349 Some(&segment.ident)
350}
351
352fn is_absolute(path: &syn::Path) -> bool {
354 path.leading_colon.is_some()
355 || path
356 .segments
357 .first()
358 .is_some_and(|segment| segment.ident == "crate")
359}
360
361pub fn absolute_path(path: syn::Path) -> Result<AbsolutePath, TypeSubstitutionError> {
363 path.try_into()
364}
365
366pub fn path_segments(path: &syn::Path) -> PathSegments {
368 path.segments.iter().map(|x| x.ident.to_string()).collect()
369}
370
371pub trait TryIntoSynPath {
373 fn syn_path(self) -> Option<syn::Path>;
375}
376
377impl TryIntoSynPath for &scale_info::Path<PortableForm> {
378 fn syn_path(self) -> Option<syn::Path> {
379 if self.segments.is_empty() {
380 return None;
381 }
382 let segments = self.segments.iter().map(|e| {
383 syn::parse_str::<PathSegment>(e)
384 .expect("scale_info::Path segments should be syn::PathSegment compatible")
385 });
386 Some(parse_quote!(#(#segments)::*))
387 }
388}
389
390impl TryIntoSynPath for syn::Path {
391 fn syn_path(self) -> Option<syn::Path> {
392 Some(self)
393 }
394}
395
396pub struct AbsolutePath(syn::Path);
398
399impl TryFrom<syn::Path> for AbsolutePath {
400 type Error = TypeSubstitutionError;
401 fn try_from(value: syn::Path) -> Result<Self, Self::Error> {
402 if is_absolute(&value) {
403 Ok(AbsolutePath(value))
404 } else {
405 Err(error(value.span(), ExpectedAbsolutePath))
406 }
407 }
408}
409
410#[cfg(test)]
411mod test {
412 use super::*;
413
414 macro_rules! syn_path {
415 ($path:path) => {{
416 let path: syn::Path = syn::parse_quote!($path);
417 path
418 }};
419 }
420
421 macro_rules! type_path {
422 ($path:path) => {{
423 let path: syn::Path = syn::parse_quote!($path);
424 TypePath::from_syn_path(path)
425 }};
426 }
427
428 fn ident(name: &'static str) -> syn::Ident {
429 syn::Ident::new(name, proc_macro2::Span::call_site())
430 }
431
432 #[test]
433 #[rustfmt::skip]
434 fn replacing_nested_type_params_works() {
435 let paths = [
437 (
439 syn_path!(::some::path::Foo<::other::Path<A, B>>),
440 vec![],
441 syn_path!(::some::path::Foo<::other::Path<A, B>>),
442 ),
443 (
445 syn_path!(::some::path::Foo<A>),
446 vec![(ident("A"), type_path!(::new::Value))],
447 syn_path!(::some::path::Foo<::new::Value>),
448 ),
449 (
451 syn_path!(::some::path::Foo<::other::Path<A, B>>),
452 vec![(ident("A"), type_path!(::new::Value))],
453 syn_path!(::some::path::Foo<::other::Path<::new::Value, B>>),
454 ),
455 (
456 syn_path!(::some::path::Foo<::other::Path<A, B>>),
457 vec![
458 (ident("A"), type_path!(::new::A)),
459 (ident("B"), type_path!(::new::B)),
460 ],
461 syn_path!(::some::path::Foo<::other::Path<::new::A, ::new::B>>),
462 ),
463 (
464 syn_path!(::some::path::Foo<::other::Path<A, ::more::path::to<::something::Argh<B>>>, C>),
465 vec![
466 (ident("A"), type_path!(::new::A)),
467 (ident("B"), type_path!(::new::B)),
468 ],
469 syn_path!(::some::path::Foo<::other::Path<::new::A, ::more::path::to<::something::Argh<::new::B>>>,C>),
470 ),
471 (
473 syn_path!(::some::path::Foo<::other::Path<A, ::foo::Argh<A, B>, A>>),
474 vec![(ident("A"), type_path!(::new::Value))],
475 syn_path!(::some::path::Foo<::other::Path<::new::Value, ::foo::Argh<::new::Value, B>, ::new::Value>>),
476 ),
477 ];
478
479 let settings = TypeGeneratorSettings::new();
480 for (mut path, replacements, expected) in paths {
481 replace_path_params_recursively(&mut path, &replacements, &settings);
482 assert_eq!(path, expected);
483 }
484 }
485}