reflectapi_schema/
subst.rs

1use std::collections::HashMap;
2
3use crate::{Enum, Field, Fields, Struct, TypeParameter, TypeReference, Variant};
4
5#[doc(hidden)]
6pub fn mk_subst(
7    parameters: &[TypeParameter],
8    args: &[TypeReference],
9) -> HashMap<String, TypeReference> {
10    assert_eq!(
11        parameters.len(),
12        args.len(),
13        "expected {} type arguments, got {}",
14        parameters.len(),
15        args.len()
16    );
17
18    parameters
19        .iter()
20        .map(|p| p.name.to_owned())
21        .zip(args.iter().cloned())
22        .collect()
23}
24
25pub trait Instantiate {
26    /// Apply type arguments to a generic struct or enum. This should return a non-generic
27    /// instance with all type parameters substituted with the matching type arguments.
28    ///
29    /// Example:
30    /// Given a generic struct `struct Foo<T, U> { a: T, b: U }` and type arguments `[i32,
31    /// String]`, the `instantiate(struct Foo<T, U>, [i32, String])` should be a non-generic struct `struct Foo { a: i32, b: String }`.
32    /// This is implemented by generating a substitution map from the type parameters to the type
33    /// argument `T -> i32, U -> String` and then substituting each occurence of the type parameter with the type argument.
34    fn instantiate(self, args: &[TypeReference]) -> Self;
35}
36
37#[doc(hidden)]
38pub trait Substitute {
39    /// The important implementation of this is `impl Substitute for TypeReference`.
40    /// All other implementations just recursively call `subst` on their relevant fields which
41    /// contain `TypeReference`s.
42    fn subst(self, subst: &HashMap<String, TypeReference>) -> Self;
43}
44
45impl Substitute for TypeReference {
46    fn subst(self, subst: &HashMap<String, TypeReference>) -> Self {
47        match subst.get(&self.name) {
48            Some(ty) => {
49                assert!(
50                    self.arguments.is_empty(),
51                    "type parameter cannot have type arguments"
52                );
53                ty.clone()
54            }
55            None => TypeReference {
56                name: self.name,
57                arguments: self.arguments.into_iter().map(|a| a.subst(subst)).collect(),
58            },
59        }
60    }
61}
62
63impl Substitute for Fields {
64    fn subst(self, subst: &HashMap<String, TypeReference>) -> Self {
65        match self {
66            Fields::Named(fields) => {
67                Fields::Named(fields.into_iter().map(|f| f.subst(subst)).collect())
68            }
69            Fields::Unnamed(fields) => {
70                Fields::Unnamed(fields.into_iter().map(|f| f.subst(subst)).collect())
71            }
72            Fields::None => Fields::None,
73        }
74    }
75}
76
77impl Substitute for Field {
78    fn subst(self, subst: &HashMap<String, TypeReference>) -> Self {
79        Field {
80            type_ref: self.type_ref.subst(subst),
81            ..self
82        }
83    }
84}
85
86impl Substitute for Variant {
87    fn subst(self, subst: &HashMap<String, TypeReference>) -> Self {
88        Self {
89            fields: self.fields.subst(subst),
90            ..self
91        }
92    }
93}
94
95impl Instantiate for Struct {
96    /// Return a new non-generic `Struct` with each type parameter substituted with a type
97    fn instantiate(self, type_args: &[TypeReference]) -> Self {
98        let subst = mk_subst(&self.parameters, type_args);
99
100        Self {
101            parameters: vec![],
102            fields: self.fields.subst(&subst),
103            ..self
104        }
105    }
106}
107
108impl Instantiate for Enum {
109    /// Return a new non-generic `Enum` with each type parameter substituted with a type
110    fn instantiate(self, type_args: &[TypeReference]) -> Self {
111        let subst = mk_subst(&self.parameters, type_args);
112
113        Self {
114            parameters: vec![],
115            variants: self.variants.into_iter().map(|v| v.subst(&subst)).collect(),
116            ..self
117        }
118    }
119}