reactive_graph_type_system_derive/
lib.rs1#[macro_use]
2extern crate darling;
3extern crate proc_macro;
4
5use proc_macro::TokenStream;
6use proc_macro2::TokenStream as TokenStream2;
7
8use quote::format_ident;
9use quote::quote;
10use syn::DeriveInput;
11use syn::Ident;
12use syn::parse_macro_input;
13
14use crate::darling::FromDeriveInput;
15
16#[derive(FromDeriveInput)]
17#[darling(attributes(type_provider))]
18struct TypeProviderConfig {
19 tys: syn::Type,
20 path: String,
21 component_alias: Option<bool>,
22}
23
24uses_type_params!(TypeProviderConfig, tys);
25
26#[proc_macro_derive(TypeProvider, attributes(type_provider))]
27pub fn type_provider(input: TokenStream) -> TokenStream {
28 let input: DeriveInput = parse_macro_input!(input);
29
30 let ident: Ident = input.ident.clone();
31 let ident_assets = format_ident!("{}Assets", ident);
32
33 let type_provider_config = match TypeProviderConfig::from_derive_input(&input) {
34 Ok(type_provider_config) => type_provider_config,
35 Err(e) => {
36 return e.write_errors().into();
37 }
38 };
39 let tys = type_provider_config.tys;
40 let path = type_provider_config.path;
41 let type_provider_id = ident.to_string();
42
43 let component_alias = if type_provider_config.component_alias.unwrap_or(true) {
44 quote! {
45 #[reactive_graph_type_system_api::springtime_di::component_alias]
46 }
47 } else {
48 TokenStream2::new()
49 };
50
51 #[cfg(feature = "json")]
52 let json = {
53 quote! {
54 match reactive_graph_type_system_api::serde_json::from_str(asset_str) {
55 Ok(parsed_entry) => {
56 let entry: <#tys as reactive_graph_graph::NamespacedTypeContainer>::Type = parsed_entry;
57 reactive_graph_graph::NamespacedTypeContainer::push(&entries, entry);
58 }
59 Err(e) => log::error!("Error in parsing JSON file {filename}: {e}"),
60 }
61 }
62 };
63 #[cfg(not(feature = "json"))]
64 let json = {
65 quote! {
66 log::error!("Failed to read type definition from {filename}: JSON is not a supported file format!");
67 }
68 };
69
70 #[cfg(feature = "json5")]
71 let json5 = {
72 quote! {
73 match reactive_graph_type_system_api::json5::from_str(asset_str) {
74 Ok(parsed_entry) => {
75 let entry: <#tys as reactive_graph_graph::NamespacedTypeContainer>::Type = parsed_entry;
76 reactive_graph_graph::NamespacedTypeContainer::push(&entries, entry);
77 }
78 Err(e) => log::error!("Error in parsing JSON5 file {filename}: {e}"),
79 }
80 }
81 };
82 #[cfg(not(feature = "json5"))]
83 let json5 = {
84 quote! {
85 log::error!("Failed to read type definition from {filename}: JSON5 is not a supported file format!");
86 }
87 };
88
89 #[cfg(feature = "toml")]
90 let toml = {
91 quote! {
92 match reactive_graph_type_system_api::toml::from_str(asset_str) {
93 Ok(parsed_entry) => {
94 let entry: <#tys as reactive_graph_graph::NamespacedTypeContainer>::Type = parsed_entry;
95 reactive_graph_graph::NamespacedTypeContainer::push(&entries, entry);
96 }
97 Err(e) => log::error!("Error in parsing TOML file {filename}: {e}"),
98 }
99 }
100 };
101 #[cfg(not(feature = "toml"))]
102 let toml = {
103 quote! {
104 log::error!("Failed to read type definition from {filename}: TOML is not a supported file format!");
105 }
106 };
107
108 let expanded = quote! {
109 #[derive(rust_embed::RustEmbed)]
110 #[folder = #path]
111 struct #ident_assets;
112
113 #[automatically_derived]
114 #component_alias
115 impl reactive_graph_type_system_api::TypeProvider<#tys> for #ident {
116 fn id<'a>(&self) -> &'a str {
117 #type_provider_id
118 }
119 fn get_types(&self) -> #tys {
120 let mut entries = <#tys as reactive_graph_graph::NamespacedTypeContainer>::new();
121 for file in #ident_assets::iter() {
122 let filename = file.as_ref();
123 if filename.starts_with(".") {
124 continue;
126 }
127 log::debug!("Loading resource {}", filename);
128 match #ident_assets::get(filename) {
129 Some(asset) => match std::str::from_utf8(asset.data.as_ref()) {
130 Ok(asset_str) => {
131 if filename.ends_with(".json") {
132 #json
133 } else if filename.ends_with(".json5") {
134 #json5
135 } else if filename.ends_with(".toml") {
136 #toml
137 } else {
138 log::error!("Can't read type definition {}: Only JSON, JSON5 and TOML are supported.", filename);
139 }
140 }
141 Err(e) => log::error!("Error in decoding file to UTF-8 {}: {}", filename, e),
142 },
143 None => {}
144 }
145 }
146 entries
147 }
148 }
149 };
150 TokenStream::from(expanded)
151}