scale_typegen/typegen/
validation.rs

1use scale_info::{form::PortableForm, PortableRegistry};
2use syn::parse_quote;
3
4use crate::{DerivesRegistry, TypeSubstitutes};
5
6use super::{
7    error::SettingsValidationError,
8    settings::substitutes::{path_segments, PathSegments, TryIntoSynPath},
9};
10
11/// Validates that the settings given are valid for the type registry.
12/// It checks for all type specific derives, attributes and substitutes if these types really exist in the type registry.
13/// It is thus overly conservative.
14///
15/// Incrementally builds up an error that contains all the bits and pieces that make the settings invalid.
16pub fn validate_substitutes_and_derives_against_registry(
17    substitutes: &TypeSubstitutes,
18    derives: &DerivesRegistry,
19    types: &PortableRegistry,
20) -> Result<(), SettingsValidationError> {
21    let mut error = SettingsValidationError::default();
22
23    for (path, derives_and_attrs) in derives.derives_on_specific_types() {
24        let path_segments = path_segments(&path.path);
25        if !registry_contains_type_path(types, &path_segments) {
26            let attributes = derives_and_attrs.attributes();
27            let derives = derives_and_attrs.derives();
28
29            if !attributes.is_empty() {
30                let already_in_err = error
31                    .attributes_for_unknown_types
32                    .iter_mut()
33                    .find(|(e, _)| e == &path.path);
34
35                match already_in_err {
36                    Some(e) => e.1.extend(attributes.iter().cloned()),
37                    None => error
38                        .attributes_for_unknown_types
39                        .push((path.path.clone(), attributes.clone())),
40                }
41            }
42
43            if !derives.is_empty() {
44                let already_in_err = error
45                    .derives_for_unknown_types
46                    .iter_mut()
47                    .find(|(e, _)| e == &path.path);
48
49                match already_in_err {
50                    Some(e) => e.1.extend(derives.iter().cloned()),
51                    None => error
52                        .derives_for_unknown_types
53                        .push((path.path.clone(), derives.clone())),
54                }
55            }
56        }
57    }
58
59    for (path, sub) in substitutes.iter() {
60        if !registry_contains_type_path(types, path) {
61            error
62                .substitutes_for_unknown_types
63                .push((path_segments_to_syn_path(path), sub.path().clone()))
64        }
65    }
66
67    if error.is_empty() {
68        Ok(())
69    } else {
70        Err(error)
71    }
72}
73
74/// Converts a `Vec<String>` into a [`syn::Path`]. Returns None if the Vec was empty.
75///
76/// # Panics
77///
78/// Panics if the segments are empty or contain strings that are not valid [`syn::path::PathSegment`]s.
79fn path_segments_to_syn_path(segments: &PathSegments) -> syn::Path {
80    if segments.is_empty() {
81        panic!("Path in Substitutes should not be empty.")
82    }
83    let segments = segments.iter().map(|e| {
84        syn::parse_str::<syn::PathSegment>(e)
85            .expect("PathSegments should be syn::PathSegment compatible")
86    });
87    parse_quote!(#(#segments)::*)
88}
89
90/// Checks if a given type path is the type path of a type in the registry.
91pub fn registry_contains_type_path(types: &PortableRegistry, path: &[String]) -> bool {
92    types.types.iter().any(|t| t.ty.path.segments == *path)
93}
94
95/// Returns types in the PortableRegistry that share the identifier with the input path.
96pub fn similar_type_paths_in_registry(
97    types: &PortableRegistry,
98    path: &syn::Path,
99) -> Vec<syn::Path> {
100    let scale_type_path = scale_info::Path::<PortableForm> {
101        segments: path.segments.iter().map(|e| e.ident.to_string()).collect(),
102    };
103    if scale_type_path.ident().is_none() {
104        return vec![];
105    }
106
107    // Find types that have a path with the same identifier, then convert these paths into `syn::Path`s.
108    types
109        .types
110        .iter()
111        .filter_map(|t| {
112            let path = &t.ty.path;
113            path.ident()
114                .filter(|ident| ident == scale_type_path.ident().as_ref().expect("qed"))?;
115            path.syn_path()
116        })
117        .collect()
118}