schemars_derive/
name.rs

1use serde_derive_internals::Ctxt;
2use std::collections::{BTreeMap, BTreeSet};
3use syn::Ident;
4
5pub fn get_rename_format_type_params<'a>(
6    errors: &Ctxt,
7    rename_format_string: &syn::LitStr,
8    generics: &'a syn::Generics,
9) -> BTreeSet<&'a Ident> {
10    let mut type_params = BTreeSet::new();
11    let mut str_value = rename_format_string.value();
12
13    if !str_value.contains('{') {
14        return type_params;
15    }
16
17    if str_value.contains("{{") {
18        str_value = str_value.replace("{{", "");
19    }
20
21    if str_value.contains("}}") {
22        str_value = str_value.replace("}}", "");
23    }
24
25    let all_const_params =
26        BTreeSet::from_iter(generics.const_params().map(|c| c.ident.to_string()));
27    let all_type_params = BTreeMap::from_iter(
28        generics
29            .type_params()
30            .map(|c| (c.ident.to_string(), &c.ident)),
31    );
32
33    let mut segments = str_value.split('{');
34
35    if segments.next().unwrap_or_default().contains('}') {
36        // The name format string contains a '}' before the first '{'
37        errors.error_spanned_by(
38            rename_format_string,
39            "invalid name format string: unmatched `}` found",
40        );
41    }
42
43    for segment in segments {
44        match segment.split_once('}') {
45            Some((param, rest)) => {
46                if rest.contains('}') {
47                    errors.error_spanned_by(
48                        rename_format_string,
49                        "invalid name format string: unmatched `}` found",
50                    );
51                }
52
53                if let Some(type_param) = all_type_params.get(param) {
54                    type_params.insert(type_param);
55                } else if all_const_params.contains(param) {
56                    // Any const params will be magically picked up from the surrounding scope by
57                    // `format!()`
58                } else {
59                    errors.error_spanned_by(
60                        rename_format_string,
61                        format_args!(
62                            "invalid name format string: expected generic param, found `{param}`"
63                        ),
64                    );
65                }
66            }
67            None => {
68                errors.error_spanned_by(
69                    rename_format_string,
70                    "invalid name format string: found `{` without matching `}`",
71                );
72            }
73        }
74    }
75
76    type_params
77}